Skip to content
This repository has been archived by the owner on Jan 13, 2025. It is now read-only.

[zk-token-sdk] Add sigma/range proof verification instructions #30816

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
142 changes: 139 additions & 3 deletions programs/zk-token-proof-tests/tests/process_transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,24 @@ use {
transaction::{Transaction, TransactionError},
},
solana_zk_token_sdk::{
encryption::elgamal::ElGamalKeypair, instruction::*, zk_token_proof_instruction::*,
zk_token_proof_program, zk_token_proof_state::ProofContextState,
encryption::{elgamal::ElGamalKeypair, pedersen::*},
instruction::*,
zk_token_proof_instruction::*,
zk_token_proof_program,
zk_token_proof_state::ProofContextState,
},
std::mem::size_of,
};

const VERIFY_INSTRUCTION_TYPES: [ProofInstruction; 6] = [
const VERIFY_INSTRUCTION_TYPES: [ProofInstruction; 8] = [
ProofInstruction::VerifyCloseAccount,
ProofInstruction::VerifyWithdraw,
ProofInstruction::VerifyWithdrawWithheldTokens,
ProofInstruction::VerifyTransfer,
ProofInstruction::VerifyTransferWithFee,
ProofInstruction::VerifyPubkeyValidity,
ProofInstruction::VerifyValidityProof,
ProofInstruction::VerifyAggregatedValidityProof,
];

#[tokio::test]
Expand Down Expand Up @@ -318,6 +323,137 @@ async fn test_pubkey_validity() {
.await;
}

#[tokio::test]
async fn test_validity_proof() {
let destination_pubkey = ElGamalKeypair::new_rand().public;
let auditor_pubkey = ElGamalKeypair::new_rand().public;

let amount: u64 = 55;
let (commitment, opening) = Pedersen::new(amount);

let destination_handle = destination_pubkey.decrypt_handle(&opening);
let auditor_handle = auditor_pubkey.decrypt_handle(&opening);

let success_proof_data = ValidityProofData::new(
&destination_pubkey,
&auditor_pubkey,
&commitment,
&destination_handle,
&auditor_handle,
amount,
&opening,
)
.unwrap();

let incorrect_amount: u64 = 0;
let fail_proof_data = ValidityProofData::new(
&destination_pubkey,
&auditor_pubkey,
&commitment,
&destination_handle,
&auditor_handle,
incorrect_amount,
&opening,
)
.unwrap();

test_verify_proof_without_context(
ProofInstruction::VerifyValidityProof,
&success_proof_data,
&fail_proof_data,
)
.await;

test_verify_proof_with_context(
ProofInstruction::VerifyValidityProof,
size_of::<ProofContextState<ValidityProofContext>>(),
&success_proof_data,
&fail_proof_data,
)
.await;

test_close_context_state(
ProofInstruction::VerifyValidityProof,
size_of::<ProofContextState<ValidityProofContext>>(),
&success_proof_data,
)
.await;
}

#[tokio::test]
async fn test_aggregated_validity_proof() {
let destination_pubkey = ElGamalKeypair::new_rand().public;
let auditor_pubkey = ElGamalKeypair::new_rand().public;

let amount_lo: u64 = 55;
let amount_hi: u64 = 77;

let (commitment_lo, opening_lo) = Pedersen::new(amount_lo);
let (commitment_hi, opening_hi) = Pedersen::new(amount_hi);

let destination_handle_lo = destination_pubkey.decrypt_handle(&opening_lo);
let destination_handle_hi = destination_pubkey.decrypt_handle(&opening_hi);

let auditor_handle_lo = auditor_pubkey.decrypt_handle(&opening_lo);
let auditor_handle_hi = auditor_pubkey.decrypt_handle(&opening_hi);

let success_proof_data = AggregatedValidityProofData::new(
&destination_pubkey,
&auditor_pubkey,
&commitment_lo,
&commitment_hi,
&destination_handle_lo,
&destination_handle_hi,
&auditor_handle_lo,
&auditor_handle_hi,
amount_lo,
amount_hi,
&opening_lo,
&opening_hi,
)
.unwrap();

let incorrect_amount_lo: u64 = 0;
let incorrect_amount_hi: u64 = 0;
let fail_proof_data = AggregatedValidityProofData::new(
&destination_pubkey,
&auditor_pubkey,
&commitment_lo,
&commitment_hi,
&destination_handle_lo,
&destination_handle_hi,
&auditor_handle_lo,
&auditor_handle_hi,
incorrect_amount_lo,
incorrect_amount_hi,
&opening_lo,
&opening_hi,
)
.unwrap();

test_verify_proof_without_context(
ProofInstruction::VerifyAggregatedValidityProof,
&success_proof_data,
&fail_proof_data,
)
.await;

test_verify_proof_with_context(
ProofInstruction::VerifyAggregatedValidityProof,
size_of::<ProofContextState<AggregatedValidityProofContext>>(),
&success_proof_data,
&fail_proof_data,
)
.await;

test_close_context_state(
ProofInstruction::VerifyAggregatedValidityProof,
size_of::<ProofContextState<AggregatedValidityProofContext>>(),
&success_proof_data,
)
.await;
}

async fn test_verify_proof_without_context<T, U>(
proof_instruction: ProofInstruction,
success_proof_data: &T,
Expand Down
16 changes: 16 additions & 0 deletions programs/zk-token-proof/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -191,5 +191,21 @@ declare_process_instruction!(process_instruction, 0, |invoke_context| {
ic_msg!(invoke_context, "VerifyPubkeyValidity");
process_verify_proof::<PubkeyValidityData, PubkeyValidityProofContext>(invoke_context)
}
ProofInstruction::VerifyValidityProof => {
ic_msg!(invoke_context, "VerifyValidityProof");
process_verify_proof::<ValidityProofData, ValidityProofContext>(invoke_context)
}
ProofInstruction::VerifyAggregatedValidityProof => {
ic_msg!(invoke_context, "VerifyAggregatedValidityProof");
process_verify_proof::<AggregatedValidityProofData, AggregatedValidityProofContext>(
invoke_context,
)
}
ProofInstruction::VerifyCtxtCommEqualityProof => {
ic_msg!(invoke_context, "VerifyCtxtCommEqualityProof");
process_verify_proof::<CtxtCommEqualityProofData, CtxtCommEqualityProofContext>(
invoke_context,
)
}
}
});
127 changes: 127 additions & 0 deletions zk-token-sdk/src/instruction/equality_proof.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
#[cfg(not(target_os = "solana"))]
use {
crate::{
encryption::{
elgamal::{ElGamalCiphertext, ElGamalKeypair},
pedersen::{PedersenCommitment, PedersenOpening},
},
errors::ProofError,
sigma_proofs::equality_proof::CtxtCommEqualityProof,
transcript::TranscriptProtocol,
},
merlin::Transcript,
};
use {
crate::{
instruction::{ProofType, ZkProofData},
zk_token_elgamal::pod,
},
bytemuck::{Pod, Zeroable},
};

