[zk-token-proof] Add functionality to read proof from accounts instead of instruction data (#34750)

* add functionality to read proof from accounts instead of instruction data

* update add tests

* clippy

* clarify instruction data discriminator

* avoid cloning entire proof data

* Update programs/zk-token-proof/src/lib.rs

Co-authored-by: Jon C <me@jonc.dev>

* update `PROOF_OFFSET_LENGTH` to `INSTRUCTION_DATA_LENGTH_WITH_PROOF_ACCOUNT`

* update instruction docs

* add feature gate

* Update sdk/src/feature_set.rs

Co-authored-by: Jon C <me@jonc.dev>

* update feature name `enable_zk_from_account` to `enable_zk_proof_from_account`

* Apply suggestions from code review

Co-authored-by: Jon C <me@jonc.dev>

* clarify the instruction data length more precisely

---------

Co-authored-by: Jon C <me@jonc.dev>
This commit is contained in:
samkim-crypto 2024-01-13 08:15:44 +09:00 committed by GitHub
parent 51eaa2b9cc
commit b222fdf3d7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 514 additions and 136 deletions

View File

@ -1,9 +1,11 @@
use {
bytemuck::Pod,
bytemuck::{bytes_of, Pod},
curve25519_dalek::scalar::Scalar,
solana_program_test::*,
solana_sdk::{
account::Account,
instruction::InstructionError,
pubkey::Pubkey,
signature::Signer,
signer::keypair::Keypair,
system_instruction,
@ -60,6 +62,14 @@ async fn test_zero_balance() {
)
.await;
test_verify_proof_from_account_with_context(
ProofInstruction::VerifyZeroBalance,
size_of::<ProofContextState<ZeroBalanceProofContext>>(),
&success_proof_data,
&fail_proof_data,
)
.await;
test_verify_proof_with_context(
ProofInstruction::VerifyZeroBalance,
size_of::<ProofContextState<ZeroBalanceProofContext>>(),
@ -128,6 +138,13 @@ async fn test_ciphertext_ciphertext_equality() {
)
.await;
test_verify_proof_from_account_with_context(
ProofInstruction::VerifyCiphertextCiphertextEquality,
size_of::<ProofContextState<CiphertextCiphertextEqualityProofContext>>(),
&success_proof_data,
&fail_proof_data,
)
.await;
test_close_context_state(
ProofInstruction::VerifyCiphertextCiphertextEquality,
size_of::<ProofContextState<CiphertextCiphertextEqualityProofContext>>(),
@ -186,6 +203,14 @@ async fn test_transfer() {
)
.await;
test_verify_proof_from_account_with_context(
ProofInstruction::VerifyTransfer,
size_of::<ProofContextState<TransferProofContext>>(),
&success_proof_data,
&fail_proof_data,
)
.await;
test_close_context_state(
ProofInstruction::VerifyTransfer,
size_of::<ProofContextState<TransferProofContext>>(),
@ -256,6 +281,14 @@ async fn test_transfer_with_fee() {
)
.await;
test_verify_proof_from_account_with_context(
ProofInstruction::VerifyTransferWithFee,
size_of::<ProofContextState<TransferWithFeeProofContext>>(),
&success_proof_data,
&fail_proof_data,
)
.await;
test_close_context_state(
ProofInstruction::VerifyTransferWithFee,
size_of::<ProofContextState<TransferWithFeeProofContext>>(),
@ -307,6 +340,14 @@ async fn test_withdraw() {
)
.await;
test_verify_proof_from_account_with_context(
ProofInstruction::VerifyWithdraw,
size_of::<ProofContextState<WithdrawProofContext>>(),
&success_proof_data,
&fail_proof_data,
)
.await;
test_close_context_state(
ProofInstruction::VerifyWithdraw,
size_of::<ProofContextState<WithdrawProofContext>>(),
@ -342,6 +383,14 @@ async fn test_pubkey_validity() {
)
.await;
test_verify_proof_from_account_with_context(
ProofInstruction::VerifyPubkeyValidity,
size_of::<ProofContextState<PubkeyValidityProofContext>>(),
&success_proof_data,
&fail_proof_data,
)
.await;
test_close_context_state(
ProofInstruction::VerifyPubkeyValidity,
size_of::<ProofContextState<PubkeyValidityProofContext>>(),
@ -375,6 +424,14 @@ async fn test_range_proof_u64() {
)
.await;
test_verify_proof_from_account_with_context(
ProofInstruction::VerifyRangeProofU64,
size_of::<ProofContextState<RangeProofContext>>(),
&success_proof_data,
&fail_proof_data,
)
.await;
test_close_context_state(
ProofInstruction::VerifyRangeProofU64,
size_of::<ProofContextState<RangeProofContext>>(),
@ -423,6 +480,14 @@ async fn test_batched_range_proof_u64() {
)
.await;
test_verify_proof_from_account_with_context(
ProofInstruction::VerifyBatchedRangeProofU64,
size_of::<ProofContextState<BatchedRangeProofContext>>(),
&success_proof_data,
&fail_proof_data,
)
.await;
test_close_context_state(
ProofInstruction::VerifyBatchedRangeProofU64,
size_of::<ProofContextState<BatchedRangeProofContext>>(),
@ -471,6 +536,14 @@ async fn test_batched_range_proof_u128() {
)
.await;
test_verify_proof_from_account_with_context(
ProofInstruction::VerifyBatchedRangeProofU128,
size_of::<ProofContextState<BatchedRangeProofContext>>(),
&success_proof_data,
&fail_proof_data,
)
.await;
test_close_context_state(
ProofInstruction::VerifyBatchedRangeProofU128,
size_of::<ProofContextState<BatchedRangeProofContext>>(),
@ -523,6 +596,14 @@ async fn test_batched_range_proof_u256() {
)
.await;
test_verify_proof_from_account_with_context(
ProofInstruction::VerifyBatchedRangeProofU256,
size_of::<ProofContextState<BatchedRangeProofContext>>(),
&success_proof_data,
&fail_proof_data,
)
.await;
test_close_context_state(
ProofInstruction::VerifyBatchedRangeProofU256,
size_of::<ProofContextState<BatchedRangeProofContext>>(),
@ -575,6 +656,14 @@ async fn test_ciphertext_commitment_equality() {
)
.await;
test_verify_proof_from_account_with_context(
ProofInstruction::VerifyCiphertextCommitmentEquality,
size_of::<ProofContextState<CiphertextCommitmentEqualityProofContext>>(),
&success_proof_data,
&fail_proof_data,
)
.await;
test_close_context_state(
ProofInstruction::VerifyCiphertextCommitmentEquality,
size_of::<ProofContextState<CiphertextCommitmentEqualityProofContext>>(),
@ -630,6 +719,14 @@ async fn test_grouped_ciphertext_2_handles_validity() {
)
.await;
test_verify_proof_from_account_with_context(
ProofInstruction::VerifyGroupedCiphertext2HandlesValidity,
size_of::<ProofContextState<GroupedCiphertext2HandlesValidityProofContext>>(),
&success_proof_data,
&fail_proof_data,
)
.await;
test_close_context_state(
ProofInstruction::VerifyGroupedCiphertext2HandlesValidity,
size_of::<ProofContextState<GroupedCiphertext2HandlesValidityProofContext>>(),
@ -697,6 +794,14 @@ async fn test_batched_grouped_ciphertext_2_handles_validity() {
)
.await;
test_verify_proof_from_account_with_context(
ProofInstruction::VerifyBatchedGroupedCiphertext2HandlesValidity,
size_of::<ProofContextState<BatchedGroupedCiphertext2HandlesValidityProofContext>>(),
&success_proof_data,
&fail_proof_data,
)
.await;
test_close_context_state(
ProofInstruction::VerifyBatchedGroupedCiphertext2HandlesValidity,
size_of::<ProofContextState<BatchedGroupedCiphertext2HandlesValidityProofContext>>(),
@ -766,6 +871,14 @@ async fn test_fee_sigma() {
)
.await;
test_verify_proof_from_account_with_context(
ProofInstruction::VerifyFeeSigma,
size_of::<ProofContextState<FeeSigmaProofContext>>(),
&success_proof_data,
&fail_proof_data,
)
.await;
test_close_context_state(
ProofInstruction::VerifyFeeSigma,
size_of::<ProofContextState<FeeSigmaProofContext>>(),
@ -782,7 +895,28 @@ async fn test_verify_proof_without_context<T, U>(
T: Pod + ZkProofData<U>,
U: Pod,
{
let mut context = ProgramTest::default().start_with_context().await;
let mut program_test = ProgramTest::default();
let success_proof_account = Pubkey::new_unique();
program_test.add_account(
success_proof_account,
Account {
lamports: 1_000_000_000,
data: bytes_of(success_proof_data).to_vec(),
owner: Pubkey::new_unique(),
..Account::default()
},
);
let fail_proof_account = Pubkey::new_unique();
program_test.add_account(
fail_proof_account,
Account {
lamports: 1_000_000_000,
data: bytes_of(fail_proof_data).to_vec(),
owner: Pubkey::new_unique(),
..Account::default()
},
);
let mut context = program_test.start_with_context().await;
let client = &mut context.banks_client;
let payer = &context.payer;
@ -840,6 +974,36 @@ async fn test_verify_proof_without_context<T, U>(
TransactionError::InstructionError(0, InstructionError::InvalidInstructionData)
);
}
// verify a valid proof from an account
let instruction =
vec![proof_instruction.encode_verify_proof_from_account(None, &success_proof_account, 0)];
let transaction = Transaction::new_signed_with_payer(
&instruction,
Some(&payer.pubkey()),
&[payer],
recent_blockhash,
);
client.process_transaction(transaction).await.unwrap();
// try to verify an invalid proof from an account
let instruction =
vec![proof_instruction.encode_verify_proof_from_account(None, &fail_proof_account, 0)];
let transaction = Transaction::new_signed_with_payer(
&instruction,
Some(&payer.pubkey()),
&[payer],
recent_blockhash,
);
client
.process_transaction(transaction)
.await
.unwrap_err()
.unwrap();
assert_eq!(
err,
TransactionError::InstructionError(0, InstructionError::InvalidInstructionData)
)
}
async fn test_verify_proof_with_context<T, U>(
@ -1045,6 +1209,157 @@ async fn test_verify_proof_with_context<T, U>(
client.process_transaction(transaction).await.unwrap();
}
async fn test_verify_proof_from_account_with_context<T, U>(
instruction_type: ProofInstruction,
space: usize,
success_proof_data: &T,
fail_proof_data: &T,
) where
T: Pod + ZkProofData<U>,
U: Pod,
{
let mut program_test = ProgramTest::default();
let success_proof_account = Pubkey::new_unique();
program_test.add_account(
success_proof_account,
Account {
lamports: 1_000_000_000,
data: bytes_of(success_proof_data).to_vec(),
owner: Pubkey::new_unique(),
..Account::default()
},
);
let fail_proof_account = Pubkey::new_unique();
program_test.add_account(
fail_proof_account,
Account {
lamports: 1_000_000_000,
data: bytes_of(fail_proof_data).to_vec(),
owner: Pubkey::new_unique(),
..Account::default()
},
);
let mut context = program_test.start_with_context().await;
let rent = context.banks_client.get_rent().await.unwrap();
let client = &mut context.banks_client;
let payer = &context.payer;
let recent_blockhash = context.last_blockhash;
let context_state_account = Keypair::new();
let context_state_authority = Keypair::new();
let context_state_info = ContextStateInfo {
context_state_account: &context_state_account.pubkey(),
context_state_authority: &context_state_authority.pubkey(),
};
// try to create proof context state with an invalid proof
let instructions = vec![
system_instruction::create_account(
&payer.pubkey(),
&context_state_account.pubkey(),
rent.minimum_balance(space),
space as u64,
&zk_token_proof_program::id(),
),
instruction_type.encode_verify_proof_from_account(
Some(context_state_info),
&fail_proof_account,
0,
),
];
let transaction = Transaction::new_signed_with_payer(
&instructions,
Some(&payer.pubkey()),
&[payer, &context_state_account],
recent_blockhash,
);
let err = client
.process_transaction(transaction)
.await
.unwrap_err()
.unwrap();
assert_eq!(
err,
TransactionError::InstructionError(1, InstructionError::InvalidInstructionData)
);
// successfully create a proof context state
let instructions = vec![
system_instruction::create_account(
&payer.pubkey(),
&context_state_account.pubkey(),
rent.minimum_balance(space),
space as u64,
&zk_token_proof_program::id(),
),
instruction_type.encode_verify_proof_from_account(
Some(context_state_info),
&success_proof_account,
0,
),
];
let transaction = Transaction::new_signed_with_payer(
&instructions,
Some(&payer.pubkey()),
&[payer, &context_state_account],
recent_blockhash,
);
client.process_transaction(transaction).await.unwrap();
// try overwriting the context state
let instructions = vec![instruction_type.encode_verify_proof_from_account(
Some(context_state_info),
&success_proof_account,
0,
)];
let transaction = Transaction::new_signed_with_payer(
&instructions,
Some(&payer.pubkey()),
&[payer],
recent_blockhash,
);
let err = client
.process_transaction(transaction)
.await
.unwrap_err()
.unwrap();
assert_eq!(
err,
TransactionError::InstructionError(0, InstructionError::AccountAlreadyInitialized)
);
// self-owned context state account
let context_state_account_and_authority = Keypair::new();
let context_state_info = ContextStateInfo {
context_state_account: &context_state_account_and_authority.pubkey(),
context_state_authority: &context_state_account_and_authority.pubkey(),
};
let instructions = vec![
system_instruction::create_account(
&payer.pubkey(),
&context_state_account_and_authority.pubkey(),
rent.minimum_balance(space),
space as u64,
&zk_token_proof_program::id(),
),
instruction_type.encode_verify_proof_from_account(
Some(context_state_info),
&success_proof_account,
0,
),
];
let transaction = Transaction::new_signed_with_payer(
&instructions,
Some(&payer.pubkey()),
&[payer, &context_state_account_and_authority],
recent_blockhash,
);
client.process_transaction(transaction).await.unwrap();
}
async fn test_close_context_state<T, U>(
instruction_type: ProofInstruction,
space: usize,

View File

@ -32,6 +32,8 @@ pub const VERIFY_GROUPED_CIPHERTEXT_2_HANDLES_VALIDITY_COMPUTE_UNITS: u64 = 6_40
pub const VERIFY_BATCHED_GROUPED_CIPHERTEXT_2_HANDLES_VALIDITY_COMPUTE_UNITS: u64 = 13_000;
pub const VERIFY_FEE_SIGMA_COMPUTE_UNITS: u64 = 6_500;
const INSTRUCTION_DATA_LENGTH_WITH_PROOF_ACCOUNT: usize = 5;
fn process_verify_proof<T, U>(invoke_context: &mut InvokeContext) -> Result<(), InstructionError>
where
T: Pod + ZkProofData<U>,
@ -40,24 +42,75 @@ where
let transaction_context = &invoke_context.transaction_context;
let instruction_context = transaction_context.get_current_instruction_context()?;
let instruction_data = instruction_context.get_instruction_data();
let proof_data = ProofInstruction::proof_data::<T, U>(instruction_data).ok_or_else(|| {
ic_msg!(invoke_context, "invalid proof data");
InstructionError::InvalidInstructionData
})?;
proof_data.verify_proof().map_err(|err| {
ic_msg!(invoke_context, "proof_verification failed: {:?}", err);
InstructionError::InvalidInstructionData
})?;
// number of accessed accounts so far
let mut accessed_accounts = 0_u16;
// create context state if accounts are provided with the instruction
if instruction_context.get_number_of_instruction_accounts() > 0 {
// if instruction data is exactly 5 bytes, then read proof from an account
let context_data = if instruction_data.len() == INSTRUCTION_DATA_LENGTH_WITH_PROOF_ACCOUNT {
if !invoke_context
.feature_set
.is_active(&feature_set::enable_zk_proof_from_account::id())
{
return Err(InstructionError::InvalidInstructionData);
}
let proof_data_account = instruction_context
.try_borrow_instruction_account(transaction_context, accessed_accounts)?;
accessed_accounts = accessed_accounts.checked_add(1).unwrap();
let proof_data_offset = u32::from_le_bytes(
// the first byte is the instruction discriminator
instruction_data[1..INSTRUCTION_DATA_LENGTH_WITH_PROOF_ACCOUNT]
.try_into()
.map_err(|_| InstructionError::InvalidInstructionData)?,
);
let proof_data_start: usize = proof_data_offset
.try_into()
.map_err(|_| InstructionError::InvalidInstructionData)?;
let proof_data_end = proof_data_start
.checked_add(std::mem::size_of::<T>())
.ok_or(InstructionError::InvalidInstructionData)?;
let proof_data_raw = proof_data_account
.get_data()
.get(proof_data_start..proof_data_end)
.ok_or(InstructionError::InvalidAccountData)?;
let proof_data = bytemuck::try_from_bytes::<T>(proof_data_raw).map_err(|_| {
ic_msg!(invoke_context, "invalid proof data");
InstructionError::InvalidInstructionData
})?;
proof_data.verify_proof().map_err(|err| {
ic_msg!(invoke_context, "proof verification failed: {:?}", err);
InstructionError::InvalidInstructionData
})?;
*proof_data.context_data()
} else {
let proof_data =
ProofInstruction::proof_data::<T, U>(instruction_data).ok_or_else(|| {
ic_msg!(invoke_context, "invalid proof data");
InstructionError::InvalidInstructionData
})?;
proof_data.verify_proof().map_err(|err| {
ic_msg!(invoke_context, "proof_verification failed: {:?}", err);
InstructionError::InvalidInstructionData
})?;
*proof_data.context_data()
};
// create context state if additional accounts are provided with the instruction
if instruction_context.get_number_of_instruction_accounts() > accessed_accounts {
let context_state_authority = *instruction_context
.try_borrow_instruction_account(transaction_context, 1)?
.try_borrow_instruction_account(
transaction_context,
accessed_accounts.checked_add(1).unwrap(),
)?
.get_key();
let mut proof_context_account =
instruction_context.try_borrow_instruction_account(transaction_context, 0)?;
let mut proof_context_account = instruction_context
.try_borrow_instruction_account(transaction_context, accessed_accounts)?;
if *proof_context_account.get_owner() != id() {
return Err(InstructionError::InvalidAccountOwner);
@ -70,11 +123,8 @@ where
return Err(InstructionError::AccountAlreadyInitialized);
}
let context_state_data = ProofContextState::encode(
&context_state_authority,
T::PROOF_TYPE,
proof_data.context_data(),
);
let context_state_data =
ProofContextState::encode(&context_state_authority, T::PROOF_TYPE, &context_data);
if proof_context_account.get_data().len() != context_state_data.len() {
return Err(InstructionError::InvalidAccountData);

View File

@ -760,6 +760,10 @@ pub mod deprecate_executable_meta_update_in_bpf_loader {
solana_sdk::declare_id!("k6uR1J9VtKJnTukBV2Eo15BEy434MBg8bT6hHQgmU8v");
}
pub mod enable_zk_proof_from_account {
solana_sdk::declare_id!("zkiTNuzBKxrCLMKehzuQeKZyLtX2yvFcEKMML8nExU8");
}
lazy_static! {
/// Map of feature identifiers to user-visible description
pub static ref FEATURE_NAMES: HashMap<Pubkey, &'static str> = [
@ -945,6 +949,7 @@ lazy_static! {
(merkle_conflict_duplicate_proofs::id(), "generate duplicate proofs for merkle root conflicts #34270"),
(disable_bpf_loader_instructions::id(), "disable bpf loader management instructions #34194"),
(deprecate_executable_meta_update_in_bpf_loader::id(), "deprecate executable meta flag update in bpf loader #34194"),
(enable_zk_proof_from_account::id(), "Enable zk token proof program to read proof from accounts instead of instruction data #34750"),
/*************** ADD NEW FEATURES HERE ***************/
]
.iter()

View File

@ -6,11 +6,21 @@
//! Each proof verification instruction verifies a certain type of zero-knowledge proof. These
//! instructions are processed by the program in two steps:
//! 1. The program verifies the zero-knowledge proof.
//! 2. The program optionally stores the context component of the instruction data to a
//! 2. The program optionally stores the context component of the zero-knowledge proof to a
//! dedicated [`context-state`] account.
//! If no accounts are provided with the instruction, the program simply verifies the proofs. If
//! accounts are provided with the instruction, then the program writes the context data to the
//! specified context-state account.
//!
//! In step 1, the zero-knowledge proof can be included directly as the instruction data or
//! pre-written to an account. The program determines whether the proof is provided as instruction
//! data or pre-written to an account by inspecting the length of the data. If the instruction data
//! is exactly 5 bytes (instruction disciminator + unsigned 32-bit integer), then the program
//! assumes that the first account provided with the instruction contains the zero-knowledge proof
//! and verifies the account data at the offset specified in the instruction data. Otherwise, the
//! program assumes that the zero-knowledge proof is provided as part of the instruction data.
//!
//! In step 2, the program determines whether to create a context-state account by inspecting the
//! number of accounts provided with the instruction. If two additional accounts are provided with
//! the instruction after verifying the zero-knowledge proof, then the program writes the context data to
//! the specified context-state account.
//!
//! NOTE: A context-state account must be pre-allocated to the exact size of the context data that
//! is expected for a proof type before it is included in a proof verification instruction.
@ -54,15 +64,13 @@ pub enum ProofInstruction {
///
/// Accounts expected by this instruction:
///
/// * Creating a proof context account
/// 0. `[writable]` The proof context account
/// 1. `[]` The proof context account owner
/// 0. `[]` (Optional) Account to read the proof from
/// 1. `[writable]` (Optional) The proof context account
/// 2. `[]` (Optional) The proof context account owner
///
/// * Otherwise
/// None
///
/// Data expected by this instruction:
/// `ZeroBalanceProofData`
/// The instruction expects either:
/// i. `ZeroBalanceProofData` if proof is provided as instruction data
/// ii. `u32` byte offset if proof is provided as an account
///
VerifyZeroBalance,
@ -73,15 +81,13 @@ pub enum ProofInstruction {
///
/// Accounts expected by this instruction:
///
/// * Creating a proof context account
/// 0. `[writable]` The proof context account
/// 1. `[]` The proof context account owner
/// 0. `[]` (Optional) Account to read the proof from
/// 1. `[writable]` (Optional) The proof context account
/// 2. `[]` (Optional) The proof context account owner
///
/// * Otherwise
/// None
///
/// Data expected by this instruction:
/// `WithdrawData`
/// The instruction expects either:
/// i. `WithdrawData` if proof is provided as instruction data
/// ii. `u32` byte offset if proof is provided as an account
///
VerifyWithdraw,
@ -92,15 +98,13 @@ pub enum ProofInstruction {
///
/// Accounts expected by this instruction:
///
/// * Creating a proof context account
/// 0. `[writable]` The proof context account
/// 1. `[]` The proof context account owner
/// 0. `[]` (Optional) Account to read the proof from
/// 1. `[writable]` (Optional) The proof context account
/// 2. `[]` (Optional) The proof context account owner
///
/// * Otherwise
/// None
///
/// Data expected by this instruction:
/// `CiphertextCiphertextEqualityProofData`
/// The instruction expects either:
/// i. `CiphertextCiphertextEqualityProofData` if proof is provided as instruction data
/// ii. `u32` byte offset if proof is provided as an account
///
VerifyCiphertextCiphertextEquality,
@ -111,15 +115,13 @@ pub enum ProofInstruction {
///
/// Accounts expected by this instruction:
///
/// * Creating a proof context account
/// 0. `[writable]` The proof context account
/// 1. `[]` The proof context account owner
/// 0. `[]` (Optional) Account to read the proof from
/// 1. `[writable]` (Optional) The proof context account
/// 2. `[]` (Optional) The proof context account owner
///
/// * Otherwise
/// None
///
/// Data expected by this instruction:
/// `TransferData`
/// The instruction expects either:
/// i. `TransferData` if proof is provided as instruction data
/// ii. `u32` byte offset if proof is provided as an account
///
VerifyTransfer,
@ -130,15 +132,13 @@ pub enum ProofInstruction {
///
/// Accounts expected by this instruction:
///
/// * Creating a proof context account
/// 0. `[writable]` The proof context account
/// 1. `[]` The proof context account owner
/// 0. `[]` (Optional) Account to read the proof from
/// 1. `[writable]` (Optional) The proof context account
/// 2. `[]` (Optional) The proof context account owner
///
/// * Otherwise
/// None
///
/// Data expected by this instruction:
/// `TransferWithFeeData`
/// The instruction expects either:
/// i. `TransferWithFeeData` if proof is provided as instruction data
/// ii. `u32` byte offset if proof is provided as an account
///
VerifyTransferWithFee,
@ -149,15 +149,13 @@ pub enum ProofInstruction {
///
/// Accounts expected by this instruction:
///
/// * Creating a proof context account
/// 0. `[writable]` The proof context account
/// 1. `[]` The proof context account owner
/// 0. `[]` (Optional) Account to read the proof from
/// 1. `[writable]` (Optional) The proof context account
/// 2. `[]` (Optional) The proof context account owner
///
/// * Otherwise
/// None
///
/// Data expected by this instruction:
/// `PubkeyValidityData`
/// The instruction expects either:
/// i. `PubkeyValidityData` if proof is provided as instruction data
/// ii. `u32` byte offset if proof is provided as an account
///
VerifyPubkeyValidity,
@ -168,15 +166,13 @@ pub enum ProofInstruction {
///
/// Accounts expected by this instruction:
///
/// * Creating a proof context account
/// 0. `[writable]` The proof context account
/// 1. `[]` The proof context account owner
/// 0. `[]` (Optional) Account to read the proof from
/// 1. `[writable]` (Optional) The proof context account
/// 2. `[]` (Optional) The proof context account owner
///
/// * Otherwise
/// None
///
/// Data expected by this instruction:
/// `RangeProofU64Data`
/// The instruction expects either:
/// i. `RangeProofU64Data` if proof is provided as instruction data
/// ii. `u32` byte offset if proof is provided as an account
///
VerifyRangeProofU64,
@ -194,15 +190,13 @@ pub enum ProofInstruction {
///
/// Accounts expected by this instruction:
///
/// * Creating a proof context account
/// 0. `[writable]` The proof context account
/// 1. `[]` The proof context account owner
/// 0. `[]` (Optional) Account to read the proof from
/// 1. `[writable]` (Optional) The proof context account
/// 2. `[]` (Optional) The proof context account owner
///
/// * Otherwise
/// None
///
/// Data expected by this instruction:
/// `BatchedRangeProof64Data`
/// The instruction expects either:
/// i. `BatchedRangeProofU64Data` if proof is provided as instruction data
/// ii. `u32` byte offset if proof is provided as an account
///
VerifyBatchedRangeProofU64,
@ -214,15 +208,13 @@ pub enum ProofInstruction {
///
/// Accounts expected by this instruction:
///
/// * Creating a proof context account
/// 0. `[writable]` The proof context account
/// 1. `[]` The proof context account owner
/// 0. `[]` (Optional) Account to read the proof from
/// 1. `[writable]` (Optional) The proof context account
/// 2. `[]` (Optional) The proof context account owner
///
/// * Otherwise
/// None
///
/// Data expected by this instruction:
/// `BatchedRangeProof128Data`
/// The instruction expects either:
/// i. `BatchedRangeProofU128Data` if proof is provided as instruction data
/// ii. `u32` byte offset if proof is provided as an account
///
VerifyBatchedRangeProofU128,
@ -234,15 +226,13 @@ pub enum ProofInstruction {
///
/// Accounts expected by this instruction:
///
/// * Creating a proof context account
/// 0. `[writable]` The proof context account
/// 1. `[]` The proof context account owner
/// 0. `[]` (Optional) Account to read the proof from
/// 1. `[writable]` (Optional) The proof context account
/// 2. `[]` (Optional) The proof context account owner
///
/// * Otherwise
/// None
///
/// Data expected by this instruction:
/// `BatchedRangeProof256Data`
/// The instruction expects either:
/// i. `BatchedRangeProofU256Data` if proof is provided as instruction data
/// ii. `u32` byte offset if proof is provided as an account
///
VerifyBatchedRangeProofU256,
@ -253,15 +243,13 @@ pub enum ProofInstruction {
///
/// Accounts expected by this instruction:
///
/// * Creating a proof context account
/// 0. `[writable]` The proof context account
/// 1. `[]` The proof context account owner
/// 0. `[]` (Optional) Account to read the proof from
/// 1. `[writable]` (Optional) The proof context account
/// 2. `[]` (Optional) The proof context account owner
///
/// * Otherwise
/// None
///
/// Data expected by this instruction:
/// `CiphertextCommitmentEqualityProofData`
/// The instruction expects either:
/// i. `CiphertextCommitmentEqualityProofData` if proof is provided as instruction data
/// ii. `u32` byte offset if proof is provided as an account
///
VerifyCiphertextCommitmentEquality,
@ -273,15 +261,13 @@ pub enum ProofInstruction {
///
/// Accounts expected by this instruction:
///
/// * Creating a proof context account
/// 0. `[writable]` The proof context account
/// 1. `[]` The proof context account owner
/// 0. `[]` (Optional) Account to read the proof from
/// 1. `[writable]` (Optional) The proof context account
/// 2. `[]` (Optional) The proof context account owner
///
/// * Otherwise
/// None
///
/// Data expected by this instruction:
/// `GroupedCiphertextValidityProofContext`
/// The instruction expects either:
/// i. `GroupedCiphertext2HandlesValidityProofData` if proof is provided as instruction data
/// ii. `u32` byte offset if proof is provided as an account
///
VerifyGroupedCiphertext2HandlesValidity,
@ -294,15 +280,13 @@ pub enum ProofInstruction {
///
/// Accounts expected by this instruction:
///
/// * Creating a proof context account
/// 0. `[writable]` The proof context account
/// 1. `[]` The proof context account owner
/// 0. `[]` (Optional) Account to read the proof from
/// 1. `[writable]` (Optional) The proof context account
/// 2. `[]` (Optional) The proof context account owner
///
/// * Otherwise
/// None
///
/// Data expected by this instruction:
/// `BatchedGroupedCiphertextValidityProofContext`
/// The instruction expects either:
/// i. `BatchedGroupedCiphertext2HandlesValidityProofData` if proof is provided as instruction data
/// ii. `u32` byte offset if proof is provided as an account
///
VerifyBatchedGroupedCiphertext2HandlesValidity,
@ -313,15 +297,13 @@ pub enum ProofInstruction {
///
/// Accounts expected by this instruction:
///
/// * Creating a proof context account
/// 0. `[writable]` The proof context account
/// 1. `[]` The proof context account owner
/// 0. `[]` (Optional) Account to read the proof from
/// 1. `[writable]` (Optional) The proof context account
/// 2. `[]` (Optional) The proof context account owner
///
/// * Otherwise
/// None
///
/// Data expected by this instruction:
/// `FeeSigmaProofData`
/// The instruction expects either:
/// i. `FeeSigmaProofData` if proof is provided as instruction data
/// ii. `u32` byte offset if proof is provided as an account
///
VerifyFeeSigma,
}
@ -474,6 +456,32 @@ impl ProofInstruction {
}
}
pub fn encode_verify_proof_from_account(
&self,
context_state_info: Option<ContextStateInfo>,
proof_account: &Pubkey,
offset: u32,
) -> Instruction {
let accounts = if let Some(context_state_info) = context_state_info {
vec![
AccountMeta::new(*proof_account, false),
AccountMeta::new(*context_state_info.context_state_account, false),
AccountMeta::new_readonly(*context_state_info.context_state_authority, false),
]
} else {
vec![AccountMeta::new(*proof_account, false)]
};
let mut data = vec![ToPrimitive::to_u8(self).unwrap()];
data.extend_from_slice(&offset.to_le_bytes());
Instruction {
program_id: crate::zk_token_proof_program::id(),
accounts,
data,
}
}
pub fn instruction_type(input: &[u8]) -> Option<Self> {
input
.first()