spl_token_confidential_transfer_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 supply: PodElGamalPubkey,
16    pub auditor: 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: supply_elgamal_pubkey_from_ciphertext_validity_proof,
53            third_pubkey: auditor_elgamal_pubkey,
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            // we don't care about the padding commitment, so ignore it
87        ];
88
89        // range proof context always contains 8 commitments and therefore,
90        // this check will verify equality of all expected commitments
91        // (`zip` will not be short-circuited)
92        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        // check that the range proof was created for the correct number of bits
101        const NEW_SUPPLY_BIT_LENGTH: u8 = 64;
102        const MINT_AMOUNT_LO_BIT_LENGTH: u8 = 16;
103        const MINT_AMOUNT_HI_BIT_LENGTH: u8 = 32;
104        const PADDING_BIT_LENGTH: u8 = 16;
105        let expected_bit_lengths = [
106            NEW_SUPPLY_BIT_LENGTH,
107            MINT_AMOUNT_LO_BIT_LENGTH,
108            MINT_AMOUNT_HI_BIT_LENGTH,
109            PADDING_BIT_LENGTH,
110        ]
111        .iter();
112
113        // range proof context always contains 8 bit lengths and therefore,
114        // this check will verify equality of all expected bit lengths
115        // (`zip` will not be short-circuited)
116        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 mint_pubkeys = MintPubkeys {
125            destination: *destination_elgamal_pubkey,
126            supply: *supply_elgamal_pubkey_from_equality_proof,
127            auditor: *auditor_elgamal_pubkey,
128        };
129
130        Ok(MintProofContext {
131            mint_amount_ciphertext_lo: PodMintAmountCiphertext(*mint_amount_ciphertext_lo),
132            mint_amount_ciphertext_hi: PodMintAmountCiphertext(*mint_amount_ciphertext_hi),
133            mint_pubkeys,
134            new_supply_ciphertext: *new_supply_ciphertext,
135        })
136    }
137}