Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Recover constraint debugging #1756

Draft
wants to merge 94 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
94 commits
Select commit Hold shift + click to select a range
84250bd
Revert "Remove counter-productive 'debug' option (#1547)"
matthiasgoergens May 31, 2024
b1a937e
Introduce new trait
matthiasgoergens May 31, 2024
0f6e437
Lifetime shenanigans
matthiasgoergens May 31, 2024
e453c64
Introduce more
matthiasgoergens May 31, 2024
7d42524
Remove unnecessary lifetime
matthiasgoergens May 31, 2024
bdb5838
Fix lifetimes
matthiasgoergens May 31, 2024
d8581ea
More conversion
matthiasgoergens May 31, 2024
59d0808
No vars
matthiasgoergens May 31, 2024
986d1cc
Inline vars
matthiasgoergens May 31, 2024
e3b6ed5
Minimize diff
matthiasgoergens May 31, 2024
fe43ff1
Convert more
matthiasgoergens May 31, 2024
0062e8e
Fix
matthiasgoergens May 31, 2024
3a8eb3c
Simpler
matthiasgoergens May 31, 2024
109ae4a
More uniform
matthiasgoergens May 31, 2024
3960271
Simplify
matthiasgoergens May 31, 2024
f724f1c
Fmt
matthiasgoergens May 31, 2024
548026b
Convert more
matthiasgoergens May 31, 2024
08ddd98
Poseidon convert
matthiasgoergens May 31, 2024
7d128b3
Forward
matthiasgoergens May 31, 2024
bfbdf9a
More
matthiasgoergens May 31, 2024
71222b2
Commitment
matthiasgoergens May 31, 2024
cea92e2
Convert all
matthiasgoergens May 31, 2024
839a8b8
PublicInputs
matthiasgoergens May 31, 2024
e9c7559
Stopgop
matthiasgoergens May 31, 2024
f36f903
Remove PublicInputs from HasNamedColumns
matthiasgoergens May 31, 2024
d427924
Merge remote-tracking branch 'origin/main' into matthias/recover-cons…
matthiasgoergens Jun 3, 2024
e062d97
Merge remote-tracking branch 'origin/main' into matthias/recover-cons…
matthiasgoergens Jun 5, 2024
4ff65b1
Fix
matthiasgoergens Jun 5, 2024
84f64d1
Merge remote-tracking branch 'origin/main' into matthias/recover-cons…
matthiasgoergens Jun 10, 2024
d58eef6
Try using Expr<'a, T> instead of U in NoColumns
Jun 10, 2024
11bac0c
Move PublicInputs to a GAT
Jun 10, 2024
5bf4795
Move View to a GAT
Jun 10, 2024
585aa7b
Relax to_typed_starkframe
Jun 11, 2024
8a8b500
Add FromIterator to GenerateConstraint's GATs
Jun 11, 2024
67697a0
Fix type of PublicInputs
Jun 11, 2024
57dc0ea
Revert "Fix type of PublicInputs"
Jun 11, 2024
5250921
Add a helper function to skip StarkFrame
Jun 11, 2024
88bb433
Fix Unstark
Jun 11, 2024
1fe6ac7
Add Debug requirement to GenerateConstraints
Jun 11, 2024
1daef17
Add Debug to GenerateConstraints::View
Jun 11, 2024
ec55208
Remove debugging code from expr
Jun 11, 2024
d2f8960
Make location and term public
Jun 11, 2024
1450845
Fix build_debug to just evaluate
Jun 11, 2024
4fee3db
Finish debug_single_trace
Jun 11, 2024
340425b
Fix indentation
Jun 11, 2024
cfd544c
Whoops
Jun 11, 2024
681fc12
Make ConstraintType public
Jun 12, 2024
c68459c
Handle FromIterator when Column is empty
Jun 12, 2024
7c2296e
Filter out when constraints are applicable
Jun 12, 2024
d9f3652
cargo clippy
Jun 13, 2024
702a64f
cargo fmt
Jun 13, 2024
88c6b6d
Fix Display instance for Unstark
Jun 13, 2024
0199021
cargo fmt
Jun 13, 2024
5b83f11
Another cargo fmt
Jun 13, 2024
6f862ab
Merge branch 'main' into matthias/recover-constraint-debugging
Jun 13, 2024
7deefcd
Simplify instances
Jun 13, 2024
40a4d37
Generalise NoColumns to ShadowColumns
Jun 18, 2024
0800ac3
cargo fmt
Jun 18, 2024
68eb2f0
cargo clippy
Jun 18, 2024
0188f8c
Merge remote-tracking branch 'origin/main' into matthias/recover-cons…
matthiasgoergens Jun 19, 2024
8c6b983
Merge remote-tracking branch 'origin/main' into matthias/recover-cons…
matthiasgoergens Jun 19, 2024
05eeaf4
Minimise diff
matthiasgoergens Jun 19, 2024
340c708
Default to no constraints
matthiasgoergens Jun 19, 2024
a2e0bff
Simplify the implementation of StarkFrameTyped
Jun 19, 2024
d70bca3
Merge remote-tracking branch 'origin/matthias/recover-constraint-debu…
matthiasgoergens Jun 20, 2024
93ad4c7
[no ci] WIP and BROKEN
Jun 20, 2024
7b2f8c2
Merge branch 'matthias/recover-constraint-debugging' into matthias/re…
matthiasgoergens Jun 21, 2024
9ddbd37
Format
matthiasgoergens Jun 21, 2024
e428b17
Merge branch 'matthias/recover-constraint-debugging' into matthias/re…
matthiasgoergens Jun 21, 2024
2cfbab1
Replace qualified path with 'use'
matthiasgoergens Jun 21, 2024
8f4d442
Revert
matthiasgoergens Jun 21, 2024
595a85c
Format
matthiasgoergens Jun 21, 2024
0c46b6d
Simplify
matthiasgoergens Jun 21, 2024
587beb0
Restore
matthiasgoergens Jun 21, 2024
c0ad81c
Simplify
matthiasgoergens Jun 21, 2024
7c78c97
Fix
matthiasgoergens Jun 21, 2024
fd2cc3b
Simplify
matthiasgoergens Jun 21, 2024
cfaeb71
Convert skeleton CPU
matthiasgoergens Jun 21, 2024
d16a030
Convert CPU
matthiasgoergens Jun 21, 2024
f40123d
Convert full word memory
matthiasgoergens Jun 21, 2024
7064b4e
Simplify
matthiasgoergens Jun 21, 2024
0cb369c
Convert more
matthiasgoergens Jun 21, 2024
22493b7
Working version
Jun 21, 2024
e3dcd5e
cargo fmt
Jun 21, 2024
bc62799
Migrate Unstark to a StarkFrom
Jun 21, 2024
07bbbca
Remove HasNamedColumns
Jun 21, 2024
c2bad47
Match directly on StarkFrom
Jun 21, 2024
715e41d
Remove passthrough to G from StarkFrom
Jun 21, 2024
ebc1c29
Inline Unstark
Jul 1, 2024
b67f8ee
Remove all the unecessary code
Jul 1, 2024
051c9a3
Refactor common structure to an unstark macro
Jul 1, 2024
ff0652b
cargo clippy
Jul 1, 2024
4b32868
Merge branch 'main' into matthias/recover-constraint-debugging
Jul 1, 2024
0f4c6f2
Clean up convenience type aliases
Jul 3, 2024
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
89 changes: 47 additions & 42 deletions circuits/src/bitshift/stark.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use starky::stark::Stark;

