#![allow(incomplete_features)]
#![deny(clippy::perf)]
#![allow(clippy::too_many_arguments)]
#![warn(clippy::default_numeric_fallback)]
#![warn(missing_docs)]
use getset::CopyGetters;
use itertools::Itertools;
#[cfg(feature = "jemallocator")]
use jemallocator::Jemalloc;
#[cfg(feature = "jemallocator")]
#[global_allocator]
static GLOBAL: Jemalloc = Jemalloc;
#[cfg(feature = "mimalloc")]
use mimalloc::MiMalloc;
#[cfg(feature = "mimalloc")]
#[global_allocator]
static GLOBAL: MiMalloc = MiMalloc;
#[cfg(not(feature = "cuda"))]
pub use halo2_proofs_axiom as halo2_proofs;
#[cfg(feature = "cuda")]
pub use halo2_proofs_axiom_gpu as halo2_proofs;
use halo2_proofs::halo2curves::ff;
use halo2_proofs::plonk::Assigned;
use utils::ScalarField;
use virtual_region::copy_constraints::SharedCopyConstraintManager;
pub mod gates;
pub mod poseidon;
pub mod safe_types;
pub mod utils;
pub mod virtual_region;
pub const SKIP_FIRST_PASS: bool = false;
#[derive(Clone, Copy, Debug)]
pub enum QuantumCell<F: ScalarField> {
Existing(AssignedValue<F>),
Witness(F),
WitnessFraction(Assigned<F>),
Constant(F),
}
impl<F: ScalarField> From<AssignedValue<F>> for QuantumCell<F> {
fn from(a: AssignedValue<F>) -> Self {
Self::Existing(a)
}
}
impl<F: ScalarField> QuantumCell<F> {
pub fn value(&self) -> &F {
match self {
Self::Existing(a) => a.value(),
Self::Witness(a) => a,
Self::WitnessFraction(_) => {
panic!("Trying to get value of a fraction before batch inversion")
}
Self::Constant(a) => a,
}
}
}
pub type ContextTag = (&'static str, usize);
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct ContextCell {
pub type_id: &'static str,
pub context_id: usize,
pub offset: usize,
}
impl ContextCell {
pub fn new(type_id: &'static str, context_id: usize, offset: usize) -> Self {
Self { type_id, context_id, offset }
}
}
#[derive(Clone, Copy, Debug)]
pub struct AssignedValue<F: crate::ff::Field> {
pub value: Assigned<F>, pub cell: Option<ContextCell>,
}
impl<F: ScalarField> AssignedValue<F> {
pub fn value(&self) -> &F {
match &self.value {
Assigned::Trivial(a) => a,
_ => unreachable!(), }
}
pub fn debug_prank(&self, ctx: &mut Context<F>, prank_value: F) {
ctx.advice[self.cell.unwrap().offset] = Assigned::Trivial(prank_value);
}
}
impl<F: ScalarField> AsRef<AssignedValue<F>> for AssignedValue<F> {
fn as_ref(&self) -> &AssignedValue<F> {
self
}
}
#[derive(Clone, Debug, CopyGetters)]
pub struct Context<F: ScalarField> {
#[getset(get_copy = "pub")]
witness_gen_only: bool,
#[getset(get_copy = "pub")]
phase: usize,
#[getset(get_copy = "pub")]
type_id: &'static str,
context_id: usize,
pub advice: Vec<Assigned<F>>,
zero_cell: Option<AssignedValue<F>>,
pub selector: Vec<bool>,
pub copy_manager: SharedCopyConstraintManager<F>,
}
impl<F: ScalarField> Context<F> {
pub fn new(
witness_gen_only: bool,
phase: usize,
type_id: &'static str,
context_id: usize,
copy_manager: SharedCopyConstraintManager<F>,
) -> Self {
Self {
witness_gen_only,
phase,
type_id,
context_id,
advice: Vec::new(),
selector: Vec::new(),
zero_cell: None,
copy_manager,
}
}
pub fn id(&self) -> usize {
self.context_id
}
pub fn tag(&self) -> ContextTag {
(self.type_id, self.context_id)
}
fn latest_cell(&self) -> ContextCell {
ContextCell::new(self.type_id, self.context_id, self.advice.len() - 1)
}
pub fn assign_cell(&mut self, input: impl Into<QuantumCell<F>>) {
match input.into() {
QuantumCell::Existing(acell) => {
self.advice.push(acell.value);
if !self.witness_gen_only {
let new_cell = self.latest_cell();
self.copy_manager
.lock()
.unwrap()
.advice_equalities
.push((new_cell, acell.cell.unwrap()));
}
}
QuantumCell::Witness(val) => {
self.advice.push(Assigned::Trivial(val));
}
QuantumCell::WitnessFraction(val) => {
self.advice.push(val);
}
QuantumCell::Constant(c) => {
self.advice.push(Assigned::Trivial(c));
if !self.witness_gen_only {
let new_cell = self.latest_cell();
self.copy_manager.lock().unwrap().constant_equalities.push((c, new_cell));
}
}
}
}
pub fn last(&self) -> Option<AssignedValue<F>> {
self.advice.last().map(|v| {
let cell = (!self.witness_gen_only).then_some(self.latest_cell());
AssignedValue { value: *v, cell }
})
}
pub fn get(&self, offset: isize) -> AssignedValue<F> {
let offset = if offset < 0 {
self.advice.len().wrapping_add_signed(offset)
} else {
offset as usize
};
assert!(offset < self.advice.len());
let cell = (!self.witness_gen_only).then_some(ContextCell::new(
self.type_id,
self.context_id,
offset,
));
AssignedValue { value: self.advice[offset], cell }
}
pub fn constrain_equal(&mut self, a: &AssignedValue<F>, b: &AssignedValue<F>) {
if !self.witness_gen_only {
self.copy_manager
.lock()
.unwrap()
.advice_equalities
.push((a.cell.unwrap(), b.cell.unwrap()));
}
}
pub fn assign_region<Q>(
&mut self,
inputs: impl IntoIterator<Item = Q>,
gate_offsets: impl IntoIterator<Item = isize>,
) where
Q: Into<QuantumCell<F>>,
{
if self.witness_gen_only {
for input in inputs {
self.assign_cell(input);
}
} else {
let row_offset = self.advice.len();
for input in inputs {
self.assign_cell(input);
}
self.selector.resize(self.advice.len(), false);
for offset in gate_offsets {
*self
.selector
.get_mut(row_offset.checked_add_signed(offset).expect("Invalid gate offset"))
.expect("Invalid selector offset") = true;
}
}
}
pub fn assign_region_last<Q>(
&mut self,
inputs: impl IntoIterator<Item = Q>,
gate_offsets: impl IntoIterator<Item = isize>,
) -> AssignedValue<F>
where
Q: Into<QuantumCell<F>>,
{
self.assign_region(inputs, gate_offsets);
self.last().unwrap()
}
pub fn assign_region_smart<Q>(
&mut self,
inputs: impl IntoIterator<Item = Q>,
gate_offsets: impl IntoIterator<Item = isize>,
equality_offsets: impl IntoIterator<Item = (isize, isize)>,
external_equality: impl IntoIterator<Item = (Option<ContextCell>, isize)>,
) where
Q: Into<QuantumCell<F>>,
{
let row_offset = self.advice.len();
self.assign_region(inputs, gate_offsets);
if !self.witness_gen_only {
for (offset1, offset2) in equality_offsets {
self.copy_manager.lock().unwrap().advice_equalities.push((
ContextCell::new(
self.type_id,
self.context_id,
row_offset.wrapping_add_signed(offset1),
),
ContextCell::new(
self.type_id,
self.context_id,
row_offset.wrapping_add_signed(offset2),
),
));
}
for (cell, offset) in external_equality {
self.copy_manager.lock().unwrap().advice_equalities.push((
cell.unwrap(),
ContextCell::new(
self.type_id,
self.context_id,
row_offset.wrapping_add_signed(offset),
),
));
}
}
}
pub fn assign_witnesses(
&mut self,
witnesses: impl IntoIterator<Item = F>,
) -> Vec<AssignedValue<F>> {
let row_offset = self.advice.len();
self.assign_region(witnesses.into_iter().map(QuantumCell::Witness), []);
self.advice[row_offset..]
.iter()
.enumerate()
.map(|(i, v)| {
let cell = (!self.witness_gen_only).then_some(ContextCell::new(
self.type_id,
self.context_id,
row_offset + i,
));
AssignedValue { value: *v, cell }
})
.collect()
}
pub fn load_witness(&mut self, witness: F) -> AssignedValue<F> {
self.assign_cell(QuantumCell::Witness(witness));
if !self.witness_gen_only {
self.selector.resize(self.advice.len(), false);
}
self.last().unwrap()
}
pub fn load_constant(&mut self, c: F) -> AssignedValue<F> {
self.assign_cell(QuantumCell::Constant(c));
if !self.witness_gen_only {
self.selector.resize(self.advice.len(), false);
}
self.last().unwrap()
}
pub fn load_constants(&mut self, c: &[F]) -> Vec<AssignedValue<F>> {
c.iter().map(|v| self.load_constant(*v)).collect_vec()
}
pub fn load_zero(&mut self) -> AssignedValue<F> {
if let Some(zcell) = &self.zero_cell {
return *zcell;
}
let zero_cell = self.load_constant(F::ZERO);
self.zero_cell = Some(zero_cell);
zero_cell
}
pub fn debug_assert_false(&mut self) {
use rand_chacha::rand_core::OsRng;
let rand1 = self.load_witness(F::random(OsRng));
let rand2 = self.load_witness(F::random(OsRng));
self.constrain_equal(&rand1, &rand2);
}
}