use core::mem;
use std::io;
use crate::{AtomicConfig, Config, Constraint, Context, ContextUnit, Element, Source, Witness};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Preamble {
pub witnesses: usize,
pub constraints: usize,
pub config: Config,
}
impl Default for Preamble {
fn default() -> Self {
Self::DEFAULT
}
}
impl Preamble {
pub const LEN: usize = 2 * mem::size_of::<usize>() + Config::LEN;
pub const DEFAULT: Self = Self {
witnesses: 1,
constraints: 0,
config: Config::DEFAULT,
};
pub const fn new() -> Self {
Self::DEFAULT
}
pub fn with_witnesses(&mut self, witnesses: usize) -> &mut Self {
self.witnesses = witnesses;
self
}
pub fn with_constraints(&mut self, constraints: usize) -> &mut Self {
self.constraints = constraints;
self
}
pub fn with_config(&mut self, config: Config) -> &mut Self {
self.config = config;
self
}
pub fn witness_offset(&self, idx: usize) -> Option<usize> {
(idx < self.witnesses).then(|| Self::LEN + idx * Witness::len(&self.config))
}
pub fn constraint_offset(&self, idx: usize) -> Option<usize> {
(idx < self.constraints).then(|| {
Self::LEN
+ self.witnesses * Witness::len(&self.config)
+ idx * Constraint::len(&self.config)
})
}
pub fn source_cache_offset(&self, idx: usize) -> usize {
Self::LEN
+ self.witnesses * Witness::len(&self.config)
+ self.constraints * Constraint::len(&self.config)
+ idx * Source::PATH_LEN as usize
}
}
impl Element for Preamble {
type Config = AtomicConfig;
fn zeroed() -> Self {
Self::DEFAULT
}
fn len(_config: &Self::Config) -> usize {
Self::LEN
}
fn to_buffer(&self, _config: &Self::Config, context: &mut ContextUnit, buf: &mut [u8]) {
let buf = self.witnesses.encode(&AtomicConfig, context, buf);
let buf = self.constraints.encode(&AtomicConfig, context, buf);
let _ = self.config.encode(&AtomicConfig, context, buf);
}
fn try_from_buffer_in_place<S>(
&mut self,
config: &Self::Config,
context: &mut Context<S>,
buf: &[u8],
) -> io::Result<()>
where
S: io::Read + io::Seek,
{
Self::validate_buffer_len(config, buf.len())?;
let buf = self
.witnesses
.try_decode_in_place(&AtomicConfig, context, buf)?;
let buf = self
.constraints
.try_decode_in_place(&AtomicConfig, context, buf)?;
let _ = self
.config
.try_decode_in_place(&AtomicConfig, context, buf)?;
Ok(())
}
fn validate(&self, _preamble: &Preamble) -> io::Result<()> {
if self.witnesses == 0 {
return Err(io::Error::new(
io::ErrorKind::Other,
"witness count can't be zero in PLONK protocol",
));
}
Ok(())
}
}
#[test]
fn validate_works() {
Preamble::zeroed()
.validate(&Default::default())
.expect("default config validate should pass");
Preamble::new()
.with_witnesses(0)
.validate(&Default::default())
.expect_err("zeroed witness count isn't a valid circuit");
}