Skip to content

Commit

Permalink
Merge pull request #494 from powdr-labs/stark-via-eigen
Browse files Browse the repository at this point in the history
eStark backend via eigen-zkvm's starky
  • Loading branch information
Leo authored Sep 26, 2023
2 parents 83cc485 + eed6949 commit 4a55a54
Show file tree
Hide file tree
Showing 12 changed files with 402 additions and 119 deletions.
3 changes: 2 additions & 1 deletion backend/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@ number = { path = "../number" }
strum = { version = "0.24.1", features = ["derive"] }
ast = { version = "0.1.0", path = "../ast" }
log = "0.4.17"
json = "^0.12"
serde_json = "1.0"
thiserror = "1.0.43"
starky = { git = "https://github.com/0xEigenLabs/eigen-zkvm.git", branch = "main" }

[dev-dependencies]
mktemp = "0.5.0"
Expand Down
15 changes: 10 additions & 5 deletions backend/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#[cfg(feature = "halo2")]
mod halo2_impl;
mod pilcom_cli;
mod pilstark;

use ast::analyzed::Analyzed;
use number::{DegreeType, FieldElement};
Expand All @@ -15,8 +15,10 @@ pub enum BackendType {
#[cfg(feature = "halo2")]
#[strum(serialize = "halo2-mock")]
Halo2Mock,
#[strum(serialize = "pilcom-cli")]
PilcomCli,
#[strum(serialize = "estark")]
EStark,
#[strum(serialize = "pil-stark-cli")]
PilStarkCli,
}

impl BackendType {
Expand All @@ -26,15 +28,18 @@ impl BackendType {
#[cfg(feature = "halo2")]
const HALO2_MOCK_FACTORY: WithoutSetupFactory<halo2_impl::Halo2Mock> =
WithoutSetupFactory(PhantomData);
const PILCOM_CLI_FACTORY: WithoutSetupFactory<pilcom_cli::PilcomCli> =
const ESTARK_FACTORY: WithoutSetupFactory<pilstark::estark::EStark> =
WithoutSetupFactory(PhantomData);
const PIL_STARK_CLI_FACTORY: WithoutSetupFactory<pilstark::PilStarkCli> =
WithoutSetupFactory(PhantomData);

match self {
#[cfg(feature = "halo2")]
BackendType::Halo2 => &HALO2_FACTORY,
#[cfg(feature = "halo2")]
BackendType::Halo2Mock => &HALO2_MOCK_FACTORY,
BackendType::PilcomCli => &PILCOM_CLI_FACTORY,
BackendType::PilStarkCli => &PIL_STARK_CLI_FACTORY,
BackendType::EStark => &ESTARK_FACTORY,
}
}
}
Expand Down
153 changes: 153 additions & 0 deletions backend/src/pilstark/estark.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
use crate::{pilstark, BackendImpl};
use ast::analyzed::Analyzed;
use number::{BigInt, DegreeType, FieldElement, GoldilocksField};

use starky::{
merklehash::MerkleTreeGL,
polsarray::{PolKind, PolsArray},
stark_gen::StarkProof,
stark_setup::StarkSetup,
stark_verify::stark_verify,
transcript::TranscriptGL,
types::{StarkStruct, Step, PIL},
};

pub struct EStark {
params: StarkStruct,
}

impl<F: FieldElement> BackendImpl<F> for EStark {
/// Creates our default configuration stark struct.
fn new(degree: DegreeType) -> Self {
if F::modulus().to_arbitrary_integer() != GoldilocksField::modulus().to_arbitrary_integer()
{
unimplemented!("eSTARK is only implemented for Goldilocks field");
}
assert!(degree > 1);

let n_bits = (DegreeType::BITS - (degree - 1).leading_zeros()) as usize;
let n_bits_ext = n_bits + 1;

let steps = (2..=n_bits_ext)
.rev()
.step_by(4)
.map(|b| Step { nBits: b })
.collect();

let params = StarkStruct {
nBits: n_bits,
nBitsExt: n_bits_ext,
nQueries: 2,
verificationHashType: "GL".to_owned(),
steps,
};

Self { params }
}

fn prove(
&self,
pil: &Analyzed<F>,
fixed: &[(&str, Vec<F>)],
witness: &[(&str, Vec<F>)],
prev_proof: Option<crate::Proof>,
) -> (Option<crate::Proof>, Option<String>) {
if prev_proof.is_some() {
unimplemented!("aggregration is not implemented");
}

log::info!("Creating eSTARK proof.");

let mut pil: PIL = pilstark::json_exporter::export(pil);

// TODO starky requires a fixed column with the equivalent
// semantics to Polygon zkEVM's `L1` column.
// It takes the name of that column via the API.
// Powdr generated PIL will always have `main.first_step`,
// but directly given PIL may not have it.
// This is a hack to inject such column if it doesn't exist.
// It should be eventually improved.
let mut fixed = fixed.to_vec();
if !fixed.iter().any(|&(k, _)| k == "main.first_step") {
use starky::types::Reference;
pil.nConstants += 1;
pil.references.insert(
"main.first_step".to_string(),
Reference {
polType: None,
type_: "constP".to_string(),
id: fixed.len(),
polDeg: fixed[0].1.len(),
isArray: false,
elementType: None,
len: None,
},
);
fixed.push((
"main.first_step",
[vec![F::one()], vec![F::zero(); fixed[0].1.len() - 1]].concat(),
));
}

let const_pols = to_starky_pols_array(&fixed, &pil, PolKind::Constant);

if witness.is_empty() {
return (None, None);
}

let cm_pols = to_starky_pols_array(witness, &pil, PolKind::Commit);

let mut setup = StarkSetup::<MerkleTreeGL>::new(
&const_pols,
&mut pil,
&self.params,
Some("main.first_step".to_string()),
)
.unwrap();

let starkproof = StarkProof::<MerkleTreeGL>::stark_gen::<TranscriptGL>(
&cm_pols,
&const_pols,
&setup.const_tree,
&setup.starkinfo,
&setup.program,
&pil,
&self.params,
)
.unwrap();

assert!(stark_verify::<MerkleTreeGL, TranscriptGL>(
&starkproof,
&setup.const_root,
&setup.starkinfo,
&self.params,
&mut setup.program,
)
.unwrap());

(
Some(serde_json::to_vec(&starkproof).unwrap()),
Some(serde_json::to_string(&pil).unwrap()),
)
}
}

fn to_starky_pols_array<F: FieldElement>(
array: &[(&str, Vec<F>)],
pil: &PIL,
kind: PolKind,
) -> PolsArray {
let mut output = PolsArray::new(pil, kind);
assert_eq!(output.array.len(), array.len());
for ((_, from), to) in array.iter().zip(output.array.iter_mut()) {
assert_eq!(from.len(), to.len());

for (f, t) in from.iter().zip(to.iter_mut()) {
*t = TryInto::<u64>::try_into(f.to_integer().to_arbitrary_integer())
.unwrap()
.into();
}
}

output
}
Loading

0 comments on commit 4a55a54

Please sign in to comment.