Skip to content

Commit

Permalink
Merge pull request #2 from penumbra-zone/uip-spend-backreference
Browse files Browse the repository at this point in the history
UIP 4: Spend Backreferences
  • Loading branch information
redshiftzero authored Nov 16, 2024
2 parents a1f2239 + c3d5347 commit e0ec642
Show file tree
Hide file tree
Showing 2 changed files with 133 additions and 0 deletions.
1 change: 1 addition & 0 deletions uips/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
- [UIP-1](./uip-1.md)
- [UIP-2](./uip-2.md)
- [UIP-3](./uip-3.md)
- [UIP-4](./uip-4.md)
- [UIP-6](./uip-6.md)

# UIP template
Expand Down
132 changes: 132 additions & 0 deletions uips/uip-4.md
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

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).

0 comments on commit e0ec642

Please sign in to comment.