merge
This commit is contained in:
parent
db69128825
commit
09b8baa4b1
|
@ -26,14 +26,26 @@ use {
|
|||
std::convert::TryInto,
|
||||
};
|
||||
|
||||
/// Just a grouping struct for the data required for the two transfer instructions. It is
|
||||
/// convenient to generate the two components jointly as they share common components.
|
||||
#[derive(Clone, Copy, Pod, Zeroable)]
|
||||
#[repr(C)]
|
||||
pub struct TransferData {
|
||||
pub range_proof: TransferRangeProofData,
|
||||
pub validity_proof: TransferValidityProofData,
|
||||
ephemeral_state: TransferEphemeralState, // 128 bytes
|
||||
/// The transfer amount encoded as Pedersen commitments
|
||||
pub amount_comms: TransferCommitments,
|
||||
|
||||
/// The decryption handles that allow decryption of the lo-bits of the transfer amount
|
||||
pub decrypt_handles_lo: TransferDecryptHandles,
|
||||
|
||||
/// The decryption handles that allow decryption of the hi-bits of the transfer amount
|
||||
pub decrypt_handles_hi: TransferDecryptHandles,
|
||||
|
||||
/// The public encryption keys associated with the transfer: source, dest, and auditor
|
||||
pub transfer_public_keys: TransferPubKeys, // 96 bytes
|
||||
|
||||
/// The final spendable ciphertext after the transfer
|
||||
pub new_spendable_ct: pod::ElGamalCiphertext, // 64 bytes
|
||||
|
||||
/// Zero-knowledge proofs for Transfer
|
||||
pub proof: TransferProof,
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "bpf"))]
|
||||
|
@ -65,19 +77,19 @@ impl TransferData {
|
|||
let handle_auditor_hi = auditor_pk.decrypt_handle(&open_hi);
|
||||
|
||||
// message encoding as Pedersen commitments, which will be included in range proof data
|
||||
let amount_comms = TransferComms {
|
||||
let amount_comms = TransferCommitments {
|
||||
lo: comm_lo.into(),
|
||||
hi: comm_hi.into(),
|
||||
};
|
||||
|
||||
// decryption handles, which will be included in the validity proof data
|
||||
let decryption_handles_lo = TransferHandles {
|
||||
let decrypt_handles_lo = TransferDecryptHandles {
|
||||
source: handle_source_lo.into(),
|
||||
dest: handle_dest_lo.into(),
|
||||
auditor: handle_auditor_lo.into(),
|
||||
};
|
||||
|
||||
let decryption_handles_hi = TransferHandles {
|
||||
let decrypt_handles_hi = TransferDecryptHandles {
|
||||
source: handle_source_hi.into(),
|
||||
dest: handle_dest_hi.into(),
|
||||
auditor: handle_auditor_hi.into(),
|
||||
|
@ -105,7 +117,7 @@ impl TransferData {
|
|||
};
|
||||
|
||||
// range_proof and validity_proof should be generated together
|
||||
let (transfer_proofs, ephemeral_state) = TransferProofs::new(
|
||||
let proof = TransferProof::new(
|
||||
source_sk,
|
||||
&source_pk,
|
||||
&dest_pk,
|
||||
|
@ -117,44 +129,27 @@ impl TransferData {
|
|||
&new_spendable_ct,
|
||||
);
|
||||
|
||||
// generate data components
|
||||
let range_proof = TransferRangeProofData {
|
||||
Self {
|
||||
amount_comms,
|
||||
proof: transfer_proofs.range_proof,
|
||||
};
|
||||
|
||||
let validity_proof = TransferValidityProofData {
|
||||
decryption_handles_lo,
|
||||
decryption_handles_hi,
|
||||
transfer_public_keys,
|
||||
decrypt_handles_lo,
|
||||
decrypt_handles_hi,
|
||||
new_spendable_ct: new_spendable_ct.into(),
|
||||
proof: transfer_proofs.validity_proof,
|
||||
};
|
||||
|
||||
TransferData {
|
||||
range_proof,
|
||||
validity_proof,
|
||||
ephemeral_state,
|
||||
transfer_public_keys,
|
||||
proof,
|
||||
}
|
||||
}
|
||||
|
||||
/// Extracts the lo and hi source ciphertexts associated with a transfer data and returns the
|
||||
/// *combined* ciphertext
|
||||
pub fn source_ciphertext(&self) -> Result<ElGamalCiphertext, ProofError> {
|
||||
let transfer_comms_lo: PedersenCommitment = self.range_proof.amount_comms.lo.try_into()?;
|
||||
let transfer_comms_hi: PedersenCommitment = self.range_proof.amount_comms.hi.try_into()?;
|
||||
let transfer_comms_lo: PedersenCommitment = self.amount_comms.lo.try_into()?;
|
||||
let transfer_comms_hi: PedersenCommitment = self.amount_comms.hi.try_into()?;
|
||||
let transfer_comm = combine_u32_comms(transfer_comms_lo, transfer_comms_hi);
|
||||
|
||||
let decryption_handle_lo: PedersenDecryptHandle = self
|
||||
.validity_proof
|
||||
.decryption_handles_lo
|
||||
.source
|
||||
.try_into()?;
|
||||
let decryption_handle_hi: PedersenDecryptHandle = self
|
||||
.validity_proof
|
||||
.decryption_handles_hi
|
||||
.source
|
||||
.try_into()?;
|
||||
let decryption_handle_lo: PedersenDecryptHandle =
|
||||
self.decrypt_handles_lo.source.try_into()?;
|
||||
let decryption_handle_hi: PedersenDecryptHandle =
|
||||
self.decrypt_handles_hi.source.try_into()?;
|
||||
let decryption_handle = combine_u32_handles(decryption_handle_lo, decryption_handle_hi);
|
||||
|
||||
Ok((transfer_comm, decryption_handle).into())
|
||||
|
@ -163,14 +158,14 @@ impl TransferData {
|
|||
/// Extracts the lo and hi destination ciphertexts associated with a transfer data and returns
|
||||
/// the *combined* ciphertext
|
||||
pub fn dest_ciphertext(&self) -> Result<ElGamalCiphertext, ProofError> {
|
||||
let transfer_comms_lo: PedersenCommitment = self.range_proof.amount_comms.lo.try_into()?;
|
||||
let transfer_comms_hi: PedersenCommitment = self.range_proof.amount_comms.hi.try_into()?;
|
||||
let transfer_comms_lo: PedersenCommitment = self.amount_comms.lo.try_into()?;
|
||||
let transfer_comms_hi: PedersenCommitment = self.amount_comms.hi.try_into()?;
|
||||
let transfer_comm = combine_u32_comms(transfer_comms_lo, transfer_comms_hi);
|
||||
|
||||
let decryption_handle_lo: PedersenDecryptHandle =
|
||||
self.validity_proof.decryption_handles_lo.dest.try_into()?;
|
||||
self.decrypt_handles_lo.dest.try_into()?;
|
||||
let decryption_handle_hi: PedersenDecryptHandle =
|
||||
self.validity_proof.decryption_handles_hi.dest.try_into()?;
|
||||
self.decrypt_handles_hi.dest.try_into()?;
|
||||
let decryption_handle = combine_u32_handles(decryption_handle_lo, decryption_handle_hi);
|
||||
|
||||
Ok((transfer_comm, decryption_handle).into())
|
||||
|
@ -180,101 +175,37 @@ impl TransferData {
|
|||
#[cfg(not(target_arch = "bpf"))]
|
||||
impl Verifiable for TransferData {
|
||||
fn verify(&self) -> Result<(), ProofError> {
|
||||
self.range_proof.verify(&self.ephemeral_state)?;
|
||||
self.validity_proof.verify(&self.ephemeral_state)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Pod, Zeroable)]
|
||||
#[repr(C)]
|
||||
pub struct TransferRangeProofData {
|
||||
/// The transfer amount encoded as Pedersen commitments
|
||||
pub amount_comms: TransferComms, // 64 bytes
|
||||
|
||||
/// Proof that certifies:
|
||||
/// 1. the source account has enough funds for the transfer (i.e. the final balance is a
|
||||
/// 64-bit positive number)
|
||||
/// 2. the transfer amount is a 64-bit positive number
|
||||
pub proof: pod::RangeProof128, // 736 bytes
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "bpf"))]
|
||||
impl TransferRangeProofData {
|
||||
fn verify(&self, ephemeral_state: &TransferEphemeralState) -> Result<(), ProofError> {
|
||||
let mut transcript = Transcript::new(b"TransferRangeProof");
|
||||
|
||||
// standard range proof verification
|
||||
let proof: RangeProof = self.proof.try_into()?;
|
||||
proof.verify_with(
|
||||
vec![
|
||||
&ephemeral_state.spendable_comm_verification.into(),
|
||||
&self.amount_comms.lo.into(),
|
||||
&self.amount_comms.hi.into(),
|
||||
],
|
||||
vec![64_usize, 32_usize, 32_usize],
|
||||
Some(ephemeral_state.x.into()),
|
||||
Some(ephemeral_state.z.into()),
|
||||
&mut transcript,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Pod, Zeroable)]
|
||||
#[repr(C)]
|
||||
pub struct TransferValidityProofData {
|
||||
/// The decryption handles that allow decryption of the lo-bits
|
||||
pub decryption_handles_lo: TransferHandles, // 96 bytes
|
||||
|
||||
/// The decryption handles that allow decryption of the hi-bits
|
||||
pub decryption_handles_hi: TransferHandles, // 96 bytes
|
||||
|
||||
/// The public encryption keys associated with the transfer: source, dest, and auditor
|
||||
pub transfer_public_keys: TransferPubKeys, // 96 bytes
|
||||
|
||||
/// The final spendable ciphertext after the transfer
|
||||
pub new_spendable_ct: pod::ElGamalCiphertext, // 64 bytes
|
||||
|
||||
/// Proof that certifies that the decryption handles are generated correctly
|
||||
pub proof: ValidityProof, // 160 bytes
|
||||
}
|
||||
|
||||
/// The joint data that is shared between the two transfer instructions.
|
||||
///
|
||||
/// Identical ephemeral data should be included in the two transfer instructions and this should be
|
||||
/// checked by the ZK Token program.
|
||||
#[derive(Clone, Copy, Pod, Zeroable, PartialEq)]
|
||||
#[repr(C)]
|
||||
pub struct TransferEphemeralState {
|
||||
pub spendable_comm_verification: pod::PedersenCommitment, // 32 bytes
|
||||
pub x: pod::Scalar, // 32 bytes
|
||||
pub z: pod::Scalar, // 32 bytes
|
||||
pub t_x_blinding: pod::Scalar, // 32 bytes
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "bpf"))]
|
||||
impl TransferValidityProofData {
|
||||
fn verify(&self, ephemeral_state: &TransferEphemeralState) -> Result<(), ProofError> {
|
||||
self.proof.verify(
|
||||
&self.new_spendable_ct.try_into()?,
|
||||
&self.decryption_handles_lo,
|
||||
&self.decryption_handles_hi,
|
||||
&self.amount_comms,
|
||||
&self.decrypt_handles_lo,
|
||||
&self.decrypt_handles_hi,
|
||||
&self.new_spendable_ct,
|
||||
&self.transfer_public_keys,
|
||||
ephemeral_state,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Just a grouping struct for the two proofs that are needed for a transfer instruction. The two
|
||||
/// proofs have to be generated together as they share joint data.
|
||||
#[cfg(not(target_arch = "bpf"))]
|
||||
pub struct TransferProofs {
|
||||
#[allow(non_snake_case)]
|
||||
#[derive(Clone, Copy, Pod, Zeroable)]
|
||||
#[repr(C)]
|
||||
pub struct TransferProof {
|
||||
// Proof component for the spendable ciphertext components: R
|
||||
pub R: pod::CompressedRistretto, // 32 bytes
|
||||
// Proof component for the spendable ciphertext components: z
|
||||
pub z: pod::Scalar, // 32 bytes
|
||||
// Proof component for the transaction amount components: T_src
|
||||
pub T_joint: pod::CompressedRistretto, // 32 bytes
|
||||
// Proof component for the transaction amount components: T_1
|
||||
pub T_1: pod::CompressedRistretto, // 32 bytes
|
||||
// Proof component for the transaction amount components: T_2
|
||||
pub T_2: pod::CompressedRistretto, // 32 bytes
|
||||
// Range proof component
|
||||
pub range_proof: pod::RangeProof128,
|
||||
pub validity_proof: ValidityProof,
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
#[cfg(not(target_arch = "bpf"))]
|
||||
impl TransferProofs {
|
||||
impl TransferProof {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
#[allow(clippy::many_single_char_names)]
|
||||
pub fn new(
|
||||
|
@ -287,9 +218,9 @@ impl TransferProofs {
|
|||
hi_open: &PedersenOpening,
|
||||
new_spendable_balance: u64,
|
||||
new_spendable_ct: &ElGamalCiphertext,
|
||||
) -> (Self, TransferEphemeralState) {
|
||||
) -> Self {
|
||||
// TODO: should also commit to pubkeys and commitments later
|
||||
let mut transcript_validity_proof = merlin::Transcript::new(b"TransferValidityProof");
|
||||
let mut transcript = merlin::Transcript::new(b"TransferProof");
|
||||
|
||||
let H = PedersenBase::default().H;
|
||||
let D = new_spendable_ct.decrypt_handle.get_point();
|
||||
|
@ -300,20 +231,28 @@ impl TransferProofs {
|
|||
let y = Scalar::random(&mut OsRng);
|
||||
let R = RistrettoPoint::multiscalar_mul(vec![y, r_new], vec![D, H]).compress();
|
||||
|
||||
transcript_validity_proof.append_point(b"R", &R);
|
||||
let c = transcript_validity_proof.challenge_scalar(b"c");
|
||||
transcript.append_point(b"R", &R);
|
||||
let c = transcript.challenge_scalar(b"c");
|
||||
|
||||
let z = s + c * y;
|
||||
let new_spendable_open = PedersenOpening(c * r_new);
|
||||
|
||||
let spendable_comm_verification =
|
||||
Pedersen::with(new_spendable_balance, &new_spendable_open);
|
||||
|
||||
// Generate proof for the transfer amounts
|
||||
let t_1_blinding = PedersenOpening::random(&mut OsRng);
|
||||
let t_2_blinding = PedersenOpening::random(&mut OsRng);
|
||||
|
||||
let u = transcript_validity_proof.challenge_scalar(b"u");
|
||||
// generate the range proof
|
||||
let range_proof = RangeProof::create_with(
|
||||
vec![new_spendable_balance, transfer_amt.0, transfer_amt.1],
|
||||
vec![64, 32, 32],
|
||||
vec![&new_spendable_open, lo_open, hi_open],
|
||||
&t_1_blinding,
|
||||
&t_2_blinding,
|
||||
&mut transcript,
|
||||
);
|
||||
|
||||
let u = transcript.challenge_scalar(b"u");
|
||||
|
||||
let P_joint = RistrettoPoint::multiscalar_mul(
|
||||
vec![Scalar::one(), u, u * u],
|
||||
vec![
|
||||
|
@ -326,85 +265,41 @@ impl TransferProofs {
|
|||
let T_1 = (t_1_blinding.get_scalar() * P_joint).compress();
|
||||
let T_2 = (t_2_blinding.get_scalar() * P_joint).compress();
|
||||
|
||||
transcript_validity_proof.append_point(b"T_1", &T_1);
|
||||
transcript_validity_proof.append_point(b"T_2", &T_2);
|
||||
transcript.append_point(b"T_1", &T_1);
|
||||
transcript.append_point(b"T_2", &T_2);
|
||||
|
||||
// define the validity proof
|
||||
let validity_proof = ValidityProof {
|
||||
Self {
|
||||
R: R.into(),
|
||||
z: z.into(),
|
||||
T_joint: T_joint.into(),
|
||||
T_1: T_1.into(),
|
||||
T_2: T_2.into(),
|
||||
};
|
||||
|
||||
// generate the range proof
|
||||
let mut transcript_range_proof = Transcript::new(b"TransferRangeProof");
|
||||
let (range_proof, x, z) = RangeProof::create_with(
|
||||
vec![new_spendable_balance, transfer_amt.0, transfer_amt.1],
|
||||
vec![64, 32, 32],
|
||||
vec![&new_spendable_open, lo_open, hi_open],
|
||||
&t_1_blinding,
|
||||
&t_2_blinding,
|
||||
&mut transcript_range_proof,
|
||||
);
|
||||
|
||||
// define ephemeral state
|
||||
let ephemeral_state = TransferEphemeralState {
|
||||
spendable_comm_verification: spendable_comm_verification.into(),
|
||||
x: x.into(),
|
||||
z: z.into(),
|
||||
t_x_blinding: range_proof.t_x_blinding.into(),
|
||||
};
|
||||
|
||||
(
|
||||
Self {
|
||||
range_proof: range_proof.try_into().expect("valid range_proof"),
|
||||
validity_proof,
|
||||
},
|
||||
ephemeral_state,
|
||||
)
|
||||
range_proof: range_proof.try_into().expect("invalid range proof"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Proof components for transfer instructions.
|
||||
///
|
||||
/// These two components should be output by a RangeProof creation function.
|
||||
#[allow(non_snake_case)]
|
||||
#[derive(Clone, Copy, Pod, Zeroable)]
|
||||
#[repr(C)]
|
||||
pub struct ValidityProof {
|
||||
// Proof component for the spendable ciphertext components: R
|
||||
pub R: pod::CompressedRistretto, // 32 bytes
|
||||
// Proof component for the spendable ciphertext components: z
|
||||
pub z: pod::Scalar, // 32 bytes
|
||||
// Proof component for the transaction amount components: T_src
|
||||
pub T_joint: pod::CompressedRistretto, // 32 bytes
|
||||
// Proof component for the transaction amount components: T_1
|
||||
pub T_1: pod::CompressedRistretto, // 32 bytes
|
||||
// Proof component for the transaction amount components: T_2
|
||||
pub T_2: pod::CompressedRistretto, // 32 bytes
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
#[cfg(not(target_arch = "bpf"))]
|
||||
impl ValidityProof {
|
||||
impl TransferProof {
|
||||
pub fn verify(
|
||||
self,
|
||||
new_spendable_ct: &ElGamalCiphertext,
|
||||
decryption_handles_lo: &TransferHandles,
|
||||
decryption_handles_hi: &TransferHandles,
|
||||
amount_comms: &TransferCommitments,
|
||||
decryption_handles_lo: &TransferDecryptHandles,
|
||||
decryption_handles_hi: &TransferDecryptHandles,
|
||||
new_spendable_ct: &pod::ElGamalCiphertext,
|
||||
transfer_public_keys: &TransferPubKeys,
|
||||
ephemeral_state: &TransferEphemeralState,
|
||||
) -> Result<(), ProofError> {
|
||||
let mut transcript = Transcript::new(b"TransferValidityProof");
|
||||
let mut transcript = Transcript::new(b"TransferProof");
|
||||
|
||||
let range_proof: RangeProof = self.range_proof.try_into()?;
|
||||
|
||||
let source_pk: ElGamalPubkey = transfer_public_keys.source_pk.try_into()?;
|
||||
let dest_pk: ElGamalPubkey = transfer_public_keys.dest_pk.try_into()?;
|
||||
let auditor_pk: ElGamalPubkey = transfer_public_keys.auditor_pk.try_into()?;
|
||||
|
||||
// verify Pedersen commitment in the ephemeral state
|
||||
let C_ephemeral: CompressedRistretto = ephemeral_state.spendable_comm_verification.into();
|
||||
// derive Pedersen commitment for range proof verification
|
||||
let new_spendable_ct: ElGamalCiphertext = (*new_spendable_ct).try_into()?;
|
||||
|
||||
let C = new_spendable_ct.message_comm.get_point();
|
||||
let D = new_spendable_ct.decrypt_handle.get_point();
|
||||
|
@ -420,9 +315,23 @@ impl ValidityProof {
|
|||
let spendable_comm_verification =
|
||||
RistrettoPoint::multiscalar_mul(vec![Scalar::one(), -z, c], vec![C, D, R]).compress();
|
||||
|
||||
if C_ephemeral != spendable_comm_verification {
|
||||
// verify range proof
|
||||
let range_proof_verification = range_proof.verify_challenges(
|
||||
vec![
|
||||
&spendable_comm_verification,
|
||||
&amount_comms.lo.into(),
|
||||
&amount_comms.hi.into(),
|
||||
],
|
||||
vec![64_usize, 32_usize, 32_usize],
|
||||
&mut transcript,
|
||||
);
|
||||
|
||||
if range_proof_verification.is_err() {
|
||||
return Err(ProofError::VerificationError);
|
||||
}
|
||||
let (z, x) = range_proof_verification.unwrap();
|
||||
|
||||
// check well-formedness of decryption handles
|
||||
|
||||
// derive joint public key
|
||||
let u = transcript.challenge_scalar(b"u");
|
||||
|
@ -435,14 +344,10 @@ impl ValidityProof {
|
|||
],
|
||||
);
|
||||
|
||||
// check well-formedness of decryption handles
|
||||
let t_x_blinding: Scalar = ephemeral_state.t_x_blinding.into();
|
||||
let t_x_blinding: Scalar = range_proof.t_x_blinding;
|
||||
let T_1: CompressedRistretto = self.T_1.into();
|
||||
let T_2: CompressedRistretto = self.T_2.into();
|
||||
|
||||
let x = ephemeral_state.x.into();
|
||||
let z: Scalar = ephemeral_state.z.into();
|
||||
|
||||
let handle_source_lo: PedersenDecryptHandle = decryption_handles_lo.source.try_into()?;
|
||||
let handle_dest_lo: PedersenDecryptHandle = decryption_handles_lo.dest.try_into()?;
|
||||
let handle_auditor_lo: PedersenDecryptHandle = decryption_handles_lo.auditor.try_into()?;
|
||||
|
@ -507,7 +412,7 @@ pub struct TransferPubKeys {
|
|||
/// The transfer amount commitments needed for a transfer
|
||||
#[derive(Clone, Copy, Pod, Zeroable)]
|
||||
#[repr(C)]
|
||||
pub struct TransferComms {
|
||||
pub struct TransferCommitments {
|
||||
pub lo: pod::PedersenCommitment, // 32 bytes
|
||||
pub hi: pod::PedersenCommitment, // 32 bytes
|
||||
}
|
||||
|
@ -515,7 +420,7 @@ pub struct TransferComms {
|
|||
/// The decryption handles needed for a transfer
|
||||
#[derive(Clone, Copy, Pod, Zeroable)]
|
||||
#[repr(C)]
|
||||
pub struct TransferHandles {
|
||||
pub struct TransferDecryptHandles {
|
||||
pub source: pod::PedersenDecryptHandle, // 32 bytes
|
||||
pub dest: pod::PedersenDecryptHandle, // 32 bytes
|
||||
pub auditor: pod::PedersenDecryptHandle, // 32 bytes
|
||||
|
|
Loading…
Reference in New Issue