Skip to content

Add the basefold recursive verifier. #13

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

Draft
wants to merge 48 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
fcb8ed5
Dense Matrix
Apr 22, 2025
66c7734
Added hints
Apr 22, 2025
6f8fe11
Finished get_base_codeword_dimensions
Apr 23, 2025
3813078
WIP mmcs
Apr 23, 2025
b10539b
WIP mmcs
Apr 24, 2025
8c35009
Update mmcs
Apr 25, 2025
3afc01b
WIP mmcs
Apr 26, 2025
1156331
Finished MMCS
Apr 28, 2025
18aef52
Added dot_product
Apr 29, 2025
31cd29c
query_phase input
Apr 29, 2025
85f00f2
WIP query_phase
Apr 29, 2025
ef34a42
Fix bug in sorting
Apr 30, 2025
b63abd8
WIP query phase
May 1, 2025
2a5f3da
WIP query phase
May 1, 2025
f4c21af
WIP query phase
May 1, 2025
746f04f
WIP query phase
May 2, 2025
6f98a17
New Sorting Impl
May 4, 2025
8e3312b
WIP query phase
May 5, 2025
dda2fff
WIP query phase
May 6, 2025
39443ef
WIP query phase
May 6, 2025
520da7e
Finished query_phase encoding
May 6, 2025
282892f
Support serialized input
May 10, 2025
0dc7b56
Implement the naive encode small method
yczhangsjtu May 15, 2025
c06f3c9
Bug workaround
May 16, 2025
7da7077
Merge branch 'kunming/basefold-verifier' into cyte/remove-early-stopping
yczhangsjtu May 16, 2025
4553fc3
Temp store: starting to use openvm mmcs instruction
yczhangsjtu May 30, 2025
39fa15c
Temp store: index to bits
yczhangsjtu May 30, 2025
2acf9eb
Temp store: clean up mmcs
yczhangsjtu May 30, 2025
4ffe361
Temp store: clear compilation errors
yczhangsjtu May 30, 2025
fbeda8b
Merge e2e modification
yczhangsjtu Jun 3, 2025
629bb23
Fix compilation error
yczhangsjtu Jun 3, 2025
8044af5
Merge remote-tracking branch 'origin/main' into cyte/switch-mmcs
yczhangsjtu Jun 3, 2025
2516cf5
Fix hash variable reading bug
yczhangsjtu Jun 3, 2025
ba93b0b
Use dyn array for dimensions
yczhangsjtu Jun 3, 2025
ec5e218
Fix multiplication between var and ext
yczhangsjtu Jun 3, 2025
586bc0d
Fix mmcs reading
yczhangsjtu Jun 6, 2025
b35aa5d
Remove unnecessary witness stream
yczhangsjtu Jun 10, 2025
0b8d751
Add doc for generating mmcs test data
yczhangsjtu Jun 10, 2025
ac1cbfd
Try fixing mmcs
yczhangsjtu Jun 11, 2025
4f22895
Try fixing mmcs
yczhangsjtu Jun 11, 2025
0b66801
Try fixing mmcs
yczhangsjtu Jun 11, 2025
25c21c0
Add comment
yczhangsjtu Jun 11, 2025
edc4848
Use the same poseidon2 constants as the test data
yczhangsjtu Jun 11, 2025
c04e4b7
Specify branch for test data gen
yczhangsjtu Jun 11, 2025
d5c8baa
MMCS test passes
yczhangsjtu Jun 11, 2025
8029daa
Rewrite fold coeff according to current basefold code
yczhangsjtu Jun 13, 2025
c1e92b0
Fix
yczhangsjtu Jun 19, 2025
188eb85
Merge branch 'main' of https://github.com/scroll-tech/ceno-recursion-…
yczhangsjtu Jun 19, 2025
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
52 changes: 46 additions & 6 deletions src/arithmetics/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -170,19 +170,31 @@ pub fn gen_idx_arr<C: Config>(builder: &mut Builder<C>, len: Usize<C::N>) -> Arr
res
}

// Evaluate eq polynomial.
pub fn eq_eval<C: Config>(
builder: &mut Builder<C>,
x: &Array<C, Ext<C::F, C::EF>>,
y: &Array<C, Ext<C::F, C::EF>>,
) -> Ext<C::F, C::EF> {
eq_eval_with_index::<C>(builder, x, y, Usize::from(0), Usize::from(0), x.len())
}

