use midnight_proofs::{circuit::Layouter, plonk::Error};
use super::EccInstructions;
use crate::{ecc::curves::CircuitCurve, types::InnerValue, CircuitField};
pub trait HashToCurveCPU<C, Input>
where
C: CircuitCurve,
{
fn hash_to_curve(inputs: &[Input]) -> C::CryptographicGroup;
}
pub trait HashToCurveInstructions<F, C, Input, E>: HashToCurveCPU<C, Input::Element>
where
F: CircuitField,
C: CircuitCurve,
Input: InnerValue,
E: EccInstructions<F, C>,
{
fn hash_to_curve(
&self,
layouter: &mut impl Layouter<F>,
inputs: &[Input],
) -> Result<E::Point, Error>;
fn ecc_chip(&self) -> &E;
}
#[cfg(test)]
pub(crate) mod tests {
use std::marker::PhantomData;
use ff::FromUniformBytes;
use group::Group;
use midnight_proofs::{
circuit::{Layouter, SimpleFloorPlanner, Value},
dev::MockProver,
plonk::{Circuit, ConstraintSystem, Error},
};
use rand::SeedableRng;
use rand_chacha::ChaCha8Rng;
use super::{HashToCurveCPU, HashToCurveInstructions};
use crate::{
ecc::curves::CircuitCurve,
instructions::{AssignmentInstructions, EccInstructions},
testing_utils::{FromScratch, Sampleable},
types::{InnerConstants, InnerValue},
utils::circuit_modeling::{circuit_to_json, cost_measure_end, cost_measure_start},
CircuitField,
};
#[derive(Clone, Debug)]
struct TestCircuit<F, C, I, EccChip, InputsChip, HashToCurveChip>
where
I: InnerValue,
C: CircuitCurve,
{
input: Value<I::Element>,
expected: C::CryptographicGroup,
_marker: PhantomData<(F, EccChip, InputsChip, HashToCurveChip)>,
}
impl<F, C, I, EccChip, InputsChip, HashToCurveChip> Circuit<F>
for TestCircuit<F, C, I, EccChip, InputsChip, HashToCurveChip>
where
F: CircuitField,
C: CircuitCurve,
I: InnerValue,
I::Element: Clone,
EccChip: EccInstructions<F, C>,
InputsChip: AssignmentInstructions<F, I> + FromScratch<F>,
HashToCurveChip: HashToCurveInstructions<F, C, I, EccChip> + FromScratch<F>,
{
type Config = (
<InputsChip as FromScratch<F>>::Config,
<HashToCurveChip as FromScratch<F>>::Config,
);
type FloorPlanner = SimpleFloorPlanner;
type Params = ();
fn without_witnesses(&self) -> Self {
unreachable!()
}
fn configure(meta: &mut ConstraintSystem<F>) -> Self::Config {
let committed_instance_column = meta.instance_column();
let instance_column = meta.instance_column();
let instance_columns = [committed_instance_column, instance_column];
let mut advice_columns = vec![];
let mut fixed_columns = vec![];
(
InputsChip::configure_from_scratch(
meta,
&mut advice_columns,
&mut fixed_columns,
&instance_columns,
),
HashToCurveChip::configure_from_scratch(
meta,
&mut advice_columns,
&mut fixed_columns,
&instance_columns,
),
)
}
fn synthesize(
&self,
config: Self::Config,
mut layouter: impl Layouter<F>,
) -> Result<(), Error> {
let inputs_chip = InputsChip::new_from_scratch(&config.0);
let htc_chip = HashToCurveChip::new_from_scratch(&config.1);
let input = inputs_chip.assign(&mut layouter, self.input.clone())?;
cost_measure_start(&mut layouter);
let res = htc_chip.hash_to_curve(&mut layouter, &[input])?;
cost_measure_end(&mut layouter);
htc_chip.ecc_chip().assert_equal_to_fixed(&mut layouter, &res, self.expected)?;
inputs_chip.load_from_scratch(&mut layouter)?;
htc_chip.load_from_scratch(&mut layouter)
}
}
fn run<F, C, I, EccChip, InputsChip, HashToCurveChip>(
input: &I::Element,
expected: C::CryptographicGroup,
must_pass: bool,
cost_model: bool,
chip_name: &str,
) where
F: CircuitField + FromUniformBytes<64> + Ord,
C: CircuitCurve,
I: InnerValue,
I::Element: Clone,
EccChip: EccInstructions<F, C>,
InputsChip: AssignmentInstructions<F, I> + FromScratch<F>,
HashToCurveChip: HashToCurveInstructions<F, C, I, EccChip> + FromScratch<F>,
{
let circuit = TestCircuit::<F, C, I, EccChip, InputsChip, HashToCurveChip> {
input: Value::known(input.clone()),
expected,
_marker: PhantomData,
};
let public_inputs = vec![vec![], vec![]];
match MockProver::run(&circuit, public_inputs) {
Ok(prover) => match prover.verify() {
Ok(()) => assert!(must_pass),
Err(e) => assert!(!must_pass, "Failed verifier with error {e:?}"),
},
Err(e) => assert!(!must_pass, "Failed prover with error {e:?}"),
}
if cost_model {
circuit_to_json(chip_name, "hash_to_curve", circuit);
}
}
pub fn test_hash_to_curve<F, C, I, EccChip, InputsChip, HashToCurveChip>(name: &str)
where
F: CircuitField + FromUniformBytes<64> + Ord,
C: CircuitCurve,
I: InnerConstants + Sampleable,
I::Element: Clone,
EccChip: EccInstructions<F, C>,
InputsChip: AssignmentInstructions<F, I> + FromScratch<F>,
HashToCurveChip: HashToCurveInstructions<F, C, I, EccChip> + FromScratch<F>,
{
let mut rng = ChaCha8Rng::seed_from_u64(0xc0ffee);
[I::sample_inner(&mut rng), I::inner_zero(), I::inner_one()]
.iter()
.for_each(|input| {
let expected = <HashToCurveChip as HashToCurveCPU<C, I::Element>>::hash_to_curve(
std::slice::from_ref(input),
);
let wrong = C::CryptographicGroup::identity();
run::<F, C, I, EccChip, InputsChip, HashToCurveChip>(
input, expected, true, true, name,
);
run::<F, C, I, EccChip, InputsChip, HashToCurveChip>(
input, wrong, false, false, name,
);
})
}
}