Skip to content
This repository was archived by the owner on Mar 11, 2025. It is now read-only.

Commit f789010

Browse files
committed
add proof extraction logic for confidential mint and burn
1 parent b82c844 commit f789010

File tree

4 files changed

+234
-0
lines changed

4 files changed

+234
-0
lines changed
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
use {
2+
crate::{encryption::PodBurnAmountCiphertext, errors::TokenProofExtractionError},
3+
solana_zk_sdk::{
4+
encryption::pod::elgamal::{PodElGamalCiphertext, PodElGamalPubkey},
5+
zk_elgamal_proof_program::proof_data::{
6+
BatchedGroupedCiphertext2HandlesValidityProofContext, BatchedRangeProofContext,
7+
CiphertextCommitmentEqualityProofContext,
8+
},
9+
},
10+
};
11+
12+
/// The public keys associated with a confidential burn
13+
pub struct BurnPubkeys {
14+
pub source: PodElGamalPubkey,
15+
pub auditor: PodElGamalPubkey,
16+
}
17+
18+
/// The proof context information needed to process a confidential burn
19+
/// instruction
20+
pub struct BurnProofContext {
21+
pub burn_amount_ciphertext_lo: PodBurnAmountCiphertext,
22+
pub burn_amount_ciphertext_hi: PodBurnAmountCiphertext,
23+
pub burn_pubkeys: BurnPubkeys,
24+
pub remaining_balance_ciphertext: PodElGamalCiphertext,
25+
}
26+
27+
impl BurnProofContext {
28+
pub fn verify_and_extract(
29+
equality_proof_context: &CiphertextCommitmentEqualityProofContext,
30+
ciphertext_validity_proof_context: &BatchedGroupedCiphertext2HandlesValidityProofContext,
31+
range_proof_context: &BatchedRangeProofContext,
32+
) -> Result<Self, TokenProofExtractionError> {
33+
// The equality proof context consists of the source ElGamal public key, the new
34+
// source available balance ciphertext, and the new source avaialble
35+
// balance commitment. The public key should be checked with ciphertext
36+
// validity proof context for consistency and the commitment should be
37+
// checked with range proof for consistency. The public key and
38+
// the cihpertext should be returned as part of `BurnProofContext`.
39+
let CiphertextCommitmentEqualityProofContext {
40+
pubkey: source_elgamal_pubkey_from_equality_proof,
41+
ciphertext: remaining_balance_ciphertext,
42+
commitment: remaining_balance_commitment,
43+
} = equality_proof_context;
44+
45+
// The ciphertext validity proof context consists of the source ElGamal public
46+
// key, the auditor ElGamal public key, and the grouped ElGamal
47+
// ciphertexts for the low and high bits of the burn amount. The source
48+
// ElGamal public key should be checked with equality
49+
// proof for consistency and the rest of the data should be returned as part of
50+
// `BurnProofContext`.
51+
let BatchedGroupedCiphertext2HandlesValidityProofContext {
52+
first_pubkey: source_elgamal_pubkey_from_validity_proof,
53+
second_pubkey: auditor_elgamal_pubkey,
54+
grouped_ciphertext_lo: burn_amount_ciphertext_lo,
55+
grouped_ciphertext_hi: burn_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+
// three commitments pertaining to the new source available balance, the
61+
// low bits of the burn amount, and high bits of the burn
62+
// amount. These commitments must be checked for bit lengths `64`, `16`,
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 source pubkey is consistent between equality and ciphertext
70+
// validity proofs
71+
if source_elgamal_pubkey_from_equality_proof != source_elgamal_pubkey_from_validity_proof {
72+
return Err(TokenProofExtractionError::ElGamalPubkeyMismatch);
73+
}
74+
75+
// check that the range proof was created for the correct set of Pedersen
76+
// commitments
77+
let burn_amount_commitment_lo = burn_amount_ciphertext_lo.extract_commitment();
78+
let burn_amount_commitment_hi = burn_amount_ciphertext_hi.extract_commitment();
79+
80+
let expected_commitments = [
81+
*remaining_balance_commitment,
82+
burn_amount_commitment_lo,
83+
burn_amount_commitment_hi,
84+
];
85+
86+
if !range_proof_commitments
87+
.iter()
88+
.zip(expected_commitments.iter())
89+
.all(|(proof_commitment, expected_commitment)| proof_commitment == expected_commitment)
90+
{
91+
return Err(TokenProofExtractionError::PedersenCommitmentMismatch);
92+
}
93+
94+
// check that the range proof was created for the correct number of bits
95+
const REMAINING_BALANCE_BIT_LENGTH: u8 = 64;
96+
const BURN_AMOUNT_LO_BIT_LENGTH: u8 = 16;
97+
const BURN_AMOUNT_HI_BIT_LENGTH: u8 = 32;
98+
const PADDING_BIT_LENGTH: u8 = 16;
99+
let expected_bit_lengths = [
100+
REMAINING_BALANCE_BIT_LENGTH,
101+
BURN_AMOUNT_LO_BIT_LENGTH,
102+
BURN_AMOUNT_HI_BIT_LENGTH,
103+
PADDING_BIT_LENGTH,
104+
]
105+
.iter();
106+
107+
if !range_proof_bit_lengths
108+
.iter()
109+
.zip(expected_bit_lengths)
110+
.all(|(proof_len, expected_len)| proof_len == expected_len)
111+
{
112+
return Err(TokenProofExtractionError::RangeProofLengthMismatch);
113+
}
114+
115+
let burn_pubkeys = BurnPubkeys {
116+
source: *source_elgamal_pubkey_from_equality_proof,
117+
auditor: *auditor_elgamal_pubkey,
118+
};
119+
120+
Ok(BurnProofContext {
121+
burn_amount_ciphertext_lo: PodBurnAmountCiphertext(*burn_amount_ciphertext_lo),
122+
burn_amount_ciphertext_hi: PodBurnAmountCiphertext(*burn_amount_ciphertext_hi),
123+
burn_pubkeys,
124+
remaining_balance_ciphertext: *remaining_balance_ciphertext,
125+
})
126+
}
127+
}

token/confidential-transfer/proof-extraction/src/encryption.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,3 +46,11 @@ impl PodFeeCiphertext {
4646
.map_err(|_| TokenProofExtractionError::CiphertextExtraction)
4747
}
4848
}
49+
50+
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
51+
#[repr(C)]
52+
pub struct PodBurnAmountCiphertext(pub(crate) PodGroupedElGamalCiphertext2Handles);
53+
54+
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
55+
#[repr(C)]
56+
pub struct PodMintAmountCiphertext(pub(crate) PodGroupedElGamalCiphertext2Handles);
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1+
pub mod burn;
12
pub mod encryption;
23
pub mod errors;
4+
pub mod mint;
35
pub mod transfer;
46
pub mod transfer_with_fee;
57
pub mod withdraw;
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
use {
2+
crate::{encryption::PodMintAmountCiphertext, errors::TokenProofExtractionError},
3+
solana_zk_sdk::{
4+
encryption::pod::elgamal::PodElGamalPubkey,
5+
zk_elgamal_proof_program::proof_data::{
6+
BatchedGroupedCiphertext2HandlesValidityProofContext, BatchedRangeProofContext,
7+
},
8+
},
9+
};
10+
11+
/// The public keys associated with a confidential mint
12+
pub struct MintPubkeys {
13+
pub destination: PodElGamalPubkey,
14+
pub auditor: PodElGamalPubkey,
15+
}
16+
17+
/// The proof context information needed to process a confidential mint
18+
/// instruction
19+
pub struct MintProofContext {
20+
pub mint_amount_ciphertext_lo: PodMintAmountCiphertext,
21+
pub mint_amount_ciphertext_hi: PodMintAmountCiphertext,
22+
pub mint_pubkeys: MintPubkeys,
23+
}
24+
25+
impl MintProofContext {
26+
pub fn verify_and_extract(
27+
ciphertext_validity_proof_context: &BatchedGroupedCiphertext2HandlesValidityProofContext,
28+
range_proof_context: &BatchedRangeProofContext,
29+
) -> Result<Self, TokenProofExtractionError> {
30+
// The ciphertext validity proof context consists of the destination ElGamal
31+
// public key, the auditor ElGamal public key, and the grouped ElGamal
32+
// ciphertexts for the low and high bits of the burn amount. These
33+
// fields should be returned as part of `MintProofContext`.
34+
let BatchedGroupedCiphertext2HandlesValidityProofContext {
35+
first_pubkey: destination_elgamal_pubkey,
36+
second_pubkey: auditor_elgamal_pubkey,
37+
grouped_ciphertext_lo: mint_amount_ciphertext_lo,
38+
grouped_ciphertext_hi: mint_amount_ciphertext_hi,
39+
} = ciphertext_validity_proof_context;
40+
41+
// The range proof context consists of the Pedersen commitments and bit-lengths
42+
// for which the range proof is proved. The commitments must consist of
43+
// two commitments pertaining to the the
44+
// low bits of the mint amount, and high bits of the mint
45+
// amount. These commitments must be checked for bit lengths `16` and
46+
// and `32`.
47+
let BatchedRangeProofContext {
48+
commitments: range_proof_commitments,
49+
bit_lengths: range_proof_bit_lengths,
50+
} = range_proof_context;
51+
52+
// check that the range proof was created for the correct set of Pedersen
53+
// commitments
54+
let mint_amount_commitment_lo = mint_amount_ciphertext_lo.extract_commitment();
55+
let mint_amount_commitment_hi = mint_amount_ciphertext_hi.extract_commitment();
56+
57+
let expected_commitments = [mint_amount_commitment_lo, mint_amount_commitment_hi];
58+
59+
if !range_proof_commitments
60+
.iter()
61+
.zip(expected_commitments.iter())
62+
.all(|(proof_commitment, expected_commitment)| proof_commitment == expected_commitment)
63+
{
64+
return Err(TokenProofExtractionError::PedersenCommitmentMismatch);
65+
}
66+
67+
// check that the range proof was created for the correct number of bits
68+
const MINT_AMOUNT_LO_BIT_LENGTH: u8 = 16;
69+
const MINT_AMOUNT_HI_BIT_LENGTH: u8 = 32;
70+
const PADDING_BIT_LENGTH: u8 = 16;
71+
let expected_bit_lengths = [
72+
MINT_AMOUNT_LO_BIT_LENGTH,
73+
MINT_AMOUNT_HI_BIT_LENGTH,
74+
PADDING_BIT_LENGTH,
75+
]
76+
.iter();
77+
78+
if !range_proof_bit_lengths
79+
.iter()
80+
.zip(expected_bit_lengths)
81+
.all(|(proof_len, expected_len)| proof_len == expected_len)
82+
{
83+
return Err(TokenProofExtractionError::RangeProofLengthMismatch);
84+
}
85+
86+
let mint_pubkeys = MintPubkeys {
87+
destination: *destination_elgamal_pubkey,
88+
auditor: *auditor_elgamal_pubkey,
89+
};
90+
91+
Ok(MintProofContext {
92+
mint_amount_ciphertext_lo: PodMintAmountCiphertext(*mint_amount_ciphertext_lo),
93+
mint_amount_ciphertext_hi: PodMintAmountCiphertext(*mint_amount_ciphertext_hi),
94+
mint_pubkeys,
95+
})
96+
}
97+
}

0 commit comments

Comments
 (0)