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 9ef1a74 commit 40d0b38
Show file tree
Hide file tree
Showing 9 changed files with 166 additions and 23 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 @@ -472,6 +472,29 @@ pub enum ConfidentialTransferInstruction {
/// Data expected by this instruction:
/// `TransferWithFeeInstructionData`
TransferWithFee,

/// Configures confidential transfers for a token account.
///
/// This instruction is identical to the `ConfigureAccount` account except
/// that a valid `ElGamalRegistry` account is expected in place of the
/// `VerifyPubkeyValidity` proof.
///
/// An `ElGamalRegistry` account is valid if it shares the same owner with
/// the token account. If a valid `ElGamalRegistry` account is provided,
/// then the program skips the verification of the ElGamal pubkey
/// validity proof as well as the token owner signature.
///
/// Accounts expected by this instruction:
///
/// * Single owner/delegate
/// 0. `[writeable]` The SPL Token account.
/// 1. `[]` The corresponding SPL Token mint.
/// 2. `[]` The ElGamal registry account.
/// 3. `[]` The single source account owner.
///
/// Data expected by this instruction:
/// None
ConfigureAccountWithRegistry,
}

/// Data expected by `ConfidentialTransferInstruction::InitializeMint`
Expand Down Expand Up @@ -1701,3 +1724,27 @@ pub fn transfer_with_fee(

Ok(instructions)
}

/// Create a `ConfigureAccountWithRegistry` instruction
pub fn configure_account_with_registry(
token_program_id: &Pubkey,
token_account: &Pubkey,
mint: &Pubkey,
elgamal_registry: &Pubkey,
) -> Result<Instruction, ProgramError> {
check_program_account(token_program_id)?;

let accounts = vec![
AccountMeta::new(*token_account, false),
AccountMeta::new_readonly(*mint, false),
AccountMeta::new_readonly(*elgamal_registry, false),
];

Ok(encode_instruction(
token_program_id,
accounts,
TokenInstruction::ConfidentialTransferExtension,
ConfidentialTransferInstruction::ConfigureAccountWithRegistry,
&(),
))
}
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
91 changes: 74 additions & 17 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,62 @@ fn process_update_mint(
Ok(())
}

enum ElGamalPubkeySource<'a> {
ProofInstructionOffset(i64),
ElGamalRegistry(&'a ElGamalRegistry),
}

/// Processes a [ConfigureAccountWithRegistry] instruction.
fn process_configure_account_with_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,
ElGamalPubkeySource::ElGamalRegistry(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,
elgamal_pubkey_source: ElGamalPubkeySource,
) -> 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 = match elgamal_pubkey_source {
ElGamalPubkeySource::ProofInstructionOffset(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
}
ElGamalPubkeySource::ElGamalRegistry(elgamal_registry_account) => {
let _elgamal_registry_account = next_account_info(account_info_iter)?;
elgamal_registry_account.elgamal_pubkey
}
};

let authority_info = next_account_info(account_info_iter)?;
let authority_info_data_len = authority_info.data_len();
Expand All @@ -121,13 +162,25 @@ 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(),
)?;
match elgamal_pubkey_source {
ElGamalPubkeySource::ProofInstructionOffset(_) => {
Processor::validate_owner(
program_id,
&token_account.base.owner,
authority_info,
authority_info_data_len,
account_info_iter.as_slice(),
)?;
}
ElGamalPubkeySource::ElGamalRegistry(elgamal_registry_account) => {
// if ElGamal registry was provided, then just verify that the owners of the
// registry and token accounts match, then skip the signature
// verification check
if elgamal_registry_account.owner != *authority_info.key {
return Err(TokenError::OwnerMismatch.into());
}
}
};

check_program_account(mint_info.owner)?;
let mint_data = &mut mint_info.data.borrow();
Expand All @@ -140,7 +193,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 @@ -1108,7 +1161,7 @@ pub(crate) fn process_instruction(
accounts,
&data.decryptable_zero_balance,
&data.maximum_pending_balance_credit_counter,
data.proof_instruction_offset as i64,
ElGamalPubkeySource::ProofInstructionOffset(data.proof_instruction_offset as i64),
)
}
ConfidentialTransferInstruction::ApproveAccount => {
Expand Down Expand Up @@ -1217,5 +1270,9 @@ pub(crate) fn process_instruction(
#[cfg(not(feature = "zk-ops"))]
Err(ProgramError::InvalidInstructionData)
}
ConfidentialTransferInstruction::ConfigureAccountWithRegistry => {
msg!("ConfidentialTransferInstruction::ConfigureAccountWithRegistry");
process_configure_account_with_registry(program_id, accounts)
}
}
}
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 40d0b38

Please sign in to comment.