Skip to content

Commit

Permalink
add support for ElGamal registry program in the token program
Browse files Browse the repository at this point in the history
  • Loading branch information
samkim-crypto committed Oct 15, 2024
1 parent 9588472 commit 8cf2dc3
Show file tree
Hide file tree
Showing 9 changed files with 125 additions and 31 deletions.
27 changes: 27 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions token/program-2022/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ num_enum = "0.7.3"
solana-program = "2.0.3"
solana-security-txt = "1.1.1"
solana-zk-sdk = "2.0.3"
spl-elgamal-registry = { version = "0.1.0", path = "../confidential-transfer/elgamal-registry", features = ["no-entrypoint"] }
spl-memo = { version = "5.0", path = "../../memo/program", features = [ "no-entrypoint" ] }
spl-token = { version = "6.0", path = "../program", features = ["no-entrypoint"] }
spl-token-confidential-transfer-ciphertext-arithmetic = { version = "0.1.0", path = "../confidential-transfer/ciphertext-arithmetic" }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ use {
check_program_account,
extension::confidential_transfer::*,
instruction::{encode_instruction, TokenInstruction},
proof::{ProofData, ProofLocation},
},
bytemuck::Zeroable,
num_enum::{IntoPrimitive, TryFromPrimitive},
Expand All @@ -21,6 +20,7 @@ use {
pubkey::Pubkey,
sysvar,
},
spl_token_confidential_transfer_proof_extraction::{ProofData, ProofLocation},
};

