Skip to content

Release Zaino 0.6.0 (Ironwood)#1401

Merged
nachog00 merged 166 commits into
stablefrom
release/0.6.0
Jul 13, 2026
Merged

Release Zaino 0.6.0 (Ironwood)#1401
nachog00 merged 166 commits into
stablefrom
release/0.6.0

Conversation

@nachog00

Copy link
Copy Markdown
Contributor

Summary

Zaino 0.6.0 — Ironwood/NU6.3 release.

  • Merges rc/0.6.0 into stable, resolving the divergence from the 0.5.1 hotfix
  • Bumps all crate versions to their final stable form
  • Fixes NonFinalizedBlocksListener struct variant regression from the merge

Crate versions

Crate 0.5.1 0.6.0
zaino-common 0.3.0 0.4.0
zaino-fetch 0.3.0 0.4.0
zaino-proto 0.1.4 0.2.0
zaino-serve 0.4.0 0.5.0
zaino-state 0.4.1 0.5.0
zainod 0.5.1 0.6.0

Highlights since 0.5.1

  • Ironwood/NU6.3 support (activation, migration, v6 transactions)
  • BlockchainSource unification (FetchService + StateService → NodeBackedIndexerService)
  • Zebra deps upgraded to v6.0.0 stable (zebra-chain 11.1, zebra-state 10.1, zebra-rpc 11.1)
  • zcash_* deps upgraded from pre-release to stable
  • Startup crash loop fix
  • Multiple zaino-state reliability fixes
  • Publish dry-run CI pipeline
  • Test runner improvements

After merge, tag 0.6.0 on the merge commit to trigger the release workflow.

zancas and others added 30 commits July 1, 2026 10:15
`podman volume inspect` reads podman's database, not the filesystem, so a
named volume can exist in the DB while its backing directory is gone (after
a partial ~/.local/share/containers cleanup or an interrupted `podman system
reset`). The old inspect-only guard then skipped `podman volume create`, and
the later container-test mount failed with "failed to validate if host path
is dangerous: lstat .../volumes/zaino-container-target: no such file or
directory", producing no nextest summary for the live partitions.

Guard on the actual Mountpoint directory instead: when the DB record is
missing or its backing dir is gone, `podman volume rm --force` drops the
dangling record before recreating it, keeping the DB and on-disk storage in
sync. Recovers automatically on the next `makers test` rather than requiring
a full `podman system reset`.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Two production sites hard-coded the finalised/non-finalised seam depth as the
pre-zebra-10 value. zebra-10 raised MAX_BLOCK_REORG_HEIGHT 99->1000, so the real
NON_FINALIZED_DEPTH is 1001 and both were wrong:

- non_finalised_state.rs: the reorg-detection recursion bound used `height + 100`,
  aborting on any reorg deeper than 100 blocks; now uses MAX_NFS_DEPTH, matching
  its sibling bounds at 553/863.
- chain_index.rs get_compact_block_range: the cache/finalised handoff used
  `tip - 99`, routing non-finalised blocks (tip-NON_FINALIZED_DEPTH .. tip-99) to
  the finalised stream that does not hold them; now derives from
  finalized_height_floor(tip). Unchanged for chains shorter than the depth
  (tip-99 == floor+1 at depth 100).

Also update production doc comments describing the seam as a literal "100 blocks"
to reference NON_FINALIZED_DEPTH / MAX_BLOCK_REORG_HEIGHT.

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

Add a consensus module holding the workspace's authoritative, upstream-derived
constants, so nothing hard-codes them:
- MAX_NONFINALISED_DEPTH  = zebra_chain MAX_BLOCK_REORG_HEIGHT + 1
- COINBASE_MATURITY       = zebra_chain MIN_TRANSPARENT_COINBASE_MATURITY
- FAST_TEST_MAX_NONFINALISED_DEPTH = MAX_NONFINALISED_DEPTH / 10 (tractable test seam)

Both consts live in zaino-common, which already depends on zebra-chain, so this
adds no new workspace dependency. Re-source zaino-state's NON_FINALIZED_DEPTH from
the mod (production = real MAX_NONFINALISED_DEPTH; in-crate tests select the derived
FAST_TEST value so short mock fixtures still exercise a moving seam), and the
tx_out_set_accumulator maturity split from COINBASE_MATURITY.