use super::columns::BitshiftView;
use crate::columns_view::{HasNamedColumns, NumberOfColumns};
use crate::expr::{build_ext, build_packed, ConstraintBuilder};
use crate::expr::{build_ext, build_packed, ConstraintBuilder, GenerateConstraints};
use crate::unstark::NoColumns;

/// Bitshift Trace Constraints
Expand All @@ -30,45 +30,50 @@ impl<F, const D: usize> HasNamedColumns for BitshiftStark<F, D> {
const COLUMNS: usize = BitshiftView::<()>::NUMBER_OF_COLUMNS;
const PUBLIC_INPUTS: usize = 0;

fn generate_constraints<'a, T: Copy>(
vars: &StarkFrameTyped<BitshiftView<Expr<'a, T>>, NoColumns<Expr<'a, T>>>,
) -> ConstraintBuilder<Expr<'a, T>> {
let lv = vars.local_values.executed;
let nv = vars.next_values.executed;
let mut constraints = ConstraintBuilder::default();

// Constraints on shift amount
// They ensure:
// 1. Shift amount increases with each row by 0 or 1.
// (We allow increases of 0 in order to allow the table to add
// multiple same value rows. This is needed when we have multiple
// `SHL` or `SHR` operations with the same shift amount.)
// 2. We have shift amounts starting from 0 to max possible value of 31.
// (This is due to RISC-V max shift amount being 31.)

