halo2_base/utils/halo2.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183
use std::collections::hash_map::Entry;
use crate::ff::Field;
use crate::halo2_proofs::{
circuit::{AssignedCell, Cell, Region, Value},
halo2curves::bn256::Bn256,
plonk::{Advice, Assigned, Circuit, Column, Fixed},
poly::kzg::commitment::ParamsKZG,
};
use crate::virtual_region::copy_constraints::{CopyConstraintManager, EXTERNAL_CELL_TYPE_ID};
use crate::AssignedValue;
pub use keygen::ProvingKeyGenerator;
/// Raw (physical) assigned cell in Plonkish arithmetization.
#[cfg(feature = "halo2-axiom")]
pub type Halo2AssignedCell<'v, F> = AssignedCell<&'v Assigned<F>, F>;
/// Raw (physical) assigned cell in Plonkish arithmetization.
#[cfg(not(feature = "halo2-axiom"))]
pub type Halo2AssignedCell<'v, F> = AssignedCell<Assigned<F>, F>;
/// Assign advice to physical region.
#[inline(always)]
pub fn raw_assign_advice<'v, F: Field>(
region: &mut Region<F>,
column: Column<Advice>,
offset: usize,
value: Value<impl Into<Assigned<F>>>,
) -> Halo2AssignedCell<'v, F> {
#[cfg(feature = "halo2-axiom")]
{
region.assign_advice(column, offset, value)
}
#[cfg(feature = "halo2-pse")]
{
let value = value.map(|a| Into::<Assigned<F>>::into(a));
region
.assign_advice(
|| format!("assign advice {column:?} offset {offset}"),
column,
offset,
|| value,
)
.unwrap()
}
}
/// Assign fixed to physical region.
#[inline(always)]
pub fn raw_assign_fixed<F: Field>(
region: &mut Region<F>,
column: Column<Fixed>,
offset: usize,
value: F,
) -> Cell {
#[cfg(feature = "halo2-axiom")]
{
region.assign_fixed(column, offset, value)
}
#[cfg(feature = "halo2-pse")]
{
region
.assign_fixed(
|| format!("assign fixed {column:?} offset {offset}"),
column,
offset,
|| Value::known(value),
)
.unwrap()
.cell()
}
}
/// Constrain two physical cells to be equal.
#[inline(always)]
pub fn raw_constrain_equal<F: Field>(region: &mut Region<F>, left: Cell, right: Cell) {
#[cfg(feature = "halo2-axiom")]
region.constrain_equal(left, right);
#[cfg(not(feature = "halo2-axiom"))]
region.constrain_equal(left, right).unwrap();
}
/// Constrains that `virtual_cell` is equal to `external_cell`. The `virtual_cell` must have
/// already been raw assigned with the raw assigned cell stored in `copy_manager`
/// **unless** it is marked an external-only cell with type id [EXTERNAL_CELL_TYPE_ID].
/// * When the virtual cell has already been assigned, the assigned cell is constrained to be equal to the external cell.
/// * When the virtual cell has not been assigned **and** it is marked as an external cell, it is assigned to `external_cell` and the mapping is stored in `copy_manager`.
///
/// This should only be called when `witness_gen_only` is false, otherwise it will panic.
///
/// ## Panics
/// If witness generation only mode is true.
pub fn constrain_virtual_equals_external<F: Field + Ord>(
region: &mut Region<F>,
virtual_cell: AssignedValue<F>,
external_cell: Cell,
copy_manager: &mut CopyConstraintManager<F>,
) {
let ctx_cell = virtual_cell.cell.unwrap();
match copy_manager.assigned_advices.entry(ctx_cell) {
Entry::Occupied(acell) => {
// The virtual cell has already been assigned, so we can constrain it to equal the external cell.
region.constrain_equal(*acell.get(), external_cell);
}
Entry::Vacant(assigned) => {
// The virtual cell **must** be an external cell
assert_eq!(ctx_cell.type_id, EXTERNAL_CELL_TYPE_ID);
// We map the virtual cell to point to the raw external cell in `copy_manager`
assigned.insert(external_cell);
}
}
}
/// This trait should be implemented on the minimal circuit configuration data necessary to
/// completely determine a circuit (independent of circuit inputs).
/// This is used to generate a _dummy_ instantiation of a concrete `Circuit` type for the purposes of key generation.
/// This dummy instantiation just needs to have the correct arithmetization format, but the witnesses do not need to
/// satisfy constraints.
pub trait KeygenCircuitIntent<F: Field> {
/// Concrete circuit type
type ConcreteCircuit: Circuit<F>;
/// Additional data that "pins" down the circuit. These can always to deterministically rederived from `Self`, but
/// storing the `Pinning` saves recomputations in future proof generations.
type Pinning;
/// The intent must include the log_2 domain size of the circuit.
/// This is used to get the correct trusted setup file.
fn get_k(&self) -> u32;
/// Builds a _dummy_ instantiation of `Self::ConcreteCircuit` for the purposes of key generation.
/// This dummy instantiation just needs to have the correct arithmetization format, but the witnesses do not need to
/// satisfy constraints.
fn build_keygen_circuit(self) -> Self::ConcreteCircuit;
/// Pinning is only fully computed after `synthesize` has been run during keygen
fn get_pinning_after_keygen(
self,
kzg_params: &ParamsKZG<Bn256>,
circuit: &Self::ConcreteCircuit,
) -> Self::Pinning;
}
mod keygen {
use crate::halo2_proofs::{
halo2curves::bn256::{Bn256, Fr, G1Affine},
plonk::{self, ProvingKey},
poly::{commitment::Params, kzg::commitment::ParamsKZG},
};
use super::KeygenCircuitIntent;
/// Trait for creating a proving key and a pinning for a circuit from minimal circuit configuration data.
pub trait ProvingKeyGenerator {
/// Create proving key and pinning.
fn create_pk_and_pinning(
self,
kzg_params: &ParamsKZG<Bn256>,
) -> (ProvingKey<G1Affine>, serde_json::Value);
}
impl<CI> ProvingKeyGenerator for CI
where
CI: KeygenCircuitIntent<Fr> + Clone,
CI::Pinning: serde::Serialize,
{
fn create_pk_and_pinning(
self,
kzg_params: &ParamsKZG<Bn256>,
) -> (ProvingKey<G1Affine>, serde_json::Value) {
assert_eq!(kzg_params.k(), self.get_k());
let circuit = self.clone().build_keygen_circuit();
#[cfg(feature = "halo2-axiom")]
let pk = plonk::keygen_pk2(kzg_params, &circuit, false).unwrap();
#[cfg(not(feature = "halo2-axiom"))]
let pk = {
let vk = plonk::keygen_vk_custom(kzg_params, &circuit, false).unwrap();
plonk::keygen_pk(kzg_params, vk, &circuit).unwrap()
};
let pinning = self.get_pinning_after_keygen(kzg_params, &circuit);
(pk, serde_json::to_value(pinning).unwrap())
}
}
}