Skip to content

Add client support#270

Merged
zancas merged 8 commits into
devfrom
add_client_support
Jul 2, 2026
Merged

Add client support#270
zancas merged 8 commits into
devfrom
add_client_support

Conversation

@zancas

@zancas zancas commented Jun 29, 2026

Copy link
Copy Markdown
Member

Add client support to zcash_local_net so that it can support running zcash_devtool

zancas and others added 8 commits June 12, 2026 20:24
  Clients are the third kind of process zcash_local_net manages,
  alongside validators and indexers. Unlike both, a client binary is
  not a daemon: each wallet operation is a run-to-completion subprocess
  invocation against a persistent wallet directory owned by the client
  struct. This lets zaino's wallet integration tests replace their
  zingolib client stack (#269).

  - `client::Client` trait: launch (restore from mnemonic + birthday
    against a running indexer), sync, send(addr, zats) -> txid,
    shield -> txid, balance -> WalletBalance, default_address, rescan.
    All operations run to completion, so strictly sequential
    act -> mine -> wait -> assert test flows need no extra
    synchronization.
  - `client::ClientConfig::setup_indexer_connection` mirrors
    `IndexerConfig::setup_validator_connection`; launch order is
    validator -> indexer -> client.
  - `client::zcash_devtool::ZcashDevtool` drives the zcash-devtool CLI
    (built with `--features regtest_support`, resolved via
    TEST_BINARIES_DIR/PATH). `ZcashDevtoolConfig::faucet()`
    (abandon-art seed) and `::recipient()` (HOSPITAL_MUSEUM seed)
    provide the standard test wallets; `init` pipes the mnemonic on
    stdin, `send`/`balance` pass `--min-confirmations` (default 1 —
    devtool's own 3-trusted/10-untrusted default policy makes nothing
    rescan from scratch, and shielding self-sent transparent funds
    conserves totals exactly (the faucet is the miner, so fees return
    via coinbase).

  Requires zcash-devtool with non-interactive init and
  --min-confirmations support (paired changes in the zcash-devtool
  branch), and NU6.2-capable zebrad (5.1.0) / zainod (0.4.0) test
  binaries.

  Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
  Wires the Client trait to the surface devtool added in 105a4af ("Add
  machine-readable CLI surface for the zaino wallet-test client"). Until
  now only the unified address was reachable and balance was scraped
  from human-readable text; this unblocks the transparent/sapling half
  of zaino's wallet matrix and removes the fragile parser.

  - `client::AddressReceiver` (Unified | Transparent | Sapling | Orchard)
    and `Client::address(receiver) -> String`. A dedicated enum, not
    `zcash_protocol::PoolType` (which is Transparent | Shielded(..) and
    has no Unified — the wrong shape here). `default_address()` becomes
    a default trait method forwarding to `address(Unified)`, so existing
    callers are unchanged.
  - `ZcashDevtool::address` drives `list-addresses --receiver <pool>`
    (reads the local wallet db, no server args) and parses the
    `Receiver(<pool>): <addr>` lines; unified still parses the
    `Default Address:` line.
  - `ZcashDevtool::balance` switches to `balance --json`, whose keys map
    field-for-field to `WalletBalance`. Deletes the text-scrape parser
    that had to reverse-scan past a `{:#?}` WalletSummary debug dump
    (`parse_balance_output` / `parse_zec_amount` and the ZEC-string
    fixture) — sturdier across zcash_client_* upgrades. Parsed via
    `serde_json::Value` rather than a derived `Deserialize` on
    `WalletBalance`, keeping serde out of the public API
    (cargo-check-external-types).
  - Unit tests pin every new parser against recorded shapes
    (`balance_json_parses`, `balance_json_rejects_bad_shapes`,
    `receiver_lines_parse`). The faucet address integration test now
    asserts all four receivers against the zingo_test_vectors constants:
    unified == REG_O_ADDR_FROM_ABANDONART, transparent ==
    REG_T_ADDR_FROM_ABANDONART, sapling == REG_Z_ADDR_FROM_ABANDONART,
    orchard decodes as a regtest UA — proving the abandon-art wallet
    owns the addresses the harness pays.

  Requires zcash-devtool at branch add_regtest commit 105a4af or later.

  Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Wires the Client trait to the get-info surface the devtool added in
d820388, the do_info analogue used as a "can the wallet reach its
server" smoke check. Completes the machine-readable CLI consumption
begun with address()/balance --json.

- client::Client::get_info -> client::GetInfo { server_uri, chain_name,
  chain_tip_height }. chain_tip_height is the server/node tip (a u64,
  matching the wire LightdInfo.block_height), never the wallet's
  locally-synced height. The field set is a frozen contract with the
  binary; parsed via serde_json::Value to keep serde out of the public
  API (cargo-check-external-types).
- ZcashDevtool::launch now writes the regtest --activation-heights TOML
  from the configured heights and passes it to init. The pinned devtool
  (d820388, via b1f9e6c) requires --activation-heights for `-n regtest`
  (heights are configured at init, not baked in); without it nothing
  launches. Schema mirrors devtool data.rs::ActivationHeights
  (deny_unknown_fields, overwinter..nu6_2, no nu7).
- Unit test pins the parser against a real get-info line captured from
  the d820388 binary: {"chain_name":"test","chain_tip_height":3,
  "server_uri":"http://127.0.0.1:42739"}. Reality corrected two guesses
  - zaino reports regtest as chain_name "test" (not "regtest"), and
  server_uri has no trailing slash.
- connect_to_node_get_info integration test exercises get_info against
  the live binary + indexer (asserts shape populated, server-tip
  semantics via a poll).
- faucet_shields_transparent_funds: replace the brittle "exactly 2
  rewards" assertions with a reward count derived from the snapshots'
  own chain_tip_height delta. The faucet is the miner, so snapshot
  capture heights race run-to-run; the delta captures the real
  invariant (fees net out via coinbase) robustly.

Requires zcash-devtool at branch add_regtest commit d820388 or later.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The single source of truth (zcash_local_net::validator) was already on
NU6.2 — regtest_test_activation_heights sets nu6_2=Some(5) and
REGTEST_FIXTURE_HEIGHTS_CLI_STRING is "all=1,nu5=2,nu6=2,nu6_1=5,nu6_2=5,
nu7=off" — but regtest-launcher trailed it (the stale
"TODO: update regtest-launcher to nu6.2" marked the gap). The parser
rejected the default string with "Unknown activation key 'nu6_2'", which
panicked cli::tests::cli_default_matches_fixture_helper and made the binary
exit at Cli::parse() before startup — also failing the e2e test.

Close all three NU6.2 drop sites in the launcher:
- cli.rs: add Nu6_2 to UpgradeKey and UPGRADE_ORDER (between Nu6_1 and Nu7,
  the correct cascade slot), recognise nu6_2|nu6.2|nu62 in parse_key, handle
  it in set_field, and add it to the "Valid keys" error text and doc comment.
- main.rs: add .set_nu6_2(...) to the ConfiguredActivationHeights ->
  ActivationHeights conversion (without this the NU6.2 height was silently
  dropped even once parsing worked).
- cli.rs test: mirror the field in cli_default_matches_fixture_helper.
- README.md: refresh the example, Keys list, and stale default string.

Also drop the mining bootstrap target_height from 101 to 5. Profiling showed
getblocktemplate (~280ms/block, flat across the NU6.2 boundary — not a
regression) dominated the 101-block bootstrap at ~30-60s, overrunning the
e2e test's 45s deadline. Mining to height 5 still crosses every upgrade
(nu6_1 and nu6_2 both activate at 5) and brings first-mine well inside the
deadline; the e2e snapshot is unaffected (its normalizer rewrites mined
heights to <HEIGHT>).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Factor three repeated patterns out of the integration tests:
- regtest_heights_nu6_1_at(h): replaces three copies of the 11-line
  ActivationHeights::builder() block; encodes the "nu6_1 and nu6_2
  co-activate" invariant in one place.
- READINESS_RPCS + readiness_rpc_failures(): collapses the two duplicated
  four-endpoint RPC failure-collection loops; the endpoint list is now a
  single source of truth.
- init_tracing(): unifies all 36 tracing_subscriber::fmt() init sites and
  standardizes on the panic-safe try_init() (was a mix of .init()/.try_init()).

No behavior change; pure refactor.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Planning doc for wiring the Client trait + ZcashDevtool impl to the
machine-readable surface that zcash-devtool's add_regtest branch shipped
(per-pool `wallet list-addresses --receiver`, `--json` balance/list-tx), so
zaino's DevtoolClients adapter can stop panicking on per-pool addresses.
Design only — no code change.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Zebrad::launch pre-mines a genesis-priming block, so the chain is
deterministically at height 1 after launch_orchard_net (verified 25/25).
faucet_sends_recipient_receives_and_rescans then mines 3 more (-> height 4)
but asserted the height-3 balance (BLOCK_1 + 2*POST_NU6 = 1,862,500,000).

The test only passed when sync_to_height(faucet, 3) happened to sample the
wallet while the indexer still lagged at height 3; when the wallet reached
the true tip 4 first it saw an extra coinbase (BLOCK_1 + 3*POST_NU6 =
2,481,250,000) and failed. That sampling race is why it flaked pass/fail by
load (fast/light load failed, slow/loaded passed).

Sync to the validator's actual tip and derive the expected balance from it,
removing both the off-by-one and the race. The downstream sync_to_height
targets are unaffected: they are `>=` waits the (off-by-one-higher) real
heights already satisfy, and their asserts are on transferred amounts, not
the tip.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Orchard coinbase mining dominated these tests (~84% of wall-clock, ~4.5-9.5s
per block of Halo2 coinbase proving, measured). Mine only the orchard blocks
each send/confirm step actually needs, and derive every sync target from the
validator's real tip so the smaller counts stay correct (also removes the
height-sampling race from faucet_shields). ~-23s / -26s per test.

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

@dorianvp dorianvp 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. I think those md files should not go to dev, but we can remove them later.

@zancas zancas merged commit f4ba2ee into dev Jul 2, 2026
9 of 12 checks passed
zancas added a commit that referenced this pull request Jul 2, 2026
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@zancas zancas mentioned this pull request Jul 2, 2026
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.

2 participants