let diff = nv.amount - lv.amount;
// Check: initial amount value is set to 0
constraints.first_row(lv.amount);
// Check: amount value is increased by 1 or kept unchanged
constraints.transition(diff * (diff - 1));
// Check: last amount value is set to 31
constraints.last_row(lv.amount - 31);

// Constraints on multiplier
// They ensure:
// 1. Shift multiplier is multiplied by 2 only if amount increases.
// 2. We have shift multiplier from 1 to max possible value of 2^31.

// Check: initial multiplier value is set to 1 = 2^0
constraints.first_row(lv.multiplier - 1);
// Check: multiplier value is doubled if amount is increased
constraints.transition(nv.multiplier - (1 + diff) * lv.multiplier);
// Check: last multiplier value is set to 2^31
// (Note that based on the previous constraint, this is already
// satisfied if the last amount value is 31. We leave it for readability.)
constraints.last_row(lv.multiplier - (1 << 31));

constraints
impl<'a, F, T: Copy, U, const D: usize>
GenerateConstraints<'a, T, T, BitshiftView<Expr<'a, T>>, NoColumns<U>>
for BitshiftStark<F, { D }>
{
fn generate_constraints(
vars: &StarkFrameTyped<BitshiftView<Expr<'a, T>>, NoColumns<U>>,
) -> ConstraintBuilder<Expr<'a, T>> {
let lv = vars.local_values.executed;
let nv = vars.next_values.executed;
let mut constraints = ConstraintBuilder::default();

// Constraints on shift amount
// They ensure:
// 1. Shift amount increases with each row by 0 or 1.
// (We allow increases of 0 in order to allow the table to add
// multiple same value rows. This is needed when we have multiple
// `SHL` or `SHR` operations with the same shift amount.)
// 2. We have shift amounts starting from 0 to max possible value of 31.
// (This is due to RISC-V max shift amount being 31.)

let diff = nv.amount - lv.amount;
// Check: initial amount value is set to 0
constraints.first_row(lv.amount);
// Check: amount value is increased by 1 or kept unchanged
constraints.transition(diff * (diff - 1));
// Check: last amount value is set to 31
constraints.last_row(lv.amount - 31);

// Constraints on multiplier
// They ensure:
// 1. Shift multiplier is multiplied by 2 only if amount increases.
// 2. We have shift multiplier from 1 to max possible value of 2^31.

// Check: initial multiplier value is set to 1 = 2^0
constraints.first_row(lv.multiplier - 1);
// Check: multiplier value is doubled if amount is increased
constraints.transition(nv.multiplier - (1 + diff) * lv.multiplier);
// Check: last multiplier value is set to 2^31
// (Note that based on the previous constraint, this is already
// satisfied if the last amount value is 31. We leave it for readability.)
constraints.last_row(lv.multiplier - (1 << 31));

constraints
}
}

