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 diff --git a/deploy/z3-stack/README.md b/deploy/z3-stack/README.md index 0f0a63e..fee91cc 100644 --- a/deploy/z3-stack/README.md +++ b/deploy/z3-stack/README.md @@ -170,6 +170,46 @@ 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, 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 / 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 '""' '""' +# -> {"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;