-
Notifications
You must be signed in to change notification settings - Fork 2.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[confidential-transfer] Add confidential burn proof generation and ex…
…traction (#6955) * add proof generation logic for confidential mint and burn * add proof extraction logic for confidential mint and burn * add tests for confidential mint and burn * Apply suggestions from code review Co-authored-by: Jon C <[email protected]> * add equality and range proof to confidential mint proof * rename `aes_key` to `source_aes_key` in confidential burn --------- Co-authored-by: Jon C <[email protected]>
- Loading branch information
1 parent
012106e
commit df498f3
Showing
9 changed files
with
730 additions
and
2 deletions.
There are no files selected for viewing
130 changes: 130 additions & 0 deletions
130
token/confidential-transfer/proof-extraction/src/burn.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
use { | ||
crate::{encryption::PodBurnAmountCiphertext, errors::TokenProofExtractionError}, | ||
solana_zk_sdk::{ | ||
encryption::pod::elgamal::{PodElGamalCiphertext, PodElGamalPubkey}, | ||
zk_elgamal_proof_program::proof_data::{ | ||
BatchedGroupedCiphertext3HandlesValidityProofContext, BatchedRangeProofContext, | ||
CiphertextCommitmentEqualityProofContext, | ||
}, | ||
}, | ||
}; | ||
|
||
/// The public keys associated with a confidential burn | ||
pub struct BurnPubkeys { | ||
pub source: PodElGamalPubkey, | ||
pub auditor: PodElGamalPubkey, | ||
pub supply: PodElGamalPubkey, | ||
} | ||
|
||
/// The proof context information needed to process a confidential burn | ||
/// instruction | ||
pub struct BurnProofContext { | ||
pub burn_amount_ciphertext_lo: PodBurnAmountCiphertext, | ||
pub burn_amount_ciphertext_hi: PodBurnAmountCiphertext, | ||
pub burn_pubkeys: BurnPubkeys, | ||
pub remaining_balance_ciphertext: PodElGamalCiphertext, | ||
} | ||
|
||
impl BurnProofContext { | ||
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 source ElGamal public key, the new | ||
// source available balance ciphertext, and the new source avaialble | ||
// balance commitment. The public key should be checked with ciphertext | ||
// validity proof context for consistency and the commitment should be | ||
// checked with range proof for consistency. The public key and | ||
// the cihpertext should be returned as part of `BurnProofContext`. | ||
let CiphertextCommitmentEqualityProofContext { | ||
pubkey: source_elgamal_pubkey_from_equality_proof, | ||
ciphertext: remaining_balance_ciphertext, | ||
commitment: remaining_balance_commitment, | ||
} = equality_proof_context; | ||
|
||
// The ciphertext validity proof context consists of the source ElGamal public | ||
// key, the auditor ElGamal public key, and the grouped ElGamal | ||
// ciphertexts for the low and high bits of the burn amount. The source | ||
// ElGamal public key should be checked with equality | ||
// proof for consistency and the rest of the data should be returned as part of | ||
// `BurnProofContext`. | ||
let BatchedGroupedCiphertext3HandlesValidityProofContext { | ||
first_pubkey: source_elgamal_pubkey_from_validity_proof, | ||
second_pubkey: auditor_elgamal_pubkey, | ||
third_pubkey: supply_elgamal_pubkey, | ||
grouped_ciphertext_lo: burn_amount_ciphertext_lo, | ||
grouped_ciphertext_hi: burn_amount_ciphertext_hi, | ||
} = ciphertext_validity_proof_context; | ||
|
||
// The range proof context consists of the Pedersen commitments and bit-lengths | ||
// for which the range proof is proved. The commitments must consist of | ||
// three commitments pertaining to the new source available balance, the | ||
// low bits of the burn amount, and high bits of the burn | ||
// amount. These commitments must be checked for bit lengths `64`, `16`, | ||
// and `32`. | ||
let BatchedRangeProofContext { | ||
commitments: range_proof_commitments, | ||
bit_lengths: range_proof_bit_lengths, | ||
} = range_proof_context; | ||
|
||
// check that the source pubkey is consistent between equality and ciphertext | ||
// validity proofs | ||
if source_elgamal_pubkey_from_equality_proof != source_elgamal_pubkey_from_validity_proof { | ||
return Err(TokenProofExtractionError::ElGamalPubkeyMismatch); | ||
} | ||
|
||
// check that the range proof was created for the correct set of Pedersen | ||
// commitments | ||
let burn_amount_commitment_lo = burn_amount_ciphertext_lo.extract_commitment(); | ||
let burn_amount_commitment_hi = burn_amount_ciphertext_hi.extract_commitment(); | ||
|
||
let expected_commitments = [ | ||
*remaining_balance_commitment, | ||
burn_amount_commitment_lo, | ||
burn_amount_commitment_hi, | ||
]; | ||
|
||
if !range_proof_commitments | ||
.iter() | ||
.zip(expected_commitments.iter()) | ||
.all(|(proof_commitment, expected_commitment)| proof_commitment == expected_commitment) | ||
{ | ||
return Err(TokenProofExtractionError::PedersenCommitmentMismatch); | ||
} | ||
|
||
// check that the range proof was created for the correct number of bits | ||
const REMAINING_BALANCE_BIT_LENGTH: u8 = 64; | ||
const BURN_AMOUNT_LO_BIT_LENGTH: u8 = 16; | ||
const BURN_AMOUNT_HI_BIT_LENGTH: u8 = 32; | ||
const PADDING_BIT_LENGTH: u8 = 16; | ||
let expected_bit_lengths = [ | ||
REMAINING_BALANCE_BIT_LENGTH, | ||
BURN_AMOUNT_LO_BIT_LENGTH, | ||
BURN_AMOUNT_HI_BIT_LENGTH, | ||
PADDING_BIT_LENGTH, | ||
] | ||
.iter(); | ||
|
||
if !range_proof_bit_lengths | ||
.iter() | ||
.zip(expected_bit_lengths) | ||
.all(|(proof_len, expected_len)| proof_len == expected_len) | ||
{ | ||
return Err(TokenProofExtractionError::RangeProofLengthMismatch); | ||
} | ||
|
||
let burn_pubkeys = BurnPubkeys { | ||
source: *source_elgamal_pubkey_from_equality_proof, | ||
auditor: *auditor_elgamal_pubkey, | ||
supply: *supply_elgamal_pubkey, | ||
}; | ||
|
||
Ok(BurnProofContext { | ||
burn_amount_ciphertext_lo: PodBurnAmountCiphertext(*burn_amount_ciphertext_lo), | ||
burn_amount_ciphertext_hi: PodBurnAmountCiphertext(*burn_amount_ciphertext_hi), | ||
burn_pubkeys, | ||
remaining_balance_ciphertext: *remaining_balance_ciphertext, | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,7 @@ | ||
pub mod burn; | ||
pub mod encryption; | ||
pub mod errors; | ||
pub mod mint; | ||
pub mod transfer; | ||
pub mod transfer_with_fee; | ||
pub mod withdraw; |
130 changes: 130 additions & 0 deletions
130
token/confidential-transfer/proof-extraction/src/mint.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
use { | ||
crate::{encryption::PodMintAmountCiphertext, errors::TokenProofExtractionError}, | ||
solana_zk_sdk::{ | ||
encryption::pod::elgamal::{PodElGamalCiphertext, PodElGamalPubkey}, | ||
zk_elgamal_proof_program::proof_data::{ | ||
BatchedGroupedCiphertext3HandlesValidityProofContext, BatchedRangeProofContext, | ||
CiphertextCommitmentEqualityProofContext, | ||
}, | ||
}, | ||
}; | ||
|
||
/// The public keys associated with a confidential mint | ||
pub struct MintPubkeys { | ||
pub destination: PodElGamalPubkey, | ||
pub auditor: PodElGamalPubkey, | ||
pub supply: PodElGamalPubkey, | ||
} | ||
|
||
/// The proof context information needed to process a confidential mint | ||
/// instruction | ||
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_from_ciphertext_validity_proof, | ||
grouped_ciphertext_lo: mint_amount_ciphertext_lo, | ||
grouped_ciphertext_hi: mint_amount_ciphertext_hi, | ||
} = ciphertext_validity_proof_context; | ||
|
||
// The range proof context consists of the Pedersen commitments and bit-lengths | ||
// for which the range proof is proved. The commitments must consist of | ||
// two commitments pertaining to the the | ||
// low bits of the mint amount, and high bits of the mint | ||
// amount. These commitments must be checked for bit lengths `16` and | ||
// and `32`. | ||
let BatchedRangeProofContext { | ||
commitments: range_proof_commitments, | ||
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 = [ | ||
*new_supply_commitment, | ||
mint_amount_commitment_lo, | ||
mint_amount_commitment_hi, | ||
]; | ||
|
||
if !range_proof_commitments | ||
.iter() | ||
.zip(expected_commitments.iter()) | ||
.all(|(proof_commitment, expected_commitment)| proof_commitment == expected_commitment) | ||
{ | ||
return Err(TokenProofExtractionError::PedersenCommitmentMismatch); | ||
} | ||
|
||
// 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, | ||
] | ||
.iter(); | ||
|
||
if !range_proof_bit_lengths | ||
.iter() | ||
.zip(expected_bit_lengths) | ||
.all(|(proof_len, expected_len)| proof_len == expected_len) | ||
{ | ||
return Err(TokenProofExtractionError::RangeProofLengthMismatch); | ||
} | ||
|
||
let mint_pubkeys = MintPubkeys { | ||
destination: *destination_elgamal_pubkey, | ||
auditor: *auditor_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, | ||
}) | ||
} | ||
} |
Oops, something went wrong.