impl<F: RichField + Extendable<D>, const D: usize> Stark<F, D> for BitshiftStark<F, D> {
Expand All @@ -88,7 +93,7 @@ impl<F: RichField + Extendable<D>, const D: usize> Stark<F, D> for BitshiftStark
FE: FieldExtension<D2, BaseField = F>,
P: PackedField<Scalar = FE>, {
let expr_builder = ExprBuilder::default();
let constraints = generate_constraints(&expr_builder.to_typed_starkframe(vars));
let constraints = Self::generate_constraints(&expr_builder.to_typed_starkframe(vars));
build_packed(constraints, constraint_consumer);
}

Expand All @@ -101,7 +106,7 @@ impl<F: RichField + Extendable<D>, const D: usize> Stark<F, D> for BitshiftStark
constraint_consumer: &mut RecursiveConstraintConsumer<F, D>,
) {
let expr_builder = ExprBuilder::default();
let constraints = generate_constraints(&expr_builder.to_typed_starkframe(vars));
let constraints = Self::generate_constraints(&expr_builder.to_typed_starkframe(vars));
build_ext(constraints, circuit_builder, constraint_consumer);
}
}
Expand Down
73 changes: 38 additions & 35 deletions circuits/src/cpu/stark.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ use super::columns::{CpuState, OpSelectors};
use super::{bitwise, branches, div, ecall, jalr, memory, mul, signed_comparison, sub};
use crate::columns_view::{HasNamedColumns, NumberOfColumns};
use crate::cpu::shift;
use crate::expr::{build_ext, build_packed, ConstraintBuilder};
use crate::expr::{build_ext, build_packed, ConstraintBuilder, GenerateConstraints};
use crate::unstark::NoColumns;

/// A Gadget for CPU Instructions
///
Expand All @@ -40,7 +41,7 @@ fn pc_ticks_up<'a, P: Copy>(lv: &CpuState<Expr<'a, P>>, cb: &mut ConstraintBuild
/// Ie exactly one of them should be 1, and all others 0 in each row.
/// See <https://en.wikipedia.org/wiki/One-hot>
fn binary_selectors<'a, P: Copy>(
ops: &'a OpSelectors<Expr<'a, P>>,
jakzale marked this conversation as resolved.
Show resolved Hide resolved
ops: &OpSelectors<Expr<'a, P>>,
cb: &mut ConstraintBuilder<Expr<'a, P>>,
) {
// selectors have value 0 or 1.
Expand Down Expand Up @@ -77,35 +78,39 @@ fn populate_op2_value<'a, P: Copy>(
const COLUMNS: usize = CpuState::<()>::NUMBER_OF_COLUMNS;
const PUBLIC_INPUTS: usize = 0;

fn generate_constraints<'a, T: Copy, U>(
vars: &'a StarkFrameTyped<CpuState<Expr<'a, T>>, Vec<U>>,
jakzale marked this conversation as resolved.
Show resolved Hide resolved
) -> ConstraintBuilder<Expr<'a, T>> {
let lv = &vars.local_values;
let mut constraints = ConstraintBuilder::default();

pc_ticks_up(lv, &mut constraints);

binary_selectors(&lv.inst.ops, &mut constraints);

// Registers
populate_op2_value(lv, &mut constraints);

// ADD is now handled by its own table.
constraints.always(lv.inst.ops.add);
sub::constraints(lv, &mut constraints);
bitwise::constraints(lv, &mut constraints);
branches::comparison_constraints(lv, &mut constraints);
branches::constraints(lv, &mut constraints);
memory::constraints(lv, &mut constraints);
signed_comparison::signed_constraints(lv, &mut constraints);
signed_comparison::slt_constraints(lv, &mut constraints);
shift::constraints(lv, &mut constraints);
div::constraints(lv, &mut constraints);
mul::constraints(lv, &mut constraints);
jalr::constraints(lv, &mut constraints);
ecall::constraints(lv, &mut constraints);

constraints
impl<'a, F, T: Copy, U, const D: usize>
GenerateConstraints<'a, T, T, CpuState<Expr<'a, T>>, NoColumns<U>> for CpuStark<F, { D }>
{
fn generate_constraints(
vars: &StarkFrameTyped<CpuState<Expr<'a, T>>, NoColumns<U>>,
) -> ConstraintBuilder<Expr<'a, T>> {
let lv = &vars.local_values;
let mut constraints = ConstraintBuilder::default();

pc_ticks_up(lv, &mut constraints);

binary_selectors(&lv.inst.ops, &mut constraints);

// Registers
populate_op2_value(lv, &mut constraints);

// ADD is now handled by its own table.
constraints.always(lv.inst.ops.add);
sub::constraints(lv, &mut constraints);
bitwise::constraints(lv, &mut constraints);
branches::comparison_constraints(lv, &mut constraints);
branches::constraints(lv, &mut constraints);
memory::constraints(lv, &mut constraints);
signed_comparison::signed_constraints(lv, &mut constraints);
signed_comparison::slt_constraints(lv, &mut constraints);
shift::constraints(lv, &mut constraints);
div::constraints(lv, &mut constraints);
mul::constraints(lv, &mut constraints);
jalr::constraints(lv, &mut constraints);
ecall::constraints(lv, &mut constraints);

constraints
}
}

