Skip to main content

gorb_ct_proof_extraction/
mint.rs

1use {
2    crate::{encryption::PodMintAmountCiphertext, 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
12/// The public keys associated with a confidential mint
13pub struct MintPubkeys {
14    pub destination: PodElGamalPubkey,
15    pub auditor: PodElGamalPubkey,
16    pub supply: PodElGamalPubkey,
17}
18
19/// The proof context information needed to process a confidential mint
20/// instruction
21pub struct MintProofContext {
22    pub mint_amount_ciphertext_lo: PodMintAmountCiphertext,
23    pub mint_amount_ciphertext_hi: PodMintAmountCiphertext,
24    pub mint_pubkeys: MintPubkeys,
25    pub new_supply_ciphertext: PodElGamalCiphertext,
26}
27
28impl MintProofContext {
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        // The equality proof context consists of the supply ElGamal public key, the new
35        // supply ciphertext, and the new supply commitment. The supply ElGamal
36        // public key should be checked with ciphertext validity proof for
37        // consistency and the new supply commitment should be checked with
38        // range proof for consistency. The new supply ciphertext should be
39        // returned as part of `MintProofContext`.
40        let CiphertextCommitmentEqualityProofContext {
41            pubkey: supply_elgamal_pubkey_from_equality_proof,
42            ciphertext: new_supply_ciphertext,
43            commitment: new_supply_commitment,
44        } = equality_proof_context;
45
46        // The ciphertext validity proof context consists of the destination ElGamal
47        // public key, the auditor ElGamal public key, and the grouped ElGamal
48        // ciphertexts for the low and high bits of the mint amount. These
49        // fields should be returned as part of `MintProofContext`.
50        let BatchedGroupedCiphertext3HandlesValidityProofContext {
51            first_pubkey: destination_elgamal_pubkey,
52            second_pubkey: auditor_elgamal_pubkey,
53            third_pubkey: supply_elgamal_pubkey_from_ciphertext_validity_proof,
54            grouped_ciphertext_lo: mint_amount_ciphertext_lo,
55            grouped_ciphertext_hi: mint_amount_ciphertext_hi,
56        } = ciphertext_validity_proof_context;
57
58        // The range proof context consists of the Pedersen commitments and bit-lengths
59        // for which the range proof is proved. The commitments must consist of
60        // two commitments pertaining to the
61        // low bits of the mint amount, and high bits of the mint
62        // amount. These commitments must be checked for bit lengths `16` and
63        // and `32`.
64        let BatchedRangeProofContext {
65            commitments: range_proof_commitments,
66            bit_lengths: range_proof_bit_lengths,
67        } = range_proof_context;
68
69        // check that the supply pubkey is consistent between equality and ciphertext
70        // validity proofs
71        if supply_elgamal_pubkey_from_equality_proof
72            != supply_elgamal_pubkey_from_ciphertext_validity_proof
73        {
74            return Err(TokenProofExtractionError::ElGamalPubkeyMismatch);
75        }
76
77        // check that the range proof was created for the correct set of Pedersen
78        // commitments
79        let mint_amount_commitment_lo = mint_amount_ciphertext_lo.extract_commitment();
80        let mint_amount_commitment_hi = mint_amount_ciphertext_hi.extract_commitment();
81
82        let expected_commitments = [
83            *new_supply_commitment,
84            mint_amount_commitment_lo,
85            mint_amount_commitment_hi,
86        ];
87
88        if !range_proof_commitments
89            .iter()
90            .zip(expected_commitments.iter())
91            .all(|(proof_commitment, expected_commitment)| proof_commitment == expected_commitment)
92        {
93            return Err(TokenProofExtractionError::PedersenCommitmentMismatch);
94        }
95
96        // check that the range proof was created for the correct number of bits
97        const NEW_SUPPLY_BIT_LENGTH: u8 = 64;
98        const MINT_AMOUNT_LO_BIT_LENGTH: u8 = 16;
99        const MINT_AMOUNT_HI_BIT_LENGTH: u8 = 32;
100        const PADDING_BIT_LENGTH: u8 = 16;
101        let expected_bit_lengths = [
102            NEW_SUPPLY_BIT_LENGTH,
103            MINT_AMOUNT_LO_BIT_LENGTH,
104            MINT_AMOUNT_HI_BIT_LENGTH,
105            PADDING_BIT_LENGTH,
106        ]
107        .iter();
108
109        if !range_proof_bit_lengths
110            .iter()
111            .zip(expected_bit_lengths)
112            .all(|(proof_len, expected_len)| proof_len == expected_len)
113        {
114            return Err(TokenProofExtractionError::RangeProofLengthMismatch);
115        }
116
117        let mint_pubkeys = MintPubkeys {
118            destination: *destination_elgamal_pubkey,
119            auditor: *auditor_elgamal_pubkey,
120            supply: *supply_elgamal_pubkey_from_equality_proof,
121        };
122
123        Ok(MintProofContext {
124            mint_amount_ciphertext_lo: PodMintAmountCiphertext(*mint_amount_ciphertext_lo),
125            mint_amount_ciphertext_hi: PodMintAmountCiphertext(*mint_amount_ciphertext_hi),
126            mint_pubkeys,
127            new_supply_ciphertext: *new_supply_ciphertext,
128        })
129    }
130}