Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Confidential mint burn extension: Token program changes #7319

Merged
Merged
Show file tree
Hide file tree
Changes from 38 commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
8fbb0fb
confidential mint-burn extension
abcalphabet Jun 19, 2024
de3d227
Merge branch 'master' into confidential_mint_burn_ext
abcalphabet Jul 1, 2024
32d528e
add ciphertext validity proof to confidential mint
abcalphabet Jul 1, 2024
3a9318d
simplify burn proofs
abcalphabet Jul 2, 2024
fee8acb
add confidential supply
abcalphabet Jul 5, 2024
5bbafec
remove debug statements
abcalphabet Jul 5, 2024
d6724a0
add tests for confidential mint-burn
abcalphabet Jul 5, 2024
0ec8537
Merge branch 'master' into confidential_mint_burn_ext
abcalphabet Jul 10, 2024
5939b72
fix clippy & serde tests
abcalphabet Jul 11, 2024
d4ad146
Merge branch 'master' into confidential_mint_burn_ext
abcalphabet Sep 4, 2024
8479bad
fix some tests
abcalphabet Sep 4, 2024
96d8f92
Merge branch 'master' into confidential_mint_burn_ext
abcalphabet Sep 4, 2024
0b20da3
clippy
abcalphabet Sep 4, 2024
48fcb02
Merge branch 'master' into confidential_mint_burn_ext
abcalphabet Sep 25, 2024
18aa58d
mint burn with new proof generation
abcalphabet Oct 1, 2024
59234ba
remove old mintburn proof generation
abcalphabet Oct 1, 2024
89c7cd2
cleanup
abcalphabet Oct 1, 2024
d46407c
remove cli / client changes and mint-burn tests
abcalphabet Oct 1, 2024
3944195
cleanup
abcalphabet Oct 1, 2024
e010003
more cleanup
abcalphabet Oct 1, 2024
42e0193
fix clippy; serde logic
abcalphabet Oct 2, 2024
e0aefa2
Update token/program-2022/src/extension/confidential_mint_burn/mod.rs
abcalphabet Oct 4, 2024
f795a3f
Update token/program-2022/src/extension/confidential_mint_burn/instru…
abcalphabet Oct 4, 2024
c2ec29a
review fixes
abcalphabet Oct 4, 2024
2a1316f
refactor proof location processing
abcalphabet Oct 4, 2024
3908688
comment
abcalphabet Oct 4, 2024
57684cf
cleanup
abcalphabet Oct 4, 2024
32e06db
fix target_os=solana ; test-sbf
abcalphabet Oct 5, 2024
4fea36d
review fixes
abcalphabet Oct 7, 2024
3a14e2e
fix fmt / serde
abcalphabet Oct 8, 2024
8ad746e
construct supply account info from ref to extension
abcalphabet Oct 8, 2024
df6cd8b
add conf mint/burn failure docs; remove todo
abcalphabet Oct 9, 2024
20c84b5
remove obsolete null check
abcalphabet Oct 9, 2024
bcc0812
Update token/program-2022/src/error.rs
abcalphabet Oct 24, 2024
1d2b804
Update token/program-2022/src/extension/confidential_mint_burn/instru…
abcalphabet Oct 24, 2024
534861e
Update token/program-2022/src/extension/confidential_mint_burn/accoun…
abcalphabet Oct 24, 2024
b01ac24
review fixes
abcalphabet Oct 24, 2024
f601fdd
Merge branch 'master' into confidential_mint_burn_ext_program
abcalphabet Oct 25, 2024
5256e9d
more fixes
abcalphabet Oct 28, 2024
3f3c66e
Merge branch 'master' into confidential_mint_burn_ext_program
abcalphabet Oct 28, 2024
fbff190
clippy; review fixes
abcalphabet Oct 29, 2024
d46193f
Merge branch 'master' into confidential_mint_burn_ext_program
abcalphabet Oct 30, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion libraries/pod/src/optional_keys.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ impl<'de> Visitor<'de> for OptionalNonZeroPubkeyVisitor {
where
E: Error,
{
let pkey = Pubkey::from_str(&v)
let pkey = Pubkey::from_str(v)
.map_err(|_| Error::invalid_value(Unexpected::Str(v), &"value string"))?;

OptionalNonZeroPubkey::try_from(Some(pkey))
Expand Down
30 changes: 30 additions & 0 deletions token/confidential-transfer/proof-extraction/src/encryption.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,36 @@ impl PodFeeCiphertext {
#[repr(C)]
pub struct PodBurnAmountCiphertext(pub(crate) PodGroupedElGamalCiphertext3Handles);

impl PodBurnAmountCiphertext {
pub fn extract_commitment(&self) -> PodPedersenCommitment {
self.0.extract_commitment()
}
abcalphabet marked this conversation as resolved.
Show resolved Hide resolved

pub fn try_extract_ciphertext(
&self,
index: usize,
) -> Result<PodElGamalCiphertext, TokenProofExtractionError> {
self.0
.try_extract_ciphertext(index)
.map_err(|_| TokenProofExtractionError::CiphertextExtraction)
}
}

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[repr(C)]
pub struct PodMintAmountCiphertext(pub(crate) PodGroupedElGamalCiphertext3Handles);

impl PodMintAmountCiphertext {
pub fn extract_commitment(&self) -> PodPedersenCommitment {
self.0.extract_commitment()
}
abcalphabet marked this conversation as resolved.
Show resolved Hide resolved

pub fn try_extract_ciphertext(
&self,
index: usize,
) -> Result<PodElGamalCiphertext, TokenProofExtractionError> {
self.0
.try_extract_ciphertext(index)
.map_err(|_| TokenProofExtractionError::CiphertextExtraction)
}
}
52 changes: 50 additions & 2 deletions token/confidential-transfer/proof-extraction/src/instruction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ use {
solana_program::{
account_info::{next_account_info, AccountInfo},
entrypoint::ProgramResult,
instruction::Instruction,
instruction::{AccountMeta, Instruction},
msg,
program_error::ProgramError,
pubkey::Pubkey,
sysvar::instructions::get_instruction_relative,
sysvar::{self, instructions::get_instruction_relative},
},
solana_zk_sdk::zk_elgamal_proof_program::{
self,
Expand Down Expand Up @@ -146,6 +146,54 @@ pub fn verify_and_extract_context<'a, T: Pod + ZkProofData<U>, U: Pod>(
}
}

/// Processes a proof location for instruction creation. Adds relevant accounts
/// to supplied account vector
///
/// If the proof location is an instruction offset the corresponding proof
/// instruction is created and added to the `proof_instructions` vector.
pub fn process_proof_location<T, U>(
accounts: &mut Vec<AccountMeta>,
expected_instruction_offset: &mut i8,
proof_instructions: &mut Vec<Instruction>,
proof_location: ProofLocation<T>,
push_sysvar_to_accounts: bool,
proof_instruction_type: ProofInstruction,
) -> Result<i8, ProgramError>
where
T: Pod + ZkProofData<U>,
U: Pod,
{
match proof_location {
ProofLocation::InstructionOffset(proof_instruction_offset, proof_data) => {
let proof_instruction_offset: i8 = proof_instruction_offset.into();
if &proof_instruction_offset != expected_instruction_offset {
return Err(ProgramError::InvalidInstructionData);
}

if push_sysvar_to_accounts {
accounts.push(AccountMeta::new_readonly(sysvar::instructions::id(), false));
}
match proof_data {
ProofData::InstructionData(data) => proof_instructions
.push(proof_instruction_type.encode_verify_proof::<T, U>(None, data)),
ProofData::RecordAccount(address, offset) => {
accounts.push(AccountMeta::new_readonly(*address, false));
proof_instructions.push(
proof_instruction_type
.encode_verify_proof_from_account(None, address, offset),
)
}
};
*expected_instruction_offset += 1;
Ok(proof_instruction_offset)
}
ProofLocation::ContextStateAccount(context_state_account) => {
accounts.push(AccountMeta::new_readonly(*context_state_account, false));
Ok(0)
}
}
}

/// Converts a zk proof type to a corresponding ZK ElGamal proof program
/// instruction that verifies the proof.
pub fn zk_proof_type_to_instruction(
Expand Down
9 changes: 3 additions & 6 deletions token/confidential-transfer/proof-generation/src/mint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,13 @@ pub struct MintProofData {
pub equality_proof_data: CiphertextCommitmentEqualityProofData,
pub ciphertext_validity_proof_data: BatchedGroupedCiphertext3HandlesValidityProofData,
pub range_proof_data: BatchedRangeProofU128Data,
pub new_decryptable_supply: AeCiphertext,
}

pub fn mint_split_proof_data(
current_supply_ciphertext: &ElGamalCiphertext,
current_decryptable_supply: &AeCiphertext,
mint_amount: u64,
current_supply: u64,
supply_elgamal_keypair: &ElGamalKeypair,
supply_aes_key: &AeKey,
destination_elgamal_pubkey: &ElGamalPubkey,
Expand Down Expand Up @@ -77,11 +78,6 @@ pub fn mint_split_proof_data(
)
.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)
Expand Down Expand Up @@ -142,5 +138,6 @@ pub fn mint_split_proof_data(
equality_proof_data,
ciphertext_validity_proof_data,
range_proof_data,
new_decryptable_supply: supply_aes_key.encrypt(new_supply),
})
}
4 changes: 2 additions & 2 deletions token/confidential-transfer/proof-tests/tests/proof_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -217,16 +217,16 @@ fn test_mint_validity(mint_amount: u64, supply: u64) {
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,
new_decryptable_supply: _,
} = mint_split_proof_data(
&supply_ciphertext,
&decryptable_supply,
mint_amount,
supply,
&supply_keypair,
&supply_aes_key,
destination_pubkey,
Expand Down
8 changes: 8 additions & 0 deletions token/program-2022/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,11 @@ pub enum TokenError {
/// Fee calculation failed
#[error("Fee calculation failed")]
FeeCalculation,

//65
/// Withdraw / Deposit not allowed for confidential-mint-burn
#[error("Withdraw / Deposit not allowed for confidential-mint-burn")]
IllegalMintBurnConversion,
}
impl From<TokenError> for ProgramError {
fn from(e: TokenError) -> Self {
Expand Down Expand Up @@ -445,6 +450,9 @@ impl PrintProgramError for TokenError {
TokenError::FeeCalculation => {
msg!("Transfer fee calculation failed")
}
TokenError::IllegalMintBurnConversion => {
msg!("Conversions from normal to confidential token balance and vice versa are illegal if the confidential-mint-burn extension is enabled")
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
use {
super::ConfidentialMintBurn,
crate::error::TokenError,
bytemuck::{Pod, Zeroable},
solana_zk_sdk::{
encryption::{
auth_encryption::{AeCiphertext, AeKey},
elgamal::{ElGamalCiphertext, ElGamalKeypair},
pedersen::PedersenOpening,
pod::{
auth_encryption::PodAeCiphertext,
elgamal::{PodElGamalCiphertext, PodElGamalPubkey},
},
},
zk_elgamal_proof_program::proof_data::CiphertextCiphertextEqualityProofData,
},
};

/// Confidential Mint Burn extension information needed to construct a
/// `RotateSupplyElgamalPubkey` instruction.
#[repr(C)]
#[derive(Clone, Copy, Debug, Default, PartialEq, Pod, Zeroable)]
pub struct SupplyAccountInfo {
/// The available balance (encrypted by `supply_elgamal_pubkey`)
pub current_supply: PodElGamalCiphertext,
/// The decryptable supply
pub decryptable_supply: PodAeCiphertext,
/// The supply's elgamal pubkey
pub supply_elgamal_pubkey: PodElGamalPubkey,
}

impl SupplyAccountInfo {
/// Creates a SupplyAccountInfo from ConfidentialMintBurn extension account
/// data
pub fn new(extension: &ConfidentialMintBurn) -> Self {
Self {
current_supply: extension.confidential_supply,
decryptable_supply: extension.decryptable_supply,
supply_elgamal_pubkey: extension.supply_elgamal_pubkey,
}
}

/// Computes the current supply from the decryptable supply and the
/// difference between the decryptable supply and the elgamal encrypted
/// supply ciphertext
pub fn decrypt_current_supply(
&self,
aes_key: &AeKey,
elgamal_keypair: &ElGamalKeypair,
) -> Result<u64, TokenError> {
// decrypt the decryptable supply
let current_decyptable_supply = AeCiphertext::try_from(self.decryptable_supply)
.map_err(|_| TokenError::MalformedCiphertext)?
.decrypt(aes_key)
.ok_or(TokenError::MalformedCiphertext)?;

// get the difference between the supply ciphertext and the decryptable supply
// explanation see https://github.com/solana-labs/solana-program-library/pull/6881#issuecomment-2385579058
let decryptable_supply_ciphertext =
elgamal_keypair.pubkey().encrypt(current_decyptable_supply);
#[allow(clippy::arithmetic_side_effects)]
let supply_delta_ciphertext = decryptable_supply_ciphertext
- ElGamalCiphertext::try_from(self.current_supply)
.map_err(|_| TokenError::MalformedCiphertext)?;
let decryptable_to_current_diff = elgamal_keypair
.secret()
.decrypt_u32(&supply_delta_ciphertext)
.ok_or(TokenError::MalformedCiphertext)?;

// compute the current supply
current_decyptable_supply
.checked_sub(decryptable_to_current_diff)
.ok_or(TokenError::Overflow)
joncinque marked this conversation as resolved.
Show resolved Hide resolved
}

/// Generates the `CiphertextCiphertextEqualityProofData` needed for a
/// `RotateSupplyElgamalPubkey` instruction
pub fn generate_rotate_supply_elgamal_pubkey_proof(
&self,
aes_key: &AeKey,
current_supply_elgamal_keypair: &ElGamalKeypair,
new_supply_elgamal_keypair: &ElGamalKeypair,
) -> Result<CiphertextCiphertextEqualityProofData, TokenError> {
let current_supply =
self.decrypt_current_supply(aes_key, current_supply_elgamal_keypair)?;

let new_supply_opening = PedersenOpening::new_rand();
let new_supply_ciphertext = new_supply_elgamal_keypair
.pubkey()
.encrypt_with(current_supply, &new_supply_opening);

CiphertextCiphertextEqualityProofData::new(
current_supply_elgamal_keypair,
new_supply_elgamal_keypair.pubkey(),
&self
.current_supply
.try_into()
.map_err(|_| TokenError::MalformedCiphertext)?,
&new_supply_ciphertext,
&new_supply_opening,
current_supply,
)
.map_err(|_| TokenError::ProofGeneration)
}
}
Loading