Skip to main content

Wallet Privacy Testkit for Zcash

Wallet Privacy Testkit is an adversarial test layer for Zcash wallet-to-indexer traffic. It records the lengths and timing visible outside TLS, injects uncertainty around SendTransaction, and evaluates size-only transaction matching without awarding full credit to ties.

This is an independent project maintained at ztsalexey/wallet-privacy-testkit. It is not affiliated with or endorsed by the Zcash Foundation, Electric Coin Company, or any wallet or indexer maintainer.

Version 0.1.0 is a research preview. It supplies reusable test components and a verified Zingolib case study. It does not certify a wallet, assign a privacy score, inspect mainnet funds, or replace a wallet's existing integration tests.

Why this exists

A shielded transaction can hide its participants and value on-chain while its network behavior still leaks information. A second failure mode appears when a node accepts a payment but the wallet loses the response. The wallet must distinguish rejection from an unknown outcome and recover without creating another payment.

Existing projects already provision Zcash regtest networks and test wallet behavior. This testkit attaches to those environments and contributes two narrower capabilities:

  • passive ciphertext metadata capture, without TLS keys or payload logging;
  • protocol-aware failure injection before or after transaction submission.

The methodology defines what each result means. The threat model states what it does not establish.

Install from a source checkout

Python 3.11 or newer is required.

python3 -m venv .venv
source .venv/bin/activate
.venv/bin/python -m pip install -r requirements-dev.txt
.venv/bin/python -m pip install --no-deps -e .
.venv/bin/python -m unittest discover -s tests -v

The release was developed with Python 3.12.9, gRPC 1.83.1, and protobuf 7.36.1. CI tests the pinned development environment on Python 3.11–3.13 on Linux and macOS. The broader runtime dependency ranges are not exhaustively tested.

Packaged wheels and source archives are available from GitHub Releases. After activating a virtual environment, install a downloaded wheel with python -m pip install ./wallet_privacy_testkit-0.1.0-py3-none-any.whl.

Passive TLS metadata capture

Point a wallet at the printed local port while the target indexer remains at localhost:9067:

wpt capture \
  --target-host localhost \
  --target-port 9067 \
  --listen-port 7443 \
  --output trace.jsonl

Stop the capture with Ctrl-C, then validate that every captured TCP byte belongs to a complete TLS record:

wpt summarize trace.jsonl

The forwarder copies bytes unchanged. Its trace contains connection numbers, monotonic timestamps, directions, byte counts, TLS content types, and TLS record lengths. It does not terminate TLS, store addresses, or record packet contents. A process boundary supplied by the test operator is still side information and must be disclosed in a report.

Transaction delivery faults

The semantic relay terminates a test TLS connection and forwards the real gRPC service. It understands only enough of the lightwalletd protocol to identify and hash RawTransaction.data; it never writes signed transaction bytes. All other known RPCs are forwarded as opaque bytes using their required streaming cardinality.

The forwarding table was checked against canonical lightwallet-protocol v0.5.0. A later protocol version that adds a streaming RPC requires a testkit update before that RPC can pass through correctly.

For a TLS upstream and TLS-facing wallet:

wpt fault-relay \
  --upstream localhost:9067 \
  --upstream-ca test-ca.pem \
  --certificate localhost.pem \
  --private-key localhost.key \
  --mode after-once \
  --events events.json

The supported modes are:

Mode Behavior
before-once Fail the first submission without forwarding it, then forward normally
after-once Forward the first submission and discard its response, then forward normally
after-all Forward every submission and discard every response

Use the relay only with disposable wallets and isolated regtest funds. The tool deliberately changes transaction-delivery behavior. The test operator remains responsible for checking the node's mempool or chain and the wallet's eventual state.

An insecure local upstream or client can omit the certificate options. The relay binds to 127.0.0.1 by default. Supplying only one of --certificate and --private-key is rejected.

The printed endpoint uses 127.0.0.1. When the wallet-facing side uses TLS, the server certificate must therefore contain the IP address 127.0.0.1 in its Subject Alternative Name. The upstream CA file is required for a TLS upstream; omitting it selects an insecure upstream channel.

Size-only matching

wpt match samples.json accepts a JSON array. Each row must contain a unique id, a batch, observed_bytes, and the corresponding public transaction_bytes. Batch 0 calibrates a single median byte offset by default. Every other observation is compared only with candidates in its own batch.

[
  {"id":"train-a","batch":0,"observed_bytes":9207,"transaction_bytes":9165},
  {"id":"train-b","batch":0,"observed_bytes":12363,"transaction_bytes":12321},
  {"id":"test-a","batch":1,"observed_bytes":9207,"transaction_bytes":9165},
  {"id":"test-b","batch":1,"observed_bytes":12363,"transaction_bytes":12321}
]

Nearest candidates share credit when their distance ties. A batch with fewer than two candidates is rejected because singleton identification says nothing about the matcher. The tool reports a random reference computed from each held-out batch's candidate count.

Verified case study

The included Zingolib v5 regtest report records 18 newly constructed shielded payments and three delivery-fault transactions in the complete run. The wallet reported a payment as failed after its acknowledgement was lost even though Zebra had accepted it. Mining and synchronization corrected the status to confirmed. Current Zingolib development code already includes related duplicate and delivery-check handling, so this is regression evidence rather than a claim of an unresolved current bug.

The case study also found that encrypted lengths distinguished 1-, 2-, and 4-recipient transaction structures in a favorable observer model. Equal-structure payments tied at chance. The result does not estimate deanonymization in normal wallet traffic.

Project status

The release boundary is intentionally small. Z3, Zcash Integration Tests, Regchest, and wallet-specific suites already cover network provisioning and broad functional behavior. The testkit is designed to complement them. See the competitive and overlap review and release checklist.

Upstream contributions should begin with a maintainer-acknowledged issue and a narrowly scoped regression, following the target repository's contribution policy.

License

Licensed under either the MIT License or Apache License 2.0, at your option.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

wallet_privacy_testkit-0.1.0.tar.gz (38.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

wallet_privacy_testkit-0.1.0-py3-none-any.whl (18.8 kB view details)

Uploaded Python 3

File details

Details for the file wallet_privacy_testkit-0.1.0.tar.gz.

File metadata

  • Download URL: wallet_privacy_testkit-0.1.0.tar.gz
  • Upload date:
  • Size: 38.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for wallet_privacy_testkit-0.1.0.tar.gz
Algorithm Hash digest
SHA256 e79c0794548188c83f074a240137165f2e565ba32488df02060b111f91166ee6
MD5 52980e336287e23549f13bf12661aacc
BLAKE2b-256 46a42dda87423ece5620a71eb2fd90d2be9d9ab2193da715fd833009373e3fc0

See more details on using hashes here.

Provenance

The following attestation bundles were made for wallet_privacy_testkit-0.1.0.tar.gz:

Publisher: publish.yml on ztsalexey/wallet-privacy-testkit

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file wallet_privacy_testkit-0.1.0-py3-none-any.whl.

File metadata

File hashes

Hashes for wallet_privacy_testkit-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 ed684e3a9736c3a57859cc32b2600d3126efb99babe0e9b10d2c7263e04a0a14
MD5 6acefb6baa6146fad49baaad0393f0b2
BLAKE2b-256 f0981c7fcd75c78f76f0bc3ad580ec97180a41a285f9548c4a29a678c02cc16a

See more details on using hashes here.

Provenance

The following attestation bundles were made for wallet_privacy_testkit-0.1.0-py3-none-any.whl:

Publisher: publish.yml on ztsalexey/wallet-privacy-testkit

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

0.2.0

2 files

This release

0.1.0 This release

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page