lettuce 0.1.3

Healthy lattice consructions in pure Rust.
Documentation
use super::*;
use crate::*;

/// argue equality of algebraic relations.
///
/// binding only
pub enum TransparentInnerProdBuilderConstraint {
    // a * b = c
    Mul(u64, u64, u64),
    // a + b = c
    Add(u64, u64, u64),
}

/// We need to commit to a witness and a set of algebraic relations. Each relation will be encoded
/// in a hash committed argument of knowledge.
///
/// Any provided witness must fulfill all constraints.
///
/// Each additional dimension adds 1 to the coef in `O(coef * log(wtns))`
#[derive(Default)]
pub struct TransparentInnerProdSystemBuilder<E: RingElement> {
    pub wtns: HashMap<u64, Vector<E>>,
    pub constraints: Vec<TransparentInnerProdBuilderConstraint>,
}

impl<E: RingElement> TransparentInnerProdSystemBuilder<E> {
    /// Commit a vector as a witness value. Can now make constraints using this vector.
    pub fn commit(&mut self, vec: Vector<E>) -> u64 {
        let wtns_i = {
            let mut key = 0;
            while self.wtns.contains_key(&key) {
                key = rand::random();
            }
            key
        };
        self.wtns.insert(wtns_i, vec);
        wtns_i
    }

    /// a*b -> c
    pub fn constrain_mul(&mut self, wtns_a: u64, wtns_b: u64, wtns_c: u64) {
        self.constraints
            .push(TransparentInnerProdBuilderConstraint::Mul(
                wtns_a, wtns_b, wtns_c,
            ));
    }

    /// a+b -> c
    pub fn constrain_add(&mut self, wtns_a: u64, wtns_b: u64, wtns_c: u64) {
        self.constraints
            .push(TransparentInnerProdBuilderConstraint::Add(
                wtns_a, wtns_b, wtns_c,
            ));
    }

    /// a * [b, b, b, b, b, ... ] -> wtns_c
    /// Assumes b is public token
    pub fn commit_public_mul(&mut self, wtns_a: u64, b: E) -> u64 {
        match self.wtns.get(&wtns_a).cloned() {
            Some(a) => {
                let b: Vector<E> = vec![b; a.len()].into();
                let b_wtns_i = self.commit(b.clone());

                let c = a * &b;
                let c_wtns_i = self.commit(c);
                self.constraints
                    .push(TransparentInnerProdBuilderConstraint::Mul(
                        wtns_a, b_wtns_i, c_wtns_i,
                    ));
                c_wtns_i
            }
            None => {
                panic!()
            }
        }
    }

    pub fn wtns(&self, i: &u64) -> Result<&Vector<E>> {
        self.wtns
            .get(i)
            .ok_or(anyhow::anyhow!("wtns value at index {i} does not exist"))
    }

    pub fn argue(self) -> Result<TransparentInnerProdNIZKArg<E>> {
        let mut wtns_args = HashMap::default();
        let mut transcript = Transcript::<E>::new();
        transcript.domain_separator("innerprod_nizk");
        for constraint in self.constraints.iter() {
            match constraint {
                TransparentInnerProdBuilderConstraint::Mul(a_i, b_i, c_i) => {
                    if wtns_args.contains_key(c_i) {
                        anyhow::bail!("duplicate witness write");
                    }
                    let a = self.wtns(&a_i)?;
                    let b = self.wtns(&b_i)?;
                    let arg = TransparentInnerProd::<10, E>::new(a.clone(), b.clone())?;
                    transcript.append_bytes(&arg.hash);
                    wtns_args.insert(*c_i, arg);
                }
                TransparentInnerProdBuilderConstraint::Add(a_i, b_i, c_i) => {
                    // this is simple equality check
                    if wtns_args.contains_key(c_i) {
                        anyhow::bail!("duplicate witness write");
                    }
                    let a = self.wtns(&a_i)?;
                    let b = self.wtns(&b_i)?;
                    let c = a.clone() + b;
                    let arg = TransparentInnerProd::<10, E>::new(
                        c.clone(),
                        vec![E::one(); c.len()].into(),
                    )?;
                    transcript.append_bytes(&arg.hash);
                    wtns_args.insert(*c_i, arg);
                }
            }
        }
        for wtns_i in self.wtns.keys() {
            if !wtns_args.contains_key(wtns_i) {
                let i = self.wtns(wtns_i)?;
                let arg =
                    TransparentInnerProd::<10, E>::new(i.clone(), vec![E::one(); i.len()].into())?;
                transcript.append_bytes(&arg.hash);
                wtns_args.insert(*wtns_i, arg);
            }
        }
        Ok(TransparentInnerProdNIZKArg {
            wtns_hash: transcript.random(),
            wtns_args,
            constraints: self.constraints,
        })
    }
}