Skip to content

Commit

Permalink
Release v0.4.1 (#246)
Browse files Browse the repository at this point in the history
In the future we will not squash merge so as to maintain history.

## Bug Fixes
* #224

## Other PRs
* #218
* #231
* #232
* #227
* #235
* #237
* #242
  • Loading branch information
jonathanpwang authored Jan 18, 2024
2 parents 36331cc + 57d3e19 commit 2fb351e
Show file tree
Hide file tree
Showing 37 changed files with 751 additions and 1,738 deletions.
1 change: 0 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ jobs:
working-directory: "hashes/zkevm"
run: |
cargo test packed_multi_keccak_prover::k_14
cargo test bit_sha256_prover::k_10
cargo t test_vanilla_keccak_kat_vectors
lint:
Expand Down
7 changes: 2 additions & 5 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,5 @@ Cargo.lock

*.png

**/params/*
**/params/
**/proptest-regressions/
**/results/
**/*.DS_Store
/halo2_ecc/src/bn254/data/
/halo2_ecc/src/secp256k1/data/
6 changes: 3 additions & 3 deletions halo2-base/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "halo2-base"
version = "0.4.0"
version = "0.4.1"
authors = ["Intrinsic Technologies"]
license = "MIT"
edition = "2021"
Expand All @@ -16,15 +16,15 @@ num-integer = "0.1"
num-traits = "0.2"
rand_chacha = "0.3"
rustc-hash = "1.1"
rayon = "1.7"
rayon = "1.8"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
log = "0.4"
getset = "0.1.2"
ark-std = { version = "0.3.0", features = ["print-trace"], optional = true }

# Use Axiom's custom halo2 monorepo for faster proving when feature = "halo2-axiom" is on
halo2_proofs_axiom = { version = "0.3", package = "halo2-axiom", optional = true }
halo2_proofs_axiom = { version = "0.4", package = "halo2-axiom", optional = true }
# Use PSE halo2 and halo2curves for compatibility when feature = "halo2-pse" is on
halo2_proofs = { git = "https://github.com/privacy-scaling-explorations/halo2.git", rev = "7a21656", optional = true }

Expand Down
12 changes: 12 additions & 0 deletions halo2-base/src/gates/circuit/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -372,3 +372,15 @@ pub struct RangeStatistics {
/// Total special advice cells that need to be looked up, per phase
pub total_lookup_advice_per_phase: Vec<usize>,
}

impl<F: ScalarField> AsRef<BaseCircuitBuilder<F>> for BaseCircuitBuilder<F> {
fn as_ref(&self) -> &BaseCircuitBuilder<F> {
self
}
}

impl<F: ScalarField> AsMut<BaseCircuitBuilder<F>> for BaseCircuitBuilder<F> {
fn as_mut(&mut self) -> &mut BaseCircuitBuilder<F> {
self
}
}
14 changes: 13 additions & 1 deletion halo2-base/src/gates/circuit/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ pub mod builder;

/// A struct defining the configuration parameters for a halo2-base circuit
/// - this is used to configure [BaseConfig].
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
#[derive(Clone, Default, Debug, Hash, Serialize, Deserialize)]
pub struct BaseCircuitParams {
// Keeping FlexGateConfigParams expanded for backwards compatibility
/// Specifies the number of rows in the circuit to be 2<sup>k</sup>
Expand Down Expand Up @@ -215,3 +215,15 @@ impl CircuitBuilderStage {
matches!(self, CircuitBuilderStage::Prover)
}
}

impl<F: ScalarField> AsRef<BaseConfig<F>> for BaseConfig<F> {
fn as_ref(&self) -> &BaseConfig<F> {
self
}
}

impl<F: ScalarField> AsMut<BaseConfig<F>> for BaseConfig<F> {
fn as_mut(&mut self) -> &mut BaseConfig<F> {
self
}
}
6 changes: 3 additions & 3 deletions halo2-base/src/gates/flex_gate/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -303,9 +303,9 @@ pub trait GateInstructions<F: ScalarField> {
ctx.assign_region([Constant(F::ZERO), Existing(x), Existing(x), Existing(x)], [0]);
}

/// Constrains and returns a / b = 0.
/// Constrains and returns a / b = out.
///
/// Defines a vertical gate of form | 0 | b^1 * a | b | a |, where b^1 * a = out.
/// Defines a vertical gate of form | 0 | a / b | b | a |, where a / b = out.
///
/// Assumes `b != 0`.
/// * `ctx`: [Context] to add the constraints to
Expand Down Expand Up @@ -823,7 +823,7 @@ pub trait GateInstructions<F: ScalarField> {

/// Constrains and returns little-endian bit vector representation of `a`.
///
/// Assumes `range_bits <= number of bits in a`.
/// Assumes `range_bits >= bit_length(a)`.
/// * `a`: [QuantumCell] of the value to convert
/// * `range_bits`: range of bits needed to represent `a`
fn num_to_bits(
Expand Down
10 changes: 8 additions & 2 deletions halo2-base/src/gates/flex_gate/threads/single_phase.rs
Original file line number Diff line number Diff line change
Expand Up @@ -219,9 +219,15 @@ pub fn assign_with_constraints<F: ScalarField, const ROTATIONS: usize>(
.assign_advice(|| "", column, row_offset, || value.map(|v| *v))
.unwrap()
.cell();
copy_manager
if let Some(old_cell) = copy_manager
.assigned_advices
.insert(ContextCell::new(ctx.type_id, ctx.context_id, i), cell);
.insert(ContextCell::new(ctx.type_id, ctx.context_id, i), cell)
{
assert!(
old_cell.row_offset == cell.row_offset && old_cell.column == cell.column,
"Trying to overwrite virtual cell with a different raw cell"
);
}

// If selector enabled and row_offset is valid add break point, account for break point overlap, and enforce equality constraint for gate outputs.
// ⚠️ This assumes overlap is of form: gate enabled at `i - delta` and `i`, where `delta = ROTATIONS - 1`. We currently do not support `delta < ROTATIONS - 1`.
Expand Down
31 changes: 19 additions & 12 deletions halo2-base/src/poseidon/hasher/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use crate::{
ScalarField,
};

use getset::Getters;
use getset::{CopyGetters, Getters};
use num_bigint::BigUint;
use std::{cell::OnceCell, mem};

Expand All @@ -23,8 +23,10 @@ pub mod spec;
pub mod state;

/// Stateless Poseidon hasher.
#[derive(Clone, Debug)]
#[derive(Clone, Debug, Getters)]
pub struct PoseidonHasher<F: ScalarField, const T: usize, const RATE: usize> {
/// Spec, contains round constants and optimized matrices.
#[getset(get = "pub")]
spec: OptimizedPoseidonSpec<F, T, RATE>,
consts: OnceCell<PoseidonHasherConsts<F, T, RATE>>,
}
Expand All @@ -51,13 +53,16 @@ impl<F: ScalarField, const T: usize, const RATE: usize> PoseidonHasherConsts<F,
}

/// 1 logical row of compact input for Poseidon hasher.
#[derive(Copy, Clone, Debug)]
#[derive(Copy, Clone, Debug, Getters, CopyGetters)]
pub struct PoseidonCompactInput<F: ScalarField, const RATE: usize> {
// Right padded inputs. No constrains on paddings.
/// Right padded inputs. No constrains on paddings.
#[getset(get = "pub")]
inputs: [AssignedValue<F>; RATE],
// is_final = 1 triggers squeeze.
/// is_final = 1 triggers squeeze.
#[getset(get_copy = "pub")]
is_final: SafeBool<F>,
// Length of `inputs`.
/// Length of `inputs`.
#[getset(get_copy = "pub")]
len: AssignedValue<F>,
}

Expand Down Expand Up @@ -87,11 +92,13 @@ impl<F: ScalarField, const RATE: usize> PoseidonCompactInput<F, RATE> {
}

/// A compact chunk input for Poseidon hasher. The end of a logical input could only be at the boundary of a chunk.
#[derive(Clone, Debug)]
#[derive(Clone, Debug, Getters, CopyGetters)]
pub struct PoseidonCompactChunkInput<F: ScalarField, const RATE: usize> {
// Inputs of a chunk. All witnesses will be absorbed.
/// Inputs of a chunk. All witnesses will be absorbed.
#[getset(get = "pub")]
inputs: Vec<[AssignedValue<F>; RATE]>,
// is_final = 1 triggers squeeze.
/// is_final = 1 triggers squeeze.
#[getset(get_copy = "pub")]
is_final: SafeBool<F>,
}

Expand All @@ -103,13 +110,13 @@ impl<F: ScalarField, const RATE: usize> PoseidonCompactChunkInput<F, RATE> {
}

/// 1 logical row of compact output for Poseidon hasher.
#[derive(Copy, Clone, Debug, Getters)]
#[derive(Copy, Clone, Debug, CopyGetters)]
pub struct PoseidonCompactOutput<F: ScalarField> {
/// hash of 1 logical input.
#[getset(get = "pub")]
#[getset(get_copy = "pub")]
hash: AssignedValue<F>,
/// is_final = 1 ==> this is the end of a logical input.
#[getset(get = "pub")]
#[getset(get_copy = "pub")]
is_final: SafeBool<F>,
}

Expand Down
2 changes: 1 addition & 1 deletion halo2-base/src/poseidon/hasher/tests/hasher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ fn hasher_compact_chunk_inputs_compatiblity_verification<
for (compact_output, chunk_input) in compact_outputs.iter().zip(chunk_inputs) {
// into() doesn't work if ! is in the beginning in the bool expression...
let is_final_input = chunk_input.is_final.as_ref().value();
let is_final_output = compact_output.is_final().as_ref().value();
let is_final_output = compact_output.is_final.as_ref().value();
assert_eq!(is_final_input, is_final_output);
if is_final_output == &Fr::ONE {
assert_eq!(native_results[output_offset], *compact_output.hash().value());
Expand Down
114 changes: 113 additions & 1 deletion halo2-base/src/utils/halo2.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
use std::collections::hash_map::Entry;

use crate::ff::Field;
use crate::halo2_proofs::{
circuit::{AssignedCell, Cell, Region, Value},
plonk::{Advice, Assigned, Column, Fixed},
plonk::{Advice, Assigned, Circuit, Column, Fixed},
};
use crate::virtual_region::copy_constraints::{CopyConstraintManager, EXTERNAL_CELL_TYPE_ID};
use crate::AssignedValue;

/// Raw (physical) assigned cell in Plonkish arithmetization.
#[cfg(feature = "halo2-axiom")]
Expand Down Expand Up @@ -71,3 +75,111 @@ pub fn raw_constrain_equal<F: Field>(region: &mut Region<F>, left: Cell, 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;
}

use halo2_proofs_axiom::halo2curves::bn256::Bn256;
use halo2_proofs_axiom::poly::kzg::commitment::ParamsKZG;
pub use keygen::ProvingKeyGenerator;

mod keygen {
use halo2_proofs_axiom::poly::commitment::Params;

use crate::halo2_proofs::{
halo2curves::bn256::{Bn256, Fr, G1Affine},
plonk::{self, ProvingKey},
poly::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())
}
}
}
13 changes: 10 additions & 3 deletions halo2-base/src/virtual_region/copy_constraints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ use crate::{ff::Field, ContextCell};

use super::manager::VirtualRegionManager;

/// Type ID to distinguish external raw Halo2 cells. **This Type ID must be unique.**
pub const EXTERNAL_CELL_TYPE_ID: &str = "halo2-base:External Raw Halo2 Cell";

/// Thread-safe shared global manager for all copy constraints.
pub type SharedCopyConstraintManager<F> = Arc<Mutex<CopyConstraintManager<F>>>;

Expand Down Expand Up @@ -86,11 +89,15 @@ impl<F: Field + Ord> CopyConstraintManager<F> {
}

fn load_external_cell_impl(&mut self, cell: Option<Cell>) -> ContextCell {
let context_cell =
ContextCell::new("halo2-base:External Raw Halo2 Cell", 0, self.external_cell_count);
let context_cell = ContextCell::new(EXTERNAL_CELL_TYPE_ID, 0, self.external_cell_count);
self.external_cell_count += 1;
if let Some(cell) = cell {
self.assigned_advices.insert(context_cell, cell);
if let Some(old_cell) = self.assigned_advices.insert(context_cell, cell) {
assert!(
old_cell.row_offset == cell.row_offset && old_cell.column == cell.column,
"External cell already assigned"
)
}
}
context_cell
}
Expand Down
Loading

0 comments on commit 2fb351e

Please sign in to comment.