forked from solana-labs/solana
-
Notifications
You must be signed in to change notification settings - Fork 255
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[zk-sdk] Add error types and zero ciphertext instruction #1453
Merged
samkim-crypto
merged 7 commits into
anza-xyz:master
from
samkim-crypto:zk-sdk-zero-balance
May 23, 2024
Merged
Changes from 5 commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
7c0e380
add `ProofGenerationError` and `ProofVerificationError`
samkim-crypto d701214
add `zero_ciphertext` proof data module from zk-token-sdk verbatim
samkim-crypto 8468880
clean up `zero_ciphertext` proof data module
samkim-crypto 25c3c88
add `VerifyZeroCiphertext` instruction
samkim-crypto bcca0d3
cargo fmt
samkim-crypto 40be069
remove `ProofGenerationError::FeeCalculation`
samkim-crypto 32a6372
remove `ProofGenerationError::NotEnoughFunds`
samkim-crypto File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,54 @@ | ||
use thiserror::Error; | ||
use { | ||
crate::{ | ||
errors::ElGamalError, | ||
range_proof::errors::{RangeProofGenerationError, RangeProofVerificationError}, | ||
sigma_proofs::errors::*, | ||
}, | ||
thiserror::Error, | ||
}; | ||
|
||
#[cfg(not(target_os = "solana"))] | ||
#[derive(Error, Clone, Debug, Eq, PartialEq)] | ||
pub enum ProofGenerationError { | ||
#[error("not enough funds in account")] | ||
NotEnoughFunds, | ||
#[error("transfer fee calculation error")] | ||
FeeCalculation, | ||
#[error("illegal number of commitments")] | ||
IllegalCommitmentLength, | ||
#[error("illegal amount bit length")] | ||
IllegalAmountBitLength, | ||
#[error("invalid commitment")] | ||
InvalidCommitment, | ||
#[error("range proof generation failed")] | ||
RangeProof(#[from] RangeProofGenerationError), | ||
#[error("unexpected proof length")] | ||
ProofLength, | ||
} | ||
|
||
#[derive(Error, Clone, Debug, Eq, PartialEq)] | ||
pub enum ProofVerificationError { | ||
#[error("range proof verification failed")] | ||
RangeProof(#[from] RangeProofVerificationError), | ||
#[error("sigma proof verification failed")] | ||
SigmaProof(SigmaProofType, SigmaProofVerificationError), | ||
#[error("ElGamal ciphertext or public key error")] | ||
ElGamal(#[from] ElGamalError), | ||
#[error("Invalid proof context")] | ||
ProofContext, | ||
#[error("illegal commitment length")] | ||
IllegalCommitmentLength, | ||
#[error("illegal amount bit length")] | ||
IllegalAmountBitLength, | ||
} | ||
|
||
#[derive(Clone, Debug, Eq, PartialEq)] | ||
pub enum SigmaProofType { | ||
ZeroCiphertext, | ||
} | ||
|
||
impl From<ZeroCiphertextProofVerificationError> for ProofVerificationError { | ||
fn from(err: ZeroCiphertextProofVerificationError) -> Self { | ||
Self::SigmaProof(SigmaProofType::ZeroCiphertext, err.0) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
126 changes: 126 additions & 0 deletions
126
zk-sdk/src/elgamal_program/proof_data/zero_ciphertext.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
//! The zero-ciphertext proof instruction. | ||
//! | ||
//! A zero-ciphertext proof is defined with respect to a twisted ElGamal ciphertext. The proof | ||
//! certifies that a given ciphertext encrypts the message 0 in the field (`Scalar::zero()`). To | ||
//! generate the proof, a prover must provide the decryption key for the ciphertext. | ||
|
||
use { | ||
crate::{ | ||
elgamal_program::{ | ||
errors::{ProofGenerationError, ProofVerificationError}, | ||
proof_data::{ProofType, ZkProofData}, | ||
}, | ||
encryption::pod::elgamal::{PodElGamalCiphertext, PodElGamalPubkey}, | ||
sigma_proofs::pod::PodZeroCiphertextProof, | ||
}, | ||
bytemuck::{bytes_of, Pod, Zeroable}, | ||
}; | ||
#[cfg(not(target_os = "solana"))] | ||
use { | ||
crate::{ | ||
encryption::elgamal::{ElGamalCiphertext, ElGamalKeypair}, | ||
sigma_proofs::zero_ciphertext::ZeroCiphertextProof, | ||
}, | ||
merlin::Transcript, | ||
std::convert::TryInto, | ||
}; | ||
|
||
/// The instruction data that is needed for the `ProofInstruction::ZeroCiphertext` instruction. | ||
/// | ||
/// It includes the cryptographic proof as well as the context data information needed to verify | ||
/// the proof. | ||
#[derive(Clone, Copy, Pod, Zeroable)] | ||
#[repr(C)] | ||
pub struct ZeroCiphertextProofData { | ||
/// The context data for the zero-ciphertext proof | ||
pub context: ZeroCiphertextProofContext, // 96 bytes | ||
|
||
/// Proof that the ciphertext is zero | ||
pub proof: PodZeroCiphertextProof, // 96 bytes | ||
} | ||
|
||
/// The context data needed to verify a zero-ciphertext proof. | ||
#[derive(Clone, Copy, Pod, Zeroable)] | ||
#[repr(C)] | ||
pub struct ZeroCiphertextProofContext { | ||
/// The ElGamal pubkey associated with the ElGamal ciphertext | ||
pub pubkey: PodElGamalPubkey, // 32 bytes | ||
|
||
/// The ElGamal ciphertext that encrypts zero | ||
pub ciphertext: PodElGamalCiphertext, // 64 bytes | ||
} | ||
|
||
#[cfg(not(target_os = "solana"))] | ||
impl ZeroCiphertextProofData { | ||
pub fn new( | ||
keypair: &ElGamalKeypair, | ||
ciphertext: &ElGamalCiphertext, | ||
) -> Result<Self, ProofGenerationError> { | ||
let pod_pubkey = PodElGamalPubkey(keypair.pubkey().into()); | ||
let pod_ciphertext = PodElGamalCiphertext(ciphertext.to_bytes()); | ||
|
||
let context = ZeroCiphertextProofContext { | ||
pubkey: pod_pubkey, | ||
ciphertext: pod_ciphertext, | ||
}; | ||
|
||
let mut transcript = context.new_transcript(); | ||
let proof = ZeroCiphertextProof::new(keypair, ciphertext, &mut transcript).into(); | ||
|
||
Ok(ZeroCiphertextProofData { context, proof }) | ||
} | ||
} | ||
|
||
impl ZkProofData<ZeroCiphertextProofContext> for ZeroCiphertextProofData { | ||
const PROOF_TYPE: ProofType = ProofType::ZeroCiphertext; | ||
|
||
fn context_data(&self) -> &ZeroCiphertextProofContext { | ||
&self.context | ||
} | ||
|
||
#[cfg(not(target_os = "solana"))] | ||
fn verify_proof(&self) -> Result<(), ProofVerificationError> { | ||
let mut transcript = self.context.new_transcript(); | ||
let pubkey = self.context.pubkey.try_into()?; | ||
let ciphertext = self.context.ciphertext.try_into()?; | ||
let proof: ZeroCiphertextProof = self.proof.try_into()?; | ||
proof | ||
.verify(&pubkey, &ciphertext, &mut transcript) | ||
.map_err(|e| e.into()) | ||
} | ||
} | ||
|
||
#[allow(non_snake_case)] | ||
#[cfg(not(target_os = "solana"))] | ||
impl ZeroCiphertextProofContext { | ||
fn new_transcript(&self) -> Transcript { | ||
let mut transcript = Transcript::new(b"zero-ciphertext-instruction"); | ||
|
||
transcript.append_message(b"pubkey", bytes_of(&self.pubkey)); | ||
transcript.append_message(b"ciphertext", bytes_of(&self.ciphertext)); | ||
|
||
transcript | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod test { | ||
use super::*; | ||
|
||
#[test] | ||
fn test_zero_ciphertext_proof_instruction_correctness() { | ||
let keypair = ElGamalKeypair::new_rand(); | ||
|
||
// general case: encryption of 0 | ||
let ciphertext = keypair.pubkey().encrypt(0_u64); | ||
let zero_ciphertext_proof_data = | ||
ZeroCiphertextProofData::new(&keypair, &ciphertext).unwrap(); | ||
assert!(zero_ciphertext_proof_data.verify_proof().is_ok()); | ||
|
||
// general case: encryption of > 0 | ||
let ciphertext = keypair.pubkey().encrypt(1_u64); | ||
let zero_ciphertext_proof_data = | ||
ZeroCiphertextProofData::new(&keypair, &ciphertext).unwrap(); | ||
assert!(zero_ciphertext_proof_data.verify_proof().is_err()); | ||
} | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should these errors be changed or removed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh great point! I removed it. Thanks!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok cool! Can you also get rid of
NotEnoughFunds
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh oops, removed. Thanks!