Skip to content
This repository was archived by the owner on Mar 11, 2025. It is now read-only.

Commit 8cf2dc3

Browse files
committed
add support for ElGamal registry program in the token program
1 parent 9588472 commit 8cf2dc3

File tree

9 files changed

+125
-31
lines changed

9 files changed

+125
-31
lines changed

Cargo.lock

Lines changed: 27 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

token/program-2022/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ num_enum = "0.7.3"
2525
solana-program = "2.0.3"
2626
solana-security-txt = "1.1.1"
2727
solana-zk-sdk = "2.0.3"
28+
spl-elgamal-registry = { version = "0.1.0", path = "../confidential-transfer/elgamal-registry", features = ["no-entrypoint"] }
2829
spl-memo = { version = "5.0", path = "../../memo/program", features = [ "no-entrypoint" ] }
2930
spl-token = { version = "6.0", path = "../program", features = ["no-entrypoint"] }
3031
spl-token-confidential-transfer-ciphertext-arithmetic = { version = "0.1.0", path = "../confidential-transfer/ciphertext-arithmetic" }

token/program-2022/src/extension/confidential_transfer/instruction.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ use {
1111
check_program_account,
1212
extension::confidential_transfer::*,
1313
instruction::{encode_instruction, TokenInstruction},
14-
proof::{ProofData, ProofLocation},
1514
},
1615
bytemuck::Zeroable,
1716
num_enum::{IntoPrimitive, TryFromPrimitive},
@@ -21,6 +20,7 @@ use {
2120
pubkey::Pubkey,
2221
sysvar,
2322
},
23+
spl_token_confidential_transfer_proof_extraction::{ProofData, ProofLocation},
2424
};
2525

