gorb_ctpg/
mint.rs

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;
21/// The padding bit length in range proofs to make the bit-length power-of-2
22const RANGE_PROOF_PADDING_BIT_LENGTH: usize = 16;
23
24/// The proof data required for a confidential mint instruction
25pub 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    // split the mint amount into low and high bits
44    let (mint_amount_lo, mint_amount_hi) = try_split_u64(mint_amount, MINT_AMOUNT_LO_BIT_LENGTH)
45        .ok_or(TokenProofGenerationError::IllegalAmountBitLength)?;
46
47    // encrypt the mint amount under the destination and auditor's ElGamal public
48    // keys
49    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    // compute the new supply ciphertext
64    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    // compute the new supply
83    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    // generate equality proof data
90    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    // generate ciphertext validity proof data
100    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    // generate range proof data
133    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}