#[derive(Clone, Copy, Pod, Zeroable)]
#[repr(C)]
pub struct CtxtCommEqualityProofData {
/// The context data for the ciphertext-commitment equality proof instruction
pub context: CtxtCommEqualityProofContext, // 128 bytes

/// Proof that an ElGamal ciphertext is valid
pub proof: pod::CtxtCommEqualityProof, // 192 bytes
}

#[derive(Clone, Copy, Pod, Zeroable)]
#[repr(C)]
pub struct CtxtCommEqualityProofContext {
/// The ElGamal pubkey
pub pubkey: pod::ElGamalPubkey, // 32 bytes

/// The ciphertext encrypted under the ElGamal pubkey
pub ciphertext: pod::ElGamalCiphertext, // 64 bytes

/// The Pedersen commitment
pub commitment: pod::PedersenCommitment, // 32 bytes
}

#[cfg(not(target_os = "solana"))]
impl CtxtCommEqualityProofData {
pub fn new(
keypair: &ElGamalKeypair,
ciphertext: &ElGamalCiphertext,
commitment: &PedersenCommitment,
amount: u64,
opening: &PedersenOpening,
) -> Result<Self, ProofError> {
let context = CtxtCommEqualityProofContext {
pubkey: pod::ElGamalPubkey(keypair.public.to_bytes()),
ciphertext: pod::ElGamalCiphertext(ciphertext.to_bytes()),
commitment: pod::PedersenCommitment(commitment.to_bytes()),
};

let mut transcript = CtxtCommEqualityProofData::transcript_new(&context);

let proof =
CtxtCommEqualityProof::new(keypair, ciphertext, amount, opening, &mut transcript);

Ok(CtxtCommEqualityProofData {
context,
proof: proof.into(),
})
}
}

