remove `UpdateAccountPk` instruction
This commit is contained in:
parent
b0e492bc06
commit
77e79221a0
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
|
@ -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, ¤t_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(¤t_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, ¤t.secret, &new.secret, ¤t_ct, &new_ct);
|
||||
assert!(proof.verify(¤t_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, ¤t.secret, &new.secret, ¤t_ct, &new_ct);
|
||||
assert!(proof.verify(¤t_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,
|
||||
¤t.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,
|
||||
¤t.secret,
|
||||
&new.secret,
|
||||
¤t_ct,
|
||||
&zeroed_ct_as_new_ct,
|
||||
);
|
||||
assert!(proof.verify(¤t_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,
|
||||
¤t.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,
|
||||
¤t.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,
|
||||
¤t.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,
|
||||
¤t.secret,
|
||||
&new.secret,
|
||||
¤t_ct,
|
||||
&zeroed_comm_ciphertext,
|
||||
);
|
||||
assert!(proof.verify(¤t_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,
|
||||
¤t.secret,
|
||||
&new.secret,
|
||||
¤t_ct,
|
||||
&zeroed_handle_ciphertext,
|
||||
);
|
||||
assert!(proof
|
||||
.verify(¤t_ct, &zeroed_handle_ciphertext)
|
||||
.is_err());
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue