Skip to content

Commit

Permalink
add equality and range proof to confidential mint proof
Browse files Browse the repository at this point in the history
  • Loading branch information
samkim-crypto committed Sep 11, 2024
1 parent 5d4b8c3 commit 419139e
Show file tree
Hide file tree
Showing 3 changed files with 131 additions and 22 deletions.
38 changes: 34 additions & 4 deletions token/confidential-transfer/proof-extraction/src/mint.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
use {
crate::{encryption::PodMintAmountCiphertext, errors::TokenProofExtractionError},
solana_zk_sdk::{
encryption::pod::elgamal::PodElGamalPubkey,
encryption::pod::elgamal::{PodElGamalCiphertext, PodElGamalPubkey},
zk_elgamal_proof_program::proof_data::{
BatchedGroupedCiphertext3HandlesValidityProofContext, BatchedRangeProofContext,
CiphertextCommitmentEqualityProofContext,
},
},
};
Expand All @@ -21,21 +22,35 @@ pub struct MintProofContext {
pub mint_amount_ciphertext_lo: PodMintAmountCiphertext,
pub mint_amount_ciphertext_hi: PodMintAmountCiphertext,
pub mint_pubkeys: MintPubkeys,
pub new_supply_ciphertext: PodElGamalCiphertext,
}

