Skip to content

Add ZIP 327: Dandelion++ Transaction Propagation for Zcash P2P Nodes#1329

Open
y4ssi wants to merge 2 commits into
zcash:mainfrom
y4ssi:zip-0327-dandelion
Open

Add ZIP 327: Dandelion++ Transaction Propagation for Zcash P2P Nodes#1329
y4ssi wants to merge 2 commits into
zcash:mainfrom
y4ssi:zip-0327-dandelion

Conversation

@y4ssi

@y4ssi y4ssi commented Jul 9, 2026

Copy link
Copy Markdown

Summary

Proposes ZIP 327, a network-layer specification for Dandelion++ stem/fluff
transaction propagation in Zcash full nodes.

Problem: Zcash transactions are currently flood-broadcast immediately,
allowing passive network observers to correlate originating IP addresses with
transactions even for shielded txs whose content is cryptographically protected.

This ZIP specifies:

  • Per-epoch (10 min ± 2 min jitter) stem-peer selection
  • Unicast stem forwarding via standard inv (no new P2P message type required)
  • 30 s stem timeout before forced fluff promotion
  • Fail-closed routing: stem-peer unavailability triggers immediate fluff, never silent drop
  • Stem-phase mempool filtering: stem txids MUST NOT appear in mempool P2P responses or mempool RPCs

Scope: Full-node relay behavior (Component B). Wallet-side direct P2P submission
(Component A) is deferred to a companion ZIP 328 (in preparation).

Reference implementation: zcashfoundation/zebra#10928 — Component B.
E2e-verified on a 3-node regtest network 2026-07-09:
txid 87ffaf8f443e9b88aa5131bd3542bfc4abd6f8c2e8852d9482de0595048fc890 accepted,
30 s stem window confirmed, adversary observer saw no inv during stem phase.
Full test procedure and e2e evidence are in docs/dandelion-e2e-regtest.md in that PR.

Test plan

  • Review ZIP text for consistency with BCP 14 usage and ZIP formatting conventions
  • Confirm ZIP number 327 is not already reserved (next after 326)
  • Reference implementation builds and tests pass (cargo test -p zebra-network dandelion)

@y4ssi
y4ssi force-pushed the zip-0327-dandelion branch from 7980f48 to 969c1a7 Compare July 9, 2026 14:29
y4ssi pushed a commit to y4ssi/zebra-dpp that referenced this pull request Jul 9, 2026
…P 327)