Behaviour-preserving: in-crate test depth stays 100, production stays 1001; the
values are now derived from a single upstream source rather than literals.

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

The FAST_TEST_MAX_NONFINALISED_DEPTH shrink was only reachable by zaino-state's
own cfg(test) build. Cross-crate live tests (e2e, clientless) drive a real
zaino-state instance whose seam depth is a compile-time constant, so the only way
for them to use the tractable seam is a build feature.

Add fast-test-seam to zaino-state: NON_FINALIZED_DEPTH now selects FAST_TEST under
cfg(test) OR the feature, else the real MAX_NONFINALISED_DEPTH. e2e and clientless
enable it via [dev-dependencies] only; resolver = "2" keeps it out of production
graphs (verified: zainod's zaino-state does not carry the feature) and
default-members excludes the live-test crates from production builds.

This un-vacuums the clientless chain_cache eviction/finalisation tests: at depth
100 their ~150-block chain has a real finalised region instead of saturating to
genesis at the production depth of 1001.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The zaino-state-local const is a cfg/feature selector — it resolves to the
authoritative MAX_NONFINALISED_DEPTH in production and to
FAST_TEST_MAX_NONFINALISED_DEPTH under cfg(test)/fast-test-seam — not a source of
truth. The old name read like a source and diverged in spelling from the
finalised_state module (FINALIZED vs FINALISED). OPERATIONAL_NFS_DEPTH names it as
the effective/selected depth the code operates on.

Pure rename, compiler-verified (cargo check -p zaino-state --tests); behaviour
unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
e2e and clientless live tests run zaino-state under fast-test-seam, so their
seam-relative boundaries and prose must reference the mod's
FAST_TEST_MAX_NONFINALISED_DEPTH (the depth those instances actually use), not
hard-coded literals or the retired NON_FINALIZED_DEPTH name.

- e2e get_outpoint_spenders: FINALITY_DEPTH = FAST_TEST_MAX_NONFINALISED_DEPTH + 5.
- e2e prose (send_to_transparent_finalization, get_outpoint_spenders ignore
  reason): retarget the stale NON_FINALIZED_DEPTH mentions to the seam const.
- clientless chain_cache: express the finalised-floor / retention boundaries as
  FAST_TEST_MAX_NONFINALISED_DEPTH +/- test margins (values preserved).

MAX_NFS_DEPTH stays a zaino-state internal (retention window = seam + margin).
Value-preserving and cargo-check clean; no NON_FINALIZED_DEPTH remains in
live-tests.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The load-bearing advance mined 99 blocks to push a send into the finalised DB,
but a send at height H finalises only once the tip reaches H + seam. At the
fast-test-seam depth (100) a 99-block advance falls one block short, so the test
would still query the non-finalised chain if un-ignored.

Advance FAST_TEST_MAX_NONFINALISED_DEPTH + 5 blocks (single-sourced, crosses the
seam with margin), and make the surrounding prose seam-relative so it can't drift.
Still #[ignore]d (waits on cheap transparent filler, round-3 P2).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The zcashd matrix (devtool_zcashd.rs, cfg feature zcashd_support) carries twins of
the zebrad finalization tests, and cargo check without that feature never compiled
them — so the send_to_transparent_finalization 99-block advance had the same
off-by-one, and send_to_all_finalization used a magic 100-block advance.

Both now advance FAST_TEST_MAX_NONFINALISED_DEPTH + 5 (single-sourced, crosses the
seam), with the prose made seam-relative. Verified with
cargo check -p e2e --tests --features zcashd_support.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The round-2 P1 / round-3 P2 / round-3 P3 / round-2 P0 tags reference an internal
roadmap document and are meaningless in the source. Replace each with the plain
capability it named — devtool transparent-coinbase shielding, cheap transparent
filler-block mining, unconfirmed (mempool) wallet balances, zcashd
activation-height alignment — or drop the tag where the surrounding text already
states the fact.

Compiles default and with --features zcashd_support; fmt clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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>
Harden init-podman-volumes against stale volume records
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Phase 3 of the zingo-common disintegration. zaino-common loses its
direct dependency on the shared vocabulary crate: the two From impls
in config/network.rs move to zaino-testutils as named boundary
functions targeting zcash_local_net's re-exported types, following
the existing local_network_from_activation_heights precedent. The
zcash_local_net pin moves to the add_zingo_consensus superset rev,
which contains all of add_client_support plus the zingo-consensus
migration. Production zaino now has no dependency on the activation
heights vocabulary at all.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Move the infrastructure pin from the add_zingo_consensus branch
(0a003b4f) to b09d9c11, the head of remove_unnecessary_deps. The new
commit replaces zcash_protocol::PoolType with zingo_consensus::MinerPool
as the harness's mine-to-pool selector, so the pin bump carries the
matching rename through zaino-testutils (re-export,
SHIELDED_FUNDING_POOL, default_mining_pool, the mine_to_pool
parameters) and the five e2e call sites. The proto-side PoolType enum
in zaino-proto is unrelated and untouched.

Only the three infrastructure-sourced crates move in the lock;
zcash_protocol drops out of zcash_local_net's dependency list.

Verified with cargo check --tests on zaino-testutils/e2e/clientless
under both default features and zcashd_support; fmt clean; clippy
shows only pre-existing warnings.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Follow zingolabs/infrastructure#276 to its current head (029e93eb) by
switching the git dependency from an exact rev to branch tracking; the
committed Cargo.lock still pins the resolved commit, so builds stay
deterministic until the next cargo update.

The PR empties zcash_local_net's dependency tables of the zebra and
librustzcash stacks, which drops an entire duplicate zebra stack from
our lock: zebra-chain 9.0.0, zebra-rpc 9.0.0, zebra-node-services
7.0.0, zebra-consensus/network/script/state 8.x, and
tower-batch-control/tower-fallback. No zaino code changes were needed —
the MinerPool rename in the previous commit was the only API break.

Verified with cargo check --tests on zaino-testutils/e2e/clientless
under both default features and zcashd_support; fmt clean; clippy
shows only pre-existing warnings.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Pure lock bump following new pushes to zingolabs/infrastructure#276:
getset replaced by hand-written accessors (dropping getset from
zcash_local_net's dependency list), the zebra-rpc oracle dev-dependency
deleted, and an external-types allowlist fix. No zaino API impact.

Verified with cargo check --tests on zaino-testutils/e2e/clientless
under both default features and zcashd_support.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Runs `cargo publish --dry-run` for all publishable crates in dependency
order. Advisory (warn-only) on regular PRs; hard-fails on rc/* and
stable branches to catch unpublishable patch overrides before release.
remove zebra and zcash* from infrastructure
nachog00 and others added 28 commits July 9, 2026 13:52
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
Advance rc/0.6.0 to dev for 0.6.0-rc.3
zaino-common    0.2.0             → 0.4.0-rc.3
zaino-fetch     0.2.1             → 0.4.0-rc.3
zaino-proto     0.1.3             → 0.2.0-rc.3
zaino-serve     0.3.1             → 0.5.0-rc.3
zaino-state     0.3.1             → 0.5.0-rc.3
zainod          0.4.3-ironwood.1  → 0.6.0-rc.3

Workspace dependency version pins updated to match.
build: bump crate versions for 0.6.0-rc.3
Merge rc/0.6.0 into stable and bump all crate versions to their
final stable form:

  zaino-common    0.4.0-rc.3  → 0.4.0
  zaino-fetch     0.4.0-rc.3  → 0.4.0
  zaino-proto     0.2.0-rc.3  → 0.2.0
  zaino-serve     0.5.0-rc.3  → 0.5.0
  zaino-state     0.5.0-rc.3  → 0.5.0
  zainod          0.6.0-rc.3  → 0.6.0

Resolves stable/rc divergence from the 0.5.1 hotfix commits that
were never backported to dev (zebra git patch removal, version bumps).

@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.

UtAck!

@nachog00
nachog00 merged commit 1796367 into stable 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