2626
/// Confidential Transfer extension instructions
@@ -81,6 +81,8 @@ pub enum ConfidentialTransferInstruction {
8181
///
8282
/// Accounts expected by this instruction:
8383
///
84+
/// TODO: Add an option to include an `ElGamalRegistry` address
85+
///
8486
/// * Single owner/delegate
8587
/// 0. `[writeable]` The SPL Token account.
8688
/// 1. `[]` The corresponding SPL Token mint.
@@ -106,7 +108,8 @@ pub enum ConfidentialTransferInstruction {
106108
/// account.
107109
///
108110
/// Data expected by this instruction:
109-
/// `ConfigureAccountInstructionData`
111+
/// None if an `ElGamalRegistry` address is provided in the list of
112+
/// accounts `ConfigureAccountInstructionData` otherwise
110113
ConfigureAccount,
111114

112115
/// Approves a token account for confidential transfers.

token/program-2022/src/extension/confidential_transfer/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ pub const MAXIMUM_DEPOSIT_TRANSFER_AMOUNT: u64 = (u16::MAX as u64) + (1 << 16) *
2323
/// Bit length of the low bits of pending balance plaintext
2424
pub const PENDING_BALANCE_LO_BIT_LENGTH: u32 = 16;
2525

26+
/// The default maximum pending balance credit counter.
27+
pub const DEFAULT_MAXIMUM_PENDING_BALANCE_CREDIT_COUNTER: u64 = 65536;
28+
2629
/// Confidential Transfer Extension instructions
2730
pub mod instruction;
2831

token/program-2022/src/extension/confidential_transfer/processor.rs

Lines changed: 76 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use {
66
};
77
use {
88
crate::{
9-
check_program_account,
9+
check_elgamal_registry_program_account, check_program_account,
1010
error::TokenError,
1111
extension::{
1212
confidential_transfer::{instruction::*, verify_proof::*, *},
@@ -22,7 +22,6 @@ use {
2222
instruction::{decode_instruction_data, decode_instruction_type},
2323
pod::{PodAccount, PodMint},
2424
processor::Processor,
25-
proof::verify_and_extract_context,
2625
},
2726
solana_program::{
2827
account_info::{next_account_info, AccountInfo},
@@ -33,8 +32,11 @@ use {
3332
pubkey::Pubkey,
3433
sysvar::Sysvar,
3534
},
35+
spl_elgamal_registry::state::ElGamalRegistry,
36+
spl_pod::bytemuck::pod_from_bytes,
3637
spl_token_confidential_transfer_proof_extraction::{
3738
transfer::TransferProofContext, transfer_with_fee::TransferWithFeeProofContext,
39+
verify_and_extract_context,
3840
},
3941
};
4042

@@ -92,23 +94,59 @@ fn process_update_mint(
9294
Ok(())
9395
}
9496

97+
/// Processes a [ConfigureAccount] instruction with the assumption that an
98+
/// ElGamal registry is provided.
99+
fn process_configure_account_from_registry(
100+
program_id: &Pubkey,
101+
accounts: &[AccountInfo],
102+
) -> ProgramResult {
103+
let elgamal_registry_account = accounts.get(2).ok_or(ProgramError::NotEnoughAccountKeys)?;
104+
check_elgamal_registry_program_account(elgamal_registry_account.owner)?;
105+
106+
let elgamal_registry_account_data = &elgamal_registry_account.data.borrow();
107+
let elgamal_registry_account =
108+
pod_from_bytes::<ElGamalRegistry>(elgamal_registry_account_data)?;
109+
110+
let decryptable_zero_balance = PodAeCiphertext::default();
111+
let maximum_pending_balance_credit_counter =
112+
DEFAULT_MAXIMUM_PENDING_BALANCE_CREDIT_COUNTER.into();
113+
114+
process_configure_account(
115+
program_id,
116+
accounts,
117+
&decryptable_zero_balance,
118+
&maximum_pending_balance_credit_counter,
119+
None,
120+
Some(elgamal_registry_account),
121+
)
122+
}
123+
95124
/// Processes a [ConfigureAccount] instruction.
96125
fn process_configure_account(
97126
program_id: &Pubkey,
98127
accounts: &[AccountInfo],
99128
decryptable_zero_balance: &DecryptableBalance,
100129
maximum_pending_balance_credit_counter: &PodU64,
101-
proof_instruction_offset: i64,
130+
proof_instruction_offset: Option<i64>,
131+
elgamal_registry_account: Option<&ElGamalRegistry>,
102132
) -> ProgramResult {
103133
let account_info_iter = &mut accounts.iter();
104134
let token_account_info = next_account_info(account_info_iter)?;
105135
let mint_info = next_account_info(account_info_iter)?;
106136

107-
// zero-knowledge proof certifies that the supplied ElGamal public key is valid
108-
let proof_context = verify_and_extract_context::<
109-
PubkeyValidityProofData,
110-
PubkeyValidityProofContext,
111-
>(account_info_iter, proof_instruction_offset, None)?;
137+
let elgamal_pubkey = if let Some(offset) = proof_instruction_offset {
138+
// zero-knowledge proof certifies that the supplied ElGamal public key is valid
139+
let proof_context = verify_and_extract_context::<
140+
PubkeyValidityProofData,
141+
PubkeyValidityProofContext,
142+
>(account_info_iter, offset, None)?;
143+
proof_context.pubkey
144+
} else {
145+
// if proof instruction offset is `None`, then assume that the proof
146+
// was already verified in an ElGamal registry account
147+
let _elgamal_registry_account = next_account_info(account_info_iter)?;
148+
elgamal_registry_account.unwrap().elgamal_pubkey
149+
};
112150

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

124-
Processor::validate_owner(
125-
program_id,
126-
&token_account.base.owner,
127-
authority_info,
128-
authority_info_data_len,
129-
account_info_iter.as_slice(),
130-
)?;
162+
if let Some(registry_account) = elgamal_registry_account {
163+
// if ElGamal registry was provided, then just verify that the registry owner
164+
// and the account match, then skip the signature verification check
165+
if registry_account.owner != *authority_info.key {
166+
return Err(TokenError::OwnerMismatch.into());
167+
}
168+
} else {
169+
Processor::validate_owner(
170+
program_id,
171+
&token_account.base.owner,
172+
authority_info,
173+
authority_info_data_len,
174+
account_info_iter.as_slice(),
175+
)?;
176+
}
131177

132178
check_program_account(mint_info.owner)?;
133179
let mint_data = &mut mint_info.data.borrow();
@@ -140,7 +186,7 @@ fn process_configure_account(
140186
let confidential_transfer_account =
141187
token_account.init_extension::<ConfidentialTransferAccount>(false)?;
142188
confidential_transfer_account.approved = confidential_transfer_mint.auto_approve_new_accounts;
143-
confidential_transfer_account.elgamal_pubkey = proof_context.pubkey;
189+
confidential_transfer_account.elgamal_pubkey = elgamal_pubkey;
144190
confidential_transfer_account.maximum_pending_balance_credit_counter =
145191
*maximum_pending_balance_credit_counter;
146192

@@ -1102,14 +1148,20 @@ pub(crate) fn process_instruction(
11021148
}
11031149
ConfidentialTransferInstruction::ConfigureAccount => {
11041150
msg!("ConfidentialTransferInstruction::ConfigureAccount");
1105-
let data = decode_instruction_data::<ConfigureAccountInstructionData>(input)?;
1106-
process_configure_account(
1107-
program_id,
1108-
accounts,
1109-
&data.decryptable_zero_balance,
1110-
&data.maximum_pending_balance_credit_counter,
1111-
data.proof_instruction_offset as i64,
1112-
)
1151+
if input.is_empty() {
1152+
// instruction data is empty, so assume an ElGamal registry is provided
1153+
process_configure_account_from_registry(program_id, accounts)
1154+
} else {
1155+
let data = decode_instruction_data::<ConfigureAccountInstructionData>(input)?;
1156+
process_configure_account(
1157+
program_id,
1158+
accounts,
1159+
&data.decryptable_zero_balance,
1160+
&data.maximum_pending_balance_credit_counter,
1161+
Some(data.proof_instruction_offset as i64),
1162+
None,
1163+
)
1164+
}
11131165
}
11141166
ConfidentialTransferInstruction::ApproveAccount => {
11151167
msg!("ConfidentialTransferInstruction::ApproveAccount");

token/program-2022/src/extension/confidential_transfer/verify_proof.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,14 @@ use {
22
crate::{
33
error::TokenError,
44
extension::{confidential_transfer::instruction::*, transfer_fee::TransferFee},
5-
proof::verify_and_extract_context,
65
},
76
solana_program::{
87
account_info::{next_account_info, AccountInfo},
98
program_error::ProgramError,
109
},
1110
spl_token_confidential_transfer_proof_extraction::{
1211
transfer::TransferProofContext, transfer_with_fee::TransferWithFeeProofContext,
13-
withdraw::WithdrawProofContext,
12+
verify_and_extract_context, withdraw::WithdrawProofContext,
1413
},
1514
std::slice::Iter,
1615
};

token/program-2022/src/extension/confidential_transfer_fee/instruction.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ use {
1111
instruction::CiphertextCiphertextEqualityProofData, DecryptableBalance,
1212
},
1313
instruction::{encode_instruction, TokenInstruction},
14-
proof::{ProofData, ProofLocation},
1514
solana_zk_sdk::{
1615
encryption::pod::elgamal::PodElGamalPubkey,
1716
zk_elgamal_proof_program::instruction::ProofInstruction,
@@ -26,6 +25,7 @@ use {
2625
sysvar,
2726
},
2827
spl_pod::optional_keys::OptionalNonZeroPubkey,
28+
spl_token_confidential_transfer_proof_extraction::{ProofData, ProofLocation},
2929
std::convert::TryFrom,
3030
};
3131

token/program-2022/src/extension/confidential_transfer_fee/processor.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ use {
2727
instruction::{decode_instruction_data, decode_instruction_type},
2828
pod::{PodAccount, PodMint},
2929
processor::Processor,
30-
proof::verify_and_extract_context,
3130
solana_zk_sdk::encryption::pod::elgamal::PodElGamalPubkey,
3231
},
3332
bytemuck::Zeroable,
@@ -39,6 +38,7 @@ use {
3938
pubkey::Pubkey,
4039
},
4140
spl_pod::optional_keys::OptionalNonZeroPubkey,
41+
spl_token_confidential_transfer_proof_extraction::verify_and_extract_context,
4242
};
4343

4444
/// Processes an [InitializeConfidentialTransferFeeConfig] instruction.

token/program-2022/src/lib.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ pub mod onchain;
1414
pub mod pod;
1515
pub mod pod_instruction;
1616
pub mod processor;
17-
pub mod proof;
1817
#[cfg(feature = "serde-traits")]
1918
pub mod serialization;
2019
pub mod state;
@@ -130,3 +129,13 @@ pub fn check_system_program_account(system_program_id: &Pubkey) -> ProgramResult
130129
}
131130
Ok(())
132131
}
132+
133+
/// Checks if the supplied program ID is that of the ElGamal registry program
134+
pub fn check_elgamal_registry_program_account(
135+
elgamal_registry_account_program_id: &Pubkey,
136+
) -> ProgramResult {
137+
if elgamal_registry_account_program_id != &spl_elgamal_registry::id() {
138+
return Err(ProgramError::IncorrectProgramId);
139+
}
140+
Ok(())
141+
}

0 commit comments

Comments
 (0)