use alloc::vec::Vec;
use dusk_bytes::{DeserializableSlice, Serializable};
use dusk_curves::bls12_381::BlsScalar;
use merlin::Transcript;
use super::PlonkVersion;
use crate::commitment_scheme::OpeningKey;
use crate::error::Error;
use crate::proof_system::{Proof, VerifierKey};
use crate::transcript::TranscriptProtocol;
pub struct Verifier {
label: Vec<u8>,
verifier_key: VerifierKey,
opening_key: OpeningKey,
public_input_indexes: Vec<usize>,
transcript: Transcript,
size: usize,
constraints: usize,
}
impl Verifier {
pub(crate) fn new(
label: Vec<u8>,
verifier_key: VerifierKey,
opening_key: OpeningKey,
public_input_indexes: Vec<usize>,
size: usize,
constraints: usize,
) -> Self {
let transcript =
Transcript::base(label.as_slice(), &verifier_key, constraints);
Self {
label,
verifier_key,
opening_key,
public_input_indexes,
transcript,
size,
constraints,
}
}
fn prepare_serialize(
&self,
) -> (usize, [u8; VerifierKey::SIZE], [u8; OpeningKey::SIZE]) {
let verifier_key = self.verifier_key.to_bytes();
let opening_key = self.opening_key.to_bytes();
let label_len = self.label.len();
let verifier_key_len = verifier_key.len();
let opening_key_len = opening_key.len();
let public_input_indexes_len = self.public_input_indexes.len() * 8;
let size = 48
+ label_len
+ verifier_key_len
+ opening_key_len
+ public_input_indexes_len;
(size, verifier_key, opening_key)
}
pub fn serialized_size(&self) -> usize {
self.prepare_serialize().0
}
pub fn to_bytes(&self) -> Vec<u8> {
let (size, verifier_key, opening_key) = self.prepare_serialize();
let mut bytes = Vec::with_capacity(size);
let label_len = self.label.len() as u64;
let verifier_key_len = verifier_key.len() as u64;
let opening_key_len = opening_key.len() as u64;
let public_input_indexes_len = self.public_input_indexes.len() as u64;
let size = self.size as u64;
let constraints = self.constraints as u64;
bytes.extend(label_len.to_be_bytes());
bytes.extend(verifier_key_len.to_be_bytes());
bytes.extend(opening_key_len.to_be_bytes());
bytes.extend(public_input_indexes_len.to_be_bytes());
bytes.extend(size.to_be_bytes());
bytes.extend(constraints.to_be_bytes());
bytes.extend(self.label.as_slice());
bytes.extend(verifier_key);
bytes.extend(opening_key);
self.public_input_indexes
.iter()
.map(|i| *i as u64)
.map(u64::to_be_bytes)
.for_each(|i| bytes.extend(i));
bytes
}
pub fn try_from_bytes<B>(bytes: B) -> Result<Self, Error>
where
B: AsRef<[u8]>,
{
let mut bytes = bytes.as_ref();
if bytes.len() < 48 {
return Err(Error::NotEnoughBytes);
}
let label_len = <[u8; 8]>::try_from(&bytes[..8]).expect("checked len");
let label_len = u64::from_be_bytes(label_len) as usize;
bytes = &bytes[8..];
let verifier_key_len =
<[u8; 8]>::try_from(&bytes[..8]).expect("checked len");
let verifier_key_len = u64::from_be_bytes(verifier_key_len) as usize;
bytes = &bytes[8..];
let opening_key_len =
<[u8; 8]>::try_from(&bytes[..8]).expect("checked len");
let opening_key_len = u64::from_be_bytes(opening_key_len) as usize;
bytes = &bytes[8..];
let public_input_indexes_len =
<[u8; 8]>::try_from(&bytes[..8]).expect("checked len");
let public_input_indexes_len =
u64::from_be_bytes(public_input_indexes_len) as usize;
bytes = &bytes[8..];
let public_input_indexes_bytes_len = public_input_indexes_len
.checked_mul(8)
.ok_or(Error::NotEnoughBytes)?;
let size = <[u8; 8]>::try_from(&bytes[..8]).expect("checked len");
let size = u64::from_be_bytes(size) as usize;
bytes = &bytes[8..];
let constraints =
<[u8; 8]>::try_from(&bytes[..8]).expect("checked len");
let constraints = u64::from_be_bytes(constraints) as usize;
bytes = &bytes[8..];
let required_len = label_len
.checked_add(verifier_key_len)
.and_then(|len| len.checked_add(opening_key_len))
.and_then(|len| len.checked_add(public_input_indexes_bytes_len))
.ok_or(Error::NotEnoughBytes)?;
if bytes.len() < required_len {
return Err(Error::NotEnoughBytes);
}
let label = &bytes[..label_len];
bytes = &bytes[label_len..];
let verifier_key = &bytes[..verifier_key_len];
bytes = &bytes[verifier_key_len..];
let opening_key = &bytes[..opening_key_len];
bytes = &bytes[opening_key_len..];
let public_input_indexes = &bytes[..public_input_indexes_bytes_len];
let label = label.to_vec();
let verifier_key = VerifierKey::from_slice(verifier_key)?;
let opening_key = OpeningKey::from_slice(opening_key)?;
let public_input_indexes = public_input_indexes
.chunks_exact(8)
.map(|c| <[u8; 8]>::try_from(c).expect("checked len"))
.map(u64::from_be_bytes)
.map(|n| n as usize)
.collect();
Ok(Self::new(
label,
verifier_key,
opening_key,
public_input_indexes,
size,
constraints,
))
}
pub fn verify(
&self,
proof: &Proof,
public_inputs: &[BlsScalar],
) -> Result<(), Error> {
self.verify_with_version(proof, public_inputs, PlonkVersion::current())
}
pub fn verify_with_version(
&self,
proof: &Proof,
public_inputs: &[BlsScalar],
version: PlonkVersion,
) -> Result<(), Error> {
if public_inputs.len() != self.public_input_indexes.len() {
return Err(Error::InconsistentPublicInputsLen {
expected: self.public_input_indexes.len(),
provided: public_inputs.len(),
});
}
let mut transcript = self.transcript_for_version(version);
public_inputs
.iter()
.for_each(|pi| transcript.append_scalar(b"pi", pi));
match version {
PlonkVersion::V1 => proof.verify_legacy(
&self.verifier_key,
&mut transcript,
&self.opening_key,
&self.public_input_indexes,
public_inputs,
),
PlonkVersion::V2 | PlonkVersion::V3 => proof.verify(
&self.verifier_key,
&mut transcript,
&self.opening_key,
&self.public_input_indexes,
public_inputs,
),
}
}
fn transcript_for_version(&self, version: PlonkVersion) -> Transcript {
match version {
PlonkVersion::V1 | PlonkVersion::V2 => self.transcript.clone(),
PlonkVersion::V3 => Transcript::base_v3(
self.label.as_slice(),
&self.verifier_key,
self.constraints,
),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn verifier_try_from_bytes_rejects_overflow_lengths_without_panicking() {
let mut bytes = Vec::with_capacity(48);
bytes.extend_from_slice(&0u64.to_be_bytes()); bytes.extend_from_slice(&0u64.to_be_bytes()); bytes.extend_from_slice(&0u64.to_be_bytes()); bytes.extend_from_slice(&u64::MAX.to_be_bytes()); bytes.extend_from_slice(&0u64.to_be_bytes()); bytes.extend_from_slice(&0u64.to_be_bytes());
let result =
std::panic::catch_unwind(|| Verifier::try_from_bytes(&bytes));
assert!(
result.is_ok(),
"try_from_bytes panicked on overflow lengths"
);
assert!(matches!(result.unwrap(), Err(Error::NotEnoughBytes)));
}
}