use super::vc::{MerkleVc, Opening, VectorCommitment};
use crate::field::Goldilocks4;
pub(crate) enum MaskOracleHandle {
Prover {
msg: Vec<Goldilocks4>,
r: Vec<Goldilocks4>,
vc: MerkleVc,
vc_state: <MerkleVc as VectorCommitment>::CommitState,
},
VerifierRootOnly { root: [u8; 32] },
}
impl MaskOracleHandle {
pub(crate) fn new_prover(
msg: Vec<Goldilocks4>,
r: Vec<Goldilocks4>,
vc: MerkleVc,
vc_state: <MerkleVc as VectorCommitment>::CommitState,
) -> Self {
Self::Prover {
msg,
r,
vc,
vc_state,
}
}
pub(crate) fn verifier_root_only(root: [u8; 32]) -> Self {
Self::VerifierRootOnly { root }
}
pub(crate) fn message(&self) -> &[Goldilocks4] {
match self {
Self::Prover { msg, .. } => msg,
Self::VerifierRootOnly { .. } => panic!("verifier handle has no message"),
}
}
pub(crate) fn randomness(&self) -> &[Goldilocks4] {
match self {
Self::Prover { r, .. } => r,
Self::VerifierRootOnly { .. } => panic!("verifier handle has no randomness"),
}
}
pub(crate) fn open(&self, positions: &[usize]) -> Opening<MerkleVc> {
match self {
Self::Prover { vc, vc_state, .. } => vc.open(vc_state, positions),
Self::VerifierRootOnly { .. } => panic!("verifier handle cannot open"),
}
}
pub(crate) fn verify_openings(
&self,
positions: &[usize],
proof: &Opening<MerkleVc>,
) -> Result<Vec<Goldilocks4>, spongefish::VerificationError> {
match self {
Self::VerifierRootOnly { root } => {
if proof.openings.len() != positions.len() {
return Err(spongefish::VerificationError);
}
let codeword_len = mask_codeword_len();
let vc = MerkleVc::new(codeword_len);
if !vc.verify(root, positions, proof) {
return Err(spongefish::VerificationError);
}
Ok(proof.openings.iter().map(|o| o[0]).collect())
}
_ => panic!("prover handle cannot verify"),
}
}
pub(crate) fn path_len(&self) -> usize {
mask_codeword_len().next_power_of_two().trailing_zeros() as usize
}
}
fn mask_codeword_len() -> usize {
let m_zk = crate::params::Params::M_ZK;
let t_zk = crate::params::Params::T_ZK;
super::encoding::ZkEncoding::new(m_zk - t_zk, t_zk).codeword_len
}
pub(crate) struct MaskConstraint {
pub alpha: Goldilocks4,
pub target: Goldilocks4,
pub sl_o_eval_point: Vec<Goldilocks4>,
}
impl MaskConstraint {
pub fn evaluate_sl(&self, msg: &[Goldilocks4]) -> Goldilocks4 {
assert_eq!(msg.len(), self.sl_o_eval_point.len());
msg.iter()
.zip(&self.sl_o_eval_point)
.map(|(a, b)| *a * *b)
.sum()
}
}
pub(crate) struct MaskStack {
pub oracles: Vec<MaskOracleHandle>,
pub constraints: Vec<MaskConstraint>,
}
impl MaskStack {
pub fn new() -> Self {
Self {
oracles: Vec::new(),
constraints: Vec::new(),
}
}
pub fn len(&self) -> usize {
debug_assert_eq!(self.oracles.len(), self.constraints.len());
self.oracles.len()
}
pub fn is_empty(&self) -> bool {
self.oracles.is_empty()
}
pub fn scale_alphas(&mut self, epsilon: Goldilocks4) {
for mc in &mut self.constraints {
mc.alpha *= epsilon;
}
}
pub fn push_sumcheck_masks(
&mut self,
masks: Vec<MaskOracleHandle>,
gammas: Vec<Goldilocks4>,
targets: Vec<Goldilocks4>,
) {
assert_eq!(masks.len(), gammas.len());
assert_eq!(masks.len(), targets.len());
for ((mask, gamma), target) in masks.into_iter().zip(gammas).zip(targets) {
let sl_o_eval_point = build_sumcheck_sl(gamma);
self.oracles.push(mask);
self.constraints.push(MaskConstraint {
alpha: Goldilocks4::ONE,
target,
sl_o_eval_point,
});
}
}
pub fn joint_mask_value(&self) -> Goldilocks4 {
self.constraints.iter().map(|mc| mc.alpha * mc.target).sum()
}
pub fn push_padding_mask(
&mut self,
mask: MaskOracleHandle,
alpha: Goldilocks4,
sl_o_eval_point: Vec<Goldilocks4>,
) {
self.oracles.push(mask);
self.constraints.push(MaskConstraint {
alpha,
target: Goldilocks4::ZERO,
sl_o_eval_point,
});
}
}
pub(crate) fn build_sumcheck_sl(gamma: Goldilocks4) -> Vec<Goldilocks4> {
let l_zk_inner = crate::params::Params::M_ZK - crate::params::Params::T_ZK;
debug_assert!(
l_zk_inner >= 3,
"L_ZK_INNER must accommodate degree-2 mask polynomial"
);
let mut sl = vec![Goldilocks4::ZERO; l_zk_inner];
sl[0] = Goldilocks4::ONE;
sl[1] = gamma;
sl[2] = gamma * gamma;
sl
}
#[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 new_mask_stack_is_empty() {
let stack = MaskStack::new();
assert!(stack.is_empty());
assert_eq!(stack.len(), 0);
}
#[test]
fn push_sumcheck_masks_grows_by_k_and_sets_alphas() {
let mut stack = MaskStack::new();
let masks = vec![
MaskOracleHandle::verifier_root_only([1u8; 32]),
MaskOracleHandle::verifier_root_only([2u8; 32]),
];
let gammas = vec![g4(7), g4(11)];
let targets = vec![g4(100), g4(200)];
stack.push_sumcheck_masks(masks, gammas, targets);
assert_eq!(stack.len(), 2);
assert_eq!(stack.constraints[0].alpha, g4(1));
assert_eq!(stack.constraints[1].alpha, g4(1));
assert_eq!(stack.constraints[0].target, g4(100));
assert_eq!(stack.constraints[1].target, g4(200));
assert_eq!(stack.constraints[0].sl_o_eval_point[0], g4(1));
assert_eq!(stack.constraints[0].sl_o_eval_point[1], g4(7));
assert_eq!(stack.constraints[0].sl_o_eval_point[2], g4(49));
assert_eq!(stack.constraints[1].sl_o_eval_point[0], g4(1));
assert_eq!(stack.constraints[1].sl_o_eval_point[1], g4(11));
assert_eq!(stack.constraints[1].sl_o_eval_point[2], g4(121));
}
#[test]
fn scale_alphas_multiplies_all_in_place() {
let mut stack = MaskStack::new();
let masks = vec![
MaskOracleHandle::verifier_root_only([1u8; 32]),
MaskOracleHandle::verifier_root_only([2u8; 32]),
];
stack.push_sumcheck_masks(masks, vec![g4(1), g4(2)], vec![g4(0), g4(0)]);
stack.scale_alphas(g4(5));
assert_eq!(stack.constraints[0].alpha, g4(5)); assert_eq!(stack.constraints[1].alpha, g4(5)); }
#[test]
fn joint_mask_value_sums_alpha_times_target() {
let mut stack = MaskStack::new();
stack.push_sumcheck_masks(
vec![
MaskOracleHandle::verifier_root_only([1u8; 32]),
MaskOracleHandle::verifier_root_only([2u8; 32]),
],
vec![g4(1), g4(2)],
vec![g4(10), g4(20)],
);
assert_eq!(stack.joint_mask_value(), g4(30));
}
#[test]
fn push_padding_mask_grows_by_one() {
let mut stack = MaskStack::new();
stack.push_padding_mask(
MaskOracleHandle::verifier_root_only([7u8; 32]),
g4(3),
vec![g4(0); crate::params::Params::M_ZK - crate::params::Params::T_ZK],
);
assert_eq!(stack.len(), 1);
assert_eq!(stack.constraints[0].alpha, g4(3));
}
#[test]
fn mask_constraint_evaluate_sl_is_dot_product() {
let msg = vec![g4(1), g4(2), g4(3)];
let eval_point = vec![g4(10), g4(20), g4(30)];
let mc = MaskConstraint {
alpha: g4(1),
target: g4(0),
sl_o_eval_point: eval_point,
};
assert_eq!(mc.evaluate_sl(&msg), g4(10 + 40 + 90));
}
#[test]
#[should_panic(expected = "verifier handle has no message")]
fn verifier_handle_message_panics() {
let h = MaskOracleHandle::verifier_root_only([0u8; 32]);
let _ = h.message();
}
#[test]
#[should_panic(expected = "verifier handle cannot open")]
fn verifier_handle_open_panics() {
let h = MaskOracleHandle::verifier_root_only([0u8; 32]);
let _ = h.open(&[0]);
}
}