Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci_main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ jobs:
command: tarpaulin
args: --all-features --timeout 600 --out Xml
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v3.1.0
uses: codecov/codecov-action@v3.1.4

doc-links:
name: Intra-doc links
Expand Down
1 change: 1 addition & 0 deletions halo2_proofs/src/circuit/floor_planner/single_pass.rs
Original file line number Diff line number Diff line change
Expand Up @@ -434,6 +434,7 @@ impl<'r, 'a, F: Field, CS: Assignment<F> + 'a> TableLayouter<F>
offset: usize,
to: &'v mut (dyn FnMut() -> Value<Assigned<F>> + 'v),
) -> Result<(), Error> {
// Cannot use the same column for different lookups ?
if self.used_columns.contains(&column) {
return Err(Error::Synthesis); // TODO better error
}
Expand Down
1 change: 1 addition & 0 deletions halo2_proofs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@

pub mod arithmetic;
pub mod circuit;
mod protostar;
pub use halo2curves;
mod multicore;
pub mod plonk;
Expand Down
7 changes: 6 additions & 1 deletion halo2_proofs/src/plonk/circuit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1500,7 +1500,7 @@ pub struct Gate<F: Field> {
/// We track queried selectors separately from other cells, so that we can use them to
/// trigger debug checks on gates.
queried_selectors: Vec<Selector>,
queried_cells: Vec<VirtualCell>,
queried_cells: Vec<VirtualCell>, // w_1 at i+1, w_2 at i, q_1 at i
}

impl<F: Field> Gate<F> {
Expand Down Expand Up @@ -1709,10 +1709,14 @@ impl<F: Field> ConstraintSystem<F> {
name: S,
table_map: impl FnOnce(&mut VirtualCells<'_, F>) -> Vec<(Expression<F>, TableColumn)>,
) -> usize {
// `cells` is named `meta` in `table_map`. We give it to the closure so that
// we get information about which columns are queried.
// Essentially extracts all the variables from `Expression`
let mut cells = VirtualCells::new(self);
let table_map = table_map(&mut cells)
.into_iter()
.map(|(mut input, table)| {
// why is table not used.
if input.contains_simple_selector() {
panic!("expression containing simple selector supplied to lookup argument");
}
Expand Down Expand Up @@ -1920,6 +1924,7 @@ impl<F: Field> ConstraintSystem<F> {
// expressions in gates, as lookup arguments cannot support simple
// selectors. Selectors that are complex or do not appear in any gates
// will have degree zero.
// For each polynomial in each gate, get the unique simple selector
let mut degrees = vec![0; selectors.len()];
for expr in self.gates.iter().flat_map(|gate| gate.polys.iter()) {
if let Some(selector) = expr.extract_simple_selector() {
Expand Down
10 changes: 9 additions & 1 deletion halo2_proofs/src/plonk/evaluation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,7 @@ impl Calculation {
}

/// Evaluator
/// Probably want a Homogenized Evaluator
#[derive(Clone, Default, Debug)]
pub struct Evaluator<C: CurveAffine> {
/// Custom gates evalution
Expand Down Expand Up @@ -224,11 +225,13 @@ impl<C: CurveAffine> Evaluator<C> {
pub fn new(cs: &ConstraintSystem<C::ScalarExt>) -> Self {
let mut ev = Evaluator::default();

// good place to homoegenize?

// Custom gates
let mut parts = Vec::new();
for gate in cs.gates.iter() {
parts.extend(
gate.polynomials()
gate.polynomials() // expressions
.iter()
.map(|poly| ev.custom_gates.add_expression(poly)),
);
Expand Down Expand Up @@ -568,12 +571,15 @@ impl<C: CurveAffine> GraphEvaluator<C> {
/// resulting value so the result can be reused when that calculation
/// is done multiple times.
fn add_calculation(&mut self, calculation: Calculation) -> ValueSource {
// attempt to find this calculation in the set of existing calculation
let existing_calculation = self
.calculations
.iter()
.find(|c| c.calculation == calculation);
match existing_calculation {
// if it exists, then reuse it
Some(existing_calculation) => ValueSource::Intermediate(existing_calculation.target),
// else create a new calculation
None => {
let target = self.num_intermediates;
self.calculations.push(CalculationInfo {
Expand All @@ -599,6 +605,8 @@ impl<C: CurveAffine> GraphEvaluator<C> {
)))
}
Expression::Advice(query) => {
// our calc depends on this specific query.
// e.g. w[1][i+1]
let rot_idx = self.add_rotation(&query.rotation);
self.add_calculation(Calculation::Store(ValueSource::Advice(
query.column_index,
Expand Down
1 change: 1 addition & 0 deletions halo2_proofs/src/plonk/prover.rs
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,7 @@ where
let mut witness = WitnessCollection {
k: params.k(),
current_phase,
// Seems inefficient to recreate all this data
advice: vec![domain.empty_lagrange_assigned(); meta.num_advice_columns],
instances,
challenges: &challenges,
Expand Down
3 changes: 3 additions & 0 deletions halo2_proofs/src/protostar.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
mod error_check;
mod expression;
mod witness;
159 changes: 159 additions & 0 deletions halo2_proofs/src/protostar/error_check.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
struct Accumulator {}
struct Transcript {}

const D: usize = 5;
const NUM_ADVICE_VARS: usize = 5;
const NUM_FIXED_COLS: usize = 5;
const NUM_CHALLENGES: usize = 5;

// Represents the data required to evaluate e_i(X)
struct Row {
// not sure we need the index here
idx: usize,
// beta_0 * beta_1 = \beta^i
beta_0: u64,
beta_1: u64,

// We want to populate this not only with the current row, but also all the rotations.
// I.e., the constraint G may depend on rows i+1, i-1 etc.
advice: [u64; NUM_ADVICE_VARS],
}

struct ChallengeRow {
// slack = [mu, mu+1, ..., mu+D]
slack: u64,
// Evaluation of acc.challenge + X*tx.challenges for X = 0,1,..,D
challenges_eval: [u64; NUM_CHALLENGES],
}

// index of active gate, and the Lagrange normalization factor for q(q-1)...(q-l)
struct SelectorActivation(usize, u64);

struct FixedRow {
// index of the only active
// represents the active row.
active_constraint: usize,

// constants for this row
fixed: [u64; NUM_FIXED_COLS],
}

struct ErrorRow {
// Evaluations
evaluations: [u64; D],
}

// represents G, and evaluates it
fn eval_constraint(constants: FixedRow, row: Row, challenges: &ChallengeRow) -> u64 {
0
}

// at a given row i, compute acc.row[i] + X * tx.row[i]
fn eval_error_at_row(
constants: FixedRow,
acc_row: &Row,
tx_row: &Row,
challenges: &[ChallengeRow; D],
degree: usize,
) -> ErrorRow {
let mut error_row: [u64; D];

// Here, acc and tx rows would just contain info about where the actual values in acc and tx are located
// We can do something like row.get(i) to actually grab the data from memory.
// something like in ValueSource::get.
// If the row constants contains information about which gate is active,
// then we only need to grab the data that is relevant for this row
// (if the selector for a gate is 0, then we don't compute anything so we don't need the data either)
let mut tmp_row = acc_row;

// compute e_i(0)
error_row[0] = eval_constraint(constants, tmp_row, &challenges[0]);
for l in 1..D {
// need to impl AddAssign
// tmp_row += acc_row;

// compute e_i(l), using the
error_row[l] = eval_constraint(constants, tmp_row, &challenges[l]);
}
ErrorRow {
evaluations: error_row,
}
}

fn prove(accumulator: Accumulator, transcript: Transcript) {
// Ensure iop_transcript is well formed/rederive challenges

// Sample `alpha`/ValueSource::Y() for random-linear combination of gates?
// Not necessary with proper selector structure.

// / // prepare evaluations of challenges ()
// / challenges: [FF;d]
// / challenges[0] = acc.challenges
// / for l : [1..d] {
// / challenges[l] = challenges[l-1] + tx.challenges
// / }

// / interpolate each row over 1,...,d-1
// / for row_i : transcript.rows {
// / acc_i = accumulator.row
// / for l : d-1 {
// / acc_i += row_i
// /
// / }
// / }
}

use std::rc::Rc;

#[derive(Clone)]
enum Node {
Var(String),
Sum(Vec<Rc<Node>>),
Product(Vec<Rc<Node>>),
Power(Rc<Node>, i32),
}

fn degree(node: &Rc<Node>) -> i32 {
match &**node {
Node::Var(_) => 1,
Node::Sum(children) => children.iter().map(degree).max().unwrap(),
Node::Product(children) => children.iter().map(degree).sum(),
Node::Power(base, exponent) => degree(base) * exponent,
}
}

fn homogenize(node: Rc<Node>, total_degree: i32, new_var: &str) -> Rc<Node> {
match &*node {
Node::Var(_) => node.clone(),
Node::Sum(children) => {
let mut new_children = vec![];
for child in children {
let child_degree = degree(child);
if child_degree < total_degree {
new_children.push(Rc::new(Node::Product(vec![
homogenize(child.clone(), total_degree, new_var),
Rc::new(Node::Power(
Rc::new(Node::Var(new_var.to_string())),
total_degree - child_degree,
)),
])));
} else {
new_children.push(homogenize(child.clone(), total_degree, new_var));
}
}
Rc::new(Node::Sum(new_children))
}
Node::Product(children) => Rc::new(Node::Product(
children
.iter()
.map(|child| homogenize(child.clone(), total_degree, new_var))
.collect(),
)),
Node::Power(_, _) => panic!("Power nodes are not supported in the original polynomial"),
}
}

fn homogenize_ast(root: Rc<Node>, new_var: &str) -> Rc<Node> {
let total_degree = degree(&root);
homogenize(root, total_degree, new_var)
}
115 changes: 115 additions & 0 deletions halo2_proofs/src/protostar/expression.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
use ff::Field;

use crate::plonk::{AdviceQuery, Challenge, Expression, FixedQuery, InstanceQuery, Selector};

#[derive(Clone)]
struct Slack;

/// Low-degree expression representing an identity that must hold over the committed columns.
#[derive(Clone)]
pub enum HomogenizedExpression<F> {
Slack(),
/// This is a constant polynomial
Constant(F),
/// This is a virtual selector
Selector(Selector),
/// This is a fixed column queried at a certain relative location
Fixed(FixedQuery),
/// This is an advice (witness) column queried at a certain relative location
Advice(AdviceQuery),
/// This is an instance (external) column queried at a certain relative location
Instance(InstanceQuery),
/// This is a challenge, where the second parameter represents the power of a Challenge
ChallengePower(Challenge, usize),
/// This is a negated polynomial
Negated(Box<HomogenizedExpression<F>>),
/// This is the sum of two polynomials
Sum(Box<HomogenizedExpression<F>>, Box<HomogenizedExpression<F>>),
/// This is the product of two polynomials
Product(Box<HomogenizedExpression<F>>, Box<HomogenizedExpression<F>>),
/// This is a scaled polynomial
Scaled(Box<HomogenizedExpression<F>>, F),
}

impl<F: Field> Expression<F> {
fn homogenize(self) -> HomogenizedExpression<F> {
match self {
Expression::Constant(v) => HomogenizedExpression::Constant(v),
Expression::Selector(v) => HomogenizedExpression::Selector(v),
Expression::Fixed(v) => HomogenizedExpression::Fixed(v),
Expression::Advice(v) => HomogenizedExpression::Advice(v),
Expression::Instance(v) => HomogenizedExpression::Instance(v),
Expression::Challenge(c) => HomogenizedExpression::ChallengePower(v, 1),
Expression::Negated(e) => HomogenizedExpression::Negated(Box::new(e.homogenize())),
Expression::Sum(e1, e2) => {
HomogenizedExpression::Sum(Box::new(e1.homogenize()), Box::new(e2.homogenize()))
}
Expression::Product(e1, e2) => {
HomogenizedExpression::Product(Box::new(e1.homogenize()), Box::new(e2.homogenize()))
}
Expression::Scaled(e, v) => HomogenizedExpression::Scaled(Box::new(e.homogenize()), v),
}
}
}

impl<F: Field> HomogenizedExpression<F> {
fn distribute_challenge_powers(self, challenge: &Challenge, power: usize) -> Self {
match self {
HomogenizedExpression::ChallengePower(other_challenge, d) => {
if other_challenge == *challenge {
if d == 1 {
panic!("Challenge power cannot be other than 1")
}
HomogenizedExpression::ChallengePower(*challenge, 1 + power)
} else {
}
}
HomogenizedExpression::Negated(e) => HomogenizedExpression::Negated(Box::new(
e.distribute_challenge_powers(challenge, power),
)),
HomogenizedExpression::Sum(e1, e2) => HomogenizedExpression::Sum(
Box::new(e1.distribute_challenge_powers(challenge, power)),
Box::new(e2.distribute_challenge_powers(challenge, power)),
),
HomogenizedExpression::Product(e1, e2) => {
if power == 0 {
HomogenizedExpression::Product(
Box::new(e1.distribute_challenge_powers(challenge, power)),
Box::new(e2.distribute_challenge_powers(challenge, power)),
)
} else {
HomogenizedExpression::Product(
Box::new(e1.distribute_challenge_powers(challenge, power)),
Box::new(e2.distribute_challenge_powers(challenge, power)),
)
}
}
HomogenizedExpression::Scaled(v, f) => HomogenizedExpression::Scaled(
Box::new(v.distribute_challenge_powers(challenge, power)),
f,
),
v => {
if power == 0 {
v
} else {
HomogenizedExpression::Product(
Box::new(v),
Box::new(HomogenizedExpression::ChallengePower(*challenge, power)),
)
}
}
}
}

fn flatten_challenge_powers(self, challenges: &[Challenge]) -> Self {
let mut expr = self;
for challenge in challenges {
(expr, _) = expr.flatten_challenge_powers_inner(challenge);
}
expr
}

fn homogenize(self) -> Self {
HomogenizedExpression::Slack()
}
}
Loading