zcash_client_sqlite: Backport PIR spent-note tracking to 0.19.x#2268
zcash_client_sqlite: Backport PIR spent-note tracking to 0.19.x#2268p0mvn wants to merge 5 commits into
Conversation
Nullifier PIR (Private Information Retrieval) lets the wallet discover Orchard note spendability by querying an external PIR server for nullifier inclusion, rather than waiting for sequential shard-tree scanning to complete. This significantly reduces the time before notes become spendable. The implementation is gated behind the `sync-nullifier-pir` feature flag and comprises: - A `pir_spent_notes` table (migration created unconditionally to keep the DAG identical across all builds; empty when the feature is off). - A `wallet::pir` module with queries for unspent notes eligible for PIR checking, pending-spend tracking, and idempotent insert logic. - Feature-gated changes to `get_wallet_summary` and note selection that skip the unscanned-range spendability gate for Orchard notes when PIR is enabled. - Unconditional `pir_spent_notes` cleanup in `truncate_to_height` to prevent stale exclusions from persisting after reorgs. Made-with: Cursor
|
If you want to do it this way, close #2267 and then we'll forward-port this instead; the maintenance branch will get merged back to |
Thanks for the recommendation. Closed #2267. I am addressing the comment about tests
On it. Will post an update |
Done. Verdict: non-breaking, patch release appropriate Summary
|
The PIR tests were using a hand-crafted SQL schema (PIR_TEST_SCHEMA_SQL) that could drift from the actual migration-created tables. Notably, the test schema still used the old `tx` column name while the real schema uses `transaction_id` (renamed in the account_delete_cascade migration). Replace the standalone schema with WalletMigrator::init_or_migrate() so the test database always matches production. The tests insert synthetic prerequisite rows (one account, one transaction) into the fully migrated schema, then exercise the PIR queries against it. Also fixes create_pir_test_db_on_disk (used by the SDK crate for concurrent-access tests) to use the same migration-based setup. Made-with: Cursor
| /// Leaf migrations in the 0.19.6 release. | ||
| pub const V_0_19_6: &[Uuid] = &[pir_spent_notes::MIGRATION_ID]; | ||
|
|
There was a problem hiding this comment.
Note to reviewer: I observed that v0.19.5 has already been pushed to crates.io, but the tag has not been pushed here.
As an outcome, I made an intuitive guess that this should be v0.19.6.
Please let me know if this was mischosen.
|
The test comment has been addressed: Semver check has been done. Would appreciate a review and/or a recommendation on next steps. Thanks |
The upstream PR (zcash/librustzcash#2268) lives on p0mvn's fork. Update all [patch.crates-io] entries from valargroup/librustzcash to p0mvn/librustzcash at 301e20e (includes migration-based test setup). Made-with: Cursor
rustfmt wants `crate::WalletDb` before the nested path `crate::wallet::init::WalletMigrator`. Made-with: Cursor
Clippy requires a Default implementation when a public `new()` method takes no arguments. This was causing CI failures with `clippy::new_without_default`. Made-with: Cursor
Add spend-client, spend-types from sync-nullifier-pir (ab81f98). Enable the sync-nullifier-pir feature on zcash_client_sqlite. All [patch.crates-io] entries point to valargroup/librustzcash at 9861871 (sync-pir-0.19.x branch), which carries PIR spent-note tracking on top of the 0.19.x release line. Related upstream PR: zcash/librustzcash#2268
Add spend-client, spend-types from sync-nullifier-pir (ab81f98). Enable the sync-nullifier-pir feature on zcash_client_sqlite. All [patch.crates-io] entries point to valargroup/librustzcash at 9861871 (sync-pir-0.19.x branch), which carries PIR spent-note tracking on top of the 0.19.x release line. Related upstream PR: zcash/librustzcash#2268
Add spend-client, spend-types from sync-nullifier-pir (ab81f98). Enable the sync-nullifier-pir feature on zcash_client_sqlite. All [patch.crates-io] entries point to valargroup/librustzcash at 9861871 (sync-pir-0.19.x branch), which carries PIR spent-note tracking on top of the 0.19.x release line. Related upstream PR: zcash/librustzcash#2268
Backport of #2267 onto the
maint/zcash_client_sqlite-0.19.xrelease line.The change is SemVer-compatible and applies cleanly to the 0.19.x maintenance branch with no conflicts. The
sync-pir-0.19.xbranch is based directly onmaint/zcash_client_sqlite-0.19.x(zero delta before the PIR commit).Summary
Adds support for nullifier PIR (Private Information Retrieval) spent-note tracking to
zcash_client_sqlite, gated behind async-nullifier-pirfeature flag.Nullifier PIR lets the wallet discover Orchard note spendability by querying an external PIR server for nullifier inclusion, rather than waiting for sequential shard-tree scanning to complete. This significantly reduces the time before notes become spendable.
What's included
pir_spent_notesmigration: Created unconditionally (not feature-gated) to keep the migration DAG identical across all builds. When the feature is off, the table exists but is empty and unused.wallet::pirmodule (sync-nullifier-pirfeature): Queries for unspent notes eligible for PIR checking, pending-spend tracking, and idempotent spent-note insertion.sync-nullifier-pirfeature):get_wallet_summaryand note selection skip the unscanned-range spendability gate for Orchard notes when PIR is enabled.truncate_to_heightunconditionally clearspir_spent_notesto prevent stale PIR exclusions after reorgs.Design decisions
MIGRATION_IDgraph. This avoids divergent database states.spent_notes_clauseare unconditional (notx_unexpired_condition) because they reflect confirmed on-chain nullifier state, not pending transactions.testingsubmodule with a minimal standalone schema for unit tests, avoiding the need to spin up a full wallet database.Motivation for backport
The downstream zcash-swift-wallet-sdk depends on
zcash_client_sqlite = "0.19". Having the PIR feature available on the 0.19.x release line unblocks integration in the Swift SDK and iOS wallet without requiring a major version bump across the entire dependency chain.Dependencies
Demo
Detect Spend During Sync
https://www.youtube.com/watch?v=TMFQUpdcnLo
Once the spend is detected, the balance decreases by the full note amount. The decreased balance remains spendable — enabled by the sync nullifier PIR — until the wallet scans the spend and discovers the output change note.
Comparison to No PIR via Debug Settings
https://www.youtube.com/watch?v=JOKyPA1pqwI
Without PIR, the full balance appears available even though it is partially spent, which would lead to a broadcast failure. Triggering a PIR check via the debug menu shows the balance dropping to the correct spendable amount.
Related PRs
librustzcash(0.19.x backport): zcash_client_sqlite: Backport PIR spent-note tracking to 0.19.x #2268zcash-swift-wallet-sdk: zcash-swift-wallet-sdk: Nullifier PIR integration zcash-swift-wallet-sdk#1674Made with Cursor