use midnight_proofs::{
circuit::{Layouter, Value},
plonk::Error,
};
use super::{ScannerChip, ALPHABET_MAX_SIZE};
use crate::{
field::{native::AssignedBit, AssignedNative},
instructions::{
AssignmentInstructions, ControlFlowInstructions, UnsafeConversionInstructions,
VectorInstructions,
},
types::{AssignedByte, AssignedVector},
CircuitField,
};
#[derive(Debug, Clone)]
pub struct ScannerVec<F: CircuitField, const M: usize, const A: usize> {
length: AssignedNative<F>,
pub(crate) buffer: Box<[AssignedNative<F>; M]>,
pub(crate) limits: (AssignedNative<F>, AssignedNative<F>),
pub(crate) padding_flags: Box<[AssignedBit<F>; M]>,
}
impl<F: CircuitField, const M: usize, const A: usize> ScannerVec<F, M, A> {
pub fn get_limits(&self) -> &(AssignedNative<F>, AssignedNative<F>) {
&self.limits
}
pub fn padding_flags(&self) -> &[AssignedBit<F>; M] {
&self.padding_flags
}
pub fn len(&self) -> &AssignedNative<F> {
&self.length
}
}
impl<F: CircuitField, const M: usize, const A: usize> From<ScannerVec<F, M, A>>
for AssignedVector<F, AssignedNative<F>, M, A>
{
fn from(value: ScannerVec<F, M, A>) -> Self {
AssignedVector {
buffer: value.buffer,
len: value.length,
}
}
}
impl<F> ScannerChip<F>
where
F: CircuitField + Ord,
{
pub fn scanner_vec_to_byte_vector<const M: usize, const A: usize>(
&self,
layouter: &mut impl Layouter<F>,
sv: &ScannerVec<F, M, A>,
) -> Result<AssignedVector<F, AssignedByte<F>, M, A>, Error> {
let zero = self.native_gadget.assign_fixed(layouter, F::ZERO)?;
let byte_buffer: Vec<AssignedByte<F>> = (sv.buffer.iter().zip(sv.padding_flags.iter()))
.map(|(elem, flag)| {
let zeroed = self.native_gadget.select(layouter, flag, &zero, elem)?;
self.native_gadget.convert_unsafe(layouter, &zeroed)
})
.collect::<Result<Vec<_>, Error>>()?;
Ok(AssignedVector {
buffer: Box::new(byte_buffer.try_into().unwrap()),
len: sv.length.clone(),
})
}
pub fn assign_scanner_vec<const M: usize, const A: usize>(
&self,
layouter: &mut impl Layouter<F>,
value: Value<Vec<u8>>,
) -> Result<ScannerVec<F, M, A>, Error> {
let byte_vec: AssignedVector<F, AssignedByte<F>, M, A> =
self.vector_gadget.assign_with_filler(layouter, value, None)?;
self.scanner_vec_from_byte_vec(layouter, byte_vec)
}
pub fn scanner_vec_from_byte_vec<const M: usize, const A: usize>(
&self,
layouter: &mut impl Layouter<F>,
vec: AssignedVector<F, AssignedByte<F>, M, A>,
) -> Result<ScannerVec<F, M, A>, Error> {
let (padding_flags, limits) = self.vector_gadget.padding_flag(layouter, &vec)?;
let filler =
self.native_gadget.assign_fixed(layouter, F::from(ALPHABET_MAX_SIZE as u64))?;
let buffer: Box<[AssignedNative<F>; M]> = Box::new(
(vec.buffer.iter().zip(padding_flags.iter()))
.map(|(elem, is_padding)| {
let native_elem = AssignedNative::from(elem);
self.native_gadget.select(layouter, is_padding, &filler, &native_elem)
})
.collect::<Result<Vec<_>, Error>>()?
.try_into()
.expect("Length mismatch in ScannerVec buffer"),
);
Ok(ScannerVec {
length: vec.len,
buffer,
limits,
padding_flags,
})
}
}