impl<F: RichField + Extendable<D>, const D: usize> Stark<F, D> for CpuStark<F, D> {
Expand All @@ -125,8 +130,7 @@ impl<F: RichField + Extendable<D>, const D: usize> Stark<F, D> for CpuStark<F, D
FE: FieldExtension<D2, BaseField = F>,
P: PackedField<Scalar = FE>, {
let expr_builder = ExprBuilder::default();
let vars = expr_builder.to_typed_starkframe(vars);
let constraints = generate_constraints(&vars);
let constraints = Self::generate_constraints(&expr_builder.to_typed_starkframe(vars));
matthiasgoergens marked this conversation as resolved.
Show resolved Hide resolved
build_packed(constraints, constraint_consumer);
}

Expand All @@ -139,8 +143,7 @@ impl<F: RichField + Extendable<D>, const D: usize> Stark<F, D> for CpuStark<F, D
constraint_consumer: &mut RecursiveConstraintConsumer<F, D>,
) {
let expr_builder = ExprBuilder::default();
let vars = expr_builder.to_typed_starkframe(vars);
let constraints = generate_constraints(&vars);
let constraints = Self::generate_constraints(&expr_builder.to_typed_starkframe(vars));
build_ext(constraints, circuit_builder, constraint_consumer);
}
}
Expand Down
84 changes: 45 additions & 39 deletions circuits/src/cpu_skeleton/stark.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use starky::stark::Stark;

use super::columns::CpuSkeleton;
use crate::columns_view::{HasNamedColumns, NumberOfColumns};
use crate::expr::{build_ext, build_packed, ConstraintBuilder};
use crate::expr::{build_ext, build_packed, ConstraintBuilder, GenerateConstraints};
use crate::stark::mozak_stark::PublicInputs;

#[derive(Clone, Copy, Default, StarkNameDisplay)]
Expand All @@ -30,42 +30,47 @@ const COLUMNS: usize = CpuSkeleton::<()>::NUMBER_OF_COLUMNS;
// Public inputs: [PC of the first row]
const PUBLIC_INPUTS: usize = PublicInputs::<()>::NUMBER_OF_COLUMNS;

fn generate_constraints<'a, T: Copy>(
vars: &StarkFrameTyped<CpuSkeleton<Expr<'a, T>>, PublicInputs<Expr<'a, T>>>,
) -> ConstraintBuilder<Expr<'a, T>> {
let lv = vars.local_values;
let nv = vars.next_values;
let public_inputs = vars.public_inputs;
let mut constraints = ConstraintBuilder::default();

constraints.first_row(lv.pc - public_inputs.entry_point);
// Clock starts at 2. This is to differentiate
// execution clocks (2 and above) from
// clk values `0` and `1` which are reserved for
// elf initialisation and zero initialisation respectively.
constraints.first_row(lv.clk - 2);

let clock_diff = nv.clk - lv.clk;
constraints.transition(clock_diff.is_binary());

// clock only counts up when we are still running.
constraints.transition(clock_diff - lv.is_running);

// We start in running state.
constraints.first_row(lv.is_running - 1);

// We may transition to a non-running state.
constraints.transition(nv.is_running * (nv.is_running - lv.is_running));

// We end in a non-running state.
constraints.last_row(lv.is_running);