impl ZkProofData<CtxtCommEqualityProofContext> for CtxtCommEqualityProofData {
const PROOF_TYPE: ProofType = ProofType::CtxtCommEqualityProof;

fn context_data(&self) -> &CtxtCommEqualityProofContext {
&self.context
}

#[cfg(not(target_os = "solana"))]
fn verify_proof(&self) -> Result<(), ProofError> {
let mut transcript = CtxtCommEqualityProofData::transcript_new(&self.context);

let pubkey = self.context.pubkey.try_into()?;
let ciphertext = self.context.ciphertext.try_into()?;
let commitment = self.context.commitment.try_into()?;

let proof: CtxtCommEqualityProof = self.proof.try_into()?;
proof
.verify(&pubkey, &ciphertext, &commitment, &mut transcript)
.map_err(|e| e.into())
}
}

impl CtxtCommEqualityProofData {
fn transcript_new(context: &CtxtCommEqualityProofContext) -> Transcript {
let mut transcript = Transcript::new(b"CtxtCommEqualityProof");

transcript.append_pubkey(b"pubkey", &context.pubkey);
transcript.append_ciphertext(b"ciphertext", &context.ciphertext);
transcript.append_commitment(b"commitment", &context.commitment);

transcript
}
}

#[cfg(test)]
mod test {
use {
super::*,
crate::encryption::{elgamal::ElGamalKeypair, pedersen::Pedersen},
};

#[test]
fn test_ctxt_comm_equality_proof_correctness() {
let keypair = ElGamalKeypair::new_rand();
let amount: u64 = 55;

let ciphertext = keypair.public.encrypt(amount);
let (commitment, opening) = Pedersen::new(amount);

let ctxt_comm_equality_proof_data =
CtxtCommEqualityProofData::new(&keypair, &ciphertext, &commitment, amount, &opening)
.unwrap();

assert!(ctxt_comm_equality_proof_data.verify_proof().is_ok());
}
}
10 changes: 10 additions & 0 deletions zk-token-sdk/src/instruction/mod.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
pub mod close_account;
pub mod equality_proof;
pub mod pubkey_validity;
pub mod transfer;
pub mod transfer_with_fee;
pub mod validity_proof;
pub mod withdraw;
pub mod withdraw_withheld;

Expand All @@ -20,9 +22,14 @@ use {
pub use {
bytemuck::Pod,
close_account::{CloseAccountData, CloseAccountProofContext},
equality_proof::{CtxtCommEqualityProofContext, CtxtCommEqualityProofData},
pubkey_validity::{PubkeyValidityData, PubkeyValidityProofContext},
transfer::{TransferData, TransferProofContext},
transfer_with_fee::{FeeParameters, TransferWithFeeData, TransferWithFeeProofContext},
validity_proof::{
AggregatedValidityProofContext, AggregatedValidityProofData, ValidityProofContext,
ValidityProofData,
},
withdraw::{WithdrawData, WithdrawProofContext},
withdraw_withheld::{WithdrawWithheldTokensData, WithdrawWithheldTokensProofContext},
};
Expand All @@ -38,6 +45,9 @@ pub enum ProofType {
Transfer,
TransferWithFee,
PubkeyValidity,
ValidityProof,
AggregatedValidityProof,
CtxtCommEqualityProof,
}

pub trait ZkProofData<T: Pod> {
Expand Down
Loading