From 16afacc8581b119cc6a21b540e33e61557f0f693 Mon Sep 17 00:00:00 2001 From: Mark Henderson Date: Mon, 6 Jul 2026 10:48:01 -0400 Subject: [PATCH 1/3] ci: assert z_importaddress feature is compiled into zero-zallet The method is gated behind the transparent-key-import cargo feature (enabled via zcashd-import in zallet/Dockerfile). Guard against a build change silently dropping it: invalid hex with a parseable account UUID must reach the feature-gated parser and answer "Invalid hex encoding", not the feature-off stub error. Params are JSON-quoted because the `zallet rpc` CLI parses each param with serde_json. Co-Authored-By: Claude Fable 5 --- .github/workflows/z3-smoke.yml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/.github/workflows/z3-smoke.yml b/.github/workflows/z3-smoke.yml index 1ba3b59..2f947cb 100644 --- a/.github/workflows/z3-smoke.yml +++ b/.github/workflows/z3-smoke.yml @@ -214,6 +214,24 @@ jobs: sleep 5 done + - name: z_importaddress is compiled in (transparent-key-import) + run: | + set -euxo pipefail + # Watch-only transparent import is feature-gated at compile time + # (transparent-key-import, pulled in by zcashd-import in the + # Dockerfile). A build without it answers every z_importaddress call + # with "requires the transparent-key-import feature". Invalid hex + # with a parseable account UUID deterministically reaches the + # feature-gated parser instead, which must answer "Invalid hex + # encoding": a positive proof the real implementation is present, + # with no wallet-account state involved. + # (`zallet rpc` parses each param as JSON, hence the quoted strings.) + out=$(docker compose exec -T zallet zallet \ + --datadir /var/lib/zallet --config /etc/zallet/zallet.toml \ + rpc z_importaddress '"00000000-0000-0000-0000-000000000000"' '"zz"' 2>&1) || true + echo "$out" + echo "$out" | grep "Invalid hex encoding" + - name: No crash-loops, no accidental exposure run: | set -euxo pipefail From c85468558a6c4db5a78567f3bf9589cc8f705a37 Mon Sep 17 00:00:00 2001 From: Mark Henderson Date: Mon, 6 Jul 2026 10:48:01 -0400 Subject: [PATCH 2/3] docs: watch-only transparent import (z_importaddress) in z3-stack README Answers the recurring migration question from zcashd importaddress / importmulti: the feature is already compiled into zero-zallet images, the method takes a hex pubkey or redeem script (not a t-address), and rescan starts from the account birthday. Also notes that help output is build-time static and proves nothing about compiled-in features. Co-Authored-By: Claude Fable 5 --- deploy/z3-stack/README.md | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/deploy/z3-stack/README.md b/deploy/z3-stack/README.md index 0f0a63e..f4f29e1 100644 --- a/deploy/z3-stack/README.md +++ b/deploy/z3-stack/README.md @@ -170,6 +170,41 @@ docker port zaino # empty (unless you deliberately published gRPC) docker port zallet # empty (unless you deliberately published the wallet RPC) ``` +## Watch-only transparent import (z_importaddress) + +Migrating a zcashd `importaddress` / `importmulti` watch-only flow? Zallet's +equivalent is `z_importaddress`, and the `zero-zallet` images ship with it +enabled (the Dockerfile builds with `--features rpc-cli,zcashd-import`, which +includes the `transparent-key-import` feature). It is a compile-time feature; +there is no `zallet.toml` or environment switch. + +Two differences from zcashd matter: + +- **It takes a hex-encoded public key (P2PKH) or redeem script (P2SH), not a + `t1...` address.** zcashd accepted the bare address and watched its + scriptPubKey; zallet needs the actual key material. For a P2PKH address you + must supply its 33-byte compressed (or 65-byte uncompressed) pubkey. +- **`rescan` (third parameter, default `true`) rescans from the target + account's birthday, not from genesis.** The call returns immediately and + the scan runs in the background sync tasks (watch `getwalletstatus`). + History older than the account's birthday will not be discovered, so + import into an account whose birthday predates the address's first use. + +```sh +# The account UUID comes from z_getnewaccount (or z_listaccounts): +docker compose exec zallet zallet \ + --datadir /var/lib/zallet --config /etc/zallet/zallet.toml \ + rpc z_importaddress +# -> {"type": "p2pkh", "address": "t1..."} +``` + +One caveat when auditing a binary: `help` and `rpc.discover` list every +method known at build time, whether or not its feature was compiled in, so +`help z_importaddress` proves nothing. A build without the feature answers +every call with "z_importaddress requires the transparent-key-import +feature"; these images answer with real parameter validation, and the smoke +workflow asserts exactly that. + ## Security notes - **Never publish Zebra's 8232.** It is unauthenticated by design here; From e73b2362febeb521154e821d5e5b57f7b4dfc5ca Mon Sep 17 00:00:00 2001 From: Mark Henderson Date: Mon, 6 Jul 2026 11:01:57 -0400 Subject: [PATCH 3/3] docs: account birthday mechanics for z_importaddress rescan z_getnewaccount pins the birthday at the current chain tip, so importing an address with existing history into a fresh account finds nothing. Point readers at z_recoveraccounts and its explicit birthday_height, and JSON-quote the CLI example params. Co-Authored-By: Claude Fable 5 --- deploy/z3-stack/README.md | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/deploy/z3-stack/README.md b/deploy/z3-stack/README.md index f4f29e1..fee91cc 100644 --- a/deploy/z3-stack/README.md +++ b/deploy/z3-stack/README.md @@ -187,14 +187,19 @@ Two differences from zcashd matter: - **`rescan` (third parameter, default `true`) rescans from the target account's birthday, not from genesis.** The call returns immediately and the scan runs in the background sync tasks (watch `getwalletstatus`). - History older than the account's birthday will not be discovered, so - import into an account whose birthday predates the address's first use. + History older than the account's birthday will not be discovered, and + `z_getnewaccount` pins a new account's birthday at the current chain tip. + To import an address that already has history, create the target account + with `z_recoveraccounts` instead: it takes an explicit `birthday_height`, + which must be set before the address's first use. ```sh -# The account UUID comes from z_getnewaccount (or z_listaccounts): +# The account UUID comes from z_getnewaccount / z_recoveraccounts (or +# z_listaccounts). The `zallet rpc` CLI parses each param as JSON, so +# string params need the extra quotes: docker compose exec zallet zallet \ --datadir /var/lib/zallet --config /etc/zallet/zallet.toml \ - rpc z_importaddress + rpc z_importaddress '""' '""' # -> {"type": "p2pkh", "address": "t1..."} ```