1+ //! Generates the zero-knowledge proofs required for a confidential transfer with a fee.
2+ //!
3+ //! A confidential transfer with a fee is more complex than a simple transfer. It requires five
4+ //! distinct zero-knowledge proofs to ensure the validity of the transfer, the solvency of the
5+ //! sender, and the correctness of the fee amount according to the on-chain mint configuration.
6+ //!
7+ //! ## Protocol Flow and Proof Components
8+ //!
9+ //! 1. **Fee Calculation**: The client first calculates the required fee based on the transfer
10+ //! amount and the on-chain fee parameters (rate and maximum cap).
11+ //!
12+ //! 2. **Encrypt Amounts**: The gross transfer amount and the fee amount are each split into low
13+ //! and high bit components. These components are then encrypted into separate grouped (twisted)
14+ //! ElGamal ciphertexts with the appropriate decryption handles for the involved parties (source,
15+ //! destination, auditor, and withdraw authority).
16+ //!
17+ //! 3. **Generate Proofs**: The sender generates five proofs that work in concert:
18+ //!
19+ //! - **Transfer Amount Ciphertext Validity Proof
20+ //! (`BatchedGroupedCiphertext3HandlesValidityProofData`)**: Certifies that the grouped
21+ //! ElGamal ciphertext for the gross transfer amount is well-formed.
22+ //!
23+ //! - **Fee Ciphertext Validity Proof
24+ //! (`BatchedGroupedCiphertext2HandlesValidityProofData`)**: Certifies that the grouped
25+ //! ElGamal ciphertext for the transfer fee is well-formed.
26+ //!
27+ //! - **Fee Calculation Proof (`PercentageWithCapProofData`)**:
28+ //! It's a "one-of-two" proof that certifies **either**:
29+ //! 1. The `fee_amount` is exactly equal to the on-chain `maximum_fee`.
30+ //! 2. The `fee_amount` was correctly calculated as a percentage of the
31+ //! `transfer_amount`, according to the on-chain `fee_rate_basis_points`.
32+ //!
33+ //! **Note**: The proof certifies that the transfer fee is a valid percentage of the
34+ //! transfer amount or that the fee is exactly the maximum fee. While the sender is
35+ //! expected to choose the lower of these two options, the proof does not enforce this
36+ //! choice.
37+ //!
38+ //! - **Range Proof (`BatchedRangeProofU256Data`)**:
39+ //! This expanded range proof ensures the solvency of the entire transaction by certifying
40+ //! that all critical monetary values are non-negative. This includes the sender's remaining
41+ //! balance, the gross transfer amount, the fee amount, and the net transfer amount that the
42+ //! destination receives.
43+ //!
44+ //! - **Ciphertext-Commitment Equality Proof (`CiphertextCommitmentEqualityProofData`)**:
45+ //! Identical in purpose to the simple transfer, this proof links the sender's remaining
46+ //! balance (as a homomorphically computed ElGamal ciphertext) to a new Pedersen commitment.
47+ //! This commitment is then used in the Range Proof to prove the sender's solvency.
48+
149#[ cfg( not( target_arch = "wasm32" ) ) ]
250use solana_zk_sdk:: encryption:: grouped_elgamal:: GroupedElGamal ;
351#[ cfg( target_arch = "wasm32" ) ]
@@ -40,11 +88,6 @@ const NET_TRANSFER_AMOUNT_BIT_LENGTH: usize = 64;
4088
4189/// The proof data required for a confidential transfer instruction when the
4290/// mint is extended for fees
43- ///
44- /// NOTE: The proofs certify that the transfer fee is a valid percentage of the
45- /// transfer amount, as determined by the fee basis points, or that the fee is
46- /// exactly the maximum fee. While the sender is expected to choose the lower of
47- /// these two options, the proof does not enforce this choice.
4891pub struct TransferWithFeeProofData {
4992 pub equality_proof_data : CiphertextCommitmentEqualityProofData ,
5093 pub transfer_amount_ciphertext_validity_proof_data_with_ciphertext :
@@ -193,12 +236,16 @@ pub fn transfer_with_fee_split_proof_data(
193236 // calculate fee
194237 let transfer_fee_basis_points = fee_rate_basis_points;
195238 let transfer_fee_maximum_fee = maximum_fee;
196- let ( raw_fee_amount, delta_fee ) = calculate_fee ( transfer_amount, transfer_fee_basis_points)
239+ let ( raw_fee_amount, raw_delta_fee ) = calculate_fee ( transfer_amount, transfer_fee_basis_points)
197240 . ok_or ( TokenProofGenerationError :: FeeCalculation ) ?;
198241
199242 // if raw fee is greater than the maximum fee, then use the maximum fee for the
200- // fee amount
201- let fee_amount = std:: cmp:: min ( transfer_fee_maximum_fee, raw_fee_amount) ;
243+ // fee amount and set the claimed delta fee to be 0 for simplicity
244+ let ( fee_amount, claimed_delta_fee) = if transfer_fee_maximum_fee < raw_fee_amount {
245+ ( transfer_fee_maximum_fee, 0 )
246+ } else {
247+ ( raw_fee_amount, raw_delta_fee)
248+ } ;
202249 let net_transfer_amount = transfer_amount
203250 . checked_sub ( fee_amount)
204251 . ok_or ( TokenProofGenerationError :: FeeCalculation ) ?;
@@ -249,7 +296,7 @@ pub fn transfer_with_fee_split_proof_data(
249296 let net_transfer_amount_opening = & combined_transfer_amount_opening - & combined_fee_opening;
250297
251298 // compute claimed and real delta commitment
252- let ( claimed_commitment, claimed_opening) = Pedersen :: new ( delta_fee ) ;
299+ let ( claimed_commitment, claimed_opening) = Pedersen :: new ( claimed_delta_fee ) ;
253300 let ( delta_commitment, delta_opening) = compute_delta_commitment_and_opening (
254301 (
255302 & combined_transfer_amount_commitment,
@@ -266,7 +313,7 @@ pub fn transfer_with_fee_split_proof_data(
266313 fee_amount,
267314 & delta_commitment,
268315 & delta_opening,
269- delta_fee ,
316+ claimed_delta_fee ,
270317 & claimed_commitment,
271318 & claimed_opening,
272319 transfer_fee_maximum_fee,
@@ -327,7 +374,7 @@ pub fn transfer_with_fee_split_proof_data(
327374
328375 // generate range proof data
329376 let delta_fee_complement = MAX_FEE_BASIS_POINTS_SUB_ONE
330- . checked_sub ( delta_fee )
377+ . checked_sub ( claimed_delta_fee )
331378 . ok_or ( TokenProofGenerationError :: FeeCalculation ) ?;
332379
333380 let max_fee_basis_points_sub_one_commitment =
@@ -353,7 +400,7 @@ pub fn transfer_with_fee_split_proof_data(
353400 new_decrypted_available_balance,
354401 transfer_amount_lo,
355402 transfer_amount_hi,
356- delta_fee ,
403+ claimed_delta_fee ,
357404 delta_fee_complement,
358405 fee_amount_lo,
359406 fee_amount_hi,
0 commit comments