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

Commit 419139e

Browse files
committed
add equality and range proof to confidential mint proof
1 parent 5d4b8c3 commit 419139e

File tree

3 files changed

+131
-22
lines changed

3 files changed

+131
-22
lines changed

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

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
use {
22
crate::{encryption::PodMintAmountCiphertext, errors::TokenProofExtractionError},
33
solana_zk_sdk::{
4-
encryption::pod::elgamal::PodElGamalPubkey,
4+
encryption::pod::elgamal::{PodElGamalCiphertext, PodElGamalPubkey},
55
zk_elgamal_proof_program::proof_data::{
66
BatchedGroupedCiphertext3HandlesValidityProofContext, BatchedRangeProofContext,
7+
CiphertextCommitmentEqualityProofContext,
78
},
89
},
910
};
@@ -21,21 +22,35 @@ pub struct MintProofContext {
2122
pub mint_amount_ciphertext_lo: PodMintAmountCiphertext,
2223
pub mint_amount_ciphertext_hi: PodMintAmountCiphertext,
2324
pub mint_pubkeys: MintPubkeys,
25+
pub new_supply_ciphertext: PodElGamalCiphertext,
2426
}
2527

2628
impl MintProofContext {
2729
pub fn verify_and_extract(
30+
equality_proof_context: &CiphertextCommitmentEqualityProofContext,
2831
ciphertext_validity_proof_context: &BatchedGroupedCiphertext3HandlesValidityProofContext,
2932
range_proof_context: &BatchedRangeProofContext,
3033
) -> 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+
3146
// The ciphertext validity proof context consists of the destination ElGamal
3247
// public key, the auditor ElGamal public key, and the grouped ElGamal
3348
// ciphertexts for the low and high bits of the mint amount. These
3449
// fields should be returned as part of `MintProofContext`.
3550
let BatchedGroupedCiphertext3HandlesValidityProofContext {
3651
first_pubkey: destination_elgamal_pubkey,
3752
second_pubkey: auditor_elgamal_pubkey,
38-
third_pubkey: supply_elgamal_pubkey,
53+
third_pubkey: supply_elgamal_pubkey_from_ciphertext_validity_proof,
3954
grouped_ciphertext_lo: mint_amount_ciphertext_lo,
4055
grouped_ciphertext_hi: mint_amount_ciphertext_hi,
4156
} = ciphertext_validity_proof_context;
@@ -51,12 +66,24 @@ impl MintProofContext {
5166
bit_lengths: range_proof_bit_lengths,
5267
} = range_proof_context;
5368

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+
5477
// check that the range proof was created for the correct set of Pedersen
5578
// commitments
5679
let mint_amount_commitment_lo = mint_amount_ciphertext_lo.extract_commitment();
5780
let mint_amount_commitment_hi = mint_amount_ciphertext_hi.extract_commitment();
5881

59-
let expected_commitments = [mint_amount_commitment_lo, mint_amount_commitment_hi];
82+
let expected_commitments = [
83+
*new_supply_commitment,
84+
mint_amount_commitment_lo,
85+
mint_amount_commitment_hi,
86+
];
6087

6188
if !range_proof_commitments
6289
.iter()
@@ -67,10 +94,12 @@ impl MintProofContext {
6794
}
6895

6996
// check that the range proof was created for the correct number of bits
97+
const NEW_SUPPLY_BIT_LENGTH: u8 = 64;
7098
const MINT_AMOUNT_LO_BIT_LENGTH: u8 = 16;
7199
const MINT_AMOUNT_HI_BIT_LENGTH: u8 = 32;
72100
const PADDING_BIT_LENGTH: u8 = 16;
73101
let expected_bit_lengths = [
102+
NEW_SUPPLY_BIT_LENGTH,
74103
MINT_AMOUNT_LO_BIT_LENGTH,
75104
MINT_AMOUNT_HI_BIT_LENGTH,
76105
PADDING_BIT_LENGTH,
@@ -88,13 +117,14 @@ impl MintProofContext {
88117
let mint_pubkeys = MintPubkeys {
89118
destination: *destination_elgamal_pubkey,
90119
auditor: *auditor_elgamal_pubkey,
91-
supply: *supply_elgamal_pubkey,
120+
supply: *supply_elgamal_pubkey_from_equality_proof,
92121
};
93122

94123
Ok(MintProofContext {
95124
mint_amount_ciphertext_lo: PodMintAmountCiphertext(*mint_amount_ciphertext_lo),
96125
mint_amount_ciphertext_hi: PodMintAmountCiphertext(*mint_amount_ciphertext_hi),
97126
mint_pubkeys,
127+
new_supply_ciphertext: *new_supply_ciphertext,
98128
})
99129
}
100130
}

token/confidential-transfer/proof-generation/src/mint.rs

Lines changed: 68 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,42 @@
11
use {
2-
crate::{encryption::MintAmountCiphertext, errors::TokenProofGenerationError, try_split_u64},
2+
crate::{
3+
encryption::MintAmountCiphertext, errors::TokenProofGenerationError,
4+
try_combine_lo_hi_ciphertexts, try_split_u64,
5+
},
36
solana_zk_sdk::{
4-
encryption::{elgamal::ElGamalPubkey, pedersen::Pedersen},
7+
encryption::{
8+
auth_encryption::{AeCiphertext, AeKey},
9+
elgamal::{ElGamalCiphertext, ElGamalKeypair, ElGamalPubkey},
10+
pedersen::Pedersen,
11+
},
512
zk_elgamal_proof_program::proof_data::{
6-
BatchedGroupedCiphertext3HandlesValidityProofData, BatchedRangeProofU64Data,
13+
BatchedGroupedCiphertext3HandlesValidityProofData, BatchedRangeProofU128Data,
14+
CiphertextCommitmentEqualityProofData,
715
},
816
},
917
};
1018

19+
const NEW_SUPPLY_BIT_LENGTH: usize = 64;
1120
const MINT_AMOUNT_LO_BIT_LENGTH: usize = 16;
1221
const MINT_AMOUNT_HI_BIT_LENGTH: usize = 32;
1322
/// The padding bit length in range proofs to make the bit-length power-of-2
1423
const RANGE_PROOF_PADDING_BIT_LENGTH: usize = 16;
1524

1625
/// The proof data required for a confidential mint instruction
1726
pub struct MintProofData {
27+
pub equality_proof_data: CiphertextCommitmentEqualityProofData,
1828
pub ciphertext_validity_proof_data: BatchedGroupedCiphertext3HandlesValidityProofData,
19-
pub range_proof_data: BatchedRangeProofU64Data,
29+
pub range_proof_data: BatchedRangeProofU128Data,
2030
}
2131

2232
pub fn mint_split_proof_data(
33+
current_supply_ciphertext: &ElGamalCiphertext,
34+
current_decryptable_supply: &AeCiphertext,
2335
mint_amount: u64,
36+
supply_elgamal_keypair: &ElGamalKeypair,
37+
supply_aes_key: &AeKey,
2438
destination_elgamal_pubkey: &ElGamalPubkey,
2539
auditor_elgamal_pubkey: &ElGamalPubkey,
26-
supply_elgamal_pubkey: &ElGamalPubkey,
2740
) -> Result<MintProofData, TokenProofGenerationError> {
2841
// split the mint amount into low and high bits
2942
let (mint_amount_lo, mint_amount_hi) = try_split_u64(mint_amount, MINT_AMOUNT_LO_BIT_LENGTH)
@@ -35,21 +48,62 @@ pub fn mint_split_proof_data(
3548
mint_amount_lo,
3649
destination_elgamal_pubkey,
3750
auditor_elgamal_pubkey,
38-
supply_elgamal_pubkey,
51+
supply_elgamal_keypair.pubkey(),
3952
);
4053

4154
let (mint_amount_grouped_ciphertext_hi, mint_amount_opening_hi) = MintAmountCiphertext::new(
4255
mint_amount_hi,
4356
destination_elgamal_pubkey,
4457
auditor_elgamal_pubkey,
45-
supply_elgamal_pubkey,
58+
supply_elgamal_keypair.pubkey(),
4659
);
4760

61+
// compute the new supply ciphertext
62+
let mint_amount_ciphertext_supply_lo = mint_amount_grouped_ciphertext_lo
63+
.0
64+
.to_elgamal_ciphertext(2)
65+
.unwrap();
66+
let mint_amount_ciphertext_supply_hi = mint_amount_grouped_ciphertext_hi
67+
.0
68+
.to_elgamal_ciphertext(2)
69+
.unwrap();
70+
71+
#[allow(clippy::arithmetic_side_effects)]
72+
let new_supply_ciphertext = current_supply_ciphertext
73+
+ try_combine_lo_hi_ciphertexts(
74+
&mint_amount_ciphertext_supply_lo,
75+
&mint_amount_ciphertext_supply_hi,
76+
MINT_AMOUNT_LO_BIT_LENGTH,
77+
)
78+
.ok_or(TokenProofGenerationError::IllegalAmountBitLength)?;
79+
80+
// decrypt the current supply
81+
let current_supply = current_decryptable_supply
82+
.decrypt(supply_aes_key)
83+
.ok_or(TokenProofGenerationError::IllegalAmountBitLength)?;
84+
85+
// compute the new supply
86+
let new_supply = current_supply
87+
.checked_add(mint_amount)
88+
.ok_or(TokenProofGenerationError::IllegalAmountBitLength)?;
89+
90+
let (new_supply_commitment, new_supply_opening) = Pedersen::new(new_supply);
91+
92+
// generate equality proof data
93+
let equality_proof_data = CiphertextCommitmentEqualityProofData::new(
94+
supply_elgamal_keypair,
95+
&new_supply_ciphertext,
96+
&new_supply_commitment,
97+
&new_supply_opening,
98+
new_supply,
99+
)
100+
.map_err(TokenProofGenerationError::from)?;
101+
48102
// generate ciphertext validity proof data
49103
let ciphertext_validity_proof_data = BatchedGroupedCiphertext3HandlesValidityProofData::new(
50104
destination_elgamal_pubkey,
51105
auditor_elgamal_pubkey,
52-
supply_elgamal_pubkey,
106+
supply_elgamal_keypair.pubkey(),
53107
&mint_amount_grouped_ciphertext_lo.0,
54108
&mint_amount_grouped_ciphertext_hi.0,
55109
mint_amount_lo,
@@ -61,19 +115,22 @@ pub fn mint_split_proof_data(
61115

62116
// generate range proof data
63117
let (padding_commitment, padding_opening) = Pedersen::new(0_u64);
64-
let range_proof_data = BatchedRangeProofU64Data::new(
118+
let range_proof_data = BatchedRangeProofU128Data::new(
65119
vec![
120+
&new_supply_commitment,
66121
mint_amount_grouped_ciphertext_lo.get_commitment(),
67122
mint_amount_grouped_ciphertext_hi.get_commitment(),
68123
&padding_commitment,
69124
],
70-
vec![mint_amount_lo, mint_amount_hi, 0],
125+
vec![new_supply, mint_amount_lo, mint_amount_hi, 0],
71126
vec![
127+
NEW_SUPPLY_BIT_LENGTH,
72128
MINT_AMOUNT_LO_BIT_LENGTH,
73129
MINT_AMOUNT_HI_BIT_LENGTH,
74130
RANGE_PROOF_PADDING_BIT_LENGTH,
75131
],
76132
vec![
133+
&new_supply_opening,
77134
&mint_amount_opening_lo,
78135
&mint_amount_opening_hi,
79136
&padding_opening,
@@ -82,6 +139,7 @@ pub fn mint_split_proof_data(
82139
.map_err(TokenProofGenerationError::from)?;
83140

84141
Ok(MintProofData {
142+
equality_proof_data,
85143
ciphertext_validity_proof_data,
86144
range_proof_data,
87145
})

token/confidential-transfer/proof-tests/tests/proof_test.rs

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -187,38 +187,59 @@ fn test_withdraw_validity(spendable_balance: u64, withdraw_amount: u64) {
187187

188188
#[test]
189189
fn test_mint_proof_correctness() {
190-
test_mint_validity(0);
191-
test_mint_validity(1);
192-
test_mint_validity(65535);
193-
test_mint_validity(65536);
194-
test_mint_validity(281474976710655);
190+
test_mint_validity(0, 0);
191+
test_mint_validity(1, 0);
192+
test_mint_validity(65535, 0);
193+
test_mint_validity(65536, 0);
194+
test_mint_validity(281474976710655, 0);
195+
196+
test_mint_validity(0, 65535);
197+
test_mint_validity(1, 65535);
198+
test_mint_validity(65535, 65535);
199+
test_mint_validity(65536, 65535);
200+
test_mint_validity(281474976710655, 65535);
201+
202+
test_mint_validity(0, 281474976710655);
203+
test_mint_validity(1, 281474976710655);
204+
test_mint_validity(65535, 281474976710655);
205+
test_mint_validity(65536, 281474976710655);
206+
test_mint_validity(281474976710655, 281474976710655);
195207
}
196208

197-
fn test_mint_validity(mint_amount: u64) {
209+
fn test_mint_validity(mint_amount: u64, supply: u64) {
198210
let destination_keypair = ElGamalKeypair::new_rand();
199211
let destination_pubkey = destination_keypair.pubkey();
200212

201213
let auditor_keypair = ElGamalKeypair::new_rand();
202214
let auditor_pubkey = auditor_keypair.pubkey();
203215

204216
let supply_keypair = ElGamalKeypair::new_rand();
205-
let supply_pubkey = supply_keypair.pubkey();
217+
let supply_aes_key = AeKey::new_rand();
218+
219+
let supply_ciphertext = supply_keypair.pubkey().encrypt(supply);
220+
let decryptable_supply = supply_aes_key.encrypt(supply);
206221

207222
let MintProofData {
223+
equality_proof_data,
208224
ciphertext_validity_proof_data,
209225
range_proof_data,
210226
} = mint_split_proof_data(
227+
&supply_ciphertext,
228+
&decryptable_supply,
211229
mint_amount,
230+
&supply_keypair,
231+
&supply_aes_key,
212232
destination_pubkey,
213233
auditor_pubkey,
214-
supply_pubkey,
215234
)
216235
.unwrap();
217236

237+
equality_proof_data.verify_proof().unwrap();
218238
ciphertext_validity_proof_data.verify_proof().unwrap();
219239
range_proof_data.verify_proof().unwrap();
220240

221241
MintProofContext::verify_and_extract(
242+
equality_proof_data.context_data(),
222243
ciphertext_validity_proof_data.context_data(),
223244
range_proof_data.context_data(),
224245
)

0 commit comments

Comments
 (0)