A smaller "Standing Trust Grant"#277
Open
zancas wants to merge 10 commits into
Open
Conversation
bip0039 existed only to re-derive the hardcoded ABANDON_ART_SEED constant inside a unit test, dragging ~18 crates (zerocopy, the old rand 0.8/getrandom 0.2 stack, pbkdf2/hmac/password-hash, zeroize, unicode-normalization) into every consumer's deploy graph. The test now validates the constant's structure against the canonical BIP-39 zero-entropy vector directly. The unmaintained json crate had a single call site in the zcashd validator; migrated to serde_json, matching the idiom already used in get_activation_heights. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The getblocktemplate -> proposal_block_bytes -> submitblock sequence existed in three copies: Zebrad::generate_blocks and both of regtest-launcher's mining loops (bootstrap and steady-state). Any change to proposal assembly or submission semantics had to land in all three or they drifted silently. One public round trip now serves all callers, returning a BlockSubmission (height, block hash, raw response) whose accepted() documents the duplicate-response ambiguity that generate_blocks' chain-advance polling exists to resolve. regtest-launcher no longer hex-encodes anything; drop its hex dep. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Launch path (findings #1-#3 of the duplication survey): - PortPins trait on launch configs replaces the per-daemon read/clear closure pairs fed to with_retry_on_collision; the three ceremonial single-field *Ports structs are gone (ZebradPorts stays - its four-field atomic pick rationale is real). Collision signatures remain per-daemon, next to their contract docs. - launch::spawn_and_wait owns pipe-setup -> spawn -> readiness wait (with uniform timing instrumentation) for all four daemons; launch::wait is now private. - MAX_LAUNCH_ATTEMPTS hoisted; the take/re-Some dance on the additional-log accumulator in wait() collapsed via as_mut(). Shared helpers: - activation_heights_from_getblockchaininfo now owns the upgrades extraction both validators duplicated. - zebra_rpc decode_hex/fixed_bytes<N> replace three hand-rolled InvalidHex mappings. - JsonLine wrapper unifies the balance/get-info line-scan parsers in the devtool client. - config.rs write_config_file is the single config write path. - The inherent logs_dir() getters duplicated the LogsToDir impls byte-for-byte on five structs; the trait is now the one definition. Left alone deliberately: the six identical Drop impls (Rust forbids blanket Drop; a macro would trade explicitness for less than it saves) and the per-endpoint RPC probe tests (separate tests preserve failure granularity). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Function-first triage: the data_dir() getters on both validators duplicated their Validator::data_dir trait impls byte-for-byte, so those die by plain deletion, no macro needed. What remains is exactly what helper fns cannot express - trait impl blocks and method definitions: - macros::impl_stop_on_drop! replaces the five identical drop-delegates-to-stop impls (Zcashd, Zebrad, Zainod, Lightwalletd, Empty). Opt-in per type because the language forbids the blanket impl<T: Process> Drop for T this would otherwise be. LocalNet's generic Drop stays hand-written. - macros::ref_getters!/copy_getters! generate the ~20 remaining field accessors across the daemon and devtool structs, doc comments preserved per field. - zingo-consensus: local height_getters!/height_setters! collapse the 11 activation-height getters and 11 builder setters. These are in-repo macro_rules, not a derive dependency - the getset removal's objection was the dependency, not macros. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…lt feature
Phase 1 of the two-phase excision recorded in
docs/adr/0001-excise-legacy-stack.md: everything zcashd- and
lightwalletd-shaped moves behind a new non-default `legacy-stack`
cargo feature; phase 2 (within two weeks) deletes the gated code and
the feature entirely. zcash_local_net bumps 0.6.0 -> 0.7.0.
Gated under `legacy-stack` (UNSUPPORTED and UNTESTED — CI never
enables it; it exists only as a short-lived migration stopgap):
- validator::zcashd and indexer::lightwalletd
- ProcessId::{Zcashd, Lightwalletd}
- LaunchError::{UnsupportedZcashdCapability, CapabilityProbeFailed}
- Validator::get_zcashd_conf_path + zebrad's impl and the
compatibility zcash.conf zebrad wrote solely for lightwalletd
- config::{write_zcashd_config, write_lightwalletd_config} and the
ZCASHD/LIGHTWALLETD filename constants, logs::LIGHTWALLETD_LOG
- all legacy integration and unit tests (kept, not deleted, so one
manual `--features legacy-stack` smoke remains possible pre-release)
Deleted outright (no consumer even under the gate):
- the checked-in zcashd-generated chain cache
(chain_cache/client_rpc_tests/) and generate_zcashd_chain_cache;
in-repo tests only use the zebrad-generated client_rpc_tests_large
- cert/cert.pem (no consumer anywhere; lightwalletd runs
--no-tls-very-insecure)
- the CI step symlinking zcashd/zcash-cli/lightwalletd binaries and
the legacy lines in the utils/*.sh binary scripts
Also: generate_zebrad_large_chain_cache launches a bare Zebrad
instead of LocalNet<Zebrad, Lightwalletd>; the executable_finder
ignored test probes zebrad instead of zcashd; crate docs/README
rewritten around the zebrad + zainod + zcash-devtool core stack.
New: CONTEXT.md (glossary: Legacy stack, Core stack, Compatibility
conf) and docs/adr/0001-excise-legacy-stack.md capturing why one
feature instead of two, why the gate is deliberately untested, and
why the Validator/Indexer abstractions stay.
Verified: cargo check --workspace --all-targets (-D warnings),
clippy, fmt, and cargo doc clean in both feature configs; lib unit
tests pass 24/24 (default) and 35/35 (legacy-stack).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Findings from a dependency audit of the workspace:
- zcash_local_net: delete the [build-dependencies] section (hex, tokio
with three features). The crate has no build.rs, so the section was
inert manifest noise. cargo-machete does not flag this class.
- regtest-launcher: drop assert_cmd (-9 lockfile entries). Its entire
usage was one macro call locating the test binary; cargo provides
this natively to integration tests via env!("CARGO_BIN_EXE_<name>").
- regtest-launcher: drop insta (-4 lockfile entries) and its orphaned
[profile.dev.package] opt-level override. The single snapshot
assertion is now a plain golden-file compare against
tests/golden/regtest_first_mine.txt; set UPDATE_GOLDEN=1 to re-bless
in place of `cargo insta review`.
Audited and deliberately kept: sha2 (crypto, never hand-roll), hex and
owo-colors (zero transitive deps), regex/nix/anyhow (dev, properly
used), serde (required by serde_json integration). A reqwest
replacement (~70 entries hang off two localhost HTTP calls) was
prototyped and rejected: not worth the breaking public API surface
(RpcClientError::Transport, RpcRequestClient::call, Zebrad
healthy/ready signatures).
Verified: cargo check --workspace --all-targets in both feature
configs, 24/24 lib unit tests, cargo fmt --check, and the
regtest-launcher e2e golden test passing against real zebrad + zainod.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Retitle the Unreleased section to 0.7.0 (2026-07-03) and add the entries missing since 0.6.0: the rpc_client and zebra_rpc modules, the zingo-consensus type migration and MinerPool (both breaking), the getset -> hand-written accessor swap, and the removal of every zebra/librustzcash/zcash ecosystem dependency plus bip0039, json, and getset. Also correct the RUSTSEC reference in the macros.rs module doc: the advisory this repo actually tracked (and whose cargo-deny ignore the getset removal deleted) is RUSTSEC-2026-0173 against proc-macro-error2 itself, not RUSTSEC-2024-0370 against its predecessor. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
546 → 142 lockfile entries since this effort diverged from main: a 74% reduction. Here's the justification:
Why these changes
Every entry in Cargo.lock is a "Standing Trust Grant".
These entries imply a publisher who can ship code into this build, a crate that must be audited (or have its audit imported via cargo-vet), and a potential vector when any of them is compromised or abandoned.
These PRs (this and previous) attack that surface on three fronts.
Link binaries' protocols, not their code.
The largest win was architectural: the harness drives zebrad but no longer links it.
I replaced zebra-node-services with a hand-rolled JSON-RPC client and the zebra-rpc block-assembly code with an in-crate zebra_rpc module.
The "in house" zebra_rpc API is pinned by golden fixtures captured from a live differential run.
These two changes allowed me to remove the entire zebra/librustzcash tree — 361 lockfile entries in the final excision commit alone. Bear in mind that this is from the build of a crate whose actual job is launching subprocesses. A test harness that transitively compiles half the Zcash ecosystem to POST JSON at localhost was pure inherited risk.
Tolerated Security Advisories: >= 2 --> 0
The effort eliminates every advisory-flagged unmaintained crate the workspace carried:
Cargo deny now passes with no advisories waived at all. The policy surface went from "we know about this one and accept it" to empty.
Smaller cuts follow the same logic: bip0039 dragged ~18 crates (the stale rand 0.8/getrandom 0.2 stack, zerocopy, pbkdf2 machinery) into every consumer's graph to re-derive one hardcoded constant in one test.
Use A Feature Gate, to prepare for lwd/zcashd deprecation:
Zcashd is deprecated upstream; its patched fork and lightwalletd were the harness's last legacy obligations.
Gating them behind an explicitly unsupported legacy-stack feature (excision scheduled in 0.8.0, ADR-0001) means the default build — what CI verifies and consumers get — carries no code, no config machinery, and no CI binary provisioning for components with no future.
I considered removing reqwest but that would have significantly altered the public API, for relatively little kruft reduction. reqwest is widely used.
How these assertions were verified