Skip to content
This repository was archived by the owner on Mar 11, 2025. It is now read-only.

Commit cccce80

Browse files
Confidential transfer zk doc (#3437)
* docs: transfer and sigma proof discussions * docs: add sigma proof notes * docs: add transfer with fee notes * docs: minor wording
1 parent 1970356 commit cccce80

File tree

4 files changed

+325
-0
lines changed

4 files changed

+325
-0
lines changed
Binary file not shown.
Binary file not shown.
Binary file not shown.
Lines changed: 325 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,328 @@
11
---
22
title: Zero-Knowledge Proofs
33
---
4+
5+
Zero-knowledge proofs are tools that allow users to prove certain properties of
6+
encrypted data. Most of the zero-knowledge proofs that are used in the
7+
confidential extension are relatively small systems that are specifically
8+
designed for the simple use-case of the confidential extension. Due to their
9+
simplicity, none of the zero-knowledge systems that are used in the program
10+
require any trusted setup or sophisticated circuit design.
11+
12+
The zero-knowledge proofs that are used in the confidential extension can be
13+
divided into two categories: _sigma protocols_ and _bulletproofs_. Sigma
14+
protocols are simple systems that are tailor designed for the confidential
15+
extension use-cases. Bulletproofs is an existing range proof system that was
16+
developed in the specified [paper](https://eprint.iacr.org/2017/1066).
17+
18+
## Transfer Instruction Data
19+
20+
The confidential extension `Transfer` instruction data requires a number of
21+
cryptographic components. Here, we provide intuition for each of these
22+
components by building the transfer data in a series of steps.
23+
24+
```rust
25+
struct TransferData {
26+
...
27+
}
28+
```
29+
30+
### ElGamal Public Keys
31+
32+
A transfer instruction has three associated ElGamal public keys: sender,
33+
receiver, and auditor. A transfer instruction data must include these three
34+
encryption public keys.
35+
36+
```rust
37+
struct TransferPubkeys {
38+
source_pubkey: ElGamalPubkey,
39+
destination_pubkey: ElGamal Pubkey,
40+
auditor_pubkey: ElGamalPubkey,
41+
}
42+
43+
struct TransferData {
44+
transfer_pubkeys: TransferPubkeys,
45+
}
46+
```
47+
48+
If there is no associated auditor associated with the mint, then the auditor
49+
pubkey is simply 32 zero bytes.
50+
51+
### Low and High-bit Encryption
52+
53+
Transfer instruction data must include the transfer amount that is encrypted
54+
under the three ElGamal public keys associated with the instruction. To cope
55+
with ElGamal decryption as discussed in the previous section, the transfer
56+
amount is restricted to 48-bit numbers and is encrypted as two separate
57+
numbers: `amount_lo` that represents the low 16-bits and `amount_hi` that
58+
represents the high 32-bits.
59+
60+
Each `amount_lo` and `amount_hi` is encrypted under the three ElGamal public
61+
keys associated with a transfer. Instead of including three independent
62+
ciphertexts as part of the transfer data, we use the randomness-reuse property
63+
of ElGamal encryption to minimize the size of ciphertexts.
64+
65+
```rust
66+
/// Ciphertext structure of the transfer amount encrypted under three ElGamal
67+
/// public keys
68+
struct TransferAmountEncryption {
69+
commitment: PedersenCommitment,
70+
source_handle: DecryptHandle,
71+
destination_handle: DecryptionHandle,
72+
auditor_handle: DecryptHandle,
73+
}
74+
75+
struct TransferData {
76+
ciphertext_lo: TransferAmountEncryption,
77+
ciphertext_hi: TransferAmountEncryption,
78+
transfer_pubkeys: TransferPubkeys,
79+
}
80+
```
81+
82+
In addition to these ciphertexts, transfer data must include proofs that these
83+
ciphertexts are generated properly. There are two ways that a user can
84+
potentially cheat the program. First a user may provide ciphertexts that are
85+
malformed. For example, even if a user may encrypt the transfer amount under a
86+
wrong public key, there is no way for the program to check the validity of a
87+
ciphertext. Therefore, we require that transfer data require a _ciphertext
88+
validity_ proof that certifies that the ciphertexts are properly generated.
89+
90+
Ciphertext validity proof only guarantees that a twisted ElGamal ciphertext is
91+
properly generated. However, it does not certify any property regarding the
92+
encrypted amount in a ciphertext. For example, a malicious user can encrypt
93+
negative values, but there is no way for the program to detect this by simply
94+
inspecting the ciphertext. Therefore, in addition to a ciphertext validity
95+
proof, a transfer instruction must include a _range proof_ that certifies that
96+
the encrypted amounts `amount_lo` and `amount_hi` are positive 16 and 32-bit
97+
values respectively.
98+
99+
```rust
100+
struct TransferProof {
101+
validity_proof: ValidityProof,
102+
range_proof: RangeProof,
103+
}
104+
105+
struct TransferData {
106+
ciphertext_lo: TransferAmountEncryption,
107+
ciphertext_hi: TransferAmountEncryption,
108+
transfer_pubkeys: TransferPubkeys,
109+
proof: TransferProof,
110+
}
111+
```
112+
113+
### Verifying Net-Balance
114+
115+
Finally, in addition to proving that the transfer amount is properly encrypted,
116+
a user must include a proof that the source account has enough balance to
117+
make the transfer. The canonical way to do this is for the user to generate a
118+
range proof that certifies that the ciphertext
119+
`source_available_balance - (ciphertext_lo + 2^16 * ciphertext_hi)`, which holds
120+
the available balance of the source account subtracted by the transfer amount,
121+
encrypts a positive 64-bit value. Since Bulletproofs supports proof
122+
aggregation, this additional range proof can be aggregated into the original
123+
range proof on the transfer amount.
124+
125+
```rust
126+
struct TransferProof {
127+
validity_proof: ValidityProof,
128+
range_proof: RangeProof, // certifies ciphertext amount and net-balance
129+
}
130+
131+
struct TransferData {
132+
ciphertext_lo: TransferAmountEncryption,
133+
ciphertext_hi: TransferAmountEncryption,
134+
transfer_pubkeys: TransferPubkeys,
135+
proof: TransferProof,
136+
}
137+
```
138+
139+
One technical problem with the above is that although the sender of a transfer
140+
knows an ElGamal decryption key for the ciphertext `source_available_balance`,
141+
it does not necessarily know a Pedersen opening for the ciphertext, which is
142+
needed to generate the range proofs on the ciphertext
143+
`source_available_balance - (ciphertext_lo + 2^16 * ciphertext_hi)`. Therefore,
144+
in a transfer instruction, we require that the sender decrypt the ciphertext
145+
`source_available_balance - (ciphertext_lo + 2^16 * ciphertext_hi)` on the
146+
client side and include a new Pedersen commitment on the new source balance
147+
`new_source_commitment` along with an _equality proof_ that certifies that the
148+
ciphertext `source_available_balance - (ciphertext_lo + 2^16 * ciphertext_hi)`
149+
and `new_source_commitment` encrypt the same message.
150+
151+
```rust
152+
struct TransferProof {
153+
new_source_commitment: PedersenCommitment,
154+
equality_proof: CtxtCommEqualityProof,
155+
validity_proof: ValidityProof,
156+
range_proof: RangeProof,
157+
}
158+
159+
struct TransferData {
160+
ciphertext_lo: TransferAmountEncryption,
161+
ciphertext_hi: TransferAmountEncryption,
162+
transfer_pubkeys: TransferPubkeys,
163+
proof: TransferProof,
164+
}
165+
```
166+
167+
## Transfer With Fee Instruction Data
168+
169+
The confidential extension can be enabled for mints that are extended for fees.
170+
If a mint is extended for fees, then any confidential transfer of the
171+
corresponding tokens must use the confidential extension `TransferWithFee`
172+
instruction. In addition to the data that are required for the `Transfer`
173+
instruction, the `TransferWithFee` instruction requires additional cryptographic
174+
components associated with fees.
175+
176+
### Background on Transfer Fees
177+
178+
If a mint is extended for fees, then transfers of tokens that pertains to the
179+
mint requires a transfer fee that is calculated as a percentage of the transfer
180+
amount. Specifically, a transaction fee is determined by two paramters:
181+
182+
- `bp`: The base point representing the fee rate. It is a positive integer that
183+
represents a percentage rate that is two points to the right of the decimal
184+
place.
185+
186+
For example, `bp = 1` represents the fee rate of 0.01%, `bp = 100` represents
187+
the fee rate of 1%, and `bp = 10000` represents the fee rate of 100%.
188+
189+
- `max_fee`: the max fee rate. A transfer fee is calculated using the fee rate
190+
that is determined by `bp`, but it is capped by `max_fee`.
191+
192+
For example, consider a transfer amount of 200 tokens.
193+
- For fee parameter `bp = 100` and `max_fee = 3`, the fee is simply 1% of the
194+
transfer amount, which is 2.
195+
- For fee parameter `bp = 200` and `max_fee = 3`, the fee is 3 since 2% of 200
196+
is 4, which is greater than the max fee of 3.
197+
198+
The transfer fee is always rounded up to the nearest positive integer. For
199+
example, if a transfer amount is `100` and the fee parameter is `bp = 110` and
200+
`max_fee = 3`, then the fee is `2`, which is rounded up from 1.1% of the
201+
transfer amount.
202+
203+
The fee parameters can be specified in mints that are extended for fees. In
204+
addition to the fee parameters, mints that are extended for fees contain the
205+
`withdraw_withheld_authority` field, which specifies the public key of an
206+
authority that can collect fees that are withheld from transfer amounts.
207+
208+
A Token account that is extended for fees has an associated field
209+
`withheld_amount`. Any transfer fee that is deducted from a transfer amount is
210+
aggregated into the `withheld_amount` field of the destination account of the
211+
transfer. The `withheld_amount` can be collected by the withdraw-withheld
212+
authority into a specific account using the
213+
`TransferFeeInstructions::WithdrawWithheldTokensFromAccounts` or into the mint
214+
account using the `TransferFeeInstructions::HarvestWithheldTokensToMint`. The
215+
withheld fees that accumulate in a mint can be collected into an account using
216+
the `TransferFeeInstructions::WithdrawWithheldTokensFromMint`.
217+
218+
### Fee Encryption
219+
220+
The actual amount of a transfer fee cannot be included in the confidential
221+
extension `TransferWithFee` instruction in the clear since the transfer amount
222+
can be inferred from the fee. Therefore, in the confidential extension, the
223+
transfer fee is encrypted under the destination and withheld authority ElGamal
224+
public key.
225+
226+
```rust
227+
struct FeeEncryption {
228+
commitment: PedersenCommitment,
229+
destination_handle: DecryptHandle,
230+
withdraw_withheld_authority_handle: DecryptHandle,
231+
}
232+
233+
struct TransferWithFeeData {
234+
... // `TransferData` components
235+
fee_ciphertext: FeeEncryption,
236+
}
237+
```
238+
239+
Upon receiving a `TransferWithFee` instruction, the Token program deducts the
240+
encrypted fee under the destination ElGamal public key from the encrypted
241+
transfer amount under the same public key. Then it aggregates the ciphertext
242+
that encrypts the fee under the withdraw withheld authority's ElGamal public key
243+
into the `withheld_fee` component of the destination account.
244+
245+
### Verifying the Fee Ciphertext
246+
247+
The remaining pieces of the `TransferWithFee` instruction data are fields that
248+
are required to verify the validity of the encrypted fee. Since the fee is
249+
encrypted, the Token program cannot check that the fee was computed correctly by
250+
simply inspecting the ciphertext. A `TransferWithFee` must include three
251+
additional proofs to certify that the fee ciphertext is valid.
252+
253+
- _ciphertext validity proof_: This proof component certifies that the actual
254+
fee ciphertext is properly generated under the correct destination and
255+
withdraw withheld authority ElGamal public key.
256+
- _fee sigma proof_: In combination with range proof component, the fee sigma
257+
proof certifies that the fee that is encrypted in `fee_ciphertext` is properly
258+
calculated according to the fee parameter.
259+
- _range proof_: In combination with the fee sigma proof components, the range
260+
proof component certifies that the encrypted fee in `fee_ciphertext` is
261+
properly calculated according to the fee parameter.
262+
263+
We refer to the proof specifications below for the additional details.
264+
265+
## Sigma Protocols
266+
267+
### Validity Proof
268+
269+
A validity proof certifies that a twisted ElGamal ciphertext is a well-formed
270+
ciphertext. The precise description of the system is specified in the following
271+
notes.
272+
273+
[[Notes]](./validity_proof.pdf)
274+
275+
Validity proofs is required for the `Withdraw`, `Transfer`, and
276+
`TransferWithFee` instructions. These instructions require the client to include
277+
twisted ElGamal ciphertexts as part of the instruction data. Validity proofs
278+
that are attached with these instructions certify that these ElGamal ciphertexts
279+
are well-formed.
280+
281+
### Zero-balance Proof
282+
283+
A zero-balance proof certifies that a twisted ElGamal ciphertext encrypts the
284+
number zero. The precise description of the system is specified in the following
285+
notes.
286+
287+
[[Notes]](./zero_proof.pdf).
288+
289+
Zero-balance proofs are required for the `EmptyAccount` instruction, which
290+
prepares a token account for closing. An account may only be closed if the
291+
balance in an account is zero. Since the balance is encrypted in the
292+
confidential extension, the Token program cannot directly check that the
293+
encrypted balance in an account is zero by inspecting the account state.
294+
Instead, the program verifies the zero-balance proof that is attached in the
295+
`EmptyAccount` instruction to check that the balance is indeed zero.
296+
297+
### Equality Proof
298+
299+
The confidential extension makes use of two kinds of equality proof. The first
300+
variant _ciphertext-commitment_ equality proof certifies that a twisted ElGamal
301+
ciphertext and a Pedersen commitment encode the same message. The second variant
302+
_ciphertext-ciphertext_ equality proof certifies that two twisted ElGamal
303+
ciphertexts encrypt the same message. The precise description of the system is
304+
specified in the following notes.
305+
306+
[[Notes]](./equality_proof.pdf).
307+
308+
Ciphertext-commitment equality proofs are required for the `Transfer` and
309+
`TransferWithFee` instructions. Ciphertext-ciphertext equaltiy proofs are
310+
required for the `WithdrawWithheldTokensFromMint` and
311+
`WithdrawWithheldTokensFromAccounts` instructions.
312+
313+
### Fee Sigma Proof
314+
315+
The fee sigma proof certifies that a committed transfer fee is computed
316+
correctly. The precise description of the system is specified in the following
317+
notes.
318+
319+
[Notes]
320+
321+
The fee sigma proof is required for the `TransferWithFee` instruction.
322+
323+
## Range Proofs
324+
325+
The confidential extension uses Bulletproofs for range proofs. We refer to the
326+
[academic paper](https://eprint.iacr.org/2017/1066) and the
327+
[dalek](https://doc-internal.dalek.rs/bulletproofs/notes/index.html)
328+
implementation for the details.

0 commit comments

Comments
 (0)