use std::marker::PhantomData; use group::ff::Field; use halo2_proofs::{ circuit::{AssignedCell, Chip, Layouter, Region, SimpleFloorPlanner, Value}, plonk::{Advice, Circuit, Column, ConstraintSystem, Error, Fixed, Instance, Selector}, poly::Rotation, }; use pasta_curves::EqAffine; // ANCHOR: instructions trait NumericInstructions: Chip { /// Variable representing a number. type Num; /// Loads a number into the circuit as a private input. fn load_private(&self, layouter: impl Layouter, a: Value) -> Result; /// Loads a number into the circuit as a fixed constant. fn load_constant(&self, layouter: impl Layouter, constant: F) -> Result; /// Returns `c = a * b`. fn mul( &self, layouter: impl Layouter, a: Self::Num, b: Self::Num, ) -> Result; /// Exposes a number as a public input to the circuit. fn expose_public( &self, layouter: impl Layouter, num: Self::Num, row: usize, ) -> Result<(), Error>; } // ANCHOR_END: instructions // ANCHOR: chip /// The chip that will implement our instructions! Chips store their own /// config, as well as type markers if necessary. struct FieldChip { config: FieldConfig, _marker: PhantomData, } // ANCHOR_END: chip // ANCHOR: chip-config /// Chip state is stored in a config struct. This is generated by the chip /// during configuration, and then stored inside the chip. #[derive(Clone, Debug)] struct FieldConfig { /// For this chip, we will use two advice columns to implement our instructions. /// These are also the columns through which we communicate with other parts of /// the circuit. advice: [Column; 2], /// This is the public input (instance) column. instance: Column, // We need a selector to enable the multiplication gate, so that we aren't placing // any constraints on cells where `NumericInstructions::mul` is not being used. // This is important when building larger circuits, where columns are used by // multiple sets of instructions. s_mul: Selector, } impl FieldChip { fn construct(config: >::Config) -> Self { Self { config, _marker: PhantomData, } } fn configure( meta: &mut ConstraintSystem, advice: [Column; 2], instance: Column, constant: Column, ) -> >::Config { meta.enable_equality(instance); meta.enable_constant(constant); for column in &advice { meta.enable_equality(*column); } let s_mul = meta.selector(); // Define our multiplication gate! meta.create_gate("mul", |meta| { // To implement multiplication, we need three advice cells and a selector // cell. We arrange them like so: // // | a0 | a1 | s_mul | // |-----|-----|-------| // | lhs | rhs | s_mul | // | out | | | // // Gates may refer to any relative offsets we want, but each distinct // offset adds a cost to the proof. The most common offsets are 0 (the // current row), 1 (the next row), and -1 (the previous row), for which // `Rotation` has specific constructors. let lhs = meta.query_advice(advice[0], Rotation::cur()); let rhs = meta.query_advice(advice[1], Rotation::cur()); let out = meta.query_advice(advice[0], Rotation::next()); let s_mul = meta.query_selector(s_mul); // Finally, we return the polynomial expressions that constrain this gate. // For our multiplication gate, we only need a single polynomial constraint. // // The polynomial expressions returned from `create_gate` will be // constrained by the proving system to equal zero. Our expression // has the following properties: // - When s_mul = 0, any value is allowed in lhs, rhs, and out. // - When s_mul != 0, this constrains lhs * rhs = out. vec![s_mul * (lhs * rhs - out)] }); FieldConfig { advice, instance, s_mul, } } } // ANCHOR_END: chip-config // ANCHOR: chip-impl impl Chip for FieldChip { type Config = FieldConfig; type Loaded = (); fn config(&self) -> &Self::Config { &self.config } fn loaded(&self) -> &Self::Loaded { &() } } // ANCHOR_END: chip-impl // ANCHOR: instructions-impl /// A variable representing a number. #[derive(Clone)] struct Number(AssignedCell); impl NumericInstructions for FieldChip { type Num = Number; fn load_private( &self, mut layouter: impl Layouter, value: Value, ) -> Result { let config = self.config(); layouter.assign_region( || "load private", |mut region| { region .assign_advice(|| "private input", config.advice[0], 0, || value) .map(Number) }, ) } fn load_constant( &self, mut layouter: impl Layouter, constant: F, ) -> Result { let config = self.config(); layouter.assign_region( || "load constant", |mut region| { region .assign_advice_from_constant(|| "constant value", config.advice[0], 0, constant) .map(Number) }, ) } fn mul( &self, mut layouter: impl Layouter, a: Self::Num, b: Self::Num, ) -> Result { let config = self.config(); layouter.assign_region( || "mul", |mut region: Region<'_, F>| { // We only want to use a single multiplication gate in this region, // so we enable it at region offset 0; this means it will constrain // cells at offsets 0 and 1. config.s_mul.enable(&mut region, 0)?; // The inputs we've been given could be located anywhere in the circuit, // but we can only rely on relative offsets inside this region. So we // assign new cells inside the region and constrain them to have the // same values as the inputs. a.0.copy_advice(|| "lhs", &mut region, config.advice[0], 0)?; b.0.copy_advice(|| "rhs", &mut region, config.advice[1], 0)?; // Now we can assign the multiplication result, which is to be assigned // into the output position. let value = a.0.value().copied() * b.0.value(); // Finally, we do the assignment to the output, returning a // variable to be used in another part of the circuit. region .assign_advice(|| "lhs * rhs", config.advice[0], 1, || value) .map(Number) }, ) } fn expose_public( &self, mut layouter: impl Layouter, num: Self::Num, row: usize, ) -> Result<(), Error> { let config = self.config(); layouter.constrain_instance(num.0.cell(), config.instance, row) } } // ANCHOR_END: instructions-impl // ANCHOR: circuit /// The full circuit implementation. /// /// In this struct we store the private input variables. We use `Option` because /// they won't have any value during key generation. During proving, if any of these /// were `None` we would get an error. #[derive(Copy, Clone)] struct MyCircuit { constant: F, a: Value, b: Value, } impl Circuit for MyCircuit { // Since we are using a single chip for everything, we can just reuse its config. type Config = FieldConfig; type FloorPlanner = SimpleFloorPlanner; fn without_witnesses(&self) -> Self { Self { constant: self.constant, a: Value::default(), b: Value::default(), } } fn configure(meta: &mut ConstraintSystem) -> Self::Config { // We create the two advice columns that FieldChip uses for I/O. let advice = [meta.advice_column(), meta.advice_column()]; // We also need an instance column to store public inputs. let instance = meta.instance_column(); // Create a fixed column to load constants. let constant = meta.fixed_column(); FieldChip::configure(meta, advice, instance, constant) } fn synthesize( &self, config: Self::Config, mut layouter: impl Layouter, ) -> Result<(), Error> { let field_chip = FieldChip::::construct(config); // Load our private values into the circuit. let a = field_chip.load_private(layouter.namespace(|| "load a"), self.a)?; let b = field_chip.load_private(layouter.namespace(|| "load b"), self.b)?; // Load the constant factor into the circuit. let constant = field_chip.load_constant(layouter.namespace(|| "load constant"), self.constant)?; // We only have access to plain multiplication. // We could implement our circuit as: // asq = a*a // bsq = b*b // absq = asq*bsq // c = constant*asq*bsq // // but it's more efficient to implement it as: // ab = a*b // absq = ab^2 // c = constant*absq let ab = field_chip.mul(layouter.namespace(|| "a * b"), a, b)?; let absq = field_chip.mul(layouter.namespace(|| "ab * ab"), ab.clone(), ab)?; let c = field_chip.mul(layouter.namespace(|| "constant * absq"), constant, absq)?; // Expose the result as a public input to the circuit. field_chip.expose_public(layouter.namespace(|| "expose c"), c, 0) } } // ANCHOR_END: circuit fn main() { use halo2_proofs::{dev::MockProver, pasta::Fp}; // ANCHOR: test-circuit // The number of rows in our circuit cannot exceed 2^k. Since our example // circuit is very small, we can pick a very small value here. const K: u32 = 4; // Prepare the private and public inputs to the circuit! let constant = Fp::from(7); let a = Fp::from(2); let b = Fp::from(3); let c = constant * a.square() * b.square(); // Instantiate the circuit with the private inputs. let circuit = MyCircuit { constant, a: Value::known(a), b: Value::known(b), }; // Arrange the public input. We expose the multiplication result in row 0 // of the instance column, so we position it there in our public inputs. let mut public_inputs = vec![c]; // Given the correct public input, our circuit will verify. let prover = MockProver::run(K, &circuit, vec![public_inputs.clone()]).unwrap(); assert_eq!(prover.verify(), Ok(())); assert!(circuit.test_prove_and_verify::(&[&public_inputs])); // If we try some other public input, the proof will fail! public_inputs[0] += Fp::one(); let prover = MockProver::run(K, &circuit, vec![public_inputs]).unwrap(); assert!(prover.verify().is_err()); // ANCHOR_END: test-circuit }