Skip to content

Advance rc/0.6.0 to dev for 0.6.0-rc.3#1397

Merged
nachog00 merged 83 commits into
rc/0.6.0from
dev
Jul 13, 2026
Merged

Advance rc/0.6.0 to dev for 0.6.0-rc.3#1397
nachog00 merged 83 commits into
rc/0.6.0from
dev

Conversation

@nachog00

Copy link
Copy Markdown
Contributor

Summary

Bring the rc/0.6.0 branch up to current dev ahead of the 0.6.0-rc.3 tag.

The previous RC tags (rc.1, rc.2) were cut from a divergent release_alpha branch. This PR starts fresh: rc/0.6.0 was recreated from the 0.6.0-rc.1 tag (which is a clean ancestor of dev), and this PR fast-forwards it to dev HEAD.

Includes all work since rc.1:

Blocked on #1394 (bump_deps) merging into dev first — that PR advances zebra deps to v6.0.0 stable and drops the zcash_* pre-release pins.

Once merged, version bump + 0.6.0-rc.3 tag follow on this branch.

zancas and others added 30 commits July 1, 2026 21:13
The front-door test set is named for the `packages/` directory it runs, so
the plural is the faithful name. Hard-cut: `makers test package` now errors
with `unknown set` rather than aliasing.

Updates every reference site: Makefile.toml (default, case matcher, dispatch,
descriptions, comment block), help.sh, docs/testing.md, README.md, and the
live-tests/CONTEXT.md glossary term (now records the retired singular under
_Avoid_). ADR-0004 gets a one-line supersession footer; its historical text
is preserved.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…o_blockchainsource

# Conflicts:
#	live-tests/e2e/tests/devtool.rs
#	live-tests/e2e/tests/test_vectors.rs
#	packages/zaino-state/src/backends.rs
#	packages/zaino-state/src/backends/state.rs
#	packages/zaino-state/src/chain_index.rs
#	packages/zaino-state/src/chain_index/source/validator_connector.rs
#	packages/zaino-state/src/indexer/node_backed_indexer.rs
Deduplicate zaino-common and delete its dead public API; net -247
lines of Rust, behavior preserved.

logging.rs (386 -> 187 lines):
- Collapse the six init_*/try_init_* bodies into one private
  try_install(LogConfig); init() panics if a global subscriber is
  already set, try_init() ignores that case.
- Delete unused DisplayHash/DisplayHexStr and the unreachable
  programmatic config surface (LogConfig builder methods,
  show_span_events, the *_with_config entry points); LogConfig and
  LogFormat are now private.
- The ZAINOLOG_FORMAT/ZAINOLOG_COLOR/RUST_LOG interface and all three
  output formats are unchanged.

config/network.rs (371 -> 308 lines):
- New activation_heights_mirror! macro records the NetworkUpgrade
  variant <-> ActivationHeights field correspondence once and expands
  to the two mirror From impls plus from_zebra_pairs (all-None init +
  upgrade-list walk). Exhaustive destructures and match keep full
  compile-time drift detection; a future network upgrade lands as a
  single (Variant, field) pair.
- Delete Network::zaino_regtest_heights (no callers workspace-wide).

CHANGELOG: breaking removals and internal refactors recorded under
Unreleased; version bump deferred to release.

Verified: cargo check --workspace, cargo clippy --all-targets,
cargo fmt, cargo nextest run -p zaino-common (19/19 passed),
doctests (8/8 passed).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Compiler-verified sweep: each dependency in zaino-common's manifest was
deleted one at a time and the crate re-checked with
`cargo check -p zaino-common --all-targets`; a passing check kept the
deletion, a failing one restored the dependency.

Dropped (zero references, check passes without them):
- thiserror — never used by this crate
- nu-ansi-term — never used directly; tracing-tree pulls its own copy
  transitively
- hex — last consumers were DisplayHash/DisplayHexStr, removed in the
  preceding DRY commit

Kept (check fails without them): zebra-chain, serde, tracing,
tracing-subscriber, tracing-tree, time.

The root [workspace.dependencies] nu-ansi-term entry is now unused by
every member but left in place; removing it is a separate root-manifest
decision.

Verified: cargo check --workspace --all-targets clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
zainod 0.5.0 panicked at startup when gRPC TLS was enabled: the zebra
bump added a second rustls provider path (zebra-rpc ->
zebra-node-services/rpc-client -> reqwest/rustls-tls -> ring) alongside
zaino's own reqwest (aws-lc-rs), and rustls 0.23 refuses to auto-select
a provider when both features are enabled. Reproduced and confirmed by
a two-arm experiment recorded on the issue. Fixes #1360.

