diff --git a/.cargo/config.toml b/.cargo/config.toml index 1368e393fe4..ec87157c946 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -3,6 +3,12 @@ # Flags that apply to all Zebra crates and configurations [target.'cfg(all())'] rustflags = [ + # Enable tx-v6 everywhere by default + "--cfg", 'feature="tx-v6"', + + # TODO: Consider removing this line later (it's needed for the ZSA version of librustzcash crates) + "--cfg", "zcash_unstable=\"nu6\"", + # Zebra standard lints for Rust 1.65+ # High-risk code @@ -82,6 +88,12 @@ rustflags = [ [build] rustdocflags = [ + # Enable tx-v6 everywhere by default + "--cfg", 'feature="tx-v6"', + + # TODO: Consider removing this line later (it's needed for the ZSA version of librustzcash crates) + "--cfg", "zcash_unstable=\"nu6\"", + # The -A and -W settings must be the same as the `RUSTDOCFLAGS` in: # https://github.com/ZcashFoundation/zebra/blob/main/.github/workflows/docs-deploy-firebase.yml#L68 diff --git a/.github/workflows/cd-deploy-nodes-gcp.patch-external.yml b/.github/workflows/cd-deploy-nodes-gcp.patch-external.yml.disabled similarity index 100% rename from .github/workflows/cd-deploy-nodes-gcp.patch-external.yml rename to .github/workflows/cd-deploy-nodes-gcp.patch-external.yml.disabled diff --git a/.github/workflows/cd-deploy-nodes-gcp.patch.yml b/.github/workflows/cd-deploy-nodes-gcp.patch.yml.disabled similarity index 100% rename from .github/workflows/cd-deploy-nodes-gcp.patch.yml rename to .github/workflows/cd-deploy-nodes-gcp.patch.yml.disabled diff --git a/.github/workflows/cd-deploy-nodes-gcp.yml b/.github/workflows/cd-deploy-nodes-gcp.yml.disabled similarity index 100% rename from .github/workflows/cd-deploy-nodes-gcp.yml rename to .github/workflows/cd-deploy-nodes-gcp.yml.disabled diff --git a/.github/workflows/chore-delete-gcp-resources.yml b/.github/workflows/chore-delete-gcp-resources.yml.disabled similarity index 100% rename from .github/workflows/chore-delete-gcp-resources.yml rename to .github/workflows/chore-delete-gcp-resources.yml.disabled diff --git a/.github/workflows/ci-basic.yml b/.github/workflows/ci-basic.yml new file mode 100644 index 00000000000..d8ca56bd4cc --- /dev/null +++ b/.github/workflows/ci-basic.yml @@ -0,0 +1,34 @@ +name: Basic checks + +#on: [push, pull_request] +on: [push] + +jobs: + test: + name: Test on ${{ matrix.os }} + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-24.04] + + env: + # Use system-installed RocksDB library instead of building from scratch + ROCKSDB_LIB_DIR: /usr/lib + # Use system-installed Snappy library for compression in RocksDB + SNAPPY_LIB_DIR: /usr/lib/x86_64-linux-gnu + + steps: + - uses: actions/checkout@v4 + - name: Install dependencies on Ubuntu + #run: sudo apt-get update && sudo apt-get install -y protobuf-compiler build-essential librocksdb-dev + run: sudo apt-get update && sudo apt-get install -y protobuf-compiler librocksdb-dev + - name: Run tests + run: cargo test --verbose + - name: Verify working directory is clean + run: git diff --exit-code + - name: Run doc check + run: cargo doc --all-features --document-private-items + - name: Run format check + run: cargo fmt -- --check + - name: Run clippy + run: cargo clippy --workspace --all-features --all-targets -- -D warnings diff --git a/.github/workflows/deploy-ecs.yaml b/.github/workflows/deploy-ecs.yaml new file mode 100644 index 00000000000..04e454dcb05 --- /dev/null +++ b/.github/workflows/deploy-ecs.yaml @@ -0,0 +1,126 @@ +# This GitHub Actions workflow automates deploying the Zebra Server to Amazon ECS. +# It allows manual triggering with the ability to choose which image tag to deploy. +# Because of the "force-new-deployment" flag, the ECS service will update to a new 'latest' image, if one was pushed. +name: Deploy to Amazon ECS +on: + workflow_dispatch: + inputs: + image_tag: + description: 'Docker image tag to deploy (e.g., latest, v1.0.0, commit-hash)' + required: true + type: string + default: 'latest' +env: + AWS_REGION: ${{ vars.AWS_REGION || 'eu-central-1' }} + ECR_REPOSITORY: ${{ vars.ECR_REPOSITORY || 'dev-zebra-server' }} + ECS_SERVICE: ${{ vars.ECS_SERVICE || 'dev-zebra' }} + ECS_CLUSTER: ${{ vars.ECS_CLUSTER || 'dev-zebra-cluster' }} + TASK_DEFINITION: ${{ vars.TASK_DEFINITION || 'dev-zebra-task' }} + CONTAINER_NAME: ${{ vars.CONTAINER_NAME || 'zebra-container' }} +jobs: + deploy-to-ecs: + name: Deploy to ECS + runs-on: ubuntu-latest + environment: production + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws-region: ${{ env.AWS_REGION }} + - name: Login to Amazon ECR + id: login-ecr + uses: aws-actions/amazon-ecr-login@v2 + - name: Deploy to Amazon ECS + id: deploy-ecs + env: + ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }} + IMAGE_TAG: ${{ github.event.inputs.image_tag }} + run: | + # Construct the full image URI + IMAGE_URI="$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG" + + echo "Deploying image: $IMAGE_URI" + echo "ECS Service: $ECS_SERVICE" + echo "ECS Cluster: $ECS_CLUSTER" + echo "Task Definition: $TASK_DEFINITION" + echo "Container Name: $CONTAINER_NAME" + + # Download the current task definition + aws ecs describe-task-definition \ + --task-definition $TASK_DEFINITION \ + --query 'taskDefinition' > task-definition.json + + # Get the current task definition ARN from the downloaded file + TASK_DEFINITION_ARN=$(jq -r '.taskDefinitionArn' task-definition.json) + + echo "Current task definition ARN: $TASK_DEFINITION_ARN" + + # Update the image in the task definition + jq --arg IMAGE_URI "$IMAGE_URI" --arg CONTAINER_NAME "$CONTAINER_NAME" \ + '.containerDefinitions |= map(if .name == $CONTAINER_NAME then .image = $IMAGE_URI else . end) | del(.taskDefinitionArn, .revision, .status, .requiresAttributes, .placementConstraints, .compatibilities, .registeredAt, .registeredBy)' \ + task-definition.json > updated-task-definition.json + + # Register the new task definition + NEW_TASK_DEFINITION_ARN=$(aws ecs register-task-definition \ + --cli-input-json file://updated-task-definition.json \ + --query 'taskDefinition.taskDefinitionArn' \ + --output text) + + echo "New task definition ARN: $NEW_TASK_DEFINITION_ARN" + + # Update the ECS service with the new task definition + aws ecs update-service \ + --cluster $ECS_CLUSTER \ + --service $ECS_SERVICE \ + --task-definition $NEW_TASK_DEFINITION_ARN \ + --force-new-deployment + + # Wait for the service to be stable + echo "Waiting for ECS service to be stable..." + aws ecs wait services-stable \ + --cluster $ECS_CLUSTER \ + --services $ECS_SERVICE + + # Check the actual service status after waiting + echo "Checking service status..." + SERVICE_STATUS=$(aws ecs describe-services \ + --cluster $ECS_CLUSTER \ + --services $ECS_SERVICE \ + --query 'services[0].status' \ + --output text) + + echo "Service Status: $SERVICE_STATUS" + + # Check if deployment was successful + if [ "$SERVICE_STATUS" = "ACTIVE" ]; then + echo "Deployment completed successfully!" + else + echo "Deployment may have issues. Service status: $SERVICE_STATUS" + exit 1 + fi + + # Output the deployment information + echo "deployed_image=$IMAGE_URI" >> $GITHUB_OUTPUT + echo "ecs_service=$ECS_SERVICE" >> $GITHUB_OUTPUT + echo "ecs_cluster=$ECS_CLUSTER" >> $GITHUB_OUTPUT + echo "task_definition_arn=$NEW_TASK_DEFINITION_ARN" >> $GITHUB_OUTPUT + - name: Get deployment status + run: | + echo "Deployment Status:" + aws ecs describe-services \ + --cluster $ECS_CLUSTER \ + --services $ECS_SERVICE \ + --query 'services[0].{ServiceName:serviceName,Status:status,DesiredCount:desiredCount,RunningCount:runningCount,PendingCount:pendingCount,TaskDefinition:taskDefinition}' \ + --output table + + echo "" + echo "Current running tasks:" + aws ecs list-tasks \ + --cluster $ECS_CLUSTER \ + --service-name $ECS_SERVICE \ + --query 'taskArns' \ + --output table diff --git a/.github/workflows/docs-deploy-firebase.patch-external.yml b/.github/workflows/docs-deploy-firebase.patch-external.yml.disabled similarity index 100% rename from .github/workflows/docs-deploy-firebase.patch-external.yml rename to .github/workflows/docs-deploy-firebase.patch-external.yml.disabled diff --git a/.github/workflows/docs-deploy-firebase.patch.yml b/.github/workflows/docs-deploy-firebase.patch.yml.disabled similarity index 100% rename from .github/workflows/docs-deploy-firebase.patch.yml rename to .github/workflows/docs-deploy-firebase.patch.yml.disabled diff --git a/.github/workflows/docs-deploy-firebase.yml b/.github/workflows/docs-deploy-firebase.yml.disabled similarity index 100% rename from .github/workflows/docs-deploy-firebase.yml rename to .github/workflows/docs-deploy-firebase.yml.disabled diff --git a/.github/workflows/docs-dockerhub-description.yml b/.github/workflows/docs-dockerhub-description.yml.disabled similarity index 100% rename from .github/workflows/docs-dockerhub-description.yml rename to .github/workflows/docs-dockerhub-description.yml.disabled diff --git a/.github/workflows/manual-zcashd-deploy.yml b/.github/workflows/manual-zcashd-deploy.yml.disabled similarity index 100% rename from .github/workflows/manual-zcashd-deploy.yml rename to .github/workflows/manual-zcashd-deploy.yml.disabled diff --git a/.github/workflows/push-ecr.yaml b/.github/workflows/push-ecr.yaml new file mode 100644 index 00000000000..6e38c2ac0dd --- /dev/null +++ b/.github/workflows/push-ecr.yaml @@ -0,0 +1,119 @@ +# This GitHub Actions workflow automates pushing the Zebra Server Docker image to Amazon ECR. +# It triggers on any new tag or manual dispatch, builds a Docker image, and pushes it to Amazon Elastic Container Registry (ECR). +name: Push to Amazon ECR + +on: + push: + tags: + - '*' # Triggers the workflow on any new tag + workflow_dispatch: + inputs: + image_tag_version: + description: 'Version to tag the Docker image' + required: false + type: string + +env: + AWS_REGION: ${{ vars.AWS_REGION || 'eu-central-1' }} + ECR_REPOSITORY: ${{ vars.ECR_REPOSITORY || 'dev-zebra-server' }} + DOCKERFILE_PATH: ${{ vars.DOCKERFILE_PATH }} + +jobs: + push-to-ecr: + name: Push to ECR + runs-on: ubuntu-latest + environment: production + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws-region: ${{ env.AWS_REGION }} + + - name: Login to Amazon ECR + id: login-ecr + uses: aws-actions/amazon-ecr-login@v2 + + - name: Get Git tags and set image tags + id: vars + run: | + git fetch --tags + + # Get exact match tag if it exists (will be empty if the current commit doesn't have a tag) + GIT_TAG=$(git describe --exact-match --tags 2>/dev/null || echo "") + + # Set environment variables and echo results + if [ -n "$GIT_TAG" ]; then + echo "GIT_TAG=$GIT_TAG" >> $GITHUB_ENV + echo "Git Tag Discovery:" + echo " Found exact match Git tag: $GIT_TAG" + else + echo "Git Tag Discovery:" + echo " No exact match Git tag found for current commit" + fi + + # Set the input IMAGE_TAG_VERSION + echo "IMAGE_TAG_VERSION=${{ github.event.inputs.image_tag_version }}" >> $GITHUB_ENV + echo " User-provided IMAGE_TAG_VERSION: ${{ github.event.inputs.image_tag_version }}" + + - name: Build, tag, and push image to Amazon ECR + id: build-image + env: + ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }} + IMAGE_TAG_LATEST: latest + run: | + # Build docker container with multiple tags + DOCKER_BUILD_ARGS=() + DOCKER_BUILD_ARGS+=("-t" "$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG_LATEST") + DOCKER_BUILD_ARGS+=("-t" "$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG_VERSION") + + # Add exact tag if it exists + if [ -n "$GIT_TAG" ]; then + DOCKER_BUILD_ARGS+=("-t" "$ECR_REGISTRY/$ECR_REPOSITORY:$GIT_TAG") + fi + + # Echo final tags that will be pushed + echo "Docker Image Tags to be pushed:" + for arg in "${DOCKER_BUILD_ARGS[@]}"; do + if [[ "$arg" != "-t" ]]; then + echo " $arg" + fi + done + echo "" + + # Build with all tags + echo "Building Docker image..." + docker build "${DOCKER_BUILD_ARGS[@]}" -f $DOCKERFILE_PATH . + + # Push all tags with error handling + for tag in "$IMAGE_TAG_LATEST" "$IMAGE_TAG_VERSION" "$GIT_TAG"; do + # Skip empty tags (e.g., if GIT_TAG is unset) + [ -z "$tag" ] && continue + + image="$ECR_REGISTRY/$ECR_REPOSITORY:$tag" + echo "Pushing $image…" + if ! docker push "$image"; then + echo "Failed to push $image" + exit 1 + fi + done + + # Output the image URIs + echo "image_latest=$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG_LATEST" >> $GITHUB_OUTPUT + echo "image_version=$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG_VERSION" >> $GITHUB_OUTPUT + + if [ -n "$GIT_TAG" ]; then + echo "image_exact_tag=$ECR_REGISTRY/$ECR_REPOSITORY:$GIT_TAG" >> $GITHUB_OUTPUT + fi + + # Print the public repository URL + echo "" + echo "=====================================" + echo "Public ECR Repository URL:" + echo "https://gallery.ecr.aws/$ECR_REPOSITORY" + echo "=====================================" diff --git a/.github/workflows/release-binaries.yml b/.github/workflows/release-binaries.yml.disabled similarity index 100% rename from .github/workflows/release-binaries.yml rename to .github/workflows/release-binaries.yml.disabled diff --git a/.github/workflows/release-crates-io.patch.yml b/.github/workflows/release-crates-io.patch.yml.disabled similarity index 100% rename from .github/workflows/release-crates-io.patch.yml rename to .github/workflows/release-crates-io.patch.yml.disabled diff --git a/.github/workflows/release-crates-io.yml b/.github/workflows/release-crates-io.yml.disabled similarity index 100% rename from .github/workflows/release-crates-io.yml rename to .github/workflows/release-crates-io.yml.disabled diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml.disabled similarity index 100% rename from .github/workflows/release-drafter.yml rename to .github/workflows/release-drafter.yml.disabled diff --git a/.github/workflows/sub-build-docker-image.yml b/.github/workflows/sub-build-docker-image.yml.disabled similarity index 100% rename from .github/workflows/sub-build-docker-image.yml rename to .github/workflows/sub-build-docker-image.yml.disabled diff --git a/.github/workflows/sub-ci-integration-tests-gcp.yml b/.github/workflows/sub-ci-integration-tests-gcp.yml.disabled similarity index 100% rename from .github/workflows/sub-ci-integration-tests-gcp.yml rename to .github/workflows/sub-ci-integration-tests-gcp.yml.disabled diff --git a/.github/workflows/sub-ci-unit-tests-docker.yml b/.github/workflows/sub-ci-unit-tests-docker.yml.disabled similarity index 100% rename from .github/workflows/sub-ci-unit-tests-docker.yml rename to .github/workflows/sub-ci-unit-tests-docker.yml.disabled diff --git a/.github/workflows/sub-deploy-integration-tests-gcp.yml b/.github/workflows/sub-deploy-integration-tests-gcp.yml.disabled similarity index 100% rename from .github/workflows/sub-deploy-integration-tests-gcp.yml rename to .github/workflows/sub-deploy-integration-tests-gcp.yml.disabled diff --git a/.github/workflows/sub-find-cached-disks.yml b/.github/workflows/sub-find-cached-disks.yml.disabled similarity index 100% rename from .github/workflows/sub-find-cached-disks.yml rename to .github/workflows/sub-find-cached-disks.yml.disabled diff --git a/.github/workflows/sub-test-zebra-config.yml b/.github/workflows/sub-test-zebra-config.yml.disabled similarity index 100% rename from .github/workflows/sub-test-zebra-config.yml rename to .github/workflows/sub-test-zebra-config.yml.disabled diff --git a/Cargo.lock b/Cargo.lock index 14220da0ea0..a55b28eb2a4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -56,12 +56,6 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" -[[package]] -name = "adler2" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" - [[package]] name = "aead" version = "0.5.2" @@ -143,9 +137,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.17" +version = "0.6.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23a1e53f0f5d86382dafe1cf314783b2044280f406e7e1506368220ad11b1338" +checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" dependencies = [ "anstyle", "anstyle-parse", @@ -158,43 +152,43 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.9" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8365de52b16c035ff4fcafe0092ba9390540e3e352870ac09933bebcaa2c8c56" +checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" [[package]] name = "anstyle-parse" -version = "0.2.6" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" +checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.1.2" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" +checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] name = "anstyle-wincon" -version = "3.0.6" +version = "3.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125" +checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" dependencies = [ "anstyle", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] name = "anyhow" -version = "1.0.91" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c042108f3ed77fd83760a5fd79b53be043192bb3b9dba91d8c574c0ada7850c8" +checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" [[package]] name = "arc-swap" @@ -204,21 +198,21 @@ checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" [[package]] name = "arrayref" -version = "0.3.9" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" +checksum = "9d151e35f61089500b617991b791fc8bfd237ae50cd5950803758a179b41e67a" [[package]] name = "arrayvec" -version = "0.7.6" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" [[package]] name = "async-compression" -version = "0.4.17" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cb8f1d480b0ea3783ab015936d2a55c87e219676f0c0b7dec61494043f21857" +checksum = "fec134f64e2bc57411226dfc4e52dec859ddfc7e711fc5e07b612584f000e4aa" dependencies = [ "flate2", "futures-core", @@ -229,9 +223,9 @@ dependencies = [ [[package]] name = "async-stream" -version = "0.3.6" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476" +checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51" dependencies = [ "async-stream-impl", "futures-core", @@ -240,9 +234,9 @@ dependencies = [ [[package]] name = "async-stream-impl" -version = "0.3.6" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" +checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", @@ -251,9 +245,9 @@ dependencies = [ [[package]] name = "async-trait" -version = "0.1.83" +version = "0.1.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" +checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107" dependencies = [ "proc-macro2", "quote", @@ -279,15 +273,15 @@ dependencies = [ [[package]] name = "autocfg" -version = "1.4.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "axum" -version = "0.7.7" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "504e3947307ac8326a5437504c517c4b56716c9d98fac0028c2acc7ca47d70ae" +checksum = "3a6c9af12842a67734c9a2e355436e5d03b22383ed60cf13cd0c18fbfe3dcbcf" dependencies = [ "async-trait", "axum-core", @@ -305,16 +299,16 @@ dependencies = [ "rustversion", "serde", "sync_wrapper 1.0.1", - "tower 0.5.1", + "tower", "tower-layer", "tower-service", ] [[package]] name = "axum-core" -version = "0.4.5" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09f2bd6146b97ae3359fa0cc6d6b376d9539582c7b4220f041a33ec24c226199" +checksum = "a15c63fd72d41492dc4f497196f5da1fb04fb7529e631d73630d1b491e47a2e3" dependencies = [ "async-trait", "bytes", @@ -325,7 +319,7 @@ dependencies = [ "mime", "pin-project-lite", "rustversion", - "sync_wrapper 1.0.1", + "sync_wrapper 0.1.2", "tower-layer", "tower-service", ] @@ -340,11 +334,17 @@ dependencies = [ "cc", "cfg-if 1.0.0", "libc", - "miniz_oxide 0.7.4", + "miniz_oxide", "object", "rustc-demangle", ] +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + [[package]] name = "base64" version = "0.11.0" @@ -371,9 +371,9 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "base64ct" -version = "1.6.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" +checksum = "8a32fd6af2b5827bce66c29053ba0e7c42b9dcab01835835058558c10851a46b" [[package]] name = "bech32" @@ -413,9 +413,9 @@ dependencies = [ [[package]] name = "bindgen" -version = "0.69.5" +version = "0.69.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088" +checksum = "a00dc851838a2120612785d195287475a3ac45514741da670b735818822129a0" dependencies = [ "bitflags 2.6.0", "cexpr", @@ -423,47 +423,28 @@ dependencies = [ "itertools 0.12.1", "lazy_static", "lazycell", - "proc-macro2", - "quote", - "regex", - "rustc-hash 1.1.0", - "shlex", - "syn 2.0.85", -] - -[[package]] -name = "bindgen" -version = "0.70.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f49d8fed880d473ea71efb9bf597651e77201bdd4893efe54c9e5d65ae04ce6f" -dependencies = [ - "bitflags 2.6.0", - "cexpr", - "clang-sys", - "itertools 0.13.0", "log", "prettyplease", "proc-macro2", "quote", "regex", - "rustc-hash 1.1.0", + "rustc-hash", "shlex", "syn 2.0.85", + "which", ] [[package]] -name = "bip32" -version = "0.5.2" +name = "bip0039" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa13fae8b6255872fd86f7faf4b41168661d7d78609f7bfe6771b85c6739a15b" +checksum = "bef0f0152ec5cf17f49a5866afaa3439816207fd4f0a224c0211ffaf5e278426" dependencies = [ - "bs58", "hmac", - "rand_core 0.6.4", - "ripemd", - "secp256k1", + "pbkdf2", + "rand 0.8.5", "sha2", - "subtle", + "unicode-normalization", "zeroize", ] @@ -562,11 +543,11 @@ dependencies = [ [[package]] name = "bridgetree" -version = "0.6.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cef977c7f8e75aa81fc589064c121ab8d32448b7939d34d58df479aa93e65ea5" +checksum = "fbfcb6c5a091e80cb3d3b0c1a7f126af4631cd5065b1f9929b139f1be8f3fb62" dependencies = [ - "incrementalmerkletree 0.7.0", + "incrementalmerkletree", ] [[package]] @@ -603,9 +584,9 @@ checksum = "c3ac9f8b63eca6fd385229b3675f6cc0dc5c8a5c8a54a59d4f52ffd670d87b0c" [[package]] name = "bytemuck" -version = "1.19.0" +version = "1.16.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8334215b81e418a0a7bdb8ef0849474f40bb10c8b71f1c4ed315cff49f32494d" +checksum = "102087e286b4677862ea56cf8fc58bb2cdfa8725c40ffb80fe3a008eb7f2fc83" [[package]] name = "byteorder" @@ -632,9 +613,9 @@ dependencies = [ [[package]] name = "camino" -version = "1.1.9" +version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b96ec4966b5813e2c0507c1f86115c8c5abaadc3980879c3424042a02fd1ad3" +checksum = "e0ec6b951b160caa93cc0c7b209e5a3bff7aae9062213451ac99493cd844c239" dependencies = [ "serde", ] @@ -685,13 +666,12 @@ dependencies = [ [[package]] name = "cc" -version = "1.1.31" +version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2e7962b54006dcfcc61cb72735f4d89bb97061dd6a7ed882ec6b8ee53714c6f" +checksum = "26a5c3fd7bfa1ce3897a3a3501d362b2d87b7f2583ebcb4a949ec25911025cbc" dependencies = [ "jobserver", "libc", - "shlex", ] [[package]] @@ -892,9 +872,9 @@ dependencies = [ [[package]] name = "colorchoice" -version = "1.0.3" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" +checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" [[package]] name = "console" @@ -911,22 +891,22 @@ dependencies = [ [[package]] name = "console-api" -version = "0.8.1" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8030735ecb0d128428b64cd379809817e620a40e5001c54465b99ec5feec2857" +checksum = "86ed14aa9c9f927213c6e4f3ef75faaad3406134efe84ba2cb7983431d5f0931" dependencies = [ "futures-core", - "prost", - "prost-types", + "prost 0.13.3", + "prost-types 0.13.1", "tonic", "tracing-core", ] [[package]] name = "console-subscriber" -version = "0.4.1" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6539aa9c6a4cd31f4b1c040f860a1eac9aa80e7df6b05d506a6e7179936d6a01" +checksum = "e2e3a111a37f3333946ebf9da370ba5c5577b18eb342ec683eb488dd21980302" dependencies = [ "console-api", "crossbeam-channel", @@ -935,8 +915,8 @@ dependencies = [ "hdrhistogram", "humantime", "hyper-util", - "prost", - "prost-types", + "prost 0.13.3", + "prost-types 0.13.1", "serde", "serde_json", "thread_local", @@ -956,9 +936,9 @@ checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" [[package]] name = "constant_time_eq" -version = "0.3.1" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6" +checksum = "f7144d30dcf0fafbce74250a3963025d8d52177934239851c917d29f1df280c2" [[package]] name = "core-foundation" @@ -972,15 +952,15 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.7" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" [[package]] name = "cpufeatures" -version = "0.2.14" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" +checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" dependencies = [ "libc", ] @@ -1070,6 +1050,18 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" +[[package]] +name = "crypto-bigint" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +dependencies = [ + "generic-array", + "rand_core 0.6.4", + "subtle", + "zeroize", +] + [[package]] name = "crypto-common" version = "0.1.6" @@ -1091,7 +1083,7 @@ dependencies = [ "curve25519-dalek-derive", "digest", "fiat-crypto", - "rustc_version 0.4.1", + "rustc_version 0.4.0", "serde", "subtle", "zeroize", @@ -1215,6 +1207,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer", + "const-oid", "crypto-common", "subtle", ] @@ -1255,6 +1248,20 @@ version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125" +[[package]] +name = "ecdsa" +version = "0.16.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" +dependencies = [ + "der", + "digest", + "elliptic-curve", + "rfc6979", + "signature", + "spki", +] + [[package]] name = "ed25519" version = "2.2.3" @@ -1299,7 +1306,7 @@ dependencies = [ "dyn-clone", "lazy_static", "percent-encoding", - "reqwest 0.11.27", + "reqwest", "rustc_version 0.2.3", "serde", "serde_json", @@ -1308,6 +1315,25 @@ dependencies = [ "void", ] +[[package]] +name = "elliptic-curve" +version = "0.13.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" +dependencies = [ + "base16ct", + "crypto-bigint", + "digest", + "ff", + "generic-array", + "group", + "pkcs8", + "rand_core 0.6.4", + "sec1", + "subtle", + "zeroize", +] + [[package]] name = "encode_unicode" version = "0.3.6" @@ -1316,9 +1342,9 @@ checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" [[package]] name = "encoding_rs" -version = "0.8.35" +version = "0.8.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" +checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" dependencies = [ "cfg-if 1.0.0", ] @@ -1346,7 +1372,7 @@ dependencies = [ [[package]] name = "equihash" version = "0.2.0" -source = "git+https://github.com/zcash/librustzcash.git?rev=1410f1449100a417bfbc4f6c7167aa9808e38792#1410f1449100a417bfbc4f6c7167aa9808e38792" +source = "git+https://github.com/QED-it/librustzcash?rev=e88964e248c9038e757771b8225c47ac8772abc4#e88964e248c9038e757771b8225c47ac8772abc4" dependencies = [ "blake2b_simd", "byteorder", @@ -1381,16 +1407,7 @@ dependencies = [ [[package]] name = "f4jumble" version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a83e8d7fd0c526af4aad893b7c9fe41e2699ed8a776a6c74aecdeafe05afc75" -dependencies = [ - "blake2b_simd", -] - -[[package]] -name = "f4jumble" -version = "0.1.0" -source = "git+https://github.com/zcash/librustzcash.git?rev=1410f1449100a417bfbc4f6c7167aa9808e38792#1410f1449100a417bfbc4f6c7167aa9808e38792" +source = "git+https://github.com/QED-it/librustzcash?rev=e88964e248c9038e757771b8225c47ac8772abc4#e88964e248c9038e757771b8225c47ac8772abc4" dependencies = [ "blake2b_simd", ] @@ -1438,12 +1455,12 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" [[package]] name = "flate2" -version = "1.0.34" +version = "1.0.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1b589b4dc103969ad3cf85c950899926ec64300a1a46d76c03a6072957036f0" +checksum = "7f211bbe8e69bbd0cfdea405084f128ae8b4aaa6b0b522fc8f2b009084797920" dependencies = [ "crc32fast", - "miniz_oxide 0.8.0", + "miniz_oxide", ] [[package]] @@ -1456,7 +1473,7 @@ dependencies = [ "futures-sink", "nanorand", "pin-project", - "spin", + "spin 0.9.8", ] [[package]] @@ -1606,6 +1623,7 @@ checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", + "zeroize", ] [[package]] @@ -1659,9 +1677,9 @@ checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] name = "globset" -version = "0.4.15" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15f1ce686646e7f1e19bf7d5533fe443a45dbfb990e00629110797578b42fb19" +checksum = "57da3b9b5b85bd66f31093f8c408b90a74431672542466497dcbdfdc02034be1" dependencies = [ "aho-corasick", "bstr", @@ -1703,9 +1721,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.4.6" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524e8ac6999421f49a846c2d4411f337e53497d8ec55d67753beffa43c5d9205" +checksum = "fa82e28a107a8cc405f0839610bdc9b15f1e25ec7d696aa5cf173edbcb1486ab" dependencies = [ "atomic-waker", "bytes", @@ -1722,19 +1740,17 @@ dependencies = [ [[package]] name = "half" -version = "2.4.1" +version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" +checksum = "02b4af3693f1b705df946e9fe5631932443781d0aabb423b62fcd4d73f6d2fd0" dependencies = [ - "cfg-if 1.0.0", "crunchy", ] [[package]] name = "halo2_gadgets" version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "126a150072b0c38c7b573fe3eaf0af944a7fed09e154071bf2436d3f016f7230" +source = "git+https://github.com/QED-it/halo2?branch=zsa1#90bc56539022c7b47d3da6201958ae2d5a694207" dependencies = [ "arrayvec", "bitvec", @@ -1757,8 +1773,7 @@ checksum = "47716fe1ae67969c5e0b2ef826f32db8c3be72be325e1aa3c1951d06b5575ec5" [[package]] name = "halo2_proofs" version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b867a8d9bbb85fca76fff60652b5cd19b853a1c4d0665cb89bee68b18d2caf0" +source = "git+https://github.com/QED-it/halo2?branch=zsa1#1195c9af90205829ba20662bdfaf20dcc878807d" dependencies = [ "blake2b_simd", "ff", @@ -1808,6 +1823,19 @@ dependencies = [ "num-traits", ] +[[package]] +name = "hdwallet" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a03ba7d4c9ea41552cd4351965ff96883e629693ae85005c501bb4b9e1c48a7" +dependencies = [ + "lazy_static", + "rand_core 0.6.4", + "ring 0.16.20", + "secp256k1 0.26.0", + "thiserror", +] + [[package]] name = "heck" version = "0.3.3" @@ -1838,12 +1866,6 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" -[[package]] -name = "hermit-abi" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" - [[package]] name = "hex" version = "0.4.3" @@ -1957,9 +1979,9 @@ dependencies = [ [[package]] name = "httparse" -version = "1.9.5" +version = "1.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946" +checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9" [[package]] name = "httpdate" @@ -1991,9 +2013,9 @@ dependencies = [ [[package]] name = "hyper" -version = "0.14.31" +version = "0.14.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c08302e8fa335b151b788c775ff56e7a03ae64ff85c548ee820fecb70356e85" +checksum = "a152ddd61dfaec7273fe8419ab357f33aee0d914c5f4efbf0d96fa749eea5ec9" dependencies = [ "bytes", "futures-channel", @@ -2022,7 +2044,7 @@ dependencies = [ "bytes", "futures-channel", "futures-util", - "h2 0.4.6", + "h2 0.4.5", "http 1.1.0", "http-body 1.0.1", "httparse", @@ -2042,28 +2064,10 @@ checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" dependencies = [ "futures-util", "http 0.2.12", - "hyper 0.14.31", - "rustls 0.21.12", - "tokio", - "tokio-rustls 0.24.1", -] - -[[package]] -name = "hyper-rustls" -version = "0.27.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08afdbb5c31130e3034af566421053ab03787c640246a446327f550d11bcb333" -dependencies = [ - "futures-util", - "http 1.1.0", - "hyper 1.5.0", - "hyper-util", - "rustls 0.23.15", - "rustls-pki-types", + "hyper 0.14.30", + "rustls", "tokio", - "tokio-rustls 0.26.0", - "tower-service", - "webpki-roots 0.26.6", + "tokio-rustls", ] [[package]] @@ -2100,9 +2104,9 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.61" +version = "0.1.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" +checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" dependencies = [ "android_system_properties", "core-foundation-sys", @@ -2159,17 +2163,9 @@ dependencies = [ [[package]] name = "incrementalmerkletree" -version = "0.6.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75346da3bd8e3d8891d02508245ed2df34447ca6637e343829f8d08986e9cde2" -dependencies = [ - "either", -] - -[[package]] -name = "incrementalmerkletree" -version = "0.7.0" -source = "git+https://github.com/zcash/incrementalmerkletree?rev=ffe4234788fd22662b937ba7c6ea01535fcc1293#ffe4234788fd22662b937ba7c6ea01535fcc1293" +checksum = "eb1872810fb725b06b8c153dde9e86f3ec26747b9b60096da7a869883b549cbe" dependencies = [ "either", ] @@ -2268,17 +2264,17 @@ dependencies = [ [[package]] name = "ipnet" -version = "2.10.1" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddc24109865250148c2e0f3d25d4f0f479571723792d3802153c60922a4fb708" +checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" [[package]] name = "is-terminal" -version = "0.4.13" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "261f68e344040fbd0edea105bef17c66edf46f984ddb1115b775ce31be948f4b" +checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b" dependencies = [ - "hermit-abi 0.4.0", + "hermit-abi 0.3.9", "libc", "windows-sys 0.52.0", ] @@ -2333,9 +2329,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.72" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9" +checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" dependencies = [ "wasm-bindgen", ] @@ -2385,7 +2381,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1dea6e07251d9ce6a552abfb5d7ad6bc290a4596c8dcc3d795fae2bbdc1f3ff" dependencies = [ "futures", - "hyper 0.14.31", + "hyper 0.14.30", "jsonrpc-core", "jsonrpc-server-utils", "log", @@ -2426,13 +2422,27 @@ dependencies = [ "subtle", ] +[[package]] +name = "k256" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "956ff9b67e26e1a6a866cb758f12c6f8746208489e3e4a4b5580802f2f0a587b" +dependencies = [ + "cfg-if 1.0.0", + "ecdsa", + "elliptic-curve", + "once_cell", + "sha2", + "signature", +] + [[package]] name = "known-folders" -version = "1.2.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7d9a1740cc8b46e259a0eb787d79d855e79ff10b9855a5eba58868d5da7927c" +checksum = "4397c789f2709d23cfcb703b316e0766a8d4b17db2d47b0ab096ef6047cae1d8" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -2441,7 +2451,7 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" dependencies = [ - "spin", + "spin 0.9.8", ] [[package]] @@ -2452,9 +2462,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.161" +version = "0.2.159" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1" +checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5" [[package]] name = "libgit2-sys" @@ -2500,7 +2510,7 @@ version = "0.16.0+8.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce3d60bc059831dc1c83903fb45c103f75db65c5a7bf22272764d9cc683e348c" dependencies = [ - "bindgen 0.69.5", + "bindgen", "bzip2-sys", "cc", "glob", @@ -2521,9 +2531,9 @@ dependencies = [ [[package]] name = "libz-sys" -version = "1.1.20" +version = "1.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2d16453e800a8cf6dd2fc3eb4bc99b786a9b90c663b8559a5b1a041bf89e472" +checksum = "c15da26e5af7e25c90b37a2d75cdbf940cf4a55316de9d84c679c9b8bfabf82e" dependencies = [ "cc", "libc", @@ -2567,9 +2577,9 @@ checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] name = "lz4-sys" -version = "1.11.1+lz4-1.10.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bd8c0d6c6ed0cd30b3652886bb8711dc4bb01d637a68105a3d5158039b418e6" +checksum = "109de74d5d2353660401699a4174a4ff23fcc649caf553df71933c7fb45ad868" dependencies = [ "cc", "libc", @@ -2680,20 +2690,11 @@ dependencies = [ "adler", ] -[[package]] -name = "miniz_oxide" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" -dependencies = [ - "adler2", -] - [[package]] name = "mio" -version = "1.0.2" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" +checksum = "4569e456d394deccd22ce1c1913e6ea0e54519f577285001215d33557431afe4" dependencies = [ "hermit-abi 0.3.9", "libc", @@ -2876,8 +2877,8 @@ checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" [[package]] name = "orchard" -version = "0.9.1" -source = "git+https://github.com/zcash/orchard?rev=55fb089a335bbbc1cda186c706bc037073df8eb7#55fb089a335bbbc1cda186c706bc037073df8eb7" +version = "0.8.0" +source = "git+https://github.com/QED-it/orchard?rev=831ca109705a409bc3d3b82e76245e45dd0f0812#831ca109705a409bc3d3b82e76245e45dd0f0812" dependencies = [ "aes", "bitvec", @@ -2885,20 +2886,22 @@ dependencies = [ "ff", "fpe", "group", + "half", "halo2_gadgets", "halo2_proofs", "hex", - "incrementalmerkletree 0.7.0", + "incrementalmerkletree", + "k256", "lazy_static", "memuse", "nonempty", "pasta_curves", + "proptest", "rand 0.8.5", "reddsa", "serde", "subtle", "tracing", - "visibility", "zcash_note_encryption", "zcash_spec", "zip32", @@ -2972,7 +2975,7 @@ version = "3.6.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d830939c76d294956402033aee57a6da7b438f2294eb94864c37b0569053a42c" dependencies = [ - "proc-macro-crate 3.2.0", + "proc-macro-crate 3.1.0", "proc-macro2", "quote", "syn 1.0.109", @@ -3021,11 +3024,22 @@ checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if 1.0.0", "libc", - "redox_syscall 0.5.7", + "redox_syscall 0.5.3", "smallvec", "windows-targets 0.52.6", ] +[[package]] +name = "password-hash" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d791538a6dcc1e7cb7fe6f6b58aca40e7f79403c45b2bc274008b5e647af1d8" +dependencies = [ + "base64ct", + "rand_core 0.6.4", + "subtle", +] + [[package]] name = "pasta_curves" version = "0.5.1" @@ -3041,6 +3055,16 @@ dependencies = [ "subtle", ] +[[package]] +name = "pbkdf2" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271779f35b581956db91a3e55737327a03aa051e90b1c47aeb189508533adfd7" +dependencies = [ + "digest", + "password-hash", +] + [[package]] name = "percent-encoding" version = "2.3.1" @@ -3049,9 +3073,9 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pest" -version = "2.7.14" +version = "2.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "879952a81a83930934cbf1786752d6dedc3b1f29e8f8fb2ad1d0a36f377cf442" +checksum = "cd53dff83f26735fdc1ca837098ccf133605d794cdae66acfc2bfac3ec809d95" dependencies = [ "memchr", "thiserror", @@ -3060,9 +3084,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.7.14" +version = "2.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d214365f632b123a47fd913301e14c946c61d1c183ee245fa76eb752e59a02dd" +checksum = "2a548d2beca6773b1c244554d36fcf8548a8a58e74156968211567250e48e49a" dependencies = [ "pest", "pest_generator", @@ -3070,9 +3094,9 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.7.14" +version = "2.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb55586734301717aea2ac313f50b2eb8f60d2fc3dc01d190eefa2e625f60c4e" +checksum = "3c93a82e8d145725dcbaf44e5ea887c8a869efdcc28706df2d08c69e17077183" dependencies = [ "pest", "pest_meta", @@ -3083,9 +3107,9 @@ dependencies = [ [[package]] name = "pest_meta" -version = "2.7.14" +version = "2.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b75da2a70cf4d9cb76833c990ac9cd3923c9a8905a8929789ce347c84564d03d" +checksum = "a941429fea7e08bedec25e4f6785b6ffaacc6b755da98df5ef3e7dcf4a124c4f" dependencies = [ "once_cell", "pest", @@ -3124,9 +3148,9 @@ dependencies = [ [[package]] name = "pin-project-lite" -version = "0.2.15" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" [[package]] name = "pin-utils" @@ -3146,15 +3170,15 @@ dependencies = [ [[package]] name = "pkg-config" -version = "0.3.31" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" [[package]] name = "plotters" -version = "0.3.7" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aeb6f403d7a4911efb1e33402027fc44f29b5bf6def3effcc22d7bb75f2b747" +checksum = "a15b6eccb8484002195a3e44fe65a4ce8e93a625797a063735536fd59cb01cf3" dependencies = [ "num-traits", "plotters-backend", @@ -3165,15 +3189,15 @@ dependencies = [ [[package]] name = "plotters-backend" -version = "0.3.7" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df42e13c12958a16b3f7f4386b9ab1f3e7933914ecea48da7139435263a4172a" +checksum = "414cec62c6634ae900ea1c56128dfe87cf63e7caece0852ec76aba307cebadb7" [[package]] name = "plotters-svg" -version = "0.3.7" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51bae2ac328883f7acdfea3d66a7c35751187f870bc81f94563733a154d7a670" +checksum = "81b30686a7d9c3e010b84284bdd26a29f2138574f52f5eb6f794fc0ad924e705" dependencies = [ "plotters-backend", ] @@ -3191,9 +3215,9 @@ dependencies = [ [[package]] name = "portable-atomic" -version = "1.9.0" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc9c68a3f6da06753e9335d63e27f6b9754dd1920d941135b7ea8224f141adb2" +checksum = "da544ee218f0d287a911e9c99a39a8c9bc8fcad3cb8db5959940044ecfc67265" [[package]] name = "powerfmt" @@ -3212,9 +3236,9 @@ dependencies = [ [[package]] name = "prettyplease" -version = "0.2.25" +version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64d1ec885c64d0457d564db4ec299b2dae3f9c02808b8ad9c3a089c591b18033" +checksum = "5f12335488a2f3b0a83b14edad48dca9879ce89b2edd10e80237e4e852dd645e" dependencies = [ "proc-macro2", "syn 2.0.85", @@ -3242,11 +3266,11 @@ dependencies = [ [[package]] name = "proc-macro-crate" -version = "3.2.0" +version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b" +checksum = "6d37c51ca738a55da99dc0c4a34860fd675453b8b36209178c2249bb13651284" dependencies = [ - "toml_edit", + "toml_edit 0.21.1", ] [[package]] @@ -3275,9 +3299,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.89" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" dependencies = [ "unicode-ident", ] @@ -3313,6 +3337,16 @@ dependencies = [ "syn 2.0.85", ] +[[package]] +name = "prost" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "deb1435c188b76130da55f17a466d252ff7b1418b2ad3e037d127b94e3411f29" +dependencies = [ + "bytes", + "prost-derive 0.12.6", +] + [[package]] name = "prost" version = "0.13.3" @@ -3320,14 +3354,35 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b0487d90e047de87f984913713b85c601c05609aad5b0df4b4573fbf69aa13f" dependencies = [ "bytes", - "prost-derive", + "prost-derive 0.13.3", ] [[package]] name = "prost-build" -version = "0.13.3" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22505a5c94da8e3b7c2996394d1c933236c4d743e81a410bcca4e6989fc066a4" +dependencies = [ + "bytes", + "heck 0.5.0", + "itertools 0.12.1", + "log", + "multimap", + "once_cell", + "petgraph", + "prettyplease", + "prost 0.12.6", + "prost-types 0.12.6", + "regex", + "syn 2.0.85", + "tempfile", +] + +[[package]] +name = "prost-build" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c1318b19085f08681016926435853bbf7858f9c082d0999b80550ff5d9abe15" +checksum = "5bb182580f71dd070f88d01ce3de9f4da5021db7115d2e1c3605a754153b77c1" dependencies = [ "bytes", "heck 0.5.0", @@ -3337,13 +3392,26 @@ dependencies = [ "once_cell", "petgraph", "prettyplease", - "prost", - "prost-types", + "prost 0.13.3", + "prost-types 0.13.1", "regex", "syn 2.0.85", "tempfile", ] +[[package]] +name = "prost-derive" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81bddcdb20abf9501610992b6759a4c888aef7d1a7247ef75e2404275ac24af1" +dependencies = [ + "anyhow", + "itertools 0.12.1", + "proc-macro2", + "quote", + "syn 2.0.85", +] + [[package]] name = "prost-derive" version = "0.13.3" @@ -3359,11 +3427,20 @@ dependencies = [ [[package]] name = "prost-types" -version = "0.13.3" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9091c90b0a32608e984ff2fa4091273cbdd755d54935c51d520887f4a1dbd5b0" +dependencies = [ + "prost 0.12.6", +] + +[[package]] +name = "prost-types" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4759aa0d3a6232fb8dbdb97b61de2c20047c68aca932c7ed76da9d788508d670" +checksum = "cee5168b05f49d4b0ca581206eb14a7b22fafd963efe729ac48eb03266e25cc2" dependencies = [ - "prost", + "prost 0.13.3", ] [[package]] @@ -3420,58 +3497,10 @@ dependencies = [ ] [[package]] -name = "quinn" -version = "0.11.5" +name = "quote" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c7c5fdde3cdae7203427dc4f0a68fe0ed09833edc525a03456b153b79828684" -dependencies = [ - "bytes", - "pin-project-lite", - "quinn-proto", - "quinn-udp", - "rustc-hash 2.0.0", - "rustls 0.23.15", - "socket2", - "thiserror", - "tokio", - "tracing", -] - -[[package]] -name = "quinn-proto" -version = "0.11.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fadfaed2cd7f389d0161bb73eeb07b7b78f8691047a6f3e73caaeae55310a4a6" -dependencies = [ - "bytes", - "rand 0.8.5", - "ring", - "rustc-hash 2.0.0", - "rustls 0.23.15", - "slab", - "thiserror", - "tinyvec", - "tracing", -] - -[[package]] -name = "quinn-udp" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fe68c2e9e1a1234e218683dbdf9f9dfcb094113c5ac2b938dfcb9bab4c4140b" -dependencies = [ - "libc", - "once_cell", - "socket2", - "tracing", - "windows-sys 0.59.0", -] - -[[package]] -name = "quote" -version = "1.0.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" dependencies = [ "proc-macro2", ] @@ -3564,9 +3593,9 @@ dependencies = [ [[package]] name = "raw-cpuid" -version = "11.2.0" +version = "11.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ab240315c661615f2ee9f0f2cd32d5a7343a84d5ebcccb99d46e6637565e7b0" +checksum = "cb9ee317cfe3fbd54b36a511efc1edd42e216903c9cd575e686dd68a2ba90d8d" dependencies = [ "bitflags 2.6.0", ] @@ -3633,18 +3662,18 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.7" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" +checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4" dependencies = [ "bitflags 2.6.0", ] [[package]] name = "redox_users" -version = "0.4.6" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" +checksum = "bd283d9651eeda4b2a83a43c1c91b266c40fd76ecd39a50a8c630ae69dc72891" dependencies = [ "getrandom 0.2.15", "libredox", @@ -3653,9 +3682,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.11.1" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +checksum = "38200e5ee88914975b69f657f0801b6f6dccafd44fd9326302a4aaeecfacb1d8" dependencies = [ "aho-corasick", "memchr", @@ -3710,8 +3739,8 @@ dependencies = [ "h2 0.3.26", "http 0.2.12", "http-body 0.4.6", - "hyper 0.14.31", - "hyper-rustls 0.24.2", + "hyper 0.14.30", + "hyper-rustls", "ipnet", "js-sys", "log", @@ -3719,77 +3748,59 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite", - "rustls 0.21.12", - "rustls-pemfile 1.0.4", + "rustls", + "rustls-pemfile", "serde", "serde_json", "serde_urlencoded", "sync_wrapper 0.1.2", "system-configuration", "tokio", - "tokio-rustls 0.24.1", + "tokio-rustls", "tokio-util 0.7.12", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "webpki-roots 0.25.4", + "webpki-roots", "winreg", ] [[package]] -name = "reqwest" -version = "0.12.8" +name = "rfc6979" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f713147fbe92361e52392c73b8c9e48c04c6625bce969ef54dc901e58e042a7b" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" dependencies = [ - "base64 0.22.1", - "bytes", - "futures-channel", - "futures-core", - "futures-util", - "http 1.1.0", - "http-body 1.0.1", - "http-body-util", - "hyper 1.5.0", - "hyper-rustls 0.27.3", - "hyper-util", - "ipnet", - "js-sys", - "log", - "mime", - "once_cell", - "percent-encoding", - "pin-project-lite", - "quinn", - "rustls 0.23.15", - "rustls-pemfile 2.2.0", - "rustls-pki-types", - "serde", - "serde_json", - "serde_urlencoded", - "sync_wrapper 1.0.1", - "tokio", - "tokio-rustls 0.26.0", - "tower-service", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", - "webpki-roots 0.26.6", - "windows-registry", + "hmac", + "subtle", ] [[package]] name = "rgb" -version = "0.8.50" +version = "0.8.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57397d16646700483b67d2dd6511d79318f9d057fdbd21a4066aeac8b41d310a" +checksum = "e12bc8d2f72df26a5d3178022df33720fbede0d31d82c7291662eff89836994d" dependencies = [ "bytemuck", ] +[[package]] +name = "ring" +version = "0.16.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" +dependencies = [ + "cc", + "libc", + "once_cell", + "spin 0.5.2", + "untrusted 0.7.1", + "web-sys", + "winapi", +] + [[package]] name = "ring" version = "0.17.8" @@ -3800,8 +3811,8 @@ dependencies = [ "cfg-if 1.0.0", "getrandom 0.2.15", "libc", - "spin", - "untrusted", + "spin 0.9.8", + "untrusted 0.9.0", "windows-sys 0.52.0", ] @@ -3856,12 +3867,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" -[[package]] -name = "rustc-hash" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "583034fd73374156e66797ed8e5b0d5690409c9226b22d87cb7f19821c05d152" - [[package]] name = "rustc-hex" version = "2.1.0" @@ -3879,9 +3884,9 @@ dependencies = [ [[package]] name = "rustc_version" -version = "0.4.1" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" dependencies = [ "semver 1.0.23", ] @@ -3906,26 +3911,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" dependencies = [ "log", - "ring", - "rustls-webpki 0.101.7", + "ring 0.17.8", + "rustls-webpki", "sct", ] -[[package]] -name = "rustls" -version = "0.23.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fbb44d7acc4e873d613422379f69f237a1b141928c02f6bc6ccfddddc2d7993" -dependencies = [ - "log", - "once_cell", - "ring", - "rustls-pki-types", - "rustls-webpki 0.102.8", - "subtle", - "zeroize", -] - [[package]] name = "rustls-pemfile" version = "1.0.4" @@ -3935,47 +3925,21 @@ dependencies = [ "base64 0.21.7", ] -[[package]] -name = "rustls-pemfile" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" -dependencies = [ - "rustls-pki-types", -] - -[[package]] -name = "rustls-pki-types" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16f1201b3c9a7ee8039bcadc17b7e605e2945b27eee7631788c1bd2b0643674b" - [[package]] name = "rustls-webpki" version = "0.101.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" dependencies = [ - "ring", - "untrusted", -] - -[[package]] -name = "rustls-webpki" -version = "0.102.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" -dependencies = [ - "ring", - "rustls-pki-types", - "untrusted", + "ring 0.17.8", + "untrusted 0.9.0", ] [[package]] name = "rustversion" -version = "1.0.18" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248" +checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" [[package]] name = "rusty-fork" @@ -4006,8 +3970,8 @@ dependencies = [ [[package]] name = "sapling-crypto" -version = "0.2.0" -source = "git+https://github.com/zcash/sapling-crypto?rev=b1ad3694ee13a2fc5d291ad04721a6252da0993c#b1ad3694ee13a2fc5d291ad04721a6252da0993c" +version = "0.1.3" +source = "git+https://github.com/QED-it/sapling-crypto?branch=zsa1#99ad0a5f0bdef332bdc91d577086abd3aca59553" dependencies = [ "aes", "bellman", @@ -4021,7 +3985,7 @@ dependencies = [ "fpe", "group", "hex", - "incrementalmerkletree 0.7.0", + "incrementalmerkletree", "jubjub", "lazy_static", "memuse", @@ -4047,8 +4011,31 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" dependencies = [ - "ring", - "untrusted", + "ring 0.17.8", + "untrusted 0.9.0", +] + +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct", + "der", + "generic-array", + "pkcs8", + "subtle", + "zeroize", +] + +[[package]] +name = "secp256k1" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4124a35fe33ae14259c490fd70fa199a32b9ce9502f2ee6bc4f81ec06fa65894" +dependencies = [ + "secp256k1-sys", ] [[package]] @@ -4106,20 +4093,20 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "sentry" -version = "0.32.3" +version = "0.32.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00421ed8fa0c995f07cde48ba6c89e80f2b312f74ff637326f392fbfd23abe02" +checksum = "766448f12e44d68e675d5789a261515c46ac6ccd240abdd451a9c46c84a49523" dependencies = [ "httpdate", - "reqwest 0.12.8", - "rustls 0.21.12", + "reqwest", + "rustls", "sentry-backtrace", "sentry-contexts", "sentry-core", "sentry-tracing", "tokio", "ureq", - "webpki-roots 0.25.4", + "webpki-roots", ] [[package]] @@ -4143,7 +4130,7 @@ dependencies = [ "hostname", "libc", "os_info", - "rustc_version 0.4.1", + "rustc_version 0.4.0", "sentry-core", "uname", ] @@ -4192,9 +4179,9 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.213" +version = "1.0.214" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ea7893ff5e2466df8d720bb615088341b295f849602c6956047f8f80f0e9bc1" +checksum = "f55c3193aca71c12ad7890f1785d2b73e1b9f63a0bbc353c08ef26fe03fc56b5" dependencies = [ "serde_derive", ] @@ -4210,9 +4197,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.213" +version = "1.0.214" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e85ad2009c50b58e87caa8cd6dac16bdf511bbfb7af6c33df902396aa480fa5" +checksum = "de523f781f095e28fa605cdce0f8307e451cc0fd14e2eb4cd2e98a355b147766" dependencies = [ "proc-macro2", "quote", @@ -4234,9 +4221,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "0.6.8" +version = "0.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" +checksum = "eb5b1b31579f3811bf615c144393417496f152e12ac8b7663bf664f4a815306d" dependencies = [ "serde", ] @@ -4342,12 +4329,13 @@ dependencies = [ [[package]] name = "shardtree" -version = "0.4.0" -source = "git+https://github.com/zcash/incrementalmerkletree?rev=ffe4234788fd22662b937ba7c6ea01535fcc1293#ffe4234788fd22662b937ba7c6ea01535fcc1293" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cdd24424ce0b381646737fedddc33c4dcf7dcd2d545056b53f7982097bef5" dependencies = [ "bitflags 2.6.0", "either", - "incrementalmerkletree 0.7.0", + "incrementalmerkletree", "tracing", ] @@ -4372,6 +4360,7 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" dependencies = [ + "digest", "rand_core 0.6.4", ] @@ -4434,6 +4423,12 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + [[package]] name = "spin" version = "0.9.8" @@ -4509,9 +4504,9 @@ dependencies = [ [[package]] name = "subtle" -version = "2.6.1" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" +checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" [[package]] name = "syn" @@ -4546,9 +4541,6 @@ name = "sync_wrapper" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" -dependencies = [ - "futures-core", -] [[package]] name = "synstructure" @@ -4622,18 +4614,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.65" +version = "1.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d11abd9594d9b38965ef50805c5e469ca9cc6f197f883f717e0269a3057b3d5" +checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.65" +version = "1.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae71770322cbd277e69d762a16c444af02aa0575ac0d174f0b9562d3b37f8602" +checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" dependencies = [ "proc-macro2", "quote", @@ -4758,18 +4750,7 @@ version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" dependencies = [ - "rustls 0.21.12", - "tokio", -] - -[[package]] -name = "tokio-rustls" -version = "0.26.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" -dependencies = [ - "rustls 0.23.15", - "rustls-pki-types", + "rustls", "tokio", ] @@ -4843,7 +4824,7 @@ dependencies = [ "serde", "serde_spanned", "toml_datetime", - "toml_edit", + "toml_edit 0.22.20", ] [[package]] @@ -4857,15 +4838,26 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.22.22" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" +dependencies = [ + "indexmap 2.6.0", + "toml_datetime", + "winnow 0.5.40", +] + +[[package]] +name = "toml_edit" +version = "0.22.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" +checksum = "583c44c02ad26b0c3f3066fe629275e50627026c51ac2e595cca4c230ce1ce1d" dependencies = [ "indexmap 2.6.0", "serde", "serde_spanned", "toml_datetime", - "winnow", + "winnow 0.6.18", ] [[package]] @@ -4879,7 +4871,7 @@ dependencies = [ "axum", "base64 0.22.1", "bytes", - "h2 0.4.6", + "h2 0.4.5", "http 1.1.0", "http-body 1.0.1", "http-body-util", @@ -4888,16 +4880,29 @@ dependencies = [ "hyper-util", "percent-encoding", "pin-project", - "prost", + "prost 0.13.3", "socket2", "tokio", "tokio-stream", - "tower 0.4.13", + "tower", "tower-layer", "tower-service", "tracing", ] +[[package]] +name = "tonic-build" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d021fc044c18582b9a2408cd0dd05b1596e3ecdb5c4df822bb0183545683889" +dependencies = [ + "prettyplease", + "proc-macro2", + "prost-build 0.12.6", + "quote", + "syn 2.0.85", +] + [[package]] name = "tonic-build" version = "0.12.3" @@ -4906,8 +4911,8 @@ checksum = "9557ce109ea773b399c9b9e5dca39294110b74f1f342cb347a80d1fce8c26a11" dependencies = [ "prettyplease", "proc-macro2", - "prost-build", - "prost-types", + "prost-build 0.13.1", + "prost-types 0.13.1", "quote", "syn 2.0.85", ] @@ -4918,8 +4923,8 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "878d81f52e7fcfd80026b7fdb6a9b578b3c3653ba987f87f0dce4b64043cba27" dependencies = [ - "prost", - "prost-types", + "prost 0.13.3", + "prost-types 0.13.1", "tokio", "tokio-stream", "tonic", @@ -4946,20 +4951,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "tower" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2873938d487c3cfb9aed7546dc9f2711d867c9f90c46b889989a2cb84eba6b4f" -dependencies = [ - "futures-core", - "futures-util", - "pin-project-lite", - "sync_wrapper 0.1.2", - "tower-layer", - "tower-service", -] - [[package]] name = "tower-batch-control" version = "0.2.41-beta.17" @@ -4975,7 +4966,7 @@ dependencies = [ "tokio", "tokio-test", "tokio-util 0.7.12", - "tower 0.4.13", + "tower", "tower-fallback", "tower-test", "tracing", @@ -4990,22 +4981,22 @@ dependencies = [ "futures-core", "pin-project", "tokio", - "tower 0.4.13", + "tower", "tracing", "zebra-test", ] [[package]] name = "tower-layer" -version = "0.3.3" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" +checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" [[package]] name = "tower-service" -version = "0.3.3" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" [[package]] name = "tower-test" @@ -5183,9 +5174,9 @@ checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "ucd-trie" -version = "0.1.7" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" +checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" [[package]] name = "uint" @@ -5228,48 +5219,51 @@ checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" [[package]] name = "unicase" -version = "2.8.0" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e51b68083f157f853b6379db119d1c1be0e6e4dec98101079dec41f6f5cf6df" +checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" +dependencies = [ + "version_check", +] [[package]] name = "unicode-bidi" -version = "0.3.17" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ab17db44d7388991a428b2ee655ce0c212e862eff1768a455c58f9aad6e7893" +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" [[package]] name = "unicode-ident" -version = "1.0.13" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-normalization" -version = "0.1.24" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" +checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" dependencies = [ "tinyvec", ] [[package]] name = "unicode-segmentation" -version = "1.12.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" +checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" [[package]] name = "unicode-width" -version = "0.1.14" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" +checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" [[package]] name = "unicode-xid" -version = "0.2.6" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" +checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" [[package]] name = "universal-hash" @@ -5281,6 +5275,12 @@ dependencies = [ "subtle", ] +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + [[package]] name = "untrusted" version = "0.9.0" @@ -5289,17 +5289,17 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "ureq" -version = "2.10.1" +version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b74fc6b57825be3373f7054754755f03ac3a8f5d70015ccad699ba2029956f4a" +checksum = "f8cdd25c339e200129fe4de81451814e5228c9b771d57378817d6117cc2b3f97" dependencies = [ - "base64 0.22.1", + "base64 0.21.7", "log", "once_cell", - "rustls 0.23.15", - "rustls-pki-types", + "rustls", + "rustls-webpki", "url", - "webpki-roots 0.26.6", + "webpki-roots", ] [[package]] @@ -5322,9 +5322,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.11.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a" +checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314" dependencies = [ "serde", ] @@ -5358,7 +5358,7 @@ dependencies = [ "cfg-if 1.0.0", "git2", "regex", - "rustc_version 0.4.1", + "rustc_version 0.4.0", "rustversion", "time", ] @@ -5369,17 +5369,6 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" -[[package]] -name = "visibility" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d674d135b4a8c1d7e813e2f8d1c9a58308aee4a680323066025e53132218bd91" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.85", -] - [[package]] name = "void" version = "1.0.2" @@ -5478,20 +5467,19 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.95" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" dependencies = [ "cfg-if 1.0.0", - "once_cell", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.95" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358" +checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" dependencies = [ "bumpalo", "log", @@ -5504,9 +5492,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.45" +version = "0.4.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc7ec4f8827a71586374db3e87abdb5a2bb3a15afed140221307c3ec06b1f63b" +checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" dependencies = [ "cfg-if 1.0.0", "js-sys", @@ -5516,9 +5504,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.95" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -5526,9 +5514,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.95" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" +checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", @@ -5539,15 +5527,15 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.95" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d" +checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" [[package]] name = "web-sys" -version = "0.3.72" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6488b90108c040df0fe62fa815cbdee25124641df01814dd7282749234c6112" +checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" dependencies = [ "js-sys", "wasm-bindgen", @@ -5559,15 +5547,6 @@ version = "0.25.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" -[[package]] -name = "webpki-roots" -version = "0.26.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "841c67bff177718f1d4dfefde8d8f0e78f9b6589319ba88312f567fc5841a958" -dependencies = [ - "rustls-pki-types", -] - [[package]] name = "which" version = "4.4.2" @@ -5630,36 +5609,6 @@ dependencies = [ "windows-targets 0.52.6", ] -[[package]] -name = "windows-registry" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0" -dependencies = [ - "windows-result", - "windows-strings", - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-result" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" -dependencies = [ - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-strings" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" -dependencies = [ - "windows-result", - "windows-targets 0.52.6", -] - [[package]] name = "windows-sys" version = "0.48.0" @@ -5810,9 +5759,18 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.6.20" +version = "0.5.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +dependencies = [ + "memchr", +] + +[[package]] +name = "winnow" +version = "0.6.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" +checksum = "68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f" dependencies = [ "memchr", ] @@ -5856,35 +5814,22 @@ checksum = "213b7324336b53d2414b2db8537e56544d981803139155afa84f76eeebb7a546" [[package]] name = "zcash_address" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6d26f21381dc220836dd8d2a9a10dbe85928a26232b011bc6a42b611789b743" -dependencies = [ - "bech32", - "bs58", - "f4jumble 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "zcash_encoding", - "zcash_protocol 0.2.0", -] - -[[package]] -name = "zcash_address" -version = "0.5.0" -source = "git+https://github.com/zcash/librustzcash.git?rev=1410f1449100a417bfbc4f6c7167aa9808e38792#1410f1449100a417bfbc4f6c7167aa9808e38792" +version = "0.3.2" +source = "git+https://github.com/QED-it/librustzcash?rev=e88964e248c9038e757771b8225c47ac8772abc4#e88964e248c9038e757771b8225c47ac8772abc4" dependencies = [ "bech32", "bs58", - "f4jumble 0.1.0 (git+https://github.com/zcash/librustzcash.git?rev=1410f1449100a417bfbc4f6c7167aa9808e38792)", + "f4jumble", "zcash_encoding", - "zcash_protocol 0.3.0", + "zcash_protocol", ] [[package]] name = "zcash_client_backend" -version = "0.13.0" -source = "git+https://github.com/zcash/librustzcash.git?rev=1410f1449100a417bfbc4f6c7167aa9808e38792#1410f1449100a417bfbc4f6c7167aa9808e38792" +version = "0.12.1" +source = "git+https://github.com/QED-it/librustzcash?rev=e88964e248c9038e757771b8225c47ac8772abc4#e88964e248c9038e757771b8225c47ac8772abc4" dependencies = [ - "base64 0.22.1", + "base64 0.21.7", "bech32", "bls12_381", "bs58", @@ -5892,12 +5837,12 @@ dependencies = [ "document-features", "group", "hex", - "incrementalmerkletree 0.7.0", + "incrementalmerkletree", "memuse", "nom", "nonempty", "percent-encoding", - "prost", + "prost 0.12.6", "rand_core 0.6.4", "rayon", "sapling-crypto", @@ -5905,23 +5850,23 @@ dependencies = [ "shardtree", "subtle", "time", - "tonic-build", + "tonic-build 0.10.2", "tracing", "which", - "zcash_address 0.5.0", + "zcash_address", "zcash_encoding", - "zcash_keys 0.3.0 (git+https://github.com/zcash/librustzcash.git?rev=1410f1449100a417bfbc4f6c7167aa9808e38792)", + "zcash_keys", "zcash_note_encryption", - "zcash_primitives 0.17.0", - "zcash_protocol 0.3.0", + "zcash_primitives", + "zcash_protocol", "zip32", "zip321", ] [[package]] name = "zcash_encoding" -version = "0.2.1" -source = "git+https://github.com/zcash/librustzcash.git?rev=1410f1449100a417bfbc4f6c7167aa9808e38792#1410f1449100a417bfbc4f6c7167aa9808e38792" +version = "0.2.0" +source = "git+https://github.com/QED-it/librustzcash?rev=e88964e248c9038e757771b8225c47ac8772abc4#e88964e248c9038e757771b8225c47ac8772abc4" dependencies = [ "byteorder", "nonempty", @@ -5930,7 +5875,7 @@ dependencies = [ [[package]] name = "zcash_history" version = "0.4.0" -source = "git+https://github.com/zcash/librustzcash.git?rev=1410f1449100a417bfbc4f6c7167aa9808e38792#1410f1449100a417bfbc4f6c7167aa9808e38792" +source = "git+https://github.com/QED-it/librustzcash?rev=e88964e248c9038e757771b8225c47ac8772abc4#e88964e248c9038e757771b8225c47ac8772abc4" dependencies = [ "blake2b_simd", "byteorder", @@ -5939,34 +5884,8 @@ dependencies = [ [[package]] name = "zcash_keys" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712faf4070107ab0b2828d0eda6aeaf4c3cb02564109832d95b97ad3467c95a5" -dependencies = [ - "bech32", - "blake2b_simd", - "bls12_381", - "bs58", - "document-features", - "group", - "memuse", - "nonempty", - "rand_core 0.6.4", - "sapling-crypto", - "secrecy", - "subtle", - "tracing", - "zcash_address 0.4.0", - "zcash_encoding", - "zcash_primitives 0.16.0", - "zcash_protocol 0.2.0", - "zip32", -] - -[[package]] -name = "zcash_keys" -version = "0.3.0" -source = "git+https://github.com/zcash/librustzcash.git?rev=1410f1449100a417bfbc4f6c7167aa9808e38792#1410f1449100a417bfbc4f6c7167aa9808e38792" +version = "0.2.0" +source = "git+https://github.com/QED-it/librustzcash?rev=e88964e248c9038e757771b8225c47ac8772abc4#e88964e248c9038e757771b8225c47ac8772abc4" dependencies = [ "bech32", "blake2b_simd", @@ -5981,18 +5900,17 @@ dependencies = [ "secrecy", "subtle", "tracing", - "zcash_address 0.5.0", + "zcash_address", "zcash_encoding", - "zcash_primitives 0.17.0", - "zcash_protocol 0.3.0", + "zcash_primitives", + "zcash_protocol", "zip32", ] [[package]] name = "zcash_note_encryption" version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b4580cd6cee12e44421dac43169be8d23791650816bdb34e6ddfa70ac89c1c5" +source = "git+https://github.com/QED-it/zcash_note_encryption?branch=zsa1#76745f00551d4442dee11ad64a8400b75132d18f" dependencies = [ "chacha20", "chacha20poly1305", @@ -6003,57 +5921,21 @@ dependencies = [ [[package]] name = "zcash_primitives" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f044bc9cf2887ec408196fbafb44749e5581f57cc18d8da7aabaeb60cc40c64" -dependencies = [ - "aes", - "blake2b_simd", - "bs58", - "byteorder", - "document-features", - "equihash 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "ff", - "fpe", - "group", - "hex", - "incrementalmerkletree 0.6.0", - "jubjub", - "memuse", - "nonempty", - "orchard", - "rand 0.8.5", - "rand_core 0.6.4", - "redjubjub", - "sapling-crypto", - "sha2", - "subtle", - "tracing", - "zcash_address 0.4.0", - "zcash_encoding", - "zcash_note_encryption", - "zcash_protocol 0.2.0", - "zcash_spec", - "zip32", -] - -[[package]] -name = "zcash_primitives" -version = "0.17.0" -source = "git+https://github.com/zcash/librustzcash.git?rev=1410f1449100a417bfbc4f6c7167aa9808e38792#1410f1449100a417bfbc4f6c7167aa9808e38792" +version = "0.15.0" +source = "git+https://github.com/QED-it/librustzcash?rev=e88964e248c9038e757771b8225c47ac8772abc4#e88964e248c9038e757771b8225c47ac8772abc4" dependencies = [ "aes", - "bip32", + "bip0039", "blake2b_simd", - "bs58", "byteorder", "document-features", - "equihash 0.2.0 (git+https://github.com/zcash/librustzcash.git?rev=1410f1449100a417bfbc4f6c7167aa9808e38792)", + "equihash 0.2.0 (git+https://github.com/QED-it/librustzcash?rev=e88964e248c9038e757771b8225c47ac8772abc4)", "ff", "fpe", "group", + "hdwallet", "hex", - "incrementalmerkletree 0.7.0", + "incrementalmerkletree", "jubjub", "memuse", "nonempty", @@ -6063,22 +5945,23 @@ dependencies = [ "redjubjub", "ripemd", "sapling-crypto", - "secp256k1", + "secp256k1 0.26.0", "sha2", "subtle", "tracing", - "zcash_address 0.5.0", + "zcash_address", "zcash_encoding", "zcash_note_encryption", - "zcash_protocol 0.3.0", + "zcash_protocol", "zcash_spec", "zip32", ] [[package]] name = "zcash_proofs" -version = "0.17.0" -source = "git+https://github.com/zcash/librustzcash.git?rev=1410f1449100a417bfbc4f6c7167aa9808e38792#1410f1449100a417bfbc4f6c7167aa9808e38792" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5163a1110f4265cc5f2fdf87ac4497fd1e014b6ce0760ca8d16d8e3853a5c0f7" dependencies = [ "bellman", "blake2b_simd", @@ -6094,23 +5977,13 @@ dependencies = [ "sapling-crypto", "tracing", "xdg", - "zcash_primitives 0.17.0", -] - -[[package]] -name = "zcash_protocol" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f35eac659fdbba614333d119217c5963c0d7cea43aee33176c4f2f95e5460d8d" -dependencies = [ - "document-features", - "memuse", + "zcash_primitives", ] [[package]] name = "zcash_protocol" -version = "0.3.0" -source = "git+https://github.com/zcash/librustzcash.git?rev=1410f1449100a417bfbc4f6c7167aa9808e38792#1410f1449100a417bfbc4f6c7167aa9808e38792" +version = "0.1.1" +source = "git+https://github.com/QED-it/librustzcash?rev=e88964e248c9038e757771b8225c47ac8772abc4#e88964e248c9038e757771b8225c47ac8772abc4" dependencies = [ "document-features", "memuse", @@ -6122,15 +5995,15 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2122a042c77d529d3c60b899e74705eda39ae96a8a992460caeb06afa76990a2" dependencies = [ - "bindgen 0.70.1", + "bindgen", "cc", ] [[package]] name = "zcash_spec" -version = "0.1.2" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cede95491c2191d3e278cab76e097a44b17fde8d6ca0d4e3a22cf4807b2d857" +checksum = "b7a3bf58b673cb3dacd8ae09ba345998923a197ab0da70d6239d8e8838949e9b" dependencies = [ "blake2b_simd", ] @@ -6158,10 +6031,11 @@ dependencies = [ "halo2_proofs", "hex", "humantime", - "incrementalmerkletree 0.7.0", + "incrementalmerkletree", "itertools 0.13.0", "jubjub", "lazy_static", + "nonempty", "num-integer", "orchard", "primitive-types", @@ -6175,7 +6049,7 @@ dependencies = [ "redjubjub", "ripemd", "sapling-crypto", - "secp256k1", + "secp256k1 0.27.0", "serde", "serde-big-array", "serde_json", @@ -6190,13 +6064,13 @@ dependencies = [ "tracing", "uint 0.10.0", "x25519-dalek", - "zcash_address 0.5.0", + "zcash_address", "zcash_client_backend", "zcash_encoding", "zcash_history", "zcash_note_encryption", - "zcash_primitives 0.17.0", - "zcash_protocol 0.3.0", + "zcash_primitives", + "zcash_protocol", "zebra-test", ] @@ -6230,7 +6104,7 @@ dependencies = [ "thiserror", "tinyvec", "tokio", - "tower 0.4.13", + "tower", "tower-batch-control", "tower-fallback", "tracing", @@ -6253,15 +6127,15 @@ dependencies = [ "color-eyre", "futures-util", "insta", - "prost", + "prost 0.13.3", "serde", "tokio", "tokio-stream", "tonic", - "tonic-build", + "tonic-build 0.12.3", "tonic-reflection", - "tower 0.4.13", - "zcash_primitives 0.17.0", + "tower", + "zcash_primitives", "zebra-chain", "zebra-node-services", "zebra-state", @@ -6301,7 +6175,7 @@ dependencies = [ "tokio-stream", "tokio-util 0.7.12", "toml 0.8.19", - "tower 0.4.13", + "tower", "tracing", "tracing-error", "tracing-futures", @@ -6315,7 +6189,7 @@ version = "1.0.0-beta.41" dependencies = [ "color-eyre", "jsonrpc-core", - "reqwest 0.11.27", + "reqwest", "serde", "serde_json", "tokio", @@ -6338,7 +6212,7 @@ dependencies = [ "jsonrpc-http-server", "nix", "proptest", - "prost", + "prost 0.13.3", "rand 0.8.5", "serde", "serde_json", @@ -6346,12 +6220,12 @@ dependencies = [ "tokio", "tokio-stream", "tonic", - "tonic-build", + "tonic-build 0.12.3", "tonic-reflection", - "tower 0.4.13", + "tower", "tracing", - "zcash_address 0.5.0", - "zcash_primitives 0.17.0", + "zcash_address", + "zcash_primitives", "zebra-chain", "zebra-consensus", "zebra-network", @@ -6390,14 +6264,14 @@ dependencies = [ "tokio", "toml 0.8.19", "tonic", - "tower 0.4.13", + "tower", "tracing", "tracing-subscriber", - "zcash_address 0.5.0", + "zcash_address", "zcash_client_backend", - "zcash_keys 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "zcash_keys", "zcash_note_encryption", - "zcash_primitives 0.17.0", + "zcash_primitives", "zebra-chain", "zebra-grpc", "zebra-node-services", @@ -6458,7 +6332,7 @@ dependencies = [ "thiserror", "tinyvec", "tokio", - "tower 0.4.13", + "tower", "tracing", "zebra-chain", "zebra-test", @@ -6486,7 +6360,7 @@ dependencies = [ "thiserror", "tinyvec", "tokio", - "tower 0.4.13", + "tower", "tracing", "tracing-error", "tracing-subscriber", @@ -6504,7 +6378,7 @@ dependencies = [ "quote", "rand 0.8.5", "regex", - "reqwest 0.11.27", + "reqwest", "serde", "serde_json", "serde_yml", @@ -6516,8 +6390,8 @@ dependencies = [ "tracing-error", "tracing-subscriber", "zcash_client_backend", - "zcash_primitives 0.17.0", - "zcash_protocol 0.3.0", + "zcash_primitives", + "zcash_protocol", "zebra-chain", "zebra-node-services", "zebra-rpc", @@ -6557,7 +6431,7 @@ dependencies = [ "pin-project", "proptest", "proptest-derive", - "prost", + "prost 0.13.3", "rand 0.8.5", "rayon", "regex", @@ -6573,8 +6447,8 @@ dependencies = [ "tokio-stream", "toml 0.8.19", "tonic", - "tonic-build", - "tower 0.4.13", + "tonic-build 0.12.3", + "tower", "tracing", "tracing-appender", "tracing-error", @@ -6638,24 +6512,23 @@ dependencies = [ [[package]] name = "zip32" -version = "0.1.2" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92022ac1e47c7b78f9cee29efac8a1a546e189506f3bb5ad46d525be7c519bf6" +checksum = "4226d0aee9c9407c27064dfeec9d7b281c917de3374e1e5a2e2cfad9e09de19e" dependencies = [ "blake2b_simd", "memuse", "subtle", - "zcash_spec", ] [[package]] name = "zip321" -version = "0.1.0" -source = "git+https://github.com/zcash/librustzcash.git?rev=1410f1449100a417bfbc4f6c7167aa9808e38792#1410f1449100a417bfbc4f6c7167aa9808e38792" +version = "0.0.0" +source = "git+https://github.com/QED-it/librustzcash?rev=e88964e248c9038e757771b8225c47ac8772abc4#e88964e248c9038e757771b8225c47ac8772abc4" dependencies = [ - "base64 0.22.1", + "base64 0.21.7", "nom", "percent-encoding", - "zcash_address 0.5.0", - "zcash_protocol 0.3.0", + "zcash_address", + "zcash_protocol", ] diff --git a/Cargo.toml b/Cargo.toml index 976c259130d..4af9a635ce4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,17 +22,18 @@ resolver = "2" # `cargo release` settings [workspace.dependencies] -incrementalmerkletree = "0.7.0" -orchard = "0.9.0" -sapling-crypto = "0.2.0" -zcash_address = "0.5.0" -zcash_client_backend = "0.13.0" -zcash_encoding = "0.2.1" +incrementalmerkletree = "0.5.1" +orchard = "0.8.0" +sapling-crypto = "0.1.3" +# The dependency versions below are in accordance with the currently used orchard version. zcash_history = "0.4.0" -zcash_keys = "0.3.0" -zcash_primitives = "0.17.0" -zcash_proofs = "0.17.0" -zcash_protocol = "0.3.0" +zcash_address = "0.3.2" +zcash_client_backend = "0.12.1" +zcash_encoding = "0.2.0" +zcash_keys = "0.2.0" +zcash_primitives = "0.15.0" +zcash_proofs = "0.15.0" +zcash_protocol = "0.1.1" [workspace.metadata.release] @@ -103,17 +104,16 @@ panic = "abort" # - see https://doc.rust-lang.org/rustc/linker-plugin-lto.html#cc-code-as-a-dependency-in-rust lto = "thin" -# We can remove this patches after we get out of 2.0 release candidate and upgrade the ECC dependencies above. -# This revisions are at the commit just before setting mainnet activation heights. [patch.crates-io] -zcash_address = { git = "https://github.com/zcash/librustzcash.git", rev = "1410f1449100a417bfbc4f6c7167aa9808e38792" } -zcash_client_backend = { git = "https://github.com/zcash/librustzcash.git", rev = "1410f1449100a417bfbc4f6c7167aa9808e38792" } -zcash_encoding = { git = "https://github.com/zcash/librustzcash.git", rev = "1410f1449100a417bfbc4f6c7167aa9808e38792" } -zcash_history = { git = "https://github.com/zcash/librustzcash.git", rev = "1410f1449100a417bfbc4f6c7167aa9808e38792" } -zcash_primitives = { git = "https://github.com/zcash/librustzcash.git", rev = "1410f1449100a417bfbc4f6c7167aa9808e38792" } -zcash_proofs = { git = "https://github.com/zcash/librustzcash.git", rev = "1410f1449100a417bfbc4f6c7167aa9808e38792" } -zcash_protocol = { git = "https://github.com/zcash/librustzcash.git", rev = "1410f1449100a417bfbc4f6c7167aa9808e38792" } -sapling-crypto = { git = "https://github.com/zcash/sapling-crypto", rev = "b1ad3694ee13a2fc5d291ad04721a6252da0993c" } -orchard = { git = "https://github.com/zcash/orchard", rev = "55fb089a335bbbc1cda186c706bc037073df8eb7" } -incrementalmerkletree = { git = "https://github.com/zcash/incrementalmerkletree", rev = "ffe4234788fd22662b937ba7c6ea01535fcc1293" } -shardtree = { git = "https://github.com/zcash/incrementalmerkletree", rev = "ffe4234788fd22662b937ba7c6ea01535fcc1293" } +halo2_proofs = { version = "0.3.0", git = "https://github.com/QED-it/halo2", branch = "zsa1" } +halo2_gadgets = { version = "0.3.0", git = "https://github.com/QED-it/halo2", branch = "zsa1" } +zcash_note_encryption = { version = "0.4.0", git = "https://github.com/QED-it/zcash_note_encryption", branch = "zsa1" } +sapling-crypto = { version = "0.1.3", git = "https://github.com/QED-it/sapling-crypto", branch = "zsa1" } +orchard = { version = "0.8.0", git = "https://github.com/QED-it/orchard", rev = "831ca109705a409bc3d3b82e76245e45dd0f0812" } +zcash_primitives = { version = "0.15.0", git = "https://github.com/QED-it/librustzcash", rev = "e88964e248c9038e757771b8225c47ac8772abc4" } +zcash_protocol = { version = "0.1.1", git = "https://github.com/QED-it/librustzcash", rev = "e88964e248c9038e757771b8225c47ac8772abc4" } +zcash_address = { version = "0.3.2", git = "https://github.com/QED-it/librustzcash", rev = "e88964e248c9038e757771b8225c47ac8772abc4" } +zcash_encoding = { version = "0.2.0", git = "https://github.com/QED-it/librustzcash", rev = "e88964e248c9038e757771b8225c47ac8772abc4" } +zcash_history = { version = "0.4.0", git = "https://github.com/QED-it/librustzcash", rev = "e88964e248c9038e757771b8225c47ac8772abc4" } +zcash_client_backend = { version = "0.12.1", git = "https://github.com/QED-it/librustzcash", rev = "e88964e248c9038e757771b8225c47ac8772abc4" } +zcash_keys = { version = "0.2.0", git = "https://github.com/QED-it/librustzcash", rev = "e88964e248c9038e757771b8225c47ac8772abc4" } diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 00000000000..f4478060854 --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,4 @@ +# TODO: This file does not exist in the original Zebra repo - consider removing it before the final merge. +[toolchain] +channel = "1.82.0" +components = [ "clippy", "rustfmt" ] diff --git a/zebra-chain/Cargo.toml b/zebra-chain/Cargo.toml index 4ab99fd8857..358b556f105 100644 --- a/zebra-chain/Cargo.toml +++ b/zebra-chain/Cargo.toml @@ -56,10 +56,16 @@ proptest-impl = [ "rand_chacha", "tokio/tracing", "zebra-test", + "orchard/test-dependencies" ] bench = ["zebra-test"] +# Support for transaction version 6 +tx-v6 = [ + "nonempty" +] + [dependencies] # Cryptography @@ -68,7 +74,8 @@ bitflags = "2.5.0" bitflags-serde-legacy = "0.1.1" blake2b_simd = "1.0.2" blake2s_simd = "1.0.2" -bridgetree = "0.6.0" +# TODO: Revert to "0.6.0" (or appropriate version) when the ZSA orchard fork is updated. +bridgetree = "0.4.0" bs58 = { version = "0.5.1", features = ["check"] } byteorder = "1.5.0" @@ -104,6 +111,9 @@ sapling-crypto.workspace = true zcash_protocol.workspace = true zcash_address.workspace = true +# Used for orchard serialization +nonempty = { version = "0.7", optional = true } + # Time chrono = { version = "0.4.38", default-features = false, features = ["clock", "std", "serde"] } humantime = "2.1.0" @@ -170,6 +180,8 @@ tokio = { version = "1.41.0", features = ["full", "tracing", "test-util"] } zebra-test = { path = "../zebra-test/", version = "1.0.0-beta.41" } +orchard = { workspace = true, features = ["test-dependencies"] } + [[bench]] name = "block" harness = false @@ -178,3 +190,6 @@ required-features = ["bench"] [[bench]] name = "redpallas" harness = false + +[lints.rust] +unexpected_cfgs = { level = "warn", check-cfg = ['cfg(zcash_unstable, values("nu6"))'] } diff --git a/zebra-chain/src/block.rs b/zebra-chain/src/block.rs index 5bbc774d35c..998fd11b860 100644 --- a/zebra-chain/src/block.rs +++ b/zebra-chain/src/block.rs @@ -175,7 +175,7 @@ impl Block { } /// Access the [orchard note commitments](pallas::Base) from all transactions in this block. - pub fn orchard_note_commitments(&self) -> impl Iterator { + pub fn orchard_note_commitments(&self) -> impl Iterator + '_ { self.transactions .iter() .flat_map(|transaction| transaction.orchard_note_commitments()) diff --git a/zebra-chain/src/block/arbitrary.rs b/zebra-chain/src/block/arbitrary.rs index 5a39afa2ee4..cf8ce64c9b8 100644 --- a/zebra-chain/src/block/arbitrary.rs +++ b/zebra-chain/src/block/arbitrary.rs @@ -452,7 +452,7 @@ impl Block { sapling_tree.append(*sapling_note_commitment).unwrap(); } for orchard_note_commitment in transaction.orchard_note_commitments() { - orchard_tree.append(*orchard_note_commitment).unwrap(); + orchard_tree.append(orchard_note_commitment).unwrap(); } } new_transactions.push(Arc::new(transaction)); diff --git a/zebra-chain/src/block/commitment.rs b/zebra-chain/src/block/commitment.rs index 2cb09e75b22..ec4ef7d2616 100644 --- a/zebra-chain/src/block/commitment.rs +++ b/zebra-chain/src/block/commitment.rs @@ -125,7 +125,7 @@ impl Commitment { // NetworkUpgrade::current() returns the latest network upgrade that's activated at the provided height, so // on Regtest for heights above height 0, it could return NU6, and it's possible for the current network upgrade // to be NU6 (or Canopy, or any network upgrade above Heartwood) at the Heartwood activation height. - (Canopy | Nu5 | Nu6, activation_height) + (Canopy | Nu5 | Nu6 | Nu7, activation_height) if height == activation_height && Some(height) == Heartwood.activation_height(network) => { @@ -136,7 +136,7 @@ impl Commitment { } } (Heartwood | Canopy, _) => Ok(ChainHistoryRoot(ChainHistoryMmrRootHash(bytes))), - (Nu5 | Nu6, _) => Ok(ChainHistoryBlockTxAuthCommitment( + (Nu5 | Nu6 | Nu7, _) => Ok(ChainHistoryBlockTxAuthCommitment( ChainHistoryBlockTxAuthCommitmentHash(bytes), )), } diff --git a/zebra-chain/src/history_tree.rs b/zebra-chain/src/history_tree.rs index 91fa3a17628..613bf0616d7 100644 --- a/zebra-chain/src/history_tree.rs +++ b/zebra-chain/src/history_tree.rs @@ -102,7 +102,7 @@ impl NonEmptyHistoryTree { )?; InnerHistoryTree::PreOrchard(tree) } - NetworkUpgrade::Nu5 | NetworkUpgrade::Nu6 => { + NetworkUpgrade::Nu5 | NetworkUpgrade::Nu6 | NetworkUpgrade::Nu7 => { let tree = Tree::::new_from_cache( network, network_upgrade, @@ -156,7 +156,7 @@ impl NonEmptyHistoryTree { )?; (InnerHistoryTree::PreOrchard(tree), entry) } - NetworkUpgrade::Nu5 | NetworkUpgrade::Nu6 => { + NetworkUpgrade::Nu5 | NetworkUpgrade::Nu6 | NetworkUpgrade::Nu7 => { let (tree, entry) = Tree::::new_from_block( network, block, diff --git a/zebra-chain/src/lib.rs b/zebra-chain/src/lib.rs index 460d3a850f0..dd6fa1a1abf 100644 --- a/zebra-chain/src/lib.rs +++ b/zebra-chain/src/lib.rs @@ -41,6 +41,9 @@ pub mod transparent; pub mod value_balance; pub mod work; +#[cfg(feature = "tx-v6")] +pub mod orchard_zsa; + #[cfg(any(test, feature = "proptest-impl"))] pub use block::LedgerState; diff --git a/zebra-chain/src/orchard.rs b/zebra-chain/src/orchard.rs index be96644c8c9..5c258723c79 100644 --- a/zebra-chain/src/orchard.rs +++ b/zebra-chain/src/orchard.rs @@ -6,6 +6,7 @@ mod action; mod address; mod commitment; mod note; +mod shielded_data_flavor; mod sinsemilla; #[cfg(any(test, feature = "proptest-impl"))] @@ -23,3 +24,7 @@ pub use commitment::{CommitmentRandomness, NoteCommitment, ValueCommitment}; pub use keys::Diversifier; pub use note::{EncryptedNote, Note, Nullifier, WrappedNoteKey}; pub use shielded_data::{AuthorizedAction, Flags, ShieldedData}; +pub use shielded_data_flavor::{OrchardVanilla, ShieldedDataFlavor}; + +#[cfg(feature = "tx-v6")] +pub use shielded_data_flavor::OrchardZSA; diff --git a/zebra-chain/src/orchard/action.rs b/zebra-chain/src/orchard/action.rs index ae7690def7a..f9e9ad9c0ec 100644 --- a/zebra-chain/src/orchard/action.rs +++ b/zebra-chain/src/orchard/action.rs @@ -11,6 +11,7 @@ use super::{ commitment::{self, ValueCommitment}, keys, note::{self, Nullifier}, + ShieldedDataFlavor, }; /// An Action description, as described in the [Zcash specification §7.3][actiondesc]. @@ -21,7 +22,7 @@ use super::{ /// /// [actiondesc]: https://zips.z.cash/protocol/nu5.pdf#actiondesc #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct Action { +pub struct Action { /// A value commitment to net value of the input note minus the output note pub cv: commitment::ValueCommitment, /// The nullifier of the input note being spent. @@ -35,14 +36,14 @@ pub struct Action { /// encrypted private key in `out_ciphertext`. pub ephemeral_key: keys::EphemeralPublicKey, /// A ciphertext component for the encrypted output note. - pub enc_ciphertext: note::EncryptedNote, + pub enc_ciphertext: Flavor::EncryptedNote, /// A ciphertext component that allows the holder of a full viewing key to /// recover the recipient diversified transmission key and the ephemeral /// private key (and therefore the entire note plaintext). pub out_ciphertext: note::WrappedNoteKey, } -impl ZcashSerialize for Action { +impl ZcashSerialize for Action { fn zcash_serialize(&self, mut writer: W) -> Result<(), io::Error> { self.cv.zcash_serialize(&mut writer)?; writer.write_all(&<[u8; 32]>::from(self.nullifier)[..])?; @@ -55,7 +56,7 @@ impl ZcashSerialize for Action { } } -impl ZcashDeserialize for Action { +impl ZcashDeserialize for Action { fn zcash_deserialize(mut reader: R) -> Result { // # Consensus // @@ -93,7 +94,7 @@ impl ZcashDeserialize for Action { // https://zips.z.cash/protocol/protocol.pdf#concretesym but fixed to // 580 bytes in https://zips.z.cash/protocol/protocol.pdf#outputencodingandconsensus // See [`note::EncryptedNote::zcash_deserialize`]. - enc_ciphertext: note::EncryptedNote::zcash_deserialize(&mut reader)?, + enc_ciphertext: Flavor::EncryptedNote::zcash_deserialize(&mut reader)?, // Type is `Sym.C`, i.e. `š”¹^Y^{\[N\]}`, i.e. arbitrary-sized byte arrays // https://zips.z.cash/protocol/protocol.pdf#concretesym but fixed to // 80 bytes in https://zips.z.cash/protocol/protocol.pdf#outputencodingandconsensus diff --git a/zebra-chain/src/orchard/arbitrary.rs b/zebra-chain/src/orchard/arbitrary.rs index 7a6544606f8..dab7cbb2840 100644 --- a/zebra-chain/src/orchard/arbitrary.rs +++ b/zebra-chain/src/orchard/arbitrary.rs @@ -10,17 +10,21 @@ use reddsa::{orchard::SpendAuth, Signature, SigningKey, VerificationKey, Verific use proptest::{array, collection::vec, prelude::*}; use super::{ - keys::*, note, tree, Action, AuthorizedAction, Flags, NoteCommitment, ValueCommitment, + keys::*, note, tree, Action, AuthorizedAction, Flags, NoteCommitment, ShieldedDataFlavor, + ValueCommitment, }; -impl Arbitrary for Action { +impl Arbitrary for Action +where + ::Strategy: 'static, +{ type Parameters = (); fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy { ( any::(), any::(), - any::(), + any::(), any::(), ) .prop_map(|(nullifier, rk, enc_ciphertext, out_ciphertext)| Self { @@ -54,11 +58,14 @@ impl Arbitrary for note::Nullifier { type Strategy = BoxedStrategy; } -impl Arbitrary for AuthorizedAction { +impl Arbitrary for AuthorizedAction +where + ::Strategy: 'static, +{ type Parameters = (); fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy { - (any::(), any::()) + (any::>(), any::()) .prop_map(|(action, spend_auth_sig)| Self { action, spend_auth_sig: spend_auth_sig.0, diff --git a/zebra-chain/src/orchard/commitment.rs b/zebra-chain/src/orchard/commitment.rs index 4e69258c4e5..2b809bdbd27 100644 --- a/zebra-chain/src/orchard/commitment.rs +++ b/zebra-chain/src/orchard/commitment.rs @@ -14,6 +14,12 @@ use halo2::{ use lazy_static::lazy_static; use rand_core::{CryptoRng, RngCore}; +#[cfg(feature = "tx-v6")] +use orchard::{ + note::AssetBase, + value::{ValueCommitTrapdoor, ValueSum}, +}; + use crate::{ amount::Amount, error::RandError, @@ -230,13 +236,27 @@ impl ValueCommitment { /// Generate a new _ValueCommitment_. /// /// + #[allow(clippy::unwrap_in_result)] pub fn randomized(csprng: &mut T, value: Amount) -> Result where T: RngCore + CryptoRng, { let rcv = generate_trapdoor(csprng)?; - Ok(Self::new(rcv, value)) + #[cfg(feature = "tx-v6")] + let vc = Self::new( + rcv, + // TODO: Make the `ValueSum::from_raw` function public in the `orchard` crate + // and use `ValueSum::from_raw(value.into())` instead of the next line. + // Remove `#[allow(clippy::unwrap_in_result)]` after doing so. + (ValueSum::default() + i64::from(value)).unwrap(), + AssetBase::native(), + ); + + #[cfg(not(feature = "tx-v6"))] + let vc = Self::new(rcv, value); + + Ok(vc) } /// Generate a new `ValueCommitment` from an existing `rcv on a `value`. @@ -244,11 +264,29 @@ impl ValueCommitment { /// ValueCommit^Orchard(v) := /// /// - #[allow(non_snake_case)] + #[cfg(not(feature = "tx-v6"))] pub fn new(rcv: pallas::Scalar, value: Amount) -> Self { let v = pallas::Scalar::from(value); Self::from(*V * v + *R * rcv) } + + /// Generate a new `ValueCommitment` from an existing `rcv on a `value` (ZSA version). + #[cfg(feature = "tx-v6")] + pub fn new(rcv: pallas::Scalar, value: ValueSum, asset: AssetBase) -> Self { + // TODO: Add `pub` methods to `ValueCommitTrapdoor` and `ValueCommitment` in `orchard` + // to simplify type conversions when calling `orchard::value::ValueCommitment::derive`. + Self( + pallas::Affine::from_bytes( + &orchard::value::ValueCommitment::derive( + value, + ValueCommitTrapdoor::from_bytes(rcv.to_repr()).unwrap(), + asset, + ) + .to_bytes(), + ) + .unwrap(), + ) + } } lazy_static! { diff --git a/zebra-chain/src/orchard/note/arbitrary.rs b/zebra-chain/src/orchard/note/arbitrary.rs index e9365de80c1..88e34618170 100644 --- a/zebra-chain/src/orchard/note/arbitrary.rs +++ b/zebra-chain/src/orchard/note/arbitrary.rs @@ -2,13 +2,13 @@ use proptest::{collection::vec, prelude::*}; use super::*; -impl Arbitrary for EncryptedNote { +impl Arbitrary for EncryptedNote { type Parameters = (); fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy { - (vec(any::(), 580)) + (vec(any::(), SIZE)) .prop_map(|v| { - let mut bytes = [0; 580]; + let mut bytes = [0; SIZE]; bytes.copy_from_slice(v.as_slice()); Self(bytes) }) diff --git a/zebra-chain/src/orchard/note/ciphertexts.rs b/zebra-chain/src/orchard/note/ciphertexts.rs index 8f857cf1444..7817d531c0b 100644 --- a/zebra-chain/src/orchard/note/ciphertexts.rs +++ b/zebra-chain/src/orchard/note/ciphertexts.rs @@ -9,58 +9,39 @@ use crate::serialization::{SerializationError, ZcashDeserialize, ZcashSerialize} /// A ciphertext component for encrypted output notes. /// /// Corresponds to the Orchard 'encCiphertext's -#[derive(Deserialize, Serialize)] -pub struct EncryptedNote(#[serde(with = "BigArray")] pub(crate) [u8; 580]); - -// These impls all only exist because of array length restrictions. -// TODO: use const generics https://github.com/ZcashFoundation/zebra/issues/2042 - -impl Copy for EncryptedNote {} - -impl Clone for EncryptedNote { - fn clone(&self) -> Self { - *self - } -} - -impl fmt::Debug for EncryptedNote { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_tuple("EncryptedNote") - .field(&hex::encode(&self.0[..])) - .finish() - } -} +#[derive(Deserialize, Serialize, Clone, Debug, Eq, PartialEq)] +pub struct EncryptedNote(#[serde(with = "BigArray")] pub(crate) [u8; SIZE]); -impl Eq for EncryptedNote {} - -impl From<[u8; 580]> for EncryptedNote { - fn from(bytes: [u8; 580]) -> Self { - EncryptedNote(bytes) +impl From<[u8; SIZE]> for EncryptedNote { + fn from(bytes: [u8; SIZE]) -> Self { + Self(bytes) } } -impl From for [u8; 580] { - fn from(enc_ciphertext: EncryptedNote) -> Self { +impl From> for [u8; SIZE] { + fn from(enc_ciphertext: EncryptedNote) -> Self { enc_ciphertext.0 } } -impl PartialEq for EncryptedNote { - fn eq(&self, other: &Self) -> bool { - self.0[..] == other.0[..] +impl TryFrom<&[u8]> for EncryptedNote { + type Error = std::array::TryFromSliceError; + + fn try_from(bytes: &[u8]) -> Result { + Ok(Self(bytes.try_into()?)) } } -impl ZcashSerialize for EncryptedNote { +impl ZcashSerialize for EncryptedNote { fn zcash_serialize(&self, mut writer: W) -> Result<(), io::Error> { writer.write_all(&self.0[..])?; Ok(()) } } -impl ZcashDeserialize for EncryptedNote { +impl ZcashDeserialize for EncryptedNote { fn zcash_deserialize(mut reader: R) -> Result { - let mut bytes = [0; 580]; + let mut bytes = [0; SIZE]; reader.read_exact(&mut bytes[..])?; Ok(Self(bytes)) } @@ -126,33 +107,56 @@ impl ZcashDeserialize for WrappedNoteKey { } #[cfg(test)] -use proptest::prelude::*; -#[cfg(test)] -proptest! { +mod tests { + use crate::{ + orchard::{OrchardVanilla, ShieldedDataFlavor, WrappedNoteKey}, + serialization::{ZcashDeserialize, ZcashSerialize}, + }; + + #[cfg(feature = "tx-v6")] + use crate::orchard::OrchardZSA; - #[test] - fn encrypted_ciphertext_roundtrip(ec in any::()) { - let _init_guard = zebra_test::init(); + use proptest::prelude::*; + fn roundtrip_encrypted_note(note: &EncryptedNote) -> EncryptedNote + where + EncryptedNote: ZcashSerialize + ZcashDeserialize, + { let mut data = Vec::new(); + note.zcash_serialize(&mut data) + .expect("EncryptedNote should serialize"); + EncryptedNote::zcash_deserialize(&data[..]) + .expect("randomized EncryptedNote should deserialize") + } - ec.zcash_serialize(&mut data).expect("EncryptedNote should serialize"); + proptest! { + #[test] + fn encrypted_ciphertext_roundtrip_orchard_vanilla(ec in any::<::EncryptedNote>()) { + let _init_guard = zebra_test::init(); + let ec2 = roundtrip_encrypted_note(&ec); + prop_assert_eq![ec, ec2]; + } - let ec2 = EncryptedNote::zcash_deserialize(&data[..]).expect("randomized EncryptedNote should deserialize"); - prop_assert_eq![ec, ec2]; - } + #[cfg(feature = "tx-v6")] + #[test] + fn encrypted_ciphertext_roundtrip_orchard_zsa(ec in any::<::EncryptedNote>()) { + let _init_guard = zebra_test::init(); + let ec2 = roundtrip_encrypted_note(&ec); + prop_assert_eq![ec, ec2]; + } - #[test] - fn out_ciphertext_roundtrip(oc in any::()) { - let _init_guard = zebra_test::init(); + #[test] + fn out_ciphertext_roundtrip(oc in any::()) { + let _init_guard = zebra_test::init(); - let mut data = Vec::new(); + let mut data = Vec::new(); - oc.zcash_serialize(&mut data).expect("WrappedNoteKey should serialize"); + oc.zcash_serialize(&mut data).expect("WrappedNoteKey should serialize"); - let oc2 = WrappedNoteKey::zcash_deserialize(&data[..]).expect("randomized WrappedNoteKey should deserialize"); + let oc2 = WrappedNoteKey::zcash_deserialize(&data[..]).expect("randomized WrappedNoteKey should deserialize"); - prop_assert_eq![oc, oc2]; + prop_assert_eq![oc, oc2]; + } } } diff --git a/zebra-chain/src/orchard/shielded_data.rs b/zebra-chain/src/orchard/shielded_data.rs index 5347919cd01..dc0453ef6ea 100644 --- a/zebra-chain/src/orchard/shielded_data.rs +++ b/zebra-chain/src/orchard/shielded_data.rs @@ -20,9 +20,28 @@ use crate::{ }, }; +#[cfg(feature = "tx-v6")] +use crate::orchard_zsa::compute_burn_value_commitment; + +#[cfg(feature = "tx-v6")] +use orchard::{note::AssetBase, value::ValueSum}; + +use super::{OrchardVanilla, ShieldedDataFlavor}; + /// A bundle of [`Action`] descriptions and signature data. #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] -pub struct ShieldedData { +#[cfg_attr( + not(feature = "tx-v6"), + serde(bound(serialize = "Flavor::EncryptedNote: serde::Serialize")) +)] +#[cfg_attr( + feature = "tx-v6", + serde(bound( + serialize = "Flavor::EncryptedNote: serde::Serialize, Flavor::BurnType: serde::Serialize", + deserialize = "Flavor::BurnType: serde::Deserialize<'de>" + )) +)] +pub struct ShieldedData { /// The orchard flags for this transaction. /// Denoted as `flagsOrchard` in the spec. pub flags: Flags, @@ -37,13 +56,18 @@ pub struct ShieldedData { pub proof: Halo2Proof, /// The Orchard Actions, in the order they appear in the transaction. /// Denoted as `vActionsOrchard` and `vSpendAuthSigsOrchard` in the spec. - pub actions: AtLeastOne, + pub actions: AtLeastOne>, /// A signature on the transaction `sighash`. /// Denoted as `bindingSigOrchard` in the spec. pub binding_sig: Signature, + + #[cfg(feature = "tx-v6")] + /// Assets intended for burning + /// Denoted as `vAssetBurn` in the spec (ZIP 230). + pub burn: Flavor::BurnType, } -impl fmt::Display for ShieldedData { +impl fmt::Display for ShieldedData { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut fmter = f.debug_struct("orchard::ShieldedData"); @@ -59,10 +83,10 @@ impl fmt::Display for ShieldedData { } } -impl ShieldedData { +impl ShieldedData { /// Iterate over the [`Action`]s for the [`AuthorizedAction`]s in this /// transaction, in the order they appear in it. - pub fn actions(&self) -> impl Iterator { + pub fn actions(&self) -> impl Iterator> { self.actions.actions() } @@ -98,10 +122,29 @@ impl ShieldedData { /// pub fn binding_verification_key(&self) -> reddsa::VerificationKeyBytes { let cv: ValueCommitment = self.actions().map(|action| action.cv).sum(); - let cv_balance: ValueCommitment = - ValueCommitment::new(pallas::Scalar::zero(), self.value_balance); - let key_bytes: [u8; 32] = (cv - cv_balance).into(); + #[cfg(not(feature = "tx-v6"))] + let key = { + let cv_balance = ValueCommitment::new(pallas::Scalar::zero(), self.value_balance); + cv - cv_balance + }; + + #[cfg(feature = "tx-v6")] + let key = { + let cv_balance = ValueCommitment::new( + pallas::Scalar::zero(), + // TODO: Make the `ValueSum::from_raw` function public in the `orchard` crate + // and use `ValueSum::from_raw(self.value_balance.into())` instead of the + // next line + (ValueSum::default() + i64::from(self.value_balance)).unwrap(), + AssetBase::native(), + ); + let burn_value_commitment = compute_burn_value_commitment(self.burn.as_ref()); + cv - cv_balance - burn_value_commitment + }; + + let key_bytes: [u8; 32] = key.into(); + key_bytes.into() } @@ -119,9 +162,9 @@ impl ShieldedData { } } -impl AtLeastOne { +impl AtLeastOne> { /// Iterate over the [`Action`]s of each [`AuthorizedAction`]. - pub fn actions(&self) -> impl Iterator { + pub fn actions(&self) -> impl Iterator> { self.iter() .map(|authorized_action| &authorized_action.action) } @@ -131,23 +174,82 @@ impl AtLeastOne { /// /// Every authorized Orchard `Action` must have a corresponding `SpendAuth` signature. #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] -pub struct AuthorizedAction { +#[serde(bound = "Flavor::EncryptedNote: serde::Serialize")] +pub struct AuthorizedAction { /// The action description of this Action. - pub action: Action, + pub action: Action, /// The spend signature. pub spend_auth_sig: Signature, } -impl AuthorizedAction { +impl AuthorizedAction { + /// The size of a single Action description. + /// + /// Computed as: + /// ```text + /// 5 Ɨ 32 (fields for nullifier, output commitment, etc.) + /// + ENC_CIPHERTEXT_SIZE (580 bytes for OrchardVanilla / Nu5–Nu6, + /// 612 bytes for OrchardZSA / Nu7) + /// + 80 (authentication tag) + /// = 820 bytes (OrchardVanilla) + /// = 852 bytes (OrchardZSA) + /// ``` + /// + /// - For OrchardVanilla (Nu5/Nu6), ENC_CIPHERTEXT_SIZE = 580; see + /// [§ 7.5 Action Description Encoding and Consensus][nu5_pdf] and + /// [ZIP-0225 § ā€œOrchard Action Descriptionā€][zip225]. + /// - For OrchardZSA (Nu7), ENC_CIPHERTEXT_SIZE = 612; see + /// [ZIP-0230 § ā€œOrchardZSA Action Descriptionā€][zip230]. + /// + /// [nu5_pdf]: https://zips.z.cash/protocol/nu5.pdf#actionencodingandconsen + /// [zip225]: https://zips.z.cash/zip-0225#orchard-action-description-orchardaction + /// [zip230]: https://zips.z.cash/zip-0230#orchardzsa-action-description-orchardzsaaction + pub const ACTION_SIZE: u64 = 5 * 32 + (Flavor::ENC_CIPHERTEXT_SIZE as u64) + 80; + + /// The size of a single `Signature`. + /// + /// Each Signature is 64 bytes. + /// [7.1 Transaction Encoding and Consensus][ps] + /// + /// [ps]: + pub const SPEND_AUTH_SIG_SIZE: u64 = 64; + + /// The size of a single AuthorizedAction + /// + /// Each serialized `Action` has a corresponding `Signature`. + pub const AUTHORIZED_ACTION_SIZE: u64 = Self::ACTION_SIZE + Self::SPEND_AUTH_SIG_SIZE; + + /// The maximum number of actions allowed in a transaction. + /// + /// A serialized `Vec` requires at least one byte for its length, + /// and each action must include a signature. Therefore, the maximum allocation + /// is constrained by these factors and cannot exceed this calculated size. + pub const ACTION_MAX_ALLOCATION: u64 = (MAX_BLOCK_BYTES - 1) / Self::AUTHORIZED_ACTION_SIZE; + + /// Enforce consensus limit at compile time: + /// + /// # Consensus + /// + /// > [NU5 onward] nSpendsSapling, nOutputsSapling, and nActionsOrchard MUST all be less than 2^16. + /// + /// https://zips.z.cash/protocol/protocol.pdf#txnconsensus + /// + /// This check works because if `ACTION_MAX_ALLOCATION` were ≄ 2^16, the subtraction below + /// would underflow for `u64`, causing a compile-time error. + const _ACTION_MAX_ALLOCATION_OK: u64 = (1 << 16) - Self::ACTION_MAX_ALLOCATION; + /// Split out the action and the signature for V5 transaction /// serialization. - pub fn into_parts(self) -> (Action, Signature) { + pub fn into_parts(self) -> (Action, Signature) { (self.action, self.spend_auth_sig) } // Combine the action and the spend auth sig from V5 transaction /// deserialization. - pub fn from_parts(action: Action, spend_auth_sig: Signature) -> AuthorizedAction { + pub fn from_parts( + action: Action, + spend_auth_sig: Signature, + ) -> AuthorizedAction { AuthorizedAction { action, spend_auth_sig, @@ -155,56 +257,20 @@ impl AuthorizedAction { } } -/// The size of a single Action -/// -/// Actions are 5 * 32 + 580 + 80 bytes so the total size of each Action is 820 bytes. -/// [7.5 Action Description Encoding and Consensus][ps] -/// -/// [ps]: -pub const ACTION_SIZE: u64 = 5 * 32 + 580 + 80; - -/// The size of a single `Signature`. -/// -/// Each Signature is 64 bytes. -/// [7.1 Transaction Encoding and Consensus][ps] -/// -/// [ps]: -pub const SPEND_AUTH_SIG_SIZE: u64 = 64; - -/// The size of a single AuthorizedAction -/// -/// Each serialized `Action` has a corresponding `Signature`. -pub const AUTHORIZED_ACTION_SIZE: u64 = ACTION_SIZE + SPEND_AUTH_SIG_SIZE; - /// The maximum number of orchard actions in a valid Zcash on-chain transaction V5. /// /// If a transaction contains more actions than can fit in maximally large block, it might be /// valid on the network and in the mempool, but it can never be mined into a block. So /// rejecting these large edge-case transactions can never break consensus. -impl TrustedPreallocate for Action { +impl TrustedPreallocate for Action { fn max_allocation() -> u64 { - // Since a serialized Vec uses at least one byte for its length, - // and the signature is required, - // a valid max allocation can never exceed this size - const MAX: u64 = (MAX_BLOCK_BYTES - 1) / AUTHORIZED_ACTION_SIZE; - // # Consensus - // - // > [NU5 onward] nSpendsSapling, nOutputsSapling, and nActionsOrchard MUST all be less than 2^16. - // - // https://zips.z.cash/protocol/protocol.pdf#txnconsensus - // - // This acts as nActionsOrchard and is therefore subject to the rule. - // The maximum value is actually smaller due to the block size limit, - // but we ensure the 2^16 limit with a static assertion. - static_assertions::const_assert!(MAX < (1 << 16)); - MAX + AuthorizedAction::::ACTION_MAX_ALLOCATION } } impl TrustedPreallocate for Signature { fn max_allocation() -> u64 { - // Each signature must have a corresponding action. - Action::max_allocation() + Action::::max_allocation() } } @@ -231,6 +297,9 @@ bitflags! { const ENABLE_SPENDS = 0b00000001; /// Enable creating new non-zero valued Orchard notes. const ENABLE_OUTPUTS = 0b00000010; + /// Enable ZSA transaction (otherwise all notes within actions must use native asset). + // FIXME: Should we use this flag explicitly anywhere in Zebra? + const ENABLE_ZSA = 0b00000100; } } diff --git a/zebra-chain/src/orchard/shielded_data_flavor.rs b/zebra-chain/src/orchard/shielded_data_flavor.rs new file mode 100644 index 00000000000..7dfae6d109a --- /dev/null +++ b/zebra-chain/src/orchard/shielded_data_flavor.rs @@ -0,0 +1,73 @@ +//! This module defines traits and structures for supporting the Orchard Shielded Protocol +//! for `V5` and `V6` versions of the transaction. +use std::fmt::Debug; + +use serde::{de::DeserializeOwned, Serialize}; + +use orchard::{domain::OrchardDomainCommon, orchard_flavor::OrchardFlavor}; + +pub use orchard::orchard_flavor::OrchardVanilla; + +#[cfg(feature = "tx-v6")] +pub use orchard::{note::AssetBase, orchard_flavor::OrchardZSA, value::NoteValue}; + +use crate::serialization::{ZcashDeserialize, ZcashSerialize}; + +#[cfg(feature = "tx-v6")] +use crate::orchard_zsa::{Burn, BurnItem, NoBurn}; + +use super::note; + +// When testing or with the proptest-impl feature, enforce Arbitrary. +#[cfg(any(test, feature = "proptest-impl"))] +mod test_arbitrary { + use proptest::prelude::Arbitrary; + + pub trait TestArbitrary: Arbitrary {} + impl TestArbitrary for T {} +} + +// Otherwise, no extra requirement. +#[cfg(not(any(test, feature = "proptest-impl")))] +mod test_arbitrary { + pub trait TestArbitrary {} + impl TestArbitrary for T {} +} + +/// A trait representing compile-time settings of ShieldedData of Orchard Shielded Protocol +/// used in the transactions `V5` and `V6`. +pub trait ShieldedDataFlavor: OrchardFlavor { + /// A type representing an encrypted note for this protocol version. + type EncryptedNote: Clone + + Debug + + PartialEq + + Eq + + DeserializeOwned + + Serialize + + ZcashDeserialize + + ZcashSerialize + + for<'a> TryFrom<&'a [u8], Error = std::array::TryFromSliceError> + + test_arbitrary::TestArbitrary; + + /// A type representing a burn field for this protocol version. + #[cfg(feature = "tx-v6")] + type BurnType: Clone + + Debug + + ZcashDeserialize + + ZcashSerialize + + AsRef<[BurnItem]> + + for<'a> From<&'a [(AssetBase, NoteValue)]> + + test_arbitrary::TestArbitrary; +} + +impl ShieldedDataFlavor for OrchardVanilla { + type EncryptedNote = note::EncryptedNote<{ OrchardVanilla::ENC_CIPHERTEXT_SIZE }>; + #[cfg(feature = "tx-v6")] + type BurnType = NoBurn; +} + +#[cfg(feature = "tx-v6")] +impl ShieldedDataFlavor for OrchardZSA { + type EncryptedNote = note::EncryptedNote<{ OrchardZSA::ENC_CIPHERTEXT_SIZE }>; + type BurnType = Burn; +} diff --git a/zebra-chain/src/orchard/tests/preallocate.rs b/zebra-chain/src/orchard/tests/preallocate.rs index 79f6a16e7d9..6b1fadfce29 100644 --- a/zebra-chain/src/orchard/tests/preallocate.rs +++ b/zebra-chain/src/orchard/tests/preallocate.rs @@ -4,10 +4,7 @@ use reddsa::{orchard::SpendAuth, Signature}; use crate::{ block::MAX_BLOCK_BYTES, - orchard::{ - shielded_data::{ACTION_SIZE, AUTHORIZED_ACTION_SIZE}, - Action, AuthorizedAction, - }, + orchard::{Action, AuthorizedAction, OrchardVanilla}, serialization::{arbitrary::max_allocation_is_big_enough, TrustedPreallocate, ZcashSerialize}, }; @@ -17,16 +14,16 @@ proptest! { /// Confirm that each `AuthorizedAction` takes exactly AUTHORIZED_ACTION_SIZE /// bytes when serialized. #[test] - fn authorized_action_size_is_small_enough(authorized_action in ::arbitrary_with(())) { + fn authorized_action_size_is_small_enough(authorized_action in >::arbitrary_with(())) { let (action, spend_auth_sig) = authorized_action.into_parts(); let mut serialized_len = action.zcash_serialize_to_vec().expect("Serialization to vec must succeed").len(); serialized_len += spend_auth_sig.zcash_serialize_to_vec().expect("Serialization to vec must succeed").len(); - prop_assert!(serialized_len as u64 == AUTHORIZED_ACTION_SIZE) + prop_assert!(serialized_len as u64 == AuthorizedAction::::AUTHORIZED_ACTION_SIZE) } /// Verify trusted preallocation for `AuthorizedAction` and its split fields #[test] - fn authorized_action_max_allocation_is_big_enough(authorized_action in ::arbitrary_with(())) { + fn authorized_action_max_allocation_is_big_enough(authorized_action in >::arbitrary_with(())) { let (action, spend_auth_sig) = authorized_action.into_parts(); let ( @@ -37,12 +34,14 @@ proptest! { ) = max_allocation_is_big_enough(action); // Calculate the actual size of all required Action fields - prop_assert!((smallest_disallowed_serialized_len as u64)/ACTION_SIZE*AUTHORIZED_ACTION_SIZE >= MAX_BLOCK_BYTES); - prop_assert!((largest_allowed_serialized_len as u64)/ACTION_SIZE*AUTHORIZED_ACTION_SIZE <= MAX_BLOCK_BYTES); + prop_assert!((smallest_disallowed_serialized_len as u64)/AuthorizedAction::::ACTION_SIZE* + AuthorizedAction::::AUTHORIZED_ACTION_SIZE >= MAX_BLOCK_BYTES); + prop_assert!((largest_allowed_serialized_len as u64)/AuthorizedAction::::ACTION_SIZE* + AuthorizedAction::::AUTHORIZED_ACTION_SIZE <= MAX_BLOCK_BYTES); // Check the serialization limits for `Action` - prop_assert!(((smallest_disallowed_vec_len - 1) as u64) == Action::max_allocation()); - prop_assert!((largest_allowed_vec_len as u64) == Action::max_allocation()); + prop_assert!(((smallest_disallowed_vec_len - 1) as u64) == Action::::max_allocation()); + prop_assert!((largest_allowed_vec_len as u64) == Action::::max_allocation()); prop_assert!((largest_allowed_serialized_len as u64) <= MAX_BLOCK_BYTES); let ( diff --git a/zebra-chain/src/orchard_zsa.rs b/zebra-chain/src/orchard_zsa.rs new file mode 100644 index 00000000000..a90c115a9b3 --- /dev/null +++ b/zebra-chain/src/orchard_zsa.rs @@ -0,0 +1,10 @@ +//! OrchardZSA related functionality. + +#[cfg(any(test, feature = "proptest-impl"))] +mod arbitrary; + +mod burn; +mod issuance; + +pub(crate) use burn::{compute_burn_value_commitment, Burn, BurnItem, NoBurn}; +pub(crate) use issuance::IssueData; diff --git a/zebra-chain/src/orchard_zsa/arbitrary.rs b/zebra-chain/src/orchard_zsa/arbitrary.rs new file mode 100644 index 00000000000..95091114260 --- /dev/null +++ b/zebra-chain/src/orchard_zsa/arbitrary.rs @@ -0,0 +1,58 @@ +//! Randomised data generation for OrchardZSA types. + +use proptest::prelude::*; + +use orchard::{bundle::testing::BundleArb, issuance::testing::arb_signed_issue_bundle}; + +use crate::transaction::arbitrary::MAX_ARBITRARY_ITEMS; + +use super::{ + burn::{Burn, BurnItem, NoBurn}, + issuance::IssueData, +}; + +impl Arbitrary for BurnItem { + type Parameters = (); + + fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy { + BundleArb::::arb_asset_to_burn() + .prop_map(|(asset_base, value)| BurnItem::from((asset_base, value))) + .boxed() + } + + type Strategy = BoxedStrategy; +} + +impl Arbitrary for NoBurn { + type Parameters = (); + + fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy { + Just(Self).boxed() + } + + type Strategy = BoxedStrategy; +} + +impl Arbitrary for Burn { + type Parameters = (); + + fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy { + prop::collection::vec(any::(), 0..MAX_ARBITRARY_ITEMS) + .prop_map(|inner| inner.into()) + .boxed() + } + + type Strategy = BoxedStrategy; +} + +impl Arbitrary for IssueData { + type Parameters = (); + + fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy { + arb_signed_issue_bundle(MAX_ARBITRARY_ITEMS) + .prop_map(|bundle| bundle.into()) + .boxed() + } + + type Strategy = BoxedStrategy; +} diff --git a/zebra-chain/src/orchard_zsa/burn.rs b/zebra-chain/src/orchard_zsa/burn.rs new file mode 100644 index 00000000000..bc738c2a7ef --- /dev/null +++ b/zebra-chain/src/orchard_zsa/burn.rs @@ -0,0 +1,177 @@ +//! OrchardZSA burn related functionality. + +use std::io; + +use halo2::pasta::pallas; + +use orchard::{note::AssetBase, value::NoteValue}; + +use zcash_primitives::transaction::components::orchard::{read_burn, write_burn}; + +use crate::{ + orchard::ValueCommitment, + serialization::{ReadZcashExt, SerializationError, ZcashDeserialize, ZcashSerialize}, +}; + +impl ZcashSerialize for AssetBase { + fn zcash_serialize(&self, mut writer: W) -> Result<(), io::Error> { + writer.write_all(&self.to_bytes()) + } +} + +impl ZcashDeserialize for AssetBase { + fn zcash_deserialize(mut reader: R) -> Result { + Option::from(AssetBase::from_bytes(&reader.read_32_bytes()?)) + .ok_or_else(|| SerializationError::Parse("Invalid orchard_zsa AssetBase!")) + } +} + +/// OrchardZSA burn item. +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub struct BurnItem(AssetBase, NoteValue); + +impl BurnItem { + /// Returns [`AssetBase`] being burned. + pub fn asset(&self) -> AssetBase { + self.0 + } + + /// Returns the amount being burned. + pub fn amount(&self) -> NoteValue { + self.1 + } + + /// Returns the raw [`u64`] amount being burned. + pub fn raw_amount(&self) -> u64 { + self.1.inner() + } +} + +// Convert from burn item type used in `orchard` crate +impl From<(AssetBase, NoteValue)> for BurnItem { + fn from(item: (AssetBase, NoteValue)) -> Self { + Self(item.0, item.1) + } +} + +// Convert to burn item type used in `orchard` crate +impl From for (AssetBase, NoteValue) { + fn from(item: BurnItem) -> Self { + (item.0, item.1) + } +} + +impl serde::Serialize for BurnItem { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + (self.0.to_bytes(), &self.1.inner()).serialize(serializer) + } +} + +impl<'de> serde::Deserialize<'de> for BurnItem { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + let (asset_base_bytes, amount) = <([u8; 32], u64)>::deserialize(deserializer)?; + Ok(BurnItem( + Option::from(AssetBase::from_bytes(&asset_base_bytes)) + .ok_or_else(|| serde::de::Error::custom("Invalid orchard_zsa AssetBase"))?, + NoteValue::from_raw(amount), + )) + } +} + +/// A special marker type indicating the absence of a burn field in Orchard ShieldedData for `V5` +/// transactions. It is unifying handling and serialization of ShieldedData across various Orchard +/// protocol variants. +#[derive(Default, Clone, Debug, PartialEq, Eq, Serialize)] +pub struct NoBurn; + +impl From<&[(AssetBase, NoteValue)]> for NoBurn { + fn from(bundle_burn: &[(AssetBase, NoteValue)]) -> Self { + assert!( + bundle_burn.is_empty(), + "Burn must be empty for OrchardVanilla" + ); + Self + } +} + +impl AsRef<[BurnItem]> for NoBurn { + fn as_ref(&self) -> &[BurnItem] { + &[] + } +} + +impl ZcashSerialize for NoBurn { + fn zcash_serialize(&self, mut _writer: W) -> Result<(), io::Error> { + Ok(()) + } +} + +impl ZcashDeserialize for NoBurn { + fn zcash_deserialize(mut _reader: R) -> Result { + Ok(Self) + } +} + +/// OrchardZSA burn items. +#[derive(Default, Clone, Debug, PartialEq, Eq, Serialize)] +pub struct Burn(Vec); + +impl From> for Burn { + fn from(inner: Vec) -> Self { + Self(inner) + } +} + +impl From<&[(AssetBase, NoteValue)]> for Burn { + fn from(bundle_burn: &[(AssetBase, NoteValue)]) -> Self { + Self( + bundle_burn + .iter() + .map(|bundle_burn_item| BurnItem::from(*bundle_burn_item)) + .collect(), + ) + } +} + +impl AsRef<[BurnItem]> for Burn { + fn as_ref(&self) -> &[BurnItem] { + &self.0 + } +} + +impl ZcashSerialize for Burn { + fn zcash_serialize(&self, mut writer: W) -> Result<(), io::Error> { + write_burn( + &mut writer, + &self.0.iter().map(|item| (*item).into()).collect::>(), + ) + } +} + +impl ZcashDeserialize for Burn { + fn zcash_deserialize(mut reader: R) -> Result { + Ok(Burn( + read_burn(&mut reader)? + .into_iter() + .map(|item| item.into()) + .collect(), + )) + } +} + +/// Computes the value commitment for a list of burns. +/// +/// For burns, the public trapdoor is always zero. +pub(crate) fn compute_burn_value_commitment(burn: &[BurnItem]) -> ValueCommitment { + burn.iter() + .map(|&BurnItem(asset, amount)| { + ValueCommitment::new(pallas::Scalar::zero(), amount.into(), asset) + }) + .sum() +} diff --git a/zebra-chain/src/orchard_zsa/issuance.rs b/zebra-chain/src/orchard_zsa/issuance.rs new file mode 100644 index 00000000000..62305fc0892 --- /dev/null +++ b/zebra-chain/src/orchard_zsa/issuance.rs @@ -0,0 +1,69 @@ +//! OrchardZSA issuance related functionality. + +use std::{fmt::Debug, io}; + +// For pallas::Base::from_repr only +use group::ff::PrimeField; + +use halo2::pasta::pallas; + +use orchard::{ + issuance::{IssueBundle, Signed}, + note::ExtractedNoteCommitment, +}; + +use zcash_primitives::transaction::components::issuance::{read_v6_bundle, write_v6_bundle}; + +use crate::serialization::{SerializationError, ZcashDeserialize, ZcashSerialize}; + +/// Wrapper for `IssueBundle` used in the context of Transaction V6. This allows the implementation of +/// a Serde serializer for unit tests within this crate. +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct IssueData(IssueBundle); + +impl IssueData { + /// Returns a reference to the inner `IssueBundle`. + pub fn inner(&self) -> &IssueBundle { + &self.0 + } +} + +impl From> for IssueData { + fn from(inner: IssueBundle) -> Self { + Self(inner) + } +} + +impl IssueData { + pub(crate) fn note_commitments(&self) -> impl Iterator + '_ { + self.0.actions().iter().flat_map(|action| { + action.notes().iter().map(|note| { + // TODO: FIXME: Make `ExtractedNoteCommitment::inner` public in `orchard` (this would + // eliminate the need for the workaround of converting `pallas::Base` from bytes + // here), or introduce a new public method in `orchard::issuance::IssueBundle` to + // retrieve note commitments directly from `orchard`. + pallas::Base::from_repr(ExtractedNoteCommitment::from(note.commitment()).to_bytes()) + .unwrap() + }) + }) + } +} + +impl ZcashSerialize for Option { + fn zcash_serialize(&self, writer: W) -> Result<(), io::Error> { + write_v6_bundle(self.as_ref().map(|issue_data| &issue_data.0), writer) + } +} + +impl ZcashDeserialize for Option { + fn zcash_deserialize(reader: R) -> Result { + Ok(read_v6_bundle(reader)?.map(IssueData)) + } +} + +#[cfg(any(test, feature = "proptest-impl", feature = "elasticsearch"))] +impl serde::Serialize for IssueData { + fn serialize(&self, _serializer: S) -> Result { + unimplemented!("Serde serialization for IssueData functionality is not needed for Zebra"); + } +} diff --git a/zebra-chain/src/parallel/tree.rs b/zebra-chain/src/parallel/tree.rs index 4f35dd44617..4f18d2359b3 100644 --- a/zebra-chain/src/parallel/tree.rs +++ b/zebra-chain/src/parallel/tree.rs @@ -73,7 +73,7 @@ impl NoteCommitmentTrees { let sprout_note_commitments: Vec<_> = block.sprout_note_commitments().cloned().collect(); let sapling_note_commitments: Vec<_> = block.sapling_note_commitments().cloned().collect(); - let orchard_note_commitments: Vec<_> = block.orchard_note_commitments().cloned().collect(); + let orchard_note_commitments: Vec<_> = block.orchard_note_commitments().collect(); let mut sprout_result = None; let mut sapling_result = None; diff --git a/zebra-chain/src/parameters/network.rs b/zebra-chain/src/parameters/network.rs index e8571340b7d..d119c087dd9 100644 --- a/zebra-chain/src/parameters/network.rs +++ b/zebra-chain/src/parameters/network.rs @@ -152,10 +152,12 @@ impl Network { pub fn new_regtest( nu5_activation_height: Option, nu6_activation_height: Option, + nu7_activation_height: Option, ) -> Self { Self::new_configured_testnet(testnet::Parameters::new_regtest( nu5_activation_height, nu6_activation_height, + nu7_activation_height, )) } diff --git a/zebra-chain/src/parameters/network/testnet.rs b/zebra-chain/src/parameters/network/testnet.rs index 78f7a69a302..dc1b4c83e28 100644 --- a/zebra-chain/src/parameters/network/testnet.rs +++ b/zebra-chain/src/parameters/network/testnet.rs @@ -206,6 +206,9 @@ pub struct ConfiguredActivationHeights { /// Activation height for `NU6` network upgrade. #[serde(rename = "NU6")] pub nu6: Option, + /// Activation height for `NU7` network upgrade. + #[serde(rename = "NU7")] + pub nu7: Option, } /// Builder for the [`Parameters`] struct. @@ -336,6 +339,7 @@ impl ParametersBuilder { canopy, nu5, nu6, + nu7, }: ConfiguredActivationHeights, ) -> Self { use NetworkUpgrade::*; @@ -358,6 +362,7 @@ impl ParametersBuilder { .chain(canopy.into_iter().map(|h| (h, Canopy))) .chain(nu5.into_iter().map(|h| (h, Nu5))) .chain(nu6.into_iter().map(|h| (h, Nu6))) + .chain(nu7.into_iter().map(|h| (h, Nu7))) .map(|(h, nu)| (h.try_into().expect("activation height must be valid"), nu)) .collect(); @@ -588,6 +593,7 @@ impl Parameters { pub fn new_regtest( nu5_activation_height: Option, nu6_activation_height: Option, + nu7_activation_height: Option, ) -> Self { #[cfg(any(test, feature = "proptest-impl"))] let nu5_activation_height = nu5_activation_height.or(Some(100)); @@ -604,6 +610,7 @@ impl Parameters { canopy: Some(1), nu5: nu5_activation_height, nu6: nu6_activation_height, + nu7: nu7_activation_height, ..Default::default() }) .with_halving_interval(PRE_BLOSSOM_REGTEST_HALVING_INTERVAL); @@ -647,7 +654,7 @@ impl Parameters { disable_pow, pre_blossom_halving_interval, post_blossom_halving_interval, - } = Self::new_regtest(None, None); + } = Self::new_regtest(None, None, None); self.network_name == network_name && self.genesis_hash == genesis_hash diff --git a/zebra-chain/src/parameters/network/tests/vectors.rs b/zebra-chain/src/parameters/network/tests/vectors.rs index 4282c86844f..6c1426e4e0e 100644 --- a/zebra-chain/src/parameters/network/tests/vectors.rs +++ b/zebra-chain/src/parameters/network/tests/vectors.rs @@ -109,7 +109,7 @@ fn activates_network_upgrades_correctly() { let expected_activation_height = 1; let network = testnet::Parameters::build() .with_activation_heights(ConfiguredActivationHeights { - nu6: Some(expected_activation_height), + nu7: Some(expected_activation_height), ..Default::default() }) .to_network(); @@ -147,7 +147,7 @@ fn activates_network_upgrades_correctly() { (Network::Mainnet, MAINNET_ACTIVATION_HEIGHTS), (Network::new_default_testnet(), TESTNET_ACTIVATION_HEIGHTS), ( - Network::new_regtest(None, None), + Network::new_regtest(None, None, None), expected_default_regtest_activation_heights, ), ] { @@ -198,7 +198,7 @@ fn check_configured_network_name() { "Mainnet should be displayed as 'Mainnet'" ); assert_eq!( - Network::new_regtest(None, None).to_string(), + Network::new_regtest(None, None, None).to_string(), "Regtest", "Regtest should be displayed as 'Regtest'" ); diff --git a/zebra-chain/src/parameters/network_upgrade.rs b/zebra-chain/src/parameters/network_upgrade.rs index 57165d0c760..3cc62abecf4 100644 --- a/zebra-chain/src/parameters/network_upgrade.rs +++ b/zebra-chain/src/parameters/network_upgrade.rs @@ -15,7 +15,7 @@ use hex::{FromHex, ToHex}; use proptest_derive::Arbitrary; /// A list of network upgrades in the order that they must be activated. -pub const NETWORK_UPGRADES_IN_ORDER: [NetworkUpgrade; 9] = [ +pub const NETWORK_UPGRADES_IN_ORDER: [NetworkUpgrade; 10] = [ Genesis, BeforeOverwinter, Overwinter, @@ -25,6 +25,7 @@ pub const NETWORK_UPGRADES_IN_ORDER: [NetworkUpgrade; 9] = [ Canopy, Nu5, Nu6, + Nu7, ]; /// A Zcash network upgrade. @@ -61,6 +62,9 @@ pub enum NetworkUpgrade { /// The Zcash protocol after the NU6 upgrade. #[serde(rename = "NU6")] Nu6, + /// The Zcash protocol after the NU7 upgrade. + #[serde(rename = "NU7")] + Nu7, } impl fmt::Display for NetworkUpgrade { @@ -90,6 +94,9 @@ pub(super) const MAINNET_ACTIVATION_HEIGHTS: &[(block::Height, NetworkUpgrade)] (block::Height(1_046_400), Canopy), (block::Height(1_687_104), Nu5), (block::Height(2_726_400), Nu6), + // FIXME: TODO: Use a proper value below. + #[cfg(zcash_unstable = "nu6" /* TODO nu7 */ )] + (block::Height(3_111_000), Nu7), ]; /// Fake mainnet network upgrade activation heights, used in tests. @@ -104,6 +111,7 @@ const FAKE_MAINNET_ACTIVATION_HEIGHTS: &[(block::Height, NetworkUpgrade)] = &[ (block::Height(30), Canopy), (block::Height(35), Nu5), (block::Height(40), Nu6), + (block::Height(45), Nu7), ]; /// Testnet network upgrade activation heights. @@ -126,6 +134,9 @@ pub(super) const TESTNET_ACTIVATION_HEIGHTS: &[(block::Height, NetworkUpgrade)] (block::Height(1_028_500), Canopy), (block::Height(1_842_420), Nu5), (block::Height(2_976_000), Nu6), + // FIXME: TODO: Use a proper value below. + #[cfg(zcash_unstable = "nu6" /* TODO nu7 */ )] + (block::Height(3_222_000), Nu7), ]; /// Fake testnet network upgrade activation heights, used in tests. @@ -140,6 +151,7 @@ const FAKE_TESTNET_ACTIVATION_HEIGHTS: &[(block::Height, NetworkUpgrade)] = &[ (block::Height(30), Canopy), (block::Height(35), Nu5), (block::Height(40), Nu6), + (block::Height(45), Nu7), ]; /// The Consensus Branch Id, used to bind transactions and blocks to a @@ -216,6 +228,9 @@ pub(crate) const CONSENSUS_BRANCH_IDS: &[(NetworkUpgrade, ConsensusBranchId)] = (Canopy, ConsensusBranchId(0xe9ff75a6)), (Nu5, ConsensusBranchId(0xc2d6d0b4)), (Nu6, ConsensusBranchId(0xc8e71055)), + // FIXME: TODO: Use a proper value below. + #[cfg(zcash_unstable = "nu6" /* TODO nu7 */ )] + (Nu7, ConsensusBranchId(0x77190ad8)), ]; /// The target block spacing before Blossom. @@ -332,7 +347,8 @@ impl NetworkUpgrade { Heartwood => Some(Canopy), Canopy => Some(Nu5), Nu5 => Some(Nu6), - Nu6 => None, + Nu6 => Some(Nu7), + Nu7 => None, } } @@ -409,7 +425,9 @@ impl NetworkUpgrade { pub fn target_spacing(&self) -> Duration { let spacing_seconds = match self { Genesis | BeforeOverwinter | Overwinter | Sapling => PRE_BLOSSOM_POW_TARGET_SPACING, - Blossom | Heartwood | Canopy | Nu5 | Nu6 => POST_BLOSSOM_POW_TARGET_SPACING.into(), + Blossom | Heartwood | Canopy | Nu5 | Nu6 | Nu7 => { + POST_BLOSSOM_POW_TARGET_SPACING.into() + } }; Duration::seconds(spacing_seconds) @@ -531,6 +549,9 @@ impl From for NetworkUpgrade { zcash_protocol::consensus::NetworkUpgrade::Canopy => Self::Canopy, zcash_protocol::consensus::NetworkUpgrade::Nu5 => Self::Nu5, zcash_protocol::consensus::NetworkUpgrade::Nu6 => Self::Nu6, + // TODO: Use a proper value below. + #[cfg(zcash_unstable = "nu6" /* TODO nu7 */ )] + zcash_protocol::consensus::NetworkUpgrade::Nu7 => Self::Nu7, } } } diff --git a/zebra-chain/src/parameters/transaction.rs b/zebra-chain/src/parameters/transaction.rs index bab59e794db..bcc44cb4f6e 100644 --- a/zebra-chain/src/parameters/transaction.rs +++ b/zebra-chain/src/parameters/transaction.rs @@ -11,3 +11,11 @@ pub const SAPLING_VERSION_GROUP_ID: u32 = 0x892F_2085; /// Orchard transactions must use transaction version 5 and this version /// group ID. Sapling transactions can use v4 or v5 transactions. pub const TX_V5_VERSION_GROUP_ID: u32 = 0x26A7_270A; + +/// The version group ID for version 6 transactions. +/// +/// OrchardZSA transactions must use transaction version 6 and this version +/// group ID. +// TODO: FIXME: use a proper value! +#[cfg(feature = "tx-v6")] +pub const TX_V6_VERSION_GROUP_ID: u32 = 0x7777_7777; diff --git a/zebra-chain/src/primitives/zcash_history.rs b/zebra-chain/src/primitives/zcash_history.rs index e8ca97d63f8..4b52c85d8e8 100644 --- a/zebra-chain/src/primitives/zcash_history.rs +++ b/zebra-chain/src/primitives/zcash_history.rs @@ -276,7 +276,8 @@ impl Version for zcash_history::V1 { NetworkUpgrade::Heartwood | NetworkUpgrade::Canopy | NetworkUpgrade::Nu5 - | NetworkUpgrade::Nu6 => zcash_history::NodeData { + | NetworkUpgrade::Nu6 + | NetworkUpgrade::Nu7 => zcash_history::NodeData { consensus_branch_id: branch_id.into(), subtree_commitment: block_hash, start_time: time, diff --git a/zebra-chain/src/primitives/zcash_note_encryption.rs b/zebra-chain/src/primitives/zcash_note_encryption.rs index cbc19afc5d2..c1dd891f9ee 100644 --- a/zebra-chain/src/primitives/zcash_note_encryption.rs +++ b/zebra-chain/src/primitives/zcash_note_encryption.rs @@ -8,7 +8,14 @@ use crate::{ transaction::Transaction, }; -/// Returns true if all Sapling or Orchard outputs, if any, decrypt successfully with +use orchard::{ + bundle::{Authorization, Bundle}, + domain::OrchardDomainCommon, +}; + +use zcash_primitives::transaction::OrchardBundle; + +/// Returns true if **all** Sapling or Orchard outputs decrypt successfully with /// an all-zeroes outgoing viewing key. /// /// # Panics @@ -49,20 +56,31 @@ pub fn decrypts_successfully(transaction: &Transaction, network: &Network, heigh } if let Some(bundle) = alt_tx.orchard_bundle() { - for act in bundle.actions() { - if zcash_note_encryption::try_output_recovery_with_ovk( - &orchard::note_encryption::OrchardDomain::for_action(act), - &orchard::keys::OutgoingViewingKey::from([0u8; 32]), - act, - act.cv_net(), - &act.encrypted_note().out_ciphertext, - ) - .is_none() - { - return false; - } + let is_decrypted_successfully = match bundle { + OrchardBundle::OrchardVanilla(bundle) => orchard_bundle_decrypts_successfully(bundle), + OrchardBundle::OrchardZSA(bundle) => orchard_bundle_decrypts_successfully(bundle), + }; + + if !is_decrypted_successfully { + return false; } } true } + +/// Checks if all actions in an Orchard bundle decrypt successfully. +fn orchard_bundle_decrypts_successfully( + bundle: &Bundle, +) -> bool { + bundle.actions().iter().all(|act| { + zcash_note_encryption::try_output_recovery_with_ovk( + &orchard::domain::OrchardDomain::for_action(act), + &orchard::keys::OutgoingViewingKey::from([0u8; 32]), + act, + act.cv_net(), + &act.encrypted_note().out_ciphertext, + ) + .is_some() + }) +} diff --git a/zebra-chain/src/primitives/zcash_primitives.rs b/zebra-chain/src/primitives/zcash_primitives.rs index 7ab2f32d751..90ea70324d7 100644 --- a/zebra-chain/src/primitives/zcash_primitives.rs +++ b/zebra-chain/src/primitives/zcash_primitives.rs @@ -10,7 +10,7 @@ use crate::{ amount::{Amount, NonNegative}, parameters::{ConsensusBranchId, Network}, serialization::ZcashSerialize, - transaction::{AuthDigest, HashType, SigHash, Transaction}, + transaction::{tx_v5_and_v6, AuthDigest, HashType, SigHash, Transaction}, transparent::{self, Script}, }; @@ -137,6 +137,15 @@ impl zp_tx::components::orchard::MapAuth + for IdentityMap +{ + fn map_issue_authorization(&self, s: orchard::issuance::Signed) -> orchard::issuance::Signed { + s + } +} + #[derive(Debug)] struct PrecomputedAuth<'a> { _phantom: std::marker::PhantomData<&'a ()>, @@ -146,6 +155,9 @@ impl<'a> zp_tx::Authorization for PrecomputedAuth<'a> { type TransparentAuth = TransparentAuth<'a>; type SaplingAuth = sapling_crypto::bundle::Authorized; type OrchardAuth = orchard::bundle::Authorized; + + #[cfg(zcash_unstable = "nu6" /* TODO nu7 */ )] + type IssueAuth = orchard::issuance::Signed; } // End of (mostly) copied code @@ -157,23 +169,27 @@ impl TryFrom<&Transaction> for zp_tx::Transaction { /// /// # Panics /// - /// If the transaction is not V5. (Currently there is no need for this + /// If the transaction is not V5/V6. (Currently there is no need for this /// conversion for other versions.) #[allow(clippy::unwrap_in_result)] fn try_from(trans: &Transaction) -> Result { let network_upgrade = match trans { - Transaction::V5 { + tx_v5_and_v6! { network_upgrade, .. } => network_upgrade, Transaction::V1 { .. } | Transaction::V2 { .. } | Transaction::V3 { .. } - | Transaction::V4 { .. } => panic!("Zebra only uses librustzcash for V5 transactions"), + | Transaction::V4 { .. } => { + panic!("Zebra only uses librustzcash for V5/V6 transactions"); + } }; convert_tx_to_librustzcash( trans, - network_upgrade.branch_id().expect("V5 txs have branch IDs"), + network_upgrade + .branch_id() + .expect("V5/V6 txs have branch IDs"), ) } } @@ -275,7 +291,7 @@ impl<'a> PrecomputedTxData<'a> { }; let tx_data: zp_tx::TransactionData = alt_tx .into_data() - .map_authorization(f_transparent, IdentityMap, IdentityMap); + .map_authorization(f_transparent, IdentityMap, IdentityMap, IdentityMap); PrecomputedTxData { tx_data, diff --git a/zebra-chain/src/transaction.rs b/zebra-chain/src/transaction.rs index 3df3edc8d53..4e4d8563fe6 100644 --- a/zebra-chain/src/transaction.rs +++ b/zebra-chain/src/transaction.rs @@ -53,6 +53,9 @@ use crate::{ value_balance::{ValueBalance, ValueBalanceError}, }; +#[cfg(feature = "tx-v6")] +use crate::orchard_zsa; + /// A Zcash transaction. /// /// A transaction is an encoded data structure that facilitates the transfer of @@ -140,7 +143,30 @@ pub enum Transaction { /// The sapling shielded data for this transaction, if any. sapling_shielded_data: Option>, /// The orchard data for this transaction, if any. - orchard_shielded_data: Option, + orchard_shielded_data: Option>, + }, + /// A `version = 6` transaction , OrchardZSA, Orchard, Sapling and transparent, but not Sprout. + #[cfg(feature = "tx-v6")] + V6 { + /// The Network Upgrade for this transaction. + /// + /// Derived from the ConsensusBranchId field. + network_upgrade: NetworkUpgrade, + /// The earliest time or block height that this transaction can be added to the + /// chain. + lock_time: LockTime, + /// The latest block height that this transaction can be added to the chain. + expiry_height: block::Height, + /// The transparent inputs to the transaction. + inputs: Vec, + /// The transparent outputs from the transaction. + outputs: Vec, + /// The sapling shielded data for this transaction, if any. + sapling_shielded_data: Option>, + /// The OrchardZSA shielded data for this transaction, if any. + orchard_shielded_data: Option>, + /// The OrchardZSA issuance data for this transaction, if any. + orchard_zsa_issue_data: Option, }, } @@ -167,7 +193,7 @@ impl fmt::Display for Transaction { fmter.field("sprout_joinsplits", &self.joinsplit_count()); fmter.field("sapling_spends", &self.sapling_spends_per_anchor().count()); fmter.field("sapling_outputs", &self.sapling_outputs().count()); - fmter.field("orchard_actions", &self.orchard_actions().count()); + fmter.field("orchard_actions", &self.orchard_action_count()); fmter.field("unmined_id", &self.unmined_id()); @@ -175,12 +201,57 @@ impl fmt::Display for Transaction { } } +// Define the macro to include the V6 variant. +// Needed only with the `tx-v6` feature to avoid duplicating code. +#[cfg(feature = "tx-v6")] +macro_rules! tx_v5_and_v6 { + { $($fields:tt)* } => { + Transaction::V5 { $($fields)* } | Transaction::V6 { $($fields)* } + }; +} + +/// Same as above, without the V6 arm. +#[cfg(not(feature = "tx-v6"))] +macro_rules! tx_v5_and_v6 { + { $($fields:tt)* } => { + Transaction::V5 { $($fields)* } + }; +} + +pub(crate) use tx_v5_and_v6; + +// Macro to get a specific field from an Orchard shielded data struct. +// Returns `None` for transaction versions that don't support Orchard (V1-V4). +// This avoids repeating the same match block pattern across multiple accessor methods. +macro_rules! orchard_shielded_data_field { + ($self:expr, $field:ident) => { + match $self { + // No Orchard shielded data + Transaction::V1 { .. } + | Transaction::V2 { .. } + | Transaction::V3 { .. } + | Transaction::V4 { .. } => None, + + Transaction::V5 { + orchard_shielded_data, + .. + } => orchard_shielded_data.as_ref().map(|data| data.$field), + + #[cfg(feature = "tx-v6")] + Transaction::V6 { + orchard_shielded_data, + .. + } => orchard_shielded_data.as_ref().map(|data| data.$field), + } + }; +} + impl Transaction { // identifiers and hashes /// Compute the hash (mined transaction ID) of this transaction. /// - /// The hash uniquely identifies mined v5 transactions, + /// The hash uniquely identifies mined v5/v6 transactions, /// and all v1-v4 transactions, whether mined or unmined. pub fn hash(&self) -> Hash { Hash::from(self) @@ -247,7 +318,7 @@ impl Transaction { | Transaction::V2 { .. } | Transaction::V3 { .. } | Transaction::V4 { .. } => None, - Transaction::V5 { .. } => Some(AuthDigest::from(self)), + tx_v5_and_v6! { .. } => Some(AuthDigest::from(self)), } } @@ -264,7 +335,7 @@ impl Transaction { pub fn has_shielded_inputs(&self) -> bool { self.joinsplit_count() > 0 || self.sapling_spends_per_anchor().count() > 0 - || (self.orchard_actions().count() > 0 + || (self.orchard_action_count() > 0 && self .orchard_flags() .unwrap_or_else(orchard::Flags::empty) @@ -282,7 +353,7 @@ impl Transaction { pub fn has_shielded_outputs(&self) -> bool { self.joinsplit_count() > 0 || self.sapling_outputs().count() > 0 - || (self.orchard_actions().count() > 0 + || (self.orchard_action_count() > 0 && self .orchard_flags() .unwrap_or_else(orchard::Flags::empty) @@ -291,7 +362,7 @@ impl Transaction { /// Does this transaction has at least one flag when we have at least one orchard action? pub fn has_enough_orchard_flags(&self) -> bool { - if self.version() < 5 || self.orchard_actions().count() == 0 { + if self.version() < 5 || self.orchard_action_count() == 0 { return true; } self.orchard_flags() @@ -320,7 +391,7 @@ impl Transaction { pub fn is_overwintered(&self) -> bool { match self { Transaction::V1 { .. } | Transaction::V2 { .. } => false, - Transaction::V3 { .. } | Transaction::V4 { .. } | Transaction::V5 { .. } => true, + Transaction::V3 { .. } | Transaction::V4 { .. } | tx_v5_and_v6! { .. } => true, } } @@ -332,6 +403,8 @@ impl Transaction { Transaction::V3 { .. } => 3, Transaction::V4 { .. } => 4, Transaction::V5 { .. } => 5, + #[cfg(feature = "tx-v6")] + Transaction::V6 { .. } => 6, } } @@ -342,7 +415,7 @@ impl Transaction { | Transaction::V2 { lock_time, .. } | Transaction::V3 { lock_time, .. } | Transaction::V4 { lock_time, .. } - | Transaction::V5 { lock_time, .. } => *lock_time, + | tx_v5_and_v6! { lock_time, .. } => *lock_time, }; // `zcashd` checks that the block height is greater than the lock height. @@ -389,7 +462,7 @@ impl Transaction { | Transaction::V2 { lock_time, .. } | Transaction::V3 { lock_time, .. } | Transaction::V4 { lock_time, .. } - | Transaction::V5 { lock_time, .. } => *lock_time, + | tx_v5_and_v6! { lock_time, .. } => *lock_time, }; let mut lock_time_bytes = Vec::new(); lock_time @@ -419,7 +492,7 @@ impl Transaction { Transaction::V1 { .. } | Transaction::V2 { .. } => None, Transaction::V3 { expiry_height, .. } | Transaction::V4 { expiry_height, .. } - | Transaction::V5 { expiry_height, .. } => match expiry_height { + | tx_v5_and_v6! { expiry_height, .. } => match expiry_height { // Consensus rule: // > No limit: To set no limit on transactions (so that they do not expire), nExpiryHeight should be set to 0. // https://zips.z.cash/zip-0203#specification @@ -448,7 +521,7 @@ impl Transaction { ref mut expiry_height, .. } - | Transaction::V5 { + | tx_v5_and_v6! { ref mut expiry_height, .. } => expiry_height, @@ -465,7 +538,7 @@ impl Transaction { | Transaction::V2 { .. } | Transaction::V3 { .. } | Transaction::V4 { .. } => None, - Transaction::V5 { + tx_v5_and_v6! { network_upgrade, .. } => Some(*network_upgrade), } @@ -480,7 +553,7 @@ impl Transaction { Transaction::V2 { ref inputs, .. } => inputs, Transaction::V3 { ref inputs, .. } => inputs, Transaction::V4 { ref inputs, .. } => inputs, - Transaction::V5 { ref inputs, .. } => inputs, + tx_v5_and_v6! { ref inputs, .. } => inputs, } } @@ -492,7 +565,7 @@ impl Transaction { Transaction::V2 { ref mut inputs, .. } => inputs, Transaction::V3 { ref mut inputs, .. } => inputs, Transaction::V4 { ref mut inputs, .. } => inputs, - Transaction::V5 { ref mut inputs, .. } => inputs, + tx_v5_and_v6! { ref mut inputs, .. } => inputs, } } @@ -510,7 +583,7 @@ impl Transaction { Transaction::V2 { ref outputs, .. } => outputs, Transaction::V3 { ref outputs, .. } => outputs, Transaction::V4 { ref outputs, .. } => outputs, - Transaction::V5 { ref outputs, .. } => outputs, + tx_v5_and_v6! { ref outputs, .. } => outputs, } } @@ -530,7 +603,7 @@ impl Transaction { Transaction::V4 { ref mut outputs, .. } => outputs, - Transaction::V5 { + tx_v5_and_v6! { ref mut outputs, .. } => outputs, } @@ -580,7 +653,7 @@ impl Transaction { joinsplit_data: None, .. } - | Transaction::V5 { .. } => Box::new(std::iter::empty()), + | tx_v5_and_v6! { .. } => Box::new(std::iter::empty()), } } @@ -615,7 +688,7 @@ impl Transaction { joinsplit_data: None, .. } - | Transaction::V5 { .. } => 0, + | tx_v5_and_v6! { .. } => 0, } } @@ -654,7 +727,7 @@ impl Transaction { joinsplit_data: None, .. } - | Transaction::V5 { .. } => Box::new(std::iter::empty()), + | tx_v5_and_v6! { .. } => Box::new(std::iter::empty()), } } @@ -690,7 +763,7 @@ impl Transaction { joinsplit_data: None, .. } - | Transaction::V5 { .. } => None, + | tx_v5_and_v6! { .. } => None, } } @@ -698,7 +771,7 @@ impl Transaction { pub fn has_sprout_joinsplit_data(&self) -> bool { match self { // No JoinSplits - Transaction::V1 { .. } | Transaction::V5 { .. } => false, + Transaction::V1 { .. } | tx_v5_and_v6! { .. } => false, // JoinSplits-on-BCTV14 Transaction::V2 { joinsplit_data, .. } | Transaction::V3 { joinsplit_data, .. } => { @@ -745,7 +818,7 @@ impl Transaction { .. } | Transaction::V1 { .. } - | Transaction::V5 { .. } => Box::new(std::iter::empty()), + | tx_v5_and_v6! { .. } => Box::new(std::iter::empty()), } } @@ -762,7 +835,7 @@ impl Transaction { .. } => Box::new(sapling_shielded_data.anchors()), - Transaction::V5 { + tx_v5_and_v6! { sapling_shielded_data: Some(sapling_shielded_data), .. } => Box::new(sapling_shielded_data.anchors()), @@ -775,7 +848,7 @@ impl Transaction { sapling_shielded_data: None, .. } - | Transaction::V5 { + | tx_v5_and_v6! { sapling_shielded_data: None, .. } => Box::new(std::iter::empty()), @@ -786,8 +859,8 @@ impl Transaction { /// returning `Spend` regardless of the underlying /// transaction version. /// - /// Shared anchors in V5 transactions are copied into each sapling spend. - /// This allows the same code to validate spends from V4 and V5 transactions. + /// Shared anchors in V5/V6 transactions are copied into each sapling spend. + /// This allows the same code to validate spends from V4 and V5/V6 transactions. /// /// # Correctness /// @@ -800,7 +873,7 @@ impl Transaction { sapling_shielded_data: Some(sapling_shielded_data), .. } => Box::new(sapling_shielded_data.spends_per_anchor()), - Transaction::V5 { + tx_v5_and_v6! { sapling_shielded_data: Some(sapling_shielded_data), .. } => Box::new(sapling_shielded_data.spends_per_anchor()), @@ -813,7 +886,7 @@ impl Transaction { sapling_shielded_data: None, .. } - | Transaction::V5 { + | tx_v5_and_v6! { sapling_shielded_data: None, .. } => Box::new(std::iter::empty()), @@ -828,7 +901,7 @@ impl Transaction { sapling_shielded_data: Some(sapling_shielded_data), .. } => Box::new(sapling_shielded_data.outputs()), - Transaction::V5 { + tx_v5_and_v6! { sapling_shielded_data: Some(sapling_shielded_data), .. } => Box::new(sapling_shielded_data.outputs()), @@ -841,7 +914,7 @@ impl Transaction { sapling_shielded_data: None, .. } - | Transaction::V5 { + | tx_v5_and_v6! { sapling_shielded_data: None, .. } => Box::new(std::iter::empty()), @@ -858,7 +931,7 @@ impl Transaction { sapling_shielded_data: Some(sapling_shielded_data), .. } => Box::new(sapling_shielded_data.nullifiers()), - Transaction::V5 { + tx_v5_and_v6! { sapling_shielded_data: Some(sapling_shielded_data), .. } => Box::new(sapling_shielded_data.nullifiers()), @@ -871,7 +944,7 @@ impl Transaction { sapling_shielded_data: None, .. } - | Transaction::V5 { + | tx_v5_and_v6! { sapling_shielded_data: None, .. } => Box::new(std::iter::empty()), @@ -888,7 +961,7 @@ impl Transaction { sapling_shielded_data: Some(sapling_shielded_data), .. } => Box::new(sapling_shielded_data.note_commitments()), - Transaction::V5 { + tx_v5_and_v6! { sapling_shielded_data: Some(sapling_shielded_data), .. } => Box::new(sapling_shielded_data.note_commitments()), @@ -901,7 +974,7 @@ impl Transaction { sapling_shielded_data: None, .. } - | Transaction::V5 { + | tx_v5_and_v6! { sapling_shielded_data: None, .. } => Box::new(std::iter::empty()), @@ -916,7 +989,7 @@ impl Transaction { sapling_shielded_data, .. } => sapling_shielded_data.is_some(), - Transaction::V5 { + tx_v5_and_v6! { sapling_shielded_data, .. } => sapling_shielded_data.is_some(), @@ -925,80 +998,114 @@ impl Transaction { // orchard - /// Access the [`orchard::ShieldedData`] in this transaction, - /// regardless of version. - pub fn orchard_shielded_data(&self) -> Option<&orchard::ShieldedData> { + /// Iterate over the [`orchard::Action`]s in this transaction. + pub fn orchard_action_count(&self) -> usize { match self { - // Maybe Orchard shielded data + Transaction::V1 { .. } + | Transaction::V2 { .. } + | Transaction::V3 { .. } + | Transaction::V4 { .. } => 0, + Transaction::V5 { orchard_shielded_data, .. - } => orchard_shielded_data.as_ref(), + } => orchard_shielded_data + .iter() + .flat_map(orchard::ShieldedData::actions) + .count(), - // No Orchard shielded data - Transaction::V1 { .. } - | Transaction::V2 { .. } - | Transaction::V3 { .. } - | Transaction::V4 { .. } => None, + #[cfg(feature = "tx-v6")] + Transaction::V6 { + orchard_shielded_data, + .. + } => orchard_shielded_data + .iter() + .flat_map(orchard::ShieldedData::actions) + .count(), } } - /// Modify the [`orchard::ShieldedData`] in this transaction, - /// regardless of version. - #[cfg(any(test, feature = "proptest-impl"))] - pub fn orchard_shielded_data_mut(&mut self) -> Option<&mut orchard::ShieldedData> { + /// Access the [`orchard::Nullifier`]s in this transaction. + pub fn orchard_nullifiers(&self) -> Box + '_> { match self { - Transaction::V5 { - orchard_shielded_data: Some(orchard_shielded_data), - .. - } => Some(orchard_shielded_data), - Transaction::V1 { .. } | Transaction::V2 { .. } | Transaction::V3 { .. } - | Transaction::V4 { .. } - | Transaction::V5 { - orchard_shielded_data: None, + | Transaction::V4 { .. } => Box::new(std::iter::empty()), + + Transaction::V5 { + orchard_shielded_data, .. - } => None, + } => Box::new( + orchard_shielded_data + .iter() + .flat_map(orchard::ShieldedData::nullifiers), + ), + + #[cfg(feature = "tx-v6")] + Transaction::V6 { + orchard_shielded_data, + .. + } => Box::new( + orchard_shielded_data + .iter() + .flat_map(orchard::ShieldedData::nullifiers), + ), } } - /// Iterate over the [`orchard::Action`]s in this transaction, if there are any, - /// regardless of version. - pub fn orchard_actions(&self) -> impl Iterator { - self.orchard_shielded_data() - .into_iter() - .flat_map(orchard::ShieldedData::actions) - } + /// Access the note commitments in this transaction. + pub fn orchard_note_commitments(&self) -> Box + '_> { + match self { + Transaction::V1 { .. } + | Transaction::V2 { .. } + | Transaction::V3 { .. } + | Transaction::V4 { .. } => Box::new(std::iter::empty()), - /// Access the [`orchard::Nullifier`]s in this transaction, if there are any, - /// regardless of version. - pub fn orchard_nullifiers(&self) -> impl Iterator { - self.orchard_shielded_data() - .into_iter() - .flat_map(orchard::ShieldedData::nullifiers) + Transaction::V5 { + orchard_shielded_data, + .. + } => Box::new( + orchard_shielded_data + .iter() + .flat_map(orchard::ShieldedData::note_commitments) + .cloned(), + ), + #[cfg(feature = "tx-v6")] + Transaction::V6 { + orchard_shielded_data, + orchard_zsa_issue_data, + .. + } => Box::new( + orchard_shielded_data + .iter() + .flat_map(orchard::ShieldedData::note_commitments) + .cloned() + .chain( + orchard_zsa_issue_data + .iter() + .flat_map(orchard_zsa::IssueData::note_commitments), + ), + ), + } } - /// Access the note commitments in this transaction, if there are any, + /// Access the [`orchard::Flags`] in this transaction, if there is any, /// regardless of version. - pub fn orchard_note_commitments(&self) -> impl Iterator { - self.orchard_shielded_data() - .into_iter() - .flat_map(orchard::ShieldedData::note_commitments) + pub fn orchard_flags(&self) -> Option { + orchard_shielded_data_field!(self, flags) } - /// Access the [`orchard::Flags`] in this transaction, if there is any, + /// Access the [`orchard::tree::Root`] in this transaction, if there is any, /// regardless of version. - pub fn orchard_flags(&self) -> Option { - self.orchard_shielded_data() - .map(|orchard_shielded_data| orchard_shielded_data.flags) + pub fn orchard_shared_anchor(&self) -> Option { + orchard_shielded_data_field!(self, shared_anchor) } /// Return if the transaction has any Orchard shielded data, /// regardless of version. pub fn has_orchard_shielded_data(&self) -> bool { - self.orchard_shielded_data().is_some() + self.orchard_flags().is_some() } // value balances @@ -1086,7 +1193,7 @@ impl Transaction { joinsplit_data: None, .. } - | Transaction::V5 { .. } => Box::new(std::iter::empty()), + | tx_v5_and_v6! { .. } => Box::new(std::iter::empty()), } } @@ -1135,7 +1242,7 @@ impl Transaction { joinsplit_data: None, .. } - | Transaction::V5 { .. } => Box::new(std::iter::empty()), + | tx_v5_and_v6! { .. } => Box::new(std::iter::empty()), } } @@ -1182,7 +1289,7 @@ impl Transaction { joinsplit_data: None, .. } - | Transaction::V5 { .. } => Box::new(std::iter::empty()), + | tx_v5_and_v6! { .. } => Box::new(std::iter::empty()), } } @@ -1231,7 +1338,7 @@ impl Transaction { joinsplit_data: None, .. } - | Transaction::V5 { .. } => Box::new(std::iter::empty()), + | tx_v5_and_v6! { .. } => Box::new(std::iter::empty()), } } @@ -1272,7 +1379,7 @@ impl Transaction { joinsplit_data: None, .. } - | Transaction::V5 { .. } => Box::new(iter::empty()), + | tx_v5_and_v6! { .. } => Box::new(iter::empty()), }; joinsplit_value_balances.map(ValueBalance::from_sprout_amount) @@ -1310,7 +1417,7 @@ impl Transaction { sapling_shielded_data: Some(sapling_shielded_data), .. } => sapling_shielded_data.value_balance, - Transaction::V5 { + tx_v5_and_v6! { sapling_shielded_data: Some(sapling_shielded_data), .. } => sapling_shielded_data.value_balance, @@ -1322,7 +1429,7 @@ impl Transaction { sapling_shielded_data: None, .. } - | Transaction::V5 { + | tx_v5_and_v6! { sapling_shielded_data: None, .. } => Amount::zero(), @@ -1342,7 +1449,7 @@ impl Transaction { sapling_shielded_data: Some(sapling_shielded_data), .. } => Some(&mut sapling_shielded_data.value_balance), - Transaction::V5 { + tx_v5_and_v6! { sapling_shielded_data: Some(sapling_shielded_data), .. } => Some(&mut sapling_shielded_data.value_balance), @@ -1353,7 +1460,7 @@ impl Transaction { sapling_shielded_data: None, .. } - | Transaction::V5 { + | tx_v5_and_v6! { sapling_shielded_data: None, .. } => None, @@ -1372,10 +1479,8 @@ impl Transaction { /// /// pub fn orchard_value_balance(&self) -> ValueBalance { - let orchard_value_balance = self - .orchard_shielded_data() - .map(|shielded_data| shielded_data.value_balance) - .unwrap_or_else(Amount::zero); + let orchard_value_balance = + orchard_shielded_data_field!(self, value_balance).unwrap_or_else(Amount::zero); ValueBalance::from_orchard_amount(orchard_value_balance) } @@ -1386,8 +1491,33 @@ impl Transaction { /// See `orchard_value_balance` for details. #[cfg(any(test, feature = "proptest-impl"))] pub fn orchard_value_balance_mut(&mut self) -> Option<&mut Amount> { - self.orchard_shielded_data_mut() - .map(|shielded_data| &mut shielded_data.value_balance) + match self { + Transaction::V5 { + orchard_shielded_data: Some(orchard_shielded_data), + .. + } => Some(&mut orchard_shielded_data.value_balance), + + #[cfg(feature = "tx-v6")] + Transaction::V6 { + orchard_shielded_data: Some(orchard_shielded_data), + .. + } => Some(&mut orchard_shielded_data.value_balance), + + Transaction::V1 { .. } + | Transaction::V2 { .. } + | Transaction::V3 { .. } + | Transaction::V4 { .. } + | Transaction::V5 { + orchard_shielded_data: None, + .. + } => None, + + #[cfg(feature = "tx-v6")] + Transaction::V6 { + orchard_shielded_data: None, + .. + } => None, + } } /// Returns the value balances for this transaction using the provided transparent outputs. diff --git a/zebra-chain/src/transaction/arbitrary.rs b/zebra-chain/src/transaction/arbitrary.rs index cf4aa7a9552..8e71b63f9e9 100644 --- a/zebra-chain/src/transaction/arbitrary.rs +++ b/zebra-chain/src/transaction/arbitrary.rs @@ -20,6 +20,9 @@ use crate::{ LedgerState, }; +#[cfg(feature = "tx-v6")] +use crate::orchard_zsa::IssueData; + use itertools::Itertools; use super::{ @@ -132,8 +135,21 @@ impl Transaction { .boxed() } - /// Generate a proptest strategy for V5 Transactions - pub fn v5_strategy(ledger_state: LedgerState) -> BoxedStrategy { + /// Helper function to generate the common transaction fields. + /// This function is generic over the Orchard shielded data type. + fn v5_v6_strategy_common( + ledger_state: LedgerState, + ) -> impl Strategy< + Value = ( + NetworkUpgrade, + LockTime, + block::Height, + Vec, + Vec, + Option>, + Option>, + ), + > + 'static { ( NetworkUpgrade::branch_id_strategy(), any::(), @@ -141,7 +157,7 @@ impl Transaction { transparent::Input::vec_strategy(&ledger_state, MAX_ARBITRARY_ITEMS), vec(any::(), 0..MAX_ARBITRARY_ITEMS), option::of(any::>()), - option::of(any::()), + option::of(any::>()), ) .prop_map( move |( @@ -153,29 +169,97 @@ impl Transaction { sapling_shielded_data, orchard_shielded_data, )| { - Transaction::V5 { - network_upgrade: if ledger_state.transaction_has_valid_network_upgrade() { - ledger_state.network_upgrade() - } else { - network_upgrade - }, + // Apply conditional logic based on ledger_state + let network_upgrade = if ledger_state.transaction_has_valid_network_upgrade() { + ledger_state.network_upgrade() + } else { + network_upgrade + }; + + let sapling_shielded_data = if ledger_state.height.is_min() { + // The genesis block should not contain any shielded data. + None + } else { + sapling_shielded_data + }; + + let orchard_shielded_data = if ledger_state.height.is_min() { + // The genesis block should not contain any shielded data. + None + } else { + orchard_shielded_data + }; + + ( + network_upgrade, lock_time, expiry_height, inputs, outputs, - sapling_shielded_data: if ledger_state.height.is_min() { - // The genesis block should not contain any shielded data. - None - } else { - sapling_shielded_data - }, - orchard_shielded_data: if ledger_state.height.is_min() { - // The genesis block should not contain any shielded data. - None - } else { - orchard_shielded_data - }, - } + sapling_shielded_data, + orchard_shielded_data, + ) + }, + ) + } + + /// Generate a proptest strategy for V5 Transactions + pub fn v5_strategy(ledger_state: LedgerState) -> BoxedStrategy { + Self::v5_v6_strategy_common::(ledger_state) + .prop_map( + move |( + network_upgrade, + lock_time, + expiry_height, + inputs, + outputs, + sapling_shielded_data, + orchard_shielded_data, + )| Transaction::V5 { + network_upgrade, + lock_time, + expiry_height, + inputs, + outputs, + sapling_shielded_data, + orchard_shielded_data, + }, + ) + .boxed() + } + + /// Generate a proptest strategy for V6 Transactions + #[cfg(feature = "tx-v6")] + pub fn v6_strategy(ledger_state: LedgerState) -> BoxedStrategy { + Self::v5_v6_strategy_common::(ledger_state) + .prop_flat_map(|common_fields| { + option::of(any::()) + .prop_map(move |issue_data| (common_fields.clone(), issue_data)) + }) + .prop_filter_map( + "orchard_shielded_data can not be None for V6", + |( + ( + network_upgrade, + lock_time, + expiry_height, + inputs, + outputs, + sapling_shielded_data, + orchard_shielded_data, + ), + orchard_zsa_issue_data, + )| { + orchard_shielded_data.is_some().then_some(Transaction::V6 { + network_upgrade, + lock_time, + expiry_height, + inputs, + outputs, + sapling_shielded_data, + orchard_shielded_data, + orchard_zsa_issue_data, + }) }, ) .boxed() @@ -697,7 +781,7 @@ impl Arbitrary for sapling::TransferData { type Strategy = BoxedStrategy; } -impl Arbitrary for orchard::ShieldedData { +impl Arbitrary for orchard::ShieldedData { type Parameters = (); fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy { @@ -707,13 +791,22 @@ impl Arbitrary for orchard::ShieldedData { any::(), any::(), vec( - any::(), + any::>(), 1..MAX_ARBITRARY_ITEMS, ), any::(), + #[cfg(feature = "tx-v6")] + any::(), ) - .prop_map( - |(flags, value_balance, shared_anchor, proof, actions, binding_sig)| Self { + .prop_map(|props| { + #[cfg(not(feature = "tx-v6"))] + let (flags, value_balance, shared_anchor, proof, actions, binding_sig) = props; + + #[cfg(feature = "tx-v6")] + let (flags, value_balance, shared_anchor, proof, actions, binding_sig, burn) = + props; + + Self { flags, value_balance, shared_anchor, @@ -722,8 +815,10 @@ impl Arbitrary for orchard::ShieldedData { .try_into() .expect("arbitrary vector size range produces at least one action"), binding_sig: binding_sig.0, - }, - ) + #[cfg(feature = "tx-v6")] + burn, + } + }) .boxed() } @@ -765,6 +860,8 @@ impl Arbitrary for Transaction { Some(3) => return Self::v3_strategy(ledger_state), Some(4) => return Self::v4_strategy(ledger_state), Some(5) => return Self::v5_strategy(ledger_state), + #[cfg(feature = "tx-v6")] + Some(6) => return Self::v6_strategy(ledger_state), Some(_) => unreachable!("invalid transaction version in override"), None => {} } @@ -783,6 +880,25 @@ impl Arbitrary for Transaction { Self::v5_strategy(ledger_state) ] .boxed(), + NetworkUpgrade::Nu7 => { + #[cfg(not(feature = "tx-v6"))] + { + prop_oneof![ + Self::v4_strategy(ledger_state.clone()), + Self::v5_strategy(ledger_state.clone()), + ] + .boxed() + } + #[cfg(feature = "tx-v6")] + { + prop_oneof![ + Self::v4_strategy(ledger_state.clone()), + Self::v5_strategy(ledger_state.clone()), + Self::v6_strategy(ledger_state), + ] + .boxed() + } + } } } @@ -918,6 +1034,8 @@ pub fn transaction_to_fake_v5( orchard_shielded_data: None, }, v5 @ V5 { .. } => v5.clone(), + #[cfg(feature = "tx-v6")] + _ => panic!(" other transaction versions are not supported"), } } @@ -1020,6 +1138,7 @@ pub fn transactions_from_blocks<'a>( }) } +// FIXME: make it a generic to support V6? /// Modify a V5 transaction to insert fake Orchard shielded data. /// /// Creates a fake instance of [`orchard::ShieldedData`] with one fake action. Note that both the @@ -1034,7 +1153,7 @@ pub fn transactions_from_blocks<'a>( /// Panics if the transaction to be modified is not V5. pub fn insert_fake_orchard_shielded_data( transaction: &mut Transaction, -) -> &mut orchard::ShieldedData { +) -> &mut orchard::ShieldedData { // Create a dummy action let mut runner = TestRunner::default(); let dummy_action = orchard::Action::arbitrary() @@ -1049,13 +1168,15 @@ pub fn insert_fake_orchard_shielded_data( }; // Place the dummy action inside the Orchard shielded data - let dummy_shielded_data = orchard::ShieldedData { + let dummy_shielded_data = orchard::ShieldedData:: { flags: orchard::Flags::empty(), value_balance: Amount::try_from(0).expect("invalid transaction amount"), shared_anchor: orchard::tree::Root::default(), proof: Halo2Proof(vec![]), actions: at_least_one![dummy_authorized_action], binding_sig: Signature::from([0u8; 64]), + #[cfg(feature = "tx-v6")] + burn: Default::default(), }; // Replace the shielded data in the transaction diff --git a/zebra-chain/src/transaction/serialize.rs b/zebra-chain/src/transaction/serialize.rs index 0e583efc5bf..31f3f5a70f1 100644 --- a/zebra-chain/src/transaction/serialize.rs +++ b/zebra-chain/src/transaction/serialize.rs @@ -9,8 +9,8 @@ use hex::FromHex; use reddsa::{orchard::Binding, orchard::SpendAuth, Signature}; use crate::{ - amount, block::MAX_BLOCK_BYTES, + orchard::{OrchardVanilla, ShieldedDataFlavor}, parameters::{OVERWINTER_VERSION_GROUP_ID, SAPLING_VERSION_GROUP_ID, TX_V5_VERSION_GROUP_ID}, primitives::{Halo2Proof, ZkSnarkProof}, serialization::{ @@ -20,6 +20,12 @@ use crate::{ }, }; +#[cfg(feature = "tx-v6")] +use crate::{ + orchard::OrchardZSA, orchard_zsa::NoBurn, parameters::TX_V6_VERSION_GROUP_ID, + serialization::CompactSizeMessage, +}; + use super::*; use crate::sapling; @@ -323,7 +329,10 @@ impl ZcashDeserialize for Option> { } } -impl ZcashSerialize for Option { +impl ZcashSerialize for Option> +where + orchard::ShieldedData: ZcashSerialize, +{ fn zcash_serialize(&self, mut writer: W) -> Result<(), io::Error> { match self { None => { @@ -339,14 +348,18 @@ impl ZcashSerialize for Option { orchard_shielded_data.zcash_serialize(&mut writer)?; } } + Ok(()) } } -impl ZcashSerialize for orchard::ShieldedData { +impl ZcashSerialize for orchard::ShieldedData { fn zcash_serialize(&self, mut writer: W) -> Result<(), io::Error> { // Split the AuthorizedAction - let (actions, sigs): (Vec, Vec>) = self + let (actions, sigs): ( + Vec>, + Vec>, + ) = self .actions .iter() .cloned() @@ -378,12 +391,60 @@ impl ZcashSerialize for orchard::ShieldedData { } } +#[cfg(feature = "tx-v6")] +#[allow(clippy::unwrap_in_result)] +impl ZcashSerialize for orchard::ShieldedData { + fn zcash_serialize(&self, mut writer: W) -> Result<(), io::Error> { + // Denoted as `nActionGroupsOrchard` in the spec (ZIP 230) (must be one for V6/NU7). + let csm = CompactSizeMessage::try_from(1).unwrap_or_else(|_| unreachable!()); + csm.zcash_serialize(&mut writer)?; + + // Split the AuthorizedAction + let (actions, sigs): (Vec>, Vec>) = self + .actions + .iter() + .cloned() + .map(orchard::AuthorizedAction::into_parts) + .unzip(); + + // Denoted as `nActionsOrchard` and `vActionsOrchard` in the spec. + actions.zcash_serialize(&mut writer)?; + + // Denoted as `flagsOrchard` in the spec. + self.flags.zcash_serialize(&mut writer)?; + + // Denoted as `anchorOrchard` in the spec. + self.shared_anchor.zcash_serialize(&mut writer)?; + + // Denoted as `nAGExpiryHeight` in the spec (ZIP 230) (must be zero for V6/NU7). + writer.write_u32::(0)?; + + // Denoted as `vAssetBurn` in the spec (ZIP 230). + self.burn.zcash_serialize(&mut writer)?; + + // Denoted as `sizeProofsOrchard` and `proofsOrchard` in the spec. + self.proof.zcash_serialize(&mut writer)?; + + // Denoted as `vSpendAuthSigsOrchard` in the spec. + zcash_serialize_external_count(&sigs, &mut writer)?; + + // Denoted as `valueBalanceOrchard` in the spec. + self.value_balance.zcash_serialize(&mut writer)?; + + // Denoted as `bindingSigOrchard` in the spec. + self.binding_sig.zcash_serialize(&mut writer)?; + + Ok(()) + } +} + // we can't split ShieldedData out of Option deserialization, // because the counts are read along with the arrays. -impl ZcashDeserialize for Option { +impl ZcashDeserialize for Option> { fn zcash_deserialize(mut reader: R) -> Result { // Denoted as `nActionsOrchard` and `vActionsOrchard` in the spec. - let actions: Vec = (&mut reader).zcash_deserialize_into()?; + let actions: Vec> = + (&mut reader).zcash_deserialize_into()?; // "The fields flagsOrchard, valueBalanceOrchard, anchorOrchard, sizeProofsOrchard, // proofsOrchard , and bindingSigOrchard are present if and only if nActionsOrchard > 0." @@ -406,7 +467,7 @@ impl ZcashDeserialize for Option { let flags: orchard::Flags = (&mut reader).zcash_deserialize_into()?; // Denoted as `valueBalanceOrchard` in the spec. - let value_balance: amount::Amount = (&mut reader).zcash_deserialize_into()?; + let value_balance: Amount = (&mut reader).zcash_deserialize_into()?; // Denoted as `anchorOrchard` in the spec. // Consensus: type is `{0 .. š‘ž_ā„™ āˆ’ 1}`. See [`orchard::tree::Root::zcash_deserialize`]. @@ -429,7 +490,88 @@ impl ZcashDeserialize for Option { let binding_sig: Signature = (&mut reader).zcash_deserialize_into()?; // Create the AuthorizedAction from deserialized parts - let authorized_actions: Vec = actions + let authorized_actions: Vec> = actions + .into_iter() + .zip(sigs) + .map(|(action, spend_auth_sig)| { + orchard::AuthorizedAction::from_parts(action, spend_auth_sig) + }) + .collect(); + + let actions: AtLeastOne> = + authorized_actions.try_into()?; + + Ok(Some(orchard::ShieldedData:: { + flags, + value_balance, + #[cfg(feature = "tx-v6")] + burn: NoBurn, + shared_anchor, + proof, + actions, + binding_sig, + })) + } +} + +// FIXME: Try to avoid duplication with OrchardVanilla version +// we can't split ShieldedData out of Option deserialization, +// because the counts are read along with the arrays. +#[cfg(feature = "tx-v6")] +impl ZcashDeserialize for Option> { + fn zcash_deserialize(mut reader: R) -> Result { + // Denoted as `nActionGroupsOrchard` in the spec (ZIP 230) (must be one for V6/NU7). + let n_action_groups: usize = (&mut reader) + .zcash_deserialize_into::()? + .into(); + if n_action_groups == 0 { + return Ok(None); + } else if n_action_groups != 1 { + return Err(SerializationError::Parse( + "V6 transaction must contain exactly one action group", + )); + } + + // Denoted as `nActionsOrchard` and `vActionsOrchard` in the spec. + let actions: Vec> = (&mut reader).zcash_deserialize_into()?; + + // Denoted as `flagsOrchard` in the spec. + // Consensus: type of each flag is š”¹, i.e. a bit. This is enforced implicitly + // in [`Flags::zcash_deserialized`]. + let flags: orchard::Flags = (&mut reader).zcash_deserialize_into()?; + + // Denoted as `anchorOrchard` in the spec. + // Consensus: type is `{0 .. š‘ž_ā„™ āˆ’ 1}`. See [`orchard::tree::Root::zcash_deserialize`]. + let shared_anchor: orchard::tree::Root = (&mut reader).zcash_deserialize_into()?; + + // Denoted as `nAGExpiryHeight` in the spec (ZIP 230) (must be zero for V6/NU7). + let n_ag_expiry_height = reader.read_u32::()?; + if n_ag_expiry_height != 0 { + return Err(SerializationError::Parse( + "nAGExpiryHeight must be zero for NU7", + )); + } + + // Denoted as `vAssetBurn` in the spec (ZIP 230). + let burn = (&mut reader).zcash_deserialize_into()?; + + // Denoted as `sizeProofsOrchard` and `proofsOrchard` in the spec. + // Consensus: type is `ZKAction.Proof`, i.e. a byte sequence. + // https://zips.z.cash/protocol/protocol.pdf#halo2encoding + let proof: Halo2Proof = (&mut reader).zcash_deserialize_into()?; + + // Denoted as `vSpendAuthSigsOrchard` in the spec. + let sigs: Vec> = + zcash_deserialize_external_count(actions.len(), &mut reader)?; + + // Denoted as `valueBalanceOrchard` in the spec. + let value_balance: Amount = (&mut reader).zcash_deserialize_into()?; + + // Denoted as `bindingSigOrchard` in the spec. + let binding_sig: Signature = (&mut reader).zcash_deserialize_into()?; + + // Create the AuthorizedAction from deserialized parts + let authorized_actions: Vec> = actions .into_iter() .zip(sigs) .map(|(action, spend_auth_sig)| { @@ -437,11 +579,13 @@ impl ZcashDeserialize for Option { }) .collect(); - let actions: AtLeastOne = authorized_actions.try_into()?; + let actions: AtLeastOne> = + authorized_actions.try_into()?; - Ok(Some(orchard::ShieldedData { + Ok(Some(orchard::ShieldedData:: { flags, value_balance, + burn, shared_anchor, proof, actions, @@ -672,6 +816,55 @@ impl ZcashSerialize for Transaction { // `proofsOrchard`, `vSpendAuthSigsOrchard`, and `bindingSigOrchard`. orchard_shielded_data.zcash_serialize(&mut writer)?; } + + #[cfg(feature = "tx-v6")] + Transaction::V6 { + network_upgrade, + lock_time, + expiry_height, + inputs, + outputs, + sapling_shielded_data, + orchard_shielded_data, + orchard_zsa_issue_data, + } => { + // Denoted as `nVersionGroupId` in the spec. + writer.write_u32::(TX_V6_VERSION_GROUP_ID)?; + + // Denoted as `nConsensusBranchId` in the spec. + writer.write_u32::(u32::from( + network_upgrade + .branch_id() + .expect("valid transactions must have a network upgrade with a branch id"), + ))?; + + // Denoted as `lock_time` in the spec. + lock_time.zcash_serialize(&mut writer)?; + + // Denoted as `nExpiryHeight` in the spec. + writer.write_u32::(expiry_height.0)?; + + // Denoted as `tx_in_count` and `tx_in` in the spec. + inputs.zcash_serialize(&mut writer)?; + + // Denoted as `tx_out_count` and `tx_out` in the spec. + outputs.zcash_serialize(&mut writer)?; + + // A bundle of fields denoted in the spec as `nSpendsSapling`, `vSpendsSapling`, + // `nOutputsSapling`,`vOutputsSapling`, `valueBalanceSapling`, `anchorSapling`, + // `vSpendProofsSapling`, `vSpendAuthSigsSapling`, `vOutputProofsSapling` and + // `bindingSigSapling`. + sapling_shielded_data.zcash_serialize(&mut writer)?; + + // A bundle of fields denoted in the spec as `nActionsOrchard`, `vActionsOrchard`, + // `flagsOrchard`,`valueBalanceOrchard`, `anchorOrchard`, `sizeProofsOrchard`, + // `proofsOrchard`, `vSpendAuthSigsOrchard`, and `bindingSigOrchard`. + orchard_shielded_data.zcash_serialize(&mut writer)?; + + // A bundle of OrchardZSA issuance fields denoted in the spec as `nIssueActions`, + // `vIssueActions`, `ik`, and `issueAuthSig`. + orchard_zsa_issue_data.zcash_serialize(&mut writer)?; + } } Ok(()) } @@ -928,6 +1121,63 @@ impl ZcashDeserialize for Transaction { orchard_shielded_data, }) } + #[cfg(feature = "tx-v6")] + (6, true) => { + // Transaction V6 spec: + // https://zips.z.cash/zip-0230#transaction-format + + // Denoted as `nVersionGroupId` in the spec. + let id = limited_reader.read_u32::()?; + if id != TX_V6_VERSION_GROUP_ID { + return Err(SerializationError::Parse("expected TX_V6_VERSION_GROUP_ID")); + } + // Denoted as `nConsensusBranchId` in the spec. + // Convert it to a NetworkUpgrade + let network_upgrade = + NetworkUpgrade::from_branch_id(limited_reader.read_u32::()?) + .ok_or_else(|| { + SerializationError::Parse( + "expected a valid network upgrade from the consensus branch id", + ) + })?; + + // Denoted as `lock_time` in the spec. + let lock_time = LockTime::zcash_deserialize(&mut limited_reader)?; + + // Denoted as `nExpiryHeight` in the spec. + let expiry_height = block::Height(limited_reader.read_u32::()?); + + // Denoted as `tx_in_count` and `tx_in` in the spec. + let inputs = Vec::zcash_deserialize(&mut limited_reader)?; + + // Denoted as `tx_out_count` and `tx_out` in the spec. + let outputs = Vec::zcash_deserialize(&mut limited_reader)?; + + // A bundle of fields denoted in the spec as `nSpendsSapling`, `vSpendsSapling`, + // `nOutputsSapling`,`vOutputsSapling`, `valueBalanceSapling`, `anchorSapling`, + // `vSpendProofsSapling`, `vSpendAuthSigsSapling`, `vOutputProofsSapling` and + // `bindingSigSapling`. + let sapling_shielded_data = (&mut limited_reader).zcash_deserialize_into()?; + + // A bundle of fields denoted in the spec as `nActionsOrchard`, `vActionsOrchard`, + // `flagsOrchard`,`valueBalanceOrchard`, `anchorOrchard`, `sizeProofsOrchard`, + // `proofsOrchard`, `vSpendAuthSigsOrchard`, and `bindingSigOrchard`. + let orchard_shielded_data = (&mut limited_reader).zcash_deserialize_into()?; + + // OrchardZSA Issuance Fields. + let orchard_zsa_issue_data = (&mut limited_reader).zcash_deserialize_into()?; + + Ok(Transaction::V6 { + network_upgrade, + lock_time, + expiry_height, + inputs, + outputs, + sapling_shielded_data, + orchard_shielded_data, + orchard_zsa_issue_data, + }) + } (_, _) => Err(SerializationError::Parse("bad tx header")), } } diff --git a/zebra-chain/src/transaction/tests/vectors.rs b/zebra-chain/src/transaction/tests/vectors.rs index 66d5009ed05..00bb81a783a 100644 --- a/zebra-chain/src/transaction/tests/vectors.rs +++ b/zebra-chain/src/transaction/tests/vectors.rs @@ -30,6 +30,18 @@ lazy_static! { sapling_shielded_data: None, orchard_shielded_data: None, }; + + #[cfg(feature = "tx-v6")] + pub static ref EMPTY_V6_TX: Transaction = Transaction::V6 { + network_upgrade: NetworkUpgrade::Nu7, + lock_time: LockTime::min_lock_time_timestamp(), + expiry_height: block::Height(0), + inputs: Vec::new(), + outputs: Vec::new(), + sapling_shielded_data: None, + orchard_shielded_data: None, + orchard_zsa_issue_data: None + }; } /// Build a mock output list for pre-V5 transactions, with (index+1) @@ -257,18 +269,9 @@ fn deserialize_large_transaction() { .expect_err("transaction should not deserialize due to its size"); } -// Transaction V5 test vectors - -/// An empty transaction v5, with no Orchard, Sapling, or Transparent data -/// -/// empty transaction are invalid, but Zebra only checks this rule in -/// zebra_consensus::transaction::Verifier -#[test] -fn empty_v5_round_trip() { +fn tx_round_trip(tx: &Transaction) { let _init_guard = zebra_test::init(); - let tx: &Transaction = &EMPTY_V5_TX; - let data = tx.zcash_serialize_to_vec().expect("tx should serialize"); let tx2: &Transaction = &data .zcash_deserialize_into() @@ -314,18 +317,47 @@ fn empty_v4_round_trip() { assert_eq!(data, data2, "data must be equal if structs are equal"); } -/// Check if an empty V5 transaction can be deserialized by librustzcash too. +/// An empty transaction v5, with no Orchard, Sapling, or Transparent data +/// +/// empty transaction are invalid, but Zebra only checks this rule in +/// zebra_consensus::transaction::Verifier #[test] -fn empty_v5_librustzcash_round_trip() { +fn empty_v5_round_trip() { + tx_round_trip(&EMPTY_V5_TX) +} + +#[cfg(feature = "tx-v6")] +/// An empty transaction v6, with no Orchard/OrchardZSA, Sapling, or Transparent data +/// +/// empty transaction are invalid, but Zebra only checks this rule in +/// zebra_consensus::transaction::Verifier +#[test] +fn empty_v6_round_trip() { + tx_round_trip(&EMPTY_V6_TX) +} + +fn tx_librustzcash_round_trip(tx: &Transaction) { let _init_guard = zebra_test::init(); - let tx: &Transaction = &EMPTY_V5_TX; let _alt_tx: zcash_primitives::transaction::Transaction = tx.try_into().expect( "librustzcash deserialization might work for empty zebra serialized transactions. \ Hint: if empty transactions fail, but other transactions work, delete this test", ); } +/// Check if an empty V5 transaction can be deserialized by librustzcash too. +#[test] +fn empty_v5_librustzcash_round_trip() { + tx_librustzcash_round_trip(&EMPTY_V5_TX); +} + +#[cfg(feature = "tx-v6")] +/// Check if an empty V6 transaction can be deserialized by librustzcash too. +#[test] +fn empty_v6_librustzcash_round_trip() { + tx_librustzcash_round_trip(&EMPTY_V6_TX); +} + /// Do a round-trip test on fake v5 transactions created from v4 transactions /// in the block test vectors. /// @@ -450,6 +482,54 @@ fn fake_v5_round_trip_for_network(network: Network) { } } +#[cfg(feature = "tx-v6")] +/// Do a serialization round-trip on OrchardZSA workflow blocks and their V6 +/// transactions. +#[test] +fn v6_round_trip() { + use zebra_test::vectors::ORCHARD_ZSA_WORKFLOW_BLOCKS; + + let _init_guard = zebra_test::init(); + + for block_bytes in ORCHARD_ZSA_WORKFLOW_BLOCKS.iter() { + let block = block_bytes + .zcash_deserialize_into::() + .expect("block is structurally valid"); + + // test full blocks + let block_bytes2 = block + .zcash_serialize_to_vec() + .expect("vec serialization is infallible"); + + assert_eq!( + block_bytes, &block_bytes2, + "data must be equal if structs are equal" + ); + + // test each transaction + for tx in &block.transactions { + let tx_bytes = tx + .zcash_serialize_to_vec() + .expect("vec serialization is infallible"); + + let tx2 = tx_bytes + .zcash_deserialize_into::() + .expect("tx is structurally valid"); + + assert_eq!(tx.as_ref(), &tx2); + + let tx_bytes2 = tx2 + .zcash_serialize_to_vec() + .expect("vec serialization is infallible"); + + assert_eq!( + tx_bytes, tx_bytes2, + "data must be equal if structs are equal" + ); + } + } +} + #[test] fn invalid_orchard_nullifier() { let _init_guard = zebra_test::init(); @@ -549,6 +629,34 @@ fn fake_v5_librustzcash_round_trip_for_network(network: Network) { } } +#[cfg(feature = "tx-v6")] +/// Confirms each V6 transaction in the OrchardZSA test blocks converts to librustzcash’s +/// transaction type without error. +#[test] +fn v6_librustzcash_tx_conversion() { + use zebra_test::vectors::ORCHARD_ZSA_WORKFLOW_BLOCKS; + + let _init_guard = zebra_test::init(); + + for block_bytes in ORCHARD_ZSA_WORKFLOW_BLOCKS.iter() { + let block = block_bytes + .zcash_deserialize_into::() + .expect("block is structurally valid"); + + // Test each V6 transaction + for tx in block + .transactions + .iter() + .filter(|tx| matches!(tx.as_ref(), &Transaction::V6 { .. })) + { + let _alt_tx: zcash_primitives::transaction::Transaction = tx + .as_ref() + .try_into() + .expect("librustzcash conversion must work for zebra transactions"); + } + } +} + #[test] fn zip244_round_trip() -> Result<()> { let _init_guard = zebra_test::init(); @@ -988,7 +1096,7 @@ fn binding_signatures_for_network(network: Network) { .expect("must pass verification"); } } - Transaction::V5 { + tx_v5_and_v6! { sapling_shielded_data, .. } => { diff --git a/zebra-chain/src/transaction/txid.rs b/zebra-chain/src/transaction/txid.rs index f67f6dee58d..2b1f9d5b050 100644 --- a/zebra-chain/src/transaction/txid.rs +++ b/zebra-chain/src/transaction/txid.rs @@ -2,7 +2,7 @@ //! from the transaction. use std::io; -use super::{Hash, Transaction}; +use super::{tx_v5_and_v6, Hash, Transaction}; use crate::serialization::{sha256d, ZcashSerialize}; /// A Transaction ID builder. It computes the transaction ID by hashing @@ -28,7 +28,7 @@ impl<'a> TxIdBuilder<'a> { | Transaction::V2 { .. } | Transaction::V3 { .. } | Transaction::V4 { .. } => self.txid_v1_to_v4(), - Transaction::V5 { .. } => self.txid_v5(), + tx_v5_and_v6! { .. } => self.txid_v5_to_v6(), } } @@ -43,10 +43,10 @@ impl<'a> TxIdBuilder<'a> { Ok(Hash(hash_writer.finish())) } - /// Compute the Transaction ID for a V5 transaction in the given network upgrade. + /// Compute the Transaction ID for transactions V5 to V6. /// In this case it's the hash of a tree of hashes of specific parts of the - /// transaction, as specified in ZIP-244 and ZIP-225. - fn txid_v5(self) -> Result { + /// transaction, as specified in ZIP-244 and ZIP-225 for Txv5 and ZIP-246 for TxV6. + fn txid_v5_to_v6(self) -> Result { // The v5 txid (from ZIP-244) is computed using librustzcash. Convert the zebra // transaction to a librustzcash transaction. let alt_tx: zcash_primitives::transaction::Transaction = self.trans.try_into()?; diff --git a/zebra-chain/src/transaction/unmined.rs b/zebra-chain/src/transaction/unmined.rs index da716573e8b..ce327ae9f76 100644 --- a/zebra-chain/src/transaction/unmined.rs +++ b/zebra-chain/src/transaction/unmined.rs @@ -21,7 +21,7 @@ use crate::{ amount::{Amount, NonNegative}, serialization::ZcashSerialize, transaction::{ - AuthDigest, Hash, + tx_v5_and_v6, AuthDigest, Hash, Transaction::{self, *}, WtxId, }, @@ -140,7 +140,7 @@ impl From<&Transaction> for UnminedTxId { fn from(transaction: &Transaction) -> Self { match transaction { V1 { .. } | V2 { .. } | V3 { .. } | V4 { .. } => Legacy(transaction.into()), - V5 { .. } => Witnessed(transaction.into()), + tx_v5_and_v6! { .. } => Witnessed(transaction.into()), } } } diff --git a/zebra-chain/src/transaction/unmined/zip317.rs b/zebra-chain/src/transaction/unmined/zip317.rs index b9f04ec7597..dc6c8277378 100644 --- a/zebra-chain/src/transaction/unmined/zip317.rs +++ b/zebra-chain/src/transaction/unmined/zip317.rs @@ -153,7 +153,7 @@ pub fn conventional_actions(transaction: &Transaction) -> u32 { let n_join_split = transaction.joinsplit_count(); let n_spends_sapling = transaction.sapling_spends_per_anchor().count(); let n_outputs_sapling = transaction.sapling_outputs().count(); - let n_actions_orchard = transaction.orchard_actions().count(); + let n_actions_orchard = transaction.orchard_action_count(); let tx_in_logical_actions = div_ceil(tx_in_total_size, P2PKH_STANDARD_INPUT_SIZE); let tx_out_logical_actions = div_ceil(tx_out_total_size, P2PKH_STANDARD_OUTPUT_SIZE); diff --git a/zebra-consensus/Cargo.toml b/zebra-consensus/Cargo.toml index 2dd58ed562d..3fa5de04d05 100644 --- a/zebra-consensus/Cargo.toml +++ b/zebra-consensus/Cargo.toml @@ -34,6 +34,12 @@ getblocktemplate-rpcs = [ # Test-only features proptest-impl = ["proptest", "proptest-derive", "zebra-chain/proptest-impl", "zebra-state/proptest-impl"] +# Support for transaction version 6 +tx-v6 = [ + "zebra-state/tx-v6", + "zebra-chain/tx-v6" +] + [dependencies] blake2b_simd = "1.0.2" bellman = "0.14.0" diff --git a/zebra-consensus/src/block/check.rs b/zebra-consensus/src/block/check.rs index 24ef2ba2ed1..eaf193cda10 100644 --- a/zebra-consensus/src/block/check.rs +++ b/zebra-consensus/src/block/check.rs @@ -272,6 +272,7 @@ pub fn miner_fees_are_valid( // input. // // > [NU6 onward] The total output of a coinbase transaction MUST be equal to its total input. + // FIXME: Would this work after Nu7 activation? if if NetworkUpgrade::current(network, height) < NetworkUpgrade::Nu6 { total_output_value > total_input_value } else { diff --git a/zebra-consensus/src/checkpoint/list/tests.rs b/zebra-consensus/src/checkpoint/list/tests.rs index 5a2fe803f3d..698eaf36082 100644 --- a/zebra-consensus/src/checkpoint/list/tests.rs +++ b/zebra-consensus/src/checkpoint/list/tests.rs @@ -237,7 +237,7 @@ fn checkpoint_list_load_hard_coded() -> Result<(), BoxError> { let _ = Mainnet.checkpoint_list(); let _ = Network::new_default_testnet().checkpoint_list(); - let _ = Network::new_regtest(None, None).checkpoint_list(); + let _ = Network::new_regtest(None, None, None).checkpoint_list(); Ok(()) } diff --git a/zebra-consensus/src/lib.rs b/zebra-consensus/src/lib.rs index 95381fd9e07..d2e0eb46357 100644 --- a/zebra-consensus/src/lib.rs +++ b/zebra-consensus/src/lib.rs @@ -65,3 +65,5 @@ pub use router::RouterError; /// A boxed [`std::error::Error`]. pub type BoxError = Box; + +mod orchard_zsa; diff --git a/zebra-consensus/src/orchard_zsa.rs b/zebra-consensus/src/orchard_zsa.rs new file mode 100644 index 00000000000..87c2771955a --- /dev/null +++ b/zebra-consensus/src/orchard_zsa.rs @@ -0,0 +1,2 @@ +#[cfg(test)] +mod tests; diff --git a/zebra-consensus/src/orchard_zsa/tests.rs b/zebra-consensus/src/orchard_zsa/tests.rs new file mode 100644 index 00000000000..237e73b2983 --- /dev/null +++ b/zebra-consensus/src/orchard_zsa/tests.rs @@ -0,0 +1,61 @@ +//! Simulates a full Zebra node’s block‐processing pipeline on a predefined Orchard/ZSA workflow. +//! +//! This integration test reads a sequence of serialized regtest blocks (including Orchard burns +//! and ZSA issuance), feeds them through the node’s deserialization, consensus router, and state +//! service exactly as if they arrived from the network, and verifies that each block is accepted +//! (or fails at the injected point). +//! +//! In a future PR, we will add tracking and verification of issuance/burn state changes so that +//! the test can also assert that on-chain asset state (total supply and finalization flags) +//! matches the expected values computed in memory. +//! +//! In short, it demonstrates end-to-end handling of Orchard asset burns and ZSA issuance through +//! consensus (with state verification to follow in the next PR). + +use std::sync::Arc; + +use color_eyre::eyre::Report; + +use zebra_chain::{ + block::{genesis::regtest_genesis_block, Block, Hash}, + parameters::Network, + serialization::ZcashDeserialize, +}; + +use zebra_test::{ + transcript::{ExpectedTranscriptError, Transcript}, + vectors::ORCHARD_ZSA_WORKFLOW_BLOCKS, +}; + +use crate::{block::Request, Config}; + +fn create_transcript_data() -> impl Iterator)> +{ + let workflow_blocks = ORCHARD_ZSA_WORKFLOW_BLOCKS.iter().map(|block_bytes| { + Arc::new(Block::zcash_deserialize(&block_bytes[..]).expect("block should deserialize")) + }); + + std::iter::once(regtest_genesis_block()) + .chain(workflow_blocks) + .map(|block| (Request::Commit(block.clone()), Ok(block.hash()))) +} + +#[tokio::test(flavor = "multi_thread")] +async fn check_zsa_workflow() -> Result<(), Report> { + let _init_guard = zebra_test::init(); + + let network = Network::new_regtest(Some(1), Some(1), Some(1)); + + let state_service = zebra_state::init_test(&network); + + let ( + block_verifier_router, + _transaction_verifier, + _groth16_download_handle, + _max_checkpoint_height, + ) = crate::router::init(Config::default(), &network, state_service.clone()).await; + + Transcript::from(create_transcript_data()) + .check(block_verifier_router.clone()) + .await +} diff --git a/zebra-consensus/src/primitives/halo2.rs b/zebra-consensus/src/primitives/halo2.rs index ffc58a5feb8..b5f665bc5ca 100644 --- a/zebra-consensus/src/primitives/halo2.rs +++ b/zebra-consensus/src/primitives/halo2.rs @@ -19,6 +19,11 @@ use tower::{util::ServiceFn, Service}; use tower_batch_control::{Batch, BatchControl}; use tower_fallback::Fallback; +use zebra_chain::orchard::{OrchardVanilla, ShieldedData, ShieldedDataFlavor}; + +#[cfg(feature = "tx-v6")] +use zebra_chain::orchard::OrchardZSA; + use crate::BoxError; use super::{spawn_fifo, spawn_fifo_and_convert}; @@ -74,8 +79,14 @@ pub type BatchVerifyingKey = ItemVerifyingKey; pub type ItemVerifyingKey = VerifyingKey; lazy_static::lazy_static! { - /// The halo2 proof verifying key. - pub static ref VERIFYING_KEY: ItemVerifyingKey = ItemVerifyingKey::build(); + /// The halo2 proof verifying key for Orchard Vanilla + pub static ref VERIFYING_KEY_VANILLA: ItemVerifyingKey = ItemVerifyingKey::build::(); +} + +#[cfg(feature = "tx-v6")] +lazy_static::lazy_static! { + /// The halo2 proof verifying key for OrchardZSA + pub static ref VERIFYING_KEY_ZSA: ItemVerifyingKey = ItemVerifyingKey::build::(); } // === TEMPORARY BATCH HALO2 SUBSTITUTE === @@ -130,18 +141,14 @@ impl BatchVerifier { // === END TEMPORARY BATCH HALO2 SUBSTITUTE === -impl From<&zebra_chain::orchard::ShieldedData> for Item { - fn from(shielded_data: &zebra_chain::orchard::ShieldedData) -> Item { +impl From<&ShieldedData> for Item { + fn from(shielded_data: &ShieldedData) -> Item { use orchard::{circuit, note, primitives::redpallas, tree, value}; let anchor = tree::Anchor::from_bytes(shielded_data.shared_anchor.into()).unwrap(); - let enable_spend = shielded_data - .flags - .contains(zebra_chain::orchard::Flags::ENABLE_SPENDS); - let enable_output = shielded_data - .flags - .contains(zebra_chain::orchard::Flags::ENABLE_OUTPUTS); + let flags = orchard::bundle::Flags::from_byte(shielded_data.flags.bits()) + .expect("failed to convert flags: shielded_data.flags contains unexpected bits that are not valid in orchard::bundle::Flags"); let instances = shielded_data .actions() @@ -155,8 +162,7 @@ impl From<&zebra_chain::orchard::ShieldedData> for Item { )) .expect("should be a valid redpallas spendauth verification key"), note::ExtractedNoteCommitment::from_bytes(&action.cm_x.into()).unwrap(), - enable_spend, - enable_output, + flags, ) }) .collect(); @@ -193,23 +199,22 @@ impl From for Halo2Error { } } -/// Global batch verification context for Halo2 proofs of Action statements. -/// -/// This service transparently batches contemporaneous proof verifications, -/// handling batch failures by falling back to individual verification. -/// -/// Note that making a `Service` call requires mutable access to the service, so -/// you should call `.clone()` on the global handle to create a local, mutable -/// handle. -pub static VERIFIER: Lazy< - Fallback< - Batch, - ServiceFn BoxFuture<'static, Result<(), BoxError>>>, - >, -> = Lazy::new(|| { +type VerificationContext = Fallback< + Batch, + ServiceFn BoxFuture<'static, Result<(), BoxError>>>, +>; + +pub(crate) trait OrchardVerifier: ShieldedDataFlavor { + const ZSA_ENABLED: bool; + + fn get_verifying_key() -> &'static ItemVerifyingKey; + fn get_verifier() -> &'static VerificationContext; +} + +fn create_verification_context() -> VerificationContext { Fallback::new( Batch::new( - Verifier::new(&VERIFYING_KEY), + Verifier::new(V::get_verifying_key()), HALO2_MAX_BATCH_SIZE, None, super::MAX_BATCH_LATENCY, @@ -224,11 +229,52 @@ pub static VERIFIER: Lazy< // to erase the result type. // (We can't use BoxCloneService to erase the service type, because it is !Sync.) tower::service_fn( - (|item: Item| Verifier::verify_single_spawning(item, &VERIFYING_KEY).boxed()) + (|item: Item| Verifier::verify_single_spawning(item, V::get_verifying_key()).boxed()) as fn(_) -> _, ), ) -}); +} + +/// Global batch verification context for Halo2 proofs of Action statements. +/// +/// This service transparently batches contemporaneous proof verifications, +/// handling batch failures by falling back to individual verification. +/// +/// Note that making a `Service` call requires mutable access to the service, so +/// you should call `.clone()` on the global handle to create a local, mutable +/// handle. +pub static VERIFIER_VANILLA: Lazy = + Lazy::new(create_verification_context::); + +/// FIXME: copy a doc from VERIFIER_VANILLA or just refer to its doc? +#[cfg(feature = "tx-v6")] +pub static VERIFIER_ZSA: Lazy = + Lazy::new(create_verification_context::); + +impl OrchardVerifier for OrchardVanilla { + const ZSA_ENABLED: bool = false; + + fn get_verifying_key() -> &'static ItemVerifyingKey { + &VERIFYING_KEY_VANILLA + } + + fn get_verifier() -> &'static VerificationContext { + &VERIFIER_VANILLA + } +} + +#[cfg(feature = "tx-v6")] +impl OrchardVerifier for OrchardZSA { + const ZSA_ENABLED: bool = true; + + fn get_verifying_key() -> &'static ItemVerifyingKey { + &VERIFYING_KEY_ZSA + } + + fn get_verifier() -> &'static VerificationContext { + &VERIFIER_ZSA + } +} /// Halo2 proof verifier implementation /// diff --git a/zebra-consensus/src/primitives/halo2/tests.rs b/zebra-consensus/src/primitives/halo2/tests.rs index e654adcc546..beb644a0c1f 100644 --- a/zebra-consensus/src/primitives/halo2/tests.rs +++ b/zebra-consensus/src/primitives/halo2/tests.rs @@ -11,34 +11,39 @@ use orchard::{ bundle::Flags, circuit::ProvingKey, keys::{FullViewingKey, Scope, SpendingKey}, + note::AssetBase, value::NoteValue, Anchor, Bundle, }; use rand::rngs::OsRng; use zebra_chain::{ - orchard::ShieldedData, - serialization::{ZcashDeserializeInto, ZcashSerialize}, + orchard::{ShieldedData, ShieldedDataFlavor}, + serialization::{ZcashDeserialize, ZcashDeserializeInto, ZcashSerialize}, }; use crate::primitives::halo2::*; #[allow(dead_code, clippy::print_stdout)] -fn generate_test_vectors() { - let proving_key = ProvingKey::build(); +fn generate_test_vectors() +where + ShieldedData: ZcashSerialize, +{ + let proving_key = ProvingKey::build::(); let rng = OsRng; let sk = SpendingKey::from_bytes([7; 32]).unwrap(); let recipient = FullViewingKey::from(&sk).address_at(0u32, Scope::External); + // FIXME: Add ENABLE_ZSA for OrchardZSA? let flags = zebra_chain::orchard::Flags::ENABLE_SPENDS | zebra_chain::orchard::Flags::ENABLE_OUTPUTS; let anchor_bytes = [0; 32]; let note_value = 10; - let shielded_data: Vec = (1..=4) + let shielded_data: Vec> = (1..=4) .map(|num_recipients| { let mut builder = Builder::new( BundleType::Transactional { @@ -50,11 +55,18 @@ fn generate_test_vectors() { for _ in 0..num_recipients { builder - .add_output(None, recipient, NoteValue::from_raw(note_value), None) + .add_output( + None, + recipient, + NoteValue::from_raw(note_value), + // FIXME: Use another AssetBase for OrchardZSA? + AssetBase::native(), + None, + ) .unwrap(); } - let bundle: Bundle<_, i64> = builder.build(rng).unwrap().unwrap().0; + let bundle: Bundle<_, i64, Flavor> = builder.build(rng).unwrap().0; let bundle = bundle .create_proof(&proving_key, rng) @@ -62,7 +74,7 @@ fn generate_test_vectors() { .apply_signatures(rng, [0; 32], &[]) .unwrap(); - zebra_chain::orchard::ShieldedData { + ShieldedData:: { flags, value_balance: note_value.try_into().unwrap(), shared_anchor: anchor_bytes.try_into().unwrap(), @@ -73,13 +85,18 @@ fn generate_test_vectors() { .actions() .iter() .map(|a| { - let action = zebra_chain::orchard::Action { + let action = zebra_chain::orchard::Action:: { cv: a.cv_net().to_bytes().try_into().unwrap(), nullifier: a.nullifier().to_bytes().try_into().unwrap(), rk: <[u8; 32]>::from(a.rk()).into(), cm_x: pallas::Base::from_repr(a.cmx().into()).unwrap(), ephemeral_key: a.encrypted_note().epk_bytes.try_into().unwrap(), - enc_ciphertext: a.encrypted_note().enc_ciphertext.into(), + enc_ciphertext: a + .encrypted_note() + .enc_ciphertext + .as_ref() + .try_into() + .unwrap(), out_ciphertext: a.encrypted_note().out_ciphertext.into(), }; zebra_chain::orchard::shielded_data::AuthorizedAction { @@ -91,6 +108,8 @@ fn generate_test_vectors() { .try_into() .unwrap(), binding_sig: <[u8; 64]>::from(bundle.authorization().binding_signature()).into(), + #[cfg(feature = "tx-v6")] + burn: bundle.burn().as_slice().into(), } }) .collect(); @@ -103,9 +122,9 @@ fn generate_test_vectors() { } } -async fn verify_orchard_halo2_proofs( +async fn verify_orchard_halo2_proofs( verifier: &mut V, - shielded_data: Vec, + shielded_data: Vec>, ) -> Result<(), V::Error> where V: tower::Service, @@ -129,16 +148,17 @@ where Ok(()) } -#[tokio::test(flavor = "multi_thread")] -async fn verify_generated_halo2_proofs() { +async fn verify_generated_halo2_proofs(shielded_data_test_vectors: &[&[u8]]) +where + Option>: ZcashDeserialize, +{ let _init_guard = zebra_test::init(); // These test vectors are generated by `generate_test_vectors()` function. - let shielded_data = zebra_test::vectors::ORCHARD_SHIELDED_DATA - .clone() + let shielded_data = shielded_data_test_vectors .iter() .map(|bytes| { - let maybe_shielded_data: Option = bytes + let maybe_shielded_data: Option> = bytes .zcash_deserialize_into() .expect("a valid orchard::ShieldedData instance"); maybe_shielded_data.unwrap() @@ -148,14 +168,18 @@ async fn verify_generated_halo2_proofs() { // Use separate verifier so shared batch tasks aren't killed when the test ends (#2390) let mut verifier = Fallback::new( Batch::new( - Verifier::new(&VERIFYING_KEY), + Verifier::new(V::get_verifying_key()), crate::primitives::MAX_BATCH_SIZE, None, crate::primitives::MAX_BATCH_LATENCY, ), tower::service_fn( - (|item: Item| ready(item.verify_single(&VERIFYING_KEY).map_err(Halo2Error::from))) - as fn(_) -> _, + (|item: Item| { + ready( + item.verify_single(V::get_verifying_key()) + .map_err(Halo2Error::from), + ) + }) as fn(_) -> _, ), ); @@ -165,9 +189,22 @@ async fn verify_generated_halo2_proofs() { .is_ok()); } -async fn verify_invalid_orchard_halo2_proofs( +#[tokio::test(flavor = "multi_thread")] +async fn verify_generated_halo2_proofs_vanilla() { + verify_generated_halo2_proofs::(&zebra_test::vectors::ORCHARD_SHIELDED_DATA) + .await +} + +#[cfg(feature = "tx-v6")] +#[tokio::test(flavor = "multi_thread")] +async fn verify_generated_halo2_proofs_zsa() { + verify_generated_halo2_proofs::(&zebra_test::vectors::ORCHARD_ZSA_SHIELDED_DATA) + .await +} + +async fn verify_invalid_orchard_halo2_proofs( verifier: &mut V, - shielded_data: Vec, + shielded_data: Vec>, ) -> Result<(), V::Error> where V: tower::Service, @@ -180,6 +217,7 @@ where sd.flags.remove(zebra_chain::orchard::Flags::ENABLE_SPENDS); sd.flags.remove(zebra_chain::orchard::Flags::ENABLE_OUTPUTS); + // FIXME: What about zebra_chain::orchard::Flags::ENABLE_ZSA? tracing::trace!(?sd); @@ -196,16 +234,18 @@ where Ok(()) } -#[tokio::test(flavor = "multi_thread")] -async fn correctly_err_on_invalid_halo2_proofs() { +async fn correctly_err_on_invalid_halo2_proofs( + shielded_data_test_vectors: &[&[u8]], +) where + Option>: ZcashDeserialize, +{ let _init_guard = zebra_test::init(); // These test vectors are generated by `generate_test_vectors()` function. - let shielded_data = zebra_test::vectors::ORCHARD_SHIELDED_DATA - .clone() + let shielded_data = shielded_data_test_vectors .iter() .map(|bytes| { - let maybe_shielded_data: Option = bytes + let maybe_shielded_data: Option> = bytes .zcash_deserialize_into() .expect("a valid orchard::ShieldedData instance"); maybe_shielded_data.unwrap() @@ -215,14 +255,18 @@ async fn correctly_err_on_invalid_halo2_proofs() { // Use separate verifier so shared batch tasks aren't killed when the test ends (#2390) let mut verifier = Fallback::new( Batch::new( - Verifier::new(&VERIFYING_KEY), + Verifier::new(V::get_verifying_key()), crate::primitives::MAX_BATCH_SIZE, None, crate::primitives::MAX_BATCH_LATENCY, ), tower::service_fn( - (|item: Item| ready(item.verify_single(&VERIFYING_KEY).map_err(Halo2Error::from))) - as fn(_) -> _, + (|item: Item| { + ready( + item.verify_single(V::get_verifying_key()) + .map_err(Halo2Error::from), + ) + }) as fn(_) -> _, ), ); @@ -233,3 +277,20 @@ async fn correctly_err_on_invalid_halo2_proofs() { .is_err() ); } + +#[tokio::test(flavor = "multi_thread")] +async fn correctly_err_on_invalid_halo2_proofs_vanilla() { + correctly_err_on_invalid_halo2_proofs::( + &zebra_test::vectors::ORCHARD_SHIELDED_DATA, + ) + .await +} + +#[cfg(feature = "tx-v6")] +#[tokio::test(flavor = "multi_thread")] +async fn correctly_err_on_invalid_halo2_proofs_zsa() { + correctly_err_on_invalid_halo2_proofs::( + &zebra_test::vectors::ORCHARD_ZSA_SHIELDED_DATA, + ) + .await +} diff --git a/zebra-consensus/src/primitives/halo2/tests/vectors.rs b/zebra-consensus/src/primitives/halo2/tests/vectors.rs index a31aa8197ea..f22c4a7b6af 100644 --- a/zebra-consensus/src/primitives/halo2/tests/vectors.rs +++ b/zebra-consensus/src/primitives/halo2/tests/vectors.rs @@ -1,5 +1,5 @@ //! Test vectors for ingesting and verifying Halo2 proofs from zebra-chain::orchard::ShieldedData -mod orchard_shielded_data; +mod orchard_vanilla_shielded_data; -pub use orchard_shielded_data::ORCHARD_SHIELDED_DATA; +pub use orchard_vanilla_shielded_data::ORCHARD_VANILLA_SHIELDED_DATA; diff --git a/zebra-consensus/src/transaction.rs b/zebra-consensus/src/transaction.rs index 1c303003615..6283da8830c 100644 --- a/zebra-consensus/src/transaction.rs +++ b/zebra-consensus/src/transaction.rs @@ -405,7 +405,21 @@ where sapling_shielded_data, orchard_shielded_data, .. - } => Self::verify_v5_transaction( + } + => Self::verify_v5_and_v6_transaction( + &req, + &network, + script_verifier, + cached_ffi_transaction.clone(), + sapling_shielded_data, + orchard_shielded_data, + )?, + #[cfg(feature = "tx-v6")] + Transaction::V6 { + sapling_shielded_data, + orchard_shielded_data, + .. + } => Self::verify_v5_and_v6_transaction( &req, &network, script_verifier, @@ -413,6 +427,7 @@ where sapling_shielded_data, orchard_shielded_data, )?, + }; if let Some(unmined_tx) = req.mempool_transaction() { @@ -488,6 +503,25 @@ where } } +trait OrchardTransaction { + const SUPPORTED_NETWORK_UPGRADES: &'static [NetworkUpgrade]; +} + +impl OrchardTransaction for orchard::OrchardVanilla { + // FIXME: is this a correct set of Nu values? + const SUPPORTED_NETWORK_UPGRADES: &'static [NetworkUpgrade] = &[ + NetworkUpgrade::Nu5, + NetworkUpgrade::Nu6, + #[cfg(feature = "tx-v6")] + NetworkUpgrade::Nu7, + ]; +} + +#[cfg(feature = "tx-v6")] +impl OrchardTransaction for orchard::OrchardZSA { + const SUPPORTED_NETWORK_UPGRADES: &'static [NetworkUpgrade] = &[NetworkUpgrade::Nu7]; +} + impl Verifier where ZS: Service + Send + Clone + 'static, @@ -678,7 +712,8 @@ where | NetworkUpgrade::Heartwood | NetworkUpgrade::Canopy | NetworkUpgrade::Nu5 - | NetworkUpgrade::Nu6 => Ok(()), + | NetworkUpgrade::Nu6 + | NetworkUpgrade::Nu7 => Ok(()), // Does not support V4 transactions NetworkUpgrade::Genesis @@ -690,7 +725,7 @@ where } } - /// Verify a V5 transaction. + /// Verify a V5/V6 transaction. /// /// Returns a set of asynchronous checks that must all succeed for the transaction to be /// considered valid. These checks include: @@ -710,18 +745,18 @@ where /// - the sapling shielded data of the transaction, if any /// - the orchard shielded data of the transaction, if any #[allow(clippy::unwrap_in_result)] - fn verify_v5_transaction( + fn verify_v5_and_v6_transaction( request: &Request, network: &Network, script_verifier: script::Verifier, cached_ffi_transaction: Arc, sapling_shielded_data: &Option>, - orchard_shielded_data: &Option, + orchard_shielded_data: &Option>, ) -> Result { let transaction = request.transaction(); let upgrade = request.upgrade(network); - Self::verify_v5_transaction_network_upgrade(&transaction, upgrade)?; + Self::verify_v5_and_v6_transaction_network_upgrade::(&transaction, upgrade)?; let shielded_sighash = transaction.sighash( upgrade @@ -748,13 +783,20 @@ where )?)) } - /// Verifies if a V5 `transaction` is supported by `network_upgrade`. - fn verify_v5_transaction_network_upgrade( + /// Verifies if a V5/V6 `transaction` is supported by `network_upgrade`. + fn verify_v5_and_v6_transaction_network_upgrade< + V: primitives::halo2::OrchardVerifier + OrchardTransaction, + >( transaction: &Transaction, network_upgrade: NetworkUpgrade, ) -> Result<(), TransactionError> { - match network_upgrade { - // Supports V5 transactions + if V::SUPPORTED_NETWORK_UPGRADES.contains(&network_upgrade) { + // FIXME: Extend this comment to include V6. Also, it may be confusing to + // mention version group IDs and other rules here since they aren’t actually + // checked. This function only verifies compatibility between the transaction + // version and the network upgrade. + + // Supports V5/V6 transactions // // # Consensus // @@ -766,19 +808,13 @@ where // // Note: Here we verify the transaction version number of the above rule, the group // id is checked in zebra-chain crate, in the transaction serialize. - NetworkUpgrade::Nu5 | NetworkUpgrade::Nu6 => Ok(()), - - // Does not support V5 transactions - NetworkUpgrade::Genesis - | NetworkUpgrade::BeforeOverwinter - | NetworkUpgrade::Overwinter - | NetworkUpgrade::Sapling - | NetworkUpgrade::Blossom - | NetworkUpgrade::Heartwood - | NetworkUpgrade::Canopy => Err(TransactionError::UnsupportedByNetworkUpgrade( + Ok(()) + } else { + // Does not support V5/V6 transactions + Err(TransactionError::UnsupportedByNetworkUpgrade( transaction.version(), network_upgrade, - )), + )) } } @@ -1012,8 +1048,8 @@ where } /// Verifies a transaction's Orchard shielded data. - fn verify_orchard_shielded_data( - orchard_shielded_data: &Option, + fn verify_orchard_shielded_data( + orchard_shielded_data: &Option>, shielded_sighash: &SigHash, ) -> Result { let mut async_checks = AsyncChecks::new(); @@ -1031,7 +1067,7 @@ where // Actions in one transaction. So we queue it for verification // only once instead of queuing it up for every Action description. async_checks.push( - primitives::halo2::VERIFIER + V::get_verifier() .clone() .oneshot(primitives::halo2::Item::from(orchard_shielded_data)), ); diff --git a/zebra-consensus/src/transaction/check.rs b/zebra-consensus/src/transaction/check.rs index 66e3d0be595..133c7e80470 100644 --- a/zebra-consensus/src/transaction/check.rs +++ b/zebra-consensus/src/transaction/check.rs @@ -172,8 +172,8 @@ pub fn coinbase_tx_no_prevout_joinsplit_spend(tx: &Transaction) -> Result<(), Tr return Err(TransactionError::CoinbaseHasSpend); } - if let Some(orchard_shielded_data) = tx.orchard_shielded_data() { - if orchard_shielded_data.flags.contains(Flags::ENABLE_SPENDS) { + if let Some(orchard_flags) = tx.orchard_flags() { + if orchard_flags.contains(Flags::ENABLE_SPENDS) { return Err(TransactionError::CoinbaseHasEnableSpendsOrchard); } } diff --git a/zebra-consensus/src/transaction/tests.rs b/zebra-consensus/src/transaction/tests.rs index 0a4c21bb039..06aa7040b1c 100644 --- a/zebra-consensus/src/transaction/tests.rs +++ b/zebra-consensus/src/transaction/tests.rs @@ -12,7 +12,7 @@ use tower::{service_fn, ServiceExt}; use zebra_chain::{ amount::{Amount, NonNegative}, block::{self, Block, Height}, - orchard::AuthorizedAction, + orchard::{AuthorizedAction, OrchardVanilla}, parameters::{Network, NetworkUpgrade}, primitives::{ed25519, x25519, Groth16Proof}, sapling, @@ -166,7 +166,7 @@ fn v5_transaction_with_no_inputs_fails_validation() { .find(|transaction| { transaction.inputs().is_empty() && transaction.sapling_spends_per_anchor().next().is_none() - && transaction.orchard_actions().next().is_none() + && transaction.orchard_action_count() == 0 && transaction.joinsplit_count() == 0 && (!transaction.outputs().is_empty() || transaction.sapling_outputs().next().is_some()) }) @@ -800,7 +800,7 @@ fn v5_transaction_with_no_outputs_fails_validation() { .find(|transaction| { transaction.outputs().is_empty() && transaction.sapling_outputs().next().is_none() - && transaction.orchard_actions().next().is_none() + && transaction.orchard_action_count() == 0 && transaction.joinsplit_count() == 0 && (!transaction.inputs().is_empty() || transaction.sapling_spends_per_anchor().next().is_some()) @@ -2795,8 +2795,7 @@ fn coinbase_outputs_are_decryptable_for_historical_blocks_for_network( // Check if the coinbase outputs are decryptable with an all-zero key. if heartwood_onward - && (coinbase_tx.sapling_outputs().count() > 0 - || coinbase_tx.orchard_actions().count() > 0) + && (coinbase_tx.sapling_outputs().count() > 0 || coinbase_tx.orchard_action_count() > 0) { // We are only truly decrypting something if it's Heartwood-onward // and there are relevant outputs. @@ -2808,7 +2807,7 @@ fn coinbase_outputs_are_decryptable_for_historical_blocks_for_network( // For remaining transactions, check if existing outputs are NOT decryptable // with an all-zero key, if applicable. for tx in block.transactions.iter().skip(1) { - let has_outputs = tx.sapling_outputs().count() > 0 || tx.orchard_actions().count() > 0; + let has_outputs = tx.sapling_outputs().count() > 0 || tx.orchard_action_count() > 0; if has_outputs && heartwood_onward { tested_non_coinbase_txs += 1; check::coinbase_outputs_are_decryptable(tx, &network, height).expect_err( @@ -2830,9 +2829,9 @@ fn coinbase_outputs_are_decryptable_for_historical_blocks_for_network( /// Given an Orchard action as a base, fill fields related to note encryption /// from the given test vector and returned the modified action. fn fill_action_with_note_encryption_test_vector( - action: &zebra_chain::orchard::Action, + action: &zebra_chain::orchard::Action, v: &zebra_test::vectors::TestVector, -) -> zebra_chain::orchard::Action { +) -> zebra_chain::orchard::Action { let mut action = action.clone(); action.cv = v.cv_net.try_into().expect("test vector must be valid"); action.cm_x = pallas::Base::from_repr(v.cmx).unwrap(); diff --git a/zebra-consensus/src/transaction/tests/prop.rs b/zebra-consensus/src/transaction/tests/prop.rs index f45b4731de0..3f57bbb74bf 100644 --- a/zebra-consensus/src/transaction/tests/prop.rs +++ b/zebra-consensus/src/transaction/tests/prop.rs @@ -344,7 +344,8 @@ fn sanitize_transaction_version( BeforeOverwinter => 2, Overwinter => 3, Sapling | Blossom | Heartwood | Canopy => 4, - Nu5 | Nu6 => 5, + // FIXME: Use 6 for Nu7 + Nu5 | Nu6 | Nu7 => 5, } }; diff --git a/zebra-network/Cargo.toml b/zebra-network/Cargo.toml index 77eb565c0d1..aca4b02bd6c 100644 --- a/zebra-network/Cargo.toml +++ b/zebra-network/Cargo.toml @@ -95,3 +95,6 @@ toml = "0.8.19" zebra-chain = { path = "../zebra-chain", features = ["proptest-impl"] } zebra-test = { path = "../zebra-test/" } + +[lints.rust] +unexpected_cfgs = { level = "warn", check-cfg = ['cfg(zcash_unstable, values("nu6"))'] } diff --git a/zebra-network/src/config.rs b/zebra-network/src/config.rs index 8619507fa0d..25e82714ae5 100644 --- a/zebra-network/src/config.rs +++ b/zebra-network/src/config.rs @@ -668,12 +668,17 @@ impl<'de> Deserialize<'de> for Config { (NetworkKind::Mainnet, _) => Network::Mainnet, (NetworkKind::Testnet, None) => Network::new_default_testnet(), (NetworkKind::Regtest, testnet_parameters) => { - let (nu5_activation_height, nu6_activation_height) = testnet_parameters - .and_then(|params| params.activation_heights) - .map(|ConfiguredActivationHeights { nu5, nu6, .. }| (nu5, nu6)) - .unwrap_or_default(); - - Network::new_regtest(nu5_activation_height, nu6_activation_height) + let (nu5_activation_height, nu6_activation_height, nu7_activation_height) = + testnet_parameters + .and_then(|params| params.activation_heights) + .map(|ConfiguredActivationHeights { nu5, nu6, nu7, .. }| (nu5, nu6, nu7)) + .unwrap_or_default(); + + Network::new_regtest( + nu5_activation_height, + nu6_activation_height, + nu7_activation_height, + ) } ( NetworkKind::Testnet, diff --git a/zebra-network/src/constants.rs b/zebra-network/src/constants.rs index a116fd63018..d9615131542 100644 --- a/zebra-network/src/constants.rs +++ b/zebra-network/src/constants.rs @@ -340,7 +340,16 @@ pub const TIMESTAMP_TRUNCATION_SECONDS: u32 = 30 * 60; /// /// This version of Zebra draws the current network protocol version from /// [ZIP-253](https://zips.z.cash/zip-0253). -pub const CURRENT_NETWORK_PROTOCOL_VERSION: Version = Version(170_120); +pub const CURRENT_NETWORK_PROTOCOL_VERSION: Version = { + #[cfg(not(zcash_unstable = "nu6" /* TODO nu7 */))] + { + Version(170_120) + } + #[cfg(zcash_unstable = "nu6" /* TODO nu7 */)] + { + Version(170_140) + } +}; /// The default RTT estimate for peer responses. /// @@ -403,7 +412,7 @@ lazy_static! { hash_map.insert(NetworkKind::Mainnet, Version::min_specified_for_upgrade(&Mainnet, Nu5)); hash_map.insert(NetworkKind::Testnet, Version::min_specified_for_upgrade(&Network::new_default_testnet(), Nu5)); - hash_map.insert(NetworkKind::Regtest, Version::min_specified_for_upgrade(&Network::new_regtest(None, None), Nu5)); + hash_map.insert(NetworkKind::Regtest, Version::min_specified_for_upgrade(&Network::new_regtest(None, None, None), Nu5)); hash_map }; diff --git a/zebra-network/src/protocol/external/types.rs b/zebra-network/src/protocol/external/types.rs index c6241ba4d78..91a1874c024 100644 --- a/zebra-network/src/protocol/external/types.rs +++ b/zebra-network/src/protocol/external/types.rs @@ -106,6 +106,10 @@ impl Version { (Mainnet, Nu5) => 170_100, (Testnet(params), Nu6) if params.is_default_testnet() => 170_110, (Mainnet, Nu6) => 170_120, + #[cfg(zcash_unstable = "nu6" /* TODO nu7 */ )] + (Testnet(params), Nu7) if params.is_default_testnet() => 170_130, + #[cfg(zcash_unstable = "nu6" /* TODO nu7 */ )] + (Mainnet, Nu7) => 170_140, // It should be fine to reject peers with earlier network protocol versions on custom testnets for now. (Testnet(_), _) => CURRENT_NETWORK_PROTOCOL_VERSION.0, @@ -205,8 +209,9 @@ mod test { let _init_guard = zebra_test::init(); let highest_network_upgrade = NetworkUpgrade::current(network, block::Height::MAX); - assert!(highest_network_upgrade == Nu6 || highest_network_upgrade == Nu5, - "expected coverage of all network upgrades: add the new network upgrade to the list in this test"); + assert!( + highest_network_upgrade == Nu7 || highest_network_upgrade == Nu6 || highest_network_upgrade == Nu5, + "expected coverage of all network upgrades: add the new network upgrade to the list in this test"); for &network_upgrade in &[ BeforeOverwinter, @@ -217,6 +222,7 @@ mod test { Canopy, Nu5, Nu6, + Nu7, ] { let height = network_upgrade.activation_height(network); if let Some(height) = height { diff --git a/zebra-rpc/qa/rpc-tests/test_framework/mininode.py b/zebra-rpc/qa/rpc-tests/test_framework/mininode.py index d56fb8bf79c..2fbe6faca89 100755 --- a/zebra-rpc/qa/rpc-tests/test_framework/mininode.py +++ b/zebra-rpc/qa/rpc-tests/test_framework/mininode.py @@ -425,6 +425,7 @@ def __repr__(self): return "RedPallasSignature(%s)" % bytes_to_hex_str(self.data) +# FIXME: add support of OrchardZSA class OrchardAction(object): def __init__(self): self.cv = None @@ -441,7 +442,7 @@ def deserialize(self, f): self.rk = deser_uint256(f) self.cmx = deser_uint256(f) self.ephemeralKey = deser_uint256(f) - self.encCiphertext = f.read(580) + self.encCiphertext = f.read(580) # FIXME: works for OrchardVanilla only self.outCiphertext = f.read(80) def serialize(self): diff --git a/zebra-rpc/src/methods/get_block_template_rpcs/types/get_block_template/proposal.rs b/zebra-rpc/src/methods/get_block_template_rpcs/types/get_block_template/proposal.rs index fc0805b533d..373ba2d7c20 100644 --- a/zebra-rpc/src/methods/get_block_template_rpcs/types/get_block_template/proposal.rs +++ b/zebra-rpc/src/methods/get_block_template_rpcs/types/get_block_template/proposal.rs @@ -217,7 +217,7 @@ pub fn proposal_block_from_template( | NetworkUpgrade::Blossom | NetworkUpgrade::Heartwood => panic!("pre-Canopy block templates not supported"), NetworkUpgrade::Canopy => chain_history_root.bytes_in_serialized_order().into(), - NetworkUpgrade::Nu5 | NetworkUpgrade::Nu6 => { + NetworkUpgrade::Nu5 | NetworkUpgrade::Nu6 | NetworkUpgrade::Nu7 => { block_commitments_hash.bytes_in_serialized_order().into() } }; diff --git a/zebra-rpc/src/methods/tests/snapshot/get_block_template_rpcs.rs b/zebra-rpc/src/methods/tests/snapshot/get_block_template_rpcs.rs index 8afb7dd312d..5479f0a9271 100644 --- a/zebra-rpc/src/methods/tests/snapshot/get_block_template_rpcs.rs +++ b/zebra-rpc/src/methods/tests/snapshot/get_block_template_rpcs.rs @@ -149,6 +149,7 @@ pub async fn test_responses( ); if network.is_a_test_network() && !network.is_default_testnet() { + // FIXME: Would this work after Nu7 activation? let fake_future_nu6_block_height = NetworkUpgrade::Nu6.activation_height(network).unwrap().0 + 100_000; let get_block_subsidy = get_block_template_rpc diff --git a/zebra-rpc/src/methods/tests/snapshots/get_blockchain_info@mainnet_10.snap b/zebra-rpc/src/methods/tests/snapshots/get_blockchain_info@mainnet_10.snap index 9986da0ec95..9fadb83a0fb 100644 --- a/zebra-rpc/src/methods/tests/snapshots/get_blockchain_info@mainnet_10.snap +++ b/zebra-rpc/src/methods/tests/snapshots/get_blockchain_info@mainnet_10.snap @@ -1,5 +1,6 @@ --- source: zebra-rpc/src/methods/tests/snapshot.rs +assertion_line: 562 expression: info --- { @@ -69,6 +70,11 @@ expression: info "name": "NU6", "activationheight": 2726400, "status": "pending" + }, + "77190ad8": { + "name": "NU7", + "activationheight": 3111000, + "status": "pending" } }, "consensus": { diff --git a/zebra-rpc/src/methods/tests/snapshots/get_blockchain_info@testnet_10.snap b/zebra-rpc/src/methods/tests/snapshots/get_blockchain_info@testnet_10.snap index 3bea6c01509..14085ee0770 100644 --- a/zebra-rpc/src/methods/tests/snapshots/get_blockchain_info@testnet_10.snap +++ b/zebra-rpc/src/methods/tests/snapshots/get_blockchain_info@testnet_10.snap @@ -1,5 +1,6 @@ --- source: zebra-rpc/src/methods/tests/snapshot.rs +assertion_line: 562 expression: info --- { @@ -69,6 +70,11 @@ expression: info "name": "NU6", "activationheight": 2976000, "status": "pending" + }, + "77190ad8": { + "name": "NU7", + "activationheight": 3222000, + "status": "pending" } }, "consensus": { diff --git a/zebra-state/Cargo.toml b/zebra-state/Cargo.toml index 6bdfdaaeb66..2a0eec9b2ca 100644 --- a/zebra-state/Cargo.toml +++ b/zebra-state/Cargo.toml @@ -15,6 +15,7 @@ keywords = ["zebra", "zcash"] categories = ["asynchronous", "caching", "cryptography::cryptocurrencies"] [features] +default = [] # Production features that activate extra dependencies, or extra features in dependencies @@ -45,6 +46,11 @@ elasticsearch = [ "zebra-chain/elasticsearch", ] +# Support for transaction version 6 +tx-v6 = [ + "zebra-chain/tx-v6" +] + [dependencies] bincode = "1.3.3" chrono = { version = "0.4.38", default-features = false, features = ["clock", "std"] } diff --git a/zebra-state/src/service/check/anchors.rs b/zebra-state/src/service/check/anchors.rs index 5f6ee293e34..b4a53c8f176 100644 --- a/zebra-state/src/service/check/anchors.rs +++ b/zebra-state/src/service/check/anchors.rs @@ -88,25 +88,21 @@ fn sapling_orchard_anchors_refer_to_final_treestates( // > earlier block’s final Orchard treestate. // // - if let Some(orchard_shielded_data) = transaction.orchard_shielded_data() { + if let Some(shared_anchor) = transaction.orchard_shared_anchor() { tracing::debug!( - ?orchard_shielded_data.shared_anchor, + ?shared_anchor, ?tx_index_in_block, ?height, "observed orchard anchor", ); if !parent_chain - .map(|chain| { - chain - .orchard_anchors - .contains(&orchard_shielded_data.shared_anchor) - }) + .map(|chain| chain.orchard_anchors.contains(&shared_anchor)) .unwrap_or(false) - && !finalized_state.contains_orchard_anchor(&orchard_shielded_data.shared_anchor) + && !finalized_state.contains_orchard_anchor(&shared_anchor) { return Err(ValidateContextError::UnknownOrchardAnchor { - anchor: orchard_shielded_data.shared_anchor, + anchor: shared_anchor, height, tx_index_in_block, transaction_hash, @@ -114,7 +110,7 @@ fn sapling_orchard_anchors_refer_to_final_treestates( } tracing::debug!( - ?orchard_shielded_data.shared_anchor, + ?shared_anchor, ?tx_index_in_block, ?height, "validated orchard anchor", diff --git a/zebra-state/src/service/check/tests/nullifier.rs b/zebra-state/src/service/check/tests/nullifier.rs index 0392f1c8e79..8a8b17e4fd0 100644 --- a/zebra-state/src/service/check/tests/nullifier.rs +++ b/zebra-state/src/service/check/tests/nullifier.rs @@ -700,8 +700,8 @@ proptest! { /// (And that the test infrastructure generally works.) #[test] fn accept_distinct_arbitrary_orchard_nullifiers_in_one_block( - authorized_action in TypeNameToDebug::::arbitrary(), - orchard_shielded_data in TypeNameToDebug::::arbitrary(), + authorized_action in TypeNameToDebug::>::arbitrary(), + orchard_shielded_data in TypeNameToDebug::>::arbitrary(), use_finalized_state in any::(), ) { let _init_guard = zebra_test::init(); @@ -759,9 +759,9 @@ proptest! { /// if they come from different AuthorizedActions in the same orchard::ShieldedData/Transaction. #[test] fn reject_duplicate_orchard_nullifiers_in_transaction( - authorized_action1 in TypeNameToDebug::::arbitrary(), - mut authorized_action2 in TypeNameToDebug::::arbitrary(), - orchard_shielded_data in TypeNameToDebug::::arbitrary(), + authorized_action1 in TypeNameToDebug::>::arbitrary(), + mut authorized_action2 in TypeNameToDebug::>::arbitrary(), + orchard_shielded_data in TypeNameToDebug::>::arbitrary(), ) { let _init_guard = zebra_test::init(); @@ -812,10 +812,10 @@ proptest! { /// if they come from different transactions in the same block. #[test] fn reject_duplicate_orchard_nullifiers_in_block( - authorized_action1 in TypeNameToDebug::::arbitrary(), - mut authorized_action2 in TypeNameToDebug::::arbitrary(), - orchard_shielded_data1 in TypeNameToDebug::::arbitrary(), - orchard_shielded_data2 in TypeNameToDebug::::arbitrary(), + authorized_action1 in TypeNameToDebug::>::arbitrary(), + mut authorized_action2 in TypeNameToDebug::>::arbitrary(), + orchard_shielded_data1 in TypeNameToDebug::>::arbitrary(), + orchard_shielded_data2 in TypeNameToDebug::>::arbitrary(), ) { let _init_guard = zebra_test::init(); @@ -872,10 +872,10 @@ proptest! { /// if they come from different blocks in the same chain. #[test] fn reject_duplicate_orchard_nullifiers_in_chain( - authorized_action1 in TypeNameToDebug::::arbitrary(), - mut authorized_action2 in TypeNameToDebug::::arbitrary(), - orchard_shielded_data1 in TypeNameToDebug::::arbitrary(), - orchard_shielded_data2 in TypeNameToDebug::::arbitrary(), + authorized_action1 in TypeNameToDebug::>::arbitrary(), + mut authorized_action2 in TypeNameToDebug::>::arbitrary(), + orchard_shielded_data1 in TypeNameToDebug::>::arbitrary(), + orchard_shielded_data2 in TypeNameToDebug::>::arbitrary(), duplicate_in_finalized_state in any::(), ) { let _init_guard = zebra_test::init(); @@ -1126,8 +1126,8 @@ fn transaction_v4_with_sapling_shielded_data( /// /// If there are no `AuthorizedAction`s in `authorized_actions`. fn transaction_v5_with_orchard_shielded_data( - orchard_shielded_data: impl Into>, - authorized_actions: impl IntoIterator, + orchard_shielded_data: impl Into>>, + authorized_actions: impl IntoIterator>, ) -> Transaction { let mut orchard_shielded_data = orchard_shielded_data.into(); let authorized_actions: Vec<_> = authorized_actions.into_iter().collect(); diff --git a/zebra-state/src/service/check/utxo.rs b/zebra-state/src/service/check/utxo.rs index 324efa3c035..9e2c0618ac0 100644 --- a/zebra-state/src/service/check/utxo.rs +++ b/zebra-state/src/service/check/utxo.rs @@ -65,7 +65,7 @@ pub fn transparent_spend( // The state service returns UTXOs from pending blocks, // which can be rejected by later contextual checks. - // This is a particular issue for v5 transactions, + // This is a particular issue for v5 and v6 transactions, // because their authorizing data is only bound to the block data // during contextual validation (#2336). // diff --git a/zebra-state/src/service/finalized_state/disk_format/upgrade/add_subtrees.rs b/zebra-state/src/service/finalized_state/disk_format/upgrade/add_subtrees.rs index d84392ebf84..636301d041f 100644 --- a/zebra-state/src/service/finalized_state/disk_format/upgrade/add_subtrees.rs +++ b/zebra-state/src/service/finalized_state/disk_format/upgrade/add_subtrees.rs @@ -800,7 +800,6 @@ fn calculate_orchard_subtree( let orchard_note_commitments = block .orchard_note_commitments() .take(prev_remaining_notes) - .cloned() .collect(); // This takes less than 1 second per tree, so we don't need to make it cancellable. diff --git a/zebra-state/src/service/finalized_state/zebra_db/arbitrary.rs b/zebra-state/src/service/finalized_state/zebra_db/arbitrary.rs index bbe0e026d8c..39bf082d43d 100644 --- a/zebra-state/src/service/finalized_state/zebra_db/arbitrary.rs +++ b/zebra-state/src/service/finalized_state/zebra_db/arbitrary.rs @@ -66,8 +66,8 @@ impl ZebraDb { } // Orchard - if let Some(orchard_shielded_data) = transaction.orchard_shielded_data() { - batch.zs_insert(&orchard_anchors, orchard_shielded_data.shared_anchor, ()); + if let Some(shared_anchor) = transaction.orchard_shared_anchor() { + batch.zs_insert(&orchard_anchors, shared_anchor, ()); } } diff --git a/zebra-state/src/service/non_finalized_state/chain.rs b/zebra-state/src/service/non_finalized_state/chain.rs index 12ee0528776..0bb9d52929a 100644 --- a/zebra-state/src/service/non_finalized_state/chain.rs +++ b/zebra-state/src/service/non_finalized_state/chain.rs @@ -1492,21 +1492,23 @@ impl Chain { .zip(transaction_hashes.iter().cloned()) .enumerate() { - let ( - inputs, - outputs, - joinsplit_data, - sapling_shielded_data_per_spend_anchor, - sapling_shielded_data_shared_anchor, - orchard_shielded_data, - ) = match transaction.deref() { + let transaction_data = match transaction.deref() { V4 { inputs, outputs, joinsplit_data, sapling_shielded_data, .. - } => (inputs, outputs, joinsplit_data, sapling_shielded_data, &None, &None), + } => ( + inputs, + outputs, + joinsplit_data, + sapling_shielded_data, + &None, + &None, + #[cfg(feature ="tx-v6")] + &None + ), V5 { inputs, outputs, @@ -1520,12 +1522,51 @@ impl Chain { &None, sapling_shielded_data, orchard_shielded_data, + #[cfg(feature ="tx-v6")] + &None, + ), + #[cfg(feature ="tx-v6")] + V6 { + inputs, + outputs, + sapling_shielded_data, + orchard_shielded_data, + .. + } => ( + inputs, + outputs, + &None, + &None, + sapling_shielded_data, + &None, + orchard_shielded_data, ), V1 { .. } | V2 { .. } | V3 { .. } => unreachable!( "older transaction versions only exist in finalized blocks, because of the mandatory canopy checkpoint", ), }; + #[cfg(not(feature = "tx-v6"))] + let ( + inputs, + outputs, + joinsplit_data, + sapling_shielded_data_per_spend_anchor, + sapling_shielded_data_shared_anchor, + orchard_shielded_data_vanilla, + ) = transaction_data; + + #[cfg(feature = "tx-v6")] + let ( + inputs, + outputs, + joinsplit_data, + sapling_shielded_data_per_spend_anchor, + sapling_shielded_data_shared_anchor, + orchard_shielded_data_vanilla, + orchard_shielded_data_zsa, + ) = transaction_data; + // add key `transaction.hash` and value `(height, tx_index)` to `tx_loc_by_hash` let transaction_location = TransactionLocation::from_usize(height, transaction_index); let prior_pair = self @@ -1545,7 +1586,9 @@ impl Chain { self.update_chain_tip_with(joinsplit_data)?; self.update_chain_tip_with(sapling_shielded_data_per_spend_anchor)?; self.update_chain_tip_with(sapling_shielded_data_shared_anchor)?; - self.update_chain_tip_with(orchard_shielded_data)?; + self.update_chain_tip_with(orchard_shielded_data_vanilla)?; + #[cfg(feature = "tx-v6")] + self.update_chain_tip_with(orchard_shielded_data_zsa)?; } // update the chain value pool balances @@ -1682,6 +1725,22 @@ impl UpdateWith for Chain { sapling_shielded_data, orchard_shielded_data, ), + #[cfg(feature = "tx-v6")] + V6 { + inputs, + outputs, + sapling_shielded_data, + orchard_shielded_data: _, + .. + } => ( + inputs, + outputs, + &None, + &None, + sapling_shielded_data, + // FIXME: support V6 shielded data? + &None, //orchard_shielded_data, + ), V1 { .. } | V2 { .. } | V3 { .. } => unreachable!( "older transaction versions only exist in finalized blocks, because of the mandatory canopy checkpoint", ), @@ -2017,11 +2076,13 @@ where } } -impl UpdateWith> for Chain { +impl UpdateWith>> + for Chain +{ #[instrument(skip(self, orchard_shielded_data))] fn update_chain_tip_with( &mut self, - orchard_shielded_data: &Option, + orchard_shielded_data: &Option>, ) -> Result<(), ValidateContextError> { if let Some(orchard_shielded_data) = orchard_shielded_data { // We do note commitment tree updates in parallel rayon threads. @@ -2042,7 +2103,7 @@ impl UpdateWith> for Chain { #[instrument(skip(self, orchard_shielded_data))] fn revert_chain_with( &mut self, - orchard_shielded_data: &Option, + orchard_shielded_data: &Option>, _position: RevertPosition, ) { if let Some(orchard_shielded_data) = orchard_shielded_data { diff --git a/zebra-state/src/tests.rs b/zebra-state/src/tests.rs index 488ab4227bd..d01a871f142 100644 --- a/zebra-state/src/tests.rs +++ b/zebra-state/src/tests.rs @@ -34,6 +34,8 @@ impl FakeChainHelper for Arc { Transaction::V3 { inputs, .. } => &mut inputs[0], Transaction::V4 { inputs, .. } => &mut inputs[0], Transaction::V5 { inputs, .. } => &mut inputs[0], + #[cfg(feature = "tx-v6")] + Transaction::V6 { inputs, .. } => &mut inputs[0], }; match input { diff --git a/zebra-test/Cargo.toml b/zebra-test/Cargo.toml index c430257f5ef..f937eb8315f 100644 --- a/zebra-test/Cargo.toml +++ b/zebra-test/Cargo.toml @@ -14,6 +14,12 @@ keywords = ["zebra", "zcash"] # Must be one of categories = ["command-line-utilities", "cryptography::cryptocurrencies"] +[features] +default = [] + +# Support for transaction version 6 +tx-v6 = [] + [dependencies] hex = "0.4.3" indexmap = "2.6.0" diff --git a/zebra-test/src/vectors.rs b/zebra-test/src/vectors.rs index cfcd48e811b..ce0db04e8e9 100644 --- a/zebra-test/src/vectors.rs +++ b/zebra-test/src/vectors.rs @@ -7,10 +7,22 @@ mod block; mod orchard_note_encryption; mod orchard_shielded_data; +#[cfg(feature = "tx-v6")] +mod orchard_zsa_shielded_data; + +#[cfg(feature = "tx-v6")] +mod orchard_zsa_workflow_blocks; + pub use block::*; pub use orchard_note_encryption::*; pub use orchard_shielded_data::*; +#[cfg(feature = "tx-v6")] +pub use orchard_zsa_shielded_data::*; + +#[cfg(feature = "tx-v6")] +pub use orchard_zsa_workflow_blocks::*; + /// A testnet transaction test vector /// /// Copied from librustzcash diff --git a/zebra-test/src/vectors/orchard-zsa-shielded-data-1.txt b/zebra-test/src/vectors/orchard-zsa-shielded-data-1.txt new file mode 100644 index 00000000000..fb2c040185a --- /dev/null +++ b/zebra-test/src/vectors/orchard-zsa-shielded-data-1.txt @@ -0,0 +1,2 @@ +01024f3b1b4de23383f8266b54629446bf287f117f153fa5cd87b39a6d7e0d1ff79dfd24051fb4a5a6e21227834fb8d9d1ff8bca7ea7b76d3e8c061b2c58837cba37137aabc3773fbbe4b0b996f8f139cfea6931074cf75c3927fbd6530031d05f095130b94772a7a585523112bca3c7cf083dfdf11fefae84b0ebb36517bf121634866c8e3dfe5730d86ed8a8e6da11ed2878229c2ebd7d6a4dc6075710a393ce14f80e2e9a0b07f0f2768769bf5fce00e536579c3e9629fd88bb90a0220c0657b36de4bc04a98934925a4bba939c9454612abd0cc44c166ff6e17131ce6f3dccc1df74a39c4f318a9ce11131b7b29e20cb55fb664cbe6cb52fca3044a8a4606bea2713985055223d9bf95fbe242c2ed153732cb8224544a501ca8028aa02586430aae74b7ba6675e7c50b700222650a8f92af2755465949347ef64264446e7baccdd24dfe5eed8b875e28a68dc076627f4ca233bb46bc7f6f1c7782f256f84bfacf46a53e5725c33b43dd4c09658609bace1225560d98f9f95d0e8baf08801eacfe760b5c8e1a0da5091072384bd2d1a89255ad8f15af94b92283a40c280ba27099cabc61953944a3d3ac8386cbfe06071c764c366d918618f21c9bdbb14bacf0c64ddc52052282772f25c01980d4e4bf08bf70fa92fb3ab55728d9bab3af961d5042b5e4e3a237ecc3ee5b3a75145b7ba95aa2ac755211b70c73adf39dcb9823bdb4817aa673cfcb9a9e93f65fbfc4b383c5e6f9d8165cac7c6040368488627752edd8df31d516e359d8a96b522a32e9d1ccdc71773ea34b67de0549d736810e7e107fadc5490c068c6c422e196dcf2630abbb0732e4e21bf610a5e6fcd737ee544fb7f431037dc373fc2645648d140b91f772e22d1c5d107c4b1bf55349cca7fccff69babcda70aec29b87ce22698781fcc01e2cee5cdf244a821fd0d112d5e11e6e8939627218faecebbd9b1cf53211a8cbaedb8d2cb4e25ea424a89275ebab0797f248778f74237fdb3c48631ae3bf48b45ac3ea7147eda2db9b5c14952335b3628dd08a6270dc9d71623a0ffef56b6f426ae5596b00c796b3ec3c82791469ce92f406621a818872e42646c68d4fdcdf1c33d3fbec2090b9af99754c64ae752c06e25978cd6f809c67fcad98cf281709f137e45e488d13b6aac6594d907df375b9fe74584c1f2621ce55de3b8361789673a92701e2a76da503cb9790c1802259b57b8ad6b8567fcb097d75067ab11dcd9dcfa11d4fb6f807adf81749cec10b0bb20c3b8ae543fead2985c1c68946c0f8c2793a89b66f8254ff98bea0045ff4e0530c657be54ae45b8f50322752459e5c9d33b67c40cb341430a8886abef137d0cd98d4b528ce440fd0cd26209b8479a5fabd14f67bd1b0f9cf6fd228fc182adcc729fe6bdb999df4706f87b8ff5f841556762ab0b8e75cc351073e9e5dfc688e88fe78879e79b3ed17ab7ae05dba2fd68f479766741d65986db5d7745ada9895e047f76d6330f13ce0d89ff504c665dc6eb2d4816d9cb2c55e234134eb967438cbc7a3a2252aa6038a4442184d9becc59de88426f2096a9f8f83332020ffe1db8093a87a982f4f9315c6a009c986675faa6ec306994bb88db010c18b2cc502405be8e10b44ffc3e5c444dbdfb4830e2e6d365633b6aa49993b8bc0217228e8869f4a942616556d3274ead57c3e844997389f834e22568bc027c5ba500af7ee2c30d60a0136ece1defc4df879d161878110ba19d354bfe3db352c7a5c9b535a9a7c5397ea9ba2742b243e01a839933764c58deb9367bf1260151ccd04f85edcf67cd2737a2fb9eb6ee6c19a086e2bcbe7f4ff030611e8be36e7d308eb3a9548f949b364b030b5a3dcc973b9333e195ad0c02f554e0e92b8cb0dfccc8dd2f23038a2db7b13875c9959996505f56f35965b10a5965cdc8ce2e59350133f7101b078e0c544a9869bc22ad7bf283974b4bbbae2775f9bb1f3a903dc7690281cfaa1c56c6489ed2347977e82ba8f82fb4a48dc2046ce367e50668a2b49680eaca7a7a88690fcdb67c745ea2d2758ea8c678d45262ef6af5b0f4f6151f9457eaa17caccd58117f6f9933465fbd10e9b73a884c810506c2a2efbed5dd10e3cb4a14412a28a22458b35f19570c5b87a3c53781bf9651abee5c08c54ac21b4c4477c8c536695f738dc10da66f97fb1e00663b56c424e530fd66453035a3fdcccdf6d585dde0012ccb1460e03d48c4eceef63cb00715752dc591c2e0e3fe3228b53d6f797e4e29d6680db82941e7d67ac29cb6b841ff4c1b92a4a4f327e8a556bd6eaed35518dace042f385516e88f449495ba33df9eac5bf21ef85d3901192868e74f54321fb191bb351e21eac264f5d02929f332a6f88e93164877cdaa06d372ba1bafd7298f9cae2871e3c0300000000000000000000000000000000000000000000000000000000000000000000000000fde01c17cfd1b7aa8632813e611ba8502084522f3cb4c9a4a9fc2ae8fb0902de65b68f068fba988b8c181bdad47d44d3a2e1ecf69c2c49f8b69ede82cfafc67924ad81448ebce0db9bec9d0e3761e25e809257ff0d7b29cff484c336da2ce38dc5941c011d8039e72b6b53f008f030d221264242cf1efb966e9179d71c4792df9c2a353765544107080f76893ee30230faea7659e979816db3b6db9ea59f4b947d7504a74fdd9856cf69f30382bebe722723a09a22afc0cd63ad75703581472e23c4b1c7a72617a868bab3790a46e243c060b7e97d3e5f77f81e3ddb8d10c6fd05e2b25058ec4dd4470a72fab4f53e41eba59f4bb0d9457036820aeb847c7bc1647581328f86fb96dc1f5dcac5d7cbc221cb5513cf57964b7c9aa07fd1c94d4548700744a6387bc253804a2acd781ae4463be6aa7cc1ed046cb9233c00591f2dda4dbedfb37558c24bb85d05e2cf4c1f72d9d9e3c0176b980d7771686c0bedf0f423a370edd5158e75d4aba724309fd52157f5e7ea6a0b31adfa70f86c5f01eadd361d167bd4880f4d32498dd5e8cfd23ca90f925fa79e7ea7560bb04f7434c4803e95314e74c15a62671b830eeea026a718dd0969710465e99282ce78f208a5f8313015cd2feb1613f21712840279ce07b64146c0425a1482ea99e9b616d31e8d4c346e49dccf30456bca5d1480e77341a717f7288beaaf960ee15d844ad681fdf3be342e377d9847d19ade5132a978a8ff14d2e2cc80742f0d89b682fd245f651f3c729674cf50c872a75691c255316dc04cd0ddd11a3182fdad371a52116659fd876fd68303513feb3701c9df8935c9c86a8075210d401eaf2c7815d317f709f22153f5d9f2652aca2fbd0627ae5a38ccc8b61a13cef40c6caf3b5ad51afd5c3a076fb6a2587a215849d9c62f565bc977167186e6aa8b29f05c382a028ae85e0d2bca52326ebb0528e9e9adac1eb7085d816a7cbff6ee1df0530722b125681cfdaa5a2522138422fed411a164ef599072c7dfd11dbbf7e73baba76e19fbd556679a7fa3e3c14334c61995d9028cfde2d5d3454c608fa3167aa40b0be494fceb3da27fad031d03a4538958d3fe556599af0bc4e565a44488100e0b591fb59f2400058e5447a55fdfe1d4fac969ae8d54cfde99b3f0189d5405cd9bb5f813551022a6e3f5b07c916064b9da98580dc0689d3a10dcbd9da061a8fddaf36c23b1b4c918fd47721072bff8ab52825f0ca00ea1911338cba97a933279ea39ec47c20a2e0ed118a77caa5737bbf4494ca070490a081cc6949bff6241a6e89ab4cd83869c29690d5eadc29e07f9322b25e7e0a66bdf225d443d5584e2df54eab02186573d9d729949fae28230998d792b1a0d05ea1ee0ee1fa6d1631392e0ee1b606223ab2fa109ae349b8dd520ec5dd246fd4dce7fc116d802a581c4aef4bff21ac533ba11646f7b51c7a63f85c80ad1c31e640322ae7af1b7005ed386e8713ed64a37c29ae0a94c6f43031dc7f82e08479fd413a9c5c21b02c99c638c0a8166139e996d1baeb0b1fd30a1f51bfa2ee552c3fd17f517da095cfb58c7db31e03a92c3db1fac4886255a9ac23c6d569cdd1996ad45ff45940930a997318d451e80914d9b6418fd9779bae9764af5d2ba1990c7f01828c18416528ccd3edd362ca6e002b38290c74e77810478889a761d764ce1186f8a1dea8712e01629c9ae17c0b63d205cb410bd82766e0f8b392c6570d7004ff956fd1e89e306fd9519a5db3e465e06aa024801341651f3b6bdfea21012c5699c2726b6c4d911124ab7b8dcfc00766c2a338d4aea2522a4fb31b4c8638f636a4fbcae0c347af7dbba573169121870c8091fc70671734d48af6d51159716749a075b852416a95bfecc264beb21a769ff4e3c9c5023cf797e71a67df709d0b3d00ff717f9c701ae0fcaae410bdb0f47b996119c28965ef68afb173edfce1c4c0ba64099914f968837098ac1d17b9d5678c580be16c419e9928bf9dec96c361c1b4f395aaec054850bff51b1736adf3595b7bad6518588c8de3d940e90237b5ea7f56334e2c83058e336cdcca14d62b1e0e229b9c470d9746f644efeb23fc9bac52beb2ab70cff3e67528b0d9044637a10193864fe68567f45dc2f142e39a38ec6c12941850440353d31b9472a8c9aeb3d1b37725164848f604b9a6454eb9e24ff936f2aaad3a455484877684ebac2ec7568800ac3e804d35807cebcea9b374ecc9787f5077c7c10691d269ad84a1df62ec5233edbb7dc0e1317b126ca8ead9a28fbafdfac1bf3ae4937c468423567c166c618d758ac92e857252b854832545fa8a74721aa4a8c82822999eb2c80b007279c2fc084becb59184fc1a020f4b31276a774f671a84db1148ac389d00afccf74fb9c4e9531a12b17c9f75196cb33492941d733fd0fbf08554dd7ee858607b90cb1052e7c7755fcb45193d96b88e5efbe519fc703c2b19ca09b4d88d07d35f9943904b80354d65b62d6417f46ead03a6107ee1735dde1a2fb8236e233399c52e5771ff1ce0112bb9e3da40d3a79f403aa58dab7ac9d13cabe3ebaa34b4dff2b2ef73276ec2be8ff54c684dc6f3ace3a1247b14e55bde8b8687710590dea7fb51f4024f8522a3440a4703fd1af635641e6d936c03c03fd69eef8449f6239301dc1541dada303536a16d2ce1eda2e3de2ac3ef36e74698d08c96d1ddf46dd3aeab0192994c69701b0bd093e733c20ee0c3b0f4b496997edd1a089cca6388bdddaf44a3bbeb56831fe0cedfbf25c21460a64ef8e9b93b54dacdd407e9adcf425fd2a63351925966bda7d50f3823029434db64123563f886eb6f7d7b28ac14b7c6b5f5727eb19ed24ca18ea060c2042a66147120964354364f405ab49fd1e8b05ea34f23c630d8765ec6a73b70eae2abb5f780e93e677ece84d541d651aceaf5284bba032021ddc7a15012290fd1a933ad3844822d65643eec51a7ea5873968844088671b05b4b3411b00a55b512ab6ff74d8db8e260db4ba47792c692160c89723901705fbe445aa71c5adc8257699cabbdd22f25b60fb5c889ad09c7a2830f6df3cfc0e82ee5d7f165b5dac9beb6bb48deeca18da046817595c2c0f4cd46c4b5c113d31a5d15090997960ba2e854f0263611f698110241ebf7231af3fe6439c886ac71d9375b7c45b87f4ac6abe5cf826ea820d4b678d65bc29ae3b8b37d5e1bd157805ce1273b9e56fed0b3a2afec754a759327f6704a6ff42371421f9afdd5ec7a30a7ccddc4b0b04562f1fd16a02bedd4c97539400fee5782be45041750e5f4daf048ecd59915696c62171e8314ad09f2d604d582b7735572cfd5210ac2fc7251a043e5ffc19b1c4d04135053bfd48ad1eae26c99a9b631dd5f6d43b8af7933f9834c5a00c5698d91618d75dbf6b7121557e7a094fdadcc5254e3366f4de490ab30b9eb207cac1022590319db8d3626a0d81aa0f205d3891484dcc6d8b407942ff0fd94de791b15d46cbb1d62cd90a3fc41f72519b53f28cff7fb3d30941d6696c382ff08cd14a783b6e593f2c88f5604313f2b7b80bea6740b2368af266d45c09339e3ba3b227ca88c6d03083b6fd46049c7c0aa82c21d46c397dbb33253b30600fe31f796950dc09932bac3363c76c705d7f23f3de0e1bce3a42c00d30c169690dbd20cea04f096ac6b4c4dfa260496edfb84032d3a348430fb5357e8aa7871f0849ee5973d5669de1efce65863748997c75033d25ad84304510acd3f94005e42b670a147fb04b02a74e9d1b5273ff0204ea956c5611ff5468fa9b1d474611f80b0fb6dde99477826f196c9168f314b7196781ceec2ee5ec210c94bd90726c5317b0cab154d7e382d97f006169730477deaa81a2501c9ddd2a6a8746681442aa2b4290e35103ca6b39df34e5e6ef6c1427d0ea9d71749188962d896ca859157c17473058c66d851baaf051d5021e349cbf171db9c8c06ae11bf7fca1cbecc0f21f7942bfb6504a57c82373474b4ce1bf5af9b7a8198982bf17e63492ec943aca3e1f5a69cbd46c5337d224944d82a5aa219f3ea05902cf4ef4e83e98f27d95a82555eccf59067c502e1151cca43f37426684105f78513436ed138859b22a14da1eef7405a285fbbe1f72ed7ebf8a5907da9360d2ffb1d067dda10de9b4768acf13689ff9bf23c35b4c6e0a645d293ccd0b46d5f26ca4a2b38df6b5884ca0329929e72a2485f81aa2366541ffb1a733435c30917cd7387c1a0671d5c40a426da601da1e2d8bb09da80b791870713a8e60507ee576c5438f9dd6423adae12f93a91eede37edab8308253dc4f7db84d488f9a82d4cf680a633246f40d341752421627e01ab9db6caf27baf27fed19c3a208d7439b482a9ba3435ab53b42494616ba138fb7c56f05332cdd647d9e8ec518c01937e4665bd9fc7ee7b072452b82b3443ebf75874bcbde6c585440caecf2b6d659f344fdd92d8bfa840a6084787e7cbf03b664b0b3c52210590bce1041d64826e2c6c8ede27bf00dabb30ca85b44881b14a83ac3af83656ebbb389ab5c34b459232848fe67fc51ea957dfbea48390fce0502f44454dfb1915a717d796f2bc18f06d8d0a739f3751f92b3157a4a0138de35ca09e90ffcb94862f959688fa86d2f9526c28853f73b934add66b7463c573b315ca934bd55540d80d15c67f9f67175523821f13504c3f3d6a3b8aa672c8f020544dad3af78fd6a962c1babaf654cd3280bf763b444cb28eaee3d0578ff6b9b3401152d5ed101ef9fbe4c08df45c55153d77d46098a39a7045e9e663a7053a63c7186c010bec8756837c352dc459976ebd9da5e7b45a1835739a84ec1f4ec7214cf524642d3c06ee129b98f6bed17629667e8ae05706824ffaa6f5e157e92cf1a639a55ff8d5ef045c06417a0b959e5eb93d23479915bc231f53aaf9584e7da1f6388c9027b3ae01035c0fde3cf300bcbc289921be8a148dcf83418a9a885e829349545972df1e526c4256abf288ed3483b9d7f7c9772fa577518d24775174f1b765a3c54cb8c8d1a4eb898aaad023daa3b58423145cca88e7d6382a0fe25c2196b37b2e0d037e8ba8fa294caa29bf35b36c458217922ec76cb61214c5b27412fad9cbd34e596d36a2ac909ed004917cadc54d874c5b93041594c8c8a1885b020540e75e2ba6fe86491775057630f1399df495344db366c59c3c8551b05f7ff31e9ef34f36e0ff99ae1853c7b8536fdcba0456b937023b9b296afb2e501e46130b3953a1d3fdfef4fcc55539b23d67cd65b759ae468fefe0f4a7b5211ddee123b5ed08921014b471d54f065c8ea750ea25e7755468d6277710692536152db5a29f3dec2c4296631766ef280b07d1da8f12efe6c771df9b9a6fa64d2b26c030a17a086fbf4c3a72fdb7351d1e7a9d9b2214e0c6accf0ce3336d867738699ce023113a3e2a30b01e84ad0e906361d0b5f501bf7a75b9c71e4580e47dee12391870cc132d6bd2e7961e87be6ce1aa90c5085fdbbc0d126adb57a27342a0a9f27ae021468ba4ea45e535a2f095fe3e78071fefae9a97c29a609fd7fcbde1bbaabd211442f22ab226f5098cf596751e1ca852425cc11c26407dbb8de3bee681f1cdc3bb1c104204a8f3b91c9d0698c739eb1b03941cbf6728800a0a92289841bb6cd17004f8ec77a2566622b3e6447708a89d8465ae985528a3e5b6c6eddfdb94caa3e88bc9c1def29f403889f25ecd9af8a8f95ebd5e6c167c870188793903afe901efe47aeba1c9f79348f36aae4343a1f703b9ac018d7001a937f57b51e5c502b3b51f8e77805055ecd408b1fc59435ceac43ae205ee3e069b6e7c20d51878e9a07c8cb7c3a6159a2b1bcc002264f7bebc6b35e1b6895c21682f6d6e0f289261629acade803146b5c2850949a2ee9d8c080d121f6ade9c5f9032b6ca244c3479a1b5d30e59cfe033f2777748eeca7684efa05bdcf59963e9e4d6aa90ca24e09512a8ec4f6bb1f6c9c8d8ace283f3bd36efe40b5f6d1403f45e5846b05299bb6d7254a4ddd0a36b1b893791fe318e05b2432f5a0bb2926a7f3d4530d28d7eb4c7a251ee068fcf981c33b680a31bad210b9ba6eea516137ef4d6ae42a542b034c6c0912e4f86adf41e86463b5b6b6ded2314c93629a636e7169391ae885ec08369023c600954ea059e2e081f39cc95fa77bd963ce335fafca84353ca1136138da2f20dc88ff68a499abf9e83353f7e1ed528b19b6b5881a0184098ce60d7e3eeb620209b05303c2ce34524415ca4885ee1a39d4299aa900750f4762275a6ad624431477f840c58df2ecd0c5fbb925402d6ad457148f5895aeac562c48f9f0ad2fb10fa7cd9629347820e3f98fb51b6f174407ba6c858ac1aa43a3d61c8b5431ca740eb8d798fca971c47e59606573812b3fd7b197f393f27748b64f023f814641903c7a96d59746b3124f36c55f820a1409881775789a83dd66fa5366bb3fe645021aa63ea89e5dbed007cb7772812eed82b6e2cbd94b3b2f94091c1d22142131d20bcd0b9b3536a2f451598019ba8ce69e7e3f60d14bffff429b2f6a0891b21016365058dc069b75a4404eabe3dbdb9105e3025cfa212dff7e83559110847d20713b72d1da9c3b86e7f05e92ab57ef6e64b4e2f649605a73c2e837d69baae7ebd000ed3a947b766b03e2bbe50ccfcddc0851b2b374efee1af5668a50688c4b65bd3ba900cfc67b3aae5746e109afc7321fbd5dc5758703a29f90b669fb2f53de8c30206e2e89a47242b50b22fc532cacd1827a71ce52fc7a65e829dcfa8d65334b1af54ee32b3327b16d3d5a1f7bd11b35470cc336f12615e5128c554ad88ba99a2849d6f9a5bbb5886dd4a61393aacd0081ccaf880879aff5c10da8c216fbe4182edf2fa6b14fe97da97a62f4d052543c80657d5ef828cba8a2ceab3afca17271140f7ad539ceee75a83d93cee1b33d09302ec7d1255be8f03422733586e817a51bb066eebf376ec3f57435a76572c9677008ea62ae6ec06e2372771f60ee42250b156d8dda94f4dbe4a05852e1ae65b07d0fc754c7eb5a34bb90083d1611786c22519b1b208d4c20bb7a8622ed7f745b29676acfbeb9fe9894bd405c279ea3c007e6c66d42b7b10dbf2160c1645ee97affa14345b32363d4c5b08f688df07dd3177b23822e9a3e6bfb4fab1c933952de849e15d6f9797ba62cd63cce033185e112f9ed91cb1ef00197ef7c9c8eeceb48c2065b957558e3a6295e5a7be8ddf7583e3e3ac6bf3759a4a8e62830c785899132e87e2ea36dd07684142bfdde3bdf1e0f4a84d6d4478a134b7722a9d9f04f0182b91dbb10d49e0651a8c8af144bc8e62ff0e276ec1632cbf83d74423b286d091fec4666b84d852b27408feecf0037ea384d91c3c832cf8d6f2cceeec0444747aff65daf6404a4d6af7325f17182fd151b4de835faf3c34882b73d49d61363e58d2cdf3fb34fe6a47367cd78ced76091237353c403a42f7a7db06c71bd53056af8ff42ec9422d126e4f4eca8a64d151f2e6499293bb7646f949926ff28d52f03550467e353043ee2934153e939e40b4415db2f4ef11b4a8766b935ac8f3d4d623664b9c9dc8b1d241512ab8bb9e06af913e0c7b7962d41951d304aac35369b58819b42d9bf2511bf9b9976607f6027e627ad8787cd8c984a1bf42deeb484506f1f9967f2bcee7e09c60812bda9e13f9f3d2b7f93d78515b3e16ef497875a04ac9cceb8d5501962e98111ca2a236c9a4330bb9b16d3753b20f016fbb809893bb4a51f361a6eff0b87a956c742cf439ff916a4c55c2bb443cf4f9836c4e67c71833d050a53459f5583489a91b23adb764b1a9839789901d31eae4f83e8530405af73f66d0963e960d021ca9be5919df5ec2ea7b34d5243b10beb3fc755863316836b2fbb53414c1dde45fe7002abad1b8c3cf9f702465ef59c51fa34221e686ddc29cdc9c80f6531b0a2fcb96cab3cf48a13dd426567b1980d758fdda6ed900957e7b8667133f89119cd5cc022f8862ee7023c154ed3c7482da0b1205bae66292585684cc82f1822679ba7c6e31794eef0244a4fea5a452162e47093378183cc99ac0c68d569abac05da14ef0a9a3d2aa907d948d4a19ffceb086305ec684f011e67c2079519708ca2bd215ba9501ba75c30d1fe2b669792c51ad132e2f39a47af98eab7f38cdf43d342c0c3992e07bcea3c18aa0fd0c6164a48e2f21065d3d8695b43f42ccc3b0e449ae0e58b3e6255550dbc16c612968010500cbe8bd75ef6bb607df81e9d540e56c4c176a363d5a31e34dcb7a6be9f556dc9bd5fb187dacbe45c2bdd8df82f4e4e2835bcf85310371832b2190d0596ef8f818c600a81fb10b46098c46b2209d8b0296d1fb1b4b3f8372ffae2101d248e417b53b61606920c10246921c18d8dda130e403b3937368af801fc4f515313425c97adb525e4ab15a8664d25b815ac25f1fdc5be58b68d00b01ac9a622dbc0af1b63ccfa30ed0602efbef486dd8e7945943d89177e215153d9331960b5a3feed03b95fbe684e9f0e8f9b924713e28e99f1f64a4f70949ffc323e21d65612e385c115624a7f246fa392bd98d1d87fd2cecccead2a96444f665e34758713301b919f70fb31a4ed19cbe95b9851d90d584769f4afcfa8e4a6c44a10e56e8dd838471b20af352d499daf5446b0dbb5070ca265edaae5ad62804fcb2faf08b54c35c44a57bc7a5850407203af2bfaca86201545b721f1f32dd761ff12991ff1809ef1e4bcd7cf1820d2857281aab9dbd2aebf47a15ad968f24060bc3d419fdae05012bd9c159fe09b17f7b6462bb08bcd78ea4b705c7aead08c7d1a0e5679903da539932a92bd78ab3edc6f8bfd3af8d4bd789c7dad1079f6186e8e28e64408b6d48335c5fe8c316a2131b70471e3665a4bab350f97bf4c4794326711019ae19cad1304b2abc790da3824c19d2ad8ddf331933c5f200f9ee8e8a7451cfb356918cccf62ff6716911f782fcbfe4ab9cd39dcde7b90a65a676da464650fd3a4b4a75074dd7bcbfe0cae5d2eaf8f9e865f29172ae83b63b4ee1a6bdc0709a885908505afa4cd847fd58fad4e5ff96fa39d3ee5e0890aeaf428c29d6500154d1522655533c2421bbfade685f8b9217d781dd8fa413133a2859485b186e810c344cfd49df117487a4696986dffe4d34e3cfda36895973daae012265b396d01489e67a4e0b1ccf60c630fa018aad7d65a89b9dcbe631ba5891df5580c735b2e124789d6d04515bd06896677f2f6ffe3f07b418875ba36773e558e6c8cbfa53d3e35cf7c1ea6247861f9353db27826b95bcaf0afdc54d44bce63e9f65b9d592b2fb0ebbc2d09532ea15fa1a57b5b44ee9f478efd2454a52c46ff3d4f8df39daedaf0a1927ec46a38b69e44ba19a110a98ae5ada516d21951eb46d2f2641160181932d9a1baacd097f67b2d258c54d93dd0fc6cb3db130aeb2ca376b2560b7d013bf92f618c3735c7ebdd38db122629f12e32d27dfda83bcf8c20dc65f22eab9f393a55b355dbdb496e321f4f82c14484887b085ca62a0a3a4d0c2d6dfa804d3e7b920cc0a12975c1e2a9a1a68255164abe07a17b5ffa4cb0a87b18b3e9e4cb89e1d27cfa1eb0242fd8585e4a1bd5a4744b680b558ec3aefc9455f002f418b5b9bbe08954ea57b0a06cd0c6437ab4ec33f396f5eb8a2c3e63450a3ab8781bdc9564372f743416b1a60bf71393ed69a2666e30f75841d04bf2e56e3afe8cdaa381336faca15c2746416318ff73de73213b0afa5a1ae1c855b94d9b92b2c1514695bb02655b4082fcf1c5004841adef51a51f9fa2f736fdf573e5717a2e1e220b349c15aa1b8860d42a65e8fff39ed78f51eca12a0154225f630dc67b6136810b2942d9a713a8b950166865831de348634a24a2dc9e035015069cb2a84cdde0cfa6cf066be8a82cd4567fb5c6b90fdf1bf2fef482b02c9641d0dbc66ad55e12ed04d4d8915a493abd8bee7ec670058a9bb41972c200d8b0a35ddcb82d9576ad228f357c26d2f8c51e7b1c9015124ef46c52e5ac4b6c9c1bc48f7ce13eea301ef88abf3a8b191e782543db418293a3fa451fa830b411fd20360283b20439f862dba3c6cf2e0831c2c3aa1fb56ce734caf6d092b91facbf3df554ebbb606d615904b3631d8782fa3a3ea0581fdb68a7d87cc4c3ba59645dff6834a0bb409e5a496a07e1a77ae0a3053740f8b9fd5bff8f6bec65d622b1e55c80cd991aaac2693c9aa9179a9fb6f81fbf10a4f0f8094e1855b0f44a7c57cf19340bc81bec340a58d1a314b8b6ec697b023d500b5f9577667f42f0e7578dcd0ebddcc7e58ab5e59198145dff35115ce9223d7ecd668d14e60d5976014ff80e131089c929615541c8e182b9e862e0ae2f70a67263ca6dfca71f6866ffacb78119b9cef7d962b4f2d75e2440902dc0e620c83eaaaab7eee2fcd18828124c5fc4a8f1147b017df22e99331b69ba08e7c27c5398a562374c1b5d874a7758e1cc530ac90454af1ba5f73a5b8251bdb4671f1b0ea7cbd33f4358eb52eb04c7fea14b54e1564f59431405668e2b3afaa2e4ac975d382059a7bb60404d7ffec82af648bc4be634797c4d44c2ea3cfaa6180225d75cd76ce05b98268a3b6bba050f2f33ceb126ab31287bd341a0120a000000000000008bdc33e41bbfac714be1a07b266b120263af89add962df7e752c0bb7c039ebb3879fdc162c1f7072e7670c16cc824ff719ffa8fdafb900fd3b5c04ea718b2b2e + diff --git a/zebra-test/src/vectors/orchard-zsa-shielded-data-2.txt b/zebra-test/src/vectors/orchard-zsa-shielded-data-2.txt new file mode 100644 index 00000000000..3f1e99966c6 --- /dev/null +++ b/zebra-test/src/vectors/orchard-zsa-shielded-data-2.txt @@ -0,0 +1,2 @@ +0102f4290671353bef3137938ad3c19f403ce031cd02b2bd7a9ff0ca5931c926f11b8dcaeedc218a06c9b18cad3cfd2325ef92175e4c56d3f3736a6f955bbde34f2f43da326927a4fe6c6c908da15c119f97ad7d6717e8c435a79c2a52c18dbc4a2485d4dd59237372e1bf05271fccb957c0b2a73653b1a37c7673462fa093d581254059ad5b842b4ab3c3bff9c28280997fd86ca2280924bb0446a071aa526ff4b77a5d8a454b0dad7c5e8dd68bc56ce1dc6ff12e3e2ebde71ebd2eb890212804633efd9978683d03e2d34c6c877b6eae49b10764196463c17e7ba55b84e3f6b37fd94ac2f838ea1564e3148f7eeddeb96371066ba3af4c457fb99639af4cbb83a015aaa05fdc88b8aa13a4c5e95204c5e5cc06857db9f4db4f7350f494d40bca23ce813690e217380899d274f1bfc8a827e553530285aa991818d87e377aa400be8c6f42f2539a50ac9b948be567db444c074668f01c8b34e22ef5850fc18a84090b435f1ce2543b3fd0b1c08da3af5da34b37356cc3b3f41ada1f1a71cc45fbe8efe3f5d66fbfbd3cc270115646d9d3c4df0c2648d7d035ce81b915e8dba15d5023c4f03dfe10165ada52751ae623ce8f67d751e46fe0fb240360f31b90e687a0501833085ff66cedc379d0120dfd0a19c526118b673a31ddc38faa944a6aa9e3da4612d99df26e34570c5905cfac8c574bf064833ecd7ab54ee079abab62a24e346db3256d0209019cd16b94cc4e01f19cd603950d26f5b489175331aca94de6ac8651cfc03bb19f68ac96b3c92286502866c8e928125df7206e319b914873b03dd1a2834aded1f83e8cfc52cf7ab9549543ee4786d22f38a97923ef045fdbb10b46acde7462b4468be762c9b7303f0ec00f4dd9baab2a1bd87bd4f230de86f819c92c893bd75a5ae92ff02ecc42ce997e07d194a3e23cb22ec6ccf9cb40d891da0ac7a250c5946975d161040acd6a5cf23bacc06914db40998b207c2111cc7f3422b622adc257f2bdd2d4686d91d060c1570ffc0978740fd0a76bb3fcb30882b6d385a9ebb76a4fd8b8fd54b38231fd163bee73c6a244855f000290b105ef894eb192144b76d9b5d90b36497bd47386a2d3a10a7f680f2d21a76fad444a9ac7854c348556b3f027ce157b38ca9f10087e4710b087d5e1c24390780980ff46117ab7a16c3ac5a7ed2302d7c0b83eefe000ce2d728cc0008f1730eb171abca1739ee06ef3dbfb6e08ce39149cdd86a6f58500381bbf8308719098881b8a07b3680964fad01d12dc126b51ace097c878fb6f51c21ddefe16ebf79ae90f03adf4a7ba95996784f3b1cc36a238f7918f2e9362d5918d08c2f2cc6287b2b0c83495ae58efb8bf41239c52fde650301f09bf41e3e49c222bd44340ea62e694e225800f454454f57caf94cb8d2aeb46d74cf14e3bf6082fd096b1a5cc9482fb38033e0f211b57de4eb6bdb10a1eefcc95ab60c032a16f79f6410905a22753009fc52a386c3b75904037fc54231389ae0feae1bcf1ba4448c443afffbabde645cc1f979b4946d69b377d2ed69f5cd54d70b487d75c64f69b7f992d878feb01d55f01ab04a72d9ace2ddea585a07e4f3a630a4819082bac6886aa30b997ecf42716025ffd9fbe71e30a60f4cb5d33f06958120df4c13c6881eaeb584de08f15509c8a6113c9e345111256782d0a80d1498129aedfb7cbfe363ba19cb580310a4e0a344935a83ff5453e11a5d92d8eb417f5a9597612958b1010e0b905fdea6ab82757265786da6f1f795c5d17043f87c9ef39373e8e56c4293ce2d395c7fa9cfed867a957685202c8ede20d68aa246d1691384189ab13f5d447feec2f5e42d3069b10d9a7c10fe092e24230eddf2e3d4610aa9ffa0f34f470c0f8fb266a04910f72df83d288ca5f7a63fa91a09b073abede9e8ac69b66ae6eecb6b1d3be70beb96900daefe17c115e8314d67450400e041814102a288a12a46dbffca9f4a5c9eb4dd662b2a77accac11e2be2232702604ab20bcff9c5fafeea3c0de46d9206b8b76602d622a4887d14e65152d4e8661f0d2bc8d36854b2fa1f53d049f80dc91d38200de7d8d2b76c62a40a95ba0eed75c875db840a936adf112efa309fc0671dcac6ffe391b6575a30dad023d82eb8445cb44724108d464ac67f699326f65d02c60fa7bdcdd59693be492471daa14273785d095dcbd2192a71923f598f93699c82ce20b5b0360a877d0c99ec62d343cf0dba820309b6ea373a17a02ea0521e284a7be8fb1da91068ad18bf48865a39c8ebf9aeb1bffc5647a2b215c37da597a550f4f78ea49ac8356b9ab32d3d65d2a498656a6677c529c47f4f12851750c51fe22cc839c381fac04a57edc9fc61d660e28c5aa87f89a96ef64a1ab686a8457dbf548605409956ad15e4146f40300000000000000000000000000000000000000000000000000000000000000000000000000fde01ca162328d9306fe484d816701fb73093249a96dd4ec6ff6acdc0ce67be149ea8494778c5f33b6d752ca2c91317b3ff8191be4fa5a3373dc5a078d560a60a604b67d81e5584a0498b57b0a0878facbce7b163bc2b494b2b74fd33b0b9a009a3837204fdbfd817deec75c2977436b14668f121a3006328537cf05771d52e46eb73e452cd2cd453a7fb98613f012d8567c09cdfbf054c7e5ab3a08530baef1bbda1cb0f14ca307ce1c68a49b61d4dfde19fe099d7bfa7db5b48a7aad65feb45d3ab11d4e3da76fe91423b389b7ff6cf15c70e1b63737353ad62ed7655972a7697907be97e1f10f3736fee0bbc4fbebffcc2d473acb6d7371f13b26e99cdf83464e14efd04b449d10f98c70d81be2a92d3a29eb00881a9d1951dd206fe2ff97792524ff7c6a2353ef42877d2a78b9efa7e1f0d81c568e8482929279e987f0367f111835dc4c0fd63fffc6360d5b9fd220596d213dbd84bce8a362dc7d80c723774c034d83baea8188b8eb6f37cbf0e66ad0877821e1fb006d2c4bb36a8652fadc940a5dad25833c568866ea6440d8b598e5b3d89d46f471dbd37914477b7b7bb0b12a4d66b789b14b69f9aeabf755b221606d3e48bbbb9f80bbfdd51c99da28eb8e2bbac7ee190a9e9fd3da880ab8df8e06d48c790b46616bdda639ed5bad615c658bd4268331fa9fa49901345c92c01c4ddacf053e0cba3e1bfbf1ab5af8687b28a64559df8cd361fd0dc34fd89d70956528a6c298571e7b8abe4e3b1c72a42fe39b0398d4e70633f13283bb9bfb7965c9dbe5275100c9e34eb21f38bf91b6e2fb2d95d7f35196ff177e3d36fa59c3a68f959045bc3894fe9d88040bac7759f23f89f12d48424f175ecad466af42d570578785d81de73fe749ade7a217f220ccec8a1a71e9ac7c19cac2dac858297849f180382af001b178427acf1f008b8be0fe1f726ce7aa9efae9a39e1b09dfdc19d97b673c88fe4d0e7447427d53b93c98ab034b5dd84f7100f1d10643ee477fb24027878b802404041bbdfc7b591c9c51c990afbe746d9510a3dc71fac2f9e4a1557fcf93ac8f81f49eb2c1eb6176f6ab8093bb5c2de52930eac3f794001bbefc6b8b76648dae98ec6e64715d01cd06665581b3b29d0e30a3ecacf57bd1578230c8bbdd392524072d4cabf43d821dcd57bd2e9559d85683e33c959aec75276607095aa6eadf9cc257be3187d6c7b2bf898cb3fbcf723b7e5831ec18f07cfee4a679527fd45d6384acd22d93a18e669b0b9c3b9eaca06e1b36aa1bf7133d5a702f8678c2b46d1531c86004cf3530c3174f6395f1c1fd593278a7c75f2603844599f2cd0f3d7f061472958e7a8796269337d023a190082fd7f4b0106c0e8693e1eee8362ad16907742b26b124fec8fb8fd4050686aa3a84d123b980741dbec476b42428a391c360fc3fd3f4dac03674d1745b2122eec227fe4f005410525c9fe2c43b10e037243920b4cc334f4b721c0f752d1fd54de00a9b863cfb3b4de8e4009dbe7755adbc45b775eb80735e55f61c7b110797d68acd3738fa9f66d8613c182c6b3c6112f4220407dce6127cf7811d31702b7d78c4375b31e8b3b7609dc64807f76f9a5cb73959d70ed753e230883ddf6d9cefc9d71d9d7c2bdcb4930bc2ec5f235369ba6a83c8c0c7f2ec96574e0ea86e03324039e9f9607ad709090bf91c718a39b9d0074fa8ede6df0072d9b627dfdb3a483e9d82bef830afe31026818236c60b3ddef30cd98bb364248e6ed26c5d6218debf859ba227e4d270c78ea267c0978a4442437bdb0eba01b9dc6b5b24c68d13fbd57d13a926ad73f04782b7cb7f9b990ee13d8432e169cf00233c0bff5489957993166109be00b138ea1ef707d2ced481a5f79c55bf77597debc76e24e72bab5078a9cdabb9d696108aafdd1fe9fa46bae35e514fb7b90e32b28092264a6f150fe62627013280c87c242b3738bd825efc1c422eec42bae5ff7c927ea554aabce89c122d79df63f01794fa6ea080d4799ef132ce994c64feda5200e59b5f558ca31406ff9e749779510506c258498d67ae8cf177a8b6c9777dedb7a2d785d91926fac8009085b70980b3a3069477a8196de03c47c0c40162448fa7c849a6841fe6f0974c9ed87d0ed277ca1aa471fabf5d54e0758bbe2efca2d46e4ab9a4a0b356834217ec5f1f9d107b03fb39836a8423959455c784f9888f237b8661b7f61e64739015dae72cadf9d0f0e89fdbff5fa92f2d9c2b46379cefe274df4b73351161b8301281f900daad4b74bb7b8fd1b6de293cd6fc0e44e0c1256a3c88e487b5c6396f56d82e7fce06fd6df72553211e381369f2997d6067a5b25a2c79fd13b445cee1a8e9b1f33ae635f028feb2d47716813817cea8dcbaf512aaeb6db30e3b924fc9c0017f06e1de15453c2a9ddccc5b899374a0a1d415872ae7e2e9880b1b99f7b149689f42eb0cc4a56c26190ef40bdd0b31b8a46c48cf2784f0926ccb291a58940b595f33064990d3ea82aea1537c7ad5d5c77119b0aff8cd52048a191b40f4df7d0647ce324c9e7befef9b6bcdc050ee8d974912b3568833262272ea3f89312be68f98cd2aa6169b9991df68810c06ba52d32f44087bd9fe7adbb95f2b72a6d137eaece59ba7e1ab652d8942a4b940723982e287fae4178ce6bbc7b72cafe5afa6f09062c75e437c04b12a288e9baaccf31204eada712c00e323bf731fca404b7edba0181beb597a2510e16968c397ebe66211de340093c1522ea5c111aa7998a838ad84319a1999df57667c31f52973f016b85806296900aca2a18b3470b473cb8dfd4909d5c73959269100907f97c6c019aa750f837b8c086f3ad81fb4bdd846a6c4703e33af76764c113a5b843b22c429bada4d871c09461421550c7bc70a8100e3eae97806342cde5232b75fbd55a1eb0be009df690c9ae6d7a52c3fd459bff0845ddbaa98a85fe488e8b47d165a6e10156ece45ebbe9ac0d00e31aadaca171b75b60d92bee482b64c3c8a46a77066f9fd00e4863939074499a21a64cfc3d2663fa8f9de44382fc2bea725da3e16b151ae8183438e24ad5ea5571e3bd6499a91a3a58879f26ce3d94aabc4d2a7a61f9b3b6e1a2f90eb19de10fd3e4299a6d162ef8adfc289652a43241b4b30f35b0458437d17b21aba6e60d61f2f9939ebd684cd23211c04e6a0e66639b2172beb4ecb11461a0755671bd1819422a279b8eb07900a8d28457da27c11f662530aeb2b1860a50834da3ebe4d63af3baf263519aebc02f783114e7fb4f663c8e04118d6b739d34974f3707cc4fea70ec761d7ae98bec4603980b2ab408535cc952da8e8ffac1fb0b4f91f2df2451e14f0ab5deded57abce0102a07074662fdab890817cfb1f57f67d93f4348794ea20ec2707aff9fd41d73349af94fe7160c7c67b388989d8326d02a055f9f99f4a38efcc6fe2a7495d2ad7f6d745da9b98cbb7c517fb31ff076ac6937cd974c5f23b32cf9e7eaba5f143c74c4aae9deea5a79d846a739a0ce194b651a45c4ada742ab9e599aa66dc52eedcc0a25d022c27decd1a71282b0209783e9d443d937e1f08ceb39566da5055e2185f3fe20dc879fba6f0a810dda7acdef8390b699f530638e040d4c99a59cbdc8489fcb565c635fedf9546fbe53548b3d7c02686882f683c1d8c689f22db125f6fd2c27db33302a2e932c2e2a6d192d6e4b3a25739839d382457c91cee7408a11a8de41072f6627a8b4c52ae739ac438cd0c62eb31f580223fa7dce00a141b07000677ee4b04dc7113c554a03598c621cc240c11fef91e055593e2c285fc0595cfba2ae88b8f76e03e5514b53460cdb4206485b01005da19fdc3e59c30744867b645b0f8ac8179e3c32b5963939ca204d755ce5cbaf892157032f19c78111ad999ad0dc7eb7563fa3e418bca2c307c83ba205ae72d468613ae8f6e025a3cb00c13fa01a52cbc4e7f492071946a7cef8b4d79f0fe031f23077d7cc2fbc33d142bc4f2f74c6bc4b6a4b876c26e9bb4db17dc5433eaa1c68322259ad5a9ab0c100a064d8fba364a9b1a11849862664e022a3cc8445d21063e16d972d5a31978ff14ad5e8d64327f241914054c1d864aff5c554abf465b9e8e03c6fb24afd104891f74cf99c7fc0e3350388c135b1df3c07d472344e6f66dc2247698e01c4011d30eaf285a1520cb95d11e8c8d94bfc460467bb39dc652fbfa1b221a7c75e113fe4974f963580e2beb9966111779c24ad5810691068d35bcb73e462faa9e8a57e450f8420f536e35d45bff98f35d51def1895c7746bc8886112ce29fef65c65b1e51a0c74e179dec89f3d18e47210f7e24388b47d559ba1c8308a39cecb0108bf58095325922483759eefa9739f9e4fb02a027b998f2e41481141ccaf16c440f442a95cd4245f540a3e3bc797745b2f9e864c7480a1cff44c000f3a62aa94cfd03e1a4069de03e9dfa2ba7e51b90a33edc4752d7b7540600c4222c4962b0c273576a0d9fcac66999d58fcf1359ea2750e726036c34ea95b7e83335a4e7797aeb8426d64aa897c4d386553e852b3ecf3203e7be174acb43017413af97be803df2bc91958ddde78ef42d5e7d08c147edaea65a36b5110d07c29c017700daed2d58e4c6083bdb7b9d36b900034ea4f9fe801bcc1c3b9a04115fab30990b640d4d645cfc35e01defbec27c88401604068850da516f9a2cf0a05b0c1a15f1e766fa992dfa6ce1d0b6ca95be2d62860cf2902d08163c3a5361c5bf470cad51018e2f7c251688d2bb475b33c3c03573eae0be23ba5335d82638199dc12994270ac7ec85d2eb1aec9debaaa6060b20d1dc8ede3bb4eef745064aba65050b1981edf8be86bbb479e79552eb8b62e7296e19e9ee7d1f73efe9810413718b075b217279c05fefe063ba0a2f9aff7156881467464b671a6199bf15cfe579a23dd260a2c3fc824839fd808ea9a132c0e25898613709fcb341fd6dcdbf6d6f2c093f9229e690febd6e0aca45c173c5496928a0c478d0036607f4500cb7014e8a2432b661be42e60a498d29ffacfcc5a3897780a3d4a701a9cb1729dabe01a420388e2cf5768a355114ae58665aeb0cb7200db10eddf343f13cbb3bc935b17f0037a0536b908b968797c702d3b275f97d859b00a13ed6a4a520b5e1e0d1661f4806ecfa8085e6ef1d137f50acca6fb4ddf7f08b48b7e321b6dcd76e525c7abbed0a87fefd01e1a545518c636e644ba994163cb33e7d071b9d0df66ef8482c28d111e0f7a8846f86490d91ea645e5d79849959bed3eede7e76597c6c52bec7e4a539c196f10ba257d8bb5d28a6d2e6c4f71eb1cf80def43db8517d4b04f626c6a81ceab9a697d95234e15d89d2665e3c30ea4ba404df7ae8a61333595fa3978c2927c519d665043d5591280879c0e171f5c7ba73edbcc4aa9f4acdc0f1a128f2e82bed5e93e060785e18d897396bae1096a77ed47900a21dc76b5dac4ee874975608bad3d12f8b384dcb11aff6033f0e3d42db636c325a73e8d52fc73cdac3887711ef9ba886b33007ee7e181c3bb65045286f4b9f58631787d3f2069a88817b7a0600b1c0a8ef5855302bb696de787d3971194a967e14392d693431b9089c4fdd35e2d7fc7e1779bb8a937991dc7cc508caf6c428e70b98f288b30556e79ce87116e4c6da53f914af76800d4b39b21c951cbc0c27b70f4c1326a9360a21f07f9003f25bac96ef4b0c5cbf8c08585e4aa9fc7783682c227e5157dcd4a2dafadd8d0abc183327cee74d26ae495d541dedbd80c58eceeafb6a51ed9edf0db5baecab0aa6d4b37e3f5235b3e9cdef7e36d49b62d7607f2ed9691ff6250bc95d53b1932460c702a3eaaa202c91158a54c4456cd52e397dbd12ce9283273b7fcb4c6c1211d04136867fc6e097f92d0369b061a22fc261f30f6883c79e6fc43e3de4adc90d904261f1605836a509e63d71c0944951b55a785158fe585192f4975c40e64227b007ee4a929a71356e5c544a094d340cff1c6369453331f9d706a3c1b62ce5015d9aabe25abe59d77065762948bdd79d9bbb7be3a1528db8d291530e859a592f29c5e161affc67d93015b642070d1e99ea1e08b52d4243b658c6ff103377f8364c3ef216004a10a004928c6ab59e1b43a6f67165989308579b9bb9bdcd8aea136fc249ebf5169577c2aefd433cfe25b3f79acf8e2edbfed9c21df68cd2fa321aa67870bb3fc6b0f2e9ff6df33c3afd5baa4c460e58e8c53b544984339a329910e4df7d4d6a6792cb6e18bd89d4dc7ffd1c6e9abdc1352a18c0ba6851a9279f091cab2f13d86b1940751b6fee1c45e321622fcd8ecba25680d732e7cd05bb3531323c8418c5003de6777b0fb43794a1536a1d9227d37736702d1318601972063e10501b2fc335b39d29b3de1beb100f09f8947cc3c8a2bcb221b3dac76dfecc305a488f6533060bfdbb078c9a66ac1d267e6da37b601843c2edc0d6c93711a030e0a3b6a94df5d2f3327d686e80f7c19add6fe90b7fd78fababb975cb638dd730c14df21e3f6c410268e856727b6e7b3a074e32b5d83b2e78c5f810a7d67835394d8cdfa98f74dcd1f913719b91a471f7b4e87b473beed4f466dc33d4e3f2ef018945d1ba3b90a7930ea6ce25783ac121f27a8da60414a8aaf8e12f342880a435f888420bacbfc8ec04d19bd5af2beb8651549966c017b9f1cbfae18c74c5d72f67eebfff65210284f3af647902e9ee2623043ed4b5024d60807b62a7575dac0b7b6a24bf84576903154d21a8d112b829fbc867818e43522033d03b09c88896064ff6a2bf3eb09f90236e856b0e8f5c3b8eba0fcfdc1f6429decfe93c0f447231bd3017d0f93c548c4f2de132822664ea9a9f3e35027641195f070787176499382ba6bf5bc24c4281b4e33d4759c7d3373350423cc3342898c40ccf8f7d0ac90387dc1129ae8349f3fc08ae5695235a60c16c775d9027a5dfbaff2ff13c14af32fd5b7d95d8ea77c45d7aeb97d85f2119528c7f1bc646a5280786b51ee3ebe433613b8b324f492da4f4d9cd905eb613733cde62c468b40fde0e665ce6e485081f8245338c196dd93206d84d336651debb3fa32747062dd22409670e2264cc311212357ed7260c0ce60c07f4700353b8bded183d0dbe89466b0ae5e61fc6e55f3a9dfdf7ccbc6c141edb0676ca9637f3b7cf133ea6026b19167cad0ed20145d83f822dc6b34da671ef93a797037c83cd6a78bf4887a77a3bd448ce36b7419a361d23bbfc380809eae943a1d745dd48adba14cbec5047802adf7cf14c42cb7fad14ccda903312a370c16008765e92131eaaad3543640a730536ad219ba26c71a638f89d68023ff84180ad2bba492ec225829023371250c8391786b56c9cf3037912851f15610af6f0ebc92ac3beaeb3f0d8e2bad0f71d8927c8531eef02c57d5f0430245a0792a1d43d6940a2ec42a982d10c6ccc4cf04271b42f7af9b1f7106d2653fdb3140e70fb09de43cbe161edf6a87854cb095e8cb258cb721c1bb89ac327447da14ac9ffc24cf0f0f5cd1c76f2afbd6af7b6a4d100c7051f55a06fcaa6166b1a16ab3d59cfb5ecf620fbea9e3119012fc50cb72ba19c0f27dd05c09efa1038b38f2f210095babcdee74b31285981590109945f7dd1c0723a52ec606df118f6662c2036cf95602abe35b285e3003d4cf4d61fe7e43801c296424f066e792ae1b40ef4d4743b5187b36f1d0069b3bf8b2eac27e9e1f6c185bd0745ed2e35117f8d72c7d2f7baf560788ecdb2a5c2d78478bbffd56ee74e3abf27b57ea128166d34bd3680b02ebe149e863a52c29041184ef47ba9ec306cfa946f935160f311d0317eff2d9b915b4c2af8c5a74a1c540b0e429346375e547194a2cf0971983cab1e30c26a4192961b1eeff6c40d698e8bcc3bbb9afc8812e4cd7077675f800eb7cc82fe6fcd06697f67b12f4fccfb0dc93d2863ad9565c250c2c9290c6caf1a648f70cd7e23ccbe4c8221e2f6d1c1f6467d22888723b787fee45cdd33345a0815f9032fab1cf968703caeb08ee9ddbbd42d0872ce861afaa9390df4df51321912e620e9437811c14b425e1df6eb3da98e4589b2864065ee7178aa811b78770f260283386172f00ce67c7456a736b1f0735a5a97426993a160febbe28be75817d991743d26ae0374455118d9291445c1f4da308f425cad02555c90301e39c9238477e4546d4e9ebfede49ba184b95b58f09722c1a040c86fd24a993a3b05281088c3e30d5d1b948e7b2d8b4cee07ebc63c274eec8e7695acc030fe0dc164a631c5a8cf792fbfb0d5b046d7debac64decad1c97b8c6fe7dd95f45f78bb79d8726088dd7bc87977a1b559cb9795df85c435ffa7303e80136879fb27e1cc131e410d82f4da0786015038bd5dbd506eedf9b16a5bff2212c3bfab58d58d37da1372e2fb222406c7913e6d7ab7112baab458ab0953f37d583c6a6683848b29bfe713ee73ac634387657834af96228b5b472dcd7099fd9e10fbc7077d214eefef09b1ef001784a9e8d3f86fe0f8094d1a0edbcc858c1ba499753378b900ebf9ee4281a39015fd7d0dab5f2049037045c37056c6a840d5f813566fd90a824d516515e034f1242d42359a794a5688a9b271aec6f659b2807d994fb551ed86fa7be25c82670dc34859bb54dfa125538784fa27d374324e1614885f1d7e2114e792be1dc027bdf37005e669bd40647d143e61ec6b8d8644251a01d5a3a08e7a1bca4aa8419101498ad3736c655c55b997912c599c0342ec64acfa9c1c75e0ee2d3a172ac3d622b990a80dd2aeca2b8e3b06bdb7f662f4c60f4ee0846ea76fb7dccccd363072842fb961abce07c5895522f0ca58413a60b1db0f5f43d3a30729784b9d8723dd72372666cc131a332ae8671ba456d2f72f7be517bd419b9410d1af20c10b60d4411ffc6e691ec9ba84df4ba049a2be9e95120de7a8f36825c71c916992e1e1b14643b991c5e025f035530929afbaaa6a8593e0e08c535c9c3319621648edc1cfd6c6c6109075b19b7474581ccf4aaa8814ae1d83da11cec030eacffb8fb6d3b1b0c86ced3f1e5ffb7fdc59bc77a0811132dc24bbf6645a65511406ade0166900a2d271e574e26c8ad8f2bd2f29969cb255f0127ff43285c9989313257a63a0f2e7ed88b31070fcc34f7c1f1f8fdd911311724bfdb488c0798043d62bbba7e27f1763291abe668b9c59fdcb0ee261193b5f09ffa91311547d925962130d1843e237d30d01fb65860df9831e88157e556ddfe9b81916bfc95e2a7da9a8643b337d4ed2afd8a5dd99f577f4a677855501edc111552889d2e8eed3b36819957ed2d20bcec1a8a41028b4e882e06732155498b20860d8f55805edfbba9b088b61891ce678657cb4b57c57eb03720f44df7563103214e5da7d1c9b009b6bb4474bb9160f75a932ce961fb5ed2d0e284b7d9d0c179ba62c6abed15c38c8279d4416991d38929f9e5556ac24d474cb9437b9df730380f5cebd62927e946449328355cae01388889f5530f033bc7c88dcc846aa42c6b77488de5972868ea1fb0d733721b2b453bac88f8c1d5c9f6261868bec190e83c19a4a3c08ae958648eb093a2cca85d5146b9a4912d2c6ace1712f648600c5b7b2f79d6167ce64052f50df731dfa4870172013cdc3ba806cf110583b3a3112ca744497a1a5b4986418e5a065f492de9b43839b7b293ab1dde4630591bd5ce75155598b1d27d2a7d8372d31e73713be2fad3aecaa64052eedc3551364aa1d3b13f35f09a1eff9df6e78ab67cd114ace23de31f0c16cc9f8cfd96cd737a3db7e172c079a80726a60568bfbd05d1bd9177678f34617a78efdadff4fd4c952475bd38b0da71d1254550384079abcedb9b76b074c8e43836dc835ab6165db7e27385edced26bb995ee7728a17f7809852b4db1724d2988b241ad7bfa94ddf5d54ef581f3d6ad8a4d11f3f443362e353f2c2c3e5db8461d43fe5bfb98f59a412dbc8d99feac52668cc62738789ad4d7f82d63608fa050b3b0bfe98ae6a431f6505b9d1e178534324ac0f255ecc40930d5babca2a49f0b2d8e5c491cc57194b326c4dc0666a1007d812c498f618de8dd21a0822b5ccf889570c23503fc5b936df320bba2fbd3fdf0c28bbb6258cad892a7aafcf45b69a87896d87ca74a8ff7d967b02007f9c794b12963c62c7bd7dad5c9915ccd1e442851c66133b7fdf42384942f020fdd58c7984c906dd02d816d375cbabba2e7af574620db72aea85de10031ac3f948a41ddfdbaefb7ba80329f2e52bfdfdee01d0f1752102cf0c4fe52e647d07e13854628669ccbec3d1ae54e5a8912ca24c40992cfde82d0b079e3f2edf496f7d42af0a4e6af0a0d9144780d7016b692bb79f37e56c2994268449cf35d55a9f3f325f258e1f4c7d29e9b695ba523057af3f83ba7f5ae40c60120bea6b48943c751957e03004a40ab372ce134bae029c60c6b47c5affd297c7af982b1453b158010dc511c8174d7de15ad75f295e69eed725e83d8bb44d3f9a378461ca9824393225ece5e2fbce467c25a7ac5347d3cc8df6e386ca5a8c8a4296c54a276b403ff035913c51ff4ace6213438dc24eb99d7f477c5429fc397c116408fcf6ac736f46b151d601352d9c599ab401456433e0a00000000000000c514e674b2a4acbb7f91836216bb7dd917eda1bb61b997ba5c9d11f5b560a9bcaff624ef9a8204123e2fb05613c79617ce79185122f3af203847ebcef900463e + diff --git a/zebra-test/src/vectors/orchard-zsa-shielded-data-3.txt b/zebra-test/src/vectors/orchard-zsa-shielded-data-3.txt new file mode 100644 index 00000000000..ce1f63fc782 --- /dev/null +++ b/zebra-test/src/vectors/orchard-zsa-shielded-data-3.txt @@ -0,0 +1,2 @@ +0103ed2e55fe065508f141dc03d6c18a3b0affdd1fa65fe080a84ec40720c2d36ebc5272e5a5f810345fb6cddb4e526a375c7ce45aaea9acd00efa2279b8bc2cff234614bb9372f5fe6180783dddba7e44585c362da61552bb73645a422d7bdcb9b8e16f3e5b5d40c2d806d0f585a54ba61510172e612713232ec2da3c0075280d36181e7869205422509238404bad7f16da27a7eb8c01ed61d784e9396f788aa33cdade57165e84558086b7722ccfc88071e6729ef652e2600d8c1e800555f5e11e0f4ed32e545d3381106acee5c329282e1cdcfc4f138ff97e90e7dabcafca6d3bcb600f472b14b93e43166880d1b8398146028e8f8d007d4035661406a0b7853c91f05befd8836230f7118cc61bd32512b2771ebf80c5991611d955c8c3e3cc8d83d8a6e1adbf5d71528bf2276070c607178702245ec0e3f6f8a302f709db577fb82a1990a879bfe7df3b5a2a42aaaa8b701e5ad2a449579934f5b2c92f47a6ca62b0adabc22f4abee89ccfd2842ebcf088df34794da0416349564649422848dd6cf21ae592afeac3fd5bd140689e4b45dfe7b65aed403a4e32fdc89dd81647e15a9b7a38b428487331ccf966eb60a66b4d7ba69ea0c17a6e51bde0146a3db14764434642247a27a8f6c38f570600d783eccd522ad08a8344489f18efb05f6ffa45a2c39f3e805e6a287dc42ab6e9acf08afeb406d870b1a39bd903cf54e836459de0d3004189b86d2d5c3ad03f6b96cb4556554ef63ab4dc2fc5dcb4663bd49c481773e3e98dd6c8a1d9bdd4224d9d646f1ec446227b2573386715d96c0f14ba5a92a4bcea9f5878f75dd30528d307d4706caa855a7955029905150e0b9224f07f22e9477ad67ab6a375f1fda01da59b45b05e4c302e87a95b92ab90f8bd9fe4c923c061f36dbae32765822a6c14c73d6dab812f31d61f447b70b7d126de98cd9bf5ec25bdc87b9f7e184d55dd61d0e46c1aeaab817ef22ff43da7514438bd6bda9e684a07aa3650677eea9d3510041d0f45b0f956d02ddecd615a9c7ec3f137b17d4fd45dd13c933a08f8e0a638d3c5501b07df334b358b2d6cf71fc0e1717fddfd5c55cfb02f234332e1f4b8892f03e7315bada12d92c236d097b6041a5292b4a21c71019731d6daae7497243c16883da375fce31aca5e105ba66177cf382196d58d037b0d0882be766b410f46de7035960e27864064cb8e40a8ece60890be813b3632fd61d1dc8daea61eb80b5e8cdb74ca1d3796265b82fbeda0a40be43a3b386fc8d6b892b8dd6acaa699a492f09881da0cde963ddc1f34540d8ff4a9d78ae9750404a863909e14d110f0f447b23d824d11a75ac6e127c01db79d3c11d44aae5955c4625f1dd127869e72dd55ff9c95e30f5a0442a39059292b8cf23e11b966c9e1a127b6553da9ae4bc52d6e9774085231aead6c346bbf324e71277887647a02a6284fd3f5f06ff1e62df3790f9fbbe08922864481f46f7569295d2e3678a6e2ac94918c7baaacf08a8d2c5a34b0475c64a7f92daa6284a97472b55cb65d97363c8fdd090d0ae30dbc2c2e0d526f165afc115a48ee60841dc517f87be40abd81f929d42c3c996d6035a4a943dd233d66ef2b0dc941bae55912548cfdda6d5995495d2e2d2add2c12d302e8193044ee09c2d47fe5a1e2794cba0077a8a5b654966f7432a4fdee6a4b7017b0c48cacbe7b4ea933a17faf14024b74368243328b77a773e872659da2ca9620eabda87d82f65160d90b1d52ec8d62eb50151148c5c923b06e2abf95c1fa370bd0dc23726fa841a119c436e9e7b0ecfb6579538a95bdfe70a7414c85f0a21edb0d6bad5c025f80806390af5dbac15a96251c125566f58a2811c975274122d56fb1bebf47cb66ddf56f7100aaeee1614e784b2bd51cd3bdccb8d3cfd28107392be64b1014e6721305dad73ac07f394914a53e255dc16c7f20a968a050c288e2261a22a982e9069090a672589f399c3608132d066fde22e370ab725471dd3c8d397bba18f8c428f2a115d128c3281aab2b398a9df7711428a6238bbe0359addd8f38a78be52f58fab72e1022a161d9865ea6c98d31e6d6f34fc4351299a1579a1edfed90b620c5e87c8227f6a241909053cfc0df3b12ba850ab79d3f58da14752e60665e902fe00a83e77308a9144e9675826e726d972e7fc9687e983eeb14488939b71052aa9029d0ee3cccd5193899ae5a80af14d766bb3dd26088d22ad1522e8d855eec3b56da382d03e3cd564dfffd6b53863373be509fd395799554a2a0583d23f0d9a934a91524431dbdf921f5cabcb136490f4e757763712de7fb00048e7e027fdf471c14bc82b23d234eb5ff76452a9a2f0994b90b216c348a76dae65e355b1f0d2bade27ef62cac2c9debc3d027f59431b0c1c4fee2f26a07a44fc1c2ea56672b76c14056dc23a8c87d654e586fe873e0fc3c087b25822730d695a26876c00ad31fed19d85843bbad58f9ca97aff5b8c39ffe7d407a48239916e48889a6692d70e09323f62277f24415cbd7ee1117f6f073abcae20ae120204c2027f9858bf345e41e8a9755e4b56c40dc2e6624dbf7bea9fd80555b3112fb12219c94805351964bd56a31921fcd89e5db7c7397a19ca78bba6a4835f4aa0648dba44cad7cfb1fa12d81097b50ddbd99f82e395a4f0573f51093e74ec774e4ea12bb990c5b161126ab8fb1440c2993bfd25491206220436c3ff619cac39a717c7f003dc5eabc372803d3473d03d966a7ecc6558d2db257e5992de1003fe56dea3fed54b5085ef002ebe00991c473047a84959d1fca33b2fc0f64d78d1af5dad1029bf7c862d04e66ff345d8b7fb142a5932f8a14e20c14231a0d460db486ec2bd5dedcb3b2ecee5cece1fe677caff36c7521367c4cbc42c3848ca6a574e57ce716c77b509b554549a7eaf08cdf9299447c1ec6b91b85efae2a40898eb09fa4184447ac18fb6e7935fd360c5666523205975ab5c01d129ac75d818f0f3be92e7e2c0e84c79f3a5343a9033f694ae133991df87dc4959d16130fb91296837aa501e77e0992bc8560192396f64d9e2d8910018f4729ada0099a6ccc2ea623f5c895d4d98182d1d25e357b48e2eb846a14954771ef4dc60d5a00fac7312d88631c2a8e7dda8d4c9ead9c14ea8bc84e3821c0d126abaede2695ab8a7bc74f30f3a056d8041e563989776de72eca7323fe89125a359e226bc28cf5f5b2daf81b6bb011ff4a4bb4bf8c5300f6118fb8b42a39cec9626ca1d8197185c5a26ae3e5c13ee17079e5e417f934d5623d8b9896a50a99277723e6c335e941fa5716ed3651ce0e7fd11effa11e58207589311a6cc0a7afc45f1b3f0072701cef5de450023552e625e77d666264154a3dbe0897ba00a7a0f5209ad03e2951ce81d6168c348268259da35c459455c24c50e72386f54cce07f60fc648ad2c8e1d3cb78bda095c7317db25ae2455442595c3019cbcfb84b9223facc841c51baae1c345e5e063aead519d94812e7fa57996b9801fa4c138b9d3ac8d09601c7f6c08d18774c322a4ef05186865b17c1e8c5066ee5484a9270a7714c0c2bd5b1226288e32472ccc057035a9c41d0ab7d5c5a31c92c2298320290a17880300000000000000000000000000000000000000000000000000000000000000000000000000fdc025182a8902b4a3793bc4cfa8ccddd717185c3ccb9b9a327fedce3377eeab36ba3e2cd23fdf22ff824a58d2444c02e2b2ad39a32de12e62072de4b2d3c14eaded0e25fa1690591fdc286bcb29d452b8d478b6f463ca50816ee14e749032c141001fc264c14af6893894fdf25e9c6b69f62a904654e220517af1d486dbe0844bd012ca7f297a43a5daf345ed0fa8249666201c3405af3bef0a79eba95f72f72faf1a67d6cd04ebc21a280d56ca7b955a23f94ce614a98892c88f7504ab3ab9ae223f6b76d52293cd618fd2b03c76562cfd8d70008f67b30d964f153075b9199563a4b0d5ae8d17cce108084e22cc211117316545f81069b55ae0f2051d79414e858796e1396b5440ef6350aaa44c3620dfd5a4d8dd5d470509fa59d0ea95853043bf913ce59645517b2470bf9415ab689a023fce0df410c8204423b5d1d16c72ce32232b6e090195ef53fa2efcf311a6ca1962411a68197a750138060bf56cfc8eb7a7f9738556ad74e0a2f92d017441631f9ed527431ea30e59f003900d890970279f4fecba621072c8b2c2eaf40afce48735ff56bae0dac0d0700ab1a78d1d39185b84e76829377f066758c07c594f0c3d8b998a35b54bc3bcf89bc5b6d3aff7bf90e5e451cfa79dc60bce2c0d1be3cf5191dbe013c59dd92bf047d2fda46e292b77070c1892605a7eefffd4e01c0eb79bf97810a758d6198bec2e0a6c6aaa249d40a354f149c192ac8a77148a3a8aede7c76ab28b4fa76984105c72b306f42a3ec6c4bd102e00198a603dc27644048b0688642cb129b76a03eee5cfd752788e1312d44f83724ff10419da66457f9688f0670d7c6bda1a77653fa207705cca051ba132db7f4a28ea83d25f7bbbca64db74a17b45c340704498d314532ee6b11093a1ebc763a63b234efe3c7944490c222d4aff1d6cbb3b43e43849503480084e2f950c5abf1e05a6c36b4854fea2e27ba11f779f8c1b7d9e7fa5d17b6f67777e06adccd7df8950b7f1ee32fea85fbc9ed12509b5a6a2e7d87c6dff9108b29e881a15873e799644c085c7da4ca4ebc281ae1171b0d57f69880906188761229862845ad0573ae53d3ec74a321b2dd1f948c7f23c7a86aef4673606454757c9aa6880dfa333d44ff11db14aa7624c482d3e0c1eb2efa609df912eee2bf793f72d062e093de6afea94e86b01ca86f81b18bd4247cb621b5180a9ff3209be39d0952c179cbd7a71701cb122cdcb7b6a14af29877a56b0657e2aee606d072c8442a5a2386fd6f555f8cc0ce1cad545c437af97d8b6f6cd5aeb73160fdf5679ff4b1c9ebf6877a800e3498e406cd4ff18fc05fd65ce186a4787fc08089f707fcac6885b3583f4818ba3a1ddbc5cb1b7db76ceec245fa9b91f35e0c9279d1a7b3a4783138b74a5b5b85a4290435dd8e658b00dcaa33484e4b3de2bf80975fa73b0a090b08d3242c7e0a33833b6572a35fcaa7d4cd0e2f9434743adf4e07e7d49333ea1078ecd21aabb7dcec6861940ef05cdb8a8efa7dc11721030da88c85e8e639ef2f8283479e06b88c8e3f095862b5c67f119a643355a35157442ab39f0013a69efaa8ad4648343393a11cb27798c3ba4fde4f9b334d21b28b21eac8f69f1945f556eaff53f0d5796d9770910965ece9695e2c86177a66281b1740a385519ef5834138f929cb5550548a3b0b1d76f6e60f49a43c57c4fe8dcce5a6f2cef94ec9537578ad7ec2509719ba5fdbe561d77c8f20d363bd3c99735d8f413566b49958aa4a329b719fd30e2ebd7c445ee78d79b83f7996de5a2e482ac1e3c2cb5ced92a784a8402f6f7bc727efc97757acfafc19f2d1c7cbfc59372f00d2d744e058746949f3d0600373298a1dbe45dcc94322f70a9a3af8da8407f4a1f22eea2e94252771cb530e13c7cd57691a7848b1f401473ab3886c16520bc0c84fd26a76949b52c5026befb00f911f0ead83e78d74c17d60a624072510dd8da3f110b742d6abef12809b4f7bcb15d4c899b444259085ebc7f4217ca1e630de9e421addea8ed8b96c785f6606d464d8c1f695811c225430deaf7d428cc1961aa1908f5345b3f757bdf82515057b5a325e4824afef6bf88e8724011ba00da5803445917c0f6e2fba34d2d90e40245a3f9f7d466e0d6bc10d51bcc50978b0b6f2fc6a7e73591c6d6decb1fba38801c19abdd66f21e61ce9034f101e296e991329bc7fc29faca2f2ad1051c47530261933d2abc7849ae42a71fd52f37d0d41f80b488173d54505343aa0a000ea57f2205b3705c1523499d8be7e60107a2bc6e8ae8300342dd9418f6239abfeadf0cd14c215d14720a8e7935e117dd572ed67e15defcba9e466c1030281bb0eee754dbf19c259add47bda2f0f6cddbd9dbdee93411b324b8e10b6a142d7c8112406d32f19618ad55991cfc90f47d628e89a1678835f45b4f32ceb241612c9abaaee8959cf9c76da8022822af6a967e87847acc41fef08d87b2237bb4d9a793e7c63e5535914fd1477f334c3ed523f7ac9360dca7a9832480457d7ef63b7cbbb83c11932586d41bf2b0bc81f0e0edd1a00de2d715efeef3715f82737fab942996cb49cf5f63bff304d8e23bcf6769ffae6c372218bece4a66bc60ef4104fb3ea6d4dba9384e87996cf16050821aca71e5e0765f6db238b3661e00c413f1fe377f7c6c805389415a6f40cf01fea0b0d42397f9fc5f6375cbaaefdfd5db218919d490e47b0cd62ec141959952e78ade7e6f3ee4b4e1c735800cd03d346e74c5b6531c1b98579d718c9eb3df71254a88f99ee833b317f9b281cc2c0928b09c123856e30c42ec5750453c7b0fe820b3a2efe02b83e36bcba3344fd272f8674bcd255047161dae8656543f6cd7fb9356c8365a7733667cbcb49eecb4b5ff1e91f31714b02db69291ad7bfb20b0e12790810c483e3d20c108bb93ccc1f31d83221628f8a5f7dcab5d05fa4319df315a4112409b91ef18abaf81a806a739120fcb441d3099e86d4a97010acea334b867f685757851fbbe82d83686f2c671f25b31f922e90a367b806025310aea71b090d65fd0bad33f69310e93f277580985c8d1310cc54afceaf3afdc7ee763d96446ced414cde8d9fef4b6e2f873f9868dd395b79193e1db155d1bca0e4ed3edc2155f603bd2463cdbdd3fa5f18bfe3e04545114ae0fbed2a1e7cedc10cbfd5fff385c6b8e7527732e989f4a6acccfa4d9b3067f12f395fbca48d8b6c9a3e4a7a6fbf7f79af8bcba08b454e0d7010efcd9aa663810a337ee56469738ebf693a804b61b2f2f33d7e0370cbe5f122aefaa669c79c29b3e789c6114791037fff7db3546ac70a5032425c55d431b347d63cb808e98c034a034d637e7dec8fb27076c6094d4e21ed5751dd8e21ef1139929563dd4fd173e40fb98ac23b9cafa7a4a7b1c1f385078fd96f2ebfa92e2790c5e0113c4c28007bcfeda38b06f5da7e3bf60593898af6e80acf23e3000f6811594e49fc945a214a134e8fe22f6d081c456e82fb4839cd906bdfb97698be7ad2bf99795dd754500b437ef8048c572289b4b3179267dc202bc699203c2a625f479764acc4d73b534e5656ecf8a8b3319502dda95748b4de8d27419f23c8691983a19434665cdb406eb5740d43908910206f7e54b19736e8ad2eff910fa7fe16395eccc4a1d27210630b2efaeaf1cfbcfa009a13bc83c3d20d2f28b50acbbae0c9293221a5340ef3dd784f659df3e8838f273af5526b93f71d18e0f9efc82b2cb2f5430d8b748722e9c4de76327417c0b2ddbf90c0a1fb31e623b4d549c5505a24e4b19ba0aa37a302d715dc87314987cc65074090fd723d88f21693f21f5df8d006f0afaf4df9732c93eb35aa9dd65336c5ce4672d2b5a0c22710dea62bcdd9f4772f7e737ef132bfb59d99a3bdc2670a0a5915ae6290086c24c7c1602f6b060bb39ff187f10bb3f7442557696e943de62d6a90cfcdac9bfcf6ef25d927dddebdf51fdb8856c2e384b44749c31b2179bed543022ff3248706340482c3cdf7fa71d1bbefd99aaab1693fd06f0176e5fdc97dee40d57598ab1ce8d07819cdbb23fb2db14ab33ce6f127d522acc6a945772f1039b03965b3487c79cb4c6a6aa6f90bbd76a6f9f97553366f274510d2e4bf9261eabbc8dec5c96b25f9e2e5a6f715f048b2412f1fcdc243a8051ab5e61722ca66c2d0ec397919204dc22ebf951160c91a3604880e72520138d38f31cc2abfc204d82fa9a0bb7ad962a0f83ddf97fb65b934a5af615c23aee56d8feedd155e9c01ccf201e8f453a3ef138e02e7f08e15befeb75a16d3812c9bf9464c92f7b0b493e82034a758291115ada4fce8d666566e28667040f6e01b5de9917795eba14cbe9969b0e8fd7f2fa6feea2af20f213657a1dc4c0797e28322ada29d781b0d78fc5e1f2fe2c7198382dcfb3dc512f90248d6a49aacd991ea77d34348f330e71436dfff776eaf69eaaa58d50f751438ce7d5acabb27aea197c2275a85121454a51c19f406be3f6262b45b804fe8697793a84483ff0aa6f1fc0d5b0d1bd9ea1bc1b439831ee5b9b4d6937e20be05fd0a4a655138b29fcbb086603fca7c61f39fe1fcfafb90b3ae89b7f446684ffd6e058c49aa5455886e63fb01e4246c92f5ece028567b6cb6fe0a4d8cb566d8ff8f00403220616981bdc154e437968c32487d263d677c35886e530d57433339a72121662c4de2d278c9e0469b1cfa71bc03aa5256570a74e9d8720b6d46385c7e50022b91bda9826df4820aa6d08f04e7c2b32c87c34d1e97904db63c97318eab4203ec9ef6d8a7ae7bc000beeac841e0151f56a08e40c02e41cd9d05f1572af57a03bff14d560f00ce60cff1d8adcd8833f5efb719e3b06500568718a9718534e53d8bc2c7285b2b52122ee97196aab9f6acf49164762beadbc0f7971b0f7c90a3c202a72f2b65acc940961f3abc0ed0d01f88a0bb9f20cb1d3b7240e9f9d04c26d23b029001d7d3dc307ab050095411a4b6d199b55651eaeaba0939b49dead6317e0be8558dc1faa7f2ed49c1e870cdcd9d20ffa02a23c15dfc8c99c8ada2964a96bcae65148f021f23ed2e5e14be1e78efdc8fd16301c23859ec22e4bba867b4ef905e5cb776647cf3edf33010f5dc2628df5a8b285feae5e3280214992ac99e92b9c6cdaea15ee450870d3b42adb545f2586766c6223ee31bbe497cea00a3c7e301714d6cc0900723df7386d562185346cf315138f2dd047e5b71893d60b72c443237909c71e3b590894e03923da374d8cf7ed9dbe5c6a6fba22d1d0bb1290d077909d0433c3444008d93c8eca683563c0f56e73597ba763349102f2f46eec2e8cb845215e05344735a79c727ae13d7a2164a882e0fe9e88f60c2d4315301595ffc3eb063477957d01b5f5f6ea39e0ea96355f16f7f8b276b8a9431a31baeb82e0234ac90cc426390bc4dd0b7c862cf18a8a987169c355c292e7f51ed8d928f68b874099cfe863422c21645786d382b3ece28e1b22b4546396e7429bf82235ba4bded306121b7e1f350ffd44b037ccc7e7993fcaddc477f356eb29328f876955a3929bf450dc40fa160f491d0782396ae5dd68b5f22478ca34052696667cd8511795e5db94e2253930f2bf7a2d2a7134a3abb9f9f7fa3217d548d0099a2cd932a66335d6a3f7993d2777ae3499f6627bb583178709225664f72b42fcf0111b10ce9db3ded7af9e06155483949b058db23d12bf1ff3033052c10f8cb2815294bcc331886eb15562ff13aa370dfa80e5991b5f9d96a6151ef73d782b14cac899ecece7bf6e85c744d8193a1a2da774d3c610590084c6f0bef9f9adbf763bf337777a967a5b1411854e2b78bec43f80c9dd89aae712b0914c00b762a2a79a9df2c4a6b02531164896592defa6cb54d4a2e9eeea1976eb10f6fbb749cfb7c877153f26d44a07a3b7004723aac06ee9bb2066a6de50aa9114884c210ae69e2dff6686e06a4e59df4dfbd72da33b256b05b9119ee67b753a7664ab02aef200a90dfaf43a5e4aa0d91fa84210e2b5e1c442788f4cb4d71b64e8adc517dcefea1a97bb51d1fb210ffab3b2211a0537385c337e55dd7a83abd2f39ae9d75bec890d9be8031c16672e2a77f91114df19158c70181d5f8f43a8dc3b262cd28ddc1a485b917cdfdca1115962aa832a74b448e0531416b53043f8bc7fe0a967ba4b67892855b08a1d27219e55506634a72e25a490bff570d21efac780db6073f2698e3b295ba3949f3f81c7a858e63ce61db5e82bdad1f860c47f4b88191c2e7066a0da11e1ebf30ea90d8b6e39bf209ae8a88648095a74033ea38515eb6a44809a8440fbeb525fc1a069b4269d481490e32c55fd3c77b182a70236ddd035e3085e9a24262657f775bb99aaaa785917e32e783bb4413f7e62a818312acbf3a16753c52c6057751a0f3403be68c7792fabc3428b92c5e70ca6b833b3aec58fe9397c0a8c11a5c98b12d288e20663a6147171bf92352482e86e6cd714f01ad39f81fb2d9e0f1a0f38a323dd51c193fc1cdce3f1acf3ddc6bd5c78ff51746a742f8656e122f2f3cf6a17c1eb9ca08e302d2257411da47ca07b5a881cf8230cd33a32ff9e1d4c1bddd7359fb2fd4f16e111888cb7e7dafc41508f06d23870fc25aded68dfd8a73d9b20d55f6ed47373e638517c8300476ae7afed6bd8ec76f46ba0f46f679cf34c1b4e89bfa5de39c0a1004ee568a59b7130285b5b63111d11c0fe40050ce33770dbfdb82c89e47906360c616679df1e18042f91efe59e2750a0e0a8408c2e25fa6ab9329a767e86cbb40a58e52d49ea15e2f29193c8fa15c9540834e807b34c51d019cf6ca5bb38683627a993dccc1235a8b4dc26d04e45a2afd7d55a153812e60a52442c399f452c0500a0284f1260dac5c72ec6cd2bf35b4a8ea4d1beccbdd1cf969095d83e85749c3db79f69e7f0e3fe28ca19e042eaeadd4a4be2ad3aac47a68d9987189f6c731a274ebffd786230015319b8d063c3c02a63f1644d29292cde32465723e337cf0f132b1904588b0bad0d2e98589cbc332d6f40c556aa3dd43be9d68ef43122235005b6541c31bf4b4e60178e8c45f8fc93ffe1fc36b43f0f32477093812afc74b30ad395ad6fae4911b48b715a410651b2d42664ae5bf374e10ddb32b3c5d0f17c3a2d6a9537b3684b6f97f69ef7c26eed77ce981499046809aa2be27b0a4224e908b08b49bbab299968de2cf3bc2b1888d9fa3408aad2c7657925048fb70cbb8217e4eb7915ea2b65e32c40e2f969208fe39774bb4c18dbccb00f6f9a7514a80f0bfa6e51a8e790b939556a57d8b32be1da720af980ce799ce960e22c165a44e8278a0a63bad886664bfdd37b49a00eb0c9d77130631be26173b1ad06dd0eaf151cc832da2ab40050c09397ea9d1500e0fea6bbec09b49763954e743cba3ae5eb072223e7bd8544eab01aafffb27b023253c43a208b6d5652fdf066fe41d4630f3f29b9b54e964fa7b7063d52b6114b922c77df523fa462d7aacca7c8756744b32b04c8d67b62042aae128cf75e563e991d46ca8830b797d4e03f03d46a4255730a361da9abf9b821a313c8158d9794bef6e5a993170a91a47b2194e2e526f7390f8f01da7d75ef5996ae037275ccbb66da04b5cbce4707dfb036d7a0f515c4b414cb1beb5b4638a2dc7883a380447445e6ac0ce570d62b69078f30f5f28ff1bd0eab3448b9f6428cf3f56c537c0be0eb349fff60dd79fc2506636c8e692b772e322a968d64c8950be356b027a3fe86a4019bc3f724642415e2d585db0ede13630f3a4c340d117426576878f6e620fd67d3c1ee5d1f0bbfcf3c64b36d1c24264c1da96ac77ced1647b7a46ff430bbb1b352051ece9652e807cf9f502ada762be60d096efb7a8c503dcb147a5003c2befde2c140ccdd2d9708635ddaff36328d7b177354efd57b8c9fa1266b63f1e77ed9bcdd7107d606c7f178ec854509d47e9424c8330237ab960a90de04f233dfe4b31725d55b09b95dcf04170ca0cbd5bb463b8834a73230c00628b3281b4c7e99e483c2b0c4ab4d3671015f62e710d439392ab058f4755252c8314331f4366ff692562e241f15ec8e1de48c56113067f8241c263973557094dc0ce7c21daeb59e99d2d451dfa211d75add61cb603c6dfa8a0e2b25e3dffa409237d8ed3d0e24f08c97a4e2eb2b0837762a85444ef5d7303436fcacc67f1878b4cd47682571ce2b60926738a4c5812b3b56f6b1c76a0e21b4255727b9c8d709f81e1591e04207f8accd81ff781feef5c5c1eeee737ec5f0c61f3785fecb0b0be5463759a0acd3c9841fcccd2b95284313df36ebb6000957b53645b8132b9237e1de8e23d11e5a3061cce514194bbf2551e235eba09f3a029f0855334ae08c3fa3bc3329ea3f21867712a495d9f3b5b2a9fe9575eccd6814a418d95324a4ebc71f9211430eaab9974d39a2343e8a9c55461788149101b75b4c1dad7e0899f3a2398fb23ab891face3020d2aa48a976204778f5729b0eee4a6027940decb86bd216aa5d25f7801a049870c8aee20cebfcb4e7082376eae71d910e800d25c2554c1af6442f58c7f7e7acb2d6871858a33b2b0400e66374182fe113fae8627650973a851ed66379a026ca28d523172624ea3f2e42825ec977b09f1508f54ca99b4a9fb415e143a19c660609550d58059b8cc3acdd770b3e57d5d607057e8ed6a3331443187bb58bc58cde0cd04992e2eba23bcbfa56b92b6bdc881a770385af2553a9262f7e15c1f7e41e52e0db95c5248f208e6412e8628dc64a0103c39b1b16bdd65bcdcef15ee55ef3396493f58f95bef1d8909a03c150b6600178555aed6b10a46399ff37752ceb127ac246a79b92bd1f80e6f7fe673ff8673cd80ec9c729b19d792e55e0e8c819c29d9c8c97f5ad67596a56948fc495311b10b012c537fa598fe2f6b0114a761f8c9b1d43da70d63e4013747d5692e38fd4062a315786c0f31880d826ba72d7d417b09db547a9271d55f0925b8effac33b82b3b585c7f55635d29c68e44452e49f1de93e76d8b2d8d8b3c7d83885b50ce7b026b839ec416aa5d5e423cc6ec74ffd9cdf56b81d8b73e666764548d8ac2cb363ee594b96636f943b71f26b7e8376bbce80b703a2a20d86af7a366a202c04fe81a77e3017e225a468e71673ad9849f54d1c7c59444d757dd92864a6ee9749340297cda7ce5b5961015a7effba5bd6b6966b70ef3d4e04fc77ad582a7f153d5aa01797611de0c1f6f80d185d079db55d2f2468556ba0312a4fd14e89a978a1e2c1a6316827064ebb70d01afea9cb5419bf37ae94b8d8404771cce4b8c0e0e6ad91efbd012fce412b87bc5d5d1e6cdfdd30a6faa7844122fe2c25cf153d6f5dbd6041df051647ae8e7c71e4fdb103f65010c9683e84e3e3b3b5e3696d0ed8c344e21aeb105e07ef44b60a1316ded99b938c7a604c36df9f2690b65d9a018831d5c14b4d62240e1f54a2fea30df9afeb230b8a0166e2f50f3136a62595f2eba71d5069d8d68434a322e29830e5d2c977e512fe672a6677675f4fd90bf056180aee6313f169142a0d163df5dcf0e8c78018b8f3fc32b29a0d3ab20539a62ea3535cd01a644dfc3b21834610b5c77504ec3afff783e43f3f21b8e3ce8e3af28bf004c2bab266657faad53888b90889765648d29af6354a977d7d10d09db603793a9fd0c3677fac179d02f679499c61428b61b4d6b2e38b90ff77d6810b01da9c5f3f8320e5b5fa8b2735e117e6f778d186267bf6c1c8052f08135eafd488f1ec7dbc31a25deeb1de657da8383efcd5275c0147555e21e1514e38bda69d0f164cbc35a24f3084f3fc9d037016f250f708613f1d14addb975aacc1ea141fc74cb27876315b194ea5fc8c5803a9e3ea962f6f19185a51a246f7cba95fea0633f47aa3d511ded73c5ad640d4852163edfb412c3958b34be0444fa91c47e777614760f544139e673dc495bd7881bd8a6247c12d81d8504265ceca63faba56923b929c104b9124d57d34fc986b30c102f8a10622399e1f28341d74473c812fbc76d1ee6d3671dde51fe4acd47433824408e82065f965cc3e286983f6932831a49c768282487032a0832a8a4ea0db766c9441cf8598a1aa2a1ac2a77cc9cb8cd0ef0b5a1585a235b611026ec97739f01243f39456c13b9a81d17ece5242a8dcd933a948eb2dc01de591732a03e76cd4f05633602631df8e4c519ef945cedb6c67682f2f354c01f9e8a5892f654655d5f667bbd13bbb6a7c936a033b69369351a71116877205d38cf70a43b6b3c4453afd078b9cf9ba7de2f954afe5d470484d99cad735f19e90f67572b943bcee16cfc9e249e9c810919b54d788355e1ab05dabf5e628b007e334dd8e8f263c2072cc776b323bc9ba44b7e567fadb74d87ec266ff30b39992828ebc675e161c5579f3921285945fd0fbf38dade79a93a5d2472681a94325d1411d3eef8b366c0041b079b4634571e918904de6cc66f9054128ea20460530a6a1472125bcd89bedf69068a4ce21abe352985703c1f1b4c5c806121c4577af18d1ef7760ac19698961d3abe49a91036ddfbf8acda5931963f6f3f201121733f8c3837792617e543ddd7fb21486d561c89317fdef684fca4cfa9d930e3c2818ad02f7da67fdee5f2595d953babde700ea1f02262fd728f7729896043255ce17389059d713f3771c6ebd6140b848c2369357267614e23e04ea360b82250bdbd5bf10685276dd89de3dd72b160220087bf76024118b229ffcb9a9af90d73b05fffbf34dcf9365bdea53aea88e398e8b974c36532bfb71851dd60d3437428f896a77536206db82c8a9e61c071686c2475f5a44ad6d5a58e35cb5b0e4fd5993b27daa01880b2db8ad4f516010306169d98acbe25ec62b3d9a930efb20b5a60e4edfc75370b4ba0e3507f5b1d28e73d257a59d467d3fa617a33abd5776a8a24998396552aad9076aa1c882e12a7df682e96b57946f7166e2c84c63e0bce2fb38b9618500d765ab040839ff7bd08fc5937de066b65f6619fef01307616384c3cc9909265217546f0da5e4a62e5aa4ea575ba369efefe605c56f28be2ea7ad3134843ff8d21cefd42f7c089f6d1b2a1614bb99f9f3dbbc1cac695241247b4203e0fbab18106faa81a4899ede001f64b829c3ca639b111b1b47148f75f116160ffa484b0590a91318c934795ce2e6113f5dab44d6549bf239ccc1cc80c2596bd7e12c9e90f13827cb925e4f748fd924c460da5c0ad2ef83851bfb575297ac182776811cb6a2170ec7687629f7da7260346b87fbd072feca5efe570f9d0adf105751c259242190cc992e5f7fdc0202ba6284a8fb9fe8b0663cfd3e73f3036a8255b041245631c22b8ab546efa65dd6ca03676c9dbd891f3d8191b4be9a6ab8ba8da8ca0b4a03df90137c6e4de3a01d5ce63b75e7cb7d88a0716eec876da05618cb68fe0cc4220ee6a5a539696baa8ed122029821ae7243fef89341fa47b80d7c727a8425f4a147df65c6a2da1f784ecfbfe5296e84bbde6f6d4c0f112f0cfe5f406c00b0f73280daa09f747d41aff44380431f3feede05054f8a6bc87c0ee986429c090243d3aba05998c156381ad3925d86f14a4b0e57271a2795d77f351f84de808a4cb7c13ca6a1f4c1b29835e11626d28977b0d88684baa190140c422aa232cc84c372f35e82b6c992c1a6473a9248f340421cc572e2ac8d8d102257e57bd3fb1e10c7b2e0ba921f643238429237e10d31641b5de2370c0c2e2b400da5870ff23d9540c3f5872559924448b96f278fbc9701319c42ef7e87c4d5be63d1958f1141ae26e3a5678e1d92f9a4a72bd4fe44042bcd0aeea7575f5a309ffc4cbbaa9116ddb0e363b1a206f78e3785f0ff8a4b68b2f313ea6d27795da1710f5a0b86c4256177335143be2e552f316018036177177c8d6168331ed6014257960b7e69bef55d48f2a4398bf30ffc63f959a341de2073e91667eb3d32d876fba93d6c1b18d28dbd23904a285252402939ef8e425fb5aeedb7fb2b693c3c179ada7ca19a173d361111a66f7837a5349125a5afb924a2a91dc0820b22c86259739e113156854dd1ad924f599d0ea861083c6aafbec4861b480db2688f9bba94c5139e5b9d8450a81850140074ed040bee3714c12ecc3141da177321ee4701f83d9109559056f113f8b2e09be9000581078b442358170c1c74595ad2fd9720ac7fbeb958ff1eb0e16cb208391d30fbe84657818052c9887cd3705933c87b8db7670b697a0fa16d51948188929086cedcbaac7db4d16b497779fe4a9f9ee2b4935ed6ee7f654b0abb3ee8ad65ed2ace59bc8a5fcebba38e7d8e92c9de0386309883b0fe63c135b508c232b2373dc1fa4658172dad6908b2313d512816bdb84a04ef65f777efb10ed49e51f5b055d1f3b0408bee406f279346374a06ccdad0ddfc6844fffe005022a4a492ebf9fcd0065277e0d9a9715ec8446bd2a72f84c72e23baf4208ccfaf3f181ce33ab4ec93dc5a66c953f1c973e32263dcf5e8b24e232ffed4e7c729aab68d0d82db34b2c2f88f690d09737264dd5b5bf5ed73cae82b31f074c1029ddefb607240418c34f5f133c7266582d0f822111e0dbd011e644c30ec0780501eea66e701fb3466a5acba3b95d77d1ba2e1f13984cce00a99a91f2c222b2014be7354e18dc8617d44afa0e1746305f674dc195b4f773fc8bb6f04a393d73f69fbac7e7ae77956225eb2e1ec9a185933230adb4bbbb86c3dc8f90e3276b46606a62f390c7ba8571ca5e0d21ac84fca33eae24199bbef8f0da35b443e21c341d701e1677261c1e483d67e9451fa6cbcd9256e2b7fff4897657a0ada198cddd339b4f4281e54e9c5080d03bd4f182ea97dc24a1baf77eff050e670dd2a575d80d71ca0ffec39c81ed5fd174a42da8e17f9dcae943baee37cdbf302e9b04a2bae913d4afa7df222c85a5e2d637ef8eb186da1f9ac294a77a336ca8533c51cecb905d8c83e86240a5b0bfcf37483d54e4f3b29901ee8768b0b19d7649a236c3a822475eb0f16c2e37aeb2a251266c877009bbf5ead26983691aa5de59f83c44f8927298c0475762a9c443b37fafdb2ef7584ad02579150700a3ce15abe67f786f8c840083da241f0a91679680899ab643c3bf72701ec95c142fe71414aebbbfb80f8e787110e03415d29e7612702c183839998eeeded5b6cb14713d05e97e4d09edbbeb6fb2c88297b94b57b85425ed6acd227a8cfc8e996f69bdf0a0cc90505115e12f17d8e36c00624407481a91c534dbf13f50f578bcca3f7a667a71a07d61ac149b2e54f3fc0934d718376d5a0e18c848515ada3a1b1e038a219d73ac25863236aad3141267b54ff44f3b43af6377c95f059c3cd84b1f0383376a6b82e676beecf976a0111327002a88a3f44f19ceeb0d40e7874941de4e917f66ad8690f8810959f629f7a23ca85a988c2ccefaa8d237ef06ce418f3edbeb75377a595856afe5e91d0b4ca9aac165ff6d7b4a25d20ad0ee82f8e224f064c6b6bbffe8b157d8b28c503268f8b1129f75f24fd52ec0dbad5053008fab6b99b079009ec44dc27ff6003b81746383060dbc7504939b0ab09e3b84cea547d170c51c93f0048f815b414da5d4a18f38b82e3094b749c566922099712580b37474b78c99b336eb84c728c394e918061e9067d6c924a9b5a7f00ffd7a8f90ba930f533de19b2d91664c789a8f49abfd9172f7c7516d7d0315eb6d35b09ef0f08790a98e5488f31d6ea1f1d97d597a7d2bd3ba93143289593fb9ebdb96de6dd4702ba58ce1bdfdfc6b98cb14c6d8551a3c51f2ff317b4ac823d8a542f859bd478ac1e4edc618969ee990d8177c3124562481b4f6c46cb5b5b6315caa98003cb6df3c4bc7f50efb043a325c94553e9fb139f03ad59ad8f7bf54006b96a067614065d383dbf690f4860a611985ae4035612e0a000000000000000fb96269a43f661871dbb5f875ead7f000aaa3289f0389b3211f2abff1434ab316814f210ddb691c89ed0d2332b4758c08d1e5a92aa228f781025c21bf531521 + diff --git a/zebra-test/src/vectors/orchard-zsa-shielded-data-4.txt b/zebra-test/src/vectors/orchard-zsa-shielded-data-4.txt new file mode 100644 index 00000000000..31f33779a97 --- /dev/null +++ b/zebra-test/src/vectors/orchard-zsa-shielded-data-4.txt @@ -0,0 +1,2 @@ +01045657a7f63b0b91b7853f04e96e4ecaa76fc32015a3882e205ff0e38c5565c41c71a8b38ae742735596adcd4a876186aab5b89ddf29c72fa3f8a10a689ec0db098eb4682e1e4e46a10d7b1ac40f7c040b42743bf9c9eeacba41c3699db1aee5a90e5a955705db832d49c84ce14e83091ff5076b5f902fdc2c84629cc99bbda01fe22ee765a73fca7a1ffb8bb32c7d3c3233942bb5c64b573fa68421a0c8cc32ac3642bf84bc270af56491f5a1933bd3eb30f41a36ea37fb0f6daf5b7eb47f729b555eb44912f2a8e33c0ebb89833cdb040665f1849b1ed716149c8455c227e7d3f909c8d01471d017f58e1b0a254878310c4923094c0388abbf814a66a898985cca42877582dd705e1d19203190848b8d9aa64f67ad49a832c49229761dfa0e87012b580f6750a16c77ab7c48b847442b245274380fd469a049411c5da871085de88fdd20d2f393bf14dda5ad2d7aefd428d8e5bf19af6e720bf1dab9b857db7bc5258eadf526876f79dbd1a0d1a95c4ca93527fd7524b3fdf8389af606ee89600c549b1d0746bb88933f66838e3297d8576aa7d1a568982568671d08811c3b4bd7173a7a227cfa4fd28d45518052a5e8e683ad79831e00b59877a66e62097892b87623468d862f5cdad08e129ea243941c935915871619fdc4cdde1b507a387efb20a653f6d6d139eadac1861757b59cb15ba6d2835e7937cb464d40fb4d5abf2c82d463f3027eabcbbc86f968d401c4870415d8cd2967eb275850128ec3b2c730f57dea7de5b9dc874357cbaeea4104e9b804dce22c11ef7a07f5f3813e405bf5f1c8e5f23e2871bf8fb283538d1b4fa8cb9911efb5e80a20d7f9b3f9db337a8b3060080f555e21d6ff43eb170683fd3e6b21e2f9396ae8183385038a050dc17effee95ddaed126301a2407ee2c90bf10f43df756f8aa87abdfc90b5c79a19fe468df2061846d4e788ea07ce4b38cc0124d69dc37144a56b174eab33d669418c127323aa9e25b17c945747da687f07402df66ac228e58e88cab652d4266660116b05550d4e6edd6741040256cbb239698a37b80f3be9dda26d726ea06bc1281fd9b0f16722b6d7f82fbfcaf79fdc087194421fd63e634796a6eeb44104f47610b3d4dce52a0d0556f018acedecb39189d10341708a95ad8e103e5ef53de85574c1c734958be9914285bbe15d17b5edf2f73deeed82be29264855efd620f321b634a381ff4c1ae4bd5f40e3913c252b8e49a2da80bc9e3d5d24dd276f60523ed758fe266b86ebf4ace3e7a6eca3cbeebfd4d620d78bcbe780839f6a55ac5269b8fae359202a8f9507dee1948b008fa13f0ca7ea637e887afa718ee94702777b1382b70da0c8574b3078cae818041fea38f16373334bc5994e6deb7ba58872017839c86ca0aafbcc4f15d3b01c3395f8ccadad20c452ea435094063ea3e580b8621536f51ee1742450b2c48bf2647a09d4babd92c2e92c89762ef4d48c9881e8d3b76e605678586f658ca06df6d533cfbac4aca93b7b76e09408686b9be1a0b61fb36c2b82e14a0202095e54017172ecdb8a2922f270fdcdab4e9e5af8aee777a5caef4f614cd6e55013fbd25282b027c17ccec2b7c676a718781e559e7c755ecb70d386245201411eb5520e9013156a02918cb299998e843d4850b5ab52cafe429e3b31f96d1cbf96e5216d4777d4aec8729aef0739e9f98a89b74b3fa58da137efaecb3cd5cd2f4e4df14a473d59e24fd1bbb1e8db8dabe2255efdeeacbe4c278645230f8b55ee65db1f187ee97f5a55fefd54a80bd99304448c8098092a4644e16a8f321365dd05bf8a0d15d19a883b43c4b540bb13bf98fe0e37f5970bfc91d31cddd34a52d27c82e0aa03f5a0c7cbd1f56d3ad38fcd8f634f62c1bd108e47613cc93309c960537672dea56dd014689ceed7167c0adff0954acb51d746087dfa5c6eca234b3918373af11559d44c766cefbb8c0f597fd397b658ae1cf7ae32096dcd2bd15f0aa9273254f0be42cd50dcb40c556712f2a3e2617755e7a9db2d1ea5115e14f2fe1651dbaf639c3f24cd32430320d15d459fe509fb64ac4714bb770d250f2b750231409c090a230fd4cb9c64464389e64c56415490fc699b85486b099628481b7540a79ab986f2a8405176a6ef4741c27feea550d7236e144c2396dde91d56d407fca11fc84134b1d18f35243ca03df9036d6ca3aa39103510bb5310e9fdea40d078bf27cfd06e64087bfccb17f5a6b03e7626e934ec6a4ebe9f38f98a256c74cca06fd93c4ceba22dd46229e89bc626dfc748df1b61a7ee95a13e2f79803272651ec52ab087af7960dbdbc0075d029b635c167f88d48e03410e88425d6581baa36982b6e5efc75f5835e3a576f22e86a1b9ceb8dbb2f1107d4764ca484f19c92ccd27288f1efcc7d33b1815b4c14f25048486d89495a496f3de3b931818b0553a4fa34b261301ccf52ceac03a9d4cd631fee99e401b1de7e8fa641568d769dfd03e44432e656d0622f335532b84f83038c07233121243010317af6e315c0f8a3b296a958bc7fae8fb74e1e27f9e3823dbe6c2aa3aa2ec9efbaee4a280b6928933e1ead6717110ccc089042f5b11a51109fbc1f1180e4ca77a2f1da176565dec82624ae3cc39aaa74fde38f81dde34396661e4cb4bacbac9a78b1dfd7c22417054876309f72204714dbc906d127851b05ddd06b48eeb7ed05d5c33fad4436c5b8d5aab1ec56c06fb7d46c3998e3c44b8bbeef9fdcf4e751736ea11ae6a79b9682553845eb38fd9f9ac6814f530916617dc82adcc374288ab19dfedce2ba4084ca4a4a180abe2289f67468d560d018ff6d4e3b5801c74c0b410ec726e1022f6d185fba9934398f1a5ca6ba4bd711c3d74b37c5b09522a7a9c590d8d740bee6fe950f5c246065ed1d949252a0aa2c31808469efebd6d1421b3dac541141064d0d7c6288ffb1ef4f1ad6bcbc797429174286849c57c77b7fd49569273cdb125a0e6d5f06ed05e8a448b36d9306fc7e3230140c185504798c822ed09f9f494759760fa1c496cb127fa625f3d6a0e470db81289d727c7bc06dba7987bc1fe1129b9d26523b5fdc4199de027731cdffa29a1b551ee479c311cb095655b6a697036fc656fab91a0d0029a7d8b581f06df3c6265ec176d02ce44638788530ea7a808f7e73e622876212364a83ccc435b7700b3d3a61412193a24502af998fc4a4b960cf662297d286b2ae23c4770ff47b3fe109ec48d3f0b4573c399eaab77638ee1c7ac0f809713715d1763fddddc6e13b713307359d68a451755872eab1b9186f8b8b9f9c51edb27659f61d74234f49e0aa76a4db752c455fc2475d93018d1ea9d2938c3b1686ede401ec607167b86766811bcaa0764f4223a6e31663ff8bc059d1ab82e56494179d04108ba27506d6da2b78a5c2527393f17d5aec3189b7c06e3c7256eae7a399e9e63efaf52445542587fa13c2d0c7c4ebbe1c8a32ab8e2f2c76fb331f1c7f2518e8876e6c4502b150e306fb8d19208d26403dcfbceb89b99d048f0e694d0cd781ae42998fd07cb041624c6c51c08c158a88b000363758527f6262a5d1c2ac88c7873d891ac52295803f93596d6926fa85f793f4dc05911ca3acdb9dc5f42289eaad9734aa3e76c4f38c39b0713f1efd810c0ba4ca48054b4394aa363429f316259f33d652e05ded16bde9a53b04c7aa2697aa9be8e4b3caba62620d1693675f968ac6cf0417a721b8f28519ca84c56995a1959c36135ceb4c5bbd24935b4993a96575f70664c0eeb479cf8b1ed86fb1095b4eec2c7cb2f9f20d3300938a3b293bf46c27bab17ab0cc6b281da52e7ce393fba11079f01c2a80c194c297c321b81852d9149c67323db04b57f15a7fbb879f6153d7dd9d2e924258cce846af9ae77f90673ec23342b29c7e709b0ea00d4d1dfa4e97fcb3ab79a715d33534a0febc4f7fa84a4247980fcd9028e793e2450f0e4be47ff533f2921d724d2cd0d2ebce935b9a51ea936cd00c32b691f84af198b08962794fc7f7360f22399bf8a6e5ab99b1916dd55fdb7af1b54b7a817d6640f873182f50b18305205f23317fcaf00dc2806d0c9c804ea904f3fa8a7dc6809132223737cc8b23aa3e5a4497c13dfe6cfc1b145e4c16a4f24a7914a0a14223cf53b9665056559edb234563c3693ebd6e05224ff0e63c72c278189bdd2cda047e8c206413a032034d3826268c5501a372e1f4044296bc9bde02f385862ac1175c4d1fd649ab0787791d9ba11a792d2ebabeb531b4eb8c61e961a7fda7dde1e52830656ed14af2a41f2ee26c0d8f7649eb34ce4fe7864b7fa4a7decca73e7c2a4b12c7c90a752877333da7b324a600014a20f9eeaeb1e0302f380c97caa3f4150effdb5c854ad661798d6f094226f02deb9ed20c1d94f12097c69d76b92d1fec4a846f026839419d67815f90616be38163616cb18e2816c688ac301fd55d3260ea660371549d6c9cb63111afd3c183aa0d2533fa0467816c9d04d01f54fe5cce2f54ecb01c9be5c508b7ca75610f2ad40f26c93361ad01a488fb4ac9aeecb58e503df2f43ff6ba4e1fe5551c307dd107f32439fdae2e2c43b6a8c0e1ad20fb90392b7dbfaf7e78d2c8937877c5b0f6eae2b58a2e67e132203087094f73655fd09caf9d488a81faa049f073993a7260ecf1ef788eaf578c11fa28d4769b3f0d5bf16cc1531be532f9ac89dff1793eb5decf8b8939bea1819aef444734a7cf0ff7e4e4a4721322b6acab8b6340af1497042c442f997bf2dd37a9a602a83b912a1a2abdcf128ac937cad5c40051672df256a2d586b2bf610300000000000000000000000000000000000000000000000000000000000000000000000000fda02ec1e22a9a7b6ae8853409e7a8dab9bd384502bcec91f852080a7916fc77ed9b3706a645e8cd7718861b6a647adc3bd7ce02cba30070b8c5ef7fa168c53fbacc007dc9100b7ab16d69b58d4d5617a2949dfae7752a3afd488dbd00182163687c26a49f25b50a1155ffe0b412dcc798022118c96e34850758595ee6728a981264971e7f70ac7b77a244d4ada133b976ab472afea1048a213dffc9208a75a06f5d8ae3803ce6f867a634ded599d4b01ad464e3e1d9db35531dd41bc731affb888ba8cd213c0a85dfae04f10085027b7a5c7b986a214f7158a6ea49f149074cffee3dcfd03a791131a0286a4a2b06a2016ccec8e7c67dc7a5f3838d96e688bdf47783d135580e1742d68c89fa9de5f73a456fa39e9514be267fac1e673e222b979b19ffcda88a6e0dd8cbf300e81b324bbdcabd24f3ed81ecfd527d415132e2b8e09fc0a6bed9be2d63ae9c3b5e18f19ea303b9bfd3208b9b6af6635fe2aa3e811d91bcc4a3f522ff6705f67d1adc05beef30f37451e6c58902e40cf5dac16317aabbd40faccc6e1f7dd725ea85534974a0c7786dd7ad1e1276ec79038c5f60a90728e0934c72f1d30a363e567e842d2a323803bb20de1710765845ca006855229f10c4a4efd38990084b46bfdb2a1c91bbc2e94dad2d8e8ae0350fc05eff450ce799e547efafbe77f67ab16ffa4376eca0a1b22fc421eede9091e3914eba9d5936bc584f0f6b749a60cbe2847945eb0106a49c43531d8a7040e1f1f47e74dc8bde0ea1427b0973483b0722e41876cab360b9ba407bf7118046b859c5307af5422da3080bf5bdb226974a02d0625cb2a024a2773a0c507172e7c6cea0719d09c7d32540026a89813e4e5e2ded17e05f12a6b36fa995d6e0f604e23a14d4a8a68d6909091f34850fee64d9f4599a6307369de4cd9c79eefdc1cc9e1ec7c868a5bf3fbacb2f6ac1d68100221da6f99c00ac84f618d6b57237e83c3c4f03cd5b262d7534fee127682ca86fdc739fe4dc0ce96db2a35caac25528a75999709fbe5bc16639a20bb4dc71ffc07320d6311c180ebd91d3133681a28ec5875d8638283d75772b178e6e061f699cfaf6f954680bacf232eea3efee50c1eb9c3135aa51ea82088222fecd2e252366c6a3112b31075bb34117dd45b635f5cc9ce31acb803ac91f3500c1cfeee9ec7c3e85d34e0c41e71b546cf6448d1c458256f8f5b5745fd22bbf8ab81c4ed7d6f7308f3689864dc201b3feb748da5f021f755dcb0e49f854113166a796ce3d2c91ee2668f54ffe8739e09bbe189ceaeaadb6cdfee3bc3a4526241ae83812a3e45839245500ed82694ec79f5bd720d286b082dc3fcb913bbab28b7fffe55fa01bf70630040bbcf116ff5aa8a686e12d06d33be1bbde50504584b7f7160be802c30f4e7286e3acae65e3b20bd2cf8664797ec242c8d1ec1911542caae6dbdc41099b46f8e4f39625adb6ebcf2891e0655fd0a585938bbc8ccbdfac45c1cf51ae5ab25922f708107f2b26d994a6c58ab2024b614e481241dc30fdbb890b05fc5824717bb4635ef1473b596ebf5ccb1155480c9231d9b80729e4ce1cebe59d291f9c5b4215013425fdb4c889f307626952e3012c4e427974e483a0b2b3e77386b81f0abf11eee92bb4652deed3ce7522b94ccb485e1b3cdfc5ef7629cc3775986c006f5baa41a10842edc723da5079030be0e2960439778827026901f7c7f55794e6457d781d6bd2f64bccb0b95f86c562e6058dd7f179902141e5a03539353cea2e9f60b7c37388821e3e87d92fb8c4ede7e8568bbd2a5f7ad85284b545549489292bcd1e50431ac98be4e5268bd815a20b70989db00006cb26ee9a46eba4d47bb18f8e8c41355e85d9a417da69965617ba90b42ecb0fd647f88d85504a324856bc82858bfed6611fb044864dbec0543c28c1fb8df2844ef3bd73a31289f0b3eac9ffc007ec130d2bb0e8d00996d1edce7ba125b52d1dd3317f919ff4082a69b76b80abfd932b2e0a8705af68053420d53ed8a5b88ce3ee07cac8bbf31d4c75a312c32142be047371667d486ef6aec7e87f840b42a7be686c0f678f3d4da43c736c1a0765a012401c27c58d0c717212bb1ebbd9ee2292976bc7d02eb1abdded2ff6740e0cc4a38701ac7ef45da612d01e67ea5b072210f256ddf5b8fe594dd78f21afcf4bbf20ba81df68698110e371b5c79597dc6c76f66c9dbe841a13278248426ddd8865ff44db1efce92f379447d4797f2aadcac719ad86a5353c1567e1dbe7e77a02e94525eb49bc47bc6f7755f7b813c917f788085543e39c2639b349734120f7b39ca231a5687ee4f89462711cf09cd4048871fa17208cbd6bfe166919321a4fb5cf7f84a3426424c174938e97d8b5cb171836acfdec2f9c4a13047fb43585d6fb5a0c7beb4c81516ef2eea347671036e2e17c41addb67a3a7ee4a53eb7f4b4c7a9051133c63a8d8866e4bd539827bbfa65e54f99f2e03964bd5d474e1abd2708c22f73b19060569cae8065540f7b2e42af82bd7d9e0d92b69cf9e0679fbd93321b738c93f826e0d86ab6b8212a768b060dd72b95bd82f358989d416cd27bef6244c3798ee25e54bc5f2db1988114a7295bd2c9a3d1b75253074631cb01d653912bb9013d07380e0cb88a47c0c6b1ac0188a788092c66a33716d874b51460ba7801d1ab1f9af8076db5a2f43351d30fb39ba580243cc9b0714d5beb46a70b7e8d6a16ed00e234cff498096a6d57f4e17c0fd4940b6a08a2563bdc06bb6fc489960099372265de1cd57dc0e64d9f0e7e3d877234fabfc87a075bdbe47c51683d046c31617fd090ade8438a686cc2860cb0773fbdf8018ec89c3a67053231c0f7313f9705db54215725c5197e6f79dc73f0ddf58dcefcf4b27e9d1018b6842621d4906e545cd03f68b4a073410bb10390ea252bb31c5456a017c16c19398e9d60ed072758f72b97683af005f8502e9cceeebf989cba68906b1a3bddb024daa4348407f1723a16d5945bc9c829a0ef637a881fd87f35ddb8225b85672b3373460e35a405a23024462cac383a1c4549eba95b88902a91dedc6056d69b15473a16b20283e5f9bcb8f6b739896c4bdd4f2cb4f9cc084795b5f8204952fed1f37dd18758723f6516839912b8556f420d4e403bbaa55d7e96ca0e72f4b827da366d4436e6cd674b60ec9b9554c71285a408a244a903e4efc4ae345bfb43922f1cdcf34a411e995901c1113dd8c2e7ab0150ccd3ed1ad45d5e4f347bf2a9e5a838d00ef74f929bb1cec67c20b785399153b99fbef3ebc4c4dba3c48ab61088fddacf078d4563bbe40b0dd68d03c41022001d072265826b45dab649e2b8641795dcfaf6c37d82b3f470b824e3f23772d7fe123c4a409e0366a43f6a89871590196a012a1de65ddbc6956a543f32264fa0332369bc856fda3105980fb835bb6040033c3b596e771f6e214f9d367733b84c4877a762e3bf47efc3689131c735dba5fe5d22d8f75898a67bf7b90ecdfa938447ba1241bcaf76fce84f3173fcc6a31a72e108de1cb2e14f03a78a1afb036c1041fb4b24a4b8d8ac99584fb37a58d2dcf3b328efb90815eb850c12f576186adcd23aaf7112c6e49d6e70c85309b3aecc15ee24a53a74083017a165a503ba5fe99fc58e6dd9c9d4b99eb5722176fb8ade041dc15588713f9d9ad861bb3291e394dc18c0b75599da9194484df9fc8dd94901133d23c4aacb74c937c549ecb189fccf99d19044035a73573b3770669594614cd87f6e984b4e205278f31a51c096da5b29efa974a93cb4b79f30b10366328f80d0cab3601635175a2e82c6447a095913636c009c414ef2602289b0511c7042ef0c2864b664bc9b16187a8daca950bf54643f794cddf898706b6e3136ca290b38838fae18b06de6d66becd54bf4ed3c2e37b8baebae771b9081b3a3b25cf07afd3c93aeeb0eff97b56bff0ee80418d1c6bb9a04544eab9801a511ca96b2ec2f533dfd5637cd0c9ba5deaa9e576d1f0afeae1bacd9498211c4282b7ace673eae201e63a19b590c353931932e10686c7351a2df98eb287e97e26547708c6781cce72c89e03b95c2f2f4de5db4d6176f2cfdb214a1495e045ecd7720d87c10765bc07f79daaf0a4592386dcc14a3d641617812588893bc59c74a056b1a6cf45a24bdaf9ecd515517da7fb141140bd0c4dfe83176f7afa0defdbb9e6bd09a7ec9aaa0cc73cfa093212fc1c033e9f7713f181a5839d1a36f12440555b73b96711ce953ee9fd5217e2527384f7ac94df4c2c911bc5bb3a02b4b207f418352647138f13b67a5f3694dd5b61009f8c472312f70f58ed8ffdbc68ff0caf51ef1a736ff18cc16f826654002b0d634716606305d052bdfe94df4d4563f409cd02b1b4c0b36efc1dd7434ec1b5238684345839d624cc3d3c07d1b42baacf43b5961ea987993fc11d0aa44ba3709038cc48c36732ebb0cc4aab707c58d4bbd688f8243e60f376ca3c8cbb6a67405b96eed6c9b48309840de72b577494151070d8a0339778d934f4484d924f83a2d5ff53573ddf33c499d4ca3e37f4abbb1c1b12a004f7c5c967051a4cc842911f9929932ea55a8b4466302bf599f6f00686015af83b44761fec9bf847e63b4cb304ad59efb5236de3149ac96ee6946f1420bfb24e04851c51bbfb43fbe77464f1bba0f0657d1cfd6ed56f221c1e735b4f27e75bf018b3f6848679205f60c0156bf6c9257783477689a38ea87c52f0d97a65e847ef05bf1bde0698c04ef84754aa03b2fc5d3e273ee968c8c3ce6a4039d8412081b7018854e7927f857dbb01628d9aaacd249863ac527a9cfcf42f4912b577dc0fbc126255164eea1a335fbe42021770c7c4877f82363e4f1741fc1c0f5c00a914211146dbd5c4c70ea61bb965789dc680353a89a91cf377c08be5ae0cb0e008a2f12355ca63e822082674145c43ca187227dad3e7082744bdb058c587c2dbdbdfee07725cd2ec5200b591031e23521d665f12adfd659c1ffd73048b85984121e4bd0d612860ce39357efe930af398f9d6d50a4996510dc7a8c481fd7adfbe8134803e30b28de64ce85a6cb314b1a28a5a8d57bee3a502e8cf70c19325db3d11469f3f733b3556695d84e088bafaff1301e2f529406aee12ccfce302db8dcd2dbf461bd009011f52fa6441a3e9483d3f8a0d9ff8fe645b31054a731ff02fc332f95a11a572f8d2632fa8f20f3a443e75b32727fc528bb34f21af86378301ad5492431beb4641c7d5b464163e3631508fbe84f7dd939ba7987449b85efb6664ee0c2c1322bf71aa854bceeb4907158529e89d750e56c3e4f7765fba7cddd29d5a69ff220ee807b63246ba892fa1678cce0e6160306f1ec0600ecd88bea0b8ffc69bb12972ed716fa09350ea3cd20168916006912b0678ea8953b1e7741a6a0c4771d6358e6027dbb3873441eeced0a9b00e1dae9fdd6bfccc041286557ba9b74fea5f0f3beb555cdcf16f8faf2b4be979ab87940907d7850952ccb2679eefa2e5a23a21a873817444253277cda4f9e03d11579bb1547ffd1b9450a6a82534ce7d7f0f00039ab2d9778c909de5f94284a5d9e325d9a5a4d92d83f380e3eb46f5432e000fbee857d52e16cac323910ef1cb2c47eb631b16e59256b50aa99e22da50152023ab73bfac5dfb95569280b369b402ee609022e13675ca3de0697ba8021e81c23969876026246b4cbe3ab51036c90cdc51c8be73f5fa6e5bd13cd176d52865bb25413df4b8518a7e03736af94b9bce90edc74a4531ad90c3170570c593be4c4e18e2d3b85a853265bed4bf9b1bae993a77277e2d8b400cc0fa2c48818475977e1f5bd89c0ab5481512e45b4f03fe39a5696da2eb25a5933387555ed9264a0a4306bc8fbd20e9e540c9c20cf2ff3224ffccc78ce5a6b878bf6e1d205afeeb303d0846e28eb9cd9c3e741acd97b362b44d62f3d43cd35cb3de854e53ab0df34bfb3f76ae240d4f348cfcbda45c14f96b4773c03000abba45649c235ba4459f4a3539cdd596c39d20c33503dd4662af3c9e0fde24898aef54d35ffbad0c06ea712e09a3ac98d038a5edf24d9752d11a3b07bb725df170d136b2275cdd4e7aecc4d306dc9c08c027cc70ace88524778f81e117afbb277675f6477a2cdd288930f1b018852e87108a080ee04a7fceb7b536cf650435d773c7c78811a7b2df47a3807429c92e62e26e3daebc1abee2b8b41cb50b8000e844c623f43865a4fa63fdd8501829e2dd7e7c8e3093109ed3a5459f4050e568d0dd57b89892930e06d60222c504c375cc8acc866c48ad1cf589bfdb728a13d300fd7099e45abfeb90b5fd70ff20e59b14f98b836783c7874e1f9f781557be3e3a5d0eed5b21b3f4c8e887f9de34fdd11bd75da1ced14e0473d2917780ff787a17be227e637a2e92db6a9964e313807e3b25d7d0617310e2a91693b82d331d7f4a4c6f2095c0b20dfd731f63c92bf150e6511473d9ce07cbb51f212cb1a47210b0ccf8a010edeb8a0dd07d3785229ce884325cc00523f766328a5582142c692cb5d197fa18311b8aa01ff34f1126c94c98f5bde08ef5c172c202d3918f800952261bcd9d799f521d2e00cbab2832ab6505431aab4a9a0333c2c3bae91152e6e16890d12912d2d21aaee6a7a0621d0eb3cb8534fc0fdaa896f54e0d14d378b252d3cb8b9cf394a11cee6053c4172d7e1d02ce0c48c5d4064a2c8536ddd86a9a36a4e6667a88ae880566a1e5f6252c628ab5f04f69e52c7ba4648a22a65645b2d2ea2dbe064d536f739004e0c47b09b6258e68afdeef764d4eee2858fe5642ea6bd170f65c58ca317f98ae687d9e154a1260f6936b661bc846000309ac6b9d932b56f277bec7891cc39bf8b83e6035c427a1a1c999dd6b74a64a41426026a4be6405fee4185525579d4e47c818b60d5bd712bdf1344c7c2de881586843c7646d2f1749f95220223912a5c1f47a4d075e044719c1ac42e89f96e097ed940521f68fc8978465b337d6d36ddd42094e303113fac47617028457c6f3bc2e2188456ef59689c426b089ee44c3ba50f73120037ed7bac08c27ac7243e9579d2105da4437683446adef8e5c15f9935ecbca0553bc802c96a88fda391bc7cf4ddbd47a7db54d0cbfa1b35b3a8dbd264022b83c7927a413817ad8b57d5b4f842114df6b0329ceadd742f3f47595520b77541900ccee94e02f04ce577506e38ea4220ebfafd32c7467f3e5fe3fc75131adf63528c2829bdbb6133e3326fc752856fa4435135cec51e5ab94abf97cf1ea9299461debbd4648a4593df89a178029020e7226dde961478c23bb188a1a7845aff50e0c7f062a04fbb9f6b985ecee4d7e948de6c30e4595fdb5498d3cb740aab6944c1b9ff0abc21041d42415d1c41ff132c9ab73d91a90fd786d748755f8a55a4fef23b23ffd9023a66b7864e9e9d632e904f5abbd897c6b4d6d6ad9c30105d7bcc634597f42579e1a6f145bf80349bcf398dad77e4fa12e67b592915c7946e444fe3e2f6b5c2f5b9178e01dba5461aebfcd0809e23cd8111799dc166117c4c1f7ca01f78a2e452ceeb18284ac549b95a73535d6950d96f329503479eda94f54e9e028978ded84a69682c4a084a66e5dc5ab16fb1fa4c7540116f812d24c287a3b0119e87a3aa8b69a11d7be71026cf03ec15f2313f1d01d27462cf1e2c5ababaa9f274066824f0c5aafc317c74e3ef5b8598f2bfd12f229453d2b6854ae145d57751285d9c057bca93c7f4776dedc03fe55651e8f346a9abbb3dd121a23e0d5431b2a14b9cc4890ea215544565dbd0b98368975ecc7e8d12492832b76b32c8c44783e6f72fe086cae6e1d16991f63621093910e57ed76baea5e4a0713545b6ba84f1689b37c61dd2cf9eef46f551adfc6342ab21c9478ac8817f9f3ff3a7e940d300da47dd1cb88bdfa64df6d019a67d09839b3136127fb459b5a7407212f0e2b3f345d3933c0d17cd2c63f2d9336ea29073e890173dc744dff66cd1a11ac17256c2325b5f41f1267ebd419603af348ecd4e1b34710a6b92b738d6238484fcbebd515d914c6f0d97fa59fcbabb50fcaf9af105f0d4eb47c7f75edd83f80c0c47b111e76a6905da2194d62d12028a5c76f06b8b02e145b927b9d88c49d0774413f093fd801f73ff8e8cc061188088380b526dd3c146a2630613820a04e7b6afb71f002d20f729bccf63d2f08f2a283dbf4e949607f68429d6ba81505ca4f5dd3c4b3113521c3c577d07f4a0fc100546b59e3061044600bc78522efa121e168df0cf9257a628373e06894baebd44643cb626ef5a8dcd8941c03907b8378bcf877b6f51af69937e27fa743b4ecce270238298f1420ddfec19f3379d2bddbd32bfa0c782ba90b46e2c0cebb408567261c65b9135bec68b8b7302dc8f2b4cfc8cb333a7b23401137145964676b5c57754192568110c5c7627528f2580454a25ce312d03b1c1e9e755394619c2582378047dc52d5a669e74a3696676f167f21aedda8211027746ab01ef8ae6b1be15ae95f15f7de5443a833af53007c5e941e7b38eb06fc02adb0860cb1fb379c9f55a226f1fe1ff1481cfec3aec1f30bcaf3daf33122402647ae3faaf3f3bc71f681935c89c845a92ae16164f24f1a152c07760551ab7d1b2208707818dfa213ff7fa3ab7c5e8ebf16b252da1588f87b3379f86799023b2e4b4f4641c277ac8149c0b27b8e7c14569bc5a152bb19ec9552f8ee293a70da2944de86c6a5cdda7a13065adb6c11693ce4b59b76fc83c933dd87366e0f6ca7002ace55351659701b2d83ddffcdd6f559a5bc4140574789142c6d23ded0dec8144805bf4cae2d351ad9cdedf72ef052a8caecc96e81a90c208f285c228262830c45ed2891a0c7c1b5adca3c808f84f2ac51f932131ea23359ae971a238ec38225605fea5ed37bd0dea4b92c37998576c9e6c75ac9f84f2bc2277148486bd66517d7914a94a0571f99af5828eee0be31062a9e80b2be73413cc2b3fc4c77f692164dd9a90b2fed552f7f0ab8d1a840c538824417afc61b954da7098449e39ec72287ca2a4425d4b368dd2f04936e90b0772c6cd17229973152a93d63fdc767d03b921c973e1b83fb7a51b551d39d311f574bd3a4dd0d3ee80bfeb612f0f5c51315bf72d84851bfd23a22b6e6b23da3c2763e739e912df732dbedfc1c572cef3f19f1db23d934c55431b94eaea8e2d1944e3948ef6ea34f3deb76f1067f332d4c186c7c8c2c411836133c30ca68e590ed74244a85219a34339dea483683a21c791f548f9cd8d4f7ff29285b9069553d1600be992871f164560cb775930fb6b69e187284195c4286b6ac8f1fd024e3230c6009679110398a7fdde14206d256dcd82981154bd8dc9da5c07e4185c96e22db3de4ed4b9840569710653eaa8d435a8a2bf008640400e6798171a0988ade63ae691fee01751405f5def545290374dfee24c9b0d4299eb87422a63717b8c680afa3af0ec21c4de3e0719bd202b06b8f742829c1e6af367de4f6652111ed862c81d7f90bf162a818842636ab2a19c872b3231fc320b9c1bffdaa36a48da7f11b77e6ee5865399f7b3f867eb06d6b81d68d0c9234158b9a5beb48d3144ac725cacf593c568d5382d9738bdd87bb5f6f17891e842652943f27aff2ec84646bfcd7cb19264228aa722693e84cff07afcdce4203be1e5242c015826e1061625c556e9057be34fb41b433a3bbe71827399497b3158ec02a1f07fb6b6455efbb1632a3720fc20ac00437e851541d57573eca8bd53eb53bbbca222cbf561990d60eec850afde3b2b318ad784a5e0d705a8ef029520b46bad87012460d3d63dd72720d1a47f46765e1e1b80b08f201f432fb3a733e39106ab32d84230cedbd11dcb747ee6d8143df2dc0150d45b1eda048126169333fee911b7074f6d89464d3e4db4f413129e9e7f01021424f2ae0a40b23bd59e40bd11eb31616fc79f36d196a86e5efbf38f7e043f666c6f0f353e4ec18b8d1243a729ff98cbd9614a4a13a1b5b6a8bcdf8b7580e808e899951034adde3d7dd470cce9b5fa23e0856f2a97c2f9c5cc77b3ec11faa188d20fc67b6357bd755985e19921289502d7ca0340e784531aba53aa0d6772a0483e81d634c7e5f1f8086d420da08c18b751a6a755f98fd0b0e7f2f2145229206882cbf5ddb55378a6244b60b3750a7606f67abc0fdc311ad98f9867d287e44ff53aa7ef860c1c1a0d53d713490502ef5a5917c9b60246983000c0d893a00d033e87cc45b69c173b95b51041fcc593f32cfe82472c92d33873ccd195cf19af9d99e068d5745ed2ee04a19ba3af41bf72e6975044a9a3402fa931ff2bea46b1e39b2cff5933514911d0df18032f1910bcd71d5b03140dde5ea95a46b9380acfb769840221086abffadec02a21c36782f31fcd1c185c2f1601e2b6c95cfb2b7467dbc7936bbe814102bc66f1e376938ac6deaa1e3056b15465d1f63a4eeb15797eeeac6aa422dc29c59b4c2f105d84cdf1c328ec24fabe6eb0ed45cddbd6bd0072c09a716f0001ad2ffb77f9b3aba51c58880220937653dc6082ca1ac1b0359bea80cb4a06a4ba841772129b23263851f00e48fff4d2e001c7981fb17268d1e30a97d78cdb6c95d428f717b69223fe40e8964f37617e945ad0bcd51ca21d0001423b12b7df229022143a8a1f0229b815fd419ab11efb5c3f6fbed61989072da67ad8ae90187e5a06e62f9fd182f7dc77f5529494d21f2c06a8438d262ed118d6dd32039a03760323e657c9a9d0c47f6cabdbed19d84559455fa40e78bfe1d9f8eb3e737863846e4f5fb2bec5804adc08d9ba1565e387287529f9f562538f86ff480520eac01c4ac1cf3a9a5db1040db9a7383d05cb1064b6e4449f41feff10277e52cd45694ee703bbfd73cab1441311b0976375c28deedded4cf0df488227bbf5065298d066413953c40b1b01a330e00243de9d9ced3508167400c1404588584798565fb1e91a8c4c12b607405b3de3e90d234d3cd0d6243e2de4a96d30c1c813af58c8295a590a6af6dba8a092e94dcc73a1f2edea2fff48f3bde49b8d6e16ee7b022c3df2743fe86720eee1e1207271d47d5e2acec6ee27182888718f5c8a6905488cb70569303193f5bb30e31acab1c6ee22dae707ee88af3075a485ac591db002b393a47b250fec2b48b2c11c2e4c4d179eea23a07cffa16431a73d21602835ef49dd53b5934468fa3ad3e59d901d83c0eed64fd8acd0c0eb07962e28aab552ea39e00b3454a1fd6c39417ef220b2e4556dbac2c904a6d9549a0d4a297fab1f8f1b38b2b4e7cbecd8bc7040b6dec8f181ac5447dce53a866ccd92125041d6b99aea02b6d039f284621a525fbf2be6e8d7bcb1d8c60efabaa2e2bda7a87feab334304cf9f435e4523b659329e9aa4a90343402225ebfc653f29dfa97269f06bfa5ffd6f4f5921f77303c01c30dc45cdb583fe8eec13afbb034705e6ce5c8a6f4ce54e27cdbf0a8c03806c370343500414d9f31b926e379697896e7cf29b6b6d398d390b24878238faa6363ac75222875af12f3155cbea75ba76ffb8cb79316e35ff4cac29d978f53bd6bc0c9496346d5b21723311f0993b7f3b5f6b895a9f2bbfd0a4f9a975e68be1b977357704a4feb75d5160ed640de35fda1f189e076f6ea447bb8c99a5490205ae123a2724c929aa3f4b94058bc34542421107e06ad26299a943bd741d990be547cc00cac5a42017578808f88e3c4e55bd5db487f890ebeb16ad1d8dc798f36b68ce383650517ed3517a8b11e447fb396750495aeb1997924c37ad339d12642ccab42a22e1db92fd0d56830184e42a13cf1c32f705fd0c076c039fa41b400aa9db8e0caddbfa782bf86ff58bc9446fb97bd3eba5faef09b9c241fccff5d18c3dcbb31e9b8ff02051cddb678f64db087ddcc1eb6656bb59c9a60a02052e5e4959ae3303623d69e2088ec12b29e0909d2888ad455ad0999cdadcdabb698bb5222ef3731f017f728eeee534d475120e31861ffafb0ee6c5ff0780f0e8e2f1ca65cb876731ed113b2893a6fd9999a06b280e2fb04754089ef456c7fcec367be99cbf4b671c002631fc22a3116950b0a8a6da7fb9b81fd212b2074e8a4dbcce424d967fee1ae04ff4dd7153ca518b61becff30bdfc2368c6ea73b0e3daf85cab610a29fad019767947985cfc62c81d79eae9a1e418fba49bbf52851bdb848512488cecda50b471e0ec10463f8666c8960a768558dccdca9748acdb07510bd6461a58fdef72710a74c2afe12fe38f5a04da7f0191b0a78f8d65c7e3fd1db376b15c1669a4313d2ff68944c97316107353b0c0237aaf941ab854125938a0a2295a5161a50eb29d274fe75e650ba73ffac7cb6e82c6cf5a7e8ee8e0e536060b40d74b9b9ad6e3ef5a2e7f3510ec9804fd7c41febad6f038db65c57857f81a83c9b983db2ec90342702f8526c5e920eb4566aa05c0103473ae7db1062bfdd023c76625babcd753cb71813807bf485b69196933fc749033f83712c217039ba565a8a71d093c7870677ddd4c96436cc2a74e1de515d596c51e7f72d4adafffe5d59f6145b4335cd16ff1f0c7759bab04cb5ee49d072b4285eb987a5e318d3cbad1ba1e49d4828d7086b6fd79699046cf140b07b013989f7330d2cf6e48df5f656463312c2291b652bc857a06aa9c047f1b245f277496fccfeb3a82c27d9a3c5a82b6f9c12b59db3074e1fc7b0f796a16003d84be6ffa00bd9b304533ee0ed5d536eed86e7bdb92a0e0cef64c727b0120abd5d854aa4c346384acfb0cd1ce39bde31cc8a9f997a001150a7ddf91cce8ea57ff6a720b3706dc0813eb87f5f59bbb5a51ce021ffc5cb191ccc1276c54fb564a25a9fddc91dd98ea7648d9d342670dabfe0333f3f7ed518b18dbb27b0fa4b58b175e69ac5eb4482c892d07d94d7eff271508072408e6b0e5a2efedf5e72c53351301d3802b360a2ffe196a21796b6e8edaf5aa3319b240915e069c468d0fca39b337ce9509f9cfda8482a9cd45aba6b6089fe9403b34b13c8f348d5032381ee6bfe40f79196bb835be3d798f8be0ada38477e386e27342e2899e7976faf7a4acbedff498cd78286bc460f4fdbe64e85259be016eeead118390aadb1d41a838b7d3722b097eac17db047baee7a9b55402882008b1868bf0f9ad2ceb43a8ad5e15c7313aff12f0ad045c4970ab4a27d501ed4bd1a0831e01a6502662424c4d45038d963716d90be7419a2db0ce620425fae00025999a3532e23a5e8f9b25116aa69ba7fb00afa261698559b09ed0555283f66a0e522418b30a5b7bc944346eac86c659d7c1ecf286a0f0d7a9072cb81c6428d3f6eaa8ebf3819f42c6e65fdb30468817f9036d5ec148d842ee9d6d1cb06278fcba65fc6371efffcf75e90ee8b16e57bea3a00d91b6eb0457269951ef77c42dad47cc7e2381269cb6e5d6bc3c98237db67b126ad3176f6bb810aa12fea539286ee1eae787d34095ac62ea324883693abc214f618cae9adc3f9027dd62d62d2ab57e694546913ef9ca7e47a50248ad1c89029f41f8c6ce823a268b440276eea283dd1cd34aa2dba06f064d7c39b45fc9a839a2cb967dcc28ea7f9db3310391eb079b057034d28091e8238ea96ee601ad18cd069015cf9195daf8cadfac40cbe0893f22255523cc2ec5553c75b1b64e522bae4da14672e174c36c26d1c3c3987facd520e855317f81bfd019dc30758aa8812a3f8a6f2b8ee656189674baa89d0183a2b7c450a324333ff6f5a8e17165be8b3d2af9fe51923a12682b8ad0273cbc4ddd699e8b3278c96269c8a20b9fea7fdf692296a663f3603e34bf5b99049f4da59192c4af8219bb8d1cb4177d2cc8fceabd1efdb5333bdf26dd4f0630cd1791c7f07e3c2f6082c7f8f1a938d10866260042d3073ce804fac593345d46ebbbdc1c7fdf6e2f034bfaec4e916e95b3b81c97329543f0090113e30e581f70e21841bbdae8de259024e05a1a26eaecbaea91470d825ab9a72ba9c423a9715cd1e80714fdf97513f200a89cfb9521e606ba1c7da6f2963481c59dc8de91081025da46e94ebe50c4b39127477fc1096c5567005ea196f1875efa6d6f9fcc5dbf15fb548afc1d19a18262a4720aad938010d57c3bb221fb3c900816a06640f75b900639f2b31ffb9e81888ed6e2bce67387ac1b5184379f8981c28517e3cbeb8019091e5a5d8512a1103cce28fc45a9a006f0714adba5308a272675ebcd91f0a2ccc3a2eaf0a1ba0302e61ba6981253dcce355473b0f011cc4ca592ead33a28e5243dd32947d01220f374e3f4280d2549c911ec92ea29a006606f9b77b67092413160588da3f07f1d12d1ece9e3a29a0d17658770889ee1ed64f6851ba3b7974b65de06a798bd9e9d92f4d64121420c59cd73712e440c749b0ab87c246bae4e82584f6681b6ff8838d30cfcc41647866ccf5b9de3d8085cdc597387e0d21c96d71ccc814a7e181053c30ae2dfded348f927a74ddecc1be5b4f54bf6b964484bc0708f644faab34c6e52fcde40e03aa622fce5f57669dd473528acaedfe49a22eda03028e46dbcfddc01ff18e276dc642a50fcfcedd07f5d94b3a8ce021adf2177d4df45e5e5e864ddc14952e4fbf2c48c1dcd8f778b969e5026d4648e290dd9c1819729c786f7f8ae60cb3d46cedfca0c7aab10e8cb7bdcce517f5c391c380c64432dc8de1693017552c0ce3335d9925b97993c29e65bd51b3b5c736e5f033f948e6416aa5de88a5210e31300f3e291fef821489eb61a3a8fac79c9371404c6f4ec508dae4bc7960c0397a801b20d82306a92073618d0d7024c81e8d69297110aa0058b40f228f71ab21f7ece49048e1a5befb2d0e4d7aea8355d81491084edc6bf5c89f27e014ac880f024bc6c9c9b4bc8e32cf96e63adb18617f23fb2754b2de26aa0b0f480d84d734436debd05d11e69c7912a3710755a446d43a48a128b6b1b9b471531a36d0190ccc80f011ad4db1cbfc82ac44d72f57ac7a4f18198f4816efbacdc661d6553a0ea106d1175c51ec575340a0f10e67bd3deeed5a053951da5092cca0fb772850261d3bf96db0f8fef71eced5d500aa09dc3dda8ecea3dab2267519a031df88c208965121e05e30bd9f65d7b28b0eef7b12d75e0275328443c75981ab5eacb5063c78c67cc784a8766e3efdc1791eb221e81435b65637a835523d952fa339ee0931f2317cae7d96f8878e8773de3cbee9272b9cb6e72c444b9441615069be089e1ea4b8179644bc4b7dfd7e3b6a4c229e33dbd564e355f2e6716a78d01a1ebd8621171e770e8ddedaaaf18cb1925d1940c718b85fd5a5685fa21e5b91c0e0736f2b6f1c55e05218f648e222ffb43d78aac0f31242e5ca0cb516808b20c94c1d1e1583a5ea8bba033f2d057b1f09f85b78aa6adc304e7a48f8f0947f56aef1054a01531cf8c99983726d4db2f7de7550f649b8fe0c988d76dc72f8fb4d9970795037d3e8bf9da6c2025e87ba2dbbf5ee957125488e30d9a96cb6d1b9552f9223858019359c96a0323db0f1e3f5cc63920c73aeb01c66d8f0f9c80ef1b7e964e54615490c77fff7d8ba68dd44ae05e1ede888bed3d3075941dc920a9c541066b42033b40d454b195bd766c20f75411379782b238601d574e79f4d25a8a4b2a617681a81c51886f3c2623a5b493b00a85dafbe12b9289b9c9df74dda83a69defdf770ece36ed2aab7c1da93b7dfc585b5eb1b1f1b918a97958053f2dc8b3c4dacdc338e9db3c5c259dc6275f7c664789b4d949a7e45dfa43cd8c3e905c7ea7b5d69516d3847fa6b85ab9b525b21a0765517bb226e40522d889f9eb742ae60b134a6f2424fec4284fd5f00ef051ab166cc44d9a28827d5725b30ff5de6e0de1d8e1e61db2fc7de7e6ab82f1fa07e3b07681a450565e836c101b6efcf02347238cb842122d14a5ca4aade68d2eb8b61addc6f6d43c61378c929135ce5938efc28716d8825b86da2c64341a4ac4da0c3b1b7e19ef8d264f8a8ce25a1405f92b5faf188199c8a4543809706b9e4eaa5633ccdbc1b07c48d61a81b82730283454c1dff7f72a593fbba8ef32933b7e68a76c448f77902cd8c9a1c7acfdb0f63997baddbb8b314b90ad81ccee835c039278fb9d8964e648164dd5b47f32358cea390ccd8b4f2e738355964bbc487f203a0e7ff7f9bfac3e6d759c4af11e6b4a250eb3587a501ae0232d0ca3072620877c5645f26e991401b66afc2e4323b43c347765baec0697c37d708e1196455405b4cbc40a26a54bb763e0f64baf07ba9880f46758a034ae2ddb441c9a5e6330ee71d6c9d91d3762bf4fdea3c47c3881fa71d52ad50a5910c66818a2e4b9c8cca7c89f12b2d27dd073ac9a867436fd8ce02583deff56e41656a52be368ee0abb6d4e1c5d3033ca73676d12d7c647498afb288053a74f3fb79edd0509637a75eac87f32bd485e4e3720aea28e3d67659a694719ab09fe2628caa63fb79b714ec1e0a0551de6179782d5cd9227288fdb2364526b8b19cb14135cb2a214ce796b2793c97fbcd39fcecdca5997ee146f8db75ba5004b2b5012111ce1f5e8fdaedc4899cef394cd47c7e554c2ff71a080c5b6a9487f30e0d2910e2a422815d8dcb04b5b7b3550dc89c2a10f683c425208f6820bb016729c88e42402f602b2e4942edfef92d30fdc5368fad72ed901c257d17af04f99e99a7acdadf38de1c237614ff2a090a13e82b3a8b901b5851cb5b39ccb2fe955874e2c64122534d14b2435caa74e15e9727647341684b1cfa7725a9e18709a4ff87f792b2f848f93e457a783ad0e45da874348c9b3f516e1b7fce60e1d91bb71b87bfcb33c7bfff67bd6e71f45b836da0ad193362eac5378a253acc9b3b04d4c0401b1693c94953ac4a8e0f2de7ecf185cd91e5970a5928444e9ef82f91c38a7fc6bac39b7a139b03c435f18962cb5cbb575b72db9e25726aeef37db7a2963c0b4883197271043ba81cedb697d4560d1540e6b8722e94e65f1cbe047253113b78430ff843713b3a66418a5a5de402498cd16d1629f5905171c296a0efe75cc57369dfd730c220e161a26d922b839711654afa6a865e8926fe09c2cfcd225b77563c7b5869d6d394c9eb24f580020d953ad7025578eec4a8830d8543d3ae4e5436d85bffb0001ef08930d9c10e57db4c4e6404886c7eea8362beadaac7045ea3fae6abc97129c8234a7e653bbac64aae1bed046faa8cd5243dbc52789ba4da93080a7e9c0030a00000000000000cae3981bd9a3d856131e6cfbd3644906942c69d678c8950ce339b6b06002449543e58c5524d00e2120d3c37d467f60a77d631a254ebe7fc286f584ab8e4edf15 + diff --git a/zebra-test/src/vectors/orchard_note_encryption.rs b/zebra-test/src/vectors/orchard_note_encryption.rs index ff52b661b53..84b576df5c3 100644 --- a/zebra-test/src/vectors/orchard_note_encryption.rs +++ b/zebra-test/src/vectors/orchard_note_encryption.rs @@ -1,6 +1,7 @@ //! Contains test vectors for Orchard note encryptions. use lazy_static::lazy_static; +// FIXME: add tests for OrchardZSA #[allow(missing_docs)] pub struct TestVector { pub incoming_viewing_key: [u8; 64], @@ -18,7 +19,7 @@ pub struct TestVector { pub shared_secret: [u8; 32], pub k_enc: [u8; 32], pub p_enc: [u8; 564], - pub c_enc: [u8; 580], + pub c_enc: [u8; 580], // FIXME: works for OrchardVanilla only! pub ock: [u8; 32], pub op: [u8; 64], pub c_out: [u8; 80], diff --git a/zebra-test/src/vectors/orchard_shielded_data.rs b/zebra-test/src/vectors/orchard_shielded_data.rs index b117cdd007a..405539cb2d1 100644 --- a/zebra-test/src/vectors/orchard_shielded_data.rs +++ b/zebra-test/src/vectors/orchard_shielded_data.rs @@ -14,7 +14,7 @@ use lazy_static::lazy_static; lazy_static! { pub static ref ORCHARD_SHIELDED_DATA: Vec<&'static [u8]> = [ ORCHARD_SHIELDED_DATA_1_BYTES.as_ref(), - ORCHARD_SHIELDED_DATA_3_BYTES.as_ref(), + ORCHARD_SHIELDED_DATA_2_BYTES.as_ref(), ORCHARD_SHIELDED_DATA_3_BYTES.as_ref(), ORCHARD_SHIELDED_DATA_4_BYTES.as_ref(), ] diff --git a/zebra-test/src/vectors/orchard_zsa_shielded_data.rs b/zebra-test/src/vectors/orchard_zsa_shielded_data.rs new file mode 100644 index 00000000000..0ff29de1117 --- /dev/null +++ b/zebra-test/src/vectors/orchard_zsa_shielded_data.rs @@ -0,0 +1,34 @@ +//! OrchardZSA shielded data (with Actions) test vectors +//! +//! Generated by `zebra_chain::primitives::halo2::tests::generate_test_vectors()` +//! +//! These are artificial/incomplete `zebra_chain::orchard::ShieldedData` +//! instances, care should be used when using them to test functionality beyond +//! verifying a standalone Orchard Acton Halo2 proof. + +#![allow(missing_docs)] + +use hex::FromHex; +use lazy_static::lazy_static; + +lazy_static! { + pub static ref ORCHARD_ZSA_SHIELDED_DATA: Vec<&'static [u8]> = [ + ORCHARD_ZSA_SHIELDED_DATA_1_BYTES.as_ref(), + ORCHARD_ZSA_SHIELDED_DATA_2_BYTES.as_ref(), + ORCHARD_ZSA_SHIELDED_DATA_3_BYTES.as_ref(), + ORCHARD_ZSA_SHIELDED_DATA_4_BYTES.as_ref(), + ] + .to_vec(); + pub static ref ORCHARD_ZSA_SHIELDED_DATA_1_BYTES: Vec = + >::from_hex(include_str!("orchard-zsa-shielded-data-1.txt").trim()) + .expect("OrchardZSA shielded data bytes are in valid hex representation"); + pub static ref ORCHARD_ZSA_SHIELDED_DATA_2_BYTES: Vec = + >::from_hex(include_str!("orchard-zsa-shielded-data-2.txt").trim()) + .expect("OrchardZSA shielded data bytes are in valid hex representation"); + pub static ref ORCHARD_ZSA_SHIELDED_DATA_3_BYTES: Vec = + >::from_hex(include_str!("orchard-zsa-shielded-data-3.txt").trim()) + .expect("OrchardZSA shielded data bytes are in valid hex representation"); + pub static ref ORCHARD_ZSA_SHIELDED_DATA_4_BYTES: Vec = + >::from_hex(include_str!("orchard-zsa-shielded-data-4.txt").trim()) + .expect("OrchardZSA shielded data bytes are in valid hex representation"); +} diff --git a/zebra-test/src/vectors/orchard_zsa_workflow_blocks.rs b/zebra-test/src/vectors/orchard_zsa_workflow_blocks.rs new file mode 100644 index 00000000000..733052cf920 --- /dev/null +++ b/zebra-test/src/vectors/orchard_zsa_workflow_blocks.rs @@ -0,0 +1,23 @@ +//! OrchardZSA test vectors + +use hex::FromHex; +use lazy_static::lazy_static; + +lazy_static! { +/// Test blocks for a Zcash Shielded Assets (ZSA) workflow. +/// The sequence demonstrates issuing, transferring and burning a custom +/// asset, then finalising the issuance and attempting an extra issue. +pub static ref ORCHARD_ZSA_WORKFLOW_BLOCKS: [Vec; 5] = [ + // Issue: 1000 + "0400000027e30134d620e9fe61f719938320bab63e7e72c91b5e23025676f90ed8119f027f6043d927d72f8b5df9984fdd36d2e2e1fd1ff8f7ee04a2b7da9306c14551c40000000000000000000000000000000000000000000000000000000000000000f2fa494d3fa60c200202020202020202020202020202020202020202020202020202020202020202fd4005000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020400008085202f89010000000000000000000000000000000000000000000000000000000000000000ffffffff025100ffffffff0140be4025000000001976a91475dd6d7f4bef95aa1ff1a711e5bfd853b4c6aaf888ac000000000100000000000000000000000000000600008077777777d80a1977000000001c1d1c000000000001029063000c87d7145492f9ded4d37b4ffdee769a1c41b0e17d622cce77f122d70ddb74fb50c5c36666482476bf5e8e190dcd8f5ed280af209b3f679d4dc06a213508774e2f7fa82dff9a985866919085523b13b0af4f534975228468feb62cb12575681e6101284f0fba5628e2ea531e9dad53d864c854e419e4c5b91fb7b35d00597572f98db1bb9f3049dbb9a08d403efd824d9d118a68493191e059ca00b2982252a2ffe5c3918a79171c294481fa267e83272858592d5890884feb90752347f33cfc9443e70a9f30d6150652eb2bb04327ee72b9c5e42462d4d2bd92725df50ce267c1588d29b08b25a719738e836f9c26ee47ce3945f9b627c4b9d3bc8ae755d8b78b840f1fcd055cd179af2ae0637f49fcc44cc975abb478fbd9922c15e946e681ff6aa64ac7275d58c7811c3d87c4e48dc97e35ca68780218e256f8bd7d9c1677bff6d75f663d24802a7b433f4461d686e1a0fd3d214b81b1398f8f79d062c4e92381741c3f96f3e81f455c96d05a623985e39c1d16361928424286483b40cc9b1249032dad9bf92a563bcd978c329ede5eb5c7933f937b6f2b73507c8ed0a2d4ca972281ed79bfe367b474b6fc89a29f20c913a7e42287074a185ea83fca9d0db796cce2cca07f3cd379eba7efdabf86a594e6743b0f30d3315daedd2afe289422cc0a5b73c3e837dc2efb5975e4fa8183fbe68b5688bd827472c41248bacde976d8f16700b4f6c9d6c83afc134e3766b7afdd85be2e373f98a7ef0d2ae19e98bfab76f3362888f3e81917b22236c6eae7c79ed9489410903bfbacf77bc1f0de11692cae0289c786ea3eb08f7fc652146d2529d0217801e2dab9d67c13cdbadd189fa302fbd402c4befe5823e70a802dd9c712396c20028f4f7c94a49409b169fa46a7569fe289d7189adb3e5e9d9dc63903aed828ecc3ec0144b59592a6a88c589577b976b7c781b3b43eba304130bf38971784c7caf8e5994d2ae59eede5ba220d7c43378b492e69c0d7b06445a49174b6aa27d08dc186b7bb5ec6b6b6e3b94185d5d10a07887b5f66f9991aadc239b578426ebb61b85ad40bd80aef5c4707963c2d2d9b79dd9cc416a597aa83c4e74cdebda03d6b7a1cd0238e88161d8ba579987335998fe39a909488455b11937e11d751f425ce7cdee73e8a99042f03eec4b4c00329da7dd90b75ac8918924205cb98346c5ab54096e7a91c9f44c4b21a885d36813221546da0609be857260bd691dff247d867f224ab98015aae153ec30248e15b5c0b2a0731496cf0518d9c63202f93d9f2023022d3fd3c83ec465ad3695d0e0d1ea0fb4eaf9dd8f6f92919ba1461e2d6e80f5d89e6b9b6d5241bffe1d91604c02e13592ef10a4b87612f82ce32b50550f0c46eb4cd6d081152b2123b0ae617e74a6f31f8721e8fcddee49e4c9269517fe55d7e364407b9fec4fb22711585c535bd6a3a656634cf034e30d4bed6e14c56ae98646a3fc42bc4906eb02cc80afdc9c5cd824ca22772567d8aec88c3b4fdc91d34133e8bb2a2787c4fddb3e5065fab306caf686f2684635aab39232c71d9211358eb2491ae39d0c5464efc0ae97b166821956d3c3e70acc7871b3d3c7a00e54e0974236fc1243caa57e04d1ddc3c42d67e23607830aff5540a806c6abc2621035f7e4280c7cd0eaf70db3e88d84da095e0c1a4d0d62728c2f8a939ac274fcddc1442b9993bd8b7f1a965b31af20637c789d93aa5e09fa6eeb4b55393f68cd9bc1a8c67f6d484b9c2134a25478e1fd28e0960ffcf8e36492e4b12f0c787fb16e80d7d0e92ab94a34e53c1b1c0b63db557e54c8e0c919073ff2366c83a4ca9b07b639172dc6df0b6602b3e8977ec3becf6b716c55fcdbaea993494e50b49a9dc8e7c09118942432ea3c5a036d4267928f2393072dc3734dd841e0c37cb2d50fe2f75c5dc77ff9e1540a52b136967862312de74af7071d4f17de67775adf87f1e540161a4eaef191a93aef5daa5ff7d42e36fcb31dd1edd73ba829b32a6d0ee48878bd6ff3ef472f48e9e8bc1f479c34f3d5509288f3181a49a8f3d7771c5cd076533924dc67b96da721ec8a19a85f903c2a2eba21c0146526dff8a8be77831f558be214c43efa29ed6e9be6a2d8e712a745bdcd0f9bce70f997da5928cee6775164168bb343d2613821b4814a1198df32cdab2da48c0188dfeeacef916472529503d8a63c4b2092133c31770d79ac922976417ba6a2d92b4108ca7ae496e039a7ef38deb19d22e1116e92e9cdd0a371b27226e6dcfb14ef5855adaf2949d1e764c6f83bd7e4259dfcf4d1831e2d9b80145128ebbcb0259e3cae8ac973204fb2bbdc5e9d967c6f7e5e4c6f0c139b4a07aa6b63430ff511c223c64933f5fd8ea367d4829c63938a2ee54b3469383824bab4807ae2935f1dfd8a24aed7c70df7de3a668eb7a49b1319880dde2bbd9031ae5d82f0000000000fde01c1ce7832fe7add3bc1fd885958fc5fa1697a0336a4504ebac5d683237a8510183f29c22defb607c48816c49704d3ce388ea27bbc7765a43961fba354af095dc9984dd6b892f223256347a3a59083aed6de70dd327a95e0f3dd1531d01f874829d8095242883e06045c1186ab08124b5dd0b5ec86c6bdd12f5713fb6ce125c0203d9191bc63a1de897698f59060b124cd81b8cea5e2a026577ebae2edf3e238203b670331c0cb32a229e263305484d7f3ef896c4e03bad52bee2250296698b47bea4f60342a23e0ab908a3c094543fbeff20748c3e75b7cf9755813388d5d8871f862bd444e3469e9e72302321bf35114dc6c8341c838f962debbeebf9727a132ea1a03a0141d6965bf152fcaa6d18ef7c24e32103cbebd9c1c87f0601da6e4f07af42615a0b2d41aebfe02e1c2ffaaced5c3d996c8fea947adf7975c4d6419b20aa0c804b867530bc1d1d6103ee6a6674530fed4b4a1289d4376902fc5ed33392111c323f6e73a07ba04c69f8e4214be8074e76124e84990d53091a4b95a9d482a0b2447d911255bb3f312c706151d8a87d284aaa0e4e24c059c07f4952d3fb0308acbbe1513842cf7881159080f10bd0f169169d0c0c126770a9b7985f0aed262ba2749b2c9a237fafefdaac68b8756c2a628f5bf2b7bdd804d23e2a8b9eb70dd38586c842d7a0e3c71dcbe5e651343375adde02e5501107339538b0e2dc45a9cb2eb8831ad77bb61d0359ad4c1a2dc31b29a850a31d7e72d00b978de4b570a9a4e4a403156cdf351154975975d424bd9933415081cdca5eeb411c4a723b6a2d19ab96d3a9ff273d5e923d158425319cce5c63c6ee3adbc5b36e05597472669d4bb48a292271a10a85ff7274a74e5a96e223d0705c08da720425e98ef270f907a20085babb3f642bf67dd8eb3fda67592b6dee4360895e22713783899ec9fe37f861e73cd5261a0be04af440b5f35fcefd345bba49a02f7e754bd5276e343a8f1f081f7e904295a12f57d8b0927be322b35368c463525415e5fc01e43c7064331258ef895a5f0f23bdc7b2095c2d27011bf17dbe37eca66d44ef565ab7cf9280a64651a39635b042ac1b74bbbfcf792e92cadaba08677a836f10bb0d1acbf1318c7b39dfed8b7ca0d64a24ca09d717dc618e036818ea11c743aa6e6a2fbbdd0c42f7c59122392bb90515b425b62ccc85b311d880cf24e621f100cdb8552c4e02360583676ae33cd314bf49a5b6979e6a7fc379759bf1dc9ac51b62c8b1851b87a58ac9fd2a9f30a61e7d96546ce53f8476b575777a533484777fa4ad9d921aa589f4d880de9c28c93c26e6d4284a3ce64ddd454490f73c9db8f4f1f49e9cc939405d635f6ba3be2511c2c1462d65905d8f2f40fb82d112141fd9591bf88ec98f82aee3e7d0a8c0156bbad06fd3eeab3da041ba47c572b3be65bae532893ae1b69d3e37a055c02e994e8429aba5dd5b455335144c63d6ebc6171423f2dd8ac600e648d34512929d7fb66b5fdb19f004c7e75e5e1d5e7af29a5acc9b87c8563c97b1c4cfc848676b1a38ac76ef4ab441f9235325dc1416911bf07ed7c598f6fc1c16b7d4a92489b5821f7151a11ce2dfe04d95d661a5cf284b4bbf83baee5165a3ceba103d36d15fc1a9739229e21789210581f9206323cf03526e2aa38f614bb59853128dd688b711afaf15986e89cac8b4b93b1ee24d55bc40743a4783746caf4f5bcad200363785c754d6af2dd5d519a4151223148e4f5c89703dfd209d8a38b5bc55c5f1f644e6e071bdd8f6597141c37530b7ef9e513c49f9b7b0e0c743830931ae2958c73b14ab1f35e2618298db2c437c95d6d4b13c41b4bdb51f13c1813762e213e18655382d670f55d97b2ed83c695488efe5831ec82656c6d42baa154388d4e212fb5c980b87476e62f4d8e84302f23c54b95b7b1b74e0e44219dabb8e8b4d4830a7494b627e1f6e62a634b86dc821dbaef4e3e3b53e69ad670f1588f2aebdb702828098508060b53cca72fa8c92881a20a852eb1315c2439ec89fa183e67a81c6590dd51a743553fa48fa9f10495c6249c7bbc51ed08e703ce7103e28b12a263fbe66466ad66c11bd9c66c27494b9815e1600bcb2e4248a514a421bd0b0363d8888ad8c9c3605b005a51e77af8a3ae4009f34a24ee242a60cf5c0b2860c715cc56337fe9983a893be43fe75c87997d6eff3e87ca34923fb39993dbfddca1d9861b9314bf420dfef04b0edd9fc9a5b6d7ecedbb7d669a5cdb5045f9a7217f83c62e3eaab4fcaeb347062b02857e7c073eee827e0f1a9c37f4fc3a914b2f583f5632a2fb974aa64f08245c706ad94e29f7b8d1b8b5a423bc3b5b4dd9106d1fa787a9d5d6f64f3273f3758600ff39b6ff0690d7f4dde701aa04e664c9c3f622622736704a523f78bcfad7e882cec28183bf15316370dbd4f3164bdb1224f49a27121e57f7cbb7f8a28650fd2589cd109ac1040194c44bcb8479d655800cac9fe82717e9496bf32e8d3e3a4b5fa7826f4cc86878fd4ef9857640c59b60ba7276af3e449679fa78939dc590c1fc392b854c7e8c4528108bda4e4a0c14d27adff03c0429bdabbe2df4249311d5f7a7ec35f023b166f7de5a1a521615db0376cc1237ec902a4f76624a8a5c65a293d4ef3344429aadb482633bccdcbe1160dcd098b71deb84153068083cc6976cb9fdb46dbe226fa587970fdb4fb14b07720de20cae800014c66da833530b84d7f5f1977f74813507715dda0071e845f9c291fcc4fa4513b24af47000d230d72ee0a42c0a356c1f96fca44b313396c974b0849aa95d0062562d0fbb31d47af78e4e857cdbb43f2014ebaa8cf796067863b0444bf7a3a207816c5eb8dac792d15a01f7ce0bd48a5a3687cbd8bedd364d176106561493bb8e83f63bc67fd07f8b11fcf3bf99b2a1daac1a001ee09d6f8d3973c623b8838988b4faaa1d9151233ff1cb89e947ccf322d59b0011fbc1bf66f5a2867c0a35385d55463cb7fd01db5932b9a163ee6cc11ef0d19e09e2dde4245571fa01b8624926e27a9bae527a27dbfa1fec4c5687a6193a5336469ff40fe03eb0338889dadd86d84a6381b2f65cdb3b6880ee67de08572d6fae5c5df6b2ec4e1216a5999cb3c2bbdffacb157d1e94061b4eb985d153b8840c537463e8a15e5533215522f1d4ab74f09a21b1e9c851688c2131f7da84c95f390eebae35dbdfe1e28d5d0755d419707317ea45d75e88c40d5df34316fcc7f59de8af82e1cb0ba3e10ff1775b8d6f6ba2141a1f83b21b577afea554f709fb4c373f6dbc66a4e97c31a500129684d7315874633453e7ac8c10f63ae4708b28361c772725a110fd3e626b020d8b5b3820faf67e02e3feb9d13ba99f40b6ae834ce75881c44d8124f3f234cd003d5cdda116a218c3dfd1019690b31ec2546b0be2660aeea3b11d375cc19ca2c57fb3ca817a53dd3357cd5f0b72c3a06dffee32ee613eb53a0f679662108f42002ea24bf40c2db026dc595710d23bd9ef571dd37134955083c25ab968d23bae81d22c3a16f10f3d75cfd7eac8337226dda9554093db1c60261931c278b11846796d56a477f454ee04053268709b935b198ffa096dce5c9d3bb1bbbd8fc19a38d529603881a9d449f522650aefa9e0530d92f2712c6122bc874587fc80c29beeee0c2f532607cc63aa8a91413bbc351a2a355b40b3fad7871976b6a46491ca94607f27b2018af66a8d6e429b8955a68e10f3585666b40005248f39fa274020d57f76af52e860f6004f20b33174e16b184d39f90ad5c563da44be6a526de1b258a20649fe5b084b546417385ede6ef19ab6770dd56583d2f3d36901aab371a341c1fbf11929950845b05b833dabff5608b2b0346d8f41ffb24b2be3187cd2ca86d06e8adaedd3f3e9ca9f6e1cbb85bf6c34eb3dcdd9931edf2312e4348481d3ba48a33ee57a314c77196fd28b63546963da0c3edb742934e33daed72cbd80b1ff33a716e22fcff53b93b8791238a92f62070a6c8f74d3c16116c1f9743bf100e0fe3e1dfd512e60fb075f193b3d100f8327a8b7011b1a12c519ec902d7183a09958adbb491a9e9de0070fc685b3963f1617112aa4edd1a4bd35bb459ad121e34851230f78913c59ac8d766b84ab510f657257a109de229ddb30b3db025f620604df250741b4eb757f6a0b6d2a0ba2cee7ac1046800eae0519243380c404a133766b685997236bfc73e3317e2da32c9f449aecebbd02c28c5e62226aeec140e4c38dabc0c6ae4d6fbd5aedab7abe0d2b0b0c7533367db3ab39ca127f688ef34aa4a61bf2cd2ca5a0f598b8009e5610efb05da12495c0bf02eec37fb857f7d1943f8f76093a27b422a910f4904cfb836f62d7ac295760ab9f2587f60e83d402854e9a3550a190f59fbcc5c94e6f6bcf9e9d7527ef7e6c4afb13b928fd2fbba2ae008f19da2d385881dfece30a3c9433909bae080e01f09e987a059f368d7712246839159ec183345e5a8607e860bf1948134d1ab791c2446094d012e14e1a82fa5d95106c0c9626df1e7e56cb7e6cbcfbea965f64cc4255319eff09bcc40ab3ccc7294ac369701ac1f083b615e532d13ea809eb68967b031fff2b0536b781e08688a51de3629d4c8e3e29987b4ddbccea41b7060ed9f635da106145bbd4dd2045f2215546edfe71205f5a139bf5e9af5b68d4c34acc19307d23b7971da98ec2ebc8282aeabba8f1a46af4baf00276aa0e9e5c212865d763687335e1ec2d6c813a516bb2f2b79056100b07488ce2fd5089be296ead42ce345ef58f73543ab102ed79e426521fea60dcce47e498180ee94f1e69bf862c9e014ad60f6041819f01107812803c986e547a5fb744dd766b92e22ca8621b56190ab1a7019eb9e288114c4c450d08a95da7282278239f7fa073a8ce444506ae171e0dcd54d1861362f12957b366b92dcb0480017c6a397b27506c55238e1656355786704489fb54bf1257e90a246f92ead455166c4217b610682a6446514a1b59d5facfc7041d4e639046e60262097557cec24c59629b891229e714db79a80e830af7fa7a2112e60ff1fb95741f39c51d37c3be241e9990bf14c325e558483f65408ba25c4cf85e10122cd5cba6010db487930eed9bedac4d533825c657aac9cb709920f6c9a537b76194eab8c330fcc7891e24207f5ca76980d94bd1b6db41692ee6bee117544e98620de4390da019b63757bc78ea7d0e27c2fc6b92d8c0366e23ff1d5a38130e5183340a905cefed2bd332d443c6fc6c3f4601bd3e4927b40388c00c842c93d01ac365bb6272f28ad28ecdbc05dc2e4f61175cd36f5fa5a4771e0dfb6e13cc2ba910e28f11fa13728bf2dc57e279ec67f8046187bdb99cabeb0c3c008c6ef26ca382f9940e0b02771fa6c2f69f1116baac1adedfae6ff68dc8cb249c112ce6f9a6208cf1fb4c4183995326dd690bc4531de9ac85a0be2f6b0795b6f9bc700b7628c272f245de3210d89fb7b552a731672779675ece0963c2835ba8c6ece9cc4e55b2d077489cc8a83558a1261a452dce0317cb8ef4e8642f3d13090305ad345906b180e50dece886830f7a349e3477a0f10df57a81a5f895e8c043085d331cd1bec20f7b8792871912776be3ef4b8b411ca9cd9a9dbd92d1f66c90b23d35b1d0cad3acbffab5141b5171336753289274d897c2449e9316c3d19fecd86e454a51c820c080ceef6421565d481792501b582190d960776cc5c6bcc3f6a33a92e213f7c2e932d8f1513d1d2bc31cdb0c9550ea21fb9d5db1acb01eaa804c594f98777652a7af27184e4ada612201c80f18d1cbd5f9a4a535444934b72f6262d582ac5802bc17c106bcc4a53eb4af6334ec1eec602bef40366d91f4b4df477f2b3b6be2111e0e6223c5f43811ccbb3c31f8f4c2138927377521cee9954a493340596fa0431fb953e7ee3c0a15b37f47592fc4cef4b47c759d6278b4fe5be6519c9927e9c08f6e89c6ff99ca69c9f89e27133b52197520b873c578ade66962bc18d0726db271671bfbe8c21c16eced0675a58ff1497cb00e239481adc4656537b80830b37264e7f1b50e3780f32ea57c9125c73f07e33cb30a51ec6c4dc98c4f9337d62152ba544eebe4d7a8eaef723ab85570d549fb90687f3b4782f7647988ca3e97b6736bdbd7cfb10393405a86118ccc415a16e7614230828430728618da31e37792f043e3777049052d58957a352e54c16b3d93c8595220a2e8322f2da669f11be3817955f1a350d3591ac81d7e627015b0653cff1eae964f89acfa41663a833e65235627eb67d30738f170da134de1d58499997315a329dcb52fedc30171f948f6f23a2be30b49398a2162f469eb161e752faa487c533e0ac6aae88c6d7ed64892a0ae4afbc2b30ace36ca7ddf4f1334b6731641599b0d2540c4fef4ae6d9c0b81c2d356b98178360b853a501dc1866343e81fccfe0e99b1042d10a38ef3a5cb20434118e16eb23244446ae69bcc1a1e699cb5981c206689578a9a2b3f6aa3ca37f1e09346fa2f4f7695a6b8f7087e9763f52d06de4208a4cc02e92801883f89ecd396248db5f0d2ef527a75d924216fcae8c76178c0b7c27f618331fac021e6c9a3a9e585d1c160f53eb39ebf4b1b3d84d97b2cb9d0f616e9b2ae10bb9e592580f27918e4a17be2570f5e4283aa8420189f72137606e2be0a9e2ca81fb2312caa0208747005ffea881f8a44add38303e7d080e4be30b44271aeb4feb37101c201d0f8504e711324ecd3b4dee9d69348c22656b7edc5f68b236030273890e9cad41258e1445ec934f9b4b2b2792365b52d0b44bbccbc721494a5671a60ed4fa289e203c68ab3c4b88ac36f9adc91a4a6c8cc4c52feb2eb34b64667a74c3bcdcd6e438e20d2b6c499500f488edc872165133fadb4fb7713a49de17f60ca4d780918f3cfe19ca1447f83761ee1808436e310fb7cc32db065c5923a4537d233be2f3311a5ea416c6bf280850647c650ac01835351eede816511edf33e59f467d0936af21a4cad0df6fbdd6711e198d896115cc3dcfac0948522e231b34e47dcfd05b921df497b190af5d621c59c94c34bd405c9be00b6dd72cde87e93ea313039c01633335044ffa8bdea20d3b8ca5db2c4516b5a59512d09d281b187722c8a5c9ebdb2064871229640354e9aea165dddafddfee4dfc1001d38229e51ab7fc33460b4b1720300aede7973a8c940e6ff297225d53bfaa6880b2b4c0ac261668eef9d6823dd5b0c6215d16c00df561e9c4abaad3ef0da84ddd599d56691dd1b121a6120c3408bdcdd972f77d207d382983b0a044647cd2b86c91b8bf19426c5742b7378e2f21c0f09bff8669f6b0bf6187d44c3bd1e50dfb65aff3aa88ad00c2e12735ee384347379d2b48ab3f0b36f712e0c1bca9698d29f17924f0343fc573a1115981161036b71f96ed9659b52eafb7794ff9a05b42f5b96aa530e45f1892f49dcde62ee824ab3dd0fd9c511bd8eda0d60eda753cd5d444c0294aac617b6c6453ce8b2274b0cbc5beb68e2761897b1e2ac612d8e6f8605830113bc800c91bdc4d4c89394d33c25f6465d813a453bf89eb3f0baf3b83856665a33d1e0a291b527d6b5704221b5b343a6fcb70f561cb496b727fbff07e48b56cc924ef51b3459449fb5211c02ff081c90645ca392d5e7a13f13160acb5b53ed20b8e01390cc4d1d7ea9533b2e7a21e9ccaf0461f1d5562c1ae28c04c138083f4ebd11f75e3298b2321a20395d3fc685d8c4986eaacc4e97d6481149aed040e07239c051d761379faecbb3c3356ba37358353b204bbe96765bcd4b9375470fac05907fceec5d94cb1195227e02f7b66005f9a76ad9ce1fbae7097b0ba824c9e415f82b0812a3bc6d6ae3824dd1d04837ca39047cc27d57dd5655f2d52e1b28ff24af701c93a169bbde2201b4ef36ef361f111838e2a59b2f1ff32410c91be4b2ca0d4d434a70c0d03d8ca0db8c9cb7b27ef0743c8a0579d6c4df5a76644637bee0d45ffc4ff83a1ff779f22e49e65550b60c279aeef6aeb89c1ff6a8cff8ae5eb645e9e15d694350ac0f7127ecf632f218759eafc04a988a3d23d1347a44b6fb1f79a2a40e41f441bb87823856b221e7cde3652a62b824d5f64b08570320f7729b500e138252a6220da6811707c097dee8d29123511a236f030381533cd5233b1d2b19da47cfefd49179430b70bce17969888c2c02b75d07bf84de64dc91fbaa6cf91052d40001bf83cdd18e8860b3527d9f72895ff0c398d4945ddd569c2c568bbce302506b5bfaaaa9e24f435e73c236730bedd4b8940bccce1cdaa2abd388646474e3d9c0afd61e28a1ebbc83ac84ed644039a7ea3aa270eac6f46f21fbaedd49ce21fe133060e416b03e28059d753b025f0c2b9f25e6fe146bf9f58956083e1baab33570493573b9d05104ec9bd2764793d655bb033c646af6ff8f8bb268d35c43acf614206bd4f5635de334ae2768e8621f24457bd6cc3f81a50c8fff871037415bd543e5515df44253f93ae05241cdeec2eca35eb2908ab07dcee8c795cf0a7442ca1213449a5c03282478bf0c0755fd7f62ca140a303867ebceffc631e8db2249c2c8a9806033a201f6e59becf382ccd5167ad5d1d7a3de10492c6bda89121bb8b075e3b6a5d1b09f6ad972c0605f1fe1610c38f42ab22625341b41c252be0fa80c9d082f56fee0669d7d4eb9762af0fd827e545ac5cb6b225571540f2e6820ad79311ed2fe57e30d12239771d79ad47577647068ae6ec7fa3602e86379f7c56709f822db68b5d35440e855509a2b86ebf86dcaae220a89b6f7dea85fe1fa5cf2d54ea4b242edf1a0b3c1a0b04b398e8a67c3ebd093e4429c9554605e2f4360449420a5fb5aebc158d80ee10349d23e9d68196cebbedbd98afd16670b66231f0f7dd9c0a4404da2db7a00172ff4f48b486183b2cc8d49b126acbf5e04af0a16b36291875c187ca2a65aeb240729167674cf5e26c2eb9f8464605279f47b57ef7584ba7b6662f221cead6f89825748ae7bf4a22d54715db9fa80609be5175c30a50025e54d11ba22ac4cb17fca2ec160184e6bacaa6e49a836697106511a1e65c679cf240b128ce1974b42812eb02105bcc59226faa7f085adf7b7ef5d0b2d5d88f59563e3d2edb1aa9ae253fda7e66996f2426d52ce0d36d5e38c07b94551851f4f1057e10beab078c85c6e1fd86b2200eee7345f0e9713061e0060b6333e91fcb7621d57333d80eeef03e912c2da7985a7dbc393aa597abd67b8240a3e72330488aaf26b53d671fe2effee1db167f747594de4de80bc1f0fd5b1786d276fe31d8a631556ad4080d7674b69f1cda2403280fe1eb962bef003d4df61cba3dd906aba9c01e5644dcba01dd790611da5bc46ee24e2ba26a4abaf8ec1c78ac48e0c5eb9c3079f802ca0dfc6550ba410b8d76885506b27efeb3bca592c2475def2dbd4f5d7ed4836abf086ce8b23d617701275a8d452085043d8b30bf66e0739471bc432a155f249f1c1c58e4fa37a02ae653b0ec71e22b9fcd3d9a00037bcfcbae7c0106d6d13dc60fad3f734ebdcc0d3fe9c29031716c746b52bf7c9db5a09cfe19bd9f0b87cd12f8a6c5e36153e0bf5587c959f3636618864362195078c591502325864283ccd1ebb071baca04a50f4069f1eaea8d8d2af49e76f29e88108df904d59a6631686be5adfca48571442a01cdb1a3de6de174fa9496e9ca50d2db065e61d82c16ef01438125a96e6300de69c30967c58545aa8f96fb66725959eb0bfeb99a54e7286740bdefc161115a404556a1dd0381f772b7ab8c1f7f1ddc0cc0ac782619fad570aa1590eb439455a642cd3122b0edfe9322a19360a98193bc75e4b8d2babaf0764099f36e3dfbce751a8fdac684ca46b0945ba593b6b18514d39542a803a3305b55b8f7c3c5adb4e516379db8ea4570020e95d69afbe165e6559636d9bb9d2936ff3272d757d393937ba4ee4edcc329e17426a7b5ab99aacbd804e453f147bc0439022a3c78b5f4df3f0cc24ea8993bc98fa7e5b4d628d36ecaa66231f2c612837b8c3704ac12862a67df9127885a8b4ce8f06d7dd0d8d2d25e6a9f77ed217869ad2d461c26059d689e4ed9774890e18dfe007ed3ae55f862abd7e4b0a53018c9c18129ec6983491c8fc9bfb26cf5bddddb1fd8db5ba17b8ae0cbaeed5ff553db2f0784cbcd76429699e3490c01a2090cba4b230fba3258d7e5cb18edc5427c1061206a1a5c3dc81e5681777216ec66cfbba42b7b506ce92664db7bfa51a9323eea0169c4cb0e83f756d3ea980461313e39d208ef70dc011722ebf284f75f07cde322ba23f211815e4b616f092652f949e369188a815613f8268a1338fc8f932e4b845739564983555417ac7b0170eb036bb80479ccdc3e9867ace0604f06da016321031402b21ad4ade201e208eed72004c59ba0fc7316c32e3735ba7c8c0dfa322ccef89e50b984bb020571702c2ec6f08d0471e72acbc1d19520ef5c9a110b953ae66ccb4278dd291b08af4621d3bd4d51e84604f6315b9cc52ed68f48f6fb51332718b3bc5e006aca5b50b1bd106ac2fcd300c77ace7517b7badc33ce9b5f0302e752cc1e3f0217cb1fe79e4202cd3614da5cad5ce95ac1c22038df2e690582130000000000000000e77c5fe5829689989e0736353bc0ffd6b69fd0512b5aa1d5292167ba63dc5719c978be1cd9cabdb5c757be48c025e5efbe17937c24504332ec6dcdcfac80e23a01ce2c6d87d0184c1799a254ed03047cf57a3bfd49b8a4438d496d741cf5aca6bd02cc36601959213b6b0cdb96a75c17c3a668a97f0d6a8c5ce164a518ea9ba9a50ea75191fd861b0ff10e62b000000000000000005fd281521444957bab12f47104f5ff295620fd17dbdb799f813f973e1eaed4162bdd8d72b03b4e2f279a05dee99e05f68f38a4b1d7f6952cfafdca675fafbb0a65f0db75a331b2c04f82fed81c6edc0291ace5edb0794c285a5338f20c891195bc1e9d7134d3c05c62b251ccb3ab8473cb5dafc7b19f3b6750e41bf24c6ad882802a60d88a2fb0fd642095e8030000000000005fd281521444957bab12f47104f5ff295620fd17dbdb799f813f973e1eaed41691c948450f0844a9eaa60503567dd7c87ed664db6236c4368575d4beade9741856f5e328f9ba28c32610207662b9638281878e43a31d3f2de4e440d6a792a01a00ecd37228b00c8973da400337300587f48997465959efe09ac5e012e14f51eb2403e78f9aa4048344a45119dc45ebfb2fdd1806662aee645a85d9951b714a42ada39d7b268db0db118784846efe571b2feca12d5dbb15ad28d23c933987607fa4", + // Transfer + "04000000045a150838106cf1bb1431cf7a7a41bdd26aa0180ea70a37258739c1915171621fc8b74000a4594700861a5e1a9eabc521772e8c0675238b3d87c47343024d8497e9a7826aa3d4e1e0e48e32e760799f2a3fe2202d90d43ea589a8a3894ded5f0a104a4d0f0f0f200202020202020202020202020202020202020202020202020202020202020202fd4005000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020400008085202f89010000000000000000000000000000000000000000000000000000000000000000ffffffff025200ffffffff0140be4025000000001976a91475dd6d7f4bef95aa1ff1a711e5bfd853b4c6aaf888ac000000000200000000000000000000000000000600008077777777d80a1977000000001c1d1c0000000000010277428e76d2263100d08f73d9e12b480494f1a361b1497a85d1cac168b76e149e9d16da0b28025e804fdbcb283f932d1aa8cc19002505940524fb43bbe78d04033893550a22b3cfa9ea4ad9a19161451455d1669c9021c9f877dbc4641259ad250333e21e2620c8914b6e28bcb1a214a19a391cf371e089337397f7535951441b6b529032525a70d975fa5d7c22e78fb5059fae45391202f00a689803f908deabaf1bdba80cde3409e605aee987a754ce805f2fc008dff17023ef9bcd75972b85ecf641a245f6a6b1e8414ba0a33db1b0f0c4cead6ac87a32cfe9328773fe7357284f0c0fa89c33723326892785e8954b6b175a5b9d80b28ab20653296b132b0a8ec7c5fe1ee4cb5ef5822445173b8bbf26e75f8118f7f2e8b34c001faedaf9c341f42691a0b0964391067bb026a2743922e5a72bef9006c05656a9e8814c9d98ea8056681fcbcfda9aadb559d0e57c88b77495670aff6272820b4af9eaceef3a8b659cf01e1859b2d0825a039dffbc9618eea2653167fde9aa700f8ea376a7dee10a0e3d24e6df07ae33063ae54de5f245a9a90f7b5d67c4a0a2d94943a81831465a5cb640c4f432f5e9efe6b0249f333b2cb7f6654b73483cf2e081536dc03aaf659fff9ef18f87ca4701b37d62cf9f4e9e0dc224a29223cd41dd229795123cdf29e4c82f16b163179333bae17cc1451910b5442ef9f4fe612407c28ea32037cbcd4ddcff9a3de920186ad441430007e4c09639b0a54739dd9d12ba4f0f8b5c653fbef6332645f636bdd6979614a06ab14c88869343f82b3db59639ce053ebf3c391b745554a2e375b6e72e912abfc50b5c5fc4a3b0ec42ff5ba331738182590c57e6015723d13a66f5c532dbbe0cd9d067d8b92ff232a444263a349803853abfc628973ec6024b65c3e7048689ed09a52c122bd7803234d3d8768e7ca8606ec674f8dd1e29450ff8ff43faf889d9fe259bc4c8abf2d3f403a95f00dff79c786ed476fff2b66f01536528e757e447f10b053c81482e6d8eb2a74fe0cf29249a64396f52d5d323487f6c6dda51bc3ea95bd4f90952288c389e7562c87ea01b280e4669d3bce31bdab9b99aaa5b1c6b9cfddb25dd067dd3c73736e65bf6f2db6b90491509ab57844b2642f53ad442ffe4107d8f7b9d5cb4795e3b83316d08398f6cb80d66bf6083b655a0cf4b21c7b43f1b97b49c547f28d82496e0bb708047c5690e629ad6565c0d73452daaf2181c78e76d5129a882bffd3964937c507deda57156c9c92dbfd7e81ff37945bba2f88c4710693b23f142ca1a8c90f95c37324cc3b352e1af16b3f89c28bdcb418370346dd0971580ea7d571d490880c332a0616233d3c72196789702d506720c417b0405ff352dc097bcbfac30aa22055d210e94c34428fe2c303613ebcaccb38fafab15acc62a6d86fb6557be639e33b9aa1f56062885a0d5cb6390f898dfd592053a72c9c64af4d26f44ab10db8ed7edc22e1a77419330e501974409ef77ce6c1e5ca703eea10471423c61114437e7ff649add0b6d538934169b072739e79ae8890f1272b2c5d464628ae73d3bcec693b86b3e8ba15f153918f5ad43b9658cfdc36fda8a0ca0a0f0c79ae7cb4998b4143e1110528fce4ce51b182dee8cf013f0ac159f15d00985f3bdf5fdc1bff9cafd6db2b86cf8fc6249ac975acf77f05a6b54a4cec711b43e129edcab0f298323e8c9b5d2ef4534cf66790667dc5f109cdbf7d74dfeba3e4505ce6aa8d67fda3191d51e69223ce8cd19f982a1daf7d27d117e32033a10608b7b5023500bf9a60b6ad30675948946ec7090dc61d154200916be1510fb304372a125f6a6f96c1a88d695d5a47e564e5071f1af9aa1d788f95a41ff701d17ccb36f7dc752f74feb77815ba2ca3ecb4ccf83535e44cf09df32034c987a633901d8c3394c211c193a5e932653854385b17b8e4dda86bbf5554bc039761133190882e36c0f16eff703c4b6e5dcc325ebe04c6a4f2ba33c4e3a7a726df7f7e7fb2c583ba7ca265633bc5f43cbb6c8e4c83bf3bad74ef83e476d0846a4acd4ca93ce56480e3d8a85398c8b9c0f962cb256d77ed938add67186a4e2dcadb09448fd85c649ec4d3abcfb90800b9237f7d66e104503d6439f4b243c4941d4419efa4ca94003a43653016ecf9f854ca76959f460affcbc17629b4771564cd3f19a08bd354f1127a2cc7e8b3434a76a2b961dfac91e67ed3cb2e2a988053ebf72179e64c92fb7829498becf406ded41016166c8f1f1ca97a7a1f8510759db1c92c618a882dc5de4be10228e658fea553aedb02ed33ed5119ba633358891671ea3abf278e21595368d3af137f1a0356edf4e468d6a71e7feab0260393800d8057e27ae468471b537cf7e03c32adeb87621858dcd44f3e432d207c1460514c0a5c079379f5b8b79352d7886b606826f97f1e5dc8402022154f8260000000000fde01c5f5b7f01d9f6bb2bbce93ac4a2f7c11565394b684a5d4817f09dbd77158d07bb95a563d9ca60dfd5ef734c70c9f23704f8778f8723c80c48e8f4e7b6394dba2aefa2c710ea5584de3342d646e8d576b8242c2a6149eae2f2e7448cf16fc274a5a1e4e824849faaadde1134c281758bbaec714ebb084526647dce64b979f32a843c691dec4e8b233de6d0ce19a6adb483da78403bb169ef1fd1e711e72424639e2af8a107264810248f14aa931e357d9975302a04f063bb34adedd232fee2ee2339c3ebabbb2f273a0342f4a4ae99739764eca9c87cedab344da51c90ecc37fa201652ff7b920db0000d2d9f88226a228dcfd432b659a6c57798b44ce7891a79a6838f9d4e98c918e0c81449ac0b7d2a00817be55f9dd38f090e476058b49068faa104313d91aa024f0e40e50a0decf81603fc9be9b3c0d956ca6eb6f7445473bbe83e1337af0743c7001c1cd1f4a6d66c67f1730619ae6310b14cf8db46a931cc5c7a5e02b3288219d8693afae979edaf3e7458184cb67a0d835d440f6dcc724666e3d8879d63d9bf9771be9a863de20ab9ee301f6e2910ff058657c61197aadab9617470f9b7a1a2d37dea851ac09364a7230221a0e3929d8ffc5cafd92dc040f5c6e34edc6443e2e4f52974fce8516cb246a9eb086e1487385b5928aba8d9f5aab5ef96ef8645674bad7a567b74f43a3767a28f7b2764bfa612d197b20e8853b54532df7a813bf05c9bd1837e88b8d54f1c77f18c147b0b53164b13a5c602e97a883c88770349e646b3f657a1d8025a56325b04e5b0623b6d8d2b92464cbba20f206183733380485b86333ce2ad4b9e0920f8ccc77ca85a1b4b574335dfe252006d04f05e2d7f6734fe5cc0958aa49e94422c68259f28a51acfa9ed88d7736abdb65400399714306d6ec226cdd9df59bdf549e1120755c838808fd943709be4dea89c47d58f9a4d4a39aa3f83d7eb79f292d9788c804946648d43d00212a0ca630e4c231879784b4695f80db42ca086b0cbcf6d96de4bb589cc9d415bb5f24272dcde52ac4fafa1e7e95cc330711a77d03dfd26cae6acffe7f1f955bedca1d8f726c47fdfc6f8491fae50288f2d7fa38b3c1ce6bddc0faf257e769e3e25b8d24c3cd7eef7e3f63732791b90de42f7b926d50d1ca5836bfcbc38d39f21f780c4df8841f7dd680acdf1548bcdd5da69ac0462054d099f9704bac788463ac3c0d49515338fcbdce587f6260b056591e4e4ec9d4744e3147aae1b31bea39d70f3bd6fe63b243619713251b4231c55b7947986fdf77c681ddcb9a4f38723b6006b9d28cbc4a8e5550a9e066b0e2f72d32c40df54e8edd8f38a6d5eceabd56f2518695cc499d8f18e1876f4a9e65ba6d982da17c7dfc154a9ff84eb660c1599a8a905e97fdcbca383131fc0bd09595c9e92195f8634d8d8537b5f7692bfd600347949f0e7c628c7f5090dd95885d383b444f602a4603f2f9bb2ebe28d8d90a071daacebda8a2b0bb23f0e21b251635a31f63ca52416f5754373076822c319fb98bac4603ccca9df843802ee3a25bf536243854726d1bbef2673e897c057983606ab5fee343612d933df7eee4d3085361767a996c872587a242c5a1c0aff3ab904b184a5fc97eec9cb6dd956e113f8253892dc111fbaac6633a92e4e1bb789f727e96b757515af3815b19f5e821a2f3bdb5018a81253b712a2e92a9813dacd0db9110ab3bc43bb6b8eda9d9eaed7d3c676ef98b7f47c7deb88eea72bc3874226f45206e195a84968a2eec341d7e51a398e742f6c7b283ee6b20fede54740caab1be3baaa7cd046fc90bc8a3c3ad59572bee829dd6d5c996f0fc2a7afc80fea51091b377ca9d31cbdb87f9e316e7a4be26c8e72a6cdaebe0cd6c5e095048523086582ffe6aa6ee29943a5a3052ae25b252af6467fa6090e2ef7bb8bed2f52b4a7b479f2c4def5bd5f427b59ef8cfaaa4a482712e2866dc582d7dd6f7c8c26bf2b3650bbf4f83110a2feb1fe8c7bf91dee1b3b40cc7693d7c7b3762c699c627562bbda24591981715aaa4d993a43cdb1c8c5c9eb4ab9f4ece1e6267ec2d24f37f73d4827484fff9cb6855737905081a7dec9990c400d5450ef9e06dca4595f8c0f840231f1419819c0e49275f187b60fc751565c3c14605c96f368da6316caa0d249a0651df95544a6042feed75f0d5483dbb44c90059ec6a7b4453ae6c8465e68e95b1718e0f86f8ab7e85f2bfc3397c7ad8c41818bb708d1e19c3e78a95848ddd1d8e6c99430b07fc8342145492961361ba1aaac73a023bd7e08b9e1484635451d930669d35f00ec636d6dbec5a8633645b97241dfb40534c79d0c924d0d30c1a128d61e1a41842ce4e6f74af2956a0599cf49247e66d08d751710647560042b9923284b83b1e5f15d5fdd10f068672dc30348a24170481bb7932385b565b740d5f0fc980326c6f258885de458be538960dca77a015e1082d67c1fa9a769e7f25453b367bef7b28554e85012eb60dff27d7d965e2ed36d60e68df89c78df5b568931a8c8282afc010829fd7c71d04c0a53c4922892745639d56f7d0cacbb098959a292872274b1de7c2b072d1aa331140b435bc1305a9b56e86b3839d2d7c9f001432f300edcfe061e4da44cf7c12b4429c17c8a6ddcbec7ff1828c1d039861c6e20affe7d208f91c7c7570a0174b98213f9e4ffb7631d853094278e31c59158b651f268779b2fb04ff5db94e525c22d45bf3dcab3769afd978e82c4fc4dbeb60b70caf072dc92a8d53786d68d0e734407c5c05353bae108e5ab0977ac6c75ff2731efd57e31e659dbc5c885f542825209e5add0507aa11e5ab5257e0089b3b55b415486e1f4a78ae81439ff3c0480484b10417ac6e72eed5682fbdd8c0ad7b383f26c67da605d632824816ce2f89298e9904f72ae3e05365ffbbce328bef1f2d62064db87dd089795e9b1cc9f41a6b3b7ccca6f7add2a0fd659b6aeea04a7c4749094a9db31b9d58ee0307877f1c3ce5b52ec0a82285a2592148ee95d100895b182a020c884a79bfd6df0bb521a2d18991d20939e10dec1f1e2886ab1522fcad6b0ed8d8851fa635032c09cfe231b54d702ca3d11856517f7042a7b406ef8193e31de76cc220e0e278293fece97107fea0785b9e0e81a57cd846bcb93638f47db90220583cc3c1575ac33bbcfd8cce592d2e4436ab22248a57b19f52973a85dbc61bab2804b6e94282df891998e45dc21858faa190cfb4587efaf633c2c264afff21841024add376cdb62d2633a648623ccf6e07d51b054f25e07479f5aff5627917d845aa11d6b25ebae859fecb95e3d3f75f46a2db06473bd20746f1b44540f706d9596393e39b7309f79a15471ccd3905f446467dca1941ebfdd161c75fa1841966dff77b2f2dcd8d7c31474f6b1c627530cd11cc002333c0b1fd06c88aed070074c2f087080a3e844b8a32f35bcf7aee15be92e1ad6e94ed3c1bd975409af526364746350b5c5ff414e27db55db958cf9235cbc27c9a7552988dfdd7b03bd90b07dbf3ffeeeb7a5710cb9d619ddee547c298a380c96b05c730b0de36c1e17235618d52eda4649baccceed7696d72962f6b8015d65cc41fcebe57a99fe26dca1416505596da41a2d2bbd1995e7fdeee17b264158df354c8be4c96846e00ece9092efd4d31df72db8220cfb53b0cb3d189263274ae17ce0a45ea5da15bd2d8df3a26dc211e75881b96700dbe918058b1d5f47e82476cfa3b04149d770f2b0e6124d0abb631ce6aa840d5801ee701bc4c4a7d1896dd37126c89669c55b13a87fc0c4e8dd5ee3271a2f8217a6b289753a6be37438f078bf6592655fb55407e9509079a16f418aca002826da7d772c7b370c02d964842e3710a26a81a70cd08ba0f00ec9a5add6912c15620aa1b97015e7e43e3093cf78d21ac656c1c2ba1b08a35284f29c4bc6cad0f0b6114b309a1d36c2119f9a7c4089e2d36070db1446b4350200049a308ba00f480b7b76a12c185e8dd409e3f6083ca214cbe8eb59254d39e23e827da803d321c4fae26fff2ae719179292d31f41a6b6d806290afa867ae06158af4fbd654d82e6fa4e54501b03a024f3a713c8a853bef9599947a2d3f14390df2d29a39e03defb74b6715f4a06e165c933255562b71578880d420f93dc0740306fb66a61def4751a6a564ceb951f1e4f301c71801f4ee7e9512a44054dee51c141b5e191a1a9d5549431c0e234088df11f7616f56c52a904482a9f6b883c11a469a1ed65f553c61ab58517a2b6c39518898660322132f011a0576b18afd3d17831a60d8569086420a571e80265793e2150d565947cd1febc29a43e5b34d3d294e1cb533e3c54d54590e6a7245688d1a353a79189ba29efc075eee5578c3d403d58585b9d28aafb1bbbebbed378c22ff18d588c01f0e19abe3d48637f71595339307ecd2f45c861940a0edde8c52756c78ca261a87c4a846e72efdefdb2119303fdd31d38fa2de10576ba5be8e034da418120cb5b822e17aeb7d60aec84e7d3924e07ab7d9041330b4f16133613817388e241b87ef2f15b6d514c36bbba812269826be519c6f15d10edc1f7783000adc6c3b9b73f500d0b7b94ac980f7ceb01839a5e6ba66bb823f84dd59f78f741fb3e6213cdfb1489af600d629630d6ce62eca9957816c97f1d224ae7d46908786b539d39471c62a2ff0727bed8d13b61b20df341e8bc535a4fedf4c96599c6455fb1b912eb941e86ae21f2d60cf95a0ae107e6c8c0ce61e39a7d65797b6199e040f1bdea88b615bd792d732f4bd7f6e4d153d723b58521c48a479ea38fe33689e29d9c675a8d8085358e1f4e26b6accc415d88c76cf1fef0575b2045792f86644092fdba99ee25eb313681c732d9c54b40d3bd136c8ec53b0f5f1b23504037e0f36a18ad80eb4fb0880de68150fde5b4e149089db88b538a0ee7bcdcfae5311db63072fc2ed9472f44f73e640ddfaefed1c621d3a0f8403b26da929b80e5b383f51dd7e4ef6c04d4211da39a6b6232fab187b379970915f566a4365f91f6e5ae9781a47902ade4eecef522977f9cbe8933dfd5220ec3afa8b59276b9612ccfcbc2c3aeb3c98af42e2b24dae01ed94706e25d76b3344b124a50dfa4b94b1cfd9f335c31b05cb15cbdd40f9f07313ef792e22182c9641991e9ec35d2e2e80c3cb8ae112a2efe329dd77e843caa4cdb1c3b439f8128d2214d3becbe602fc616d8922c4dce4ac9205458c1e5b4d7c082826153746243c04b95a9b48f8c637a6229791e13789e9f424c11e401a5a684ced9ee7272aef7c63a6f79d864f25234a9cd45feeabe5a2345db0c7d8e8f5420e81a65a2cf6c1857efac87f889e6a40cd7833e13bc47e2722b3ff26085a832e3ab7951e144a3012e935353261db512a761c783ab7d9d54d880a1412b1a9a5b4e521386d086107701190a4255e4df3951d3d8cce874ab8792bb0aade5aace3f72624b59506649a6cf1549241219efc79abbec787e51fe2a6c5c14b957a1e801fb9cea2ed31fe69407798de057aff0993bc626b0393434ee0430e97e7318b7d5f4cc8741f21d1b044beaf5f4f18dccaaf344be1a9f461c988f596561ac1d50d5cde25cb571f343552a53c32850be39674463d24d2503d6c2357b86ff0da726f1cb62c397c10d61ff182908369be72d9d843d45604e72da908d0f68eeb20022b049a67007028df6d724410d96e38f75399e9eea7a0af2f21b99c8e1e1d45c3ec18a62771c734426aa1d979342838259c2f1fa6cc5e8b07b2895970cc36ce51acad66f0e36d226747548918a36e7b8354677c05daff9f2a9856206bd367a1ea359d284615b60b85be649a7d8005813c7bacb7831f3b09f38fa4301ffbbfc5c7ab222640dbc8382e95ca381c38d2d30c04f0dc91802278e2c86bb0f04908169a183fb7dc75ce079ac2409f03f4c9f2e845a72c3af7e9ca63f52cb773faccfc306b9d5ca3ced3255fb435fe864fa013da761aa15e810dc090bec759bc19ba9ceca86b1dc30d4931968d412410d772159aa5b83e0406953ba8ca2d58b893caf4a01400978f40e915d681416f59402aa56921f6e8298d5624b46e658524956031804edb7cd3e86d0a84e2298c54edffce36578d7f4499a75b4ea6d5ae6b5723995e869d795fc582149a27dc5ac33c8e7595a99e32359b5d96beffa41a22481a7ede41c239eef63e2dccb61fccb71a97591d5fab53ef944ddac6325d12479c36184223cdfbcf96c816f1b9cd1cd114b84c879f26127bd95e8a45587596e1154fb15b0630507f5a5e0a966eff2a049620e6de591089fd00525616d7d41fcb86602c64b58100aba6fb2300c38607d219f6f1976ac7c50269183e3b58f4eb202e8fdabf73d7e19ab5400eb8f482771c8d8ad060ce38ef8a7000c04c925663348ae7121d6e69e56e46b433623aa63336aea5522ef386c6b2a35464e0ad87edf3a2dd59ecf8db4b05d4f62577692aa687269482a1ffca8efc5eca9798ada5cb7bb1cc9fc9def13da0d80a1ffd8f1ffc0adb9fbde4bb208b07940a903076d0f51224164ce050c0d67db17703bbb38cfb5d67ba2211059ce12f3f3d4ff7b2bcdba0c9508334d6a1a45c889e15c02a128b6a2a31c6aaf2303fa726edb3931b03a8b0d7768a055a74e280dd811f07e3702b903c3c54c7163603926c5c7b026e376d6b2cfa74725b876c39818c09e36ac45acb677a8971b0f1c10fbf349897aedf9fdcd29526c5026c6f0828bc201c497c4819e662bc2c756ef6ae288116e78567980c00556c7da5c042dc009e3dcb5026b0f29038560512f75af1c237e281b758bf0ac28cb46ac7f22cf095850170dc2ef24162661163ee3080e44c9d8baff9049d15a4deb59619123341fa8a3bf3977acd61739d45e89918064d79a94b9727f306e4323c4763feb5fcd08e100d7f8f7b16a856353dc0615a0d6803211bccad6bf4ef542ae042d1b54967119429fca21d9b133654b24bfffa93ab8b9bcb63fb341dd8e6c0aa63b8bf67e89f0b3f48fdebd1006d41b7ae8edebc08199d8f175cd05c094b8636fac7e62b879d9119fcccc7484fd0b00a73fcd3350456df57e84d37eaa6081e5846b1c164cb249413fcc2a0da4c281979e23ae1137839a619b78355ef3d6f130ba09de8556d2dfb2d52b3ed6ea6ac5586d9984c19688003c6aa7587381a2ffba589bf954f0744cb02f9e40a254b3e4ae475d335c4526469c6ddd5ef15f68477b7994e7e7e4a33e33cb05469825ab87d3ff5e77c494482fb4e8c67dc823d1a79478b3a921fc483051f009a5e4489dd1a8a2226ad309ea7a452a2f1c599273e60045c569d993f400e09331d32d09198b4c188d42e5786adb91076cff9b9d0fe3e257bb005c86cc85f0176b6b8e9e9db7c08a62cf245f1afe89cf899cbcccc209ee73fd0b5e0c9d60c1c4ea90500043d45911c50851b91a0baf55ed50a758b49b530b82b0a5909005c1c0b42ab96f5fad958be72547fdf142a7732e2f987675e36db6c86b44b64adfe26da104f7362a061337d710c80815998a146516313bcfd81fe271ccc63f5ef8a26e54fe461c5309cc62f84f6c28241f8fd5a91d26f182b4e63818d56edb5c31931c8479ee3814849606e1a2f12c4ff79fa2629278a3cbd0f2e8af6e38a6b6a163f90e17411788b805b3ded17da1633e0780d8d8dcccfbeb7a1a8cd8001d562601042c1527a2d13d0147ce104f0b7e7efb8cf7d405954d81f38cb24c0dc704a6e1b1e0e15ac47fe8bbaa3d6d80ed06f1e12f68ea9d97538f4096f92c0d35b35c31e87dad3043a4f4e7a2620c94da9876024ecb5f0176a6881c1a34048fd96a5440666f93c4df9d987d4a5af51a5de2b816816f2817ed3e7c53b47dd5d799d44de20688892f35329100424b359b4094315ca4109571e3625c563451ddc51c151c336a43e9506026738da16452f9865231993d15373ef6f5e2c7979b78ee0f083e132e30a04a565530848666bba73f62b1585bd49c249e16499822e21094a356a3c36418acff77b28e894fa80ac8619199a2f26100ede26e34facdbf3c07e7cb0af36b37c15f6bc0ee6fc1e59f41011d913570f885a0b13617103d9762c34aa5bd20bfccc7036191a266cd059097a3d749a3b3f30770729fb8ad2de4fa97c9b42163bfad2c943a30aa9cd72f065535dc8679916e3f7718960a25dfe592893bb2d410a207c0c172c24e3f02013447e836d474eee559c7d43d2e8256a4f96eb6596a610339cbc005acd000dd5e24a3b81f2dd7731cbf9de138ba803b9eacb6c6eb8533f3443a5ff569f97c5db388443193f753e97058437c1a2de32e43dc8d37402ee07843d574ab980f2e6486a0da96ffc51005ca65701dc0b26fdc08624ad993dd930aa595e22daed87af42ff6aa0308c6c7b7c4e397054b8eafb7240024c0f09e80bfda2ae4eea26ded33cb018ec5aefc04ce45ac0581fca27c7274889104b8d2914e3cf37fa27fcba9e1f5e02aa76bfc5073b04bd7b7f2b3947204a5167f879733a8788de4dea7cd8f4cca6e796165633b24dc97444a29d9b6339fe50b3b00d08109f6b971c4bede9c400920a3e308d92c195353e42ca132c6aea2fef7bb1f8932a97270047b6179692bd1030a5cea0de226f415adf937669acde0174873d363c2fbb82545895303cbdc91339a66ec97e042a836a30f03b7c1933d6c2ab80023f1992ed5f914d243a3fa668a0319bd47e5f89eda4751d72ed6c39558db626c67e237bc0904658cc492c4624ba497ec50e1c3e764d4203e5bd929cbfcc0f1e6ab01ccf0b15c2ac6eca9ce6d87ef1fb1034053b68922f6f842e14d6397de6bf5bb406abaa81aad78a977cef4b95abcf57d13f99254947bba18751434cd1cdcd119f0687953197679e2de0fb1fba3cd8d692336ebac6dac2cac0136937b557ee91c4065f65e50be6c260be6d0d2c087b890e70159e9328a2d2bc0a64bb4cc51cf8be3d62a3225d12cb45b6476caff1faf1fc20e33f138da6e3b5fd6c412788b05b723741cb9aba0092d11382b04b19726042933cf6055e8b0be63351a1f8596b471b147f3dc0c119ed540c29fa3e629f977865c359e6a76fd2c73a9be1ecf85518a72634c8f494f6863f28a09e0de35e749bfae1746dc2e0d4e7e85f45cb2fe4b81304f802f9cc403344593367a139b47fe6cb72b701fedbb2889535db9fb2984e1b0a8fd785864374d85b77035343d8d9d8b9b35de6a5203f2ed64723f8ecd31f882da867969dc4ea2dc8cd2cfa75a79ab22fa0250b4615706c8abcd1be27c4990b30e8f20cca2757c204868719af5acb7aa61f94595f5ee3eceb730a83af53409204ac6ce777c200dd4b5efd6f1ac7a6f8d276b8679d05149d2230e974e4dc599c13776c07d64defd03f0fc7373d7fe197f75a0a5ab2413040e6455837dfe9bdb5a7127ed2c9bc8362815582314f1b17df67853e47cd1d718fb2be813f183c92663cc60c2d0b0e0ad7ac2895600bcf757cd4a57145efc25b1d86000ad90d048d2985ce2505394f7ef6d0c41efdf5f175e84fd54718a0ae0a0e8813defa9a68fb960b8ceba58d17318dd0b8b41e7f785a5265401769b034f3692e5e29b41f0f815f0b6a10d6554fbd20c671f7cec90fad2d11fc6f54c79d2fcb40c087ac05f7df3f17b3442d1de69264ece23b9866ef37cddfd88e860c84ff9c9c740da06ec6a1ac9965162bfac11307e86e608336ba037e047773272c9ba68262a355160a42468919b48bb9c04e395dd901f0e2294587b56b46cc0339f7ce1516a038cacd4debe48b1429bf66a09f23c05c1940d351b2e7a3a3ac4f7fb3d09ef57a3dde809cea050f97f8f14ced397434ea778fe6c2db7614988d1ee7b0f616d74991a935aa73671b66ef0ff6a4972451546b61ed23765b5377068a94e584fe4bf7c5290d43c228380896ef7ac779f596aadd22f6e07184de85af22eb2fc75339f16b23ca16e6cf3cedb661d297994432f86d2c8f28e4e8b2b1e3e57cbb1480d573fae7bb50004e1dfc3d315763809531fe09536b2dc4d85a4d3259ff0ee91f58e7627db5de29b49268067ecf7a9f8e877802d33a2045db36ea6881d7bf0d619645cf639fa1fb7027db73c04521918393a7a789a7ae1639245e640767dc664445ee9eb1e7d9a9f5be5381e2b232d1006be42b3e0b972fea958604b808588203569bf43a876ab4240bc349cbbb2a4113c510953a64ffb935531d7401d6bad817d170343f01443f86282e263a21569f3b67b37e4769de9c694848e07c75dcb87778fa64397720b13e8e38d86127e48ea3222ceeadc247d2e61525e2989986943c5851814b4bb518f596a1673a335b4b97eafa9d51cd915bc7f87223cb47585cceca66fba57b3ddc3110643f5ed362eb2413fb3042b5aac8e1c4c659bbad0b4d383d8283660cf389e030216f543d37044218e9b1a8eab0f91e8e418ac842c1e2f99fde11bbe7f7cef9023c4bdc6065bf41615370e9e69a5afa547633146902839b88cb6bca91dd15051966952e5f62f2b4a65225e1394d7f6f4784bbb8db457fca477e34303016a84b28412bab26a001baf05eada79fd337d2020b56367c6035c3ba052552dca09214aa29a07c9a1b406950109b30f3b69d72d9782a368614895e6ed2c89d71de52313c0000000000000000b311e74f42e7c3c2f3020d12678b87cd7a16e8442000411c2326cdae179e7db0841fcd7b4e892ad85c9fcc11a22a1c87e3e2fa36add273bf150b04fe1c19b43600", + // Burn: 7, Burn: 2 + "04000000454789e1a27f3e0206b16254d58d17029bd5ad228109096c26117b4402ccaa8d7af0b2ef5aeed1d5fe7d09013785cd1ae671da0c8e2f9c14bd9cbbc1178588824ad83a269a743f8bb908dfdfa2a23c6832f9dc0a2a84f9cef5c2f24a434fad3a0a104a4d0f0f0f200202020202020202020202020202020202020202020202020202020202020202fd4005000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030400008085202f89010000000000000000000000000000000000000000000000000000000000000000ffffffff025300ffffffff0140be4025000000001976a91475dd6d7f4bef95aa1ff1a711e5bfd853b4c6aaf888ac000000000300000000000000000000000000000600008077777777d80a1977000000001c1d1c00000000000102e4cf041ce2714fce6b2deac48e63d661ce0fb4622102f16275ac8da3f0b1adb066a16328d511763a00fe5e4f92d22b3514fd59b1d6e202a5c6d9890b557105000d9f2ca6f1f8fa39915aa49ec722018c2101077c22bfa250492b61703d1af03530d23486638dd2f5ba49d537d1d8c45f965ce6a0a14dd5097f44532166e534255d6477b31173feb480ec9ba5aae0db74252def312bcef41a011166d5f33d8e0789112c7aa60ed86d96287cfbe3e8e20be82b94c97e4d41f6b819fc36649ba91bc2ce274613dc06a6ef8fca001acc4c5028dab519f2b9b6fff9baf676d1a69857e50de5f7c37602e6a7eb953bc3e1ca4e6e14c024d59a145ed5289fb3498b53f0da8f5a4354703b12b06388f307741479fe70ffc95ae1430cd853ba53778ec33be771d78e312bd02494c462e258fd807615516d5dea59679f859b63ee04c82b4fd43fab331b5fc84f5bfe149c71adc42b60e857a7cad23f484a5b7bc7b0ef66e4fba0eef3d3747bd0a63729bf9cc5d9dd917b040368dd6b0ca7bf7a6070ee954e40d901f2ffa6d50caaf4d721653d5fe254e82fc50ddebb91c05e8b83e55bc4dafc45b078fce6391cc4d0456128dd80ee1c9965a9371d2f6c7533a6a49af3ad85da81bce5148b23589443166ae62137855c0d698309d0108f0793e7a9a852a9c7597b1040fafeb9d693219648a3b677e6c07c635539523c6a555317a925489a9da78cd8192437886b07473ce2ca2a3a7c48726e842d93a80c85fa2022738a4325f287880ba6b6e8accddfbd5ca28e67e5645328c9986e6e4b38557bd175e5dc92592331df1701c8a1bcfebfbf091ec246a46ca1e0b4799c5c35796015b8727a2e823f9430e25db23c96ba4e5ac3ff3ff05a9467f032a4550a3a777659973d5d8f7366e6a96e2f79655c2e885018096d0460db04b9238020812d6164818427da0e58ea6212eb91094c7c2ad13b124c965097da1c43e40a2274f1870fc60f26cad967a8f15258e591e3a7f7dbd17ba04389e482bbc79cbc73f2f30e861c927bd24f6bc825de62778ec9b61225d8ef0559ab122f2090de38933cc0f2007dce12edaea60f6be5560de16ec336e78e693355191b02ba0641dce17315ad08ae6b03bd71efe5b5bd24c692c50d6c165d2c8807abeed676f9de986cd45790ca193cbab1f0cb07ac42cbcc89cb8a7b5b709fa94aa85174978c868f349d10c5c65df30cf5343a5c0f13a8b36dab8f86ad272a64888078b8c996f506c2cbdb73056664d1f5c9e27aded03d5f22382c43de50db28ab28af1b9e00813b8bbbbd6173766789143824af9d003367225c018f6215587b5cc76763bd24b1be9d6026de65ce9354e1bfcd9d44ce81b2eca498f81660e17ea08c2d1dac1f8571e426f131dec26cc390884acee64da070eff9c381a4e6da1bd9adeb13b20de77b8ca9b9849a028c2c2f079c74fca62c34bf6672fdd2226713800e96b10fef3e60de8263a2618bbc49f5e641697fb8ba4fc4ae2ad8c7241a91a09f4dffe1986dd01590623216c72d70ee84e13154db0cf8342d6f36b00ba1bf8f640868d14c69fbc6765d9fee53f1bf4fdbd73240ef4a6969bb3791b2206badb2dcf97173c99cab5a39bd3a4ffe49355e294b33cb9153fe32dfd196842e3761f51cdce3ae0df0ce51b14e5a6eedf3a9a57c9cecc0f19730f6b82c362cc79c0001e2122a1c8fc781a0670e09110f2c64613286fadf33d16e64ce192c8dec9de63e6bc7734cc5b1d24598b616e83edf9fb228ee724bc0d0dc8a858efdc01bf8978ae1a9b1cdc23ac54b4af01ea7312bfdf3316b23aeeec8f51c8a12a89a8416ac1b5f7bbafd83dded33485e68f150e39a35e70b6ea6514fcfd13c5e10be2dc52a3c2e05f37e327f8b2eea2907d670b592b31a877c3e8cc326e2be8a64bd9a5802aaad49df3716dae08cd1704ca950c645705332302895ab0613e1d8d14db5d9e46282a1051f07f3902b69776166829ecc3c2381039c1d892c662898eb9de09432f2ee145ff7701aac2d4e76e215917dad18d91ab4abe068e20471e071bcd120d36f824a77f0f628ccdfdf28bfb7789a745d6b30d8ebcb1031d55c50b651b2b23883bb37b8b753822dfa61ef703aba2b7eb84fd9aff965af4225e16773a79cdb85e22b0dc4a3ae7e3ad62a573446eea70d51238e529ed0eb1c0c71f80e0a62dd8f9a830a05cd0494548aad157a2a7fb2d611084431a1ff9724d8879984a881259dd3355452f08eb4faf2905f26c22c00ac57fdf6ad417207034a9914916a163b37de4c86154813448e8a06fa64b7dd6b62164f96d0f5214ab6c135dac363034ccd4aba000d388e6b259e531bf00be1870efe6157604ce0f90b5a226c07fb134cb17b172d27f45d94a7308cad63bf070c7c3785e47bb1b99b2952460763426eab8f9a114424138529f6773f4c4293c47c5b8311b8d0182ad0d49e823900000000015fd281521444957bab12f47104f5ff295620fd17dbdb799f813f973e1eaed4160700000000000000fde01ccb8336bd914d2840536422f47a495f99fb275a94036bdc26357196da4bfcbd8f1c0558e6698c8f5de4d92c97baf06de243383ee008944a81f75506237ac1dc049cb22a12c152b1eeda269b4756e8ef1f6e4fe50f2bd65fb00acfb460d178fc365b437135582d045f54b623eb978efc37452f119b1989169ffcb8948054972a93a1413b9364c3d8c2709c7fc71c4bb43cf678b295727103d0c573eed11b4d138573d9068a7bcff134ff25c0ba6ae65f7f0eae31f129d238e4e855b6d46033613f4bff845aeb9036b440b8d821c3c01527b797fb5177b13856b0b0a417bc8b1ea6b076f4bad29d5746f76aa64e8da22b33db49bc6be2938b56864402c69c96849d0b5caa8ae0411a1724cdfb42cb7d86cb30a8bba11575dc72bf9aa6c296dc710420c9aa6d864de981fd735f00efbc392087d927a4fe65bbc0b6fc89facfcc3eafb5d6e00ed6d567a6dbaff95b57e858fb4303a0d108080638a1ac7cc6ce0a86a16b8fc3d16178e3dc946675af10d1f964d1f391a8673b7277bfd0eabe35db6fb98a465ed64ff1e311aebe7c4bc6c62d744304454e701699b71ceb69fefa80770ff25191d4fd2c23bc9b8790833df08d383df7248236ee9d492ff18d901a7dc230039231d1cc7742deff6a359640b6eff588d2a3137ccb04c0cff68cfd05394fbbfd4dd09e290f3842af42eb170fc403659180d38112dd0e5b6247c56ec5ccf3ac8398bb136247de11037a94005897399805533748e9a71723a6c7d791be744134d46835bb3818da353915edfd05e538cbe1a30d64a079f5056a9b5fed8bbeae99bea0b3e0b54c50f8a0fdc42db5eccf41f57e2fac38c0310f07b8d6d5255d723e9ff1776847795d137224ca2954e125547963678967da6b44216b9c53d6c9b33bacd913203e08f3551a334dd4d7780487d68bed9631ebd46a4bcdd08e4b13e7aa51a279c45cf31fb00a68ba389bde4e033ae6b4359428f50062c69fe23042c824b5712806cf8ebd110a792d399f50d6657b714401475c868f1cc000a26fcb23243860940449e08c7983497ac88ff431242abd6d0a2e489f2f47c046bff32ef29774a3fe93fd710b6d45da4fe9785d07bd47cd9bef1050a125b3b4518a44cf3baf050ecef26b2f80d3f28c648e8e44695e2e605fdcad802d0216afb932754e092a3d2c450aa42c5011159206180004be19598ed9fdaa0f27bf77542f9709270615ff5827646e11533285e466b8cf1af3674b2f4ac74d77745ee5f8143c8606c40af15c612a5d2e65b4eafbdafe0a68e57eb6e0c85f8bb703a9a8ab1b7515ef67a924479a03d7e710f67b342159c340e88df29d30d7c0edb572002da039a07bf9af819355602c019720e17873c0f73a359ed9fd1136d5478a2605de995482060c9c83f4bdd4d000280fe96e40e2c968918ea53ab2159b94aca623843dd9923fe6b609ad501fe6d002bb36775f274eeebe449b8cea00ce80b9e82c97d59808fd2ebc8d3e5d0245f0c4d5b7ae85af3b3a36f9f40dda9bd282e35f2bd2608d1137a992be3ed200e36ea82a93c59f37a1d657e1e13b76496408fbfecd9dafa4a8fec72cd9ccc036cf48ccc81b8a0e792f3d97689259bd9c846bbdf2e51cd2356f2c5e17eef3d0d4a33b44701b18e3475ea42d37639fed6abfba19498483de9c26e297265b721ddaf3c6ebee876a9b4a310ad1df24f6db2b6614d88b414cc5df778d690f750083ecce0a779d713b088c01b1f0010106f9290afbf92c85cc9d0aa9d8730539fa2a43f8aae9d19194edeac1a8571044061f0c922492f179c5cd54470f90ab5c074e83575645c0345c540ef09e3f934fa6d09210c101ac83152ed5480acfb64cc5cdc59408e1899c038eb0b0254a130920222a549c17992c445e46522928a6033b89fd07122063ab40ffa3cd9724cf2c5f1b170577ad9418666aa43f537d3f35bcc475f89930dc9f5a9c2b3f88aeefcd6689a14ee59b632adad7cf08db8832533281dc4f5fdefbcbc06f8c923ed2591458925cb42971f7d1b469086da757827044300a8ed82cd362dd8f73ce39cd2f7d511dfcde84f7fb230c2f03dc1fbc163c650d7a6b6316196b5571c07cf4bf00c142a307d55b57bc8cf5f91d67a7019bf3a42bc4083f455594f5cdaee0ab2e4f49608ac53d9a9dbf7e2d9b6e97f6668d8eedf234fbc05e5d15e42d45a68c4fdebf20173c2a713eecc3797aa34822f580668da9daa71875189a0be00ad2882e14a4052291943d416157e08e800b479927e87603502553a51210a19716beb98a1ac009066d7a811228aa3361b151e25031490c3b3e2b7333eb20cc1fd30ddd02d57f531d0bcb8ed67b851c14366e41cc9dff350bcb0dfb6a09b8b2a5261291f850f86efd21f53bd8a1dbcca9c98453dab7a08ee7b0698b3b63d65964cf8764c961c3f4100b02a71ddaaf1cc06d210cf83598846bd95e74795b5a374db1cb0e3642706583f9e53cf37c760b0710d2d8030bf2113166450b46e82169467aa0e5de3afad785dd7593ca67c5331d5d2b5ff31ef22cd6212ad04872bfd5d049ed92fc9d58759cc09c529259bbdb789f7e2b3a0af540c8dcb224abdbeef9daa3db99d789c57d01f75867a8f8dd4db47d67f49c1cc04ef8dbcead3931919f434846f43ffa096ee0ee4ecfc2ed9e7a48585769ea029a5137a00a34c97429694c757b6d2e37ed4ebe3713e951f026c35af69df0bc0d6fc9d064953c8f310b085b2f7c0f3dc7713dbcd5381ef1b1a6fbfb0a594e1102c0e1cc95232c3f38e5146a999764483ccbe9336b24998bb35c12861c0a8cf930f04f137b85315a84caef02cb669ad4f363896f98ec3ab4e5d7f81329748dfa261add21a21683ea70f13764cd14dc5037b6216777b145f01b8e76177ad76a3120f4d8ac9e19d4fc0a389ede0dc9a0485fb8321e82802e5ca7ec5dcc462604430ce1a6e857f274c1c56d2cbe9deab852559e65887dd2ecd951327eabd5f6a9cc1ece73218f9c4cd23148258f8800dddce551810cd8935f8d2df2e483a2b8657d27646aa378b8e14d69db375839c94599c298c4a784e471ce188483534d1aa9f1057e87a8b8f34be2c47723b7bd424f168a2518e1f39d0c246920517e8ad28ef538628554494f11b23959ddac9ef39ae2298fb6e9a9ad1e476aca957dcbab5c882d82af5268bdff02a50d91f4bd65e7fc60ced2c256100ad4fe8f2e78201a707e14e539012f535677ee2101411905a723c34141fcc36a7ade3e2617e2e3b395f234eb2bf69ffbda482ecee215a07558a73311c1bcaed764f2230c286c1b99745303dff24d5c6fca5e1151d457a60fb84d7c6d6884f405008bab90ecaf8bb36a0d2c8505eec1f8dddcd5e66915e129298bd1b260fd8f4a381057adfe6f4124e5723226d7f8a48560350c626ca6452d597a4502911e9fd29a6900d3d343586758842f4cae3f41d6d5d8b4f1664788c4b2181d1d8eaa179fe0b9a80982d6a5930ee40e506fc38da4ec19f921f5173b74965a0139af6d9e971c3e5b38d5088620b2bc19d336e0a09758647124935f1c0b0102e06435a8096f15c8665d7317f98b69ac2a6622cdbdb780f684e801f5e4f181ee4adfd0cc9a799716bbc84d300f49c2af06fdf173e45ac5d8c3ff4de464004dd1058da5bdf8df2197d551df5575eeba4806c3821cae37fbf483e3bb65cf5f9b62ac0d61d9d97d8ebfdbc74fbfb59a22a407e5dfddd931b186a0df52c2ae60ff5d963913f282bbedad9674fb6a0fbf32460e1746ef08c3ad2b0157db386a982485ead2ad1b977b92d066725e4a76df59a10a508e9c00d8832eaf72b84c7f276e1504ad16357db8378a71a60023c900796226602ee97277b899917d71fcf11b4f4125c5b454dc1dec2971424319ffb195c910cedd226576dc82b3b84e2cb3ec8956a1805a7a4fdf5a9e49b6034fb1c7f2d131368c9cf1daabd8ebfc93f499d737e411792656a0e8fb0c89db1f351a10458a2c8d5803c4e8ca672b3e63c8be3bb545bada8261912bd4dc018fba6d916c2aa80fbb7d4f591d915b21d87ce73b52673d6a3f8d765ca584bffe6e68140e7d501a20cba127dae5ce023bd6495261e8d4aff031385c693b7499444c41e95dce8ce00027ba5c6882f309e7c01fedcb3cc95bfe5322b5d95ad15405c062a3c0d9977906c2686a3950a50694fe971fbaee9b2c8e6be04c2d4795dd0e96ea3d263fa57f3598c010e7a6497e3200cb2efc3cb9b155953eb20cc616f811b975adfaf7ab5009c2f07ceb4f8858295fc05743ae13112e8f8cb4fc5dd964df755c3f01d184a33f5a6543ddd09eff839d06902bde1cccea58c42ffb0ab1d0d1a0edcf9753b51a026d0695d39dbcfba0d2fe88636926147e59ede5387411cabc27be6b90ae06291da3c69147ab757fd19529b66421cfa0ad364808d2a07420943ddc5797c1f9bb3cd02f9c017587df52e3b552a8c4c91a6d79f688ee83f869e67de7195de505f21c6872a4f0434c9445a62e2d93d3d87badb66eff3a5c7eb612e1e2bd17fd26c43fa24197fbaa348ee35166a2c68d7e81499736e501fbbc8ec8d3fc9179d717a4143310e64ff307dac933283f34a8d0aee966df245af33756f1aa2664604c0d30046f030c8a366c0f411f56732881682ed3d04c26a1d2bdc54a87922725db56420da091e86c61b76437ab3deda0d0c74c480fde36de78fd370710e32dd9e752ed1625657f3cb769b649b368beed5c1cbcd5b7990160326cee961e9b9a1af9c344375cf19987687045db03e645ada5d20ed4529fc4cfcbdefa42727c9d4176d347033b5928a3ff90bce5579bb00e87d1d9e040d1adca7d188410e55b8bcbd7bff5012a0171f84962c88e2ef2a2b209a1f7b8b3d7dc1bb60c249832defbb9027aa80f5e119cef25e405249540c042313f2b7603470e84ecdeb1059e8c19a09e5cae35018df0ce514487e78af238b23279f39833c4b56af83644cdd121fe632db58c2eecdf0619164f3a89f9afe8f6a05dfb9ce0511b4dd280c0e7bff4ef90c5e2d22e9b10bf04cbb342dac0728bbfaa0849ebf06f38b786ba7c64e6c0f2e5365f972b46a87ea2273bf745530fa51f226110518e5e6cf95f66693ad58ecfd19d9aac1d9776aa4ab53e9d189d25ed4d1fa49b828ecb8fa54d53aa56ba564f7ba4853637522f4371228ceb84829121e75e05cd2f0bc71dce3dc3061d9afceda45ef8e038a72996745b6cf555e1697ff9a986194a80fd8b81729db5963f683467aff5261f752297f8baf392bc5761a85c75d58bd810e2a4f09d2b0d903e154d92ecf8b3193d0a4b4458f65d86f81af18fb399241e2b3359c2fb9d2671177633e24616f61e6bf61b2e9cc2dccf775066c4077ac92e972eb4652a7a7b60bd8701704bf418094b9c6eb3b30bbd770f1bdbc4e66f2f956b1237e0332eb1289cf85099147b3e33cd0c45d522b334f7d76ac203a8cae76d2807e8b769861ac3c9e448badca71c03510b1cab65ee97b907389e6941fc6c386f053f2145f6ebb25da1406cdf4c593b70dd9852008e88ba56e5c3f7a3f54a7df1646018b2801d34ed49093e91f1471f76a9d569381887f64b8072044953d5a877f96f41b13c25170b15a5a218ea210291f71c67ff6d7b4d9470710c6eef3908cd4d901f66b5c8af81f6a5596a75df335e29940f214c83312bbb418d0cff219e61aa2e7faca6346038f8444cfce6a435ac9046ed94038a5ff32fb5d62ea76de4a73c411dca8e49f04fcfefb7cecbce23f56c384a4b81fa3abea4bbe37dd13cec998d6df62efb23ee87d5824acceebc0115d4f7d84d4aaf691b0cd25d0f059ff7a9ea560a999ad43a8896186a9f787227a368f5febea348a67a748f566f5671b71708030a7063188bf3a008173fe0870c44c70231a47febfde1d5dcb0e3ed4ecfc0a5a1218c9b1ecc4e5aefabc4f3580392b6285a25e7f3b224db2a3509ab7be4699504f4f3711ecc6467e3a69d1f9101dec579726aec9f2e707cee2d337d7e96524e1ea2a925682cde94b0d943972c042fe1e0a404d8ce137f912c4a890d7ede7ccff30b5a001810f75ffe6b337afa2c0f3bba8fc4e94afe7529305632c2b4ae925ecfd55473fcd562a719ea19a7ab255b4375bb812fe9e228d39f20c33c3d2cbf431652a6876df80c3d6fdad1b60e2d70163584c6a50d2855691b8256aeb1e84d7dabf0ff83f7f0ed17c1a9c72dc416d35b8744ff12640c39f4b86c0b33299405c924357a4aa4db8f8a88cb49934b329d09a0ed5c23a5250ee2d0d25b86b0f07f1fc62c63578de9c308ac886e77fe033c8da42ccd70b3e627e5620552ab972edf72021e480e2c82a829f705d3ac8015aabcaa6bd2a1b68f002991853e02f7e440ef3cfa15b840b6e6aa926020d7e43356cbb434259703150e163422eb5c74d159aefb59e8f961f8f68710085ba76114a8a0de5a2b14f72e7976b106e1e0cf91b706f6a7c490dcb8001320e0db5d1012ba71c1026954844685193e6dbdbd202d4a7aa36d9c0644ea2165fccf260fa91a792816cc882b7da2270928b48f34ca15b5ae406f9f8fde325e4b306eb2772e10635fa7ca2b23576e35af57b29f2f5ecb01da489c90988c71959f6bf3d3b491238fd4c13bd6862ed1841983121c58c257f1943fbd4b523018e444538ae5699e2cf4c34bb4fad51180adbfffb2797049984f62f317acc0aefd7b49ef2394f41a019a11541f26141a1c35e1bcc05a9230fce3582b9ea160f935ded5c9dd515e7e27a68244a2e91392bcbdc0573658feb349140fd93d8715f6bcdff9bdd4186716192c80be3fcd7c338434c5fcfebb4339765c15b8c0ac0ab14ff03799d2454bed171a4a528d94e6026b3836f5deffdab2b77d9a32b24112e97abc3271e93c32bf2c1eff42acd1726233299e7855c1e2423cc35d3bcc2823d4cf52b5ec2fa172da26d00da0aee048bbaadf2ab1fb28289b0d6f02038a266c7160dc516cf7674632028f8144c44a971be620b43199a3466dd622cb71b7ae3aa06eee385cf59a17bb32a08c90057f131dc03952d4a35d86a6d6979f4b672fbca7da5dcfe082d69c3f09135f499eee82071299a6350ad74fe5cce702505175df85f680ef531f640ada162ff6f070814d78e8d28fc3316b54f170f508bbb29d8a96618a6cc211776ae30038a1e904942f2728591344da49984736b456ed59507d5c6053c3cfbecb5e11046d87a300a6bedf6ac18a8beb3daae79cc3ac4603b1d2d705b2d215f8fffdd5038b1ee7d4cacd86114cf8f6feed76bb810e542561c4c848affb71d12c630f9b29de4509b00203948b139065e1feaeeaee13c2f574530f90a277be135f75806221f631c744355189d1390330709b2bbcb61f548d8fc7f39f54413a3090d09a962e283442bbe01b6f7b7a6055620ac94ebdfdf95982722b844e3b708af50f3f9601a44f5c4757e7a5067dbc41ebd9ce6e2eebbe0f3547ffbfce538287f39adb5826117dc146bc621d617d0b4811c053e1aa2e2f8343d269cadead9ab4110531ef03f973cb43ddd89f6ced52238cb693b8850e9df3d9cdbcb97c4c84749eec92b823f29721c7c200f69db415277456626814d646ae57ef136e36a0273ac76d51fb11b37631d928a901238718eb4ff8336a0a750a5819bf6a2da76da1a06964ef4800b865d3bd026d0edd315e74337fc9fa692fff61c4c7153cfcfba2f5a16209460f90c5a353f6d581c6529bc2290799b615a9dfc1acf6889967d9b979c62ee16e3a997dceaf1fc020d4b28ec3c80ecc8f2163244895b67ab4eff8239a805252cf2e19e58e0c4fdbf595402b5625fb261e641a1a9c938faad9848ce84f41004e682748e5a9a8d4c181c9837f8b5ce33e10b74d24337dc4a67ed066979a45d52f6c3cbdc5c49b62f41eb54287907abe5fad99c5776a167d918b5747da356d373cd313d8c3fb3b30b7040ed00d815e0782b6595bb4341105d84db9e4edb0684f60f20bc9652f58cd8abb5bf1813e6d3f2c1d75a2279c1ba7951eb34126601fe497e93b9cb642bfbe13ad396863d499e762467f2e22b5c78428b775e523007747338b2b8c2ceee59ce16f03479892e129573e914da841d58d43a33a1331e9ef7d96ad374534e386b677cf3b438a642d4fc04a8bf71326cdf2e6ea25739d89aae37abd09eb8481a32821f6d6789875c2d56425c31ecf2bf179d125a003c5d9b79181182705a357170ac031985e1e182ed63be6c5e600a77fbbbf23ac8d18c9edb79cee2b4e529563a6019ef4b4805bbd84b2c27dae627728a39a3c3c91578fb4bc362223751e67c939a5c3f89ab2ea85c27b70d1cc0d96972070cbcaf8440f8b2505653de677ab2976646be75a7bccd3bbcef3a25019096f9e558328daa4f3af6d458d3b1a395079858ff3d6bd6cbe810def364a44bc2a3ff558f29dd4dd1c6087034b32e1ccaabce7cfa25951985c9e00c69de2ef5d0c8181f47b81f747db86e5ed291574dcb8ac53e22407900be677b9723104216ad0577762d6ec7fb694551d72e5357d62fcdef4900596efee13259d3a2557dea28153d4b6d65ef8ef4ce2acd92101423a38997febe2582661862c5bdde0e64db509562965c7ae8042a1887485f21a4880fd1345792e1f5462d63e6e2fa9c8d63a3203101902f0c8e17aca5cca2d0d205a3b8269cc1af7177775778db264785c5bc520494b2039062633421f09651be1a01467d3214e19c020eb3992f771a44f1d22dd5bdc708f1414ea342d0f2a3ea8356b497e7552c447d1c59acc00ec92293552998c4eac2a6051d2f4a9ebdd26722c03d8693add2429e77dcf35943d81dc0860c13035aaac4e19197f86dd400148302abf7910c45c738926ad3fcd92ff1e639b03c4dba5938c58d0c09c16051751026e87b436ca7a14f21eaffca3e9b6eccc8c8a8f91653fce0aa1e8c983fd3eeb5f2948254c6069479302431663ff869ce60b24d3bf668ab9d899d4525f79287ba041b510c70f683b40489c44d9f36d56d36d7562d0d73ab7d68727dbb169035b385676015e7cb44fc925ad773e7ac28b367029c57851d0c90520cac906f426c75afd7269124d7e2114c8511b331ef1d43ad5e758be645a2469590f5d2bc7321f739034608fe9bfec9622c5d89e9dd9ed8a0b63c23edb5a26a49b7fc681a33ea3c56b87ca36f7ac4079125e426fe01837fc0dadefe253f6db601c824d246a19b6fd3621eaa9835b58b3c00fbcdd6558203678e9ab1e113deabcbdad998f320cec5e7fb20d619f1a66fd6a73d7b51a0c2a18e5e7dd82e8adbea481485bc6b535c445995d221a43333e4d8f986d91a7bb24044c4f84216e7739077979f065c63df0bebb771053211ffaebd3c97fc978bfe1057f3ecd99019d475bc28cbc5e408946ecf651792c14bb15f27b52074bafc8b4386932f56d9ad57568a511a9daf1af3b89e571226c4419d06a3198a04fc473e67115201c6ed1c82fc049bb1f330914fee317b7dd7b48df0ecf5684c68ba3db8222d65486c4548509c00e1f1da0f8bd8db2e3e5573e1e94380b3e4f6c8644db45debfff88336be787fe7fc76ede488f1328a87e0ea6df38a622601a1bf26ccafea3e9f53b88816cedb7c03f7a1cc29e204fd3dcec39ae72728b9149d8982cef971077e05cd27636da2967a1ec1a0cb823d76dbb310fe948cf1d4bffee9be1ebb7f545c51258d6dd0a95cc54be6d24274d2500363dcd6d8ee0c30088a6eb8129fe8943e91adce089c90ab4e484a4898183dfcf0ca08b0b46e4a06fe66cb2eb4e7bceddb6b1147b0812eaf9a9997456202bf1f04d912efef7c2b58adbaaa031a66e06ed91a8d7a8c332414bd8d42957aa2519d610b09f2fc2905a189aa190b3e339804447c72ae0879c5675594045d60ea6e3e602e419bde86dd178e34f7c031f8b53a3cfe700ad4c870651f95223ccbe528fc4ce15483035ce3d7165cf5682b05736456c52daab2617ff4eba928a4d9e16610b42fc7c329e9313454f0879d4cc7c9c5d9a343a1d7652255c39f3eefdb5889658c023157f010d27b866a2d93df072c2be7fd60680178075c362082e6d2378ef6078e4342fa5b2b4f70003d640bbfbd589b93cab2d468d2316b089565bbea2717f9478b4f77d8cb0e80578a1245dd7044e7dc1ae5d248054b479be44522e4710f699046873681f6538d863a5b53ffe1c2732ca8b083148fbf2b42458040782880511f92b272b7b3eeaa96d395041ea2ca7d6d99dfd2156a250d521cccc09a7339da1caa6b099ff863d90dd9f41c66ed95502bc719006fd4a92011d364c2f4fc8ccf9cbdbe3b5efd50626854f400ea1769eeac79a54d7feecc37abe462f82a9a0fc1c10f0cc2a5083e201377370ca8f3f0b726cc48c0a94e5981eaca90137ba2b6eafa89b1a9170be15b92d3f3d483e1a1bf27667418d805a470fe9cd8b0bcf3d6c7588271f64227c70d449067d0325eb915a81e26db947e9bb27bb093813ca81e515192f34eaf86a9fc6761f8c5fa2c2f76203c04da59467ce5457f2de037886ecc2af9ae1f8b3c716fe5642d3f4bc3aaadfb942622a203e71407fc5490feee383364bafb5058f555df593ff316a62f4eb8ddee3e48d08f9e11566c2919e4edae58dc84a468dc25529669a9f4a9e869edb5c8ed0c91cc7d56ced84086f150000000000000000d48768c7d4846c02bfe34721adaad00b4b6baada2039ff05afa1b9dfa2920c3a8a61a2b2a836d3f18023ad165c251b3e97214beda5a1e885d24a55a614ccee3e000600008077777777d80a1977000000001c1d1c000000000001020384f589f53dbe67b156b476ec6c556473c7f5a581751f925e0a7977d8ea8a01fb93f0f2fff17a559dd6b52a5df79dfd35ed51207418d6ec1c8eb857b8944b17d08df0faf8810e7204fc012c3950923014a934c92d380c7f90cb16967f0a9784494a335f3fed476c58493e9c837415ff4fbb0b0fff46ca3557e8318b9c77d701736313060b28518920c70eae1982fa7a4043ce3703e4d4d5840eb8d87cc5dcbe5fc9eace2679e54b588ff5e478d9766a63c2c60e6b968ee3c8e01137da480709496656a909c2bd381bbe7688747fe7cef49556ed726f6f1241fef480027dc6771db3529ca4ad558528136721dabf2ce775db8f399e834faa0536d7d416ffccf60b4e23fd8b5d2ab36168e04214f3365c21b2157f6b1842fb1d7679845d276f06b74efe3514df0ce3c17d549de9a5145d4f46a4c65d6c6700fdf6c33af90c1e86585dd96de4c31fa40d0254c0c49a10061f966c0cfed6284f6ae200886aa201e48b26383d4f6bba596ea9bb2da4e79dc93ebf74a1616ea13a9d2292635681f121ae87ef722bb4e21a20522cff5add2d9219761302d01cf8d6f449810cbe60cfb4ff705488c618a96b799e15ce3640a8c0669228a2409ed64f4515776a02b678172a9bd04f78c199dd7437d563b3db076d3ce875679aa164ac3963645605a86d7cf1be63e049710871520521c2ca877fb48ed74d49ab17c90603d22007c156e25c09a1c32009e950b35b98fad179583c673a6f19599d9e9222ae255f594d68861cedda28d7f975d467daba081cb2ec2295770acd0fdc51fec23b6e4482be8eeeafebddb2974c789ad3b531783f11fa9f1beceb8ea11eb4365085d87b8c92895ebabe527a41009e28d6ee75fa3dac57d7bf12aaade832ef6d95f64055e1eba1fa2beac660ca9705494b0475c8c34bcc47f10acd023c5e621b2108c1ae6bdca9d8b3da04a549aa1a63e87fe84e875be4245402646e2151e789f5385ad46523d0eaf2525d02dba3b5623d7e2ba3151fffca158092a52d8cdc3aecee5c663ff1a5da3e6887de4050963333eba2bc59ce10d89b3c9070bb63f8af2db54be24554890648db11870cf731534a03b2aabf4d0476ac41f41406a8b2fbd0513c2ede4a1ab0cc930864b8b7c18253179e772392e0c7eaf473c966d587e6bacd00e3e68119d372dea195b286ef68e19b06c20cad46beeb5601988e2762c71d7cea86a6c498e1ed0d186c40e8da6c60b897332da6a0b0b2950a3f883d1a12650f6a3c89c1dd5928fee288d730fbcc470276acee3aa45db665e356266bef179823577f7f7dabd94a5aa7587b4cc03befec5e5e9bb241ab283fd0bcbc5ff178d19cde301c4aac4afa036500014fec6109fe3c2337b770ecad6761892bc72ead06584e4458668616587b280050e262cecce65491e6a9c93bbb264cef27d7f247058816c6026adf8b040ecb3ba559209fcc7c24827c460221541a7bcd97e87d1ec7b96efb31a48f429654cacb12c80c94b43a8cab2fb461ccfae31356b89d476d139cfc0398a29a6b40cda8a3cc619ea3e824a4a5854a93f03a6786325cde806b0866ece63583c45f512884f07a9596b55f84c1ce36b450d7d9ce07cf54524667191ea2da407d0a4781f2a8dc1980da32ed545bc58307c2e561f03119d1e2246466d96ca630265f49050bc11b4055e6b47162e92c9db77ec05b706d406a4c8cc1dd1cca1c5d9dc0319d1494c40088fd6417bad12883631f27aa61d0bdff6a7da203c8418d4414166e47d6b779c9bf1c8464d8221e2151af634f3e7a9c75f2d5e66ac0b080a9c3196c6437171100d109fbe04e7c116cbcbc46328aea1b4cdc0e8d6af423b9a8da48f3c238409faa01b32a6d9b16dd5fd413aa28ff3545d99882d68d4b44eb0d9c5a71b33da94b65c4d5185e847f3560e780df75270161f1dc9bdfd394b48ae34c63096a553d930d75d9c48b8dd6391b0f4ee6bf8643b4a35f40f6ac65bd33a5934f2232a1550a74bc811fb85ec55a78a8b61a36d388297c77b16100c2d72c10ad2ff1d3104b3387da460c3e8919675521ddf2ed8ce8860f2230b461cd88890f25942f2b7dccf13b500cbd867856e962c447bba5bce644f745876ea8fb6e00bf10007d7faf2e433220f1903bf24f28d72f775faf631509fbcebe068411da6831d8b652eb0f899237304d6b7b76db2000378d46f31e264e3cbcbddbd2b2aeaa2fbc5a29d05221cceff5a35dbc1dd70f5a045f1c8630d5dd228cc1dbec06f1d635a1de31df355f92d1f0e3f836f0f4008d49e9eec0062ed09b13a6454dbfd52e437871836b81b586717d7bacba215b83bca49f4a40c00052841159b8edeb45c5dc2c29a63db78e123567f94c3a09574d90a1c5de03443ffb9e5e7f789e1280102da6cdc69a1a3d58d63f7d988a0763426eab8f9a114424138529f6773f4c4293c47c5b8311b8d0182ad0d49e823900000000015fd281521444957bab12f47104f5ff295620fd17dbdb799f813f973e1eaed4160200000000000000fde01c19a4c0b6d89d27687ee204a5bc6b5ea42abdf1f05229d7331d10dc4288e513a6b0e8894cf2dc34070af46eb9b75f80f8f891596b4bd40785cb3ad03988201b99f780524ad252cd2127e789847c8557662f80893fc9f4c3e281b8cf21e6c0e7968e399edae0fe6c8d34b3eba51c44f5a40f3589ca682c5c37883ddec39f1bd8b9c33b5249f5f20fec00c0a0b214e67e812fdf95504f0bbe50a4c8f85ac9d000932e325aacce525bfb4d2eac7ab12313951a380de403b43fcf0f873e04a889953a125aa505f05fa796513aeb72e0f0580c62f6e12bbee42ce820e9389c1697c80865908efd821767bfdd763ec7ae50ffc6259efd47bf8174e0a7c73da2f955a6189cf7e87391589f9ac59e61da8982e81f371a27f6d930ebdd76cc91d8611052adbf23a4179cd3b10d6a399164487e776e4f4577004718bdab42599ec4bf97d9b1d7a3e9591465e7eea5e483ae77d4dd02a5cdbc665bad1af457edb5b27d359c15f576292f740c926c438050f1f53fe7c1a8a0fd43696dcbca9d1be00558b33431a660b4b8ea0c2d4eb2e1c0262a4736a8ec5a74aacb27b661c13985e02813f12e7635adb84ac53624a77ce147bf1b409123ed37968dbd6dd7e89059c36ebad29f48ee55712a6ba94324ac978901bacd3343a1909d8e240e8b0084f75def3e3dabeb9a9650a19973d8b16976a790d4ab6ddaf1ac44b89569b61733860c6e7911810b8f7d916dda6b11df8a5adcda6e4faf128d80108690c551b2a6abdc4118e6b441857c145bed5bfe71d74579e14534cab2c80ec02071abfc44e019ae4bf09d39e626873a838440fa4a9766a0ef4f761692ee2828d099e86461aaba9635157f2b9076a93e1beb36fdcb6c6a9951b57b87afe3965ef71d3ced48fc65615e5d100b7bbaaa93f3dbb85ca8c14bf8591187d2fcff59a5f0bc9016569998e4c9378318aae999bdedf5dc48521cd088a96bfc01cac3cea0824fe5fa172545e97f55422ba1bbe17a9a37332493df8b7cc15edfe3c468fb1dc70633e9a225d8420badbe01a0a090b8ee23799b3617cbc741247ff7bec2b31c39a92c5038c25c2472047701109252132066b208a67c397b183ba0ad5f89744b85f61901c5441808d1ee8a3f765a5e26df200e95276499de8d241a007524ef1ade07dba6b48d153d4077f33022503a21683948059281b641f06cafec840420ce97e314989a4043706e9e838c36be15c518c58560855a9548634dc03a0deabc4c509998e5c5aeeb8e0045a436a9690354613d924ae4d1c0866972f877a9918917ca8bf3f1bbe7de3bae426c92e82f4320e500754ee69e94837d93bc5a47de31d06810d2da7124d200ecb8038a4169630f9f2847bf57e0710ae9e94330c3b29d44f56044419ced22ec028a689641513a7a81acfa2a62d85bd7749c78c37b6adc71b9c2da0a0e14cdae67fdc616a1f68931c1ca1b56a49cbca6c3457ca0fedbd15ac85967b9f4abea106ae3630de2040d84455bc6878304be38bdd8ab6f146d2afc47a09997a4441be1474d44b497347c3e3491c83b2a0e1f29743e1766acd36e8512f36c252fc3cca9a936ce9787e0166097779446c90843e03dde533196070bba9e6f08214d53da20f09ea50541567415ea7b24b3027f59bf68c8bb858914af7fddfe6a3aa018a67af7c65a33c7225b04e723d0bd21ff76a6afb8d3ca8a9a7772c1f72ff78a8c94ddeb13cd371ee6d7886cf5b38946e4d081a70fb475b8569aaaefc5799b773c9e0de70d08be90d3499a99fbdc31e695de27787bba5a3e752b7b311224d3119cd19ec467a4b015a179426fd8cdfa59be798d461c2944f2e4461d80e9a98499979d5e3c916aaad820ac2dd05d16e608c79fa595476c0906c178a85e6823f843701fbc1f59bf027099fbe7dac41d0b8c889bc4d44ee81b44df4580b20c9ad1ec0088fac0ef62a0fed24f7dc4cc2fb7aba41bed1160ee4afe95e811a9662708cf0f0534d4f5b30f2e64a79ff42c2483848c650166f9568f7189b398ada635e87340282edc1ea81e85e23b8b37b20755f92ff068ed5468495bd705b7dc28b82bd70435ca3293a3b59c88d6b148a26a7c22d4499e4aa3777bd56c148745c316607cba9041c4ba8005fadf294dc9d275e203927205056fa122ad2651e557700ba4a0eb21aa8f2fe60d12c4962f86fed97385e16afd662ecf6e881749f67538b76287d56cdeef957c22b3139093947d7328882b5cdc6fcf9e01f9e850c7d9940f855d0e605fcae62d3c4756f5e5dd9f9b5881755c4d8079efba410792c5a3a13b8295161b916133f615275d317b264c434dd16d83a2c5859a1f842b43d81416edc3ae14deecbbc6beb33621b9fc3aba9d2443a4e6923f5e6e41408eda4b2af7f417b3741764fa324d02f739a1b0dd7a6b61853527b4a298f94be16cf972e2550417d124dc04005d110ef86c08287065d5f5d1561777794f78cd95de31513f3838cf10568b23d89ab8170345a8c1002e2f69061fc402e7f0f332406642df71589849408517ea68d4d03f29921d93c0137f9a76a89d9c1abcec36cd5886fbe75a931537bdbbd7afe9fc2e37f4f81d02d63ff52e19a6d6f85f86d62eb499131a919a58f40d36eb3c83fc1e4c57d9510671affecb8e7690dfc03084fcaad6f85a39e631557a484f342c9c17ba9255fc52dd5c8e9f65748b2f484191090bfcbe391849c19a38c7a0bed4910b20863d135d28c1caba429677412e06c23e228a353dfcdeb22d5bfd71a63c1724ad25f6dc9cc8a8756b8e5840cb4fd4607041618abff02ea7c7f8971daec8b10025257099ea27c4fecfb00c9774cce098f6168ce15615efe8aa6ec82c136ca93e6d7ba57e6a00a1d5b33d32c4938d0c87096f08d3263620efe5a74ec96e01a40dc5ea0e04755984e9690b121ba6cfa1af5a0f428bd415a34dab80aaf12ca4e43337b3294d33189d039f7a66c15c75484ea8c40fde16527c1d6659e493b538d1006ba89185616298dcbbeda58f418f02754cc30e416bdae781697a05f9a09edf288012cb8004c5683fc190cd4294bece2feb5928519cd00671f98b1d3c7533e4137a7f5bff4eb3a212bb257ac1478602c27984ae60bb793e0fbcda2ec18c79153cebbe93b15784eeb2d275095837cd471264bf80761b48eb3c6fe57a37590b44116ab23b1b6b7a76f6650874609ca41551bba8ee4266f9f66348a3851f0fcae725c1c8a9757cf15e36f815e9a52794ccc663af4d9a092e0ebdd37f40d3e3ad313f53dd3af43c14ea0e0399170ca3583fc0689bf33853ce1309b09d269369d65d222abac736738926d3eab0160c4f1c47f8c429c43806c74968513681d539d163319334f2d95da3f720827f0c4083f1903390efb3959c8f97902dd0440d0a4b22349c440b3b9512244d0c10bd673514cc4ec5d946ea8b7252635a5381eb9bce4d1ed7568a9fdad59fb927486762b82037c733a15e26bca1e819219486e1ccaca1015ddfcac49355cb1fa3002c1d8e89459870fb8f006dea98acd5edc6a1525f7e25d51929d3013acbb760e5e0bd7f412290cc3f84b41ed8ac0958b9b2846299913807e9cb0850316746fb50bac24f1cdd9f6a8a0839815e030190fed2429a530c01bc59724e26377aa86bf6cb2a7d7d3e07ee90c7efb4110328725259c8fd3e151e4a40858f2c74e4e1cb08a49c300c30d176d1c94f11133a133bfec6a768c9233043b8fe2de46f3fb297837d5ce6cb036b33aad3b3ff087be7bb248f8d2796ba1866f7e1f9d1a28f91c001b8733dadd13a1ff2afdd7dc7b78edd108b3f686a5e2392f67e5a81a2d73ad99ea1c3e28b3bb60f7d8e88322b3e1bc92cac958fc2fb157ea156f4d19f025d87efbdc1cee5cf97a480e3ec60a9e92ba94db1ccd3ff73376377650d161f454ff09b2acc074b72a3d6955c9e3b44731b6d86dbcf04affb346e2f49dd001c7a74dc86651a50b03a2d9a774255fe6ebd66204a0ac308c3ab2a55e2fd3973b2079eded3769b2c1b98c1a84a92815067e8f774a7640dc4b6e03b8c9f443740d5093dfc2c02d459af1f4351fe07232383e39b975e0fb3ffc5d108d74b356922eb363f43addf1e838309c7bbfbce1ae4cba1e2e68d891302a55a24db72796b8c00674e55191594435cb55d5faf5067de865a7715cf90bb5fa005170f99ab0614040538d4cb200738f4c095012f374c493e67d91a5197e51468a5229633476b6e806c3c11fc17f838473463b0658c9fd5319a71af551d34417e4916381e9432a84efde20aafe6273c67a91ab505cd6bb1439d4b3baf9fc51a84d8070b7ad885597d3571a2555ff3959a3d3144fec942e62658a28a527eae3363ae302fc9118100a2f2b29fd4869978b48a7e3abe3546536fd72f0d2b0679a914943b58b7df350b504344999838ab632b0c09d791ace77968ba435aded173a8820501ced83a4a43ed324b83d66e7ca22a0016f8475d88948e188f02d70b7b90afed02e868202125d9e1f9b15e21ccb40c06bce591de9eb29cfc64705a52f705b4eb321ef6d806c17bf0f3ff8d40c222f780f72b7e7c1e1d0fc9d83e2db94eb026b803a5e87878b92efed14cdc3ca75fe2ccd0e9e39ea9c538e23deadcabc2dc5ae526bf0112a6e4eec377ea9a98fdeacff21ae7815b543f2d29ac7dce63e5075356210b5b711d2a2b667809ec75b1f4ea967e552e3e49ee24867072ec1ddf47eab60fd949c6ec7b7e918f88bdaeb870dd95b4f621805210eece080578a02a41961b3da9515eb61df05c561b7725218e3fa2514a92fc267251d340f2e9b9507dc1ad1936bea06d3b81bbb81a572c0514c57cba5710880cc5916a9cce089d69a0250919c04fdf99f2491837d53ae457428077c863247b5479f2b3b59c4291440a75943ba702ed6bab80a430a27baa6f186424098684da5acff62423e5221b032566a534139aee34bd5bf1765c7e915a57e5830a1b1d310033a7d17d3bc0d62001c9760fe135468ffdaffd62e4b1597305f37fd9ea804ffabfc218f06046bf39018ef13f53d554499082297b80a55535c4a8192fe2749e43bf57d955d610dcb58d186617dfea892de5093083703d3b56adc057a0a86b11a1f2b620d003e7d2c0590f19044a65e5daf2506a6caba8ea8f617dc75841d2949480f52a4ddd83b3d37bb49d2e98e3e5111187b982d97c0447f4a7a113f92a4e6a8f453bb23ee481464402803e1494333d9c2437ce54235026e4b74dadebe6b2a76eb04aeeb26cb069a2b3882379456245dc4083f92ed47403778cd8c22627557a6e57de06a54bb3ff1eef6420045cf2aba353307999c99bcf8151b2532b609c41c2ff8368320b308c87c93c16ea900f5440008b4b830db86620d0ab4ad6cc8f7df879e76e9fc7290038d5b51b1b2aedffd1170085295d0a1ad65e8a55b52739fa524fbc3e011608e50578aa09123340f2a6c6ec640acaf862ad4762beeaf38c409fd4380765b346fcaae3af2ec1cd40e32c1cc36f045c18ad3cb45f6f0b47b1630c2ccbf211ac4812a0b92b2c1562605bfe0a4a010b031d9859eb2f2c08bafb04a6fb7ed3faf3c9b3f47683335b0f06d59c2b121430b0039f3eef3d71826857b5ad643f6c1ae65066d637e30c8b7d0dcbb809aae68da17d8fefd51ede72c31ae05d3d2203fa5e7f06ce63c6370d7cbaafa5bdd14bc406457a301f91e8e837de2c6a6e18e767b7d4bbe2002138e4dd836feb78d1f0aceaa735c08b07747cacb592515642132efef3b3037fe5218ac7988d5b6d150374cb57e50eb0b1efa335e05e0be9ec52ce3b12a7f7f44116f758fbdc9fcee665e3bbe6da45609cda02e11afe6faf55ab06c885b289b8c520cc6961497441361ccc72547eca4ccc8a85b6acce8e7ad93b07e155d06f282c0fad64b15245dc7a0dae557599b05f9f4beb1b09405dfee97917f502acccb9a63f3a1a4175b5cb6ef96b66be25169c2e4ff683239bd2d5300514ad6a0be8493b14e721306c233bce3076bf31e1326dbf9159bdeb6684ded56ec1682f2d3cef0c056df2d3451f8a3a2f50b7fe1f3ea219ab2d63f29d212ee7f7250d9f14d95a5338e8e7735b1eb0b3d82b53934639370e560129f7d789f6ea1948a34a362a14ef3d74a16ff682530aaf751377f9e51c585aacbe97c1b0c9b0c978b93352bff70e3376d07ff4691933a88ecfe9a567549d1ec99ccca674403f5a91b39cf5214c03189d049bf7bec642be5dd028d027e35d536e6d76f758dfb995615f4c85251d2e24bb46ef6a050b8b22c3ce03b1a95a5b7c3eb2e680c32216268a266c794884c50de4025e1626878baf2b1c6d6c8bb034fe68e6c4947c39b12c5a496eb03d03053cdd8981b7c7141a7ebe3c371a2eb2b3fc2951a3dfb787f77fe15b7413d1e1bf0135c6ac189ac272d7fccdc16df0bf5241a06b18484b6809e9d7ed3bbe09bc6b038be8b62d33848e55487f4ee7923a6cd41c52e54a61a02eaa7168d16a632f2b298ecedd947725cd022c090e5e61498d529e443f0c481f7ecb3972ca240c032f3be9410a6fbec75115cfbc801a2e5ff53dc8280d2f68760c68240eed2ace16fd1618ed004055846dba422ae23dfb8889e680154df2a6e7e4b64037e1593a06b2118290dcf47770150491c2ff2a9e38294eb568c3afe3fc359c2e85aa080ff44c3298f1dcf828a3da96a1dd84f1345e41c45c7313091e8a56d65bc710f59f3a720ac23cb3d78d05e14102029257d1a524083bf24ed5985ca5f4b0732d6cbb2c6806f475428cede69709aa6e2d1b0f2f32e7bbde2523c3c42499165a9615cc2e7c33fdaa4ee7e8e87237c531948f718574cf8126183348b21dca1e0b332cf73fb72eba76d4d4cbe1dc0fd11bf6bf43e3a8902ce5d84d0dd18786b63588cfe5f14b370bbe49f7cffe4ad9943f84c3b1509d60ff2ce8b1ec7f3f3d32ec9cc464cb1d081be2595f572cf9abec6eebd0eb4a2deeaf47d727e4745ce51b3c49630aecab02d306d503a93922d261f6af5e784fe6efe9d4b2889562a1ed8cecc3dc43658b2bc078357f83d0982ac7e4e8d6bc775626cfd81a543153592e79636974577a2027af517184ab4b0a83b28de16886118e34e527f0604aeb97bcb622a14783338818a449318b96676b96143660e8e68edf124e29b5f2191567e0cff9c8394d291e24266c172446ce92f52a5403c87607b837c101219386efaf43ed8363ce8bf3362c1fb3f3a1b05cba0bb70679c33f439173358fc4f4b0defd1fbcd460c21aa251042e12eefdaac187cbe2509e44983dba90daaa9f02d5424ed1dba6a028f06117000d592ac65b5480960bcb19d83e0049c6680af325207be3ab317320f8035a0f3ef6b0b1371d1dfabd08aaf3aae6ab75fc273ea73004fae87854743f6a249e8b17b183654283d37685fa6607597058b8b678503b4d198cac92e010ea5123a72d098a2630edec6d02a2e27030159d8939fcd4898b28307060c3755e6e6ce1df3e273b56de1ae9165e9f3e775c03e099744f14d1af4a188570a2a751e301f6764b2866583ef0577e72eda194472a9ab0d54971116af59f2507b5e9c0d592bf9b40332359b4e607369fe5bf3814587b3f4d6370494f292dd1a3578f731ea5103a652b7fe5337acf79f1fa70b4a82fc28db4520abbaa6981d883709afff95191300127a7882bec3a990b79733a81e5200c6ac57ba2867863015bb0921c07bae40f8a31814d87e213c5447ef816b1870b71006f033bfe14b5b037e1cda6b3e945358c1c97f35cf5db2a917529d0e9b502484c25451dfb40d825cf99bf3e122630a7571619f351cc988f0a4549aa3c9d7ec5de25587202f46b1f7b7d3e66432f8e078b2689c9aea041027782e9bebc1f15b9a17c0de3139fa075538d490f59b89440791051d75887da2a892a7bd36cae79345c1357340a79e0910f90b48acfbe4aa9dc2d03d9a2b7ef37f70199dfefb48770f3eac69b55f7d05f0a18891cade1b4077538b4039bb623b0b1fcc97b1f757908052d0a6df76432f84377ba7087698e9841114b403acf5415deb6673120396b716606ee23f54e9e81562751d7cfd9e832e01cb2c0bed2b1eacaeca766a97315483abad781c792d278e94dd9cecad727084a26bb24a42fca18b7672e6e893d78bc20871abf6c4b73de1e99c74a5cc840d197113812724b7d8f95c513dfa076b3d6fbf5e4bf55fb37e4d678470bfd57d198b3251012432b929fe94a4aaf01bdef22baf295c3de1f50a055b1e12ae9e1359ccd297ea6d69949c0a228089f758390e31ec340e7b964b7d58f9888a11b37f50c1e1d1216b910546bd9b8249708f32ab69689d5110a6a60c32d4f4215974f00b5163442aea9669db9bb677b2ba4043c890f0f28d089890a9174294c10acdd47b0cd1422424b4d5860a92bf6f30ee0d1f4e8eff47ab168407938a54c68c2ad370c77334a1d84577f93f2d3b1ef02b92aa9cab4c92643c545763a23826d1892de88bf2efc675ca76c0ffe5e3ff8d7cda04915dad357fc0db2369b85128dfdea57e9251d3e0a23410f1fc5259fa8e5a9d0dcde99ced0604ffbcbc9018b3db0a7a9d36326dd0afd0e74d87ad8af132bbff13343fef9134c8428c84fce9086183add1c2b194c2682ef02d578a0da8e62ff8df85eba69c016b739a1aafcff4c4f54b87a3a0bc276cee2818c68fc3ac557ee7416393df40db820210d10a0c0e8c426e77b9700f48d1c23372363a65f6ae46a5d82dd4bebd93d44fde397243dcd1adea8bab70d3a67fea6e9371ebe9685ba22f40429e4666a902b1605ebc6dc7770921741ed38ce97e450fb89b6040fa63d5c668dc662980b105df3ddc982257124a255e1431c71f47b9a8e9c323c9ba86bec06dfe7ee7755cc4fd28e0d247c27b423d43d323a1b60734fec82506101373c612ddad3aabc7cc79cc554fca87d6b3b47891f9e272111abe2c9408319506bc7cbd4ccdbee625c698ac400e9a02f2e36b525d1b0287dfef18dc0a1f9bddcc496894e205a8c7241d26b15498238460c93b9d54807308a12fc74b94ea35e8959b5cb4d80af29de50490bf49e1603ee15e66a519d801fe936f4578c16d4b404f78f27422970c177a4d87d8befc931b6b5d8f2b50b2414170a2bf47dde4b582122c521ce483299f8d0b4145b971007bc6a4b9982dc1a0e7913f92a4eb12cb2a6143eefdb27b9428d3c992bdaa220831a61dbf8972cb31a6238257892d6fe030ee27b451bf5cbf266b851d62c80df9fc24d8064ad28fd1aabe7ec33f8636b83f830b1d441a1dc0cfb655229656109e7cf56ff426d907f383d54b7e8b77dc558dddd0c16443ffda07a477c1122193bedd17f0454967a142918a4946b90f75a7071aaab03b5ec89afbdead6f3f67a0720b1fd2c99687519b64b048ab13cd13ac74f5ec4a2b622d8d0e1388cc659cb0007d01afd115808c8871665f15dedfca02e7dd177210dbff0d20c36a1d4dbaaae648b6d48b8fdb5c628d36ee737610100f1073e12d8b70aa9f9ff3a8985e5cd4f4bd79cf83513074c2b45032bde07b22731403052b8419dd1803a74781b7331ce0659cc6d3dd7e5c29f03360fc05c14301b3f53d7d988fd71a348a4a0a776a3708d0557e7377ecebd984f8e7b6e3a59573811a0b609d28b2a137947488a4cdfa0ed6f98fc7c63970c2ec7cf7581841bc2d774838bc6aef836c3a24dcc70e5e629548470ef45f5657288254eaaadc346600edcb3d078c6596730a35189833bc037dfffd06457dd29bb80bd2369f24f068dd9ae012540de432230f8846e301ce4fb085e970c8bc3e30daded87fc12b0225179b4bb1fc0e03c3c736198a7efcd32a4ef2d043a642fcc7313ed873336edf8ef41abbf5bb82f6666ab60cbd64f5daf9af8cc24b5badb419e3001cf98f2d6365011e9fd5724d06a4db46a07c7e320e5ec776eb2698dd055e220586a392a466e77d539af3e1edc875288d19e735ef086426af589e1e92075851ca64bca470102c773e4732be56cb5f5eff6a4618a3ab232547b60d7f31da8ae29b7cdaae68666a966d6f103e4e0b459f9849f53495bd563e3ab61c41267e939078996c1ff16875059425d1f7e21fbd72e1bc23ca82d44c4621cfac844358fd23ba986f81718e7cf53237398c2c15694f46cf3632595c2f1753fa56980d300761bf1010d47545b466bc7cf0915f996b6335cc4c7fe23e5bf21b59ed558b8882982fdbc4bcc0e230d7103dc7d51e1d957ad92dbdd7bd46b80ce196dd00f8dac512725bdcdeea6bdf678a3e77e0b36accb44f75b23343903d68b39639608096d7e0fd941374f1691324fa7e6a5c7ad89fff086fb5160a9ef7366dafd082c68887d8a808276f7504fef460af7b0823444a2a30a6257ff588bf8075add929c262c7fb4e9837b7503fded41fc4e3eaf4ebede1718905522c60e18033fbc5b0701c79400a97391c87f0604b26b9ddf23184b0d74283e008e22ae17b27fe4296c0401050ac0e1556e7c069f033213f196fb1c141480439ba875005fc2cd0dd0ffcbfc5e109ff36104964b2a8f85497023e534dbdd50233ca9a54e93f40a6add13b69a391bc6ff243b0c78052b5ce4a827f3def88abf005856bfdb129a39d199dcc589b2be190ff508cdc8dc3e439b00473941404a0492ea2d95f0fbb944078288f204f42e0000000000000000d2cf92dfe685660efd09f2d12328b6d4b3833137dce6988fd0dbd21181ac2f827736607cf0ff13a942b92975e1591b5d15cf81ff6095652a36c1a1d6c29d2a2f00", + // Issue: finalize + "0400000018e646c01fbe9fabaea1229bf5929eee72f85d2365e3e33d8d23dadd327e52ecd605360ac681ebec116532d82473ff1fe9df07f26dba25db1bf4fcf3a7970f99a6605142b74e9df537a667fdbe1eb38f2e306ebe5bb400b190b01a1398ee9d5122254a4d0f0f0f200202020202020202020202020202020202020202020202020202020202020202fd4005000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020400008085202f89010000000000000000000000000000000000000000000000000000000000000000ffffffff025400ffffffff0140be4025000000001976a91475dd6d7f4bef95aa1ff1a711e5bfd853b4c6aaf888ac000000000400000000000000000000000000000600008077777777d80a1977000000001c1d1c00000000000102628d75cbbd65077f6e386c9ec636b25b23871bdf31fbfe21c2aca23bf49d6f0a02e83ddfd6cacdd64c1bcbe46a9a974425a46230b5541a4e9e0a31b4e0568c3426a990d52ebe770479935b39a1e9f423e75065b7f90f28fb6839f54c71d2b10f24c25a78000b6b255e7bff5d96d0fd99af36040de2ef941a135b35613297151777beae4706defb39cfc0a85ae8c2527fa0eb74dbe18598edc121d30e657d60379bd5ba3324ee10bbbd99fc44bf1515017b080895255819f51381a46623503e46d95a700ba7f5d1ccbaf58b894e33e73e658fffbcd026ef1325863ae58916caf2b0278893c10e2517fca0c137b9197b709f66cfb40c4e688303d352a1aa269f45688d19023f08aee0e0000945332b411b37b4bdd5a3b84cf7ec9a8efc760bacaa50748a6c7f06fed192d384b4784c96c2002e5822da378fc8594996e61e12e6135f42cad1568d50ecc4105cf88c02e7ce22284d02ca32cd9f94d3eba6a5e462a2ece6e1fa9384618e52cfe2982bd8aaf7d9d1cd5516d810d264cfb453844c57709d3f1bbdfc1d439c93a13d49ea3ac22efdd90fa2ae9eb0073f74b54488788be98ee8f4f9a53358e4884c3d1b831d70244b5f937ff0a585771f5b71362d871904e4aa7c58bf762a875386db9c9e022d6a5d52be192b02fd5de105ca6f3ae78be51aefddb1d09d5864efe2e6c57fcf9406962aa0ac605d94854fb40bb529533408ebed13dace74702a9d3b674b3d1f77704035d156742e602cf6faaba258f2d74cd6c2ab38c747ca5b6cd53375568aefcf50097788c49b1b93a551e13df4b39d07b38d04cd4949eb4b3404390e665fbf4c7a291aa608821dcff237710ee35334a8f2c91546177c667c1a3a261a8bdfd73a25bbd4df5d9ec133dc56ca7b0212a8e106239404cece3f196d82d58f97c749f147941ca9ac36e5a203b28828b00ab1b664dac93fcf6f1fdd8f9bbb6a81b5a270bf16914663bf2329090cca86222769117c6ecb89256762d529fcb00fb8677fba1eb6781c1cc4ce0c375a7991e29d79e8aa0fd9b6dfc389ffaae2576ef70dfb61084c06f7cca65ff71d130e2aa232a9e087e1199a781466f3087ec609d7ae57658d1a6bdf3f948bd44ec0d1b8a569bd0b0273d518d47e2df295cc3a740075b2aacea9cb6673e6a6b9a53b5e9e4f9fadbfcf1f95be41f5a46e31c4660a392a548c74d57421d10cc0c73518ace975eb39ff4345efab215da258d4ffe5b5f984f4745c4356bd1439e71a34d713a06954cef06738717e9d7d6ddfbce8289278b385e9f562501dd52323f90ec24f4d7ab3aa5488085ce2344fd3860ad6d01a6aaecb7ca902e6055034f63d40411c6f0c95d5777650d0320b8a27f0c8100f37e52ee66d437a622f1b12bd510f41546bd882c2a87857bf4b47804d0390cbd1215a8a1bb5680a8a30228ba6642cd67545879233ecb410270123fb8dc337f5a78b3adcb2145e38d112031d8a146b6ca360d6816768783b66cb654258d91d05e33330495433d398b1ee2acd96bb1f1671f9770912c292cd652805a372b5ebf3216020cd010b3c9f7b48ec601caa667b0ce6bc6e98e98db26d2fee3ee44849ccaf78be2f197f5e48ad2b74a21b7e39f9a26c442d921fb980e28fc9b6ffec0eb4656e89eaff62f05258f3834de459eb2066dbfd876d7652b6b0671bbe5a38040a6a1f58fe7806990cb83bc7f891f7d3d3c311af305ab94ca460ec0928bc7433d4a8db0d4d3fa0ee6cb181e6877d62f9a1a9eb5c786764515a9c2a8fca74579c12f44eab040755c72548393577f8ad01357dd54a29681156f3f3a5102c86a217ed375d685aa46775f33bafd7e4e8986f0a6c21bc8601fe924ba2668dc67bc6dcd681639aa574229713cdebd19399b6d163475355ab952c276c79723b529958b2e49544b171b2aa9cac58d1d98cf8072da9bba9caef899488eacc1dd5febad0f1714a0ddc43c75bc770268e15fd22a59c16b7f5a016f6a1fdf2100b6b6455173d121dde17b8ff78020df4a0eb45bb3212862f56ad9b109fdb7e024340236f7bf8bfa6daa22e1993beccca4cc7883de89a890c19ec2d32ea4e3cb086dcd445f62847d0d44663bef82fe09f8c697d047af9ae3b53644c53ffdf3215038b3ed0ab0270c33db0a797b94a5b2df87c4dad0c937f1eff4b66ee74344ff14e8a7e86ac60c6ed6d0cab5db526f8ef46caf86bad6ef0cc0d39d2b93da5d87cda084bc8d73c01cb0670fb179ede41b4b390f5a8ac29bb87aae9adbc43a68eeaaaa704b9242fce419a517eb3ab44b4703e978da93d8f1f6c0d66c8b53227f78b1446955739dc57fbcccda7c98bcb05dca08da25d931d22f036d87364e55c624e764e40d167ffacbb2c96b45ba7d3f221ec40d51860144e4acb53022c685e6365e2f268ef1307373e743d5f92040362a69fbf8a454f79e0e04d12afb814cd8e67d80b723915220000000000fde01c9d33ced835b15452fa086ad53391b28ccdf0210206e35239f3fee00ad88abaa582fa2d6cb945bd309ca0b5793dc4ce8014377b8fff43701b627cb1b471c1969a227a8b7785de0ded213899663962b4fda7351bced80a0d4ed2734ecc6e9b32aade990b7c2553b7b142ed387ae271902f1b7b864c6d00ccb105bbe9fc00377987593cda4a31bb93864a9b6e3173f865bd366b199d58aea08a8c22dfb0bd579f387df543c3cdaef1c9c30c79708e13f187c735ef6ccc20f0ed0d2dba1cf471cc8cc6410ffbf0acd2a351cd66deb1f15ef49f94db58e74f416546d214959d48ca387387de7f03705b18b9658e772b51c7a085e557d7cff376b051b1576f6d6df034a88157b73ff90edbfabe5185f994777e14e8812d70f4441a3ff8a5247382d6a9a09e6c709471169a89123e76e52e9b81aecd09eec6711b7b5206eebb5d6ee6940fc69b6d04fe1a281d2177dc3f587848828dac47a1a84c2a8df8452a945226b09bc0be5f0dd637565935c373149fcf100090258b1590beaa4c7f0743acc914185cac7fefae52f86be940c309e707a6720ab5a5994e35981487d98a65ff69e4b1521118a041ad029aa8046662a9c61a634977b5c2b8b38a7c5019c2302ab46c0c026b817ebe635f1df2ff168cbc3e964971f2f96e4ac8c3fe47b32a69733c8a95609fd059c68f35f7b9ba39ce38750ee23f6e94478f9f72517eb0e68f44ef2a18a2a56f876b4408486265da0927968551ef858df8dc5d66f43c6ed1611948220cdea7743ac2fad4b7812c85dc4ac201e3a7651906e2b67b669640f21a985f5329b45f5835c08f53cb34d28be4d5cbf3eb465f118733261d49237ca6abf24ed6102a8a56bd367144b64c58aec21c8d7a9274665c985a042854d6eeb7edf74206980d57f00664f963d096dd097d8c40cf647a1ad0fbe444df678716dd2df84957084de89bc07c44de3407838de5eef90f7bfdeab0d847436381d4ea87d06a2e6f0f02a1c8c34efb2bad3f1406f068d2a9a9ac6218fb29f130e0712ac653dd7042227ef36086e9e19e69813669c3818a9eeb44f29b6c8f7703ade77dee1d12e29a86283c6dca16436e90424e5065c5d2e1b0dfb91c6c3ab1658ee77bd7e037442c3e52c1bf2493cc243b226b017540ce22e50bd2a03ea5b80c6a439a06683c62218c132ee079872525c3e66071338029e3f77b04c471bb5bbcede40b15a733cd911af5b9b5bee02471a3c43734fed29fdeb18defaf0d3cd00cd1b8c6007dfa5482175a1ee329ad9a7ef173e8d83389687baed2ecf5aa4f48fae71254c971c9aea485aa0e0ad43d74e9c1cfccd171983adea2f080e1fc21f8650372d6b62722f8a8208c7694958c470a2a150d15c910bfbad94cc6c9e373a4c0d74b55a81f5e34302221024f212480fe7b1d6e354e42f4855f0eac896cd10f68898edf3dc2cdf1e03171d1fbdacbc9f1df99bdef1cb4e9db03250f9ffad6336991dfff8bb87ea0fe3b2abc368abd049f3d60d9d5459f2771375cce4c721bc8628443f34fbb6812e402e12950b4c9a107474ed6e18d16a00632932bd657d0c27b1fda1761b8c6af73b1f779196bbdeb0ce8f017c6f523c0e0c9a5a5db6284a82e1063b9675ee739ac1e66cf9516216c506aca35397fb5f7321adc10f5ec62c57b23ce65d5c62f91053cb136a73c2e22efe83940e058e76ba247619ac97e7ad09353960aff163f4ae1b0eebed1706e798b44582fafb4f399ff6107d9d3f9ab644d7641e218c727e1fb316dfe124b690c1b52a196d399ce8d998f25875a444181b718b08e5531bcd2b7a43c936de078cfdfb5d6851e9d13a44df864b0c85cb8a3f1162bd93df9d196c100d65294461647bd3690fb01f4e5ebe23f28f4c257b36222989403e776653183092b3e89eb2f065d85cac53c65d275fb84b3f98f1e40f567e80d7410c362c7ecbcbde4a58acb59beb6cf9156375b96e8f48f88e158184f75e5f485e1594bad2a2bb666c782f6dea4f851d1b3186c39d33ad156386a20d31a98018d7ee02a067d3395f56f37043e88daedf800a3a98a144707ba0416a42ac0b826a29d9661d5661652581af406783f6cabcee763d008a52ba885e5c67ba32697674f57dcf442c4940e18803cf5a521b47a66bfabf1e999fd629ab522e10917858143974d9a969a289d9365e4dda9241245617a417fbc3a9eca174145766a089740a2af58bee480846014ad0131e11be1a733b21ec896a3f54198f9abb4a5a75c00804527afeffe0cd2e722bb3d832f2484caea22a3ab6b0bf407fa5ef19a5257f1a5ce59ada0f30a697ab4c9e7611af3aa32640169df8a7de55759867d9c531dfdddf953a4a69f355f6d0802825ab7c877119183a1427324221efb21a0972a47cd0fe109a7e374907a804cb067335e5a6062e17f6e49ca89e3b97e51e161b1b008aeff386950f73b26d2c33a43589bcadd60a34b28596f55cb9ea075dc86b8280ec04f0a648167218a4f2474a7de0b09734b995d6514d6fee7aa97c57189e5405ada7340e91cd00c18cc40b4f2eb7442dbf62ec953e408367846385f49d3d70a197555128fde6c2bbc7ea69989167ab0577e54fe35c21116a2b1415afa9c5393307d4aa35076b231fbad904979b02643531a5c6c7b31166a282e766bd2c40e06bc3dea81cd0d6b1fbeb4d0cca3c683ae91ac194a6b20f86c226c2592720bdf8824cbc56b5af9c510eb5b63f96d7a3c53725f9da2922a02f96ef4ed1c64b6803a28220aeb34a58801b64dcd62f8ddd4877ffc69a5d7bce2f5fa4b285286cada881e9ee5d93b12cc26a54282cd295130f40883d3538d8f68e2c0df89fd38f6cb5a19635c75603832280c1913fdcfd1c9d6e341aa8b4c370c6ac73622cf73226200095c17859fff1c15489e9e517a7b431bbc7b55d1df74bd7f29cc79999162a1f3423f6db1f4a3f42200600abfff3ce451499b505f582becd1198a9bd16db506b0fd177aa5a840af1ff581335dc2fc61a3f9e4fef03eca500dbe8ae72e099d47d855c52c4c350a410f9bf185f8a5f23fd03afa8d8bb7ad2b6db8d0a42d1a7b6e2656d69723e5f9d737b1acf513a6159e668bae56bb1c8e809e25279c96f9583a3fdb7a19c440ecd019ce2bc868d8c9ec0ee5f1ef1850c4c9ef65effd348ad274ddbd6c8cd7e2e1e60f1cc46a5ec73afc177eb50d7b53fc0e45cc8eb59d3b6e5e481154323ae29090228c5ecfffadcc5c9836e0ce21b964d239c6ddee09ddda612ed389acefc1b17a1bf7c1a089b8c02065fafb52f83b0cba5e4a10d17378cb2c909082ab8ec20c433563856c2ce0009bbbb33b7ce3c75c82ce9fb5b7b6a2bb7b1ca4a37da4669e780c2050651506e07d6d8e4b60bdba0ec85faf408091ae587adc212d12199e008400a2c336d9cde2d50509984475a0ce3c79113b7a5a19a6bc9208ea70d5dd138c236cb6cac58390caba406ef27e5b6508f354eba435e2b2ab9d955448b6213d5700d0c04bb6f49a371a072d3f6a92ecf5a4aac3a22a656767171fc30f6feece961d13a55207169c28eac8d482331483aac6af319fabd2e64d2e00de7c423e77a20f71e9efdae5c0d2dfb75e7cf34db3b8a4ada40b2764db432d81844702149d7a1f5942c71729b5c231ac0f2939ee09980bfe4fe8b651e309e26d6d89f21ee19b0e749a734c2f51075010a378bb36778176ea1b47908650c5f303c22e51f84e132ae8127ca65fddb155974412b3bec41a87338423cc95c396f6b79090147513e03a4b831e885c4f095df6fc1509c1c9df24e88aa12a362e26f98eaa3a1c3d03990722aee6757b694167f28f447b5f19b74b7ce29ecc1adb1d4e92d8bf6fad0ac42f2d7470135d15dd19ba20f724a99f50fc36ba3e4f4662bf3dd49b838d73cad716241f26ceb686bc5ce30850148334c59ed31a42d9d192bc5d2d6e0e13982cbb1462950de625cbb78ec7a29fcc39e07940ad0656e6bf42074f70183e083944d319d57ce9ba51ff667c7693f683b9b55362e653b5842fc45ea1c164087b27437c306a07a65cd0a30ebe6aeaccdc5fce4ef90dbc15d5fe7f485184cd6afa307f7b349dff4719e647caa2cf427154a09f88fb3a5e6d5477bb95b56cc4b482e1803723a5ced54fbcd97fce3247e2d20f8e33d7fc5dde7df41d12172e047bbfeb52ce16c28678b92404c4525c382591de840d067cd20ed751de801eaba8845833aaa62c1b93145e09fe5043da1f67b45850d5ea581ad7381f2d9197d4ca6808e7e9590082630f105d0fd478a3caf7162c747dbab72fb564dc0611d81f8b0f29b214e9172edf0a308da2684b1b486b651c0cecfb766b8e03a463638aeb523f3cfcc3fe24fd264384e41b0ef6a2f31c4529350a6add828dbd7d4dccca51e4b57b436a7d32a333c62261f9a101ccd9266851c9c149bc14708cf913f8f19c5bf449e9a81509be0e781a64c5539967c6d88ce157d75d497d3edaef9ed30afc87bb5e2641cc38044422634a754a08f1c26b150c98e4a114d6c0d3a66ccb8cb48d94896c44810c7373fbb5a72ffc4886173f1c56e12572fd9da5d0ce6491aa7dc9e08b5dfe3b1ed85a2be19b0979facda123c29654c6665e756337a5997825e2ffbc5001b9640d1053b5d9f254a6332ef37edaf7270edf3c6ad41b2b33693a00de171b364d8004277f435a14f7ea77d367290a2e720fdf7614a6ee9f2b6a19c923ce2b98a02d2983f46ccb65a75103f885c3f7e7a0e176aa91756546703ab0e313412b2464bb1d9636dbac20014b517909e42a747d8f12e385aafaa617f86a48c15a577e568925a3a2dbc8c2f73aa3804c65b0a5f16952b29af0d13a7a5976b13a22c1e13f8a17df3aec990aa095e57c8353dc8b2bb6df6fd01903a57dd58cba72cce472620a3a3a8c024fb8f011cd03053f251f6eb37de3cf42325f65ee656a3e19d9413ba52653c22bdc151f2e5abcad2073e0a72d7bfba20e3350b807b1c5844634379b8914e4b964a409b3adafa6055a061b531f1f76e17cd26dd85312362d5a6d18b78b0c59bfca912e46b8cc8bdb3947597a0d389134b0d8bbe73f4056abf0bf8356a91060ede6ce78ddb3fc1bbe8b77298edf7270e9fbe49c0f006ef96e35b4ae351006465e7656b1bf3c5f3ffb87e7b3e2212895ffa8b783784178d06ef43b31875a0bff7337285abc311b5652b8e7af4d5ce7229e0d315e70f6d1a67d791d61e61d1bb5f4ca9e1d65da1b31a1d1a464e74e1e0f97db1d02668f51b1e9305b82e8523e7d136ef1329c30026ab8be15ff98a2c3c6aaa1108633ae3369eb6aec93d3f12fd5d44fd4109436fe88930f940a82748f2e13408464bbc0ae53782fc80c3a0f2252b3599cd4329595707335bc1fae0f9f2d6185fa5ea32266477bd87c8d66b806eb6ee05d764cf2c0d72698dc8d385016bf86fcc63e12d4cee22f4f809b6b8d301044c9dcfe8c7493b7720ebc5f1fde308b3dcf12371c7139fdaf677ed843f62f7d4cdf512533dc6ae5a21f1a321cc0fdfa1d9c9052c57059dddac4f37e6cf531725bba7aa8a0218979d6a3cb681f41ea5ab5aa54509ec207ab559a96d30a8b0454d11c34987bc3370029eb879e489c9b052e31eab2b25c7b7d1f10e7d2e621214031b7b949a1a9e60679dd5a7b0d4d98bd69778522dd57b7958a1af59b71212d0ce00763f10096afde3fe9c39209cd2a10cc33e142c1165c7da0663271523d154ce52e36e143ce7d51845c63fca3e41d006a22677f3dc3d26d7e7945ec3d6d1f8dc942a07a0056edd7655c7b46598c6c5f78368ae645f04ae74b33ff8c93b13560da37baf52b2ab2fd0bba14db5134645bc9a9d25f3fa527b296b5ad7ff64f03731a661bbb62817a6017c166a88e9b85fb3e6a3a9f3aff9cd7dca945b3f6602f0ac575fe2fc390b411bcfda53eb6679d4e3ba96bcde3a8f7609c8b21867af5234040f3e3a768514662db65c386e4f191dce3cbed8f7dab97ba93216a78e1e71ba68e3b99a503caaa872252f8d138bd067a5bc8c662810f21947e165d140b793f825d1601842c7ad6429165a281039fbf92939065115fd2947ed0507d5a304912a5582a2baac9f7a0863cc0b2e24c2de916fbfc8bbf4198e79536902c772e4b1f8f517fa913575b85fa2e51ec59f9b505d3d3dddd9763ea76476126ba14a47d1365256c9bde29fb77afbf663fd5ba407db715cd7532903002b84bfbd3dfd72632c3694f298177da690dc16ada4f5f7d087f4f5c9660f483ea6bc0d18e00cc56112fe56cd5362693ea52d048b41912312444058e9341962e1845f5a3dc3d913b22ba9824b17e465e2b1a8f331979dd1b4f645ac87b48e048e2ea0b6ebdf22c762bfc8747522fb0bff296344d87b44db8370725fd4e360387c248143e4f4a23fc25dbbb0bbb4df1ecff10b90e04ecc993dc5d23882feb008387acd7b79217a8500c9b9395b1e7126db1df3b41cb4c0b772bb7b2df6aa8ffc1785efbca925caa67171c238992760f81cf06b943f5aff5752e8ad5c6f430dc003322160e6ac808a41e666f118b20cc46288157ded4dd3217c772f1462a3aaab27b96808f5066cb34189537cb21d1df3a6f7865dbacd8e262da965683d4b58b2cc011d1c4ae9becc3146cfed8747fb175585081607aa0fda7e894bea589bbf44e9b5e54d7e31aa8c9061ffcb9d0051dc9dfd450aacdc2c3099f6cfbcd045b2d5ad1a8736c7cee6d2d1f1140c814653646ce20124026ad5b621d6adce633b21ef696f8393070d245d81c4f65ed1e5247141c6f95294e47c33118714d0ef18022cfedbcde5b038023ec0f51879947bd60fc09b14395e79ed8493973e46b5dbbd6e628c435bec7a3803b33976addd0c62a9594fd93b1a666794c1cbb74dfbdec622d0f8e42cad0d89d1f1f7363f1ba7c3fd1213298ca31464e0e8bb64f27dc4f1b352183d4759d3c99f224e7329a7b555f5208b0c31314077b3ec467a1a14fa7820e69c631f864f18d9734fa4e7c8949ab6489e6488172cedfe04536b33b2dd7b29c165d0030a12bbd470d4faba21a026a52ecc1ce10bc2db53b3ad8752d13dd3d0fea6742b25addb8250d4ca04b128285604e76c77c7281833bf30fd9bfc68fd6ae1805170ee9a2ccac0dc261cfdd0a59755a3f46147806f3ab005c44209bcd15869b764aeacd7673622d0764a8f47adf8dadc63a5a9ec3e25ff84fa74b8f05d84d4a7fecaa5215e8640952c3d0ca9ee88ece1f682a7ded5097412f9e38cd6162aca62da3047337e1a409d7079b1540c7225ad097a3c8fdf708da7364ae079186f384709d5c415395f8374f7418d176bd8fecaf2516f683fb0d915069bd14061b1f364301f4e3b5a5e3208d4b55de8640c1ac3b0108f6903eb14a76e698e1094646bee5d91dfc8d850036258402214c9be7a692eac0b7e9a8bee20d423ff0bf00a7443c1c8115fe5a3901fb7f307f41fd9d1f7600048937fe7dac93484dbc2cae522df70650f98f79c3246801e5491a349462900c95204f291f4bbba1ac71360f9f3e2b53b95702a0200408557ca6c47c02a576440447a2362544fbfbd41ef3761b834f5e3c88f0971d2e9c8182d9057b3b6d5ced2b3d641878ae8329b08811269a44500266fc45f4a4153cef3a409fa879b8d749311813654b93319a07d52134d1017dedb010069177251d319ddedc39b72081061a95c1acab0dd3fdd205a354c200ce467d2e0dad57266c8b474cc8cfe1a8d2ea3561c55be3a6403ec6cd0d03014b7b82506aa064e035443d9c52f9e5bfcc0fbde73c67e16dae99a2a8cc851b8004dd6e963289621d08e07363fed595bcfb07c4e73ff9d16a3969096c99c671f22f276d6c6788f6111e5c25aa92481c85c8d0a083de5129e1738d68647b64b6b8508cf6a88096b0a810f738c40352cde8e42826b323acd8499372111bf4444af1ad8ea10d93560516232e96f2d8128125fb6a56ab31f5743ffe1dc33572694b5f2c75857209bc671b0a13674e5eb5babaa272a1462d4592a4d9083b02807c14390d12c7bc9f6542ee1ca053dd69f3d1694a14c2d387d8819f5c8c6b84a61c8bb25c6f3dcdccce137334dd73c498888ab52058e6756a927953ed8d115068672609d603acd65c1098b916530a2a05612d275d417115a3fdc8ebd58e742509dd5576ce4086eb50ad75cf1f39b1753b11be245d62cce3d623fb09dd18a335c51c24f48059735315e146aa2976a212c622594a5000ebf49540c4707996fbd2126996adeaeeff706e7edd0a1cbc2cb477c1b4854f8128123ea22d7b1a6ac7c992f0231ad040acebec0a836130aa1bb042d72f9f8981d13bb5732519c4f641a9924ca94a86d33c0703cc05e31245043952d228ba38b1947c51745747e176bd92ed9ed543eb979a0c3b4048df2b442eb0927345749edbef3ea6540dd27e964c40de5a915aef237487b45cd9992bc7776e4b87ba84d3f3d4045cf2bbd5836e5e77a37a4530e54076fde125ad2e22736f3357c44a7428bee6d9954d46c821a7c9daee50be5e9099d02ac1bf6c242af96f8165f21657247bcfc5c1a7ac53216515e70f35141533afe2bd4c9a29ca274b97bcc71d2701827d279eaf92ddbf2f3b9f6a2ce5f2467aa6bdd424cb6d2633366e9341f95c7f36f155bdd275e765e30c5f47a44508300ca2c4cb1dc19c3c3fb1a6d9e54e9ba4d3903222f41ef1e9d23c49d4bcff56bff68bd473a65f2dfb0c08bb44be42210098767169eb91580892e044d81e3b8de0c9720213d8f6a44427cdd11d0be1ef59213eb9f0a7f60fe2b3fcfc07a4bcc253efa4225dd8d7df1d28e42913f74430422e5d987d850b3e8eef322cdb0a0f844cf1340bd9dcb06836302d1dae0b1379b4a8ae8a363460544892fe846165822aaf8bbad5d2f3e142792fa3ab569cf9c3999f4eff3b3a351c7d5146a1931ecc5281ad3ede14812bf1db047c46913890bcd0a047fe6292ed70f8a27f578c8c9fdbbb9835fc23135dc798080f3f7183dbdcae52c8ebdffae5089444b5c59c27fb454d5accd94e5bc371e12ad1c8bc114345fe87bd6804e83f6662349e325ae07aa8f3f3157268622ea60b10b80fbb52ab3f29a51768f7937cbdadbaf3f9d95537310e831a561f9d005887999f1ade67c69eb7f70e041924627b288b39f355f06f30b74b569a5156adf03435bf6df0035ce927753d58774780655114d36b8e9ae8b5ac6feaa600656927770c2fdd45b8f005298379124faf12a07bc1dee666e5d868d243289f79cd14201e2fbfe865de6e2ba4da15210b9524eec6c0f6caca6650a8f3f2ae6fe60c12a92c2c6b94e98a45f4f3c82dbdbe324a72b2516af0215e01418ae9389f8f668315a53498438b590ded4941999d9f537a32b83458eb63ad0146f5f28ac4c571fd3dc58c68b3391a16328ec89712e5ed42565507b638810f1edfc93f66f6cd4413147b210b1127e8b487282a92474a22316dc51e38c21d31578d489084e3e01481b1881d7ab856ca8622233da9eab7aa436bc8982b9220b3e22cdcf99be86e5cb8cd8a9bc60ddca60ca26ced3e01e069026b5125f32f82901f05caf68214faf25d3fbaa25df0d4077620d684a73552319a65694a08c0bc30b1e312dea1e56c7040150f2de80771009b06c692bcd8f36371966cb022ec8298d55498b1cc2053a6e08b3a9163ffb5a18d5f06671caeed00fe62aeba50c97c3924e49c2070b6493778c71790dc7af7c9e624284e67ee8b4ed2c7aeba16cbef34de819268a0e27191c1cc4501e43a03c1558cfb6e3ec26cc27ef014316abf8a387f07678b9d9f1995968ec4a5942f83d4ae6fc6d936ea5b8e7a28e37b10a2fd1fb4db9fc5ff3dc41fe10cc3b8dcfa8f73f1d21fbde24a8af1f3d69d869d99fe37bf84de52ec96f3be16163f8c858fa7b1abccaf310d3ca4791c078932cca7bdd4f4e936475a5b094a68fc700e818600ad9511fff642f9e417b63cbddfb64fa1860cc4c364807f491e437ed315b54b9ca9bb14b24d3eda10500ec6d3903d16c262aa6a2e5ef6682d082c9bfa373ba238079ee37add4f62fc668601245548f406e4bb4b9d2d80c808e365058115d48719b7f9a892c129a4579471ac59a24e76989d10a9efd57fc882c0e756ec98b0191ed3818f8409f57339e04fd2442f4627037d5e9a5877544d127d9a2008b6786f9725abc1a3aea9f7471d4c77b4d6e9ec292a3c938292c4e26ff1b90db982dfaef371f92449e9ddf9916649484a3b9810ca886f4e42c5b42b93a9dfc6de1122a7d3997bd53fbe5176c96e058f010b94f45c2c87979a4a3b39b8f3a9e7f9909512b58b6f09dca77f0b8d553a1c7d9e0946c832a97c42ab94cd0b1ce2cf47a80192eb8209ac00f4bd747c95cbb6dc586944b8d1860dde447171fd2710e8fd0e42a925b9c64f0f084cc7efdfe10b2b22c55ab8df9be2ab48aa4039cadfee771f357ceb85c424c286b0fcb140da0a34a034d21070f20119d3f176bc1fdbc94f095a19190b5263c39f7f89fec3ae7d616131387f3516b626477354ecc65270190281de45688ebd8c1292fad6b6e10d8c7b9d82b95f85dcd0487c5f6a05c60ab927f31eab2891bb85ecb84e491319bfe81217627b05bc676e5cb7f794d6fbd3852638651736bcb6f921485e93db444089294bc1299079b11176a657709c56a7f33300000000000000000a4d046bc727a9f1308aa5c32f807fc44f40317b4f578cf54040412d8f7e3e360442896562ca45f089bba0fab83fe26f4ff2cbeaf36981a2f99295b63ad2962501ce2c6d87d0184c1799a254ed03047cf57a3bfd49b8a4438d496d741cf5aca6bd0001ecd37228b00c8973da400337300587f48997465959efe09ac5e012e14f51eb24fc1d3982d5bc0996b19df2d9d7ae1ac5916ab47ef6efa16e12c395d5fe2c432221655bce44ed91e150bd5e250652dd7dca25e34ce2edfb3e7782c20956cfa305", + // Try to issue: 2000 + "04000000a43f07c488c6d586168c51ee2c40d79e94cbd20d13896698b2b8300800a41f1ad82adef74a272fb2c11bab1e2737ac4c6f2633e0b4e290a239be722d334c79108393a1bcdbd820f204400b39c01f1b06d4224b242c3889af5a5aba028fbe4c6a22254a4d0f0f0f200202020202020202020202020202020202020202020202020202020202020202fd4005000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020400008085202f89010000000000000000000000000000000000000000000000000000000000000000ffffffff025500ffffffff0140be4025000000001976a91475dd6d7f4bef95aa1ff1a711e5bfd853b4c6aaf888ac000000000500000000000000000000000000000600008077777777d80a1977000000001c1d1c00000000000102e6998d77a5a534cedf7f283b172f8ef6bca328bd8d72de3c50d4a7fd5e99bf22f7bda165dfdc5436d9c5cc2850d9323afaa5fc318a7baacf633ab9b411482630dfdddf42d14417a132ba990440d794a7cf6be121fcbbf434cb8360e54a327180cca6bd85f50f5c1220018e377ddd802611e57a5deab4cacac3aa2aca59a76d2112dea5e9abf5c800d1dc99cc552e651b32289d89b9dcffacfc8a052f5418461d06799701af4e170286d7cf6af482c1b20bf62e5146fc3d2e1df8686ecdb094a4f8f7144da67a3024887c56164da382cfbcbf20b802fb52b70fa4f3ab4dab2ef73fa9d8e8eaf29a4bb8eef157ec5b9c6b10cc44878346293561553d1096ba431e62a0de941733018828ccf9979cee0198e4b8e5a081da9ca9d11bdf5c81180a33ea4a59afbc9f0fe396a35e25a1988d2e6c99a0e4fa8719d610ddb4fcf2a243053bb8314c39ba3cdb931283eeac896cbd063d4f1d0a5bcfb3ffba36ef2d6eea371550682953563432ff8b51224d3dc87899da8df32b01d17d092e63522e30381138463670dc34ce4548d684642f596b43d517b0de2e1589125a11b98ee8c1199650ed12b47443ef1bbbc98e8e396e21c1296e741b41d45aa5dda9d2121056fbd34fc00eba1caaa768615945fc437527cb18076a482591ac9690a6c181c8f2fac96da91cf3d5b255f8a56a75df242f67173613933bd0f9bcf0e822ee60a99882f930e67fdad2febcee724825180b4a94062ba0e273f2d5112fffa2d4ff169db2b053a224205f13fd8304050e33c2e51115ec44108740ddfcf45a010eae99377f1e505f0d7571e4fcda2c31f0e6f95b4e159a79c7160bc2c0b963fdfe9c48daf8b4048031d01fb3b9059ebbea408caa66e8119c3784ab464d002b0b7dc99871c3a5b7c10fe9a23487e789a6081367f8b043168269b0526ef5f8eb191d2b1d619d0e3a276f190829cfe47918b78fa03818069bdafce42c53371858eeeca725aeb2a7af0a4609e25ac14a3a0dcc8f28fe67c1fbaf331f674369c480f0dda94a346e23c4b9754c7febb0164dad9acd66090a7d870ce42b2f8707650e3a1c9b12abf73e4fd2c88413983bfe0aa0a83234f79cf3d7e46b8f071d4db16a42ba6c74b233be5b4fe432c76dc7bdf95aaea49693f532bc3ad0c059ab280b866306db9f743dd04abe4efbe3f62374324f2649a406e80158e3e9ac7cd1df82434daa4899efdf3febd9dbafb019e6ac3265e93631e3bd7558e8c9b41113e2b11f617c179acd9d577a02a0cda56236cb70e3984fd17dff31686c5a04deb697c85d834bd1c057ad8f25ebd9065af0b2caaf4fb77b04573cedad35410a21b7cb22559a61846a49ebf680a29df884319880949ec70edbad4f1d2447d0341f63de68f2ba0b115e1c6194ff27bac2dddd079a777768966d5f4640c50d04ad6d0082808f7988a45803645603d371380cc451b71299f6519d1e056303b916bf715bf1eff1c21302fd3a9bee087c3ed904d32db2cafeb4e2ed4d860c83617b52042336aa11f1df6c19ccafa022c2937fa47bf1918db0fa6a08d17d76756bb844fb90944a0e2cc609f7c4700ec1a829f800dbd5fbdd2eff3710e30f12a3e0d6499ea34215000fc386231883c206bed4bb2d694879f5ffed8987a331153978f0cde719b3bbd5258a496f200bc4dfe042381beb8569962eb8582cf19edf1b0627943891d21e60acb816dc32d2502199bbe4cb08aff56923e125fd5cb5cd5cb790045d1f0b6f163d262e8add8c0d6a24a12abef2b5eefb64af82333164e31ab6664bc179be74a4af58247b80a587d810e1bc205c4bfc51b441ec3d59f6e79a902f5227d9471b44add711916ba72913fdbd2cd2df4096eccbb749ac9323c4ccb09540cd4367f1846ff57ea239f702499c5d1c7b2d7908727c56bb6555d12e5f83ddecec901c39c6274d1712b6d497c5f838afbe07b19e8209297abdd4852e556dd5ba7baeb0623970a27f4c3dc5abe3a9968fca0a20f27fad059cea22d764f563a6313c1e3bb5bb6daa4c99c630e36c7426e2ac4c64843232f4dd89ea25affc655079010f113f67ea2251d1c8e5eab399e37fa9b3272b9b12fb1cd955bc99b7dd17e7cac1d522ccb45867f82811cc1731926adf904a6a939d86859b759dc2d79044c2c3a69bcca6797fae8377d3369e9f26012bc4245ca53f3f4e94401f6b0565dfc5e60d8436dccc40430d1639090355117df71fd5d3bb383cf42caae335fcc821164d28758e4880abe280e8daac3604670a32d93330bf464044960e85b5a525f9a0f9f6fc4525824c592bd3a8d866e82fa3274444224c3a2545e69d04a8e81055c2a44b0d743e1f38522be4ae25d0d52ebb103a85d3f1e4c13aca71f28780b6e728202572cd586f9c9e29b99a69049de0ad5b7ce113076defee649808cd1eec13966ace14681126916e65a9305efe7af1440f5f408e3b0000000000fde01ca683d830fbefd67b98b1f84125e9e2d3a844f38fed52400efbe4f5f92515ef0bdd76fc972643acf8ebadd4d97dca4c591968eced405820d608d847a63341b13e4949adfd95106b0035aa1759a9b7687863a351b12ee938d37e6c3f7efbc0d699b99fead694f583b67f7359ab7ce5d9f843491bf8e64fc4b563045de535a391ad07e9696a68bd54792d27d56a15c67212cb57c78abf4ae1a7898aa9a7156eee813cbc2fd254464c10468a8af843b15864919a7b00628dfd6c4362f72cd365b9a766a9bc1e2dcf716d7bcf59dac3ebae1e4115999dd8f6985d38a3025bcb92001bbcd4185e61d47ac5088d9a65c28ff99a26cd8648a383f7d93bb1543e7dd12d91639d4314decedf85132b953f6f6f1b873f32b44f610d3e708d4e99be9f8051acaa821483d0a466c80abea6984c25f7ad438d59e58e6bf00806c2cfe66147878505573ddf9783d6fd1b3d89a9097b92e6e03bde0c903d82cc6f4fdaef8905ca1a5071e6380859eff61f11ca29411e2efa2e948406e08f59b3201b8a69f34fa7be69899cf40c035c9965c3814b79406ad21c507e58a9632f832aa89cf05cdf6b83fc244a8cc7439009226b52eeebab5c59ff878175d1630c3a6cce444f3ebbcb9ddb64a2d34ce166a7c157e67037a5345a8a6f4d8be2809d3e771623434cc0c08d93de4f32ceadc28bb298fe4c8bbba1279dcd079ae3a0f23c520f70eb1e5953a15a0cd911926346e19e53c9bdb64675711f2017715835c2eb10b1a6f858192f1ed566521146fc5c330c682919f9f566ae26d078a153c20be69298d305a54a26aa0cb2419acb7f2df24f7fba18d60b3f61f92af7e14c95fae8e14ecd0652f66e25287a71b3c76678077a86204b71770ac2cfa93726ad4085e31002ffed0c6b8204c75f3819fd9c428a827047690eef0f1b43e5580470d87902690aa69f925e33a94d23bdfe4cdd0d6700895ebfa418e8f57ba46b4968615095878b3218a6ef0f2c574f916912cfdc3a1dab6ededf87300359087f157ab022c1ffc0bbbabe241bb5895129bf92ab6b79be08f3f18fb0db349a8e4e725967b6c579275c5de0c82705cb690ba836c884a9680b5dae639092f57f07e7aed117565cdcd243b9d547fd3daf255dab39e651ffb0125ee75241d18ef4fa5dc6ff103366edbc12a62f1e8b24dbb39e9597e3cf0a8ef6a73766529e53775b72693cc561164b9f2bb81304f7ac34b3334f2b56dc1807ae3288ef139a0d9e8160bf39f2580bfec1b3fb671582b32b086ea072490dc1333b1999eb6689ce955965d21d2d13c383d5eff022772d18aa4449ace5ed9ffc9adca96d7c07ff99f43b41189af2766255acd7e52b14a8a3e67d7d7f5469db1abd777b646f5008efd117ed66318830079c82c478c57cb02a575fb0025701a41db8b55d4eae176de6acd3f0013dda0672d1847fe20fe2e7b4f3ac0fd7548ad5e09171996d287c0ced00a4afce8ddf09a558038db450d8bb9f698e775443ee81556bc913ca191f0cc1613806f5a46daac751738db4d6096016d628775a57636b0a8b5aff256398ea5f920eace08f49a0f1bca6d108a9437cb47348b44b3ae40db63ce3e1f003301613ec6e0194e168c1545e1ad4e5500ab330a1db3c3bebd5a022b3ca838077cc7a396c1bcc55878b759f0287ffbf045afbbdc9a7ed5cd93f7e57e9c729f4232623033def63eb9c923a8670b5c4d88ec65f0cf4a7b6976951aa9db4d6963f8df2279feb8a244783d069b7f462e32fd72e4f0ab27831eac30a2d45d73a3d3786e7252b6132d402cb0523401ecb0a3d19798720a970d53b2ac1a9e31885f4162da4b3e62502bc955b1c7c8eee0aa9efb614188a4adb53e3ff5d223164c0143b624153f0ed3b5003bd2d6460c69b38e6bc79299a6bba241db6f757f87813396ac26e06d8888c551b36736553b2ebe96bb6fe6120feaa143b054c6f3db98d4889dea43dfaee594140416de2e6dadd4c4e9bec519f446e4a5ce91615d2bd09529e023758490f5d5514634cd82351d69e6435527aa7a2d8c099071a5350561371dbe83074b0c6f00985ce57b1f60946a5e96238bf80d5005215a5b563fccbefd147cfb2901077f45ea9c9a12fdbfda6c907877d4e3ea64beea348bbe1dfe3c479dbe72a70cb47434991b110c0594244976ef2e6dc31b0ca2aed9bc07549aa06630bf1232003e0c4197e182ee61e74da7903dc6e41090d05cda0a700523699463d330be1b49e3f305208ae234f1aa734b5796fab90381368d3ff0ed0564ca10cb48c2359d69a3304ca73dc6752cff1c8e932cabbf91aee01dc6acc77e9bcc0fd9b0a19878ab1f0a1477336e9d046f480b20e9be0d8b11a3a3625dd93cd5c2779e81a85a14a742a189e6c3e51820a09d83e34b3b7b58a92a393f5370302d3414460b83ca080bef598fd8a9fa39fd051a1b8909d06290709531716e24b2aaf692015d18e5ea17cad1d06f7d90751a4a04124fea400be3e4e5ed2e1895feefd12696ba2ee863483be430181776786713b03c91706e87a0d79cfc728d700649e2110a8d47b9a5aa65713a3989bb52b02871120f4ac4f0428e5dd6e76a6c1dd89dffd1a5539654f22dc421355d73296600611f9a187c0c31f992a9685e4746b6ad46edfb2e381b6681e26ec147a98cfe100c124c1d68b760dfcb36e1f01416e2f3133915a96cd8e4bc31fed6ef93a6fed8c4ffe9d0419cc2aea85800ce7b1ece31cf099f7e159a1a0b8621b934f63e8f1071107d3bd2d1d3880fd65cd39ab98f5c98e4a0a1a25b4aedcc57a8aaf684ad74ca9568b88f4ff00c84bef4d331b5752062dc290b04a917d19faa3ccbfef9eac43ab28333625630c6e6f5d2bccaa5fb3c6c7ed7f0719645bf159269b51dff6e358e1fb10b6c2f7072f357fc1983a8f4ea58405ecadb5de04badd108f19daf3c50120e966239f12027f85b52b244718a92c010f19b1c0779b2f1e5baca947d5c7d48e011dacbe070a6d4bc01e4cb3c1c1efee207a3b1372d767340f859f78869c534f5e148700552116c55208d5e762da64e7f456899fd6b89957d3f2db9b71be989e221ad86e712484d3a387695ae6ca2579f04f17a43674f50619f230af306e0f336d329188a41c0b64363dcaa40298b12e9356f357bb52a57b6fc321c806a8a39ce4441e5e3236914fdde731a2e66a63ce57e5f8185a4fa96e16b18d0b14e9299ca0a15f551f16119e387fd3fdc39b8e91fe1e273d5765c4641a1df928140222cdb8c161a65c050e998d025cb20d997240add7bcbc938f64e8cefe4312614e89e26f88fb2f9f3cf7a42f494bd56764f170ac7ab9ac523c7eb816232b47af572340f731e5afeb20f13ba78898de0995079d561fd39107fd5654e70ac5e57d6b5b20fcbeba5f052001d850fa165604fc64722e402826c9adb2973fb8de4238324de0274736d68911ffd25ef97bb3990625c61752ec69541e4ca6db1d83a939fbc4dcbdccb7888400dbc554440d4f391d9c3ffdba6ee44e009ff3f214ed539930ecd66f77e03317236ab8ee458aa0dfa6535714308c5c5171705981662fbb03256e63412e4eace603c63ef9b951c8775cf90c4fe89fe45fecb648b4dee1afe85cc1091ceeb75284285f813771503567d6bb8f2934e5060a8240f470007b62ee89a98e69836afdd11d67ace981600b1efaf3448ee7c2835d5fef5c0418b32c2213426fce3add164521a7b5600b9dbd9f23cafbbb9235be20e808eb2d0be6b85033c12928612ed97628f05a49d0131395c3503cb478b1fb6cd7d6057c61979e49d3eff07f843bcd4104a99341e8096eec42bdd60bce91f5a3b1cdba7cc1b97cc6df80ff72d1ce3ba523d9459d42df3a3db6f8afc939eac73ba69495759b83f8990e5090e9f607d0c63a55da1c6903887da422c00ec00fce80063cb8b236f640cb3ff351189c1f96552c3dfa879bac696d50dee4cb68e7e7ab1ed316e65269eee399dc703cc4a02ac01100a822b1fdb375ec0ca1978283cbb8b0af9a140a3f8472f293d1b1ac221cfa1532f5626f1aa05f17984af7ad5e06587c7c54ba2c078837c1a442bed9872bf70081fb534c33c72e392e975af8336f72735fd205a2f0ed96deb8103b6bcd139236128fcaa254546432ec64374d209296cd2dc09afadc697b956fa3da4a25b88d007e8b67f49039911b114b94d83a3ae7bee26da163730a5674c31106be9f917d1fff301fd9092f050613b609af7b36503080c375e3c4a8cf8ea342c741562d8a19773b166d5536bd3f505dd1c19788b6da29db3b1641a75b4b802b72a0dea1aa0235ab0c7718990896b22aa53ad31c91c8e5da28aaf8ba8f279770c7ff3399f5101615ba919bf81d2bcb72d155959f611b064443fd80589247be066a338d61d20156adc6d0634e2824ae94b99f3d6eb1e6477373c2ee881d817b859b4a4a5d9b00bbdf8dd59e338473a30ee5facfe3b9ca519da0465f758a57398274149174122a3a1f06e19289a755089f6e4a8561ea5b18e7ee222a35c766e09b36431d58163c90e9efc43abf28c1e9fa7c0d046f4b8f88b858e5cc7ac6ba7b1a25074e540d201bb5a0174821c8ed04152f0dee28646f9e3ce816be54d5ccbfce073f65176d095d2338ee411876af7599268b23f6897cc63b0fa71c2ea331fc07fd5aeef7912486b2c81f90728ea0c30e758580ea1da099baa8f84fe0a27bd5804845b4b01104147f385e51d309028a6539843f75a21051592282e07c1ec132e52a0b6903e72717615e34d5c2ab82e9ec74e498e56e20f8c3485628868f3205a1119814b00a1637cbfc4c7669c111b0b20db5b63a8eb8d89b2ea6f4d6fe2d1ff7b6dd8549bb02cc0c4bdc7893127d4122f66e6703038b8176d361e454dbb172af3128e86cff3a1be1bf006e9c9d45b3320f8bc9177612d217b5bf29e2d945d1da0721a8672f2dc73d87a3fc5f91b72df2843d34df2620e8bbbd2a1ae754433a76f7c01fa3c31566dc3bc53baeecb13ad3ae78c032eae53fac510933a1233cd09f77bda506c424ba2ceb3ecde9363350a90963a615b1a9d8604fd1c3d8faa32d8238c10a03cb3dd1442f7f4ae88444713536c99a7d4e0d82eae446278a572ecf087f98e1f62d251b5111b5c07c7c022f9466cb471e0f692e22b6a01f4883ccbc9aef354b13db3ed86128c7b48bfa38aac481bb164e144cf7cc5cd9ddc89c390f05c8b8f1fc6139fb90aca4f05b846e724187083ada982655c7282530b34832f28af0d341b3d21f281c5b2d11380e78899358121b4985536199ddb8bd6a1d589c10439f9b63070c902c96916e392c1aacd734ca2bca4ef118937aebcf8b7f5d6892af7aa170ab17bf6ef1409b12477328e4409b8591a3b32f2151a73f5e3b8238e24b175ae8e92da8dd8ac4a6b3e48f9b9a93b3281332e0023d618ffd27b402e986e62869f56033d020c595f77117c857bda58e59e10d06165b44d86cff78cf6aecd3b8884426053ac0b395c6e38c8f74d36725a4ea60b38dcc5da25c6996453dabe287ec48ad03062ce9ceaec6c521554fec71e246c1d7528d30bbb63b5713f1c798c6b8003327b15492798b800e9fdbc4ead8e5f999a4fca0c88b47b1fccd5998d54095b1003fd0b07f225200a8e044de43bc32e92ac72b8eec3d9c63d4b1db67e4dd884dab1366cb8c5ef8efb5b7755392838dd2d5e05ce9ff330781571c52634977b4ca1e1696466c93bb61e3a1aea52946121b9804cd50d020afe21467f146760dad8f123490fa5074cfbc17bd862e0288e93b4ea6e537946f2fb6b05e5ceeb73b859bdf2bb61b6f38f332df7ec6b20d37c0ace3f0307dea511f0df1c81079d1553a1c550f6f65f6e9334d64d19c81b9ded113bf945d58df5be0372275e7f59dbd8738bf3939836b7aa7442a98f01ebf5744dacfb4f01218abac2c6a58387bc793bbdb1c041dd56c14dc02f7f0690f4ae2800508ac6fcf8a56733773aed9e899eeab42a7229971a8da896f5b49a344b640d19ae46a489dda7da393c423a97300a28c2e2b10ae580fb0164f83f1655df5e66780cde64b7bbdef16563acc3d65e98dd800f90b3a685d17c2b79371aa4c25757fb206c6b1f77c170ec0469c2ff61250acd5192d49bad378cf09fca94de403bd50f0caa51e8b59ba16c71ae02078d99e3af1b735a37d8af3b8985d449ba0f9054b2bc3c80f6cddc9fb05848467afc2a64633d323c88fa7a4b7ab33f81c6d8262c2ca64e7b9f311d168161bb06c39bd7fd7aed814933832546c271f54eb662689c1cd7c967b027b351bac886ce25ff24e7c10a507489be529029f8270ef99dd21e043ca7a675e2785d629b22979464a32ecdae53cc87b52da6c878a6d7073d1c3fdfc81c34f2846ba40b911c536c4dda0d44ae41923d80a7b6169af46bed4d632c04fe578065e262731ff1d11d81d80527e2be11316520f21c84d38b466c0d58e09cf1b8d54745264ca9a8102f9d4c8eaacc5b11111f17096f4de3f02ec6e6f40006aae55ab4d64ae4d117ba486d264376e6c471b85979514ea86b95bda4dd29666c45623efeb8e2f0a2a0e7bd7e93de9a42c5c1420bd0ca9d27af76a2ca3f2e8241fa3c521aa70f47c9700a4f131472685409f081823e0f2b11febf7d87f80c9bb139e3b91328672c4e38b175441eca114e2572d29f7006671907fb586daad0a594372ea110dabfb2f5db51b4ee8f2ef7174db21ec091393a641a2bf1d239cc0f230dcf88531fbd0f71ba274b817d356c20e163295f77a1c72031b75449ce46e0eb26c60056fc9a480c4238782a193155a9c26233e464c82d5b2472806802cad75fbba9beb2a484b9d5cdcb44fd61228af121a13580b91d3e0fab80fe4d54096b97626199eb9316ec375f4d862acc8ac10af9d104144ca04f6c0c62cc4c3867951047e80d038dcb38cf4faf35a7e35722767d43d6c70041f37aa056db3a85d3c36686351b2d001942a316265ee79c8848ce19f1276bf7b89dff80180e8a54919e7f0e300299cb574fffc41b0172a06a0682eb12b0afbc40556fac70abb6cb24459d86f8d976757775e3d011462944794eecadc2cd2998e1f202aeb7ca813ca5966908b3eeac3577650015311de2ce584546dcb00a588541a2d73a742d3ba01ada0794abb5acebf9467ef4c40b43ae94426cb06077451e6966cf2c9f3050bd89cfdec233130660a6683b5ac320b5543dd77e9c11ad1e9b5385a0c93765858ed20833c6c262841e5aa3a5faf3e944deb4a1611be21e34cda9d021c0a4f0c09e001f9412e1272a21aa03744f175f1ace6c90ebf203bfc8fbe436c3b83f03417c1c486532a6a621e81265389b10cead34c1577faca3bbbc53dd9e9d95bd1411bba48a29acf797071166b3baf12b1ba41bae9d5c6e201634801516e2a3eb1a7dc431f0ad5074cd2723e6061b50710d213e52eff591638eeeca833e2429652f15864a725f2d96613547b166cab7a637d199978fc160e1e5ee8bb9e23d3f4cf0f3d162df93059707fd0f4e8cce5fb00cbd4f4fbfa1061265a210a0ce1a7a7aa813d835ebf6e6bda32399bff4cfb33db8c36e46935a0fd04ab7d734c2d94840e604d7871d43284757f01f099201a7eca576fed8d72727320e2a245734e78402312dc4073efce4910d200548e68bad92c8bf7167ad05998386fd9d02a0cc5ae81bd021b810f98b6a6abc6ec94dbaac22555bdc7b676c31c2bdc2ac5c61466715b73225171d0f89d0562cb7f99120e71660351c82230378b3134b61d522bb2c0d8aa671d12d048df3bec31c778e152b567f88d93137b3380318cabe20f73c210f91701763cea06df6ce966e2f02c57cbda0feeeef55bfebf1bfa77f7fc5e8cf30c55d6e6f6d9752670d82701c5737dccdd36b539f22f9d122babd0408274c2ff56eb4725466195c4aa5bd28e9b241ece4aaa86aa18a04dfb076ff6257c0893b2e65f5ba62d4540f1f19d828b0c9cb4e337c1156ace7b550705411b38bce1a68bccc9184f27aa03f6fd6fc31453ef537254de043dfd461361381b4864573ca8e06046f8d500d1e60b79c54763fd5e244042e7f77fefd1691229f6a621db132b8811d0f32e9871bf85ffd81a088f902280081b53b3026fdb66329cafcb3c9ee0b1c1213d190e86d990f4b1a4b7a9c360b80050fc2af6f1b73d3084d3173b8689eab365e181e2c1e4554614b4ac658a4684cc9e976eee419aff2536832d5f2eb6c63ff7a9fe987d5165798ed5731ed3877af8e5b88f10b8dece1592b8c5e7cb6765e0368651a107a6273539ac155f4030d9cd842270bb284f76184e664e8e7d89710b6676ce2cafef2451b11c092dd49a1603f1f8eeb7010ff72edd2f91700634d1d24eb5ad70ef6862cd4e122304eaf84e6f3e14c3d6206bf6207544a48cb4f2f4a67bbc731be3babdd1872315c239c9ceb234aa40175549ea1e029bee42ceeae3fc3c9e0c8896eea93b829102b6f06c8c3a91e995aede46731fc260862a6e47eeb0bc9f377b4893dd542d902835afa6f8618efca6a191246717f8afcd80bf92c6d1d35c22c37e5bb26968f16b057b285df374433a251781f7069e9ca069fee52607f11c238ea0e3c4d70e2f1115ec64216030ab1a0e35687211d27676d679a510a430069be1b0b7a1b877caaed37230ac7a833cf7aa5ecbfa063f38dc3cac5c663b67b86739151713da11706af98bdf0eb0ee1fd8bc94dd31033a1084d6bd520d9399e4dac46fff62bc819ac5e0bd856c194e05c6708a65712f6b4bba424497def0f6b108fd8f23cc20dc1409842fac7cc80834a31db1be8d01d5f920579eefe2155ce973831f723d5a4151a7d2508ccb09a2d26d3282e8d6301e46a5e1ef1b5e2243d8f8a87c787d92c48975e615153d1318a8fb4ebb47d80908e6f63622a61009593b574024921119765e9c7d19cdb730be5c533364e12921b13267c2a64783ed788274830e2f806330d17df3e0935b9c5cba64c281578331fa0d6c9eff7b1b10afd8bb4dbca9f5a8dc3a35e68b0545d3089234cbd65b372c29d5a0526577a2402fcdc733d904986e327a0d3fd6e29ea62f183bbfa7db743793e8cf1912370c7da5aef6f91c007324289880911dc79eef6311189eda702a38deec8af58bf3d69a8fddeb4826d766690d0bbc51b1e87e620fe8533a25b9f93f8d40b64b15cd4f0f761d36ebb974b2cc51be0c98381131dc05cf331e3a636405174638f1101c62fcca193103814686758adae28662c0c7b80ab5bd2c58abae34b2f3633bc79cc88c195e7281929f5ecf9ec4f2330a14d992d6e2bc849b158b3404a02c53d18584860ddbcfbaedc9b4321761b56befa57b59b2b566a125aa263a037c6b0cac72e1f686aec527e7306c4a1b30e5c5bfffa40c2fc5e8475a0733add8012920be1759cf3926a8f98c166c7b391184f992f7483858bf6500bfe9b52a1e2db438122996f73b2514ffc0175567ece1badae39b6a9c719e32a14898c8abf5b92583d13695480677673f20475f271919d60915423d696e868fddfa60e889a7586d510c62c63a16b1998cf65951399d7892d137ac9168e09bc43b5989c79a442a091c8fff191c5066e289e98f855ccffdf996157e9cfa917c244da4ba3e0cafd28046c9a9aea3df72ae68b0c275b878aae46a9139b2139c6193f9b765f901fc5b3e41d5986aec0c427d7542d5399ed3a58ff5c40da1648d1379a890e4f82d58f29ba225a94a62defc333f12335dbe559b70f4c95923a12dce670967a21eaff10438356d9276305a9d0a33f5b152e88d7e98f20402cf327b181a1479019bac5ea43781ac0726ff8027181215b36995fcc35817790d6b0882801f18dfe95390c440465983435d9ef3104e4916cec4f0346899048a2e6fc1a389c2ebb9efa20bb14587f108d232d4cccb833c3cc46f44b735cff07c1e6e4dbe4b1c4db926368de1643cb0ee2094e2bb9e774c34e668832d59acca65c33c92fb850b5be96650aca5c070d312edd0158310c6f575e646f529d195f722d3b566585b21e99950702156144c0a59cebcd47586989c7b96b924196f236ca23bf95aa8fd170046288d3ac5e2a016d0b8c4dfe2266f8a144c1646d21f8f2ed58209103eaf13a33da2daac85523766d81d04647a794607b6102ae623b4a8142ef3c318959cf2b8da6566160acff46f5f6fdb4e13df148f5e2062c1b7611ae1ba5e7755c49bab6d7c511a37152cf09beec0092c7bb026ec39ab798392d4b977b1ab0e1d613a0e2ccf2eca1d739cf1862396a8f3b2b33c1448bad4b3b3e3fd5743c1aed101f213c9735a622d1fee4349995cbc332f8dacc86ef92abac2cdf7793eae4dec69c6856955260a8ad34bd8deef0b25edada3ee95859cb520240b19fa23ab57890c7f7fcc53de6511c6bfae9889bd1b2b25c51a093f82b2b7e103c56708f264ed4f6a577f3002b63992158781d394c8227eabfb039cd1adb2993ec9f6d6c5cd162976b1ba16943a0306ff6a2cf67fc27389d6fa88e10920932f36a4a5aa30b322d05ceb77e471ec23f298537432b66e6b81bc2271820e4f3cf4741bfcc942f1fff1e4228ec6861d16c0e6ed6e06c5e0ce0c0bb41d0a2c046b0e93e197cd28d6620ce5a8e05116c79ead7f9d57e31059ada908be814cb0cc314dc147b5278ff7b02e3ba2a6f8b1f0210000000000000000fe2d83865ec6d0e18021047a13eff3312ef198d06f7dc7229494a5b871bcbd0a0250ce39b176b732728cb8728a59f12f8dd4c8ef874ae58d009e62c65f67873601ce2c6d87d0184c1799a254ed03047cf57a3bfd49b8a4438d496d741cf5aca6bd01bc1e9d7134d3c05c62b251ccb3ab8473cb5dafc7b19f3b6750e41bf24c6ad882802a60d88a2fb0fd642095d0070000000000005fd281521444957bab12f47104f5ff295620fd17dbdb799f813f973e1eaed416b1c5ecbc9495e905a2f167db0661507a37b8d83e6e98d494d1005d6bcdb74e3332aaf23dfcfd3870faae91996d33135a892319a3bfb08d3fbf19bf938e687ccf00ecd37228b00c8973da400337300587f48997465959efe09ac5e012e14f51eb2425b8b53fc4c9cfc3627fde7c57412b7f985853b0db8f2969b13016d992700eb6af998d6937755995afd6c769174189b5eb6b84fb35871b5844b1f320a6a16bd8", + ] + .map(|hex| >::from_hex(hex).expect("Block bytes are in valid hex representation")); +} diff --git a/zebrad/Cargo.toml b/zebrad/Cargo.toml index 2bea0392f9d..72a8afd7dc0 100644 --- a/zebrad/Cargo.toml +++ b/zebrad/Cargo.toml @@ -156,6 +156,13 @@ test_sync_to_mandatory_checkpoint_testnet = [] test_sync_past_mandatory_checkpoint_mainnet = [] test_sync_past_mandatory_checkpoint_testnet = [] +# Support for transaction version 6 +tx-v6 = [ + "zebra-consensus/tx-v6", + "zebra-state/tx-v6", + "zebra-chain/tx-v6" +] + [dependencies] zebra-chain = { path = "../zebra-chain", version = "1.0.0-beta.41" } zebra-consensus = { path = "../zebra-consensus", version = "1.0.0-beta.41" } diff --git a/zebrad/src/components/mempool/storage/tests/prop.rs b/zebrad/src/components/mempool/storage/tests/prop.rs index eca65935acb..eaf99bb8ad9 100644 --- a/zebrad/src/components/mempool/storage/tests/prop.rs +++ b/zebrad/src/components/mempool/storage/tests/prop.rs @@ -446,6 +446,22 @@ enum SpendConflictTestInput { conflict: SpendConflictForTransactionV5, }, + + /// Test V6 transactions to include OrchardZSA nullifier conflicts. + #[cfg(feature = "tx-v6")] + V6 { + #[proptest( + strategy = "Transaction::v6_strategy(LedgerState::default()).prop_map(DisplayToDebug)" + )] + first: DisplayToDebug, + + #[proptest( + strategy = "Transaction::v6_strategy(LedgerState::default()).prop_map(DisplayToDebug)" + )] + second: DisplayToDebug, + + conflict: SpendConflictForTransactionV6, + }, } impl SpendConflictTestInput { @@ -470,6 +486,17 @@ impl SpendConflictTestInput { conflict.clone().apply_to(&mut first); conflict.apply_to(&mut second); + (first, second) + } + #[cfg(feature = "tx-v6")] + SpendConflictTestInput::V6 { + mut first, + mut second, + conflict, + } => { + conflict.clone().apply_to(&mut first); + conflict.apply_to(&mut second); + (first, second) } }; @@ -497,6 +524,8 @@ impl SpendConflictTestInput { let (mut first, mut second) = match self { SpendConflictTestInput::V4 { first, second, .. } => (first, second), SpendConflictTestInput::V5 { first, second, .. } => (first, second), + #[cfg(feature = "tx-v6")] + SpendConflictTestInput::V6 { first, second, .. } => (first, second), }; Self::remove_transparent_conflicts(&mut first, &mut second); @@ -568,6 +597,10 @@ impl SpendConflictTestInput { // No JoinSplits Transaction::V1 { .. } | Transaction::V5 { .. } => {} + + // No JoinSplits + #[cfg(feature = "tx-v6")] + Transaction::V6 { .. } => {} } } } @@ -638,6 +671,14 @@ impl SpendConflictTestInput { Self::remove_sapling_transfers_with_conflicts(sapling_shielded_data, &conflicts) } + #[cfg(feature = "tx-v6")] + Transaction::V6 { + sapling_shielded_data, + .. + } => { + Self::remove_sapling_transfers_with_conflicts(sapling_shielded_data, &conflicts) + } + // No Spends Transaction::V1 { .. } | Transaction::V2 { .. } | Transaction::V3 { .. } => {} } @@ -709,6 +750,12 @@ impl SpendConflictTestInput { .. } => Self::remove_orchard_actions_with_conflicts(orchard_shielded_data, &conflicts), + #[cfg(feature = "tx-v6")] + Transaction::V6 { + orchard_shielded_data, + .. + } => Self::remove_orchard_actions_with_conflicts(orchard_shielded_data, &conflicts), + // No Spends Transaction::V1 { .. } | Transaction::V2 { .. } @@ -722,8 +769,8 @@ impl SpendConflictTestInput { /// present in the `conflicts` set. /// /// This may clear the entire shielded data. - fn remove_orchard_actions_with_conflicts( - maybe_shielded_data: &mut Option, + fn remove_orchard_actions_with_conflicts( + maybe_shielded_data: &mut Option>, conflicts: &HashSet, ) { if let Some(shielded_data) = maybe_shielded_data.take() { @@ -757,7 +804,16 @@ enum SpendConflictForTransactionV4 { enum SpendConflictForTransactionV5 { Transparent(Box), Sapling(Box>), - Orchard(Box), + Orchard(Box>), +} + +/// A spend conflict valid for V6 transactions. +#[cfg(feature = "tx-v6")] +#[derive(Arbitrary, Clone, Debug)] +enum SpendConflictForTransactionV6 { + Transparent(Box), + Sapling(Box>), + Orchard(Box>), } /// A conflict caused by spending the same UTXO. @@ -782,8 +838,9 @@ struct SaplingSpendConflict { /// A conflict caused by revealing the same Orchard nullifier. #[derive(Arbitrary, Clone, Debug)] -struct OrchardSpendConflict { - new_shielded_data: DisplayToDebug, +#[proptest(no_bound)] +struct OrchardSpendConflict { + new_shielded_data: DisplayToDebug>, } impl SpendConflictForTransactionV4 { @@ -834,6 +891,31 @@ impl SpendConflictForTransactionV5 { } } +#[cfg(feature = "tx-v6")] +impl SpendConflictForTransactionV6 { + /// Apply a spend conflict to a V6 transaction. + /// + /// Changes the `transaction_v6` to include the spend that will result in a conflict. + pub fn apply_to(self, transaction_v6: &mut Transaction) { + let (inputs, sapling_shielded_data, orchard_shielded_data) = match transaction_v6 { + Transaction::V6 { + inputs, + sapling_shielded_data, + orchard_shielded_data, + .. + } => (inputs, sapling_shielded_data, orchard_shielded_data), + _ => unreachable!("incorrect transaction version generated for test"), + }; + + use SpendConflictForTransactionV6::*; + match self { + Transparent(transparent_conflict) => transparent_conflict.apply_to(inputs), + Sapling(sapling_conflict) => sapling_conflict.apply_to(sapling_shielded_data), + Orchard(orchard_conflict) => orchard_conflict.apply_to(orchard_shielded_data), + } + } +} + impl TransparentSpendConflict { /// Apply a transparent spend conflict. /// @@ -920,7 +1002,7 @@ impl SaplingSpendConflict { } } -impl OrchardSpendConflict { +impl OrchardSpendConflict { /// Apply a Orchard spend conflict. /// /// Ensures that a transaction's `orchard_shielded_data` has a nullifier used to represent a @@ -929,7 +1011,7 @@ impl OrchardSpendConflict { /// the new action is inserted in the transaction. /// /// The transaction will then conflict with any other transaction with the same new nullifier. - pub fn apply_to(self, orchard_shielded_data: &mut Option) { + pub fn apply_to(self, orchard_shielded_data: &mut Option>) { if let Some(shielded_data) = orchard_shielded_data.as_mut() { shielded_data.actions.first_mut().action.nullifier = self.new_shielded_data.actions.first().action.nullifier; diff --git a/zebrad/tests/acceptance.rs b/zebrad/tests/acceptance.rs index cd3572ce3f2..4c717f62eb0 100644 --- a/zebrad/tests/acceptance.rs +++ b/zebrad/tests/acceptance.rs @@ -2907,7 +2907,7 @@ async fn fully_synced_rpc_z_getsubtreesbyindex_snapshot_test() -> Result<()> { async fn validate_regtest_genesis_block() { let _init_guard = zebra_test::init(); - let network = Network::new_regtest(None, None); + let network = Network::new_regtest(None, None, None); let state = zebra_state::init_test(&network); let ( block_verifier_router, @@ -2982,7 +2982,7 @@ async fn trusted_chain_sync_handles_forks_correctly() -> Result<()> { use zebra_state::{ReadResponse, Response}; let _init_guard = zebra_test::init(); - let mut config = os_assigned_rpc_port_config(false, &Network::new_regtest(None, None))?; + let mut config = os_assigned_rpc_port_config(false, &Network::new_regtest(None, None, None))?; config.state.ephemeral = false; let network = config.network.network.clone(); @@ -3362,6 +3362,7 @@ async fn nu6_funding_streams_and_coinbase_balance() -> Result<()> { panic!("this getblocktemplate call without parameters should return the `TemplateMode` variant of the response") }; + // FIXME: Would this work after Nu7 activation? let proposal_block = proposal_block_from_template(&block_template, None, NetworkUpgrade::Nu6)?; let hex_proposal_block = HexData(proposal_block.zcash_serialize_to_vec()?); @@ -3459,6 +3460,7 @@ async fn nu6_funding_streams_and_coinbase_balance() -> Result<()> { ..(*block_template) }; + // FIXME: Would this work after Nu7 activation? let proposal_block = proposal_block_from_template(&block_template, None, NetworkUpgrade::Nu6)?; // Submit the invalid block with an excessive coinbase output value @@ -3502,6 +3504,7 @@ async fn nu6_funding_streams_and_coinbase_balance() -> Result<()> { ..block_template }; + // FIXME: Would this work after Nu7 activation? let proposal_block = proposal_block_from_template(&block_template, None, NetworkUpgrade::Nu6)?; // Submit the invalid block with an excessive coinbase input value diff --git a/zebrad/tests/common/configs/v1.9.0.toml b/zebrad/tests/common/configs/v1.9.0.toml index 11bcf62107a..98a4b14bf67 100644 --- a/zebrad/tests/common/configs/v1.9.0.toml +++ b/zebrad/tests/common/configs/v1.9.0.toml @@ -73,6 +73,7 @@ Heartwood = 903_800 Canopy = 1_028_500 NU5 = 1_842_420 NU6 = 2_000_000 +NU7 = 2_000_001 [network.testnet_parameters.pre_nu6_funding_streams.height_range] start = 0 diff --git a/zebrad/tests/common/regtest.rs b/zebrad/tests/common/regtest.rs index bf1cba697de..f7b571e5a09 100644 --- a/zebrad/tests/common/regtest.rs +++ b/zebrad/tests/common/regtest.rs @@ -43,7 +43,7 @@ pub(crate) async fn submit_blocks_test() -> Result<()> { let _init_guard = zebra_test::init(); info!("starting regtest submit_blocks test"); - let network = Network::new_regtest(None, None); + let network = Network::new_regtest(None, None, None); let mut config = os_assigned_rpc_port_config(false, &network)?; config.mempool.debug_enable_at_height = Some(0);