spl_token_confidential_transfer_proof_extraction/
transfer.rs1use {
2 crate::{encryption::PodTransferAmountCiphertext, errors::TokenProofExtractionError},
3 solana_zk_sdk::{
4 encryption::pod::elgamal::{PodElGamalCiphertext, PodElGamalPubkey},
5 zk_elgamal_proof_program::proof_data::{
6 BatchedGroupedCiphertext3HandlesValidityProofContext, BatchedRangeProofContext,
7 CiphertextCommitmentEqualityProofContext,
8 },
9 },
10};
11
12pub struct TransferPubkeys {
14 pub source: PodElGamalPubkey,
16 pub destination: PodElGamalPubkey,
18 pub auditor: PodElGamalPubkey,
20}
21
22pub struct TransferProofContext {
24 pub ciphertext_lo: PodTransferAmountCiphertext,
26 pub ciphertext_hi: PodTransferAmountCiphertext,
28 pub transfer_pubkeys: TransferPubkeys,
30 pub new_source_ciphertext: PodElGamalCiphertext,
32}
33
34impl TransferProofContext {
35 pub fn verify_and_extract(
36 equality_proof_context: &CiphertextCommitmentEqualityProofContext,
37 ciphertext_validity_proof_context: &BatchedGroupedCiphertext3HandlesValidityProofContext,
38 range_proof_context: &BatchedRangeProofContext,
39 ) -> Result<Self, TokenProofExtractionError> {
40 let CiphertextCommitmentEqualityProofContext {
46 pubkey: source_pubkey_from_equality_proof,
47 ciphertext: new_source_ciphertext,
48 commitment: new_source_commitment,
49 } = equality_proof_context;
50
51 let BatchedGroupedCiphertext3HandlesValidityProofContext {
58 first_pubkey: source_pubkey_from_validity_proof,
59 second_pubkey: destination_pubkey,
60 third_pubkey: auditor_pubkey,
61 grouped_ciphertext_lo: transfer_amount_ciphertext_lo,
62 grouped_ciphertext_hi: transfer_amount_ciphertext_hi,
63 } = ciphertext_validity_proof_context;
64
65 let BatchedRangeProofContext {
72 commitments: range_proof_commitments,
73 bit_lengths: range_proof_bit_lengths,
74 } = range_proof_context;
75
76 if source_pubkey_from_equality_proof != source_pubkey_from_validity_proof {
79 return Err(TokenProofExtractionError::ElGamalPubkeyMismatch);
80 }
81
82 let transfer_amount_commitment_lo = transfer_amount_ciphertext_lo.extract_commitment();
85 let transfer_amount_commitment_hi = transfer_amount_ciphertext_hi.extract_commitment();
86
87 let expected_commitments = [
88 *new_source_commitment,
89 transfer_amount_commitment_lo,
90 transfer_amount_commitment_hi,
91 ];
93
94 if !range_proof_commitments
98 .iter()
99 .zip(expected_commitments.iter())
100 .all(|(proof_commitment, expected_commitment)| proof_commitment == expected_commitment)
101 {
102 return Err(TokenProofExtractionError::PedersenCommitmentMismatch);
103 }
104
105 const REMAINING_BALANCE_BIT_LENGTH: u8 = 64;
107 const TRANSFER_AMOUNT_LO_BIT_LENGTH: u8 = 16;
108 const TRANSFER_AMOUNT_HI_BIT_LENGTH: u8 = 32;
109 const PADDING_BIT_LENGTH: u8 = 16;
110 let expected_bit_lengths = [
111 REMAINING_BALANCE_BIT_LENGTH,
112 TRANSFER_AMOUNT_LO_BIT_LENGTH,
113 TRANSFER_AMOUNT_HI_BIT_LENGTH,
114 PADDING_BIT_LENGTH,
115 ]
116 .iter();
117
118 if !range_proof_bit_lengths
122 .iter()
123 .zip(expected_bit_lengths)
124 .all(|(proof_len, expected_len)| proof_len == expected_len)
125 {
126 return Err(TokenProofExtractionError::RangeProofLengthMismatch);
127 }
128
129 let transfer_pubkeys = TransferPubkeys {
130 source: *source_pubkey_from_equality_proof,
131 destination: *destination_pubkey,
132 auditor: *auditor_pubkey,
133 };
134
135 let context_info = TransferProofContext {
136 ciphertext_lo: PodTransferAmountCiphertext(*transfer_amount_ciphertext_lo),
137 ciphertext_hi: PodTransferAmountCiphertext(*transfer_amount_ciphertext_hi),
138 transfer_pubkeys,
139 new_source_ciphertext: *new_source_ciphertext,
140 };
141
142 Ok(context_info)
143 }
144}