impl MintProofContext {
pub fn verify_and_extract(
equality_proof_context: &CiphertextCommitmentEqualityProofContext,
ciphertext_validity_proof_context: &BatchedGroupedCiphertext3HandlesValidityProofContext,
range_proof_context: &BatchedRangeProofContext,
) -> Result<Self, TokenProofExtractionError> {
// The equality proof context consists of the supply ElGamal public key, the new
// supply ciphertext, and the new supply commitment. The supply ElGamal
// public key should be checked with ciphertext validity proof for
// consistency and the new supply commitment should be checked with
// range proof for consistency. The new supply ciphertext should be
// returned as part of `MintProofContext`.
let CiphertextCommitmentEqualityProofContext {
pubkey: supply_elgamal_pubkey_from_equality_proof,
ciphertext: new_supply_ciphertext,
commitment: new_supply_commitment,
} = equality_proof_context;

// The ciphertext validity proof context consists of the destination ElGamal
// public key, the auditor ElGamal public key, and the grouped ElGamal
// ciphertexts for the low and high bits of the mint amount. These
// fields should be returned as part of `MintProofContext`.
let BatchedGroupedCiphertext3HandlesValidityProofContext {
first_pubkey: destination_elgamal_pubkey,
second_pubkey: auditor_elgamal_pubkey,
third_pubkey: supply_elgamal_pubkey,
third_pubkey: supply_elgamal_pubkey_from_ciphertext_validity_proof,
grouped_ciphertext_lo: mint_amount_ciphertext_lo,
grouped_ciphertext_hi: mint_amount_ciphertext_hi,
} = ciphertext_validity_proof_context;
Expand All @@ -51,12 +66,24 @@ impl MintProofContext {
bit_lengths: range_proof_bit_lengths,
} = range_proof_context;

// check that the supply pubkey is consistent between equality and ciphertext
// validity proofs
if supply_elgamal_pubkey_from_equality_proof
!= supply_elgamal_pubkey_from_ciphertext_validity_proof
{
return Err(TokenProofExtractionError::ElGamalPubkeyMismatch);
}

// check that the range proof was created for the correct set of Pedersen
// commitments
let mint_amount_commitment_lo = mint_amount_ciphertext_lo.extract_commitment();
let mint_amount_commitment_hi = mint_amount_ciphertext_hi.extract_commitment();

let expected_commitments = [mint_amount_commitment_lo, mint_amount_commitment_hi];
let expected_commitments = [
*new_supply_commitment,
mint_amount_commitment_lo,
mint_amount_commitment_hi,
];

if !range_proof_commitments
.iter()
Expand All @@ -67,10 +94,12 @@ impl MintProofContext {
}

// check that the range proof was created for the correct number of bits
const NEW_SUPPLY_BIT_LENGTH: u8 = 64;
const MINT_AMOUNT_LO_BIT_LENGTH: u8 = 16;
const MINT_AMOUNT_HI_BIT_LENGTH: u8 = 32;
const PADDING_BIT_LENGTH: u8 = 16;
let expected_bit_lengths = [
NEW_SUPPLY_BIT_LENGTH,
MINT_AMOUNT_LO_BIT_LENGTH,
MINT_AMOUNT_HI_BIT_LENGTH,
PADDING_BIT_LENGTH,
Expand All @@ -88,13 +117,14 @@ impl MintProofContext {
let mint_pubkeys = MintPubkeys {
destination: *destination_elgamal_pubkey,
auditor: *auditor_elgamal_pubkey,
supply: *supply_elgamal_pubkey,
supply: *supply_elgamal_pubkey_from_equality_proof,
};

Ok(MintProofContext {
mint_amount_ciphertext_lo: PodMintAmountCiphertext(*mint_amount_ciphertext_lo),
mint_amount_ciphertext_hi: PodMintAmountCiphertext(*mint_amount_ciphertext_hi),
mint_pubkeys,
new_supply_ciphertext: *new_supply_ciphertext,
})
}
}
78 changes: 68 additions & 10 deletions token/confidential-transfer/proof-generation/src/mint.rs
Original file line number Diff line number Diff line change
@@ -1,29 +1,42 @@
use {
crate::{encryption::MintAmountCiphertext, errors::TokenProofGenerationError, try_split_u64},
crate::{
encryption::MintAmountCiphertext, errors::TokenProofGenerationError,
try_combine_lo_hi_ciphertexts, try_split_u64,
},
solana_zk_sdk::{
encryption::{elgamal::ElGamalPubkey, pedersen::Pedersen},
encryption::{
auth_encryption::{AeCiphertext, AeKey},
elgamal::{ElGamalCiphertext, ElGamalKeypair, ElGamalPubkey},
pedersen::Pedersen,
},
zk_elgamal_proof_program::proof_data::{
BatchedGroupedCiphertext3HandlesValidityProofData, BatchedRangeProofU64Data,
BatchedGroupedCiphertext3HandlesValidityProofData, BatchedRangeProofU128Data,
CiphertextCommitmentEqualityProofData,
},
},
};

const NEW_SUPPLY_BIT_LENGTH: usize = 64;
const MINT_AMOUNT_LO_BIT_LENGTH: usize = 16;
const MINT_AMOUNT_HI_BIT_LENGTH: usize = 32;
/// The padding bit length in range proofs to make the bit-length power-of-2
const RANGE_PROOF_PADDING_BIT_LENGTH: usize = 16;

/// The proof data required for a confidential mint instruction
pub struct MintProofData {
pub equality_proof_data: CiphertextCommitmentEqualityProofData,
pub ciphertext_validity_proof_data: BatchedGroupedCiphertext3HandlesValidityProofData,
pub range_proof_data: BatchedRangeProofU64Data,
pub range_proof_data: BatchedRangeProofU128Data,
}

pub fn mint_split_proof_data(
current_supply_ciphertext: &ElGamalCiphertext,
current_decryptable_supply: &AeCiphertext,
mint_amount: u64,
supply_elgamal_keypair: &ElGamalKeypair,
supply_aes_key: &AeKey,
destination_elgamal_pubkey: &ElGamalPubkey,
auditor_elgamal_pubkey: &ElGamalPubkey,
supply_elgamal_pubkey: &ElGamalPubkey,
) -> Result<MintProofData, TokenProofGenerationError> {
// split the mint amount into low and high bits
let (mint_amount_lo, mint_amount_hi) = try_split_u64(mint_amount, MINT_AMOUNT_LO_BIT_LENGTH)
Expand All @@ -35,21 +48,62 @@ pub fn mint_split_proof_data(
mint_amount_lo,
destination_elgamal_pubkey,
auditor_elgamal_pubkey,
supply_elgamal_pubkey,
supply_elgamal_keypair.pubkey(),
);

let (mint_amount_grouped_ciphertext_hi, mint_amount_opening_hi) = MintAmountCiphertext::new(
mint_amount_hi,
destination_elgamal_pubkey,
auditor_elgamal_pubkey,
supply_elgamal_pubkey,
supply_elgamal_keypair.pubkey(),
);

// compute the new supply ciphertext
let mint_amount_ciphertext_supply_lo = mint_amount_grouped_ciphertext_lo
.0
.to_elgamal_ciphertext(2)
.unwrap();
let mint_amount_ciphertext_supply_hi = mint_amount_grouped_ciphertext_hi
.0
.to_elgamal_ciphertext(2)
.unwrap();

#[allow(clippy::arithmetic_side_effects)]
let new_supply_ciphertext = current_supply_ciphertext
+ try_combine_lo_hi_ciphertexts(
&mint_amount_ciphertext_supply_lo,
&mint_amount_ciphertext_supply_hi,
MINT_AMOUNT_LO_BIT_LENGTH,
)
.ok_or(TokenProofGenerationError::IllegalAmountBitLength)?;

// decrypt the current supply
let current_supply = current_decryptable_supply
.decrypt(supply_aes_key)
.ok_or(TokenProofGenerationError::IllegalAmountBitLength)?;

// compute the new supply
let new_supply = current_supply
.checked_add(mint_amount)
.ok_or(TokenProofGenerationError::IllegalAmountBitLength)?;

let (new_supply_commitment, new_supply_opening) = Pedersen::new(new_supply);

// generate equality proof data
let equality_proof_data = CiphertextCommitmentEqualityProofData::new(
supply_elgamal_keypair,
&new_supply_ciphertext,
&new_supply_commitment,
&new_supply_opening,
new_supply,
)
.map_err(TokenProofGenerationError::from)?;

// generate ciphertext validity proof data
let ciphertext_validity_proof_data = BatchedGroupedCiphertext3HandlesValidityProofData::new(
destination_elgamal_pubkey,
auditor_elgamal_pubkey,
supply_elgamal_pubkey,
supply_elgamal_keypair.pubkey(),
&mint_amount_grouped_ciphertext_lo.0,
&mint_amount_grouped_ciphertext_hi.0,
mint_amount_lo,
Expand All @@ -61,19 +115,22 @@ pub fn mint_split_proof_data(

// generate range proof data
let (padding_commitment, padding_opening) = Pedersen::new(0_u64);
let range_proof_data = BatchedRangeProofU64Data::new(
let range_proof_data = BatchedRangeProofU128Data::new(
vec![
&new_supply_commitment,
mint_amount_grouped_ciphertext_lo.get_commitment(),
mint_amount_grouped_ciphertext_hi.get_commitment(),
&padding_commitment,
],
vec![mint_amount_lo, mint_amount_hi, 0],
vec![new_supply, mint_amount_lo, mint_amount_hi, 0],
vec![
NEW_SUPPLY_BIT_LENGTH,
MINT_AMOUNT_LO_BIT_LENGTH,
MINT_AMOUNT_HI_BIT_LENGTH,
RANGE_PROOF_PADDING_BIT_LENGTH,
],
vec![
&new_supply_opening,
&mint_amount_opening_lo,
&mint_amount_opening_hi,
&padding_opening,
Expand All @@ -82,6 +139,7 @@ pub fn mint_split_proof_data(
.map_err(TokenProofGenerationError::from)?;

Ok(MintProofData {
equality_proof_data,
ciphertext_validity_proof_data,
range_proof_data,
})
Expand Down
37 changes: 29 additions & 8 deletions token/confidential-transfer/proof-tests/tests/proof_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -187,38 +187,59 @@ fn test_withdraw_validity(spendable_balance: u64, withdraw_amount: u64) {

#[test]
fn test_mint_proof_correctness() {
test_mint_validity(0);
test_mint_validity(1);
test_mint_validity(65535);
test_mint_validity(65536);
test_mint_validity(281474976710655);
test_mint_validity(0, 0);
test_mint_validity(1, 0);
test_mint_validity(65535, 0);
test_mint_validity(65536, 0);
test_mint_validity(281474976710655, 0);

test_mint_validity(0, 65535);
test_mint_validity(1, 65535);
test_mint_validity(65535, 65535);
test_mint_validity(65536, 65535);
test_mint_validity(281474976710655, 65535);

test_mint_validity(0, 281474976710655);
test_mint_validity(1, 281474976710655);
test_mint_validity(65535, 281474976710655);
test_mint_validity(65536, 281474976710655);
test_mint_validity(281474976710655, 281474976710655);
}

fn test_mint_validity(mint_amount: u64) {
fn test_mint_validity(mint_amount: u64, supply: u64) {
let destination_keypair = ElGamalKeypair::new_rand();
let destination_pubkey = destination_keypair.pubkey();

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

let supply_keypair = ElGamalKeypair::new_rand();
let supply_pubkey = supply_keypair.pubkey();
let supply_aes_key = AeKey::new_rand();

let supply_ciphertext = supply_keypair.pubkey().encrypt(supply);
let decryptable_supply = supply_aes_key.encrypt(supply);

let MintProofData {
equality_proof_data,
ciphertext_validity_proof_data,
range_proof_data,
} = mint_split_proof_data(
&supply_ciphertext,
&decryptable_supply,
mint_amount,
&supply_keypair,
&supply_aes_key,
destination_pubkey,
auditor_pubkey,
supply_pubkey,
)
.unwrap();

equality_proof_data.verify_proof().unwrap();
ciphertext_validity_proof_data.verify_proof().unwrap();
range_proof_data.verify_proof().unwrap();

MintProofContext::verify_and_extract(
equality_proof_data.context_data(),
ciphertext_validity_proof_data.context_data(),
range_proof_data.context_data(),
)
Expand Down

0 comments on commit 419139e

Please sign in to comment.