// NOTE: in our old CPU table we had constraints that made sure nothing
// changes anymore, once we are halted. We don't need those
// anymore: the only thing that can change are memory or registers. And
// our CTLs make sure, that after we are halted, no more memory
// or register changes are allowed.
constraints
impl<'a, F, T: Copy, const D: usize>
GenerateConstraints<'a, T, T, CpuSkeleton<Expr<'a, T>>, PublicInputs<Expr<'a, T>>>
for CpuSkeletonStark<F, { D }>
{
fn generate_constraints(
vars: &StarkFrameTyped<CpuSkeleton<Expr<'a, T>>, PublicInputs<Expr<'a, T>>>,
) -> ConstraintBuilder<Expr<'a, T>> {
let lv = vars.local_values;
let nv = vars.next_values;
let public_inputs = vars.public_inputs;
let mut constraints = ConstraintBuilder::default();

constraints.first_row(lv.pc - public_inputs.entry_point);
// Clock starts at 2. This is to differentiate
// execution clocks (2 and above) from
// clk values `0` and `1` which are reserved for
// elf initialisation and zero initialisation respectively.
constraints.first_row(lv.clk - 2);

let clock_diff = nv.clk - lv.clk;
constraints.transition(clock_diff.is_binary());

// clock only counts up when we are still running.
constraints.transition(clock_diff - lv.is_running);

// We start in running state.
constraints.first_row(lv.is_running - 1);

// We may transition to a non-running state.
constraints.transition(nv.is_running * (nv.is_running - lv.is_running));

// We end in a non-running state.
constraints.last_row(lv.is_running);

// NOTE: in our old CPU table we had constraints that made sure nothing
// changes anymore, once we are halted. We don't need those
// anymore: the only thing that can change are memory or registers. And
// our CTLs make sure, that after we are halted, no more memory
// or register changes are allowed.
constraints
}
}

impl<F: RichField + Extendable<D>, const D: usize> Stark<F, D> for CpuSkeletonStark<F, D> {
Expand All @@ -86,7 +91,7 @@ impl<F: RichField + Extendable<D>, const D: usize> Stark<F, D> for CpuSkeletonSt
P: PackedField<Scalar = FE>, {
let expr_builder = ExprBuilder::default();
let vars = expr_builder.to_typed_starkframe(vars);
let constraints = generate_constraints(&vars);
let constraints = Self::generate_constraints(&vars);
build_packed(constraints, consumer);
}

Expand All @@ -97,7 +102,8 @@ impl<F: RichField + Extendable<D>, const D: usize> Stark<F, D> for CpuSkeletonSt
consumer: &mut RecursiveConstraintConsumer<F, D>,
) {
let eb = ExprBuilder::default();
let constraints = generate_constraints(&eb.to_typed_starkframe(vars));
let vars = eb.to_typed_starkframe(vars);
let constraints = Self::generate_constraints(&vars);
build_ext(constraints, builder, consumer);
}

Expand Down
16 changes: 15 additions & 1 deletion circuits/src/expr.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::panic::Location;

pub use expr::PureEvaluator;
use expr::{BinOp, Cached, Evaluator, Expr, UnaOp};
use expr::{BinOp, Cached, Evaluator, Expr, StarkFrameTyped, UnaOp};
use plonky2::field::extension::{Extendable, FieldExtension};
use plonky2::field::packed::PackedField;
use plonky2::hash::hash_types::RichField;
Expand Down Expand Up @@ -175,7 +175,15 @@ pub fn build_packed<F, FE, P, const D: usize, const D2: usize>(
.map(|c| c.map(|constraint| evaluator.eval(constraint)))
.collect::<Vec<_>>();

let mozak_stark_debug = std::env::var("MOZAK_STARK_DEBUG").is_ok();
for c in evaluated {
if mozak_stark_debug && !c.term.is_zeros() {
log::error!(
"ConstraintConsumer - DEBUG trace (non-zero-constraint): {}",
c.location
);
}

(match c.constraint_type {
ConstraintType::FirstRow => ConstraintConsumer::constraint_first_row,
ConstraintType::Always => ConstraintConsumer::constraint,
Expand All @@ -184,3 +192,9 @@ pub fn build_packed<F, FE, P, const D: usize, const D2: usize>(
})(yield_constr, c.term);
}
}

pub trait GenerateConstraints<'a, T, U, View, PublicInputs> {
fn generate_constraints(
vars: &StarkFrameTyped<View, PublicInputs>,
) -> ConstraintBuilder<Expr<'a, T>>;
}
Loading
Loading