// Evaluate eq polynomial.
pub fn eq_eval_with_index<C: Config>(
builder: &mut Builder<C>,
x: &Array<C, Ext<C::F, C::EF>>,
y: &Array<C, Ext<C::F, C::EF>>,
xlo: Usize<C::N>,
ylo: Usize<C::N>,
len: Usize<C::N>,
) -> Ext<C::F, C::EF> {
let acc: Ext<C::F, C::EF> = builder.constant(C::EF::ONE);

iter_zip!(builder, x, y).for_each(|idx_vec, builder| {
let ptr_x = idx_vec[0];
let ptr_y = idx_vec[1];
let v_x = builder.iter_ptr_get(&x, ptr_x);
let v_y = builder.iter_ptr_get(&y, ptr_y);
builder.range(0, len).for_each(|i_vec, builder| {
let i = i_vec[0];
let ptr_x: Var<C::N> = builder.eval(xlo.clone() + i);
let ptr_y: Var<C::N> = builder.eval(ylo.clone() + i);
let v_x = builder.get(&x, ptr_x);
let v_y = builder.get(&y, ptr_y);
let xi_yi: Ext<C::F, C::EF> = builder.eval(v_x * v_y);
let one: Ext<C::F, C::EF> = builder.constant(C::EF::ONE);
let new_acc: Ext<C::F, C::EF> = builder.eval(acc * (xi_yi + xi_yi - v_x - v_y + one));
Expand Down Expand Up @@ -385,6 +397,34 @@ pub fn build_eq_x_r_vec_sequential<C: Config>(
evals
}

pub fn build_eq_x_r_vec_sequential_with_offset<C: Config>(
builder: &mut Builder<C>,
r: &Array<C, Ext<C::F, C::EF>>,
offset: Usize<C::N>,
) -> Array<C, Ext<C::F, C::EF>> {
// we build eq(x,r) from its evaluations
// we want to evaluate eq(x,r) over x \in {0, 1}^num_vars
// for example, with num_vars = 4, x is a binary vector of 4, then
// 0 0 0 0 -> (1-r0) * (1-r1) * (1-r2) * (1-r3)
// 1 0 0 0 -> r0 * (1-r1) * (1-r2) * (1-r3)
// 0 1 0 0 -> (1-r0) * r1 * (1-r2) * (1-r3)
// 1 1 0 0 -> r0 * r1 * (1-r2) * (1-r3)
// ....
// 1 1 1 1 -> r0 * r1 * r2 * r3
// we will need 2^num_var evaluations

let r_len: Var<C::N> = builder.eval(r.len() - offset);
let evals_len: Felt<C::F> = builder.constant(C::F::ONE);
let evals_len = builder.exp_power_of_2_v::<Felt<C::F>>(evals_len, r_len);
let evals_len = builder.cast_felt_to_var(evals_len);

let evals: Array<C, Ext<C::F, C::EF>> = builder.dyn_array(evals_len);
// _debug
// build_eq_x_r_helper_sequential_offset(r, &mut evals, E::ONE);
// unsafe { std::mem::transmute(evals) }
evals
}

pub fn ceil_log2(x: usize) -> usize {
assert!(x > 0, "ceil_log2: x must be positive");
// Calculate the number of bits in usize
Expand Down
51 changes: 51 additions & 0 deletions src/basefold_verifier/basefold.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
use openvm_native_compiler::{asm::AsmConfig, prelude::*};
use openvm_native_recursion::hints::Hintable;
use openvm_stark_sdk::p3_baby_bear::BabyBear;
use p3_field::extension::BinomialExtensionField;
use serde::Deserialize;

use super::{mmcs::*, structs::DIMENSIONS};

pub type F = BabyBear;
pub type E = BinomialExtensionField<F, DIMENSIONS>;
pub type InnerConfig = AsmConfig<F, E>;

pub type HashDigest = MmcsCommitment;
#[derive(Deserialize)]
pub struct BasefoldCommitment {
pub commit: HashDigest,
pub log2_max_codeword_size: usize,
// pub trivial_commits: Vec<HashDigest>,
}

impl Hintable<InnerConfig> for BasefoldCommitment {
type HintVariable = BasefoldCommitmentVariable<InnerConfig>;

fn read(builder: &mut Builder<InnerConfig>) -> Self::HintVariable {
let commit = HashDigest::read(builder);
let log2_max_codeword_size = Usize::Var(usize::read(builder));
// let trivial_commits = Vec::<HashDigest>::read(builder);

BasefoldCommitmentVariable {
commit,
log2_max_codeword_size,
// trivial_commits,
}
}

fn write(&self) -> Vec<Vec<<InnerConfig as Config>::N>> {
let mut stream = Vec::new();
stream.extend(self.commit.write());
stream.extend(<usize as Hintable<InnerConfig>>::write(&self.log2_max_codeword_size));
// stream.extend(self.trivial_commits.write());
stream
}
}

pub type HashDigestVariable<C> = MmcsCommitmentVariable<C>;
#[derive(DslVariable, Clone)]
pub struct BasefoldCommitmentVariable<C: Config> {
pub commit: HashDigestVariable<C>,
pub log2_max_codeword_size: Usize<C::N>,
// pub trivial_commits: Array<C, HashDigestVariable<C>>,
}
89 changes: 89 additions & 0 deletions src/basefold_verifier/extension_mmcs.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
use openvm_native_compiler::{asm::AsmConfig, prelude::*};
use openvm_native_recursion::{hints::Hintable, vars::HintSlice};
use openvm_stark_sdk::p3_baby_bear::BabyBear;
use p3_field::extension::BinomialExtensionField;

use super::{mmcs::*, structs::*};

pub type F = BabyBear;
pub type E = BinomialExtensionField<F, DIMENSIONS>;
pub type InnerConfig = AsmConfig<F, E>;

pub struct ExtMmcsVerifierInput {
pub commit: MmcsCommitment,
pub dimensions: Vec<usize>,
pub index: usize,
pub opened_values: Vec<Vec<E>>,
pub proof: MmcsProof,
}

impl Hintable<InnerConfig> for ExtMmcsVerifierInput {
type HintVariable = ExtMmcsVerifierInputVariable<InnerConfig>;

fn read(builder: &mut Builder<InnerConfig>) -> Self::HintVariable {
let commit = MmcsCommitment::read(builder);
let dimensions = Vec::<usize>::read(builder);
let index_bits = Vec::<usize>::read(builder);
let opened_values = Vec::<Vec<E>>::read(builder);
let length = Usize::from(builder.hint_var());
let id = Usize::from(builder.hint_load());
let proof = HintSlice { length, id };

ExtMmcsVerifierInputVariable {
commit,
dimensions,
index_bits,
opened_values,
proof,
}
}

fn write(&self) -> Vec<Vec<<InnerConfig as Config>::N>> {
let mut stream = Vec::new();
stream.extend(self.commit.write());
stream.extend(self.dimensions.write());
let mut index_bits = Vec::new();
let mut index = self.index;
while index > 0 {
index_bits.push(index % 2);
index /= 2;
}
stream.extend(<Vec<usize> as Hintable<InnerConfig>>::write(&index_bits));
stream.extend(self.opened_values.write());
stream.extend(
self.proof
.iter()
.map(|p| p.to_vec())
.collect::<Vec<_>>()
.write(),
);
stream
}
}

#[derive(DslVariable, Clone)]
pub struct ExtMmcsVerifierInputVariable<C: Config> {
pub commit: MmcsCommitmentVariable<C>,
pub dimensions: Array<C, Var<C::N>>,
pub index_bits: Array<C, Var<C::N>>,
pub opened_values: Array<C, Array<C, Ext<C::F, C::EF>>>,
pub proof: HintSlice<C>,
}

pub(crate) fn ext_mmcs_verify_batch<C: Config>(
builder: &mut Builder<C>,
input: ExtMmcsVerifierInputVariable<C>,
) {
let dimensions = match input.dimensions {
Array::Dyn(ptr, len) => Array::Dyn(ptr, len.clone()),
_ => panic!("Expected a dynamic array of felts"),
};

builder.verify_batch_ext(
&dimensions,
&input.opened_values,
input.proof.id.get_var(),
&input.index_bits,
&input.commit.value,
);
}
54 changes: 54 additions & 0 deletions src/basefold_verifier/field.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
const TWO_ADICITY: usize = 32;
const TWO_ADIC_GENERATORS: [usize; 33] = [
0x0000000000000001,
0xffffffff00000000,
0x0001000000000000,
0xfffffffeff000001,
0xefffffff00000001,
0x00003fffffffc000,
0x0000008000000000,
0xf80007ff08000001,
0xbf79143ce60ca966,
0x1905d02a5c411f4e,
0x9d8f2ad78bfed972,
0x0653b4801da1c8cf,
0xf2c35199959dfcb6,
0x1544ef2335d17997,
0xe0ee099310bba1e2,
0xf6b2cffe2306baac,
0x54df9630bf79450e,
0xabd0a6e8aa3d8a0e,
0x81281a7b05f9beac,
0xfbd41c6b8caa3302,
0x30ba2ecd5e93e76d,
0xf502aef532322654,
0x4b2a18ade67246b5,
0xea9d5a1336fbc98b,
0x86cdcc31c307e171,
0x4bbaf5976ecfefd8,
0xed41d05b78d6e286,
0x10d78dd8915a171d,
0x59049500004a4485,
0xdfa8c93ba46d2666,
0x7e9bd009b86a0845,
0x400a7f755588e659,
0x185629dcda58878c,
];

use openvm_native_compiler::prelude::*;
use p3_field::FieldAlgebra;

fn two_adic_generator<C: Config>(
builder: &mut Builder<C>,
bits: Var<C::N>,
) -> Var<C::F> {
let bits_limit = builder.eval(Usize::from(TWO_ADICITY) + Usize::from(1));
builder.assert_less_than_slow_small_rhs(bits, bits_limit);

let two_adic_generator: Array<C, Var<<C as Config>::F>> = builder.dyn_array(TWO_ADICITY + 1);
builder.range(0, TWO_ADICITY + 1).for_each(|i_vec, builder| {
let i = i_vec[0];
builder.set_value(&two_adic_generator, i, C::F::from_canonical_usize(TWO_ADIC_GENERATORS[i.value()]));
});
builder.get(&two_adic_generator, bits)
}
98 changes: 98 additions & 0 deletions src/basefold_verifier/hash.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
use openvm_native_compiler::{asm::AsmConfig, prelude::*};
use openvm_native_recursion::hints::{Hintable, VecAutoHintable};
use openvm_stark_sdk::p3_baby_bear::BabyBear;
use p3_field::extension::BinomialExtensionField;
use p3_field::FieldAlgebra;
use serde::Deserialize;

use super::structs::DIMENSIONS;

pub const DIGEST_ELEMS: usize = 8;

pub type F = BabyBear;
pub type E = BinomialExtensionField<F, DIMENSIONS>;
pub type InnerConfig = AsmConfig<F, E>;

#[derive(Deserialize)]
pub struct Hash {
pub value: [F; DIGEST_ELEMS],
}

impl Default for Hash {
fn default() -> Self {
Hash {
value: [F::ZERO; DIGEST_ELEMS],
}
}
}

impl Hintable<InnerConfig> for Hash {
type HintVariable = HashVariable<InnerConfig>;

fn read(builder: &mut Builder<InnerConfig>) -> Self::HintVariable {
let value = builder.dyn_array(DIGEST_ELEMS);
for i in 0..DIGEST_ELEMS {
let tmp = F::read(builder);
builder.set(&value, i, tmp);
}

HashVariable { value }
}

fn write(&self) -> Vec<Vec<<InnerConfig as Config>::N>> {
let mut stream = Vec::new();
// Write out each entries
for i in 0..DIGEST_ELEMS {
stream.extend(self.value[i].write());
}
stream
}
}
impl VecAutoHintable for Hash {}

#[derive(DslVariable, Clone)]
pub struct HashVariable<C: Config> {
pub value: Array<C, Felt<C::F>>,
}

pub fn hash_iter_slices<C: Config>(
builder: &mut Builder<C>,
// _hash: HashVariable<C>,
_values: Array<C, Array<C, Felt<C::F>>>,
) -> Array<C, Felt<C::F>> {
// XXX: verify hash
builder.hint_felts_fixed(DIGEST_ELEMS)
}

pub fn compress<C: Config>(
builder: &mut Builder<C>,
// _hash: HashVariable<C>,
_values: Array<C, Array<C, Felt<C::F>>>,
) -> Array<C, Felt<C::F>> {
// XXX: verify hash
builder.hint_felts_fixed(DIGEST_ELEMS)
}

#[cfg(test)]
mod tests {
use openvm_native_compiler::asm::AsmBuilder;
use openvm_native_compiler_derive::iter_zip;
use openvm_stark_backend::config::StarkGenericConfig;
use openvm_stark_sdk::config::baby_bear_poseidon2::BabyBearPoseidon2Config;
type SC = BabyBearPoseidon2Config;
type F = BabyBear;
type E = BinomialExtensionField<F, 4>;
type EF = <SC as StarkGenericConfig>::Challenge;

use crate::basefold_verifier::basefold::HashDigest;

use super::*;
#[test]
fn test_read_to_hash_variable() {
let mut builder = AsmBuilder::<F, EF>::default();

let hint = HashDigest::read(&mut builder);
let dst: HashVariable<_> = builder.uninit();
builder.assign(&dst, hint);
}
}
Loading