Implements Dandelion++ stem/fluff transaction propagation for Zebra to
reduce IP-to-transaction correlation for network observers. Per the
draft ZIP 327 (zcash/zips#1329).

Core mechanism:
- Per-epoch stem-peer selection (10 min base + up to 2 min jitter),
  `zebra-network/src/dandelion/epoch.rs`.
- Per-transaction propagation state with a 30 s stem timeout,
  `zebra-network/src/dandelion/state.rs`.
- Fail-closed stem unicast: new `Request::AdvertiseTransactionIdsToPeer`
  variant routed by `PeerSet::route_to_peer()` to exactly the named stem
  peer; if that peer isn't ready it fails with `PeerError::NoReadyPeers`
  rather than falling back to a different peer (which would defeat the
  privacy property). The gossip task then promotes the tx to fluff.
- Gossip task (`zebrad/src/components/mempool/dandelion_gossip.rs`)
  wired into zebrad startup, replacing the immediate-flood gossip.
- `MempoolChangeKind::StemAdded`: locally-submitted txs (RPC Queue) are
  routed through stem; peer-relayed txs go directly to fluff.

Stem-phase hiding (a stem tx MUST NOT be observable before it fluffs):
- P2P `MempoolTransactionIds` and `TransactionsById` (getdata) handlers
  filter active-stem txids.
- `getrawmempool` and `getrawtransaction` RPCs filter them.
- The indexer gRPC `mempool_change` stream (Zaino/lightwalletd)
  suppresses `StemAdded` events until the tx fluffs.
- All exposure filters gate on the `Stem` state (not the elapsed
  timeout), so a timed-out-but-not-yet-swept tx stays hidden until the
  gossip task actually broadcasts it.

Robustness:
- `PropagationStateMap` entries are removed on Mined/Invalidated and on
  fluff promotion, bounding memory.
- The gossip task fails open on transient PeerSet readiness errors
  rather than terminating.

Tests: 10 unit tests (epoch, propagation state, Phase 4 filters) + 2
PeerSet routing tests. Verified end-to-end on a 3-node regtest network;
see docs/dandelion-e2e-regtest.md.

A companion wallet-side change (Component A: direct P2P submission) is
implemented in the Android and Swift SDKs
(zcash/zcash-android-wallet-sdk#2010, zcash/zcash-swift-wallet-sdk#1804).
Comment thread zips/zip-0327.md Outdated
@@ -0,0 +1,262 @@
ZIP: 327

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Zip number assigned is done by zip editors. Leave unassigned

Comment thread zips/draft-dandelion.md

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rename to draft-something.md

y4ssi pushed a commit to zodl-inc/zcash-android-wallet-sdk that referenced this pull request Jul 9, 2026
… 327/328)

Adds an opt-in transaction submission path that sends transactions
directly to a Zcash P2P full node via the native wire protocol instead
of routing them through lightwalletd/Zaino. This closes the
IP-to-transaction correlation gap at the submission boundary (a
compact-block server otherwise sees the wallet IP and the transaction
at the same instant).

Component A of the Dandelion++ direct-submission design (ZIP 327 /
ZIP 328). Default behaviour is unchanged: DandelionSubmitConfig.LightWalletD
remains the default, and chain synchronisation always uses the
lightwalletd endpoint regardless of this setting.

- ZcashP2PSubmitter (TransactionSubmitter): DNS seeder discovery, TCP
  connect (5s timeout), version/verack handshake, tx message without a
  prior inv (unadvertised-tx convention), disconnect. java.net stdlib
  only. Advertises protocol version 170_150 (Zebra's NU6.2 minimum).
- DandelionSubmitConfig: LightWalletD (default) or
  DirectP2P(network, fallbackToLightWalletD).
- FallbackTransactionSubmitter: P2P primary, lwd fallback on transport
  error; node rejections surface immediately.
- Wiring via a new dandelionSubmitConfig parameter (defaults preserve
  current behaviour).

Related: ZIP 327 (zcash/zips#1329), ZIP 328 (zcash/zips#1330),
ZcashFoundation/zebra#10928 (Component B).
y4ssi pushed a commit to zodl-inc/zcash-swift-wallet-sdk that referenced this pull request Jul 9, 2026
… 327/328)

Adds an opt-in transaction submission path that sends transactions
directly to a Zcash P2P full node via the native wire protocol instead
of routing them through lightwalletd/Zaino, closing the
IP-to-transaction correlation gap at the submission boundary.

Component A of the Dandelion++ direct-submission design (ZIP 327 /
ZIP 328). iOS equivalent of zcash/zcash-android-wallet-sdk#2010. Default
behaviour unchanged: .lightWalletD remains the default and chain sync
always uses the lightwalletd endpoint.

- ZcashP2PSubmitter (EndpointSubmitter): DNS seeder discovery, TCP
  connect, version/verack handshake, tx without a prior inv
  (unadvertised-tx convention), disconnect. Foundation + CommonCrypto
  only. Advertises protocol version 170_150 (Zebra's NU6.2 minimum).
- DandelionSubmitConfig enum: .lightWalletD / .directP2P; ZcashP2PNetwork.
- FallbackEndpointSubmitter: P2P primary, gRPC lwd fallback.
- Wiring via Dependencies.setup() and Initializer (new
  dandelionSubmitConfig parameter; defaults preserve current behaviour).

Related: ZIP 327 (zcash/zips#1329), ZIP 328 (zcash/zips#1330),
ZcashFoundation/zebra#10928 (Component B).
y4ssi pushed a commit to y4ssi/zips that referenced this pull request Jul 9, 2026
… Relay

Specifies the wallet-side path (Component A) for submitting transactions
directly to a Zcash full node over P2P instead of via a compact-block
server, using the Dandelion++ stem-phase forwarding defined in ZIP 327.
Closes the IP-to-transaction correlation gap at the submission boundary:
no lightwalletd/Zaino operator sees the wallet IP and the transaction
together.

Covers peer discovery, the version/verack handshake, the unadvertised-tx
submission hint, the no-concurrent-centralised-submission rule, and
fallback. Reference implementations: zcash/zcash-android-wallet-sdk#2010
and zcash/zcash-swift-wallet-sdk#1804.

Co-owned network/wallet ZIP; companion to ZIP 327 (zcash#1329).
Specifies how Zcash full nodes implement Dandelion++ stem/fluff
transaction propagation to reduce IP-to-transaction correlation for
network observers (Component B). Covers per-epoch stem-peer selection
(10 min + up to 2 min jitter), fail-closed unicast, 30 s stem timeout,
stem-phase hiding from MempoolTransactionIds/getdata/getrawmempool/
getrawtransaction and the indexer stream, and fluff-phase broadcast.
No new P2P message type; incremental deployment.

Reference implementation: ZcashFoundation/zebra#10928. Companion
wallet-side spec: ZIP 328 (zcash#1330), with SDK implementations
zcash/zcash-android-wallet-sdk#2010 and zcash/zcash-swift-wallet-sdk#1804.
@y4ssi
y4ssi force-pushed the zip-0327-dandelion branch from 969c1a7 to d31ce6d Compare July 9, 2026 14:54
… number to Unassigned, update ZIP 328 cross-references to PR links
y4ssi pushed a commit to y4ssi/zips that referenced this pull request Jul 14, 2026
…set ZIP number to Unassigned, update all ZIP 327 cross-references to PR zcash#1329 links
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