diff --git a/.cargo/config.toml b/.cargo/config.toml index 4175fa37571..8b66aa3cb93 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -3,8 +3,11 @@ # Flags that apply to all Zebra crates and configurations [target.'cfg(all())'] rustflags = [ - # Enable tx-v6 everywhere by default - "--cfg", 'feature="tx-v6"', + # Enable tx_v6 everywhere by default + "--cfg", 'feature="tx_v6"', + + # TODO: Remove this when Cargo.toml is updated to use the latest librustzcash zsa1 version + "--cfg", "zcash_unstable=\"nu6.1\"", # TODO: Consider removing this line later (it's needed for the ZSA version of librustzcash crates) "--cfg", "zcash_unstable=\"nu7\"", @@ -88,8 +91,11 @@ rustflags = [ [build] rustdocflags = [ - # Enable tx-v6 everywhere by default - "--cfg", 'feature="tx-v6"', + # Enable tx_v6 everywhere by default + "--cfg", 'feature="tx_v6"', + + # TODO: Remove this when Cargo.toml is updated to use the latest librustzcash zsa1 version + "--cfg", "zcash_unstable=\"nu6.1\"", # TODO: Consider removing this line later (it's needed for the ZSA version of librustzcash crates) "--cfg", "zcash_unstable=\"nu7\"", diff --git a/.codespellrc b/.codespellrc index d14b397da25..8c06f3917e1 100644 --- a/.codespellrc +++ b/.codespellrc @@ -1,4 +1,4 @@ [codespell] -ignore-words-list = crate,Sur,inout,Groth,groth,re-use, +ignore-words-list = crate,Sur,inout,Groth,groth,re-use,abl, exclude-file = book/mermaid.min.js skip = ./zebra-rpc/qa/rpc-tests,./supply-chain diff --git a/.dockerignore b/.dockerignore index 9d62f3c5c13..137c05f2931 100644 --- a/.dockerignore +++ b/.dockerignore @@ -21,3 +21,4 @@ !zebra-* !zebrad !docker/entrypoint.sh +!docker/default-zebra-config.toml diff --git a/.github/ISSUE_TEMPLATE/release.md b/.github/ISSUE_TEMPLATE/release.md index 0e2ee30b7b1..dc42b4aa05e 100644 --- a/.github/ISSUE_TEMPLATE/release.md +++ b/.github/ISSUE_TEMPLATE/release.md @@ -2,7 +2,7 @@ name: "🚀 Zebra Release" about: 'Zebra team use only' title: 'Publish next Zebra release: (version)' -labels: 'A-release, C-trivial, P-Medium :zap:' +labels: 'A-release, C-exclude-from-changelog, P-Medium :zap:' assignees: '' --- @@ -16,7 +16,7 @@ They can be skipped for urgent releases. To check consensus correctness, we want to test that the state format is valid after a full sync. (Format upgrades are tested in CI on each PR.) -- [ ] Make sure there has been [at least one successful full sync test](https://github.com/ZcashFoundation/zebra/actions/workflows/ci-integration-tests-gcp.yml?query=event%3Aschedule) since the last state change, or +- [ ] Make sure there has been [at least one successful full sync test](https://github.com/ZcashFoundation/zebra/actions/workflows/ci-tests.yml?query=event%3Aschedule) since the last state change, or - [ ] Start a manual workflow run with a Zebra and `lightwalletd` full sync. State format changes can be made in `zebra-state` or `zebra-chain`. The state format can be changed by data that is sent to the state, data created within the state using `zebra-chain`, or serialization formats in `zebra-state` or `zebra-chain`. diff --git a/.github/ISSUE_TEMPLATE/usability_testing_plan.md b/.github/ISSUE_TEMPLATE/usability_testing_plan.md index c93f413b605..570f1393d1f 100644 --- a/.github/ISSUE_TEMPLATE/usability_testing_plan.md +++ b/.github/ISSUE_TEMPLATE/usability_testing_plan.md @@ -31,7 +31,7 @@ assignees: '' ### Method - + ### Test environment, equipment and logistics @@ -56,7 +56,7 @@ assignees: '' ## Session Outline and timing - + ### 1.Introduction to the session (5\') diff --git a/.github/PULL_REQUEST_TEMPLATE/hotfix-release-checklist.md b/.github/PULL_REQUEST_TEMPLATE/hotfix-release-checklist.md new file mode 100644 index 00000000000..ab730cac1b4 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE/hotfix-release-checklist.md @@ -0,0 +1,100 @@ +--- +name: 'Hotfix Release Checklist Template' +about: 'Checklist to create and publish a hotfix Zebra release' +title: 'Release Zebra (version)' +labels: 'A-release, C-exclude-from-changelog, P-Critical :ambulance:' +assignees: '' + +--- + +A hotfix release should only be created when a bug or critical issue is discovered in an existing release, and waiting for the next scheduled release is impractical or unacceptable. + +## Create the Release PR + +- [ ] Create a branch to fix the issue based on the tag of the release being fixed (not the main branch). + for example: `hotfix-v2.3.1` - this needs to be different to the tag name +- [ ] Make the required changes +- [ ] Create a hotfix release PR by adding `&template=hotfix-release-checklist.md` to the comparing url ([Example](https://github.com/ZcashFoundation/zebra/compare/bump-v1.0.0?expand=1&template=hotfix-release-checklist.md)). +- [ ] Add the `C-exclude-from-changelog` label so that the PR is omitted from the next release changelog +- [ ] Add the `A-release` tag to the release pull request in order for the `check_no_git_refs_in_cargo_lock` to run. +- [ ] Ensure the `check_no_git_refs_in_cargo_lock` check passes. +- [ ] Add a changelog entry for the release summarizing user-visible changes. + +## Update Versions + +The release level for a hotfix should always follow semantic versioning as a `patch` release. + +
+Update crate versions, commit the changes to the release branch, and do a release dry-run: + +```sh +# Update everything except for alpha crates and zebrad: +cargo release version --verbose --execute --allow-branch '*' --workspace --exclude zebrad beta +# Due to a bug in cargo-release, we need to pass exact versions for alpha crates: +# Update zebrad: +cargo release version --verbose --execute --allow-branch '*' --package zebrad patch +# Continue with the release process: +cargo release replace --verbose --execute --allow-branch '*' --package zebrad +cargo release commit --verbose --execute --allow-branch '*' +``` + +
+ +## Update the Release PR + +- [ ] Push the version increments and the release constants to the hotfix release branch. + +# Publish the Zebra Release + +## Create the GitHub Pre-Release + +- [ ] Wait for the hotfix release PR to be reviewed, approved, and merged into main. +- [ ] Create a new release +- [ ] Set the tag name to the version tag, + for example: `v2.3.1` +- [ ] Set the release to target the hotfix release branch +- [ ] Set the release title to `Zebra ` followed by the version tag, + for example: `Zebra 2.3.1` +- [ ] Populate the release description with the final changelog you created; + starting just _after_ the title `## [Zebra ...` of the current version being released, + and ending just _before_ the title of the previous release. +- [ ] Mark the release as 'pre-release', until it has been built and tested +- [ ] Publish the pre-release to GitHub using "Publish Release" + +## Test the Pre-Release + +- [ ] Wait until the Docker binaries have been built on the hotfix release branch, and the quick tests have passed: + - [ ] [ci-tests.yml](https://github.com/ZcashFoundation/zebra/actions/workflows/ci-tests.yml) +- [ ] Wait until the [pre-release deployment machines have successfully launched](https://github.com/ZcashFoundation/zebra/actions/workflows/cd-deploy-nodes-gcp.yml?query=event%3Arelease) + +## Publish Release + +- [ ] [Publish the release to GitHub](https://github.com/ZcashFoundation/zebra/releases) by disabling 'pre-release', then clicking "Set as the latest release" + +## Publish Crates + +- [ ] Checkout the hotfix release branch +- [ ] [Run `cargo login`](https://zebra.zfnd.org/dev/crate-owners.html#logging-in-to-cratesio) +- [ ] Run `cargo clean` in the zebra repo +- [ ] Publish the crates to crates.io: `cargo release publish --verbose --workspace --execute --allow-branch {hotfix-release-branch}` +- [ ] Check that the published version of Zebra can be installed from `crates.io`: + `cargo install --locked --force --version 2.minor.patch zebrad && ~/.cargo/bin/zebrad` + and put the output in a comment on the PR. + +## Publish Docker Images + +- [ ] Wait for the [the Docker images to be published successfully](https://github.com/ZcashFoundation/zebra/actions/workflows/release-binaries.yml?query=event%3Arelease). +- [ ] Wait for the new tag in the [dockerhub zebra space](https://hub.docker.com/r/zfnd/zebra/tags) + +## Merge hotfix into main + +- [ ] Review and merge the hotfix branch into the main branch. The changes and the update to the changelog must be included in the next release from main as well. +- [ ] If there are conflicts between the hotfix branch and main, the conflicts should be resolved after the hotfix release is tagged and published. + +## Release Failures + +If building or running fails after tagging: + +
+1. Create a new hotfix release, starting from the top of this document. +
diff --git a/.github/PULL_REQUEST_TEMPLATE/release-checklist.md b/.github/PULL_REQUEST_TEMPLATE/release-checklist.md index 5445834df3e..a99cd9d308a 100644 --- a/.github/PULL_REQUEST_TEMPLATE/release-checklist.md +++ b/.github/PULL_REQUEST_TEMPLATE/release-checklist.md @@ -2,17 +2,32 @@ name: 'Release Checklist Template' about: 'Checklist to create and publish a Zebra release' title: 'Release Zebra (version)' -labels: 'A-release, C-trivial, P-Critical :ambulance:' +labels: 'A-release, C-exclude-from-changelog, P-Critical :ambulance:' assignees: '' --- # Prepare for the Release -- [ ] Make sure there has been [at least one successful full sync test](https://github.com/ZcashFoundation/zebra/actions/workflows/ci-integration-tests-gcp.yml?query=event%3Aschedule) since the last state change, or start a manual full sync. -- [ ] Make sure the PRs with the new checkpoint hashes and missed dependencies are already merged. - (See the release ticket checklist for details) +- [ ] Make sure there has been [at least one successful full sync test in the main branch](https://github.com/ZcashFoundation/zebra/actions/workflows/ci-tests.yml?query=branch%3Amain) since the last state change, or start a manual full sync. +# Checkpoints + +For performance and security, we want to update the Zebra checkpoints in every release. +- [ ] You can copy the latest checkpoints from CI by following [the zebra-checkpoints README](https://github.com/ZcashFoundation/zebra/blob/main/zebra-utils/README.md#zebra-checkpoints). + +# Missed Dependency Updates + +Sometimes `dependabot` misses some dependency updates, or we accidentally turned them off. + +This step can be skipped if there is a large pending dependency upgrade. (For example, shared ECC crates.) + +Here's how we make sure we got everything: +- [ ] Run `cargo update` on the latest `main` branch, and keep the output +- [ ] If needed, [add duplicate dependency exceptions to deny.toml](https://github.com/ZcashFoundation/zebra/blob/main/book/src/dev/continuous-integration.md#fixing-duplicate-dependencies-in-check-denytoml-bans) +- [ ] If needed, remove resolved duplicate dependencies from `deny.toml` +- [ ] Open a separate PR with the changes +- [ ] Add the output of `cargo update` to that PR as a comment # Summarise Release Changes @@ -26,7 +41,9 @@ Once you are ready to tag a release, copy the draft changelog into `CHANGELOG.md We use [the Release Drafter workflow](https://github.com/marketplace/actions/release-drafter) to automatically create a [draft changelog](https://github.com/ZcashFoundation/zebra/releases). We follow the [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) format. To create the final change log: -- [ ] Copy the **latest** draft changelog into `CHANGELOG.md` (there can be multiple draft releases) +- [ ] Copy the [**latest** draft + changelog](https://github.com/ZcashFoundation/zebra/releases) into + `CHANGELOG.md` (there can be multiple draft releases) - [ ] Delete any trivial changes - [ ] Put the list of deleted changelog entries in a PR comment to make reviewing easier - [ ] Combine duplicate changes @@ -57,7 +74,13 @@ fastmod --fixed-strings '1.58' '1.65' - [ ] Freeze the [`batched` queue](https://dashboard.mergify.com/github/ZcashFoundation/repo/zebra/queues) using Mergify. - [ ] Mark all the release PRs as `Critical` priority, so they go in the `urgent` Mergify queue. - [ ] Mark all non-release PRs with `do-not-merge`, because Mergify checks approved PRs against every commit, even when a queue is frozen. +- [ ] Add the `A-release` tag to the release pull request in order for the `check-no-git-dependencies` to run. + +## Zebra git sources dependencies +- [ ] Ensure the `check-no-git-dependencies` check passes. + +This check runs automatically on pull requests with the `A-release` label. It must pass for crates to be published to crates.io. If the check fails, you should either halt the release process or proceed with the understanding that the crates will not be published on crates.io. # Update Versions and End of Support @@ -72,38 +95,39 @@ Choose a release level for `zebrad`. Release levels are based on user-visible ch - significant new features or behaviour changes; changes to RPCs, command-line, or configs; and deprecations or removals are `minor` releases - otherwise, it is a `patch` release -Zebra's Rust API doesn't have any support or stability guarantees, so we keep all the `zebra-*` and `tower-*` crates on a beta `pre-release` version. - -### Update Crate Versions +### Update Crate Versions and Crate Change Logs -If you're publishing crates for the first time, [log in to crates.io](https://github.com/ZcashFoundation/zebra/blob/doc-crate-own/book/src/dev/crate-owners.md#logging-in-to-cratesio), +If you're publishing crates for the first time, [log in to crates.io](https://zebra.zfnd.org/dev/crate-owners.html#logging-in-to-cratesio), and make sure you're a member of owners group. Check that the release will work: -- [ ] Update crate versions, commit the changes to the release branch, and do a release dry-run: + +- [ ] Determine which crates require release. Run `git diff --stat ` + and enumerate the crates that had changes. +- [ ] Determine which type of release to make. Run `semver-checks` to list API + changes: `cargo semver-checks -p --default-features`. If there are + breaking API changes, do a major release, or try to revert the API change + if it was accidental. Otherwise do a minor or patch release depending on + whether a new API was added. Note that `semver-checks` won't work + if the previous realase was yanked; you will have to determine the + type of release manually. +- [ ] Update the crate `CHANGELOG.md` listing the API changes or other + relevant information for a crate consumer. It might make sense to copy + entries from the `zebrad` changelog. +- [ ] Update crate versions: ```sh -# Update everything except for alpha crates and zebrad: -cargo release version --verbose --execute --allow-branch '*' --workspace --exclude zebrad --exclude zebra-scan --exclude zebra-grpc beta -# Due to a bug in cargo-release, we need to pass exact versions for alpha crates: -cargo release version --verbose --execute --allow-branch '*' --package zebra-scan 0.1.0-alpha.4 -cargo release version --verbose --execute --allow-branch '*' --package zebra-grpc 0.1.0-alpha.2 -# Update zebrad: -cargo release version --verbose --execute --allow-branch '*' --package zebrad patch # [ major | minor | patch ] -# Continue with the release process: -cargo release replace --verbose --execute --allow-branch '*' --package zebrad -cargo release commit --verbose --execute --allow-branch '*' +cargo release version --verbose --execute --allow-branch '*' -p patch # [ major | minor ] +cargo release replace --verbose --execute --allow-branch '*' -p ``` -Crate publishing is [automatically checked in CI](https://github.com/ZcashFoundation/zebra/actions/workflows/release-crates-io.yml) using "dry run" mode, however due to a bug in `cargo-release` we need to pass exact versions to the alpha crates: - -- [ ] Update `zebra-scan` and `zebra-grpc` alpha crates in the [release-crates-dry-run workflow script](https://github.com/ZcashFoundation/zebra/blob/main/.github/workflows/scripts/release-crates-dry-run.sh) -- [ ] Push the above version changes to the release branch. +- [ ] Update the crate `CHANGELOG.md` +- [ ] Commit and push the above version changes to the release branch. ## Update End of Support The end of support height is calculated from the current blockchain height: -- [ ] Find where the Zcash blockchain tip is now by using a [Zcash explorer](https://zcashblockexplorer.com/blocks) or other tool. +- [ ] Find where the Zcash blockchain tip is now by using a [Zcash Block Explorer](https://mainnet.zcashexplorer.app/) or other tool. - [ ] Replace `ESTIMATED_RELEASE_HEIGHT` in [`end_of_support.rs`](https://github.com/ZcashFoundation/zebra/blob/main/zebrad/src/components/sync/end_of_support.rs) with the height you estimate the release will be tagged.
@@ -141,8 +165,7 @@ The end of support height is calculated from the current blockchain height: ## Test the Pre-Release - [ ] Wait until the Docker binaries have been built on `main`, and the quick tests have passed: - - [ ] [ci-unit-tests-docker.yml](https://github.com/ZcashFoundation/zebra/actions/workflows/ci-unit-tests-docker.yml?query=branch%3Amain) - - [ ] [ci-integration-tests-gcp.yml](https://github.com/ZcashFoundation/zebra/actions/workflows/ci-integration-tests-gcp.yml?query=branch%3Amain) + - [ ] [ci-tests.yml](https://github.com/ZcashFoundation/zebra/actions/workflows/ci-tests.yml?query=branch%3Amain) - [ ] Wait until the [pre-release deployment machines have successfully launched](https://github.com/ZcashFoundation/zebra/actions/workflows/cd-deploy-nodes-gcp.yml?query=event%3Arelease) ## Publish Release @@ -151,15 +174,25 @@ The end of support height is calculated from the current blockchain height: ## Publish Crates -- [ ] [Run `cargo login`](https://github.com/ZcashFoundation/zebra/blob/doc-crate-own/book/src/dev/crate-owners.md#logging-in-to-cratesio) -- [ ] Run `cargo clean` in the zebra repo (optional) -- [ ] Publish the crates to crates.io: `cargo release publish --verbose --workspace --execute` +- [ ] [Run `cargo login`](https://zebra.zfnd.org/dev/crate-owners.html#logging-in-to-cratesio) +- [ ] It is recommended that the following step be run from a fresh checkout of + the repo, to avoid accidentally publishing files like e.g. logs that might + be lingering around +- [ ] Publish the crates to crates.io; edit the list to only include the crates that + have been changed, but keep their overall order: + +``` +for c in zebra-test tower-fallback zebra-chain tower-batch-control zebra-node-services zebra-script zebra-state zebra-consensus zebra-network zebra-rpc zebra-utils zebrad; do cargo release publish --verbose --execute -p $c; done +``` + - [ ] Check that Zebra can be installed from `crates.io`: - `cargo install --locked --force --version 1.minor.patch zebrad && ~/.cargo/bin/zebrad` + `cargo install --locked --force --version zebrad && ~/.cargo/bin/zebrad` and put the output in a comment on the PR. ## Publish Docker Images + - [ ] Wait for the [the Docker images to be published successfully](https://github.com/ZcashFoundation/zebra/actions/workflows/release-binaries.yml?query=event%3Arelease). +- [ ] Wait for the new tag in the [dockerhub zebra space](https://hub.docker.com/r/zfnd/zebra/tags) - [ ] Un-freeze the [`batched` queue](https://dashboard.mergify.com/github/ZcashFoundation/repo/zebra/queues) using Mergify. - [ ] Remove `do-not-merge` from the PRs you added it to diff --git a/.github/dependabot.yml b/.github/dependabot.yml index da2e1f1206a..5e49874de2b 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -3,15 +3,18 @@ updates: # Rust section - package-ecosystem: cargo directory: '/' + # Update only the lockfile. We shouldn't update Cargo.toml unless it's for + # a security issue, or if we need a new feature of the dependency. + versioning-strategy: lockfile-only # serde, clap, and other dependencies sometimes have multiple updates in a week schedule: - interval: weekly + interval: monthly day: monday timezone: America/New_York # Limit dependabot to 1 PR per reviewer open-pull-requests-limit: 6 labels: - - 'C-trivial' + - 'C-exclude-from-changelog' - 'A-rust' - 'A-dependencies' - 'P-Low :snowflake:' @@ -46,11 +49,11 @@ updates: timezone: America/New_York open-pull-requests-limit: 4 labels: - - 'C-trivial' + - 'C-exclude-from-changelog' - 'A-devops' - 'A-dependencies' - 'P-Low :snowflake:' groups: devops: patterns: - - "*" \ No newline at end of file + - "*" diff --git a/.github/mergify.yml b/.github/mergify.yml index a0a5b7e282d..95e464822b3 100644 --- a/.github/mergify.yml +++ b/.github/mergify.yml @@ -3,97 +3,67 @@ # This file can be edited and validated using: # https://dashboard.mergify.com/github/ZcashFoundation/repo/zebra/config-editor -queue_rules: - - name: urgent +# Set the maximum number of PRs that can be checked in parallel in a queue +merge_queue: + max_parallel_checks: 5 + +# Provides a means to set configuration values that act as fallbacks +# for queue_rules and pull_request_rules +defaults: + # Define our default queue rules + queue_rule: # Allow to update/rebase the original pull request if possible to check its mergeability, # and it does not create a draft PR if not needed allow_inplace_checks: True - allow_checks_interruption: False - speculative_checks: 1 - batch_size: 8 - # Wait a short time to embark hotfixes together in a merge train - batch_max_wait_time: "2 minutes" - conditions: + # Wait for about 10% of the time it takes Rust PRs to run CI (~1h) + batch_max_wait_time: "10 minutes" + queue_conditions: # Mergify automatically applies status check, approval, and conversation rules, # which are the same as the GitHub main branch protection rules # https://docs.mergify.com/conditions/#about-branch-protection - base=main - - - name: batched - allow_inplace_checks: True - allow_checks_interruption: True - speculative_checks: 1 - batch_size: 20 - # Wait for about 10% of the time it takes Rust PRs to run CI (3h) - batch_max_wait_time: "20 minutes" - conditions: - - base=main - -# These rules are checked in order, the first one to be satisfied applies -pull_request_rules: - - name: move to urgent queue when CI passes with multiple reviews - conditions: - # This queue handles a PR if it: - # has multiple approving reviewers - - "#approved-reviews-by>=2" - # is labeled with Critical priority - - 'label~=^P-Critical' - # and satisfies the standard merge conditions: - # targets main - - base=main # is not in draft - -draft # does not include the do-not-merge label - label!=do-not-merge - actions: - queue: - name: urgent - method: squash + # has at least one approving reviewer + - "#approved-reviews-by >= 1" - - name: move to urgent queue when CI passes with 1 review - conditions: - # This queue handles a PR if it: - # has at least one approving reviewer (branch protection rule) - # does not need extra reviews - - 'label!=extra-reviews' + +# Allows to define the rules that reign over our merge queues +queue_rules: + - name: urgent + batch_size: 5 + # Wait a short time to embark hotfixes together in a merge train + batch_max_wait_time: "2 minutes" + queue_conditions: # is labeled with Critical priority - 'label~=^P-Critical' - # and satisfies the standard merge conditions: - - base=main - - -draft - - label!=do-not-merge + + - name: batched + batch_size: 20 + +pull_request_rules: + - name: move to any queue if GitHub Rulesets are satisfied + conditions: [] actions: queue: - name: urgent - method: squash - - name: move to batched queue when CI passes with multiple reviews +# Rules that will determine which priority a pull request has when entering +# our merge queue +# +# These rules are checked in order, the first one to be satisfied applies +priority_rules: + - name: urgent conditions: - # This queue handles a PR if it: - # has multiple approving reviewers - - "#approved-reviews-by>=2" - # is labeled with any other priority (rules are checked in order) - # and satisfies the standard merge conditions: - - base=main - - -draft - - label!=do-not-merge - actions: - queue: - name: batched - method: squash + # is labeled with Critical priority + - 'label~=^P-Critical' + allow_checks_interruption: true + priority: high - - name: move to batched queue when CI passes with 1 review + - name: low conditions: - # This queue handles a PR if it: - # has at least one approving reviewer (branch protection rule) - # does not need extra reviews - - 'label!=extra-reviews' - # is labeled with any other priority (rules are checked in order) - # and satisfies the standard merge conditions: - - base=main - - -draft - - label!=do-not-merge - actions: - queue: - name: batched - method: squash + # is labeled with Optional or Low priority + - 'label~=^P-(Optional|Low)' + allow_checks_interruption: true + priority: low diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 2c96aae4c07..70ebc94f6df 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,54 +1,43 @@ -## Motivation - -### Specifications & References +## Motivation ## Solution - + ### Tests +### Specifications & References + + + ### Follow-up Work -### PR Author's Checklist +### PR Checklist - + -- [ ] The PR name will make sense to users. -- [ ] The PR provides a CHANGELOG summary. +- [ ] The PR name is suitable for the release notes. - [ ] The solution is tested. - [ ] The documentation is up to date. -- [ ] The PR has a priority label. - -### PR Reviewer's Checklist - - - -- [ ] The PR Author's checklist is complete. -- [ ] The PR resolves the issue. - diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml index f291980b693..1c9c002609b 100644 --- a/.github/release-drafter.yml +++ b/.github/release-drafter.yml @@ -6,123 +6,91 @@ # Automatically label PRs based on their branch, title, or changed files. # This helps categorise PRs in the CHANGELOG. autolabeler: - - label: 'C-security' + - label: "C-security" branch: - - '/secur/i' + - "/secur/i" title: - - '/secur/i' - - '/crash/i' - - '/destr/i' - - '/unsafe/i' - - label: 'C-deprecated' + - "/secur/i" + - "/crash/i" + - "/destr/i" + - "/unsafe/i" + - label: "C-deprecated" branch: - - '/deprecat/i' + - "/deprecat/i" title: - - '/deprecat/i' - - label: 'extra-reviews' + - "/deprecat/i" + - label: "extra-reviews" branch: - - '/remov/i' - - '/deprecat/i' + - "/remov/i" + - "/deprecat/i" title: - - '/remov/i' - - '/deprecat/i' - - '/crash/i' - - '/destr/i' - - '/unsafe/i' - - label: 'C-feature' + - "/remov/i" + - "/deprecat/i" + - "/crash/i" + - "/destr/i" + - "/unsafe/i" + - label: "C-feature" branch: - - '/feat/i' + - "/feat/i" title: - - '/feat/i' - - label: 'C-bug' + - "/feat/i" + - label: "C-bug" branch: - - '/bug/i' + - "/bug/i" title: - - '/bug/i' - # Changes that are almost always trivial for users - - label: 'C-trivial' - branch: - - '/clean/i' - - '/chore/i' - - '/clippy/i' - - '/test/i' - title: - - '/clean/i' - - '/chore/i' - - '/clippy/i' - - '/test/i' - - '/(ci)/i' - - '/(cd)/i' - - '/job/i' - - '/patch/i' - - '/actions/i' - files: - # Regular changes that don't need to go in the CHANGELOG - - 'CHANGELOG.md' - - 'zebra-consensus/src/checkpoint/*-checkpoints.txt' - # Developer-only changes - - '.gitignore' - - '.dockerignore' - # Test-only changes - - 'zebra-test' - - '.cargo/config.toml' - - 'clippy.toml' - # CI-only changes - - '.github' - - '.codespellrc' - - 'codecov.yml' - - 'deny.toml' + - "/bug/i" # The release name, tag, and settings for the draft CHANGELOG. -name-template: 'Zebra $RESOLVED_VERSION' -tag-template: 'v$RESOLVED_VERSION' -tag-prefix: 'v' -prerelease: true +name-template: "Zebra $RESOLVED_VERSION" +tag-template: "v$RESOLVED_VERSION" +tag-prefix: "v" +# Do not mark the draft release as a pre-release +prerelease: false +# Do not include pre-releases in the draft release +include-pre-releases: false # Categories in rough order of importance to users. # Based on https://keepachangelog.com/en/1.0.0/ -category-template: '### $TITLE' +category-template: "### $TITLE" categories: - - title: 'Security' + - title: "Security" labels: - - 'C-security' + - "C-security" # Other labels that are usually security issues - - 'I-invalid-data' - - 'I-consensus' - - 'I-crash' - - 'I-destructive' - - 'I-hang' - - 'I-lose-funds' - - 'I-privacy' - - 'I-remote-node-overload' - - 'I-unbounded-growth' - - 'I-memory-safety' - - title: 'Removed' + - "I-invalid-data" + - "I-consensus" + - "I-crash" + - "I-destructive" + - "I-hang" + - "I-lose-funds" + - "I-privacy" + - "I-remote-node-overload" + - "I-unbounded-growth" + - "I-memory-safety" + - title: "Removed" labels: - - 'C-removal' - - title: 'Deprecated' + - "C-removal" + - title: "Deprecated" labels: - - 'C-deprecation' + - "C-deprecation" # TODO: when release drafter has per-category templates, add this to the Deprecated category template: # 'These features might be removed in Zebra $NEXT_MINOR_VERSION' - - title: 'Added' + - title: "Added" labels: - - 'C-feature' - - title: 'Changed' + - "C-feature" + - title: "Changed" labels: - - 'C-enhancement' - - title: 'Fixed' + - "C-enhancement" + - title: "Fixed" labels: - - 'C-bug' + - "C-bug" # Other labels that are usually bugs - - 'I-build-fail' - - 'I-integration-fail' - - 'I-panic' - # TODO: if we're happy with the trivial PRs, use "exclude-labels:" instead - - title: 'Trivial *TODO:* put this in a PR comment, not the CHANGELOG' + - "I-build-fail" + - "I-integration-fail" + - "I-panic" + - title: "Excluded *TODO:* put this in a PR comment, not the CHANGELOG" labels: - - 'C-trivial' - - 'C-cleanup' + - "C-exclude-from-changelog" # The next release's $RESOLVED_VERSION, based on the labels of the PRs in the release. # @@ -136,15 +104,15 @@ version-resolver: # - # network upgrade release PRs minor: labels: - - 'C-feature' - - 'C-breaking' - - 'C-removal' - - 'C-deprecation' + - "C-feature" + - "C-breaking" + - "C-removal" + - "C-deprecation" # We increment the patch version for every release default: patch # How PR names get turned into CHANGELOG entries. -change-template: '- $TITLE ([#$NUMBER]($URL))' +change-template: "- $TITLE ([#$NUMBER]($URL))" sort-by: title sort-direction: ascending # Characters escaped when converting PR titles to CHANGELOG entries. @@ -153,31 +121,30 @@ change-title-escapes: '\<*_&#@' # Strip PR series numbers, leading spaces, and conventional commit prefixes from PR titles. replacers: - search: '/- [0-9\. ]*([a-zA-Z0-9\(\)!]+:)?/' - replace: '- ' + replace: "- " # The list of contributors to each release. exclude-contributors: - - 'dependabot' # 'dependabot[bot]' - - 'mergifyio' # 'mergify[bot]' + - "dependabot" # 'dependabot[bot]' + - "mergifyio" # 'mergify[bot]' # The template for the draft CHANGELOG. template: | ## [Zebra $RESOLVED_VERSION](https://github.com/ZcashFoundation/zebra/releases/tag/v$RESOLVED_VERSION) - *TODO*: date - + This release *TODO*: a summary of the significant user-visible changes in the release - + ### Breaking Changes - + This release has the following breaking changes: - *TODO*: Check the `Removed` and `Deprecated` sections for any breaking changes - *TODO*: Add a short description of the user impact of each breaking change, and any actions users need to take - + $CHANGES - + ### Contributors - + Thank you to everyone who contributed to this release, we couldn't make Zebra without you: $CONTRIBUTORS - - + # the trailing newlines in the template are deliberate diff --git a/.github/workflows/README.md b/.github/workflows/README.md new file mode 100644 index 00000000000..2d07f985b9c --- /dev/null +++ b/.github/workflows/README.md @@ -0,0 +1,359 @@ +# Zebra CI/CD Architecture + +This document provides a comprehensive overview of Zebra's Continuous Integration and Continuous Deployment (CI/CD) system. It serves as a guide for contributors, maintainers, and new team members. + +## Table of Contents + +1. [System Overview](#system-overview) +2. [CI/CD Workflow Diagram](#cicd-workflow-diagram) +3. [Core Infrastructure](#core-infrastructure) +4. [Workflow Organization](#workflow-organization) +5. [Test Execution Strategy](#test-execution-strategy) +6. [Infrastructure Details](#infrastructure-details) +7. [Best Practices](#best-practices) +8. [Known Issues](#known-issues) + +## System Overview + +Zebra's CI/CD system is built on GitHub Actions, providing a unified platform for automation. The system ensures code quality, maintains stability, and automates routine tasks through specialized workflows. + +## CI/CD Workflow Diagram + +Below is a Mermaid diagram illustrating how our CI workflows relate to each other, with a focus on parallel execution patterns and job dependencies. The diagram shows the main CI pipeline, integration test flow, unit test flow, underlying infrastructure, and the various triggers that initiate the pipeline. + +```mermaid +graph TB + %% Define Triggers subgraph with parallel triggers + subgraph "Triggers" + direction TB + P[Pull Request] & Q[Push to main] & R[Weekly Schedule] & S[Manual Trigger] & T[Merge Queue] + end + + %% Main CI Pipeline with parallel flows after build + subgraph "Main CI Pipeline" + direction TB + A[ci-tests.yml] + B[sub-build-docker-image.yml] + A --> B + end + + %% Infrastructure dependencies + subgraph "Infrastructure" + direction TB + M[Docker Build Cloud] + N[GCP Resources] + O[GitHub Runners] + end + + %% Unit Test Flow with parallel test execution + subgraph "Unit Test Flow" + direction TB + C[sub-ci-unit-tests-docker.yml] + H[test-all] & I[test-fake-activation-heights] & J[test-empty-sync] & K[test-lightwalletd-integration] & L[test-docker-configurations] + C --> H + C --> I + C --> J + C --> K + C --> L + end + + %% Integration Test Flow with some parallel and some sequential steps + subgraph "Integration Test Flow" + direction TB + D[sub-ci-integration-tests-gcp.yml] + E[sub-find-cached-disks.yml] + F[sub-deploy-integration-tests-gcp.yml] + G[sub-test-zebra-config.yml] + D --> E + D --> F + E --> F + F --> G + end + + %% Connect triggers to main pipeline + P --> A + Q --> A + R --> A + S --> A + T --> A + + %% Connect infrastructure to respective components + M --> B + N --> D + O --> C + + %% Connect main pipeline to test flows + B --> C + B --> D + + %% Style definitions + classDef primary fill:#2374ab,stroke:#2374ab,color:white + classDef secondary fill:#48a9a6,stroke:#48a9a6,color:white + classDef infra fill:#4b4e6d,stroke:#4b4e6d,color:white + classDef trigger fill:#95a5a6,stroke:#95a5a6,color:white + + %% Apply styles + class A,B primary + class C,D,E,F,G secondary + class H,I,J,K,L secondary + class M,N,O infra + class P,Q,R,S,T trigger +``` + +*The diagram above illustrates the parallel execution patterns in our CI/CD system. All triggers can initiate the pipeline concurrently, unit tests run in parallel after the Docker image build, and integration tests follow a mix of parallel and sequential steps. The infrastructure components support their respective workflow parts concurrently.* + +## Core Infrastructure + +### 1. GitHub Actions + +- Primary CI/CD platform +- Workflow automation and orchestration +- Integration with other services + +### 2. Infrastructure as Code + +- Uses [Cloud Foundation Fabric](https://github.com/ZcashFoundation/cloud-foundation-fabric) for GCP infrastructure +- Terraform-based architecture, networking, and permissions +- Resources (VMs, Disks, Images, etc.) deployed via GitHub Actions pipelines + +### 3. Build and Registry Services + +#### Docker-based Testing + +- Most tests run in containers defined by our [Dockerfile](http://../../docker/Dockerfile) +- The [entrypoint script](http://../../docker/entrypoint.sh) manages: + - Test execution + - Environment configuration + - Resource cleanup + +#### [Docker Build Cloud](https://www.docker.com/products/build-cloud/) + +- Optimized build times (~10 min for non-cached, ~30 sec for cached) +- More efficient than GitHub Runners +- Addresses [Rust caching limitations](https://github.com/ZcashFoundation/zebra/issues/6169#issuecomment-1712776391) + +#### Container Registries + +- Google Cloud Registry: Internal CI artifacts +- [Docker Hub](https://hub.docker.com/): Public release artifacts +- Ensures proper artifact distribution + +### 4. Test Infrastructure + +#### GitHub-hosted Runners + +- All Unit Tests jobs +- Standard CI/CD operations +- Limited to 6-hour runtime + +#### Self-hosted Runners (GKE) + +- All Integration Tests jobs (deployed to GCP) +- Support for tests exceeding 6 hours +- Extended logging capabilities +- Full GitHub Actions console integration + +**Note**: Self-hosted Runners are just used to keep the logs running in the GitHub Actions UI for over 6 hours, the Integration Tests are not run in the Self-hosted Runner itself, but in the deployed VMs in GCP through GitHub Actions. + +### 5. Queue Management + +[Mergify](https://mergify.yml) + +- Automated PR merging and queue-based testing +- Priority management +- Ensures code quality before merge +- See our [`.mergify.yml`](http://../../.mergify.yml) for configuration + +## Workflow Organization + +### Main Workflows + +- **CI Tests** (`ci-*.yml`): Core testing workflows + - Unit tests + - Integration tests + - Code coverage + - Linting +- **CD Deployments** (`cd-*.yml`): Deployment workflows + - Node deployment to GCP + - Documentation deployment +- **Release Management** (`release-*.yml`): Version and release workflows + +### Supporting Workflows + +- **Sub-workflows** (`sub-*.yml`): Reusable workflow components + - Docker image building + - Test configurations + - GCP resource management +- **Patch Workflows** (`*.patch.yml`, `*.patch-external.yml`): Handle GitHub Actions limitations for required checks + +### Patch Workflows Rationale + +Our use of patch workflows (`.patch.yml` and `.patch-external.yml`) is a workaround for a [known limitation in GitHub Actions](https://github.com/orgs/community/discussions/44490) regarding path filters and required checks. When a workflow is marked as required for PR merging: + +1. **Path Filtering Limitation**: GitHub Actions does not properly handle the case where a required workflow is skipped due to path filters. Instead of marking the check as "skipped" or "passed", it remains in a "pending" state, blocking PR merges. + +2. **Our Solution**: We maintain parallel "patch" workflows that: + + - Run without path filters + - Contain minimal steps that always pass when the original workflow would have been skipped + - Allow PRs to merge when changes don't affect relevant paths + +3. **Impact**: + + - Doubled number of workflow files to maintain + - Additional complexity in workflow management + - Extra status checks in PR UI + +## Test Execution Strategy + +### Test Orchestration + +Our test execution is centralized through our Docker [entrypoint script](http://../../docker/entrypoint.sh), providing a unified way to run tests both in CI and locally. + +#### Environment Variable-driven Testing + +```bash +# Full test suite +docker run --rm -e RUN_ALL_TESTS=1 zebra-tests + +# Specific test suites +docker run --rm -e LIGHTWALLETD_INTEGRATION=1 zebra-tests +``` + +#### Test Categories + +- Full suite (`RUN_ALL_TESTS`) +- Integration tests (`LIGHTWALLETD_INTEGRATION`) +- Network sync (`SYNC_LARGE_CHECKPOINTS_EMPTY`, `SYNC_UPDATE`) +- State management (`SYNC_TO_MANDATORY_CHECKPOINT`) + +### Pull Request Testing + +#### Continuous Validation + +- Tests run automatically on each commit +- Contributors get immediate feedback on their changes +- Regressions are caught early in the development process +- Reduces manual testing burden on reviewers + +#### Fast Feedback Loop + +- Linting: Code style and formatting +- Unit tests: Function and component behavior +- Basic integration tests: Core functionality +- All results are reported directly in the PR interface + +#### Deep Validation + +- Full integration test suite +- Cross-platform compatibility checks +- Performance benchmarks +- State management validation + +### Scheduled Testing + +Weekly runs include: + +- Full Mainnet synchronization +- Extended integration suites +- Resource cleanup + +## Infrastructure Details + +### VM-based Test Infrastructure + +#### Test-specific Requirements + +- Some integration tests need a fully synced network +- Certain tests validate against specific chain heights +- Network state persistence between test runs +- Not all tests require this infrastructure - many run in standard containers + +#### State Management Complexity + +- **Creation**: Initial sync and state building for test environments +- **Versioning**: Multiple state versions for different test scenarios +- **Caching**: Reuse of existing states to avoid re-sync +- **Attachment**: Dynamic VM disk mounting for tests +- **Cleanup**: Automated state and resource cleanup + +#### Infrastructure Implications + +- GCP VM infrastructure for state-dependent tests +- Complex disk image management for test states +- State versioning and compatibility checks +- Resource lifecycle management + +#### Future Considerations + +- Potential migration of state-dependent tests to container-native environments +- Would require solving state persistence in Kubernetes +- Need to balance containerization benefits with test requirements +- Opportunity to reduce infrastructure complexity + +## Best Practices + +### For Contributors + +#### Local Testing + +```bash +# Build and run tests +docker build -t zebra-tests --target tests . +docker run --rm zebra-tests +``` + +#### PR Guidelines + +- Use descriptive labels +- Mark as draft when needed +- Address test failures + +### For Maintainers + +#### Workflow Maintenance + +- Regular review of workflow efficiency +- Update resource allocations as needed +- Monitor test execution times + +#### Security Considerations + +- Regular secret rotation +- Access control review +- Dependency updates + +## Known Issues + +### External Contributor Limitations + +#### GCP Dependencies + +- Most CI workflows depend on Google Cloud Platform resources +- Docker artifacts and VM images are tied to GCP +- External contributors cannot run full CI suite in their forks +- Integration tests require GCP infrastructure access +- This particularly impacts: + - Integration test execution + - Node deployment testing + - State storage and caching validation + +#### GitHub Actions Variables Restriction + +- Due to a [GitHub Actions limitation](https://github.com/orgs/community/discussions/44322), workflows in forked repositories cannot access repository variables +- This affects external contributors' ability to run CI workflows +- Required configuration values are not available in fork-based PRs +- Currently no workaround available from GitHub +- Impact on external contributors: + - Cannot run workflows requiring GCP credentials + - Unable to access configuration variables + - Limited ability to test infrastructure changes + +### Mitigation Through Mergify + +- When external PRs enter the merge queue, they are tested with full access to variables and resources +- All CI workflows run in the context of our repository, not the fork +- This provides a safety net, ensuring no untested code reaches production +- External contributors can still get feedback through code review before their changes are tested in the queue + +These safeguards help maintain code quality while working around the platform limitations for external contributions. diff --git a/.github/workflows/cd-deploy-nodes-gcp.patch-external.yml.disabled b/.github/workflows/cd-deploy-nodes-gcp.patch-external.yml.disabled index 8381f0011a0..da0153bc92b 100644 --- a/.github/workflows/cd-deploy-nodes-gcp.patch-external.yml.disabled +++ b/.github/workflows/cd-deploy-nodes-gcp.patch-external.yml.disabled @@ -1,45 +1,29 @@ # Workflow patches for skipping Google Cloud CD deployments on PRs from external repositories. name: Deploy Nodes to GCP -# Run on PRs from external repositories, let them pass, and then Mergify will check them. +# Run on PRs from external repositories, let them pass, and then GitHub's Merge Queue will check them. # GitHub doesn't support filtering workflows by source branch names, so we have to do it for each # job. on: pull_request: -# IMPORTANT -# -# The job names in `cd-deploy-nodes-gcp.yml`, `cd-deploy-nodes-gcp.patch.yml` and -# `cd-deploy-nodes-gcp.patch-external.yml` must be kept in sync. +#! IMPORTANT +#! +#! The job names in `cd-deploy-nodes-gcp.yml`, `cd-deploy-nodes-gcp.patch.yml` and +#! `cd-deploy-nodes-gcp.patch-external.yml` must be kept in sync. jobs: # We don't patch the testnet job, because testnet isn't required to merge (it's too unstable) - build: - name: Build CD Docker / Build images - # Only run on PRs from external repositories, skipping ZF branches and tags. - if: ${{ startsWith(github.event_name, 'pull') && github.event.pull_request.head.repo.fork }} - runs-on: ubuntu-latest - steps: - - run: 'echo "Skipping job on fork"' - - test-configuration-file: - name: Test CD default Docker config file / Test default-conf in Docker - # This dependency allows all these jobs to depend on a single condition, making it easier to - # change. - needs: build + get-disk-name: + name: Get disk name / Get Mainnet cached disk + if: ${{ (github.event_name != 'release' && !(github.event.pull_request.head.repo.fork)) && (github.event_name != 'workflow_dispatch' || inputs.need_cached_disk) }} runs-on: ubuntu-latest steps: - run: 'echo "Skipping job on fork"' - test-configuration-file-testnet: - name: Test CD testnet Docker config file / Test default-conf in Docker - needs: build - runs-on: ubuntu-latest - steps: - - run: 'echo "Skipping job on fork"' - - test-zebra-conf-path: - name: Test CD custom Docker config file / Test custom-conf in Docker - needs: build + build: + name: Build CD Docker / Build images + # Only run on PRs from external repositories, skipping ZF branches and tags. runs-on: ubuntu-latest + if: ${{ github.event_name == 'pull_request' && !github.event.pull_request.head.repo.fork }} steps: - run: 'echo "Skipping job on fork"' diff --git a/.github/workflows/cd-deploy-nodes-gcp.patch.yml.disabled b/.github/workflows/cd-deploy-nodes-gcp.patch.yml.disabled index fb963ec3726..8ca9804f4ed 100644 --- a/.github/workflows/cd-deploy-nodes-gcp.patch.yml.disabled +++ b/.github/workflows/cd-deploy-nodes-gcp.patch.yml.disabled @@ -22,33 +22,22 @@ on: - '.github/workflows/cd-deploy-nodes-gcp.yml' - '.github/workflows/sub-build-docker-image.yml' -# IMPORTANT -# -# The job names in `cd-deploy-nodes-gcp.yml`, `cd-deploy-nodes-gcp.patch.yml` and -# `cd-deploy-nodes-gcp.patch-external.yml` must be kept in sync. +#! IMPORTANT +#! +#! The job names in `cd-deploy-nodes-gcp.yml`, `cd-deploy-nodes-gcp.patch.yml` and +#! `cd-deploy-nodes-gcp.patch-external.yml` must be kept in sync. jobs: # We don't patch the testnet job, because testnet isn't required to merge (it's too unstable) - build: - name: Build CD Docker / Build images + get-disk-name: + name: Get disk name / Get Mainnet cached disk runs-on: ubuntu-latest + if: ${{ (github.event_name != 'release' && !(github.event.pull_request.head.repo.fork)) && (github.event_name != 'workflow_dispatch' || inputs.need_cached_disk) }} steps: - run: 'echo "No build required"' - test-configuration-file: - name: Test CD default Docker config file / Test default-conf in Docker - runs-on: ubuntu-latest - steps: - - run: 'echo "No build required"' - - test-configuration-file-testnet: - name: Test CD testnet Docker config file / Test default-conf in Docker - runs-on: ubuntu-latest - steps: - - run: 'echo "No build required"' - - - test-zebra-conf-path: - name: Test CD custom Docker config file / Test custom-conf in Docker + build: + name: Build CD Docker / Build images runs-on: ubuntu-latest + if: ${{ github.event_name == 'pull_request' && !github.event.pull_request.head.repo.fork }} steps: - run: 'echo "No build required"' diff --git a/.github/workflows/cd-deploy-nodes-gcp.yml.disabled b/.github/workflows/cd-deploy-nodes-gcp.yml.disabled index 1333816530f..78c505c7d7d 100644 --- a/.github/workflows/cd-deploy-nodes-gcp.yml.disabled +++ b/.github/workflows/cd-deploy-nodes-gcp.yml.disabled @@ -1,12 +1,10 @@ # Google Cloud node deployments and tests that run when Rust code or dependencies are modified, # but only on PRs from the ZcashFoundation/zebra repository. -# (External PRs are tested/deployed by mergify.) +# (External PRs are tested/deployed by GitHub's Merge Queue.) # # 1. `versioning`: Extracts the major version from the release semver. Useful for segregating instances based on major versions. # 2. `build`: Builds a Docker image named `zebrad` with the necessary tags derived from Git. -# 3. `test-configuration-file`: Validates Zebra using the default config with the latest version. -# 4. `test-configuration-file-testnet`: Tests the Docker image for the testnet configuration. -# 5. `test-zebra-conf-path`: Verifies Zebra with a custom Docker config file. +# 3. `test-docker-configurations`: Validates all Zebra Docker configurations by running a matrix of configuration tests. # 6. `deploy-nodes`: Deploys Managed Instance Groups (MiGs) for Mainnet and Testnet. If triggered by main branch pushes, it always replaces the MiG. For releases, MiGs are replaced only if deploying the same major version; otherwise, a new major version is deployed. # 7. `deploy-instance`: Deploys a single node in a specified GCP zone for testing specific commits. Instances from this job aren't auto-replaced or deleted. # @@ -27,11 +25,14 @@ concurrency: cancel-in-progress: ${{ github.event_name == 'pull_request' }} on: + merge_group: + types: [checks_requested] + workflow_dispatch: inputs: network: default: Mainnet - description: 'Network to deploy: Mainnet or Testnet' + description: "Network to deploy: Mainnet or Testnet" required: true type: choice options: @@ -39,71 +40,66 @@ on: - Testnet cached_disk_type: default: tip - description: 'Type of cached disk to use' + description: "Type of cached disk to use" required: true type: choice options: - tip - checkpoint - prefer_main_cached_state: - default: false - description: 'Prefer cached state from the main branch' - required: false - type: boolean - no_cached_disk: - default: false - description: 'Do not use a cached state disk' + need_cached_disk: + default: true + description: "Use a cached state disk" required: false type: boolean no_cache: - description: 'Disable the Docker cache for this build' + description: "Disable the Docker cache for this build" required: false type: boolean default: false log_file: - default: '' - description: 'Log to a file path rather than standard output' + default: "" + description: "Log to a file path rather than standard output" push: - # Skip main branch updates where Rust code and dependencies aren't modified. - branches: - - main - paths: - # code and tests - - '**/*.rs' - # hard-coded checkpoints and proptest regressions - - '**/*.txt' - # dependencies - - '**/Cargo.toml' - - '**/Cargo.lock' - # configuration files - - '.cargo/config.toml' - - '**/clippy.toml' - # workflow definitions - - 'docker/**' - - '.dockerignore' - - '.github/workflows/cd-deploy-nodes-gcp.yml' - - '.github/workflows/sub-build-docker-image.yml' + # Skip main branch updates where Rust code and dependencies aren't modified. + branches: + - main + paths: + # code and tests + - "**/*.rs" + # hard-coded checkpoints and proptest regressions + - "**/*.txt" + # dependencies + - "**/Cargo.toml" + - "**/Cargo.lock" + # configuration files + - ".cargo/config.toml" + - "**/clippy.toml" + # workflow definitions + - "docker/**" + - ".dockerignore" + - ".github/workflows/cd-deploy-nodes-gcp.yml" + - ".github/workflows/sub-build-docker-image.yml" # Only runs the Docker image tests, doesn't deploy any instances pull_request: # Skip PRs where Rust code and dependencies aren't modified. paths: # code and tests - - '**/*.rs' + - "**/*.rs" # hard-coded checkpoints and proptest regressions - - '**/*.txt' + - "**/*.txt" # dependencies - - '**/Cargo.toml' - - '**/Cargo.lock' + - "**/Cargo.toml" + - "**/Cargo.lock" # configuration files - - '.cargo/config.toml' - - '**/clippy.toml' + - ".cargo/config.toml" + - "**/clippy.toml" # workflow definitions - - 'docker/**' - - '.dockerignore' - - '.github/workflows/cd-deploy-nodes-gcp.yml' - - '.github/workflows/sub-build-docker-image.yml' + - "docker/**" + - ".dockerignore" + - ".github/workflows/cd-deploy-nodes-gcp.yml" + - ".github/workflows/sub-build-docker-image.yml" release: types: @@ -141,75 +137,47 @@ jobs: id: set run: echo "major_version=${{ steps.get.outputs.result }}" >> "$GITHUB_OUTPUT" + # Finds a cached state disk for zebra + # + # Passes the disk name to subsequent jobs using `cached_disk_name` output + # + # For push events, this job always runs. + # For workflow_dispatch events, it runs only if inputs.need_cached_disk is true. + # PRs from forked repositories are skipped. + get-disk-name: + name: Get disk name + uses: ./.github/workflows/sub-find-cached-disks.yml + if: ${{ !(github.event.pull_request.head.repo.fork) && (github.event_name != 'workflow_dispatch' || inputs.need_cached_disk) }} + with: + network: ${{ inputs.network || vars.ZCASH_NETWORK }} + disk_prefix: zebrad-cache + disk_suffix: ${{ inputs.cached_disk_type || 'tip' }} + # Each time this workflow is executed, a build will be triggered to create a new image # with the corresponding tags using information from Git # # The image will be commonly named `zebrad:` build: name: Build CD Docker - # Skip PRs from external repositories, let them pass, and then Mergify will check them. - # This workflow also runs on release tags, the event name check will run it on releases. - if: ${{ !startsWith(github.event_name, 'pull') || !github.event.pull_request.head.repo.fork }} uses: ./.github/workflows/sub-build-docker-image.yml + if: ${{ github.event_name == 'pull_request' && !github.event.pull_request.head.repo.fork }} with: dockerfile_path: ./docker/Dockerfile dockerfile_target: runtime image_name: zebrad no_cache: ${{ inputs.no_cache || false }} rust_log: info + features: ${{ format('{0} {1}', vars.RUST_PROD_FEATURES, vars.RUST_TEST_FEATURES) }} # This step needs access to Docker Hub secrets to run successfully secrets: inherit - # Test that Zebra works using the default config with the latest Zebra version. - test-configuration-file: - name: Test CD default Docker config file - needs: build - uses: ./.github/workflows/sub-test-zebra-config.yml - with: - test_id: 'default-conf' - docker_image: ${{ vars.GAR_BASE }}/zebrad@${{ needs.build.outputs.image_digest }} - grep_patterns: '-e "net.*=.*Main.*estimated progress to chain tip.*BeforeOverwinter"' - test_variables: '-e NETWORK' - network: 'Mainnet' - - # Test reconfiguring the docker image for testnet. - test-configuration-file-testnet: - name: Test CD testnet Docker config file - needs: build - # Make sure Zebra can sync the genesis block on testnet - uses: ./.github/workflows/sub-test-zebra-config.yml - with: - test_id: 'testnet-conf' - docker_image: ${{ vars.GAR_BASE }}/zebrad@${{ needs.build.outputs.image_digest }} - grep_patterns: '-e "net.*=.*Test.*estimated progress to chain tip.*Genesis" -e "net.*=.*Test.*estimated progress to chain tip.*BeforeOverwinter"' - test_variables: '-e NETWORK' - network: 'Testnet' - - # Test that Zebra works using $ZEBRA_CONF_PATH config - test-zebra-conf-path: - name: Test CD custom Docker config file + # Run a matrix of configuration tests against the Docker image + test-docker-configurations: + name: Test Zebra Docker configurations needs: build uses: ./.github/workflows/sub-test-zebra-config.yml with: - test_id: 'custom-conf' docker_image: ${{ vars.GAR_BASE }}/zebrad@${{ needs.build.outputs.image_digest }} - grep_patterns: '-e "loaded zebrad config.*config_path.*=.*v1.0.0-rc.2.toml"' - test_variables: '-e NETWORK -e ZEBRA_CONF_PATH="zebrad/tests/common/configs/v1.0.0-rc.2.toml"' - network: ${{ inputs.network || vars.ZCASH_NETWORK }} - - # Finds a cached state disk for zebra - # - # Passes the disk name to subsequent jobs using `cached_disk_name` output - # - get-disk-name: - name: Get disk name - uses: ./.github/workflows/sub-find-cached-disks.yml - if: ${{ !inputs.no_cached_disk }} - with: - network: ${{ inputs.network || vars.ZCASH_NETWORK }} - disk_prefix: zebrad-cache - disk_suffix: ${{ inputs.cached_disk_type || 'tip' }} - prefer_main_cached_state: ${{ inputs.prefer_main_cached_state || (github.event_name == 'push' && github.ref_name == 'main' && true) || false }} # Deploy Managed Instance Groups (MiGs) for Mainnet and Testnet, # with one node in the configured GCP region. @@ -226,28 +194,53 @@ jobs: # Runs: # - on every push to the `main` branch # - on every release, when it's published + # - on workflow_dispatch for manual deployments + + # Determine which networks to deploy based on the trigger + set-matrix: + runs-on: ubuntu-latest + outputs: + networks: ${{ steps.set-networks.outputs.matrix }} + steps: + - id: set-networks + run: | + if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then + # Manually triggered deployment: output a valid JSON array with the single chosen network. + echo "matrix=[\"${{ inputs.network }}\"]" >> $GITHUB_OUTPUT + else + echo 'matrix=["Mainnet","Testnet"]' >> $GITHUB_OUTPUT + fi + deploy-nodes: strategy: matrix: - network: [Mainnet, Testnet] + network: ${{ fromJSON(needs.set-matrix.outputs.networks) }} name: Deploy ${{ matrix.network }} nodes - needs: [ build, versioning, test-configuration-file, test-zebra-conf-path, get-disk-name ] + needs: + [ + set-matrix, + build, + versioning, + test-docker-configurations, + get-disk-name, + ] runs-on: ubuntu-latest timeout-minutes: 60 env: CACHED_DISK_NAME: ${{ needs.get-disk-name.outputs.cached_disk_name }} + environment: ${{ github.event_name == 'release' && 'prod' || 'dev' }} permissions: - contents: 'read' - id-token: 'write' - if: ${{ !cancelled() && !failure() && ((github.event_name == 'push' && github.ref_name == 'main') || github.event_name == 'release') }} + contents: "read" + id-token: "write" + if: ${{ !cancelled() && !failure() && needs.build.result == 'success' && github.repository_owner == 'ZcashFoundation' && ((github.event_name == 'push' && github.ref_name == 'main') || github.event_name == 'release' || github.event_name == 'workflow_dispatch') }} steps: - - uses: actions/checkout@v4.2.1 + - uses: actions/checkout@v4.2.2 with: persist-credentials: false - name: Inject slug/short variables - uses: rlespinasse/github-slug-action@v4 + uses: rlespinasse/github-slug-action@v5 with: short-length: 7 @@ -256,7 +249,7 @@ jobs: # Labels in GCP are required to be in lowercase, but the blockchain network # uses sentence case, so we need to downcase the network. # - # Passes the lowercase network to subsequent steps using $NETWORK env variable. + # Passes lowercase network to subsequent steps using $NETWORK env variable. - name: Downcase network name for labels run: | NETWORK_CAPS="${{ matrix.network }}" @@ -265,43 +258,81 @@ jobs: # Setup gcloud CLI - name: Authenticate to Google Cloud id: auth - uses: google-github-actions/auth@v2.1.6 + uses: google-github-actions/auth@v2.1.10 with: - workload_identity_provider: '${{ vars.GCP_WIF }}' - service_account: '${{ vars.GCP_DEPLOYMENTS_SA }}' + workload_identity_provider: "${{ vars.GCP_WIF }}" + service_account: "${{ vars.GCP_DEPLOYMENTS_SA }}" - name: Set up Cloud SDK - uses: google-github-actions/setup-gcloud@v2.1.1 + uses: google-github-actions/setup-gcloud@v2.1.4 + # Retrieves a static IP address for long-running nodes. + # This step runs only when triggered by a release or a manual workflow_dispatch event. + # - Exits immediately if any command fails. + # - Attempts to retrieve the static IP for the current network and region. + # - Sets the IP_ADDRESS environment variable. + - name: Get static IP address for long-running nodes + if: ${{ github.event_name == 'release' || github.event_name == 'workflow_dispatch' }} + run: | + set -e + echo "IP_ADDRESS=$(gcloud compute addresses describe zebra-${NETWORK} --region ${{ vars.GCP_REGION }} --format='value(address)')" >> "$GITHUB_ENV" + + # Creates a GCP instance template with specific disk handling: + # - Releases: Uses a fixed disk name (e.g., "zebrad-cache-mainnet") and attempts to re-attach this existing + # persistent disk to maintain node state. A new blank disk is created if not found. Dynamic cached images are NOT used. + # - Other Events (push/workflow_dispatch): Uses a unique disk name (branch/SHA). If a cached disk is requested + # and found by 'get-disk-name', its image seeds the new disk. Errors if an expected cached disk isn't available. - name: Create instance template for ${{ matrix.network }} run: | - DISK_NAME="zebrad-cache-${{ env.GITHUB_HEAD_REF_SLUG_URL || env.GITHUB_REF_SLUG_URL }}-${{ env.GITHUB_SHA_SHORT }}-${NETWORK}" - DISK_PARAMS="name=${DISK_NAME},device-name=${DISK_NAME},size=400GB,type=pd-ssd" - if [ -n "${{ env.CACHED_DISK_NAME }}" ]; then + if [ ${{ github.event_name == 'release' }} ]; then + DISK_NAME="zebrad-cache-${NETWORK}" + else + DISK_NAME="zebrad-cache-${{ env.GITHUB_HEAD_REF_SLUG_URL || env.GITHUB_REF_SLUG_URL }}-${{ env.GITHUB_SHA_SHORT }}-${NETWORK}" + fi + if [ -n "${{ env.IP_ADDRESS }}" ]; then + IP_FLAG="--address=${{ env.IP_ADDRESS }}" + else + IP_FLAG="" + fi + DISK_PARAMS="name=${DISK_NAME},device-name=${DISK_NAME},size=400GB,type=pd-balanced" + + if [ ${{ github.event_name == 'release' }} ]; then + echo "Release event: Using disk ${DISK_NAME} without a dynamic cached image source." + elif [ -n "${{ env.CACHED_DISK_NAME }}" ]; then + echo "Non-release event: Using cached disk image ${{ env.CACHED_DISK_NAME }} for disk ${DISK_NAME}." DISK_PARAMS+=",image=${{ env.CACHED_DISK_NAME }}" - elif [ ${{ inputs.no_cached_disk && github.event_name == 'workflow_dispatch' }} ]; then - echo "No cached disk required" + elif [ ${{ !inputs.need_cached_disk && github.event_name == 'workflow_dispatch' }} ]; then + echo "Workflow dispatch: No cached disk required by input for disk ${DISK_NAME}." else - echo "No cached disk found for ${{ matrix.network }} in main branch" + echo "Error: A cached disk was expected for disk ${{ matrix.network }} but is not available (event: ${{ github.event_name }}, CACHED_DISK_NAME: '${{ env.CACHED_DISK_NAME }}', inputs.need_cached_disk: '${{ inputs.need_cached_disk }}')." exit 1 fi + + # Set log file based on input or default + if [ ${{ github.event_name == 'workflow_dispatch' }} ]; then + LOG_FILE="${{ inputs.log_file }}" + else + LOG_FILE="${{ vars.CD_LOG_FILE }}" + fi + gcloud compute instance-templates create-with-container zebrad-${{ needs.versioning.outputs.major_version || env.GITHUB_REF_SLUG_URL }}-${{ env.GITHUB_SHA_SHORT }}-${NETWORK} \ --machine-type ${{ vars.GCP_SMALL_MACHINE }} \ - --boot-disk-size 50GB \ - --boot-disk-type=pd-ssd \ + --boot-disk-size=10GB \ + --boot-disk-type=pd-standard \ --image-project=cos-cloud \ --image-family=cos-stable \ - --network-interface=subnet=${{ vars.GCP_SUBNETWORK }} \ + --subnet=${{ vars.GCP_SUBNETWORK }} \ + ${IP_FLAG} \ --create-disk="${DISK_PARAMS}" \ - --container-mount-disk=mount-path='/var/cache/zebrad-cache',name=${DISK_NAME},mode=rw \ + --container-mount-disk=mount-path='/home/zebra/.cache/zebra',name=${DISK_NAME},mode=rw \ --container-stdin \ --container-tty \ --container-image ${{ vars.GAR_BASE }}/zebrad@${{ needs.build.outputs.image_digest }} \ - --container-env "NETWORK=${{ matrix.network }},LOG_FILE=${{ vars.CD_LOG_FILE }},LOG_COLOR=false,SENTRY_DSN=${{ vars.SENTRY_DSN }}" \ + --container-env "NETWORK=${{ matrix.network }},LOG_FILE=${{ vars.CD_LOG_FILE }},SENTRY_DSN=${{ vars.SENTRY_DSN }}" \ --service-account ${{ vars.GCP_DEPLOYMENTS_SA }} \ --scopes cloud-platform \ --metadata google-logging-enabled=true,google-logging-use-fluentbit=true,google-monitoring-enabled=true \ - --labels=app=zebrad,environment=staging,network=${NETWORK},github_ref=${{ env.GITHUB_REF_SLUG_URL }} \ + --labels=app=zebrad,environment=${{ github.event_name == 'workflow_dispatch' && 'qa' || 'staging' }},network=${NETWORK},github_ref=${{ env.GITHUB_REF_SLUG_URL }} \ --tags zebrad # Check if our destination instance group exists already @@ -318,11 +349,23 @@ jobs: gcloud compute instance-groups managed create \ "zebrad-${{ needs.versioning.outputs.major_version || env.GITHUB_REF_SLUG_URL }}-${NETWORK}" \ --template "zebrad-${{ needs.versioning.outputs.major_version || env.GITHUB_REF_SLUG_URL }}-${{ env.GITHUB_SHA_SHORT }}-${NETWORK}" \ - --health-check zebrad-tracing-filter \ --initial-delay 30 \ --region "${{ vars.GCP_REGION }}" \ --size 1 + # Configure stateful disk policy for release MIGs to ensure disk persistence. + # This policy tells the MIG to preserve the disk with the specified device-name + # when instances are recreated or deleted, and to reattach it. + - name: Configure stateful disk policy for release MIG + if: ${{ github.event_name == 'release' }} + run: | + MIG_NAME="zebrad-${{ needs.versioning.outputs.major_version }}-${NETWORK}" + DEVICE_NAME_TO_PRESERVE="zebrad-cache-${NETWORK}" + echo "Applying stateful policy to MIG: ${MIG_NAME} for device: ${DEVICE_NAME_TO_PRESERVE}" + gcloud compute instance-groups managed set-stateful-policy "${MIG_NAME}" \ + --region "${{ vars.GCP_REGION }}" \ + --stateful-disk "device-name=${DEVICE_NAME_TO_PRESERVE},auto-delete=never" + # Rolls out update to existing group using the new instance template - name: Update managed instance group for ${{ matrix.network }} if: steps.does-group-exist.outcome == 'success' @@ -332,97 +375,13 @@ jobs: --version template="zebrad-${{ needs.versioning.outputs.major_version || env.GITHUB_REF_SLUG_URL }}-${{ env.GITHUB_SHA_SHORT }}-${NETWORK}" \ --region "${{ vars.GCP_REGION }}" - # This jobs handles the deployment of a single node (1) in the configured GCP zone - # when an instance is required to test a specific commit - # - # Runs: - # - on request, using workflow_dispatch with regenerate-disks - # - # Note: this instances are not automatically replaced or deleted - deploy-instance: - name: Deploy single ${{ inputs.network }} instance - needs: [ build, test-configuration-file, test-zebra-conf-path, get-disk-name ] - runs-on: ubuntu-latest - timeout-minutes: 30 - env: - CACHED_DISK_NAME: ${{ needs.get-disk-name.outputs.cached_disk_name }} - permissions: - contents: 'read' - id-token: 'write' - # Run even if we don't need a cached disk, but only when triggered by a workflow_dispatch - if: ${{ !failure() && github.event_name == 'workflow_dispatch' }} - - steps: - - uses: actions/checkout@v4.2.1 - with: - persist-credentials: false - - - name: Inject slug/short variables - uses: rlespinasse/github-slug-action@v4 - with: - short-length: 7 - - # Makes the Zcash network name lowercase. - # - # Labels in GCP are required to be in lowercase, but the blockchain network - # uses sentence case, so we need to downcase the network. - # - # Passes the lowercase network to subsequent steps using $NETWORK env variable. - - name: Downcase network name for labels - run: | - NETWORK_CAPS="${{ inputs.network }}" - echo "NETWORK=${NETWORK_CAPS,,}" >> "$GITHUB_ENV" - - # Setup gcloud CLI - - name: Authenticate to Google Cloud - id: auth - uses: google-github-actions/auth@v2.1.6 - with: - workload_identity_provider: '${{ vars.GCP_WIF }}' - service_account: '${{ vars.GCP_DEPLOYMENTS_SA }}' - - - name: Set up Cloud SDK - uses: google-github-actions/setup-gcloud@v2.1.1 - - # Create instance template from container image - - name: Manual deploy of a single ${{ inputs.network }} instance running zebrad - run: | - DISK_NAME="zebrad-cache-${{ env.GITHUB_HEAD_REF_SLUG_URL || env.GITHUB_REF_SLUG_URL }}-${{ env.GITHUB_SHA_SHORT }}-${NETWORK}" - DISK_PARAMS="name=${DISK_NAME},device-name=${DISK_NAME},size=400GB,type=pd-ssd" - if [ -n "${{ env.CACHED_DISK_NAME }}" ]; then - DISK_PARAMS+=",image=${{ env.CACHED_DISK_NAME }}" - elif [ ${{ inputs.no_cached_disk && github.event_name == 'workflow_dispatch' }} ]; then - echo "No cached disk required" - else - echo "No cached disk found for ${{ matrix.network }} in main branch" - exit 1 - fi - gcloud compute instances create-with-container "zebrad-${{ env.GITHUB_REF_SLUG_URL }}-${{ env.GITHUB_SHA_SHORT }}-${NETWORK}" \ - --machine-type ${{ vars.GCP_SMALL_MACHINE }} \ - --boot-disk-size 50GB \ - --boot-disk-type=pd-ssd \ - --image-project=cos-cloud \ - --image-family=cos-stable \ - --network-interface=subnet=${{ vars.GCP_SUBNETWORK }} \ - --create-disk="${DISK_PARAMS}" \ - --container-mount-disk=mount-path='/var/cache/zebrad-cache',name=${DISK_NAME},mode=rw \ - --container-stdin \ - --container-tty \ - --container-image ${{ vars.GAR_BASE }}/zebrad@${{ needs.build.outputs.image_digest }} \ - --container-env "NETWORK=${{ inputs.network }},LOG_FILE=${{ inputs.log_file }},LOG_COLOR=false,SENTRY_DSN=${{ vars.SENTRY_DSN }}" \ - --service-account ${{ vars.GCP_DEPLOYMENTS_SA }} \ - --scopes cloud-platform \ - --metadata google-logging-enabled=true,google-monitoring-enabled=true \ - --labels=app=zebrad,environment=qa,network=${NETWORK},github_ref=${{ env.GITHUB_REF_SLUG_URL }} \ - --tags zebrad \ - --zone ${{ vars.GCP_ZONE }} - failure-issue: name: Open or update issues for release failures # When a new job is added to this workflow, add it to this list. - needs: [ versioning, build, deploy-nodes, deploy-instance ] + needs: [versioning, build, deploy-nodes] + # Only open tickets for failed or cancelled jobs that are not coming from PRs. - # (PR statuses are already reported in the PR jobs list, and checked by Mergify.) + # (PR statuses are already reported in the PR jobs list, and checked by GitHub's Merge Queue.) if: (failure() && github.event.pull_request == null) || (cancelled() && github.event.pull_request == null) runs-on: ubuntu-latest steps: diff --git a/.github/workflows/chore-delete-gcp-resources.yml.disabled b/.github/workflows/chore-delete-gcp-resources.yml.disabled index 4470d244029..00e254a9e53 100644 --- a/.github/workflows/chore-delete-gcp-resources.yml.disabled +++ b/.github/workflows/chore-delete-gcp-resources.yml.disabled @@ -34,25 +34,30 @@ env: jobs: delete-resources: name: Delete old GCP resources + if: github.repository_owner == 'ZcashFoundation' runs-on: ubuntu-latest permissions: contents: 'read' id-token: 'write' + strategy: + matrix: + environment: [dev, prod] + environment: ${{ matrix.environment }} steps: - - uses: actions/checkout@v4.2.1 + - uses: actions/checkout@v4.2.2 with: persist-credentials: false # Setup gcloud CLI - name: Authenticate to Google Cloud id: auth - uses: google-github-actions/auth@v2.1.6 + uses: google-github-actions/auth@v2.1.10 with: workload_identity_provider: '${{ vars.GCP_WIF }}' service_account: '${{ vars.GCP_DEPLOYMENTS_SA }}' - name: Set up Cloud SDK - uses: google-github-actions/setup-gcloud@v2.1.1 + uses: google-github-actions/setup-gcloud@v2.1.4 # Deletes all mainnet and testnet instances older than $DELETE_INSTANCE_DAYS days. # @@ -101,26 +106,31 @@ jobs: # The same artifacts are used for both mainnet and testnet. clean-registries: name: Delete unused artifacts in registry + if: github.repository_owner == 'ZcashFoundation' runs-on: ubuntu-latest permissions: contents: 'read' id-token: 'write' + strategy: + matrix: + environment: [dev, prod] + environment: ${{ matrix.environment }} steps: - - uses: actions/checkout@v4.2.1 + - uses: actions/checkout@v4.2.2 with: persist-credentials: false # Setup gcloud CLI - name: Authenticate to Google Cloud id: auth - uses: google-github-actions/auth@v2.1.6 + uses: google-github-actions/auth@v2.1.10 with: workload_identity_provider: '${{ vars.GCP_WIF }}' service_account: '${{ vars.GCP_DEPLOYMENTS_SA }}' token_format: 'access_token' - name: Login to Google Artifact Registry - uses: docker/login-action@v3.3.0 + uses: docker/login-action@v3.4.0 with: registry: us-docker.pkg.dev username: oauth2accesstoken @@ -128,7 +138,7 @@ jobs: # Deletes all images older than $DELETE_IMAGE_HOURS days. - uses: 'docker://us-docker.pkg.dev/gcr-cleaner/gcr-cleaner/gcr-cleaner-cli' - continue-on-error: true # TODO: remove after fixig https://github.com/ZcashFoundation/zebra/issues/5933 + continue-on-error: true # TODO: remove after fixing https://github.com/ZcashFoundation/zebra/issues/5933 # Refer to the official documentation to understand available arguments: # https://github.com/GoogleCloudPlatform/gcr-cleaner with: diff --git a/.github/workflows/ci-basic.yml b/.github/workflows/ci-basic.yml index 064f28efdc5..0c2f9d15a7a 100644 --- a/.github/workflows/ci-basic.yml +++ b/.github/workflows/ci-basic.yml @@ -25,7 +25,16 @@ jobs: - name: Install formatting & linting tools run: rustup component add rustfmt clippy - name: Run tests - run: cargo test --verbose + env: + RUST_BACKTRACE: 1 + # Skip two tests that intermittently fail on CI (likely a race/ordering issue). + # FIXME: investigate and fix the underlying flake; remove these skips once resolved. + run: cargo test --locked --verbose -- --skip v4_with_sapling_spends --skip v5_with_sapling_spends + # Run the skipped tests separately with constrained parallelism + - name: Run Sapling spend tests + run: | + cargo test -p zebra-consensus v4_with_sapling_spends -- --nocapture + cargo test -p zebra-consensus v5_with_sapling_spends -- --nocapture - name: Verify working directory is clean run: git diff --exit-code - name: Run doc check diff --git a/.github/workflows/ci-build-crates.patch.yml b/.github/workflows/ci-build-crates.patch.yml index c4333a86aba..525904507e8 100644 --- a/.github/workflows/ci-build-crates.patch.yml +++ b/.github/workflows/ci-build-crates.patch.yml @@ -23,7 +23,7 @@ jobs: outputs: matrix: ${{ steps.set-matrix.outputs.matrix }} steps: - - uses: actions/checkout@v4.2.1 + - uses: actions/checkout@v4.2.2 # Setup Rust with stable toolchain and minimal profile - name: Setup Rust diff --git a/.github/workflows/ci-build-crates.yml b/.github/workflows/ci-build-crates.yml index 67404f75972..d38835e46b1 100644 --- a/.github/workflows/ci-build-crates.yml +++ b/.github/workflows/ci-build-crates.yml @@ -60,7 +60,7 @@ jobs: outputs: matrix: ${{ steps.set-matrix.outputs.matrix }} steps: - - uses: actions/checkout@v4.2.1 + - uses: actions/checkout@v4.2.2 - uses: r7kamura/rust-problem-matchers@v1.5.0 # Setup Rust with stable toolchain and minimal profile @@ -113,7 +113,7 @@ jobs: timeout-minutes: 90 needs: [ matrix, check-matrix ] # Some of these builds take more than 14GB disk space - runs-on: ubuntu-latest-m + runs-on: ${{ github.repository_owner == 'ZcashFoundation' && 'ubuntu-latest-m' || 'ubuntu-latest' }} strategy: # avoid rate-limit errors by only launching a few of these jobs at a time, # but still finish in a similar time to the longest tests @@ -122,7 +122,7 @@ jobs: matrix: ${{ fromJson(needs.matrix.outputs.matrix) }} steps: - - uses: actions/checkout@v4.2.1 + - uses: actions/checkout@v4.2.2 with: persist-credentials: false - uses: r7kamura/rust-problem-matchers@v1.5.0 @@ -169,7 +169,7 @@ jobs: # When a new job is added to this workflow, add it to this list. needs: [ matrix, build ] # Only open tickets for failed or cancelled jobs that are not coming from PRs. - # (PR statuses are already reported in the PR jobs list, and checked by Mergify.) + # (PR statuses are already reported in the PR jobs list, and checked by GitHub's Merge Queue.) if: (failure() && github.event.pull_request == null) || (cancelled() && github.event.pull_request == null) runs-on: ubuntu-latest steps: diff --git a/.github/workflows/ci-coverage.yml b/.github/workflows/ci-coverage.yml index 057cc1cf916..b5b0c6f0024 100644 --- a/.github/workflows/ci-coverage.yml +++ b/.github/workflows/ci-coverage.yml @@ -66,10 +66,10 @@ jobs: # The large timeout is to accommodate: # - stable builds (typically 50-90 minutes), and timeout-minutes: 120 - runs-on: ubuntu-latest-xl + runs-on: ${{ github.repository_owner == 'ZcashFoundation' && 'ubuntu-latest-xl' || 'ubuntu-latest' }} steps: - - uses: actions/checkout@v4.2.1 + - uses: actions/checkout@v4.2.2 with: persist-credentials: false @@ -103,4 +103,4 @@ jobs: run: cargo llvm-cov --lcov --no-run --output-path lcov.info - name: Upload coverage report to Codecov - uses: codecov/codecov-action@v4.6.0 + uses: codecov/codecov-action@v5.4.3 diff --git a/.github/workflows/ci-lint.yml b/.github/workflows/ci-lint.yml index b9966de9058..ff5e8489244 100644 --- a/.github/workflows/ci-lint.yml +++ b/.github/workflows/ci-lint.yml @@ -37,14 +37,14 @@ jobs: rust: ${{ steps.changed-files-rust.outputs.any_changed == 'true' }} workflows: ${{ steps.changed-files-workflows.outputs.any_changed == 'true' }} steps: - - uses: actions/checkout@v4.2.1 + - uses: actions/checkout@v4.2.2 with: persist-credentials: false fetch-depth: 0 - name: Rust files id: changed-files-rust - uses: tj-actions/changed-files@v45.0.3 + uses: tj-actions/changed-files@v46.0.5 with: files: | **/*.rs @@ -56,7 +56,7 @@ jobs: - name: Workflow files id: changed-files-workflows - uses: tj-actions/changed-files@v45.0.3 + uses: tj-actions/changed-files@v46.0.5 with: files: | .github/workflows/*.yml @@ -69,7 +69,7 @@ jobs: if: ${{ needs.changed-files.outputs.rust == 'true' }} steps: - - uses: actions/checkout@v4.2.1 + - uses: actions/checkout@v4.2.2 with: persist-credentials: false @@ -93,7 +93,7 @@ jobs: run: | curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain=stable --profile=default - - uses: Swatinem/rust-cache@v2.7.5 + - uses: Swatinem/rust-cache@v2.7.8 with: shared-key: "clippy-cargo-lock" @@ -119,7 +119,7 @@ jobs: if: ${{ needs.changed-files.outputs.rust == 'true' }} steps: - - uses: actions/checkout@v4.2.1 + - uses: actions/checkout@v4.2.2 with: persist-credentials: false - uses: r7kamura/rust-problem-matchers@v1.5.0 @@ -138,7 +138,7 @@ jobs: # We don't cache `fmt` outputs because the job is quick, # and we want to use the limited GitHub actions cache space for slower jobs. - #- uses: Swatinem/rust-cache@v2.7.5 + #- uses: Swatinem/rust-cache@v2.7.8 - run: | cargo fmt --all -- --check @@ -149,7 +149,7 @@ jobs: needs: changed-files if: ${{ needs.changed-files.outputs.workflows == 'true' }} steps: - - uses: actions/checkout@v4.2.1 + - uses: actions/checkout@v4.2.2 - name: actionlint uses: reviewdog/action-actionlint@v1.48.0 with: @@ -166,7 +166,7 @@ jobs: runs-on: ubuntu-latest needs: changed-files steps: - - uses: actions/checkout@v4.2.1 + - uses: actions/checkout@v4.2.2 - uses: codespell-project/actions-codespell@v2.1 with: only_warn: 1 diff --git a/.github/workflows/ci-tests.patch-external.yml b/.github/workflows/ci-tests.patch-external.yml index 48a6ab667b9..09046987d19 100644 --- a/.github/workflows/ci-tests.patch-external.yml +++ b/.github/workflows/ci-tests.patch-external.yml @@ -1,7 +1,7 @@ # Workflow patches for skipping CI tests on PRs from external repositories name: Run tests -# Run on PRs from external repositories, let them pass, and then Mergify will check them. +# Run on PRs from external repositories, let them pass, and then GitHub's Merge Queue will check them. # GitHub doesn't support filtering workflows by source branch names, so we have to do it for each # job. on: @@ -15,7 +15,7 @@ jobs: build: name: Build CI Docker / Build images # Only run on PRs from external repositories. - if: ${{ startsWith(github.event_name, 'pull') && github.event.pull_request.head.repo.fork }} + if: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork }} runs-on: ubuntu-latest steps: - run: 'echo "Skipping job on fork"' @@ -24,116 +24,119 @@ jobs: ## The following jobs are related to sub-ci-unit-tests-docker.yml ### test-all: - name: Test all + name: Unit tests / Test all runs-on: ubuntu-latest + if: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork }} steps: - run: 'echo "Skipping job on fork"' - test-fake-activation-heights: - name: Test with fake activation heights + state-fake-activation-heights: + name: Unit tests / Test with fake activation heights runs-on: ubuntu-latest + if: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork }} steps: - run: 'echo "Skipping job on fork"' - test-empty-sync: - name: Test checkpoint sync from empty state + sync-large-checkpoints-empty: + name: Unit tests / Test checkpoint sync from empty state runs-on: ubuntu-latest + if: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork }} steps: - run: 'echo "Skipping job on fork"' test-lightwalletd-integration: - name: Test integration with lightwalletd + name: Unit tests / Test integration with lightwalletd runs-on: ubuntu-latest + if: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork }} steps: - run: 'echo "Skipping job on fork"' - test-configuration-file: - name: Test CI default Docker config file / Test default-conf in Docker - runs-on: ubuntu-latest - steps: - - run: 'echo "Skipping job on fork"' - - test-configuration-file-testnet: - name: Test CI testnet Docker config file / Test default-conf in Docker - needs: build - runs-on: ubuntu-latest - steps: - - run: 'echo "Skipping job on fork"' - - test-zebra-conf-path: - name: Test CI custom Docker config file / Test custom-conf in Docker - runs-on: ubuntu-latest - steps: - - run: 'echo "Skipping job on fork"' - - + #### #### ## The following jobs are related to sub-ci-integration-tests-gcp.yml ### # We don't patch the testnet job, because testnet isn't required to merge (it's too unstable) get-available-disks: - name: Check if cached state disks exist for Mainnet / Check if cached state disks exist + name: Integration tests / Check if cached state disks exist for Mainnet / Get Mainnet cached disk + runs-on: ubuntu-latest + if: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork }} + steps: + - run: 'echo "Skipping job on fork"' + + sync-past-mandatory-checkpoint: + name: Integration tests / Zebra checkpoint update / Run sync-past-checkpoint test runs-on: ubuntu-latest + if: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork }} steps: - run: 'echo "Skipping job on fork"' - test-stateful-sync: - name: Zebra checkpoint update / Run sync-past-checkpoint test + sync-update-mainnet: + name: Integration tests / Zebra tip update / Run update-to-tip test runs-on: ubuntu-latest + if: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork }} steps: - run: 'echo "Skipping job on fork"' - test-update-sync: - name: Zebra tip update / Run update-to-tip test + generate-checkpoints-mainnet: + name: Integration tests / Generate checkpoints mainnet / Run checkpoints-mainnet test runs-on: ubuntu-latest + if: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork }} steps: - run: 'echo "Skipping job on fork"' - checkpoints-mainnet: - name: Generate checkpoints mainnet / Run checkpoints-mainnet test + generate-checkpoints-testnet: + name: Integration tests / Generate checkpoints testnet / Run checkpoints-testnet test runs-on: ubuntu-latest + if: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork }} steps: - run: 'echo "Skipping job on fork"' - lightwalletd-rpc-test: - name: Zebra tip JSON-RPC / Run fully-synced-rpc test + rpc-fully-synced-test: + name: Integration tests / Zebra tip JSON-RPC / Run fully-synced-rpc test runs-on: ubuntu-latest + if: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork }} steps: - run: 'echo "Skipping job on fork"' - lightwalletd-transactions-test: - name: lightwalletd tip send / Run lwd-send-transactions test + lwd-rpc-send-tx: + name: Integration tests / lightwalletd tip send / Run lwd-send-transactions test runs-on: ubuntu-latest + if: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork }} steps: - run: 'echo "Skipping job on fork"' - get-block-template-test: - name: get block template / Run get-block-template test + rpc-get-template: + name: Integration tests / get block template / Run get-block-template test runs-on: ubuntu-latest + if: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork }} steps: - run: 'echo "Skipping job on fork"' - submit-block-test: - name: submit block / Run submit-block test + rpc-submit-block: + name: Integration tests / submit block / Run submit-block test runs-on: ubuntu-latest + if: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork }} steps: - run: 'echo "Skipping job on fork"' - lightwalletd-full-sync: - name: lightwalletd tip / Run lwd-full-sync test + lwd-sync-full: + name: Integration tests / lightwalletd tip / Run lwd-full-sync test runs-on: ubuntu-latest + if: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork }} steps: - run: 'echo "Skipping job on fork"' - lightwalletd-update-sync: - name: lightwalletd tip update / Run lwd-update-sync test + lwd-sync-update: + name: Integration tests / lightwalletd tip update / Run lwd-update-sync test runs-on: ubuntu-latest + if: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork }} steps: - run: 'echo "Skipping job on fork"' - lightwalletd-grpc-test: - name: lightwalletd GRPC tests / Run lwd-grpc-wallet test + lwd-grpc-wallet: + name: Integration tests / lightwalletd GRPC tests / Run lwd-grpc-wallet test runs-on: ubuntu-latest + if: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork }} steps: - run: 'echo "Skipping job on fork"' diff --git a/.github/workflows/ci-tests.patch.yml b/.github/workflows/ci-tests.patch.yml index 5320b149bbb..ff2ecd5d7f5 100644 --- a/.github/workflows/ci-tests.patch.yml +++ b/.github/workflows/ci-tests.patch.yml @@ -43,116 +43,118 @@ jobs: ## The following jobs are related to sub-ci-unit-tests-docker.yml ### test-all: - name: Test all + name: Unit tests / Test all runs-on: ubuntu-latest + if: ${{ github.event_name == 'pull_request' && !github.event.pull_request.head.repo.fork }} steps: - run: 'echo "No build required"' - test-fake-activation-heights: - name: Test with fake activation heights + state-fake-activation-heights: + name: Unit tests / Test with fake activation heights runs-on: ubuntu-latest + if: ${{ github.event_name == 'pull_request' && !github.event.pull_request.head.repo.fork }} steps: - run: 'echo "No build required"' - test-empty-sync: - name: Test checkpoint sync from empty state + sync-large-checkpoints-empty: + name: Unit tests / Test checkpoint sync from empty state runs-on: ubuntu-latest + if: ${{ github.event_name == 'pull_request' && !github.event.pull_request.head.repo.fork }} steps: - run: 'echo "No build required"' test-lightwalletd-integration: - name: Test integration with lightwalletd + name: Unit tests / Test integration with lightwalletd runs-on: ubuntu-latest + if: ${{ github.event_name == 'pull_request' && !github.event.pull_request.head.repo.fork }} steps: - run: 'echo "No build required"' - test-configuration-file: - name: Test CI default Docker config file / Test default-conf in Docker - runs-on: ubuntu-latest - steps: - - run: 'echo "No build required"' - - test-configuration-file-testnet: - name: Test CI testnet Docker config file / Test default-conf in Docker - needs: build - runs-on: ubuntu-latest - steps: - - run: 'echo "No build required"' - - test-zebra-conf-path: - name: Test CI custom Docker config file / Test custom-conf in Docker - runs-on: ubuntu-latest - steps: - - run: 'echo "No build required"' - - #### ## The following jobs are related to sub-ci-integration-tests-gcp.yml ### # We don't patch the testnet job, because testnet isn't required to merge (it's too unstable) get-available-disks: - name: Check if cached state disks exist for Mainnet / Check if cached state disks exist + name: Integration tests / Check if cached state disks exist for Mainnet / Get Mainnet cached disk runs-on: ubuntu-latest + if: ${{ github.event_name == 'pull_request' && !github.event.pull_request.head.repo.fork }} steps: - run: 'echo "No build required"' - test-stateful-sync: - name: Zebra checkpoint update / Run sync-past-checkpoint test + sync-past-mandatory-checkpoint: + name: Integration tests / Zebra checkpoint update / Run sync-past-checkpoint test runs-on: ubuntu-latest + if: ${{ github.event_name == 'pull_request' && !github.event.pull_request.head.repo.fork }} steps: - run: 'echo "No build required"' - test-update-sync: - name: Zebra tip update / Run update-to-tip test + sync-update-mainnet: + name: Integration tests / Zebra tip update / Run update-to-tip test runs-on: ubuntu-latest + if: ${{ github.event_name == 'pull_request' && !github.event.pull_request.head.repo.fork }} steps: - run: 'echo "No build required"' - checkpoints-mainnet: - name: Generate checkpoints mainnet / Run checkpoints-mainnet test + generate-checkpoints-mainnet: + name: Integration tests / Generate checkpoints mainnet / Run checkpoints-mainnet test runs-on: ubuntu-latest + if: ${{ github.event_name == 'pull_request' && !github.event.pull_request.head.repo.fork }} steps: - run: 'echo "No build required"' - lightwalletd-rpc-test: - name: Zebra tip JSON-RPC / Run fully-synced-rpc test + generate-checkpoints-testnet: + name: Integration tests / Generate checkpoints testnet / Run checkpoints-testnet test + runs-on: ubuntu-latest + if: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork }} + steps: + - run: 'echo "Skipping job on fork"' + + rpc-fully-synced-test: + name: Integration tests / Zebra tip JSON-RPC / Run fully-synced-rpc test runs-on: ubuntu-latest + if: ${{ github.event_name == 'pull_request' && !github.event.pull_request.head.repo.fork }} steps: - run: 'echo "No build required"' - lightwalletd-transactions-test: - name: lightwalletd tip send / Run lwd-send-transactions test + lwd-rpc-send-tx: + name: Integration tests / lightwalletd tip send / Run lwd-send-transactions test runs-on: ubuntu-latest + if: ${{ github.event_name == 'pull_request' && !github.event.pull_request.head.repo.fork }} steps: - run: 'echo "No build required"' - get-block-template-test: - name: get block template / Run get-block-template test + rpc-get-template: + name: Integration tests / get block template / Run get-block-template test runs-on: ubuntu-latest + if: ${{ github.event_name == 'pull_request' && !github.event.pull_request.head.repo.fork }} steps: - run: 'echo "No build required"' - submit-block-test: - name: submit block / Run submit-block test + rpc-submit-block: + name: Integration tests / submit block / Run submit-block test runs-on: ubuntu-latest + if: ${{ github.event_name == 'pull_request' && !github.event.pull_request.head.repo.fork }} steps: - run: 'echo "No build required"' - lightwalletd-full-sync: - name: lightwalletd tip / Run lwd-full-sync test + lwd-sync-full: + name: Integration tests / lightwalletd tip / Run lwd-full-sync test runs-on: ubuntu-latest + if: ${{ github.event_name == 'pull_request' && !github.event.pull_request.head.repo.fork }} steps: - run: 'echo "No build required"' - lightwalletd-update-sync: - name: lightwalletd tip update / Run lwd-update-sync test + lwd-sync-update: + name: Integration tests / lightwalletd tip update / Run lwd-update-sync test runs-on: ubuntu-latest + if: ${{ github.event_name == 'pull_request' && !github.event.pull_request.head.repo.fork }} steps: - run: 'echo "No build required"' - lightwalletd-grpc-test: - name: lightwalletd GRPC tests / Run lwd-grpc-wallet test + lwd-grpc-wallet: + name: Integration tests / lightwalletd GRPC tests / Run lwd-grpc-wallet test runs-on: ubuntu-latest + if: ${{ github.event_name == 'pull_request' && !github.event.pull_request.head.repo.fork }} steps: - run: 'echo "No build required"' diff --git a/.github/workflows/ci-tests.yml b/.github/workflows/ci-tests.yml index 997bf25eb00..ffb886b4512 100644 --- a/.github/workflows/ci-tests.yml +++ b/.github/workflows/ci-tests.yml @@ -10,6 +10,9 @@ concurrency: cancel-in-progress: true on: + merge_group: + types: [checks_requested] + schedule: # Run this job every Friday at mid-day UTC # This is limited to the Zebra and lightwalletd Full Sync jobs @@ -118,8 +121,8 @@ jobs: # workflow or repository variable is configured differently. Testnet jobs change that config to # testnet when running the image. build: - name: Build images - # Skip PRs from external repositories, let them pass, and then Mergify will check them + name: Build CI Docker + # Skip PRs from external repositories, let them pass, and then GitHub's Merge Queue will check them if: ${{ !startsWith(github.event_name, 'pull') || !github.event.pull_request.head.repo.fork }} uses: ./.github/workflows/sub-build-docker-image.yml with: @@ -130,6 +133,7 @@ jobs: rust_backtrace: full rust_lib_backtrace: full rust_log: info + features: ${{ format('{0} {1}', vars.RUST_PROD_FEATURES, vars.RUST_TEST_FEATURES) }} # This step needs access to Docker Hub secrets to run successfully secrets: inherit diff --git a/.github/workflows/ci-unit-tests-os.yml b/.github/workflows/ci-unit-tests-os.yml index 7c194c51c5e..dfe82158902 100644 --- a/.github/workflows/ci-unit-tests-os.yml +++ b/.github/workflows/ci-unit-tests-os.yml @@ -14,6 +14,9 @@ concurrency: cancel-in-progress: true on: + merge_group: + types: [checks_requested] + workflow_dispatch: pull_request: @@ -70,7 +73,7 @@ jobs: ### Build and test Zebra on all OSes ### ######################################## test: - name: Test ${{ matrix.rust }} on ${{ matrix.os }}${{ matrix.features }} + name: Test ${{ matrix.rust }} on ${{ matrix.os }} # The large timeout is to accommodate: # - macOS and Windows builds (typically 50-90 minutes), and timeout-minutes: 120 @@ -80,11 +83,9 @@ jobs: matrix: os: [ubuntu-latest, macos-latest, windows-latest] rust: [stable, beta] - # TODO: When vars.EXPERIMENTAL_FEATURES has features in it, add it here. - # Or work out a way to trim the space from the variable: GitHub doesn't allow empty variables. - # Or use `default` for the empty feature set and EXPERIMENTAL_FEATURES, and update the branch protection rules. - #features: ${{ fromJSON(format('["", "{0}"]', vars.EXPERIMENTAL_FEATURES)) }} - features: [""] + # We only test with default features in this workflow + # Other feature combinations are tested in specific workflows + features: ["default-release-binaries"] exclude: # We're excluding macOS beta for the following reasons: # - the concurrent macOS runner limit is much lower than the Linux limit @@ -94,7 +95,7 @@ jobs: rust: beta steps: - - uses: actions/checkout@v4.2.1 + - uses: actions/checkout@v4.2.2 with: persist-credentials: false - uses: r7kamura/rust-problem-matchers@v1.5.0 @@ -112,14 +113,13 @@ jobs: curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain=${{ matrix.rust }} --profile=minimal - - uses: Swatinem/rust-cache@v2.7.5 + - uses: Swatinem/rust-cache@v2.7.8 # TODO: change Rust cache target directory on Windows, # or remove this workaround once the build is more efficient (#3005). #with: # workspaces: ". -> C:\\zebra-target" with: - # Split the experimental features cache from the regular cache, to avoid linker errors. - # (These might be "disk full" errors, or they might be dependency resolution issues.) + # Split the cache by feature set to avoid linker errors key: ${{ matrix.features }} - name: Change target output directory on Windows @@ -183,11 +183,18 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4.2.1 + - uses: actions/checkout@v4.2.2 with: persist-credentials: false - uses: r7kamura/rust-problem-matchers@v1.5.0 + - name: Install last version of Protoc + uses: arduino/setup-protoc@v3.0.0 + with: + # TODO: increase to latest version after https://github.com/arduino/setup-protoc/issues/33 is fixed + version: '23.x' + repo-token: ${{ secrets.GITHUB_TOKEN }} + # Setup Rust with stable toolchain and minimal profile - name: Setup Rust run: | @@ -205,7 +212,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4.2.1 + - uses: actions/checkout@v4.2.2 with: persist-credentials: false - uses: r7kamura/rust-problem-matchers@v1.5.0 @@ -221,7 +228,7 @@ jobs: run: | curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain=stable --profile=minimal - - uses: Swatinem/rust-cache@v2.7.5 + - uses: Swatinem/rust-cache@v2.7.8 with: shared-key: "clippy-cargo-lock" @@ -248,7 +255,7 @@ jobs: continue-on-error: ${{ matrix.checks == 'advisories' }} steps: - - uses: actions/checkout@v4.2.1 + - uses: actions/checkout@v4.2.2 with: persist-credentials: false - uses: r7kamura/rust-problem-matchers@v1.5.0 @@ -269,7 +276,7 @@ jobs: steps: - name: Checkout git repository - uses: actions/checkout@v4.2.1 + uses: actions/checkout@v4.2.2 with: persist-credentials: false - uses: r7kamura/rust-problem-matchers@v1.5.0 @@ -280,7 +287,7 @@ jobs: curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain=stable --profile=minimal - name: Install cargo-machete - uses: baptiste0928/cargo-install@v3.1.1 + uses: baptiste0928/cargo-install@v3.3.0 with: crate: cargo-machete @@ -305,8 +312,8 @@ jobs: # When a new job is added to this workflow, add it to this list. needs: [ test, install-from-lockfile-no-cache, check-cargo-lock, cargo-deny, unused-deps ] # Only open tickets for failed or cancelled jobs that are not coming from PRs. - # (PR statuses are already reported in the PR jobs list, and checked by Mergify.) - if: (failure() && github.event.pull_request == null) || (cancelled() && github.event.pull_request == null) + # (PR statuses are already reported in the PR jobs list, and checked by GitHub's Merge Queue.) + if: (failure() || cancelled()) && github.repository_owner == 'ZcashFoundation' && github.event.pull_request == null runs-on: ubuntu-latest steps: - uses: jayqi/failed-build-issue-action@v1 diff --git a/.github/workflows/docs-deploy-firebase.patch-external.yml.disabled b/.github/workflows/docs-deploy-firebase.patch-external.yml.disabled index 8478e4c2ded..9a725ba21b6 100644 --- a/.github/workflows/docs-deploy-firebase.patch-external.yml.disabled +++ b/.github/workflows/docs-deploy-firebase.patch-external.yml.disabled @@ -1,22 +1,21 @@ # Workflow patches for skipping Google Cloud docs updates on PRs from external repositories. name: Docs -# Run on PRs from external repositories, let them pass, and then Mergify will check them. +# Run on PRs from external repositories, let them pass, and then GitHub's Merge Queue will check them. # GitHub doesn't support filtering workflows by source branch names, so we have to do it for each # job. on: pull_request: -# IMPORTANT -# -# The job names in `docs-deploy-firebase.yml`, `docs-deploy-firebase.patch.yml` and -# `docs-deploy-firebase.patch-external.yml` must be kept in sync. +#! IMPORTANT +#! +#! The job names in `docs-deploy-firebase.yml`, `docs-deploy-firebase.patch.yml` and +#! `docs-deploy-firebase.patch-external.yml` must be kept in sync. jobs: build-docs-book: name: Build and Deploy Zebra Book Docs - # Only run on PRs from external repositories. - if: ${{ startsWith(github.event_name, 'pull') && github.event.pull_request.head.repo.fork }} runs-on: ubuntu-latest + if: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork }} steps: - run: 'echo "Skipping job on fork"' @@ -26,5 +25,6 @@ jobs: # change. needs: build-docs-book runs-on: ubuntu-latest + if: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork }} steps: - run: 'echo "Skipping job on fork"' diff --git a/.github/workflows/docs-deploy-firebase.patch.yml.disabled b/.github/workflows/docs-deploy-firebase.patch.yml.disabled index 7d84ff75961..30c0793d852 100644 --- a/.github/workflows/docs-deploy-firebase.patch.yml.disabled +++ b/.github/workflows/docs-deploy-firebase.patch.yml.disabled @@ -21,19 +21,21 @@ on: # workflow definitions - '.github/workflows/docs-deploy-firebase.yml' -# IMPORTANT -# -# The job names in `docs-deploy-firebase.yml`, `docs-deploy-firebase.patch.yml` and -# `docs-deploy-firebase.patch-external.yml` must be kept in sync. +#! IMPORTANT +#! +#! The job names in `docs-deploy-firebase.yml`, `docs-deploy-firebase.patch.yml` and +#! `docs-deploy-firebase.patch-external.yml` must be kept in sync. jobs: build-docs-book: name: Build and Deploy Zebra Book Docs runs-on: ubuntu-latest + if: ${{ github.event_name == 'pull_request' && !github.event.pull_request.head.repo.fork }} steps: - run: 'echo "No build required"' build-docs-internal: name: Build and Deploy Zebra Internal Docs runs-on: ubuntu-latest + if: ${{ github.event_name == 'pull_request' && !github.event.pull_request.head.repo.fork }} steps: - run: 'echo "No build required"' diff --git a/.github/workflows/docs-deploy-firebase.yml.disabled b/.github/workflows/docs-deploy-firebase.yml.disabled index 1c8ce7fd773..6b8ce2c3554 100644 --- a/.github/workflows/docs-deploy-firebase.yml.disabled +++ b/.github/workflows/docs-deploy-firebase.yml.disabled @@ -1,5 +1,5 @@ # Google Cloud docs updates that run when docs, Rust code, or dependencies are modified, -# but only on PRs from the ZcashFoundation/zebra repository. (External PRs are deployed by mergify.) +# but only on PRs from the ZcashFoundation/zebra repository. (External PRs are deployed by GitHub's Merge Queue.) # - Builds and deploys Zebra Book Docs using mdBook, setting up necessary tools and deploying to Firebase. # - Compiles and deploys external documentation, setting up Rust with the beta toolchain and default profile, building the docs, and deploying them to Firebase. @@ -74,7 +74,7 @@ env: jobs: build-docs-book: name: Build and Deploy Zebra Book Docs - # Skip PRs from external repositories, let them pass, and then Mergify will check them + # Skip PRs from external repositories, let them pass, and then GitHub's Merge Queue will check them if: ${{ !startsWith(github.event_name, 'pull') || !github.event.pull_request.head.repo.fork }} timeout-minutes: 5 runs-on: ubuntu-latest @@ -85,14 +85,14 @@ jobs: pull-requests: write steps: - name: Checkout the source code - uses: actions/checkout@v4.2.1 + uses: actions/checkout@v4.2.2 with: persist-credentials: false - uses: r7kamura/rust-problem-matchers@v1.5.0 - name: Setup mdBook - uses: jontze/action-mdbook@v3.0.0 + uses: jontze/action-mdbook@v4.0.0 with: token: ${{ secrets.GITHUB_TOKEN }} mdbook-version: '~0.4' @@ -105,8 +105,9 @@ jobs: # Setup gcloud CLI - name: Authenticate to Google Cloud + if: github.repository_owner == 'ZcashFoundation' id: auth - uses: google-github-actions/auth@v2.1.6 + uses: google-github-actions/auth@v2.1.10 with: workload_identity_provider: '${{ vars.GCP_WIF }}' service_account: '${{ vars.GCP_FIREBASE_SA }}' @@ -114,11 +115,13 @@ jobs: # TODO: remove this step after issue https://github.com/FirebaseExtended/action-hosting-deploy/issues/174 is fixed - name: Add $GCP_FIREBASE_SA_PATH to env + if: github.repository_owner == 'ZcashFoundation' run: | # shellcheck disable=SC2002 echo "GCP_FIREBASE_SA_PATH=$(cat ${{ steps.auth.outputs.credentials_file_path }} | tr -d '\n')" >> "$GITHUB_ENV" - name: Deploy Zebra book to firebase + if: github.repository_owner == 'ZcashFoundation' uses: FirebaseExtended/action-hosting-deploy@v0.9.0 with: firebaseServiceAccount: ${{ env.GCP_FIREBASE_SA_PATH }} @@ -138,7 +141,7 @@ jobs: pull-requests: write steps: - name: Checkout the source code - uses: actions/checkout@v4.2.1 + uses: actions/checkout@v4.2.2 with: persist-credentials: false @@ -155,7 +158,7 @@ jobs: run: | curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain=beta --profile=default - - uses: Swatinem/rust-cache@v2.7.5 + - uses: Swatinem/rust-cache@v2.7.8 - name: Build internal docs run: | @@ -163,19 +166,22 @@ jobs: # Setup gcloud CLI - name: Authenticate to Google Cloud + if: github.repository_owner == 'ZcashFoundation' id: auth - uses: google-github-actions/auth@v2.1.6 + uses: google-github-actions/auth@v2.1.10 with: workload_identity_provider: '${{ vars.GCP_WIF }}' service_account: '${{ vars.GCP_FIREBASE_SA }}' # TODO: remove this step after issue https://github.com/FirebaseExtended/action-hosting-deploy/issues/174 is fixed - name: Add $GCP_FIREBASE_SA_PATH to env + if: github.repository_owner == 'ZcashFoundation' run: | # shellcheck disable=SC2002 echo "GCP_FIREBASE_SA_PATH=$(cat ${{ steps.auth.outputs.credentials_file_path }} | tr -d '\n')" >> "$GITHUB_ENV" - name: Deploy internal docs to firebase + if: github.repository_owner == 'ZcashFoundation' uses: FirebaseExtended/action-hosting-deploy@v0.9.0 with: firebaseServiceAccount: ${{ env.GCP_FIREBASE_SA_PATH }} diff --git a/.github/workflows/docs-dockerhub-description.yml.disabled b/.github/workflows/docs-dockerhub-description.yml.disabled index b96a2e2fb1c..5d0367b6037 100644 --- a/.github/workflows/docs-dockerhub-description.yml.disabled +++ b/.github/workflows/docs-dockerhub-description.yml.disabled @@ -15,14 +15,15 @@ on: jobs: dockerHubDescription: + if: github.repository_owner == 'ZcashFoundation' runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4.2.1 + - uses: actions/checkout@v4.2.2 with: persist-credentials: false - name: Docker Hub Description - uses: peter-evans/dockerhub-description@v4.0.0 + uses: peter-evans/dockerhub-description@v4.0.2 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_PASSWORD }} diff --git a/.github/workflows/manual-zcashd-deploy.yml.disabled b/.github/workflows/manual-zcashd-deploy.yml.disabled index 05872f2532d..f96f6e482ca 100644 --- a/.github/workflows/manual-zcashd-deploy.yml.disabled +++ b/.github/workflows/manual-zcashd-deploy.yml.disabled @@ -29,12 +29,12 @@ jobs: id-token: 'write' steps: - - uses: actions/checkout@v4.2.1 + - uses: actions/checkout@v4.2.2 with: persist-credentials: false - name: Inject slug/short variables - uses: rlespinasse/github-slug-action@v4 + uses: rlespinasse/github-slug-action@v5 with: short-length: 7 @@ -52,20 +52,20 @@ jobs: # Setup gcloud CLI - name: Authenticate to Google Cloud id: auth - uses: google-github-actions/auth@v2.1.6 + uses: google-github-actions/auth@v2.1.10 with: workload_identity_provider: '${{ vars.GCP_WIF }}' service_account: '${{ vars.GCP_DEPLOYMENTS_SA }}' - name: Set up Cloud SDK - uses: google-github-actions/setup-gcloud@v2.1.1 + uses: google-github-actions/setup-gcloud@v2.1.4 # Create instance template from container image - name: Create instance template run: | gcloud compute instance-templates create-with-container zcashd-${{ env.GITHUB_REF_SLUG_URL }}-${{ env.GITHUB_SHA_SHORT }} \ - --boot-disk-size 10GB \ - --boot-disk-type=pd-ssd \ + --boot-disk-size=10GB \ + --boot-disk-type=pd-standard \ --image-project=cos-cloud \ --image-family=cos-stable \ --container-stdin \ @@ -73,7 +73,7 @@ jobs: --container-image electriccoinco/zcashd \ --container-env ZCASHD_NETWORK="${{ inputs.network }}" \ --machine-type ${{ vars.GCP_SMALL_MACHINE }} \ - --network-interface=subnet=${{ vars.GCP_SUBNETWORK }} \ + --subnet=${{ vars.GCP_SUBNETWORK }} \ --service-account ${{ vars.GCP_DEPLOYMENTS_SA }} \ --scopes cloud-platform \ --labels=app=zcashd,environment=prod,network=${NETWORK},github_ref=${{ env.GITHUB_REF_SLUG_URL }} \ diff --git a/.github/workflows/release-binaries.yml.disabled b/.github/workflows/release-binaries.yml.disabled index 1cf4e6b5da7..f2c915c86f8 100644 --- a/.github/workflows/release-binaries.yml.disabled +++ b/.github/workflows/release-binaries.yml.disabled @@ -15,36 +15,14 @@ on: - released jobs: - # Each time this workflow is executed, a build will be triggered to create a new image - # with the corresponding tags using information from git - - # The image will be named `zebra:-experimental` - build-experimental: - name: Build Experimental Features Release Docker - uses: ./.github/workflows/sub-build-docker-image.yml - with: - dockerfile_path: ./docker/Dockerfile - dockerfile_target: runtime - image_name: zebra - tag_suffix: -experimental - features: ${{ format('{0} {1}', vars.RUST_PROD_FEATURES, vars.RUST_EXPERIMENTAL_FEATURES) }} - rust_log: ${{ vars.RUST_LOG }} - # This step needs access to Docker Hub secrets to run successfully - secrets: inherit - # The image will be named `zebra:` - # It should be built last, so tags with the same name point to the production build, not the experimental build. build: name: Build Release Docker - # Run this build last, regardless of whether experimental worked - needs: build-experimental - if: always() uses: ./.github/workflows/sub-build-docker-image.yml with: dockerfile_path: ./docker/Dockerfile dockerfile_target: runtime image_name: zebra - latest_tag: true features: ${{ vars.RUST_PROD_FEATURES }} rust_log: ${{ vars.RUST_LOG }} # This step needs access to Docker Hub secrets to run successfully @@ -53,7 +31,7 @@ jobs: failure-issue: name: Open or update issues for release binaries failures # When a new job is added to this workflow, add it to this list. - needs: [ build, build-experimental ] + needs: [ build ] # Open tickets for any failed build in this workflow. if: failure() || cancelled() runs-on: ubuntu-latest diff --git a/.github/workflows/release-drafter.yml.disabled b/.github/workflows/release-drafter.yml.disabled index fd4c39a728c..3538c4da913 100644 --- a/.github/workflows/release-drafter.yml.disabled +++ b/.github/workflows/release-drafter.yml.disabled @@ -16,8 +16,8 @@ on: - main # pull_request event is required only for autolabeler pull_request: - # Only following types are handled by the action, but one can default to all as well - #types: [opened, reopened, synchronize] + # Only following types are handled by the action + types: [opened, reopened, synchronize] # pull_request_target event is required for autolabeler to support PRs from forks pull_request_target: #types: [opened, reopened, synchronize] @@ -38,7 +38,7 @@ jobs: runs-on: ubuntu-latest steps: # Drafts your next Release notes - - uses: release-drafter/release-drafter@v6 + - uses: release-drafter/release-drafter@v6.1.0 with: config-name: release-drafter.yml commitish: main diff --git a/.github/workflows/scripts/gcp-get-cached-disks.sh b/.github/workflows/scripts/gcp-get-cached-disks.sh index 9716dc9f5a7..ade3285f430 100755 --- a/.github/workflows/scripts/gcp-get-cached-disks.sh +++ b/.github/workflows/scripts/gcp-get-cached-disks.sh @@ -3,9 +3,9 @@ # This script finds a cached Google Cloud Compute image based on specific criteria. # # If there are multiple disks: -# - if `PREFER_MAIN_CACHED_STATE` is "true", then select an image from the `main` branch, else -# - try to find a cached disk image from the current branch (or PR), else -# - try to find an image from any branch. +# - try to find a cached disk image from the current branch (or PR), +# - if no image was found, try to find an image from the `main` branch, +# - if no image was found, try to find an image from any branch. # # Within each of these categories: # - prefer newer images to older images @@ -47,12 +47,10 @@ if [[ -n "${DISK_PREFIX}" && -n "${DISK_SUFFIX}" ]]; then echo "Finding a ${DISK_PREFIX}-${DISK_SUFFIX} disk image for ${NETWORK}..." CACHED_DISK_NAME="" - # Try to find an image based on the `main` branch if that branch is preferred. - if [[ "${PREFER_MAIN_CACHED_STATE}" == "true" ]]; then - CACHED_DISK_NAME=$(find_cached_disk_image "main-[0-9a-f]+" "main branch") - fi - # If no image was found, try to find one from the current branch (or PR). - CACHED_DISK_NAME=${CACHED_DISK_NAME:-$(find_cached_disk_image ".+-${GITHUB_REF}" "branch")} + # Try to find one from the current branch (or PR). + CACHED_DISK_NAME=$(find_cached_disk_image ".+-${GITHUB_REF}" "branch") + # If no image was found, try to find an image based on the `main` branch. + CACHED_DISK_NAME=${CACHED_DISK_NAME:-$(find_cached_disk_image "main-[0-9a-f]+" "main branch")} # If we still have no image, try to find one from any branch. CACHED_DISK_NAME=${CACHED_DISK_NAME:-$(find_cached_disk_image ".+-[0-9a-f]+" "any branch")} diff --git a/.github/workflows/scripts/release-crates-dry-run.sh b/.github/workflows/scripts/release-crates-dry-run.sh deleted file mode 100755 index 9935e23a947..00000000000 --- a/.github/workflows/scripts/release-crates-dry-run.sh +++ /dev/null @@ -1,40 +0,0 @@ -#!/usr/bin/env bash -set -ex - -# Check if necessary tools are installed -if ! command -v git &>/dev/null || ! command -v cargo &>/dev/null; then - echo "ERROR: Required tools (git, cargo) are not installed." - exit 1 -fi - -git config --global user.email "release-tests-no-reply@zfnd.org" -git config --global user.name "Automated Release Test" - -# Ensure cargo-release is installed -if ! cargo release --version &>/dev/null; then - echo "ERROR: cargo release must be installed." - exit 1 -fi - -# Release process -# We use the same commands as the [release drafter](https://github.com/ZcashFoundation/zebra/blob/main/.github/PULL_REQUEST_TEMPLATE/release-checklist.md#update-crate-versions) -# with an extra `--no-confirm` argument for non-interactive testing. -# Update everything except for alpha crates and zebrad: -cargo release version --verbose --execute --no-confirm --allow-branch '*' --workspace --exclude zebrad --exclude zebra-scan --exclude zebra-grpc patch - -# Due to a bug in cargo-release, we need to pass exact versions for alpha crates: -cargo release version --verbose --execute --no-confirm --allow-branch '*' --package zebra-scan 0.1.0-alpha.10 -cargo release version --verbose --execute --no-confirm --allow-branch '*' --package zebra-grpc 0.1.0-alpha.8 - -# Update zebrad: -cargo release version --verbose --execute --no-confirm --allow-branch '*' --package zebrad patch -# Continue with the release process: -cargo release replace --verbose --execute --no-confirm --allow-branch '*' --package zebrad -cargo release commit --verbose --execute --no-confirm --allow-branch '*' - -# Dry run to check the release -# Workaround for unpublished dependency version errors: https://github.com/crate-ci/cargo-release/issues/691 -# TODO: check all crates after fixing these errors -cargo release publish --verbose --dry-run --allow-branch '*' --workspace --exclude zebra-consensus --exclude zebra-utils --exclude zebrad --exclude zebra-scan - -echo "Release process completed." diff --git a/.github/workflows/sub-build-docker-image.yml.disabled b/.github/workflows/sub-build-docker-image.yml.disabled index 11b6399d625..f40c07cf559 100644 --- a/.github/workflows/sub-build-docker-image.yml.disabled +++ b/.github/workflows/sub-build-docker-image.yml.disabled @@ -32,68 +32,56 @@ on: rust_log: required: false type: string - # defaults to: vars.RUST_PROD_FEATURES features: required: false type: string - # defaults to: vars.RUST_TEST_FEATURES (and entrypoint.sh adds vars.RUST_PROD_FEATURES) - test_features: - required: false - type: string - latest_tag: - required: false - type: boolean - default: false - tag_suffix: - required: false - type: string no_cache: - description: 'Disable the Docker cache for this build' + description: "Disable the Docker cache for this build" required: false type: boolean default: false outputs: image_digest: - description: 'The image digest to be used on a caller workflow' + description: "The image digest to be used on a caller workflow" value: ${{ jobs.build.outputs.image_digest }} - env: - FEATURES: ${{ inputs.features || vars.RUST_PROD_FEATURES }} - TEST_FEATURES: ${{ inputs.test_features || vars.RUST_TEST_FEATURES }} + FEATURES: ${{ inputs.features }} RUST_LOG: ${{ inputs.rust_log || vars.RUST_LOG }} CARGO_INCREMENTAL: ${{ vars.CARGO_INCREMENTAL }} jobs: build: name: Build images + if: github.repository_owner == 'ZcashFoundation' timeout-minutes: 210 runs-on: ubuntu-latest + environment: ${{ github.event_name == 'release' && 'prod' || 'dev' }} outputs: image_digest: ${{ steps.docker_build.outputs.digest }} image_name: ${{ fromJSON(steps.docker_build.outputs.metadata)['image.name'] }} permissions: - contents: 'read' - id-token: 'write' + contents: "read" + id-token: "write" pull-requests: write # for `docker-scout` to be able to write the comment env: DOCKER_BUILD_SUMMARY: ${{ vars.DOCKER_BUILD_SUMMARY }} steps: - - uses: actions/checkout@v4.2.1 + - uses: actions/checkout@v4.2.2 with: persist-credentials: false - uses: r7kamura/rust-problem-matchers@v1.5.0 - name: Inject slug/short variables - uses: rlespinasse/github-slug-action@v4 + uses: rlespinasse/github-slug-action@v5 with: short-length: 7 # Automatic tag management and OCI Image Format Specification for labels - name: Docker meta id: meta - uses: docker/metadata-action@v5.5.1 + uses: docker/metadata-action@v5.7.0 with: # list of Docker images to use as base name for tags # We only publish images to DockerHub if a release is not a pre-release @@ -101,50 +89,48 @@ jobs: images: | us-docker.pkg.dev/${{ vars.GCP_PROJECT }}/zebra/${{ inputs.image_name }} zfnd/${{ inputs.image_name }},enable=${{ github.event_name == 'release' && !github.event.release.prerelease }} - # appends inputs.tag_suffix to image tags/names - flavor: | - suffix=${{ inputs.tag_suffix }} - latest=${{ inputs.latest_tag }} # generate Docker tags based on the following events/attributes tags: | # These DockerHub release tags support the following use cases: - # - `latest`: always use the latest Zebra release when you pull or update - # - `v1.x.y` or `1.x.y`: always use the exact version, don't automatically upgrade + # - `latest`: Automatically points to the most recent Zebra release, ensuring users always get the latest stable version when pulling or updating. + # - `1.x.y`: Represents a specific semantic version (e.g., 1.2.3), allowing users to pin to an exact version for stability, preventing automatic upgrades. # - # `semver` adds a "latest" tag if `inputs.latest_tag` is `true`. type=semver,pattern={{version}} - type=ref,event=tag - # DockerHub release and CI tags. - # This tag makes sure tests are using exactly the right image, even when multiple PRs run at the same time. - type=sha,event=push - # These CI-only tags support CI on PRs, the main branch, and scheduled full syncs. - # These tags do not appear on DockerHub, because DockerHub images are only published on the release event. + # CI-only tags (not published to DockerHub, only in Google Artifact Registry): + # - `pr-xxx`: Tags images with the pull request number for CI tracking during PR workflows. + # - `branch-name`: Tags images with the branch name (e.g., `main`, `dev`) for CI builds on branch pushes. + # - `edge`: Tags the latest build on the default branch (e.g., `main`), used in CI to represent the cutting-edge version for testing. + # - `schedule`: Tags images built during scheduled workflows (e.g., nightly or periodic builds) for CI monitoring and testing. type=ref,event=pr type=ref,event=branch type=edge,enable={{is_default_branch}} type=schedule + # - `sha-xxxxxx`: Uses the commit SHA (shortened) to tag images for precise identification. + # Applied during pull requests and branch pushes to ensure CI tests use the exact image from the last commit. + type=sha,event=pr + type=sha,event=branch - name: Authenticate to Google Cloud id: auth - uses: google-github-actions/auth@v2.1.6 + uses: google-github-actions/auth@v2.1.10 with: - workload_identity_provider: '${{ vars.GCP_WIF }}' - service_account: '${{ vars.GCP_ARTIFACTS_SA }}' - token_format: 'access_token' + workload_identity_provider: "${{ vars.GCP_WIF }}" + service_account: "${{ vars.GCP_ARTIFACTS_SA }}" + token_format: "access_token" # Some builds might take over an hour, and Google's default lifetime duration for # an access token is 1 hour (3600s). We increase this to 3 hours (10800s) # as some builds take over an hour. access_token_lifetime: 10800s - name: Login to Google Artifact Registry - uses: docker/login-action@v3.3.0 + uses: docker/login-action@v3.4.0 with: registry: us-docker.pkg.dev username: oauth2accesstoken password: ${{ steps.auth.outputs.access_token }} - name: Login to DockerHub - uses: docker/login-action@v3.3.0 + uses: docker/login-action@v3.4.0 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} @@ -152,7 +138,7 @@ jobs: # Setup Docker Buildx to use Docker Build Cloud - name: Set up Docker Buildx id: buildx - uses: docker/setup-buildx-action@v3.7.1 + uses: docker/setup-buildx-action@v3.10.0 with: version: "lab:latest" driver: cloud @@ -161,7 +147,7 @@ jobs: # Build and push image to Google Artifact Registry, and possibly DockerHub - name: Build & push id: docker_build - uses: docker/build-push-action@v6.9.0 + uses: docker/build-push-action@v6.18.0 with: target: ${{ inputs.dockerfile_target }} context: . @@ -171,8 +157,8 @@ jobs: build-args: | SHORT_SHA=${{ env.GITHUB_SHA_SHORT }} RUST_LOG=${{ env.RUST_LOG }} + CARGO_INCREMENTAL=${{ env.CARGO_INCREMENTAL }} FEATURES=${{ env.FEATURES }} - TEST_FEATURES=${{ env.TEST_FEATURES }} push: true # It's recommended to build images with max-level provenance attestations # https://docs.docker.com/build/ci/github-actions/attestations/ @@ -193,7 +179,7 @@ jobs: # - `dev` for a pull request event - name: Docker Scout id: docker-scout - uses: docker/scout-action@v1.14.0 + uses: docker/scout-action@v1.18.1 # We only run Docker Scout on the `runtime` target, as the other targets are not meant to be released # and are commonly used for testing, and thus are ephemeral. # TODO: Remove the `contains` check once we have a better way to determine if just new vulnerabilities are present. diff --git a/.github/workflows/sub-ci-integration-tests-gcp.yml.disabled b/.github/workflows/sub-ci-integration-tests-gcp.yml.disabled index 3ff5ab1e79a..7a19cf965ad 100644 --- a/.github/workflows/sub-ci-integration-tests-gcp.yml.disabled +++ b/.github/workflows/sub-ci-integration-tests-gcp.yml.disabled @@ -1,5 +1,5 @@ # Google Cloud integration tests that run when Rust code or dependencies are modified, -# but only on PRs from the ZcashFoundation/zebra repository. (External PRs are tested by mergify.) +# but only on PRs from the ZcashFoundation/zebra repository. (External PRs are tested by GitHub's Merge Queue.) # # Specific conditions and dependencies are set for each job to ensure they are executed in the correct sequence and under the right circumstances. # Each test has a description of the conditions under which it runs. @@ -32,14 +32,10 @@ on: #! The job names in `ci-integration-tests-gcp.yml`, `ci-integration-tests-gcp.patch.yml` and #! `ci-integration-tests-gcp.patch-external.yml` must be kept in sync. #! -#! The test variables ZEBRA_CACHED_STATE_DIR and LIGHTWALLETD_DATA_DIR used in some steps are set in the +#! The test variables ZEBRA_CACHE_DIR and LWD_CACHE_DIR used in some steps are set in the #! `sub-deploy-integration-tests-gcp.yml` workflow file as inputs. If modified in this file, they must #! also be updated in the `sub-deploy-integration-tests-gcp.yml` file. jobs: - # to also run a job on Mergify head branches, - # add `|| (github.event_name == 'push' && startsWith(github.head_ref, 'mergify/merge-queue/'))`: - # https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#running-your-workflow-based-on-the-head-or-base-branch-of-a-pull-request-1 - # Check if the cached state disks used by the tests are available for the default network. # # The default network is mainnet unless a manually triggered workflow or repository variable @@ -48,7 +44,7 @@ jobs: # The outputs for this job have the same names as the workflow outputs in sub-find-cached-disks.yml get-available-disks: name: Check if cached state disks exist for ${{ inputs.network || vars.ZCASH_NETWORK }} - # Skip PRs from external repositories, let them pass, and then Mergify will check them + # Skip PRs from external repositories, let them pass, and then GitHub's Merge Queue will check them if: ${{ !startsWith(github.event_name, 'pull') || !github.event.pull_request.head.repo.fork }} uses: ./.github/workflows/sub-find-cached-disks.yml with: @@ -74,16 +70,18 @@ jobs: # - on request, using workflow_dispatch with regenerate-disks # # Note: the output from get-available-disks should match with the caller workflow inputs - regenerate-stateful-disks: + sync-to-mandatory-checkpoint: name: Zebra checkpoint needs: [get-available-disks] uses: ./.github/workflows/sub-deploy-integration-tests-gcp.yml if: ${{ !fromJSON(needs.get-available-disks.outputs.zebra_checkpoint_disk) || github.event.inputs.regenerate-disks == 'true' }} with: app_name: zebrad - test_id: sync-to-checkpoint + test_id: sync-to-mandatory-checkpoint test_description: Test sync up to mandatory checkpoint - test_variables: "-e NETWORK=${{ inputs.network || vars.ZCASH_NETWORK }} -e TEST_DISK_REBUILD=1 -e ZEBRA_FORCE_USE_COLOR=1 -e ZEBRA_CACHED_STATE_DIR=/var/cache/zebrad-cache" + test_variables: "NETWORK=${{ inputs.network || vars.ZCASH_NETWORK }},SYNC_TO_MANDATORY_CHECKPOINT=1,ZEBRA_CACHE_DIR=/home/zebra/.cache/zebra" + # This test commonly less than 3 hours by October 2024, but now it takes longer + is_long_test: true needs_zebra_state: false saves_to_disk: true force_save_to_disk: ${{ inputs.force_save_to_disk || false }} @@ -94,25 +92,25 @@ jobs: # but we don't want to cancel running syncs on `main` if a new PR gets merged, # because we might never get a finished sync. # - # See the concurrency comment on the zebrad test-full-sync job for details. + # See the concurrency comment on the zebrad sync-full-mainnet job for details. concurrency: - group: ${{ github.workflow }}−manual-${{ format('{0}', github.event.inputs.regenerate-disks == 'true') }}-regenerate-stateful-disks + group: ${{ github.workflow }}−manual-${{ format('{0}', github.event.inputs.regenerate-disks == 'true') }}-sync-to-mandatory-checkpoint cancel-in-progress: false # Test that Zebra syncs and fully validates a few thousand blocks from a cached mandatory checkpoint disk # # If the state version has changed, waits for the new cached state to be created. # Otherwise, if the state rebuild was skipped, runs immediately after the build job. - test-stateful-sync: + sync-past-mandatory-checkpoint: name: Zebra checkpoint update - needs: [regenerate-stateful-disks, get-available-disks] + needs: [sync-to-mandatory-checkpoint, get-available-disks] uses: ./.github/workflows/sub-deploy-integration-tests-gcp.yml - if: ${{ !cancelled() && !failure() && (fromJSON(needs.get-available-disks.outputs.zebra_checkpoint_disk) || needs.regenerate-stateful-disks.result == 'success') && github.event.inputs.regenerate-disks != 'true' && github.event.inputs.run-full-sync != 'true' && github.event.inputs.run-lwd-sync != 'true' }} + if: ${{ !cancelled() && !failure() && (fromJSON(needs.get-available-disks.outputs.zebra_checkpoint_disk) || needs.sync-to-mandatory-checkpoint.result == 'success') && github.event.inputs.regenerate-disks != 'true' && github.event.inputs.run-full-sync != 'true' && github.event.inputs.run-lwd-sync != 'true' }} with: app_name: zebrad - test_id: sync-past-checkpoint + test_id: sync-past-mandatory-checkpoint test_description: Test full validation sync from a cached state - test_variables: "-e NETWORK=${{ inputs.network || vars.ZCASH_NETWORK }} -e TEST_CHECKPOINT_SYNC=1 -e ZEBRA_FORCE_USE_COLOR=1 -e ZEBRA_CACHED_STATE_DIR=/var/cache/zebrad-cache" + test_variables: "NETWORK=${{ inputs.network || vars.ZCASH_NETWORK }},SYNC_PAST_MANDATORY_CHECKPOINT=1,ZEBRA_CACHE_DIR=/home/zebra/.cache/zebra" needs_zebra_state: true saves_to_disk: false disk_suffix: checkpoint @@ -131,18 +129,17 @@ jobs: # - in manual workflow runs, when run-full-sync is 'true' and network is 'Mainnet' # # Note: the output from get-available-disks should match with the caller workflow inputs - test-full-sync: + sync-full-mainnet: name: Zebra tip needs: [get-available-disks] uses: ./.github/workflows/sub-deploy-integration-tests-gcp.yml if: ${{ github.event_name == 'schedule' || !fromJSON(needs.get-available-disks.outputs.zebra_tip_disk) || (github.event.inputs.run-full-sync == 'true' && (inputs.network || vars.ZCASH_NETWORK) == 'Mainnet') }} with: app_name: zebrad - test_id: full-sync + test_id: sync-full-mainnet test_description: Test a full sync up to the tip - # The value of FULL_SYNC_MAINNET_TIMEOUT_MINUTES is currently ignored. # TODO: update the test to use {{ input.network }} instead? - test_variables: "-e NETWORK=Mainnet -e FULL_SYNC_MAINNET_TIMEOUT_MINUTES=0 -e ZEBRA_FORCE_USE_COLOR=1 -e ZEBRA_CACHED_STATE_DIR=/var/cache/zebrad-cache" + test_variables: NETWORK=Mainnet,SYNC_FULL_MAINNET_TIMEOUT_MINUTES=0,ZEBRA_CACHE_DIR=/home/zebra/.cache/zebra # This test runs for longer than 6 hours, so it needs multiple jobs is_long_test: true needs_zebra_state: false @@ -161,7 +158,7 @@ jobs: # TODO: # - allow multiple manual syncs on a branch by adding '-${{ github.run_id }}' when github.event.inputs.run-full-sync is true concurrency: - group: ${{ github.workflow }}−manual-${{ format('{0}', github.event.inputs.run-full-sync == 'true') }}-test-full-sync + group: ${{ github.workflow }}−manual-${{ format('{0}', github.event.inputs.run-full-sync == 'true') }}-sync-full-mainnet cancel-in-progress: false # Test that Zebra can sync to the chain tip, using a cached Zebra tip state, @@ -173,16 +170,16 @@ jobs: # # If the state version has changed, waits for the new cached state to be created. # Otherwise, if the state rebuild was skipped, runs immediately after the build job. - test-update-sync: + sync-update-mainnet: name: Zebra tip update - needs: [test-full-sync, get-available-disks] + needs: [sync-full-mainnet, get-available-disks] uses: ./.github/workflows/sub-deploy-integration-tests-gcp.yml - if: ${{ !cancelled() && !failure() && (fromJSON(needs.get-available-disks.outputs.zebra_tip_disk) || needs.test-full-sync.result == 'success') && github.event.inputs.regenerate-disks != 'true' && github.event.inputs.run-full-sync != 'true' && github.event.inputs.run-lwd-sync != 'true' }} + if: ${{ !cancelled() && !failure() && (fromJSON(needs.get-available-disks.outputs.zebra_tip_disk) || needs.sync-full-mainnet.result == 'success') && github.event.inputs.regenerate-disks != 'true' && github.event.inputs.run-full-sync != 'true' && github.event.inputs.run-lwd-sync != 'true' }} with: app_name: zebrad - test_id: update-to-tip + test_id: sync-update-mainnet test_description: Test syncing to tip with a Zebra tip state - test_variables: "-e NETWORK=${{ inputs.network || vars.ZCASH_NETWORK }} -e TEST_UPDATE_SYNC=1 -e ZEBRA_FORCE_USE_COLOR=1 -e ZEBRA_CACHED_STATE_DIR=/var/cache/zebrad-cache" + test_variables: "NETWORK=${{ inputs.network || vars.ZCASH_NETWORK }},SYNC_UPDATE_MAINNET=1,ZEBRA_CACHE_DIR=/home/zebra/.cache/zebra" needs_zebra_state: true # update the disk on every PR, to increase CI speed saves_to_disk: true @@ -203,19 +200,19 @@ jobs: # # If the state version has changed, waits for the new cached state to be created. # Otherwise, if the state rebuild was skipped, runs immediately after the build job. - checkpoints-mainnet: + generate-checkpoints-mainnet: name: Generate checkpoints mainnet - needs: [test-full-sync, get-available-disks] + needs: [sync-full-mainnet, get-available-disks] uses: ./.github/workflows/sub-deploy-integration-tests-gcp.yml - if: ${{ !cancelled() && !failure() && (fromJSON(needs.get-available-disks.outputs.zebra_tip_disk) || needs.test-full-sync.result == 'success') && github.event.inputs.regenerate-disks != 'true' && github.event.inputs.run-full-sync != 'true' && github.event.inputs.run-lwd-sync != 'true' }} + if: ${{ !cancelled() && !failure() && (fromJSON(needs.get-available-disks.outputs.zebra_tip_disk) || needs.sync-full-mainnet.result == 'success') && github.event.inputs.regenerate-disks != 'true' && github.event.inputs.run-full-sync != 'true' && github.event.inputs.run-lwd-sync != 'true' }} with: app_name: zebrad - test_id: checkpoints-mainnet + test_id: generate-checkpoints-mainnet test_description: Generate Zebra checkpoints on mainnet # TODO: update the test to use {{ input.network }} instead? - test_variables: "-e NETWORK=Mainnet -e GENERATE_CHECKPOINTS_MAINNET=1 -e ZEBRA_FORCE_USE_COLOR=1 -e ZEBRA_CACHED_STATE_DIR=/var/cache/zebrad-cache" + test_variables: NETWORK=Mainnet,GENERATE_CHECKPOINTS_MAINNET=1,ZEBRA_CACHE_DIR=/home/zebra/.cache/zebra needs_zebra_state: true - # test-update-sync updates the disk on every PR, so we don't need to do it here + # sync-update-mainnet updates the disk on every PR, so we don't need to do it here saves_to_disk: false height_grep_text: 'current_height.*=.*Height.*\(' secrets: inherit @@ -235,18 +232,17 @@ jobs: # - in manual workflow runs, when run-full-sync is 'true' and network is 'Testnet' # # Note: the output from get-available-disks-testnet should match with the caller workflow inputs - test-full-sync-testnet: + sync-full-testnet: name: Zebra tip on testnet needs: [get-available-disks-testnet] uses: ./.github/workflows/sub-deploy-integration-tests-gcp.yml if: ${{ (github.event_name == 'schedule' && vars.SCHEDULE_TESTNET_FULL_SYNC == 'true') || !fromJSON(needs.get-available-disks-testnet.outputs.zebra_tip_disk) || (github.event.inputs.run-full-sync == 'true' && (inputs.network || vars.ZCASH_NETWORK) == 'Testnet') }} with: app_name: zebrad - test_id: full-sync-testnet + test_id: sync-full-testnet test_description: Test a full sync up to the tip on testnet - # The value of FULL_SYNC_TESTNET_TIMEOUT_MINUTES is currently ignored. - test_variables: "-e NETWORK=Testnet -e FULL_SYNC_TESTNET_TIMEOUT_MINUTES=0 -e ZEBRA_FORCE_USE_COLOR=1 -e ZEBRA_CACHED_STATE_DIR=/var/cache/zebrad-cache" - network: "Testnet" + test_variables: NETWORK=Testnet,SYNC_FULL_TESTNET=1,ZEBRA_CACHE_DIR=/home/zebra/.cache/zebra + network: Testnet # A full testnet sync could take 2-10 hours in April 2023. # The time varies a lot due to the small number of nodes. is_long_test: true @@ -266,7 +262,7 @@ jobs: # TODO: # - allow multiple manual syncs on a branch by adding '-${{ github.run_id }}' when github.event.inputs.run-full-sync is true concurrency: - group: ${{ github.workflow }}−manual-${{ format('{0}', github.event.inputs.run-full-sync == 'true') }}-test-full-sync-testnet + group: ${{ github.workflow }}−manual-${{ format('{0}', github.event.inputs.run-full-sync == 'true') }}-sync-full-testnet cancel-in-progress: false # Test that Zebra can generate testnet checkpoints after syncing to the chain tip, @@ -280,20 +276,20 @@ jobs: # # If the state version has changed, waits for the new cached state to be created. # Otherwise, if the state rebuild was skipped, runs immediately after the build job. - checkpoints-testnet: + generate-checkpoints-testnet: name: Generate checkpoints testnet - needs: [test-full-sync-testnet, get-available-disks-testnet] + needs: [sync-full-testnet, get-available-disks-testnet] uses: ./.github/workflows/sub-deploy-integration-tests-gcp.yml - if: ${{ !cancelled() && !failure() && (fromJSON(needs.get-available-disks-testnet.outputs.zebra_tip_disk) || needs.test-full-sync-testnet.result == 'success') && github.event.inputs.regenerate-disks != 'true' && github.event.inputs.run-full-sync != 'true' && github.event.inputs.run-lwd-sync != 'true' }} + if: ${{ !cancelled() && !failure() && (fromJSON(needs.get-available-disks-testnet.outputs.zebra_tip_disk) || needs.sync-full-testnet.result == 'success') && github.event.inputs.regenerate-disks != 'true' && github.event.inputs.run-full-sync != 'true' && github.event.inputs.run-lwd-sync != 'true' }} with: app_name: zebrad - test_id: checkpoints-testnet + test_id: generate-checkpoints-testnet test_description: Generate Zebra checkpoints on testnet - test_variables: "-e NETWORK=Testnet -e GENERATE_CHECKPOINTS_TESTNET=1 -e ZEBRA_FORCE_USE_COLOR=1 -e ZEBRA_CACHED_STATE_DIR=/var/cache/zebrad-cache" - network: "Testnet" + test_variables: NETWORK=Testnet,GENERATE_CHECKPOINTS_TESTNET=1,ZEBRA_CACHE_DIR=/home/zebra/.cache/zebra + network: Testnet needs_zebra_state: true # update the disk on every PR, to increase CI speed - # we don't have a test-update-sync-testnet job, so we need to update the disk here + # we don't have a sync-update-mainnet-testnet job, so we need to update the disk here saves_to_disk: true force_save_to_disk: ${{ inputs.force_save_to_disk || false }} height_grep_text: 'zebra_tip_height.*=.*Height.*\(' @@ -306,21 +302,21 @@ jobs: # Runs: # - on schedule, as defined at the top of the workflow # - on every PR update, but only if the state version in constants.rs has no cached disk - # - in manual workflow runs, when run-lwd-sync is 'true' and network is 'Mainnet' (the network is required by the test-full-sync job) + # - in manual workflow runs, when run-lwd-sync is 'true' and network is 'Mainnet' (the network is required by the sync-full-mainnet job) # # If the state version has changed, waits for the new cached state to be created. # Otherwise, if the state rebuild was skipped, runs immediately after the build job. - lightwalletd-full-sync: + lwd-sync-full: name: lightwalletd tip - needs: [test-full-sync, get-available-disks] + needs: [sync-full-mainnet, get-available-disks] uses: ./.github/workflows/sub-deploy-integration-tests-gcp.yml # Currently the lightwalletd tests only work on Mainnet - if: ${{ !cancelled() && !failure() && (inputs.network || vars.ZCASH_NETWORK) == 'Mainnet' && (fromJSON(needs.get-available-disks.outputs.zebra_tip_disk) || needs.test-full-sync.result == 'success') && (github.event_name == 'schedule' || !fromJSON(needs.get-available-disks.outputs.lwd_tip_disk) || github.event.inputs.run-lwd-sync == 'true' ) }} + if: ${{ !cancelled() && !failure() && (inputs.network || vars.ZCASH_NETWORK) == 'Mainnet' && (fromJSON(needs.get-available-disks.outputs.zebra_tip_disk) || needs.sync-full-mainnet.result == 'success') && (github.event_name == 'schedule' || !fromJSON(needs.get-available-disks.outputs.lwd_tip_disk) || github.event.inputs.run-lwd-sync == 'true' ) }} with: app_name: lightwalletd - test_id: lwd-full-sync + test_id: lwd-sync-full test_description: Test lightwalletd full sync - test_variables: "-e NETWORK=${{ inputs.network || vars.ZCASH_NETWORK }} -e TEST_LWD_FULL_SYNC=1 -e ZEBRA_TEST_LIGHTWALLETD=1 -e ZEBRA_FORCE_USE_COLOR=1 -e ZEBRA_CACHED_STATE_DIR=/var/cache/zebrad-cache -e LIGHTWALLETD_DATA_DIR=/var/cache/lwd-cache" + test_variables: "NETWORK=${{ inputs.network || vars.ZCASH_NETWORK }},LWD_SYNC_FULL=1,ZEBRA_TEST_LIGHTWALLETD=1,ZEBRA_CACHE_DIR=/home/zebra/.cache/zebra,LWD_CACHE_DIR=/home/zebra/.cache/lwd" # This test runs for longer than 6 hours, so it needs multiple jobs is_long_test: true needs_zebra_state: true @@ -334,9 +330,9 @@ jobs: # but we don't want to cancel running syncs on `main` if a new PR gets merged, # because we might never get a finished sync. # - # See the concurrency comment on the zebrad test-full-sync job for details. + # See the concurrency comment on the zebrad sync-full-mainnet job for details. concurrency: - group: ${{ github.workflow }}−manual-${{ format('{0}', github.event.inputs.run-lwd-sync == 'true') }}-lightwalletd-full-sync + group: ${{ github.workflow }}−manual-${{ format('{0}', github.event.inputs.run-lwd-sync == 'true') }}-lwd-sync-full cancel-in-progress: false # Test update sync of lightwalletd with a lightwalletd and Zebra tip state @@ -346,16 +342,16 @@ jobs: # # If the state version has changed, waits for the new cached states to be created. # Otherwise, if the state rebuild was skipped, runs immediately after the build job. - lightwalletd-update-sync: + lwd-sync-update: name: lightwalletd tip update - needs: [lightwalletd-full-sync, get-available-disks] + needs: [lwd-sync-full, get-available-disks] uses: ./.github/workflows/sub-deploy-integration-tests-gcp.yml - if: ${{ !cancelled() && !failure() && (inputs.network || vars.ZCASH_NETWORK) == 'Mainnet' && (fromJSON(needs.get-available-disks.outputs.lwd_tip_disk) || needs.lightwalletd-full-sync.result == 'success') && github.event.inputs.regenerate-disks != 'true' && github.event.inputs.run-full-sync != 'true' && github.event.inputs.run-lwd-sync != 'true' }} + if: ${{ !cancelled() && !failure() && (inputs.network || vars.ZCASH_NETWORK) == 'Mainnet' && (fromJSON(needs.get-available-disks.outputs.lwd_tip_disk) || needs.lwd-sync-full.result == 'success') && github.event.inputs.regenerate-disks != 'true' && github.event.inputs.run-full-sync != 'true' && github.event.inputs.run-lwd-sync != 'true' }} with: app_name: lightwalletd - test_id: lwd-update-sync + test_id: lwd-sync-update test_description: Test lightwalletd update sync with both states - test_variables: "-e NETWORK=${{ inputs.network || vars.ZCASH_NETWORK }} -e TEST_LWD_UPDATE_SYNC=1 -e ZEBRA_TEST_LIGHTWALLETD=1 -e ZEBRA_FORCE_USE_COLOR=1 -e ZEBRA_CACHED_STATE_DIR=/var/cache/zebrad-cache -e LIGHTWALLETD_DATA_DIR=/var/cache/lwd-cache" + test_variables: "NETWORK=${{ inputs.network || vars.ZCASH_NETWORK }},LWD_SYNC_UPDATE=1,ZEBRA_TEST_LIGHTWALLETD=1,ZEBRA_CACHE_DIR=/home/zebra/.cache/zebra,LWD_CACHE_DIR=/home/zebra/.cache/lwd" needs_zebra_state: true needs_lwd_state: true saves_to_disk: true @@ -374,16 +370,16 @@ jobs: # Otherwise, if the state rebuild was skipped, runs immediately after the build job. # # TODO: move this job below the rest of the mainnet jobs that just use Zebra cached state - lightwalletd-rpc-test: + lwd-rpc-test: name: Zebra tip JSON-RPC - needs: [test-full-sync, get-available-disks] + needs: [sync-full-mainnet, get-available-disks] uses: ./.github/workflows/sub-deploy-integration-tests-gcp.yml - if: ${{ !cancelled() && !failure() && (inputs.network || vars.ZCASH_NETWORK) == 'Mainnet' && (fromJSON(needs.get-available-disks.outputs.zebra_tip_disk) || needs.test-full-sync.result == 'success') && github.event.inputs.regenerate-disks != 'true' && github.event.inputs.run-full-sync != 'true' && github.event.inputs.run-lwd-sync != 'true' }} + if: ${{ !cancelled() && !failure() && (inputs.network || vars.ZCASH_NETWORK) == 'Mainnet' && (fromJSON(needs.get-available-disks.outputs.zebra_tip_disk) || needs.sync-full-mainnet.result == 'success') && github.event.inputs.regenerate-disks != 'true' && github.event.inputs.run-full-sync != 'true' && github.event.inputs.run-lwd-sync != 'true' }} with: app_name: lightwalletd - test_id: fully-synced-rpc + test_id: lwd-rpc-test test_description: Test lightwalletd RPC with a Zebra tip state - test_variables: "-e NETWORK=${{ inputs.network || vars.ZCASH_NETWORK }} -e TEST_LWD_RPC_CALL=1 -e ZEBRA_TEST_LIGHTWALLETD=1 -e ZEBRA_FORCE_USE_COLOR=1 -e ZEBRA_CACHED_STATE_DIR=/var/cache/zebrad-cache" + test_variables: "NETWORK=${{ inputs.network || vars.ZCASH_NETWORK }},LWD_RPC_TEST=1,ZEBRA_TEST_LIGHTWALLETD=1,ZEBRA_CACHE_DIR=/home/zebra/.cache/zebra" needs_zebra_state: true saves_to_disk: false secrets: inherit @@ -396,16 +392,16 @@ jobs: # # If the state version has changed, waits for the new cached states to be created. # Otherwise, if the state rebuild was skipped, runs immediately after the build job. - lightwalletd-transactions-test: - name: lightwalletd tip send - needs: [lightwalletd-full-sync, get-available-disks] + lwd-rpc-send-tx: + name: Lightwalletd send transactions + needs: [lwd-sync-full, get-available-disks] uses: ./.github/workflows/sub-deploy-integration-tests-gcp.yml - if: ${{ !cancelled() && !failure() && (inputs.network || vars.ZCASH_NETWORK) == 'Mainnet' && (fromJSON(needs.get-available-disks.outputs.lwd_tip_disk) || needs.lightwalletd-full-sync.result == 'success') && github.event.inputs.regenerate-disks != 'true' && github.event.inputs.run-full-sync != 'true' && github.event.inputs.run-lwd-sync != 'true' }} + if: ${{ !cancelled() && !failure() && (inputs.network || vars.ZCASH_NETWORK) == 'Mainnet' && (fromJSON(needs.get-available-disks.outputs.lwd_tip_disk) || needs.lwd-sync-full.result == 'success') && github.event.inputs.regenerate-disks != 'true' && github.event.inputs.run-full-sync != 'true' && github.event.inputs.run-lwd-sync != 'true' }} with: app_name: lightwalletd - test_id: lwd-send-transactions + test_id: lwd-rpc-send-tx test_description: Test sending transactions via lightwalletd - test_variables: "-e NETWORK=${{ inputs.network || vars.ZCASH_NETWORK }} -e TEST_LWD_TRANSACTIONS=1 -e ZEBRA_TEST_LIGHTWALLETD=1 -e ZEBRA_FORCE_USE_COLOR=1 -e ZEBRA_CACHED_STATE_DIR=/var/cache/zebrad-cache -e LIGHTWALLETD_DATA_DIR=/var/cache/lwd-cache" + test_variables: "NETWORK=${{ inputs.network || vars.ZCASH_NETWORK }},LWD_RPC_SEND_TX=1,ZEBRA_TEST_LIGHTWALLETD=1,ZEBRA_CACHE_DIR=/home/zebra/.cache/zebra,LWD_CACHE_DIR=/home/zebra/.cache/lwd" needs_zebra_state: true needs_lwd_state: true saves_to_disk: false @@ -419,22 +415,22 @@ jobs: # # If the state version has changed, waits for the new cached states to be created. # Otherwise, if the state rebuild was skipped, runs immediately after the build job. - lightwalletd-grpc-test: + lwd-grpc-wallet: name: lightwalletd GRPC tests - needs: [lightwalletd-full-sync, get-available-disks] + needs: [lwd-sync-full, get-available-disks] uses: ./.github/workflows/sub-deploy-integration-tests-gcp.yml - if: ${{ !cancelled() && !failure() && (inputs.network || vars.ZCASH_NETWORK) == 'Mainnet' && (fromJSON(needs.get-available-disks.outputs.lwd_tip_disk) || needs.lightwalletd-full-sync.result == 'success') && github.event.inputs.regenerate-disks != 'true' && github.event.inputs.run-full-sync != 'true' && github.event.inputs.run-lwd-sync != 'true' }} + if: ${{ !cancelled() && !failure() && (inputs.network || vars.ZCASH_NETWORK) == 'Mainnet' && (fromJSON(needs.get-available-disks.outputs.lwd_tip_disk) || needs.lwd-sync-full.result == 'success') && github.event.inputs.regenerate-disks != 'true' && github.event.inputs.run-full-sync != 'true' && github.event.inputs.run-lwd-sync != 'true' }} with: app_name: lightwalletd test_id: lwd-grpc-wallet test_description: Test gRPC calls via lightwalletd - test_variables: "-e NETWORK=${{ inputs.network || vars.ZCASH_NETWORK }} -e TEST_LWD_GRPC=1 -e ZEBRA_TEST_LIGHTWALLETD=1 -e ZEBRA_FORCE_USE_COLOR=1 -e ZEBRA_CACHED_STATE_DIR=/var/cache/zebrad-cache -e LIGHTWALLETD_DATA_DIR=/var/cache/lwd-cache" + test_variables: "NETWORK=${{ inputs.network || vars.ZCASH_NETWORK }},LWD_GRPC_WALLET=1,ZEBRA_TEST_LIGHTWALLETD=1,ZEBRA_CACHE_DIR=/home/zebra/.cache/zebra,LWD_CACHE_DIR=/home/zebra/.cache/lwd" needs_zebra_state: true needs_lwd_state: true saves_to_disk: false secrets: inherit - ## getblocktemplate-rpcs using cached Zebra state on mainnet + ## getblocktemplate RPC tests using cached Zebra state on mainnet # # TODO: move these below the rest of the mainnet jobs that just use Zebra cached state @@ -446,16 +442,16 @@ jobs: # # If the state version has changed, waits for the new cached states to be created. # Otherwise, if the state rebuild was skipped, runs immediately after the build job. - get-block-template-test: + rpc-get-block-template: name: get block template - needs: [test-full-sync, get-available-disks] + needs: [sync-full-mainnet, get-available-disks] uses: ./.github/workflows/sub-deploy-integration-tests-gcp.yml - if: ${{ !cancelled() && !failure() && (fromJSON(needs.get-available-disks.outputs.zebra_tip_disk) || needs.test-full-sync.result == 'success') && github.event.inputs.regenerate-disks != 'true' && github.event.inputs.run-full-sync != 'true' && github.event.inputs.run-lwd-sync != 'true' }} + if: ${{ !cancelled() && !failure() && (fromJSON(needs.get-available-disks.outputs.zebra_tip_disk) || needs.sync-full-mainnet.result == 'success') && github.event.inputs.regenerate-disks != 'true' && github.event.inputs.run-full-sync != 'true' && github.event.inputs.run-lwd-sync != 'true' }} with: app_name: zebrad - test_id: get-block-template + test_id: rpc-get-block-template test_description: Test getblocktemplate RPC method via Zebra's rpc server - test_variables: "-e NETWORK=${{ inputs.network || vars.ZCASH_NETWORK }} -e TEST_GET_BLOCK_TEMPLATE=1 -e ZEBRA_FORCE_USE_COLOR=1 -e ZEBRA_CACHED_STATE_DIR=/var/cache/zebrad-cache" + test_variables: "NETWORK=${{ inputs.network || vars.ZCASH_NETWORK }},RPC_GET_BLOCK_TEMPLATE=1,ZEBRA_CACHE_DIR=/home/zebra/.cache/zebra" needs_zebra_state: true needs_lwd_state: false saves_to_disk: false @@ -469,62 +465,16 @@ jobs: # # If the state version has changed, waits for the new cached states to be created. # Otherwise, if the state rebuild was skipped, runs immediately after the build job. - submit-block-test: + rpc-submit-block: name: submit block - needs: [test-full-sync, get-available-disks] + needs: [sync-full-mainnet, get-available-disks] uses: ./.github/workflows/sub-deploy-integration-tests-gcp.yml - if: ${{ !cancelled() && !failure() && (fromJSON(needs.get-available-disks.outputs.zebra_tip_disk) || needs.test-full-sync.result == 'success') && github.event.inputs.regenerate-disks != 'true' && github.event.inputs.run-full-sync != 'true' && github.event.inputs.run-lwd-sync != 'true' }} + if: ${{ !cancelled() && !failure() && (fromJSON(needs.get-available-disks.outputs.zebra_tip_disk) || needs.sync-full-mainnet.result == 'success') && github.event.inputs.regenerate-disks != 'true' && github.event.inputs.run-full-sync != 'true' && github.event.inputs.run-lwd-sync != 'true' }} with: app_name: zebrad - test_id: submit-block + test_id: rpc-submit-block test_description: Test submitting blocks via Zebra's rpc server - test_variables: "-e NETWORK=${{ inputs.network || vars.ZCASH_NETWORK }} -e TEST_SUBMIT_BLOCK=1 -e ZEBRA_FORCE_USE_COLOR=1 -e ZEBRA_CACHED_STATE_DIR=/var/cache/zebrad-cache" - needs_zebra_state: true - needs_lwd_state: false - saves_to_disk: false - secrets: inherit - - # Test that the scanner can continue scanning where it was left when zebrad restarts. - # - # Runs: - # - after every PR is merged to `main` - # - on every PR update - # - # If the state version has changed, waits for the new cached states to be created. - # Otherwise, if the state rebuild was skipped, runs immediately after the build job. - scan-start-where-left-test: - name: Scan starts where left - needs: [test-full-sync, get-available-disks] - uses: ./.github/workflows/sub-deploy-integration-tests-gcp.yml - if: ${{ !cancelled() && !failure() && (fromJSON(needs.get-available-disks.outputs.zebra_tip_disk) || needs.test-full-sync.result == 'success') && github.event.inputs.regenerate-disks != 'true' && github.event.inputs.run-full-sync != 'true' && github.event.inputs.run-lwd-sync != 'true' }} - with: - app_name: zebra-scan - test_id: scan-start-where-left - test_description: Test that the scanner can continue scanning where it was left when zebrad restarts. - test_variables: "-e NETWORK=${{ inputs.network || vars.ZCASH_NETWORK }} -e TEST_SCAN_START_WHERE_LEFT=1 -e ZEBRA_FORCE_USE_COLOR=1 -e ZEBRA_CACHED_STATE_DIR=/var/cache/zebrad-cache" - needs_zebra_state: true - needs_lwd_state: false - saves_to_disk: true - secrets: inherit - - # Test that the scan task registers keys, deletes keys, and subscribes to results for keys while running. - # - # Runs: - # - after every PR is merged to `main` - # - on every PR update - # - # If the state version has changed, waits for the new cached states to be created. - # Otherwise, if the state rebuild was skipped, runs immediately after the build job. - scan-task-commands-test: - name: scan task commands - needs: [test-full-sync, get-available-disks] - uses: ./.github/workflows/sub-deploy-integration-tests-gcp.yml - if: ${{ !cancelled() && !failure() && (fromJSON(needs.get-available-disks.outputs.zebra_tip_disk) || needs.test-full-sync.result == 'success') && github.event.inputs.regenerate-disks != 'true' && github.event.inputs.run-full-sync != 'true' && github.event.inputs.run-lwd-sync != 'true' }} - with: - app_name: zebra-scan - test_id: scan-task-commands - test_description: Test that the scan task registers keys, deletes keys, and subscribes to results for keys while running. - test_variables: "-e NETWORK=${{ inputs.network || vars.ZCASH_NETWORK }} -e TEST_SCAN_TASK_COMMANDS=1 -e ZEBRA_FORCE_USE_COLOR=1 -e ZEBRA_CACHED_STATE_DIR=/var/cache/zebrad-cache" + test_variables: "NETWORK=${{ inputs.network || vars.ZCASH_NETWORK }},RPC_SUBMIT_BLOCK=1,ZEBRA_CACHE_DIR=/home/zebra/.cache/zebra" needs_zebra_state: true needs_lwd_state: false saves_to_disk: false @@ -538,23 +488,21 @@ jobs: # Testnet jobs are not in this list, because we expect testnet to fail occasionally. needs: [ - regenerate-stateful-disks, - test-full-sync, - lightwalletd-full-sync, - test-stateful-sync, - test-update-sync, - checkpoints-mainnet, - lightwalletd-update-sync, - lightwalletd-rpc-test, - lightwalletd-transactions-test, - lightwalletd-grpc-test, - get-block-template-test, - submit-block-test, - scan-start-where-left-test, - scan-task-commands-test, + sync-to-mandatory-checkpoint, + sync-full-mainnet, + lwd-sync-full, + sync-past-mandatory-checkpoint, + sync-update-mainnet, + generate-checkpoints-mainnet, + lwd-sync-update, + lwd-rpc-test, + lwd-rpc-send-tx, + lwd-grpc-wallet, + rpc-get-block-template, + rpc-submit-block, ] # Only open tickets for failed scheduled jobs, manual workflow runs, or `main` branch merges. - # (PR statuses are already reported in the PR jobs list, and checked by Mergify.) + # (PR statuses are already reported in the PR jobs list, and checked by GitHub's Merge Queue.) if: (failure() && github.event.pull_request == null) || (cancelled() && github.event.pull_request == null) runs-on: ubuntu-latest steps: diff --git a/.github/workflows/sub-ci-unit-tests-docker.yml.disabled b/.github/workflows/sub-ci-unit-tests-docker.yml.disabled index 3f80d24ebbd..505ae29fc6e 100644 --- a/.github/workflows/sub-ci-unit-tests-docker.yml.disabled +++ b/.github/workflows/sub-ci-unit-tests-docker.yml.disabled @@ -1,16 +1,14 @@ # Google Cloud unit tests that run when Rust code or dependencies are modified, -# but only on PRs from the ZcashFoundation/zebra repository. (External PRs are tested by mergify.) +# but only on PRs from the ZcashFoundation/zebra repository. (External PRs are tested by GitHub's Merge Queue.) # # This workflow is designed for running various unit tests within Docker containers. # Jobs: # 1. Builds a Docker image for tests, adaptable to the specified network (Mainnet or Testnet). # 2. 'test-all': Executes all Zebra tests, including normally ignored ones, in a Docker environment. -# 3. 'test-fake-activation-heights': Runs state tests with fake activation heights, isolating its build products. -# 4. 'test-empty-sync': Tests Zebra's ability to sync and checkpoint from an empty state. +# 3. 'state-fake-activation-heights': Runs state tests with fake activation heights, isolating its build products. +# 4. 'sync-large-checkpoints-empty': Tests Zebra's ability to sync and checkpoint from an empty state. # 5. 'test-lightwalletd-integration': Validates integration with 'lightwalletd' starting from an empty state. -# 6. 'test-configuration-file': Assesses the default Docker configuration for Zebra. -# 7. 'test-configuration-file-testnet': Checks the Docker image reconfiguration for the Testnet. -# 8. 'test-zebra-conf-path': Tests Zebra using a custom Docker configuration. +# 6. 'test-docker-configurations': Runs a matrix of configuration tests to validate various Docker configurations. name: Docker Unit Tests on: @@ -20,7 +18,7 @@ on: type: string network: type: string - default: 'Mainnet' + default: "Mainnet" no_cache: type: boolean default: false @@ -32,45 +30,37 @@ env: COLORBT_SHOW_HIDDEN: ${{ vars.COLORBT_SHOW_HIDDEN }} CARGO_INCREMENTAL: ${{ vars.CARGO_INCREMENTAL }} -#! IMPORTANT -#! -#! The job names in `ci-unit-tests-docker.yml`, `ci-unit-tests-docker.patch.yml` and -#! `ci-unit-tests-docker.patch-external.yml` must be kept in sync. jobs: - # Run all the zebra tests, including tests that are ignored by default. - # - # - We activate the gRPC feature to avoid recompiling `zebrad`, but we don't actually run any gRPC tests. test-all: name: Test all timeout-minutes: 180 - runs-on: ubuntu-latest-xl + runs-on: ${{ github.repository_owner == 'ZcashFoundation' && 'ubuntu-latest-xl' || 'ubuntu-latest' }} steps: - uses: r7kamura/rust-problem-matchers@v1.5.0 - name: Inject slug/short variables - uses: rlespinasse/github-slug-action@v4 + uses: rlespinasse/github-slug-action@v5 with: short-length: 7 - # Run unit, basic acceptance tests, and ignored tests, only showing command output if the test fails. - # - # If some tests hang, add "-- --nocapture" for just that test, or for all the tests. + # Run unit, basic acceptance tests, and ignored tests, only showing + # command output if the test fails. # - - name: Run zebrad tests - env: - NETWORK: ${{ inputs.network || vars.ZCASH_NETWORK }} + # If some tests hang, add "-- --nocapture" for just that test, or for all + # the tests. + - name: Run all tests run: | docker pull ${{ vars.GAR_BASE }}/${{ vars.CI_IMAGE_NAME }}@${{ inputs.image_digest }} - docker run --tty -e NETWORK -e RUN_ALL_TESTS=1 ${{ vars.GAR_BASE }}/${{ vars.CI_IMAGE_NAME }}@${{ inputs.image_digest }} - - # Run unit, basic acceptance tests, and ignored tests with experimental features. - # - - name: Run zebrad tests with experimental features - env: - NETWORK: ${{ inputs.network || vars.ZCASH_NETWORK }} - run: | - docker pull ${{ vars.GAR_BASE }}/${{ vars.CI_IMAGE_NAME }}@${{ inputs.image_digest }} - docker run --tty -e NETWORK -e RUN_ALL_EXPERIMENTAL_TESTS=1 ${{ vars.GAR_BASE }}/${{ vars.CI_IMAGE_NAME }}@${{ inputs.image_digest }} + docker run --tty \ + -e RUN_ALL_TESTS=1 \ + -e FEATURES="journald prometheus filter-reload" \ + -e NETWORK="${{ inputs.network || vars.ZCASH_NETWORK }}" \ + -e RUST_LOG=${{ env.RUST_LOG }} \ + -e RUST_BACKTRACE=${{ env.RUST_BACKTRACE }} \ + -e RUST_LIB_BACKTRACE=${{ env.RUST_LIB_BACKTRACE }} \ + -e COLORBT_SHOW_HIDDEN=${{ env.COLORBT_SHOW_HIDDEN }} \ + -e CARGO_INCREMENTAL=${{ env.CARGO_INCREMENTAL }} \ + ${{ vars.GAR_BASE }}/${{ vars.CI_IMAGE_NAME }}@${{ inputs.image_digest }} # Run state tests with fake activation heights. # @@ -81,7 +71,7 @@ jobs: # Also, we don't want to accidentally use the fake heights in other tests. # # (We activate the test features to avoid recompiling dependencies, but we don't actually run any gRPC tests.) - test-fake-activation-heights: + state-fake-activation-heights: name: Test with fake activation heights timeout-minutes: 60 runs-on: ubuntu-latest @@ -89,7 +79,7 @@ jobs: - uses: r7kamura/rust-problem-matchers@v1.5.0 - name: Inject slug/short variables - uses: rlespinasse/github-slug-action@v4 + uses: rlespinasse/github-slug-action@v5 with: short-length: 7 @@ -98,10 +88,18 @@ jobs: NETWORK: ${{ inputs.network || vars.ZCASH_NETWORK }} run: | docker pull ${{ vars.GAR_BASE }}/${{ vars.CI_IMAGE_NAME }}@${{ inputs.image_digest }} - docker run --tty -e NETWORK -e TEST_FAKE_ACTIVATION_HEIGHTS=1 ${{ vars.GAR_BASE }}/${{ vars.CI_IMAGE_NAME }}@${{ inputs.image_digest }} + docker run --tty \ + -e STATE_FAKE_ACTIVATION_HEIGHTS=1 \ + -e NETWORK="${{ inputs.network || vars.ZCASH_NETWORK }}" \ + -e RUST_LOG=${{ env.RUST_LOG }} \ + -e RUST_BACKTRACE=${{ env.RUST_BACKTRACE }} \ + -e RUST_LIB_BACKTRACE=${{ env.RUST_LIB_BACKTRACE }} \ + -e COLORBT_SHOW_HIDDEN=${{ env.COLORBT_SHOW_HIDDEN }} \ + -e CARGO_INCREMENTAL=${{ env.CARGO_INCREMENTAL }} \ + ${{ vars.GAR_BASE }}/${{ vars.CI_IMAGE_NAME }}@${{ inputs.image_digest }} # Test that Zebra syncs and checkpoints a few thousand blocks from an empty state. - test-empty-sync: + sync-large-checkpoints-empty: name: Test checkpoint sync from empty state timeout-minutes: 60 runs-on: ubuntu-latest @@ -109,7 +107,7 @@ jobs: - uses: r7kamura/rust-problem-matchers@v1.5.0 - name: Inject slug/short variables - uses: rlespinasse/github-slug-action@v4 + uses: rlespinasse/github-slug-action@v5 with: short-length: 7 @@ -118,18 +116,26 @@ jobs: NETWORK: ${{ inputs.network || vars.ZCASH_NETWORK }} run: | docker pull ${{ vars.GAR_BASE }}/${{ vars.CI_IMAGE_NAME }}@${{ inputs.image_digest }} - docker run --tty -e NETWORK -e TEST_ZEBRA_EMPTY_SYNC=1 ${{ vars.GAR_BASE }}/${{ vars.CI_IMAGE_NAME }}@${{ inputs.image_digest }} + docker run --tty \ + -e SYNC_LARGE_CHECKPOINTS_EMPTY=1 \ + -e NETWORK="${{ inputs.network || vars.ZCASH_NETWORK }}" \ + -e RUST_LOG=${{ env.RUST_LOG }} \ + -e RUST_BACKTRACE=${{ env.RUST_BACKTRACE }} \ + -e RUST_LIB_BACKTRACE=${{ env.RUST_LIB_BACKTRACE }} \ + -e COLORBT_SHOW_HIDDEN=${{ env.COLORBT_SHOW_HIDDEN }} \ + -e CARGO_INCREMENTAL=${{ env.CARGO_INCREMENTAL }} \ + ${{ vars.GAR_BASE }}/${{ vars.CI_IMAGE_NAME }}@${{ inputs.image_digest }} # Test launching lightwalletd with an empty lightwalletd and Zebra state. test-lightwalletd-integration: - name: Test integration with lightwalletd + name: Lightwalletd integration timeout-minutes: 60 runs-on: ubuntu-latest steps: - uses: r7kamura/rust-problem-matchers@v1.5.0 - name: Inject slug/short variables - uses: rlespinasse/github-slug-action@v4 + uses: rlespinasse/github-slug-action@v5 with: short-length: 7 @@ -138,42 +144,20 @@ jobs: NETWORK: ${{ inputs.network || vars.ZCASH_NETWORK }} run: | docker pull ${{ vars.GAR_BASE }}/${{ vars.CI_IMAGE_NAME }}@${{ inputs.image_digest }} - docker run --tty -e NETWORK -e ZEBRA_TEST_LIGHTWALLETD=1 ${{ vars.GAR_BASE }}/${{ vars.CI_IMAGE_NAME }}@${{ inputs.image_digest }} - - # Test that Zebra works using the default config with the latest Zebra version. - test-configuration-file: - name: Test CI default Docker config file + docker run --tty \ + -e ZEBRA_TEST_LIGHTWALLETD=1 \ + -e LIGHTWALLETD_INTEGRATION=1 \ + -e NETWORK="${{ inputs.network || vars.ZCASH_NETWORK }}" \ + -e RUST_LOG=${{ env.RUST_LOG }} \ + -e RUST_BACKTRACE=${{ env.RUST_BACKTRACE }} \ + ${{ vars.GAR_BASE }}/${{ vars.CI_IMAGE_NAME }}@${{ inputs.image_digest }} + + # Run a matrix of configuration tests against the Docker image + test-docker-configurations: + name: Test Zebra Docker configurations uses: ./.github/workflows/sub-test-zebra-config.yml with: - test_id: 'default-conf' docker_image: ${{ vars.GAR_BASE }}/${{ vars.CI_IMAGE_NAME }}@${{ inputs.image_digest }} - grep_patterns: '-e "net.*=.*Main.*estimated progress to chain tip.*BeforeOverwinter"' - test_variables: '-e NETWORK' - network: 'Mainnet' - - # Test reconfiguring the the docker image for tesnet. - test-configuration-file-testnet: - name: Test CI testnet Docker config file - # Make sure Zebra can sync the genesis block on testnet - uses: ./.github/workflows/sub-test-zebra-config.yml - with: - test_id: 'testnet-conf' - docker_image: ${{ vars.GAR_BASE }}/${{ vars.CI_IMAGE_NAME }}@${{ inputs.image_digest }} - grep_patterns: '-e "net.*=.*Test.*estimated progress to chain tip.*Genesis" -e "net.*=.*Test.*estimated progress to chain tip.*BeforeOverwinter"' - # TODO: improve the entrypoint to avoid using `ENTRYPOINT_FEATURES=""` - test_variables: '-e NETWORK -e ZEBRA_CONF_PATH="/etc/zebrad/zebrad.toml" -e ENTRYPOINT_FEATURES=""' - network: 'Testnet' - - # Test that Zebra works using $ZEBRA_CONF_PATH config - test-zebra-conf-path: - name: Test CI custom Docker config file - uses: ./.github/workflows/sub-test-zebra-config.yml - with: - test_id: 'custom-conf' - docker_image: ${{ vars.GAR_BASE }}/${{ vars.CI_IMAGE_NAME }}@${{ inputs.image_digest }} - grep_patterns: '-e "loaded zebrad config.*config_path.*=.*v1.0.0-rc.2.toml"' - test_variables: '-e NETWORK -e ZEBRA_CONF_PATH="zebrad/tests/common/configs/v1.0.0-rc.2.toml"' - network: ${{ inputs.network || vars.ZCASH_NETWORK }} failure-issue: name: Open or update issues for main branch failures @@ -181,9 +165,16 @@ jobs: # # This list is for reliable tests that are run on the `main` branch. # Testnet jobs are not in this list, because we expect testnet to fail occasionally. - needs: [ test-all, test-fake-activation-heights, test-empty-sync, test-lightwalletd-integration, test-configuration-file, test-zebra-conf-path ] + needs: + [ + test-all, + state-fake-activation-heights, + sync-large-checkpoints-empty, + test-lightwalletd-integration, + test-docker-configurations, + ] # Only open tickets for failed scheduled jobs, manual workflow runs, or `main` branch merges. - # (PR statuses are already reported in the PR jobs list, and checked by Mergify.) + # (PR statuses are already reported in the PR jobs list, and checked by GitHub's Merge Queue.) # TODO: if a job times out, we want to create a ticket. Does failure() do that? Or do we need cancelled()? if: failure() && github.event.pull_request == null runs-on: ubuntu-latest @@ -197,3 +188,19 @@ jobs: # If there is already an open issue with this label, any failures become comments on that issue. always-create-new-issue: false github-token: ${{ secrets.GITHUB_TOKEN }} + + check-no-git-dependencies: + if: contains(github.event.pull_request.labels.*.name, 'A-release') + runs-on: ubuntu-latest + steps: + - name: Run check_no_git_refs_in_cargo_lock + run: | + docker pull ${{ vars.GAR_BASE }}/${{ vars.CI_IMAGE_NAME }}@${{ inputs.image_digest }} + docker run --tty \ + -e CHECK_NO_GIT_DEPENDENCIES=1 \ + -e NETWORK="${{ inputs.network || vars.ZCASH_NETWORK }}" \ + -e RUST_LOG=${{ env.RUST_LOG }} \ + -e RUST_BACKTRACE=${{ env.RUST_BACKTRACE }} \ + -e RUST_LIB_BACKTRACE=${{ env.RUST_LIB_BACKTRACE }} \ + -e COLORBT_SHOW_HIDDEN=${{ env.COLORBT_SHOW_HIDDEN }} \ + ${{ vars.GAR_BASE }}/${{ vars.CI_IMAGE_NAME }}@${{ inputs.image_digest }} diff --git a/.github/workflows/sub-deploy-integration-tests-gcp.yml.disabled b/.github/workflows/sub-deploy-integration-tests-gcp.yml.disabled index 7266f60ea54..cead78679b9 100644 --- a/.github/workflows/sub-deploy-integration-tests-gcp.yml.disabled +++ b/.github/workflows/sub-deploy-integration-tests-gcp.yml.disabled @@ -38,12 +38,12 @@ on: zebra_state_dir: required: false type: string - default: '/var/cache/zebrad-cache' + default: '/home/zebra/.cache/zebra' description: 'Zebra cached state directory and input image prefix to search in GCP' lwd_state_dir: required: false type: string - default: '/var/cache/lwd-cache' + default: '/home/zebra/.cache/lwd' description: 'Lightwalletd cached state directory and input image prefix to search in GCP' disk_prefix: required: false @@ -64,11 +64,6 @@ on: type: boolean description: 'Does the test use Lightwalletd and Zebra cached state?' # main branch states can be outdated and slower, but they can also be more reliable - prefer_main_cached_state: - required: false - type: boolean - default: false - description: 'Does the test prefer to use a main branch cached state?' saves_to_disk: required: true type: boolean @@ -85,6 +80,11 @@ on: description: 'Application name, used to work out when a job is an update job' env: + RUST_LOG: ${{ vars.RUST_LOG }} + RUST_BACKTRACE: ${{ vars.RUST_BACKTRACE }} + RUST_LIB_BACKTRACE: ${{ vars.RUST_LIB_BACKTRACE }} + COLORBT_SHOW_HIDDEN: ${{ vars.COLORBT_SHOW_HIDDEN }} + CARGO_INCREMENTAL: ${{ vars.CARGO_INCREMENTAL }} # How many previous log lines we show at the start of each new log job. # Increase this number if some log lines are skipped between jobs # @@ -95,7 +95,6 @@ env: # How many blocks to wait before creating an updated cached state image. # 1 day is approximately 1152 blocks. CACHED_STATE_UPDATE_LIMIT: 576 - jobs: # Find a cached state disk for ${{ inputs.test_id }}, matching all of: # - disk cached state prefix -> zebrad-cache or lwd-cache @@ -113,12 +112,11 @@ jobs: get-disk-name: name: Get disk name uses: ./.github/workflows/sub-find-cached-disks.yml - if: ${{ inputs.needs_zebra_state || inputs.needs_lwd_state }} + if: ${{ (inputs.needs_zebra_state || inputs.needs_lwd_state) || (inputs.saves_to_disk || inputs.force_save_to_disk) }} with: network: ${{ inputs.network || vars.ZCASH_NETWORK }} disk_prefix: ${{ inputs.needs_lwd_state && 'lwd-cache' || inputs.needs_zebra_state && 'zebrad-cache' }} - disk_suffix: ${{ inputs.disk_suffix }} - prefer_main_cached_state: ${{ inputs.prefer_main_cached_state }} + disk_suffix: ${{ (inputs.needs_zebra_state || inputs.needs_lwd_state) && inputs.disk_suffix || '' }} test_id: ${{ inputs.test_id }} # Show all the test logs, then follow the logs of the test we just launched, until it finishes. @@ -129,25 +127,26 @@ jobs: name: Run ${{ inputs.test_id }} test runs-on: zfnd-runners needs: [ get-disk-name ] - if: ${{ !cancelled() && !failure() }} + if: ${{ !cancelled() && !failure() && (needs.get-disk-name.result == 'success' || needs.get-disk-name.result == 'skipped') }} timeout-minutes: ${{ inputs.is_long_test && 7200 || 180 }} outputs: - cached_disk_name: ${{ needs.get-disk-name.outputs.cached_disk_name }} - state_version: ${{ needs.get-disk-name.outputs.state_version }} + cached_disk_name: ${{ (inputs.needs_zebra_state || inputs.needs_lwd_state) && needs.get-disk-name.outputs.cached_disk_name || '' }} + state_version: ${{ (inputs.needs_zebra_state || inputs.needs_lwd_state) && needs.get-disk-name.outputs.state_version || '' }} + container_id: ${{ steps.find-container.outputs.CONTAINER_ID }} env: - CACHED_DISK_NAME: ${{ needs.get-disk-name.outputs.cached_disk_name }} + CACHED_DISK_NAME: ${{ (inputs.needs_zebra_state || inputs.needs_lwd_state) && needs.get-disk-name.outputs.cached_disk_name || '' }} permissions: contents: 'read' id-token: 'write' steps: - - uses: actions/checkout@v4.2.1 + - uses: actions/checkout@v4.2.2 with: persist-credentials: false fetch-depth: '2' - uses: r7kamura/rust-problem-matchers@v1.5.0 - name: Inject slug/short variables - uses: rlespinasse/github-slug-action@v4 + uses: rlespinasse/github-slug-action@v5 with: short-length: 7 @@ -172,123 +171,81 @@ jobs: # Setup gcloud CLI - name: Authenticate to Google Cloud id: auth - uses: google-github-actions/auth@v2.1.6 + uses: google-github-actions/auth@v2.1.10 with: workload_identity_provider: '${{ vars.GCP_WIF }}' service_account: '${{ vars.GCP_DEPLOYMENTS_SA }}' - name: Set up Cloud SDK - uses: google-github-actions/setup-gcloud@v2.1.1 + uses: google-github-actions/setup-gcloud@v2.1.4 - # Create a Compute Engine virtual machine and attach a cached state disk using the - # $CACHED_DISK_NAME env as the source image to populate the disk cached state - # if the test needs it. + # Disk Mounting Logic Explanation: + # + # The following step creates a GCP instance using create-with-container. + # The $CONTAINER_MOUNT_DISKS variable, constructed within the run script, + # defines how the created persistent disk (specified in $DISK_PARAMS) + # is mounted into the test container using --container-mount-disk. + # + # If the test needs Lightwalletd state (inputs.needs_lwd_state is true or test_id is lwd-sync-full), + # the same persistent disk is mounted to both the Zebra state path (inputs.zebra_state_dir) + # and the Lightwalletd state path (inputs.lwd_state_dir). + # + # Using a single disk simplifies the VM and container setup. + # Mounting the same disk to multiple paths doesn't cause conflicts because Zebra and + # lightwalletd create different subdirectories for their data within the mounted volume. + # (However, Zebra, lightwalletd, and the test harness must not delete the whole cache directory root.) + # + # The container mount paths (inputs.zebra_state_dir and inputs.lwd_state_dir) must match + # the paths expected by the tests in Rust (also configured in ci-unit-tests-docker.yml). + # The application inside the container will typically use environment variables (like those set in + # $CONTAINER_ENV) or these known paths to access the state data. - name: Create ${{ inputs.test_id }} GCP compute instance id: create-instance - shell: /usr/bin/bash -x {0} run: | NAME="${{ inputs.test_id }}-${{ env.GITHUB_SHA_SHORT }}" - DISK_PARAMS="size=400GB,type=pd-ssd,name=${NAME},device-name=${NAME}" + DISK_PARAMS="size=400GB,type=pd-balanced,name=${NAME},device-name=${NAME}" if [ -n "${{ env.CACHED_DISK_NAME }}" ]; then DISK_PARAMS+=",image=${{ env.CACHED_DISK_NAME }}" fi + + # Mount the disk(s) to the container + CONTAINER_MOUNT_DISKS="--container-mount-disk=mount-path=${{ inputs.zebra_state_dir }},name=${NAME},mode=rw" + # Mount the same disk to the lwd path if needed + if [[ "${{ inputs.needs_lwd_state }}" == "true" || "${{ inputs.test_id }}" == "lwd-sync-full" ]]; then + CONTAINER_MOUNT_DISKS+=" --container-mount-disk=mount-path=${{ inputs.lwd_state_dir }},name=${NAME},mode=rw" + fi + + # Environment variables for the container + CONTAINER_ENV="${{ inputs.test_variables }},RUST_LOG=${{ env.RUST_LOG }},RUST_BACKTRACE=${{ env.RUST_BACKTRACE }},RUST_LIB_BACKTRACE=${{ env.RUST_LIB_BACKTRACE }},COLORBT_SHOW_HIDDEN=${{ env.COLORBT_SHOW_HIDDEN }},CARGO_INCREMENTAL=${{ env.CARGO_INCREMENTAL }}" + + # Trim whitespace from GAR_BASE as for some reason it's getting a trailing space + GAR_BASE_TRIMMED=$(echo "${{ vars.GAR_BASE }}" | xargs) + gcloud compute instances create-with-container "${{ inputs.test_id }}-${{ env.GITHUB_REF_SLUG_URL }}-${{ env.GITHUB_SHA_SHORT }}" \ - --boot-disk-size 50GB \ - --boot-disk-type pd-ssd \ + --machine-type ${{ inputs.is_long_test && vars.GCP_LARGE_MACHINE || vars.GCP_SMALL_MACHINE }} \ + --boot-disk-size=50GB \ + --boot-disk-type=pd-ssd \ --image-project=cos-cloud \ --image-family=cos-stable \ --create-disk="${DISK_PARAMS}" \ - --container-image=gcr.io/google-containers/busybox \ - --machine-type ${{ vars.GCP_LARGE_MACHINE }} \ - --network-interface=subnet=${{ vars.GCP_SUBNETWORK }} \ + ${CONTAINER_MOUNT_DISKS} \ + --container-stdin \ + --container-tty \ + --container-image="${GAR_BASE_TRIMMED}/${{ vars.CI_IMAGE_NAME }}:sha-${{ env.GITHUB_SHA_SHORT }}" \ + --container-env="${CONTAINER_ENV}" \ + --container-restart-policy=never \ + --subnet=${{ vars.GCP_SUBNETWORK }} \ --scopes cloud-platform \ - --metadata=google-monitoring-enabled=TRUE,google-logging-enabled=TRUE \ + --service-account=${{ vars.GCP_DEPLOYMENTS_SA }} \ + --metadata=google-logging-enabled=true,google-logging-use-fluentbit=true,google-monitoring-enabled=true \ --metadata-from-file=startup-script=.github/workflows/scripts/gcp-vm-startup-script.sh \ --labels=app=${{ inputs.app_name }},environment=test,network=${NETWORK},github_ref=${{ env.GITHUB_REF_SLUG_URL }},test=${{ inputs.test_id }} \ --tags ${{ inputs.app_name }} \ --zone ${{ vars.GCP_ZONE }} - # Format the mounted disk if the test doesn't use a cached state. - - name: Format ${{ inputs.test_id }} volume - if: ${{ !inputs.needs_zebra_state && !inputs.needs_lwd_state }} - shell: /usr/bin/bash -ex {0} - run: | - gcloud compute ssh ${{ inputs.test_id }}-${{ env.GITHUB_REF_SLUG_URL }}-${{ env.GITHUB_SHA_SHORT }} \ - --zone ${{ vars.GCP_ZONE }} \ - --ssh-flag="-o ServerAliveInterval=5" \ - --ssh-flag="-o ConnectionAttempts=20" \ - --ssh-flag="-o ConnectTimeout=5" \ - --command=' \ - set -ex; - # Extract the correct disk name based on the device-name - DISK_NAME=$(ls -l /dev/disk/by-id | grep -oE "google-${{ inputs.test_id }}-${{ env.GITHUB_SHA_SHORT }} -> ../../[^ ]+" | grep -oE "/[^/]+$" | cut -c 2-); - sudo mkfs.ext4 -v /dev/$DISK_NAME \ - ' - - # Launch the test with the previously created disk or cached state. - # - # This step uses a $MOUNT_FLAGS variable to mount the disk to the docker container. - # If the test needs Lightwalletd state, we add the Lightwalletd state mount to the $MOUNT_FLAGS variable. - # - # SSH into the just created VM, and create a Docker container to run the incoming test - # from ${{ inputs.test_id }}, then mount the sudo docker volume created in the previous job. - # - # In this step we're using the same disk for simplicity, as mounting multiple disks to the - # VM and to the container might require more steps in this workflow, and additional - # considerations. - # - # The disk mounted in the VM is located at /dev/$DISK_NAME, we mount the root `/` of this disk to the docker - # container, and might have two different paths (if lightwalletd state is needed): - # - ${{ inputs.zebra_state_dir }} and ${{ inputs.lwd_state_dir }} - # - # Currently we do this by mounting the same disk at both paths. - # - # This doesn't cause any path conflicts, because Zebra and lightwalletd create different - # subdirectories for their data. (But Zebra, lightwalletd, and the test harness must not - # delete the whole cache directory.) - # - # These paths must match the variables used by the tests in Rust, which are also set in - # `ci-unit-tests-docker.yml` to be able to run this tests. - # - # Although we're mounting the disk root to both directories, Zebra and Lightwalletd, tests - # will only respect the values from $ZEBRA_CACHED_STATE_DIR and $LIGHTWALLETD_DATA_DIR, - # the inputs like ${{ inputs.zebra_state_dir }} and ${{ inputs.lwd_state_dir }} - # are only used to match those variables paths. - - name: Launch ${{ inputs.test_id }} test - id: launch-test - shell: /usr/bin/bash -x {0} - run: | - gcloud compute ssh ${{ inputs.test_id }}-${{ env.GITHUB_REF_SLUG_URL }}-${{ env.GITHUB_SHA_SHORT }} \ - --zone ${{ vars.GCP_ZONE }} \ - --ssh-flag="-o ServerAliveInterval=5" \ - --ssh-flag="-o ConnectionAttempts=20" \ - --ssh-flag="-o ConnectTimeout=5" \ - --command=' \ - - # Extract the correct disk name based on the device-name - DISK_NAME=$(ls -l /dev/disk/by-id | grep -oE "google-${{ inputs.test_id }}-${{ env.GITHUB_SHA_SHORT }} -> ../../[^ ]+" | grep -oE "/[^/]+$" | cut -c 2-) - - MOUNT_FLAGS="--mount type=volume,volume-driver=local,volume-opt=device=/dev/$DISK_NAME,volume-opt=type=ext4,dst=${{ inputs.zebra_state_dir }}" - - # Check if we need to mount for Lightwalletd state - # lightwalletd-full-sync reads Zebra and writes lwd, so it is handled specially. - if [[ "${{ inputs.needs_lwd_state }}" == "true" || "${{ inputs.test_id }}" == "lwd-full-sync" ]]; then - MOUNT_FLAGS="$MOUNT_FLAGS --mount type=volume,volume-driver=local,volume-opt=device=/dev/$DISK_NAME,volume-opt=type=ext4,dst=${{ inputs.lwd_state_dir }}" - fi - - sudo docker run \ - --name ${{ inputs.test_id }} \ - --tty \ - --detach \ - ${{ inputs.test_variables }} \ - ${MOUNT_FLAGS} \ - ${{ vars.GAR_BASE }}/${{ vars.CI_IMAGE_NAME }}:sha-${{ env.GITHUB_SHA_SHORT }} \ - ' - # Show debug logs if previous job failed - name: Show debug logs if previous job failed if: ${{ failure() }} - shell: /usr/bin/bash -x {0} run: | gcloud compute ssh ${{ inputs.test_id }}-${{ env.GITHUB_REF_SLUG_URL }}-${{ env.GITHUB_SHA_SHORT }} \ --zone ${{ vars.GCP_ZONE }} \ @@ -302,36 +259,35 @@ jobs: sudo journalctl -b \ ' - # Show all the logs since the container launched, - # following until we see zebrad startup messages. - # - # This check limits the number of log lines, so tests running on the wrong network don't - # run until the job timeout. If Zebra does a complete recompile, there are a few hundred log - # lines before the startup logs. So that's what we use here. - # - # The log pipeline ignores the exit status of `docker logs`. - # It also ignores the expected 'broken pipe' error from `tee`, - # which happens when `grep` finds a matching output and moves on to the next job. - # - # Errors in the tests are caught by the final test status job. - - name: Check startup logs for ${{ inputs.test_id }} - shell: /usr/bin/bash -x {0} + # Find the container ID and save it for use in subsequent steps + - name: Find container ID + id: find-container run: | - gcloud compute ssh ${{ inputs.test_id }}-${{ env.GITHUB_REF_SLUG_URL }}-${{ env.GITHUB_SHA_SHORT }} \ - --zone ${{ vars.GCP_ZONE }} \ - --ssh-flag="-o ServerAliveInterval=5" \ - --ssh-flag="-o ConnectionAttempts=20" \ - --ssh-flag="-o ConnectTimeout=5" \ - --command=' \ - sudo docker logs \ - --tail all \ - --follow \ - ${{ inputs.test_id }} | \ - head -700 | \ - tee --output-error=exit-nopipe /dev/stderr | \ - grep --max-count=1 --extended-regexp --color=always \ - "Zcash network: ${{ inputs.network }}"; \ - ' + INSTANCE_NAME="${{ inputs.test_id }}-${{ env.GITHUB_REF_SLUG_URL }}-${{ env.GITHUB_SHA_SHORT }}" + CONTAINER_PREFIX="klt-${INSTANCE_NAME}" + + echo "Looking for container with prefix: ${CONTAINER_PREFIX}" + + # Wait up to 60 seconds for container to start + for attempt in {1..30}; do + echo "Attempt ${attempt}/30: Checking for running container..." + CONTAINER_ID=$(gcloud compute ssh ${INSTANCE_NAME} \ + --zone ${{ vars.GCP_ZONE }} \ + --ssh-flag="-o ServerAliveInterval=5" \ + --ssh-flag="-o ConnectionAttempts=20" \ + --ssh-flag="-o ConnectTimeout=5" \ + --command="sudo docker ps --filter name=${CONTAINER_PREFIX} -q --no-trunc" 2>/dev/null || echo "") + if [ -n "${CONTAINER_ID}" ]; then + echo "Found running container: ${CONTAINER_ID}" + echo "CONTAINER_ID=${CONTAINER_ID}" >> $GITHUB_OUTPUT + exit 0 + fi + echo "No running container found yet, waiting 2 seconds..." + sleep 2 + done + + echo "Container not found after 60 seconds" + exit 1 # Check that the container executed at least 1 Rust test harness test, and that all tests passed. # Then wait for the container to finish, and exit with the test's exit status. @@ -344,35 +300,38 @@ jobs: # with that status. # (`docker wait` can also wait for multiple containers, but we only ever wait for a single container.) - name: Result of ${{ inputs.test_id }} test - shell: /usr/bin/bash -x {0} run: | + CONTAINER_ID="${{ steps.find-container.outputs.CONTAINER_ID }}" + echo "Using pre-discovered container ID: ${CONTAINER_ID}" gcloud compute ssh ${{ inputs.test_id }}-${{ env.GITHUB_REF_SLUG_URL }}-${{ env.GITHUB_SHA_SHORT }} \ --zone ${{ vars.GCP_ZONE }} \ --ssh-flag="-o ServerAliveInterval=5" \ --ssh-flag="-o ConnectionAttempts=20" \ --ssh-flag="-o ConnectTimeout=5" \ - --command=' \ - sudo docker logs \ - --tail all \ - --follow \ - ${{ inputs.test_id }} | \ - tee --output-error=exit-nopipe /dev/stderr | \ - grep --max-count=1 --extended-regexp --color=always \ - "test result: .*ok.* [1-9][0-9]* passed.*finished in"; - LOGS_EXIT_STATUS=$?; - - EXIT_STATUS=$(sudo docker wait ${{ inputs.test_id }} || echo "Error retrieving exit status"); - echo "sudo docker exit status: $EXIT_STATUS"; - - # If grep found the pattern, exit with the Docker container"s exit status - if [ $LOGS_EXIT_STATUS -eq 0 ]; then - exit $EXIT_STATUS; + --command=" + echo 'Streaming logs and waiting for test success message...'; + sudo docker logs --tail all --follow ${CONTAINER_ID} \ + | tee --output-error=exit-nopipe /dev/stderr \ + | grep --max-count=1 --extended-regexp --color=always \ + 'test result: .*ok.* [1-9][0-9]* passed.*finished in'; + LOGS_EXIT_STATUS=\$?; + echo 'Waiting for container ${CONTAINER_ID} to exit...'; + EXIT_STATUS=\$(sudo docker wait ${CONTAINER_ID} || echo 'Error retrieving exit status'); + echo 'Container exit status: '\$EXIT_STATUS; + + if [ \$LOGS_EXIT_STATUS -ne 0 ]; then + echo 'Test failed: Success log pattern not found (grep exit status: '\$LOGS_EXIT_STATUS').'; + exit 1; + else + if [ \"\$EXIT_STATUS\" -eq 1 ]; then + echo 'Test failed: Success log pattern found BUT container exited with status 1.'; + exit 1; + else + echo 'Test successful: Success log pattern found. Container exit status '\$EXIT_STATUS' ignored (as it is not 1).'; + exit 0; + fi fi - - # Handle other potential errors here - echo "An error occurred while processing the logs."; - exit 1; \ - ' + " # create a state image from the instance's state disk, if requested by the caller create-state-image: @@ -382,7 +341,8 @@ jobs: # We run exactly one of without-cached-state or with-cached-state, and we always skip the other one. # Normally, if a job is skipped, all the jobs that depend on it are also skipped. # So we need to override the default success() check to make this job run. - if: ${{ !cancelled() && !failure() && (inputs.saves_to_disk || inputs.force_save_to_disk) }} + #! Only create disk images when the test-result job succeeded + if: ${{ needs.test-result.result == 'success' && (inputs.saves_to_disk || inputs.force_save_to_disk) }} env: STATE_VERSION: ${{ needs.test-result.outputs.state_version }} CACHED_DISK_NAME: ${{ needs.test-result.outputs.cached_disk_name }} @@ -390,14 +350,14 @@ jobs: contents: 'read' id-token: 'write' steps: - - uses: actions/checkout@v4.2.1 + - uses: actions/checkout@v4.2.2 with: persist-credentials: false fetch-depth: '2' - uses: r7kamura/rust-problem-matchers@v1.5.0 - name: Inject slug/short variables - uses: rlespinasse/github-slug-action@v4 + uses: rlespinasse/github-slug-action@v5 with: short-length: 7 @@ -410,12 +370,12 @@ jobs: # branch names to 12 characters. # # Passes ${{ inputs.network }} to subsequent steps using $NETWORK env variable. - # Passes ${{ env.GITHUB_REF_SLUG_URL }} to subsequent steps using $SHORT_GITHUB_REF env variable. + # Passes ${{ env.GITHUB_REF_POINT_SLUG_URL }} to subsequent steps using $SHORT_GITHUB_REF env variable. - name: Format network name and branch name for disks run: | NETWORK_CAPS="${{ inputs.network }}" echo "NETWORK=${NETWORK_CAPS,,}" >> "$GITHUB_ENV" - LONG_GITHUB_REF="${{ env.GITHUB_REF_SLUG_URL }}" + LONG_GITHUB_REF="${{ env.GITHUB_REF_POINT_SLUG_URL }}" echo "SHORT_GITHUB_REF=${LONG_GITHUB_REF:0:12}" >> "$GITHUB_ENV" # Install our SSH secret @@ -434,13 +394,13 @@ jobs: # Setup gcloud CLI - name: Authenticate to Google Cloud id: auth - uses: google-github-actions/auth@v2.1.6 + uses: google-github-actions/auth@v2.1.10 with: workload_identity_provider: '${{ vars.GCP_WIF }}' service_account: '${{ vars.GCP_DEPLOYMENTS_SA }}' - name: Set up Cloud SDK - uses: google-github-actions/setup-gcloud@v2.1.1 + uses: google-github-actions/setup-gcloud@v2.1.4 # Sets the $UPDATE_SUFFIX env var to "-u" if updating a previous cached state, # and the empty string otherwise. @@ -454,8 +414,8 @@ jobs: UPDATE_SUFFIX="-u" fi - # TODO: find a better logic for the lwd-full-sync case - if [[ "${{ inputs.needs_lwd_state }}" == "true" ]] && [[ "${{ inputs.app_name }}" == "lightwalletd" ]] && [[ "${{ inputs.test_id }}" != 'lwd-full-sync' ]]; then + # TODO: find a better logic for the lwd-sync-full case + if [[ "${{ inputs.needs_lwd_state }}" == "true" ]] && [[ "${{ inputs.app_name }}" == "lightwalletd" ]] && [[ "${{ inputs.test_id }}" != 'lwd-sync-full' ]]; then UPDATE_SUFFIX="-u" fi @@ -477,21 +437,29 @@ jobs: # Passes the versions to subsequent steps using the $INITIAL_DISK_DB_VERSION, # $RUNNING_DB_VERSION, and $DB_VERSION_SUMMARY env variables. - name: Get database versions from logs - shell: /usr/bin/bash -x {0} run: | INITIAL_DISK_DB_VERSION="" RUNNING_DB_VERSION="" DB_VERSION_SUMMARY="" + # Get Instance Name + INSTANCE_NAME="${{ inputs.test_id }}-${{ env.GITHUB_REF_SLUG_URL }}-${{ env.GITHUB_SHA_SHORT }}" + + echo "Fetching first 1000 log entries via SSH for instance ${INSTANCE_NAME} to find DB versions..." + CONTAINER_ID="${{ needs.test-result.outputs.container_id }}" DOCKER_LOGS=$( \ - gcloud compute ssh ${{ inputs.test_id }}-${{ env.GITHUB_REF_SLUG_URL }}-${{ env.GITHUB_SHA_SHORT }} \ + gcloud compute ssh ${INSTANCE_NAME} \ --zone ${{ vars.GCP_ZONE }} \ --ssh-flag="-o ServerAliveInterval=5" \ --ssh-flag="-o ConnectionAttempts=20" \ --ssh-flag="-o ConnectTimeout=5" \ - --command=' \ - sudo docker logs ${{ inputs.test_id }} | head -1000 \ - ') + --command="sudo docker logs ${CONTAINER_ID} | head -1000" \ + ) + + if [[ $? -ne 0 ]] || [[ -z "$DOCKER_LOGS" ]]; then + echo "Failed to retrieve logs via SSH." + exit 1 + fi # either a semantic version or "creating new database" INITIAL_DISK_DB_VERSION=$( \ @@ -503,18 +471,19 @@ jobs: ) if [[ -z "$INITIAL_DISK_DB_VERSION" ]]; then + # Check for new database creation + if echo "$DOCKER_LOGS" | grep -q "creating.new.database"; then + INITIAL_DISK_DB_VERSION="new" + else echo "Checked logs:" echo "" echo "$DOCKER_LOGS" echo "" - echo "Missing initial disk database version in logs: $INITIAL_DISK_DB_VERSION" + echo "Missing initial disk database version in logs" # Fail the tests, because Zebra didn't log the initial disk database version, # or the regex in this step is wrong. - false + exit 1 fi - - if [[ "$INITIAL_DISK_DB_VERSION" = "creating.new.database" ]]; then - INITIAL_DISK_DB_VERSION="new" else INITIAL_DISK_DB_VERSION="v${INITIAL_DISK_DB_VERSION//./-}" fi @@ -538,7 +507,7 @@ jobs: echo "Missing running database version in logs: $RUNNING_DB_VERSION" # Fail the tests, because Zebra didn't log the running database version, # or the regex in this step is wrong. - false + exit 1 fi RUNNING_DB_VERSION="v${RUNNING_DB_VERSION//./-}" @@ -567,19 +536,27 @@ jobs: # # Passes the sync height to subsequent steps using the $SYNC_HEIGHT env variable. - name: Get sync height from logs - shell: /usr/bin/bash -x {0} run: | SYNC_HEIGHT="" + # Get Instance Name + INSTANCE_NAME="${{ inputs.test_id }}-${{ env.GITHUB_REF_SLUG_URL }}-${{ env.GITHUB_SHA_SHORT }}" + + echo "Fetching last 200 log entries via SSH for instance ${INSTANCE_NAME} to find sync height..." + CONTAINER_ID="${{ needs.test-result.outputs.container_id }}" DOCKER_LOGS=$( \ - gcloud compute ssh ${{ inputs.test_id }}-${{ env.GITHUB_REF_SLUG_URL }}-${{ env.GITHUB_SHA_SHORT }} \ + gcloud compute ssh ${INSTANCE_NAME} \ --zone ${{ vars.GCP_ZONE }} \ --ssh-flag="-o ServerAliveInterval=5" \ --ssh-flag="-o ConnectionAttempts=20" \ --ssh-flag="-o ConnectTimeout=5" \ - --command=' \ - sudo docker logs ${{ inputs.test_id }} --tail 200 \ - ') + --command="sudo docker logs --tail 200 ${CONTAINER_ID}" \ + ) + + if [[ $? -ne 0 ]] || [[ -z "$DOCKER_LOGS" ]]; then + echo "Failed to retrieve logs via SSH." + exit 1 + fi SYNC_HEIGHT=$( \ echo "$DOCKER_LOGS" | \ @@ -662,14 +639,30 @@ jobs: run: | MINIMUM_UPDATE_HEIGHT=$((ORIGINAL_HEIGHT+CACHED_STATE_UPDATE_LIMIT)) if [[ -z "$UPDATE_SUFFIX" ]] || [[ "$SYNC_HEIGHT" -gt "$MINIMUM_UPDATE_HEIGHT" ]] || [[ "${{ inputs.force_save_to_disk }}" == "true" ]]; then + + # Use RUNNING_DB_VERSION for image naming (more reliable than STATE_VERSION) + # Extract just the major version number for the image name + IMAGE_VERSION_FOR_NAME=${RUNNING_DB_VERSION#v} # Remove v prefix + IMAGE_VERSION_FOR_NAME=${IMAGE_VERSION_FOR_NAME%%-*} # Keep only major version (before first dash) + + # Validate that we have a version number + if [[ -z $IMAGE_VERSION_FOR_NAME ]] || [[ ! $IMAGE_VERSION_FOR_NAME =~ ^[0-9]+$ ]]; then + echo "ERROR: Invalid version extracted for image naming: $IMAGE_VERSION_FOR_NAME" + echo "RUNNING_DB_VERSION was: $RUNNING_DB_VERSION" + echo "STATE_VERSION was: ${{ env.STATE_VERSION }}" + exit 1 + fi + + echo "Using version $IMAGE_VERSION_FOR_NAME for image naming (from RUNNING_DB_VERSION: $RUNNING_DB_VERSION)" + gcloud compute images create \ - "${{ inputs.disk_prefix }}-${SHORT_GITHUB_REF}-${{ env.GITHUB_SHA_SHORT }}-v${{ env.STATE_VERSION }}-${NETWORK}-${{ inputs.disk_suffix }}${UPDATE_SUFFIX}-${TIME_SUFFIX}" \ + "${{ inputs.disk_prefix }}-${SHORT_GITHUB_REF}-${{ env.GITHUB_SHA_SHORT }}-v${IMAGE_VERSION_FOR_NAME}-${NETWORK}-${{ inputs.disk_suffix }}${UPDATE_SUFFIX}-${TIME_SUFFIX}" \ --force \ --source-disk=${{ inputs.test_id }}-${{ env.GITHUB_SHA_SHORT }} \ --source-disk-zone=${{ vars.GCP_ZONE }} \ --storage-location=us \ --description="Created from commit ${{ env.GITHUB_SHA_SHORT }} with height ${{ env.SYNC_HEIGHT }} and database format ${{ env.DB_VERSION_SUMMARY }}" \ - --labels="height=${{ env.SYNC_HEIGHT }},purpose=${{ inputs.disk_prefix }},commit=${{ env.GITHUB_SHA_SHORT }},state-version=${{ env.STATE_VERSION }},state-running-version=${RUNNING_DB_VERSION},initial-state-disk-version=${INITIAL_DISK_DB_VERSION},network=${NETWORK},target-height-kind=${{ inputs.disk_suffix }},update-flag=${UPDATE_SUFFIX},force-save=${{ inputs.force_save_to_disk }},updated-from-height=${ORIGINAL_HEIGHT},updated-from-disk=${ORIGINAL_DISK_NAME},test-id=${{ inputs.test_id }},app-name=${{ inputs.app_name }}" + --labels="height=${{ env.SYNC_HEIGHT }},purpose=${{ inputs.disk_prefix }},branch=${{ env.GITHUB_REF_SLUG_URL }},commit=${{ env.GITHUB_SHA_SHORT }},state-version=${IMAGE_VERSION_FOR_NAME},state-running-version=${RUNNING_DB_VERSION},initial-state-disk-version=${INITIAL_DISK_DB_VERSION},network=${NETWORK},target-height-kind=${{ inputs.disk_suffix }},update-flag=${UPDATE_SUFFIX},force-save=${{ inputs.force_save_to_disk }},updated-from-height=${ORIGINAL_HEIGHT},updated-from-disk=${ORIGINAL_DISK_NAME},test-id=${{ inputs.test_id }},app-name=${{ inputs.app_name }}" else echo "Skipped cached state update because the new sync height $SYNC_HEIGHT was less than $CACHED_STATE_UPDATE_LIMIT blocks above the original height $ORIGINAL_HEIGHT of $ORIGINAL_DISK_NAME" fi @@ -687,27 +680,27 @@ jobs: contents: 'read' id-token: 'write' steps: - - uses: actions/checkout@v4.2.1 + - uses: actions/checkout@v4.2.2 with: persist-credentials: false fetch-depth: '2' - uses: r7kamura/rust-problem-matchers@v1.5.0 - name: Inject slug/short variables - uses: rlespinasse/github-slug-action@v4 + uses: rlespinasse/github-slug-action@v5 with: short-length: 7 # Setup gcloud CLI - name: Authenticate to Google Cloud id: auth - uses: google-github-actions/auth@v2.1.6 + uses: google-github-actions/auth@v2.1.10 with: workload_identity_provider: '${{ vars.GCP_WIF }}' service_account: '${{ vars.GCP_DEPLOYMENTS_SA }}' - name: Set up Cloud SDK - uses: google-github-actions/setup-gcloud@v2.1.1 + uses: google-github-actions/setup-gcloud@v2.1.4 # Deletes the instances that has been recently deployed in the actual commit after all # previous jobs have run, no matter the outcome of the job. diff --git a/.github/workflows/sub-find-cached-disks.yml.disabled b/.github/workflows/sub-find-cached-disks.yml.disabled index a71237887e2..7f260ebdb86 100644 --- a/.github/workflows/sub-find-cached-disks.yml.disabled +++ b/.github/workflows/sub-find-cached-disks.yml.disabled @@ -20,9 +20,6 @@ on: disk_suffix: required: false type: string - prefer_main_cached_state: - required: false - type: boolean test_id: description: 'The test ID requiring the cached state disks' required: false @@ -48,31 +45,36 @@ jobs: get-cached-disks: name: Get ${{ inputs.test_id || inputs.network }} cached disk runs-on: ubuntu-latest + environment: ${{ github.event_name == 'release' && 'prod' || 'dev' }} outputs: - state_version: ${{ steps.get-available-disks.outputs.state_version }} - cached_disk_name: ${{ steps.get-available-disks.outputs.cached_disk_name }} - lwd_tip_disk: ${{ steps.get-available-disks.outputs.lwd_tip_disk }} - zebra_tip_disk: ${{ steps.get-available-disks.outputs.zebra_tip_disk }} - zebra_checkpoint_disk: ${{ steps.get-available-disks.outputs.zebra_checkpoint_disk }} + state_version: ${{ steps.get-available-disks.outputs.state_version || steps.set-release-defaults.outputs.state_version }} + cached_disk_name: ${{ steps.get-available-disks.outputs.cached_disk_name || steps.set-release-defaults.outputs.cached_disk_name }} + lwd_tip_disk: ${{ steps.get-available-disks.outputs.lwd_tip_disk || steps.set-release-defaults.outputs.lwd_tip_disk }} + zebra_tip_disk: ${{ steps.get-available-disks.outputs.zebra_tip_disk || steps.set-release-defaults.outputs.zebra_tip_disk }} + zebra_checkpoint_disk: ${{ steps.get-available-disks.outputs.zebra_checkpoint_disk || steps.set-release-defaults.outputs.zebra_checkpoint_disk }} permissions: contents: 'read' id-token: 'write' steps: - - uses: actions/checkout@v4.2.1 + - uses: actions/checkout@v4.2.2 with: persist-credentials: false fetch-depth: 0 + - name: Inject slug/short variables + uses: rlespinasse/github-slug-action@v5 + with: + short-length: 7 # Setup gcloud CLI - name: Authenticate to Google Cloud id: auth - uses: google-github-actions/auth@v2.1.6 + uses: google-github-actions/auth@v2.1.10 with: workload_identity_provider: '${{ vars.GCP_WIF }}' service_account: '${{ vars.GCP_DEPLOYMENTS_SA }}' - name: Set up Cloud SDK - uses: google-github-actions/setup-gcloud@v2.1.1 + uses: google-github-actions/setup-gcloud@v2.1.4 # Performs formatting on disk name components. # @@ -85,23 +87,24 @@ jobs: # More info: https://cloud.google.com/compute/docs/naming-resources#resource-name-format # # Passes ${{ inputs.network }} to subsequent steps using $NETWORK env variable. - # Passes ${{ env.GITHUB_REF_SLUG_URL }} to subsequent steps using $SHORT_GITHUB_REF env variable. + # Passes ${{ env.GITHUB_REF_POINT_SLUG_URL }} to subsequent steps using $SHORT_GITHUB_REF env variable. - name: Format network name and branch name for disks run: | NETWORK_CAPS="${{ inputs.network }}" echo "NETWORK=${NETWORK_CAPS,,}" >> "$GITHUB_ENV" - LONG_GITHUB_REF="${{ env.GITHUB_REF_SLUG_URL }}" + LONG_GITHUB_REF="${{ env.GITHUB_REF_POINT_SLUG_URL }}" echo "SHORT_GITHUB_REF=${LONG_GITHUB_REF:0:12}" >> "$GITHUB_ENV" # Check if there are cached state disks available for subsequent jobs to use. + # Skip disk lookup for releases - they should deploy from scratch or use production disks - name: Check if cached state disks exists id: get-available-disks + if: ${{ github.event_name != 'release' }} env: GITHUB_REF: ${{ env.SHORT_GITHUB_REF }} NETWORK: ${{ env.NETWORK }} # use lowercase version from env, not input DISK_PREFIX: ${{ inputs.disk_prefix }} DISK_SUFFIX: ${{ inputs.disk_suffix }} - PREFER_MAIN_CACHED_STATE: ${{ inputs.prefer_main_cached_state }} run: | source ./.github/workflows/scripts/gcp-get-cached-disks.sh echo "state_version=${LOCAL_STATE_VERSION}" >> "${GITHUB_OUTPUT}" @@ -109,3 +112,14 @@ jobs: echo "lwd_tip_disk=${LWD_TIP_DISK}" >> "${GITHUB_OUTPUT}" echo "zebra_tip_disk=${ZEBRA_TIP_DISK}" >> "${GITHUB_OUTPUT}" echo "zebra_checkpoint_disk=${ZEBRA_CHECKPOINT_DISK}" >> "${GITHUB_OUTPUT}" + + # For releases, set default outputs indicating no cached disks are available + - name: Set default outputs for releases + id: set-release-defaults + if: ${{ github.event_name == 'release' }} + run: | + echo "state_version=" >> "${GITHUB_OUTPUT}" + echo "cached_disk_name=" >> "${GITHUB_OUTPUT}" + echo "lwd_tip_disk=false" >> "${GITHUB_OUTPUT}" + echo "zebra_tip_disk=false" >> "${GITHUB_OUTPUT}" + echo "zebra_checkpoint_disk=false" >> "${GITHUB_OUTPUT}" diff --git a/.github/workflows/sub-test-zebra-config.yml.disabled b/.github/workflows/sub-test-zebra-config.yml.disabled index 1f8c455b4b9..3480dfcb0dc 100644 --- a/.github/workflows/sub-test-zebra-config.yml.disabled +++ b/.github/workflows/sub-test-zebra-config.yml.disabled @@ -1,91 +1,129 @@ # This workflow is designed to test Zebra configuration files using Docker containers. +# It acts as a centralized test suite for Docker configuration scenarios, running multiple +# distinct tests against a provided Docker image using a matrix approach. # - Runs a specified Docker image with the provided test variables and network settings. # - Monitors and analyzes container logs for specific patterns to determine test success. # - Provides flexibility in testing various configurations and networks by dynamically adjusting input parameters. -name: Test Zebra Config Files + +name: Test Zebra Configs in Docker on: workflow_call: inputs: - # Status and logging - test_id: - required: true - type: string - description: 'Unique identifier for the test' - grep_patterns: - required: true - type: string - description: 'Patterns to grep for in the logs' - - # Test selection and parameters docker_image: required: true type: string - description: 'Docker image to test' - test_variables: - required: true - type: string - description: 'Environmental variables used to select and configure the test' - network: - required: false - type: string - default: Mainnet - description: 'Zcash network to test against' + description: "Docker image to test, including digest (e.g., gcr.io/example/zebrad@sha256:...)" jobs: - test-docker-config: - name: Test ${{ inputs.test_id }} in Docker + test-configurations: + # Use the matrix 'name' for the job name for clarity in UI + name: Test ${{ matrix.name }} timeout-minutes: 30 - runs-on: ubuntu-latest-m + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + include: + # Basic network configurations + - id: default-conf + name: Default config + env_vars: "" + grep_patterns: '-e "net.*=.*Main.*estimated progress to chain tip.*BeforeOverwinter"' + + - id: testnet-conf + name: Testnet config + env_vars: "-e NETWORK=Testnet" + grep_patterns: '-e "net.*=.*Test.*estimated progress to chain tip.*BeforeOverwinter"' + + # Only runs when using the CI image, because the CD image doesn't have the custom config file + # available in the tests/common/configs directory + - id: custom-conf + name: Custom config + env_vars: '-e ZEBRA_CONF_PATH="/home/zebra/zebrad/tests/common/configs/custom-conf.toml"' + grep_patterns: '-e "extra_coinbase_data:\\sSome\\(\\\"do you even shield\\?\\\"\\)"' + + # RPC configuration tests + - id: rpc-conf + name: RPC config + env_vars: "-e ZEBRA_RPC_PORT=8232" + grep_patterns: '-e "Opened RPC endpoint at.*0.0.0.0:8232"' + + - id: rpc-custom-conf + name: RPC with custom port + env_vars: "-e ZEBRA_RPC_PORT=28232 -e RPC_LISTEN_ADDR=127.0.0.1" + grep_patterns: '-e "Opened RPC endpoint at.*127.0.0.1:28232"' + + # Custom directory tests + - id: rpc-cookie-conf + name: RPC with custom cookie dir + env_vars: "-e ZEBRA_RPC_PORT=8232 -e ENABLE_COOKIE_AUTH=true -e ZEBRA_COOKIE_DIR=/home/zebra/.config/cookie" + grep_patterns: '-e "RPC auth cookie written to disk"' + + # Custom directory tests + - id: custom-dirs-conf + name: Custom cache and cookie directories + env_vars: "-e ZEBRA_CACHE_DIR=/tmp/zebra-cache" + grep_patterns: '-e "Opened Zebra state cache at /tmp/zebra-cache"' + + # Feature-based configurations + - id: prometheus-feature + name: Prometheus metrics + env_vars: "-e FEATURES=prometheus -e METRICS_ENDPOINT_PORT=9999" + grep_patterns: '-e "0.0.0.0:9999"' + + # Mining configuration + - id: mining-config + name: Mining configuration + env_vars: '-e MINER_ADDRESS="u1cymdny2u2vllkx7t5jnelp0kde0dgnwu0jzmggzguxvxj6fe7gpuqehywejndlrjwgk9snr6g69azs8jfet78s9zy60uepx6tltk7ee57jlax49dezkhkgvjy2puuue6dvaevt53nah7t2cc2k4p0h0jxmlu9sx58m2xdm5f9sy2n89jdf8llflvtml2ll43e334avu2fwytuna404a"' + grep_patterns: '-e "miner_address = \\\"u1cymdny2u2vllkx7t5jnelp0kde0dgnwu0jzmggzguxvxj6fe7gpuqehywejndlrjwgk9snr6g69azs8jfet78s9zy60uepx6tltk7ee57jlax49dezkhkgvjy2puuue6dvaevt53nah7t2cc2k4p0h0jxmlu9sx58m2xdm5f9sy2n89jdf8llflvtml2ll43e334avu2fwytuna404a\\\""' + steps: - - uses: actions/checkout@v4.2.1 + - uses: actions/checkout@v4.2.2 with: persist-credentials: false - name: Inject slug/short variables - uses: rlespinasse/github-slug-action@v4 + uses: rlespinasse/github-slug-action@v5 with: short-length: 7 - uses: r7kamura/rust-problem-matchers@v1.5.0 - - name: Run ${{ inputs.test_id }} test + - name: Run ${{ matrix.name }} test + # Only run if this isn't a skipped custom-conf test + if: ${{ matrix.id != 'custom-conf' || contains(inputs.docker_image, vars.CI_IMAGE_NAME) }} run: | docker pull ${{ inputs.docker_image }} - docker run ${{ inputs.test_variables }} --detach --name ${{ inputs.test_id }} -t ${{ inputs.docker_image }} zebrad start + docker run ${{ matrix.env_vars }} --detach --name ${{ matrix.id }} -t ${{ inputs.docker_image }} zebrad start # Use a subshell to handle the broken pipe error gracefully ( trap "" PIPE; docker logs \ --tail all \ --follow \ - ${{ inputs.test_id }} | \ + ${{ matrix.id }} | \ tee --output-error=exit /dev/stderr | \ grep --max-count=1 --extended-regexp --color=always \ - ${{ inputs.grep_patterns }} - ) || true + ${{ matrix.grep_patterns }} + ) LOGS_EXIT_STATUS=$? - docker stop ${{ inputs.test_id }} + # Display grep status for debugging + echo "grep exit status: $LOGS_EXIT_STATUS" - EXIT_STATUS=$(docker wait ${{ inputs.test_id }} || echo "Error retrieving exit status"); + docker stop ${{ matrix.id }} + + EXIT_STATUS=$(docker wait ${{ matrix.id }} || echo "Error retrieving exit status"); echo "docker exit status: $EXIT_STATUS"; - # If grep found the pattern, exit with the Docker container exit status - if [ $LOGS_EXIT_STATUS -eq 0 ]; then - # We can't diagnose or fix these errors, so we're just ignoring them for now. - # They don't actually impact the test because they happen after it succeeds. - # See ticket #7898 for details. - if [ $EXIT_STATUS -eq 137 ] || [ $EXIT_STATUS -eq 139 ]; then - echo "Warning: ignoring docker exit status $EXIT_STATUS"; - exit 0; - else - exit $EXIT_STATUS; - fi + # If grep didn't find the pattern, fail immediately + if [ $LOGS_EXIT_STATUS -ne 0 ]; then + echo "ERROR: Failed to find the expected pattern in logs. Check grep_patterns."; + exit 1; + else + echo "SUCCESS: Found the expected pattern in logs."; + # Exit successfully if grep passed, even if docker stop resulted in SIGKILL (137 or 139) + # See ticket #7898 for details. + exit 0; fi - - # Handle other potential errors here - echo "An error occurred while processing the logs."; - exit 1; - env: - NETWORK: '${{ inputs.network }}' diff --git a/.github/workflows/zizmor.yml b/.github/workflows/zizmor.yml new file mode 100644 index 00000000000..ca5ab84e0ee --- /dev/null +++ b/.github/workflows/zizmor.yml @@ -0,0 +1,31 @@ +name: GitHub Actions Security Analysis with zizmor 🌈 + +on: + push: + branches: ["main"] + pull_request: + branches: ["*"] + +jobs: + zizmor: + name: zizmor latest via Cargo + runs-on: ubuntu-latest + permissions: + contents: read + security-events: write + steps: + - name: Checkout repository + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + with: + persist-credentials: false + - name: Install the latest version of uv + uses: astral-sh/setup-uv@f0ec1fc3b38f5e7cd731bb6ce540c5af426746bb # v4 + - name: Run zizmor 🌈 + run: uvx zizmor --format sarif . > results.sarif + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Upload SARIF file + uses: github/codeql-action/upload-sarif@fca7ace96b7d713c7035871441bd52efbe39e27e # v3.28.19 + with: + sarif_file: results.sarif + category: zizmor diff --git a/CHANGELOG.md b/CHANGELOG.md index e5565429f8a..643d4535a9e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,14 +5,337 @@ All notable changes to Zebra are documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org). -## [Zebra 2.0.0](https://github.com/ZcashFoundation/zebra/releases/tag/v2.0.0) - 2024-10-25 +## [Zebra 2.4.2](https://github.com/ZcashFoundation/zebra/releases/tag/v2.4.2) - 2025-07-28 -This release brings full support for NU6. +This release fixes a database upgrade bug that was introduced in the 2.4.0 +release (which has been removed). If you have upgraded to 2.4.0, your Zebra +address index has become corrupted. This does not affect consensus, but will +make the RPC interface return invalid data for calls like `getaddressutxos` and +other address-related calls. + +**(Also refer to the 2.4.0 release notes below for important breaking changes.)** + +Zebra 2.4.2 prints a warning upon starting if you have been impacted by the bug. +The log line will look like: + +``` +2025-07-17T17:12:41.636549Z WARN zebra_state::service::finalized_state::zebra_db: You have been impacted by the Zebra 2.4.0 address indexer corruption bug. If you rely on the data from the RPC interface, you will need to recover your database. Follow the instructions in the 2.4.2 release notes: https://github.com/ZcashFoundation/zebra/releases/tag/v2.4.2 If you just run the node for consensus and don't use data from the RPC interface, you can ignore this warning. +``` + +If you rely on the RPC data, you will need to restore your database. If you have +backed up the state up before upgrading to 2.4.0, you can simply restore the backup +and run 2.4.2 from it. If you have not, you have two options: + +- Stop Zebra, delete the state (e.g. `~/.cache/zebra/state/v27/mainnet` and + `testnet` too if applicable), upgrade to 2.4.2 (if you haven't already), and + start Zebra. It will sync from scratch, which will take around 48 hours to + complete, depending on the machine specifications. +- Use the `copy-state` subcommand to regenerate a valid state. + This will require an additional ~300 GB of free disk size. It is likely that + it will take around the same time as syncing from scratch, but it has the + advantage of not depending on the network. + - Stop Zebra. + - Rename the old corrupted state folder, e.g. `mv ~/.cache/zebra ~/.cache/zebra.old` + - Copy your current `zebrad.toml` file to a `zebrad-source.toml` file and edit + the `cache_dir` config to the renamed folder, e.g. `cache_dir = '/home/zebrad/.cache/zebra.old'` + - The `copy-state` command that will be run requires a bigger amount of opened + files. Increase the limit by running `ulimit -n 2048`; refer to your OS + documentation if that does not work. + - Run the `copy-state` command: `zebrad -c zebrad-source.toml copy-state + --target-config-path zebrad.toml`. The command will take several hours to + complete. + +### Fixed + +- Fix 2.4.0 DB upgrade; add warning if impacted ([#9709](https://github.com/ZcashFoundation/zebra/pull/9709)) +- Downgrade verbose mempool message ([#9700](https://github.com/ZcashFoundation/zebra/pull/9700)) + +### Contributors + +Thanks to @ebfull for reporting the bug and helping investigating its cause. + + +## Zebra 2.4.1 - \[REMOVED\] + +This version of Zebra wasn't fully published; it was tagged but the tag was +removed, and it was published on `crates.io` but it was yanked. It was not +published on Docker Hub. + +We removed it due to a panic that happened during the pre-release validation. +However, we determined that the panic was caused by an external tool (`ldb +checkpoint`) being used internally to make database backups and it was not a bug +in Zebra. + + +## [Zebra 2.4.0](https://github.com/ZcashFoundation/zebra/releases/tag/v2.4.0) - 2025-07-11 \[REMOVED\] + +### Breaking Changes + +This release has the following breaking changes: + +- This release contains a major database upgrade. It will upgrade an existing + database, automatically moving it from the `v26/` folder to a new `v27/` + folder. However, downgrading is not possible. If you want to keep the + possibility of reverting Zebra in case of an unexpected issue, backup the + `v26/` folder _before_ running the upgraded Zebra. Note that the upgrade is slow + and could take several hours or more to complete on smaller machines. Zebra will + operate normally during that time; the only difference is that some RPC + responses might return empty or not accurate data (pool values for arbitrary + block heights and received balances for addresses). +- While this was never documented as an option for backups, if you relied on the + `ldb checkpoint` tool to generate database backups, be advised that the tool + is no longer supported and _will_ corrupt databases generated or touched by + Zebra 2.4.0 or later releases. +- The `debug_like_zcashd` config option for mining is no longer available. It + was not enabled by default; if it is now present in the config file, Zebra + will panic. Simply delete the config option to fix. +- The `cm_u` field byte order was fixed in `getrawtransaction`/`getblock` + response, so if you relied on the wrong order, you will need to fix your + application. +- The `zebra-scan` and `zebra-grpc` crates are no longer supported and were + removed from the codebase. + + +### Security + +- Fix a consensus rule violation in V5 coinbase transactions at low heights. This issue could only occur on Regtest or custom testnets and is now resolved ([#9620](https://github.com/ZcashFoundation/zebra/pull/9620)) + +### Added + +- Implemented deserialization for Zebra's block and transaction types ([#9522](https://github.com/ZcashFoundation/zebra/pull/9522)) +- Update `getaddressbalance` RPC to return `received` field ([#9295](https://github.com/ZcashFoundation/zebra/pull/9295), [#9539](https://github.com/ZcashFoundation/zebra/pull/9539)) +- Added a `mempool_change()` gRPC method for listening to changes in the mempool ([#9494](https://github.com/ZcashFoundation/zebra/pull/9494)) +- Added `raw_value` feature to serde\_json ([#9538](https://github.com/ZcashFoundation/zebra/pull/9538)) +- Modified `zebra_network::Config` type to use IPv6 listen\_addr by default ([#9609](https://github.com/ZcashFoundation/zebra/pull/9609)) +- Added `invalidateblock` and `reconsiderblock` RPC methods ([#9551](https://github.com/ZcashFoundation/zebra/pull/9551)) +- Updated `(z_)validateaddress` to validate TEX addresses ([#9483](https://github.com/ZcashFoundation/zebra/pull/9483)) +- Added a `addnode` RPC method ([#9604](https://github.com/ZcashFoundation/zebra/pull/9604)) +- Added missing fields to getrawtransaction ([#9636](https://github.com/ZcashFoundation/zebra/pull/9636)) +- Added value pool balances to `getblock` RPC output ([#9432](https://github.com/ZcashFoundation/zebra/pull/9432), [#9539](https://github.com/ZcashFoundation/zebra/pull/9539)) +- Added support for configuring shielded addresses for mining ([#9574](https://github.com/ZcashFoundation/zebra/pull/9574)) +- Added binding_sig, joinsplit_pub_key and joinsplit_sig fields to `getrawtransaction`/`getblock` response ([#9652](https://github.com/ZcashFoundation/zebra/pull/9652)) +- Added a method in `zebra-rpc` to allow validating addresses ([#9658](https://github.com/ZcashFoundation/zebra/pull/9658)) + +### Changed + +- Allow Zebra crates to be compiled with alternative versions of their dependencies ([#9484](https://github.com/ZcashFoundation/zebra/pull/9484)) +- Updated README with Arch build patch ([#9513](https://github.com/ZcashFoundation/zebra/pull/9513)) +- Renamed and moved exports in `zebra-rpc` ([#9568](https://github.com/ZcashFoundation/zebra/pull/9568)) +- Upgraded DB format to support new fields in RPC outputs ([#9539](https://github.com/ZcashFoundation/zebra/pull/9539)) +- Moved GBT RPCs into the main RPC server ([#9459](https://github.com/ZcashFoundation/zebra/pull/9459)) +- Added a `Nu6_1` variant to `NetworkUpgrade` ([#9526](https://github.com/ZcashFoundation/zebra/pull/9526)) +- Use zcash\_script’s new `Script` trait ([#8751](https://github.com/ZcashFoundation/zebra/pull/8751)) +- Removed `debug_like_zcashd` config option ([#9627](https://github.com/ZcashFoundation/zebra/pull/9627)) +- Sync all chains in `TrustedChainSync::sync`, add `NonFinalizedStateChange` gRPC method ([#9654](https://github.com/ZcashFoundation/zebra/pull/9654)) +- Added `prometheus` as a default feature in zebrad ([#9677](https://github.com/ZcashFoundation/zebra/pull/9677)) + +### Fixed + +- Preserve order of RPC output fields ([#9474](https://github.com/ZcashFoundation/zebra/pull/9474)) +- Fixed `cm_u` field byte order in `getrawtransaction`/`getblock` response ([#9667](https://github.com/ZcashFoundation/zebra/pull/9667)) +- Avoid repeatedly converting transactions to librustzcash types when computing sighashes ([#9594](https://github.com/ZcashFoundation/zebra/pull/9594)) +- Correctly set optional `scriptPubKey` fields of transactions in `getblock` and `getrawtransaction` RPC outputs ([#9536](https://github.com/ZcashFoundation/zebra/pull/9536)) +- Allow local outbound connections on Regtest ([#9580](https://github.com/ZcashFoundation/zebra/pull/9580)) +- Allow for parsing `z_gettreestate` output type where optional fields are omitted ([#9451](https://github.com/ZcashFoundation/zebra/pull/9451)) + +### Removed + +- Removed `zebra-scan` and `zebra-grpc` ([#9683](https://github.com/ZcashFoundation/zebra/pull/9683)) + +### Contributors + +Thank you to everyone who contributed to this release, we couldn't make Zebra without you: +@ala-mode, @arya2, @conradoplg, @elijahhampton, @gustavovalverde, @idky137, @mpguerra, @oxarbitrage, @sellout, @str4d and @upbqdn + + +## [Zebra 2.3.0](https://github.com/ZcashFoundation/zebra/releases/tag/v2.3.0) - 2025-05-06 + +### Breaking Changes + +- The RPC endpoint is no longer enabled by default in Docker. To enable it, + follow the docs [here](https://zebra.zfnd.org/user/docker.html#rpc). +- We will no longer be publishing Docker images tagged with the `sha-` or `v` + prefixes. If you use tags starting with the `v` prefix, please update to + images tagged `N.N.N`. For example, use `2.3.0` instead of `v2.3.0`. If you + need a specific hash, each tag has a digest that you can use instead. +- All functionality that used to be guarded by the `getblocktemplate-rpcs` Cargo + feature was moved out and the feature is no longer present in the codebase. + Note that all release builds following Zebra 1.3.0 had this feature enabled by + default. + +### Added + +- Track misbehaving peer connections and ban them past a threshold ([#9201](https://github.com/ZcashFoundation/zebra/pull/9201)) +- Restore internal miner ([#9311](https://github.com/ZcashFoundation/zebra/pull/9311)) +- Add `reconsider_block` method to non-finalized state ([#9260](https://github.com/ZcashFoundation/zebra/pull/9260)) +- Add NU7 constants ([#9256](https://github.com/ZcashFoundation/zebra/pull/9256)) +- Add `invalidate_block_method` and `invalidated_blocks` field to non-finalized state ([#9167](https://github.com/ZcashFoundation/zebra/pull/9167)) +- Add unused `Transaction::V6` variant ([#9339](https://github.com/ZcashFoundation/zebra/pull/9339)) + +### Changed + +- Downgrade verbose info message ([#9448](https://github.com/ZcashFoundation/zebra/pull/9448)) +- Use read-only db instance when running `tip-height` or `copy-state` commands ([#9359](https://github.com/ZcashFoundation/zebra/pull/9359)) +- Refactor format upgrades into trait ([#9263](https://github.com/ZcashFoundation/zebra/pull/9263)) +- Remove the `getblocktemplate-rpcs` Cargo feature ([#9401](https://github.com/ZcashFoundation/zebra/pull/9401)) +- Improve cache dir and database startup panics ([#9441](https://github.com/ZcashFoundation/zebra/pull/9441)) +- Added `txid` field to `TransactionObject` ([#9617](https://github.com/ZcashFoundation/zebra/issues/9617)) +### Fixed + +- Remove a redundant startup warning ([#9397](https://github.com/ZcashFoundation/zebra/pull/9397)) +- Advertise mined blocks ([#9176](https://github.com/ZcashFoundation/zebra/pull/9176)) +- Ensure secondary rocksdb instance has caught up to the primary instance ([#9346](https://github.com/ZcashFoundation/zebra/pull/9346)) +- Use network kind of `TestnetKind` in transparent addresses on Regtest ([#9175](https://github.com/ZcashFoundation/zebra/pull/9175)) +- Fix redundant attributes on enum variants ([#9309](https://github.com/ZcashFoundation/zebra/pull/9309)) + +### RPCs + +- Add `time` and `size` fields to `TransactionObject` ([#9458](https://github.com/ZcashFoundation/zebra/pull/9458)) +- Add inbound peers to `getpeerinfo` response ([#9214](https://github.com/ZcashFoundation/zebra/pull/9214)) +- Extend `getinfo` ([#9261](https://github.com/ZcashFoundation/zebra/pull/9261)) +- Add fields to `getblockchaininfo` RPC output ([#9215](https://github.com/ZcashFoundation/zebra/pull/9215)) +- Add some missing fields to transaction object ([#9329](https://github.com/ZcashFoundation/zebra/pull/9329)) +- Support negative heights in `HashOrHeight` ([#9316](https://github.com/ZcashFoundation/zebra/pull/9316)) +- Add verbose support to getrawmempool ([#9249](https://github.com/ZcashFoundation/zebra/pull/9249)) +- Fill size field in getblock with verbosity=2 ([#9327](https://github.com/ZcashFoundation/zebra/pull/9327)) +- Add `blockcommitments` field to `getblock` output ([#9217](https://github.com/ZcashFoundation/zebra/pull/9217)) +- Accept an unused second param in `sendrawtransaction` RPC ([#9242](https://github.com/ZcashFoundation/zebra/pull/9242)) +- Make start and end fields optional and apply range rules to match zcashd ([#9408](https://github.com/ZcashFoundation/zebra/pull/9408)) +- Return only the history tree root in `GetBlockTemplateChainInfo` response ([#9444](https://github.com/ZcashFoundation/zebra/pull/9444)) +- Correctly map JSON-RPC to/from 2.0 ([#9216](https://github.com/ZcashFoundation/zebra/pull/9216)) +- Permit JSON-RPC IDs to be non-strings ([#9341](https://github.com/ZcashFoundation/zebra/pull/9341)) +- Match coinbase outputs order in `Getblocktemplate` ([#9272](https://github.com/ZcashFoundation/zebra/pull/9272)) + +### Docker + +- Refactor Dockerfile and entrypoint ([#8923](https://github.com/ZcashFoundation/zebra/pull/8923)) +- Enhance Zebra configuration options and entrypoint logic ([#9344](https://github.com/ZcashFoundation/zebra/pull/9344)) +- Better permission and cache dirs handling in Docker ([#9323](https://github.com/ZcashFoundation/zebra/pull/9323)) +- Allow r/w access in mounted volumes ([#9281](https://github.com/ZcashFoundation/zebra/pull/9281)) + +### Documentation + +- Update examples for running Zebra in Docker ([#9269](https://github.com/ZcashFoundation/zebra/pull/9269)) +- Add architectural decision records structure ([#9310](https://github.com/ZcashFoundation/zebra/pull/9310)) +- Add Mempool Specification to Zebra Book ([#9336](https://github.com/ZcashFoundation/zebra/pull/9336)) +- Complete the Treestate RFC documentation ([#9340](https://github.com/ZcashFoundation/zebra/pull/9340)) + +### Contributors + +@AloeareV, @Metalcape, @PaulLaux, @VolodymyrBg, @aphelionz, @arya2, @conradoplg, +@crStiv, @elijahhampton, @gustavovalverde, @mdqst, @natalieesk, @nuttycom, +@oxarbitrage, @podZzzzz, @sellout, @str4d, @upbqdn and @zeroprooff. + +## [Zebra 2.2.0](https://github.com/ZcashFoundation/zebra/releases/tag/v2.2.0) - 2025-02-03 + +In this release, Zebra introduced an additional consensus check on the branch ID of Nu6 transactions +(which is currently also checked elsewhere; but we believe it's important to check on its own to protect +against future code changes), along with important refactors and improvements. + +### Added + +- An index to track spending transaction ids by spent outpoints and revealed nullifiers ([#8895](https://github.com/ZcashFoundation/zebra/pull/8895)) +- Accessor methods to `zebra-rpc` request/response types ([#9113](https://github.com/ZcashFoundation/zebra/pull/9113)) +- `getblock` RPC method now can return transaction details with verbosity=2 ([#9083](https://github.com/ZcashFoundation/zebra/pull/9083)) +- Serialized NU5 blocks to test vectors ([#9098](https://github.com/ZcashFoundation/zebra/pull/9098)) + +### Changed + +- Migrated from deprecated `jsonrpc_*` crates to `jsonrpsee` ([#9059](https://github.com/ZcashFoundation/zebra/pull/9059), [#9151](https://github.com/ZcashFoundation/zebra/pull/9151)) +- Optimized checks for coinbase transactions ([#9126](https://github.com/ZcashFoundation/zebra/pull/9126)) +- Avoid re-verifying transactions in blocks if those transactions are in the mempool ([#8951](https://github.com/ZcashFoundation/zebra/pull/8951), [#9118](https://github.com/ZcashFoundation/zebra/pull/9118)) +- Allow transactions spending coinbase outputs to have transparent outputs on Regtest ([#9085](https://github.com/ZcashFoundation/zebra/pull/9085)) + +### Fixed + +- Respond to getblockchaininfo with genesis block when empty state ([#9138](https://github.com/ZcashFoundation/zebra/pull/9138)) +- Verify consensus branch ID in SIGHASH precomputation ([#9139](https://github.com/ZcashFoundation/zebra/pull/9139)) +- More closely match zcashd RPC errors and `getrawtransaction` RPC behaviour ([#9049](https://github.com/ZcashFoundation/zebra/pull/9049)) +- Fixes bugs in the lightwalletd integration tests ([#9052](https://github.com/ZcashFoundation/zebra/pull/9052)) + +### Contributors + +Thank you to everyone who contributed to this release, we couldn't make Zebra without you: +@Fallengirl, @arya2, @conradoplg, @elijahhampton, @futreall, @gustavovalverde, @idky137, @mpguerra, @oxarbitrage, @rex4539, @rootdiae, @sandakersmann and @upbqdn + + +## [Zebra 2.1.0](https://github.com/ZcashFoundation/zebra/releases/tag/v2.1.0) - 2024-12-06 + +This release adds a check to verify that V5 transactions in the mempool have the correct consensus branch ID; +Zebra would previously accept those and return a transaction ID (indicating success) even though they would +be eventually rejected by the block consensus checks. Similarly, Zebra also now returns an error when trying +to submit transactions that would eventually fail some consensus checks (e.g. double spends) but would also +return a transaction ID indicating success. The release also bumps +Zebra's initial minimum protocol version such that this release of Zebra will always reject connections with peers advertising +a network protocol version below 170,120 on Mainnet and 170,110 on Testnet instead of accepting those connections until Zebra's +chain state reaches the NU6 activation height. +The `getblock` RPC method has been updated and now returns some additional information +such as the block height (even if you provide a block hash) and other fields as supported +by the `getblockheader` RPC call. + +### Breaking Changes + +- Upgrade minimum protocol versions for all Zcash networks ([#9058](https://github.com/ZcashFoundation/zebra/pull/9058)) + +### Added + +- `getblockheader` RPC method ([#8967](https://github.com/ZcashFoundation/zebra/pull/8967)) +- `rust-toolchain.toml` file ([#8985](https://github.com/ZcashFoundation/zebra/pull/8985)) + +### Changed + +- Updated `getblock` RPC to more closely match zcashd ([#9006](https://github.com/ZcashFoundation/zebra/pull/9006)) +- Updated error messages to include inner error types (notably for the transaction verifier) ([#9066](https://github.com/ZcashFoundation/zebra/pull/9066)) + +### Fixed + +- Validate consensus branch ids of mempool transactions ([#9063](https://github.com/ZcashFoundation/zebra/pull/9063)) +- Verify mempool transactions with unmined inputs if those inputs are in the mempool to support TEX transactions ([#8857](https://github.com/ZcashFoundation/zebra/pull/8857)) +- Wait until transactions have been added to the mempool before returning success response from `sendrawtransaction` RPC ([#9067](https://github.com/ZcashFoundation/zebra/pull/9067)) + +### Contributors + +Thank you to everyone who contributed to this release, we couldn't make Zebra without you: +@arya2, @conradoplg, @cypherpepe, @gustavovalverde, @idky137, @oxarbitrage, @pinglanlu and @upbqdn + +## [Zebra 2.0.1](https://github.com/ZcashFoundation/zebra/releases/tag/v2.0.1) - 2024-10-30 + +- Zebra now supports NU6 on Mainnet. This patch release updates dependencies + required for NU6. The 2.0.0 release was pointing to the incorrect dependencies + and would panic on NU6 activation. + +### Breaking Changes + +- The JSON RPC endpoint has cookie-based authentication enabled by default. + **If you rely on Zebra RPC, you will need to adjust your config.** The + simplest change is to disable authentication by adding `enable_cookie_auth = + false` to the `[rpc]` section of the Zebra config file; [refer to the + docs for more information](https://zebra.zfnd.org/user/lightwalletd.html#json-rpc) (this was added + in v2.0.0, but is being mentioned again here for clarity). + +### Changed + +- Use ECC deps with activation height for NU6 + ([#8960](https://github.com/ZcashFoundation/zebra/pull/8978)) + +### Contributors + +Thank you to everyone who contributed to this release, we couldn't make Zebra without you: +@arya2, @gustavovalverde, @oxarbitrage and @upbqdn. + +## [Zebra 2.0.0](https://github.com/ZcashFoundation/zebra/releases/tag/v2.0.0) - 2024-10-25 - \[REMOVED\] + +This release was intended to support NU6 but was pointing to the wrong version +of dependencies which would make Zebra panic at NU6 activation. Use v2.0.1 instead. ### Breaking Changes - Zebra now supports NU6 on Mainnet. - The JSON RPC endpoint has a cookie-based authentication enabled by default. + **If you rely on Zebra RPC, you will need to adjust your config.** The + simplest change is to disable authentication by adding `enable_cookie_auth = + false` to the `[rpc]` section of the Zebra config file; [refer to the + docs](https://zebra.zfnd.org/user/lightwalletd.html#json-rpc). ### Added @@ -81,7 +404,7 @@ by syncing Zebra from scratch, or by using the `copy-state` command to create a command, first make a copy Zebra's Testnet configuration with a different cache directory path, for example, if Zebra's configuration is at the default path, by running `cp ~/.config/zebrad.toml ./zebrad-copy-target.toml`, then opening the new configuration file and editing the `cache_dir` path in the `state` section. Once there's a copy of Zebra's configuration with the new state cache directory path, run: -`zebrad copy-state --target-config-path "./zebrad-copy-target.toml" --max-source-height "2975999"`, and then update the original +`zebrad copy-state --target-config-path "./zebrad-copy-target.toml" --max-source-height "2975999"`, and then update the original Zebra configuration to use the new state cache directory. ### Added @@ -127,7 +450,7 @@ Thank you to everyone who contributed to this release, we couldn't make Zebra wi - Support for custom Testnets and Regtest is greatly enhanced. - Windows is now back in the second tier of supported platforms. - The end-of-support time interval is set to match `zcashd`'s 16 weeks. -- The RPC serialization of empty treestates matches `zcashd`. +- The RPC serialization of empty treestates matches `zcashd`. ### Added @@ -193,11 +516,11 @@ Thank you to everyone who contributed to this release, we couldn't make Zebra wi ## [Zebra 1.6.1](https://github.com/ZcashFoundation/zebra/releases/tag/v1.6.1) - 2024-04-15 -This release adds an OpenAPI specification for Zebra's RPC methods and startup logs about Zebra's storage usage and other database information. +This release adds an OpenAPI specification for Zebra's RPC methods and startup logs about Zebra's storage usage and other database information. It also includes: - Bug fixes and improved error messages for some zebra-scan gRPC methods -- A performance improvement in Zebra's `getblock` RPC method +- A performance improvement in Zebra's `getblock` RPC method ### Added diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 94029dc9a75..8c9025f5fab 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -20,8 +20,8 @@ Please [create an issue](https://github.com/ZcashFoundation/zebra/issues/new?ass [pull-requests]: #pull-requests PRs are welcome for small and large changes, but please don't make large PRs -without coordinating with us via the issue tracker or Discord. This helps -increase development coordination and makes PRs easier to merge. +without coordinating with us via the [issue tracker](https://github.com/ZcashFoundation/zebra/issues) or [Discord](https://discord.gg/yVNhQwQE68). This helps +increase development coordination and makes PRs easier to merge. Low-effort PRs, including but not limited to fixing typos and grammatical corrections, will generally be redone by us to dissuade metric farming. Check out the [help wanted][hw] or [good first issue][gfi] labels if you're looking for a place to get started! @@ -33,19 +33,4 @@ are conformant. [hw]: https://github.com/ZcashFoundation/zebra/labels/E-help-wanted [gfi]: https://github.com/ZcashFoundation/zebra/labels/good%20first%20issue -[conventional]: https://www.conventionalcommits.org/en/v1.0.0/#specification - -## Coverage Reports -[coverage-reports]: #coverage-reports - -Zebra's CI currently generates coverage reports for every PR with rust's new -source based coverage feature. The coverage reports are generated by the -`coverage.yml` file. - -These reports are then saved as html and zipped up into a github action's -artifact. These artifacts can be accessed on the `checks` tab of any PR, next -to the "re-run jobs" button on the `Coverage (+nightly)` CI job's tab -[example](https://github.com/ZcashFoundation/zebra/pull/1907/checks?check_run_id=2127676611). - -To access a report download and extract the zip artifact then open the top -level `index.html`. +[conventional]: https://www.conventionalcommits.org/en/v1.0.0/#specification \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 6ef93e20732..4482153d3ea 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -18,7 +18,7 @@ dependencies = [ "once_cell", "regex", "secrecy", - "semver 1.0.23", + "semver", "serde", "termcolor", "toml 0.5.11", @@ -56,6 +56,12 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + [[package]] name = "aead" version = "0.5.2" @@ -72,7 +78,7 @@ version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "cipher", "cpufeatures", ] @@ -83,7 +89,7 @@ version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "getrandom 0.2.15", "once_cell", "version_check", @@ -186,9 +192,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.86" +version = "1.0.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" +checksum = "b0674a1ddeecb70197781e945de4b3b8ffb61fa939a5597bcf48503737663100" [[package]] name = "arc-swap" @@ -240,7 +246,7 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.106", ] [[package]] @@ -251,7 +257,7 @@ checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.106", ] [[package]] @@ -287,8 +293,8 @@ dependencies = [ "axum-core", "bytes", "futures-util", - "http 1.1.0", - "http-body 1.0.1", + "http", + "http-body", "http-body-util", "itoa", "matchit", @@ -313,8 +319,8 @@ dependencies = [ "async-trait", "bytes", "futures-util", - "http 1.1.0", - "http-body 1.0.1", + "http", + "http-body", "http-body-util", "mime", "pin-project-lite", @@ -332,9 +338,9 @@ checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" dependencies = [ "addr2line", "cc", - "cfg-if 1.0.0", + "cfg-if", "libc", - "miniz_oxide", + "miniz_oxide 0.7.4", "object", "rustc-demangle", ] @@ -345,12 +351,6 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" -[[package]] -name = "base64" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7" - [[package]] name = "base64" version = "0.13.1" @@ -417,21 +417,38 @@ version = "0.69.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a00dc851838a2120612785d195287475a3ac45514741da670b735818822129a0" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.2", "cexpr", "clang-sys", "itertools 0.12.1", "lazy_static", "lazycell", + "proc-macro2", + "quote", + "regex", + "rustc-hash 1.1.0", + "shlex", + "syn 2.0.106", +] + +[[package]] +name = "bindgen" +version = "0.72.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f72209734318d0b619a5e0f5129918b848c416e122a3c4ce054e03cb87b726f" +dependencies = [ + "bitflags 2.9.2", + "cexpr", + "clang-sys", + "itertools 0.13.0", "log", "prettyplease", "proc-macro2", "quote", "regex", - "rustc-hash", + "rustc-hash 2.1.1", "shlex", - "syn 2.0.85", - "which 4.4.2", + "syn 2.0.106", ] [[package]] @@ -444,7 +461,7 @@ dependencies = [ "hmac", "rand_core 0.6.4", "ripemd 0.2.0-pre.4", - "secp256k1 0.29.1", + "secp256k1", "sha2 0.11.0-pre.4", "subtle", "zeroize", @@ -452,18 +469,18 @@ dependencies = [ [[package]] name = "bit-set" -version = "0.5.3" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" +checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" dependencies = [ "bit-vec", ] [[package]] name = "bit-vec" -version = "0.6.3" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" +checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" [[package]] name = "bitflags" @@ -473,9 +490,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.6.0" +version = "2.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +checksum = "6a65b545ab31d687cff52899d4890855fec459eb6afe0da6417b8a18da87aa29" [[package]] name = "bitflags-serde-legacy" @@ -483,7 +500,7 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b64e60c28b6d25ad92e8b367801ff9aa12b41d05fc8798055d296bace4a60cc" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.2", "serde", ] @@ -552,15 +569,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "bridgetree" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3990cc973c3b9c41c4dbb66cde22e3af77c4c7b133008d81f292ad12c27ed794" -dependencies = [ - "incrementalmerkletree", -] - [[package]] name = "bs58" version = "0.5.1" @@ -571,16 +579,6 @@ dependencies = [ "tinyvec", ] -[[package]] -name = "bstr" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40723b8fb387abc38f4f4a37c09073622e41dd12327033091ef8950659e6dc0c" -dependencies = [ - "memchr", - "serde", -] - [[package]] name = "bumpalo" version = "3.16.0" @@ -607,9 +605,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.8.0" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ac0150caa2ae65ca5bd83f25c7de183dea78d4d366469f148435e2acfbad0da" +checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" [[package]] name = "bzip2-sys" @@ -654,10 +652,10 @@ checksum = "2d886547e41f740c616ae73108f6eb70afe6d940c7bc697cb30f13daec073037" dependencies = [ "camino", "cargo-platform", - "semver 1.0.23", + "semver", "serde", "serde_json", - "thiserror", + "thiserror 1.0.64", ] [[package]] @@ -677,12 +675,13 @@ dependencies = [ [[package]] name = "cc" -version = "1.1.7" +version = "1.2.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26a5c3fd7bfa1ce3897a3a3501d362b2d87b7f2583ebcb4a949ec25911025cbc" +checksum = "3ee0f8803222ba5a7e2777dd72ca451868909b1ac410621b676adf07280e9b5f" dependencies = [ "jobserver", "libc", + "shlex", ] [[package]] @@ -694,12 +693,6 @@ dependencies = [ "nom", ] -[[package]] -name = "cfg-if" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" - [[package]] name = "cfg-if" version = "1.0.0" @@ -718,7 +711,7 @@ version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "cipher", "cpufeatures", ] @@ -738,15 +731,15 @@ dependencies = [ [[package]] name = "chrono" -version = "0.4.38" +version = "0.4.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" +checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d" dependencies = [ "android-tzdata", "iana-time-zone", - "num-traits", + "num-traits 0.2.19", "serde", - "windows-targets 0.52.6", + "windows-link", ] [[package]] @@ -844,7 +837,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.106", ] [[package]] @@ -951,16 +944,6 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7144d30dcf0fafbce74250a3963025d8d52177934239851c917d29f1df280c2" -[[package]] -name = "core-foundation" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" -dependencies = [ - "core-foundation-sys", - "libc", -] - [[package]] name = "core-foundation-sys" version = "0.8.6" @@ -991,7 +974,7 @@ version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", ] [[package]] @@ -1007,7 +990,7 @@ dependencies = [ "criterion-plot", "is-terminal", "itertools 0.10.5", - "num-traits", + "num-traits 0.2.19", "once_cell", "oorandom", "plotters", @@ -1032,9 +1015,9 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.13" +version = "0.5.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2" +checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2" dependencies = [ "crossbeam-utils", ] @@ -1107,12 +1090,12 @@ version = "4.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "cpufeatures", "curve25519-dalek-derive", "digest 0.10.7", "fiat-crypto", - "rustc_version 0.4.0", + "rustc_version", "serde", "subtle", "zeroize", @@ -1126,17 +1109,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", -] - -[[package]] -name = "darling" -version = "0.13.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c" -dependencies = [ - "darling_core 0.13.4", - "darling_macro 0.13.4", + "syn 2.0.106", ] [[package]] @@ -1145,22 +1118,8 @@ version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" dependencies = [ - "darling_core 0.20.10", - "darling_macro 0.20.10", -] - -[[package]] -name = "darling_core" -version = "0.13.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610" -dependencies = [ - "fnv", - "ident_case", - "proc-macro2", - "quote", - "strsim 0.10.0", - "syn 1.0.109", + "darling_core", + "darling_macro", ] [[package]] @@ -1174,18 +1133,7 @@ dependencies = [ "proc-macro2", "quote", "strsim 0.11.1", - "syn 2.0.85", -] - -[[package]] -name = "darling_macro" -version = "0.13.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835" -dependencies = [ - "darling_core 0.13.4", - "quote", - "syn 1.0.109", + "syn 2.0.106", ] [[package]] @@ -1194,9 +1142,9 @@ version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ - "darling_core 0.20.10", + "darling_core", "quote", - "syn 2.0.85", + "syn 2.0.106", ] [[package]] @@ -1229,6 +1177,59 @@ dependencies = [ "serde", ] +[[package]] +name = "derive-getters" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74ef43543e701c01ad77d3a5922755c6a1d71b22d942cb8042be4994b380caff" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", +] + +[[package]] +name = "derive-new" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3418329ca0ad70234b9735dc4ceed10af4df60eff9c8e7b06cb5e520d92c3535" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "derive_builder" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "507dfb09ea8b7fa618fcf76e953f4f5e192547945816d5358edffe39f6f94947" +dependencies = [ + "derive_builder_macro", +] + +[[package]] +name = "derive_builder_core" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d5bcf7b024d6835cfb3d473887cd966994907effbe9227e8c8219824d06c4e8" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.106", +] + +[[package]] +name = "derive_builder_macro" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" +dependencies = [ + "derive_builder_core", + "syn 2.0.106", +] + [[package]] name = "digest" version = "0.10.7" @@ -1253,23 +1254,23 @@ dependencies = [ [[package]] name = "dirs" -version = "5.0.1" +version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" +checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e" dependencies = [ "dirs-sys", ] [[package]] name = "dirs-sys" -version = "0.4.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" +checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab" dependencies = [ "libc", "option-ext", "redox_users", - "windows-sys 0.48.0", + "windows-sys 0.59.0", ] [[package]] @@ -1334,20 +1335,22 @@ checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" [[package]] name = "elasticsearch" -version = "8.5.0-alpha.1" +version = "8.17.0-alpha.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40d9bd57d914cc66ce878f098f63ed7b5d5b64c30644a5adb950b008f874a6c6" +checksum = "52be486463ef0b89e45191803db146387d5f594c26a0c8790807bb3e988ec5f6" dependencies = [ - "base64 0.11.0", + "base64 0.22.1", "bytes", "dyn-clone", + "flate2", "lazy_static", "percent-encoding", "reqwest", - "rustc_version 0.2.3", + "rustc_version", "serde", "serde_json", - "serde_with 1.14.0", + "serde_with", + "tokio", "url", "void", ] @@ -1377,12 +1380,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" [[package]] -name = "encoding_rs" -version = "0.8.34" +name = "enum_primitive" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" +checksum = "be4551092f4d519593039259a9ed8daedf0da12e5109c5280338073eaeb81180" dependencies = [ - "cfg-if 1.0.0", + "num-traits 0.1.43", ] [[package]] @@ -1407,6 +1410,7 @@ version = "0.2.2" source = "git+https://github.com/QED-it/librustzcash?rev=a996e53e7a561a4723be6a712e44c8eb5fad4a6c#a996e53e7a561a4723be6a712e44c8eb5fad4a6c" dependencies = [ "blake2b_simd", + "cc", "core2", "document-features", ] @@ -1488,12 +1492,12 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" [[package]] name = "flate2" -version = "1.0.31" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f211bbe8e69bbd0cfdea405084f128ae8b4aaa6b0b522fc8f2b009084797920" +checksum = "4a3d7db9596fecd151c5f638c0ee5d5bd487b6e0ea232e5dc96d5250f6f94b1d" dependencies = [ "crc32fast", - "miniz_oxide", + "miniz_oxide 0.8.9", ] [[package]] @@ -1541,7 +1545,7 @@ dependencies = [ "libm", "num-bigint", "num-integer", - "num-traits", + "num-traits 0.2.19", ] [[package]] @@ -1615,7 +1619,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.106", ] [[package]] @@ -1665,7 +1669,7 @@ version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "libc", "wasi 0.9.0+wasi-snapshot-preview1", ] @@ -1676,13 +1680,27 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "js-sys", "libc", "wasi 0.11.0+wasi-snapshot-preview1", "wasm-bindgen", ] +[[package]] +name = "getrandom" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "r-efi", + "wasi 0.14.2+wasi-0.2.4", + "wasm-bindgen", +] + [[package]] name = "getset" version = "0.1.6" @@ -1692,7 +1710,7 @@ dependencies = [ "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.106", ] [[package]] @@ -1707,7 +1725,7 @@ version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b903b73e45dc0c6c596f2d37eccece7c1c8bb6e4407b001096387c63d0d93724" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.2", "libc", "libgit2-sys", "log", @@ -1720,19 +1738,6 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" -[[package]] -name = "globset" -version = "0.4.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57da3b9b5b85bd66f31093f8c408b90a74431672542466497dcbdfdc02034be1" -dependencies = [ - "aho-corasick", - "bstr", - "log", - "regex-automata 0.4.8", - "regex-syntax 0.8.5", -] - [[package]] name = "group" version = "0.13.0" @@ -1745,25 +1750,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "h2" -version = "0.3.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" -dependencies = [ - "bytes", - "fnv", - "futures-core", - "futures-sink", - "futures-util", - "http 0.2.12", - "indexmap 2.6.0", - "slab", - "tokio", - "tokio-util 0.7.12", - "tracing", -] - [[package]] name = "h2" version = "0.4.5" @@ -1775,11 +1761,11 @@ dependencies = [ "fnv", "futures-core", "futures-sink", - "http 1.1.0", - "indexmap 2.6.0", + "http", + "indexmap 2.10.0", "slab", "tokio", - "tokio-util 0.7.12", + "tokio-util", "tracing", ] @@ -1878,7 +1864,7 @@ dependencies = [ "byteorder", "flate2", "nom", - "num-traits", + "num-traits 0.2.19", ] [[package]] @@ -1950,7 +1936,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f9c7c7c8ac16c798734b8a24560c1362120597c40d5e1459f09498f8f6c8f2ba" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "libc", "windows", ] @@ -1963,18 +1949,7 @@ checksum = "f34059280f617a59ee59a0455e93460d67e5c76dec42dd262d38f0f390f437b2" dependencies = [ "flume", "indicatif", - "parking_lot 0.12.3", -] - -[[package]] -name = "http" -version = "0.2.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" -dependencies = [ - "bytes", - "fnv", - "itoa", + "parking_lot", ] [[package]] @@ -1988,17 +1963,6 @@ dependencies = [ "itoa", ] -[[package]] -name = "http-body" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" -dependencies = [ - "bytes", - "http 0.2.12", - "pin-project-lite", -] - [[package]] name = "http-body" version = "1.0.1" @@ -2006,19 +1970,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ "bytes", - "http 1.1.0", + "http", ] [[package]] name = "http-body-util" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" dependencies = [ "bytes", - "futures-util", - "http 1.1.0", - "http-body 1.0.1", + "futures-core", + "http", + "http-body", "pin-project-lite", ] @@ -2042,9 +2006,9 @@ checksum = "91f255a4535024abf7640cb288260811fc14794f62b063652ed349f9a6c2348e" [[package]] name = "humantime" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" +checksum = "9b112acc8b3adf4b107a8ec20977da0273a8c386765a3ec0229bd500a1443f9f" [[package]] name = "humantime-serde" @@ -2067,40 +2031,16 @@ dependencies = [ [[package]] name = "hyper" -version = "0.14.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a152ddd61dfaec7273fe8419ab357f33aee0d914c5f4efbf0d96fa749eea5ec9" -dependencies = [ - "bytes", - "futures-channel", - "futures-core", - "futures-util", - "h2 0.3.26", - "http 0.2.12", - "http-body 0.4.6", - "httparse", - "httpdate", - "itoa", - "pin-project-lite", - "socket2", - "tokio", - "tower-service", - "tracing", - "want", -] - -[[package]] -name = "hyper" -version = "1.5.0" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbbff0a806a4728c99295b254c8838933b5b082d75e3cb70c8dab21fdfbcfa9a" +checksum = "cc2b571658e38e0c01b1fdca3bbbe93c00d3d71693ff2770043f8c29bc7d6f80" dependencies = [ "bytes", "futures-channel", "futures-util", - "h2 0.4.5", - "http 1.1.0", - "http-body 1.0.1", + "h2", + "http", + "http-body", "httparse", "httpdate", "itoa", @@ -2112,16 +2052,19 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.24.2" +version = "0.27.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" +checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" dependencies = [ - "futures-util", - "http 0.2.12", - "hyper 0.14.30", + "http", + "hyper", + "hyper-util", "rustls", + "rustls-pki-types", "tokio", "tokio-rustls", + "tower-service", + "webpki-roots 1.0.2", ] [[package]] @@ -2130,7 +2073,7 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3203a961e5c83b6f5498933e78b6b263e208c197b63e9c6c53cc82ffd3f63793" dependencies = [ - "hyper 1.5.0", + "hyper", "hyper-util", "pin-project-lite", "tokio", @@ -2139,18 +2082,20 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.9" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41296eb09f183ac68eec06e03cdbea2e759633d4067b2f6552fc2e009bcad08b" +checksum = "8d9b05277c7e8da2c93a568989bb6207bef0112e8d17df7a6eda4a3cf143bc5e" dependencies = [ "bytes", "futures-channel", + "futures-core", "futures-util", - "http 1.1.0", - "http-body 1.0.1", - "hyper 1.5.0", + "http", + "http-body", + "hyper", + "libc", "pin-project-lite", - "socket2", + "socket2 0.6.0", "tokio", "tower-service", "tracing", @@ -2243,9 +2188,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.6.0" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" +checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" dependencies = [ "equivalent", "hashbrown 0.15.0", @@ -2267,12 +2212,11 @@ dependencies = [ [[package]] name = "inferno" -version = "0.11.21" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "232929e1d75fe899576a3d5c7416ad0d88dbfbb3c3d6aa00873a7408a50ddb88" +checksum = "e96d2465363ed2d81857759fc864cf6bb7997f79327aec028d65bd7989393685" dependencies = [ "ahash", - "is-terminal", "itoa", "log", "num-format", @@ -2293,13 +2237,12 @@ dependencies = [ [[package]] name = "insta" -version = "1.40.0" +version = "1.43.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6593a41c7a73841868772495db7dc1e8ecab43bb5c0b6da2059246c4b506ab60" +checksum = "154934ea70c58054b556dd430b99a98c2a7ff5309ac9891597e339b5c28f4371" dependencies = [ "console", - "lazy_static", - "linked-hash-map", + "once_cell", "pest", "pest_derive", "ron", @@ -2313,7 +2256,18 @@ version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", +] + +[[package]] +name = "io-uring" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d93587f37623a1a17d94ef2bc9ada592f5465fe7732084ab7beefabe5c77c0c4" +dependencies = [ + "bitflags 2.9.2", + "cfg-if", + "libc", ] [[package]] @@ -2366,6 +2320,15 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.11" @@ -2383,10 +2346,11 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.69" +version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" dependencies = [ + "once_cell", "wasm-bindgen", ] @@ -2402,64 +2366,90 @@ dependencies = [ ] [[package]] -name = "jsonrpc-core" -version = "18.0.0" +name = "jsonrpsee" +version = "0.24.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14f7f76aef2d054868398427f6c54943cf3d1caa9a7ec7d0c38d69df97a965eb" +checksum = "37b26c20e2178756451cfeb0661fb74c47dd5988cb7e3939de7e9241fd604d42" dependencies = [ - "futures", - "futures-executor", + "jsonrpsee-core", + "jsonrpsee-server", + "jsonrpsee-types", + "tokio", +] + +[[package]] +name = "jsonrpsee-core" +version = "0.24.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "456196007ca3a14db478346f58c7238028d55ee15c1df15115596e411ff27925" +dependencies = [ + "async-trait", + "bytes", "futures-util", - "log", + "http", + "http-body", + "http-body-util", + "jsonrpsee-types", + "parking_lot", + "rand 0.8.5", + "rustc-hash 2.1.1", "serde", - "serde_derive", "serde_json", + "thiserror 1.0.64", + "tokio", + "tracing", ] [[package]] -name = "jsonrpc-derive" -version = "18.0.0" +name = "jsonrpsee-proc-macros" +version = "0.24.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b939a78fa820cdfcb7ee7484466746a7377760970f6f9c6fe19f9edcc8a38d2" +checksum = "5e65763c942dfc9358146571911b0cd1c361c2d63e2d2305622d40d36376ca80" dependencies = [ - "proc-macro-crate 0.1.5", + "heck 0.5.0", + "proc-macro-crate", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.106", ] [[package]] -name = "jsonrpc-http-server" -version = "18.0.0" +name = "jsonrpsee-server" +version = "0.24.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1dea6e07251d9ce6a552abfb5d7ad6bc290a4596c8dcc3d795fae2bbdc1f3ff" +checksum = "55e363146da18e50ad2b51a0a7925fc423137a0b1371af8235b1c231a0647328" dependencies = [ - "futures", - "hyper 0.14.30", - "jsonrpc-core", - "jsonrpc-server-utils", - "log", - "net2", - "parking_lot 0.11.2", - "unicase", -] - -[[package]] -name = "jsonrpc-server-utils" -version = "18.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa4fdea130485b572c39a460d50888beb00afb3e35de23ccd7fad8ff19f0e0d4" -dependencies = [ - "bytes", - "futures", - "globset", - "jsonrpc-core", - "lazy_static", - "log", + "futures-util", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-util", + "jsonrpsee-core", + "jsonrpsee-types", + "pin-project", + "route-recognizer", + "serde", + "serde_json", + "soketto", + "thiserror 1.0.64", "tokio", "tokio-stream", - "tokio-util 0.6.10", - "unicase", + "tokio-util", + "tower", + "tracing", +] + +[[package]] +name = "jsonrpsee-types" +version = "0.24.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08a8e70baf945b6b5752fc8eb38c918a48f1234daf11355e07106d963f860089" +dependencies = [ + "http", + "serde", + "serde_json", + "thiserror 1.0.64", ] [[package]] @@ -2482,7 +2472,7 @@ version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "956ff9b67e26e1a6a866cb758f12c6f8746208489e3e4a4b5580802f2f0a587b" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "ecdsa", "elliptic-curve", "sha2 0.10.8", @@ -2537,8 +2527,8 @@ version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" dependencies = [ - "cfg-if 1.0.0", - "windows-targets 0.52.6", + "cfg-if", + "windows-targets", ] [[package]] @@ -2553,7 +2543,7 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.2", "libc", ] @@ -2563,7 +2553,7 @@ version = "0.16.0+8.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce3d60bc059831dc1c83903fb45c103f75db65c5a7bf22272764d9cc683e348c" dependencies = [ - "bindgen", + "bindgen 0.69.4", "bzip2-sys", "cc", "glob", @@ -2594,18 +2584,6 @@ dependencies = [ "vcpkg", ] -[[package]] -name = "linked-hash-map" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" - -[[package]] -name = "linux-raw-sys" -version = "0.4.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" - [[package]] name = "linux-raw-sys" version = "0.9.4" @@ -2630,9 +2608,15 @@ dependencies = [ [[package]] name = "log" -version = "0.4.22" +version = "0.4.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" + +[[package]] +name = "lru-slab" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" +checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" [[package]] name = "lz4-sys" @@ -2665,7 +2649,7 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ea1f30cedd69f0a2954655f7188c6a834246d2bcf1e315e2ac40c4b24dc9519" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "rayon", ] @@ -2699,14 +2683,14 @@ checksum = "85b6f8152da6d7892ff1b7a1c0fa3f435e92b5918ad67035c3bb432111d9a29b" dependencies = [ "base64 0.22.1", "http-body-util", - "hyper 1.5.0", + "hyper", "hyper-util", - "indexmap 2.6.0", + "indexmap 2.10.0", "ipnet", "metrics", "metrics-util", "quanta", - "thiserror", + "thiserror 1.0.64", "tokio", "tracing", ] @@ -2746,6 +2730,15 @@ dependencies = [ "adler", ] +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", +] + [[package]] name = "mio" version = "1.0.1" @@ -2779,25 +2772,14 @@ dependencies = [ "getrandom 0.2.15", ] -[[package]] -name = "net2" -version = "0.2.39" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b13b648036a2339d06de780866fbdfda0dde886de7b3af2ddeba8b14f4ee34ac" -dependencies = [ - "cfg-if 0.1.10", - "libc", - "winapi", -] - [[package]] name = "nix" version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" dependencies = [ - "bitflags 2.6.0", - "cfg-if 1.0.0", + "bitflags 2.9.2", + "cfg-if", "cfg_aliases", "libc", ] @@ -2812,12 +2794,6 @@ dependencies = [ "minimal-lexical", ] -[[package]] -name = "nonempty" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9e591e719385e6ebaeb5ce5d3887f7d5676fceca6411d1925ccc95745f3d6f7" - [[package]] name = "nonempty" version = "0.11.0" @@ -2841,7 +2817,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" dependencies = [ "num-integer", - "num-traits", + "num-traits 0.2.19", ] [[package]] @@ -2866,7 +2842,16 @@ version = "0.1.46" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" dependencies = [ - "num-traits", + "num-traits 0.2.19", +] + +[[package]] +name = "num-traits" +version = "0.1.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92e5113e9fd4cc14ded8e499429f396a20f98c772a47cc8622a736e1ec843c31" +dependencies = [ + "num-traits 0.2.19", ] [[package]] @@ -2876,7 +2861,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", - "libm", ] [[package]] @@ -2915,9 +2899,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.20.2" +version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" [[package]] name = "oorandom" @@ -2958,7 +2942,7 @@ dependencies = [ "k256", "lazy_static", "memuse", - "nonempty 0.11.0", + "nonempty", "pasta_curves", "proptest", "rand 0.8.5", @@ -3009,9 +2993,9 @@ checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f" [[package]] name = "owo-colors" -version = "4.1.0" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb37767f6569cd834a413442455e0f066d0d522de8630436e2a1761d9726ba56" +checksum = "48dd4f4a2c8405440fd0462561f0e5806bd0f77e86f51c761481bdd4018b545e" [[package]] name = "pairing" @@ -3042,23 +3026,12 @@ version = "3.6.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d830939c76d294956402033aee57a6da7b438f2294eb94864c37b0569053a42c" dependencies = [ - "proc-macro-crate 3.1.0", + "proc-macro-crate", "proc-macro2", "quote", "syn 1.0.109", ] -[[package]] -name = "parking_lot" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" -dependencies = [ - "instant", - "lock_api", - "parking_lot_core 0.8.6", -] - [[package]] name = "parking_lot" version = "0.12.3" @@ -3066,21 +3039,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" dependencies = [ "lock_api", - "parking_lot_core 0.9.10", -] - -[[package]] -name = "parking_lot_core" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc" -dependencies = [ - "cfg-if 1.0.0", - "instant", - "libc", - "redox_syscall 0.2.16", - "smallvec", - "winapi", + "parking_lot_core", ] [[package]] @@ -3089,11 +3048,11 @@ version = "0.9.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "libc", - "redox_syscall 0.5.3", + "redox_syscall", "smallvec", - "windows-targets 0.52.6", + "windows-targets", ] [[package]] @@ -3124,7 +3083,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cd53dff83f26735fdc1ca837098ccf133605d794cdae66acfc2bfac3ec809d95" dependencies = [ "memchr", - "thiserror", + "thiserror 1.0.64", "ucd-trie", ] @@ -3148,7 +3107,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.106", ] [[package]] @@ -3169,27 +3128,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" dependencies = [ "fixedbitset", - "indexmap 2.6.0", + "indexmap 2.10.0", ] [[package]] name = "pin-project" -version = "1.1.7" +version = "1.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be57f64e946e500c8ee36ef6331845d40a93055567ec57e8fae13efd33759b95" +checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.7" +version = "1.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c0f5fad0874fc7abcd4d750e76917eaebbecaa2c20bde22e1dbeeba8beb758c" +checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.106", ] [[package]] @@ -3226,7 +3185,7 @@ version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a15b6eccb8484002195a3e44fe65a4ce8e93a625797a063735536fd59cb01cf3" dependencies = [ - "num-traits", + "num-traits 0.2.19", "plotters-backend", "plotters-svg", "wasm-bindgen", @@ -3287,7 +3246,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f12335488a2f3b0a83b14edad48dca9879ce89b2edd10e80237e4e852dd645e" dependencies = [ "proc-macro2", - "syn 2.0.85", + "syn 2.0.106", ] [[package]] @@ -3301,15 +3260,6 @@ dependencies = [ "uint 0.9.5", ] -[[package]] -name = "proc-macro-crate" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d6ea3c4595b96363c13943497db34af4460fb474a95c43f4446ad341b8c9785" -dependencies = [ - "toml 0.5.11", -] - [[package]] name = "proc-macro-crate" version = "3.1.0" @@ -3362,29 +3312,29 @@ dependencies = [ "proc-macro-error-attr2", "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.106", ] [[package]] name = "proc-macro2" -version = "1.0.86" +version = "1.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" dependencies = [ "unicode-ident", ] [[package]] name = "proptest" -version = "1.5.0" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4c2511913b88df1637da85cc8d96ec8e43a3f8bb8ccb71ee1ac240d6f3df58d" +checksum = "14cae93065090804185d3b75f0bf93b8eeda30c7a9b4a33d3bdb3988d6229e50" dependencies = [ "bit-set", "bit-vec", - "bitflags 2.6.0", + "bitflags 2.9.2", "lazy_static", - "num-traits", + "num-traits 0.2.19", "rand 0.8.5", "rand_chacha 0.3.1", "rand_xorshift", @@ -3402,14 +3352,14 @@ checksum = "6ff7ff745a347b87471d859a377a9a404361e7efc2a971d73424a6d183c0fc77" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.106", ] [[package]] name = "prost" -version = "0.13.3" +version = "0.13.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b0487d90e047de87f984913713b85c601c05609aad5b0df4b4573fbf69aa13f" +checksum = "2796faa41db3ec313a31f7624d9286acf277b52de526150b7e69f3debf891ee5" dependencies = [ "bytes", "prost-derive", @@ -3432,21 +3382,21 @@ dependencies = [ "prost", "prost-types", "regex", - "syn 2.0.85", + "syn 2.0.106", "tempfile", ] [[package]] name = "prost-derive" -version = "0.13.3" +version = "0.13.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9552f850d5f0964a4e4d0bf306459ac29323ddfbae05e35a7c0d35cb0803cc5" +checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d" dependencies = [ "anyhow", "itertools 0.13.0", "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.106", ] [[package]] @@ -3481,9 +3431,9 @@ checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" [[package]] name = "quick-xml" -version = "0.26.0" +version = "0.37.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f50b1c63b38611e7d4d7f68b82d3ad0cc71a2ad2e7f61fc10f1328d917c93cd" +checksum = "331e97a1af0bf59823e6eadffe373d7b27f485be8748f71471c662c1f269b7fb" dependencies = [ "memchr", ] @@ -3511,15 +3461,76 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "quinn" +version = "0.11.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "626214629cda6781b6dc1d316ba307189c85ba657213ce642d9c77670f8202c8" +dependencies = [ + "bytes", + "cfg_aliases", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash 2.1.1", + "rustls", + "socket2 0.5.7", + "thiserror 2.0.15", + "tokio", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-proto" +version = "0.11.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49df843a9161c85bb8aae55f101bc0bac8bcafd637a620d9122fd7e0b2f7422e" +dependencies = [ + "bytes", + "getrandom 0.3.3", + "lru-slab", + "rand 0.9.2", + "ring", + "rustc-hash 2.1.1", + "rustls", + "rustls-pki-types", + "slab", + "thiserror 2.0.15", + "tinyvec", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-udp" +version = "0.5.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcebb1209ee276352ef14ff8732e24cc2b02bbac986cd74a4c81bcb2f9881970" +dependencies = [ + "cfg_aliases", + "libc", + "once_cell", + "socket2 0.5.7", + "tracing", + "windows-sys 0.59.0", +] + [[package]] name = "quote" -version = "1.0.37" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" dependencies = [ "proc-macro2", ] +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + [[package]] name = "radium" version = "0.7.0" @@ -3550,6 +3561,16 @@ dependencies = [ "rand_core 0.6.4", ] +[[package]] +name = "rand" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" +dependencies = [ + "rand_chacha 0.9.0", + "rand_core 0.9.3", +] + [[package]] name = "rand_chacha" version = "0.2.2" @@ -3570,6 +3591,16 @@ dependencies = [ "rand_core 0.6.4", ] +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.3", +] + [[package]] name = "rand_core" version = "0.5.1" @@ -3588,6 +3619,15 @@ dependencies = [ "getrandom 0.2.15", ] +[[package]] +name = "rand_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +dependencies = [ + "getrandom 0.3.3", +] + [[package]] name = "rand_hc" version = "0.2.0" @@ -3612,7 +3652,7 @@ version = "11.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb9ee317cfe3fbd54b36a511efc1edd42e216903c9cd575e686dd68a2ba90d8d" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.2", ] [[package]] @@ -3649,62 +3689,61 @@ dependencies = [ "pasta_curves", "rand_core 0.6.4", "serde", - "thiserror", + "thiserror 1.0.64", "zeroize", ] [[package]] name = "redjubjub" -version = "0.7.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a60db2c3bc9c6fd1e8631fee75abc008841d27144be744951d6b9b75f9b569c" +checksum = "89b0ac1bc6bb3696d2c6f52cff8fba57238b81da8c0214ee6cd146eb8fde364e" dependencies = [ "rand_core 0.6.4", "reddsa", "serde", - "thiserror", + "thiserror 1.0.64", "zeroize", ] [[package]] -name = "redjubjub" -version = "0.8.0" +name = "redox_syscall" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89b0ac1bc6bb3696d2c6f52cff8fba57238b81da8c0214ee6cd146eb8fde364e" +checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4" dependencies = [ - "rand_core 0.6.4", - "reddsa", - "thiserror", - "zeroize", + "bitflags 2.9.2", ] [[package]] -name = "redox_syscall" -version = "0.2.16" +name = "redox_users" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac" dependencies = [ - "bitflags 1.3.2", + "getrandom 0.2.15", + "libredox", + "thiserror 2.0.15", ] [[package]] -name = "redox_syscall" -version = "0.5.3" +name = "ref-cast" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4" +checksum = "4a0ae411dbe946a674d89546582cea4ba2bb8defac896622d6496f14c23ba5cf" dependencies = [ - "bitflags 2.6.0", + "ref-cast-impl", ] [[package]] -name = "redox_users" -version = "0.4.5" +name = "ref-cast-impl" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd283d9651eeda4b2a83a43c1c91b266c40fd76ecd39a50a8c630ae69dc72891" +checksum = "1165225c21bff1f3bbce98f5a1f889949bc902d3575308cc7b0de30b4f6d27c7" dependencies = [ - "getrandom 0.2.15", - "libredox", - "thiserror", + "proc-macro2", + "quote", + "syn 2.0.106", ] [[package]] @@ -3753,21 +3792,22 @@ checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "reqwest" -version = "0.11.27" +version = "0.12.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" +checksum = "a77c62af46e79de0a562e1a9849205ffcb7fc1238876e9bd743357570e04046f" dependencies = [ "async-compression", - "base64 0.21.7", + "base64 0.22.1", "bytes", - "encoding_rs", + "futures-channel", "futures-core", "futures-util", - "h2 0.3.26", - "http 0.2.12", - "http-body 0.4.6", - "hyper 0.14.30", + "http", + "http-body", + "http-body-util", + "hyper", "hyper-rustls", + "hyper-util", "ipnet", "js-sys", "log", @@ -3775,23 +3815,24 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite", + "quinn", "rustls", "rustls-pemfile", + "rustls-pki-types", "serde", "serde_json", "serde_urlencoded", - "sync_wrapper 0.1.2", - "system-configuration", + "sync_wrapper 1.0.1", "tokio", "tokio-rustls", - "tokio-util 0.7.12", + "tokio-util", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "webpki-roots", - "winreg", + "webpki-roots 0.26.11", + "windows-registry", ] [[package]] @@ -3810,7 +3851,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" dependencies = [ "cc", - "cfg-if 1.0.0", + "cfg-if", "getrandom 0.2.15", "libc", "spin", @@ -3866,6 +3907,12 @@ dependencies = [ "serde", ] +[[package]] +name = "route-recognizer" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afab94fb28594581f62d981211a9a4d53cc8130bbcbbb89a0440d9b8e81a7746" + [[package]] name = "rustc-demangle" version = "0.1.24" @@ -3879,19 +3926,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] -name = "rustc-hex" -version = "2.1.0" +name = "rustc-hash" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" [[package]] -name = "rustc_version" -version = "0.2.3" +name = "rustc-hex" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" -dependencies = [ - "semver 0.9.0", -] +checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" [[package]] name = "rustc_version" @@ -3899,20 +3943,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" dependencies = [ - "semver 1.0.23", -] - -[[package]] -name = "rustix" -version = "0.38.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" -dependencies = [ - "bitflags 2.6.0", - "errno", - "libc", - "linux-raw-sys 0.4.14", - "windows-sys 0.52.0", + "semver", ] [[package]] @@ -3921,49 +3952,63 @@ version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "11181fbabf243db407ef8df94a6ce0b2f9a733bd8be4ad02b4eda9602296cac8" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.2", "errno", "libc", - "linux-raw-sys 0.9.4", + "linux-raw-sys", "windows-sys 0.59.0", ] [[package]] name = "rustls" -version = "0.21.12" +version = "0.23.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" +checksum = "c0ebcbd2f03de0fc1122ad9bb24b127a5a6cd51d72604a3f3c50ac459762b6cc" dependencies = [ "log", + "once_cell", "ring", + "rustls-pki-types", "rustls-webpki", - "sct", + "subtle", + "zeroize", ] [[package]] name = "rustls-pemfile" -version = "1.0.4" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" +checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" dependencies = [ - "base64 0.21.7", + "rustls-pki-types", +] + +[[package]] +name = "rustls-pki-types" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79" +dependencies = [ + "web-time", + "zeroize", ] [[package]] name = "rustls-webpki" -version = "0.101.7" +version = "0.103.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" +checksum = "0a17884ae0c1b773f1ccd2bd4a8c72f16da897310a98b0e84bf349ad5ead92fc" dependencies = [ "ring", + "rustls-pki-types", "untrusted", ] [[package]] name = "rustversion" -version = "1.0.17" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" [[package]] name = "rusty-fork" @@ -4016,7 +4061,7 @@ dependencies = [ "memuse", "rand 0.8.5", "rand_core 0.6.4", - "redjubjub 0.8.0", + "redjubjub", "subtle", "tracing", "zcash_note_encryption", @@ -4025,21 +4070,35 @@ dependencies = [ ] [[package]] -name = "scopeguard" -version = "1.2.0" +name = "schemars" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +checksum = "4cd191f9397d57d581cddd31014772520aa448f65ef991055d7f61582c65165f" +dependencies = [ + "dyn-clone", + "ref-cast", + "serde", + "serde_json", +] [[package]] -name = "sct" -version = "0.7.1" +name = "schemars" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" +checksum = "82d20c4491bc164fa2f6c5d44565947a52ad80b9505d8e36f8d54c27c739fcd0" dependencies = [ - "ring", - "untrusted", + "dyn-clone", + "ref-cast", + "serde", + "serde_json", ] +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + [[package]] name = "sec1" version = "0.7.3" @@ -4053,32 +4112,14 @@ dependencies = [ "zeroize", ] -[[package]] -name = "secp256k1" -version = "0.27.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25996b82292a7a57ed3508f052cfff8640d38d32018784acd714758b43da9c8f" -dependencies = [ - "secp256k1-sys 0.8.1", - "serde", -] - [[package]] name = "secp256k1" version = "0.29.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9465315bc9d4566e1724f0fffcbcc446268cb522e60f9a27bcded6b19c108113" dependencies = [ - "secp256k1-sys 0.10.1", -] - -[[package]] -name = "secp256k1-sys" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70a129b9e9efbfb223753b9163c4ab3b13cff7fd9c7f010fbac25ab4099fa07e" -dependencies = [ - "cc", + "secp256k1-sys", + "serde", ] [[package]] @@ -4102,33 +4143,18 @@ dependencies = [ [[package]] name = "semver" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" -dependencies = [ - "semver-parser", -] - -[[package]] -name = "semver" -version = "1.0.23" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" +checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" dependencies = [ "serde", ] -[[package]] -name = "semver-parser" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" - [[package]] name = "sentry" -version = "0.32.2" +version = "0.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "766448f12e44d68e675d5789a261515c46ac6ccd240abdd451a9c46c84a49523" +checksum = "69ccd7644577f876e92e7873d505ad35c30fc4fcf1654d4885c74605c95d0a07" dependencies = [ "httpdate", "reqwest", @@ -4139,43 +4165,40 @@ dependencies = [ "sentry-tracing", "tokio", "ureq", - "webpki-roots", ] [[package]] name = "sentry-backtrace" -version = "0.32.3" +version = "0.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a79194074f34b0cbe5dd33896e5928bbc6ab63a889bd9df2264af5acb186921e" +checksum = "cd11d45dfae763b628d5dc45872ade9dc7b1e6894e3d3787ee8d95bf426afe12" dependencies = [ "backtrace", - "once_cell", "regex", "sentry-core", ] [[package]] name = "sentry-contexts" -version = "0.32.3" +version = "0.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eba8870c5dba2bfd9db25c75574a11429f6b95957b0a78ac02e2970dd7a5249a" +checksum = "42cd374f0936ce0fdb0afcfbd896aa2db5f828b4b4c6883d4b9c4e680577a9cd" dependencies = [ "hostname", "libc", "os_info", - "rustc_version 0.4.0", + "rustc_version", "sentry-core", "uname", ] [[package]] name = "sentry-core" -version = "0.32.3" +version = "0.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46a75011ea1c0d5c46e9e57df03ce81f5c7f0a9e199086334a1f9c0a541e0826" +checksum = "36e9d6c4dbf4db62c8aa6f9808ce16447ac265221c8620f78e63c159256c5f85" dependencies = [ - "once_cell", - "rand 0.8.5", + "rand 0.9.2", "sentry-types", "serde", "serde_json", @@ -4183,9 +4206,9 @@ dependencies = [ [[package]] name = "sentry-tracing" -version = "0.32.3" +version = "0.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f715932bf369a61b7256687c6f0554141b7ce097287e30e3f7ed6e9de82498fe" +checksum = "532c5f7a9b633e6f2bf6623223cede95b112a7f104141a8cf050003417fc4d54" dependencies = [ "sentry-backtrace", "sentry-core", @@ -4195,16 +4218,16 @@ dependencies = [ [[package]] name = "sentry-types" -version = "0.32.3" +version = "0.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4519c900ce734f7a0eb7aba0869dfb225a7af8820634a7dd51449e3b093cfb7c" +checksum = "aad0332036824f2c4e5f241495107a4769a64f4b46a8e95db1108a124e6d2d00" dependencies = [ "debugid", "hex", - "rand 0.8.5", + "rand 0.9.2", "serde", "serde_json", - "thiserror", + "thiserror 2.0.15", "time", "url", "uuid", @@ -4212,9 +4235,9 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.214" +version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f55c3193aca71c12ad7890f1785d2b73e1b9f63a0bbc353c08ef26fe03fc56b5" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" dependencies = [ "serde_derive", ] @@ -4230,22 +4253,22 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.214" +version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de523f781f095e28fa605cdce0f8307e451cc0fd14e2eb4cd2e98a355b147766" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.106", ] [[package]] name = "serde_json" -version = "1.0.132" +version = "1.0.142" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03" +checksum = "030fedb782600dcbd6f02d479bf0d817ac3bb40d644745b769d6a96bc3afc5a7" dependencies = [ - "indexmap 2.6.0", + "indexmap 2.10.0", "itoa", "memchr", "ryu", @@ -4275,54 +4298,34 @@ dependencies = [ [[package]] name = "serde_with" -version = "1.14.0" +version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "678b5a069e50bf00ecd22d0cd8ddf7c236f68581b03db652061ed5eb13a312ff" -dependencies = [ - "serde", - "serde_with_macros 1.5.2", -] - -[[package]] -name = "serde_with" -version = "3.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e28bdad6db2b8340e449f7108f020b3b092e8583a9e3fb82713e1d4e71fe817" +checksum = "f2c45cd61fefa9db6f254525d46e392b852e0e61d9a1fd36e5bd183450a556d5" dependencies = [ "base64 0.22.1", "chrono", "hex", "indexmap 1.9.3", - "indexmap 2.6.0", + "indexmap 2.10.0", + "schemars 0.9.0", + "schemars 1.0.4", "serde", "serde_derive", "serde_json", - "serde_with_macros 3.11.0", + "serde_with_macros", "time", ] [[package]] name = "serde_with_macros" -version = "1.5.2" +version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e182d6ec6f05393cc0e5ed1bf81ad6db3a8feedf8ee515ecdd369809bcce8082" +checksum = "de90945e6565ce0d9a25098082ed4ee4002e047cb59892c318d66821e14bb30f" dependencies = [ - "darling 0.13.4", + "darling", "proc-macro2", "quote", - "syn 1.0.109", -] - -[[package]] -name = "serde_with_macros" -version = "3.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d846214a9854ef724f3da161b426242d8de7c1fc7de2f89bb1efcb154dca79d" -dependencies = [ - "darling 0.20.10", - "proc-macro2", - "quote", - "syn 2.0.85", + "syn 2.0.106", ] [[package]] @@ -4331,7 +4334,7 @@ version = "0.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "59e2dd588bf1597a252c3b920e0143eb99b0f76e4e082f4c92ce34fbc9e71ddd" dependencies = [ - "indexmap 2.6.0", + "indexmap 2.10.0", "itoa", "libyml", "memchr", @@ -4340,13 +4343,35 @@ dependencies = [ "version_check", ] +[[package]] +name = "sha-1" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5058ada175748e33390e40e872bd0fe59a19f265d0158daa551c5a88a76009c" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.7", +] + +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.7", +] + [[package]] name = "sha2" version = "0.10.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "cpufeatures", "digest 0.10.7", ] @@ -4357,7 +4382,7 @@ version = "0.11.0-pre.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "540c0893cce56cdbcfebcec191ec8e0f470dd1889b6e7a0b503e310a94a168f5" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "cpufeatures", "digest 0.11.0-pre.9", ] @@ -4377,7 +4402,7 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "637e95dcd06bc1bb3f86ed9db1e1832a70125f32daae071ef37dcb7701b7d4fe" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.2", "either", "incrementalmerkletree", "tracing", @@ -4466,6 +4491,32 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "socket2" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + +[[package]] +name = "soketto" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e859df029d160cb88608f5d7df7fb4753fd20fdfb4de5644f3d8b8440841721" +dependencies = [ + "base64 0.22.1", + "bytes", + "futures", + "http", + "httparse", + "log", + "rand 0.8.5", + "sha1", +] + [[package]] name = "spandoc" version = "0.2.2" @@ -4525,12 +4576,6 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" -[[package]] -name = "strsim" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" - [[package]] name = "strsim" version = "0.11.1" @@ -4580,9 +4625,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.85" +version = "2.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5023162dfcd14ef8f32034d8bcd4cc5ddc61ef7a247c024a33e24e1f24d21b56" +checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" dependencies = [ "proc-macro2", "quote", @@ -4600,6 +4645,9 @@ 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" @@ -4613,27 +4661,6 @@ dependencies = [ "unicode-xid", ] -[[package]] -name = "system-configuration" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" -dependencies = [ - "bitflags 1.3.2", - "core-foundation", - "system-configuration-sys", -] - -[[package]] -name = "system-configuration-sys" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" -dependencies = [ - "core-foundation-sys", - "libc", -] - [[package]] name = "tap" version = "1.0.1" @@ -4642,14 +4669,14 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "tempfile" -version = "3.13.0" +version = "3.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0f2c9fc62d0beef6951ccffd757e241266a2c833136efbe35af6cd2567dca5b" +checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1" dependencies = [ - "cfg-if 1.0.0", "fastrand", + "getrandom 0.3.3", "once_cell", - "rustix 0.38.37", + "rustix", "windows-sys 0.59.0", ] @@ -4668,37 +4695,57 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" dependencies = [ - "unicode-width", + "unicode-width", +] + +[[package]] +name = "thiserror" +version = "1.0.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84" +dependencies = [ + "thiserror-impl 1.0.64", +] + +[[package]] +name = "thiserror" +version = "2.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d76d3f064b981389ecb4b6b7f45a0bf9fdac1d5b9204c7bd6714fecc302850" +dependencies = [ + "thiserror-impl 2.0.15", ] [[package]] -name = "thiserror" +name = "thiserror-impl" version = "1.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84" +checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" dependencies = [ - "thiserror-impl", + "proc-macro2", + "quote", + "syn 2.0.106", ] [[package]] name = "thiserror-impl" -version = "1.0.64" +version = "2.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" +checksum = "44d29feb33e986b6ea906bd9c3559a856983f92371b3eaa5e83782a351623de0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.106", ] [[package]] name = "thread-priority" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d3b04d33c9633b8662b167b847c7ab521f83d1ae20f2321b65b5b925e532e36" +checksum = "cfe075d7053dae61ac5413a34ea7d4913b6e6207844fd726bdd858b37ff72bf5" dependencies = [ - "bitflags 2.6.0", - "cfg-if 1.0.0", + "bitflags 2.9.2", + "cfg-if", "libc", "log", "rustversion", @@ -4711,7 +4758,7 @@ version = "1.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "once_cell", ] @@ -4760,9 +4807,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.8.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" +checksum = "09b3661f17e86524eccd4371ab0429194e0d7c008abb45f7a7495b1719463c71" dependencies = [ "tinyvec_macros", ] @@ -4775,39 +4822,41 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.41.0" +version = "1.47.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "145f3413504347a2be84393cc8a7d2fb4d863b375909ea59f2158261aa258bbb" +checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038" dependencies = [ "backtrace", "bytes", + "io-uring", "libc", "mio", - "parking_lot 0.12.3", + "parking_lot", "pin-project-lite", "signal-hook-registry", - "socket2", + "slab", + "socket2 0.6.0", "tokio-macros", "tracing", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] name = "tokio-macros" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" +checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.106", ] [[package]] name = "tokio-rustls" -version = "0.24.1" +version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" +checksum = "8e727b36a1a0e8b74c376ac2211e40c2c8af09fb4013c60d910495810f008e9b" dependencies = [ "rustls", "tokio", @@ -4815,14 +4864,14 @@ dependencies = [ [[package]] name = "tokio-stream" -version = "0.1.16" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f4e6ce100d0eb49a2734f8c0812bcd324cf357d21810932c5df6b96ef2b86f1" +checksum = "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047" dependencies = [ "futures-core", "pin-project-lite", "tokio", - "tokio-util 0.7.12", + "tokio-util", ] [[package]] @@ -4840,26 +4889,13 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.6.10" +version = "0.7.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36943ee01a6d67977dd3f84a5a1d2efeb4ada3a1ae771cadfaa535d9d9fc6507" -dependencies = [ - "bytes", - "futures-core", - "futures-sink", - "log", - "pin-project-lite", - "tokio", -] - -[[package]] -name = "tokio-util" -version = "0.7.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61e7c3654c13bcd040d4a03abee2c75b1d14a37b423cf5a813ceae1cc903ec6a" +checksum = "14307c986784f72ef81c89db7d9e28d6ac26d16213b109ea501696195e6e3ce5" dependencies = [ "bytes", "futures-core", + "futures-io", "futures-sink", "pin-project-lite", "tokio", @@ -4901,7 +4937,7 @@ version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" dependencies = [ - "indexmap 2.6.0", + "indexmap 2.10.0", "toml_datetime", "winnow 0.5.40", ] @@ -4912,7 +4948,7 @@ version = "0.22.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "583c44c02ad26b0c3f3066fe629275e50627026c51ac2e595cca4c230ce1ce1d" dependencies = [ - "indexmap 2.6.0", + "indexmap 2.10.0", "serde", "serde_spanned", "toml_datetime", @@ -4930,17 +4966,17 @@ dependencies = [ "axum", "base64 0.22.1", "bytes", - "h2 0.4.5", - "http 1.1.0", - "http-body 1.0.1", + "h2", + "http", + "http-body", "http-body-util", - "hyper 1.5.0", + "hyper", "hyper-timeout", "hyper-util", "percent-encoding", "pin-project", "prost", - "socket2", + "socket2 0.5.7", "tokio", "tokio-stream", "tower", @@ -4960,7 +4996,7 @@ dependencies = [ "prost-build", "prost-types", "quote", - "syn 2.0.85", + "syn 2.0.106", ] [[package]] @@ -4974,7 +5010,7 @@ dependencies = [ "prost-build", "prost-types", "quote", - "syn 2.0.85", + "syn 2.0.106", ] [[package]] @@ -5005,7 +5041,7 @@ dependencies = [ "rand 0.8.5", "slab", "tokio", - "tokio-util 0.7.12", + "tokio-util", "tower-layer", "tower-service", "tracing", @@ -5013,7 +5049,7 @@ dependencies = [ [[package]] name = "tower-batch-control" -version = "0.2.41-beta.17" +version = "0.2.41" dependencies = [ "color-eyre", "ed25519-zebra", @@ -5025,7 +5061,7 @@ dependencies = [ "tinyvec", "tokio", "tokio-test", - "tokio-util 0.7.12", + "tokio-util", "tower", "tower-fallback", "tower-test", @@ -5036,7 +5072,7 @@ dependencies = [ [[package]] name = "tower-fallback" -version = "0.2.41-beta.17" +version = "0.2.41" dependencies = [ "futures-core", "pin-project", @@ -5074,9 +5110,9 @@ dependencies = [ [[package]] name = "tracing" -version = "0.1.40" +version = "0.1.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" dependencies = [ "log", "pin-project-lite", @@ -5091,27 +5127,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3566e8ce28cc0a3fe42519fc80e6b4c943cc4c8cef275620eb8dac2d3d4e06cf" dependencies = [ "crossbeam-channel", - "thiserror", + "thiserror 1.0.64", "time", "tracing-subscriber", ] [[package]] name = "tracing-attributes" -version = "0.1.27" +version = "0.1.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.106", ] [[package]] name = "tracing-core" -version = "0.1.32" +version = "0.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" dependencies = [ "once_cell", "valuable", @@ -5183,9 +5219,9 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.18" +version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" dependencies = [ "matchers", "nu-ansi-term", @@ -5217,7 +5253,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04659ddb06c87d233c566112c1c9c5b9e98256d9af50ec3bc9c8327f873a7568" dependencies = [ "quote", - "syn 2.0.85", + "syn 2.0.106", ] [[package]] @@ -5277,15 +5313,6 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" -[[package]] -name = "unicase" -version = "2.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" -dependencies = [ - "version_check", -] - [[package]] name = "unicode-bidi" version = "0.3.15" @@ -5343,17 +5370,31 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "ureq" -version = "2.9.1" +version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8cdd25c339e200129fe4de81451814e5228c9b771d57378817d6117cc2b3f97" +checksum = "00432f493971db5d8e47a65aeb3b02f8226b9b11f1450ff86bb772776ebadd70" dependencies = [ - "base64 0.21.7", + "base64 0.22.1", "log", - "once_cell", + "percent-encoding", "rustls", - "rustls-webpki", - "url", - "webpki-roots", + "rustls-pemfile", + "rustls-pki-types", + "ureq-proto", + "utf-8", + "webpki-roots 1.0.2", +] + +[[package]] +name = "ureq-proto" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5b6cabebbecc4c45189ab06b52f956206cea7d8c8a20851c35a85cb169224cc" +dependencies = [ + "base64 0.22.1", + "http", + "httparse", + "log", ] [[package]] @@ -5368,6 +5409,12 @@ dependencies = [ "serde", ] +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + [[package]] name = "utf8parse" version = "0.2.2" @@ -5403,18 +5450,43 @@ checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" [[package]] name = "vergen" -version = "8.3.2" +version = "9.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2990d9ea5967266ea0ccf413a4aa5c42a93dbcfda9cb49a97de6931726b12566" +checksum = "349ed9e45296a581f455bc18039878f409992999bc1d5da12a6800eb18c8752f" dependencies = [ "anyhow", "cargo_metadata", - "cfg-if 1.0.0", - "git2", + "derive_builder", "regex", - "rustc_version 0.4.0", + "rustc_version", + "rustversion", + "vergen-lib", +] + +[[package]] +name = "vergen-git2" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e771aff771c0d7c2f42e434e2766d304d917e29b40f0424e8faaaa936bbc3f29" +dependencies = [ + "anyhow", + "derive_builder", + "git2", "rustversion", "time", + "vergen", + "vergen-lib", +] + +[[package]] +name = "vergen-lib" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b07e6010c0f3e59fcb164e0163834597da68d1f864e2b8ca49f74de01e9c166" +dependencies = [ + "anyhow", + "derive_builder", + "rustversion", ] [[package]] @@ -5431,7 +5503,7 @@ checksum = "d674d135b4a8c1d7e813e2f8d1c9a58308aee4a680323066025e53132218bd91" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.106", ] [[package]] @@ -5530,28 +5602,38 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasi" +version = "0.14.2+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" +dependencies = [ + "wit-bindgen-rt", +] + [[package]] name = "wasm-bindgen" -version = "0.2.92" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", + "once_cell", + "rustversion", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.92" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" dependencies = [ "bumpalo", "log", - "once_cell", "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.106", "wasm-bindgen-shared", ] @@ -5561,7 +5643,7 @@ version = "0.4.42" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "js-sys", "wasm-bindgen", "web-sys", @@ -5569,9 +5651,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.92" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -5579,22 +5661,25 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.92" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.106", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.92" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] [[package]] name = "web-sys" @@ -5606,22 +5691,32 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "webpki-roots" -version = "0.25.4" +version = "0.26.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" +checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" +dependencies = [ + "webpki-roots 1.0.2", +] [[package]] -name = "which" -version = "4.4.2" +name = "webpki-roots" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" +checksum = "7e8983c3ab33d6fb807cfcdad2491c4ea8cbc8ed839181c7dfd9c67c83e261b2" dependencies = [ - "either", - "home", - "once_cell", - "rustix 0.38.37", + "rustls-pki-types", ] [[package]] @@ -5632,7 +5727,7 @@ checksum = "24d643ce3fd3e5b54854602a080f34fb10ab75e0b813ee32d00ca2b44fa74762" dependencies = [ "either", "env_home", - "rustix 1.0.8", + "rustix", "winsafe", ] @@ -5674,7 +5769,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" dependencies = [ "windows-core", - "windows-targets 0.52.6", + "windows-targets", ] [[package]] @@ -5683,49 +5778,61 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-targets 0.52.6", + "windows-targets", ] [[package]] -name = "windows-sys" -version = "0.48.0" +name = "windows-link" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" + +[[package]] +name = "windows-registry" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0" dependencies = [ - "windows-targets 0.48.5", + "windows-result", + "windows-strings", + "windows-targets", ] [[package]] -name = "windows-sys" -version = "0.52.0" +name = "windows-result" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-strings" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" dependencies = [ - "windows-targets 0.52.6", + "windows-result", + "windows-targets", ] [[package]] name = "windows-sys" -version = "0.59.0" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.6", + "windows-targets", ] [[package]] -name = "windows-targets" -version = "0.48.5" +name = "windows-sys" +version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ - "windows_aarch64_gnullvm 0.48.5", - "windows_aarch64_msvc 0.48.5", - "windows_i686_gnu 0.48.5", - "windows_i686_msvc 0.48.5", - "windows_x86_64_gnu 0.48.5", - "windows_x86_64_gnullvm 0.48.5", - "windows_x86_64_msvc 0.48.5", + "windows-targets", ] [[package]] @@ -5734,46 +5841,28 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm 0.52.6", - "windows_aarch64_msvc 0.52.6", - "windows_i686_gnu 0.52.6", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", "windows_i686_gnullvm", - "windows_i686_msvc 0.52.6", - "windows_x86_64_gnu 0.52.6", - "windows_x86_64_gnullvm 0.52.6", - "windows_x86_64_msvc 0.52.6", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", ] -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" - [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" -[[package]] -name = "windows_i686_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" - [[package]] name = "windows_i686_gnu" version = "0.52.6" @@ -5786,48 +5875,24 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" -[[package]] -name = "windows_i686_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" - [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" - [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" - [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" - [[package]] name = "windows_x86_64_msvc" version = "0.52.6" @@ -5852,22 +5917,21 @@ dependencies = [ "memchr", ] -[[package]] -name = "winreg" -version = "0.50.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" -dependencies = [ - "cfg-if 1.0.0", - "windows-sys 0.48.0", -] - [[package]] name = "winsafe" version = "0.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904" +[[package]] +name = "wit-bindgen-rt" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" +dependencies = [ + "bitflags 2.9.2", +] + [[package]] name = "wyz" version = "0.5.1" @@ -5923,7 +5987,7 @@ dependencies = [ "hex", "incrementalmerkletree", "memuse", - "nonempty 0.11.0", + "nonempty", "percent-encoding", "prost", "rand_core 0.6.4", @@ -5936,7 +6000,7 @@ dependencies = [ "time-core", "tonic-build 0.13.1", "tracing", - "which 7.0.3", + "which", "zcash_address", "zcash_encoding", "zcash_keys", @@ -5954,7 +6018,7 @@ version = "0.3.0" source = "git+https://github.com/QED-it/librustzcash?rev=a996e53e7a561a4723be6a712e44c8eb5fad4a6c#a996e53e7a561a4723be6a712e44c8eb5fad4a6c" dependencies = [ "core2", - "nonempty 0.11.0", + "nonempty", ] [[package]] @@ -5980,7 +6044,7 @@ dependencies = [ "document-features", "group", "memuse", - "nonempty 0.11.0", + "nonempty", "rand_core 0.6.4", "sapling-crypto", "secrecy", @@ -6026,14 +6090,14 @@ dependencies = [ "incrementalmerkletree", "jubjub", "memuse", - "nonempty 0.11.0", + "nonempty", "orchard", "rand 0.8.5", "rand_core 0.6.4", - "redjubjub 0.8.0", + "redjubjub", "ripemd 0.1.3", "sapling-crypto", - "secp256k1 0.29.1", + "secp256k1", "sha2 0.10.8", "subtle", "tracing", @@ -6061,7 +6125,7 @@ dependencies = [ "known-folders", "lazy_static", "rand_core 0.6.4", - "redjubjub 0.8.0", + "redjubjub", "sapling-crypto", "tracing", "xdg", @@ -6081,12 +6145,20 @@ dependencies = [ [[package]] name = "zcash_script" -version = "0.2.0" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2122a042c77d529d3c60b899e74705eda39ae96a8a992460caeb06afa76990a2" +checksum = "caf6e76f310bb2d3cc233086a97c1710ba1de7ffbbf8198b8113407d0f427dfc" dependencies = [ - "bindgen", + "bindgen 0.72.0", + "bitflags 2.9.2", "cc", + "enum_primitive", + "ripemd 0.1.3", + "secp256k1", + "sha-1", + "sha2 0.10.8", + "thiserror 2.0.15", + "tracing", ] [[package]] @@ -6111,7 +6183,7 @@ dependencies = [ "getset", "hex", "ripemd 0.1.3", - "secp256k1 0.29.1", + "secp256k1", "sha2 0.10.8", "subtle", "zcash_address", @@ -6123,14 +6195,14 @@ dependencies = [ [[package]] name = "zebra-chain" -version = "1.0.0-beta.41" +version = "1.0.0" dependencies = [ - "bitflags 2.6.0", + "bech32", + "bitflags 2.9.2", "bitflags-serde-legacy", "bitvec", "blake2b_simd", "blake2s_simd", - "bridgetree", "bs58", "byteorder", "chrono", @@ -6145,10 +6217,9 @@ dependencies = [ "hex", "humantime", "incrementalmerkletree", - "itertools 0.13.0", + "itertools 0.14.0", "jubjub", "lazy_static", - "nonempty 0.7.0", "num-integer", "orchard", "primitive-types", @@ -6159,26 +6230,26 @@ dependencies = [ "rand_core 0.6.4", "rayon", "reddsa", - "redjubjub 0.7.0", + "redjubjub", "ripemd 0.1.3", "sapling-crypto", - "secp256k1 0.27.0", + "secp256k1", "serde", "serde-big-array", "serde_json", - "serde_with 3.11.0", + "serde_with", "sha2 0.10.8", + "sinsemilla 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "spandoc", "static_assertions", "tempfile", - "thiserror", + "thiserror 2.0.15", "tinyvec", "tokio", "tracing", "uint 0.10.0", "x25519-dalek", "zcash_address", - "zcash_client_backend", "zcash_encoding", "zcash_history", "zcash_note_encryption", @@ -6190,7 +6261,7 @@ dependencies = [ [[package]] name = "zebra-consensus" -version = "1.0.0-beta.41" +version = "1.0.0" dependencies = [ "bellman", "blake2b_simd", @@ -6215,7 +6286,7 @@ dependencies = [ "sapling-crypto", "serde", "spandoc", - "thiserror", + "thiserror 2.0.15", "tinyvec", "tokio", "tower", @@ -6234,33 +6305,11 @@ dependencies = [ "zebra-test", ] -[[package]] -name = "zebra-grpc" -version = "0.1.0-alpha.8" -dependencies = [ - "color-eyre", - "futures-util", - "insta", - "prost", - "serde", - "tokio", - "tokio-stream", - "tonic", - "tonic-build 0.12.3", - "tonic-reflection", - "tower", - "zcash_primitives", - "zebra-chain", - "zebra-node-services", - "zebra-state", - "zebra-test", -] - [[package]] name = "zebra-network" -version = "1.0.0-beta.41" +version = "1.0.0" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.2", "byteorder", "bytes", "chrono", @@ -6269,8 +6318,8 @@ dependencies = [ "hex", "howudoin", "humantime-serde", - "indexmap 2.6.0", - "itertools 0.13.0", + "indexmap 2.10.0", + "itertools 0.14.0", "lazy_static", "metrics", "num-integer", @@ -6284,10 +6333,10 @@ dependencies = [ "serde", "static_assertions", "tempfile", - "thiserror", + "thiserror 2.0.15", "tokio", "tokio-stream", - "tokio-util 0.7.12", + "tokio-util", "toml 0.8.19", "tower", "tracing", @@ -6299,10 +6348,10 @@ dependencies = [ [[package]] name = "zebra-node-services" -version = "1.0.0-beta.41" +version = "1.0.0" dependencies = [ "color-eyre", - "jsonrpc-core", + "jsonrpsee-types", "reqwest", "serde", "serde_json", @@ -6312,25 +6361,31 @@ dependencies = [ [[package]] name = "zebra-rpc" -version = "1.0.0-beta.41" +version = "1.0.0" dependencies = [ "base64 0.22.1", "chrono", "color-eyre", + "derive-getters", + "derive-new", "futures", "hex", - "indexmap 2.6.0", + "http-body-util", + "hyper", + "indexmap 2.10.0", "insta", - "jsonrpc-core", - "jsonrpc-derive", - "jsonrpc-http-server", + "jsonrpsee", + "jsonrpsee-proc-macros", + "jsonrpsee-types", "nix", "proptest", "prost", "rand 0.8.5", + "semver", "serde", "serde_json", - "thiserror", + "serde_with", + "thiserror 2.0.15", "tokio", "tokio-stream", "tonic", @@ -6339,8 +6394,10 @@ dependencies = [ "tower", "tracing", "zcash_address", + "zcash_keys", "zcash_primitives", "zcash_protocol", + "zcash_transparent", "zebra-chain", "zebra-consensus", "zebra-network", @@ -6350,60 +6407,13 @@ dependencies = [ "zebra-test", ] -[[package]] -name = "zebra-scan" -version = "0.1.0-alpha.10" -dependencies = [ - "bls12_381", - "chrono", - "color-eyre", - "ff", - "futures", - "group", - "hex", - "indexmap 2.6.0", - "insta", - "itertools 0.13.0", - "jsonrpc", - "jubjub", - "lazy_static", - "proptest", - "proptest-derive", - "rand 0.8.5", - "sapling-crypto", - "semver 1.0.23", - "serde", - "serde_json", - "structopt", - "tempfile", - "tokio", - "toml 0.8.19", - "tonic", - "tower", - "tracing", - "tracing-subscriber", - "zcash_address", - "zcash_client_backend", - "zcash_keys", - "zcash_note_encryption", - "zcash_primitives", - "zcash_protocol", - "zebra-chain", - "zebra-grpc", - "zebra-node-services", - "zebra-rpc", - "zebra-state", - "zebra-test", - "zebrad", -] - [[package]] name = "zebra-script" -version = "1.0.0-beta.41" +version = "1.0.0" dependencies = [ "hex", "lazy_static", - "thiserror", + "thiserror 2.0.15", "zcash_script", "zebra-chain", "zebra-test", @@ -6411,11 +6421,12 @@ dependencies = [ [[package]] name = "zebra-state" -version = "1.0.0-beta.41" +version = "1.0.1" dependencies = [ "bincode", "chrono", "color-eyre", + "crossbeam-channel", "dirs", "elasticsearch", "futures", @@ -6425,9 +6436,9 @@ dependencies = [ "howudoin", "human_bytes", "humantime-serde", - "indexmap 2.6.0", + "indexmap 2.10.0", "insta", - "itertools 0.13.0", + "itertools 0.14.0", "jubjub", "lazy_static", "metrics", @@ -6440,12 +6451,12 @@ dependencies = [ "regex", "rlimit", "rocksdb", - "semver 1.0.23", + "semver", "serde", "serde_json", "spandoc", "tempfile", - "thiserror", + "thiserror 2.0.15", "tinyvec", "tokio", "tower", @@ -6456,24 +6467,24 @@ dependencies = [ [[package]] name = "zebra-test" -version = "1.0.0-beta.41" +version = "1.0.0" dependencies = [ "color-eyre", "futures", "hex", "humantime", - "indexmap 2.6.0", + "indexmap 2.10.0", "insta", - "itertools 0.13.0", + "itertools 0.14.0", "lazy_static", "once_cell", - "owo-colors 4.1.0", + "owo-colors 4.2.2", "proptest", "rand 0.8.5", "regex", "spandoc", "tempfile", - "thiserror", + "thiserror 2.0.15", "tinyvec", "tokio", "tower", @@ -6484,12 +6495,12 @@ dependencies = [ [[package]] name = "zebra-utils" -version = "1.0.0-beta.41" +version = "1.0.0" dependencies = [ "color-eyre", "hex", - "indexmap 2.6.0", - "itertools 0.13.0", + "indexmap 2.10.0", + "itertools 0.14.0", "jsonrpc", "quote", "rand 0.8.5", @@ -6499,8 +6510,8 @@ dependencies = [ "serde_json", "serde_yml", "structopt", - "syn 2.0.85", - "thiserror", + "syn 2.0.106", + "thiserror 2.0.15", "tinyvec", "tokio", "tracing-error", @@ -6515,7 +6526,7 @@ dependencies = [ [[package]] name = "zebrad" -version = "2.0.0" +version = "2.4.2" dependencies = [ "abscissa_core", "atty", @@ -6531,13 +6542,13 @@ dependencies = [ "howudoin", "http-body-util", "humantime-serde", - "hyper 1.5.0", + "hyper", "hyper-util", - "indexmap 2.6.0", + "indexmap 2.10.0", "indicatif", "inferno", "insta", - "jsonrpc-core", + "jsonrpsee-types", "lazy_static", "log", "metrics", @@ -6551,12 +6562,12 @@ dependencies = [ "rand 0.8.5", "rayon", "regex", - "semver 1.0.23", + "semver", "sentry", "serde", "serde_json", "tempfile", - "thiserror", + "thiserror 2.0.15", "thread-priority", "tinyvec", "tokio", @@ -6573,13 +6584,14 @@ dependencies = [ "tracing-journald", "tracing-subscriber", "tracing-test", - "vergen", + "vergen-git2", + "zcash_keys", "zebra-chain", "zebra-consensus", - "zebra-grpc", "zebra-network", "zebra-node-services", "zebra-rpc", + "zebra-script", "zebra-state", "zebra-test", "zebra-utils", @@ -6603,7 +6615,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.106", ] [[package]] @@ -6623,7 +6635,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.106", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 78c2e69381c..5ba1dfb654c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,8 +10,6 @@ members = [ "zebra-node-services", "zebra-test", "zebra-utils", - "zebra-scan", - "zebra-grpc", "tower-batch-control", "tower-fallback", ] @@ -22,19 +20,139 @@ resolver = "2" # `cargo release` settings [workspace.dependencies] -incrementalmerkletree = "0.8.2" -orchard = "0.11.0" +incrementalmerkletree = { version = "0.8.2", features = ["legacy-api"] } +orchard = "0.11" sapling-crypto = "0.5" -# The dependency versions below are in accordance with the currently used orchard version. -zcash_history = "0.4.0" zcash_address = "0.8.0" zcash_client_backend = "0.19.0" -zcash_encoding = "0.3.0" +zcash_encoding = "0.3" +zcash_history = "0.4" zcash_keys = "0.9.0" zcash_primitives = "0.23.0" zcash_proofs = "0.23.0" -zcash_protocol = "0.5.3" zcash_transparent = "0.3.0" +zcash_protocol = "0.5.3" +zip32 = "0.2" +abscissa_core = "0.7" +atty = "0.2.14" +base64 = "0.22.1" +bech32 = "0.11.0" +bellman = "0.14" +bincode = "1.3" +bitflags = "2.9" +bitflags-serde-legacy = "0.1.1" +bitvec = "1.0" +blake2b_simd = "1.0" +blake2s_simd = "1.0" +bls12_381 = "0.8" +bs58 = "0.5" +byteorder = "1.5" +bytes = "1.10" +chrono = { version = "0.4.40", default-features = false } +clap = "4.5" +color-eyre = { version = "0.6.3", default-features = false } +console-subscriber = "0.4" +criterion = "0.5" +crossbeam-channel = "0.5.14" +derive-getters = "0.5" +derive-new = "0.5" +dirs = "6.0" +ed25519-zebra = "4.0.3" +elasticsearch = { version = "8.17.0-alpha.1", default-features = false } +equihash = "0.2.2" +ff = "0.13" +futures = "0.3.31" +futures-core = "0.3.31" +futures-util = "0.3.31" +group = "0.13" +halo2 = "0.3" +hex = "0.4.3" +hex-literal = "0.4" +howudoin = "0.1" +http-body-util = "0.1.3" +human_bytes = { version = "0.4", default-features = false } +humantime = "2.2" +humantime-serde = "1.1" +hyper = "1.6" +hyper-util = "0.1.11" +indexmap = "2.8" +indicatif = "0.17" +inferno = { version = "0.12", default-features = false } +insta = "1.42" +itertools = "0.14" +jsonrpc = "0.18" +jsonrpsee = "0.24.8" +jsonrpsee-proc-macros = "0.24.9" +jsonrpsee-types = "0.24.9" +jubjub = "0.10" +lazy_static = "1.4" +log = "0.4.27" +metrics = "0.24" +metrics-exporter-prometheus = { version = "0.16", default-features = false } +mset = "0.1" +nix = "0.29" +num-integer = "0.1.46" +once_cell = "1.21" +ordered-map = "0.4.2" +owo-colors = "4.2.0" +pin-project = "1.1.10" +primitive-types = "0.12" +proptest = "1.6" +proptest-derive = "0.5" +prost = "0.13.5" +quote = "1.0.40" +rand = "0.8.5" +rand_chacha = "0.3" +rand_core = "0.6" +rayon = "1.10" +reddsa = "0.5" +redjubjub = "0.8" +regex = "1.11" +reqwest = { version = "0.12", default-features = false } +ripemd = "0.1" +rlimit = "0.10" +rocksdb = { version = "0.22", default-features = false } +secp256k1 = "0.29" +semver = "1.0.26" +sentry = { version = "0.40", default-features = false } +serde = "1.0.219" +serde-big-array = "0.5" +serde_json = "1.0.140" +serde_with = "3.12" +serde_yml = "0.0" +sha2 = "0.10" +spandoc = "0.2" +static_assertions = "1.1" +structopt = "0.3" +syn = "2.0.100" +tempfile = "3.20" +thiserror = "2.0" +thread-priority = "1.2" +tinyvec = "1.9" +tokio = "1.44" +tokio-stream = "0.1.17" +tokio-test = "0.4" +tokio-util = "0.7.14" +toml = "0.8" +tonic = "0.12.3" +tonic-build = "0.12.3" +tonic-reflection = "0.12.3" +tower = "0.4.13" +tower-test = "0.4" +tracing = "0.1.41" +tracing-appender = "0.2" +tracing-error = "0.2" +tracing-flame = "0.2" +tracing-futures = "0.2.5" +tracing-journald = "0.3" +tracing-subscriber = "0.3.19" +tracing-test = "0.2.4" +uint = "0.10" +vergen-git2 = { version = "1.0", default-features = false } +wagyu-zcash-parameters = "0.2" +x25519-dalek = "2.0.1" +zcash_note_encryption = "0.4.1" +zcash_script = "0.3.1" [workspace.metadata.release] diff --git a/LICENSE-APACHE b/LICENSE-APACHE index 456a4aa21ad..26d4cc05323 100644 --- a/LICENSE-APACHE +++ b/LICENSE-APACHE @@ -1,4 +1,4 @@ -Copyright (c) 2019-2024 Zcash Foundation +Copyright (c) 2019-2025 Zcash Foundation Apache License Version 2.0, January 2004 diff --git a/LICENSE-MIT b/LICENSE-MIT index d85e83e16ab..7f46ce5537a 100644 --- a/LICENSE-MIT +++ b/LICENSE-MIT @@ -1,4 +1,4 @@ -Copyright (c) 2019-2024 Zcash Foundation +Copyright (c) 2019-2025 Zcash Foundation Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated diff --git a/README.md b/README.md index 569b6f46d4b..47c582fb087 100644 --- a/README.md +++ b/README.md @@ -9,41 +9,21 @@ [![Build docs](https://github.com/ZcashFoundation/zebra/actions/workflows/docs-deploy-firebase.yml/badge.svg)](https://github.com/ZcashFoundation/zebra/actions/workflows/docs-deploy-firebase.yml) ![License](https://img.shields.io/badge/license-MIT%2FApache--2.0-blue.svg) -- [About](#about) - [Getting Started](#getting-started) - [Docker](#docker) - [Manual Build](#manual-build) +- [CI/CD Architecture](#cicd-architecture) - [Documentation](#documentation) - [User support](#user-support) - [Security](#security) - [License](#license) -## About - [Zebra](https://zebra.zfnd.org/) is a Zcash full-node written in Rust. -Zebra implements all the features required to reach Zcash network consensus, and -the network stack is interoperable with `zcashd`. -[Here](https://docs.rs/zebrad/latest/zebrad/index.html#zebra-advantages) are -some benefits of Zebra. - -Zebra validates blocks and transactions, but needs extra software to generate -them: - -- To generate transactions, [run Zebra with `lightwalletd`](https://zebra.zfnd.org/user/lightwalletd.html). -- To generate blocks, use a mining pool or miner with Zebra's mining JSON-RPCs. - Currently Zebra can only send mining rewards to a single fixed address. - To distribute rewards, use mining software that creates its own distribution transactions, - a light wallet or the `zcashd` wallet. - -Please [join us on Discord](https://discord.gg/na6QZNd) if you'd like to find -out more or get involved! - ## Getting Started -You can run Zebra using our Docker image or you can build it manually. Please -see the [System Requirements](https://zebra.zfnd.org/user/requirements.html) -section in the Zebra book for system requirements. +You can run Zebra using our [Docker +image](https://hub.docker.com/r/zfnd/zebra/tags) or you can build it manually. ### Docker @@ -59,20 +39,13 @@ For more information, read our [Docker documentation](https://zebra.zfnd.org/use Building Zebra requires [Rust](https://www.rust-lang.org/tools/install), [libclang](https://clang.llvm.org/doxygen/group__CINDEX.html), and a C++ -compiler. - -Zebra is tested with the latest `stable` Rust version. Earlier versions are not -supported or tested. Any Zebra release can start depending on new features in the -latest stable Rust. - -Around every 6 weeks, we release a [new Zebra version](https://github.com/ZcashFoundation/zebra/releases). - -Below are quick summaries for installing the dependencies on your machine. +compiler. Below are quick summaries for installing the dependencies. [//]: # "The empty line in the `summary` tag below is required for correct Markdown rendering."
#### General instructions for installing dependencies + 1. Install [`cargo` and `rustc`](https://www.rust-lang.org/tools/install). @@ -98,6 +71,7 @@ Below are quick summaries for installing the dependencies on your machine.
#### Dependencies on Arch + ```sh @@ -106,15 +80,21 @@ sudo pacman -S rust clang protobuf Note that the package `clang` includes `libclang` as well as the C++ compiler. +Recently the GCC version on Arch has broken a build script in the `rocksdb` dependency. A workaround is: + +```sh +export CXXFLAGS="$CXXFLAGS -include cstdint" +``` +
-Once the dependencies are in place, you can build and install Zebra: +Once you have the dependencies in place, you can build and install Zebra with: ```sh cargo install --locked zebrad ``` -You can start Zebra by +You can start Zebra by running ```sh zebrad start @@ -124,6 +104,21 @@ Refer to the [Installing Zebra](https://zebra.zfnd.org/user/install.html) and [Running Zebra](https://zebra.zfnd.org/user/run.html) sections in the book for enabling optional features, detailed configuration and further details. +## CI/CD Architecture + +Zebra uses a comprehensive CI/CD system built on GitHub Actions to ensure code +quality, maintain stability, and automate routine tasks. Our CI/CD +infrastructure: + +- Runs automated tests on every PR and commit. +- Manages deployments to various environments. +- Handles cross-platform compatibility checks. +- Automates release processes. + +For a detailed understanding of our CI/CD system, including workflow diagrams, +infrastructure details, and best practices, see our [CI/CD Architecture +Documentation](.github/workflows/README.md). + ## Documentation The Zcash Foundation maintains the following resources documenting Zebra: @@ -142,27 +137,27 @@ The Zcash Foundation maintains the following resources documenting Zebra: ## User support -For bug reports please [open a bug report ticket in the Zebra repository](https://github.com/ZcashFoundation/zebra/issues/new?assignees=&labels=C-bug%2C+S-needs-triage&projects=&template=bug_report.yml&title=%5BUser+reported+bug%5D%3A+). - -Alternatively by chat, [Join the Zcash Foundation Discord -Server](https://discord.com/invite/aRgNRVwsM8) and find the #zebra-support -channel. - -We maintain a list of known issues in the +If Zebra doesn't behave the way you expected, [open an +issue](https://github.com/ZcashFoundation/zebra/issues/new/choose). We regularly +triage new issues and we will respond. We maintain a list of known issues in the [Troubleshooting](https://zebra.zfnd.org/user/troubleshooting.html) section of the book. +If you want to chat with us, [Join the Zcash Foundation Discord +Server](https://discord.com/invite/aRgNRVwsM8) and find the "zebra-support" +channel. + ## Security -Zebra has a [responsible disclosure policy](https://github.com/ZcashFoundation/zebra/blob/main/SECURITY.md), which we encourage security researchers to follow. +Zebra has a [responsible disclosure +policy](https://github.com/ZcashFoundation/zebra/blob/main/SECURITY.md), which +we encourage security researchers to follow. ## License -Zebra is distributed under the terms of both the MIT license -and the Apache License (Version 2.0). +Zebra is distributed under the terms of both the MIT license and the Apache +License (Version 2.0). Some Zebra crates are distributed under the [MIT license +only](LICENSE-MIT), because some of their code was originally from MIT-licensed +projects. See each crate's directory for details. See [LICENSE-APACHE](LICENSE-APACHE) and [LICENSE-MIT](LICENSE-MIT). - -Some Zebra crates are distributed under the [MIT license only](LICENSE-MIT), -because some of their code was originally from MIT-licensed projects. -See each crate's directory for details. diff --git a/book/src/SUMMARY.md b/book/src/SUMMARY.md index 1ec8dc35d67..6a128310ffb 100644 --- a/book/src/SUMMARY.md +++ b/book/src/SUMMARY.md @@ -16,8 +16,6 @@ - [Mining](user/mining.md) - [Testnet Mining with s-nomp](user/mining-testnet-s-nomp.md) - [Mining with Zebra in Docker](user/mining-docker.md) - - [Shielded Scanning](user/shielded-scan.md) - - [Shielded Scanning gRPC Server](user/shielded-scan-grpc-server.md) - [Kibana blockchain explorer](user/elasticsearch.md) - [Forking the Zcash Testnet with Zebra](user/fork-zebra-testnet.md) - [Custom Testnets](user/custom-testnets.md) @@ -27,8 +25,10 @@ - [Developer Documentation](dev.md) - [Contribution Guide](CONTRIBUTING.md) - [Design Overview](dev/overview.md) + - [Mempool Specification](dev/mempool-specification.md) - [Diagrams](dev/diagrams.md) - [Network Architecture](dev/diagrams/zebra-network.md) + - [Mempool Architecture](dev/diagrams/mempool-architecture.md) - [Upgrading the State Database](dev/state-db-upgrades.md) - [Zebra versioning and releases](dev/release-process.md) - [Continuous Integration](dev/continuous-integration.md) @@ -36,6 +36,8 @@ - [Generating Zebra Checkpoints](dev/zebra-checkpoints.md) - [Doing Mass Renames](dev/mass-renames.md) - [Updating the ECC dependencies](dev/ecc-updates.md) + - [Running a Private Testnet Test](dev/private-testnet.md) + - [Zebra crates](dev/crate-owners.md) - [Zebra RFCs](dev/rfcs.md) - [Pipelinable Block Lookup](dev/rfcs/0001-pipelinable-block-lookup.md) - [Parallel Verification](dev/rfcs/0002-parallel-verification.md) @@ -43,6 +45,7 @@ - [Asynchronous Script Verification](dev/rfcs/0004-asynchronous-script-verification.md) - [State Updates](dev/rfcs/0005-state-updates.md) - [Contextual Difficulty Validation](dev/rfcs/0006-contextual-difficulty.md) + - [Tree States](dev/rfcs/0007-treestate.md) - [Zebra Client](dev/rfcs/0009-zebra-client.md) - [V5 Transaction](dev/rfcs/0010-v5-transaction.md) - [Async Rust in Zebra](dev/rfcs/0011-async-rust-in-zebra.md) diff --git a/book/src/dev/continuous-delivery.md b/book/src/dev/continuous-delivery.md index 30c3ed7e86b..c977de01fc3 100644 --- a/book/src/dev/continuous-delivery.md +++ b/book/src/dev/continuous-delivery.md @@ -1,6 +1,6 @@ # Zebra Continuous Delivery -Zebra has an extension of it's continuous integration since it automatically deploys all +Zebra has an extension of its continuous integration since it automatically deploys all code changes to a testing and/or pre-production environment after each PR gets merged into the `main` branch, and on each Zebra `release`. diff --git a/book/src/dev/continuous-integration.md b/book/src/dev/continuous-integration.md index d59ad00aeee..7e2a452d03c 100644 --- a/book/src/dev/continuous-integration.md +++ b/book/src/dev/continuous-integration.md @@ -16,7 +16,7 @@ Some of our builds and tests are repeated on the `main` branch, due to: - our cached state sharing rules, or - generating base coverage for PR coverage reports. -Currently, each Zebra and lightwalletd full and update sync will updates cached state images, +Currently, each Zebra and lightwalletd full and update sync will update cached state images, which are shared by all tests. Tests prefer the latest image generated from the same commit. But if a state from the same commit is not available, tests will use the latest image from any branch and commit, as long as the state version is the same. diff --git a/book/src/dev/diagrams/mempool-architecture.md b/book/src/dev/diagrams/mempool-architecture.md new file mode 100644 index 00000000000..f0e11184048 --- /dev/null +++ b/book/src/dev/diagrams/mempool-architecture.md @@ -0,0 +1,88 @@ +# Mempool Architecture Diagram + +This diagram illustrates the architecture of the Zebra mempool, showing its main components and the flow of transactions through the system. + +```mermaid +graph TD + %% External Components + Net[Network Service] + State[State Service] + TxVerifier[Transaction Verifier] + RPC[RPC Service] + + %% Mempool Main Components + Mempool{{Mempool Service}} + Storage{{Storage}} + Downloads{{Transaction Downloads}} + Crawler{{Crawler}} + QueueChecker{{Queue Checker}} + + %% Transaction Flow + Net -->|1- Poll peers| Mempool + RPC -->|1- Direct submit| Mempool + Crawler -->|1- Poll peers| Net + Crawler -->|2- Queue transactions| Mempool + + Mempool -->|3- Queue for download| Downloads + Downloads -->|4a- Download request| Net + Net -->|4b- Transaction data| Downloads + + Downloads -->|5a- Verify request| TxVerifier + TxVerifier -->|5b- Verification result| Downloads + + Downloads -->|6a- Check UTXO| State + State -->|6b- UTXO data| Downloads + + Downloads -->|7- Store verified tx| Storage + + QueueChecker -->|8a- Check for verified| Mempool + Mempool -->|8b- Process verified| QueueChecker + + Storage -->|9- Query responses| Mempool + Mempool -->|10- Gossip new tx| Net + + %% State Management + State -->|Chain tip changes| Mempool + Mempool -->|Updates verification context| Downloads + + %% Mempool responds to service requests + RPC -->|Query mempool| Mempool + Mempool -->|Mempool data| RPC + + %% Styling + classDef external fill:#444,stroke:#888,stroke-width:1px,color:white; + classDef component fill:#333,stroke:#888,stroke-width:1px,color:white; + + class Net,State,TxVerifier,RPC external; + class Mempool,Storage,Downloads,Crawler,QueueChecker component; +``` + +## Component Descriptions + +1. **Mempool Service**: The central coordinator that handles requests and manages the mempool state. + +2. **Storage**: In-memory storage for verified transactions and rejection lists. + +3. **Transaction Downloads**: Handles downloading and verifying transactions from peers. + +4. **Crawler**: Periodically polls peers for new transactions. + +5. **Queue Checker**: Regularly polls for newly verified transactions. + +## Transaction Flow + +1. Transactions arrive via network gossiping, direct RPC submission, or crawler polling. + +2. The mempool checks if transactions are already known or rejected. If not, it queues them for download. + +3. The download service retrieves transaction data from peers. + +4. Transactions are verified against consensus rules using the transaction verifier. + +5. Verified transactions are stored in memory and gossiped to peers. + +6. The queue checker regularly checks for newly verified transactions. + +7. Transactions remain in the mempool until they are mined or evicted due to size limits. + +8. When the chain tip changes, the mempool updates its verification context and potentially evicts invalid transactions. diff --git a/book/src/dev/mempool-specification.md b/book/src/dev/mempool-specification.md new file mode 100644 index 00000000000..1142ab0e827 --- /dev/null +++ b/book/src/dev/mempool-specification.md @@ -0,0 +1,206 @@ +# Mempool Specification + +The Zebra mempool handles unmined Zcash transactions: collecting them from peers, verifying them, storing them in memory, providing APIs for other components to access them, and gossiping transactions to peers. This document specifies the architecture, behavior, and interfaces of the mempool. + +## Overview + +The mempool is a fundamental component of the Zebra node, responsible for managing the lifecycle of unmined transactions. It provides an in-memory storage for valid transactions that haven't yet been included in a block, and offers interfaces for other components to interact with these transactions. + +Key responsibilities of the mempool include: +- Accepting new transactions from the network +- Verifying transactions against a subset of consensus rules +- Storing verified transactions in memory +- Managing memory usage and transaction eviction +- Providing transaction queries to other components +- Gossiping transactions to peers + +## Architecture + +The mempool is comprised of several subcomponents: + +1. **Mempool Service** (`Mempool`): The main service that handles requests from other components, manages the active state of the mempool, and coordinates the other subcomponents. + +2. **Transaction Storage** (`Storage`): Manages the in-memory storage of verified transactions and rejected transactions, along with their rejection reasons. + +3. **Transaction Downloads** (`Downloads`): Handles downloading and verifying transactions, coordinating with the network and verification services. + +4. **Crawler** (`Crawler`): Periodically polls peers for new transactions to add to the mempool. + +5. **Queue Checker** (`QueueChecker`): Regularly checks the transaction verification queue to process newly verified transactions. + +6. **Transaction Gossip** (`gossip`): Broadcasts newly added transactions to peers. + +7. **Pending Outputs** (`PendingOutputs`): Tracks requests for transaction outputs that haven't yet been seen. + +For a visual representation of the architecture and transaction flow, see the [Mempool Architecture Diagram](diagrams/mempool-architecture.md). + +## Activation + +The mempool is activated when: +- The node is near the blockchain tip (determined by `SyncStatus`) +- OR when the current chain height reaches a configured debug height (`debug_enable_at_height`) + +When activated, the mempool creates transaction download and verify services, initializes storage, and starts background tasks for crawling and queue checking. + +## Configuration + +The mempool has the following configurable parameters: + +1. **Transaction Cost Limit** (`tx_cost_limit`): The maximum total serialized byte size of all transactions in the mempool, defaulting to 80,000,000 bytes as required by [ZIP-401](https://zips.z.cash/zip-0401). + +2. **Eviction Memory Time** (`eviction_memory_time`): The maximum time to remember evicted transaction IDs in the rejection list, defaulting to 60 minutes. + +3. **Debug Enable At Height** (`debug_enable_at_height`): An optional height at which to enable the mempool for debugging, regardless of sync status. + +## State Management + +The mempool maintains an `ActiveState` which can be either: +- `Disabled`: Mempool is not active +- `Enabled`: Mempool is active and contains: + - `storage`: The Storage instance for transactions + - `tx_downloads`: Transaction download and verification stream + - `last_seen_tip_hash`: Hash of the last chain tip the mempool has seen + +The mempool responds to chain tip changes: +- On new blocks: Updates verification context, removes mined transactions +- On reorgs: Clears tip-specific rejections, retries all transactions + +## Transaction Processing Flow + +1. **Transaction Arrival**: + - From peer gossip (inv messages) + - From direct submission (RPC) + - From periodic peer polling (crawler) + +2. **Transaction Download**: + - Checks if transaction exists in mempool or rejection lists + - Queues transaction for download if needed + - Downloads transaction data from peers + +3. **Transaction Verification**: + - Checks transaction against consensus rules + - Verifies transaction against the current chain state + - Manages dependencies between transactions + +4. **Transaction Storage**: + - Stores verified transactions in memory + - Tracks transaction dependencies + - Enforces size limits and eviction policies + +5. **Transaction Gossip**: + - Broadcasts newly verified transactions to peers + +## Transaction Rejection + +Transactions can be rejected for multiple reasons, categorized into: + +1. **Exact Tip Rejections** (`ExactTipRejectionError`): + - Failures in consensus validation + - Only applies to exactly matching transactions at the current tip + +2. **Same Effects Tip Rejections** (`SameEffectsTipRejectionError`): + - Spending conflicts with other mempool transactions + - Missing outputs from mempool transactions + - Applies to any transaction with the same effects at the current tip + +3. **Same Effects Chain Rejections** (`SameEffectsChainRejectionError`): + - Expired transactions + - Duplicate spends already in the blockchain + - Transactions already mined + - Transactions evicted due to memory limits + - Applies until a rollback or network upgrade + +Rejection reasons are stored alongside rejected transaction IDs to prevent repeated verification of invalid transactions. + +## Memory Management + +The mempool employs several strategies for memory management: + +1. **Transaction Cost Limit**: Enforces a maximum total size for all mempool transactions. + +2. **Random Eviction**: When the mempool exceeds the cost limit, transactions are randomly evicted following the [ZIP-401](https://zips.z.cash/zip-0401) specification. + +3. **Eviction Memory**: Remembers evicted transaction IDs for a configurable period to prevent re-verification. + +4. **Rejection List Size Limit**: Caps rejection lists at 40,000 entries per [ZIP-401](https://zips.z.cash/zip-0401). + +5. **Automatic Cleanup**: Removes expired transactions and rejections that are no longer relevant. + +## Service Interface + +The mempool exposes a service interface with the following request types: + +1. **Query Requests**: + - `TransactionIds`: Get all transaction IDs in the mempool + - `TransactionsById`: Get transactions by their unmined IDs + - `TransactionsByMinedId`: Get transactions by their mined hashes + - `FullTransactions`: Get all verified transactions with fee information + - `RejectedTransactionIds`: Query rejected transaction IDs + - `TransactionWithDepsByMinedId`: Get a transaction and its dependencies + +2. **Action Requests**: + - `Queue`: Queue transactions or transaction IDs for download and verification + - `CheckForVerifiedTransactions`: Check for newly verified transactions + - `AwaitOutput`: Wait for a specific transparent output to become available + +## Interaction with Other Components + +The mempool interacts with several other Zebra components: + +1. **Network Service**: For downloading transactions and gossiping to peers. + +2. **State Service**: For checking transaction validity against the current chain state. + +3. **Transaction Verifier**: For consensus validation of transactions. + +4. **Chain Tip Service**: For tracking the current blockchain tip. + +5. **RPC Services**: To provide transaction data for RPC methods. + +## Implementation Constraints + +1. **Correctness**: + - All transactions in the mempool must be verified + - Transactions must be re-verified when the chain tip changes + - Rejected transactions must be properly tracked to prevent DoS attacks + +2. **Performance**: + - Transaction processing should be asynchronous + - Memory usage should be bounded + - Critical paths should be optimized for throughput + +3. **Reliability**: + - The mempool should recover from crashes and chain reorganizations + - Background tasks should be resilient to temporary failures + +## ZIP-401 Compliance + +The mempool implements the requirements specified in [ZIP-401](https://zips.z.cash/zip-0401): + +1. Implements `mempooltxcostlimit` configuration (default: 80,000,000) +2. Implements `mempoolevictionmemoryminutes` configuration (default: 60 minutes) +3. Uses random eviction when the mempool exceeds the cost limit +4. Caps eviction memory lists at 40,000 entries +5. Uses transaction IDs (txid) for version 5 transactions in eviction lists + +## Error Handling + +The mempool employs a comprehensive error handling strategy: + +1. **Temporary Failures**: For network or resource issues, transactions remain in the queue for retry. + +2. **Permanent Rejections**: For consensus or semantic failures, transactions are rejected with specific error reasons. + +3. **Dependency Failures**: For missing inputs or dependencies, transactions may wait for dependencies to be resolved. + +4. **Recovery**: On startup or after a crash, the mempool is rebuilt from scratch. + +## Metrics and Diagnostics + +The mempool provides metrics for monitoring: + +1. **Transaction Count**: Number of transactions in the mempool +2. **Total Cost**: Total size of all mempool transactions +3. **Queued Count**: Number of transactions pending download or verification +4. **Rejected Count**: Number of rejected transactions in memory +5. **Background Task Status**: Health of crawler and queue checker tasks diff --git a/book/src/dev/overview.md b/book/src/dev/overview.md index afefbd33403..642f472fd42 100644 --- a/book/src/dev/overview.md +++ b/book/src/dev/overview.md @@ -41,8 +41,6 @@ The following are general desiderata for Zebra: ## Service Dependencies -Note: dotted lines are for "getblocktemplate-rpcs" feature -
{{#include diagrams/service-dependencies.svg}}
@@ -74,6 +72,8 @@ digraph services { Render here: https://dreampuf.github.io/GraphvizOnline --> +The dotted lines are for the `getblocktemplate` RPC. + ## Architecture Unlike `zcashd`, which originated as a Bitcoin Core fork and inherited its diff --git a/book/src/dev/private-testnet.md b/book/src/dev/private-testnet.md new file mode 100644 index 00000000000..47e3d807739 --- /dev/null +++ b/book/src/dev/private-testnet.md @@ -0,0 +1,181 @@ +# Private Testnet Test + +The objective of a private Testnet test is to test Testnet activation of an upcoming +network upgrade in an isolated fashion, before the actual Testnet activation. +It is usually done using the current state of the existing Testnet. For NU6, it was done +by ZF and ECC engineers over a call. + +## Steps + +### Make Backup + +Make a backup of your current Testnet state. Rename/copy the `testnet` folder in +Zebra's state cache directory to the lowercase version of the configured network name, +or the default `unknowntestnet` if no network name is explicitly configured. + +### Set Protocol Version + +Double check that Zebra has bumped its protocol version. + +### Set Up Lightwalletd Server + +It's a good idea to set up a lightwalletd server connected to your node, and +have a (Testnet) wallet connected to your lightwalletd server. + +### Connect to Peers + +Make sure everyone can connect to each other. You can **use Tailscale** to do +that. Everyone needs to send invites to everyone else. Note that being able to +access someone's node does not imply that they can access yours, it needs to be +enabled both ways. + +### Choose an Activation Height + +Choose an activation height with the other participants. It should be in +the near future, but with enough time for people to set things up; something +like 30 minutes in the future? + +### Ensure the Activation Height is Set in Code + +While Zebra allows creating a private Testnet in the config file, the height is +also set in some librustzcash crates. For this reason, someone will need to +**create a branch of librustzcash** with the chosen height set and you will need +to **change Zebra to use that**. However, double check if that's still +necessary. + +### Configure Zebra to use a custom testnet + +See sample config file below. The critical part is setting the activation +height. It is good to enable verbose logging to help debug things. Some of the +participants must enable mining also. It's not a huge deal to keep the DNS +seeders; the blockchain will fork when the activation happens and only the +participants will stay connected. On the other hand, if you want to ensure you +won't connect to anyone else, set `cache_dir = false` in the `[network]` section +and delete the peers file (`~/.cache/zebra/network/unknowntestnet.peers`). + +### Run Nodes + +Everyone runs their nodes, and checks if they connect to other nodes. You can use +e.g. `curl --data-binary '{"jsonrpc": "1.0", "id":"curltest", "method": +"getpeerinfo", "params": [] }' -H 'Content-Type: application/json' +http://127.0.0.1:8232` to check that. See "Getting Peers" section below. + +### Wait Until Activation Happens + +And monitor logs for behaviour. + +### Do Tests + +Do tests, including sending transactions if possible (which will require the +lightwalletd server). Check if whatever activated in the upgrade works. + + +## Zebra + +Relevant information about Zebra for the testing process. + +### Getting peers + +It seems Zebra is not very reliable at returning its currently connected peers; +you can use `getpeerinfo` RPC as above or check the peers file +(`~/.cache/zebra/network/unknowntestnet.peers`) if `cache_dir = true` in the +`[network]` section. You might want to sort this out before the next private +testnet test. + +### Unredact IPs + +Zebra redacts IPs when logging for privacy reasons. However, for a test like +this it can be annoying. You can disable that by editing `peer_addr.rs` +with something like + + +```diff +--- a/zebra-network/src/meta_addr/peer_addr.rs ++++ b/zebra-network/src/meta_addr/peer_addr.rs +@@ -30,7 +30,7 @@ impl fmt::Display for PeerSocketAddr { + let ip_version = if self.is_ipv4() { "v4" } else { "v6" }; + + // The port is usually not sensitive, and it's useful for debugging. +- f.pad(&format!("{}redacted:{}", ip_version, self.port())) ++ f.pad(&format!("{}:{}", self.ip(), self.port())) + } + } +``` + +### Sample config file + +Note: Zebra's db path will end in "unknowntestnet" instead of "testnet" with +this configuration. + +``` +[consensus] +checkpoint_sync = true + +[mempool] +eviction_memory_time = "1h" +tx_cost_limit = 80000000 + +[metrics] + +[mining] +miner_address = "t27eWDgjFYJGVXmzrXeVjnb5J3uXDM9xH9v" +# if you want to enable mining, which also requires selecting the `internal-miner` compilation feature +internal_miner = true + +[network] +# This will save peers to a file. Take care that it also reads peers from it; +# if you want to be truly isolated and only connect to the other participants, +# either disable this or delete the peers file before starting. +cache_dir = true +crawl_new_peer_interval = "1m 1s" + +initial_mainnet_peers = [] + +initial_testnet_peers = [ + # List the other participant's Tailscale IPs here. + # You can also keep the default DNS seeders if you wish. + "100.10.0.1:18233", +] + +listen_addr = "0.0.0.0:18233" +max_connections_per_ip = 1 +network = "Testnet" +peerset_initial_target_size = 25 + +[network.testnet_parameters] + +[network.testnet_parameters.activation_heights] +BeforeOverwinter = 1 +Overwinter = 207_500 +Sapling = 280_000 +Blossom = 584_000 +Heartwood = 903_800 +Canopy = 1_028_500 +NU5 = 1_842_420 +NU6 = 2_969_920 + +[rpc] +debug_force_finished_sync = false +parallel_cpu_threads = 0 +listen_addr = "127.0.0.1:8232" +indexer_listen_addr = "127.0.0.1:8231" + +[state] +delete_old_database = true +ephemeral = false + +[sync] +checkpoint_verify_concurrency_limit = 1000 +download_concurrency_limit = 50 +full_verify_concurrency_limit = 20 +parallel_cpu_threads = 0 + +[tracing] +buffer_limit = 128000 +force_use_color = false +use_color = true +use_journald = false +# This enables debug network logging. It can be useful but it's very verbose! +filter = 'info,zebra_network=debug' +``` + diff --git a/book/src/dev/release-process.md b/book/src/dev/release-process.md index 606e9a03682..abcf90d11f3 100644 --- a/book/src/dev/release-process.md +++ b/book/src/dev/release-process.md @@ -65,7 +65,7 @@ We let you preview what's coming by providing Release Candidate \(`rc`\) pre-rel ### Distribution tags -Zebras's tagging relates directly to versions published on Docker. We will reference these [Docker Hub distribution tags](https://hub.docker.com/r/zfnd/zebra/tags) throughout: +Zebra's tagging relates directly to versions published on Docker. We will reference these [Docker Hub distribution tags](https://hub.docker.com/r/zfnd/zebra/tags) throughout: | Tag | Description | |:--- |:--- | diff --git a/book/src/dev/rfcs/0000-template.md b/book/src/dev/rfcs/0000-template.md index 72bcd9affcf..cdf4022bc24 100644 --- a/book/src/dev/rfcs/0000-template.md +++ b/book/src/dev/rfcs/0000-template.md @@ -122,7 +122,7 @@ Think about what the natural extension and evolution of your proposal would be and how it would affect Zebra and Zcash as a whole. Try to use this section as a tool to more fully consider all possible interactions with the project and cryptocurrency ecosystem in your proposal. -Also consider how the this all fits into the roadmap for the project +Also consider how this all fits into the roadmap for the project and of the relevant sub-team. This is also a good place to "dump ideas", if they are out of scope for the diff --git a/book/src/dev/rfcs/0003-inventory-tracking.md b/book/src/dev/rfcs/0003-inventory-tracking.md index ff7bac4d18a..a562843070f 100644 --- a/book/src/dev/rfcs/0003-inventory-tracking.md +++ b/book/src/dev/rfcs/0003-inventory-tracking.md @@ -192,7 +192,7 @@ specific inventory request is ready, because until we get the request, we can't determine which peers might be required to process it. We could attempt to ensure that the peer set would be ready to process a -specific inventory request would be to pre-emptively "reserve" a peer as soon +specific inventory request would be to preemptively "reserve" a peer as soon as it advertises an inventory item. But this doesn't actually work to ensure readiness, because a peer could advertise two inventory items, and only be able to service one request at a time. It also potentially locks the peer diff --git a/book/src/dev/rfcs/0006-contextual-difficulty.md b/book/src/dev/rfcs/0006-contextual-difficulty.md index dc27692adb2..b23fbab8c24 100644 --- a/book/src/dev/rfcs/0006-contextual-difficulty.md +++ b/book/src/dev/rfcs/0006-contextual-difficulty.md @@ -421,7 +421,7 @@ fn mean_target_difficulty(&self) -> ExpandedDifficulty { ... } Since the `PoWLimit`s are `2^251 − 1` for Testnet, and `2^243 − 1` for Mainnet, the sum of these difficulty thresholds will be less than or equal to `(2^251 − 1)*17 = 2^255 + 2^251 - 17`. Therefore, this calculation can not -overflow a `u256` value. So the function is infalliable. +overflow a `u256` value. So the function is infallible. In Zebra, contextual validation starts after Canopy activation, so we can assume that the relevant chain contains at least 17 blocks. Therefore, the `PoWLimit` @@ -499,7 +499,7 @@ that the relevant chain contains at least 28 blocks. Therefore: * there is always an odd number of blocks in `MedianTime()`, so the median is always the exact middle of the sequence. -Therefore, the function is infalliable. +Therefore, the function is infallible. ### Test network minimum difficulty calculation [test-net-min-difficulty-calculation]: #test-net-min-difficulty-calculation @@ -580,7 +580,7 @@ In Zcash, the Testnet minimum difficulty rule starts at block 299188, and in Zebra, contextual validation starts after Canopy activation. So we can assume that there is always a previous block. -Therefore, this function is infalliable. +Therefore, this function is infallible. ### Block difficulty threshold calculation [block-difficulty-threshold-calculation]: #block-difficulty-threshold-calculation @@ -647,7 +647,7 @@ Note that the multiplication by `ActualTimespanBounded` must happen after the division by `AveragingWindowTimespan`. Performing the multiplication first could overflow. -If implemented in this way, the function is infalliable. +If implemented in this way, the function is infallible. `zcashd` truncates the `MeanTarget` after the mean calculation, and after dividing by `AveragingWindowTimespan`. But as long as there is no overflow, @@ -753,10 +753,10 @@ would be a security issue. # Future possibilities [future-possibilities]: #future-possibilities -## Re-using the relevant chain API in other contextual checks +## Reusing the relevant chain API in other contextual checks [relevant-chain-api-reuse]: #relevant-chain-api-reuse -The relevant chain iterator can be re-used to implement other contextual +The relevant chain iterator can be reused to implement other contextual validation checks. For example, responding to peer requests for block locators, which means diff --git a/book/src/dev/rfcs/0007-treestate.md b/book/src/dev/rfcs/0007-treestate.md new file mode 100644 index 00000000000..1de547c6ead --- /dev/null +++ b/book/src/dev/rfcs/0007-treestate.md @@ -0,0 +1,346 @@ +# Treestate + +- Feature Name: treestate +- Start Date: 2020-08-31 +- Design PR: [ZcashFoundation/zebra#983](https://github.com/ZcashFoundation/zebra/issues/983) +- Zebra Issue: [ZcashFoundation/zebra#958](https://github.com/ZcashFoundation/zebra/issues/958) + +# Summary +[summary]: #summary + +To validate blocks involving shielded transactions, we have to check the +computed treestate from the included transactions against the block header +metadata (for Sapling and Orchard) or previously finalized state (for Sprout). +This document describes how we compute and manage that data, assuming a finalized +state service as described in the [State Updates RFC](https://zebra.zfnd.org/dev/rfcs/0005-state-updates.md). + + +# Motivation +[motivation]: #motivation + +Block validation requires checking that the treestate of the block (consisting +of the note commitment tree and nullifier set) is consistent with the metadata +we have in the block header (the root of the note commitment tree) or previously +finalized state (for Sprout). + + +# Definitions +[definitions]: #definitions + +## Common Definitions + +Many terms used here are defined in the [Zcash Protocol Specification](https://zips.z.cash/protocol/protocol.pdf) + +**notes**: Represents a value bound to a shielded payment address (public key) +which is spendable by the recipient who holds the spending key corresponding to +a given shielded payment address. + +**nullifiers**: A value that prevents double-spending of a shielded payment. +Revealed by `Spend` descriptions when its associated `Note` is spent. + +**nullifier set**: The set of unique `Nullifier`s revealed by any `Transaction`s +within a `Block`. `Nullifier`s are enforced to be unique within a valid block chain +by committing to previous treestates in `Spend` descriptions, in order to prevent +double-spends. + +**note commitments**: Pedersen commitment to the values consisting a `Note`. One +should not be able to construct a `Note` from its commitment. + +**note commitment tree**: An incremental Merkle tree of fixed depth used to +store `NoteCommitment`s that `JoinSplit` transfers or `Spend` transfers produce. It +is used to express the existence of value and the capability to spend it. It is +not the job of this tree to protect against double-spending, as it is +append-only: that's what the `Nullifier` set is for. + +**note position**: The index of a `NoteCommitment` at the leafmost layer, +counting leftmost to rightmost. The [position in the tree is determined by the +order of transactions in the block](https://zips.z.cash/protocol/protocol.pdf#transactions). + +**root**: The layer 0 node of a Merkle tree. + +**anchor**: A Merkle tree root of a `NoteCommitment` tree. It uniquely +identifies a `NoteCommitment` tree state given the assumed security properties +of the Merkle tree’s hash function. Since the `Nullifier` set is always updated +together with the `NoteCommitment` tree, this also identifies a particular state +of the associated `Nullifier` set. + +## Sprout Definitions + +**joinsplit**: A shielded transfer that can spend Sprout `Note`s and transparent +value, and create new Sprout `Note`s and transparent value, in one Groth16 proof +statement. + +## Sapling Definitions + +**spend descriptions**: A shielded Sapling transfer that spends a `Note`. Includes +an anchor of some previous `Block`'s `NoteCommitment` tree. + +**output descriptions**: A shielded Sapling transfer that creates a +`Note`. Includes the u-coordinate of the `NoteCommitment` itself. + +## Orchard Definitions + +**action descriptions**: A shielded Orchard transfer that spends and/or creates a +`Note`. Does not include an anchor, because that is encoded once in the +`anchorOrchard` field of a V5 `Transaction`. + + +# Guide-level explanation +[guide-level-explanation]: #guide-level-explanation + +## Common Processing for All Protocols + +As `Block`s are validated, the `NoteCommitment`s revealed by all the transactions +within that block are used to construct `NoteCommitmentTree`s, with the +`NoteCommitment`s aligned in their note positions in the bottom layer of the +Sprout or Sapling tree from the left-most leaf to the right-most in +`Transaction` order in the `Block`. So the Sprout `NoteCommitment`s revealed by +the first `JoinSplit` in a block would take note position 0 in the Sprout +note commitment tree, for example. Once all the transactions in a block are +parsed and the notes for each tree collected in their appropriate positions, the +root of each tree is computed. While the trees are being built, the respective +block nullifier sets are updated in memory as note nullifiers are revealed. If +the rest of the block is validated according to consensus rules, that root is +committed to its own data structure via our state service (Sprout anchors, +Sapling anchors). Sapling block validation includes comparing the specified +FinalSaplingRoot in its block header to the root of the Sapling `NoteCommitment` +tree that we have just computed to make sure they match. + +## Sprout Processing + +For Sprout, we must compute/update interstitial `NoteCommitmentTree`s between +`JoinSplit`s that may reference an earlier one's root as its anchor. If we do +this at the transaction layer, we can iterate through all the `JoinSplit`s and +compute the Sprout `NoteCommitmentTree` and nullifier set similar to how we do +the Sapling ones as described below, but at each state change (ie, +per-`JoinSplit`) we note the root and cache it for lookup later. As the +`JoinSplit`s are validated without context, we check for its specified anchor +amongst the interstitial roots we've already calculated (according to the spec, +these interstitial roots don't have to be finalized or the result of an +independently validated `JoinSplit`, they just must refer to any prior `JoinSplit` +root in the same transaction). So we only have to wait for our previous root to +be computed via any of our candidates, which in the worst case is waiting for +all of them to be computed for the last `JoinSplit`. If our `JoinSplit`s defined +root pops out, that `JoinSplit` passes that check. + +## Sapling Processing + +As the transactions within a block are parsed, Sapling shielded transactions +including `Spend` descriptions and `Output` descriptions describe the spending and +creation of Zcash Sapling notes. `Spend` descriptions specify an anchor, which +references a previous `NoteCommitment` tree root. This is a previous block's anchor +as defined in their block header. This is convenient because we can query our state +service for previously finalized Sapling block anchors, and if they are found, then +that [consensus check](https://zips.z.cash/protocol/canopy.pdf#spendsandoutputs) +has been satisfied and the `Spend` description can be validated independently. + +For Sapling, at the block layer, we can iterate over all the transactions in +order and if they have `Spend`s and/or `Output`s, we update our Nullifer set for +the block as nullifiers are revealed in `Spend` descriptions, and update our note +commitment tree as `NoteCommitment`s are revealed in `Output` descriptions, adding +them as leaves in positions according to their order as they appear transaction +to transaction, output to output, in the block. This can be done independent of +the transaction validations. When the Sapling transactions are all validated, +the `NoteCommitmentTree` root should be computed: this is the anchor for this +block. + +### Anchor Validation Across Network Upgrades + +For Sapling and Blossom blocks, we need to check that this root matches +the `RootHash` bytes in this block's header, as the `FinalSaplingRoot`. Once all +other consensus and validation checks are done, this will be saved down to our +finalized state to our `sapling_anchors` set, making it available for lookup by +other Sapling descriptions in future transactions. + +In Heartwood and Canopy, the rules for final Sapling roots are modified to support +empty blocks by allowing an empty subtree hash instead of requiring the root to +match the previous block's final Sapling root when there are no Sapling transactions. + +In NU5, the rules are further extended to include Orchard note commitment trees, +with similar logic applied to the `anchorOrchard` field in V5 transactions. + +## Orchard Processing + +For Orchard, similar to Sapling, action descriptions can spend and create notes. +The anchor is specified at the transaction level in the `anchorOrchard` field of +a V5 transaction. The process follows similar steps to Sapling for validation and +inclusion in blocks. + +## Block Finalization + +To finalize the block, the Sprout, Sapling, and Orchard treestates are the ones +resulting from the last transaction in the block, and determines the respective +anchors that will be associated with this block as we commit it to our finalized +state. The nullifiers revealed in the block will be merged with the existing ones +in our finalized state (ie, it should strictly grow over time). + +## State Management + +### Orchard + +- There is a single copy of the latest Orchard Note Commitment Tree for the +finalized tip. +- When finalizing a block, the finalized tip is updated with a serialization of +the latest Orchard Note Commitment Tree. (The previous tree should be deleted as +part of the same database transaction.) +- Each non-finalized chain gets its own copy of the Orchard note commitment tree, +cloned from the note commitment tree of the finalized tip or fork root. +- When a block is added to a non-finalized chain tip, the Orchard note commitment +tree is updated with the note commitments from that block. +- When a block is rolled back from a non-finalized chain tip, the Orchard tree +state is restored to its previous state before the block was added. This involves +either keeping a reference to the previous state or recalculating from the fork +point. + +### Sapling + +- There is a single copy of the latest Sapling Note Commitment Tree for the +finalized tip. +- When finalizing a block, the finalized tip is updated with a serialization of +the Sapling Note Commitment Tree. (The previous tree should be deleted as part +of the same database transaction.) +- Each non-finalized chain gets its own copy of the Sapling note commitment tree, +cloned from the note commitment tree of the finalized tip or fork root. +- When a block is added to a non-finalized chain tip, the Sapling note commitment +tree is updated with the note commitments from that block. +- When a block is rolled back from a non-finalized chain tip, the Sapling tree +state is restored to its previous state, similar to the Orchard process. This +involves either maintaining a history of tree states or recalculating from the +fork point. + +### Sprout + +- Every finalized block stores a separate copy of the Sprout note commitment +tree (😿), as of that block. +- When finalizing a block, the Sprout note commitment tree for that block is stored +in the state. (The trees for previous blocks also remain in the state.) +- Every block in each non-finalized chain gets its own copy of the Sprout note +commitment tree. The initial tree is cloned from the note commitment tree of the +finalized tip or fork root. +- When a block is added to a non-finalized chain tip, the Sprout note commitment +tree is cloned, then updated with the note commitments from that block. +- When a block is rolled back from a non-finalized chain tip, the trees for each +block are deleted, along with that block. + +We can't just compute a fresh tree with just the note commitments within a block, +we are adding them to the tree referenced by the anchor, but we cannot update that +tree with just the anchor, we need the 'frontier' nodes and leaves of the +incremental merkle tree. + + +# Reference-level explanation +[reference-level-explanation]: #reference-level-explanation + +The implementation involves several key components: + +1. **Incremental Merkle Trees**: We use the `incrementalmerkletree` crate to +implement the note commitment trees for each shielded pool. + +2. **Nullifier Storage**: We maintain nullifier sets in RocksDB to efficiently +check for duplicates. + +3. **Tree State Management**: + - For finalized blocks, we store the tree states in RocksDB. + - For non-finalized chains, we keep tree states in memory. + +4. **Anchor Verification**: + - For Sprout: we check anchors against our stored Sprout tree roots. + - For Sapling: we compare the computed root against the block header's +`FinalSaplingRoot`. + - For Orchard: we validate the `anchorOrchard` field in V5 transactions. + +5. **Re-insertion Prevention**: Our implementation should prevent re-inserts +of keys that have been deleted from the database, as this could lead to +inconsistencies. The state service tracks deletion events and validates insertion +operations accordingly. + + +# Drawbacks +[drawbacks]: #drawbacks + +1. **Storage Requirements**: Storing separate tree states (especially for Sprout) +requires significant disk space. + +2. **Performance Impact**: Computing and verifying tree states can be +computationally expensive, potentially affecting sync performance. + +3. **Implementation Complexity**: Managing multiple tree states across different +protocols adds complexity to the codebase. + +4. **Fork Handling**: Maintaining correct tree states during chain reorganizations +requires careful handling. + + +# Rationale and alternatives +[rationale-and-alternatives]: #rationale-and-alternatives + +We chose this approach because: + +1. **Protocol Compatibility**: Our implementation follows the Zcash protocol +specification requirements for handling note commitment trees and anchors. + +2. **Performance Optimization**: By caching tree states, we avoid recomputing +them for every validation operation. + +3. **Memory Efficiency**: For non-finalized chains, we only keep necessary tree +states in memory. + +4. **Scalability**: The design scales with chain growth by efficiently managing +storage requirements. + +Alternative approaches considered: + +1. **Recompute Trees On-Demand**: Instead of storing tree states, recompute them +when needed. This would save storage but significantly impact performance. + +2. **Single Tree State**: Maintain only the latest tree state and recompute for +historical blocks. This would simplify implementation but make historical validation harder. + +3. **Full History Storage**: Store complete tree states for all blocks. This would optimize +validation speed but require excessive storage. + + +# Prior art +[prior-art]: #prior-art + +1. **Zcashd**: Uses similar concepts but with differences in implementation details, +particularly around storage and concurrency. + +2. **Lightwalletd**: Provides a simplified approach to tree state management focused +on scanning rather than full validation. + +3. **Incrementalmerkletree Crate**: Our implementation leverages this existing Rust +crate for efficient tree management. + + +# Unresolved questions +[unresolved-questions]: #unresolved-questions + +1. **Optimization Opportunities**: Are there further optimizations we can make to reduce +storage requirements while maintaining performance? + +2. **Root Storage**: Should we store the `Root` hash in `sprout_note_commitment_tree`, +and use it to look up the complete tree state when needed? + +3. **Re-insertion Prevention**: What's the most efficient approach to prevent re-inserts +of deleted keys? + +4. **Concurrency Model**: How do we best handle concurrent access to tree states during +parallel validation? + + +# Future possibilities +[future-possibilities]: #future-possibilities + +1. **Pruning Strategies**: Implement advanced pruning strategies for historical tree states +to reduce storage requirements. + +2. **Parallelization**: Further optimize tree state updates for parallel processing. + +3. **Checkpoint Verification**: Use tree states for efficient checkpoint-based verification. + +4. **Light Client Support**: Leverage tree states to support Zebra-based light clients with +efficient proof verification. + +5. **State Storage Optimization**: Investigate more efficient serialization formats and storage +mechanisms for tree states. diff --git a/book/src/dev/rfcs/0009-zebra-client.md b/book/src/dev/rfcs/0009-zebra-client.md index 3aa153daea6..771aa7d3a2d 100644 --- a/book/src/dev/rfcs/0009-zebra-client.md +++ b/book/src/dev/rfcs/0009-zebra-client.md @@ -341,7 +341,7 @@ endpoint - + diff --git a/book/src/dev/rfcs/drafts/0005-treestate.md b/book/src/dev/rfcs/drafts/0005-treestate.md deleted file mode 100644 index f8c83936da7..00000000000 --- a/book/src/dev/rfcs/drafts/0005-treestate.md +++ /dev/null @@ -1,227 +0,0 @@ -# Treestate - -- Feature Name: treestate -- Start Date: 2020-08-31 -- Design PR: [ZcashFoundation/zebra#983](https://github.com/ZcashFoundation/zebra/issues/983) -- Zebra Issue: [ZcashFoundation/zebra#958](https://github.com/ZcashFoundation/zebra/issues/958) - -# Summary -[summary]: #summary - -To validate blocks involving shielded transactions, we have to check the -computed treestate from the included transactions against the block header -metadata (for Sapling and Orchard) or previously finalized state (for Sprout). This document -describes how we compute and manage that data, assuming a finalized state -service as described in the [State Updates RFC](https://zebra.zfnd.org/dev/rfcs/0005-state-updates.md). - - -# Motivation -[motivation]: #motivation - -Block validation requires checking that the treestate of the block (consisting -of the note commitment tree and nullifier set) is consistent with the metadata -we have in the block header (the root of the note commitment tree) or previously -finalized state (for Sprout). - - -# Definitions -[definitions]: #definitions - -TODO: split up these definitions into common, Sprout, Sapling, and possibly Orchard sections - -Many terms used here are defined in the [Zcash Protocol Specification](https://zips.z.cash/protocol/protocol.pdf) - -**notes**: Represents a value bound to a shielded payment address (public key) -which is spendable by the recipient who holds the spending key corresponding to -a given shielded payment address. - -**nullifiers**: Revealed by `Spend` descriptions when its associated `Note` is spent. - -**nullifier set**: The set of unique `Nullifier`s revealed by any `Transaction`s -within a `Block`. `Nullifier`s are enforced to be unique within a valid block chain -by committing to previous treestates in `Spend` descriptions, in order to prevent -double-spends. - -**note commitments**: Pedersen commitment to the values consisting a `Note`. One -should not be able to construct a `Note` from its commitment. - -**note commitment tree**: An incremental Merkle tree of fixed depth used to -store `NoteCommitment`s that `JoinSplit` transfers or `Spend` transfers produce. It -is used to express the existence of value and the capability to spend it. It is -not the job of this tree to protect against double-spending, as it is -append-only: that's what the `Nullifier` set is for. - -**note position**: The index of a `NoteCommitment` at the leafmost layer, -counting leftmost to rightmost. The [position in the tree is determined by the -order of transactions in the block](https://zips.z.cash/protocol/canopy.pdf#transactions). - -**root**: The layer 0 node of a Merkle tree. - -**anchor**: A Merkle tree root of a `NoteCommitment` tree. It uniquely -identifies a `NoteCommitment` tree state given the assumed security properties -of the Merkle tree’s hash function. Since the `Nullifier` set is always updated -together with the `NoteCommitment` tree, this also identifies a particular state -of the associated `Nullifier` set. - -**spend descriptions**: A shielded Sapling transfer that spends a `Note`. Includes -an anchor of some previous `Block`'s `NoteCommitment` tree. - -**output descriptions**: A shielded Sapling transfer that creates a -`Note`. Includes the u-coordinate of the `NoteCommitment` itself. - -**action descriptions**: A shielded Orchard transfer that spends and/or creates a `Note`. -Does not include an anchor, because that is encoded once in the `anchorOrchard` -field of a V5 `Transaction`. - - - -**joinsplit**: A shielded transfer that can spend Sprout `Note`s and transparent -value, and create new Sprout `Note`s and transparent value, in one Groth16 proof -statement. - - -# Guide-level explanation -[guide-level-explanation]: #guide-level-explanation - -TODO: split into common, Sprout, Sapling, and probably Orchard sections - -As `Block`s are validated, the `NoteCommitment`s revealed by all the transactions -within that block are used to construct `NoteCommitmentTree`s, with the -`NoteCommitment`s aligned in their note positions in the bottom layer of the -Sprout or Sapling tree from the left-most leaf to the right-most in -`Transaction` order in the `Block`. So the Sprout `NoteCommitment`s revealed by -the first `JoinSplit` in a block would take note position 0 in the Sprout -note commitment tree, for example. Once all the transactions in a block are -parsed and the notes for each tree collected in their appropriate positions, the -root of each tree is computed. While the trees are being built, the respective -block nullifier sets are updated in memory as note nullifiers are revealed. If -the rest of the block is validated according to consensus rules, that root is -committed to its own datastructure via our state service (Sprout anchors, -Sapling anchors). Sapling block validation includes comparing the specified -FinalSaplingRoot in its block header to the root of the Sapling `NoteCommitment` -tree that we have just computed to make sure they match. - -As the transactions within a block are parsed, Sapling shielded transactions -including `Spend` descriptions and `Output` descriptions describe the spending and -creation of Zcash Sapling notes, and JoinSplit-on-Groth16 descriptions to -transfer/spend/create Sprout notes and transparent value. `JoinSplit` and `Spend` -descriptions specify an anchor, which references a previous `NoteCommitment` tree -root: for `Spend`s, this is a previous block's anchor as defined in their block -header, for `JoinSplit`s, it may be a previous block's anchor or the root -produced by a strictly previous `JoinSplit` description in its transaction. For -`Spend`s, this is convenient because we can query our state service for -previously finalized Sapling block anchors, and if they are found, then that -[consensus check](https://zips.z.cash/protocol/canopy.pdf#spendsandoutputs) has -been satisfied and the `Spend` description can be validated independently. For -`JoinSplit`s, if it's not a previously finalized block anchor, it must be the -treestate anchor of previous `JoinSplit` in this transaction, and we have to wait -for that one to be parsed and its root computed to check that ours is -valid. Luckily, it can only be a previous `JoinSplit` in this transaction, and is -[usually the immediately previous one](zcashd), so the set of candidate anchors -is smaller for earlier `JoinSplit`s in a transaction, but larger for the later -ones. For these `JoinSplit`s, they can be validated independently of their -anchor's finalization status as long as the final check of the anchor is done, -when available, such as at the Transaction level after all the `JoinSplit`s have -finished validating everything that can be validated without the context of -their anchor's finalization state. - -So for each transaction, for both `Spend` descriptions and `JoinSplit`s, we can -pre-emptively try to do our consensus check by looking up the anchors in our -finalized set first. For `Spend`s, we then trigger the remaining validation and -when that finishes we are full done with those. For `JoinSplit`s, the anchor -state check may pass early if it's a previous block Sprout `NoteCommitment` tree -root, but it may fail because it's an earlier `JoinSplit`s root instead, so once -the `JoinSplit` validates independently of the anchor, we wait for all candidate -previous `JoinSplit`s in that transaction finish validating before doing the -anchor consensus check again, but against the output treestate roots of earlier -`JoinSplit`s. - -Both Sprout and Sapling `NoteCommitment` trees must be computed for the whole -block to validate. For Sprout, we need to compute interstitial treestates in -between `JoinSplit`s in order to do the final consensus check for each/all -`JoinSplit`s, not just for the whole block, as in Sapling. - -For Sapling, at the block layer, we can iterate over all the transactions in -order and if they have `Spend`s and/or `Output`s, we update our Nullifer set for -the block as nullifiers are revealed in `Spend` descriptions, and update our note -commitment tree as `NoteCommitment`s are revealed in `Output` descriptions, adding -them as leaves in positions according to their order as they appear transaction -to transaction, output to output, in the block. This can be done independent of -the transaction validations. When the Sapling transactions are all validated, -the `NoteCommitmentTree` root should be computed: this is the anchor for this -block. For Sapling and Blossom blocks, we need to check that this root matches -the `RootHash` bytes in this block's header, as the `FinalSaplingRoot`. Once all -other consensus and validation checks are done, this will be saved down to our -finalized state to our `sapling_anchors` set, making it available for lookup by -other Sapling descriptions in future transactions. -TODO: explain Heartwood, Canopy, NU5 rule variants around anchors. -For Sprout, we must compute/update interstitial `NoteCommitmentTree`s between -`JoinSplit`s that may reference an earlier one's root as its anchor. If we do -this at the transaction layer, we can iterate through all the `JoinSplit`s and -compute the Sprout `NoteCommitmentTree` and nullifier set similar to how we do -the Sapling ones as described above, but at each state change (ie, -per-`JoinSplit`) we note the root and cache it for lookup later. As the -`JoinSplit`s are validated without context, we check for its specified anchor -amongst the interstitial roots we've already calculated (according to the spec, -these interstitial roots don't have to be finalized or the result of an -independently validated `JoinSplit`, they just must refer to any prior `JoinSplit` -root in the same transaction). So we only have to wait for our previous root to -be computed via any of our candidates, which in the worst case is waiting for -all of them to be computed for the last `JoinSplit`. If our `JoinSplit`s defined -root pops out, that `JoinSplit` passes that check. - -To finalize the block, the Sprout and Sapling treestates are the ones resulting -from the last transaction in the block, and determines the Sprout and Sapling -anchors that will be associated with this block as we commit it to our finalized -state. The Sprout and Sapling nullifiers revealed in the block will be merged -with the existing ones in our finalized state (ie, it should strictly grow over -time). - -## State Management - -### Orchard -- There is a single copy of the latest Orchard Note Commitment Tree for the finalized tip. -- When finalizing a block, the finalized tip is updated with a serialization of the latest Orchard Note Commitment Tree. (The previous tree should be deleted as part of the same database transaction.) -- Each non-finalized chain gets its own copy of the Orchard note commitment tree, cloned from the note commitment tree of the finalized tip or fork root. -- When a block is added to a non-finalized chain tip, the Orchard note commitment tree is updated with the note commitments from that block. -- When a block is rolled back from a non-finalized chain tip... (TODO) - -### Sapling -- There is a single copy of the latest Sapling Note Commitment Tree for the finalized tip. -- When finalizing a block, the finalized tip is updated with a serialization of the Sapling Note Commitment Tree. (The previous tree should be deleted as part of the same database transaction.) -- Each non-finalized chain gets its own copy of the Sapling note commitment tree, cloned from the note commitment tree of the finalized tip or fork root. -- When a block is added to a non-finalized chain tip, the Sapling note commitment tree is updated with the note commitments from that block. -- When a block is rolled back from a non-finalized chain tip... (TODO) - -### Sprout -- Every finalized block stores a separate copy of the Sprout note commitment tree (😿), as of that block. -- When finalizing a block, the Sprout note commitment tree for that block is stored in the state. (The trees for previous blocks also remain in the state.) -- Every block in each non-finalized chain gets its own copy of the Sprout note commitment tree. The initial tree is cloned from the note commitment tree of the finalized tip or fork root. -- When a block is added to a non-finalized chain tip, the Sprout note commitment tree is cloned, then updated with the note commitments from that block. -- When a block is rolled back from a non-finalized chain tip, the trees for each block are deleted, along with that block. - -We can't just compute a fresh tree with just the note commitments within a block, we are adding them to the tree referenced by the anchor, but we cannot update that tree with just the anchor, we need the 'frontier' nodes and leaves of the incremental merkle tree. - -# Reference-level explanation -[reference-level-explanation]: #reference-level-explanation - - -# Drawbacks -[drawbacks]: #drawbacks - - - -# Rationale and alternatives -[rationale-and-alternatives]: #rationale-and-alternatives - - -# Prior art -[prior-art]: #prior-art - - -# Unresolved questions -[unresolved-questions]: #unresolved-questions - - -# Future possibilities -[future-possibilities]: #future-possibilities diff --git a/book/src/dev/rfcs/drafts/data-flow-2020-07-22.md b/book/src/dev/rfcs/drafts/data-flow-2020-07-22.md index 5818ad119a1..8c366472ad4 100644 --- a/book/src/dev/rfcs/drafts/data-flow-2020-07-22.md +++ b/book/src/dev/rfcs/drafts/data-flow-2020-07-22.md @@ -23,7 +23,7 @@ - nullifiers (within a single transaction) - // Transactions containing empty `vin` must have either non-empty `vJoinSplit` or non-empty `vShieldedSpend`. - // Transactions containing empty `vout` must have either non-empty `vJoinSplit` or non-empty `vShieldedOutput`. - - Moar: https://github.com/zcash/zcash/blob/ab2b7c0969391d8a57d90d008665da02f3f618e7/src/main.cpp#L1091 + - More: https://github.com/zcash/zcash/blob/ab2b7c0969391d8a57d90d008665da02f3f618e7/src/main.cpp#L1091 - Sum up "LegacySigOps" for each transaction and check that it's less than some maximum - Acquires a lock, then calls `MarkBlockAsReceived` (networking?) - Calls `AcceptBlock`, defined at: https://github.com/zcash/zcash/blob/ab2b7c0969391d8a57d90d008665da02f3f618e7/src/main.cpp#L4180 diff --git a/book/src/dev/rfcs/drafts/xxxx-basic-integration-testing.md b/book/src/dev/rfcs/drafts/xxxx-basic-integration-testing.md index 7f9c0594e3c..dd77cc14cdc 100644 --- a/book/src/dev/rfcs/drafts/xxxx-basic-integration-testing.md +++ b/book/src/dev/rfcs/drafts/xxxx-basic-integration-testing.md @@ -124,7 +124,7 @@ Think about what the natural extension and evolution of your proposal would be and how it would affect Zebra and Zcash as a whole. Try to use this section as a tool to more fully consider all possible interactions with the project and cryptocurrency ecosystem in your proposal. -Also consider how the this all fits into the roadmap for the project +Also consider how this all fits into the roadmap for the project and of the relevant sub-team. This is also a good place to "dump ideas", if they are out of scope for the diff --git a/book/src/dev/rfcs/drafts/xxxx-block-subsidy.md b/book/src/dev/rfcs/drafts/xxxx-block-subsidy.md index 2ab752c4be6..838343ee77d 100644 --- a/book/src/dev/rfcs/drafts/xxxx-block-subsidy.md +++ b/book/src/dev/rfcs/drafts/xxxx-block-subsidy.md @@ -70,7 +70,7 @@ In Zebra the consensus related code lives in the `zebra-consensus` crate. The bl Inside `zebra-consensus/src/block/subsidy/` the following submodules will be created: - `general.rs`: General block reward functions and utilities. -- `founders_reward.rs`: Specific functions related to funders reward. +- `founders_reward.rs`: Specific functions related to founders reward. - `funding_streams.rs`: Specific functions for funding streams. In addition to calculations the block subsidy requires constants defined in the protocol. The implementation will also create additional constants, all of them will live at: diff --git a/book/src/dev/state-db-upgrades.md b/book/src/dev/state-db-upgrades.md index 15f962e88b4..dd9e5bcd587 100644 --- a/book/src/dev/state-db-upgrades.md +++ b/book/src/dev/state-db-upgrades.md @@ -12,7 +12,8 @@ family doesn't exist. Instead: - define the name and type of each column family at the top of the implementation module, - add a method on the database that returns that type, and -- add the column family name to the list of column families in the database: +- add the column family name to the list of column families in the database + (in the `STATE_COLUMN_FAMILIES_IN_CODE` array): For example: ```rust @@ -256,7 +257,7 @@ simulates typical user behaviour. And ideally: - An upgrade from the earliest supported Zebra version - (the CI sync-past-checkpoint tests do this on every PR) + (the CI sync-past-mandatory-checkpoint tests do this on every PR) #### Manually Triggering a Format Upgrade [manual-upgrade]: #manual-upgrade @@ -304,7 +305,7 @@ We use the following rocksdb column families: | `hash_by_tx_loc` | `TransactionLocation` | `transaction::Hash` | Create | | `tx_loc_by_hash` | `transaction::Hash` | `TransactionLocation` | Create | | *Transparent* | | | | -| `balance_by_transparent_addr` | `transparent::Address` | `Amount \|\| AddressLocation` | Update | +| `balance_by_transparent_addr` | `transparent::Address` | `AddressBalanceLocation` | Update | | `tx_loc_by_transparent_addr_loc` | `AddressTransaction` | `()` | Create | | `utxo_by_out_loc` | `OutputLocation` | `transparent::Output` | Delete | | `utxo_loc_by_transparent_addr_loc` | `AddressUnspentOutput` | `()` | Delete | @@ -325,6 +326,20 @@ We use the following rocksdb column families: | *Chain* | | | | | `history_tree` | `()` | `NonEmptyHistoryTree` | Update | | `tip_chain_value_pool` | `()` | `ValueBalance` | Update | +| `block_info` | `block::Height` | `BlockInfo` | Create | + +With the following additional modifications when compiled with the `indexer` feature: + +| Column Family | Keys | Values | Changes | +| ------------------------- | -------------------- | --------------------- | ------- | +| *Transparent* | | | | +| `tx_loc_by_spent_out_loc` | `OutputLocation` | `TransactionLocation` | Create | +| *Sprout* | | | | +| `sprout_nullifiers` | `sprout::Nullifier` | `TransactionLocation` | Create | +| *Sapling* | | | | +| `sapling_nullifiers` | `sapling::Nullifier` | `TransactionLocation` | Create | +| *Orchard* | | | | +| `orchard_nullifiers` | `orchard::Nullifier` | `TransactionLocation` | Create | ### Data Formats [rocksdb-data-format]: #rocksdb-data-format @@ -339,6 +354,7 @@ Block and Transaction Data: - `TransactionIndex`: 16 bits, big-endian, unsigned (max ~23,000 transactions in the 2 MB block limit) - `TransactionCount`: same as `TransactionIndex` - `TransactionLocation`: `Height \|\| TransactionIndex` +- `AddressBalanceLocation`: `Amount \|\| u64 \|\| AddressLocation` - `OutputIndex`: 24 bits, big-endian, unsigned (max ~223,000 transfers in the 2 MB block limit) - transparent and shielded input indexes, and shielded output indexes: 16 bits, big-endian, unsigned (max ~49,000 transfers in the 2 MB block limit) - `OutputLocation`: `TransactionLocation \|\| OutputIndex` @@ -587,9 +603,16 @@ So they should not be used for consensus-critical checks. **TODO:** store the `Root` hash in `sprout_note_commitment_tree`, and use it to look up the note commitment tree. This de-duplicates tree state data. But we currently only store one sprout tree by height. -- The value pools are only stored for the finalized tip. +- The value pools are only stored for the finalized tip. Per-block value pools + are stored in `block_info`, see below. - We do not store the cumulative work for the finalized chain, because the finalized work is equal for all non-finalized chains. So the additional non-finalized work can be used to calculate the relative chain order, and choose the best chain. + +- The `block_info` contains additional per-block data. Currently it stores + the value pools after that block, and its size. It has been implemented + in a future-proof way so it is possible to add more data to it whiles + still allowing database downgrades (i.e. it does not require the + data length to match exactly what is expected and ignores the rest) diff --git a/book/src/dev/zebra-dependencies-for-audit.md b/book/src/dev/zebra-dependencies-for-audit.md index f33aba321b8..7655ff275de 100644 --- a/book/src/dev/zebra-dependencies-for-audit.md +++ b/book/src/dev/zebra-dependencies-for-audit.md @@ -63,7 +63,7 @@ The following consensus, security, and functional changes are in Zebra's develop The following list of dependencies is out of scope for the audit. -Please ignore the dependency versions in these tables, some of them are are outdated. All versions of these dependencies are out of scope. +Please ignore the dependency versions in these tables, some of them are outdated. All versions of these dependencies are out of scope. The latest versions of Zebra's dependencies are in [`Cargo.lock`](https://github.com/ZcashFoundation/zebra/tree/audit-v1.0.0-rc.0/Cargo.lock), including transitive dependencies. They can be viewed using `cargo tree`. diff --git a/book/src/user/custom-testnets.md b/book/src/user/custom-testnets.md index 76406cdf704..18a19673ce9 100644 --- a/book/src/user/custom-testnets.md +++ b/book/src/user/custom-testnets.md @@ -156,7 +156,7 @@ The remaining consensus differences between Mainnet and Testnet could be made co ## Differences Between Custom Testnets and Regtest Zebra's Regtest network is a special case of a custom Testnet that: -- Won't make peer connections[^fn4], +- Won't make remote peer connections[^fn4], - Skips Proof-of-Work validation, - Uses a reserved network magic and network name, - Activates network upgrades up to and including Canopy at block height 1, @@ -183,4 +183,4 @@ Zebra nodes on custom Testnets will also reject peer connections with nodes that [^fn3]: Configuring any of the Testnet parameters that are currently configurable except the network name will result in an incompatible custom Testnet, these are: the network magic, network upgrade activation heights, slow start interval, genesis hash, disabled Proof-of-Work and target difficulty limit. -[^fn4]: Zebra won't make outbound peer connections on Regtest, but currently still listens for inbound peer connections, which will be rejected unless they use the Regtest network magic, and Zcash nodes using the Regtest network magic should not be making outbound peer connections. It may be updated to skip initialization of the peerset service altogether so that it won't listen for peer connections at all when support for isolated custom Testnets is added. +[^fn4]: Zebra won't make remote outbound peer connections on Regtest, but currently still listens for remote inbound peer connections, which will be rejected unless they use the Regtest network magic, and Zcash nodes using the Regtest network magic should not be making outbound peer connections. It may be updated to skip initialization of the peerset service altogether so that it won't listen for peer connections at all when support for isolated custom Testnets is added. diff --git a/book/src/user/docker.md b/book/src/user/docker.md index 90491024df3..95657770610 100644 --- a/book/src/user/docker.md +++ b/book/src/user/docker.md @@ -1,182 +1,178 @@ # Zebra with Docker -The easiest way to run Zebra is using [Docker](https://docs.docker.com/get-docker/). +The foundation maintains a Docker infrastructure for deploying and testing Zebra. -We've embraced Docker in Zebra for most of the solution lifecycle, from development environments to CI (in our pipelines), and deployment to end users. +## Quick Start -> [!TIP] -> We recommend using `docker compose` sub-command over the plain `docker` CLI, especially for more advanced use-cases like running CI locally, as it provides a more convenient and powerful way to manage multi-container Docker applications. See [CI/CD Local Testing](#cicd-local-testing) for more information, and other compose files available in the [docker](https://github.com/ZcashFoundation/zebra/tree/main/docker) folder. - -## Quick usage - -You can deploy Zebra for daily use with the images available in [Docker Hub](https://hub.docker.com/r/zfnd/zebra) or build it locally for testing. - -### Ready to use image - -Using `docker compose`: +To get Zebra quickly up and running, you can use an off-the-rack image from +[Docker Hub](https://hub.docker.com/r/zfnd/zebra/tags): ```shell -docker compose -f docker/docker-compose.yml up +docker run --name zebra zfnd/zebra ``` -With plain `docker` CLI: +If you want to preserve Zebra's state, you can create a Docker volume: ```shell docker volume create zebrad-cache - -docker run -d --platform linux/amd64 \ - --restart unless-stopped \ - --env-file .env \ - --mount type=volume,source=zebrad-cache,target=/var/cache/zebrad-cache \ - -p 8233:8233 \ - --memory 16G \ - --cpus 4 \ - zfnd/zebra ``` -### Build it locally +And mount it before you start the container: ```shell -git clone --depth 1 --branch v2.0.0 https://github.com/ZcashFoundation/zebra.git -docker build --file docker/Dockerfile --target runtime --tag zebra:local . -docker run --detach zebra:local +docker run \ + --mount source=zebrad-cache,target=/home/zebra/.cache/zebra \ + --name zebra \ + zfnd/zebra ``` -### Alternatives - -See [Building Zebra](https://github.com/ZcashFoundation/zebra#building-zebra) for more information. - -## Advanced usage - -You're able to specify various parameters when building or launching the Docker image, which are meant to be used by developers and CI pipelines. For example, specifying the Network where Zebra will run (Mainnet, Testnet, etc), or enabling features like metrics with Prometheus. - -For example, if we'd like to enable metrics on the image, we'd build it using the following `build-arg`: - -> [!IMPORTANT] -> To fully use and display the metrics, you'll need to run a Prometheus and Grafana server, and configure it to scrape and visualize the metrics endpoint. This is explained in more detailed in the [Metrics](https://zebra.zfnd.org/user/metrics.html#zebra-metrics) section of the User Guide. +You can also use `docker compose`, which we recommend. First get the repo: ```shell -docker build -f ./docker/Dockerfile --target runtime --build-arg FEATURES='default-release-binaries prometheus' --tag local/zebra.mining:latest . +git clone --depth 1 --branch v2.4.2 https://github.com/ZcashFoundation/zebra.git +cd zebra ``` -To increase the log output we can optionally add these `build-arg`s: +Then run: ```shell ---build-arg RUST_BACKTRACE=full --build-arg RUST_LOG=debug --build-arg COLORBT_SHOW_HIDDEN=1 +docker compose -f docker/docker-compose.yml up ``` -And after our image has been built, we can run it on `Mainnet` with the following command, which will expose the metrics endpoint on port `9999` and force the logs to be colored: +## Custom Images + +If you want to use your own images with, for example, some opt-in compilation +features enabled, add the desired features to the `FEATURES` variable in the +`docker/.env` file and build the image: ```shell -docker run --env LOG_COLOR="true" -p 9999:9999 local/zebra.mining +docker build \ + --file docker/Dockerfile \ + --env-file docker/.env \ + --target runtime \ + --tag zebra:local \ + . ``` -Based on our actual `entrypoint.sh` script, the following configuration file will be generated (on the fly, at startup) and used by Zebra: +### Alternatives -```toml -[network] -network = "Mainnet" -listen_addr = "0.0.0.0" -[state] -cache_dir = "/var/cache/zebrad-cache" -[metrics] -endpoint_addr = "127.0.0.1:9999" -``` +See [Building Zebra](https://github.com/ZcashFoundation/zebra#manual-build) for more information. -### Running Zebra with Lightwalletd -To run Zebra with Lightwalletd, we recommend using the provided `docker compose` files for Zebra and Lightwalletd, which will start both services and connect them together, while exposing ports, mounting volumes, and setting environment variables. +### Building with Custom Features -```shell -docker compose -f docker/docker-compose.yml -f docker/docker-compose.lwd.yml up -``` +Zebra supports various features that can be enabled during build time using the `FEATURES` build argument: -### CI/CD Local Testing +For example, if we'd like to enable metrics on the image, we'd build it using the following `build-arg`: + +> [!IMPORTANT] +> To fully use and display the metrics, you'll need to run a Prometheus and Grafana server, and configure it to scrape and visualize the metrics endpoint. This is explained in more detailed in the [Metrics](https://zebra.zfnd.org/user/metrics.html#zebra-metrics) section of the User Guide. -To run CI tests locally, which mimics the testing done in our CI pipelines on GitHub Actions, use the `docker-compose.test.yml` file. This setup allows for a consistent testing environment both locally and in CI. +```shell +# Build with specific features +docker build -f ./docker/Dockerfile --target runtime \ + --build-arg FEATURES="default-release-binaries prometheus" \ + --tag zebra:metrics . +``` -#### Running Tests Locally +All available Cargo features are listed at +. -1. **Setting Environment Variables**: - - Modify the `test.env` file to set the desired test configurations. - - For running all tests, set `RUN_ALL_TESTS=1` in `test.env`. +## Configuring Zebra -2. **Starting the Test Environment**: - - Use Docker Compose to start the testing environment: +To configure Zebra using Docker, you have a few options, processed in this order: - ```shell - docker-compose -f docker/docker-compose.test.yml up - ``` +1. **Provide a specific config file path:** Set the `ZEBRA_CONF_PATH` environment variable to point to your config file within the container. +2. **Use the default config file:** By default, the `docker-compose.yml` file mounts `./default-zebra-config.toml` to `/home/zebra/.config/zebrad.toml` using the `configs:` mapping. Zebra will use this file if `ZEBRA_CONF_PATH` is not set. To use environment variables instead, you must **comment out** the `configs:` mapping in `docker/docker-compose.yml`. +3. **Generate config from environment variables:** If neither of the above methods provides a config file (i.e., `ZEBRA_CONF_PATH` is unset *and* the `configs:` mapping in `docker-compose.yml` is commented out), the container's entrypoint script will *automatically generate* a default configuration file at `/home/zebra/.config/zebrad.toml`. This generated file uses specific environment variables (like `NETWORK`, `ZEBRA_RPC_PORT`, `ENABLE_COOKIE_AUTH`, `MINER_ADDRESS`, etc.) to define the settings. Using the `docker/.env` file is the primary way to set these variables for this auto-generation mode. - - This will start the Docker container and run the tests based on `test.env` settings. +You can see if your config works as intended by looking at Zebra's logs. -3. **Viewing Test Output**: - - The test results and logs will be displayed in the terminal. +Note that if you provide a configuration file using methods 1 or 2, environment variables from `docker/.env` will **not** override the settings within that file. The environment variables are primarily used for the auto-generation scenario (method 3). -4. **Stopping the Environment**: - - Once testing is complete, stop the environment using: +### RPC - ```shell - docker-compose -f docker/docker-compose.test.yml down - ``` +Zebra's RPC server is disabled by default. To enable it, you need to define the RPC settings in Zebra's configuration. You can achieve this using one of the configuration methods described above: -This approach ensures you can run the same tests locally that are run in CI, providing a robust way to validate changes before pushing to the repository. +* **Using a config file (methods 1 or 2):** Add or uncomment the `[rpc]` section in your `zebrad.toml` file (like the one provided in `docker/default-zebra-config.toml`). Ensure you set the `listen_addr` (e.g., `"0.0.0.0:8232"` for Mainnet). +* **Using environment variables (method 3):** Set the `ZEBRA_RPC_PORT` environment variable (e.g., in `docker/.env`). This tells the entrypoint script to include an enabled `[rpc]` section listening on `0.0.0.0:` in the auto-generated configuration file. -### Build and Run Time Configuration +**Cookie Authentication:** -#### Build Time Arguments +By default, Zebra uses cookie-based authentication for RPC requests (`enable_cookie_auth = true`). When enabled, Zebra generates a unique, random cookie file required for client authentication. -#### Configuration +* **Cookie Location:** The entrypoint script configures Zebra to store this file at `/home/zebra/.cache/zebra/.cookie` inside the container. +* **Viewing the Cookie:** If the container is running and RPC is enabled with authentication, you can view the cookie content using: -- `FEATURES`: Specifies the features to build `zebrad` with. Example: `"default-release-binaries getblocktemplate-rpcs"` -- `TEST_FEATURES`: Specifies the features for tests. Example: `"lightwalletd-grpc-tests zebra-checkpoints"` + ```bash + docker exec cat /home/zebra/.cache/zebra/.cookie + ``` -#### Logging + (Replace `` with your container's name, typically `zebra` if using the default `docker-compose.yml`). Your RPC client will need this value. +* **Disabling Authentication:** If you need to disable cookie authentication (e.g., for compatibility with tools like `lightwalletd`): + * If using a **config file** (methods 1 or 2), set `enable_cookie_auth = false` within the `[rpc]` section: -- `RUST_LOG`: Sets the trace log level. Example: `"debug"` -- `RUST_BACKTRACE`: Enables or disables backtraces. Example: `"full"` -- `RUST_LIB_BACKTRACE`: Enables or disables library backtraces. Example: `1` -- `COLORBT_SHOW_HIDDEN`: Enables or disables showing hidden backtraces. Example: `1` + ```toml + [rpc] + # listen_addr = ... + enable_cookie_auth = false + ``` -#### Tests + * If using **environment variables** for auto-generation (method 3), set `ENABLE_COOKIE_AUTH=false` in your `docker/.env` file. -- `TEST_FEATURES`: Specifies the features for tests. Example: `"lightwalletd-grpc-tests zebra-checkpoints"` -- `ZEBRA_SKIP_IPV6_TESTS`: Skips IPv6 tests. Example: `1` -- `ENTRYPOINT_FEATURES`: Overrides the specific features used to run tests in `entrypoint.sh`. Example: `"default-release-binaries lightwalletd-grpc-tests"` +Remember that Zebra only generates the cookie file if the RPC server is enabled *and* `enable_cookie_auth` is set to `true` (or omitted, as `true` is the default). -#### CI/CD +## Examples -- `SHORT_SHA`: Represents the short SHA of the commit. Example: `"a1b2c3d"` +To make the initial setup of Zebra with other services easier, we provide some +example files for `docker compose`. The following subsections will walk you +through those examples. -#### Run Time Variables +### Running Zebra with Lightwalletd -- `NETWORK`: Specifies the network type. Example: `"Mainnet"` +The following command will run Zebra with Lightwalletd: -#### Zebra Configuration +```shell +docker compose -f docker/docker-compose.lwd.yml up +``` -- `ZEBRA_CHECKPOINT_SYNC`: Enables or disables checkpoint sync. Example: `true` -- `ZEBRA_LISTEN_ADDR`: Address for Zebra to listen on. Example: `"0.0.0.0"` -- `ZEBRA_CACHED_STATE_DIR`: Directory for cached state. Example: `"/var/cache/zebrad-cache"` +Note that Docker will run Zebra with the RPC server enabled and the cookie +authentication mechanism disabled since Lightwalletd doesn't support it. Instead +of configuring Zebra via the recommended config file or `docker/.env` file, we +configured the RPC server by setting environment variables directly in the +`docker/docker-compose.lwd.yml` file. This takes advantage of the entrypoint +script's auto-generation feature (method 3 described above). -#### Mining Configuration +### Running Zebra with Prometheus and Grafana -- `RPC_LISTEN_ADDR`: Address for RPC to listen on. Example: `"0.0.0.0"` -- `RPC_PORT`: Port for RPC. Example: `8232` -- `MINER_ADDRESS`: Address for the miner. Example: `"t1XhG6pT9xRqRQn3BHP7heUou1RuYrbcrCc"` +The following commands will run Zebra with Prometheus and Grafana: -#### Other Configuration +```shell +docker compose -f docker/docker-compose.grafana.yml build --no-cache +docker compose -f docker/docker-compose.grafana.yml up +``` -- `METRICS_ENDPOINT_ADDR`: Address for metrics endpoint. Example: `"0.0.0.0"` -- `METRICS_ENDPOINT_PORT`: Port for metrics endpoint. Example: `9999` -- `LOG_FILE`: Path to the log file. Example: `"/path/to/log/file.log"` -- `LOG_COLOR`: Enables or disables log color. Example: `false` -- `TRACING_ENDPOINT_ADDR`: Address for tracing endpoint. Example: `"0.0.0.0"` -- `TRACING_ENDPOINT_PORT`: Port for tracing endpoint. Example: `3000` +In this example, we build a local Zebra image with the `prometheus` Cargo +compilation feature. Note that we enable this feature by specifying its name in +the build arguments. Having this Cargo feature specified at build time makes +`cargo` compile Zebra with the metrics support for Prometheus enabled. Note that +we also specify this feature as an environment variable at run time. Having this +feature specified at run time makes Docker's entrypoint script configure Zebra +to open a scraping endpoint on `localhost:9999` for Prometheus. -Specific tests are defined in `docker/test.env` file and can be enabled by setting the corresponding environment variable to `1`. +Once all services are up, the Grafana web UI should be available at +`localhost:3000`, the Prometheus web UI should be at `localhost:9090`, and +Zebra's scraping page should be at `localhost:9999`. The default login and +password for Grafana are both `admin`. To make Grafana use Prometheus, you need +to add Prometheus as a data source with the URL `http://localhost:9090` in +Grafana's UI. You can then import various Grafana dashboards from the `grafana` +directory in the Zebra repo. -## Registries +### Running CI Tests Locally -The images built by the Zebra team are all publicly hosted. Old image versions meant to be used by our [CI pipeline](https://github.com/ZcashFoundation/zebra/blob/main/.github/workflows/ci-integration-tests-gcp.yml) (`zebrad-test`, `lighwalletd`) might be deleted on a scheduled basis. +To run CI tests locally, first set the variables in the `test.env` file to +configure the tests, then run: -We use [Docker Hub](https://hub.docker.com/r/zfnd/zebra) for end-user images and [Google Artifact Registry](https://console.cloud.google.com/artifacts/docker/zfnd-dev-zebra/us/zebra) to build external tools and test images. +```shell +docker-compose -f docker/docker-compose.test.yml up +``` diff --git a/book/src/user/fork-zebra-testnet.md b/book/src/user/fork-zebra-testnet.md index a7bf7b410c4..732129dafbe 100644 --- a/book/src/user/fork-zebra-testnet.md +++ b/book/src/user/fork-zebra-testnet.md @@ -94,7 +94,6 @@ Relevant parts of the configuration file: debug_enable_at_height = 0 [mining] -debug_like_zcashd = true miner_address = 't27eWDgjFYJGVXmzrXeVjnb5J3uXDM9xH9v' [network] diff --git a/book/src/user/install.md b/book/src/user/install.md index 6648339f743..a7bc4b4ae97 100644 --- a/book/src/user/install.md +++ b/book/src/user/install.md @@ -45,7 +45,6 @@ features](https://doc.rust-lang.org/cargo/reference/features.html#command-line-f - `prometheus` for [Prometheus metrics](https://zebra.zfnd.org/user/metrics.html) - `sentry` for [Sentry monitoring](https://zebra.zfnd.org/user/tracing.html#sentry-production-monitoring) - `elasticsearch` for [experimental Elasticsearch support](https://zebra.zfnd.org/user/elasticsearch.html) -- `shielded-scan` for [experimental shielded scan support](https://zebra.zfnd.org/user/shielded-scan.html) You can combine multiple features by listing them as parameters of the `--features` flag: @@ -76,7 +75,7 @@ To compile Zebra directly from GitHub, or from a GitHub release source archive: ```sh git clone https://github.com/ZcashFoundation/zebra.git cd zebra -git checkout v2.0.0 +git checkout v2.4.2 ``` 3. Build and Run `zebrad` @@ -89,7 +88,7 @@ target/release/zebrad start ### Compiling from git using cargo install ```sh -cargo install --git https://github.com/ZcashFoundation/zebra --tag v2.0.0 zebrad +cargo install --git https://github.com/ZcashFoundation/zebra --tag v2.4.2 zebrad ``` ### Compiling on ARM @@ -114,11 +113,6 @@ If you're having trouble with: - use `cargo install` without `--locked` to build with the latest versions of each dependency -## Experimental Shielded Scanning feature - -- install the `rocksdb-tools` or `rocksdb` packages to get the `ldb` binary, which allows expert users to - [query the scanner database](https://zebra.zfnd.org/user/shielded-scan.html). This binary is sometimes called `rocksdb_ldb`. - ## Optional Tor feature - **sqlite linker errors:** libsqlite3 is an optional dependency of the `zebra-network/tor` feature. diff --git a/book/src/user/mining-docker.md b/book/src/user/mining-docker.md index 002848c0ca3..2550ba23610 100644 --- a/book/src/user/mining-docker.md +++ b/book/src/user/mining-docker.md @@ -1,42 +1,43 @@ # Mining with Zebra in Docker -Zebra's [Docker images](https://hub.docker.com/r/zfnd/zebra/tags) can be used for your mining -operations. If you don't have Docker, see the -[manual configuration instructions](https://zebra.zfnd.org/user/mining.html). +Zebra's [Docker images](https://hub.docker.com/r/zfnd/zebra/tags) can be used +for your mining operations. If you don't have Docker, see the [manual +configuration instructions](https://zebra.zfnd.org/user/mining.html). Using docker, you can start mining by running: ```bash -docker run -e MINER_ADDRESS="t3dvVE3SQEi7kqNzwrfNePxZ1d4hUyztBA1" -p 8232:8232 zfnd/zebra:latest +docker run --name -zebra_local -e MINER_ADDRESS="t3dvVE3SQEi7kqNzwrfNePxZ1d4hUyztBA1" -e ZEBRA_RPC_PORT=8232 -p 8232:8232 zfnd/zebra:latest ``` -This command starts a container on Mainnet and binds port 8232 on your Docker host. If you -want to start generating blocks, you need to let Zebra sync first. +This command starts a container on Mainnet and binds port 8232 on your Docker +host. If you want to start generating blocks, you need to let Zebra sync first. Note that you must pass the address for your mining rewards via the `MINER_ADDRESS` environment variable when you are starting the container, as we -did with the ZF funding stream address above. The address we used starts with the prefix `t1`, -meaning it is a Mainnet P2PKH address. Please remember to set your own address -for the rewards. +did with the ZF funding stream address above. The address we used starts with +the prefix `t1`, meaning it is a Mainnet P2PKH address. Please remember to set +your own address for the rewards. The port we mapped between the container and the host with the `-p` flag in the -example above is Zebra's default Mainnet RPC port. If you want to use a -different one, you can specify it in the `RPC_PORT` environment variable, -similarly to `MINER_ADDRESS`, and then map it with the Docker's `-p` flag. +example above is Zebra's default Mainnet RPC port. Instead of listing the environment variables on the command line, you can use -Docker's `--env-file` flag to specify a file containing the variables. You -can find more info here +Docker's `--env-file` flag to specify a file containing the variables. You can +find more info here https://docs.docker.com/engine/reference/commandline/run/#env. -## Mining on Testnet +If you don't want to set any environment variables, you can edit the +`docker/default-zebra-config.toml` file, and pass it to Zebra before starting +the container. There's an example in `docker/docker-compose.yml` of how to do +that. If you want to mine on Testnet, you need to set the `NETWORK` environment variable to `Testnet` and use a Testnet address for the rewards. For example, running ```bash -docker run -e NETWORK="Testnet" -e MINER_ADDRESS="t27eWDgjFYJGVXmzrXeVjnb5J3uXDM9xH9v" -p 18232:18232 zfnd/zebra:latest +docker run --name zebra_local -e NETWORK="Testnet" -e MINER_ADDRESS="t27eWDgjFYJGVXmzrXeVjnb5J3uXDM9xH9v" -e ZEBRA_RPC_PORT=18232 -p 18232:18232 zfnd/zebra:latest ``` will start a container on Testnet and bind port 18232 on your Docker host, which @@ -44,3 +45,21 @@ is the standard Testnet RPC port. Notice that we also used a different rewards address. It starts with the prefix `t2`, indicating that it is a Testnet address. A Mainnet address would prevent Zebra from starting on Testnet, and conversely, a Testnet address would prevent Zebra from starting on Mainnet. + +To connect to the RPC port, you will need the contents of the [cookie +file](https://zebra.zfnd.org/user/mining.html?highlight=cookie#testing-the-setup) +Zebra uses for authentication. By default, it is stored at +`/home/zebra/.cache/zebra/.cookie`. You can print its contents by running + +```bash +docker exec -it zebra_local cat /home/zebra/.cache/zebra/.cookie +``` + +If you want to avoid authentication, you can turn it off by setting + +```toml +[rpc] +enable_cookie_auth = false +``` + +in Zebra's config file before you start the container. diff --git a/book/src/user/regtest.md b/book/src/user/regtest.md index 4acb5b36194..90962f95b59 100644 --- a/book/src/user/regtest.md +++ b/book/src/user/regtest.md @@ -13,15 +13,15 @@ Relevant parts of the configuration file: ```toml [mining] miner_address = 't27eWDgjFYJGVXmzrXeVjnb5J3uXDM9xH9v' - + [network] network = "Regtest" # This section may be omitted when testing only Canopy [network.testnet_parameters.activation_heights] -# Configured activation heights must be greater than or equal to 1, +# Configured activation heights must be greater than or equal to 1, # block height 0 is reserved for the Genesis network upgrade in Zebra -NU5 = 1 +NU5 = 1 # This section may be omitted if a persistent Regtest chain state is desired [state] @@ -80,14 +80,8 @@ let block_template: GetBlockTemplate = client .await .expect("response should be success output with a serialized `GetBlockTemplate`"); -let network_upgrade = if block_template.height < NU5_ACTIVATION_HEIGHT { - NetworkUpgrade::Canopy -} else { - NetworkUpgrade::Nu5 -}; - let block_data = hex::encode( - proposal_block_from_template(&block_template, TimeSource::default(), network_upgrade)? + proposal_block_from_template(&block_template, TimeSource::default(), Network::Mainnet)? .zcash_serialize_to_vec()?, ); diff --git a/book/src/user/shielded-scan-grpc-server.md b/book/src/user/shielded-scan-grpc-server.md deleted file mode 100644 index d08bc0df49e..00000000000 --- a/book/src/user/shielded-scan-grpc-server.md +++ /dev/null @@ -1,146 +0,0 @@ -# Zebra Shielded Scanning gRPC Server - -## Get Started - -### Setup - -After setting up [Zebra Shielded Scanning](https://zebra.zfnd.org/user/shielded-scan.html), you can add a `listen-addr` argument to the scanner binary: - - -```bash -zebra-scanner --sapling-keys-to-scan '{"key":"zxviews1q0duytgcqqqqpqre26wkl45gvwwwd706xw608hucmvfalr759ejwf7qshjf5r9aa7323zulvz6plhttp5mltqcgs9t039cx2d09mgq05ts63n8u35hyv6h9nc9ctqqtue2u7cer2mqegunuulq2luhq3ywjcz35yyljewa4mgkgjzyfwh6fr6jd0dzd44ghk0nxdv2hnv4j5nxfwv24rwdmgllhe0p8568sgqt9ckt02v2kxf5ahtql6s0ltjpkckw8gtymxtxuu9gcr0swvz", "birthday_height": 419200}' --zebrad-cache-dir /media/alfredo/stuff/chain/zebra --zebra-rpc-listen-addr '127.0.0.1:8232' --listen-addr '127.0.0.1:8231' -``` - -Making requests to the server will also require a gRPC client, the examples here use `grpcurl`, though any gRPC client should work. - -[See installation instructions for `grpcurl` here](https://github.com/fullstorydev/grpcurl?tab=readme-ov-file#installation). - -The types can be accessed through the `zebra-grpc` crate's root `scanner` module for clients in a Rust environment, and the [`scanner.proto` file here](https://github.com/ZcashFoundation/zebra/blob/main/zebra-grpc/proto/scanner.proto) can be used to build types in other environments. - -### Usage - -To check that the gRPC server is running, try calling `scanner.Scanner/GetInfo`, for example with `grpcurl`: - -```bash -grpcurl -plaintext '127.0.0.1:8231' scanner.Scanner/GetInfo -``` - -The response should look like: - -``` -{ - "minSaplingBirthdayHeight": 419200 -} -``` - -An example request to the `Scan` method with `grpcurl` would look like: - -```bash -grpcurl -plaintext -d '{ "keys": { "key": ["sapling_extended_full_viewing_key"] } }' '127.0.0.1:8231' scanner.Scanner/Scan -``` - -This will start scanning for transactions in Zebra's state and in new blocks as they're validated. - -Or, to use the scanner gRPC server without streaming, try calling `RegisterKeys` with your Sapling extended full viewing key, waiting for the scanner to cache some results, then calling `GetResults`: - -```bash -grpcurl -plaintext -d '{ "keys": { "key": ["sapling_extended_full_viewing_key"] } }' '127.0.0.1:8231' scanner.Scanner/RegisterKeys -grpcurl -plaintext -d '{ "keys": ["sapling_extended_full_viewing_key"] }' '127.0.0.1:8231' scanner.Scanner/GetResults -``` - -## gRPC Reflection - -To see all of the provided methods with `grpcurl`, try: - -```bash -grpcurl -plaintext '127.0.0.1:8231' list scanner.Scanner -``` - -This will list the paths to each method in the `Scanner` service: -``` -scanner.Scanner.ClearResults -scanner.Scanner.DeleteKeys -scanner.Scanner.GetInfo -scanner.Scanner.GetResults -scanner.Scanner.RegisterKeys -``` - -To see the the request and response types for a method, for example the `GetResults` method, try: - - -```bash -grpcurl -plaintext '127.0.0.1:8231' describe scanner.Scanner.GetResults \ -&& grpcurl -plaintext '127.0.0.1:8231' describe scanner.GetResultsRequest \ -&& grpcurl -plaintext '127.0.0.1:8231' describe scanner.GetResultsResponse \ -&& grpcurl -plaintext '127.0.0.1:8231' describe scanner.Results \ -&& grpcurl -plaintext '127.0.0.1:8231' describe scanner.Transactions \ -&& grpcurl -plaintext '127.0.0.1:8231' describe scanner.Transaction -``` - -The response should be the request and response types for the `GetResults` method: - -``` -scanner.Scanner.GetResults is a method: -// Get all data we have stored for the given keys. -rpc GetResults ( .scanner.GetResultsRequest ) returns ( .scanner.GetResultsResponse ); -scanner.GetResultsRequest is a message: -// A request for getting results for a set of keys. -message GetResultsRequest { - // Keys for which to get results. - repeated string keys = 1; -} -scanner.GetResultsResponse is a message: -// A set of responses for each provided key of a GetResults call. -message GetResultsResponse { - // Results for each key. - map results = 1; -} -scanner.Results is a message: -// A result for a single key. -message Results { - // A height, transaction id map - map by_height = 1; -} -scanner.Transactions is a message: -// A vector of transaction hashes -message Transactions { - // Transactions - repeated Transaction transactions = 1; -} -scanner.Transaction is a message: -// Transaction data -message Transaction { - // The transaction hash/id - string hash = 1; -} -``` - -## Methods - - - ---- -#### GetInfo - -Returns basic information about the `zebra-scan` instance. - -#### RegisterKeys - -Starts scanning for a set of keys, with optional start heights, and caching the results. -Cached results can later be retrieved by calling the `GetResults` or `Scan` methods. - -#### DeleteKeys - -Stops scanning transactions for a set of keys. Deletes the keys and their cached results for the keys from zebra-scan. - -#### GetResults - -Returns cached results for a set of keys. - -#### ClearResults - -Deletes any cached results for a set of keys. - -#### Scan - -Starts scanning for a set of keys and returns a stream of results. diff --git a/book/src/user/shielded-scan.md b/book/src/user/shielded-scan.md deleted file mode 100644 index dff3e599ed8..00000000000 --- a/book/src/user/shielded-scan.md +++ /dev/null @@ -1,103 +0,0 @@ -# Zebra Shielded Scanning - -The `zebra-scanner` binary is a standalone application that utilizes Zebra libraries to scan for transactions associated with specific Sapling viewing keys. It stores the discovered transactions and scanning progress data in a RocksDB database. - -For this application to function, it requires access to a Zebra node's RPC server and state cache. - -For now, we only support Sapling, and only store transaction IDs in the scanner results database. - -Ongoing development is tracked in issue [#7728](https://github.com/ZcashFoundation/zebra/issues/7728). - -## Important Security Warning - -Zebra's shielded scanning feature has known security issues. It is for experimental use only. - -Do not use regular or sensitive viewing keys with Zebra's experimental scanning feature. Do not use this feature on a shared machine. We suggest generating new keys for experimental use or using publicly known keys. - -## Build & Install - -Use [Zebra 1.9.0](https://github.com/ZcashFoundation/zebra/releases/tag/v1.9.0) or greater, or the `main` branch to get the latest features. - -You can also use Rust's `cargo` to install `zebra-scanner` from the latest release Zebra repository: - -```bash -cargo install --locked --git https://github.com/ZcashFoundation/zebra zebra-scan -``` - -The scanner binary will be at `~/.cargo/bin/zebra-scanner`, which should be in your `PATH`. - -## Arguments - -Retrieve the binary arguments with: - -```bash -zebra-scanner --help -``` - -## Scanning the Block Chain - -Before starting, ensure a `zebrad` node is running locally with the RPC endpoint open. Refer to the [lightwalletd zebrad setup](https://zebra.zfnd.org/user/lightwalletd.html#configure-zebra-for-lightwalletd) or [zebrad mining setup](https://zebra.zfnd.org/user/mining.html#configure-zebra-for-mining) for instructions. - -To initiate the scanning process, you need the following: - -- A zebrad cache state directory. This can be obtained from the running zebrad configuration file, under the `state` section in the `cache_dir` field. -- A key to scan with, optionally including a birthday height, which specifies the starting height for the scanning process for that key. -- A zebrad RPC endpoint address. This can be found in the running zebrad configuration file, under the `rpc` section in the `listen_addr` field. - - -Sapling diversifiable/extended full viewing keys strings start with `zxviews` as -described in -[ZIP-32](https://zips.z.cash/zip-0032#sapling-extended-full-viewing-keys). - -For example, to scan the block chain with the [public ZECpages viewing -key](https://zecpages.com/boardinfo), use: - -```bash -RUST_LOG=info zebra-scanner --sapling-keys-to-scan '{"key":"zxviews1q0duytgcqqqqpqre26wkl45gvwwwd706xw608hucmvfalr759ejwf7qshjf5r9aa7323zulvz6plhttp5mltqcgs9t039cx2d09mgq05ts63n8u35hyv6h9nc9ctqqtue2u7cer2mqegunuulq2luhq3ywjcz35yyljewa4mgkgjzyfwh6fr6jd0dzd44ghk0nxdv2hnv4j5nxfwv24rwdmgllhe0p8568sgqt9ckt02v2kxf5ahtql6s0ltjpkckw8gtymxtxuu9gcr0swvz", "birthday_height": 419200}' --zebrad-cache-dir /media/alfredo/stuff/chain/zebra --zebra-rpc-listen-addr '127.0.0.1:8232' -``` - -- A birthday lower than the Sapling activation height defaults to Sapling activation height. -- A birthday greater or equal than Sapling activation height will start scanning at provided height, improving scanner speed. - -The scanning process begins once Zebra syncs its state past the Sapling activation height. Scanning a synced state takes between 12 and 24 hours. The scanner searches for transactions containing Sapling notes with outputs decryptable by the provided viewing keys. - -You will see log messages in the output every 10,000 blocks scanned, similar to: - -``` -... -2024-07-13T16:07:47.944309Z INFO zebra_scan::service::scan_task::scan: Scanning the blockchain for key 0, started at block 571001, now at block 580000, current tip 2556979 -2024-07-13T16:08:07.811013Z INFO zebra_scan::service::scan_task::scan: Scanning the blockchain for key 0, started at block 571001, now at block 590000, current tip 2556979 -... -``` - -If your Zebra instance goes down for any reason, the Zebra scanner will resume the task. Upon a new start, Zebra will display: - -``` -2024-07-13T16:07:17.700073Z INFO zebra_scan::storage::db: Last scanned height for key number 0 is 590000, resuming at 590001 -2024-07-13T16:07:17.706727Z INFO zebra_scan::service::scan_task::scan: got min scan height start_height=Height(590000) -``` - -## Displaying Scanning Results - -An easy way to query the results is to use the -[Scanning Results Reader](https://github.com/ZcashFoundation/zebra/tree/main/zebra-utils#scanning-results-reader). - -## Querying Raw Scanning Results - -A more advanced way to query results is to use `ldb` tool, requires a certain level of expertise. - -Install `ldb`: - -```bash -sudo apt install rocksdb-tools -``` - -Run `ldb` with the scanner database: - -```bash -ldb --db="$HOME/.cache/zebra/private-scan/v1/mainnet" --secondary_path= --column_family=sapling_tx_ids --hex scan -``` - -Some of the output will be markers the scanner uses to keep track of progress, however, some of them will be transactions found. - -To lean more about how to filter the database please refer to [RocksDB Administration and Data Access Tool](https://github.com/facebook/rocksdb/wiki/Administration-and-Data-Access-Tool) diff --git a/book/src/user/tracing.md b/book/src/user/tracing.md index d1b984f05df..cbacddb9ad4 100644 --- a/book/src/user/tracing.md +++ b/book/src/user/tracing.md @@ -36,10 +36,10 @@ and the [`flamegraph`][flamegraph] runtime config option. Compile Zebra with `--features sentry` to monitor it using [Sentry][sentry] in production. -[tracing_section]: https://docs.rs/zebrad/latest/zebrad/components/tracing/struct.Config.html -[filter]: https://docs.rs/zebrad/latest/zebrad/components/tracing/struct.Config.html#structfield.filter -[flamegraph]: https://docs.rs/zebrad/latest/zebrad/components/tracing/struct.Config.html#structfield.flamegraph +[tracing_section]: https://docs.rs/zebrad/latest/zebrad/components/tracing/struct.InnerConfig.html +[filter]: https://docs.rs/zebrad/latest/zebrad/components/tracing/struct.InnerConfig.html#structfield.filter +[flamegraph]: https://docs.rs/zebrad/latest/zebrad/components/tracing/struct.InnerConfig.html#structfield.flamegraph [flamegraphs]: http://www.brendangregg.com/flamegraphs.html [systemd_journald]: https://www.freedesktop.org/software/systemd/man/systemd-journald.service.html -[use_journald]: https://docs.rs/zebrad/latest/zebrad/components/tracing/struct.Config.html#structfield.use_journald +[use_journald]: https://docs.rs/zebrad/latest/zebrad/components/tracing/struct.InnerConfig.html#structfield.use_journald [sentry]: https://sentry.io/welcome/ diff --git a/book/src/user/troubleshooting.md b/book/src/user/troubleshooting.md index 09b31e4513d..3566fecadf6 100644 --- a/book/src/user/troubleshooting.md +++ b/book/src/user/troubleshooting.md @@ -4,8 +4,6 @@ There are a few bugs in Zebra that we're still working on fixing: -- [The `getpeerinfo` RPC shows current and recent outbound connections](https://github.com/ZcashFoundation/zebra/issues/7893), rather than current inbound and outbound connections. - - [Progress bar estimates can become extremely large](https://github.com/console-rs/indicatif/issues/556). We're waiting on a fix in the progress bar library. - Zebra currently gossips and connects to [private IP addresses](https://en.wikipedia.org/wiki/IP_address#Private_addresses), we want to [disable private IPs but provide a config (#3117)](https://github.com/ZcashFoundation/zebra/issues/3117) in an upcoming release diff --git a/deny.toml b/deny.toml index 6c809cabd12..4273223c9d2 100644 --- a/deny.toml +++ b/deny.toml @@ -52,9 +52,6 @@ skip-tree = [ # wait for ordered-map to release a dependency fix { name = "ordered-map", version = "=0.4.2" }, - # wait for primitive-types to upgrade - { name = "proc-macro-crate", version = "=0.1.5" }, - # wait for `color-eyre` to upgrade { name = "owo-colors", version = "=3.5.0" }, @@ -64,33 +61,12 @@ skip-tree = [ # wait for abscissa_core to upgrade {name = "tracing-log", version = "=0.1.4" }, - # wait for tokio-test -> tokio-stream to upgrade - { name = "tokio-util", version = "=0.6.10" }, - # wait for console-subscriber and tower to update hdrhistogram. # also wait for ron to update insta, and wait for tonic update. { name = "base64", version = "=0.13.1" }, - # wait for elasticsearch to update base64, darling, rustc_version, serde_with - { name = "elasticsearch", version = "=8.5.0-alpha.1" }, - - # wait for reqwest to update base64 - { name = "base64", version = "=0.21.7" }, - { name = "sync_wrapper", version = "0.1.2" }, - - # wait for jsonrpc-http-server to update hyper or for Zebra to replace jsonrpc (#8682) - { name = "h2", version = "=0.3.26" }, - { name = "http", version = "=0.2.12" }, - { name = "http-body", version = "=0.4.6" }, - { name = "hyper", version = "=0.14.31" }, - { name = "hyper-rustls", version = "=0.24.2" }, - - { name = "reqwest", version = "=0.11.27" }, - { name = "rustls", version = "=0.21.12" }, - { name = "rustls-pemfile", version = "=1.0.4" }, - { name = "rustls-webpki", version = "=0.101.7" }, - { name = "tokio-rustls", version = "=0.24.1" }, - { name = "webpki-roots", version = "=0.25.4" }, + # wait for abscissa_core to update toml + { name = "toml", version = "=0.5.11" }, # wait for structopt-derive to update heck { name = "heck", version = "=0.3.3" }, @@ -102,21 +78,35 @@ skip-tree = [ # wait for halo2_gadgets and primitive-types to update uint { name = "uint", version = "=0.9.5" }, - # wait for dirs-sys to update windows-sys - { name = "windows-sys", version = "=0.48.0" }, - # wait for zebra to update tower { name = "tower", version = "=0.4.13" }, - { name = "hashbrown", version = "=0.12.3" }, - - # Remove after release candicate period is over and the ECC crates are not patched anymore - { name = "equihash", version = "=0.2.0" }, - { name = "f4jumble", version = "=0.1.0" }, - { name = "incrementalmerkletree", version = "=0.6.0" }, - { name = "zcash_address", version = "=0.4.0" }, - { name = "zcash_keys", version = "=0.3.0" }, - { name = "zcash_primitives", version = "=0.16.0" }, - { name = "zcash_protocol", version = "=0.2.0" } + { name = "hashbrown", version = "=0.14.5" }, + + # wait for zebra to update vergen + { name = "thiserror", version = "=1.0.69" }, + { name = "thiserror-impl", version = "=1.0.69" }, + + # wait for all librustzcash crates to update sha2, secp256k1, and ripemd + { name = "sha2", version = "=0.10.9" }, + { name = "ripemd", version = "=0.1.3" }, + + # wait for zcash_script to update itertools + { name = "itertools", version = "=0.13.0" }, + + # wait for abscissa_core to update synstructure + { name = "synstructure", version = "=0.12.6" }, + + # wait until zcash_client_backend update rustix + { name = "rustix", version = "=0.38.44" }, + + # wait for reqwest to update windows-registry + { name = "windows-strings", version = "=0.3.1" }, + + # wait for sentry to update windows-core + { name = "windows-core", version = "=0.52.0" }, + + # wait for `inferno` to upgrade + { name = "quick-xml", version = "=0.37.5" } ] # This section is considered when running `cargo deny check sources`. diff --git a/docker/.env b/docker/.env index 2d96240f23e..629d7d6b9c7 100644 --- a/docker/.env +++ b/docker/.env @@ -1,33 +1,74 @@ -RUST_LOG=info -# This variable forces the use of color in the logs -ZEBRA_FORCE_USE_COLOR=1 -LOG_COLOR=true - -### -# Configuration Variables -# These variables are used to configure the zebra node -# Check the entrypoint.sh script for more details -### - -# The config file full path used in the Dockerfile. -ZEBRA_CONF_PATH=/etc/zebrad/zebrad.toml -# [network] -NETWORK=Mainnet -ZEBRA_LISTEN_ADDR=0.0.0.0 -# [consensus] -ZEBRA_CHECKPOINT_SYNC=true -# [state] -# Set this to change the default cached state directory -ZEBRA_CACHED_STATE_DIR=/var/cache/zebrad-cache -LIGHTWALLETD_DATA_DIR=/var/cache/lwd-cache -# [metrics] -METRICS_ENDPOINT_ADDR=0.0.0.0 -METRICS_ENDPOINT_PORT=9999 -# [tracing] -TRACING_ENDPOINT_ADDR=0.0.0.0 -TRACING_ENDPOINT_PORT=3000 -# [rpc] -RPC_LISTEN_ADDR=0.0.0.0 -# if ${RPC_PORT} is not set, it will use the default value for the current network -RPC_PORT=8232 +# Configuration variables for running Zebra in Docker +# Sets the path to a custom Zebra config file. If not set, Zebra will look for a config at +# ${HOME}/.config/zebrad.toml or generate one using environment variables below. +# ! Setting ZEBRA_CONF_PATH will make most of the following environment variables ineffective. +# +# ZEBRA_CONF_PATH="/path/to/your/custom/zebrad.toml" + +# Sets the network Zebra runs will run on. +# +# NETWORK=Mainnet + +# Zebra's RPC server is disabled by default. To enable it, set its port number. +# +# ZEBRA_RPC_PORT=8232 # Default RPC port number on Mainnet. +# ZEBRA_RPC_PORT=18232 # Default RPC port number on Testnet. + +# To disable cookie authentication, set the value below to false. +# +# ENABLE_COOKIE_AUTH=true + +# Sets a custom directory for the cookie authentication file. +# +# ZEBRA_COOKIE_DIR="/home/zebra/.config/cookie" + +# Sets a custom directory for the state and network caches. +# +# ZEBRA_CACHE_DIR="/home/zebra/.cache/zebra" + +# Sets custom Cargo features. Available features are listed at +# . +# +# Must be set at build time. +# +# FEATURES="" + +# Sets the listen address and port for Prometheus metrics. +# +# METRICS_ENDPOINT_ADDR="0.0.0.0" +# METRICS_ENDPOINT_PORT=9999 + +# Logging to a file is disabled by default. To enable it, uncomment the line +# below and alternatively set your own path. +# +# LOG_FILE="/home/zebra/.local/state/zebrad.log" + +# Zebra recognizes whether its logs are being written to a terminal or a file, +# and uses colored logs for terminals and uncolored logs for files. Setting the +# variable below to true will force colored logs even for files and setting it +# to false will disable colors even for terminals. +# +# LOG_COLOR=true + +# To disable logging to journald, set the value to false. +# +# USE_JOURNALD=true + +# Sets the listen address and port for the tracing endpoint. +# Only active when the 'filter-reload' feature is enabled. +# +# TRACING_ENDPOINT_ADDR="0.0.0.0" +# TRACING_ENDPOINT_PORT=3000 + +# If you are going to use Zebra as a backend for a mining pool, set your mining +# address. +# +# MINER_ADDRESS="your_mining_address" + +# Controls the output of `env_logger`: +# https://docs.rs/env_logger/latest/env_logger/ +# +# Must be set at build time. +# +# RUST_LOG=info diff --git a/docker/Dockerfile b/docker/Dockerfile index c441ce44e22..dad9346f228 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,76 +1,58 @@ # syntax=docker/dockerfile:1 -# check=skip=UndefinedVar +# check=skip=UndefinedVar,UserExist # We use gosu in the entrypoint instead of USER directive # If you want to include a file in the Docker image, add it to .dockerignore. # -# We are using 4 stages: -# - deps: install build dependencies and sets the needed variables -# - tests: builds tests binaries +# We use 4 (TODO: 5) stages: +# - deps: installs build dependencies and sets default values +# - tests: prepares a test image # - release: builds release binaries -# - runtime: runs the release binaries +# - runtime: prepares the release image +# - TODO: Add a `monitoring` stage # # We first set default values for build arguments used across the stages. # Each stage must define the build arguments (ARGs) it uses. -# -# Build zebrad with these features -# -# Keep these argument defaults in sync with GitHub vars.RUST_PROD_FEATURES and vars.RUST_TEST_FEATURES + +ARG RUST_VERSION=1.85.0 + +# Keep in sync with vars.RUST_PROD_FEATURES in GitHub # https://github.com/ZcashFoundation/zebra/settings/variables/actions ARG FEATURES="default-release-binaries" -ARG TEST_FEATURES="lightwalletd-grpc-tests zebra-checkpoints" -ARG EXPERIMENTAL_FEATURES="" -ARG APP_HOME="/opt/zebrad" -ARG RUST_VERSION=1.82.0 -# In this stage we download all system requirements to build the project -# -# It also captures all the build arguments to be used as environment variables. -# We set defaults for the arguments, in case the build does not include this information. +ARG UID=10001 +ARG GID=${UID} +ARG USER="zebra" +ARG HOME="/home/${USER}" +ARG CARGO_HOME="${HOME}/.cargo" + +# This stage prepares Zebra's build deps and captures build args as env vars. FROM rust:${RUST_VERSION}-bookworm AS deps SHELL ["/bin/bash", "-xo", "pipefail", "-c"] -# Set the default path for the zebrad binary -ARG APP_HOME -ENV APP_HOME=${APP_HOME} -WORKDIR ${APP_HOME} - -# Install zebra build deps and Dockerfile deps +# Install zebra build deps RUN apt-get -qq update && \ apt-get -qq install -y --no-install-recommends \ - llvm \ libclang-dev \ - clang \ - ca-certificates \ protobuf-compiler \ - rocksdb-tools \ && rm -rf /var/lib/apt/lists/* /tmp/* -# Build arguments and variables set for tracelog levels and debug information -# -# We set defaults to all variables. -ARG RUST_LOG -ENV RUST_LOG=${RUST_LOG:-info} +# Build arguments and variables +ARG CARGO_INCREMENTAL +ENV CARGO_INCREMENTAL=${CARGO_INCREMENTAL:-0} -ARG RUST_BACKTRACE -ENV RUST_BACKTRACE=${RUST_BACKTRACE:-1} +ARG CARGO_HOME +ENV CARGO_HOME=${CARGO_HOME} -ARG RUST_LIB_BACKTRACE -ENV RUST_LIB_BACKTRACE=${RUST_LIB_BACKTRACE:-1} - -ARG COLORBT_SHOW_HIDDEN -ENV COLORBT_SHOW_HIDDEN=${COLORBT_SHOW_HIDDEN:-1} +ARG FEATURES +ENV FEATURES=${FEATURES} -ARG SHORT_SHA -# If this is not set, it must be an empty string, so Zebra can try an alternative git commit source: +# If this is not set, it must be an empty string, so Zebra can try an +# alternative git commit source: # https://github.com/ZcashFoundation/zebra/blob/9ebd56092bcdfc1a09062e15a0574c94af37f389/zebrad/src/application.rs#L179-L182 +ARG SHORT_SHA ENV SHORT_SHA=${SHORT_SHA:-} -ENV CARGO_HOME="${APP_HOME}/.cargo/" - -# Copy the entrypoint script to be used on both images -COPY ./docker/entrypoint.sh /etc/zebrad/entrypoint.sh - -# In this stage we build tests (without running then) +# This stage builds tests without running them. # # We also download needed dependencies for tests to work, from other images. # An entrypoint.sh is only available in this step for easier test handling with variables. @@ -80,23 +62,33 @@ FROM deps AS tests ARG ZEBRA_SKIP_IPV6_TESTS ENV ZEBRA_SKIP_IPV6_TESTS=${ZEBRA_SKIP_IPV6_TESTS:-1} -# Use ENTRYPOINT_FEATURES to override the specific features used to run tests in entrypoint.sh, -# separately from the test and production image builds. -ARG FEATURES -ARG TEST_FEATURES -ARG EXPERIMENTAL_FEATURES -# TODO: add empty $EXPERIMENTAL_FEATURES when we can avoid adding an extra space to the end of the string -ARG ENTRYPOINT_FEATURES="${FEATURES} ${TEST_FEATURES}" +# This environment setup is almost identical to the `runtime` target so that the +# `tests` target differs minimally. In fact, a subset of this setup is used for +# the `runtime` target. +ARG UID +ENV UID=${UID} +ARG GID +ENV GID=${GID} +ARG USER +ENV USER=${USER} +ARG HOME +ENV HOME=${HOME} -# Build Zebra test binaries, but don't run them +RUN addgroup --quiet --gid ${GID} ${USER} && \ + adduser --quiet --gid ${GID} --uid ${UID} --home ${HOME} ${USER} --disabled-password --gecos "" + +# Set the working directory for the build. +WORKDIR ${HOME} +# Build Zebra test binaries, but don't run them +# # Leverage a cache mount to /usr/local/cargo/registry/ # for downloaded dependencies, a cache mount to /usr/local/cargo/git/db -# for git repository dependencies, and a cache mount to ${APP_HOME}/target/ for +# for git repository dependencies, and a cache mount to ${HOME}/target/ for # compiled dependencies which will speed up subsequent builds. # Leverage a bind mount to each crate directory to avoid having to copy the # source code into the container. Once built, copy the executable to an -# output directory before the cache mounted ${APP_HOME}/target/ is unmounted. +# output directory before the cache mounted ${HOME}/target/ is unmounted. RUN --mount=type=bind,source=zebrad,target=zebrad \ --mount=type=bind,source=zebra-chain,target=zebra-chain \ --mount=type=bind,source=zebra-network,target=zebra-network \ @@ -107,50 +99,53 @@ RUN --mount=type=bind,source=zebrad,target=zebrad \ --mount=type=bind,source=zebra-node-services,target=zebra-node-services \ --mount=type=bind,source=zebra-test,target=zebra-test \ --mount=type=bind,source=zebra-utils,target=zebra-utils \ - --mount=type=bind,source=zebra-scan,target=zebra-scan \ - --mount=type=bind,source=zebra-grpc,target=zebra-grpc \ --mount=type=bind,source=tower-batch-control,target=tower-batch-control \ --mount=type=bind,source=tower-fallback,target=tower-fallback \ --mount=type=bind,source=Cargo.toml,target=Cargo.toml \ --mount=type=bind,source=Cargo.lock,target=Cargo.lock \ - --mount=type=cache,target=${APP_HOME}/target/ \ + --mount=type=cache,target=${HOME}/target/ \ --mount=type=cache,target=/usr/local/cargo/git/db \ --mount=type=cache,target=/usr/local/cargo/registry/ \ -cargo test --locked --release --features "${ENTRYPOINT_FEATURES}" --workspace --no-run && \ -cp ${APP_HOME}/target/release/zebrad /usr/local/bin && \ -cp ${APP_HOME}/target/release/zebra-checkpoints /usr/local/bin + cargo test --locked --release --workspace --no-run \ + --features "${FEATURES} zebra-checkpoints" && \ + cp ${HOME}/target/release/zebrad /usr/local/bin && \ + cp ${HOME}/target/release/zebra-checkpoints /usr/local/bin # Copy the lightwalletd binary and source files to be able to run tests -COPY --from=electriccoinco/lightwalletd:latest /usr/local/bin/lightwalletd /usr/local/bin/ -COPY ./ ./ +COPY --from=electriccoinco/lightwalletd:v0.4.17 /usr/local/bin/lightwalletd /usr/local/bin/ + +# Copy the gosu binary to be able to run the entrypoint as non-root user +# and allow to change permissions for mounted cache directories +COPY --from=tianon/gosu:bookworm /gosu /usr/local/bin/ -# Entrypoint environment variables -ENV ENTRYPOINT_FEATURES=${ENTRYPOINT_FEATURES} -# We repeat the ARGs here, so they are available in the entrypoint.sh script for $RUN_ALL_EXPERIMENTAL_TESTS -ARG EXPERIMENTAL_FEATURES="journald prometheus filter-reload" -ENV ENTRYPOINT_FEATURES_EXPERIMENTAL="${ENTRYPOINT_FEATURES} ${EXPERIMENTAL_FEATURES}" +# As the build has already run with the root user, +# we need to set the correct permissions for the home and cargo home dirs owned by it. +RUN chown -R ${UID}:${GID} "${HOME}" && \ + chown -R ${UID}:${GID} "${CARGO_HOME}" -# By default, runs the entrypoint tests specified by the environmental variables (if any are set) -ENTRYPOINT [ "/etc/zebrad/entrypoint.sh" ] +COPY --chown=${UID}:${GID} ./ ${HOME} +COPY --chown=${UID}:${GID} ./docker/entrypoint.sh /usr/local/bin/entrypoint.sh -# In this stage we build a release (generate the zebrad binary) +ENTRYPOINT [ "entrypoint.sh", "test" ] +CMD [ "cargo", "test" ] + +# This stage builds the zebrad release binary. # -# This step also adds `cache mounts` as this stage is completely independent from the -# `test` stage. This step is a dependency for the `runtime` stage, which uses the resulting -# zebrad binary from this step. +# It also adds `cache mounts` as this stage is completely independent from the +# `test` stage. The resulting zebrad binary is used in the `runtime` stage. FROM deps AS release -ARG FEATURES +# Set the working directory for the build. +ARG HOME +WORKDIR ${HOME} RUN --mount=type=bind,source=tower-batch-control,target=tower-batch-control \ --mount=type=bind,source=tower-fallback,target=tower-fallback \ --mount=type=bind,source=zebra-chain,target=zebra-chain \ --mount=type=bind,source=zebra-consensus,target=zebra-consensus \ - --mount=type=bind,source=zebra-grpc,target=zebra-grpc \ --mount=type=bind,source=zebra-network,target=zebra-network \ --mount=type=bind,source=zebra-node-services,target=zebra-node-services \ --mount=type=bind,source=zebra-rpc,target=zebra-rpc \ - --mount=type=bind,source=zebra-scan,target=zebra-scan \ --mount=type=bind,source=zebra-script,target=zebra-script \ --mount=type=bind,source=zebra-state,target=zebra-state \ --mount=type=bind,source=zebra-test,target=zebra-test \ @@ -158,71 +153,67 @@ RUN --mount=type=bind,source=tower-batch-control,target=tower-batch-control \ --mount=type=bind,source=zebrad,target=zebrad \ --mount=type=bind,source=Cargo.toml,target=Cargo.toml \ --mount=type=bind,source=Cargo.lock,target=Cargo.lock \ - --mount=type=cache,target=${APP_HOME}/target/ \ + --mount=type=cache,target=${HOME}/target/ \ --mount=type=cache,target=/usr/local/cargo/git/db \ --mount=type=cache,target=/usr/local/cargo/registry/ \ -cargo build --locked --release --features "${FEATURES}" --package zebrad --bin zebrad && \ -cp ${APP_HOME}/target/release/zebrad /usr/local/bin + cargo build --locked --release --features "${FEATURES}" --package zebrad --bin zebrad && \ + cp ${HOME}/target/release/zebrad /usr/local/bin -# This stage is only used when deploying nodes or when only the resulting zebrad binary is needed -# -# To save space, this step starts from scratch using debian, and only adds the resulting -# binary from the `release` stage +# This stage starts from scratch using Debian and copies the built zebrad binary +# from the `release` stage along with other binaries and files. FROM debian:bookworm-slim AS runtime -# Set the default path for the zebrad binary -ARG APP_HOME -ENV APP_HOME=${APP_HOME} -WORKDIR ${APP_HOME} - -RUN apt-get update && \ - apt-get install -y --no-install-recommends \ - ca-certificates \ - curl \ - rocksdb-tools \ - gosu \ - && rm -rf /var/lib/apt/lists/* /tmp/* +ARG FEATURES +ENV FEATURES=${FEATURES} -# Create a non-privileged user that the app will run under. -# Running as root inside the container is running as root in the Docker host -# If an attacker manages to break out of the container, they will have root access to the host -# See https://docs.docker.com/go/dockerfile-user-best-practices/ -ARG USER=zebra -ENV USER=${USER} -ARG UID=10001 +# Create a non-privileged user for running `zebrad`. +# +# We use a high UID/GID (10001) to avoid overlap with host system users. +# This reduces the risk of container user namespace conflicts with host accounts, +# which could potentially lead to privilege escalation if a container escape occurs. +# +# We do not use the `--system` flag for user creation since: +# 1. System user ranges (100-999) can collide with host system users +# (see: https://github.com/nginxinc/docker-nginx/issues/490) +# 2. There's no value added and warning messages can be raised at build time +# (see: https://github.com/dotnet/dotnet-docker/issues/4624) +# +# The high UID/GID values provide an additional security boundary in containers +# where user namespaces are shared with the host. +ARG UID ENV UID=${UID} -ARG GID=10001 +ARG GID ENV GID=${GID} +ARG USER +ENV USER=${USER} +ARG HOME +ENV HOME=${HOME} -RUN addgroup --system --gid ${GID} ${USER} \ - && adduser \ - --system \ - --disabled-login \ - --shell /bin/bash \ - --home ${APP_HOME} \ - --uid "${UID}" \ - --gid "${GID}" \ - ${USER} - -# Config settings for zebrad -ARG FEATURES -ENV FEATURES=${FEATURES} - -# Path and name of the config file -# These are set to a default value when not defined in the environment -ENV ZEBRA_CONF_DIR=${ZEBRA_CONF_DIR:-/etc/zebrad} -ENV ZEBRA_CONF_FILE=${ZEBRA_CONF_FILE:-zebrad.toml} +RUN addgroup --quiet --gid ${GID} ${USER} && \ + adduser --quiet --gid ${GID} --uid ${UID} --home ${HOME} ${USER} --disabled-password --gecos "" -RUN mkdir -p ${ZEBRA_CONF_DIR} && chown ${UID}:${UID} ${ZEBRA_CONF_DIR} \ - && chown ${UID}:${UID} ${APP_HOME} +WORKDIR ${HOME} +RUN chown -R ${UID}:${GID} ${HOME} -COPY --from=release /usr/local/bin/zebrad /usr/local/bin -COPY --from=release /etc/zebrad/entrypoint.sh /etc/zebrad +# We're explicitly NOT using the USER directive here. +# Instead, we run as root initially and use gosu in the entrypoint.sh +# to step down to the non-privileged user. This allows us to change permissions +# on mounted volumes before running the application as a non-root user. +# User with UID=${UID} is created above and used via gosu in entrypoint.sh. -# Expose configured ports -EXPOSE 8233 18233 +# Copy the gosu binary to be able to run the entrypoint as non-root user +COPY --from=tianon/gosu:bookworm /gosu /usr/local/bin/ +COPY --from=release /usr/local/bin/zebrad /usr/local/bin/ +COPY --chown=${UID}:${GID} ./docker/entrypoint.sh /usr/local/bin/entrypoint.sh -# Update the config file based on the Docker run variables, -# and launch zebrad with it -ENTRYPOINT [ "/etc/zebrad/entrypoint.sh" ] +ENTRYPOINT [ "entrypoint.sh" ] CMD ["zebrad"] + +# TODO: Add a `monitoring` stage +# +# This stage will be based on `runtime`, and initially: +# +# - run `zebrad` on Testnet +# - with mining enabled using S-nomp and `nheqminer`. +# +# We can add further functionality to this stage for further purposes. diff --git a/docker/default-zebra-config.toml b/docker/default-zebra-config.toml new file mode 100644 index 00000000000..5b5e5b6ebd1 --- /dev/null +++ b/docker/default-zebra-config.toml @@ -0,0 +1,65 @@ +# Default configuration file for running Zebra in Docker. +# +# This file is tailored for Zebra running in Docker. Do not use it with Zebra +# running directly on your localhost as some fields are adjusted specifically +# for Docker. +# +# You can use this file as a starting point for custom configuration. If you +# don't specify a field, Zebra will use its default value. +# +# The config format, including a complete list of sections and fields, is +# documented here: +# https://docs.rs/zebrad/latest/zebrad/config/struct.ZebradConfig.html + +[network] +network = "Mainnet" +listen_addr = "0.0.0.0" +cache_dir = "/home/zebra/.cache/zebra" + +[rpc] +# The RPC server is disabled by default. To enable it, uncomment one of the +# lines below and alternatively set your own port. + +# listen_addr = "0.0.0.0:8232" # Mainnet +# listen_addr = "0.0.0.0:18232" # Testnet + +cookie_dir = "/home/zebra/.cache/zebra" + +# To disable cookie authentication, uncomment the line below and set the value +# to false. + +# enable_cookie_auth = true + +[state] +cache_dir = "/home/zebra/.cache/zebra" + +[tracing] +# Zebra recognizes whether its logs are being written to a terminal or a file, +# and uses colored logs for terminals and uncolored logs for files. To force +# colors even for files, uncomment the line below. To disable colors, set +# `use_color` to false. + +# force_use_color = true +use_color = true + +# Logging to a file is disabled by default. To enable it, uncomment the line +# below and alternatively set your own path. + +# log_file = "/home/zebra/.local/state/zebrad.log" + +# Sending tracing events to systemd-journald is disabled by default. To enable +# it, uncomment the line below. + +# use_journald = true + +[metrics] +# Metrics via Prometheus are disabled by default. To enable them, uncomment the +# line below and alternatively set your own port. + +# endpoint_addr = "0.0.0.0:9999" # Prometheus + +[mining] +# If you are going to use Zebra as a backend for a mining pool, set your mining +# address. + +# miner_address = "your_mining_address" diff --git a/docker/docker-compose.grafana.yml b/docker/docker-compose.grafana.yml new file mode 100644 index 00000000000..2c7b6b4d7ab --- /dev/null +++ b/docker/docker-compose.grafana.yml @@ -0,0 +1,52 @@ +services: + zebra: + container_name: zebra + build: + context: ../ + dockerfile: docker/Dockerfile + target: runtime + args: + - FEATURES=prometheus + volumes: + - zebrad-cache:/home/zebra/.cache/zebra + tty: true + environment: + - FEATURES=prometheus + network_mode: "host" + ports: + - 9999:9999 + + prometheus: + container_name: prometheus + image: prom/prometheus + volumes: + - prometheus-cache:/prometheus + configs: + - source: prometheus-config + target: /etc/prometheus/prometheus.yml + network_mode: "host" + ports: + - 9090:9090 + + grafana: + container_name: grafana + image: grafana/grafana + volumes: + - grafana-cache:/var/lib/grafana + network_mode: "host" + ports: + - 3000:3000 + +volumes: + zebrad-cache: + driver: local + + grafana-cache: + driver: local + + prometheus-cache: + driver: local + +configs: + prometheus-config: + file: ../prometheus.yaml diff --git a/docker/docker-compose.lwd.yml b/docker/docker-compose.lwd.yml index 7d8c56b1855..456e7602d97 100644 --- a/docker/docker-compose.lwd.yml +++ b/docker/docker-compose.lwd.yml @@ -1,15 +1,22 @@ -version: "3.8" - services: zebra: + container_name: zebra + image: zfnd/zebra + platform: linux/amd64 + restart: unless-stopped + deploy: + resources: + reservations: + cpus: "4" + memory: 16G + volumes: + - zebrad-cache:/home/zebra/.cache/zebra + tty: true + environment: + - ZEBRA_RPC_PORT=8232 + - ENABLE_COOKIE_AUTH=false ports: - - "8232:8232" # Opens an RPC endpoint (for lightwalletd and mining) - healthcheck: - start_period: 1m - interval: 15s - timeout: 10s - retries: 3 - test: ["CMD-SHELL", "curl --data-binary '{\"id\":\"curltest\", \"method\": \"getinfo\"}' -H 'content-type: application/json' 127.0.0.1:8232 || exit 1"] + - "8232:8232" lightwalletd: image: electriccoinco/lightwalletd @@ -29,13 +36,11 @@ services: configs: - source: lwd_config target: /etc/lightwalletd/zcash.conf - uid: '2002' # Golang's container default user uid - gid: '2002' # Golang's container default group gid - mode: 0440 volumes: - - litewalletd-data:/var/lib/lightwalletd/db - #! This setup with --no-tls-very-insecure is only for testing purposes - #! For production environments follow the guidelines here: https://github.com/zcash/lightwalletd#production-usage + - lwd-cache:/var/lib/lightwalletd/db + #! This setup with `--no-tls-very-insecure` is only for testing purposes. + #! For production environments, follow the guidelines here: + #! https://github.com/zcash/lightwalletd#production-usage command: > --no-tls-very-insecure --grpc-bind-addr=0.0.0.0:9067 @@ -50,10 +55,11 @@ services: configs: lwd_config: - # Change the following line to point to a zcash.conf on your host machine - # to allow for easy configuration changes without rebuilding the image - file: ./zcash-lightwalletd/zcash.conf + file: ./zcash.conf volumes: - litewalletd-data: + zebrad-cache: + driver: local + + lwd-cache: driver: local diff --git a/docker/docker-compose.test.yml b/docker/docker-compose.test.yml index fac94e3f4db..d3659612a82 100644 --- a/docker/docker-compose.test.yml +++ b/docker/docker-compose.test.yml @@ -1,30 +1,13 @@ -version: "3.8" - services: zebra: + container_name: zebra build: context: ../ dockerfile: docker/Dockerfile target: tests - restart: unless-stopped - deploy: - resources: - reservations: - cpus: "4" - memory: 16G - # Change this to the command you want to run, respecting the entrypoint.sh - # For example, to run the tests, use the following command: - # command: ["cargo", "test", "--locked", "--release", "--features", "${TEST_FEATURES}", "--package", "zebrad", "--test", "acceptance", "--", "--nocapture", "--include-ignored", "sync_large_checkpoints_"] volumes: - - zebrad-cache:/var/cache/zebrad-cache - - lwd-cache:/var/cache/lwd-cache - ports: - # Zebra uses the following inbound and outbound TCP ports - - "8232:8232" # Opens an RPC endpoint (for wallet storing and mining) - - "8233:8233" # Mainnet Network (for peer connections) - - "18233:18233" # Testnet Network - # - "9999:9999" # Metrics - # - "3000:3000" # Tracing + - zebrad-cache:/home/zebra/.cache/zebra + - lwd-cache:/home/zebra/.cache/lwd env_file: - test.env diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 22359488de1..b561312fe27 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -1,13 +1,8 @@ -version: "3.8" - services: zebra: + container_name: zebra image: zfnd/zebra platform: linux/amd64 - build: - context: ../ - dockerfile: docker/Dockerfile - target: runtime restart: unless-stopped deploy: resources: @@ -16,33 +11,32 @@ services: memory: 16G env_file: - .env - logging: - options: - max-size: "10m" - max-file: "5" - #! Uncomment the `configs` mapping below to use the `zebrad.toml` config file from the host machine - #! NOTE: This will override the zebrad.toml in the image and make some variables irrelevant - # configs: - # - source: zebra_config - # target: /etc/zebrad/zebrad.toml - # uid: '2001' # Rust's container default user uid - # gid: '2001' # Rust's container default group gid - # mode: 0440 volumes: - - zebrad-cache:/var/cache/zebrad-cache - ports: - # Zebra uses the following default inbound and outbound TCP ports - - "8233:8233" # Mainnet Network (for peer connections) - # - "8232:8232" # Opens an RPC endpoint (for wallet storing and mining) - # - "18233:18233" # Testnet Network - # - "9999:9999" # Metrics - # - "3000:3000" # Tracing + - zebrad-cache:/home/zebra/.cache/zebra + # Having `tty` set to true makes Zebra use colored logs. + tty: true + # ! Comment out the `configs` mapping below to use the environment variables in the + # ! `.env` file, instead of the default configuration file. + configs: + - source: zebra-config + target: /home/zebra/.config/zebrad.toml + + # Uncomment the `ports` mapping below to map ports between the container and + # host. + # + # ports: + # - "8232:8232" # RPC endpoint on Mainnet + # - "18232:18232" # RPC endpoint on Testnet + # - "8233:8233" # peer connections on Mainnet + # - "18233:18233" # peer connections on Testnet + # - "9999:9999" # Metrics + # - "3000:3000" # Tracing configs: - zebra_config: - # Change the following line to point to a zebrad.toml on your host machine - # to allow for easy configuration changes without rebuilding the image - file: ../zebrad/tests/common/configs/v1.0.0-rc.2.toml + zebra-config: + #! To customize the default configuration, edit this file before starting + #! the container. + file: ./default-zebra-config.toml volumes: zebrad-cache: diff --git a/docker/entrypoint.sh b/docker/entrypoint.sh index d71be57805d..3fbee7203c8 100755 --- a/docker/entrypoint.sh +++ b/docker/entrypoint.sh @@ -1,365 +1,344 @@ #!/usr/bin/env bash -# This script serves as the entrypoint for the Zebra Docker container. +# Entrypoint for running Zebra in Docker. # -# Description: -# This script serves as the primary entrypoint for the Docker container. Its main responsibilities include: -# 1. Environment Setup: Prepares the environment by setting various flags and parameters. -# 2. Configuration Management: Dynamically generates the `zebrad.toml` configuration file based on environment variables, ensuring the node starts with the desired settings. -# 3. Test Execution: Can run a series of tests to validate functionality based on specified environment variables. -# 4. Node Startup: Starts the node, allowing it to begin its operations. +# The main script logic is at the bottom. # +# ## Notes +# +# - `$ZEBRA_CONF_PATH` can point to an existing Zebra config file, or if not set, +# the script will look for a default config at ${HOME}/.config/zebrad.toml, +# or generate one using environment variables. -# Exit if a command fails -set -e -# Exit if any command in a pipeline fails -set -o pipefail - -#### -# General Variables -# These variables are used to run the Zebra node. -#### - -# Path and name of the config file. These two have defaults set in the Dockerfile. -: "${ZEBRA_CONF_DIR:=}" -: "${ZEBRA_CONF_FILE:=}" -# [network] -: "${NETWORK:=Mainnet}" -: "${ZEBRA_LISTEN_ADDR:=0.0.0.0}" -# [consensus] -: "${ZEBRA_CHECKPOINT_SYNC:=true}" -# [state] -# Set this to change the default cached state directory -: "${ZEBRA_CACHED_STATE_DIR:=/var/cache/zebrad-cache}" -: "${LIGHTWALLETD_DATA_DIR:=/var/cache/lwd-cache}" -# [metrics] -: "${METRICS_ENDPOINT_ADDR:=0.0.0.0}" -: "${METRICS_ENDPOINT_PORT:=9999}" -# [tracing] -: "${LOG_COLOR:=false}" -: "${TRACING_ENDPOINT_ADDR:=0.0.0.0}" -: "${TRACING_ENDPOINT_PORT:=3000}" -# [rpc] -: "${RPC_LISTEN_ADDR:=0.0.0.0}" -# if ${RPC_PORT} is not set, use the default value for the current network -if [[ -z "${RPC_PORT}" ]]; then - if [[ "${NETWORK}" = "Mainnet" ]]; then - : "${RPC_PORT:=8232}" - elif [[ "${NETWORK}" = "Testnet" ]]; then - : "${RPC_PORT:=18232}" - fi -fi +set -eo pipefail -#### -# Test Variables -# These variables are used to run tests in the Dockerfile. -#### - -: "${RUN_ALL_TESTS:=}" -: "${RUN_ALL_EXPERIMENTAL_TESTS:=}" -: "${TEST_FAKE_ACTIVATION_HEIGHTS:=}" -: "${TEST_ZEBRA_EMPTY_SYNC:=}" -: "${ZEBRA_TEST_LIGHTWALLETD:=}" -: "${FULL_SYNC_MAINNET_TIMEOUT_MINUTES:=}" -: "${FULL_SYNC_TESTNET_TIMEOUT_MINUTES:=}" -: "${TEST_DISK_REBUILD:=}" -: "${TEST_UPDATE_SYNC:=}" -: "${TEST_CHECKPOINT_SYNC:=}" -: "${GENERATE_CHECKPOINTS_MAINNET:=}" -: "${GENERATE_CHECKPOINTS_TESTNET:=}" -: "${TEST_LWD_RPC_CALL:=}" -: "${TEST_LWD_FULL_SYNC:=}" -: "${TEST_LWD_UPDATE_SYNC:=}" -: "${TEST_LWD_GRPC:=}" -: "${TEST_LWD_TRANSACTIONS:=}" -: "${TEST_GET_BLOCK_TEMPLATE:=}" -: "${TEST_SUBMIT_BLOCK:=}" -: "${TEST_SCAN_START_WHERE_LEFT:=}" -: "${ENTRYPOINT_FEATURES:=}" -: "${TEST_SCAN_TASK_COMMANDS:=}" - -# Configuration file path -if [[ -n "${ZEBRA_CONF_DIR}" ]] && [[ -n "${ZEBRA_CONF_FILE}" ]] && [[ -z "${ZEBRA_CONF_PATH}" ]]; then - ZEBRA_CONF_PATH="${ZEBRA_CONF_DIR}/${ZEBRA_CONF_FILE}" -fi +# These are the default cached state directories for Zebra and lightwalletd. +# +# They are set to `${HOME}/.cache/zebra` and `${HOME}/.cache/lwd` +# respectively, but can be overridden by setting the +# `ZEBRA_CACHE_DIR` and `LWD_CACHE_DIR` environment variables. +: "${ZEBRA_CACHE_DIR:=${HOME}/.cache/zebra}" +: "${LWD_CACHE_DIR:=${HOME}/.cache/lwd}" +: "${ZEBRA_COOKIE_DIR:=${HOME}/.cache/zebra}" + +# Use gosu to drop privileges and execute the given command as the specified UID:GID +exec_as_user() { + user=$(id -u) + if [[ ${user} == '0' ]]; then + exec gosu "${UID}:${GID}" "$@" + else + exec "$@" + fi +} -# Populate `zebrad.toml` before starting zebrad, using the environmental -# variables set by the Dockerfile or the user. If the user has already created a config, don't replace it. +# Modifies the Zebra config file using environment variables. # -# We disable most ports by default, so the default config is secure. -# Users have to opt-in to additional functionality by setting environmental variables. -if [[ -n "${ZEBRA_CONF_PATH}" ]] && [[ ! -f "${ZEBRA_CONF_PATH}" ]] && [[ -z "${ENTRYPOINT_FEATURES}" ]]; then - # Create the conf path and file - (mkdir -p "$(dirname "${ZEBRA_CONF_PATH}")" && touch "${ZEBRA_CONF_PATH}") || { echo "Error creating file ${ZEBRA_CONF_PATH}"; exit 1; } - # Populate the conf file - cat < "${ZEBRA_CONF_PATH}" +# This function generates a new config file from scratch at ZEBRA_CONF_PATH +# using the provided environment variables. +# +# It creates a complete configuration with network settings, state, RPC, +# metrics, tracing, and mining sections based on environment variables. +prepare_conf_file() { + # Base configuration + cat >"${ZEBRA_CONF_PATH}" <> "${ZEBRA_CONF_PATH}" -[metrics] -endpoint_addr = "${METRICS_ENDPOINT_ADDR}:${METRICS_ENDPOINT_PORT}" -EOF - fi +$( [[ -n ${ZEBRA_RPC_PORT} ]] && cat <<-SUB_EOF - if [[ -n "${RPC_PORT}" ]]; then - cat <> "${ZEBRA_CONF_PATH}" [rpc] -listen_addr = "${RPC_LISTEN_ADDR}:${RPC_PORT}" -EOF - fi +listen_addr = "${RPC_LISTEN_ADDR:=0.0.0.0}:${ZEBRA_RPC_PORT}" +enable_cookie_auth = ${ENABLE_COOKIE_AUTH:=true} +$( [[ -n ${ZEBRA_COOKIE_DIR} ]] && echo "cookie_dir = \"${ZEBRA_COOKIE_DIR}\"" ) +SUB_EOF +) + +$( ( ! [[ " ${FEATURES} " =~ " prometheus " ]] ) && cat <<-SUB_EOF + +[metrics] +# endpoint_addr = "${METRICS_ENDPOINT_ADDR:=0.0.0.0}:${METRICS_ENDPOINT_PORT:=9999}" +SUB_EOF +) + +$( [[ " ${FEATURES} " =~ " prometheus " ]] && cat <<-SUB_EOF + +[metrics] +endpoint_addr = "${METRICS_ENDPOINT_ADDR:=0.0.0.0}:${METRICS_ENDPOINT_PORT:=9999}" +SUB_EOF +) + +$( [[ -n ${LOG_FILE} || -n ${LOG_COLOR} || -n ${TRACING_ENDPOINT_ADDR} || -n ${USE_JOURNALD} ]] && cat <<-SUB_EOF - if [[ -n "${LOG_FILE}" ]] || [[ -n "${LOG_COLOR}" ]] || [[ -n "${TRACING_ENDPOINT_ADDR}" ]]; then - cat <> "${ZEBRA_CONF_PATH}" [tracing] -EOF - if [[ " ${FEATURES} " =~ " filter-reload " ]]; then # spaces are important here to avoid partial matches - cat <> "${ZEBRA_CONF_PATH}" -endpoint_addr = "${TRACING_ENDPOINT_ADDR}:${TRACING_ENDPOINT_PORT}" -EOF - fi - # Set this to log to a file, if not set, logs to standard output - if [[ -n "${LOG_FILE}" ]]; then - mkdir -p "$(dirname "${LOG_FILE}")" - cat <> "${ZEBRA_CONF_PATH}" -log_file = "${LOG_FILE}" -EOF - fi - # Zebra automatically detects if it is attached to a terminal, and uses colored output. - # Set this to 'true' to force using color even if the output is not a terminal. - # Set this to 'false' to disable using color even if the output is a terminal. - if [[ "${LOG_COLOR}" = "true" ]]; then - cat <> "${ZEBRA_CONF_PATH}" -force_use_color = true -EOF - elif [[ "${LOG_COLOR}" = "false" ]]; then - cat <> "${ZEBRA_CONF_PATH}" -use_color = false -EOF - fi - fi +$( [[ -n ${USE_JOURNALD} ]] && echo "use_journald = ${USE_JOURNALD}" ) +$( [[ " ${FEATURES} " =~ " filter-reload " ]] && echo "endpoint_addr = \"${TRACING_ENDPOINT_ADDR:=0.0.0.0}:${TRACING_ENDPOINT_PORT:=3000}\"" ) +$( [[ -n ${LOG_FILE} ]] && echo "log_file = \"${LOG_FILE}\"" ) +$( [[ ${LOG_COLOR} == "true" ]] && echo "force_use_color = true" ) +$( [[ ${LOG_COLOR} == "false" ]] && echo "use_color = false" ) +SUB_EOF +) + +$( [[ -n ${MINER_ADDRESS} ]] && cat <<-SUB_EOF - if [[ -n "${MINER_ADDRESS}" ]]; then - cat <> "${ZEBRA_CONF_PATH}" [mining] miner_address = "${MINER_ADDRESS}" +SUB_EOF +) EOF - fi -fi -if [[ -n "${ZEBRA_CONF_PATH}" ]] && [[ -z "${ENTRYPOINT_FEATURES}" ]]; then - # Print the config file - echo "Using zebrad.toml:" - cat "${ZEBRA_CONF_PATH}" -fi +# Ensure the config file itself has the correct ownership +# +# This is safe in this context because prepare_conf_file is called only when +# ZEBRA_CONF_PATH is not set, and there's no file mounted at that path. +chown "${UID}:${GID}" "${ZEBRA_CONF_PATH}" || exit_error "Failed to secure config file: ${ZEBRA_CONF_PATH}" + +} -# Function to list directory -check_directory_files() { +# Helper function +exit_error() { + echo "$1" >&2 + exit 1 +} + +# Creates a directory if it doesn't exist and sets ownership to specified UID:GID. +# Also ensures the parent directories have the correct ownership. +# +# ## Parameters +# +# - $1: Directory path to create and own +create_owned_directory() { local dir="$1" - # Check if the directory exists - if [[ -d "${dir}" ]]; then - # Check if there are any subdirectories - if find "${dir}" -mindepth 1 -type d | read -r; then - # Subdirectories exist, so we continue - : - else - # No subdirectories, print message and exit with status 1 - echo "No subdirectories found in ${dir}." - exit 1 - fi - else - # Directory doesn't exist, print message and exit with status 1 - echo "Directory ${dir} does not exist." - exit 1 + # Skip if directory is empty + [[ -z ${dir} ]] && return + + # Create directory with parents + mkdir -p "${dir}" || exit_error "Failed to create directory: ${dir}" + + # Set ownership for the created directory + chown -R "${UID}:${GID}" "${dir}" || exit_error "Failed to secure directory: ${dir}" + + # Set ownership for parent directory (but not if it's root or home) + local parent_dir + parent_dir="$(dirname "${dir}")" + if [[ "${parent_dir}" != "/" && "${parent_dir}" != "${HOME}" ]]; then + chown "${UID}:${GID}" "${parent_dir}" fi } -# Function to run cargo test with an arbitrary number of arguments -run_cargo_test() { - # Start constructing the command, ensuring that $1 is enclosed in single quotes as it's a feature list - local cmd="exec cargo test --locked --release --features '$1' --package zebrad --test acceptance -- --nocapture --include-ignored" +# Create and own cache and config directories +[[ -n ${ZEBRA_CACHE_DIR} ]] && create_owned_directory "${ZEBRA_CACHE_DIR}" +[[ -n ${LWD_CACHE_DIR} ]] && create_owned_directory "${LWD_CACHE_DIR}" +[[ -n ${ZEBRA_COOKIE_DIR} ]] && create_owned_directory "${ZEBRA_COOKIE_DIR}" +[[ -n ${LOG_FILE} ]] && create_owned_directory "$(dirname "${LOG_FILE}")" +# Runs cargo test with an arbitrary number of arguments. +# +# Positional Parameters +# +# - '$1' must contain cargo FEATURES as described here: +# https://doc.rust-lang.org/cargo/reference/features.html#command-line-feature-options +# - The remaining params will be appended to a command starting with +# `exec_as_user cargo test ... -- ...` +run_cargo_test() { # Shift the first argument, as it's already included in the cmd + local features="$1" shift + # Start constructing the command array + local cmd=(cargo test --locked --release --features "${features}" --package zebrad --test acceptance -- --nocapture --include-ignored) + # Loop through the remaining arguments for arg in "$@"; do if [[ -n ${arg} ]]; then # If the argument is non-empty, add it to the command - cmd+=" ${arg}" + cmd+=("${arg}") fi done - # Run the command using eval, this will replace the current process with the cargo command - eval "${cmd}" || { echo "Cargo test failed"; exit 1; } + echo "Running: ${cmd[*]}" + # Execute directly to become PID 1 + exec_as_user "${cmd[@]}" +} + +# Runs tests depending on the env vars. +# +# ## Positional Parameters +# +# - $@: Arbitrary command that will be executed if no test env var is set. +run_tests() { + if [[ "${RUN_ALL_TESTS}" -eq "1" ]]; then + # Run unit, basic acceptance tests, and ignored tests, only showing command + # output if the test fails. If the lightwalletd environment variables are + # set, we will also run those tests. + exec_as_user cargo test --locked --release --workspace --features "${FEATURES}" \ + -- --nocapture --include-ignored --skip check_no_git_dependencies + + elif [[ "${CHECK_NO_GIT_DEPENDENCIES}" -eq "1" ]]; then + # Run the check_no_git_dependencies test. + exec_as_user cargo test --locked --release --workspace --features "${FEATURES}" \ + -- --nocapture --include-ignored check_no_git_dependencies + + elif [[ "${STATE_FAKE_ACTIVATION_HEIGHTS}" -eq "1" ]]; then + # Run state tests with fake activation heights. + exec_as_user cargo test --locked --release --lib --features "zebra-test" \ + --package zebra-state \ + -- --nocapture --include-ignored with_fake_activation_heights + + elif [[ "${SYNC_LARGE_CHECKPOINTS_EMPTY}" -eq "1" ]]; then + # Test that Zebra syncs and checkpoints a few thousand blocks from an empty + # state. + run_cargo_test "${FEATURES}" "sync_large_checkpoints_" + + elif [[ -n "${SYNC_FULL_MAINNET_TIMEOUT_MINUTES}" ]]; then + # Run a Zebra full sync test on mainnet. + run_cargo_test "${FEATURES}" "sync_full_mainnet" + + elif [[ -n "${SYNC_FULL_TESTNET_TIMEOUT_MINUTES}" ]]; then + # Run a Zebra full sync test on testnet. + run_cargo_test "${FEATURES}" "sync_full_testnet" + + elif [[ "${SYNC_TO_MANDATORY_CHECKPOINT}" -eq "1" ]]; then + # Run a Zebra sync up to the mandatory checkpoint. + run_cargo_test "${FEATURES} sync_to_mandatory_checkpoint_${NETWORK,,}" \ + "sync_to_mandatory_checkpoint_${NETWORK,,}" + echo "ran test_disk_rebuild" + + elif [[ "${SYNC_UPDATE_MAINNET}" -eq "1" ]]; then + # Run a Zebra sync starting at the cached tip, and syncing to the latest + # tip. + run_cargo_test "${FEATURES}" "sync_update_mainnet" + + elif [[ "${SYNC_PAST_MANDATORY_CHECKPOINT}" -eq "1" ]]; then + # Run a Zebra sync starting at the cached mandatory checkpoint, and syncing + # past it. + run_cargo_test "${FEATURES} sync_past_mandatory_checkpoint_${NETWORK,,}" \ + "sync_past_mandatory_checkpoint_${NETWORK,,}" + + elif [[ "${GENERATE_CHECKPOINTS_MAINNET}" -eq "1" ]]; then + # Generate checkpoints after syncing Zebra from a cached state on mainnet. + # + # TODO: disable or filter out logs like: + # test generate_checkpoints_mainnet has been running for over 60 seconds + run_cargo_test "${FEATURES}" "generate_checkpoints_mainnet" + + elif [[ "${GENERATE_CHECKPOINTS_TESTNET}" -eq "1" ]]; then + # Generate checkpoints after syncing Zebra on testnet. + # + # This test might fail if testnet is unstable. + run_cargo_test "${FEATURES}" "generate_checkpoints_testnet" + + elif [[ "${LWD_RPC_TEST}" -eq "1" ]]; then + # Starting at a cached Zebra tip, test a JSON-RPC call to Zebra. + # Run both the fully synced RPC test and the subtree snapshot test, one test + # at a time. Since these tests use the same cached state, a state problem in + # the first test can fail the second test. + run_cargo_test "${FEATURES}" "--test-threads" "1" "lwd_rpc_test" + + elif [[ "${LIGHTWALLETD_INTEGRATION}" -eq "1" ]]; then + # Test launching lightwalletd with an empty lightwalletd and Zebra state. + run_cargo_test "${FEATURES}" "lwd_integration" + + elif [[ "${LWD_SYNC_FULL}" -eq "1" ]]; then + # Starting at a cached Zebra tip, run a lightwalletd sync to tip. + run_cargo_test "${FEATURES}" "lwd_sync_full" + + elif [[ "${LWD_SYNC_UPDATE}" -eq "1" ]]; then + # Starting with a cached Zebra and lightwalletd tip, run a quick update sync. + run_cargo_test "${FEATURES}" "lwd_sync_update" + + # These tests actually use gRPC. + elif [[ "${LWD_GRPC_WALLET}" -eq "1" ]]; then + # Starting with a cached Zebra and lightwalletd tip, test all gRPC calls to + # lightwalletd, which calls Zebra. + run_cargo_test "${FEATURES}" "lwd_grpc_wallet" + + elif [[ "${LWD_RPC_SEND_TX}" -eq "1" ]]; then + # Starting with a cached Zebra and lightwalletd tip, test sending + # transactions gRPC call to lightwalletd, which calls Zebra. + run_cargo_test "${FEATURES}" "lwd_rpc_send_tx" + + # These tests use mining code, but don't use gRPC. + elif [[ "${RPC_GET_BLOCK_TEMPLATE}" -eq "1" ]]; then + # Starting with a cached Zebra tip, test getting a block template from + # Zebra's RPC server. + run_cargo_test "${FEATURES}" "rpc_get_block_template" + + elif [[ "${RPC_SUBMIT_BLOCK}" -eq "1" ]]; then + # Starting with a cached Zebra tip, test sending a block to Zebra's RPC + # port. + run_cargo_test "${FEATURES}" "rpc_submit_block" + + else + exec_as_user "$@" + fi } -# Main Execution Logic: -# This script orchestrates the execution flow based on the provided arguments and environment variables. -# - If "$1" is '--', '-', or 'zebrad', the script processes the subsequent arguments for the 'zebrad' command. -# - If ENTRYPOINT_FEATURES is unset, it checks for ZEBRA_CONF_PATH. If set, 'zebrad' runs with this custom configuration; otherwise, it runs with the provided arguments. -# - If "$1" is an empty string and ENTRYPOINT_FEATURES is set, the script enters the testing phase, checking various environment variables to determine the specific tests to run. -# - Different tests or operations are triggered based on the respective conditions being met. -# - If "$1" doesn't match any of the above, it's assumed to be a command, which is executed directly. -# This structure ensures a flexible execution strategy, accommodating various scenarios such as custom configurations, different testing phases, or direct command execution. +# Main Script Logic +# +# 1. First check if ZEBRA_CONF_PATH is explicitly set or if a file exists at that path +# 2. If not set but default config exists, use that +# 3. If neither exists, generate a default config at ${HOME}/.config/zebrad.toml +# 4. Print environment variables and config for debugging +# 5. Process command-line arguments and execute appropriate action +if [[ -n ${ZEBRA_CONF_PATH} ]]; then + if [[ -f ${ZEBRA_CONF_PATH} ]]; then + echo "ZEBRA_CONF_PATH was set to ${ZEBRA_CONF_PATH} and a file exists." + echo "Using user-provided config file" + else + echo "ERROR: ZEBRA_CONF_PATH was set and no config file found at ${ZEBRA_CONF_PATH}." + echo "Please ensure a config file exists or set ZEBRA_CONF_PATH to point to your config file." + exit 1 + fi +else + if [[ -f "${HOME}/.config/zebrad.toml" ]]; then + echo "ZEBRA_CONF_PATH was not set." + echo "Using default config at ${HOME}/.config/zebrad.toml" + ZEBRA_CONF_PATH="${HOME}/.config/zebrad.toml" + else + echo "ZEBRA_CONF_PATH was not set and no default config found at ${HOME}/.config/zebrad.toml" + echo "Preparing a default one..." + ZEBRA_CONF_PATH="${HOME}/.config/zebrad.toml" + create_owned_directory "$(dirname "${ZEBRA_CONF_PATH}")" + prepare_conf_file + fi +fi +echo "INFO: Using the following environment variables:" +printenv + +echo "Using Zebra config at ${ZEBRA_CONF_PATH}:" +cat "${ZEBRA_CONF_PATH}" + +# - If "$1" is "--", "-", or "zebrad", run `zebrad` with the remaining params. +# - If "$1" is "test": +# - and "$2" is "zebrad", run `zebrad` with the remaining params, +# - else run tests with the remaining params. +# - TODO: If "$1" is "monitoring", start a monitoring node. +# - If "$1" doesn't match any of the above, run "$@" directly. case "$1" in - --* | -* | zebrad) +--* | -* | zebrad) + shift + exec_as_user zebrad --config "${ZEBRA_CONF_PATH}" "$@" + ;; +test) + shift + if [[ "$1" == "zebrad" ]]; then shift - if [[ -n "${ZEBRA_CONF_PATH}" ]]; then - exec zebrad -c "${ZEBRA_CONF_PATH}" "$@" || { echo "Execution with custom configuration failed"; exit 1; } - else - exec zebrad "$@" || { echo "Execution failed"; exit 1; } - fi - ;; - "") - if [[ -n "${ENTRYPOINT_FEATURES}" ]]; then - # Validate the test variables - # For these tests, we activate the test features to avoid recompiling `zebrad`, - # but we don't actually run any gRPC tests. - if [[ "${RUN_ALL_TESTS}" -eq "1" ]]; then - # Run unit, basic acceptance tests, and ignored tests, only showing command output if the test fails. - # If the lightwalletd environmental variables are set, we will also run those tests. - exec cargo test --locked --release --features "${ENTRYPOINT_FEATURES}" --workspace -- --nocapture --include-ignored - - elif [[ "${RUN_ALL_EXPERIMENTAL_TESTS}" -eq "1" ]]; then - # Run unit, basic acceptance tests, and ignored tests with experimental features. - # If the lightwalletd environmental variables are set, we will also run those tests. - exec cargo test --locked --release --features "${ENTRYPOINT_FEATURES_EXPERIMENTAL}" --workspace -- --nocapture --include-ignored - - elif [[ "${TEST_FAKE_ACTIVATION_HEIGHTS}" -eq "1" ]]; then - # Run state tests with fake activation heights. - exec cargo test --locked --release --features "zebra-test" --package zebra-state --lib -- --nocapture --include-ignored with_fake_activation_heights - - elif [[ "${TEST_ZEBRA_EMPTY_SYNC}" -eq "1" ]]; then - # Test that Zebra syncs and checkpoints a few thousand blocks from an empty state. - run_cargo_test "${ENTRYPOINT_FEATURES}" "sync_large_checkpoints_" - - elif [[ "${ZEBRA_TEST_LIGHTWALLETD}" -eq "1" ]]; then - # Test launching lightwalletd with an empty lightwalletd and Zebra state. - run_cargo_test "${ENTRYPOINT_FEATURES}" "lightwalletd_integration" - - elif [[ -n "${FULL_SYNC_MAINNET_TIMEOUT_MINUTES}" ]]; then - # Run a Zebra full sync test on mainnet. - run_cargo_test "${ENTRYPOINT_FEATURES}" "full_sync_mainnet" - # List directory generated by test - check_directory_files "${ZEBRA_CACHED_STATE_DIR}" - - elif [[ -n "${FULL_SYNC_TESTNET_TIMEOUT_MINUTES}" ]]; then - # Run a Zebra full sync test on testnet. - run_cargo_test "${ENTRYPOINT_FEATURES}" "full_sync_testnet" - # List directory generated by test - check_directory_files "${ZEBRA_CACHED_STATE_DIR}" - - elif [[ "${TEST_DISK_REBUILD}" -eq "1" ]]; then - # Run a Zebra sync up to the mandatory checkpoint. - # - # TODO: use environmental variables instead of Rust features (part of #2995) - run_cargo_test "test_sync_to_mandatory_checkpoint_${NETWORK,,},${ENTRYPOINT_FEATURES}" "sync_to_mandatory_checkpoint_${NETWORK,,}" - check_directory_files "${ZEBRA_CACHED_STATE_DIR}" - - elif [[ "${TEST_UPDATE_SYNC}" -eq "1" ]]; then - # Run a Zebra sync starting at the cached tip, and syncing to the latest tip. - # - # List directory used by test - check_directory_files "${ZEBRA_CACHED_STATE_DIR}" - run_cargo_test "${ENTRYPOINT_FEATURES}" "zebrad_update_sync" - - elif [[ "${TEST_CHECKPOINT_SYNC}" -eq "1" ]]; then - # Run a Zebra sync starting at the cached mandatory checkpoint, and syncing past it. - # - # List directory used by test - check_directory_files "${ZEBRA_CACHED_STATE_DIR}" - # TODO: use environmental variables instead of Rust features (part of #2995) - run_cargo_test "test_sync_past_mandatory_checkpoint_${NETWORK,,},${ENTRYPOINT_FEATURES}" "sync_past_mandatory_checkpoint_${NETWORK,,}" - - elif [[ "${GENERATE_CHECKPOINTS_MAINNET}" -eq "1" ]]; then - # Generate checkpoints after syncing Zebra from a cached state on mainnet. - # - # TODO: disable or filter out logs like: - # test generate_checkpoints_mainnet has been running for over 60 seconds - # - # List directory used by test - check_directory_files "${ZEBRA_CACHED_STATE_DIR}" - run_cargo_test "${ENTRYPOINT_FEATURES}" "generate_checkpoints_mainnet" - - elif [[ "${GENERATE_CHECKPOINTS_TESTNET}" -eq "1" ]]; then - # Generate checkpoints after syncing Zebra on testnet. - # - # This test might fail if testnet is unstable. - # - # List directory used by test - check_directory_files "${ZEBRA_CACHED_STATE_DIR}" - run_cargo_test "${ENTRYPOINT_FEATURES}" "generate_checkpoints_testnet" - - elif [[ "${TEST_LWD_RPC_CALL}" -eq "1" ]]; then - # Starting at a cached Zebra tip, test a JSON-RPC call to Zebra. - check_directory_files "${ZEBRA_CACHED_STATE_DIR}" - # Run both the fully synced RPC test and the subtree snapshot test, one test at a time. - # Since these tests use the same cached state, a state problem in the first test can fail the second test. - run_cargo_test "${ENTRYPOINT_FEATURES}" "--test-threads" "1" "fully_synced_rpc_" - - elif [[ "${TEST_LWD_FULL_SYNC}" -eq "1" ]]; then - # Starting at a cached Zebra tip, run a lightwalletd sync to tip. - check_directory_files "${ZEBRA_CACHED_STATE_DIR}" - run_cargo_test "${ENTRYPOINT_FEATURES}" "lightwalletd_full_sync" - check_directory_files "${LIGHTWALLETD_DATA_DIR}/db" - - elif [[ "${TEST_LWD_UPDATE_SYNC}" -eq "1" ]]; then - # Starting with a cached Zebra and lightwalletd tip, run a quick update sync. - check_directory_files "${ZEBRA_CACHED_STATE_DIR}" - check_directory_files "${LIGHTWALLETD_DATA_DIR}/db" - run_cargo_test "${ENTRYPOINT_FEATURES}" "lightwalletd_update_sync" - - # These tests actually use gRPC. - elif [[ "${TEST_LWD_GRPC}" -eq "1" ]]; then - # Starting with a cached Zebra and lightwalletd tip, test all gRPC calls to lightwalletd, which calls Zebra. - check_directory_files "${ZEBRA_CACHED_STATE_DIR}" - check_directory_files "${LIGHTWALLETD_DATA_DIR}/db" - run_cargo_test "${ENTRYPOINT_FEATURES}" "lightwalletd_wallet_grpc_tests" - - elif [[ "${TEST_LWD_TRANSACTIONS}" -eq "1" ]]; then - # Starting with a cached Zebra and lightwalletd tip, test sending transactions gRPC call to lightwalletd, which calls Zebra. - check_directory_files "${ZEBRA_CACHED_STATE_DIR}" - check_directory_files "${LIGHTWALLETD_DATA_DIR}/db" - run_cargo_test "${ENTRYPOINT_FEATURES}" "sending_transactions_using_lightwalletd" - - # These tests use mining code, but don't use gRPC. - elif [[ "${TEST_GET_BLOCK_TEMPLATE}" -eq "1" ]]; then - # Starting with a cached Zebra tip, test getting a block template from Zebra's RPC server. - check_directory_files "${ZEBRA_CACHED_STATE_DIR}" - run_cargo_test "${ENTRYPOINT_FEATURES}" "get_block_template" - - elif [[ "${TEST_SUBMIT_BLOCK}" -eq "1" ]]; then - # Starting with a cached Zebra tip, test sending a block to Zebra's RPC port. - check_directory_files "${ZEBRA_CACHED_STATE_DIR}" - run_cargo_test "${ENTRYPOINT_FEATURES}" "submit_block" - - elif [[ "${TEST_SCAN_START_WHERE_LEFT}" -eq "1" ]]; then - # Test that the scanner can continue scanning where it was left when zebra-scanner restarts. - check_directory_files "${ZEBRA_CACHED_STATE_DIR}" - exec cargo test --locked --release --features "zebra-test" --package zebra-scan -- --nocapture --include-ignored scan_start_where_left - - elif [[ "${TEST_SCAN_TASK_COMMANDS}" -eq "1" ]]; then - # Test that the scan task commands are working. - check_directory_files "${ZEBRA_CACHED_STATE_DIR}" - exec cargo test --locked --release --features "zebra-test" --package zebra-scan -- --nocapture --include-ignored scan_task_commands - - else - exec "$@" - fi - fi - ;; - *) - if command -v gosu >/dev/null 2>&1; then - exec gosu "$USER" "$@" - else - exec "$@" - fi - ;; + exec_as_user zebrad --config "${ZEBRA_CONF_PATH}" "$@" + else + run_tests "$@" + fi + ;; +monitoring) + # TODO: Impl logic for starting a monitoring node. + : + ;; +*) + exec_as_user "$@" + ;; esac diff --git a/docker/test.env b/docker/test.env index fd2a7c876b7..7aa4a6dfe77 100644 --- a/docker/test.env +++ b/docker/test.env @@ -1,60 +1,94 @@ -### -# Configuration Variables -# These variables are used to configure the zebra node -# Check the entrypoint.sh script for more details -### - -# Set this to change the default log level (must be set at build time) -RUST_LOG=info -# This variable forces the use of color in the logs -ZEBRA_FORCE_USE_COLOR=1 -LOG_COLOR=true -# Path to the config file. This variable has a default set in entrypoint.sh -# ZEBRA_CONF_PATH=/etc/zebrad/zebrad.toml -# [network] -NETWORK=Mainnet -# [state] -# Set this to change the default cached state directory -ZEBRA_CACHED_STATE_DIR=/var/cache/zebrad-cache -LIGHTWALLETD_DATA_DIR=/var/cache/lwd-cache -# [tracing] -LOG_COLOR=false -TRACING_ENDPOINT_ADDR=0.0.0.0 -TRACING_ENDPOINT_PORT=3000 - -#### -# Test Variables -# These variables are used to run tests in the Dockerfile -# Check the entrypoint.sh script for more details -#### +# Configuration variables for running Zebra in Docker + +# Sets the network Zebra runs will run on. +# +# NETWORK=Mainnet + +# Zebra's RPC server is disabled by default. To enable it, set its port number. +# +# ZEBRA_RPC_PORT=8232 # Default RPC port number on Mainnet. +# ZEBRA_RPC_PORT=18323 # Default RPC port number on Testnet. + +# To disable cookie authentication, set the value below to false. +# +# ENABLE_COOKIE_AUTH=true + +# Sets a custom directory for the state and network caches. Zebra will also +# store its cookie authentication file in this directory. +# +# ZEBRA_CACHE_DIR="/home/zebra/.cache/zebra" + +# Sets custom Cargo features. Available features are listed at +# . +# +# Must be set at build time. +# +# FEATURES="" + +# Logging to a file is disabled by default. To enable it, uncomment the line +# below and alternatively set your own path. +# +# LOG_FILE="/home/zebra/.local/state/zebrad.log" + +# Zebra recognizes whether its logs are being written to a terminal or a file, +# and uses colored logs for terminals and uncolored logs for files. Setting the +# variable below to true will force colored logs even for files and setting it +# to false will disable colors even for terminals. +# +# LOG_COLOR=true + +# To disable logging to journald, set the value to false. +# +# USE_JOURNALD=true + +# If you are going to use Zebra as a backend for a mining pool, set your mining +# address. +# +# MINER_ADDRESS="your_mining_address" + +# Controls the output of `env_logger`: +# https://docs.rs/env_logger/latest/env_logger/ +# +# Must be set at build time. +# +# RUST_LOG=info # Unit tests -# TODO: These variables are evaluated to any value, even setting a NULL value will evaluate to true + +# TODO: These variables are evaluated to any value, even setting a NULL value +# will evaluate to true. +# # TEST_FAKE_ACTIVATION_HEIGHTS= -# ZEBRA_SKIP_NETWORK_TESTS -# ZEBRA_SKIP_IPV6_TESTS +# ZEBRA_SKIP_NETWORK_TESTS= +# ZEBRA_SKIP_IPV6_TESTS= RUN_ALL_TESTS= -RUN_ALL_EXPERIMENTAL_TESTS= -TEST_ZEBRA_EMPTY_SYNC= +SYNC_LARGE_CHECKPOINTS_EMPTY= ZEBRA_TEST_LIGHTWALLETD= + # Integration Tests -# Most of these tests require a cached state directory to save the network state -TEST_DISK_REBUILD= -# These tests needs a Zebra cached state -TEST_CHECKPOINT_SYNC= + +# Most of these tests require a cached state directory to save the network state. +SYNC_TO_CHECKPOINT= +SYNC_PAST_CHECKPOINT= GENERATE_CHECKPOINTS_MAINNET= GENERATE_CHECKPOINTS_TESTNET= -TEST_UPDATE_SYNC= -# These tests need a Lightwalletd binary + a Zebra cached state -TEST_LWD_RPC_CALL= -TEST_GET_BLOCK_TEMPLATE= -TEST_SUBMIT_BLOCK= -# These tests need a Lightwalletd binary + Lightwalletd cached state + a Zebra cached state -TEST_LWD_UPDATE_SYNC= -TEST_LWD_GRPC= -TEST_LWD_TRANSACTIONS= +SYNC_UPDATE= +TEST_SCANNER= + +# These tests need a Lightwalletd binary + a Zebra cached state. +RPC_FULLY_SYNCED_TEST= +RPC_GET_BLOCK_TEMPLATE= +RPC_SUBMIT_BLOCK= + +# These tests need a Lightwalletd binary + Lightwalletd cached state + a Zebra +# cached state. +LIGHTWALLETD_SYNC_UPDATE= +LIGHTWALLETD_GRPC_WALLET= +LIGHTWALLETD_SEND_TRANSACTIONS= + # Full sync tests -# These tests could take a long time to run, depending on the network -FULL_SYNC_MAINNET_TIMEOUT_MINUTES= -FULL_SYNC_TESTNET_TIMEOUT_MINUTES= -TEST_LWD_FULL_SYNC= + +# These tests take 3 days on Mainnet and one day on Testnet. +SYNC_FULL_MAINNET_TIMEOUT_MINUTES= +SYNC_FULL_TESTNET_TIMEOUT_MINUTES= +LIGHTWALLETD_SYNC_FULL= diff --git a/docker/zcash.conf b/docker/zcash.conf new file mode 100644 index 00000000000..22f9ab8495d --- /dev/null +++ b/docker/zcash.conf @@ -0,0 +1,2 @@ +rpcpassword=none +rpcbind=zebra diff --git a/docs/decisions/README.md b/docs/decisions/README.md new file mode 100644 index 00000000000..91d5bd9188f --- /dev/null +++ b/docs/decisions/README.md @@ -0,0 +1,22 @@ +# Decision Log + +We capture important decisions with [architectural decision records](https://adr.github.io/). + +These records provide context, trade-offs, and reasoning taken at our community & technical cross-roads. Our goal is to preserve the understanding of the project growth, and capture enough insight to effectively revisit previous decisions. + +To get started, create a new decision record using the template: + +```sh +cp template.md NNNN-title-with-dashes.md +``` + +For more rationale for this approach, see [Michael Nygard's article](http://thinkrelevance.com/blog/2011/11/15/documenting-architecture-decisions). + +We've inherited MADR [ADR template](https://adr.github.io/madr/), which is a bit more verbose than Nygard's original template. We may simplify it in the future. + +## Evolving Decisions + +Many decisions build on each other, a driver of iterative change and messiness +in software. By laying out the "story arc" of a particular system within the +application, we hope future maintainers will be able to identify how to rewind +decisions when refactoring the application becomes necessary. diff --git a/docs/decisions/devops/0001-docker-high-uid.md b/docs/decisions/devops/0001-docker-high-uid.md new file mode 100644 index 00000000000..bef88c1980a --- /dev/null +++ b/docs/decisions/devops/0001-docker-high-uid.md @@ -0,0 +1,51 @@ +--- +status: accepted +date: 2025-02-28 +story: Appropriate UID/GID values for container users +--- + +# Use High UID/GID Values for Container Users + +## Context & Problem Statement + +Docker containers share the host's user namespace by default. If container UIDs/GIDs overlap with privileged host accounts, this could lead to privilege escalation if a container escape vulnerability is exploited. Low UIDs (especially in the system user range of 100-999) are particularly risky as they often map to privileged system users on the host. + +Our previous approach used UID/GID 101 with the `--system` flag for user creation, which falls within the system user range and could potentially overlap with critical system users on the host. + +## Priorities & Constraints + +* Enhance security by reducing the risk of container user namespace overlaps +* Avoid warnings during container build related to system user ranges +* Maintain compatibility with common Docker practices +* Prevent potential privilege escalation in case of container escape + +## Considered Options + +* Option 1: Keep using low UID/GID (101) with `--system` flag +* Option 2: Use UID/GID (1000+) without `--system` flag +* Option 3: Use high UID/GID (10000+) without `--system` flag + +## Decision Outcome + +Chosen option: [Option 3: Use high UID/GID (10000+) without `--system` flag] + +We decided to: + +1. Change the default UID/GID from 101 to 10001 +2. Remove the `--system` flag from user/group creation commands +3. Document the security rationale for these changes + +This approach significantly reduces the risk of UID/GID collision with host system users while avoiding build-time warnings related to system user ranges. Using a very high UID/GID (10001) provides an additional security boundary in containers where user namespaces are shared with the host. + +### Expected Consequences + +* Improved security posture by reducing the risk of container escapes leading to privilege escalation +* Elimination of build-time warnings related to system user UID/GID ranges +* Consistency with industry best practices for container security +* No functional impact on container operation, as the internal user permissions remain the same + +## More Information + +* [NGINX Docker User ID Issue](https://github.com/nginxinc/docker-nginx/issues/490) - Demonstrates the risks of using UID 101 which overlaps with `systemd-network` user on Debian systems +* [.NET Docker Issue on System Users](https://github.com/dotnet/dotnet-docker/issues/4624) - Details the problems with using `--system` flag and the SYS_UID_MAX warnings +* [Docker Security Best Practices](https://docs.docker.com/develop/security-best-practices/) - General security recommendations for Docker containers diff --git a/docs/decisions/devops/0002-docker-use-gosu.md b/docs/decisions/devops/0002-docker-use-gosu.md new file mode 100644 index 00000000000..0bdd2931f89 --- /dev/null +++ b/docs/decisions/devops/0002-docker-use-gosu.md @@ -0,0 +1,51 @@ +--- +status: accepted +date: 2025-02-28 +story: Volumes permissions and privilege management in container entrypoint +--- + +# Use gosu for Privilege Dropping in Entrypoint + +## Context & Problem Statement + +Running containerized applications as the root user is a security risk. If an attacker compromises the application, they gain root access within the container, potentially facilitating a container escape. However, some operations during container startup, such as creating directories or modifying file permissions in locations not owned by the application user, require root privileges. We need a way to perform these initial setup tasks as root, but then switch to a non-privileged user *before* executing the main application (`zebrad`). Using `USER` in the Dockerfile is insufficient because it applies to the entire runtime, and we need to change permissions *after* volumes are mounted. + +## Priorities & Constraints + +* Minimize the security risk by running the main application (`zebrad`) as a non-privileged user. +* Allow initial setup tasks (file/directory creation, permission changes) that require root privileges. +* Maintain a clean and efficient entrypoint script. +* Avoid complex signal handling and TTY issues associated with `su` and `sudo`. +* Ensure 1:1 parity with Docker's `--user` flag behavior. + +## Considered Options + +* Option 1: Use `USER` directive in Dockerfile. +* Option 2: Use `su` within the entrypoint script. +* Option 3: Use `sudo` within the entrypoint script. +* Option 4: Use `gosu` within the entrypoint script. +* Option 5: Use `chroot --userspec` +* Option 6: Use `setpriv` + +## Decision Outcome + +Chosen option: [Option 4: Use `gosu` within the entrypoint script] + +We chose to use `gosu` because it provides a simple and secure way to drop privileges from root to a non-privileged user *after* performing necessary setup tasks. `gosu` avoids the TTY and signal-handling complexities of `su` and `sudo`. It's designed specifically for this use case (dropping privileges in container entrypoints) and leverages the same underlying mechanisms as Docker itself for user/group handling, ensuring consistent behavior. + +### Expected Consequences + +* Improved security by running `zebrad` as a non-privileged user. +* Simplified entrypoint script compared to using `su` or `sudo`. +* Avoidance of TTY and signal-handling issues. +* Consistent behavior with Docker's `--user` flag. +* No negative impact on functionality, as initial setup tasks can still be performed. + +## More Information + +* [gosu GitHub repository](https://github.com/tianon/gosu#why) - Explains the rationale behind `gosu` and its advantages over `su` and `sudo`. +* [gosu usage warning](https://github.com/tianon/gosu#warning) - Highlights the core use case (stepping down from root) and potential vulnerabilities in other scenarios. +* Alternatives considered: + * `chroot --userspec`: While functional, it's less common and less directly suited to this specific task than `gosu`. + * `setpriv`: A viable alternative, but `gosu` is already well-established in our workflow and offers the desired functionality with a smaller footprint than a full `util-linux` installation. + * `su-exec`: Another minimal alternative, but it has known parser bugs that could lead to unexpected root execution. diff --git a/docs/decisions/devops/0003-filesystem-hierarchy.md b/docs/decisions/devops/0003-filesystem-hierarchy.md new file mode 100644 index 00000000000..13c626dec5e --- /dev/null +++ b/docs/decisions/devops/0003-filesystem-hierarchy.md @@ -0,0 +1,115 @@ +--- +status: proposed +date: 2025-02-28 +story: Standardize filesystem hierarchy for Zebra deployments +--- + +# Standardize Filesystem Hierarchy: FHS vs. XDG + +## Context & Problem Statement + +Zebra currently has inconsistencies in its filesystem layout, particularly regarding where configuration, data, cache files, and binaries are stored. We need a standardized approach compatible with: + +1. Traditional Linux systems. +2. Containerized deployments (Docker). +3. Cloud environments with stricter filesystem restrictions (e.g., Google's Container-Optimized OS). + +We previously considered using the Filesystem Hierarchy Standard (FHS) exclusively ([Issue #3432](https://github.com/ZcashFoundation/zebra/issues/3432)). However, recent changes introduced the XDG Base Directory Specification, which offers a user-centric approach. We need to decide whether to: + +* Adhere to FHS. +* Adopt XDG Base Directory Specification. +* Use a hybrid approach, leveraging the strengths of both. + +The choice impacts how we structure our Docker images, where configuration files are located, and how users interact with Zebra in different environments. + +## Priorities & Constraints + +* **Security:** Minimize the risk of privilege escalation by adhering to least-privilege principles. +* **Maintainability:** Ensure a clear and consistent filesystem layout that is easy to understand and maintain. +* **Compatibility:** Work seamlessly across various Linux distributions, Docker, and cloud environments (particularly those with restricted filesystems like Google's Container-Optimized OS). +* **User Experience:** Provide a predictable and user-friendly experience for locating configuration and data files. +* **Flexibility:** Allow users to override default locations via environment variables where appropriate. +* **Avoid Breaking Changes:** Minimize disruption to existing users and deployments, if possible. + +## Considered Options + +### Option 1: FHS + +* Configuration: `/etc/zebrad/` +* Data: `/var/lib/zebrad/` +* Cache: `/var/cache/zebrad/` +* Logs: `/var/log/zebrad/` +* Binary: `/opt/zebra/bin/zebrad` or `/usr/local/bin/zebrad` + +### Option 2: XDG Base Directory Specification + +* Configuration: `$HOME/.config/zebrad/` +* Data: `$HOME/.local/share/zebrad/` +* Cache: `$HOME/.cache/zebrad/` +* State: `$HOME/.local/state/zebrad/` +* Binary: `$HOME/.local/bin/zebrad` or `/usr/local/bin/zebrad` + +### Option 3: Hybrid Approach (FHS for System-Wide, XDG for User-Specific) + +* System-wide configuration: `/etc/zebrad/` +* User-specific configuration: `$XDG_CONFIG_HOME/zebrad/` +* System-wide data (read-only, shared): `/usr/share/zebrad/` (e.g., checkpoints) +* User-specific data: `$XDG_DATA_HOME/zebrad/` +* Cache: `$XDG_CACHE_HOME/zebrad/` +* State: `$XDG_STATE_HOME/zebrad/` +* Runtime: `$XDG_RUNTIME_DIR/zebrad/` +* Binary: `/opt/zebra/bin/zebrad` (system-wide) or `$HOME/.local/bin/zebrad` (user-specific) + +## Pros and Cons of the Options + +### FHS + +* **Pros:** + * Traditional and well-understood by system administrators. + * Clear separation of configuration, data, cache, and binaries. + * Suitable for packaged software installations. + +* **Cons:** + * Less user-friendly; requires root access to modify configuration. + * Can conflict with stricter cloud environments restricting writes to `/etc` and `/var`. + * Doesn't handle multi-user scenarios as gracefully as XDG. + +### XDG Base Directory Specification + +* **Pros:** + * User-centric: configuration and data stored in user-writable locations. + * Better suited for containerized and cloud environments. + * Handles multi-user scenarios gracefully. + * Clear separation of configuration, data, cache, and state. + +* **Cons:** + * Less traditional; might be unfamiliar to some system administrators. + * Requires environment variables to be set correctly. + * Binary placement less standardized. + +### Hybrid Approach (FHS for System-Wide, XDG for User-Specific) + +* **Pros:** + * Combines strengths of FHS and XDG. + * Allows system-wide defaults while prioritizing user-specific configurations. + * Flexible and adaptable to different deployment scenarios. + * Clear binary placement in `/opt`. + +* **Cons:** + * More complex than either FHS or XDG alone. + * Requires careful consideration of precedence rules. + +## Decision Outcome + +Pending + +## Expected Consequences + +Pending + +## More Information + +* [Filesystem Hierarchy Standard (FHS) v3.0](https://refspecs.linuxfoundation.org/FHS_3.0/fhs-3.0.html) +* [XDG Base Directory Specification](https://specifications.freedesktop.org/basedir-spec/latest/) +* [Zebra Issue #3432: Use the Filesystem Hierarchy Standard (FHS) for deployments and artifacts](https://github.com/ZcashFoundation/zebra/issues/3432) +* [Google Container-Optimized OS: Working with the File System](https://cloud.google.com/container-optimized-os/docs/concepts/disks-and-filesystem#working_with_the_file_system) diff --git a/docs/decisions/devops/004-improve-docker-conf-tests.md b/docs/decisions/devops/004-improve-docker-conf-tests.md new file mode 100644 index 00000000000..f1332c15ef5 --- /dev/null +++ b/docs/decisions/devops/004-improve-docker-conf-tests.md @@ -0,0 +1,89 @@ +--- +# status and date are the only required elements. Feel free to remove the rest. +status: accepted +date: 2025-04-07 +builds-on: N/A +story: Need a scalable and maintainable way to test various Docker image configurations derived from `.env` variables and `entrypoint.sh` logic, ensuring consistency between CI and CD pipelines. https://github.com/ZcashFoundation/zebra/pull/8948 +--- + +# Centralize Docker Configuration Testing using a Reusable Workflow + +## Context and Problem Statement + +Currently, tests verifying Zebra's Docker image configuration (based on environment variables processed by `docker/entrypoint.sh`) are implemented using a reusable workflow (`sub-test-zebra-config.yml`). However, the _invocation_ of these tests, including the specific scenarios (environment variables, grep patterns), is duplicated and scattered across different workflows, notably the CI workflow (`sub-ci-unit-tests-docker.yml`) and the CD workflow (`cd-deploy-nodes-gcp.yml`). + +This leads to: + +1. **Code Duplication:** Similar test setup logic exists in multiple places. +2. **Maintenance Overhead:** Adding or modifying configuration tests requires changes in multiple files. +3. **Scalability Issues:** Adding numerous new test scenarios would significantly clutter the main CI and CD workflow files. +4. **Potential Inconsistency:** Risk of configuration tests diverging between CI and CD environments. + +We need a centralized, scalable, and maintainable approach to define and run these configuration tests against Docker images built in both CI and CD contexts. + +## Priorities & Constraints + +- **DRY Principle:** Avoid repeating test logic and scenario definitions. +- **Maintainability:** Configuration tests should be easy to find, understand, and modify. +- **Scalability:** The solution should easily accommodate adding many more test scenarios in the future. +- **Consistency:** Ensure the same tests run against both CI and CD images where applicable. +- **Integration:** Leverage existing GitHub Actions tooling and workflows effectively. +- **Reliability:** Testing relies on running a container and grepping its logs for specific patterns to determine success. + +## Considered Options + +1. **Status Quo:** Continue defining and invoking configuration tests within the respective CI (`sub-ci-unit-tests-docker.yml`) and CD (`cd-deploy-nodes-gcp.yml`) workflows, using `sub-test-zebra-config.yml` for the core run/grep logic. +2. **Modify and Extend `sub-test-zebra-config.yml`:** Convert the existing `sub-test-zebra-config.yml` workflow. Remove its specific test inputs (`test_id`, `grep_patterns`, `test_variables`). Add multiple jobs _inside_ this workflow, each hardcoding a specific test scenario (run container + grep logs). The workflow would only take `docker_image` as input. +3. **Use `docker-compose.test.yml`:** Define test scenarios as services within a dedicated `docker-compose.test.yml` file. The CI/CD workflows would call a script (like `sub-test-zebra-config.yml`) that uses `docker compose` to run specific services and performs log grepping. +4. **Create a _New_ Dedicated Reusable Workflow:** Create a _new_ reusable workflow (e.g., `sub-test-all-configs.yml`) that takes a Docker image digest as input and contains multiple jobs, each defining and executing a specific configuration test scenario (run container + grep logs). + +## Pros and Cons of the Options + +### Option 1: Status Quo + +- Bad: High duplication, poor maintainability, poor scalability. + +### Option 2: Modify and Extend `sub-test-zebra-config.yml` + +- Good: Centralizes test definition, execution, and assertion logic within the GHA ecosystem. Maximizes DRY principle for GHA workflows. High maintainability and scalability for adding tests. Clear separation of concerns (build vs. test config). Reuses an existing workflow file structure. +- Bad: Modifies the existing workflow's purpose significantly. Callers need to adapt. + +### Option 3: Use `docker-compose.test.yml` + +- Good: Centralizes test _environment definitions_ in a standard format (`docker-compose.yml`). Easy local testing via `docker compose`. +- Bad: Requires managing an extra file (`docker-compose.test.yml`). Still requires a GitHub Actions script/workflow step to orchestrate `docker compose` commands and perform the essential log grepping/assertion logic. Less integrated into the pure GHA workflow structure. + +### Option 4: Create a _New_ Dedicated Reusable Workflow + +- Good: Cleanest separation - new workflow has a clear single purpose from the start. High maintainability and scalability. +- Bad: Introduces an additional workflow file. Adds a layer of workflow call chaining. + +## Decision Outcome + +Chosen option [Option 2: Modify and Extend `sub-test-zebra-config.yml`] + +This option provides a good balance of maintainability, scalability, and consistency by centralizing the configuration testing logic within a single, dedicated GitHub Actions reusable workflow (`sub-test-zebra-config.yml`). It directly addresses the code duplication across CI and CD pipelines and leverages GHA's native features for modularity by converting the existing workflow into a multi-job test suite runner. + +While Option 4 (creating a new workflow) offered slightly cleaner separation initially, modifying the existing workflow (Option 2) achieves the same goal of centralization while minimizing the number of workflow files. It encapsulates the entire test process (definition, execution, assertion) within GHA jobs in the reused file. + +The `sub-test-zebra-config.yml` workflow will be modified to remove its specific test inputs and instead contain individual jobs for each configuration scenario to be tested, taking only the `docker_image` digest as input. The CI and CD workflows will be simplified to call this modified workflow once after their respective build steps. + +### Expected Consequences + +- Reduction in code duplication within CI/CD workflow files. +- Improved maintainability: configuration tests are located in a single file (`sub-test-zebra-config.yml`). +- Easier addition of new configuration test scenarios by adding jobs to `sub-test-zebra-config.yml`. +- Clearer separation between image building and configuration testing logic. +- `sub-test-zebra-config.yml` will fundamentally change its structure and inputs. +- CI/CD workflows (`cd-deploy-nodes-gcp.yml`, parent of `sub-ci-unit-tests-docker.yml`) will need modification to remove old test jobs and add calls to the modified reusable workflow, passing the correct image digest. +- Debugging might involve tracing execution across workflow calls and within the multiple jobs of `sub-test-zebra-config.yml`. + +## More Information + +- GitHub Actions: Reusing Workflows: [https://docs.github.com/en/actions/using-workflows/reusing-workflows](https://docs.github.com/en/actions/using-workflows/reusing-workflows) +- Relevant files: + - `.github/workflows/sub-test-zebra-config.yml` (To be modified) + - `.github/workflows/cd-deploy-nodes-gcp.yml` (To be modified) + - `.github/workflows/sub-ci-unit-tests-docker.yml` (To be modified) + - `docker/entrypoint.sh` (Script processing configurations) + - `docker/.env` (Example environment variables) diff --git a/docs/decisions/template.md b/docs/decisions/template.md new file mode 100644 index 00000000000..2913cd99116 --- /dev/null +++ b/docs/decisions/template.md @@ -0,0 +1,49 @@ +--- +# status and date are the only required elements. Feel free to remove the rest. +status: {[proposed | rejected | accepted | deprecated | … | superseded by [ADR-NAME](adr-file-name.md)]} +date: {YYYY-MM-DD when the decision was last updated} +builds-on: {[Short Title](0001-short-title.md)} +story: {description or link to contextual issue} +--- + +# {short title of solved problem and solution} + +## Context and Problem Statement + +{2-3 sentences explaining the problem and the forces influencing the decision.} + + +## Priorities & Constraints + +* {List of concerns or constraints} +* {Factors influencing the decision} + +## Considered Options + +* Option 1: Thing +* Option 2: Another + +### Pros and Cons of the Options + +#### Option 1: {Brief description} + +* Good, because {reason} +* Bad, because {reason} + +## Decision Outcome + +Chosen option [Option 1: Thing] + +{Clearly state the chosen option and provide justification. Reference the "Pros and Cons of the Options" section below if applicable.} + +### Expected Consequences + +* List of outcomes resulting from this decision + + +## More Information + + + + + diff --git a/prometheus.yaml b/prometheus.yaml index 5501da2b3f1..86ca1d5e7e4 100644 --- a/prometheus.yaml +++ b/prometheus.yaml @@ -1,7 +1,6 @@ scrape_configs: - - job_name: 'zebrad' + - job_name: "zebrad" scrape_interval: 500ms - metrics_path: '/' + metrics_path: "/" static_configs: - - targets: ['localhost:9999'] - + - targets: ["localhost:9999"] diff --git a/rust-toolchain.toml b/rust-toolchain.toml index dd1c8aa4359..5e0398f70bc 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,3 +1,3 @@ # TODO: Upstream specifies `channel = "stable"` — consider restoring it before final merge. [toolchain] -channel = "1.82.0" +channel = "1.85.0" diff --git a/tower-batch-control/CHANGELOG.md b/tower-batch-control/CHANGELOG.md new file mode 100644 index 00000000000..b1abe936f2b --- /dev/null +++ b/tower-batch-control/CHANGELOG.md @@ -0,0 +1,11 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [0.2.41] - 2025-07-11 + +First "stable" release. However, be advised that the API may still greatly +change so major version bumps can be common. diff --git a/tower-batch-control/Cargo.toml b/tower-batch-control/Cargo.toml index 398a2baddbc..60b5f4759ab 100644 --- a/tower-batch-control/Cargo.toml +++ b/tower-batch-control/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tower-batch-control" -version = "0.2.41-beta.17" +version = "0.2.41" authors = ["Zcash Foundation ", "Tower Maintainers "] description = "Tower middleware for batch request processing" # # Legal @@ -22,31 +22,31 @@ keywords = ["tower", "batch"] categories = ["algorithms", "asynchronous"] [dependencies] -futures = "0.3.31" -futures-core = "0.3.28" -pin-project = "1.1.6" -rayon = "1.10.0" -tokio = { version = "1.41.0", features = ["time", "sync", "tracing", "macros"] } -tokio-util = "0.7.12" -tower = { version = "0.4.13", features = ["util", "buffer"] } -tracing = "0.1.39" -tracing-futures = "0.2.5" +futures = { workspace = true } +futures-core = { workspace = true } +pin-project = { workspace = true } +rayon = { workspace = true } +tokio = { workspace = true, features = ["time", "sync", "tracing", "macros"] } +tokio-util = { workspace = true } +tower = { workspace = true, features = ["util", "buffer"] } +tracing = { workspace = true } +tracing-futures = { workspace = true } [dev-dependencies] -color-eyre = "0.6.3" +color-eyre = { workspace = true } # This is a transitive dependency via color-eyre. # Enable a feature that makes tinyvec compile much faster. -tinyvec = { version = "1.8.0", features = ["rustc_1_55"] } +tinyvec = { workspace = true, features = ["rustc_1_55"] } -ed25519-zebra = "4.0.3" -rand = "0.8.5" +ed25519-zebra = { workspace = true } +rand = { workspace = true } -tokio = { version = "1.41.0", features = ["full", "tracing", "test-util"] } -tokio-test = "0.4.4" -tower-fallback = { path = "../tower-fallback/", version = "0.2.41-beta.17" } -tower-test = "0.4.0" +tokio = { workspace = true, features = ["full", "tracing", "test-util"] } +tokio-test = { workspace = true } +tower-fallback = { path = "../tower-fallback/", version = "0.2.41" } +tower-test = { workspace = true } -zebra-test = { path = "../zebra-test/", version = "1.0.0-beta.41" } +zebra-test = { path = "../zebra-test/", version = "1.0.0" } [lints.rust] unexpected_cfgs = { level = "warn", check-cfg = ['cfg(tokio_unstable)'] } diff --git a/tower-batch-control/LICENSE b/tower-batch-control/LICENSE index 5428318519b..c4e34c0f7c6 100644 --- a/tower-batch-control/LICENSE +++ b/tower-batch-control/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2019-2024 Zcash Foundation +Copyright (c) 2019-2025 Zcash Foundation Copyright (c) 2019 Tower Contributors Permission is hereby granted, free of charge, to any diff --git a/tower-batch-control/src/service.rs b/tower-batch-control/src/service.rs index 414e2452529..394e38a5b69 100644 --- a/tower-batch-control/src/service.rs +++ b/tower-batch-control/src/service.rs @@ -137,6 +137,7 @@ where tokio::task::Builder::new() .name(&format!("{} batch", batch_kind)) .spawn(worker.run().instrument(span)) + .expect("panic on error to match tokio::spawn") }; #[cfg(not(tokio_unstable))] let worker_handle = tokio::spawn(worker.run().instrument(span)); diff --git a/tower-batch-control/src/worker.rs b/tower-batch-control/src/worker.rs index f2266e67100..865a2f37009 100644 --- a/tower-batch-control/src/worker.rs +++ b/tower-batch-control/src/worker.rs @@ -323,7 +323,7 @@ where // We don't schedule any batches on an errored service self.pending_batch_timer = None; - // By closing the mpsc::Receiver, we know that that the run() loop will + // By closing the mpsc::Receiver, we know that the run() loop will // drain all pending requests. We just need to make sure that any // requests that we receive before we've exhausted the receiver receive // the error: diff --git a/tower-fallback/CHANGELOG.md b/tower-fallback/CHANGELOG.md new file mode 100644 index 00000000000..b1abe936f2b --- /dev/null +++ b/tower-fallback/CHANGELOG.md @@ -0,0 +1,11 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [0.2.41] - 2025-07-11 + +First "stable" release. However, be advised that the API may still greatly +change so major version bumps can be common. diff --git a/tower-fallback/Cargo.toml b/tower-fallback/Cargo.toml index 5919b1bc632..1b6424ff387 100644 --- a/tower-fallback/Cargo.toml +++ b/tower-fallback/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tower-fallback" -version = "0.2.41-beta.17" +version = "0.2.41" authors = ["Zcash Foundation "] description = "A Tower service combinator that sends requests to a first service, then retries processing on a second fallback service if the first service errors." license = "MIT OR Apache-2.0" @@ -16,12 +16,12 @@ keywords = ["tower", "batch"] categories = ["algorithms", "asynchronous"] [dependencies] -pin-project = "1.1.6" -tower = "0.4.13" -futures-core = "0.3.28" -tracing = "0.1.39" +pin-project = { workspace = true } +tower = { workspace = true } +futures-core = { workspace = true } +tracing = { workspace = true } [dev-dependencies] -tokio = { version = "1.41.0", features = ["full", "tracing", "test-util"] } +tokio = { workspace = true, features = ["full", "tracing", "test-util"] } -zebra-test = { path = "../zebra-test/", version = "1.0.0-beta.41" } +zebra-test = { path = "../zebra-test/", version = "1.0.0" } diff --git a/tower-fallback/tests/fallback.rs b/tower-fallback/tests/fallback.rs index 8b60481d7b8..486dfb4a47e 100644 --- a/tower-fallback/tests/fallback.rs +++ b/tower-fallback/tests/fallback.rs @@ -1,3 +1,5 @@ +//! Tests for tower-fallback + use tower::{service_fn, Service, ServiceExt}; use tower_fallback::Fallback; diff --git a/zebra-chain/CHANGELOG.md b/zebra-chain/CHANGELOG.md new file mode 100644 index 00000000000..666b7d14483 --- /dev/null +++ b/zebra-chain/CHANGELOG.md @@ -0,0 +1,11 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [1.0.0] - 2025-07-11 + +First "stable" release. However, be advised that the API may still greatly +change so major version bumps can be common. \ No newline at end of file diff --git a/zebra-chain/Cargo.toml b/zebra-chain/Cargo.toml index 55c9e5ecb1c..0c4c26e4e23 100644 --- a/zebra-chain/Cargo.toml +++ b/zebra-chain/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "zebra-chain" -version = "1.0.0-beta.41" +version = "1.0.0" authors = ["Zcash Foundation "] description = "Core Zcash data structures" license = "MIT OR Apache-2.0" @@ -29,20 +29,8 @@ async-error = [ "tokio", ] -# Mining RPC support -getblocktemplate-rpcs = [ -] - -# Experimental shielded scanning support -shielded-scan = [ - "zcash_client_backend" -] - # Experimental internal miner support -# TODO: Internal miner feature functionality was removed at https://github.com/ZcashFoundation/zebra/issues/8180 -# See what was removed at https://github.com/ZcashFoundation/zebra/blob/v1.5.1/zebra-chain/Cargo.toml#L38-L43 -# Restore support when conditions are met. https://github.com/ZcashFoundation/zebra/issues/8183 -internal-miner = [] +internal-miner = ["equihash/solver"] # Experimental elasticsearch support elasticsearch = [] @@ -61,124 +49,113 @@ proptest-impl = [ bench = ["zebra-test"] -# Support for transaction version 6 -tx-v6 = [ - "nonempty" -] +tx_v6 = [] [dependencies] # Cryptography -bitvec = "1.0.1" -bitflags = "2.5.0" -bitflags-serde-legacy = "0.1.1" -blake2b_simd = "1.0.2" -blake2s_simd = "1.0.2" -bridgetree = "0.7.0" -bs58 = { version = "0.5.1", features = ["check"] } -byteorder = "1.5.0" - -# TODO: Internal miner feature functionality was removed at https://github.com/ZcashFoundation/zebra/issues/8180 -# See what was removed at https://github.com/ZcashFoundation/zebra/blob/v1.5.1/zebra-chain/Cargo.toml#L73-L85 -# Restore support when conditions are met. https://github.com/ZcashFoundation/zebra/issues/8183 -equihash = "0.2.2" - -group = "0.13.0" +bitvec = { workspace = true } +bitflags = { workspace = true } +bitflags-serde-legacy = { workspace = true } +blake2b_simd = { workspace = true } +blake2s_simd = { workspace = true } +bs58 = { workspace = true, features = ["check"] } +byteorder = { workspace = true } + +equihash = { workspace = true } + +group = { workspace = true } incrementalmerkletree.workspace = true -jubjub = "0.10.0" -lazy_static = "1.4.0" -tempfile = "3.13.0" -dirs = "5.0.1" -num-integer = "0.1.46" -primitive-types = "0.12.2" -rand_core = "0.6.4" -ripemd = "0.1.3" +jubjub = { workspace = true } +lazy_static = { workspace = true } +tempfile = { workspace = true } +dirs = { workspace = true } +num-integer = { workspace = true } +primitive-types = { workspace = true } +rand_core = { workspace = true } +ripemd = { workspace = true } # Matches version used by hdwallet -secp256k1 = { version = "0.27.0", features = ["serde"] } -sha2 = { version = "0.10.7", features = ["compress"] } -uint = "0.10.0" -x25519-dalek = { version = "2.0.1", features = ["serde"] } +secp256k1 = { workspace = true, features = ["serde"] } +sha2 = { workspace = true, features = ["compress"] } +uint = { workspace = true } +x25519-dalek = { workspace = true, features = ["serde"] } +bech32 = { workspace = true } # ECC deps -halo2 = { package = "halo2_proofs", version = "0.3.0" } +halo2 = { package = "halo2_proofs", version = "0.3" } orchard.workspace = true zcash_encoding.workspace = true zcash_history.workspace = true -zcash_note_encryption = "0.4.0" +zcash_note_encryption = { workspace = true } zcash_primitives = { workspace = true, features = ["transparent-inputs"] } sapling-crypto.workspace = true zcash_protocol.workspace = true zcash_address.workspace = true zcash_transparent.workspace = true - -# Used for orchard serialization -nonempty = { version = "0.7", optional = true } +sinsemilla = { version = "0.1" } # Time -chrono = { version = "0.4.38", default-features = false, features = ["clock", "std", "serde"] } -humantime = "2.1.0" +chrono = { workspace = true, features = ["clock", "std", "serde"] } +humantime = { workspace = true } # Error Handling & Formatting -static_assertions = "1.1.0" -thiserror = "1.0.64" -tracing = "0.1.39" +static_assertions = { workspace = true } +thiserror = { workspace = true } +tracing = { workspace = true } # Serialization -hex = { version = "0.4.3", features = ["serde"] } -serde = { version = "1.0.211", features = ["serde_derive", "rc"] } -serde_with = "3.11.0" -serde-big-array = "0.5.1" +hex = { workspace = true, features = ["serde"] } +serde = { workspace = true, features = ["serde_derive", "rc"] } +serde_with = { workspace = true } +serde-big-array = { workspace = true } # Processing -futures = "0.3.31" -itertools = "0.13.0" -rayon = "1.10.0" +futures = { workspace = true } +itertools = { workspace = true } +rayon = { workspace = true } # ZF deps -ed25519-zebra = "4.0.3" -redjubjub = "0.7.0" -reddsa = "0.5.1" +ed25519-zebra = { workspace = true } +redjubjub = { workspace = true } +reddsa = { workspace = true } # Production feature json-conversion -serde_json = { version = "1.0.132", optional = true } +serde_json = { workspace = true, optional = true } # Production feature async-error and testing feature proptest-impl -tokio = { version = "1.41.0", optional = true } - -# Experimental feature shielded-scan -zcash_client_backend = { workspace = true, optional = true } +tokio = { workspace = true, optional = true } # Optional testing dependencies -proptest = { version = "1.4.0", optional = true } -proptest-derive = { version = "0.5.0", optional = true } +proptest = { workspace = true, optional = true } +proptest-derive = { workspace = true, optional = true } -rand = { version = "0.8.5", optional = true } -rand_chacha = { version = "0.3.1", optional = true } +rand = { workspace = true, optional = true } +rand_chacha = { workspace = true, optional = true } -zebra-test = { path = "../zebra-test/", version = "1.0.0-beta.41", optional = true } +zebra-test = { path = "../zebra-test/", version = "1.0.0", optional = true } [dev-dependencies] # Benchmarks -criterion = { version = "0.5.1", features = ["html_reports"] } +criterion = { workspace = true, features = ["html_reports"] } # Error Handling & Formatting -color-eyre = "0.6.3" +color-eyre = { workspace = true } # This is a transitive dependency via color-eyre. # Enable a feature that makes tinyvec compile much faster. -tinyvec = { version = "1.8.0", features = ["rustc_1_55"] } -spandoc = "0.2.2" -tracing = "0.1.39" +tinyvec = { workspace = true, features = ["rustc_1_55"] } +spandoc = { workspace = true } +tracing = { workspace = true } # Make the optional testing dependencies required -proptest = "1.4.0" -proptest-derive = "0.5.0" +proptest = { workspace = true } +proptest-derive = { workspace = true } -rand = "0.8.5" -rand_chacha = "0.3.1" +rand = { workspace = true } +rand_chacha = { workspace = true } -tokio = { version = "1.41.0", features = ["full", "tracing", "test-util"] } +tokio = { workspace = true, features = ["full", "tracing", "test-util"] } -zebra-test = { path = "../zebra-test/", version = "1.0.0-beta.41" } +zebra-test = { path = "../zebra-test/", version = "1.0.0" } orchard = { workspace = true, features = ["test-dependencies"] } diff --git a/zebra-chain/src/amount.rs b/zebra-chain/src/amount.rs index f4a81c14893..355a8b63bb0 100644 --- a/zebra-chain/src/amount.rs +++ b/zebra-chain/src/amount.rs @@ -416,7 +416,7 @@ where } } -// TODO: add infalliable impls for NonNegative <-> NegativeOrZero, +// TODO: add infallible impls for NonNegative <-> NegativeOrZero, // when Rust uses trait output types to disambiguate overlapping impls. impl std::ops::Neg for Amount where @@ -538,6 +538,10 @@ impl Constraint for NegativeAllowed { /// ); /// ``` #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Default)] +#[cfg_attr( + any(test, feature = "proptest-impl"), + derive(proptest_derive::Arbitrary) +)] pub struct NonNegative; impl Constraint for NonNegative { diff --git a/zebra-chain/src/amount/tests/vectors.rs b/zebra-chain/src/amount/tests/vectors.rs index 933b2824d41..13ed0748d59 100644 --- a/zebra-chain/src/amount/tests/vectors.rs +++ b/zebra-chain/src/amount/tests/vectors.rs @@ -180,12 +180,12 @@ fn deserialize_checks_bounds() -> Result<()> { let mut big_bytes = Vec::new(); (&mut big_bytes) .write_u64::(big) - .expect("unexpected serialization failure: vec should be infalliable"); + .expect("unexpected serialization failure: vec should be infallible"); let mut neg_bytes = Vec::new(); (&mut neg_bytes) .write_i64::(neg) - .expect("unexpected serialization failure: vec should be infalliable"); + .expect("unexpected serialization failure: vec should be infallible"); Amount::::zcash_deserialize(big_bytes.as_slice()) .expect_err("deserialization should reject too large values"); @@ -335,9 +335,7 @@ fn test_sum() -> Result<()> { let times: usize = (i64::MAX / MAX_MONEY) .try_into() .expect("4392 can always be converted to usize"); - let amounts: Vec = std::iter::repeat(MAX_MONEY.try_into()?) - .take(times + 1) - .collect(); + let amounts: Vec = std::iter::repeat_n(MAX_MONEY.try_into()?, times + 1).collect(); let sum_ref = amounts.iter().sum::>(); let sum_value = amounts.into_iter().sum::>(); @@ -357,7 +355,7 @@ fn test_sum() -> Result<()> { .expect("4392 can always be converted to usize"); let neg_max_money: Amount = (-MAX_MONEY).try_into()?; let amounts: Vec> = - std::iter::repeat(neg_max_money).take(times + 1).collect(); + std::iter::repeat_n(neg_max_money, times + 1).collect(); let sum_ref = amounts.iter().sum::>(); let sum_value = amounts.into_iter().sum::>(); diff --git a/zebra-chain/src/block/arbitrary.rs b/zebra-chain/src/block/arbitrary.rs index cf8ce64c9b8..42ccd19fdc8 100644 --- a/zebra-chain/src/block/arbitrary.rs +++ b/zebra-chain/src/block/arbitrary.rs @@ -568,7 +568,7 @@ where + Copy + 'static, { - let mut spend_restriction = transaction.coinbase_spend_restriction(height); + let mut spend_restriction = transaction.coinbase_spend_restriction(&Network::Mainnet, height); let mut new_inputs = Vec::new(); let mut spent_outputs = HashMap::new(); @@ -650,7 +650,8 @@ where + 'static, { let has_shielded_outputs = transaction.has_shielded_outputs(); - let delete_transparent_outputs = CoinbaseSpendRestriction::OnlyShieldedOutputs { spend_height }; + let delete_transparent_outputs = + CoinbaseSpendRestriction::CheckCoinbaseMaturity { spend_height }; let mut attempts: usize = 0; // choose an arbitrary spendable UTXO, in hash set order diff --git a/zebra-chain/src/block/commitment.rs b/zebra-chain/src/block/commitment.rs index ec4ef7d2616..c2832d00e3b 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 | Nu7, activation_height) + (Canopy | Nu5 | Nu6 | Nu6_1 | 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 | Nu7, _) => Ok(ChainHistoryBlockTxAuthCommitment( + (Nu5 | Nu6 | Nu6_1 | Nu7, _) => Ok(ChainHistoryBlockTxAuthCommitment( ChainHistoryBlockTxAuthCommitmentHash(bytes), )), } @@ -164,7 +164,7 @@ impl Commitment { // - add methods for maintaining the MMR peaks, and calculating the root // hash from the current set of peaks // - move to a separate file -#[derive(Clone, Copy, Eq, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Copy, Eq, PartialEq, Serialize, Deserialize, Default)] pub struct ChainHistoryMmrRootHash([u8; 32]); impl fmt::Display for ChainHistoryMmrRootHash { diff --git a/zebra-chain/src/block/header.rs b/zebra-chain/src/block/header.rs index 1bbec3b471c..39b265e0304 100644 --- a/zebra-chain/src/block/header.rs +++ b/zebra-chain/src/block/header.rs @@ -7,11 +7,12 @@ use thiserror::Error; use crate::{ fmt::HexDebug, + parameters::Network, serialization::{TrustedPreallocate, MAX_PROTOCOL_MESSAGE_LEN}, work::{difficulty::CompactDifficulty, equihash::Solution}, }; -use super::{merkle, Hash, Height}; +use super::{merkle, Commitment, CommitmentError, Hash, Height}; #[cfg(any(test, feature = "proptest-impl"))] use proptest_derive::Arbitrary; @@ -58,7 +59,7 @@ pub struct Header { /// without incrementing the block [`version`](Self::version). Therefore, /// this field cannot be parsed without the network and height. Use /// [`Block::commitment`](super::Block::commitment) to get the parsed - /// [`Commitment`](super::Commitment). + /// [`Commitment`]. pub commitment_bytes: HexDebug<[u8; 32]>, /// The block timestamp is a Unix epoch time (UTC) when the miner @@ -124,6 +125,16 @@ impl Header { } } + /// Get the parsed block [`Commitment`] for this header. + /// Its interpretation depends on the given `network` and block `height`. + pub fn commitment( + &self, + network: &Network, + height: Height, + ) -> Result { + Commitment::from_bytes(*self.commitment_bytes, network, height) + } + /// Compute the hash of this header. pub fn hash(&self) -> Hash { Hash::from(self) diff --git a/zebra-chain/src/block/merkle.rs b/zebra-chain/src/block/merkle.rs index 639324f9d82..9d707489803 100644 --- a/zebra-chain/src/block/merkle.rs +++ b/zebra-chain/src/block/merkle.rs @@ -1,6 +1,6 @@ //! The Bitcoin-inherited Merkle tree of transactions. -use std::{fmt, io::Write, iter}; +use std::{fmt, io::Write}; use hex::{FromHex, ToHex}; @@ -404,7 +404,7 @@ impl std::iter::FromIterator for AuthDataRoot { // https://zips.z.cash/zip-0244#block-header-changes // Pad with enough leaves to make the tree full (a power of 2). let pad_count = hashes.len().next_power_of_two() - hashes.len(); - hashes.extend(iter::repeat([0u8; 32]).take(pad_count)); + hashes.extend(std::iter::repeat_n([0u8; 32], pad_count)); assert!(hashes.len().is_power_of_two()); while hashes.len() > 1 { diff --git a/zebra-chain/src/block/serialize.rs b/zebra-chain/src/block/serialize.rs index e763915e499..2dd3ee56f37 100644 --- a/zebra-chain/src/block/serialize.rs +++ b/zebra-chain/src/block/serialize.rs @@ -4,6 +4,7 @@ use std::{borrow::Borrow, io}; use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; use chrono::{TimeZone, Utc}; +use hex::{FromHex, FromHexError}; use crate::{ block::{header::ZCASH_BLOCK_VERSION, merkle, Block, CountedHeader, Hash, Header}, @@ -39,7 +40,7 @@ fn check_version(version: u32) -> Result<(), &'static str> { // but this is not actually part of the consensus rules, and in fact // broken mining software created blocks that do not have version 4. // There are approximately 4,000 blocks with version 536870912; this - // is the bit-reversal of the value 4, indicating that that mining pool + // is the bit-reversal of the value 4, indicating that mining pool // reversed bit-ordering of the version field. Because the version field // was not properly validated, these blocks were added to the chain. // @@ -63,7 +64,7 @@ fn check_version(version: u32) -> Result<(), &'static str> { impl ZcashSerialize for Header { #[allow(clippy::unwrap_in_result)] fn zcash_serialize(&self, mut writer: W) -> Result<(), io::Error> { - check_version(self.version).map_err(|msg| io::Error::new(io::ErrorKind::Other, msg))?; + check_version(self.version).map_err(io::Error::other)?; writer.write_u32::(self.version)?; self.previous_block_hash.zcash_serialize(&mut writer)?; @@ -194,3 +195,12 @@ impl From> for SerializedBlock { Self { bytes } } } + +impl FromHex for SerializedBlock { + type Error = FromHexError; + + fn from_hex>(hex: T) -> Result { + let bytes = Vec::from_hex(hex)?; + Ok(SerializedBlock { bytes }) + } +} diff --git a/zebra-chain/src/block/tests/generate.rs b/zebra-chain/src/block/tests/generate.rs index b908b6f9747..4fae246f082 100644 --- a/zebra-chain/src/block/tests/generate.rs +++ b/zebra-chain/src/block/tests/generate.rs @@ -131,9 +131,8 @@ fn multi_transaction_block(oversized: bool) -> Block { } // Create transactions to be just below or just above the limit - let transactions = std::iter::repeat(Arc::new(transaction)) - .take(max_transactions_in_block) - .collect::>(); + let transactions = + std::iter::repeat_n(Arc::new(transaction), max_transactions_in_block).collect::>(); // Add the transactions into a block let block = Block { @@ -193,9 +192,7 @@ fn single_transaction_block_many_inputs(oversized: bool) -> Block { let mut outputs = Vec::new(); // Create inputs to be just below the limit - let inputs = std::iter::repeat(input) - .take(max_inputs_in_tx) - .collect::>(); + let inputs = std::iter::repeat_n(input, max_inputs_in_tx).collect::>(); // 1 single output outputs.push(output); @@ -268,9 +265,7 @@ fn single_transaction_block_many_outputs(oversized: bool) -> Block { let inputs = vec![input]; // Create outputs to be just below the limit - let outputs = std::iter::repeat(output) - .take(max_outputs_in_tx) - .collect::>(); + let outputs = std::iter::repeat_n(output, max_outputs_in_tx).collect::>(); // Create a big transaction let big_transaction = Transaction::V1 { diff --git a/zebra-chain/src/block/tests/vectors.rs b/zebra-chain/src/block/tests/vectors.rs index 5ff19ca1092..02764c19ef7 100644 --- a/zebra-chain/src/block/tests/vectors.rs +++ b/zebra-chain/src/block/tests/vectors.rs @@ -9,10 +9,8 @@ use crate::{ block::{ serialize::MAX_BLOCK_BYTES, Block, BlockTimeError, Commitment::*, Hash, Header, Height, }, - parameters::{ - Network::{self, *}, - NetworkUpgrade::*, - }, + parameters::{Network, NetworkUpgrade::*}, + sapling, serialization::{ sha256d, SerializationError, ZcashDeserialize, ZcashDeserializeInto, ZcashSerialize, }, @@ -191,88 +189,80 @@ fn block_test_vectors_unique() { ); } +/// Checks that: +/// +/// - the block test vector indexes match the heights in the block data; +/// - each post-Sapling block has a corresponding final Sapling root; +/// - each post-Orchard block has a corresponding final Orchard root. #[test] -fn block_test_vectors_height_mainnet() { - let _init_guard = zebra_test::init(); - - block_test_vectors_height(Mainnet); -} - -#[test] -fn block_test_vectors_height_testnet() { +fn block_test_vectors() { let _init_guard = zebra_test::init(); - block_test_vectors_height(Network::new_default_testnet()); -} + for net in Network::iter() { + let sapling_anchors = net.sapling_anchors(); + let orchard_anchors = net.orchard_anchors(); -/// Test that the block test vector indexes match the heights in the block data, -/// and that each post-sapling block has a corresponding final sapling root. -fn block_test_vectors_height(network: Network) { - let (block_iter, sapling_roots) = network.block_sapling_roots_iter(); - - for (&height, block) in block_iter { - let block = block - .zcash_deserialize_into::() - .expect("block is structurally valid"); - assert_eq!( - block.coinbase_height().expect("block height is valid").0, - height, - "deserialized height must match BTreeMap key height" - ); + for (&height, block) in net.block_iter() { + let block = block + .zcash_deserialize_into::() + .expect("block is structurally valid"); + assert_eq!( + block.coinbase_height().expect("block height is valid").0, + height, + "deserialized height must match BTreeMap key height" + ); - if height - >= Sapling - .activation_height(&network) - .expect("sapling activation height is set") - .0 - { - assert!( - sapling_roots.contains_key(&height), - "post-sapling block test vectors must have matching sapling root test vectors: missing {network} {height}" + if height + >= Sapling + .activation_height(&net) + .expect("activation height") + .0 + { + assert!( + sapling_anchors.contains_key(&height), + "post-sapling block test vectors must have matching sapling root test vectors: \ + missing {net} {height}" ); + } + + if height >= Nu5.activation_height(&net).expect("activation height").0 { + assert!( + orchard_anchors.contains_key(&height), + "post-nu5 block test vectors must have matching orchard root test vectors: \ + missing {net} {height}" + ); + } } } } -#[test] -fn block_commitment_mainnet() { - let _init_guard = zebra_test::init(); - - block_commitment(Mainnet); -} - -#[test] -fn block_commitment_testnet() { - let _init_guard = zebra_test::init(); - - block_commitment(Network::new_default_testnet()); -} - -/// Check that the block commitment field parses without errors. +/// Checks that the block commitment field parses without errors. /// For sapling and blossom blocks, also check the final sapling root value. /// /// TODO: add chain history test vectors? -fn block_commitment(network: Network) { - let (block_iter, sapling_roots) = network.block_sapling_roots_iter(); - - for (height, block) in block_iter { - let block = block - .zcash_deserialize_into::() - .expect("block is structurally valid"); +#[test] +fn block_commitment() { + let _init_guard = zebra_test::init(); - let commitment = block.commitment(&network).unwrap_or_else(|_| { - panic!("unexpected structurally invalid block commitment at {network} {height}") - }); + for net in Network::iter() { + let sapling_anchors = net.sapling_anchors(); - if let FinalSaplingRoot(final_sapling_root) = commitment { - let expected_final_sapling_root = *sapling_roots - .get(height) - .expect("unexpected missing final sapling root test vector"); - assert_eq!( - final_sapling_root, - crate::sapling::tree::Root::try_from(*expected_final_sapling_root).unwrap(), - "unexpected invalid final sapling root commitment at {network} {height}" - ); + for (height, block) in net.block_iter() { + if let FinalSaplingRoot(anchor) = block + .zcash_deserialize_into::() + .expect("block is structurally valid") + .commitment(&net) + .expect("unexpected structurally invalid block commitment at {net} {height}") + { + let expected_anchor = *sapling_anchors + .get(height) + .expect("unexpected missing final sapling root test vector"); + assert_eq!( + anchor, + sapling::tree::Root::try_from(*expected_anchor).unwrap(), + "unexpected invalid final sapling root commitment at {net} {height}" + ); + } } } } diff --git a/zebra-chain/src/block_info.rs b/zebra-chain/src/block_info.rs new file mode 100644 index 00000000000..7d72380b177 --- /dev/null +++ b/zebra-chain/src/block_info.rs @@ -0,0 +1,28 @@ +//! Extra per-block info tracked in the state. +use crate::{amount::NonNegative, value_balance::ValueBalance}; + +/// Extra per-block info tracked in the state. +#[derive(Debug, Clone, Default, PartialEq, Eq)] +pub struct BlockInfo { + /// The pool balances after the block. + value_pools: ValueBalance, + /// The size of the block in bytes. + size: u32, +} + +impl BlockInfo { + /// Creates a new [`BlockInfo`] with the given value pools. + pub fn new(value_pools: ValueBalance, size: u32) -> Self { + BlockInfo { value_pools, size } + } + + /// Returns the value pools of this block. + pub fn value_pools(&self) -> &ValueBalance { + &self.value_pools + } + + /// Returns the size of this block. + pub fn size(&self) -> u32 { + self.size + } +} diff --git a/zebra-chain/src/chain_tip.rs b/zebra-chain/src/chain_tip.rs index 04e98ecbff7..9d37048a275 100644 --- a/zebra-chain/src/chain_tip.rs +++ b/zebra-chain/src/chain_tip.rs @@ -3,7 +3,6 @@ use std::{future, sync::Arc}; use chrono::{DateTime, Utc}; -use futures::{future::BoxFuture, Future, FutureExt}; use crate::{block, parameters::Network, transaction, BoxError}; @@ -64,11 +63,9 @@ pub trait ChainTip { /// Returns an error if Zebra is shutting down, or the state has permanently failed. /// /// See [`tokio::watch::Receiver::changed()`](https://docs.rs/tokio/latest/tokio/sync/watch/struct.Receiver.html#method.changed) for details. - // - // TODO: - // Use async_fn_in_trait or return_position_impl_trait_in_trait when one of them stabilises: - // https://github.com/rust-lang/rust/issues/91611 - fn best_tip_changed(&mut self) -> BestTipChanged; + fn best_tip_changed( + &mut self, + ) -> impl std::future::Future> + Send; /// Mark the current best tip as seen. /// @@ -123,33 +120,6 @@ pub trait ChainTip { } } -/// A future for the [`ChainTip::best_tip_changed()`] method. -/// See that method for details. -pub struct BestTipChanged<'f> { - fut: BoxFuture<'f, Result<(), BoxError>>, -} - -impl<'f> BestTipChanged<'f> { - /// Returns a new [`BestTipChanged`] containing `fut`. - pub fn new(fut: Fut) -> Self - where - Fut: Future> + Send + 'f, - { - Self { fut: Box::pin(fut) } - } -} - -impl<'f> Future for BestTipChanged<'f> { - type Output = Result<(), BoxError>; - - fn poll( - mut self: std::pin::Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - ) -> std::task::Poll { - self.fut.poll_unpin(cx) - } -} - /// A chain tip that is always empty and never changes. /// /// Used in production for isolated network connections, @@ -183,8 +153,8 @@ impl ChainTip for NoChainTip { } /// The [`NoChainTip`] best tip never changes, so this never returns. - fn best_tip_changed(&mut self) -> BestTipChanged { - BestTipChanged::new(future::pending()) + async fn best_tip_changed(&mut self) -> Result<(), BoxError> { + future::pending().await } /// The [`NoChainTip`] best tip never changes, so this does nothing. diff --git a/zebra-chain/src/chain_tip/mock.rs b/zebra-chain/src/chain_tip/mock.rs index f1fc8fb6e27..500218fd409 100644 --- a/zebra-chain/src/chain_tip/mock.rs +++ b/zebra-chain/src/chain_tip/mock.rs @@ -3,15 +3,10 @@ use std::sync::Arc; use chrono::{DateTime, Utc}; -use futures::{future, FutureExt, TryFutureExt}; +use futures::TryFutureExt; use tokio::sync::watch; -use crate::{ - block, - chain_tip::{BestTipChanged, ChainTip}, - parameters::Network, - transaction, -}; +use crate::{block, chain_tip::ChainTip, parameters::Network, transaction, BoxError}; /// A sender to sets the values read by a [`MockChainTip`]. // @@ -127,23 +122,16 @@ impl ChainTip for MockChainTip { /// Marks the changed channel as seen when the returned future completes. // // Update this method when each new mock field is added. - fn best_tip_changed(&mut self) -> BestTipChanged { - // A future that returns when the first watch channel has changed - let select_changed = future::select_all([ - // Erase the differing future types for each channel, and map their error types - BestTipChanged::new(self.best_tip_height.changed().err_into()), - BestTipChanged::new(self.best_tip_hash.changed().err_into()), - BestTipChanged::new(self.best_tip_block_time.changed().err_into()), - BestTipChanged::new( - self.estimated_distance_to_network_chain_tip - .changed() - .err_into(), - ), - ]) - // Map the select result to the expected type, dropping the unused channels - .map(|(changed_result, _changed_index, _remaining_futures)| changed_result); - - BestTipChanged::new(select_changed) + async fn best_tip_changed(&mut self) -> Result<(), BoxError> { + // A future that returns when the first watch channel has changed. + // Erase the differing future types for each channel, and map their error types, and + // map the select result to the expected type, dropping the unused channels + tokio::select! { + result = self.best_tip_height.changed().err_into() => result.map(|_| ()), + result = self.best_tip_hash.changed().err_into() => result.map(|_| ()), + result = self.best_tip_block_time.changed().err_into() => result.map(|_| ()), + result = self.estimated_distance_to_network_chain_tip.changed().err_into() => result.map(|_| ()), + } } /// Marks all sender channels as seen. diff --git a/zebra-chain/src/diagnostic/task/future.rs b/zebra-chain/src/diagnostic/task/future.rs index a119d7c6234..411c242c51c 100644 --- a/zebra-chain/src/diagnostic/task/future.rs +++ b/zebra-chain/src/diagnostic/task/future.rs @@ -59,7 +59,7 @@ impl CheckForPanics for JoinError { task_cancelled } else { - panic!("task unexpectedly exited with: {:?}", task_cancelled) + panic!("task unexpectedly exited with: {task_cancelled:?}") } } } diff --git a/zebra-chain/src/diagnostic/task/thread.rs b/zebra-chain/src/diagnostic/task/thread.rs index 517aef01411..75682647ccd 100644 --- a/zebra-chain/src/diagnostic/task/thread.rs +++ b/zebra-chain/src/diagnostic/task/thread.rs @@ -43,7 +43,7 @@ where thread_output } else { - panic!("thread unexpectedly exited with: {:?}", thread_output) + panic!("thread unexpectedly exited with: {thread_output:?}") } } diff --git a/zebra-chain/src/error.rs b/zebra-chain/src/error.rs index a3182a21feb..9f84be249f5 100644 --- a/zebra-chain/src/error.rs +++ b/zebra-chain/src/error.rs @@ -1,6 +1,10 @@ //! Errors that can occur inside any `zebra-chain` submodule. + +use std::{io, sync::Arc}; use thiserror::Error; +// TODO: Move all these enums into a common enum at the bottom. + /// Errors related to random bytes generation. #[derive(Error, Copy, Clone, Debug, PartialEq, Eq)] pub enum RandError { @@ -51,3 +55,49 @@ pub enum AddressError { #[error("Randomness did not hash into the Jubjub group for producing a new diversifier")] DiversifierGenerationFailure, } + +/// `zebra-chain`'s errors +#[derive(Clone, Error, Debug)] +pub enum Error { + /// Invalid consensus branch ID. + #[error("invalid consensus branch id")] + InvalidConsensusBranchId, + + /// Zebra's type could not be converted to its librustzcash equivalent. + #[error("Zebra's type could not be converted to its librustzcash equivalent: {0}")] + Conversion(#[from] Arc), + + /// The transaction is missing a network upgrade. + #[error("the transaction is missing a network upgrade")] + MissingNetworkUpgrade, +} + +/// Allow converting `io::Error` to `Error`; we need this since we +/// use `Arc` in `Error::Conversion`. +impl From for Error { + fn from(value: io::Error) -> Self { + Arc::new(value).into() + } +} + +// We need to implement this manually because io::Error does not implement +// PartialEq. +impl PartialEq for Error { + fn eq(&self, other: &Self) -> bool { + match self { + Error::InvalidConsensusBranchId => matches!(other, Error::InvalidConsensusBranchId), + Error::Conversion(e) => { + if let Error::Conversion(o) = other { + // Not perfect, but good enough for testing, which + // is the main purpose for our usage of PartialEq for errors + e.to_string() == o.to_string() + } else { + false + } + } + Error::MissingNetworkUpgrade => matches!(other, Error::MissingNetworkUpgrade), + } + } +} + +impl Eq for Error {} diff --git a/zebra-chain/src/history_tree.rs b/zebra-chain/src/history_tree.rs index 613bf0616d7..b7a5e96297d 100644 --- a/zebra-chain/src/history_tree.rs +++ b/zebra-chain/src/history_tree.rs @@ -30,7 +30,7 @@ pub enum HistoryTreeError { #[non_exhaustive] InnerError { inner: zcash_history::Error }, - #[error("I/O error")] + #[error("I/O error: {0}")] IOError(#[from] io::Error), } @@ -102,7 +102,10 @@ impl NonEmptyHistoryTree { )?; InnerHistoryTree::PreOrchard(tree) } - NetworkUpgrade::Nu5 | NetworkUpgrade::Nu6 | NetworkUpgrade::Nu7 => { + NetworkUpgrade::Nu5 + | NetworkUpgrade::Nu6 + | NetworkUpgrade::Nu6_1 + | NetworkUpgrade::Nu7 => { let tree = Tree::::new_from_cache( network, network_upgrade, @@ -156,7 +159,10 @@ impl NonEmptyHistoryTree { )?; (InnerHistoryTree::PreOrchard(tree), entry) } - NetworkUpgrade::Nu5 | NetworkUpgrade::Nu6 | NetworkUpgrade::Nu7 => { + NetworkUpgrade::Nu5 + | NetworkUpgrade::Nu6 + | NetworkUpgrade::Nu6_1 + | NetworkUpgrade::Nu7 => { let (tree, entry) = Tree::::new_from_block( network, block, diff --git a/zebra-chain/src/history_tree/tests/vectors.rs b/zebra-chain/src/history_tree/tests/vectors.rs index e65da99b676..e8aeeeb447d 100644 --- a/zebra-chain/src/history_tree/tests/vectors.rs +++ b/zebra-chain/src/history_tree/tests/vectors.rs @@ -55,7 +55,7 @@ fn push_and_prune_for_network_upgrade( assert_eq!(first_commitment, ChainHistoryActivationReserved); } - // Build initial history tree tree with only the first block + // Build initial history tree with only the first block let first_sapling_root = sapling::tree::Root::try_from(**sapling_roots.get(&height).expect("test vector exists"))?; let mut tree = NonEmptyHistoryTree::from_block( diff --git a/zebra-chain/src/lib.rs b/zebra-chain/src/lib.rs index dd6fa1a1abf..e2755e040bc 100644 --- a/zebra-chain/src/lib.rs +++ b/zebra-chain/src/lib.rs @@ -20,6 +20,7 @@ extern crate tracing; pub mod amount; pub mod block; +pub mod block_info; pub mod chain_sync_status; pub mod chain_tip; pub mod common; @@ -41,9 +42,11 @@ pub mod transparent; pub mod value_balance; pub mod work; -#[cfg(feature = "tx-v6")] +#[cfg(feature = "tx_v6")] pub mod orchard_zsa; +pub use error::Error; + #[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 5c258723c79..19d70bdf767 100644 --- a/zebra-chain/src/orchard.rs +++ b/zebra-chain/src/orchard.rs @@ -24,7 +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}; +pub use shielded_data_flavor::{OrchardDomainCommon, OrchardVanilla, ShieldedDataFlavor}; -#[cfg(feature = "tx-v6")] +#[cfg(feature = "tx_v6")] pub use shielded_data_flavor::OrchardZSA; diff --git a/zebra-chain/src/orchard/commitment.rs b/zebra-chain/src/orchard/commitment.rs index 2b809bdbd27..c97e5053f20 100644 --- a/zebra-chain/src/orchard/commitment.rs +++ b/zebra-chain/src/orchard/commitment.rs @@ -14,7 +14,7 @@ use halo2::{ use lazy_static::lazy_static; use rand_core::{CryptoRng, RngCore}; -#[cfg(feature = "tx-v6")] +#[cfg(feature = "tx_v6")] use orchard::{ note::AssetBase, value::{ValueCommitTrapdoor, ValueSum}, @@ -243,7 +243,7 @@ impl ValueCommitment { { let rcv = generate_trapdoor(csprng)?; - #[cfg(feature = "tx-v6")] + #[cfg(feature = "tx_v6")] let vc = Self::new( rcv, // TODO: Make the `ValueSum::from_raw` function public in the `orchard` crate @@ -253,7 +253,7 @@ impl ValueCommitment { AssetBase::native(), ); - #[cfg(not(feature = "tx-v6"))] + #[cfg(not(feature = "tx_v6"))] let vc = Self::new(rcv, value); Ok(vc) @@ -264,14 +264,14 @@ impl ValueCommitment { /// ValueCommit^Orchard(v) := /// /// - #[cfg(not(feature = "tx-v6"))] + #[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")] + #[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`. diff --git a/zebra-chain/src/orchard/note/ciphertexts.rs b/zebra-chain/src/orchard/note/ciphertexts.rs index 7817d531c0b..1057cc63f45 100644 --- a/zebra-chain/src/orchard/note/ciphertexts.rs +++ b/zebra-chain/src/orchard/note/ciphertexts.rs @@ -32,6 +32,12 @@ impl TryFrom<&[u8]> for EncryptedNote { } } +impl AsRef<[u8]> for EncryptedNote { + fn as_ref(&self) -> &[u8] { + &self.0 + } +} + impl ZcashSerialize for EncryptedNote { fn zcash_serialize(&self, mut writer: W) -> Result<(), io::Error> { writer.write_all(&self.0[..])?; @@ -113,7 +119,7 @@ mod tests { serialization::{ZcashDeserialize, ZcashSerialize}, }; - #[cfg(feature = "tx-v6")] + #[cfg(feature = "tx_v6")] use crate::orchard::OrchardZSA; use proptest::prelude::*; @@ -138,7 +144,7 @@ mod tests { } - #[cfg(feature = "tx-v6")] + #[cfg(feature = "tx_v6")] #[test] fn encrypted_ciphertext_roundtrip_orchard_zsa(ec in any::<::EncryptedNote>()) { let _init_guard = zebra_test::init(); diff --git a/zebra-chain/src/orchard/shielded_data.rs b/zebra-chain/src/orchard/shielded_data.rs index dc0453ef6ea..24c180c13f9 100644 --- a/zebra-chain/src/orchard/shielded_data.rs +++ b/zebra-chain/src/orchard/shielded_data.rs @@ -20,10 +20,10 @@ use crate::{ }, }; -#[cfg(feature = "tx-v6")] +#[cfg(feature = "tx_v6")] use crate::orchard_zsa::compute_burn_value_commitment; -#[cfg(feature = "tx-v6")] +#[cfg(feature = "tx_v6")] use orchard::{note::AssetBase, value::ValueSum}; use super::{OrchardVanilla, ShieldedDataFlavor}; @@ -31,11 +31,11 @@ use super::{OrchardVanilla, ShieldedDataFlavor}; /// A bundle of [`Action`] descriptions and signature data. #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] #[cfg_attr( - not(feature = "tx-v6"), + not(feature = "tx_v6"), serde(bound(serialize = "Flavor::EncryptedNote: serde::Serialize")) )] #[cfg_attr( - feature = "tx-v6", + feature = "tx_v6", serde(bound( serialize = "Flavor::EncryptedNote: serde::Serialize, Flavor::BurnType: serde::Serialize", deserialize = "Flavor::BurnType: serde::Deserialize<'de>" @@ -61,7 +61,7 @@ pub struct ShieldedData { /// Denoted as `bindingSigOrchard` in the spec. pub binding_sig: Signature, - #[cfg(feature = "tx-v6")] + #[cfg(feature = "tx_v6")] /// Assets intended for burning /// Denoted as `vAssetBurn` in the spec (ZIP 230). pub burn: Flavor::BurnType, @@ -123,13 +123,13 @@ impl ShieldedData { pub fn binding_verification_key(&self) -> reddsa::VerificationKeyBytes { let cv: ValueCommitment = self.actions().map(|action| action.cv).sum(); - #[cfg(not(feature = "tx-v6"))] + #[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")] + #[cfg(feature = "tx_v6")] let key = { let cv_balance = ValueCommitment::new( pallas::Scalar::zero(), @@ -339,6 +339,6 @@ impl ZcashDeserialize for Flags { // the reserved bits 2..7 of the flagsOrchard field MUST be zero." // https://zips.z.cash/protocol/protocol.pdf#txnencodingandconsensus Flags::from_bits(reader.read_u8()?) - .ok_or_else(|| SerializationError::Parse("invalid reserved orchard flags")) + .ok_or(SerializationError::Parse("invalid reserved orchard flags")) } } diff --git a/zebra-chain/src/orchard/shielded_data_flavor.rs b/zebra-chain/src/orchard/shielded_data_flavor.rs index 7dfae6d109a..14e7f366be8 100644 --- a/zebra-chain/src/orchard/shielded_data_flavor.rs +++ b/zebra-chain/src/orchard/shielded_data_flavor.rs @@ -4,16 +4,16 @@ use std::fmt::Debug; use serde::{de::DeserializeOwned, Serialize}; -use orchard::{domain::OrchardDomainCommon, orchard_flavor::OrchardFlavor}; +use orchard::orchard_flavor::OrchardFlavor; -pub use orchard::orchard_flavor::OrchardVanilla; +pub use orchard::{domain::OrchardDomainCommon, orchard_flavor::OrchardVanilla}; -#[cfg(feature = "tx-v6")] +#[cfg(feature = "tx_v6")] pub use orchard::{note::AssetBase, orchard_flavor::OrchardZSA, value::NoteValue}; use crate::serialization::{ZcashDeserialize, ZcashSerialize}; -#[cfg(feature = "tx-v6")] +#[cfg(feature = "tx_v6")] use crate::orchard_zsa::{Burn, BurnItem, NoBurn}; use super::note; @@ -46,11 +46,12 @@ pub trait ShieldedDataFlavor: OrchardFlavor { + Serialize + ZcashDeserialize + ZcashSerialize + + AsRef<[u8]> + 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")] + #[cfg(feature = "tx_v6")] type BurnType: Clone + Debug + ZcashDeserialize @@ -62,11 +63,11 @@ pub trait ShieldedDataFlavor: OrchardFlavor { impl ShieldedDataFlavor for OrchardVanilla { type EncryptedNote = note::EncryptedNote<{ OrchardVanilla::ENC_CIPHERTEXT_SIZE }>; - #[cfg(feature = "tx-v6")] + #[cfg(feature = "tx_v6")] type BurnType = NoBurn; } -#[cfg(feature = "tx-v6")] +#[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/sinsemilla.rs b/zebra-chain/src/orchard/sinsemilla.rs index 6fa8bad751d..ca721d7f234 100644 --- a/zebra-chain/src/orchard/sinsemilla.rs +++ b/zebra-chain/src/orchard/sinsemilla.rs @@ -3,8 +3,9 @@ use bitvec::prelude::*; use halo2::{ arithmetic::{Coordinates, CurveAffine, CurveExt}, - pasta::{group::Group, pallas}, + pasta::pallas, }; +use sinsemilla::HashDomain; /// [Coordinate Extractor for Pallas][concreteextractorpallas] /// @@ -25,17 +26,6 @@ pub fn extract_p(point: pallas::Point) -> pallas::Base { } } -/// Extract⊥ P: P ∪ {⊥} → P𝑥 ∪ {⊥} such that -/// -/// Extract⊥ P(︀⊥)︀ = ⊥ -/// Extract⊥ P(︀𝑃: P)︀ = ExtractP(𝑃). -/// -/// -pub fn extract_p_bottom(maybe_point: Option) -> Option { - // Maps an Option to Option by applying a function to a contained value. - maybe_point.map(extract_p) -} - /// GroupHash into Pallas, aka _GroupHash^P_ /// /// Produces a random point in the Pallas curve. The first input element acts @@ -50,113 +40,24 @@ pub fn pallas_group_hash(D: &[u8], M: &[u8]) -> pallas::Point { pallas::Point::hash_to_curve(domain_separator)(M) } -/// Q(D) := GroupHash^P(︀“z.cash:SinsemillaQ”, D) -/// -/// -#[allow(non_snake_case)] -fn Q(D: &[u8]) -> pallas::Point { - pallas_group_hash(b"z.cash:SinsemillaQ", D) -} - -/// S(j) := GroupHash^P(︀“z.cash:SinsemillaS”, LEBS2OSP32(I2LEBSP32(j))) -/// -/// S: {0 .. 2^k - 1} -> P^*, aka 10 bits hashed into the group -/// -/// -#[allow(non_snake_case)] -fn S(j: &BitSlice) -> pallas::Point { - // The value of j is a 10-bit value, therefore must never exceed 2^10 in - // value. - assert_eq!(j.len(), 10); - - // I2LEOSP_32(𝑗) - let mut leosp_32_j = [0u8; 4]; - leosp_32_j[..2].copy_from_slice(j.to_bitvec().as_raw_slice()); - - pallas_group_hash(b"z.cash:SinsemillaS", &leosp_32_j) -} - -/// Incomplete addition on the Pallas curve. -/// -/// P ∪ {⊥} × P ∪ {⊥} → P ∪ {⊥} -/// -/// -fn incomplete_addition( - left: Option, - right: Option, -) -> Option { - let identity = pallas::Point::identity(); - - match (left, right) { - (None, _) | (_, None) => None, - (Some(l), _) if l == identity => None, - (_, Some(r)) if r == identity => None, - (Some(l), Some(r)) if l == r => None, - // The inverse of l, (x, -y) - (Some(l), Some(r)) if l == -r => None, - (Some(l), Some(r)) => Some(l + r), - } -} - -/// "...an algebraic hash function with collision resistance (for fixed input -/// length) derived from assumed hardness of the Discrete Logarithm Problem on -/// the Pallas curve." -/// -/// SinsemillaHash is used in the definitions of Sinsemilla commitments and of -/// the Sinsemilla hash for the Orchard incremental Merkle tree (§ 5.4.1.3 -/// ‘MerkleCRH^Orchard Hash Function’). -/// -/// SinsemillaHashToPoint(𝐷: B^Y^\[N\] , 𝑀 : B ^[{0 .. 𝑘·𝑐}] ) → P ∪ {⊥} -/// -/// -/// -/// # Panics -/// -/// If `M` is greater than `k*c = 2530` bits. -#[allow(non_snake_case)] -pub fn sinsemilla_hash_to_point(D: &[u8], M: &BitVec) -> Option { - let k = 10; - let c = 253; - - assert!(M.len() <= k * c); - - let mut acc = Some(Q(D)); - - // Split M into n segments of k bits, where k = 10 and c = 253, padding - // the last segment with zeros. - // - // https://zips.z.cash/protocol/nu5.pdf#concretesinsemillahash - for chunk in M.chunks(k) { - // Pad each chunk with zeros. - let mut store = [0u8; 2]; - let bits = BitSlice::<_, Lsb0>::from_slice_mut(&mut store); - bits[..chunk.len()].copy_from_bitslice(chunk); - - acc = incomplete_addition(incomplete_addition(acc, Some(S(&bits[..k]))), acc); - } - - acc -} - /// Sinsemilla Hash Function /// /// "SinsemillaHash is an algebraic hash function with collision resistance (for /// fixed input length) derived from assumed hardness of the Discrete Logarithm /// Problem. It is designed by Sean Bowe and Daira Hopwood. The motivation for /// introducing a new discrete-log-based hash function (rather than using -/// PedersenHash) is to make efcient use of the lookups available in recent +/// PedersenHash) is to make efficient use of the lookups available in recent /// proof systems including Halo 2." /// /// SinsemillaHash: B^Y^\[N\] × B[{0 .. 𝑘·𝑐}] → P_𝑥 ∪ {⊥} /// /// -/// -/// # Panics -/// -/// If `M` is greater than `k*c = 2530` bits in `sinsemilla_hash_to_point`. #[allow(non_snake_case)] pub fn sinsemilla_hash(D: &[u8], M: &BitVec) -> Option { - extract_p_bottom(sinsemilla_hash_to_point(D, M)) + let domain = std::str::from_utf8(D).expect("must be valid UTF-8"); + let hash_domain = HashDomain::new(domain); + + hash_domain.hash(M.iter().map(|b| *b.as_ref())).into() } #[cfg(test)] @@ -165,40 +66,6 @@ mod tests { use super::*; use crate::orchard::tests::vectors; - #[cfg(test)] - fn x_from_str(s: &str) -> pallas::Base { - use halo2::pasta::group::ff::PrimeField; - - pallas::Base::from_str_vartime(s).unwrap() - } - - #[test] - #[allow(non_snake_case)] - fn sinsemilla_single_test_vector() { - use halo2::pasta::group::Curve; - - let D = b"z.cash:test-Sinsemilla"; - let M = bitvec![ - u8, Lsb0; 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, - 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, - ]; - - let test_vector = pallas::Affine::from_xy( - x_from_str( - "19681977528872088480295086998934490146368213853811658798708435106473481753752", - ), - x_from_str( - "14670850419772526047574141291705097968771694788047376346841674072293161339903", - ), - ) - .unwrap(); - - assert_eq!( - sinsemilla_hash_to_point(&D[..], &M).expect("").to_affine(), - test_vector - ) - } - // Checks Sinsemilla hashes to point and to bytes (aka the x-coordinate // bytes of a point) with: // - One of two domains. @@ -207,17 +74,12 @@ mod tests { #[test] #[allow(non_snake_case)] fn sinsemilla_hackworks_test_vectors() { - use halo2::pasta::group::{ff::PrimeField, GroupEncoding}; + use halo2::pasta::group::ff::PrimeField; for tv in tests::vectors::SINSEMILLA.iter() { let D = tv.domain.as_slice(); let M: &BitVec = &tv.msg.iter().collect(); - assert_eq!( - sinsemilla_hash_to_point(D, M).expect("should not fail per Theorem 5.4.4"), - pallas::Point::from_bytes(&tv.point).unwrap() - ); - assert_eq!( sinsemilla_hash(D, M).expect("should not fail per Theorem 5.4.4"), pallas::Base::from_repr(tv.hash).unwrap() diff --git a/zebra-chain/src/orchard/tests/vectors/sinsemilla.rs b/zebra-chain/src/orchard/tests/vectors/sinsemilla.rs index 73d67b77570..ee04d799846 100644 --- a/zebra-chain/src/orchard/tests/vectors/sinsemilla.rs +++ b/zebra-chain/src/orchard/tests/vectors/sinsemilla.rs @@ -3,7 +3,6 @@ use lazy_static::lazy_static; pub struct TestVector { pub(crate) domain: Vec, pub(crate) msg: Vec, - pub(crate) point: [u8; 32], pub(crate) hash: [u8; 32], } @@ -21,11 +20,6 @@ lazy_static! { false, false, true, true, false, true, true, false, true, true, true, true, false, true, true, false, ], - point: [ - 0x98, 0x54, 0xaa, 0x38, 0x43, 0x63, 0xb5, 0x70, 0x8e, 0x06, 0xb4, 0x19, 0xb6, 0x43, - 0x58, 0x68, 0x39, 0x65, 0x3f, 0xba, 0x5a, 0x78, 0x2d, 0x2d, 0xb1, 0x4c, 0xed, 0x13, - 0xc1, 0x9a, 0x83, 0xab, - ], hash: [ 0x98, 0x54, 0xaa, 0x38, 0x43, 0x63, 0xb5, 0x70, 0x8e, 0x06, 0xb4, 0x19, 0xb6, 0x43, 0x58, 0x68, 0x39, 0x65, 0x3f, 0xba, 0x5a, 0x78, 0x2d, 0x2d, 0xb1, 0x4c, 0xed, 0x13, @@ -50,11 +44,6 @@ lazy_static! { true, true, false, true, false, true, false, true, true, false, true, false, false, false, true, true, false, false, false, true, true, false, true, ], - point: [ - 0xed, 0x5b, 0x98, 0x8e, 0x4e, 0x98, 0x17, 0x1f, 0x61, 0x8f, 0xee, 0xb1, 0x23, 0xe5, - 0xcd, 0x0d, 0xc2, 0xd3, 0x67, 0x11, 0xc5, 0x06, 0xd5, 0xbe, 0x11, 0x5c, 0xfe, 0x38, - 0x8f, 0x03, 0xc4, 0x80, - ], hash: [ 0xed, 0x5b, 0x98, 0x8e, 0x4e, 0x98, 0x17, 0x1f, 0x61, 0x8f, 0xee, 0xb1, 0x23, 0xe5, 0xcd, 0x0d, 0xc2, 0xd3, 0x67, 0x11, 0xc5, 0x06, 0xd5, 0xbe, 0x11, 0x5c, 0xfe, 0x38, @@ -76,11 +65,6 @@ lazy_static! { false, true, false, true, true, false, true, true, false, false, true, true, true, true, true, false, false, false, true, false, false, true, false, false, ], - point: [ - 0xd9, 0x5e, 0xe5, 0x8f, 0xbd, 0xaa, 0x6f, 0x3d, 0xe5, 0xe4, 0xfd, 0x7a, 0xfc, 0x35, - 0xfa, 0x9d, 0xcf, 0xe8, 0x2a, 0xd1, 0x93, 0x06, 0xb0, 0x7e, 0x6c, 0xda, 0x0c, 0x30, - 0xe5, 0x98, 0x34, 0x07, - ], hash: [ 0xd9, 0x5e, 0xe5, 0x8f, 0xbd, 0xaa, 0x6f, 0x3d, 0xe5, 0xe4, 0xfd, 0x7a, 0xfc, 0x35, 0xfa, 0x9d, 0xcf, 0xe8, 0x2a, 0xd1, 0x93, 0x06, 0xb0, 0x7e, 0x6c, 0xda, 0x0c, 0x30, @@ -111,11 +95,6 @@ lazy_static! { true, true, true, false, true, false, true, false, false, false, true, true, false, false, true, false, false, false, true, true, false, false, ], - point: [ - 0x6a, 0x92, 0x4b, 0x41, 0x39, 0x84, 0x29, 0x91, 0x0a, 0x78, 0x83, 0x2b, 0x61, 0x19, - 0x2a, 0x0b, 0x67, 0x40, 0xd6, 0x27, 0x77, 0xeb, 0x71, 0x54, 0x50, 0x32, 0xeb, 0x6c, - 0xe9, 0x3e, 0xc9, 0xb8, - ], hash: [ 0x6a, 0x92, 0x4b, 0x41, 0x39, 0x84, 0x29, 0x91, 0x0a, 0x78, 0x83, 0x2b, 0x61, 0x19, 0x2a, 0x0b, 0x67, 0x40, 0xd6, 0x27, 0x77, 0xeb, 0x71, 0x54, 0x50, 0x32, 0xeb, 0x6c, @@ -135,11 +114,6 @@ lazy_static! { true, false, false, false, false, true, true, true, false, true, false, false, false, false, false, false, true, false, false, false, false, false, ], - point: [ - 0xdc, 0x5f, 0xf0, 0x5b, 0x6f, 0x18, 0xb0, 0x76, 0xb6, 0x12, 0x82, 0x37, 0xa7, 0x59, - 0xed, 0xc7, 0xc8, 0x77, 0x8c, 0x70, 0x22, 0x2c, 0x79, 0xb7, 0x34, 0x03, 0x7b, 0x69, - 0x39, 0x3a, 0xbf, 0xbe, - ], hash: [ 0xdc, 0x5f, 0xf0, 0x5b, 0x6f, 0x18, 0xb0, 0x76, 0xb6, 0x12, 0x82, 0x37, 0xa7, 0x59, 0xed, 0xc7, 0xc8, 0x77, 0x8c, 0x70, 0x22, 0x2c, 0x79, 0xb7, 0x34, 0x03, 0x7b, 0x69, @@ -170,11 +144,6 @@ lazy_static! { false, false, true, false, true, true, false, true, true, true, false, true, true, true, false, false, true, true, ], - point: [ - 0xc7, 0x6c, 0x8d, 0x7c, 0x43, 0x55, 0x04, 0x1b, 0xd7, 0xa7, 0xc9, 0x9b, 0x54, 0x86, - 0x44, 0x19, 0x6f, 0x41, 0x94, 0x56, 0x20, 0x75, 0x37, 0xc2, 0x82, 0x85, 0x8a, 0x9b, - 0x19, 0x2d, 0x07, 0xbb, - ], hash: [ 0xc7, 0x6c, 0x8d, 0x7c, 0x43, 0x55, 0x04, 0x1b, 0xd7, 0xa7, 0xc9, 0x9b, 0x54, 0x86, 0x44, 0x19, 0x6f, 0x41, 0x94, 0x56, 0x20, 0x75, 0x37, 0xc2, 0x82, 0x85, 0x8a, 0x9b, @@ -197,11 +166,6 @@ lazy_static! { true, false, false, true, false, true, true, false, true, true, true, true, true, false, true, true, false, false, false, false, false, false, false, false, ], - point: [ - 0x1a, 0xe8, 0x25, 0xeb, 0x42, 0xd7, 0x4e, 0x1b, 0xca, 0x7e, 0xe8, 0xa1, 0xf8, 0xf3, - 0xde, 0xd8, 0x01, 0xff, 0xcd, 0x1f, 0x22, 0xba, 0x75, 0xc3, 0x4b, 0xd6, 0xe0, 0x6a, - 0x2c, 0x7c, 0x5a, 0xa0, - ], hash: [ 0x1a, 0xe8, 0x25, 0xeb, 0x42, 0xd7, 0x4e, 0x1b, 0xca, 0x7e, 0xe8, 0xa1, 0xf8, 0xf3, 0xde, 0xd8, 0x01, 0xff, 0xcd, 0x1f, 0x22, 0xba, 0x75, 0xc3, 0x4b, 0xd6, 0xe0, 0x6a, @@ -231,11 +195,6 @@ lazy_static! { true, false, false, false, false, true, true, false, false, false, true, true, true, true, ], - point: [ - 0x38, 0xcf, 0xa6, 0x00, 0xaf, 0xd8, 0x67, 0x0e, 0x1f, 0x9a, 0x79, 0xcb, 0x22, 0x42, - 0x5f, 0xa9, 0x50, 0xcc, 0x4d, 0x3a, 0x3f, 0x5a, 0xfe, 0x39, 0x76, 0xd7, 0x1b, 0xb1, - 0x11, 0x46, 0x0c, 0x2b, - ], hash: [ 0x38, 0xcf, 0xa6, 0x00, 0xaf, 0xd8, 0x67, 0x0e, 0x1f, 0x9a, 0x79, 0xcb, 0x22, 0x42, 0x5f, 0xa9, 0x50, 0xcc, 0x4d, 0x3a, 0x3f, 0x5a, 0xfe, 0x39, 0x76, 0xd7, 0x1b, 0xb1, @@ -256,11 +215,6 @@ lazy_static! { true, true, false, true, true, true, true, false, true, false, true, false, false, false, ], - point: [ - 0x82, 0x6f, 0xcb, 0xed, 0xfc, 0x83, 0xb9, 0xfa, 0xa5, 0x71, 0x1a, 0xab, 0x59, 0xbf, - 0xc9, 0x1b, 0xd4, 0x45, 0x58, 0x14, 0x67, 0x72, 0x5d, 0xde, 0x94, 0x1d, 0x58, 0xe6, - 0x26, 0x56, 0x66, 0x15, - ], hash: [ 0x82, 0x6f, 0xcb, 0xed, 0xfc, 0x83, 0xb9, 0xfa, 0xa5, 0x71, 0x1a, 0xab, 0x59, 0xbf, 0xc9, 0x1b, 0xd4, 0x45, 0x58, 0x14, 0x67, 0x72, 0x5d, 0xde, 0x94, 0x1d, 0x58, 0xe6, @@ -284,11 +238,6 @@ lazy_static! { false, false, true, true, false, false, true, false, true, false, false, false, true, true, false, ], - point: [ - 0x0b, 0xf0, 0x6c, 0xe8, 0x10, 0x05, 0xb8, 0x1a, 0x14, 0x80, 0x9f, 0xa6, 0xeb, 0xcb, - 0x94, 0xe2, 0xb6, 0x37, 0x5f, 0x87, 0xce, 0x51, 0x95, 0x8c, 0x94, 0x98, 0xed, 0x1a, - 0x31, 0x3c, 0x6a, 0x94, - ], hash: [ 0x0b, 0xf0, 0x6c, 0xe8, 0x10, 0x05, 0xb8, 0x1a, 0x14, 0x80, 0x9f, 0xa6, 0xeb, 0xcb, 0x94, 0xe2, 0xb6, 0x37, 0x5f, 0x87, 0xce, 0x51, 0x95, 0x8c, 0x94, 0x98, 0xed, 0x1a, @@ -301,11 +250,6 @@ lazy_static! { 0x6e, 0x73, 0x65, 0x6d, 0x69, 0x6c, 0x6c, 0x61, ], msg: vec![true, false, true, true, true, false, true, false], - point: [ - 0x80, 0x6a, 0xcc, 0x24, 0x7a, 0xc9, 0xba, 0x90, 0xd2, 0x5f, 0x58, 0x3d, 0xad, 0xb5, - 0xe0, 0xee, 0x5c, 0x03, 0xe1, 0xab, 0x35, 0x70, 0xb3, 0x62, 0xb4, 0xbe, 0x5a, 0x8b, - 0xce, 0xb6, 0x0b, 0x00, - ], hash: [ 0x80, 0x6a, 0xcc, 0x24, 0x7a, 0xc9, 0xba, 0x90, 0xd2, 0x5f, 0x58, 0x3d, 0xad, 0xb5, 0xe0, 0xee, 0x5c, 0x03, 0xe1, 0xab, 0x35, 0x70, 0xb3, 0x62, 0xb4, 0xbe, 0x5a, 0x8b, diff --git a/zebra-chain/src/orchard/tree.rs b/zebra-chain/src/orchard/tree.rs index b3433a99c6d..81dd3f8e089 100644 --- a/zebra-chain/src/orchard/tree.rs +++ b/zebra-chain/src/orchard/tree.rs @@ -18,10 +18,9 @@ use std::{ }; use bitvec::prelude::*; -use bridgetree::NonEmptyFrontier; use halo2::pasta::{group::ff::PrimeField, pallas}; use hex::ToHex; -use incrementalmerkletree::Hashable; +use incrementalmerkletree::{frontier::NonEmptyFrontier, Hashable}; use lazy_static::lazy_static; use thiserror::Error; use zcash_primitives::merkle_tree::HashSer; @@ -248,7 +247,7 @@ impl ToHex for Node { /// [`z_gettreestate`][2] RPC requires [`CommitmentTree`][3]s. Implementing /// [`HashSer`] for [`Node`]s allows the conversion. /// -/// [1]: bridgetree::Frontier +/// [1]: incrementalmerkletree::frontier::Frontier /// [2]: https://zcash.github.io/rpc/z_gettreestate.html /// [3]: incrementalmerkletree::frontier::CommitmentTree impl HashSer for Node { @@ -348,7 +347,7 @@ pub struct NoteCommitmentTree { /// /// /// Note: MerkleDepth^Orchard = MERKLE_DEPTH = 32. - inner: bridgetree::Frontier, + inner: incrementalmerkletree::frontier::Frontier, /// A cached root of the tree. /// @@ -637,7 +636,7 @@ impl NoteCommitmentTree { /// Serializes [`Self`] to a format compatible with `zcashd`'s RPCs. pub fn to_rpc_bytes(&self) -> Vec { - // Convert the tree from [`Frontier`](bridgetree::Frontier) to + // Convert the tree from [`Frontier`](incrementalmerkletree::frontier::Frontier) to // [`CommitmentTree`](merkle_tree::CommitmentTree). let tree = incrementalmerkletree::frontier::CommitmentTree::from_frontier(&self.inner); @@ -665,7 +664,7 @@ impl Clone for NoteCommitmentTree { impl Default for NoteCommitmentTree { fn default() -> Self { Self { - inner: bridgetree::Frontier::empty(), + inner: incrementalmerkletree::frontier::Frontier::empty(), cached_root: Default::default(), } } diff --git a/zebra-chain/src/orchard_zsa/burn.rs b/zebra-chain/src/orchard_zsa/burn.rs index bc738c2a7ef..8ba4696e82c 100644 --- a/zebra-chain/src/orchard_zsa/burn.rs +++ b/zebra-chain/src/orchard_zsa/burn.rs @@ -22,7 +22,7 @@ impl ZcashSerialize for AssetBase { 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!")) + .ok_or(SerializationError::Parse("Invalid orchard_zsa AssetBase!")) } } diff --git a/zebra-chain/src/parameters/network.rs b/zebra-chain/src/parameters/network.rs index ec4d26301bb..81d9096d786 100644 --- a/zebra-chain/src/parameters/network.rs +++ b/zebra-chain/src/parameters/network.rs @@ -29,14 +29,19 @@ pub enum NetworkKind { /// A test network. Testnet, - /// Regtest mode, not yet implemented - // TODO: Add `new_regtest()` and `is_regtest` methods on `Network`. + /// Regtest mode Regtest, } impl From for NetworkKind { - fn from(network: Network) -> Self { - network.kind() + fn from(net: Network) -> Self { + NetworkKind::from(&net) + } +} + +impl From<&Network> for NetworkKind { + fn from(net: &Network) -> Self { + net.kind() } } @@ -85,6 +90,16 @@ impl NetworkKind { "test".to_string() } } + + /// Returns the 2 bytes prefix for Bech32m-encoded transparent TEX + /// payment addresses for the network as defined in [ZIP-320](https://zips.z.cash/zip-0320.html). + pub fn tex_address_prefix(self) -> [u8; 2] { + // TODO: Add this bytes to `zcash_primitives::constants`? + match self { + Self::Mainnet => [0x1c, 0xb8], + Self::Testnet | Self::Regtest => [0x1d, 0x25], + } + } } impl From for &'static str { @@ -150,14 +165,10 @@ impl Network { /// Creates a new [`Network::Testnet`] with `Regtest` parameters and the provided network upgrade activation heights. pub fn new_regtest( - nu5_activation_height: Option, - nu6_activation_height: Option, - nu7_activation_height: Option, + configured_activation_heights: testnet::ConfiguredActivationHeights, ) -> Self { Self::new_configured_testnet(testnet::Parameters::new_regtest( - nu5_activation_height, - nu6_activation_height, - nu7_activation_height, + configured_activation_heights, )) } @@ -188,6 +199,16 @@ impl Network { } } + /// Returns [`NetworkKind::Testnet`] on Testnet and Regtest, or [`NetworkKind::Mainnet`] on Mainnet. + /// + /// This is used for transparent addresses, as the address prefix is the same on Regtest as it is on Testnet. + pub fn t_addr_kind(&self) -> NetworkKind { + match self { + Network::Mainnet => NetworkKind::Mainnet, + Network::Testnet(_) => NetworkKind::Testnet, + } + } + /// Returns an iterator over [`Network`] variants. pub fn iter() -> impl Iterator { [Self::Mainnet, Self::new_default_testnet()].into_iter() diff --git a/zebra-chain/src/parameters/network/magic.rs b/zebra-chain/src/parameters/network/magic.rs index 692a1b8d1ab..236e0149091 100644 --- a/zebra-chain/src/parameters/network/magic.rs +++ b/zebra-chain/src/parameters/network/magic.rs @@ -29,7 +29,7 @@ impl Network { } #[cfg(test)] -mod proptest { +mod magic_proptest { use proptest::prelude::*; diff --git a/zebra-chain/src/parameters/network/subsidy.rs b/zebra-chain/src/parameters/network/subsidy.rs index df3d8c966f4..effd77383a7 100644 --- a/zebra-chain/src/parameters/network/subsidy.rs +++ b/zebra-chain/src/parameters/network/subsidy.rs @@ -12,14 +12,15 @@ //! Typically, consensus parameters are accessed via a function that takes a //! `Network` and `block::Height`. -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; use lazy_static::lazy_static; use crate::{ - amount::COIN, + amount::{self, Amount, NonNegative, COIN}, block::{Height, HeightDiff}, parameters::{Network, NetworkUpgrade}, + transaction::Transaction, transparent, }; @@ -54,7 +55,7 @@ pub(crate) const FIRST_HALVING_TESTNET: Height = Height(1_116_000); const FIRST_HALVING_REGTEST: Height = Height(287); /// The funding stream receiver categories. -#[derive(Deserialize, Clone, Copy, Debug, Eq, Hash, PartialEq)] +#[derive(Serialize, Deserialize, Clone, Copy, Debug, Eq, Hash, PartialEq)] pub enum FundingStreamReceiver { /// The Electric Coin Company (Bootstrap Foundation) funding stream. #[serde(rename = "ECC")] @@ -77,8 +78,8 @@ impl FundingStreamReceiver { /// [ZIP-1014]: https://zips.z.cash/zip-1014#abstract /// [`zcashd`]: https://github.com/zcash/zcash/blob/3f09cfa00a3c90336580a127e0096d99e25a38d6/src/consensus/funding.cpp#L13-L32 /// [ZIP-1015]: https://zips.z.cash/zip-1015 - pub fn info(&self, is_nu6: bool) -> (&'static str, &'static str) { - if is_nu6 { + pub fn info(&self, is_post_nu6: bool) -> (&'static str, &'static str) { + if is_post_nu6 { ( match self { FundingStreamReceiver::Ecc => "Electric Coin Company", @@ -600,3 +601,163 @@ pub fn height_for_halving(halving: u32, network: &Network) -> Option { let height = u32::try_from(height).ok()?; height.try_into().ok() } + +/// Returns the `fs.Value(height)` for each stream receiver +/// as described in [protocol specification §7.8][7.8] +/// +/// [7.8]: https://zips.z.cash/protocol/protocol.pdf#subsidies +pub fn funding_stream_values( + height: Height, + network: &Network, + expected_block_subsidy: Amount, +) -> Result>, crate::amount::Error> { + let canopy_height = NetworkUpgrade::Canopy.activation_height(network).unwrap(); + let mut results = HashMap::new(); + + if height >= canopy_height { + let funding_streams = network.funding_streams(height); + if funding_streams.height_range().contains(&height) { + for (&receiver, recipient) in funding_streams.recipients() { + // - Spec equation: `fs.value = floor(block_subsidy(height)*(fs.numerator/fs.denominator))`: + // https://zips.z.cash/protocol/protocol.pdf#subsidies + // - In Rust, "integer division rounds towards zero": + // https://doc.rust-lang.org/stable/reference/expressions/operator-expr.html#arithmetic-and-logical-binary-operators + // This is the same as `floor()`, because these numbers are all positive. + let amount_value = ((expected_block_subsidy * recipient.numerator())? + / FUNDING_STREAM_RECEIVER_DENOMINATOR)?; + + results.insert(receiver, amount_value); + } + } + } + + Ok(results) +} + +/// Block subsidy errors. +#[derive(thiserror::Error, Clone, Debug, PartialEq, Eq)] +#[allow(missing_docs)] +pub enum SubsidyError { + #[error("no coinbase transaction in block")] + NoCoinbase, + + #[error("funding stream expected output not found")] + FundingStreamNotFound, + + #[error("miner fees are invalid")] + InvalidMinerFees, + + #[error("a sum of amounts overflowed")] + SumOverflow, + + #[error("unsupported height")] + UnsupportedHeight, + + #[error("invalid amount")] + InvalidAmount(#[from] amount::Error), +} + +/// The divisor used for halvings. +/// +/// `1 << Halving(height)`, as described in [protocol specification §7.8][7.8] +/// +/// [7.8]: https://zips.z.cash/protocol/protocol.pdf#subsidies +/// +/// Returns `None` if the divisor would overflow a `u64`. +pub fn halving_divisor(height: Height, network: &Network) -> Option { + // Some far-future shifts can be more than 63 bits + 1u64.checked_shl(num_halvings(height, network)) +} + +/// The halving index for a block height and network. +/// +/// `Halving(height)`, as described in [protocol specification §7.8][7.8] +/// +/// [7.8]: https://zips.z.cash/protocol/protocol.pdf#subsidies +pub fn num_halvings(height: Height, network: &Network) -> u32 { + let slow_start_shift = network.slow_start_shift(); + let blossom_height = NetworkUpgrade::Blossom + .activation_height(network) + .expect("blossom activation height should be available"); + + let halving_index = if height < slow_start_shift { + 0 + } else if height < blossom_height { + let pre_blossom_height = height - slow_start_shift; + pre_blossom_height / network.pre_blossom_halving_interval() + } else { + let pre_blossom_height = blossom_height - slow_start_shift; + let scaled_pre_blossom_height = + pre_blossom_height * HeightDiff::from(BLOSSOM_POW_TARGET_SPACING_RATIO); + + let post_blossom_height = height - blossom_height; + + (scaled_pre_blossom_height + post_blossom_height) / network.post_blossom_halving_interval() + }; + + halving_index + .try_into() + .expect("already checked for negatives") +} + +/// `BlockSubsidy(height)` as described in [protocol specification §7.8][7.8] +/// +/// [7.8]: https://zips.z.cash/protocol/protocol.pdf#subsidies +pub fn block_subsidy( + height: Height, + network: &Network, +) -> Result, SubsidyError> { + let blossom_height = NetworkUpgrade::Blossom + .activation_height(network) + .expect("blossom activation height should be available"); + + // If the halving divisor is larger than u64::MAX, the block subsidy is zero, + // because amounts fit in an i64. + // + // Note: bitcoind incorrectly wraps here, which restarts large block rewards. + let Some(halving_div) = halving_divisor(height, network) else { + return Ok(Amount::zero()); + }; + + // Zebra doesn't need to calculate block subsidies for blocks with heights in the slow start + // interval because it handles those blocks through checkpointing. + if height < network.slow_start_interval() { + Err(SubsidyError::UnsupportedHeight) + } else if height < blossom_height { + // this calculation is exact, because the halving divisor is 1 here + Ok(Amount::try_from(MAX_BLOCK_SUBSIDY / halving_div)?) + } else { + let scaled_max_block_subsidy = + MAX_BLOCK_SUBSIDY / u64::from(BLOSSOM_POW_TARGET_SPACING_RATIO); + // in future halvings, this calculation might not be exact + // Amount division is implemented using integer division, + // which truncates (rounds down) the result, as specified + Ok(Amount::try_from(scaled_max_block_subsidy / halving_div)?) + } +} + +/// `MinerSubsidy(height)` as described in [protocol specification §7.8][7.8] +/// +/// [7.8]: https://zips.z.cash/protocol/protocol.pdf#subsidies +pub fn miner_subsidy( + height: Height, + network: &Network, + expected_block_subsidy: Amount, +) -> Result, amount::Error> { + let total_funding_stream_amount: Result, _> = + funding_stream_values(height, network, expected_block_subsidy)? + .values() + .sum(); + + expected_block_subsidy - total_funding_stream_amount? +} + +/// Returns all output amounts in `Transaction`. +pub fn output_amounts(transaction: &Transaction) -> HashSet> { + transaction + .outputs() + .iter() + .map(|o| &o.value) + .cloned() + .collect() +} diff --git a/zebra-chain/src/parameters/network/testnet.rs b/zebra-chain/src/parameters/network/testnet.rs index dc1b4c83e28..81e88a302ad 100644 --- a/zebra-chain/src/parameters/network/testnet.rs +++ b/zebra-chain/src/parameters/network/testnet.rs @@ -1,5 +1,5 @@ //! Types and implementation for Testnet consensus parameters -use std::{collections::BTreeMap, fmt}; +use std::{collections::BTreeMap, fmt, sync::Arc}; use crate::{ block::{self, Height, HeightDiff}, @@ -7,7 +7,7 @@ use crate::{ constants::{magics, SLOW_START_INTERVAL, SLOW_START_SHIFT}, network_upgrade::TESTNET_ACTIVATION_HEIGHTS, subsidy::{funding_stream_address_period, FUNDING_STREAM_RECEIVER_DENOMINATOR}, - Network, NetworkKind, NetworkUpgrade, NETWORK_UPGRADES_IN_ORDER, + Network, NetworkKind, NetworkUpgrade, }, work::difficulty::{ExpandedDifficulty, U256}, }; @@ -23,11 +23,6 @@ use super::{ }, }; -/// The Regtest NU5 activation height in tests -// TODO: Serialize testnet parameters in Config then remove this and use a configured NU5 activation height. -#[cfg(any(test, feature = "proptest-impl"))] -pub const REGTEST_NU5_ACTIVATION_HEIGHT: u32 = 100; - /// Reserved network names that should not be allowed for configured Testnets. pub const RESERVED_NETWORK_NAMES: [&str; 6] = [ "Mainnet", @@ -57,7 +52,7 @@ const TESTNET_GENESIS_HASH: &str = const PRE_BLOSSOM_REGTEST_HALVING_INTERVAL: HeightDiff = 144; /// Configurable funding stream recipient for configured Testnets. -#[derive(Deserialize, Clone, Debug)] +#[derive(Serialize, Deserialize, Clone, Debug)] #[serde(deny_unknown_fields)] pub struct ConfiguredFundingStreamRecipient { /// Funding stream receiver, see [`FundingStreams::recipients`] for more details. @@ -79,7 +74,7 @@ impl ConfiguredFundingStreamRecipient { } /// Configurable funding streams for configured Testnets. -#[derive(Deserialize, Clone, Default, Debug)] +#[derive(Serialize, Deserialize, Clone, Default, Debug)] #[serde(deny_unknown_fields)] pub struct ConfiguredFundingStreams { /// Start and end height for funding streams see [`FundingStreams::height_range`] for more details. @@ -88,6 +83,61 @@ pub struct ConfiguredFundingStreams { pub recipients: Option>, } +impl From<&FundingStreams> for ConfiguredFundingStreams { + fn from(value: &FundingStreams) -> Self { + Self { + height_range: Some(value.height_range().clone()), + recipients: Some( + value + .recipients() + .iter() + .map(|(receiver, recipient)| ConfiguredFundingStreamRecipient { + receiver: *receiver, + numerator: recipient.numerator(), + addresses: Some( + recipient + .addresses() + .iter() + .map(ToString::to_string) + .collect(), + ), + }) + .collect(), + ), + } + } +} + +impl From<&BTreeMap> for ConfiguredActivationHeights { + fn from(activation_heights: &BTreeMap) -> Self { + let mut configured_activation_heights = ConfiguredActivationHeights::default(); + + for (height, network_upgrade) in activation_heights.iter() { + let field = match network_upgrade { + NetworkUpgrade::BeforeOverwinter => { + &mut configured_activation_heights.before_overwinter + } + NetworkUpgrade::Overwinter => &mut configured_activation_heights.overwinter, + NetworkUpgrade::Sapling => &mut configured_activation_heights.sapling, + NetworkUpgrade::Blossom => &mut configured_activation_heights.blossom, + NetworkUpgrade::Heartwood => &mut configured_activation_heights.heartwood, + NetworkUpgrade::Canopy => &mut configured_activation_heights.canopy, + NetworkUpgrade::Nu5 => &mut configured_activation_heights.nu5, + NetworkUpgrade::Nu6 => &mut configured_activation_heights.nu6, + NetworkUpgrade::Nu6_1 => &mut configured_activation_heights.nu6_1, + NetworkUpgrade::Nu7 => &mut configured_activation_heights.nu7, + NetworkUpgrade::Genesis => { + continue; + } + }; + + *field = Some(height.0) + } + + configured_activation_heights + } +} + impl ConfiguredFundingStreams { /// Returns an empty [`ConfiguredFundingStreams`]. fn empty() -> Self { @@ -185,7 +235,7 @@ fn check_funding_stream_address_period(funding_streams: &FundingStreams, network } /// Configurable activation heights for Regtest and configured Testnets. -#[derive(Deserialize, Default, Clone)] +#[derive(Serialize, Deserialize, Default, Clone)] #[serde(rename_all = "PascalCase", deny_unknown_fields)] pub struct ConfiguredActivationHeights { /// Activation height for `BeforeOverwinter` network upgrade. @@ -206,6 +256,9 @@ pub struct ConfiguredActivationHeights { /// Activation height for `NU6` network upgrade. #[serde(rename = "NU6")] pub nu6: Option, + /// Activation height for `NU6.1` network upgrade. + #[serde(rename = "NU6.1")] + pub nu6_1: Option, /// Activation height for `NU7` network upgrade. #[serde(rename = "NU7")] pub nu7: Option, @@ -235,6 +288,9 @@ pub struct ParametersBuilder { target_difficulty_limit: ExpandedDifficulty, /// A flag for disabling proof-of-work checks when Zebra is validating blocks disable_pow: bool, + /// Whether to allow transactions with transparent outputs to spend coinbase outputs, + /// similar to `fCoinbaseMustBeShielded` in zcashd. + should_allow_unshielded_coinbase_spends: bool, /// The pre-Blossom halving interval for this network pre_blossom_halving_interval: HeightDiff, /// The post-Blossom halving interval for this network @@ -274,6 +330,7 @@ impl Default for ParametersBuilder { should_lock_funding_stream_address_period: false, pre_blossom_halving_interval: PRE_BLOSSOM_HALVING_INTERVAL, post_blossom_halving_interval: POST_BLOSSOM_HALVING_INTERVAL, + should_allow_unshielded_coinbase_spends: false, } } } @@ -339,6 +396,7 @@ impl ParametersBuilder { canopy, nu5, nu6, + nu6_1, nu7, }: ConfiguredActivationHeights, ) -> Self { @@ -362,6 +420,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(nu6_1.into_iter().map(|h| (h, Nu6_1))) .chain(nu7.into_iter().map(|h| (h, Nu7))) .map(|(h, nu)| (h.try_into().expect("activation height must be valid"), nu)) .collect(); @@ -370,7 +429,7 @@ impl ParametersBuilder { // Check that the provided network upgrade activation heights are in the same order by height as the default testnet activation heights let mut activation_heights_iter = activation_heights.iter(); - for expected_network_upgrade in NETWORK_UPGRADES_IN_ORDER { + for expected_network_upgrade in NetworkUpgrade::iter() { if !network_upgrades.contains(&expected_network_upgrade) { continue; } else if let Some((&height, &network_upgrade)) = activation_heights_iter.next() { @@ -382,7 +441,7 @@ impl ParametersBuilder { assert!( network_upgrade == expected_network_upgrade, - "network upgrades must be activated in order, the correct order is {NETWORK_UPGRADES_IN_ORDER:?}" + "network upgrades must be activated in order specified by the protocol" ); } } @@ -444,6 +503,15 @@ impl ParametersBuilder { self } + /// Sets the `disable_pow` flag to be used in the [`Parameters`] being built. + pub fn with_unshielded_coinbase_spends( + mut self, + should_allow_unshielded_coinbase_spends: bool, + ) -> Self { + self.should_allow_unshielded_coinbase_spends = should_allow_unshielded_coinbase_spends; + self + } + /// Sets the pre and post Blosssom halving intervals to be used in the [`Parameters`] being built. pub fn with_halving_interval(mut self, pre_blossom_halving_interval: HeightDiff) -> Self { if self.should_lock_funding_stream_address_period { @@ -469,6 +537,7 @@ impl ParametersBuilder { should_lock_funding_stream_address_period: _, target_difficulty_limit, disable_pow, + should_allow_unshielded_coinbase_spends, pre_blossom_halving_interval, post_blossom_halving_interval, } = self; @@ -483,6 +552,7 @@ impl ParametersBuilder { post_nu6_funding_streams, target_difficulty_limit, disable_pow, + should_allow_unshielded_coinbase_spends, pre_blossom_halving_interval, post_blossom_halving_interval, } @@ -521,6 +591,7 @@ impl ParametersBuilder { should_lock_funding_stream_address_period: _, target_difficulty_limit, disable_pow, + should_allow_unshielded_coinbase_spends, pre_blossom_halving_interval, post_blossom_halving_interval, } = Self::default(); @@ -533,6 +604,8 @@ impl ParametersBuilder { && self.post_nu6_funding_streams == post_nu6_funding_streams && self.target_difficulty_limit == target_difficulty_limit && self.disable_pow == disable_pow + && self.should_allow_unshielded_coinbase_spends + == should_allow_unshielded_coinbase_spends && self.pre_blossom_halving_interval == pre_blossom_halving_interval && self.post_blossom_halving_interval == post_blossom_halving_interval } @@ -565,6 +638,9 @@ pub struct Parameters { target_difficulty_limit: ExpandedDifficulty, /// A flag for disabling proof-of-work checks when Zebra is validating blocks disable_pow: bool, + /// Whether to allow transactions with transparent outputs to spend coinbase outputs, + /// similar to `fCoinbaseMustBeShielded` in zcashd. + should_allow_unshielded_coinbase_spends: bool, /// Pre-Blossom halving interval for this network pre_blossom_halving_interval: HeightDiff, /// Post-Blossom halving interval for this network @@ -591,26 +667,25 @@ impl Parameters { /// /// Creates an instance of [`Parameters`] with `Regtest` values. pub fn new_regtest( - nu5_activation_height: Option, - nu6_activation_height: Option, - nu7_activation_height: Option, + ConfiguredActivationHeights { nu5, nu6, nu7, .. }: ConfiguredActivationHeights, ) -> Self { #[cfg(any(test, feature = "proptest-impl"))] - let nu5_activation_height = nu5_activation_height.or(Some(100)); + let nu5 = nu5.or(Some(100)); let parameters = Self::build() .with_genesis_hash(REGTEST_GENESIS_HASH) // This value is chosen to match zcashd, see: .with_target_difficulty_limit(U256::from_big_endian(&[0x0f; 32])) .with_disable_pow(true) + .with_unshielded_coinbase_spends(true) .with_slow_start_interval(Height::MIN) // Removes default Testnet activation heights if not configured, // most network upgrades are disabled by default for Regtest in zcashd .with_activation_heights(ConfiguredActivationHeights { canopy: Some(1), - nu5: nu5_activation_height, - nu6: nu6_activation_height, - nu7: nu7_activation_height, + nu5, + nu6, + nu7, ..Default::default() }) .with_halving_interval(PRE_BLOSSOM_REGTEST_HALVING_INTERVAL); @@ -652,9 +727,10 @@ impl Parameters { post_nu6_funding_streams, target_difficulty_limit, disable_pow, + should_allow_unshielded_coinbase_spends, pre_blossom_halving_interval, post_blossom_halving_interval, - } = Self::new_regtest(None, None, None); + } = Self::new_regtest(Default::default()); self.network_name == network_name && self.genesis_hash == genesis_hash @@ -664,6 +740,8 @@ impl Parameters { && self.post_nu6_funding_streams == post_nu6_funding_streams && self.target_difficulty_limit == target_difficulty_limit && self.disable_pow == disable_pow + && self.should_allow_unshielded_coinbase_spends + == should_allow_unshielded_coinbase_spends && self.pre_blossom_halving_interval == pre_blossom_halving_interval && self.post_blossom_halving_interval == post_blossom_halving_interval } @@ -718,6 +796,12 @@ impl Parameters { self.disable_pow } + /// Returns true if this network should allow transactions with transparent outputs + /// that spend coinbase outputs. + pub fn should_allow_unshielded_coinbase_spends(&self) -> bool { + self.should_allow_unshielded_coinbase_spends + } + /// Returns the pre-Blossom halving interval for this network pub fn pre_blossom_halving_interval(&self) -> HeightDiff { self.pre_blossom_halving_interval @@ -730,6 +814,15 @@ impl Parameters { } impl Network { + /// Returns the parameters of this network if it is a Testnet. + pub fn parameters(&self) -> Option> { + if let Self::Testnet(parameters) = self { + Some(parameters.clone()) + } else { + None + } + } + /// Returns true if proof-of-work validation should be disabled for this network pub fn disable_pow(&self) -> bool { if let Self::Testnet(params) = self { @@ -793,4 +886,14 @@ impl Network { self.post_nu6_funding_streams() } } + + /// Returns true if this network should allow transactions with transparent outputs + /// that spend coinbase outputs. + pub fn should_allow_unshielded_coinbase_spends(&self) -> bool { + if let Self::Testnet(params) = self { + params.should_allow_unshielded_coinbase_spends() + } else { + false + } + } } diff --git a/zebra-chain/src/parameters/network/tests.rs b/zebra-chain/src/parameters/network/tests.rs index cc95d9d451f..f6658ab7723 100644 --- a/zebra-chain/src/parameters/network/tests.rs +++ b/zebra-chain/src/parameters/network/tests.rs @@ -1,2 +1,318 @@ mod prop; mod vectors; + +use color_eyre::Report; + +use super::Network; +use crate::{ + amount::{Amount, NonNegative}, + block::Height, + parameters::{ + subsidy::{ + block_subsidy, halving_divisor, height_for_halving, num_halvings, + ParameterSubsidy as _, POST_BLOSSOM_HALVING_INTERVAL, + }, + NetworkUpgrade, + }, +}; + +#[test] +fn halving_test() -> Result<(), Report> { + let _init_guard = zebra_test::init(); + for network in Network::iter() { + halving_for_network(&network)?; + } + + Ok(()) +} + +fn halving_for_network(network: &Network) -> Result<(), Report> { + let blossom_height = NetworkUpgrade::Blossom.activation_height(network).unwrap(); + let first_halving_height = network.height_for_first_halving(); + + assert_eq!( + 1, + halving_divisor((network.slow_start_interval() + 1).unwrap(), network).unwrap() + ); + assert_eq!( + 1, + halving_divisor((blossom_height - 1).unwrap(), network).unwrap() + ); + assert_eq!(1, halving_divisor(blossom_height, network).unwrap()); + assert_eq!( + 1, + halving_divisor((first_halving_height - 1).unwrap(), network).unwrap() + ); + + assert_eq!(2, halving_divisor(first_halving_height, network).unwrap()); + assert_eq!( + 2, + halving_divisor((first_halving_height + 1).unwrap(), network).unwrap() + ); + + assert_eq!( + 4, + halving_divisor( + (first_halving_height + POST_BLOSSOM_HALVING_INTERVAL).unwrap(), + network + ) + .unwrap() + ); + assert_eq!( + 8, + halving_divisor( + (first_halving_height + (POST_BLOSSOM_HALVING_INTERVAL * 2)).unwrap(), + network + ) + .unwrap() + ); + + assert_eq!( + 1024, + halving_divisor( + (first_halving_height + (POST_BLOSSOM_HALVING_INTERVAL * 9)).unwrap(), + network + ) + .unwrap() + ); + assert_eq!( + 1024 * 1024, + halving_divisor( + (first_halving_height + (POST_BLOSSOM_HALVING_INTERVAL * 19)).unwrap(), + network + ) + .unwrap() + ); + assert_eq!( + 1024 * 1024 * 1024, + halving_divisor( + (first_halving_height + (POST_BLOSSOM_HALVING_INTERVAL * 29)).unwrap(), + network + ) + .unwrap() + ); + assert_eq!( + 1024 * 1024 * 1024 * 1024, + halving_divisor( + (first_halving_height + (POST_BLOSSOM_HALVING_INTERVAL * 39)).unwrap(), + network + ) + .unwrap() + ); + + // The largest possible integer divisor + assert_eq!( + (i64::MAX as u64 + 1), + halving_divisor( + (first_halving_height + (POST_BLOSSOM_HALVING_INTERVAL * 62)).unwrap(), + network + ) + .unwrap(), + ); + + // Very large divisors which should also result in zero amounts + assert_eq!( + None, + halving_divisor( + (first_halving_height + (POST_BLOSSOM_HALVING_INTERVAL * 63)).unwrap(), + network, + ), + ); + + assert_eq!( + None, + halving_divisor( + (first_halving_height + (POST_BLOSSOM_HALVING_INTERVAL * 64)).unwrap(), + network, + ), + ); + + assert_eq!( + None, + halving_divisor(Height(Height::MAX_AS_U32 / 4), network), + ); + + assert_eq!( + None, + halving_divisor(Height(Height::MAX_AS_U32 / 2), network), + ); + + assert_eq!(None, halving_divisor(Height::MAX, network)); + + Ok(()) +} + +#[test] +fn block_subsidy_test() -> Result<(), Report> { + let _init_guard = zebra_test::init(); + + for network in Network::iter() { + block_subsidy_for_network(&network)?; + } + + Ok(()) +} + +fn block_subsidy_for_network(network: &Network) -> Result<(), Report> { + let blossom_height = NetworkUpgrade::Blossom.activation_height(network).unwrap(); + let first_halving_height = network.height_for_first_halving(); + + // After slow-start mining and before Blossom the block subsidy is 12.5 ZEC + // https://z.cash/support/faq/#what-is-slow-start-mining + assert_eq!( + Amount::::try_from(1_250_000_000)?, + block_subsidy((network.slow_start_interval() + 1).unwrap(), network)? + ); + assert_eq!( + Amount::::try_from(1_250_000_000)?, + block_subsidy((blossom_height - 1).unwrap(), network)? + ); + + // After Blossom the block subsidy is reduced to 6.25 ZEC without halving + // https://z.cash/upgrade/blossom/ + assert_eq!( + Amount::::try_from(625_000_000)?, + block_subsidy(blossom_height, network)? + ); + + // After the 1st halving, the block subsidy is reduced to 3.125 ZEC + // https://z.cash/upgrade/canopy/ + assert_eq!( + Amount::::try_from(312_500_000)?, + block_subsidy(first_halving_height, network)? + ); + + // After the 2nd halving, the block subsidy is reduced to 1.5625 ZEC + // See "7.8 Calculation of Block Subsidy and Founders' Reward" + assert_eq!( + Amount::::try_from(156_250_000)?, + block_subsidy( + (first_halving_height + POST_BLOSSOM_HALVING_INTERVAL).unwrap(), + network + )? + ); + + // After the 7th halving, the block subsidy is reduced to 0.04882812 ZEC + // Check that the block subsidy rounds down correctly, and there are no errors + assert_eq!( + Amount::::try_from(4_882_812)?, + block_subsidy( + (first_halving_height + (POST_BLOSSOM_HALVING_INTERVAL * 6)).unwrap(), + network + )? + ); + + // After the 29th halving, the block subsidy is 1 zatoshi + // Check that the block subsidy is calculated correctly at the limit + assert_eq!( + Amount::::try_from(1)?, + block_subsidy( + (first_halving_height + (POST_BLOSSOM_HALVING_INTERVAL * 28)).unwrap(), + network + )? + ); + + // After the 30th halving, there is no block subsidy + // Check that there are no errors + assert_eq!( + Amount::::try_from(0)?, + block_subsidy( + (first_halving_height + (POST_BLOSSOM_HALVING_INTERVAL * 29)).unwrap(), + network + )? + ); + + assert_eq!( + Amount::::try_from(0)?, + block_subsidy( + (first_halving_height + (POST_BLOSSOM_HALVING_INTERVAL * 39)).unwrap(), + network + )? + ); + + assert_eq!( + Amount::::try_from(0)?, + block_subsidy( + (first_halving_height + (POST_BLOSSOM_HALVING_INTERVAL * 49)).unwrap(), + network + )? + ); + + assert_eq!( + Amount::::try_from(0)?, + block_subsidy( + (first_halving_height + (POST_BLOSSOM_HALVING_INTERVAL * 59)).unwrap(), + network + )? + ); + + // The largest possible integer divisor + assert_eq!( + Amount::::try_from(0)?, + block_subsidy( + (first_halving_height + (POST_BLOSSOM_HALVING_INTERVAL * 62)).unwrap(), + network + )? + ); + + // Other large divisors which should also result in zero + assert_eq!( + Amount::::try_from(0)?, + block_subsidy( + (first_halving_height + (POST_BLOSSOM_HALVING_INTERVAL * 63)).unwrap(), + network + )? + ); + + assert_eq!( + Amount::::try_from(0)?, + block_subsidy( + (first_halving_height + (POST_BLOSSOM_HALVING_INTERVAL * 64)).unwrap(), + network + )? + ); + + assert_eq!( + Amount::::try_from(0)?, + block_subsidy(Height(Height::MAX_AS_U32 / 4), network)? + ); + + assert_eq!( + Amount::::try_from(0)?, + block_subsidy(Height(Height::MAX_AS_U32 / 2), network)? + ); + + assert_eq!( + Amount::::try_from(0)?, + block_subsidy(Height::MAX, network)? + ); + + Ok(()) +} + +#[test] +fn check_height_for_num_halvings() { + for network in Network::iter() { + for halving in 1..1000 { + let Some(height_for_halving) = height_for_halving(halving, &network) else { + panic!("could not find height for halving {halving}"); + }; + + let prev_height = height_for_halving + .previous() + .expect("there should be a previous height"); + + assert_eq!( + halving, + num_halvings(height_for_halving, &network), + "num_halvings should match the halving index" + ); + + assert_eq!( + halving - 1, + num_halvings(prev_height, &network), + "num_halvings for the prev height should be 1 less than the halving index" + ); + } + } +} diff --git a/zebra-chain/src/parameters/network/tests/vectors.rs b/zebra-chain/src/parameters/network/tests/vectors.rs index f15b3f6388e..86c25c2bcd6 100644 --- a/zebra-chain/src/parameters/network/tests/vectors.rs +++ b/zebra-chain/src/parameters/network/tests/vectors.rs @@ -14,13 +14,12 @@ use crate::{ self, ConfiguredActivationHeights, ConfiguredFundingStreamRecipient, ConfiguredFundingStreams, MAX_NETWORK_NAME_LENGTH, RESERVED_NETWORK_NAMES, }, - Network, NetworkUpgrade, MAINNET_ACTIVATION_HEIGHTS, NETWORK_UPGRADES_IN_ORDER, - TESTNET_ACTIVATION_HEIGHTS, + Network, NetworkUpgrade, MAINNET_ACTIVATION_HEIGHTS, TESTNET_ACTIVATION_HEIGHTS, }, }; /// Checks that every method in the `Parameters` impl for `zebra_chain::Network` has the same output -/// as the Parameters impl for `zcash_primitives::consensus::Network` on Mainnet and the default Testnet. +/// as the Parameters impl for `zcash_protocol::consensus::NetworkType` on Mainnet and the default Testnet. #[test] fn check_parameters_impl() { let zp_network_upgrades = [ @@ -123,7 +122,7 @@ fn activates_network_upgrades_correctly() { "activation height for all networks after Genesis and BeforeOverwinter should match NU5 activation height" ); - for nu in NETWORK_UPGRADES_IN_ORDER.into_iter().skip(1) { + for nu in NetworkUpgrade::iter().skip(1) { let activation_height = nu .activation_height(&network) .expect("must return an activation height"); @@ -146,7 +145,7 @@ fn activates_network_upgrades_correctly() { (Network::Mainnet, MAINNET_ACTIVATION_HEIGHTS), (Network::new_default_testnet(), TESTNET_ACTIVATION_HEIGHTS), ( - Network::new_regtest(None, None, None), + Network::new_regtest(Default::default()), expected_default_regtest_activation_heights, ), ] { @@ -197,7 +196,7 @@ fn check_configured_network_name() { "Mainnet should be displayed as 'Mainnet'" ); assert_eq!( - Network::new_regtest(None, None, None).to_string(), + Network::new_regtest(Default::default()).to_string(), "Regtest", "Regtest should be displayed as 'Regtest'" ); @@ -285,8 +284,8 @@ fn check_full_activation_list() { }) .to_network(); - // We expect the first 8 network upgrades to be included, up to NU5 - let expected_network_upgrades = &NETWORK_UPGRADES_IN_ORDER[..8]; + // We expect the first 8 network upgrades to be included, up to and including NU5 + let expected_network_upgrades = NetworkUpgrade::iter().take(8); let full_activation_list_network_upgrades: Vec<_> = network .full_activation_list() .into_iter() @@ -295,7 +294,7 @@ fn check_full_activation_list() { for expected_network_upgrade in expected_network_upgrades { assert!( - full_activation_list_network_upgrades.contains(expected_network_upgrade), + full_activation_list_network_upgrades.contains(&expected_network_upgrade), "full activation list should contain expected network upgrade" ); } diff --git a/zebra-chain/src/parameters/network_upgrade.rs b/zebra-chain/src/parameters/network_upgrade.rs index b79e4e0007e..3c45726bfc2 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; 10] = [ +const NETWORK_UPGRADES_IN_ORDER: &[NetworkUpgrade] = &[ Genesis, BeforeOverwinter, Overwinter, @@ -25,6 +25,12 @@ pub const NETWORK_UPGRADES_IN_ORDER: [NetworkUpgrade; 10] = [ Canopy, Nu5, Nu6, + // FIXME: unify zebra-test/zcash_unstable nu7 usages, + // check if the code is covered of those flags properly, + // try to build with and without those flags + #[cfg(any(test, feature = "zebra-test", zcash_unstable = "nu7"))] + Nu6_1, + #[cfg(any(test, feature = "zebra-test", zcash_unstable = "nu7"))] Nu7, ]; @@ -62,11 +68,26 @@ pub enum NetworkUpgrade { /// The Zcash protocol after the NU6 upgrade. #[serde(rename = "NU6")] Nu6, + /// The Zcash protocol after the NU6.1 upgrade. + #[serde(rename = "NU6.1")] + Nu6_1, /// The Zcash protocol after the NU7 upgrade. #[serde(rename = "NU7")] Nu7, } +impl TryFrom for NetworkUpgrade { + type Error = crate::Error; + + fn try_from(branch_id: u32) -> Result { + CONSENSUS_BRANCH_IDS + .iter() + .find(|id| id.1 == ConsensusBranchId(branch_id)) + .map(|nu| nu.0) + .ok_or(Self::Error::InvalidConsensusBranchId) + } +} + impl fmt::Display for NetworkUpgrade { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { // Same as the debug representation for now @@ -94,9 +115,11 @@ 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. + // FIXME: TODO: Update height values for Nu6_1 and Nu7 when real values will be known. + #[cfg(zcash_unstable = "nu7")] + (block::Height(4_111_000), Nu6_1), #[cfg(zcash_unstable = "nu7")] - (block::Height(3_111_000), Nu7), + (block::Height(4_333_000), Nu7), ]; /// Fake mainnet network upgrade activation heights, used in tests. @@ -111,7 +134,8 @@ const FAKE_MAINNET_ACTIVATION_HEIGHTS: &[(block::Height, NetworkUpgrade)] = &[ (block::Height(30), Canopy), (block::Height(35), Nu5), (block::Height(40), Nu6), - (block::Height(45), Nu7), + (block::Height(45), Nu6_1), + (block::Height(50), Nu7), ]; /// Testnet network upgrade activation heights. @@ -134,9 +158,11 @@ 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. + // FIXME: TODO: Update height values for Nu6_1 and Nu7 when real values will be known. #[cfg(zcash_unstable = "nu7")] - (block::Height(3_222_000), Nu7), + (block::Height(4_222_000), Nu6_1), + #[cfg(zcash_unstable = "nu7")] + (block::Height(4_444_000), Nu7), ]; /// Fake testnet network upgrade activation heights, used in tests. @@ -151,7 +177,8 @@ const FAKE_TESTNET_ACTIVATION_HEIGHTS: &[(block::Height, NetworkUpgrade)] = &[ (block::Height(30), Canopy), (block::Height(35), Nu5), (block::Height(40), Nu6), - (block::Height(45), Nu7), + (block::Height(45), Nu6_1), + (block::Height(50), Nu7), ]; /// The Consensus Branch Id, used to bind transactions and blocks to a @@ -175,6 +202,12 @@ impl From for u32 { } } +impl From for ConsensusBranchId { + fn from(branch: u32) -> Self { + ConsensusBranchId(branch) + } +} + impl ToHex for &ConsensusBranchId { fn encode_hex>(&self) -> T { self.bytes_in_display_order().encode_hex() @@ -210,6 +243,15 @@ impl fmt::Display for ConsensusBranchId { } } +impl TryFrom for zcash_primitives::consensus::BranchId { + type Error = crate::Error; + + fn try_from(id: ConsensusBranchId) -> Result { + zcash_primitives::consensus::BranchId::try_from(u32::from(id)) + .map_err(|_| Self::Error::InvalidConsensusBranchId) + } +} + /// Network Upgrade Consensus Branch Ids. /// /// Branch ids are the same for mainnet and testnet. If there is a testnet @@ -228,8 +270,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 = "nu7")] + #[cfg(any(test, feature = "zebra-test", zcash_unstable = "nu7"))] + (Nu6_1, ConsensusBranchId(0x4dec4df0)), + #[cfg(any(test, feature = "zebra-test", zcash_unstable = "nu7"))] (Nu7, ConsensusBranchId(0x77190ad8)), ]; @@ -306,8 +349,8 @@ impl Network { /// in ascending height order. pub fn full_activation_list(&self) -> Vec<(block::Height, NetworkUpgrade)> { NETWORK_UPGRADES_IN_ORDER - .into_iter() - .map_while(|nu| Some((NetworkUpgrade::activation_height(&nu, self)?, nu))) + .iter() + .map_while(|&nu| Some((NetworkUpgrade::activation_height(&nu, self)?, nu))) .collect() } } @@ -336,20 +379,14 @@ impl NetworkUpgrade { .expect("every height has a current network upgrade") } - /// Returns the next expected network upgrade after this network upgrade + /// Returns the next expected network upgrade after this network upgrade. pub fn next_upgrade(self) -> Option { - match self { - Genesis => Some(BeforeOverwinter), - BeforeOverwinter => Some(Overwinter), - Overwinter => Some(Sapling), - Sapling => Some(Blossom), - Blossom => Some(Heartwood), - Heartwood => Some(Canopy), - Canopy => Some(Nu5), - Nu5 => Some(Nu6), - Nu6 => Some(Nu7), - Nu7 => None, - } + Self::iter().skip_while(|&nu| self != nu).nth(1) + } + + /// Returns the previous network upgrade before this network upgrade. + pub fn previous_upgrade(self) -> Option { + Self::iter().rev().skip_while(|&nu| self != nu).nth(1) } /// Returns the next network upgrade for `network` and `height`. @@ -425,7 +462,7 @@ 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 | Nu7 => { + Blossom | Heartwood | Canopy | Nu5 | Nu6 | Nu6_1 | Nu7 => { POST_BLOSSOM_POW_TARGET_SPACING.into() } }; @@ -530,12 +567,9 @@ impl NetworkUpgrade { NetworkUpgrade::current(network, height).averaging_window_timespan() } - /// Returns the NetworkUpgrade given an u32 as ConsensusBranchId - pub fn from_branch_id(branch_id: u32) -> Option { - CONSENSUS_BRANCH_IDS - .iter() - .find(|id| id.1 == ConsensusBranchId(branch_id)) - .map(|nu| nu.0) + /// Returns an iterator over [`NetworkUpgrade`] variants. + pub fn iter() -> impl DoubleEndedIterator { + NETWORK_UPGRADES_IN_ORDER.iter().copied() } } @@ -549,8 +583,7 @@ 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 = "nu7")] + zcash_protocol::consensus::NetworkUpgrade::Nu6_1 => Self::Nu6_1, zcash_protocol::consensus::NetworkUpgrade::Nu7 => Self::Nu7, } } diff --git a/zebra-chain/src/parameters/transaction.rs b/zebra-chain/src/parameters/transaction.rs index bcc44cb4f6e..85f91d083e8 100644 --- a/zebra-chain/src/parameters/transaction.rs +++ b/zebra-chain/src/parameters/transaction.rs @@ -13,9 +13,9 @@ pub const SAPLING_VERSION_GROUP_ID: u32 = 0x892F_2085; 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")] +/// TODO: update this after it's chosen +// FIXME: The upstream version uses 0xFFFF_FFFF for TX_V6_VERSION_GROUP_ID, +// but it was changed here to 0x7777_7777 for compatibility with the value of +// V6_VERSION_GROUP_ID in the ZSA version of librustzcash/zcash_protocol. +// Update to the proper value once it's fixed in librustzcash. pub const TX_V6_VERSION_GROUP_ID: u32 = 0x7777_7777; diff --git a/zebra-chain/src/primitives.rs b/zebra-chain/src/primitives.rs index fcaa78360ae..2fe1885dd16 100644 --- a/zebra-chain/src/primitives.rs +++ b/zebra-chain/src/primitives.rs @@ -10,9 +10,6 @@ mod address; pub use address::Address; -#[cfg(feature = "shielded-scan")] -pub mod viewing_key; - pub mod byte_array; pub use ed25519_zebra as ed25519; diff --git a/zebra-chain/src/primitives/address.rs b/zebra-chain/src/primitives/address.rs index 42ab577216b..aa7023c5771 100644 --- a/zebra-chain/src/primitives/address.rs +++ b/zebra-chain/src/primitives/address.rs @@ -3,6 +3,7 @@ //! Usage: use zcash_address::unified::{self, Container}; +use zcash_protocol::consensus::NetworkType; use crate::{parameters::NetworkKind, transparent, BoxError}; @@ -44,7 +45,7 @@ impl zcash_address::TryFromAddress for Address { type Error = BoxError; fn try_from_transparent_p2pkh( - network: zcash_protocol::consensus::NetworkType, + network: NetworkType, data: [u8; 20], ) -> Result> { Ok(Self::Transparent(transparent::Address::from_pub_key_hash( @@ -54,7 +55,7 @@ impl zcash_address::TryFromAddress for Address { } fn try_from_transparent_p2sh( - network: zcash_protocol::consensus::NetworkType, + network: NetworkType, data: [u8; 20], ) -> Result> { Ok(Self::Transparent(transparent::Address::from_script_hash( @@ -64,7 +65,7 @@ impl zcash_address::TryFromAddress for Address { } fn try_from_sapling( - network: zcash_protocol::consensus::NetworkType, + network: NetworkType, data: [u8; 43], ) -> Result> { let network = network.into(); @@ -74,7 +75,7 @@ impl zcash_address::TryFromAddress for Address { } fn try_from_unified( - network: zcash_protocol::consensus::NetworkType, + network: NetworkType, unified_address: zcash_address::unified::Address, ) -> Result> { let network = network.into(); @@ -128,6 +129,16 @@ impl zcash_address::TryFromAddress for Address { transparent, }) } + + fn try_from_tex( + network: NetworkType, + data: [u8; 20], + ) -> Result> { + Ok(Self::Transparent(transparent::Address::from_tex( + network.into(), + data, + ))) + } } impl Address { @@ -170,27 +181,27 @@ impl Address { } } -impl From for NetworkKind { - fn from(network: zcash_protocol::consensus::NetworkType) -> Self { +impl From for NetworkKind { + fn from(network: NetworkType) -> Self { match network { - zcash_protocol::consensus::NetworkType::Main => NetworkKind::Mainnet, - zcash_protocol::consensus::NetworkType::Test => NetworkKind::Testnet, - zcash_protocol::consensus::NetworkType::Regtest => NetworkKind::Regtest, + NetworkType::Main => NetworkKind::Mainnet, + NetworkType::Test => NetworkKind::Testnet, + NetworkType::Regtest => NetworkKind::Regtest, } } } -impl From for zcash_protocol::consensus::NetworkType { +impl From for NetworkType { fn from(network: NetworkKind) -> Self { match network { - NetworkKind::Mainnet => zcash_protocol::consensus::NetworkType::Main, - NetworkKind::Testnet => zcash_protocol::consensus::NetworkType::Test, - NetworkKind::Regtest => zcash_protocol::consensus::NetworkType::Regtest, + NetworkKind::Mainnet => NetworkType::Main, + NetworkKind::Testnet => NetworkType::Test, + NetworkKind::Regtest => NetworkType::Regtest, } } } -impl From<&NetworkKind> for zcash_protocol::consensus::NetworkType { +impl From<&NetworkKind> for NetworkType { fn from(network: &NetworkKind) -> Self { (*network).into() } diff --git a/zebra-chain/src/primitives/viewing_key.rs b/zebra-chain/src/primitives/viewing_key.rs deleted file mode 100644 index e59498b72d3..00000000000 --- a/zebra-chain/src/primitives/viewing_key.rs +++ /dev/null @@ -1,43 +0,0 @@ -//! Type definitions for viewing keys and their hashes. - -use crate::parameters::Network; - -mod orchard; -mod sapling; - -use orchard::OrchardViewingKey; -use sapling::SaplingViewingKey; - -#[cfg(test)] -mod tests; - -/// A Zcash Sapling or Orchard viewing key -#[derive(Debug, Clone)] -pub enum ViewingKey { - /// A viewing key for Sapling - Sapling(SaplingViewingKey), - - /// A viewing key for Orchard - Orchard(OrchardViewingKey), -} - -impl ViewingKey { - /// Accepts an encoded Sapling viewing key to decode - /// - /// Returns a [`ViewingKey`] if successful, or None otherwise - fn parse_sapling(sapling_key: &str, network: &Network) -> Option { - SaplingViewingKey::parse(sapling_key, network).map(Self::Sapling) - } - - /// Accepts an encoded Orchard viewing key to decode - /// - /// Returns a [`ViewingKey`] if successful, or None otherwise - fn parse_orchard(sapling_key: &str, network: &Network) -> Option { - OrchardViewingKey::parse(sapling_key, network).map(Self::Orchard) - } - - /// Parses an encoded viewing key and returns it as a [`ViewingKey`] type. - pub fn parse(key: &str, network: &Network) -> Option { - Self::parse_sapling(key, network).or_else(|| Self::parse_orchard(key, network)) - } -} diff --git a/zebra-chain/src/primitives/viewing_key/orchard.rs b/zebra-chain/src/primitives/viewing_key/orchard.rs deleted file mode 100644 index 140331a496e..00000000000 --- a/zebra-chain/src/primitives/viewing_key/orchard.rs +++ /dev/null @@ -1,17 +0,0 @@ -//! Defines types and implements methods for parsing Orchard viewing keys and converting them to `zebra-chain` types - -use crate::parameters::Network; - -/// A Zcash Orchard viewing key -#[derive(Debug, Clone)] -pub enum OrchardViewingKey {} - -impl OrchardViewingKey { - /// Accepts an encoded Orchard viewing key to decode - /// - /// Returns a [`OrchardViewingKey`] if successful, or None otherwise - pub fn parse(_key: &str, _network: &Network) -> Option { - // TODO: parse Orchard viewing keys - None - } -} diff --git a/zebra-chain/src/primitives/viewing_key/sapling.rs b/zebra-chain/src/primitives/viewing_key/sapling.rs deleted file mode 100644 index 85c0d0d2da9..00000000000 --- a/zebra-chain/src/primitives/viewing_key/sapling.rs +++ /dev/null @@ -1,90 +0,0 @@ -//! Defines types and implements methods for parsing Sapling viewing keys and converting them to `zebra-chain` types - -use sapling_crypto::keys::{FullViewingKey as SaplingFvk, SaplingIvk}; -use zcash_client_backend::{ - encoding::decode_extended_full_viewing_key, - keys::sapling::DiversifiableFullViewingKey as SaplingDfvk, -}; -use zcash_protocol::constants::*; - -use crate::parameters::Network; - -/// A Zcash Sapling viewing key -#[derive(Debug, Clone)] -pub enum SaplingViewingKey { - /// An incoming viewing key for Sapling - Ivk(Box), - - /// A full viewing key for Sapling - Fvk(Box), - - /// A diversifiable full viewing key for Sapling - Dfvk(Box), -} - -impl SaplingViewingKey { - /// Accepts an encoded Sapling extended full viewing key to decode - /// - /// Returns a [`SaplingViewingKey::Dfvk`] if successful, or None otherwise - fn parse_extended_full_viewing_key(sapling_key: &str, network: &Network) -> Option { - decode_extended_full_viewing_key(network.sapling_efvk_hrp(), sapling_key) - // this should fail often, so a debug-level log is okay - .map_err(|err| debug!(?err, "could not decode Sapling extended full viewing key")) - .ok() - .map(|efvk| Box::new(efvk.to_diversifiable_full_viewing_key())) - .map(Self::Dfvk) - } - - /// Accepts an encoded Sapling diversifiable full viewing key to decode - /// - /// Returns a [`SaplingViewingKey::Dfvk`] if successful, or None otherwise - fn parse_diversifiable_full_viewing_key( - _sapling_key: &str, - _network: &Network, - ) -> Option { - // TODO: Parse Sapling diversifiable full viewing key - None - } - - /// Accepts an encoded Sapling full viewing key to decode - /// - /// Returns a [`SaplingViewingKey::Fvk`] if successful, or None otherwise - fn parse_full_viewing_key(_sapling_key: &str, _network: &Network) -> Option { - // TODO: Parse Sapling full viewing key - None - } - - /// Accepts an encoded Sapling incoming viewing key to decode - /// - /// Returns a [`SaplingViewingKey::Ivk`] if successful, or None otherwise - fn parse_incoming_viewing_key(_sapling_key: &str, _network: &Network) -> Option { - // TODO: Parse Sapling incoming viewing key - None - } - - /// Accepts an encoded Sapling viewing key to decode - /// - /// Returns a [`SaplingViewingKey`] if successful, or None otherwise - pub(super) fn parse(key: &str, network: &Network) -> Option { - // TODO: Try types with prefixes first if some don't have prefixes? - Self::parse_extended_full_viewing_key(key, network) - .or_else(|| Self::parse_diversifiable_full_viewing_key(key, network)) - .or_else(|| Self::parse_full_viewing_key(key, network)) - .or_else(|| Self::parse_incoming_viewing_key(key, network)) - } -} - -impl Network { - /// Returns the human-readable prefix for an Zcash Sapling extended full viewing key - /// for this network. - pub fn sapling_efvk_hrp(&self) -> &'static str { - if self.is_a_test_network() { - // Assume custom testnets have the same HRP - // - // TODO: add the regtest HRP here - testnet::HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY - } else { - mainnet::HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY - } - } -} diff --git a/zebra-chain/src/primitives/viewing_key/tests.rs b/zebra-chain/src/primitives/viewing_key/tests.rs deleted file mode 100644 index a073f484e34..00000000000 --- a/zebra-chain/src/primitives/viewing_key/tests.rs +++ /dev/null @@ -1,15 +0,0 @@ -//! Tests for zebra-chain viewing key hashes - -use super::*; - -/// The extended Sapling viewing key of [ZECpages](https://zecpages.com/boardinfo) -pub const ZECPAGES_SAPLING_VIEWING_KEY: &str = "zxviews1q0duytgcqqqqpqre26wkl45gvwwwd706xw608hucmvfalr759ejwf7qshjf5r9aa7323zulvz6plhttp5mltqcgs9t039cx2d09mgq05ts63n8u35hyv6h9nc9ctqqtue2u7cer2mqegunuulq2luhq3ywjcz35yyljewa4mgkgjzyfwh6fr6jd0dzd44ghk0nxdv2hnv4j5nxfwv24rwdmgllhe0p8568sgqt9ckt02v2kxf5ahtql6s0ltjpkckw8gtymxtxuu9gcr0swvz"; - -/// Tests that `ViewingKey::parse` successfully decodes the zecpages sapling extended full viewing key -#[test] -fn parses_sapling_efvk_correctly() { - let _init_guard = zebra_test::init(); - - ViewingKey::parse(ZECPAGES_SAPLING_VIEWING_KEY, &Network::Mainnet) - .expect("should parse hard-coded viewing key successfully"); -} diff --git a/zebra-chain/src/primitives/zcash_history.rs b/zebra-chain/src/primitives/zcash_history.rs index 4b52c85d8e8..bf348b56f82 100644 --- a/zebra-chain/src/primitives/zcash_history.rs +++ b/zebra-chain/src/primitives/zcash_history.rs @@ -277,6 +277,7 @@ impl Version for zcash_history::V1 { | NetworkUpgrade::Canopy | NetworkUpgrade::Nu5 | NetworkUpgrade::Nu6 + | NetworkUpgrade::Nu6_1 | NetworkUpgrade::Nu7 => zcash_history::NodeData { consensus_branch_id: branch_id.into(), subtree_commitment: block_hash, diff --git a/zebra-chain/src/primitives/zcash_note_encryption.rs b/zebra-chain/src/primitives/zcash_note_encryption.rs index c1dd891f9ee..a3563d04d19 100644 --- a/zebra-chain/src/primitives/zcash_note_encryption.rs +++ b/zebra-chain/src/primitives/zcash_note_encryption.rs @@ -4,7 +4,6 @@ use crate::{ block::Height, parameters::{Network, NetworkUpgrade}, - primitives::zcash_primitives::convert_tx_to_librustzcash, transaction::Transaction, }; @@ -17,32 +16,24 @@ use zcash_primitives::transaction::OrchardBundle; /// Returns true if **all** Sapling or Orchard outputs decrypt successfully with /// an all-zeroes outgoing viewing key. -/// -/// # Panics -/// -/// If passed a network/height without matching consensus branch ID (pre-Overwinter), -/// since `librustzcash` won't be able to parse it. -pub fn decrypts_successfully(transaction: &Transaction, network: &Network, height: Height) -> bool { - let network_upgrade = NetworkUpgrade::current(network, height); - let alt_tx = convert_tx_to_librustzcash( - transaction, - network_upgrade - .branch_id() - .expect("should have a branch ID"), - ) - .expect("zcash_primitives and Zebra transaction formats must be compatible"); +pub fn decrypts_successfully(tx: &Transaction, network: &Network, height: Height) -> bool { + let nu = NetworkUpgrade::current(network, height); + + let Ok(tx) = tx.to_librustzcash(nu) else { + return false; + }; let null_sapling_ovk = sapling_crypto::keys::OutgoingViewingKey([0u8; 32]); // Note that, since this function is used to validate coinbase transactions, we can ignore // the "grace period" mentioned in ZIP-212. - let zip_212_enforcement = if network_upgrade >= NetworkUpgrade::Canopy { + let zip_212_enforcement = if nu >= NetworkUpgrade::Canopy { sapling_crypto::note_encryption::Zip212Enforcement::On } else { sapling_crypto::note_encryption::Zip212Enforcement::Off }; - if let Some(bundle) = alt_tx.sapling_bundle() { + if let Some(bundle) = tx.sapling_bundle() { for output in bundle.shielded_outputs().iter() { let recovery = sapling_crypto::note_encryption::try_sapling_output_recovery( &null_sapling_ovk, @@ -55,7 +46,7 @@ pub fn decrypts_successfully(transaction: &Transaction, network: &Network, heigh } } - if let Some(bundle) = alt_tx.orchard_bundle() { + if let Some(bundle) = tx.orchard_bundle() { let is_decrypted_successfully = match bundle { OrchardBundle::OrchardVanilla(bundle) => orchard_bundle_decrypts_successfully(bundle), OrchardBundle::OrchardZSA(bundle) => orchard_bundle_decrypts_successfully(bundle), diff --git a/zebra-chain/src/primitives/zcash_primitives.rs b/zebra-chain/src/primitives/zcash_primitives.rs index f8286661c31..02ae8ab58b9 100644 --- a/zebra-chain/src/primitives/zcash_primitives.rs +++ b/zebra-chain/src/primitives/zcash_primitives.rs @@ -1,17 +1,18 @@ //! Contains code that interfaces with the zcash_primitives crate from //! librustzcash. -use std::{io, ops::Deref}; +use std::{io, ops::Deref, sync::Arc}; use zcash_primitives::transaction::{self as zp_tx, TxDigests}; use zcash_protocol::value::BalanceError; use crate::{ amount::{Amount, NonNegative}, - parameters::{ConsensusBranchId, Network}, + parameters::NetworkUpgrade, serialization::ZcashSerialize, - transaction::{tx_v5_and_v6, AuthDigest, HashType, SigHash, Transaction}, + transaction::{AuthDigest, HashType, SigHash, Transaction}, transparent::{self, Script}, + Error, }; // TODO: move copied and modified code to a separate module. @@ -19,17 +20,17 @@ use crate::{ // Used by boilerplate code below. #[derive(Clone, Debug)] -struct TransparentAuth<'a> { - all_prev_outputs: &'a [transparent::Output], +struct TransparentAuth { + all_prev_outputs: Arc>, } -impl zp_tx::components::transparent::Authorization for TransparentAuth<'_> { +impl zcash_transparent::bundle::Authorization for TransparentAuth { type ScriptSig = zcash_primitives::legacy::Script; } // In this block we convert our Output to a librustzcash to TxOut. // (We could do the serialize/deserialize route but it's simple enough to convert manually) -impl zcash_transparent::sighash::TransparentAuthorizingContext for TransparentAuth<'_> { +impl zcash_transparent::sighash::TransparentAuthorizingContext for TransparentAuth { fn input_amounts(&self) -> Vec { self.all_prev_outputs .iter() @@ -56,27 +57,21 @@ impl zcash_transparent::sighash::TransparentAuthorizingContext for TransparentAu // to compute sighash. // TODO: remove/change if they improve the API to not require this. -struct MapTransparent<'a> { - auth: TransparentAuth<'a>, +struct MapTransparent { + auth: TransparentAuth, } -impl<'a> - zp_tx::components::transparent::MapAuth< - zp_tx::components::transparent::Authorized, - TransparentAuth<'a>, - > for MapTransparent<'a> +impl zcash_transparent::bundle::MapAuth + for MapTransparent { fn map_script_sig( &self, - s: ::ScriptSig, - ) -> ::ScriptSig { + s: ::ScriptSig, + ) -> ::ScriptSig { s } - fn map_authorization( - &self, - _: zp_tx::components::transparent::Authorized, - ) -> TransparentAuth<'a> { + fn map_authorization(&self, _: zcash_transparent::bundle::Authorized) -> TransparentAuth { // TODO: This map should consume self, so we can move self.auth self.auth.clone() } @@ -147,12 +142,10 @@ impl zp_tx::components::issuance::MapIssueAuth { - _phantom: std::marker::PhantomData<&'a ()>, -} +struct PrecomputedAuth {} -impl<'a> zp_tx::Authorization for PrecomputedAuth<'a> { - type TransparentAuth = TransparentAuth<'a>; +impl zp_tx::Authorization for PrecomputedAuth { + type TransparentAuth = TransparentAuth; type SaplingAuth = sapling_crypto::bundle::Authorized; type OrchardAuth = orchard::bundle::Authorized; @@ -162,52 +155,6 @@ impl<'a> zp_tx::Authorization for PrecomputedAuth<'a> { // End of (mostly) copied code -impl TryFrom<&Transaction> for zp_tx::Transaction { - type Error = io::Error; - - /// Convert a Zebra transaction into a librustzcash one. - /// - /// # Panics - /// - /// 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 { - tx_v5_and_v6! { - network_upgrade, .. - } => network_upgrade, - Transaction::V1 { .. } - | Transaction::V2 { .. } - | Transaction::V3 { .. } - | Transaction::V4 { .. } => { - panic!("Zebra only uses librustzcash for V5/V6 transactions"); - } - }; - - convert_tx_to_librustzcash( - trans, - network_upgrade - .branch_id() - .expect("V5/V6 txs have branch IDs"), - ) - } -} - -pub(crate) fn convert_tx_to_librustzcash( - trans: &Transaction, - branch_id: ConsensusBranchId, -) -> Result { - let serialized_tx = trans.zcash_serialize_to_vec()?; - let branch_id: u32 = branch_id.into(); - // We've already parsed this transaction, so its network upgrade must be valid. - let branch_id: zcash_primitives::consensus::BranchId = branch_id - .try_into() - .expect("zcash_primitives and Zebra have the same branch ids"); - let alt_tx = zp_tx::Transaction::read(&serialized_tx[..], branch_id)?; - Ok(alt_tx) -} - /// Convert a Zebra transparent::Output into a librustzcash one. impl TryFrom<&transparent::Output> for zcash_transparent::bundle::TxOut { type Error = io::Error; @@ -260,44 +207,70 @@ impl From