remove `UpdateAccountPk` instruction
This commit is contained in:
parent
b0e492bc06
commit
77e79221a0
|
@ -1,6 +1,5 @@
|
||||||
mod close_account;
|
mod close_account;
|
||||||
mod transfer;
|
mod transfer;
|
||||||
mod update_account_pk;
|
|
||||||
mod withdraw;
|
mod withdraw;
|
||||||
|
|
||||||
#[cfg(not(target_arch = "bpf"))]
|
#[cfg(not(target_arch = "bpf"))]
|
||||||
|
@ -8,7 +7,6 @@ use crate::errors::ProofError;
|
||||||
pub use {
|
pub use {
|
||||||
close_account::CloseAccountData,
|
close_account::CloseAccountData,
|
||||||
transfer::{TransferCommitments, TransferData, TransferPubKeys},
|
transfer::{TransferCommitments, TransferData, TransferPubKeys},
|
||||||
update_account_pk::UpdateAccountPkData,
|
|
||||||
withdraw::WithdrawData,
|
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)]
|
#[derive(Clone, Copy, Debug, FromPrimitive, ToPrimitive, PartialEq)]
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
pub enum ProofInstruction {
|
pub enum ProofInstruction {
|
||||||
/// Verify a `UpdateAccountPkData` struct
|
|
||||||
///
|
|
||||||
/// Accounts expected by this instruction:
|
|
||||||
/// None
|
|
||||||
///
|
|
||||||
/// Data expected by this instruction:
|
|
||||||
/// `UpdateAccountPkData`
|
|
||||||
///
|
|
||||||
VerifyUpdateAccountPk,
|
|
||||||
|
|
||||||
/// Verify a `CloseAccountData` struct
|
/// Verify a `CloseAccountData` struct
|
||||||
///
|
///
|
||||||
/// Accounts expected by this instruction:
|
/// Accounts expected by this instruction:
|
||||||
|
@ -84,10 +74,6 @@ pub fn verify_close_account(proof_data: &CloseAccountData) -> Instruction {
|
||||||
ProofInstruction::VerifyCloseAccount.encode(proof_data)
|
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 {
|
pub fn verify_withdraw(proof_data: &WithdrawData) -> Instruction {
|
||||||
ProofInstruction::VerifyWithdraw.encode(proof_data)
|
ProofInstruction::VerifyWithdraw.encode(proof_data)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue