use crate::field::Goldilocks4;
pub(crate) struct ZkEncoding {
pub(crate) msg_len: usize,
pub(crate) rand_len: usize,
pub(crate) codeword_len: usize,
}
impl ZkEncoding {
pub(crate) fn new_at_rate(msg_len: usize, rand_len: usize, rate_inv: usize) -> Self {
let total = msg_len + rand_len;
assert!(
total.is_power_of_two(),
"ZkEncoding::new_at_rate: msg_len + rand_len must be a power of two, got {total}"
);
assert!(
rate_inv.is_power_of_two(),
"ZkEncoding::new_at_rate: rate_inv must be a power of two, got {rate_inv}"
);
Self {
msg_len,
rand_len,
codeword_len: total * rate_inv,
}
}
pub(crate) fn new(msg_len: usize, rand_len: usize) -> Self {
Self::new_at_rate(msg_len, rand_len, crate::params::Params::RATE_INV_ZK)
}
pub(crate) fn encode_with(&self, msg: &[Goldilocks4], r: &[Goldilocks4]) -> Vec<Goldilocks4> {
assert_eq!(msg.len(), self.msg_len);
assert_eq!(r.len(), self.rand_len);
let mut padded: Vec<Goldilocks4> = Vec::with_capacity(self.codeword_len);
padded.extend_from_slice(msg);
padded.extend_from_slice(r);
padded.resize(self.codeword_len, Goldilocks4::default());
Goldilocks4::ntt(padded)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::field::Goldilocks;
fn g4(v: u64) -> Goldilocks4 {
Goldilocks4::new([
Goldilocks::new(v),
Goldilocks::new(0),
Goldilocks::new(0),
Goldilocks::new(0),
])
}
#[test]
fn encoding_is_linear() {
let enc = ZkEncoding::new(4, 4);
let m1: Vec<_> = (0..4).map(|i| g4(i as u64)).collect();
let m2: Vec<_> = (0..4).map(|i| g4(10 + i as u64)).collect();
let r1: Vec<_> = (0..4).map(|i| g4(100 + i as u64)).collect();
let r2: Vec<_> = (0..4).map(|i| g4(200 + i as u64)).collect();
let c1 = enc.encode_with(&m1, &r1);
let c2 = enc.encode_with(&m2, &r2);
let m_sum: Vec<_> = m1.iter().zip(&m2).map(|(a, b)| *a + *b).collect();
let r_sum: Vec<_> = r1.iter().zip(&r2).map(|(a, b)| *a + *b).collect();
let c_sum = enc.encode_with(&m_sum, &r_sum);
for i in 0..enc.codeword_len {
assert_eq!(c1[i] + c2[i], c_sum[i], "linearity at i={i}");
}
}
}