use crate::{
core::{
circuits::boolean::{
boolean_value::{Boolean, BooleanValue},
byte::Byte,
},
global_value::{curve_value::CompressedCurveValue, value::FieldValue},
},
traits::{FromLeBits, ToLeBytes},
utils::{
field::ScalarField,
zkp::{
elgamal::ElGamalCiphertext,
grouped_elgamal::{
GroupedElGamalCiphertext2Handles,
GroupedElGamalCiphertext3Handles,
GROUPED_ELGAMAL_CIPHERTEXT_2_HANDLES_LEN,
GROUPED_ELGAMAL_CIPHERTEXT_3_HANDLES_LEN,
},
strobe::Strobe128,
},
},
};
use zk_elgamal_proof::encryption::{
DECRYPT_HANDLE_LEN,
ELGAMAL_CIPHERTEXT_LEN,
PEDERSEN_COMMITMENT_LEN,
};
pub const MERLIN_PROTOCOL_LABEL: &[u8] = b"Merlin v1.0";
fn encode_u64(x: u64) -> [u8; 8] {
use byteorder::{ByteOrder, LittleEndian};
let mut buf = [0; 8];
LittleEndian::write_u64(&mut buf, x);
buf
}
fn encode_usize_as_u32(x: usize) -> [u8; 4] {
use byteorder::{ByteOrder, LittleEndian};
assert!(x <= (u32::MAX as usize));
let mut buf = [0; 4];
LittleEndian::write_u32(&mut buf, x as u32);
buf
}
#[derive(Clone)]
pub struct Transcript<B: Boolean> {
strobe: Strobe128<B>,
}
impl<B: Boolean> Transcript<B> {
pub fn new(label: &'static [u8]) -> Transcript<B> {
let mut transcript = Transcript {
strobe: Strobe128::new(MERLIN_PROTOCOL_LABEL),
};
transcript.append_message(
b"dom-sep",
&label
.iter()
.copied()
.map(Byte::<B>::from)
.collect::<Vec<Byte<B>>>(),
);
transcript
}
pub fn append_message(&mut self, label: &'static [u8], message: &[Byte<B>]) {
let data_len = encode_usize_as_u32(message.len());
self.strobe.meta_ad(label, false);
self.strobe.meta_ad(&data_len, true);
self.strobe.ad(message, false);
}
fn append_u64(&mut self, label: &'static [u8], x: u64) {
self.append_message(
label,
&encode_u64(x)
.into_iter()
.map(Byte::<B>::from)
.collect::<Vec<Byte<B>>>(),
);
}
pub fn challenge_bytes(&mut self, label: &'static [u8], dest: &mut [Byte<B>]) {
let data_len = encode_usize_as_u32(dest.len());
self.strobe.meta_ad(label, false);
self.strobe.meta_ad(&data_len, true);
self.strobe.prf(dest, false);
}
pub fn range_proof_domain_separator(&mut self, n: u64) {
self.append_message(
b"dom-sep",
&b"range-proof"
.iter()
.copied()
.map(Byte::<B>::from)
.collect::<Vec<Byte<B>>>(),
);
self.append_u64(b"n", n);
}
pub fn inner_product_proof_domain_separator(&mut self, n: u64) {
self.append_message(
b"dom-sep",
&b"inner-product"
.iter()
.copied()
.map(Byte::<B>::from)
.collect::<Vec<Byte<B>>>(),
);
self.append_u64(b"n", n);
}
pub fn ciphertext_commitment_equality_proof_domain_separator(&mut self) {
self.append_message(
b"dom-sep",
&b"ciphertext-commitment-equality-proof"
.iter()
.copied()
.map(Byte::<B>::from)
.collect::<Vec<Byte<B>>>(),
)
}
pub fn zero_ciphertext_proof_domain_separator(&mut self) {
self.append_message(
b"dom-sep",
&b"zero-ciphertext-proof"
.iter()
.copied()
.map(Byte::<B>::from)
.collect::<Vec<Byte<B>>>(),
)
}
pub fn grouped_ciphertext_validity_proof_domain_separator(&mut self, handles: u64) {
self.append_message(
b"dom-sep",
&b"validity-proof"
.iter()
.copied()
.map(Byte::<B>::from)
.collect::<Vec<Byte<B>>>(),
);
self.append_u64(b"handles", handles);
}
pub fn batched_grouped_ciphertext_validity_proof_domain_separator(&mut self, handles: u64) {
self.append_message(
b"dom-sep",
&b"batched-validity-proof"
.iter()
.copied()
.map(Byte::<B>::from)
.collect::<Vec<Byte<B>>>(),
);
self.append_u64(b"handles", handles);
}
pub fn percentage_with_cap_proof_domain_separator(&mut self) {
self.append_message(
b"dom-sep",
&b"percentage-with-cap-proof"
.iter()
.copied()
.map(Byte::<B>::from)
.collect::<Vec<Byte<B>>>(),
)
}
pub fn pubkey_proof_domain_separator(&mut self) {
self.append_message(
b"dom-sep",
&b"pubkey-proof"
.iter()
.copied()
.map(Byte::<B>::from)
.collect::<Vec<Byte<B>>>(),
)
}
}
impl Transcript<BooleanValue> {
pub fn append_scalar(&mut self, label: &'static [u8], scalar: &FieldValue<ScalarField>) {
self.append_message(label, &scalar.to_le_bytes());
}
pub fn append_point(&mut self, label: &'static [u8], point: &CompressedCurveValue) {
self.append_message(label, &point.to_bytes());
}
pub fn append_elgamal_ciphertext(
&mut self,
label: &'static [u8],
ciphertext: &ElGamalCiphertext,
) {
let mut bytes = [Byte::<BooleanValue>::from(0u8); ELGAMAL_CIPHERTEXT_LEN];
bytes[..PEDERSEN_COMMITMENT_LEN]
.copy_from_slice(&ciphertext.commitment.get_point().compress().to_bytes());
bytes[PEDERSEN_COMMITMENT_LEN..]
.copy_from_slice(&ciphertext.handle.get_point().compress().to_bytes());
self.append_message(label, &bytes);
}
pub fn append_elgamal_ciphertext_2_handles(
&mut self,
label: &'static [u8],
ciphertext: &GroupedElGamalCiphertext2Handles,
) {
let mut bytes = [Byte::<BooleanValue>::from(0u8); GROUPED_ELGAMAL_CIPHERTEXT_2_HANDLES_LEN];
bytes[..PEDERSEN_COMMITMENT_LEN]
.copy_from_slice(&ciphertext.0.commitment.get_point().compress().to_bytes());
let mut offset = PEDERSEN_COMMITMENT_LEN;
for i in 0..2 {
bytes[offset..offset + DECRYPT_HANDLE_LEN]
.copy_from_slice(&ciphertext.0.handles[i].get_point().compress().to_bytes());
offset += DECRYPT_HANDLE_LEN;
}
self.append_message(label, &bytes);
}
pub fn append_elgamal_ciphertext_3_handles(
&mut self,
label: &'static [u8],
ciphertext: &GroupedElGamalCiphertext3Handles,
) {
let mut bytes = [Byte::<BooleanValue>::from(0u8); GROUPED_ELGAMAL_CIPHERTEXT_3_HANDLES_LEN];
bytes[..PEDERSEN_COMMITMENT_LEN]
.copy_from_slice(&ciphertext.0.commitment.get_point().compress().to_bytes());
let mut offset = PEDERSEN_COMMITMENT_LEN;
for i in 0..3 {
bytes[offset..offset + DECRYPT_HANDLE_LEN]
.copy_from_slice(&ciphertext.0.handles[i].get_point().compress().to_bytes());
offset += DECRYPT_HANDLE_LEN;
}
self.append_message(label, &bytes);
}
pub fn challenge_scalar(&mut self, label: &'static [u8]) -> FieldValue<ScalarField> {
let mut buf = [Byte::from(0u8); 64];
self.challenge_bytes(label, &mut buf);
let buf_bits = buf
.into_iter()
.flat_map(|byte| byte.to_vec())
.collect::<Vec<BooleanValue>>();
FieldValue::<ScalarField>::from_le_bits(buf_bits, false)
}
}