spl_token_confidential_transfer_proof_extraction/
burn.rs1use {
2 crate::{encryption::PodBurnAmountCiphertext, 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 BurnPubkeys {
14 pub source: PodElGamalPubkey,
15 pub supply: PodElGamalPubkey,
16 pub auditor: PodElGamalPubkey,
17}
18
19pub struct BurnProofContext {
22 pub burn_amount_ciphertext_lo: PodBurnAmountCiphertext,
23 pub burn_amount_ciphertext_hi: PodBurnAmountCiphertext,
24 pub burn_pubkeys: BurnPubkeys,
25 pub remaining_balance_ciphertext: PodElGamalCiphertext,
26}
27
28impl BurnProofContext {
29 pub fn verify_and_extract(
30 equality_proof_context: &CiphertextCommitmentEqualityProofContext,
31 ciphertext_validity_proof_context: &BatchedGroupedCiphertext3HandlesValidityProofContext,
32 range_proof_context: &BatchedRangeProofContext,
33 ) -> Result<Self, TokenProofExtractionError> {
34 let CiphertextCommitmentEqualityProofContext {
41 pubkey: source_elgamal_pubkey_from_equality_proof,
42 ciphertext: remaining_balance_ciphertext,
43 commitment: remaining_balance_commitment,
44 } = equality_proof_context;
45
46 let BatchedGroupedCiphertext3HandlesValidityProofContext {
53 first_pubkey: source_elgamal_pubkey_from_validity_proof,
54 second_pubkey: supply_elgamal_pubkey,
55 third_pubkey: auditor_elgamal_pubkey,
56 grouped_ciphertext_lo: burn_amount_ciphertext_lo,
57 grouped_ciphertext_hi: burn_amount_ciphertext_hi,
58 } = ciphertext_validity_proof_context;
59
60 let BatchedRangeProofContext {
67 commitments: range_proof_commitments,
68 bit_lengths: range_proof_bit_lengths,
69 } = range_proof_context;
70
71 if source_elgamal_pubkey_from_equality_proof != source_elgamal_pubkey_from_validity_proof {
74 return Err(TokenProofExtractionError::ElGamalPubkeyMismatch);
75 }
76
77 let burn_amount_commitment_lo = burn_amount_ciphertext_lo.extract_commitment();
80 let burn_amount_commitment_hi = burn_amount_ciphertext_hi.extract_commitment();
81
82 let expected_commitments = [
83 *remaining_balance_commitment,
84 burn_amount_commitment_lo,
85 burn_amount_commitment_hi,
86 ];
88
89 if !range_proof_commitments
93 .iter()
94 .zip(expected_commitments.iter())
95 .all(|(proof_commitment, expected_commitment)| proof_commitment == expected_commitment)
96 {
97 return Err(TokenProofExtractionError::PedersenCommitmentMismatch);
98 }
99
100 const REMAINING_BALANCE_BIT_LENGTH: u8 = 64;
102 const BURN_AMOUNT_LO_BIT_LENGTH: u8 = 16;
103 const BURN_AMOUNT_HI_BIT_LENGTH: u8 = 32;
104 const PADDING_BIT_LENGTH: u8 = 16;
105 let expected_bit_lengths = [
106 REMAINING_BALANCE_BIT_LENGTH,
107 BURN_AMOUNT_LO_BIT_LENGTH,
108 BURN_AMOUNT_HI_BIT_LENGTH,
109 PADDING_BIT_LENGTH,
110 ]
111 .iter();
112
113 if !range_proof_bit_lengths
117 .iter()
118 .zip(expected_bit_lengths)
119 .all(|(proof_len, expected_len)| proof_len == expected_len)
120 {
121 return Err(TokenProofExtractionError::RangeProofLengthMismatch);
122 }
123
124 let burn_pubkeys = BurnPubkeys {
125 source: *source_elgamal_pubkey_from_equality_proof,
126 supply: *supply_elgamal_pubkey,
127 auditor: *auditor_elgamal_pubkey,
128 };
129
130 Ok(BurnProofContext {
131 burn_amount_ciphertext_lo: PodBurnAmountCiphertext(*burn_amount_ciphertext_lo),
132 burn_amount_ciphertext_hi: PodBurnAmountCiphertext(*burn_amount_ciphertext_hi),
133 burn_pubkeys,
134 remaining_balance_ciphertext: *remaining_balance_ciphertext,
135 })
136 }
137}