gorb_ct_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 ];
92
93 if !range_proof_commitments
94 .iter()
95 .zip(expected_commitments.iter())
96 .all(|(proof_commitment, expected_commitment)| proof_commitment == expected_commitment)
97 {
98 return Err(TokenProofExtractionError::PedersenCommitmentMismatch);
99 }
100
101 const REMAINING_BALANCE_BIT_LENGTH: u8 = 64;
103 const TRANSFER_AMOUNT_LO_BIT_LENGTH: u8 = 16;
104 const TRANSFER_AMOUNT_HI_BIT_LENGTH: u8 = 32;
105 const PADDING_BIT_LENGTH: u8 = 16;
106 let expected_bit_lengths = [
107 REMAINING_BALANCE_BIT_LENGTH,
108 TRANSFER_AMOUNT_LO_BIT_LENGTH,
109 TRANSFER_AMOUNT_HI_BIT_LENGTH,
110 PADDING_BIT_LENGTH,
111 ]
112 .iter();
113
114 if !range_proof_bit_lengths
115 .iter()
116 .zip(expected_bit_lengths)
117 .all(|(proof_len, expected_len)| proof_len == expected_len)
118 {
119 return Err(TokenProofExtractionError::RangeProofLengthMismatch);
120 }
121
122 let transfer_pubkeys = TransferPubkeys {
123 source: *source_pubkey_from_equality_proof,
124 destination: *destination_pubkey,
125 auditor: *auditor_pubkey,
126 };
127
128 let context_info = TransferProofContext {
129 ciphertext_lo: PodTransferAmountCiphertext(*transfer_amount_ciphertext_lo),
130 ciphertext_hi: PodTransferAmountCiphertext(*transfer_amount_ciphertext_hi),
131 transfer_pubkeys,
132 new_source_ciphertext: *new_source_ciphertext,
133 };
134
135 Ok(context_info)
136 }
137}