solana/zk-token-sdk/src/instruction/close_account.rs

138 lines
4.0 KiB
Rust
Raw Normal View History

2021-09-29 21:45:35 -07:00
use {
2021-09-30 10:25:36 -07:00
crate::zk_token_elgamal::pod,
2021-09-29 21:45:35 -07:00
bytemuck::{Pod, Zeroable},
};
#[cfg(not(target_os = "solana"))]
2021-09-29 21:45:35 -07:00
use {
crate::{
encryption::elgamal::{ElGamalCiphertext, ElGamalKeypair, ElGamalPubkey},
2021-09-29 21:45:35 -07:00
errors::ProofError,
instruction::Verifiable,
sigma_proofs::zero_balance_proof::ZeroBalanceProof,
2021-09-29 21:45:35 -07:00
transcript::TranscriptProtocol,
},
merlin::Transcript,
std::convert::TryInto,
};
/// This struct includes the cryptographic proof *and* the account data information needed to verify
/// the proof
///
/// - The pre-instruction should call CloseAccountData::verify_proof(&self)
/// - The actual program should check that `balance` is consistent with what is
/// currently stored in the confidential token account
///
#[derive(Clone, Copy, Pod, Zeroable)]
#[repr(C)]
pub struct CloseAccountData {
2021-12-06 08:23:31 -08:00
/// The source account ElGamal pubkey
pub pubkey: pod::ElGamalPubkey, // 32 bytes
2021-12-06 08:23:31 -08:00
2021-09-29 21:45:35 -07:00
/// The source account available balance in encrypted form
pub ciphertext: pod::ElGamalCiphertext, // 64 bytes
2021-09-29 21:45:35 -07:00
/// Proof that the source account available balance is zero
pub proof: CloseAccountProof, // 96 bytes
2021-09-29 21:45:35 -07:00
}
#[cfg(not(target_os = "solana"))]
2021-09-29 21:45:35 -07:00
impl CloseAccountData {
pub fn new(
keypair: &ElGamalKeypair,
ciphertext: &ElGamalCiphertext,
) -> Result<Self, ProofError> {
let pod_pubkey = pod::ElGamalPubkey(keypair.public.to_bytes());
let pod_ciphertext = pod::ElGamalCiphertext(ciphertext.to_bytes());
2021-09-29 21:45:35 -07:00
let mut transcript = CloseAccountProof::transcript_new(&pod_pubkey, &pod_ciphertext);
let proof = CloseAccountProof::new(keypair, ciphertext, &mut transcript);
Ok(CloseAccountData {
pubkey: pod_pubkey,
ciphertext: pod_ciphertext,
2021-09-29 21:45:35 -07:00
proof,
})
2021-09-29 21:45:35 -07:00
}
}
#[cfg(not(target_os = "solana"))]
2021-09-29 21:45:35 -07:00
impl Verifiable for CloseAccountData {
fn verify(&self) -> Result<(), ProofError> {
let mut transcript = CloseAccountProof::transcript_new(&self.pubkey, &self.ciphertext);
let pubkey = self.pubkey.try_into()?;
let ciphertext = self.ciphertext.try_into()?;
self.proof.verify(&pubkey, &ciphertext, &mut transcript)
2021-09-29 21:45:35 -07:00
}
}
/// This struct represents the cryptographic proof component that certifies that the encrypted
/// balance is zero
#[derive(Clone, Copy, Pod, Zeroable)]
#[repr(C)]
#[allow(non_snake_case)]
pub struct CloseAccountProof {
pub proof: pod::ZeroBalanceProof,
2021-09-29 21:45:35 -07:00
}
#[allow(non_snake_case)]
#[cfg(not(target_os = "solana"))]
2021-09-29 21:45:35 -07:00
impl CloseAccountProof {
fn transcript_new(
pubkey: &pod::ElGamalPubkey,
ciphertext: &pod::ElGamalCiphertext,
) -> Transcript {
let mut transcript = Transcript::new(b"CloseAccountProof");
2021-09-29 21:45:35 -07:00
transcript.append_pubkey(b"pubkey", pubkey);
transcript.append_ciphertext(b"ciphertext", ciphertext);
2021-09-29 21:45:35 -07:00
transcript
}
2021-09-29 21:45:35 -07:00
pub fn new(
keypair: &ElGamalKeypair,
ciphertext: &ElGamalCiphertext,
transcript: &mut Transcript,
) -> Self {
let proof = ZeroBalanceProof::new(keypair, ciphertext, transcript);
2021-09-29 21:45:35 -07:00
Self {
proof: proof.into(),
2021-09-29 21:45:35 -07:00
}
}
2021-12-06 08:23:31 -08:00
pub fn verify(
&self,
pubkey: &ElGamalPubkey,
ciphertext: &ElGamalCiphertext,
transcript: &mut Transcript,
2021-12-06 08:23:31 -08:00
) -> Result<(), ProofError> {
let proof: ZeroBalanceProof = self.proof.try_into()?;
proof.verify(pubkey, ciphertext, transcript)?;
2022-01-04 05:39:03 -08:00
Ok(())
2021-09-29 21:45:35 -07:00
}
}
#[cfg(test)]
mod test {
use super::*;
2021-09-29 21:45:35 -07:00
#[test]
fn test_close_account_correctness() {
let keypair = ElGamalKeypair::new_rand();
2021-09-29 21:45:35 -07:00
2021-12-06 08:23:31 -08:00
// general case: encryption of 0
let ciphertext = keypair.public.encrypt(0_u64);
let close_account_data = CloseAccountData::new(&keypair, &ciphertext).unwrap();
assert!(close_account_data.verify().is_ok());
2021-09-29 21:45:35 -07:00
2021-12-06 08:23:31 -08:00
// general case: encryption of > 0
let ciphertext = keypair.public.encrypt(1_u64);
let close_account_data = CloseAccountData::new(&keypair, &ciphertext).unwrap();
assert!(close_account_data.verify().is_err());
2021-09-29 21:45:35 -07:00
}
}