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
This commit is contained in:
parent
1970356887
commit
cccce80e65
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -1,3 +1,328 @@
|
||||||
---
|
---
|
||||||
title: Zero-Knowledge Proofs
|
title: Zero-Knowledge Proofs
|
||||||
---
|
---
|
||||||
|
|
||||||
|
Zero-knowledge proofs are tools that allow users to prove certain properties of
|
||||||
|
encrypted data. Most of the zero-knowledge proofs that are used in the
|
||||||
|
confidential extension are relatively small systems that are specifically
|
||||||
|
designed for the simple use-case of the confidential extension. Due to their
|
||||||
|
simplicity, none of the zero-knowledge systems that are used in the program
|
||||||
|
require any trusted setup or sophisticated circuit design.
|
||||||
|
|
||||||
|
The zero-knowledge proofs that are used in the confidential extension can be
|
||||||
|
divided into two categories: _sigma protocols_ and _bulletproofs_. Sigma
|
||||||
|
protocols are simple systems that are tailor designed for the confidential
|
||||||
|
extension use-cases. Bulletproofs is an existing range proof system that was
|
||||||
|
developed in the specified [paper](https://eprint.iacr.org/2017/1066).
|
||||||
|
|
||||||
|
## Transfer Instruction Data
|
||||||
|
|
||||||
|
The confidential extension `Transfer` instruction data requires a number of
|
||||||
|
cryptographic components. Here, we provide intuition for each of these
|
||||||
|
components by building the transfer data in a series of steps.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
struct TransferData {
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### ElGamal Public Keys
|
||||||
|
|
||||||
|
A transfer instruction has three associated ElGamal public keys: sender,
|
||||||
|
receiver, and auditor. A transfer instruction data must include these three
|
||||||
|
encryption public keys.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
struct TransferPubkeys {
|
||||||
|
source_pubkey: ElGamalPubkey,
|
||||||
|
destination_pubkey: ElGamal Pubkey,
|
||||||
|
auditor_pubkey: ElGamalPubkey,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct TransferData {
|
||||||
|
transfer_pubkeys: TransferPubkeys,
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
If there is no associated auditor associated with the mint, then the auditor
|
||||||
|
pubkey is simply 32 zero bytes.
|
||||||
|
|
||||||
|
### Low and High-bit Encryption
|
||||||
|
|
||||||
|
Transfer instruction data must include the transfer amount that is encrypted
|
||||||
|
under the three ElGamal public keys associated with the instruction. To cope
|
||||||
|
with ElGamal decryption as discussed in the previous section, the transfer
|
||||||
|
amount is restricted to 48-bit numbers and is encrypted as two separate
|
||||||
|
numbers: `amount_lo` that represents the low 16-bits and `amount_hi` that
|
||||||
|
represents the high 32-bits.
|
||||||
|
|
||||||
|
Each `amount_lo` and `amount_hi` is encrypted under the three ElGamal public
|
||||||
|
keys associated with a transfer. Instead of including three independent
|
||||||
|
ciphertexts as part of the transfer data, we use the randomness-reuse property
|
||||||
|
of ElGamal encryption to minimize the size of ciphertexts.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
/// Ciphertext structure of the transfer amount encrypted under three ElGamal
|
||||||
|
/// public keys
|
||||||
|
struct TransferAmountEncryption {
|
||||||
|
commitment: PedersenCommitment,
|
||||||
|
source_handle: DecryptHandle,
|
||||||
|
destination_handle: DecryptionHandle,
|
||||||
|
auditor_handle: DecryptHandle,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct TransferData {
|
||||||
|
ciphertext_lo: TransferAmountEncryption,
|
||||||
|
ciphertext_hi: TransferAmountEncryption,
|
||||||
|
transfer_pubkeys: TransferPubkeys,
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
In addition to these ciphertexts, transfer data must include proofs that these
|
||||||
|
ciphertexts are generated properly. There are two ways that a user can
|
||||||
|
potentially cheat the program. First a user may provide ciphertexts that are
|
||||||
|
malformed. For example, even if a user may encrypt the transfer amount under a
|
||||||
|
wrong public key, there is no way for the program to check the validity of a
|
||||||
|
ciphertext. Therefore, we require that transfer data require a _ciphertext
|
||||||
|
validity_ proof that certifies that the ciphertexts are properly generated.
|
||||||
|
|
||||||
|
Ciphertext validity proof only guarantees that a twisted ElGamal ciphertext is
|
||||||
|
properly generated. However, it does not certify any property regarding the
|
||||||
|
encrypted amount in a ciphertext. For example, a malicious user can encrypt
|
||||||
|
negative values, but there is no way for the program to detect this by simply
|
||||||
|
inspecting the ciphertext. Therefore, in addition to a ciphertext validity
|
||||||
|
proof, a transfer instruction must include a _range proof_ that certifies that
|
||||||
|
the encrypted amounts `amount_lo` and `amount_hi` are positive 16 and 32-bit
|
||||||
|
values respectively.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
struct TransferProof {
|
||||||
|
validity_proof: ValidityProof,
|
||||||
|
range_proof: RangeProof,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct TransferData {
|
||||||
|
ciphertext_lo: TransferAmountEncryption,
|
||||||
|
ciphertext_hi: TransferAmountEncryption,
|
||||||
|
transfer_pubkeys: TransferPubkeys,
|
||||||
|
proof: TransferProof,
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Verifying Net-Balance
|
||||||
|
|
||||||
|
Finally, in addition to proving that the transfer amount is properly encrypted,
|
||||||
|
a user must include a proof that the source account has enough balance to
|
||||||
|
make the transfer. The canonical way to do this is for the user to generate a
|
||||||
|
range proof that certifies that the ciphertext
|
||||||
|
`source_available_balance - (ciphertext_lo + 2^16 * ciphertext_hi)`, which holds
|
||||||
|
the available balance of the source account subtracted by the transfer amount,
|
||||||
|
encrypts a positive 64-bit value. Since Bulletproofs supports proof
|
||||||
|
aggregation, this additional range proof can be aggregated into the original
|
||||||
|
range proof on the transfer amount.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
struct TransferProof {
|
||||||
|
validity_proof: ValidityProof,
|
||||||
|
range_proof: RangeProof, // certifies ciphertext amount and net-balance
|
||||||
|
}
|
||||||
|
|
||||||
|
struct TransferData {
|
||||||
|
ciphertext_lo: TransferAmountEncryption,
|
||||||
|
ciphertext_hi: TransferAmountEncryption,
|
||||||
|
transfer_pubkeys: TransferPubkeys,
|
||||||
|
proof: TransferProof,
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
One technical problem with the above is that although the sender of a transfer
|
||||||
|
knows an ElGamal decryption key for the ciphertext `source_available_balance`,
|
||||||
|
it does not necessarily know a Pedersen opening for the ciphertext, which is
|
||||||
|
needed to generate the range proofs on the ciphertext
|
||||||
|
`source_available_balance - (ciphertext_lo + 2^16 * ciphertext_hi)`. Therefore,
|
||||||
|
in a transfer instruction, we require that the sender decrypt the ciphertext
|
||||||
|
`source_available_balance - (ciphertext_lo + 2^16 * ciphertext_hi)` on the
|
||||||
|
client side and include a new Pedersen commitment on the new source balance
|
||||||
|
`new_source_commitment` along with an _equality proof_ that certifies that the
|
||||||
|
ciphertext `source_available_balance - (ciphertext_lo + 2^16 * ciphertext_hi)`
|
||||||
|
and `new_source_commitment` encrypt the same message.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
struct TransferProof {
|
||||||
|
new_source_commitment: PedersenCommitment,
|
||||||
|
equality_proof: CtxtCommEqualityProof,
|
||||||
|
validity_proof: ValidityProof,
|
||||||
|
range_proof: RangeProof,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct TransferData {
|
||||||
|
ciphertext_lo: TransferAmountEncryption,
|
||||||
|
ciphertext_hi: TransferAmountEncryption,
|
||||||
|
transfer_pubkeys: TransferPubkeys,
|
||||||
|
proof: TransferProof,
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Transfer With Fee Instruction Data
|
||||||
|
|
||||||
|
The confidential extension can be enabled for mints that are extended for fees.
|
||||||
|
If a mint is extended for fees, then any confidential transfer of the
|
||||||
|
corresponding tokens must use the confidential extension `TransferWithFee`
|
||||||
|
instruction. In addition to the data that are required for the `Transfer`
|
||||||
|
instruction, the `TransferWithFee` instruction requires additional cryptographic
|
||||||
|
components associated with fees.
|
||||||
|
|
||||||
|
### Background on Transfer Fees
|
||||||
|
|
||||||
|
If a mint is extended for fees, then transfers of tokens that pertains to the
|
||||||
|
mint requires a transfer fee that is calculated as a percentage of the transfer
|
||||||
|
amount. Specifically, a transaction fee is determined by two paramters:
|
||||||
|
|
||||||
|
- `bp`: The base point representing the fee rate. It is a positive integer that
|
||||||
|
represents a percentage rate that is two points to the right of the decimal
|
||||||
|
place.
|
||||||
|
|
||||||
|
For example, `bp = 1` represents the fee rate of 0.01%, `bp = 100` represents
|
||||||
|
the fee rate of 1%, and `bp = 10000` represents the fee rate of 100%.
|
||||||
|
|
||||||
|
- `max_fee`: the max fee rate. A transfer fee is calculated using the fee rate
|
||||||
|
that is determined by `bp`, but it is capped by `max_fee`.
|
||||||
|
|
||||||
|
For example, consider a transfer amount of 200 tokens.
|
||||||
|
- For fee parameter `bp = 100` and `max_fee = 3`, the fee is simply 1% of the
|
||||||
|
transfer amount, which is 2.
|
||||||
|
- For fee parameter `bp = 200` and `max_fee = 3`, the fee is 3 since 2% of 200
|
||||||
|
is 4, which is greater than the max fee of 3.
|
||||||
|
|
||||||
|
The transfer fee is always rounded up to the nearest positive integer. For
|
||||||
|
example, if a transfer amount is `100` and the fee parameter is `bp = 110` and
|
||||||
|
`max_fee = 3`, then the fee is `2`, which is rounded up from 1.1% of the
|
||||||
|
transfer amount.
|
||||||
|
|
||||||
|
The fee parameters can be specified in mints that are extended for fees. In
|
||||||
|
addition to the fee parameters, mints that are extended for fees contain the
|
||||||
|
`withdraw_withheld_authority` field, which specifies the public key of an
|
||||||
|
authority that can collect fees that are withheld from transfer amounts.
|
||||||
|
|
||||||
|
A Token account that is extended for fees has an associated field
|
||||||
|
`withheld_amount`. Any transfer fee that is deducted from a transfer amount is
|
||||||
|
aggregated into the `withheld_amount` field of the destination account of the
|
||||||
|
transfer. The `withheld_amount` can be collected by the withdraw-withheld
|
||||||
|
authority into a specific account using the
|
||||||
|
`TransferFeeInstructions::WithdrawWithheldTokensFromAccounts` or into the mint
|
||||||
|
account using the `TransferFeeInstructions::HarvestWithheldTokensToMint`. The
|
||||||
|
withheld fees that accumulate in a mint can be collected into an account using
|
||||||
|
the `TransferFeeInstructions::WithdrawWithheldTokensFromMint`.
|
||||||
|
|
||||||
|
### Fee Encryption
|
||||||
|
|
||||||
|
The actual amount of a transfer fee cannot be included in the confidential
|
||||||
|
extension `TransferWithFee` instruction in the clear since the transfer amount
|
||||||
|
can be inferred from the fee. Therefore, in the confidential extension, the
|
||||||
|
transfer fee is encrypted under the destination and withheld authority ElGamal
|
||||||
|
public key.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
struct FeeEncryption {
|
||||||
|
commitment: PedersenCommitment,
|
||||||
|
destination_handle: DecryptHandle,
|
||||||
|
withdraw_withheld_authority_handle: DecryptHandle,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct TransferWithFeeData {
|
||||||
|
... // `TransferData` components
|
||||||
|
fee_ciphertext: FeeEncryption,
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Upon receiving a `TransferWithFee` instruction, the Token program deducts the
|
||||||
|
encrypted fee under the destination ElGamal public key from the encrypted
|
||||||
|
transfer amount under the same public key. Then it aggregates the ciphertext
|
||||||
|
that encrypts the fee under the withdraw withheld authority's ElGamal public key
|
||||||
|
into the `withheld_fee` component of the destination account.
|
||||||
|
|
||||||
|
### Verifying the Fee Ciphertext
|
||||||
|
|
||||||
|
The remaining pieces of the `TransferWithFee` instruction data are fields that
|
||||||
|
are required to verify the validity of the encrypted fee. Since the fee is
|
||||||
|
encrypted, the Token program cannot check that the fee was computed correctly by
|
||||||
|
simply inspecting the ciphertext. A `TransferWithFee` must include three
|
||||||
|
additional proofs to certify that the fee ciphertext is valid.
|
||||||
|
|
||||||
|
- _ciphertext validity proof_: This proof component certifies that the actual
|
||||||
|
fee ciphertext is properly generated under the correct destination and
|
||||||
|
withdraw withheld authority ElGamal public key.
|
||||||
|
- _fee sigma proof_: In combination with range proof component, the fee sigma
|
||||||
|
proof certifies that the fee that is encrypted in `fee_ciphertext` is properly
|
||||||
|
calculated according to the fee parameter.
|
||||||
|
- _range proof_: In combination with the fee sigma proof components, the range
|
||||||
|
proof component certifies that the encrypted fee in `fee_ciphertext` is
|
||||||
|
properly calculated according to the fee parameter.
|
||||||
|
|
||||||
|
We refer to the proof specifications below for the additional details.
|
||||||
|
|
||||||
|
## Sigma Protocols
|
||||||
|
|
||||||
|
### Validity Proof
|
||||||
|
|
||||||
|
A validity proof certifies that a twisted ElGamal ciphertext is a well-formed
|
||||||
|
ciphertext. The precise description of the system is specified in the following
|
||||||
|
notes.
|
||||||
|
|
||||||
|
[[Notes]](./validity_proof.pdf)
|
||||||
|
|
||||||
|
Validity proofs is required for the `Withdraw`, `Transfer`, and
|
||||||
|
`TransferWithFee` instructions. These instructions require the client to include
|
||||||
|
twisted ElGamal ciphertexts as part of the instruction data. Validity proofs
|
||||||
|
that are attached with these instructions certify that these ElGamal ciphertexts
|
||||||
|
are well-formed.
|
||||||
|
|
||||||
|
### Zero-balance Proof
|
||||||
|
|
||||||
|
A zero-balance proof certifies that a twisted ElGamal ciphertext encrypts the
|
||||||
|
number zero. The precise description of the system is specified in the following
|
||||||
|
notes.
|
||||||
|
|
||||||
|
[[Notes]](./zero_proof.pdf).
|
||||||
|
|
||||||
|
Zero-balance proofs are required for the `EmptyAccount` instruction, which
|
||||||
|
prepares a token account for closing. An account may only be closed if the
|
||||||
|
balance in an account is zero. Since the balance is encrypted in the
|
||||||
|
confidential extension, the Token program cannot directly check that the
|
||||||
|
encrypted balance in an account is zero by inspecting the account state.
|
||||||
|
Instead, the program verifies the zero-balance proof that is attached in the
|
||||||
|
`EmptyAccount` instruction to check that the balance is indeed zero.
|
||||||
|
|
||||||
|
### Equality Proof
|
||||||
|
|
||||||
|
The confidential extension makes use of two kinds of equality proof. The first
|
||||||
|
variant _ciphertext-commitment_ equality proof certifies that a twisted ElGamal
|
||||||
|
ciphertext and a Pedersen commitment encode the same message. The second variant
|
||||||
|
_ciphertext-ciphertext_ equality proof certifies that two twisted ElGamal
|
||||||
|
ciphertexts encrypt the same message. The precise description of the system is
|
||||||
|
specified in the following notes.
|
||||||
|
|
||||||
|
[[Notes]](./equality_proof.pdf).
|
||||||
|
|
||||||
|
Ciphertext-commitment equality proofs are required for the `Transfer` and
|
||||||
|
`TransferWithFee` instructions. Ciphertext-ciphertext equaltiy proofs are
|
||||||
|
required for the `WithdrawWithheldTokensFromMint` and
|
||||||
|
`WithdrawWithheldTokensFromAccounts` instructions.
|
||||||
|
|
||||||
|
### Fee Sigma Proof
|
||||||
|
|
||||||
|
The fee sigma proof certifies that a committed transfer fee is computed
|
||||||
|
correctly. The precise description of the system is specified in the following
|
||||||
|
notes.
|
||||||
|
|
||||||
|
[Notes]
|
||||||
|
|
||||||
|
The fee sigma proof is required for the `TransferWithFee` instruction.
|
||||||
|
|
||||||
|
## Range Proofs
|
||||||
|
|
||||||
|
The confidential extension uses Bulletproofs for range proofs. We refer to the
|
||||||
|
[academic paper](https://eprint.iacr.org/2017/1066) and the
|
||||||
|
[dalek](https://doc-internal.dalek.rs/bulletproofs/notes/index.html)
|
||||||
|
implementation for the details.
|
||||||
|
|
Loading…
Reference in New Issue