-
Notifications
You must be signed in to change notification settings - Fork 324
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: verify constraints in DSL + basic verifier setup (#395)
Co-authored-by: John Guibas <[email protected]> Co-authored-by: John Guibas <[email protected]> Co-authored-by: John Guibas <[email protected]>
- Loading branch information
1 parent
72ab5a5
commit b305d73
Showing
11 changed files
with
247 additions
and
44 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,33 +1,63 @@ | ||
use p3_air::Air; | ||
use std::fs::File; | ||
use std::marker::PhantomData; | ||
|
||
use p3_field::AbstractField; | ||
use sp1_core::air::MachineAir; | ||
use sp1_core::stark::{GenericVerifierConstraintFolder, MachineChip, StarkGenericConfig}; | ||
use sp1_recursion_compiler::ir::{Ext, SymbolicExt}; | ||
|
||
#[allow(clippy::type_complexity)] | ||
#[allow(dead_code)] | ||
fn verify_constraints<SC: StarkGenericConfig, A: MachineAir<SC::Val>>( | ||
chip: MachineChip<SC, A>, | ||
folder: &mut GenericVerifierConstraintFolder< | ||
SC::Val, | ||
SC::Challenge, | ||
Ext<SC::Val, SC::Challenge>, | ||
SymbolicExt<SC::Val, SC::Challenge>, | ||
>, | ||
) where | ||
A: for<'a> Air< | ||
GenericVerifierConstraintFolder< | ||
'a, | ||
SC::Val, | ||
SC::Challenge, | ||
Ext<SC::Val, SC::Challenge>, | ||
SymbolicExt<SC::Val, SC::Challenge>, | ||
>, | ||
>, | ||
{ | ||
chip.eval(folder); | ||
} | ||
use sp1_core::stark::RiscvAir; | ||
use sp1_core::stark::StarkGenericConfig; | ||
use sp1_core::utils; | ||
use sp1_core::utils::BabyBearPoseidon2; | ||
use sp1_core::SP1Prover; | ||
use sp1_core::SP1Stdin; | ||
use sp1_recursion_compiler::gnark::GnarkBackend; | ||
use sp1_recursion_compiler::ir::Builder; | ||
use sp1_recursion_compiler::ir::{Ext, Felt}; | ||
use sp1_recursion_compiler::verifier::verify_constraints; | ||
use sp1_recursion_compiler::verifier::StarkGenericBuilderConfig; | ||
use std::collections::HashMap; | ||
use std::io::Write; | ||
|
||
fn main() { | ||
println!("Hello, world!"); | ||
type SC = BabyBearPoseidon2; | ||
type F = <SC as StarkGenericConfig>::Val; | ||
type EF = <SC as StarkGenericConfig>::Challenge; | ||
|
||
// Generate a dummy proof. | ||
utils::setup_logger(); | ||
let elf = | ||
include_bytes!("../../../examples/cycle-tracking/program/elf/riscv32im-succinct-zkvm-elf"); | ||
let proofs = SP1Prover::prove(elf, SP1Stdin::new()) | ||
.unwrap() | ||
.proof | ||
.shard_proofs; | ||
let proof = &proofs[0]; | ||
|
||
// Extract verification metadata. | ||
let machine = RiscvAir::machine(SC::new()); | ||
let chips = machine | ||
.chips() | ||
.iter() | ||
.filter(|chip| proof.chip_ids.contains(&chip.name())) | ||
.collect::<Vec<_>>(); | ||
let chip = chips[0]; | ||
let opened_values = &proof.opened_values.chips[0]; | ||
|
||
// Run the verify inside the DSL. | ||
let mut builder = Builder::<StarkGenericBuilderConfig<F, SC>>::default(); | ||
let g: Felt<F> = builder.eval(F::one()); | ||
let zeta: Ext<F, EF> = builder.eval(F::one()); | ||
let alpha: Ext<F, EF> = builder.eval(F::one()); | ||
verify_constraints::<F, SC, _>(&mut builder, chip, opened_values, g, zeta, alpha); | ||
|
||
// Emit the constraints using the Gnark backend. | ||
let mut backend = GnarkBackend::<StarkGenericBuilderConfig<F, BabyBearPoseidon2>> { | ||
nb_backend_vars: 0, | ||
used: HashMap::new(), | ||
phantom: PhantomData, | ||
}; | ||
let result = backend.compile(builder.operations); | ||
let manifest_dir = env!("CARGO_MANIFEST_DIR"); | ||
let path = format!("{}/src/gnark/lib/main.go", manifest_dir); | ||
let mut file = File::create(path).unwrap(); | ||
file.write_all(result.as_bytes()).unwrap(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,6 +6,7 @@ mod instructions; | |
mod ptr; | ||
mod symbolic; | ||
mod types; | ||
mod utils; | ||
mod var; | ||
|
||
pub use builder::*; | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
use std::ops::MulAssign; | ||
|
||
use super::{Builder, Config, Variable}; | ||
|
||
impl<C: Config> Builder<C> { | ||
pub fn exp_power_of_2<V: Variable<C>, E: Into<V::Expression>>( | ||
&mut self, | ||
e: E, | ||
power_log: usize, | ||
) -> V | ||
where | ||
V::Expression: MulAssign<V::Expression> + Clone, | ||
{ | ||
let mut e = e.into(); | ||
for _ in 0..power_log { | ||
e *= e.clone(); | ||
} | ||
self.eval(e) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
use std::marker::PhantomData; | ||
|
||
use p3_air::Air; | ||
use p3_field::AbstractExtensionField; | ||
use p3_field::AbstractField; | ||
use p3_field::Field; | ||
use sp1_core::air::MachineAir; | ||
use sp1_core::stark::ChipOpenedValues; | ||
use sp1_core::stark::{ | ||
AirOpenedValues, GenericVerifierConstraintFolder, MachineChip, StarkGenericConfig, | ||
}; | ||
|
||
use crate::prelude::{Builder, Config, Ext, Felt, SymbolicExt}; | ||
use crate::verifier::StarkGenericBuilderConfig; | ||
|
||
impl<C: Config> Builder<C> { | ||
pub fn const_opened_values( | ||
&mut self, | ||
opened_values: &AirOpenedValues<C::EF>, | ||
) -> AirOpenedValues<Ext<C::F, C::EF>> { | ||
AirOpenedValues::<Ext<C::F, C::EF>> { | ||
local: opened_values | ||
.local | ||
.iter() | ||
.map(|s| self.eval(SymbolicExt::Const(*s))) | ||
.collect(), | ||
next: opened_values | ||
.next | ||
.iter() | ||
.map(|s| self.eval(SymbolicExt::Const(*s))) | ||
.collect(), | ||
} | ||
} | ||
} | ||
|
||
pub fn verify_constraints<N: Field, SC: StarkGenericConfig + Clone, A: MachineAir<SC::Val>>( | ||
builder: &mut Builder<StarkGenericBuilderConfig<N, SC>>, | ||
chip: &MachineChip<SC, A>, | ||
opening: &ChipOpenedValues<SC::Challenge>, | ||
g: Felt<SC::Val>, | ||
zeta: Ext<SC::Val, SC::Challenge>, | ||
alpha: Ext<SC::Val, SC::Challenge>, | ||
) where | ||
A: for<'a> Air< | ||
GenericVerifierConstraintFolder< | ||
'a, | ||
SC::Val, | ||
SC::Challenge, | ||
Ext<SC::Val, SC::Challenge>, | ||
SymbolicExt<SC::Val, SC::Challenge>, | ||
>, | ||
>, | ||
{ | ||
let g_inv: Felt<SC::Val> = builder.eval(g / SC::Val::one()); | ||
let z_h: Ext<SC::Val, SC::Challenge> = builder.exp_power_of_2(zeta, opening.log_degree); | ||
let one: Ext<SC::Val, SC::Challenge> = builder.eval(SC::Val::one()); | ||
let is_first_row = builder.eval(z_h / (zeta - one)); | ||
let is_last_row = builder.eval(z_h / (zeta - g_inv)); | ||
let is_transition = builder.eval(zeta - g_inv); | ||
|
||
let preprocessed = builder.const_opened_values(&opening.preprocessed); | ||
let main = builder.const_opened_values(&opening.main); | ||
let perm = builder.const_opened_values(&opening.permutation); | ||
|
||
let zero: Ext<SC::Val, SC::Challenge> = builder.eval(SC::Val::zero()); | ||
let zero_expr: SymbolicExt<SC::Val, SC::Challenge> = zero.into(); | ||
let mut folder = GenericVerifierConstraintFolder::< | ||
SC::Val, | ||
SC::Challenge, | ||
Ext<SC::Val, SC::Challenge>, | ||
SymbolicExt<SC::Val, SC::Challenge>, | ||
> { | ||
preprocessed: preprocessed.view(), | ||
main: main.view(), | ||
perm: perm.view(), | ||
perm_challenges: &[SC::Challenge::zero(), SC::Challenge::zero()], | ||
cumulative_sum: builder.eval(SC::Val::zero()), | ||
is_first_row, | ||
is_last_row, | ||
is_transition, | ||
alpha, | ||
accumulator: zero_expr, | ||
_marker: PhantomData, | ||
}; | ||
|
||
let monomials = (0..SC::Challenge::D) | ||
.map(SC::Challenge::monomial) | ||
.collect::<Vec<_>>(); | ||
|
||
let quotient_parts = opening | ||
.quotient | ||
.chunks_exact(SC::Challenge::D) | ||
.map(|chunk| { | ||
chunk | ||
.iter() | ||
.zip(monomials.iter()) | ||
.map(|(x, m)| *x * *m) | ||
.sum() | ||
}) | ||
.collect::<Vec<SC::Challenge>>(); | ||
|
||
let mut zeta_powers = zeta; | ||
let quotient: Ext<SC::Val, SC::Challenge> = builder.eval(SC::Val::zero()); | ||
let quotient_expr: SymbolicExt<SC::Val, SC::Challenge> = quotient.into(); | ||
for quotient_part in quotient_parts { | ||
zeta_powers = builder.eval(zeta_powers * zeta); | ||
builder.assign(quotient, zeta_powers * quotient_part); | ||
} | ||
let quotient: Ext<SC::Val, SC::Challenge> = builder.eval(quotient_expr); | ||
folder.alpha = alpha; | ||
|
||
chip.eval(&mut folder); | ||
let folded_constraints = folder.accumulator; | ||
let expected_folded_constraints = z_h * quotient; | ||
builder.assert_ext_eq(folded_constraints, expected_folded_constraints); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
mod constraints; | ||
|
||
use std::marker::PhantomData; | ||
|
||
#[allow(unused_imports)] | ||
pub use constraints::*; | ||
use p3_field::Field; | ||
use sp1_core::stark::StarkGenericConfig; | ||
|
||
use crate::prelude::Config; | ||
|
||
#[derive(Clone)] | ||
pub struct StarkGenericBuilderConfig<N, SC> { | ||
marker: PhantomData<(N, SC)>, | ||
} | ||
|
||
impl<N: Field, SC: StarkGenericConfig + Clone> Config for StarkGenericBuilderConfig<N, SC> { | ||
type N = N; | ||
type F = SC::Val; | ||
type EF = SC::Challenge; | ||
} |