/// Confidential Transfer extension instructions
Expand Down Expand Up @@ -81,6 +81,8 @@ pub enum ConfidentialTransferInstruction {
///
/// Accounts expected by this instruction:
///
/// TODO: Add an option to include an `ElGamalRegistry` address
///
/// * Single owner/delegate
/// 0. `[writeable]` The SPL Token account.
/// 1. `[]` The corresponding SPL Token mint.
Expand All @@ -106,7 +108,8 @@ pub enum ConfidentialTransferInstruction {
/// account.
///
/// Data expected by this instruction:
/// `ConfigureAccountInstructionData`
/// None if an `ElGamalRegistry` address is provided in the list of
/// accounts `ConfigureAccountInstructionData` otherwise
ConfigureAccount,

/// Approves a token account for confidential transfers.
Expand Down
3 changes: 3 additions & 0 deletions token/program-2022/src/extension/confidential_transfer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ pub const MAXIMUM_DEPOSIT_TRANSFER_AMOUNT: u64 = (u16::MAX as u64) + (1 << 16) *
/// Bit length of the low bits of pending balance plaintext
pub const PENDING_BALANCE_LO_BIT_LENGTH: u32 = 16;

/// The default maximum pending balance credit counter.
pub const DEFAULT_MAXIMUM_PENDING_BALANCE_CREDIT_COUNTER: u64 = 65536;

/// Confidential Transfer Extension instructions
pub mod instruction;

Expand Down
100 changes: 76 additions & 24 deletions token/program-2022/src/extension/confidential_transfer/processor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use {
};
use {
crate::{
check_program_account,
check_elgamal_registry_program_account, check_program_account,
error::TokenError,
extension::{
confidential_transfer::{instruction::*, verify_proof::*, *},
Expand All @@ -22,7 +22,6 @@ use {
instruction::{decode_instruction_data, decode_instruction_type},
pod::{PodAccount, PodMint},
processor::Processor,
proof::verify_and_extract_context,
},
solana_program::{
account_info::{next_account_info, AccountInfo},
Expand All @@ -33,8 +32,11 @@ use {
pubkey::Pubkey,
sysvar::Sysvar,
},
spl_elgamal_registry::state::ElGamalRegistry,
spl_pod::bytemuck::pod_from_bytes,
spl_token_confidential_transfer_proof_extraction::{
transfer::TransferProofContext, transfer_with_fee::TransferWithFeeProofContext,
verify_and_extract_context,
},
};

Expand Down Expand Up @@ -92,23 +94,59 @@ fn process_update_mint(
Ok(())
}

/// Processes a [ConfigureAccount] instruction with the assumption that an
/// ElGamal registry is provided.
fn process_configure_account_from_registry(
program_id: &Pubkey,
accounts: &[AccountInfo],
) -> ProgramResult {
let elgamal_registry_account = accounts.get(2).ok_or(ProgramError::NotEnoughAccountKeys)?;
check_elgamal_registry_program_account(elgamal_registry_account.owner)?;

let elgamal_registry_account_data = &elgamal_registry_account.data.borrow();
let elgamal_registry_account =
pod_from_bytes::<ElGamalRegistry>(elgamal_registry_account_data)?;

let decryptable_zero_balance = PodAeCiphertext::default();
let maximum_pending_balance_credit_counter =
DEFAULT_MAXIMUM_PENDING_BALANCE_CREDIT_COUNTER.into();

process_configure_account(
program_id,
accounts,
&decryptable_zero_balance,
&maximum_pending_balance_credit_counter,
None,
Some(elgamal_registry_account),
)
}

/// Processes a [ConfigureAccount] instruction.
fn process_configure_account(
program_id: &Pubkey,
accounts: &[AccountInfo],
decryptable_zero_balance: &DecryptableBalance,
maximum_pending_balance_credit_counter: &PodU64,
proof_instruction_offset: i64,
proof_instruction_offset: Option<i64>,
elgamal_registry_account: Option<&ElGamalRegistry>,
) -> ProgramResult {
let account_info_iter = &mut accounts.iter();
let token_account_info = next_account_info(account_info_iter)?;
let mint_info = next_account_info(account_info_iter)?;

// zero-knowledge proof certifies that the supplied ElGamal public key is valid
let proof_context = verify_and_extract_context::<
PubkeyValidityProofData,
PubkeyValidityProofContext,
>(account_info_iter, proof_instruction_offset, None)?;
let elgamal_pubkey = if let Some(offset) = proof_instruction_offset {
// zero-knowledge proof certifies that the supplied ElGamal public key is valid
let proof_context = verify_and_extract_context::<
PubkeyValidityProofData,
PubkeyValidityProofContext,
>(account_info_iter, offset, None)?;
proof_context.pubkey
} else {
// if proof instruction offset is `None`, then assume that the proof
// was already verified in an ElGamal registry account
let _elgamal_registry_account = next_account_info(account_info_iter)?;
elgamal_registry_account.unwrap().elgamal_pubkey
};

let authority_info = next_account_info(account_info_iter)?;
let authority_info_data_len = authority_info.data_len();
Expand All @@ -121,13 +159,21 @@ fn process_configure_account(
return Err(TokenError::MintMismatch.into());
}

Processor::validate_owner(
program_id,
&token_account.base.owner,
authority_info,
authority_info_data_len,
account_info_iter.as_slice(),
)?;
if let Some(registry_account) = elgamal_registry_account {
// if ElGamal registry was provided, then just verify that the registry owner
// and the account match, then skip the signature verification check
if registry_account.owner != *authority_info.key {
return Err(TokenError::OwnerMismatch.into());
}
} else {
Processor::validate_owner(
program_id,
&token_account.base.owner,
authority_info,
authority_info_data_len,
account_info_iter.as_slice(),
)?;
}

check_program_account(mint_info.owner)?;
let mint_data = &mut mint_info.data.borrow();
Expand All @@ -140,7 +186,7 @@ fn process_configure_account(
let confidential_transfer_account =
token_account.init_extension::<ConfidentialTransferAccount>(false)?;
confidential_transfer_account.approved = confidential_transfer_mint.auto_approve_new_accounts;
confidential_transfer_account.elgamal_pubkey = proof_context.pubkey;
confidential_transfer_account.elgamal_pubkey = elgamal_pubkey;
confidential_transfer_account.maximum_pending_balance_credit_counter =
*maximum_pending_balance_credit_counter;

Expand Down Expand Up @@ -1102,14 +1148,20 @@ pub(crate) fn process_instruction(
}
ConfidentialTransferInstruction::ConfigureAccount => {
msg!("ConfidentialTransferInstruction::ConfigureAccount");
let data = decode_instruction_data::<ConfigureAccountInstructionData>(input)?;
process_configure_account(
program_id,
accounts,
&data.decryptable_zero_balance,
&data.maximum_pending_balance_credit_counter,
data.proof_instruction_offset as i64,
)
if input.is_empty() {
// instruction data is empty, so assume an ElGamal registry is provided
process_configure_account_from_registry(program_id, accounts)
} else {
let data = decode_instruction_data::<ConfigureAccountInstructionData>(input)?;
process_configure_account(
program_id,
accounts,
&data.decryptable_zero_balance,
&data.maximum_pending_balance_credit_counter,
Some(data.proof_instruction_offset as i64),
None,
)
}
}
ConfidentialTransferInstruction::ApproveAccount => {
msg!("ConfidentialTransferInstruction::ApproveAccount");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,14 @@ use {
crate::{
error::TokenError,
extension::{confidential_transfer::instruction::*, transfer_fee::TransferFee},
proof::verify_and_extract_context,
},
solana_program::{
account_info::{next_account_info, AccountInfo},
program_error::ProgramError,
},
spl_token_confidential_transfer_proof_extraction::{
transfer::TransferProofContext, transfer_with_fee::TransferWithFeeProofContext,
withdraw::WithdrawProofContext,
verify_and_extract_context, withdraw::WithdrawProofContext,
},
std::slice::Iter,
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ use {
instruction::CiphertextCiphertextEqualityProofData, DecryptableBalance,
},
instruction::{encode_instruction, TokenInstruction},
proof::{ProofData, ProofLocation},
solana_zk_sdk::{
encryption::pod::elgamal::PodElGamalPubkey,
zk_elgamal_proof_program::instruction::ProofInstruction,
Expand All @@ -26,6 +25,7 @@ use {
sysvar,
},
spl_pod::optional_keys::OptionalNonZeroPubkey,
spl_token_confidential_transfer_proof_extraction::{ProofData, ProofLocation},
std::convert::TryFrom,
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ use {
instruction::{decode_instruction_data, decode_instruction_type},
pod::{PodAccount, PodMint},
processor::Processor,
proof::verify_and_extract_context,
solana_zk_sdk::encryption::pod::elgamal::PodElGamalPubkey,
},
bytemuck::Zeroable,
Expand All @@ -39,6 +38,7 @@ use {
pubkey::Pubkey,
},
spl_pod::optional_keys::OptionalNonZeroPubkey,
spl_token_confidential_transfer_proof_extraction::verify_and_extract_context,
};

/// Processes an [InitializeConfidentialTransferFeeConfig] instruction.
Expand Down
11 changes: 10 additions & 1 deletion token/program-2022/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ pub mod onchain;
pub mod pod;
pub mod pod_instruction;
pub mod processor;
pub mod proof;
#[cfg(feature = "serde-traits")]
pub mod serialization;
pub mod state;
Expand Down Expand Up @@ -130,3 +129,13 @@ pub fn check_system_program_account(system_program_id: &Pubkey) -> ProgramResult
}
Ok(())
}

/// Checks if the supplied program ID is that of the ElGamal registry program
pub fn check_elgamal_registry_program_account(
elgamal_registry_account_program_id: &Pubkey,
) -> ProgramResult {
if elgamal_registry_account_program_id != &spl_elgamal_registry::id() {
return Err(ProgramError::IncorrectProgramId);
}
Ok(())
}

0 comments on commit 8cf2dc3

Please sign in to comment.