1use {
2 crate::{
3 encryption::MintAmountCiphertext, errors::TokenProofGenerationError,
4 try_combine_lo_hi_ciphertexts, try_split_u64, CiphertextValidityProofWithAuditorCiphertext,
5 },
6 solana_zk_sdk::{
7 encryption::{
8 elgamal::{ElGamalCiphertext, ElGamalKeypair, ElGamalPubkey},
9 pedersen::Pedersen,
10 },
11 zk_elgamal_proof_program::proof_data::{
12 BatchedGroupedCiphertext3HandlesValidityProofData, BatchedRangeProofU128Data,
13 CiphertextCommitmentEqualityProofData, ZkProofData,
14 },
15 },
16};
17
18const NEW_SUPPLY_BIT_LENGTH: usize = 64;
19const MINT_AMOUNT_LO_BIT_LENGTH: usize = 16;
20const MINT_AMOUNT_HI_BIT_LENGTH: usize = 32;
21const RANGE_PROOF_PADDING_BIT_LENGTH: usize = 16;
23
24pub struct MintProofData {
26 pub equality_proof_data: CiphertextCommitmentEqualityProofData,
27 pub ciphertext_validity_proof_data_with_ciphertext:
28 CiphertextValidityProofWithAuditorCiphertext,
29 pub range_proof_data: BatchedRangeProofU128Data,
30}
31
32pub fn mint_split_proof_data(
33 current_supply_ciphertext: &ElGamalCiphertext,
34 mint_amount: u64,
35 current_supply: u64,
36 supply_elgamal_keypair: &ElGamalKeypair,
37 destination_elgamal_pubkey: &ElGamalPubkey,
38 auditor_elgamal_pubkey: Option<&ElGamalPubkey>,
39) -> Result<MintProofData, TokenProofGenerationError> {
40 let default_auditor_pubkey = ElGamalPubkey::default();
41 let auditor_elgamal_pubkey = auditor_elgamal_pubkey.unwrap_or(&default_auditor_pubkey);
42
43 let (mint_amount_lo, mint_amount_hi) = try_split_u64(mint_amount, MINT_AMOUNT_LO_BIT_LENGTH)
45 .ok_or(TokenProofGenerationError::IllegalAmountBitLength)?;
46
47 let (mint_amount_grouped_ciphertext_lo, mint_amount_opening_lo) = MintAmountCiphertext::new(
50 mint_amount_lo,
51 destination_elgamal_pubkey,
52 auditor_elgamal_pubkey,
53 supply_elgamal_keypair.pubkey(),
54 );
55
56 let (mint_amount_grouped_ciphertext_hi, mint_amount_opening_hi) = MintAmountCiphertext::new(
57 mint_amount_hi,
58 destination_elgamal_pubkey,
59 auditor_elgamal_pubkey,
60 supply_elgamal_keypair.pubkey(),
61 );
62
63 let mint_amount_ciphertext_supply_lo = mint_amount_grouped_ciphertext_lo
65 .0
66 .to_elgamal_ciphertext(2)
67 .unwrap();
68 let mint_amount_ciphertext_supply_hi = mint_amount_grouped_ciphertext_hi
69 .0
70 .to_elgamal_ciphertext(2)
71 .unwrap();
72
73 #[allow(clippy::arithmetic_side_effects)]
74 let new_supply_ciphertext = current_supply_ciphertext
75 + try_combine_lo_hi_ciphertexts(
76 &mint_amount_ciphertext_supply_lo,
77 &mint_amount_ciphertext_supply_hi,
78 MINT_AMOUNT_LO_BIT_LENGTH,
79 )
80 .ok_or(TokenProofGenerationError::IllegalAmountBitLength)?;
81
82 let new_supply = current_supply
84 .checked_add(mint_amount)
85 .ok_or(TokenProofGenerationError::IllegalAmountBitLength)?;
86
87 let (new_supply_commitment, new_supply_opening) = Pedersen::new(new_supply);
88
89 let equality_proof_data = CiphertextCommitmentEqualityProofData::new(
91 supply_elgamal_keypair,
92 &new_supply_ciphertext,
93 &new_supply_commitment,
94 &new_supply_opening,
95 new_supply,
96 )
97 .map_err(TokenProofGenerationError::from)?;
98
99 let ciphertext_validity_proof_data = BatchedGroupedCiphertext3HandlesValidityProofData::new(
101 destination_elgamal_pubkey,
102 auditor_elgamal_pubkey,
103 supply_elgamal_keypair.pubkey(),
104 &mint_amount_grouped_ciphertext_lo.0,
105 &mint_amount_grouped_ciphertext_hi.0,
106 mint_amount_lo,
107 mint_amount_hi,
108 &mint_amount_opening_lo,
109 &mint_amount_opening_hi,
110 )
111 .map_err(TokenProofGenerationError::from)?;
112
113 let mint_amount_auditor_ciphertext_lo = ciphertext_validity_proof_data
114 .context_data()
115 .grouped_ciphertext_lo
116 .try_extract_ciphertext(2)
117 .map_err(|_| TokenProofGenerationError::CiphertextExtraction)?;
118
119 let mint_amount_auditor_ciphertext_hi = ciphertext_validity_proof_data
120 .context_data()
121 .grouped_ciphertext_hi
122 .try_extract_ciphertext(2)
123 .map_err(|_| TokenProofGenerationError::CiphertextExtraction)?;
124
125 let ciphertext_validity_proof_data_with_ciphertext =
126 CiphertextValidityProofWithAuditorCiphertext {
127 proof_data: ciphertext_validity_proof_data,
128 ciphertext_lo: mint_amount_auditor_ciphertext_lo,
129 ciphertext_hi: mint_amount_auditor_ciphertext_hi,
130 };
131
132 let (padding_commitment, padding_opening) = Pedersen::new(0_u64);
134 let range_proof_data = BatchedRangeProofU128Data::new(
135 vec![
136 &new_supply_commitment,
137 mint_amount_grouped_ciphertext_lo.get_commitment(),
138 mint_amount_grouped_ciphertext_hi.get_commitment(),
139 &padding_commitment,
140 ],
141 vec![new_supply, mint_amount_lo, mint_amount_hi, 0],
142 vec![
143 NEW_SUPPLY_BIT_LENGTH,
144 MINT_AMOUNT_LO_BIT_LENGTH,
145 MINT_AMOUNT_HI_BIT_LENGTH,
146 RANGE_PROOF_PADDING_BIT_LENGTH,
147 ],
148 vec![
149 &new_supply_opening,
150 &mint_amount_opening_lo,
151 &mint_amount_opening_hi,
152 &padding_opening,
153 ],
154 )
155 .map_err(TokenProofGenerationError::from)?;
156
157 Ok(MintProofData {
158 equality_proof_data,
159 ciphertext_validity_proof_data_with_ciphertext,
160 range_proof_data,
161 })
162}