- workspace reqwest: "rustls" -> "rustls-no-provider" (drops aws-lc-rs
  from the graph entirely)
- zaino-serve: tonic gains "tls-ring" so zaino chooses ring itself
- zaino-common: new crypto::ensure_default_crypto_provider(), a guarded
  first-install-wins ring install that respects an embedder's provider;
  this is the load-bearing fix (Arm B: dual-provider graph passes with
  it), the feature pinning is defense-in-depth and build hygiene
- called at the two boundaries that build rustls configs: the outbound
  reqwest client factory in zaino-fetch (new build_rpc_client, DRYing
  three hand-rolled ClientBuilder chains) and the gRPC TLS acceptor in
  zaino-serve
- regression test: TLS-enabled spawn + real handshake against a
  committed test-CA/leaf fixture (CI previously ran plaintext only);
  fails with the exact reported panic under the pre-fix feature graph

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The two functions zaino-common calls — CryptoProvider::install_default
and get_default — have no-std implementations in rustls 0.23 (the
process default falls back from std::sync::OnceLock to
once_cell::race::OnceBox), so "std" is not required by our usage. It is
also enabled in any full build regardless, because tokio-rustls
hard-enables rustls/std. Minimum-necessary-scope: declare only "ring".

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
continue-on-error makes the run succeed but still shows the failed job
as a red X in the PR checks list, which reads as a blocking failure.
Move the advisory decision into the step: on non-rc/non-stable contexts
a failure emits a ::warning annotation and exits 0; rc/** and stable
keep the hard failure. Also generalize the failure epilogue: same-name
version divergence from crates.io (new cross-crate API or features,
resolved by release-time bumps) is as common a cause as patch overrides.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ossary

Record the decision reversing zaino's rustls provider from ring to
aws-lc-rs, per Daira Emma's post-quantum argument:
- aws-lc-rs is the preferred (first-install-wins, not mandated) provider
- rustls prefer-post-quantum enabled: X25519MLKEM768 leads on the
  client leg; rustls servers follow client group preference, so inbound
  hybrid uptake is client-driven
- classical key exchange accepted but deprecating, gated on wallet
  stacks (zingolib is ring-pinned; ZODL rides mobile platform TLS)
- ring tolerated in Cargo.lock via the zebra-node-services reqwest path,
  fenced by an exact-snapshot CI lint, until the upstream zebra fix

References:
- https://crates.io/crates/rustls-post-quantum
- https://www.reddit.com/r/rust/comments/1de13y6/comment/l89pbmc/

CONTEXT.md begins the project glossary: Preferred CryptoProvider,
Hybrid key exchange, Classical key exchange.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Switch zaino's rustls provider from ring to aws-lc-rs and enable
rustls's prefer-post-quantum feature, so the X25519MLKEM768 hybrid key
exchange leads zaino's outbound handshakes (rustls servers follow the
client's group preference, so inbound hybrid uptake is client-driven).
Installation stays first-install-wins: an embedder that installs a
provider before zaino keeps its choice. Full rationale in ADR-0006.

- zaino-common: rustls features ring → aws_lc_rs + prefer-post-quantum;
  ensure_default_crypto_provider installs aws-lc-rs
- zaino-serve: tonic tls-ring → tls-aws-lc
- ring remains in Cargo.lock solely via zebra-rpc → zebra-node-services
  rpc-client → reqwest 0.12 "rustls-tls" (reqwest 0.12 offers no aws-lc
  alternative); it is compiled but dormant — reqwest consults the
  process-default provider first. Removal tracks an upstream zebra fix.
- new workbench guard check-crypto-provider (makers lint-crypto-provider)
  pins the ring-selecting feature edges to exactly that tolerated path,
  failing loud on new edges and on ring's disappearance alike
- classical key exchange (X25519, SECP256R1, SECP384R1) is deprecated:
  still accepted for wallet compatibility, slated for refusal once major
  wallet TLS stacks negotiate hybrids

References:
- https://crates.io/crates/rustls-post-quantum
- https://www.reddit.com/r/rust/comments/1de13y6/comment/l89pbmc/

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Replace the matches! closure in check-crypto-provider's tree-prefix
trimming with the char-array pattern clippy suggests. The lint escaped
local verification because a chained `fmt --check && clippy` short-
circuited on the fmt diff, so clippy never ran.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
fix: install rustls CryptoProvider explicitly and pin provider to aws-lc-rs
…truth

The Validator's configured activation heights are now the only source of
truth for every component, enforced at compile time on both sides of the
zcash_local_net seam (zaino#1076; infrastructure ADR 0003 and PR #278;
the inbound spec is committed as zainod-heights-from-validator-spec.md).

On the indexer side, zaino_common::Network is a payload-free kind, so a
configuration value cannot carry heights and a pre-handshake height read
does not typecheck.

Both backends construct the runtime zebra_chain::parameters::Network in
exactly one place: the spawn handshake, which uses zebra's compiled
parameters for the public networks and adopts the validator's reported
schedule from getblockchaininfo.upgrades for regtest.

An upgrade absent from the validator's map is never-activated, nothing
is backfilled from defaults, and a failed or unparseable handshake
aborts startup with an error naming the validator endpoint. Every
downstream consumer — the chain-index config, the connectors, the
finalised state, and the subscribers via ServiceMetadata — receives that
runtime network and can no longer manufacture its own; the conversions
that let a config value impersonate chain truth (to_zebra_network and
the From impls into zebra's type) are deleted, along with the dead
accessors that invited a bypass. Test fixtures that are their own chain
construct runtime networks explicitly through

ActivationHeights::to_regtest_network.

On the wallet side, the zcash_local_net pin moves to 78511f7, where the
devtool client's network is a WalletNetwork whose regtest variant only
WalletNetwork::from_validator can construct. The e2e client builder
becomes build_clients(port, &validator) and derives the wallet schedule
from the running validator, so:

 *wallet/validator height drift is unrepresentable*

 and a fixture's heights are typed exactly once, in the
zebrad launch config. The harness no longer mirrors fixture heights
into zainod: every custom-heights launch doubles as a regression test
of the adoption path. The canonical launch set moves to zaino-testutils
as ZEBRAD_DEFAULT_ACTIVATION_HEIGHTS, which is validator-launch
vocabulary rather than anyone's schedule.

The uniqueness regressions live in
clientless/tests/validator_heights.rs: a kind-only-configured zainod
must sync a NU6.3-at-6 schedule its configuration never saw, and a
golden test pins the upgrades map a live zebrad reports. Three unit
tests beside activation_heights_from_upgrades pin the mapping itself.

The production workspace, all live-test crates, and the unit suites
compile and pass clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…xture

The ironwood_activation suite exercises the wallet-observable
predicates across the Orchard-to-Ironwood boundary on
ORCHARD_THEN_IRONWOOD_ACTIVATION_HEIGHTS, the hermetic replay of the
public testnet's NU6.3 activation at height 4,134,000. The wallets
derive their schedule from the running validator and zainod adopts the
same schedule over getblockchaininfo, so the fixture heights are typed
exactly once, in the zebrad launch config.

unified_receipt_lands_in_orchard_before_boundary pins Orchard-era
receipt semantics. orchard_note_spends_to_ironwood_across_boundary
exercises the ZIP 318 migration shape: an Orchard note minted before
the boundary, spent after activation to a freshly generated unified
address, with the receipt asserted in the Ironwood pool. It also pins
that the send actually exits the Orchard pool, which is sound under the
cross-address restriction because change can return only to the spent
note's own receiver. Both cells register on the fetch and state
backends. The public testnet can never host the migration cell: its
pre-NU6.3 epoch is closed and no pre-activation Orchard TAZ is
obtainable, so this fixture is the only controlled venue.

Renames apply the pool-naming convention (NU6_3_ACTIVE_* becomes
IRONWOOD_ONLY_*: pool names pair with pool names, and NU6.3 names only
the upgrade itself). CONTEXT.md gains the supporting glossary entries —
testnet versus regtest nets, the era-naming convention, and the
cross-address restriction with its normative constraint quoted
verbatim. docs/notes/ironwood-activation-plan.md records the design
decisions and their sources.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
nachog00 and others added 24 commits July 9, 2026 13:47
fix(zaino-fetch): deserialize verbosity-2 getblock transaction objects
refactor(state): apply ironwood-migration polish from review of #1379
The new rev (85502cd0) picks up the twelve commits on the bump_to_NU6.3
branch since the previous pin, including deterministic listener-port
allocation with an Indexer-convergence barrier, the generic Wallet
abstraction, and the front proxies published as the canonical endpoints.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The interesting resolutions all follow from dev's unification of
FetchService/StateService into NodeBackedIndexerService meeting this
branch's rule that the validator is the sole source of activation-height
truth (zaino#1076):

The network-adoption logic that lived in the deleted backends module
moves into ValidatorConnector::{spawn_fetch,spawn_state}, which now
return the adopted runtime network alongside the connector, and
activation_heights_from_upgrades moves (with its tests) into
validator_connector.rs. source::State carries the adopted
zebra_chain::parameters::Network together with dev's new
chain_tip_change and sync_task_handle fields.

The v1.3.0 migration keeps dev's PoolActivationHeights::resolve shape,
resolved against the runtime network the config now carries directly.
Test-only construction sites replace the removed payload-carrying
Network::Regtest kind with ActivationHeights::to_regtest_network().

The generic live-test suites (ironwood_activation, validator_heights)
move from FetchService/StateService type parameters to the Rpc/Direct
ValidatorConnectionMarker types, matching dev's ported devtool suite.
dev's dry_zaino_common refactor (#1371) touched the payload-carrying
Network type this branch deletes; the resolution keeps the branch's
payload-free network kind in zaino-common.
The dev merge took the incoming Cargo.lock, which predates this
branch keeping rustls in zaino-common (ADR-0006); cargo regenerates
the entry on the first build.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The NetworkUpgrade-variant ↔ ActivationHeights-field correspondence is
recorded exactly once, in zaino-common's activation_heights_mirror!
macro, but activation_heights_from_upgrades restated it as a
hand-written thirteen-arm match and both it and its tests restated the
all-None schedule as field-by-field literals. The macro now generates
the two missing derived items — the NEVER_ACTIVATED schedule and the
per-upgrade slot_mut accessor — and the adoption function collapses
onto them. The generated match stays exhaustive, so a new zebra
upgrade variant still fails the build until its pair is added to the
mirror.

Also remove the dead ChainIndexConfig::new: it had no callers and was
the one constructor shape that invited supplying a network from
somewhere other than validator adoption; from_backend_config is now
the sole named production constructor (zaino#1076).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Public-network activation heights come only from checked-in,
ecosystem-shared consensus code; regtest parameters are configured on
the validator at scenario launch and every other component adopts them
from it. adopt_network previously enforced the regtest half but took
the compiled Mainnet/Testnet parameters on faith: a validator running a
*configured* testnet (zebra supports custom testnet activation heights)
would silently drift from the index — the exact bug class this branch
exists to kill, on a different network kind.

adopt_network now fetches getblockchaininfo on every network kind and,
on Mainnet and Testnet, verifies each reported (upgrade, height) pair
against the compiled parameters before trusting them, failing loud on
the first disagreement. Only reported entries are checked, not report
completeness: the upgrades map is branch-ID-keyed, so unconfigured
upgrades are simply absent and an older validator may not know the
newest ones.

Also document two acceptance decisions the spec made but the code did
not state: adoption happens exactly once at spawn (a validator
restarted mid-session with a different schedule is out of scope), and
before_overwinter is always None when adopted (BeforeOverwinter has no
consensus branch ID, so it can never appear in the report; zebra's
new_regtest supplies its handling).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The public shared-consensus test network and a hermetic regtest chain
are categorically different things, and the bare word "testnet" has
named both. Where the intention is the public network, the canonical
vocabulary is now: "The Public Testnet" in prose, `the_pub_testnet` in
identifiers, and `PubTestnet` in types. The CONTEXT.md glossary entry
records the rule.

The config kind renames to `Network::PubTestnet`. Its canonical config
spelling becomes `"PubTestnet"`, with `#[serde(alias = "Testnet")]`
keeping existing config files parsing; a zainod config test pins the
legacy spelling. The Public-Testnet chain-cache static renames to
ZEBRAD_THE_PUB_TESTNET_CACHE_DIR, and the boundary-walk suite renames
to the_pub_testnet_ironwood_boundary (the Makefile ironwood filter
follows). References to zebra's own types and config format
(parameters::Network::Testnet, NetworkType::Testnet, zebrad.toml) are
foreign vocabulary and keep their spelling.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
ChainWork::to_u256 takes self by value (the type is Copy, and clippy's
wrong_self_convention expects to_* methods on Copy types to consume
self), and the address-deltas test fixture uses i.is_multiple_of(2)
in place of the manual modulo test.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
feat!: enforce the Validator as the sole source of activation-height truth
An audit of every zebra* and zcash* dependency edge (cargo machete, the
compiler's unused_crate_dependencies lint, and inspection of each
remaining single-reference hit) found exactly one dead edge: zainod
declared zebra-chain but never referenced it. Remove it. Every other
declared edge is genuinely used; zebra-chain remains the type backbone
of the workspace, with some 350 references in zaino-state alone.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Advance ZEBRA_VERSION in .env.testing-artifacts from 6.0.0-rc.0 to the
final 6.0.0 release. Zebrad 6.0.0 depends on zebra-chain ^11.1.0, the
same crate generation the workspace now pins, so the zebrad binary the
live tests run against and the zebra libraries zaino links are once
again from a single release. The makers check-matching-zebras guard
passes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A workspace-wide unused-dependency audit (the compiler's
unused_crate_dependencies lint analyzed per target, cross-checked with
cargo machete and a text search of every candidate's package, including
feature-gated code) found twelve dead edges. Remove them: prost, bs58,
nonempty, derive_more, and once_cell from zaino-state;
tracing-subscriber from zainod; and tracing, corez, serde_json,
tempfile, tower, and zip32 from clientless. The bs58, nonempty, and
zip32 entries in [workspace.dependencies] lost their last inheritors
and are removed with them.

One edge that looks dead is kept deliberately: clientless does not
import zainod in any test source, but its
transparent_address_history_experimental feature forwards to
zainod/transparent_address_history_experimental, and cargo rejects a
feature forward to a non-dependency. A comment in the manifest now
records that.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The same audit that removed the dead edges found three dependencies
that are real but used exclusively under #[cfg(test)]: primitive-types
in zaino-state (the legacy_chainwork_reference round-trip tests),
tempfile in zainod (the config tests), and tonic in zaino-testutils
(the cfg(test)-gated build_client helper). Move each from
[dependencies] to [dev-dependencies] so shipped builds stop linking
them. The lockfile is unchanged: dependency kind does not affect
resolution.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…mary

`makers test all` previously ran the packages set outside the summary
runner, so the closing table covered only the two live partitions. The
`all` branch now delegates to `live-summary --all`, which runs `makers
container-test` first and prints its tallies as a `packages:` row above
`clientless:`, `e2e:`, and `TOTAL:`, under a "test summary" header.
`makers test live` is unchanged apart from the new column. The old
contract is preserved: every set runs even when an earlier one fails,
and the runner exits non-zero if any set did.

Each row also gains a wall-clock time column, parsed from the nextest
summary line's `[ 510.718s]` stamp. Durations are held as integer
milliseconds in a u64 — whole seconds and a right-padded three-digit
fraction parsed digit-by-digit, no floating point — and the TOTAL row
sums them, which reads as true wall time because the suites run
sequentially. Six new unit tests cover the parser (padding, short and
absent fractions, and propagation through parse_summary), and the
pre-existing `filter(..).last()` on a double-ended iterator is
tightened to `rfind(..)` per clippy.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The test-environment image rebuild (first since the ZEBRA_VERSION bump
changed the image tag) failed because Debian trixie point updates
replaced two pinned package versions, and the archive does not keep
superseded versions: protobuf-compiler 3.21.12-11 -> 3.21.12-11+deb13u1
(all three build stages) and curl 8.14.1-2+deb13u3 -> 8.14.1-2+deb13u4
(both stages). Every other pin in the Containerfile still matches the
archive; verified against qa.debian.org/madison and a live
trixie-slim container.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ut parsing

Replace the duration_millis parser (which scraped nextest's human-readable
summary line) with Instant::now()/elapsed() around each run_partition call.
The nextest display format is not a stable contract; the runner already
owns the process lifecycle so it can time it directly. This also captures
build time, which the nextest stamp did not include.
PRs that target rc/** or stable carry unbumped dev code by design —
version bumps happen on the RC branch after merge. The blocking mode
was failing these PRs for an expected, non-actionable condition.

Only direct pushes to rc/** or stable are blocking now; PRs targeting
them are advisory.
…for-prs

fix(ci): publish dry-run advisory for PRs targeting rc/stable
@nachog00
nachog00 enabled auto-merge July 13, 2026 18:24
@nachog00
nachog00 requested a review from zancas July 13, 2026 18:24

@zancas zancas left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Already reviewed for merge into dev.

@nachog00
nachog00 merged commit 5aa5934 into rc/0.6.0 Jul 13, 2026
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants