remove `UpdateAccountPk` instruction

This commit is contained in:
Sam Kim 2021-10-15 07:28:08 -04:00 committed by Michael Vines
parent b0e492bc06
commit 77e79221a0
3 changed files with 0 additions and 378 deletions

View File

@ -1,6 +1,5 @@
mod close_account;
mod transfer;
mod update_account_pk;
mod withdraw;
#[cfg(not(target_arch = "bpf"))]
@ -8,7 +7,6 @@ use crate::errors::ProofError;
pub use {
close_account::CloseAccountData,
transfer::{TransferCommitments, TransferData, TransferPubKeys},
update_account_pk::UpdateAccountPkData,
withdraw::WithdrawData,
};

View File

@ -1,362 +0,0 @@
use {
crate::zk_token_elgamal::pod,
bytemuck::{Pod, Zeroable},
};
#[cfg(not(target_arch = "bpf"))]
use {
crate::{
encryption::{
elgamal::{ElGamalCiphertext, ElGamalPubkey, ElGamalSecretKey},
pedersen::PedersenBase,
},
errors::ProofError,
instruction::Verifiable,
transcript::TranscriptProtocol,
},
curve25519_dalek::{
ristretto::RistrettoPoint,
scalar::Scalar,
traits::{IsIdentity, MultiscalarMul},
},
merlin::Transcript,
rand::rngs::OsRng,
std::convert::TryInto,
};
/// This struct includes the cryptographic proof *and* the account data information needed to verify
/// the proof
///
/// - The pre-instruction should call UpdateAccountPKData::verify(&self)
/// - The actual program should check that `current_ct` is consistent with what is
/// currently stored in the confidential token account
///
#[derive(Clone, Copy, Pod, Zeroable)]
#[repr(C)]
pub struct UpdateAccountPkData {
/// Current ElGamal encryption key
pub current_pk: pod::ElGamalPubkey, // 32 bytes
/// Current encrypted available balance
pub current_ct: pod::ElGamalCiphertext, // 64 bytes
/// New ElGamal encryption key
pub new_pk: pod::ElGamalPubkey, // 32 bytes
/// New encrypted available balance
pub new_ct: pod::ElGamalCiphertext, // 64 bytes
/// Proof that the current and new ciphertexts are consistent
pub proof: UpdateAccountPkProof, // 160 bytes
}
impl UpdateAccountPkData {
#[cfg(not(target_arch = "bpf"))]
pub fn new(
current_balance: u64,
current_ct: ElGamalCiphertext,
current_pk: ElGamalPubkey,
current_sk: &ElGamalSecretKey,
new_pk: ElGamalPubkey,
new_sk: &ElGamalSecretKey,
) -> Self {
let new_ct = new_pk.encrypt(current_balance);
let proof =
UpdateAccountPkProof::new(current_balance, current_sk, new_sk, &current_ct, &new_ct);
Self {
current_pk: current_pk.into(),
current_ct: current_ct.into(),
new_ct: new_ct.into(),
new_pk: new_pk.into(),
proof,
}
}
}
#[cfg(not(target_arch = "bpf"))]
impl Verifiable for UpdateAccountPkData {
fn verify(&self) -> Result<(), ProofError> {
let current_ct = self.current_ct.try_into()?;
let new_ct = self.new_ct.try_into()?;
self.proof.verify(&current_ct, &new_ct)
}
}
/// This struct represents the cryptographic proof component that certifies that the current_ct and
/// new_ct encrypt equal values
#[derive(Clone, Copy, Pod, Zeroable)]
#[repr(C)]
#[allow(non_snake_case)]
pub struct UpdateAccountPkProof {
pub R_0: pod::CompressedRistretto, // 32 bytes
pub R_1: pod::CompressedRistretto, // 32 bytes
pub z_sk_0: pod::Scalar, // 32 bytes
pub z_sk_1: pod::Scalar, // 32 bytes
pub z_x: pod::Scalar, // 32 bytes
}
#[allow(non_snake_case)]
#[cfg(not(target_arch = "bpf"))]
impl UpdateAccountPkProof {
fn transcript_new() -> Transcript {
Transcript::new(b"UpdateAccountPkProof")
}
fn new(
current_balance: u64,
current_sk: &ElGamalSecretKey,
new_sk: &ElGamalSecretKey,
current_ct: &ElGamalCiphertext,
new_ct: &ElGamalCiphertext,
) -> Self {
let mut transcript = Self::transcript_new();
// add a domain separator to record the start of the protocol
transcript.update_account_public_key_proof_domain_sep();
// extract the relevant scalar and Ristretto points from the input
let s_0 = current_sk.get_scalar();
let s_1 = new_sk.get_scalar();
let x = Scalar::from(current_balance);
let D_0 = current_ct.decrypt_handle.get_point();
let D_1 = new_ct.decrypt_handle.get_point();
let G = PedersenBase::default().G;
// generate a random masking factor that also serves as a nonce
let r_sk_0 = Scalar::random(&mut OsRng);
let r_sk_1 = Scalar::random(&mut OsRng);
let r_x = Scalar::random(&mut OsRng);
let R_0 = (r_sk_0 * D_0 + r_x * G).compress();
let R_1 = (r_sk_1 * D_1 + r_x * G).compress();
// record R_0, R_1 on transcript and receive a challenge scalar
transcript.append_point(b"R_0", &R_0);
transcript.append_point(b"R_1", &R_1);
let c = transcript.challenge_scalar(b"c");
let _w = transcript.challenge_scalar(b"w"); // for consistency of transcript
// compute the masked secret keys and amount
let z_sk_0 = c * s_0 + r_sk_0;
let z_sk_1 = c * s_1 + r_sk_1;
let z_x = c * x + r_x;
UpdateAccountPkProof {
R_0: R_0.into(),
R_1: R_1.into(),
z_sk_0: z_sk_0.into(),
z_sk_1: z_sk_1.into(),
z_x: z_x.into(),
}
}
fn verify(
&self,
current_ct: &ElGamalCiphertext,
new_ct: &ElGamalCiphertext,
) -> Result<(), ProofError> {
let mut transcript = Self::transcript_new();
// add a domain separator to record the start of the protocol
transcript.update_account_public_key_proof_domain_sep();
// extract the relevant scalar and Ristretto points from the input
let C_0 = current_ct.message_comm.get_point();
let D_0 = current_ct.decrypt_handle.get_point();
let C_1 = new_ct.message_comm.get_point();
let D_1 = new_ct.decrypt_handle.get_point();
let R_0 = self.R_0.into();
let R_1 = self.R_1.into();
let z_sk_0 = self.z_sk_0.into();
let z_sk_1: Scalar = self.z_sk_1.into();
let z_x = self.z_x.into();
let G = PedersenBase::default().G;
// generate a challenge scalar
transcript.validate_and_append_point(b"R_0", &R_0)?;
transcript.validate_and_append_point(b"R_1", &R_1)?;
let c = transcript.challenge_scalar(b"c");
let w = transcript.challenge_scalar(b"w");
// decompress R_0, R_1 or return verification error
let R_0 = R_0.decompress().ok_or(ProofError::VerificationError)?;
let R_1 = R_1.decompress().ok_or(ProofError::VerificationError)?;
// check the required algebraic relation
let check = RistrettoPoint::multiscalar_mul(
vec![
z_sk_0,
z_x,
-c,
-Scalar::one(),
w * z_sk_1,
w * z_x,
-w * c,
-w * Scalar::one(),
],
vec![D_0, G, C_0, R_0, D_1, G, C_1, R_1],
);
if check.is_identity() {
Ok(())
} else {
Err(ProofError::VerificationError)
}
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::encryption::elgamal::ElGamalKeypair;
use crate::encryption::pedersen::{Pedersen, PedersenDecryptHandle, PedersenOpening};
#[test]
fn test_update_account_public_key_general_cases() {
let current = ElGamalKeypair::default();
let new = ElGamalKeypair::default();
// If current_ct and new_ct encrypt same values, then the proof verification should succeed
let balance: u64 = 77;
let current_ct = current.public.encrypt(balance);
let new_ct = new.public.encrypt(balance);
let proof =
UpdateAccountPkProof::new(balance, &current.secret, &new.secret, &current_ct, &new_ct);
assert!(proof.verify(&current_ct, &new_ct).is_ok());
// If current_ct and new_ct encrypt different values, then the proof verification should fail
let new_ct = new.public.encrypt(55_u64);
let proof =
UpdateAccountPkProof::new(balance, &current.secret, &new.secret, &current_ct, &new_ct);
assert!(proof.verify(&current_ct, &new_ct).is_err());
}
#[test]
fn test_update_account_public_key_zeroed_ciphertexts() {
let current = ElGamalKeypair::default();
let new = ElGamalKeypair::default();
// A zeroed cipehrtext should be considered as an account balance of 0
let balance: u64 = 0;
let zeroed_ct_as_current_ct: ElGamalCiphertext =
pod::ElGamalCiphertext::zeroed().try_into().unwrap();
let new_ct: ElGamalCiphertext = new.public.encrypt(balance);
let proof = UpdateAccountPkProof::new(
balance,
&current.secret,
&new.secret,
&zeroed_ct_as_current_ct,
&new_ct,
);
assert!(proof.verify(&zeroed_ct_as_current_ct, &new_ct).is_ok());
let current_ct = current.public.encrypt(balance);
let zeroed_ct_as_new_ct: ElGamalCiphertext =
pod::ElGamalCiphertext::zeroed().try_into().unwrap();
let proof = UpdateAccountPkProof::new(
balance,
&current.secret,
&new.secret,
&current_ct,
&zeroed_ct_as_new_ct,
);
assert!(proof.verify(&current_ct, &zeroed_ct_as_new_ct).is_ok());
let zeroed_ct_as_current_ct: ElGamalCiphertext =
pod::ElGamalCiphertext::zeroed().try_into().unwrap();
let zeroed_ct_as_new_ct: ElGamalCiphertext =
pod::ElGamalCiphertext::zeroed().try_into().unwrap();
let proof = UpdateAccountPkProof::new(
balance,
&current.secret,
&new.secret,
&zeroed_ct_as_current_ct,
&zeroed_ct_as_new_ct,
);
assert!(proof
.verify(&zeroed_ct_as_current_ct, &zeroed_ct_as_new_ct)
.is_ok());
}
#[test]
fn test_update_account_public_key_partially_zeroed_ciphertexts() {
let current = ElGamalKeypair::default();
let new = ElGamalKeypair::default();
let balance = 0_u64;
let balance_ciphertext = new.public.encrypt(balance);
let zeroed_comm = Pedersen::with(0_u64, &PedersenOpening::default());
let handle = balance_ciphertext.decrypt_handle;
// Partially zeroed ciphertext as current ciphertext
let zeroed_comm_ciphertext = ElGamalCiphertext {
message_comm: zeroed_comm,
decrypt_handle: handle,
};
let new_ct: ElGamalCiphertext = new.public.encrypt(balance);
let proof = UpdateAccountPkProof::new(
balance,
&current.secret,
&new.secret,
&zeroed_comm_ciphertext,
&new_ct,
);
assert!(proof.verify(&zeroed_comm_ciphertext, &new_ct).is_err());
let zeroed_handle_ciphertext = ElGamalCiphertext {
message_comm: balance_ciphertext.message_comm,
decrypt_handle: PedersenDecryptHandle::default(),
};
let proof = UpdateAccountPkProof::new(
balance,
&current.secret,
&new.secret,
&zeroed_handle_ciphertext,
&new_ct,
);
assert!(proof.verify(&zeroed_handle_ciphertext, &new_ct).is_err());
// Partially zeroed cipehrtext as new ciphertext
let zeroed_comm_ciphertext = ElGamalCiphertext {
message_comm: zeroed_comm,
decrypt_handle: handle,
};
let current_ct: ElGamalCiphertext = current.public.encrypt(balance);
let proof = UpdateAccountPkProof::new(
balance,
&current.secret,
&new.secret,
&current_ct,
&zeroed_comm_ciphertext,
);
assert!(proof.verify(&current_ct, &zeroed_comm_ciphertext).is_err());
let zeroed_handle_ciphertext = ElGamalCiphertext {
message_comm: balance_ciphertext.message_comm,
decrypt_handle: PedersenDecryptHandle::default(),
};
let proof = UpdateAccountPkProof::new(
balance,
&current.secret,
&new.secret,
&current_ct,
&zeroed_handle_ciphertext,
);
assert!(proof
.verify(&current_ct, &zeroed_handle_ciphertext)
.is_err());
}
}

View File

@ -11,16 +11,6 @@ use {
#[derive(Clone, Copy, Debug, FromPrimitive, ToPrimitive, PartialEq)]
#[repr(u8)]
pub enum ProofInstruction {
/// Verify a `UpdateAccountPkData` struct
///
/// Accounts expected by this instruction:
/// None
///
/// Data expected by this instruction:
/// `UpdateAccountPkData`
///
VerifyUpdateAccountPk,
/// Verify a `CloseAccountData` struct
///
/// Accounts expected by this instruction:
@ -84,10 +74,6 @@ pub fn verify_close_account(proof_data: &CloseAccountData) -> Instruction {
ProofInstruction::VerifyCloseAccount.encode(proof_data)
}
pub fn verify_update_account_pk(proof_data: &UpdateAccountPkData) -> Instruction {
ProofInstruction::VerifyUpdateAccountPk.encode(proof_data)
}
pub fn verify_withdraw(proof_data: &WithdrawData) -> Instruction {
ProofInstruction::VerifyWithdraw.encode(proof_data)
}