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

Commit 367d367

Browse files
committed
refactor zk elgamal instruction data logic into a separate file
1 parent e8b3ea1 commit 367d367

File tree

11 files changed

+193
-190
lines changed

11 files changed

+193
-190
lines changed

token/client/src/token.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ use {
6262
},
6363
state::{Account, AccountState, Mint, Multisig},
6464
},
65-
spl_token_confidential_transfer_proof_extraction::{
65+
spl_token_confidential_transfer_proof_extraction::instruction::{
6666
zk_proof_type_to_instruction, ProofData, ProofLocation,
6767
},
6868
spl_token_confidential_transfer_proof_generation::{

token/confidential-transfer/elgamal-registry/src/instruction.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use {
99
solana_zk_sdk::zk_elgamal_proof_program::{
1010
instruction::ProofInstruction, proof_data::PubkeyValidityProofData,
1111
},
12-
spl_token_confidential_transfer_proof_extraction::{ProofData, ProofLocation},
12+
spl_token_confidential_transfer_proof_extraction::instruction::{ProofData, ProofLocation},
1313
};
1414

1515
#[derive(Clone, Debug, PartialEq)]

token/confidential-transfer/elgamal-registry/src/processor.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ use {
2020
PubkeyValidityProofContext, PubkeyValidityProofData,
2121
},
2222
spl_pod::bytemuck::pod_from_bytes_mut,
23-
spl_token_confidential_transfer_proof_extraction::verify_and_extract_context,
23+
spl_token_confidential_transfer_proof_extraction::instruction::verify_and_extract_context,
2424
};
2525

2626
/// Processes `CreateRegistry` instruction
Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
//! Utility functions to simplify the handling of ZK ElGamal proof program
2+
//! instruction data in SPL crates
3+
4+
use {
5+
bytemuck::Pod,
6+
solana_program::{
7+
account_info::{next_account_info, AccountInfo},
8+
entrypoint::ProgramResult,
9+
instruction::Instruction,
10+
msg,
11+
program_error::ProgramError,
12+
pubkey::Pubkey,
13+
sysvar::instructions::get_instruction_relative,
14+
},
15+
solana_zk_sdk::zk_elgamal_proof_program::{
16+
self,
17+
instruction::ProofInstruction,
18+
proof_data::{ProofType, ZkProofData},
19+
state::ProofContextState,
20+
},
21+
spl_pod::bytemuck::pod_from_bytes,
22+
std::{num::NonZeroI8, slice::Iter},
23+
};
24+
25+
/// Checks that the supplied program ID is correct for the ZK ElGamal proof
26+
/// program
27+
pub fn check_zk_elgamal_proof_program_account(
28+
zk_elgamal_proof_program_id: &Pubkey,
29+
) -> ProgramResult {
30+
if zk_elgamal_proof_program_id != &solana_zk_sdk::zk_elgamal_proof_program::id() {
31+
return Err(ProgramError::IncorrectProgramId);
32+
}
33+
Ok(())
34+
}
35+
36+
/// If a proof is to be read from a record account, the proof instruction data
37+
/// must be 5 bytes: 1 byte for the proof type and 4 bytes for the u32 offset
38+
const INSTRUCTION_DATA_LENGTH_WITH_RECORD_ACCOUNT: usize = 5;
39+
40+
/// Decodes the proof context data associated with a zero-knowledge proof
41+
/// instruction.
42+
pub fn decode_proof_instruction_context<T: Pod + ZkProofData<U>, U: Pod>(
43+
account_info_iter: &mut Iter<'_, AccountInfo<'_>>,
44+
expected: ProofInstruction,
45+
instruction: &Instruction,
46+
) -> Result<U, ProgramError> {
47+
if instruction.program_id != zk_elgamal_proof_program::id()
48+
|| ProofInstruction::instruction_type(&instruction.data) != Some(expected)
49+
{
50+
msg!("Unexpected proof instruction");
51+
return Err(ProgramError::InvalidInstructionData);
52+
}
53+
54+
// If the instruction data size is exactly 5 bytes, then interpret it as an
55+
// offset byte for a record account. This behavior is identical to that of
56+
// the ZK ElGamal proof program.
57+
if instruction.data.len() == INSTRUCTION_DATA_LENGTH_WITH_RECORD_ACCOUNT {
58+
let record_account = next_account_info(account_info_iter)?;
59+
60+
// first byte is the proof type
61+
let start_offset = u32::from_le_bytes(instruction.data[1..].try_into().unwrap()) as usize;
62+
let end_offset = start_offset
63+
.checked_add(std::mem::size_of::<T>())
64+
.ok_or(ProgramError::InvalidAccountData)?;
65+
66+
let record_account_data = record_account.data.borrow();
67+
let raw_proof_data = record_account_data
68+
.get(start_offset..end_offset)
69+
.ok_or(ProgramError::AccountDataTooSmall)?;
70+
71+
bytemuck::try_from_bytes::<T>(raw_proof_data)
72+
.map(|proof_data| *ZkProofData::context_data(proof_data))
73+
.map_err(|_| ProgramError::InvalidAccountData)
74+
} else {
75+
ProofInstruction::proof_data::<T, U>(&instruction.data)
76+
.map(|proof_data| *ZkProofData::context_data(proof_data))
77+
.ok_or(ProgramError::InvalidInstructionData)
78+
}
79+
}
80+
81+
/// A proof location type meant to be used for arguments to instruction
82+
/// constructors.
83+
#[derive(Clone, Copy)]
84+
pub enum ProofLocation<'a, T> {
85+
/// The proof is included in the same transaction of a corresponding
86+
/// token-2022 instruction.
87+
InstructionOffset(NonZeroI8, ProofData<'a, T>),
88+
/// The proof is pre-verified into a context state account.
89+
ContextStateAccount(&'a Pubkey),
90+
}
91+
92+
impl<'a, T> ProofLocation<'a, T> {
93+
/// Returns true if the proof location is an instruction offset
94+
pub fn is_instruction_offset(&self) -> bool {
95+
match self {
96+
Self::InstructionOffset(_, _) => true,
97+
Self::ContextStateAccount(_) => false,
98+
}
99+
}
100+
}
101+
102+
/// A proof data type to distinguish between proof data included as part of
103+
/// zk-token proof instruction data and proof data stored in a record account.
104+
#[derive(Clone, Copy)]
105+
pub enum ProofData<'a, T> {
106+
/// The proof data
107+
InstructionData(&'a T),
108+
/// The address of a record account containing the proof data and its byte
109+
/// offset
110+
RecordAccount(&'a Pubkey, u32),
111+
}
112+
113+
/// Verify zero-knowledge proof and return the corresponding proof context.
114+
pub fn verify_and_extract_context<'a, T: Pod + ZkProofData<U>, U: Pod>(
115+
account_info_iter: &mut Iter<'_, AccountInfo<'a>>,
116+
proof_instruction_offset: i64,
117+
sysvar_account_info: Option<&'_ AccountInfo<'a>>,
118+
) -> Result<U, ProgramError> {
119+
if proof_instruction_offset == 0 {
120+
// interpret `account_info` as a context state account
121+
let context_state_account_info = next_account_info(account_info_iter)?;
122+
check_zk_elgamal_proof_program_account(context_state_account_info.owner)?;
123+
let context_state_account_data = context_state_account_info.data.borrow();
124+
let context_state = pod_from_bytes::<ProofContextState<U>>(&context_state_account_data)?;
125+
126+
if context_state.proof_type != T::PROOF_TYPE.into() {
127+
return Err(ProgramError::InvalidInstructionData);
128+
}
129+
130+
Ok(context_state.proof_context)
131+
} else {
132+
// if sysvar account is not provided, then get the sysvar account
133+
let sysvar_account_info = if let Some(sysvar_account_info) = sysvar_account_info {
134+
sysvar_account_info
135+
} else {
136+
next_account_info(account_info_iter)?
137+
};
138+
let zkp_instruction =
139+
get_instruction_relative(proof_instruction_offset, sysvar_account_info)?;
140+
let expected_proof_type = zk_proof_type_to_instruction(T::PROOF_TYPE)?;
141+
Ok(decode_proof_instruction_context::<T, U>(
142+
account_info_iter,
143+
expected_proof_type,
144+
&zkp_instruction,
145+
)?)
146+
}
147+
}
148+
149+
/// Converts a zk proof type to a corresponding ZK ElGamal proof program
150+
/// instruction that verifies the proof.
151+
pub fn zk_proof_type_to_instruction(
152+
proof_type: ProofType,
153+
) -> Result<ProofInstruction, ProgramError> {
154+
match proof_type {
155+
ProofType::ZeroCiphertext => Ok(ProofInstruction::VerifyZeroCiphertext),
156+
ProofType::CiphertextCiphertextEquality => {
157+
Ok(ProofInstruction::VerifyCiphertextCiphertextEquality)
158+
}
159+
ProofType::PubkeyValidity => Ok(ProofInstruction::VerifyPubkeyValidity),
160+
ProofType::BatchedRangeProofU64 => Ok(ProofInstruction::VerifyBatchedRangeProofU64),
161+
ProofType::BatchedRangeProofU128 => Ok(ProofInstruction::VerifyBatchedRangeProofU128),
162+
ProofType::BatchedRangeProofU256 => Ok(ProofInstruction::VerifyBatchedRangeProofU256),
163+
ProofType::CiphertextCommitmentEquality => {
164+
Ok(ProofInstruction::VerifyCiphertextCommitmentEquality)
165+
}
166+
ProofType::GroupedCiphertext2HandlesValidity => {
167+
Ok(ProofInstruction::VerifyGroupedCiphertext2HandlesValidity)
168+
}
169+
ProofType::BatchedGroupedCiphertext2HandlesValidity => {
170+
Ok(ProofInstruction::VerifyBatchedGroupedCiphertext2HandlesValidity)
171+
}
172+
ProofType::PercentageWithCap => Ok(ProofInstruction::VerifyPercentageWithCap),
173+
ProofType::GroupedCiphertext3HandlesValidity => {
174+
Ok(ProofInstruction::VerifyGroupedCiphertext3HandlesValidity)
175+
}
176+
ProofType::BatchedGroupedCiphertext3HandlesValidity => {
177+
Ok(ProofInstruction::VerifyBatchedGroupedCiphertext3HandlesValidity)
178+
}
179+
ProofType::Uninitialized => Err(ProgramError::InvalidInstructionData),
180+
}
181+
}
Lines changed: 1 addition & 179 deletions
Original file line numberDiff line numberDiff line change
@@ -1,186 +1,8 @@
11
pub mod burn;
22
pub mod encryption;
33
pub mod errors;
4+
pub mod instruction;
45
pub mod mint;
56
pub mod transfer;
67
pub mod transfer_with_fee;
78
pub mod withdraw;
8-
9-
use {
10-
bytemuck::Pod,
11-
solana_program::{
12-
account_info::{next_account_info, AccountInfo},
13-
entrypoint::ProgramResult,
14-
instruction::Instruction,
15-
msg,
16-
program_error::ProgramError,
17-
pubkey::Pubkey,
18-
sysvar::instructions::get_instruction_relative,
19-
},
20-
solana_zk_sdk::zk_elgamal_proof_program::{
21-
self,
22-
instruction::ProofInstruction,
23-
proof_data::{ProofType, ZkProofData},
24-
state::ProofContextState,
25-
},
26-
spl_pod::bytemuck::pod_from_bytes,
27-
std::{num::NonZeroI8, slice::Iter},
28-
};
29-
30-
/// Checks that the supplied program ID is correct for the ZK ElGamal proof
31-
/// program
32-
pub fn check_zk_elgamal_proof_program_account(
33-
zk_elgamal_proof_program_id: &Pubkey,
34-
) -> ProgramResult {
35-
if zk_elgamal_proof_program_id != &solana_zk_sdk::zk_elgamal_proof_program::id() {
36-
return Err(ProgramError::IncorrectProgramId);
37-
}
38-
Ok(())
39-
}
40-
41-
/// If a proof is to be read from a record account, the proof instruction data
42-
/// must be 5 bytes: 1 byte for the proof type and 4 bytes for the u32 offset
43-
const INSTRUCTION_DATA_LENGTH_WITH_RECORD_ACCOUNT: usize = 5;
44-
45-
/// Decodes the proof context data associated with a zero-knowledge proof
46-
/// instruction.
47-
pub fn decode_proof_instruction_context<T: Pod + ZkProofData<U>, U: Pod>(
48-
account_info_iter: &mut Iter<'_, AccountInfo<'_>>,
49-
expected: ProofInstruction,
50-
instruction: &Instruction,
51-
) -> Result<U, ProgramError> {
52-
if instruction.program_id != zk_elgamal_proof_program::id()
53-
|| ProofInstruction::instruction_type(&instruction.data) != Some(expected)
54-
{
55-
msg!("Unexpected proof instruction");
56-
return Err(ProgramError::InvalidInstructionData);
57-
}
58-
59-
// If the instruction data size is exactly 5 bytes, then interpret it as an
60-
// offset byte for a record account. This behavior is identical to that of
61-
// the ZK ElGamal proof program.
62-
if instruction.data.len() == INSTRUCTION_DATA_LENGTH_WITH_RECORD_ACCOUNT {
63-
let record_account = next_account_info(account_info_iter)?;
64-
65-
// first byte is the proof type
66-
let start_offset = u32::from_le_bytes(instruction.data[1..].try_into().unwrap()) as usize;
67-
let end_offset = start_offset
68-
.checked_add(std::mem::size_of::<T>())
69-
.ok_or(ProgramError::InvalidAccountData)?;
70-
71-
let record_account_data = record_account.data.borrow();
72-
let raw_proof_data = record_account_data
73-
.get(start_offset..end_offset)
74-
.ok_or(ProgramError::AccountDataTooSmall)?;
75-
76-
bytemuck::try_from_bytes::<T>(raw_proof_data)
77-
.map(|proof_data| *ZkProofData::context_data(proof_data))
78-
.map_err(|_| ProgramError::InvalidAccountData)
79-
} else {
80-
ProofInstruction::proof_data::<T, U>(&instruction.data)
81-
.map(|proof_data| *ZkProofData::context_data(proof_data))
82-
.ok_or(ProgramError::InvalidInstructionData)
83-
}
84-
}
85-
86-
/// A proof location type meant to be used for arguments to instruction
87-
/// constructors.
88-
#[derive(Clone, Copy)]
89-
pub enum ProofLocation<'a, T> {
90-
/// The proof is included in the same transaction of a corresponding
91-
/// token-2022 instruction.
92-
InstructionOffset(NonZeroI8, ProofData<'a, T>),
93-
/// The proof is pre-verified into a context state account.
94-
ContextStateAccount(&'a Pubkey),
95-
}
96-
97-
impl<'a, T> ProofLocation<'a, T> {
98-
/// Returns true if the proof location is an instruction offset
99-
pub fn is_instruction_offset(&self) -> bool {
100-
match self {
101-
Self::InstructionOffset(_, _) => true,
102-
Self::ContextStateAccount(_) => false,
103-
}
104-
}
105-
}
106-
107-
/// A proof data type to distinguish between proof data included as part of
108-
/// zk-token proof instruction data and proof data stored in a record account.
109-
#[derive(Clone, Copy)]
110-
pub enum ProofData<'a, T> {
111-
/// The proof data
112-
InstructionData(&'a T),
113-
/// The address of a record account containing the proof data and its byte
114-
/// offset
115-
RecordAccount(&'a Pubkey, u32),
116-
}
117-
118-
/// Verify zero-knowledge proof and return the corresponding proof context.
119-
pub fn verify_and_extract_context<'a, T: Pod + ZkProofData<U>, U: Pod>(
120-
account_info_iter: &mut Iter<'_, AccountInfo<'a>>,
121-
proof_instruction_offset: i64,
122-
sysvar_account_info: Option<&'_ AccountInfo<'a>>,
123-
) -> Result<U, ProgramError> {
124-
if proof_instruction_offset == 0 {
125-
// interpret `account_info` as a context state account
126-
let context_state_account_info = next_account_info(account_info_iter)?;
127-
check_zk_elgamal_proof_program_account(context_state_account_info.owner)?;
128-
let context_state_account_data = context_state_account_info.data.borrow();
129-
let context_state = pod_from_bytes::<ProofContextState<U>>(&context_state_account_data)?;
130-
131-
if context_state.proof_type != T::PROOF_TYPE.into() {
132-
return Err(ProgramError::InvalidInstructionData);
133-
}
134-
135-
Ok(context_state.proof_context)
136-
} else {
137-
// if sysvar account is not provided, then get the sysvar account
138-
let sysvar_account_info = if let Some(sysvar_account_info) = sysvar_account_info {
139-
sysvar_account_info
140-
} else {
141-
next_account_info(account_info_iter)?
142-
};
143-
let zkp_instruction =
144-
get_instruction_relative(proof_instruction_offset, sysvar_account_info)?;
145-
let expected_proof_type = zk_proof_type_to_instruction(T::PROOF_TYPE)?;
146-
Ok(decode_proof_instruction_context::<T, U>(
147-
account_info_iter,
148-
expected_proof_type,
149-
&zkp_instruction,
150-
)?)
151-
}
152-
}
153-
154-
/// Converts a zk proof type to a corresponding ZK ElGamal proof program
155-
/// instruction that verifies the proof.
156-
pub fn zk_proof_type_to_instruction(
157-
proof_type: ProofType,
158-
) -> Result<ProofInstruction, ProgramError> {
159-
match proof_type {
160-
ProofType::ZeroCiphertext => Ok(ProofInstruction::VerifyZeroCiphertext),
161-
ProofType::CiphertextCiphertextEquality => {
162-
Ok(ProofInstruction::VerifyCiphertextCiphertextEquality)
163-
}
164-
ProofType::PubkeyValidity => Ok(ProofInstruction::VerifyPubkeyValidity),
165-
ProofType::BatchedRangeProofU64 => Ok(ProofInstruction::VerifyBatchedRangeProofU64),
166-
ProofType::BatchedRangeProofU128 => Ok(ProofInstruction::VerifyBatchedRangeProofU128),
167-
ProofType::BatchedRangeProofU256 => Ok(ProofInstruction::VerifyBatchedRangeProofU256),
168-
ProofType::CiphertextCommitmentEquality => {
169-
Ok(ProofInstruction::VerifyCiphertextCommitmentEquality)
170-
}
171-
ProofType::GroupedCiphertext2HandlesValidity => {
172-
Ok(ProofInstruction::VerifyGroupedCiphertext2HandlesValidity)
173-
}
174-
ProofType::BatchedGroupedCiphertext2HandlesValidity => {
175-
Ok(ProofInstruction::VerifyBatchedGroupedCiphertext2HandlesValidity)
176-
}
177-
ProofType::PercentageWithCap => Ok(ProofInstruction::VerifyPercentageWithCap),
178-
ProofType::GroupedCiphertext3HandlesValidity => {
179-
Ok(ProofInstruction::VerifyGroupedCiphertext3HandlesValidity)
180-
}
181-
ProofType::BatchedGroupedCiphertext3HandlesValidity => {
182-
Ok(ProofInstruction::VerifyBatchedGroupedCiphertext3HandlesValidity)
183-
}
184-
ProofType::Uninitialized => Err(ProgramError::InvalidInstructionData),
185-
}
186-
}

0 commit comments

Comments
 (0)