-
Notifications
You must be signed in to change notification settings - Fork 2
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
UIP 4: Spend Backreferences #2
Merged
Merged
Changes from all commits
Commits
Show all changes
15 commits
Select commit
Hold shift + click to select a range
0d59332
Spend Backreference UIP
redshiftzero cc63cbc
add note on `EffectHash` backwards compatibility
redshiftzero 964a4df
Assign UIP 4
hdevalence e62b78c
Add to UIP table
hdevalence 210c9e7
Update uips/uip-4.md
redshiftzero 53ccec1
UIP 4: rename `backref_commitment` to `encrypted_backref`
redshiftzero 70991ec
UIP 4: Name the symmetric backreference key `brk`
redshiftzero 13f95fd
UIP 4: Do not use `optional` keyword for `encrypted_backref`
redshiftzero 462bcce
UIP 4: Add explicit call to `ChaCha20-Poly1305`
redshiftzero 7e1f4c3
UIP 4: Use truncated nullifier for 12-byte nonce
redshiftzero b3711f9
UIP 4: Add privacy considerations section
redshiftzero 11fe0f7
UIP 4: add some discussion of duplicate note commitments
redshiftzero 16e8492
UIP 4: Fix markdownlint
redshiftzero 8cd1a6d
UIP 4: Add backreference key section with further details
redshiftzero c3d5347
UIP 4: Use Blake2b-256 for deriving backreference key
redshiftzero 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
This file contains 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 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,132 @@ | ||
# UIP: Spend Backreferences | ||
|
||
| uip | 4 | | ||
| - | - | | ||
| title | Spend Backreferences | | ||
| description | Spend Backreferences enable improved sync performance | | ||
| author | Jennifer Helsby ([@redshiftzero](https://github.com/redshiftzero)), Henry de Valence ([@hdevalence](https://github.com/hdevalence)), Lúcás Meier ([@cronokirby](https://github.com/cronokirby))| | ||
| discussions-to | <https://forum.penumbra.zone/t/uip-spend-backreferences/110> | | ||
| status | Draft | | ||
| type | Standards Track | | ||
| consensus | Yes | | ||
| created | 2024-11-06 | | ||
|
||
## Abstract | ||
|
||
This specification introduces a method to improve Penumbra sync speeds by adding additional data that can be used by DAGSync clients. `Spend` actions will contain a new `encrypted_backref` field, allowing clients to traverse their transaction graph backwards and quickly recover their entire transaction history. | ||
|
||
## Motivation | ||
|
||
DAGSync is a graph-aware fast syncing algorithm. A client, upon detecting a single transaction involving them, can check that outputs visible to them are spent or not. If the output is unspent, then they have identified a live note they can potentially spend in the future, else if the output is unspent, they can continue the process forwards in the transaction graph, until they reach unspent notes. | ||
|
||
The design of Penumbra currently does not allow traversal backwards through the transaction graph, only forwards. A `Spend` intentionally does not reveal the note being spent, only the nullifier that is revealed. By including on the `Spend` an encrypted reference back to the note commitment being spent, such that only the note owner can view it, we enable DAGSync clients to efficienctly reconstruct the transaction history both backwards and forwards. | ||
|
||
## Specification | ||
|
||
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119 and RFC 8174. | ||
|
||
### Modification to `SpendBody` | ||
|
||
The `Spend` action will be augmented with an additional field `encrypted_backref` on the `SpendBody`: | ||
|
||
```protobuf | ||
message SpendBody { | ||
// A commitment to the value of the input note. | ||
penumbra.core.asset.v1.BalanceCommitment balance_commitment = 1; | ||
// The nullifier of the input note. | ||
penumbra.core.component.sct.v1.Nullifier nullifier = 6; | ||
// The randomized validating key for the spend authorization signature. | ||
penumbra.crypto.decaf377_rdsa.v1.SpendVerificationKey rk = 4; | ||
// NEW: An encryption of the commitment of the input note to the sender's OVK. | ||
bytes encrypted_backref = 7; | ||
} | ||
``` | ||
|
||
Clients MAY populate the `encrypted_backref` field with the encrypted note commitment corresponding to the note they are spending. | ||
|
||
Transaction parsing rules MUST ensure the length of the `encrypted_backref` bytes field on a `Spend` has either 48 or zero bytes in length. | ||
|
||
This allows for a phased adoption period such that clients have time to implement support for `Spend` backreferences. See the [Backwards Compatibility section](#backwards-compatibility) for further discussion. | ||
|
||
### Backreference Key | ||
|
||
We derive a new symmetric key, the _Backreference Key_ $brk$, from the `OutgoingViewingKey` $ovk$ using the BLAKE2b-256 hash function and personalization string `"Penumbra_Backref"`: | ||
|
||
```rust | ||
brk = BLAKE2b_256("Penumbra_Backref", ovk) | ||
``` | ||
|
||
One advantage of using a new key is that it has a single purpose with a new capability: it can be disclosed to show the transaction graph only and provides no other information. | ||
|
||
Another advantage of using a single key is that we can scan all spends without having to do key derivation each time. | ||
|
||
For incoming scanning, for each note, we currently do Diffie-Hellman (DH) key exchange between the Incoming Viewing Key and the ephemeral public key associated with the note. This allows us to derive the key that may have been used to encrypt the note. | ||
|
||
For outgoing scanning, for each note, we first attempt to decrypt the `OvkWrappedKey` using a key derived from the `OutgoingViewingKey` and the other public fields (value commitment, note commitment, and ephemeral public key). This approach allows us to identify if the action belongs to us prior to doing DH key exchange. The same benefit of avoiding a DH key exchange is also true of scanning with the Backreference Key. | ||
|
||
### Encryption of Spend Backreference | ||
|
||
The `encrypted_backref` should be encrypted using the Backreference key $brk$ and `ChaCha20-Poly1305`. [RFC-8349](https://datatracker.ietf.org/doc/rfc8439/) specifies that (key, nonce) pairs MUST NOT be reused. | ||
|
||
The first 12 bytes of the nullifier `nf` on the spend is used as the nonce $n$: | ||
|
||
```rust | ||
n = nf[:12] | ||
``` | ||
|
||
There is a single nullifier per spend/note, thus this nonce will not repeat, satisfying the requirement that no (key, nonce) pair be reused for encrypting different plaintexts. | ||
|
||
Encryption of the 32-byte note commitment $cm$ is performed using `ChaCha20-Poly1305` with the $(brk, n)$ tuple and outputs the 32-byte ciphertext $c$ and a 16-byte MAC: | ||
|
||
```rust | ||
(c, MAC) = ChaCha20_Poly1305(brk, n, cm) | ||
``` | ||
|
||
The transmitted data in the `encrypted_backref` field consists of a concatenation of the ciphertext $c$ and MAC. The `encrypted_backref` is thus 48 bytes (32 byte ciphertext + 16 byte MAC). | ||
|
||
### EffectHash | ||
|
||
Currently the `EffectHash` for the `Spend` action is computed as: | ||
|
||
`effect_hash = BLAKE2b-256(len(type_url) || type_url || proto_encode(proto))` | ||
|
||
where `type_url` is the bytes of a variable-length Type URL defining the proto message, `len(type_url)` is the length of the Type URL encoded as 8 bytes in little-endian order, and `proto` represents the proto used to represent the effecting data, and `proto_encode` represents encoding the proto message as a vector of bytes. | ||
|
||
#### `EffectHash` Backwards Compatibility | ||
|
||
The `EffectHash` computation is unchanged if the new `encrypted_backref` field is not populated. The `EffectHash` computation is a domain-separated hash of the Protobuf encoding of the `Spend` message. Protobuf encoding rules skip encoding default values. The new `encrypted_backref` field is a `bytes` field with a default value of an empty array, thus if it is not populated, it will be skipped, ensuring backwards compatibility. | ||
|
||
For spends that populate a 48-byte `encrypted_backref` field, the field will be included in the `EffectHash` per the existing `proto_encode` method as described above. | ||
|
||
### Transaction Perspectives and Views | ||
|
||
The `TransactionPerspective` and `TransactionView` will be unchanged. The backreference is treated as an internal sync optimization detail. | ||
|
||
## Rationale | ||
|
||
ZCash has considered a similar approach wherein backwards syncing is enabled using references encoded into the memo fields. Wallets can periodically construct transactions that stuff references to previous transaction hashes into the memo field of the dummy transaction. The advantage of the memo-stuffing approach is that DAGSync-aware clients can populate these fields without a change to the consensus rules. The disadvantage, however, is that the user's transaction history is polluted with dummy transactions, and a client must scan forward to find one of these dummy transactions before it can go backwards. | ||
|
||
### Non-Unique Note Commitments | ||
|
||
Note commitments correspond to the contents of a note, not to individual note instances. If two note instances have the same exact contents, they will share the same note commitment. This requires two notes to be generated with the same `Rseed`: for honest users the `Rseed` is generated randomly, but an honest user may nevertheless receive two notes constructed with the same `Rseed`. However, the Penumbra protocol allows this possibility of duplicate note commitments, so during syncing clients should allow the possibility of selecting a note commitment that appears in multiple transaction IDs. In the rare case that the `encrypted_backref` field refers to a note commitment that is a duplicate note commitment, the client should continue syncing using each transaction ID. | ||
|
||
## Backwards Compatibility | ||
|
||
There should be no compatibility issues since the `EffectHash` for a `Spend` will be unchanged if the `encrypted_backref` field is absent. Once all clients have added `encrypted_backref` support, a future UIP could make the field mandatory. | ||
|
||
## Security Considerations | ||
redshiftzero marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
This specification considered several security considerations: | ||
|
||
1. Encryption: The symmetric encryption scheme used for `encrypted_backref` uses a symmetric key derived from the OVK. Using a nonce derived from the nullifier field that is guaranteed to be unique for double-spend protection, we ensure that no duplicate (key, nonce) pairs can appear. | ||
2. Malleability prevention: Including `encrypted_backref` in the `EffectHash` transaction signing mechanism ensures that the field cannot be replaced by an adversary. If the field is malleable and the adversary knows the client is using DAGSync, an adversary may attempt to force clients to forget or lose funds. | ||
|
||
## Privacy Considerations | ||
|
||
Adding the `encrypted_backref` field introduces a potential distinguisher for client software based on the presence or absence of the field. The privacy leak is that the field signals whether a user has updated to a specific client version or higher, i.e. one that supports `encrypted_backref`. No other information is revealed. The privacy impact can be mitigated entirely by requiring `encrypted_backref` for all spend actions in a future protocol upgrade once there is broad client support. | ||
|
||
The design decision to include `encrypted_backref` reflects the fact that the information leakage is minor, and is justified to improve sync performance, reducing user friction and improving protocol adoption and thus the anonymity set of the network. | ||
|
||
## Copyright | ||
|
||
Copyright and related rights waived via [CC0](https://github.com/penumbra-zone/UIPs/blob/main/LICENSE). |
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.
I think there is a conceptual issue here that's worth discussing at some point in the UIP, namely, that note commitments uniquely identify note contents, but not note instances. Two identical notes can be created and added to the chain, and they will have the same note commitment, although they are different instances. (This is why nullifier derivation hashes the position of the note, and it's the same conceptual mismatch that creates the faerie gold attack).
In practice, note commitments are usually also commitments to note instances, because honest users should generate random
rseed
values, which causes the commitments to be unique. But this isn't guaranteed by the protocol, and we should pay attention to the implications.My suspicion is that the note commitment is still the right thing to put in the backreference, but it's worth thinking through the implications and writing that up in the UIP. What are the consequences of non-unique backreferences? Should the backreference also include the position, so that it is unique? (I believe the position is supplied as part of the
SpendPlan
data to be able to derive a nullifier, so I think this wouldn't introduce any new data/plumbing).For instance, is a tagging attack possible along the following lines:
After thinking about it a little (the time it took to write this comment) I don't think there's a real issue here, but probably good to cover in the writeup.
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.
In terms of the graph, you might already have multiple backreferences in a given transaction, so if one backreference points to two transactions, or you have two backreferences pointing to two transactions, the syncing algorithm works the same way, so I don't think there's really an issue there.
I think we just need to make sure to be aware of the non-uniqueness, and not do some kind of clobbering accidentally.
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.
For the transaction ID by note commitment, I think the RPC should ideally be returning all matching TxIds, instead of the most recent one such that no data is "lost" - i.e. the Penumbra protocol already allows there to be duplicate note commitments and we should keep that in mind. User B would continue to DAGSync with the list of relevant TxIds they got for the entire transaction, and from there can recover their transaction history, so I think this is fine. It's definitely worth flagging this possibility in the UIP so I'll add some notes to the Rationale.
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.
One implication is that an attacker could attempt to spam a target user with notes that have duplicate note commitments such that DAGSyncing takes slightly longer. Fees should mitigate that though.
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.
added e04f488