Repository navigation
Add the zakura-one-time-ivk and zakura-near-intents crates #120
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
Open
Changes from 12 commits
Commits
Show all changes
22 commits
Select commit
Hold shift + click to select a range
691d27c
Add the zakura-dynamic-ivk crate
czarcas7ic 68efa4e
Drop later-PR integration prose from the dynamic-ivk README
czarcas7ic 908e174
Treat a missing or unknown swap type as an unknown receipt
czarcas7ic 129fd30
Zeroize the derived rivk before copying it into the FVK
czarcas7ic e9edece
Stop exposing the unbound memo from a recovered note
czarcas7ic 6144c65
Drop the SQLite claim from the dynamic-ivk changelog
czarcas7ic b7b3f47
Reject publishable packages that depend on unpublishable ones
czarcas7ic f42480f
Make zakura-dynamic-ivk publishable
czarcas7ic c4fa00a
Keep exact-input refunds unknown without a refunded amount
czarcas7ic 7cba471
Merge main into the dynamic-ivk crate
czarcas7ic d67a472
Normalize failed swaps by refund direction
czarcas7ic 2808186
Expect no refund after an exact-output success reporting zero
czarcas7ic cfeafcc
Note that an incomplete deposit settles through REFUNDED
czarcas7ic c36ab7c
Avoid asserting an incomplete deposit always becomes REFUNDED
czarcas7ic f623926
Root the graph check at the hand-written members too
czarcas7ic 7ffd40e
Note that the rivk PRF state is not wiped
czarcas7ic 6b259b0
Say dynamic-key notes carry no memo the wallet reads
czarcas7ic 4d1f81e
Ignore refund memos that do not decode as v1 records
czarcas7ic 95c1559
Rename dynamic IVKs to one-time IVKs
czarcas7ic 032a42c
Move NEAR status normalization out of the one-time IVK crate
czarcas7ic f0f1b14
Move swap conventions into a `swap` module
czarcas7ic ceb231a
Add the `zakura-near-intents` provider adapter crate
czarcas7ic File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| # Changelog | ||
|
|
||
| ## [Unreleased] | ||
|
|
||
| - Add dynamic IVK derivation (`KeyId::derive`): each key keeps the | ||
| account's `ak` and `nk` with its own `rivk`, identified by purpose and index. | ||
| Add the `RefundMemo` funding record, which carries only the refund index, and | ||
| its `REFUND_MEMO_MAGIC` prefix, with independent KDF vectors and an Ironwood | ||
| receive/reconstruct/spend proof test. | ||
| - Add the shared completion limits, `RESTORE_WATCH_SECS` (24 hours for restored | ||
| keys) and `COMPLETION_LIMIT_SECS` (30 days), and direction-aware NEAR | ||
| status normalization, `lifecycle::near_observation`, including refunded amounts | ||
| and exact-output leftovers. Scheduling and receipt accounting are left to the | ||
| wallet's store. | ||
| - Add transport-independent incoming-note authentication and commitment-path | ||
| validation for privately discovered payments. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| [package] | ||
| name = "zakura-dynamic-ivk" | ||
| version = "0.0.1-rc0" | ||
| description = "Experimental dynamic IVKs: derived Ironwood receiving keys and refund memos" | ||
| edition.workspace = true | ||
| rust-version.workspace = true | ||
| license.workspace = true | ||
| repository.workspace = true | ||
| readme = "README.md" | ||
| categories.workspace = true | ||
| keywords = ["zcash", "wallet", "privacy"] | ||
|
|
||
| [dependencies] | ||
| blake2b_simd.workspace = true | ||
| ff.workspace = true | ||
| orchard.workspace = true | ||
| pasta_curves.workspace = true | ||
| zcash_protocol.workspace = true | ||
| zcash_note_encryption.workspace = true | ||
| zeroize.workspace = true | ||
|
|
||
| [dev-dependencies] | ||
| hex.workspace = true | ||
| incrementalmerkletree.workspace = true | ||
| orchard = { workspace = true, features = ["circuit"] } | ||
| rand = { workspace = true, features = ["thread_rng"] } | ||
| shardtree.workspace = true | ||
|
|
||
| [lints] | ||
| workspace = true |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,94 @@ | ||
| # Dynamic IVKs | ||
|
|
||
| Implementation of the draft v1 dynamic IVK and refund-memo | ||
| conventions. A dynamic key keeps the account's spending authority (`ak` and `nk`) | ||
| with its own `rivk`, so each has its own incoming viewing key and address, and a | ||
| wallet trial-decrypts with a changing set of them. Wallets use them for swap refund | ||
| and incoming addresses. The derivation still requires cryptographic review before | ||
| issuing live addresses. | ||
|
|
||
| ## Wallet integration | ||
|
|
||
| 1. Reserve a purpose-specific index durably before exposing an address. | ||
| 2. Call `KeyId::new(purpose, index).derive(account_external_fvk)` and select | ||
| `address_at(0u32, Scope::External)` for the receiver. | ||
| 3. Register that FVK for scanning and retain its purpose/index with received notes. | ||
| 4. For a refund, encode `RefundMemo` in an ordinary internal note in the funding | ||
| transaction, whose only transparent output pays the P2PKH or P2SH deposit. | ||
| Decode only after note authentication and verification that the transaction | ||
| was the wallet's own send, including zero-value and spent notes. | ||
| 5. Reconstruct inputs with their derived FVKs. Use the account's spending key to | ||
| sign, and its ordinary internal key for change. | ||
|
|
||
| `has_same_spending_authority` compares the `ak` and `nk` of valid FVKs. It does not | ||
| replace recipient, nullifier, signature, value, or change validation. The wallet | ||
| owns reservation, persistence, coverage, lifecycle, and note selection. There is | ||
| no alternate balance store in this crate. | ||
|
|
||
| Software PCZT signing of dynamic-key notes works through the same builder. | ||
| Hardware signers need firmware qualification before dynamic keys are enabled for | ||
| hardware accounts. | ||
|
|
||
| ## Derivation | ||
|
|
||
| A dynamic key keeps the account's `ak` and `nk` and replaces its external `rivk`: | ||
|
|
||
| ```text | ||
| rivk' = ToScalar(PRF^expand_rivk([0x85] || ak || nk || [purpose] || I2LEOSP_64(index))) | ||
| ``` | ||
|
|
||
| `PRF^expand` is BLAKE2b-512 personalized with `Zcash_ExpandSeed` over | ||
| `rivk || input`, and `ToScalar` reduces its little-endian output modulo the Pallas | ||
| scalar order. `purpose` is 0 for refund and 1 for incoming. This is ZIP 32's | ||
| internal-key derivation (first byte `0x83`, no suffix) with a different first byte | ||
| and a 9-byte suffix. The input fits one BLAKE2b block, as the internal key's does, | ||
| so a ZIP 2005 recovery circuit could accept dynamic keys as one more `rivk` case | ||
| with purpose and index as private inputs. Network and pool identify stored keys | ||
| but are not derivation inputs. | ||
|
|
||
| `0x85` was unused by the protocol specification and ZIPs when chosen. Reserve it | ||
| with the ZIP editors before issuing live addresses. Parsing the result validates | ||
| both external and internal incoming viewing keys; with negligible probability an | ||
| index has no valid key, and derivation returns an error. | ||
|
|
||
| ## Refund memo | ||
|
|
||
| | Offset | Bytes | Field | | ||
| |---|---:|---| | ||
| | 0 | 5 | `FF 5A 53 57 50` (`0xFF`, `ZSWP`) | | ||
| | 5 | 1 | Version `1` | | ||
| | 6 | 1 | Refund purpose `0` | | ||
| | 7 | 8 | Index, little-endian | | ||
| | 15 | 497 | Reserved: written as zero, ignored on decode | | ||
|
|
||
| The memo does not store the deposit address. A restored refund key needs none: it | ||
| is swept once and, if the provider was given its address in the last day, scans for | ||
| another 24 hours, with no provider lookups. Incoming indices are | ||
| recovered through lookahead, not this memo. `RefundMemo::decode` returns `None` | ||
| for any memo that is not a v1 record. A wallet that selects records by their | ||
| `REFUND_MEMO_MAGIC` prefix keeps one it cannot decode pending, without failing, | ||
| and refund issuance waits for it, since it may hold a refund index. | ||
|
|
||
| ## Validation | ||
|
|
||
| From the workspace root: | ||
|
|
||
| ```sh | ||
| python3 scripts/dev.py test --config default -p zakura-dynamic-ivk | ||
| ``` | ||
|
|
||
| Regenerate the KDF vectors from this directory with | ||
| `python3 tests/vectors/generate.py > tests/vectors/rivk.csv`. | ||
|
|
||
| ## Protocol baseline | ||
|
|
||
| Zcash Protocol Specification **v2026.7.0-202-gafa086, NU6.3 proposal**, commit | ||
| `afa086bd976e316612a5c06fb139429958d07d84`: | ||
|
|
||
| - [§4.2.3, Key Components, pp.40–41](https://github.com/zcash/zips/blob/afa086bd976e316612a5c06fb139429958d07d84/protocol/protocol.tex#L5753-L5909), | ||
| PDF anchor `orchardkeycomponents`. | ||
| - [§5.6.4.4, Raw Full Viewing Keys, p.123](https://github.com/zcash/zips/blob/afa086bd976e316612a5c06fb139429958d07d84/protocol/protocol.tex#L13077-L13104), | ||
| PDF anchor `orchardfullviewingkeyencoding`. | ||
|
|
||
| The dynamic IVK KDF and memo format are proposed wallet conventions, separate from | ||
| these protocol requirements. Passing these tests is not a cryptographic review. |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Adding this crate only to
layout.extra_membersleaves it outside the graph verifier's reachability roots:verify-zakura-graph.shseeds its queue solely frommanifest["crate"], and a repo-wide dependency search confirms no vendored crate reacheszakura-dynamic-ivk. Consequently, forbidden crates or duplicate Zakura versions introduced through this new dependency-family boundary would still pass the repository's advertised graph check; include applicable extra members as verification roots while continuing to exclude the dual-family facade.AGENTS.md reference: AGENTS.md:L43-L46
Useful? React with 👍 / 👎.