use std::{cell::RefCell, rc::Rc};
use midnight_circuits::{
ecc::curves::CircuitCurve,
field::NativeChip,
hash::poseidon::{constants::PoseidonField, PoseidonChip},
instructions::{
public_input::CommittedInstanceInstructions, AssignmentInstructions,
PublicInputInstructions,
},
types::{AssignedNative, InnerValue, Instantiable},
verifier::SelfEmulation,
};
use midnight_proofs::{
circuit::{Layouter, Value},
plonk::Error,
transcript::Hashable,
};
use crate::light_aggregator::light_fiat_shamir::LightPoseidonFS;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct FakePoint<C: CircuitCurve> {
pieces: Vec<AssignedNative<C::ScalarField>>,
public: Rc<RefCell<bool>>,
}
impl<C> Instantiable<C::ScalarField> for FakePoint<C>
where
C: CircuitCurve + Hashable<LightPoseidonFS<C::ScalarField>>,
C::ScalarField: PoseidonField,
{
fn as_public_input(p: &C) -> Vec<C::ScalarField> {
<C as Hashable<LightPoseidonFS<C::ScalarField>>>::to_input(p)
}
fn from_public_input(_fields: &[C::ScalarField]) -> Option<C> {
None
}
}
impl<C: CircuitCurve> InnerValue for FakePoint<C> {
type Element = C;
fn value(&self) -> Value<Self::Element> {
unimplemented!("The value of a FakePoint cannot be recovered")
}
}
#[derive(Clone, Debug)]
pub struct FakeCurveChip<C: CircuitCurve> {
scalar_chip: NativeChip<C::ScalarField>,
public_points: Rc<RefCell<Vec<FakePoint<C>>>>,
}
impl<C: CircuitCurve> FakeCurveChip<C> {
pub fn new(scalar_chip: &NativeChip<C::ScalarField>) -> Self {
Self {
scalar_chip: scalar_chip.clone(),
public_points: Rc::new(RefCell::new(Vec::new())),
}
}
pub fn finalize(&self) -> Result<(), Error> {
if self.public_points.borrow().iter().any(|p| !*p.public.borrow()) {
panic!("Not all assigned `FakePoint`s were made public")
}
Ok(())
}
}
impl<C> AssignmentInstructions<C::ScalarField, FakePoint<C>> for FakeCurveChip<C>
where
C: CircuitCurve + Hashable<LightPoseidonFS<C::ScalarField>>,
C::ScalarField: PoseidonField,
{
fn assign(
&self,
layouter: &mut impl Layouter<C::ScalarField>,
value: Value<C>,
) -> Result<FakePoint<C>, Error> {
let l = <C as Hashable<LightPoseidonFS<C::ScalarField>>>::to_input(&C::generator()).len();
let pieces_val = value
.map(|p| <C as Hashable<LightPoseidonFS<C::ScalarField>>>::to_input(&p))
.transpose_vec(l);
let assigned_point = FakePoint::<C> {
pieces: self.scalar_chip.assign_many(layouter, &pieces_val)?,
public: Rc::new(RefCell::new(false)),
};
self.public_points.borrow_mut().push(assigned_point.clone());
Ok(assigned_point)
}
fn assign_fixed(
&self,
layouter: &mut impl Layouter<C::ScalarField>,
constant: <FakePoint<C> as InnerValue>::Element,
) -> Result<FakePoint<C>, Error> {
let pieces_val = <C as Hashable<LightPoseidonFS<C::ScalarField>>>::to_input(&constant);
let assigned_point = FakePoint::<C> {
pieces: self.scalar_chip.assign_many_fixed(layouter, &pieces_val)?,
public: Rc::new(RefCell::new(true)),
};
self.public_points.borrow_mut().push(assigned_point.clone());
Ok(assigned_point)
}
}
impl<C> PublicInputInstructions<C::ScalarField, FakePoint<C>> for FakeCurveChip<C>
where
C: CircuitCurve + Hashable<LightPoseidonFS<C::ScalarField>>,
C::ScalarField: PoseidonField,
{
fn as_public_input(
&self,
_layouter: &mut impl Layouter<C::ScalarField>,
point: &FakePoint<C>,
) -> Result<Vec<AssignedNative<C::ScalarField>>, Error> {
Ok(point.pieces.clone())
}
fn constrain_as_public_input(
&self,
layouter: &mut impl Layouter<C::ScalarField>,
point: &FakePoint<C>,
) -> Result<(), Error> {
*point.public.borrow_mut() = true;
point
.pieces
.iter()
.try_for_each(|x| self.scalar_chip.constrain_as_public_input(layouter, x))
}
fn assign_as_public_input(
&self,
layouter: &mut impl Layouter<C::ScalarField>,
value: Value<C>,
) -> Result<FakePoint<C>, Error> {
let assigned_point = self.assign(layouter, value)?;
self.constrain_as_public_input(layouter, &assigned_point)?;
Ok(assigned_point)
}
}
#[derive(Clone, Debug)]
pub struct LightBlstrsEmulation {}
impl SelfEmulation for LightBlstrsEmulation {
type F = midnight_curves::Fq;
type C = midnight_curves::G1Projective;
type AssignedPoint = FakePoint<Self::C>;
type Hash = LightPoseidonFS<Self::F>;
type ScalarChip = NativeChip<Self::F>;
type CurveChip = FakeCurveChip<Self::C>;
type SpongeChip = PoseidonChip<Self::F>;
type G1Affine = midnight_curves::G1Affine;
type G2Affine = midnight_curves::G2Affine;
type Engine = midnight_curves::Bls12;
fn msm(
_layouter: &mut impl Layouter<Self::F>,
_curve_chip: &Self::CurveChip,
_scalars: &[(AssignedNative<Self::F>, usize)],
_bases: &[Self::AssignedPoint],
) -> Result<Self::AssignedPoint, Error> {
unimplemented!("msm is not allowed with light blstrs emulation")
}
fn constrain_scalar_as_committed_public_input(
layouter: &mut impl Layouter<Self::F>,
scalar_chip: &Self::ScalarChip,
assigned_scalar: &AssignedNative<Self::F>,
) -> Result<(), Error> {
scalar_chip.constrain_as_committed_public_input(layouter, assigned_scalar)
}
fn assign_without_subgroup_check(
layouter: &mut impl Layouter<Self::F>,
curve_chip: &Self::CurveChip,
base: Value<Self::C>,
) -> Result<Self::AssignedPoint, Error> {
curve_chip.assign(layouter, base)
}
}