mirror of
https://github.com/powdr-labs/powdr.git
synced 2026-04-20 03:03:25 -04:00
Pipeline
This commit is contained in:
@@ -7,7 +7,7 @@ use number::FieldElement;
|
||||
pub mod batcher;
|
||||
pub mod inference;
|
||||
|
||||
pub fn analyze<T: FieldElement>(
|
||||
pub(crate) fn analyze<T: FieldElement>(
|
||||
file: AnalysisASMFile<T>,
|
||||
monitor: &mut DiffMonitor,
|
||||
) -> Result<AnalysisASMFile<T>, Vec<String>> {
|
||||
|
||||
@@ -9,7 +9,7 @@ use std::ops;
|
||||
|
||||
use number::{DegreeType, FieldElement};
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||
pub struct PILFile<T>(pub Vec<PilStatement<T>>);
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use ::compiler::inputs_to_query_callback;
|
||||
use ::compiler::pipeline::Pipeline;
|
||||
use ast::analyzed::Analyzed;
|
||||
use criterion::{criterion_group, criterion_main, Criterion};
|
||||
|
||||
use executor::constant_evaluator;
|
||||
use mktemp::Temp;
|
||||
use number::{FieldElement, GoldilocksField};
|
||||
use riscv::{compile_rust_crate_to_riscv_asm, compiler};
|
||||
@@ -11,23 +11,8 @@ use riscv::CoProcessors;
|
||||
|
||||
type T = GoldilocksField;
|
||||
|
||||
fn get_pil() -> Analyzed<T> {
|
||||
let tmp_dir = Temp::new_dir().unwrap();
|
||||
let riscv_asm_files =
|
||||
compile_rust_crate_to_riscv_asm("../riscv/tests/riscv_data/keccak/Cargo.toml", &tmp_dir);
|
||||
let contents = compiler::compile(riscv_asm_files, &CoProcessors::base(), false);
|
||||
let parsed = parser::parse_asm::<T>(None, &contents).unwrap();
|
||||
let resolved = importer::resolve(None, parsed).unwrap();
|
||||
let analyzed = analysis::convert_asm_to_pil(resolved).unwrap();
|
||||
let graph = airgen::compile(analyzed);
|
||||
let pil = linker::link(graph).unwrap();
|
||||
let analyzed = pil_analyzer::analyze_string(&format!("{pil}"));
|
||||
pilopt::optimize(analyzed)
|
||||
}
|
||||
|
||||
fn run_witgen<T: FieldElement>(analyzed: &Analyzed<T>, input: Vec<T>) {
|
||||
let query_callback = inputs_to_query_callback(input);
|
||||
let constants = constant_evaluator::generate(analyzed);
|
||||
fn run_witgen<T: FieldElement>(analyzed: &Analyzed<T>, constants: Vec<(String, Vec<T>)>) {
|
||||
let query_callback = inputs_to_query_callback(vec![]);
|
||||
executor::witgen::WitnessGenerator::new(analyzed, &constants, query_callback).generate();
|
||||
}
|
||||
|
||||
@@ -35,8 +20,23 @@ fn criterion_benchmark(c: &mut Criterion) {
|
||||
let mut group = c.benchmark_group("keccak-executor-benchmark");
|
||||
group.sample_size(10);
|
||||
|
||||
let keccak_pil = get_pil();
|
||||
group.bench_function("keccak", |b| b.iter(|| run_witgen(&keccak_pil, vec![])));
|
||||
let tmp_dir = Temp::new_dir().unwrap();
|
||||
let riscv_asm_files =
|
||||
compile_rust_crate_to_riscv_asm("../riscv/tests/riscv_data/keccak/Cargo.toml", &tmp_dir);
|
||||
let contents = compiler::compile(riscv_asm_files, &CoProcessors::base(), false);
|
||||
let pil_with_constants = Pipeline::<T>::default()
|
||||
.from_asm_string(contents, None)
|
||||
.optimized_pil_with_constants()
|
||||
.unwrap();
|
||||
|
||||
group.bench_function("keccak", |b| {
|
||||
b.iter(|| {
|
||||
run_witgen(
|
||||
&pil_with_constants.pil,
|
||||
pil_with_constants.constants.clone(),
|
||||
)
|
||||
})
|
||||
});
|
||||
group.finish();
|
||||
}
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ fn build_book_tests(kind: &str) {
|
||||
r#"
|
||||
#[test]
|
||||
fn {test_name}() {{
|
||||
run_book_test("book/{relative_name}");
|
||||
run_book_test("{kind}/book/{relative_name}");
|
||||
}}
|
||||
"#,
|
||||
)
|
||||
|
||||
@@ -2,414 +2,17 @@
|
||||
|
||||
#![deny(clippy::print_stdout)]
|
||||
|
||||
use std::ffi::OsStr;
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
use std::time::Instant;
|
||||
|
||||
use analysis::analyze;
|
||||
use analysis::convert_analyzed_to_pil_constraints;
|
||||
use ast::analyzed::Analyzed;
|
||||
use ast::DiffMonitor;
|
||||
|
||||
pub mod pipeline;
|
||||
pub mod test_util;
|
||||
pub mod util;
|
||||
mod verify;
|
||||
pub mod verify;
|
||||
|
||||
use ast::asm_analysis::AnalysisASMFile;
|
||||
pub use backend::{BackendType, Proof};
|
||||
use executor::witgen::QueryCallback;
|
||||
use itertools::Itertools;
|
||||
pub use verify::{
|
||||
verify, verify_asm_string, write_commits_to_fs, write_constants_to_fs, write_constraints_to_fs,
|
||||
};
|
||||
|
||||
use ast::parsed::PILFile;
|
||||
use executor::constant_evaluator;
|
||||
use number::FieldElement;
|
||||
|
||||
pub fn no_callback<T>() -> Option<fn(&str) -> Option<T>> {
|
||||
None
|
||||
}
|
||||
|
||||
/// Compiles a .pil or .asm file and runs witness generation.
|
||||
/// If the file ends in .asm, converts it to .pil first.
|
||||
/// Returns the compilation result if any compilation took place.
|
||||
pub fn compile_pil_or_asm<T: FieldElement>(
|
||||
file_name: &str,
|
||||
inputs: Vec<T>,
|
||||
output_dir: &Path,
|
||||
force_overwrite: bool,
|
||||
prove_with: Option<BackendType>,
|
||||
external_witness_values: Vec<(&str, Vec<T>)>,
|
||||
) -> Result<Option<CompilationResult<T>>, Vec<String>> {
|
||||
if file_name.ends_with(".asm") {
|
||||
compile_asm(
|
||||
file_name,
|
||||
inputs,
|
||||
output_dir,
|
||||
force_overwrite,
|
||||
prove_with,
|
||||
external_witness_values,
|
||||
)
|
||||
} else {
|
||||
Ok(Some(compile_pil(
|
||||
Path::new(file_name),
|
||||
output_dir,
|
||||
inputs_to_query_callback(inputs),
|
||||
prove_with,
|
||||
external_witness_values,
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn analyze_pil<T: FieldElement>(pil_file: &Path) -> Analyzed<T> {
|
||||
pil_analyzer::analyze(pil_file)
|
||||
}
|
||||
|
||||
/// Compiles a .pil file to its json form and also tries to generate
|
||||
/// constants and committed polynomials.
|
||||
/// @returns a compilation result, containing witness and fixed columns
|
||||
/// if they could be successfully generated.
|
||||
pub fn compile_pil<T: FieldElement, Q: QueryCallback<T>>(
|
||||
pil_file: &Path,
|
||||
output_dir: &Path,
|
||||
query_callback: Q,
|
||||
prove_with: Option<BackendType>,
|
||||
external_witness_values: Vec<(&str, Vec<T>)>,
|
||||
) -> CompilationResult<T> {
|
||||
compile(
|
||||
pil_analyzer::analyze(pil_file),
|
||||
pil_file.file_name().unwrap(),
|
||||
output_dir,
|
||||
query_callback,
|
||||
prove_with,
|
||||
external_witness_values,
|
||||
)
|
||||
}
|
||||
|
||||
/// Compiles a given PIL and tries to generate fixed and witness columns.
|
||||
/// @returns a compilation result, containing witness and fixed columns
|
||||
pub fn compile_pil_ast<T: FieldElement, Q: QueryCallback<T>>(
|
||||
pil: &PILFile<T>,
|
||||
file_name: &OsStr,
|
||||
output_dir: &Path,
|
||||
query_callback: Q,
|
||||
prove_with: Option<BackendType>,
|
||||
external_witness_values: Vec<(&str, Vec<T>)>,
|
||||
) -> CompilationResult<T> {
|
||||
// TODO exporting this to string as a hack because the parser
|
||||
// is tied into the analyzer due to imports.
|
||||
compile(
|
||||
pil_analyzer::analyze_string(&format!("{pil}")),
|
||||
file_name,
|
||||
output_dir,
|
||||
query_callback,
|
||||
prove_with,
|
||||
external_witness_values,
|
||||
)
|
||||
}
|
||||
|
||||
/// Compiles a .asm file, outputs the PIL on stdout and tries to generate
|
||||
/// fixed and witness columns.
|
||||
/// @returns a compilation result if any compilation was done.
|
||||
pub fn compile_asm<T: FieldElement>(
|
||||
file_name: &str,
|
||||
inputs: Vec<T>,
|
||||
output_dir: &Path,
|
||||
force_overwrite: bool,
|
||||
prove_with: Option<BackendType>,
|
||||
external_witness_values: Vec<(&str, Vec<T>)>,
|
||||
) -> Result<Option<CompilationResult<T>>, Vec<String>> {
|
||||
let contents = fs::read_to_string(file_name).unwrap();
|
||||
Ok(compile_asm_string(
|
||||
file_name,
|
||||
&contents,
|
||||
inputs,
|
||||
None,
|
||||
output_dir,
|
||||
force_overwrite,
|
||||
prove_with,
|
||||
external_witness_values,
|
||||
)?
|
||||
.1)
|
||||
}
|
||||
|
||||
#[allow(clippy::print_stderr)]
|
||||
pub fn compile_asm_string_to_analyzed_ast<T: FieldElement>(
|
||||
file_name: &str,
|
||||
contents: &str,
|
||||
monitor: Option<&mut DiffMonitor>,
|
||||
) -> Result<AnalysisASMFile<T>, Vec<String>> {
|
||||
let parsed = parser::parse_asm(Some(file_name), contents).unwrap_or_else(|err| {
|
||||
eprintln!("Error parsing .asm file:");
|
||||
err.output_to_stderr();
|
||||
panic!();
|
||||
});
|
||||
log::debug!("Resolve imports");
|
||||
let resolved =
|
||||
importer::resolve(Some(PathBuf::from(file_name)), parsed).map_err(|e| vec![e])?;
|
||||
log::debug!("Run analysis");
|
||||
let mut default_monitor = DiffMonitor::default();
|
||||
let monitor = monitor.unwrap_or(&mut default_monitor);
|
||||
let analyzed = analyze(resolved, monitor)?;
|
||||
log::debug!("Analysis done");
|
||||
log::trace!("{analyzed}");
|
||||
|
||||
Ok(analyzed)
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn convert_analyzed_to_pil<T: FieldElement>(
|
||||
file_name: &str,
|
||||
monitor: &mut DiffMonitor,
|
||||
analyzed: AnalysisASMFile<T>,
|
||||
inputs: Vec<T>,
|
||||
output_dir: &Path,
|
||||
force_overwrite: bool,
|
||||
prove_with: Option<BackendType>,
|
||||
external_witness_values: Vec<(&str, Vec<T>)>,
|
||||
) -> Result<(PathBuf, Option<CompilationResult<T>>), Vec<String>> {
|
||||
let constraints = convert_analyzed_to_pil_constraints(analyzed, monitor);
|
||||
log::debug!("Run airgen");
|
||||
let graph = airgen::compile(constraints);
|
||||
log::debug!("Airgen done");
|
||||
log::trace!("{graph}");
|
||||
log::debug!("Run linker");
|
||||
let pil = linker::link(graph)?;
|
||||
log::debug!("Linker done");
|
||||
log::trace!("{pil}");
|
||||
|
||||
let pil_file_name = format!(
|
||||
"{}.pil",
|
||||
Path::new(file_name).file_stem().unwrap().to_str().unwrap()
|
||||
);
|
||||
|
||||
let pil_file_path = output_dir.join(pil_file_name);
|
||||
if pil_file_path.exists() && !force_overwrite {
|
||||
eprintln!(
|
||||
"Target file {} already exists. Not overwriting.",
|
||||
pil_file_path.to_str().unwrap()
|
||||
);
|
||||
return Ok((pil_file_path, None));
|
||||
}
|
||||
|
||||
fs::write(&pil_file_path, format!("{pil}")).unwrap();
|
||||
|
||||
let pil_file_name = pil_file_path.file_name().unwrap();
|
||||
Ok((
|
||||
pil_file_path.clone(),
|
||||
Some(compile_pil_ast(
|
||||
&pil,
|
||||
pil_file_name,
|
||||
output_dir,
|
||||
inputs_to_query_callback(inputs),
|
||||
prove_with,
|
||||
external_witness_values,
|
||||
)),
|
||||
))
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn convert_analyzed_to_pil_with_callback<T: FieldElement, Q: QueryCallback<T>>(
|
||||
file_name: &str,
|
||||
monitor: &mut DiffMonitor,
|
||||
analyzed: AnalysisASMFile<T>,
|
||||
query_callback: Q,
|
||||
output_dir: &Path,
|
||||
force_overwrite: bool,
|
||||
prove_with: Option<BackendType>,
|
||||
external_witness_values: Vec<(&str, Vec<T>)>,
|
||||
) -> Result<(PathBuf, Option<CompilationResult<T>>), Vec<String>> {
|
||||
let constraints = convert_analyzed_to_pil_constraints(analyzed, monitor);
|
||||
log::debug!("Run airgen");
|
||||
let graph = airgen::compile(constraints);
|
||||
log::debug!("Airgen done");
|
||||
log::trace!("{graph}");
|
||||
log::debug!("Run linker");
|
||||
let pil = linker::link(graph)?;
|
||||
log::debug!("Linker done");
|
||||
log::trace!("{pil}");
|
||||
|
||||
let pil_file_name = format!(
|
||||
"{}.pil",
|
||||
Path::new(file_name).file_stem().unwrap().to_str().unwrap()
|
||||
);
|
||||
|
||||
let pil_file_path = output_dir.join(pil_file_name);
|
||||
if pil_file_path.exists() && !force_overwrite {
|
||||
eprintln!(
|
||||
"Target file {} already exists. Not overwriting.",
|
||||
pil_file_path.to_str().unwrap()
|
||||
);
|
||||
return Ok((pil_file_path, None));
|
||||
}
|
||||
|
||||
fs::write(&pil_file_path, format!("{pil}")).unwrap();
|
||||
|
||||
let pil_file_name = pil_file_path.file_name().unwrap();
|
||||
Ok((
|
||||
pil_file_path.clone(),
|
||||
Some(compile_pil_ast(
|
||||
&pil,
|
||||
pil_file_name,
|
||||
output_dir,
|
||||
query_callback,
|
||||
prove_with,
|
||||
external_witness_values,
|
||||
)),
|
||||
))
|
||||
}
|
||||
|
||||
pub type AnalyzedASTHook<'a, T> = &'a mut dyn FnMut(&AnalysisASMFile<T>);
|
||||
|
||||
/// Compiles the contents of a .asm file, outputs the PIL on stdout and tries to generate
|
||||
/// fixed and witness columns.
|
||||
///
|
||||
/// Returns the relative pil file name and the compilation result if any compilation was done.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn compile_asm_string<T: FieldElement>(
|
||||
file_name: &str,
|
||||
contents: &str,
|
||||
inputs: Vec<T>,
|
||||
analyzed_hook: Option<AnalyzedASTHook<T>>,
|
||||
output_dir: &Path,
|
||||
force_overwrite: bool,
|
||||
prove_with: Option<BackendType>,
|
||||
external_witness_values: Vec<(&str, Vec<T>)>,
|
||||
) -> Result<(PathBuf, Option<CompilationResult<T>>), Vec<String>> {
|
||||
let mut monitor = DiffMonitor::default();
|
||||
let analyzed = compile_asm_string_to_analyzed_ast(file_name, contents, Some(&mut monitor))?;
|
||||
if let Some(hook) = analyzed_hook {
|
||||
hook(&analyzed);
|
||||
};
|
||||
convert_analyzed_to_pil(
|
||||
file_name,
|
||||
&mut monitor,
|
||||
analyzed,
|
||||
inputs,
|
||||
output_dir,
|
||||
force_overwrite,
|
||||
prove_with,
|
||||
external_witness_values,
|
||||
)
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn compile_asm_string_with_callback<T: FieldElement, Q: QueryCallback<T>>(
|
||||
file_name: &str,
|
||||
contents: &str,
|
||||
query_callback: Q,
|
||||
analyzed_hook: Option<AnalyzedASTHook<T>>,
|
||||
output_dir: &Path,
|
||||
force_overwrite: bool,
|
||||
prove_with: Option<BackendType>,
|
||||
external_witness_values: Vec<(&str, Vec<T>)>,
|
||||
) -> Result<(PathBuf, Option<CompilationResult<T>>), Vec<String>> {
|
||||
let mut monitor = DiffMonitor::default();
|
||||
let analyzed = compile_asm_string_to_analyzed_ast(file_name, contents, Some(&mut monitor))?;
|
||||
if let Some(hook) = analyzed_hook {
|
||||
hook(&analyzed);
|
||||
};
|
||||
convert_analyzed_to_pil_with_callback(
|
||||
file_name,
|
||||
&mut monitor,
|
||||
analyzed,
|
||||
query_callback,
|
||||
output_dir,
|
||||
force_overwrite,
|
||||
prove_with,
|
||||
external_witness_values,
|
||||
)
|
||||
}
|
||||
|
||||
pub struct CompilationResult<T: FieldElement> {
|
||||
/// Constant columns, potentially incomplete (if success is false)
|
||||
pub constants: Vec<(String, Vec<T>)>,
|
||||
/// Witness columns, potentially None (if success is false)
|
||||
pub witness: Option<Vec<(String, Vec<T>)>>,
|
||||
/// Proof, potentially None (if success is false)
|
||||
pub proof: Option<Proof>,
|
||||
/// Serialized low level constraints, potentially None (if success is false)
|
||||
pub constraints_serialization: Option<String>,
|
||||
}
|
||||
|
||||
/// Optimizes a given pil and tries to generate constants and committed polynomials.
|
||||
/// @returns a compilation result, containing witness and fixed columns, if successful.
|
||||
fn compile<T: FieldElement, Q: QueryCallback<T>>(
|
||||
analyzed: Analyzed<T>,
|
||||
file_name: &OsStr,
|
||||
output_dir: &Path,
|
||||
query_callback: Q,
|
||||
prove_with: Option<BackendType>,
|
||||
external_witness_values: Vec<(&str, Vec<T>)>,
|
||||
) -> CompilationResult<T> {
|
||||
log::info!("Optimizing pil...");
|
||||
let analyzed = pilopt::optimize(analyzed);
|
||||
let optimized_pil_file_name = output_dir.join(format!(
|
||||
"{}_opt.pil",
|
||||
Path::new(file_name).file_stem().unwrap().to_str().unwrap()
|
||||
));
|
||||
fs::write(optimized_pil_file_name.clone(), format!("{analyzed}")).unwrap();
|
||||
log::info!("Wrote {}.", optimized_pil_file_name.to_str().unwrap());
|
||||
let start = Instant::now();
|
||||
log::info!("Evaluating fixed columns...");
|
||||
let constants = constant_evaluator::generate(&analyzed);
|
||||
log::info!("Took {}", start.elapsed().as_secs_f32());
|
||||
|
||||
let witness = (analyzed.constant_count() == constants.len()).then(|| {
|
||||
log::info!("Deducing witness columns...");
|
||||
let commits =
|
||||
executor::witgen::WitnessGenerator::new(&analyzed, &constants, query_callback)
|
||||
.with_external_witness_values(external_witness_values)
|
||||
.generate();
|
||||
|
||||
commits
|
||||
.into_iter()
|
||||
.map(|(name, c)| (name.to_string(), c))
|
||||
.collect::<Vec<_>>()
|
||||
});
|
||||
|
||||
let constants = constants
|
||||
.into_iter()
|
||||
.map(|(name, c)| (name.to_string(), c))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// Even if we don't have all constants and witnesses, some backends will
|
||||
// still output the constraint serialization.
|
||||
let (proof, constraints_serialization) = if let Some(backend) = prove_with {
|
||||
let factory = backend.factory::<T>();
|
||||
let backend = factory.create(analyzed.degree());
|
||||
|
||||
backend.prove(
|
||||
&analyzed,
|
||||
&constants,
|
||||
witness.as_deref().unwrap_or_default(),
|
||||
None,
|
||||
)
|
||||
} else {
|
||||
(None, None)
|
||||
};
|
||||
|
||||
let constants = constants
|
||||
.into_iter()
|
||||
.map(|(name, c)| (name.to_owned(), c))
|
||||
.collect();
|
||||
|
||||
let witness = witness.map(|v| {
|
||||
v.into_iter()
|
||||
.map(|(name, c)| (name.to_owned(), c))
|
||||
.collect()
|
||||
});
|
||||
|
||||
CompilationResult {
|
||||
constants,
|
||||
witness,
|
||||
proof,
|
||||
constraints_serialization,
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::print_stdout)]
|
||||
pub fn inputs_to_query_callback<T: FieldElement>(inputs: Vec<T>) -> impl QueryCallback<T> {
|
||||
// TODO: Pass bootloader inputs into this function
|
||||
|
||||
512
compiler/src/pipeline.rs
Normal file
512
compiler/src/pipeline.rs
Normal file
@@ -0,0 +1,512 @@
|
||||
use std::{
|
||||
fmt::Display,
|
||||
fs,
|
||||
path::{Path, PathBuf},
|
||||
time::Instant,
|
||||
};
|
||||
|
||||
use analysis::convert_analyzed_to_pil_constraints;
|
||||
use ast::{
|
||||
analyzed::Analyzed,
|
||||
asm_analysis::AnalysisASMFile,
|
||||
object::PILGraph,
|
||||
parsed::{asm::ASMProgram, PILFile},
|
||||
DiffMonitor,
|
||||
};
|
||||
use backend::{BackendType, Proof};
|
||||
use executor::{constant_evaluator, witgen::QueryCallback};
|
||||
use log::Level;
|
||||
use mktemp::Temp;
|
||||
use number::FieldElement;
|
||||
|
||||
use crate::verify::{write_commits_to_fs, write_constants_to_fs, write_constraints_to_fs};
|
||||
|
||||
pub struct GeneratedWitness<T: FieldElement> {
|
||||
pub pil: Analyzed<T>,
|
||||
pub constants: Vec<(String, Vec<T>)>,
|
||||
pub witness: Option<Vec<(String, Vec<T>)>>,
|
||||
}
|
||||
|
||||
pub struct PilWithConstants<T: FieldElement> {
|
||||
pub pil: Analyzed<T>,
|
||||
pub constants: Vec<(String, Vec<T>)>,
|
||||
}
|
||||
|
||||
pub struct ProofResult<T: FieldElement> {
|
||||
/// Constant columns, potentially incomplete (if success is false)
|
||||
pub constants: Vec<(String, Vec<T>)>,
|
||||
/// Witness columns, potentially None (if success is false)
|
||||
pub witness: Option<Vec<(String, Vec<T>)>>,
|
||||
/// Proof, potentially None (if success is false)
|
||||
pub proof: Option<Proof>,
|
||||
/// Serialized low level constraints, potentially None (if success is false)
|
||||
pub constraints_serialization: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
enum Stage {
|
||||
AsmFile,
|
||||
AsmString,
|
||||
Parsed,
|
||||
Resolved,
|
||||
AnalyzedAsm,
|
||||
PilConstraints,
|
||||
Graph,
|
||||
Linked,
|
||||
PilFile,
|
||||
PilString,
|
||||
AnalyzedPil,
|
||||
OptimizedPil,
|
||||
PilWithConstants,
|
||||
GeneratedWitness,
|
||||
Proof,
|
||||
}
|
||||
|
||||
enum Artifact<T: FieldElement> {
|
||||
/// The path to the .asm file.
|
||||
AsmFile(PathBuf),
|
||||
/// The contents of the .asm file, with an optional Path (for error messages).
|
||||
AsmString(Option<PathBuf>, String),
|
||||
/// The parsed .asm file, with an optional Path (for error messages).
|
||||
Parsed(Option<PathBuf>, ASMProgram<T>),
|
||||
/// The resolved .asm file.
|
||||
Resolved(ASMProgram<T>),
|
||||
/// The analyzed .asm file.
|
||||
AnalyzedAsm(AnalysisASMFile<T>),
|
||||
/// The pil constraints.
|
||||
PilConstraints(AnalysisASMFile<T>),
|
||||
/// The airgen graph.
|
||||
Graph(PILGraph<T>),
|
||||
/// The linked pil.
|
||||
Linked(PILFile<T>),
|
||||
/// The path to the .pil file.
|
||||
PilFile(PathBuf),
|
||||
/// The contents of the .pil file.
|
||||
PilString(String),
|
||||
/// The analyzed .pil file.
|
||||
AnaylyzedPil(Analyzed<T>),
|
||||
/// The optimized .pil file.
|
||||
OptimzedPil(Analyzed<T>),
|
||||
/// The optimized .pil file with constants.
|
||||
PilWithConstants(PilWithConstants<T>),
|
||||
/// The generated witness.
|
||||
GeneratedWitness(GeneratedWitness<T>),
|
||||
/// The proof (if successful)
|
||||
Proof(ProofResult<T>),
|
||||
}
|
||||
|
||||
pub struct Pipeline<T: FieldElement> {
|
||||
/// The current artifact. It is never None in practice, making it an Option is
|
||||
/// only necessary so that we can take ownership of it in advance().
|
||||
artifact: Option<Artifact<T>>,
|
||||
/// The diff monitor is used to track changes between pipeline stages.
|
||||
diff_monitor: DiffMonitor,
|
||||
/// Output directory for intermediate files. If None, no files are written.
|
||||
output_dir: Option<PathBuf>,
|
||||
/// The name of the pipeline. Used to name output files.
|
||||
name: Option<String>,
|
||||
/// Whether to overwrite existing files. If false, an error is returned if a file
|
||||
/// already exists.
|
||||
force_overwrite: bool,
|
||||
/// The log level to use for this pipeline.
|
||||
log_level: Level,
|
||||
// The output directory if `Pipeline::with_tmp_output` was called.
|
||||
// Note that there is some redundancy with `output_dir`, but the Temp
|
||||
// object has to live for the lifetime of the pipeline, so we keep it here.
|
||||
tmp_dir: Option<Temp>,
|
||||
}
|
||||
|
||||
impl<T> Default for Pipeline<T>
|
||||
where
|
||||
T: FieldElement,
|
||||
{
|
||||
fn default() -> Self {
|
||||
Pipeline {
|
||||
artifact: None,
|
||||
diff_monitor: DiffMonitor::default(),
|
||||
output_dir: None,
|
||||
log_level: Level::Debug,
|
||||
name: None,
|
||||
force_overwrite: false,
|
||||
tmp_dir: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A Powdr pipeline, going from a PIL or ASM file to a proof.
|
||||
///
|
||||
/// The pipeline steps are:
|
||||
/// ```mermaid
|
||||
/// graph TD
|
||||
/// AsmFile --> AsmString
|
||||
/// AsmString --> Parsed
|
||||
/// Parsed --> Resolved
|
||||
/// Resolved --> AnalyzedAsm
|
||||
/// AnalyzedAsm --> PilConstraints
|
||||
/// PilConstraints --> Graph
|
||||
/// Graph --> Linked
|
||||
/// Linked --> AnaylyzedPil
|
||||
/// PilFile --> AnaylyzedPil
|
||||
/// PilString --> AnaylyzedPil
|
||||
/// AnaylyzedPil --> OptimzedPil
|
||||
/// OptimzedPil --> PilWithConstants
|
||||
/// PilWithConstants --> GeneratedWitness
|
||||
/// GeneratedWitness --> Proof
|
||||
/// ```
|
||||
///
|
||||
/// # Example
|
||||
/// ```rust
|
||||
/// use compiler::{pipeline::Pipeline, verify, BackendType, test_util::resolve_test_file};
|
||||
/// use std::path::PathBuf;
|
||||
/// use number::GoldilocksField;
|
||||
///
|
||||
/// let mut pipeline = Pipeline::<GoldilocksField>::default()
|
||||
/// .from_file(resolve_test_file("pil/fibonacci.pil"));
|
||||
/// pipeline
|
||||
/// .generate_witness(
|
||||
/// compiler::inputs_to_query_callback(vec![]),
|
||||
/// vec![],
|
||||
/// )
|
||||
/// .unwrap();
|
||||
/// pipeline.prove(BackendType::PilStarkCli).unwrap();
|
||||
/// ```
|
||||
impl<T: FieldElement> Pipeline<T> {
|
||||
pub fn with_tmp_output(self) -> Self {
|
||||
let tmp_dir = mktemp::Temp::new_dir().unwrap();
|
||||
Pipeline {
|
||||
output_dir: Some(tmp_dir.to_path_buf()),
|
||||
tmp_dir: Some(tmp_dir),
|
||||
..self
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_output(self, output_dir: PathBuf, force_overwrite: bool) -> Self {
|
||||
Pipeline {
|
||||
output_dir: Some(output_dir),
|
||||
force_overwrite,
|
||||
..self
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_file(self, asm_file: PathBuf) -> Self {
|
||||
if asm_file.extension().unwrap() == "asm" {
|
||||
self.from_asm_file(asm_file)
|
||||
} else {
|
||||
self.from_pil_file(asm_file)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_asm_file(self, asm_file: PathBuf) -> Self {
|
||||
let name = self.name.or(Some(Self::name_from_path(&asm_file)));
|
||||
Pipeline {
|
||||
artifact: Some(Artifact::AsmFile(asm_file)),
|
||||
name,
|
||||
..self
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_asm_string(self, asm_string: String, path: Option<PathBuf>) -> Self {
|
||||
let name = self.name.or(path.as_ref().map(|p| Self::name_from_path(p)));
|
||||
Pipeline {
|
||||
artifact: Some(Artifact::AsmString(path, asm_string)),
|
||||
name,
|
||||
..self
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_pil_file(self, pil_file: PathBuf) -> Self {
|
||||
let name = self.name.or(Some(Self::name_from_path(&pil_file)));
|
||||
Pipeline {
|
||||
artifact: Some(Artifact::PilFile(pil_file)),
|
||||
name,
|
||||
..self
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_pil_string(self, pil_string: String) -> Self {
|
||||
Pipeline {
|
||||
artifact: Some(Artifact::PilString(pil_string)),
|
||||
..self
|
||||
}
|
||||
}
|
||||
|
||||
fn name_from_path(path: &Path) -> String {
|
||||
path.file_stem().unwrap().to_str().unwrap().to_string()
|
||||
}
|
||||
|
||||
fn log(&self, msg: &str) {
|
||||
log::log!(self.log_level, "{}", msg);
|
||||
}
|
||||
|
||||
#[allow(clippy::print_stderr)]
|
||||
fn advance(&mut self) -> Result<(), Vec<String>> {
|
||||
let artifact = std::mem::take(&mut self.artifact).unwrap();
|
||||
self.artifact = Some(match artifact {
|
||||
Artifact::AsmFile(path) => Artifact::AsmString(
|
||||
Some(path.clone()),
|
||||
fs::read_to_string(&path).map_err(|e| {
|
||||
vec![format!("Error reading .asm file: {}\n{e}", path.display())]
|
||||
})?,
|
||||
),
|
||||
Artifact::AsmString(path, asm_string) => {
|
||||
let parsed_asm = parser::parse_asm(None, &asm_string).unwrap_or_else(|err| {
|
||||
match path.as_ref() {
|
||||
Some(path) => eprintln!("Error parsing .asm file: {}", path.display()),
|
||||
None => eprintln!("Error parsing .asm file:"),
|
||||
}
|
||||
err.output_to_stderr();
|
||||
panic!();
|
||||
});
|
||||
self.diff_monitor.push(&parsed_asm);
|
||||
Artifact::Parsed(path, parsed_asm)
|
||||
}
|
||||
Artifact::Parsed(path, parsed) => {
|
||||
self.log("Resolve imports");
|
||||
let resolved = importer::resolve(path, parsed).map_err(|e| vec![e])?;
|
||||
self.diff_monitor.push(&resolved);
|
||||
Artifact::Resolved(resolved)
|
||||
}
|
||||
Artifact::Resolved(resolved) => {
|
||||
self.log("Run analysis");
|
||||
self.log("Analysis done");
|
||||
let analyzed_asm = analysis::analyze(resolved, &mut self.diff_monitor)?;
|
||||
log::trace!("{analyzed_asm}");
|
||||
Artifact::AnalyzedAsm(analyzed_asm)
|
||||
}
|
||||
Artifact::AnalyzedAsm(analyzed_asm) => Artifact::PilConstraints(
|
||||
convert_analyzed_to_pil_constraints(analyzed_asm, &mut self.diff_monitor),
|
||||
),
|
||||
Artifact::PilConstraints(analyzed_asm) => {
|
||||
self.log("Run airgen");
|
||||
let graph = airgen::compile(analyzed_asm);
|
||||
self.diff_monitor.push(&graph);
|
||||
self.log("Airgen done");
|
||||
log::trace!("{graph}");
|
||||
Artifact::Graph(graph)
|
||||
}
|
||||
Artifact::Graph(graph) => {
|
||||
self.log("Run linker");
|
||||
self.log("Linker done");
|
||||
let linked = linker::link(graph)?;
|
||||
self.diff_monitor.push(&linked);
|
||||
log::trace!("{linked}");
|
||||
self.maybe_write_pil(&linked, "")?;
|
||||
Artifact::Linked(linked)
|
||||
}
|
||||
Artifact::Linked(linked) => {
|
||||
Artifact::AnaylyzedPil(pil_analyzer::analyze_string(&format!("{linked}")))
|
||||
}
|
||||
Artifact::PilFile(pil_file) => Artifact::AnaylyzedPil(pil_analyzer::analyze(&pil_file)),
|
||||
Artifact::PilString(pil_string) => {
|
||||
Artifact::AnaylyzedPil(pil_analyzer::analyze_string(&pil_string))
|
||||
}
|
||||
Artifact::AnaylyzedPil(analyzed_pil) => {
|
||||
self.log("Optimizing pil...");
|
||||
let optimized = pilopt::optimize(analyzed_pil);
|
||||
self.maybe_write_pil(&optimized, "_opt")?;
|
||||
Artifact::OptimzedPil(optimized)
|
||||
}
|
||||
Artifact::OptimzedPil(pil) => {
|
||||
self.log("Evaluating fixed columns...");
|
||||
let start = Instant::now();
|
||||
let constants = constant_evaluator::generate(&pil);
|
||||
let constants = constants
|
||||
.into_iter()
|
||||
.map(|(k, v)| (k.to_string(), v))
|
||||
.collect();
|
||||
self.log(&format!("Took {}", start.elapsed().as_secs_f32()));
|
||||
Artifact::PilWithConstants(PilWithConstants { pil, constants })
|
||||
}
|
||||
Artifact::PilWithConstants(_) => {
|
||||
return Err(vec![
|
||||
"Witness generation requires arguments, call generate_witness() instead!"
|
||||
.into(),
|
||||
])
|
||||
}
|
||||
Artifact::GeneratedWitness(_) => {
|
||||
return Err(vec![
|
||||
"Proof generation requires arguments, call prove() instead!".into(),
|
||||
])
|
||||
}
|
||||
Artifact::Proof(_) => panic!("Last pipeline step!"),
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn maybe_write_pil<C: Display>(&self, content: &C, suffix: &str) -> Result<(), Vec<String>> {
|
||||
if let Some(output_dir) = &self.output_dir {
|
||||
let name = self
|
||||
.name
|
||||
.as_ref()
|
||||
.expect("name must be set if output_dir is set");
|
||||
let output_file = output_dir.join(format!("{name}{suffix}.pil"));
|
||||
if !output_file.exists() || self.force_overwrite {
|
||||
fs::write(&output_file, format!("{content}")).map_err(|e| {
|
||||
vec![format!(
|
||||
"Error writing {}: {e}",
|
||||
output_file.to_str().unwrap()
|
||||
)]
|
||||
})?;
|
||||
self.log(&format!("Wrote {}.", output_file.to_str().unwrap()));
|
||||
} else {
|
||||
return Err(vec![format!(
|
||||
"{} already exists! Use --force to overwrite.",
|
||||
output_file.to_str().unwrap()
|
||||
)]);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn stage(&self) -> Stage {
|
||||
match self.artifact.as_ref().unwrap() {
|
||||
Artifact::AsmFile(_) => Stage::AsmFile,
|
||||
Artifact::AsmString(_, _) => Stage::AsmString,
|
||||
Artifact::Parsed(_, _) => Stage::Parsed,
|
||||
Artifact::Resolved(_) => Stage::Resolved,
|
||||
Artifact::AnalyzedAsm(_) => Stage::AnalyzedAsm,
|
||||
Artifact::PilConstraints(_) => Stage::PilConstraints,
|
||||
Artifact::Graph(_) => Stage::Graph,
|
||||
Artifact::Linked(_) => Stage::Linked,
|
||||
Artifact::PilFile(_) => Stage::PilFile,
|
||||
Artifact::PilString(_) => Stage::PilString,
|
||||
Artifact::AnaylyzedPil(_) => Stage::AnalyzedPil,
|
||||
Artifact::OptimzedPil(_) => Stage::OptimizedPil,
|
||||
Artifact::PilWithConstants(_) => Stage::PilWithConstants,
|
||||
Artifact::GeneratedWitness(_) => Stage::GeneratedWitness,
|
||||
Artifact::Proof(_) => Stage::Proof,
|
||||
}
|
||||
}
|
||||
|
||||
fn advance_to(&mut self, target_stage: Stage) -> Result<(), Vec<String>> {
|
||||
while self.stage() != target_stage {
|
||||
self.advance()?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn generate_witness<Q: QueryCallback<T>>(
|
||||
&mut self,
|
||||
query_callback: Q,
|
||||
external_witness_values: Vec<(&str, Vec<T>)>,
|
||||
) -> Result<(), Vec<String>> {
|
||||
self.advance_to(Stage::PilWithConstants)?;
|
||||
let Artifact::PilWithConstants(PilWithConstants { pil, constants }) =
|
||||
std::mem::take(&mut self.artifact).unwrap()
|
||||
else {
|
||||
panic!()
|
||||
};
|
||||
let witness = (pil.constant_count() == constants.len()).then(|| {
|
||||
self.log("Deducing witness columns...");
|
||||
let start = Instant::now();
|
||||
let witness = executor::witgen::WitnessGenerator::new(&pil, &constants, query_callback)
|
||||
.with_external_witness_values(external_witness_values)
|
||||
.generate();
|
||||
|
||||
self.log(&format!("Took {}", start.elapsed().as_secs_f32()));
|
||||
witness
|
||||
.into_iter()
|
||||
.map(|(name, c)| (name.to_string(), c))
|
||||
.collect::<Vec<_>>()
|
||||
});
|
||||
self.artifact = Some(Artifact::GeneratedWitness(GeneratedWitness {
|
||||
pil,
|
||||
constants,
|
||||
witness,
|
||||
}));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn prove(&mut self, backend: BackendType) -> Result<(), Vec<String>> {
|
||||
self.advance_to(Stage::GeneratedWitness)?;
|
||||
let Artifact::GeneratedWitness(GeneratedWitness {
|
||||
pil: analyzed,
|
||||
constants,
|
||||
witness,
|
||||
}) = std::mem::take(&mut self.artifact).unwrap()
|
||||
else {
|
||||
panic!()
|
||||
};
|
||||
let factory = backend.factory::<T>();
|
||||
let backend = factory.create(analyzed.degree());
|
||||
|
||||
// Even if we don't have all constants and witnesses, some backends will
|
||||
// still output the constraint serialization.
|
||||
let (proof, constraints_serialization) = backend.prove(
|
||||
&analyzed,
|
||||
&constants,
|
||||
witness.as_deref().unwrap_or_default(),
|
||||
None,
|
||||
);
|
||||
|
||||
if let Some(output_dir) = &self.output_dir {
|
||||
write_constants_to_fs(&constants, output_dir);
|
||||
if let Some(witness) = &witness {
|
||||
write_commits_to_fs(witness, output_dir);
|
||||
}
|
||||
if let Some(constraints_serialization) = &constraints_serialization {
|
||||
write_constraints_to_fs(constraints_serialization, output_dir);
|
||||
}
|
||||
};
|
||||
|
||||
self.artifact = Some(Artifact::Proof(ProofResult {
|
||||
constants,
|
||||
witness,
|
||||
proof,
|
||||
constraints_serialization,
|
||||
}));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn analyzed_asm(mut self) -> Result<AnalysisASMFile<T>, Vec<String>> {
|
||||
self.advance_to(Stage::AnalyzedAsm)?;
|
||||
let Artifact::AnalyzedAsm(analyzed_asm) = self.artifact.unwrap() else {
|
||||
panic!()
|
||||
};
|
||||
Ok(analyzed_asm)
|
||||
}
|
||||
|
||||
pub fn analyzed_asm_ref(&mut self) -> Result<&AnalysisASMFile<T>, Vec<String>> {
|
||||
self.advance_to(Stage::AnalyzedAsm)?;
|
||||
match self.artifact.as_ref().unwrap() {
|
||||
Artifact::AnalyzedAsm(analyzed_asm) => Ok(analyzed_asm),
|
||||
_ => panic!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn optimized_pil(mut self) -> Result<Analyzed<T>, Vec<String>> {
|
||||
self.advance_to(Stage::OptimizedPil)?;
|
||||
let Artifact::OptimzedPil(optimized_pil) = self.artifact.unwrap() else {
|
||||
panic!()
|
||||
};
|
||||
Ok(optimized_pil)
|
||||
}
|
||||
|
||||
pub fn optimized_pil_with_constants(mut self) -> Result<PilWithConstants<T>, Vec<String>> {
|
||||
self.advance_to(Stage::PilWithConstants)?;
|
||||
let Artifact::PilWithConstants(pil_with_constants) = self.artifact.unwrap() else {
|
||||
panic!()
|
||||
};
|
||||
Ok(pil_with_constants)
|
||||
}
|
||||
|
||||
pub fn generated_witness(mut self) -> Result<GeneratedWitness<T>, Vec<String>> {
|
||||
self.advance_to(Stage::GeneratedWitness)?;
|
||||
let Artifact::GeneratedWitness(generated_witness) = self.artifact.unwrap() else {
|
||||
panic!()
|
||||
};
|
||||
Ok(generated_witness)
|
||||
}
|
||||
|
||||
pub fn proof(mut self) -> Result<ProofResult<T>, Vec<String>> {
|
||||
self.advance_to(Stage::Proof)?;
|
||||
let Artifact::Proof(proof) = self.artifact.unwrap() else {
|
||||
panic!()
|
||||
};
|
||||
Ok(proof)
|
||||
}
|
||||
|
||||
pub fn tmp_dir(&self) -> &Path {
|
||||
self.tmp_dir.as_ref().unwrap()
|
||||
}
|
||||
}
|
||||
75
compiler/src/test_util.rs
Normal file
75
compiler/src/test_util.rs
Normal file
@@ -0,0 +1,75 @@
|
||||
use backend::BackendType;
|
||||
use number::FieldElement;
|
||||
use number::{Bn254Field, GoldilocksField};
|
||||
use std::path::PathBuf;
|
||||
|
||||
use crate::inputs_to_query_callback;
|
||||
use crate::pipeline::Pipeline;
|
||||
use crate::verify::verify;
|
||||
|
||||
pub fn resolve_test_file(file_name: &str) -> PathBuf {
|
||||
PathBuf::from(format!(
|
||||
"{}/../test_data/{file_name}",
|
||||
env!("CARGO_MANIFEST_DIR")
|
||||
))
|
||||
}
|
||||
|
||||
pub fn verify_test_file<T: FieldElement>(
|
||||
file_name: &str,
|
||||
inputs: Vec<T>,
|
||||
external_witness_values: Vec<(&str, Vec<T>)>,
|
||||
) {
|
||||
let pipeline = Pipeline::default().from_file(resolve_test_file(file_name));
|
||||
verify_pipeline(pipeline, inputs, external_witness_values)
|
||||
}
|
||||
|
||||
pub fn verify_asm_string<T: FieldElement>(
|
||||
file_name: &str,
|
||||
contents: &str,
|
||||
inputs: Vec<T>,
|
||||
external_witness_values: Vec<(&str, Vec<T>)>,
|
||||
) {
|
||||
let pipeline =
|
||||
Pipeline::default().from_asm_string(contents.to_string(), Some(PathBuf::from(file_name)));
|
||||
verify_pipeline(pipeline, inputs, external_witness_values)
|
||||
}
|
||||
|
||||
pub fn verify_pipeline<T: FieldElement>(
|
||||
pipeline: Pipeline<T>,
|
||||
inputs: Vec<T>,
|
||||
external_witness_values: Vec<(&str, Vec<T>)>,
|
||||
) {
|
||||
let mut pipeline = pipeline.with_tmp_output();
|
||||
pipeline
|
||||
.generate_witness(inputs_to_query_callback(inputs), external_witness_values)
|
||||
.unwrap();
|
||||
pipeline.prove(BackendType::PilStarkCli).unwrap();
|
||||
|
||||
verify(pipeline.tmp_dir());
|
||||
}
|
||||
|
||||
pub fn gen_estark_proof(file_name: &str, inputs: Vec<GoldilocksField>) {
|
||||
let file_name = format!("{}/../test_data/{file_name}", env!("CARGO_MANIFEST_DIR"));
|
||||
let mut pipeline = Pipeline::default()
|
||||
.with_tmp_output()
|
||||
.from_file(PathBuf::from(file_name));
|
||||
pipeline
|
||||
.generate_witness(inputs_to_query_callback(inputs), vec![])
|
||||
.unwrap();
|
||||
pipeline.prove(backend::BackendType::EStark).unwrap();
|
||||
}
|
||||
|
||||
#[cfg(feature = "halo2")]
|
||||
pub fn gen_halo2_proof(file_name: &str, inputs: Vec<Bn254Field>) {
|
||||
let file_name = format!("{}/../test_data/{file_name}", env!("CARGO_MANIFEST_DIR"));
|
||||
let mut pipeline = Pipeline::default()
|
||||
.with_tmp_output()
|
||||
.from_file(PathBuf::from(file_name));
|
||||
pipeline
|
||||
.generate_witness(inputs_to_query_callback(inputs), vec![])
|
||||
.unwrap();
|
||||
pipeline.prove(backend::BackendType::Halo2).unwrap();
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "halo2"))]
|
||||
pub fn gen_halo2_proof(_file_name: &str, _inputs: Vec<Bn254Field>) {}
|
||||
@@ -1,4 +1,3 @@
|
||||
use backend::BackendType;
|
||||
use number::write_polys_file;
|
||||
use number::FieldElement;
|
||||
use std::{
|
||||
@@ -8,35 +7,6 @@ use std::{
|
||||
process::Command,
|
||||
};
|
||||
|
||||
use crate::compile_asm_string;
|
||||
|
||||
pub fn verify_asm_string<T: FieldElement>(
|
||||
file_name: &str,
|
||||
contents: &str,
|
||||
inputs: Vec<T>,
|
||||
external_witness_values: Vec<(&str, Vec<T>)>,
|
||||
) {
|
||||
let temp_dir = mktemp::Temp::new_dir().unwrap();
|
||||
let (_, result) = compile_asm_string(
|
||||
file_name,
|
||||
contents,
|
||||
inputs,
|
||||
None,
|
||||
&temp_dir,
|
||||
true,
|
||||
Some(BackendType::PilStarkCli),
|
||||
external_witness_values,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let result = result.unwrap();
|
||||
write_constants_to_fs(&result.constants, &temp_dir);
|
||||
write_commits_to_fs(&result.witness.unwrap(), &temp_dir);
|
||||
write_constraints_to_fs(&result.constraints_serialization.unwrap(), &temp_dir);
|
||||
|
||||
verify(&temp_dir);
|
||||
}
|
||||
|
||||
pub fn write_constants_to_fs<T: FieldElement>(constants: &[(String, Vec<T>)], output_dir: &Path) {
|
||||
let to_write = output_dir.join("constants.bin");
|
||||
write_polys_file(
|
||||
|
||||
@@ -1,70 +1,18 @@
|
||||
use compiler::verify_asm_string;
|
||||
use number::{Bn254Field, FieldElement, GoldilocksField};
|
||||
use std::fs;
|
||||
use compiler::test_util::{gen_estark_proof, gen_halo2_proof, verify_test_file};
|
||||
use number::{FieldElement, GoldilocksField};
|
||||
use test_log::test;
|
||||
|
||||
fn verify_asm<T: FieldElement>(file_name: &str, inputs: Vec<T>) {
|
||||
verify_asm_with_external_witness(file_name, inputs, vec![]);
|
||||
verify_test_file(file_name, inputs, vec![]);
|
||||
}
|
||||
|
||||
fn verify_asm_with_external_witness<T: FieldElement>(
|
||||
file_name: &str,
|
||||
inputs: Vec<T>,
|
||||
external_witness_values: Vec<(&str, Vec<T>)>,
|
||||
) {
|
||||
let file_name = format!(
|
||||
"{}/../test_data/asm/{file_name}",
|
||||
env!("CARGO_MANIFEST_DIR")
|
||||
);
|
||||
|
||||
let contents = fs::read_to_string(&file_name).unwrap();
|
||||
|
||||
verify_asm_string(&file_name, &contents, inputs, external_witness_values);
|
||||
}
|
||||
|
||||
fn gen_estark_proof(file_name: &str, inputs: Vec<GoldilocksField>) {
|
||||
compiler::compile_pil_or_asm(
|
||||
format!(
|
||||
"{}/../test_data/asm/{file_name}",
|
||||
env!("CARGO_MANIFEST_DIR")
|
||||
)
|
||||
.as_str(),
|
||||
inputs,
|
||||
&mktemp::Temp::new_dir().unwrap(),
|
||||
true,
|
||||
Some(backend::BackendType::EStark),
|
||||
vec![],
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[cfg(feature = "halo2")]
|
||||
fn gen_halo2_proof(file_name: &str, inputs: Vec<Bn254Field>) {
|
||||
compiler::compile_pil_or_asm(
|
||||
format!(
|
||||
"{}/../test_data/asm/{file_name}",
|
||||
env!("CARGO_MANIFEST_DIR")
|
||||
)
|
||||
.as_str(),
|
||||
inputs,
|
||||
&mktemp::Temp::new_dir().unwrap(),
|
||||
true,
|
||||
Some(backend::BackendType::Halo2),
|
||||
vec![],
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "halo2"))]
|
||||
fn gen_halo2_proof(_file_name: &str, _inputs: Vec<Bn254Field>) {}
|
||||
|
||||
fn slice_to_vec<T: FieldElement>(arr: &[i32]) -> Vec<T> {
|
||||
arr.iter().cloned().map(|x| x.into()).collect()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn simple_sum_asm() {
|
||||
let f = "simple_sum.asm";
|
||||
let f = "asm/simple_sum.asm";
|
||||
let i = [16, 4, 1, 2, 8, 5];
|
||||
verify_asm::<GoldilocksField>(f, slice_to_vec(&i));
|
||||
gen_halo2_proof(f, slice_to_vec(&i));
|
||||
@@ -73,7 +21,7 @@ fn simple_sum_asm() {
|
||||
|
||||
#[test]
|
||||
fn secondary_block_machine_add2() {
|
||||
let f = "secondary_block_machine_add2.asm";
|
||||
let f = "asm/secondary_block_machine_add2.asm";
|
||||
verify_asm::<GoldilocksField>(f, vec![]);
|
||||
gen_halo2_proof(f, vec![]);
|
||||
gen_estark_proof(f, vec![]);
|
||||
@@ -81,7 +29,7 @@ fn secondary_block_machine_add2() {
|
||||
|
||||
#[test]
|
||||
fn mem_write_once() {
|
||||
let f = "mem_write_once.asm";
|
||||
let f = "asm/mem_write_once.asm";
|
||||
verify_asm::<GoldilocksField>(f, vec![]);
|
||||
gen_halo2_proof(f, vec![]);
|
||||
gen_estark_proof(f, vec![]);
|
||||
@@ -89,17 +37,17 @@ fn mem_write_once() {
|
||||
|
||||
#[test]
|
||||
fn mem_write_once_external_write() {
|
||||
let f = "mem_write_once_external_write.asm";
|
||||
let f = "asm/mem_write_once_external_write.asm";
|
||||
let mut mem = vec![GoldilocksField::from(0); 256];
|
||||
mem[17] = GoldilocksField::from(42);
|
||||
mem[62] = GoldilocksField::from(123);
|
||||
mem[255] = GoldilocksField::from(-1);
|
||||
verify_asm_with_external_witness::<GoldilocksField>(f, vec![], vec![("main.v", mem)]);
|
||||
verify_test_file::<GoldilocksField>(f, vec![], vec![("main.v", mem)]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn block_machine_cache_miss() {
|
||||
let f = "block_machine_cache_miss.asm";
|
||||
let f = "asm/block_machine_cache_miss.asm";
|
||||
verify_asm::<GoldilocksField>(f, vec![]);
|
||||
gen_halo2_proof(f, vec![]);
|
||||
gen_estark_proof(f, vec![]);
|
||||
@@ -107,7 +55,7 @@ fn block_machine_cache_miss() {
|
||||
|
||||
#[test]
|
||||
fn palindrome() {
|
||||
let f = "palindrome.asm";
|
||||
let f = "asm/palindrome.asm";
|
||||
let i = [7, 1, 7, 3, 9, 3, 7, 1];
|
||||
verify_asm::<GoldilocksField>(f, slice_to_vec(&i));
|
||||
gen_halo2_proof(f, slice_to_vec(&i));
|
||||
@@ -119,7 +67,7 @@ fn palindrome() {
|
||||
|
||||
#[test]
|
||||
fn single_function_vm() {
|
||||
let f = "single_function_vm.asm";
|
||||
let f = "asm/single_function_vm.asm";
|
||||
let i = [];
|
||||
verify_asm::<GoldilocksField>(f, slice_to_vec(&i));
|
||||
gen_halo2_proof(f, slice_to_vec(&i));
|
||||
@@ -128,7 +76,7 @@ fn single_function_vm() {
|
||||
|
||||
#[test]
|
||||
fn empty() {
|
||||
let f = "empty.asm";
|
||||
let f = "asm/empty.asm";
|
||||
let i = [];
|
||||
verify_asm::<GoldilocksField>(f, slice_to_vec(&i));
|
||||
gen_halo2_proof(f, slice_to_vec(&i));
|
||||
@@ -137,7 +85,7 @@ fn empty() {
|
||||
|
||||
#[test]
|
||||
fn single_operation() {
|
||||
let f = "single_operation.asm";
|
||||
let f = "asm/single_operation.asm";
|
||||
let i = [];
|
||||
verify_asm::<GoldilocksField>(f, slice_to_vec(&i));
|
||||
gen_halo2_proof(f, slice_to_vec(&i));
|
||||
@@ -146,7 +94,7 @@ fn single_operation() {
|
||||
|
||||
#[test]
|
||||
fn empty_vm() {
|
||||
let f = "empty_vm.asm";
|
||||
let f = "asm/empty_vm.asm";
|
||||
let i = [];
|
||||
verify_asm::<GoldilocksField>(f, slice_to_vec(&i));
|
||||
gen_halo2_proof(f, slice_to_vec(&i));
|
||||
@@ -155,7 +103,7 @@ fn empty_vm() {
|
||||
|
||||
#[test]
|
||||
fn vm_to_block_unique_interface() {
|
||||
let f = "vm_to_block_unique_interface.asm";
|
||||
let f = "asm/vm_to_block_unique_interface.asm";
|
||||
let i = [];
|
||||
verify_asm::<GoldilocksField>(f, slice_to_vec(&i));
|
||||
gen_halo2_proof(f, slice_to_vec(&i));
|
||||
@@ -167,7 +115,7 @@ fn vm_to_block_unique_interface() {
|
||||
|
||||
#[test]
|
||||
fn vm_to_block_to_block() {
|
||||
let f = "vm_to_block_to_block.asm";
|
||||
let f = "asm/vm_to_block_to_block.asm";
|
||||
let i = [];
|
||||
verify_asm::<GoldilocksField>(f, slice_to_vec(&i));
|
||||
gen_halo2_proof(f, slice_to_vec(&i));
|
||||
@@ -175,7 +123,7 @@ fn vm_to_block_to_block() {
|
||||
|
||||
#[test]
|
||||
fn block_to_block() {
|
||||
let f = "block_to_block.asm";
|
||||
let f = "asm/block_to_block.asm";
|
||||
let i = [];
|
||||
verify_asm::<GoldilocksField>(f, slice_to_vec(&i));
|
||||
gen_halo2_proof(f, slice_to_vec(&i));
|
||||
@@ -184,7 +132,7 @@ fn block_to_block() {
|
||||
|
||||
#[test]
|
||||
fn vm_to_block_multiple_interfaces() {
|
||||
let f = "vm_to_block_multiple_interfaces.asm";
|
||||
let f = "asm/vm_to_block_multiple_interfaces.asm";
|
||||
let i = [];
|
||||
verify_asm::<GoldilocksField>(f, slice_to_vec(&i));
|
||||
gen_halo2_proof(f, slice_to_vec(&i));
|
||||
@@ -193,7 +141,7 @@ fn vm_to_block_multiple_interfaces() {
|
||||
|
||||
#[test]
|
||||
fn vm_to_vm() {
|
||||
let f = "vm_to_vm.asm";
|
||||
let f = "asm/vm_to_vm.asm";
|
||||
let i = [];
|
||||
verify_asm::<GoldilocksField>(f, slice_to_vec(&i));
|
||||
gen_halo2_proof(f, slice_to_vec(&i));
|
||||
@@ -202,7 +150,7 @@ fn vm_to_vm() {
|
||||
|
||||
#[test]
|
||||
fn vm_to_vm_dynamic_trace_length() {
|
||||
let f = "vm_to_vm_dynamic_trace_length.asm";
|
||||
let f = "asm/vm_to_vm_dynamic_trace_length.asm";
|
||||
let i = [];
|
||||
verify_asm::<GoldilocksField>(f, slice_to_vec(&i));
|
||||
gen_halo2_proof(f, slice_to_vec(&i));
|
||||
@@ -211,7 +159,7 @@ fn vm_to_vm_dynamic_trace_length() {
|
||||
|
||||
#[test]
|
||||
fn vm_to_vm_to_block() {
|
||||
let f = "vm_to_vm_to_block.asm";
|
||||
let f = "asm/vm_to_vm_to_block.asm";
|
||||
let i = [];
|
||||
verify_asm::<GoldilocksField>(f, slice_to_vec(&i));
|
||||
gen_halo2_proof(f, slice_to_vec(&i));
|
||||
@@ -220,7 +168,7 @@ fn vm_to_vm_to_block() {
|
||||
|
||||
#[test]
|
||||
fn vm_to_block_array() {
|
||||
let f = "vm_to_block_array.asm";
|
||||
let f = "asm/vm_to_block_array.asm";
|
||||
let i = [];
|
||||
verify_asm::<GoldilocksField>(f, slice_to_vec(&i));
|
||||
gen_halo2_proof(f, slice_to_vec(&i));
|
||||
@@ -229,7 +177,7 @@ fn vm_to_block_array() {
|
||||
|
||||
#[test]
|
||||
fn vm_to_vm_to_vm() {
|
||||
let f = "vm_to_vm_to_vm.asm";
|
||||
let f = "asm/vm_to_vm_to_vm.asm";
|
||||
let i = [];
|
||||
verify_asm::<GoldilocksField>(f, slice_to_vec(&i));
|
||||
gen_halo2_proof(f, slice_to_vec(&i));
|
||||
@@ -238,7 +186,7 @@ fn vm_to_vm_to_vm() {
|
||||
|
||||
#[test]
|
||||
fn test_mem_read_write() {
|
||||
let f = "mem_read_write.asm";
|
||||
let f = "asm/mem_read_write.asm";
|
||||
verify_asm::<GoldilocksField>(f, Default::default());
|
||||
gen_halo2_proof(f, Default::default());
|
||||
gen_estark_proof(f, Default::default());
|
||||
@@ -246,7 +194,7 @@ fn test_mem_read_write() {
|
||||
|
||||
#[test]
|
||||
fn test_multi_assign() {
|
||||
let f = "multi_assign.asm";
|
||||
let f = "asm/multi_assign.asm";
|
||||
let i = [7];
|
||||
verify_asm::<GoldilocksField>(f, slice_to_vec(&i));
|
||||
gen_halo2_proof(f, slice_to_vec(&i));
|
||||
@@ -255,7 +203,7 @@ fn test_multi_assign() {
|
||||
|
||||
#[test]
|
||||
fn test_multi_return() {
|
||||
let f = "multi_return.asm";
|
||||
let f = "asm/multi_return.asm";
|
||||
let i = [];
|
||||
verify_asm::<GoldilocksField>(f, slice_to_vec(&i));
|
||||
gen_halo2_proof(f, slice_to_vec(&i));
|
||||
@@ -265,7 +213,7 @@ fn test_multi_return() {
|
||||
#[test]
|
||||
#[should_panic = "called `Result::unwrap()` on an `Err` value: [\"Assignment register `Z` is incompatible with `square_and_double(3)`. Try using `<==` with no explicit assignment registers.\", \"Assignment register `Y` is incompatible with `square_and_double(3)`. Try using `<==` with no explicit assignment registers.\"]"]
|
||||
fn test_multi_return_wrong_assignment_registers() {
|
||||
let f = "multi_return_wrong_assignment_registers.asm";
|
||||
let f = "asm/multi_return_wrong_assignment_registers.asm";
|
||||
let i = [];
|
||||
verify_asm::<GoldilocksField>(f, slice_to_vec(&i));
|
||||
}
|
||||
@@ -273,14 +221,14 @@ fn test_multi_return_wrong_assignment_registers() {
|
||||
#[test]
|
||||
#[should_panic = "Result::unwrap()` on an `Err` value: [\"Mismatched number of registers for assignment A, B <=Y= square_and_double(3);\"]"]
|
||||
fn test_multi_return_wrong_assignment_register_length() {
|
||||
let f = "multi_return_wrong_assignment_register_length.asm";
|
||||
let f = "asm/multi_return_wrong_assignment_register_length.asm";
|
||||
let i = [];
|
||||
verify_asm::<GoldilocksField>(f, slice_to_vec(&i));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_bit_access() {
|
||||
let f = "bit_access.asm";
|
||||
let f = "asm/bit_access.asm";
|
||||
let i = [20];
|
||||
verify_asm::<GoldilocksField>(f, slice_to_vec(&i));
|
||||
gen_halo2_proof(f, slice_to_vec(&i));
|
||||
@@ -292,7 +240,7 @@ fn test_bit_access() {
|
||||
|
||||
#[test]
|
||||
fn test_sqrt() {
|
||||
let f = "sqrt.asm";
|
||||
let f = "asm/sqrt.asm";
|
||||
verify_asm::<GoldilocksField>(f, Default::default());
|
||||
gen_halo2_proof(f, Default::default());
|
||||
gen_estark_proof(f, Default::default());
|
||||
@@ -300,7 +248,7 @@ fn test_sqrt() {
|
||||
|
||||
#[test]
|
||||
fn functional_instructions() {
|
||||
let f = "functional_instructions.asm";
|
||||
let f = "asm/functional_instructions.asm";
|
||||
let i = [20];
|
||||
verify_asm::<GoldilocksField>(f, slice_to_vec(&i));
|
||||
gen_halo2_proof(f, slice_to_vec(&i));
|
||||
@@ -312,7 +260,7 @@ fn functional_instructions() {
|
||||
|
||||
#[test]
|
||||
fn full_pil_constant() {
|
||||
let f = "full_pil_constant.asm";
|
||||
let f = "asm/full_pil_constant.asm";
|
||||
verify_asm::<GoldilocksField>(f, Default::default());
|
||||
gen_halo2_proof(f, Default::default());
|
||||
gen_estark_proof(f, Default::default());
|
||||
@@ -320,7 +268,7 @@ fn full_pil_constant() {
|
||||
|
||||
#[test]
|
||||
fn intermediate() {
|
||||
let f = "intermediate.asm";
|
||||
let f = "asm/intermediate.asm";
|
||||
verify_asm::<GoldilocksField>(f, Default::default());
|
||||
gen_halo2_proof(f, Default::default());
|
||||
gen_estark_proof(f, Default::default());
|
||||
@@ -328,7 +276,7 @@ fn intermediate() {
|
||||
|
||||
#[test]
|
||||
fn intermediate_nested() {
|
||||
let f = "intermediate_nested.asm";
|
||||
let f = "asm/intermediate_nested.asm";
|
||||
verify_asm::<GoldilocksField>(f, Default::default());
|
||||
gen_halo2_proof(f, Default::default());
|
||||
gen_estark_proof(f, Default::default());
|
||||
@@ -343,7 +291,7 @@ mod book {
|
||||
// passing 0 to all tests currently works as they either take no prover input or 0 works
|
||||
let i = [0];
|
||||
|
||||
verify_asm::<GoldilocksField>(file, slice_to_vec(&i));
|
||||
verify_asm::<GoldilocksField>(&format!("{file}"), slice_to_vec(&i));
|
||||
gen_halo2_proof(file, slice_to_vec(&i));
|
||||
gen_estark_proof(file, slice_to_vec(&i));
|
||||
}
|
||||
@@ -354,7 +302,7 @@ mod book {
|
||||
#[test]
|
||||
#[should_panic = "Witness generation failed."]
|
||||
fn hello_world_asm_fail() {
|
||||
let f = "book/hello_world.asm";
|
||||
let f = "asm/book/hello_world.asm";
|
||||
let i = [1];
|
||||
verify_asm::<GoldilocksField>(f, slice_to_vec(&i));
|
||||
}
|
||||
|
||||
@@ -1,100 +1,32 @@
|
||||
use backend::BackendType;
|
||||
use number::{Bn254Field, GoldilocksField};
|
||||
use compiler::test_util::{gen_estark_proof, gen_halo2_proof, verify_test_file};
|
||||
use number::GoldilocksField;
|
||||
use std::path::Path;
|
||||
use test_log::test;
|
||||
|
||||
type QueryCallbackFn = fn(&str) -> Result<Option<GoldilocksField>, String>;
|
||||
|
||||
pub fn verify_pil(file_name: &str, query_callback: Option<QueryCallbackFn>) {
|
||||
verify_pil_with_external_witness(file_name, query_callback, vec![]);
|
||||
pub fn verify_pil(file_name: &str, inputs: Vec<GoldilocksField>) {
|
||||
verify_test_file(file_name, inputs, vec![]);
|
||||
}
|
||||
|
||||
pub fn verify_pil_with_external_witness(
|
||||
file_name: &str,
|
||||
query_callback: Option<QueryCallbackFn>,
|
||||
external_witness_values: Vec<(&str, Vec<GoldilocksField>)>,
|
||||
) {
|
||||
let input_file = Path::new(&format!(
|
||||
"{}/../test_data/pil/{file_name}",
|
||||
env!("CARGO_MANIFEST_DIR")
|
||||
))
|
||||
.canonicalize()
|
||||
.unwrap();
|
||||
|
||||
let query_callback = query_callback.unwrap_or(|_| -> _ { unreachable!() });
|
||||
|
||||
let temp_dir = mktemp::Temp::new_dir().unwrap();
|
||||
let result = compiler::compile_pil(
|
||||
&input_file,
|
||||
&temp_dir,
|
||||
query_callback,
|
||||
Some(BackendType::PilStarkCli),
|
||||
external_witness_values,
|
||||
);
|
||||
|
||||
compiler::write_constants_to_fs(&result.constants, &temp_dir);
|
||||
compiler::write_commits_to_fs(&result.witness.unwrap(), &temp_dir);
|
||||
compiler::write_constraints_to_fs(&result.constraints_serialization.unwrap(), &temp_dir);
|
||||
|
||||
compiler::verify(&temp_dir);
|
||||
}
|
||||
|
||||
fn gen_estark_proof(file_name: &str, inputs: Vec<GoldilocksField>) {
|
||||
compiler::compile_pil_or_asm(
|
||||
format!(
|
||||
"{}/../test_data/pil/{file_name}",
|
||||
env!("CARGO_MANIFEST_DIR")
|
||||
)
|
||||
.as_str(),
|
||||
inputs,
|
||||
&mktemp::Temp::new_dir().unwrap(),
|
||||
true,
|
||||
Some(BackendType::EStark),
|
||||
vec![],
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[cfg(feature = "halo2")]
|
||||
fn gen_halo2_proof(file_name: &str, inputs: Vec<Bn254Field>) {
|
||||
compiler::compile_pil_or_asm(
|
||||
format!(
|
||||
"{}/../test_data/pil/{file_name}",
|
||||
env!("CARGO_MANIFEST_DIR")
|
||||
)
|
||||
.as_str(),
|
||||
inputs,
|
||||
&mktemp::Temp::new_dir().unwrap(),
|
||||
true,
|
||||
Some(BackendType::Halo2),
|
||||
vec![],
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "halo2"))]
|
||||
fn gen_halo2_proof(_file_name: &str, _inputs: Vec<Bn254Field>) {}
|
||||
|
||||
#[test]
|
||||
fn test_fibonacci() {
|
||||
let f = "fibonacci.pil";
|
||||
verify_pil(f, None);
|
||||
let f = "pil/fibonacci.pil";
|
||||
verify_pil(f, vec![]);
|
||||
gen_halo2_proof(f, Default::default());
|
||||
gen_estark_proof(f, Default::default());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_constant_in_identity() {
|
||||
let f = "constant_in_identity.pil";
|
||||
verify_pil(f, None);
|
||||
let f = "pil/constant_in_identity.pil";
|
||||
verify_pil(f, vec![]);
|
||||
gen_halo2_proof(f, Default::default());
|
||||
gen_estark_proof(f, Default::default());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fib_arrays() {
|
||||
let f = "fib_arrays.pil";
|
||||
verify_pil(f, None);
|
||||
let f = "pil/fib_arrays.pil";
|
||||
verify_pil(f, vec![]);
|
||||
gen_halo2_proof(f, Default::default());
|
||||
gen_estark_proof(f, Default::default());
|
||||
}
|
||||
@@ -102,49 +34,49 @@ fn fib_arrays() {
|
||||
#[test]
|
||||
#[should_panic = "Witness generation failed."]
|
||||
fn test_external_witgen_fails_if_none_provided() {
|
||||
let f = "external_witgen.pil";
|
||||
verify_pil(f, None);
|
||||
let f = "pil/external_witgen.pil";
|
||||
verify_pil(f, vec![]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_external_witgen_a_provided() {
|
||||
let f = "external_witgen.pil";
|
||||
let f = "pil/external_witgen.pil";
|
||||
let external_witness = vec![("main.a", vec![GoldilocksField::from(3); 16])];
|
||||
verify_pil_with_external_witness(f, None, external_witness);
|
||||
verify_test_file(f, vec![], external_witness);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_external_witgen_b_provided() {
|
||||
let f = "external_witgen.pil";
|
||||
let f = "pil/external_witgen.pil";
|
||||
let external_witness = vec![("main.b", vec![GoldilocksField::from(4); 16])];
|
||||
verify_pil_with_external_witness(f, None, external_witness);
|
||||
verify_test_file(f, vec![], external_witness);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_external_witgen_both_provided() {
|
||||
let f = "external_witgen.pil";
|
||||
let f = "pil/external_witgen.pil";
|
||||
let external_witness = vec![
|
||||
("main.a", vec![GoldilocksField::from(3); 16]),
|
||||
("main.b", vec![GoldilocksField::from(4); 16]),
|
||||
];
|
||||
verify_pil_with_external_witness(f, None, external_witness);
|
||||
verify_test_file(f, vec![], external_witness);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic = "called `Result::unwrap()` on an `Err` value: Generic(\"main.b = (main.a + 1);:\\n Linear constraint is not satisfiable: -1 != 0\")"]
|
||||
fn test_external_witgen_fails_on_conflicting_external_witness() {
|
||||
let f = "external_witgen.pil";
|
||||
let f = "pil/external_witgen.pil";
|
||||
let external_witness = vec![
|
||||
("main.a", vec![GoldilocksField::from(3); 16]),
|
||||
// Does not satisfy b = a + 1
|
||||
("main.b", vec![GoldilocksField::from(3); 16]),
|
||||
];
|
||||
verify_pil_with_external_witness(f, None, external_witness);
|
||||
verify_test_file(f, vec![], external_witness);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_global() {
|
||||
verify_pil("global.pil", None);
|
||||
verify_pil("pil/global.pil", vec![]);
|
||||
// Halo2 would take too long for this.
|
||||
// Starky requires at least one witness column, this test has none.
|
||||
}
|
||||
@@ -152,16 +84,9 @@ fn test_global() {
|
||||
#[test]
|
||||
fn test_sum_via_witness_query() {
|
||||
verify_pil(
|
||||
"sum_via_witness_query.pil",
|
||||
Some(|q| {
|
||||
Ok(match q {
|
||||
"\"in\", 0" => Some(7.into()),
|
||||
"\"in\", 1" => Some(8.into()),
|
||||
"\"in\", 2" => Some(2.into()),
|
||||
"\"in\", 3" => None, // This line checks that if we return "None", the system still tries to figure it out on its own.
|
||||
_ => None,
|
||||
})
|
||||
}),
|
||||
"pil/sum_via_witness_query.pil",
|
||||
// Only 3 inputs -> Checks that if we return "None", the system still tries to figure it out on its own.
|
||||
vec![7.into(), 8.into(), 2.into()],
|
||||
);
|
||||
// prover query string uses a different convention,
|
||||
// so we cannot directly use the halo2_proof and estark functions here.
|
||||
@@ -169,101 +94,91 @@ fn test_sum_via_witness_query() {
|
||||
|
||||
#[test]
|
||||
fn test_witness_lookup() {
|
||||
let f = "witness_lookup.pil";
|
||||
verify_pil(
|
||||
f,
|
||||
Some(|q| {
|
||||
Ok(match q {
|
||||
"\"input\", 0" => Some(3.into()),
|
||||
"\"input\", 1" => Some(5.into()),
|
||||
"\"input\", 2" => Some(2.into()),
|
||||
_ => Some(7.into()),
|
||||
})
|
||||
}),
|
||||
);
|
||||
let f = "pil/witness_lookup.pil";
|
||||
let inputs = [3, 5, 2, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7]
|
||||
.into_iter()
|
||||
.map(GoldilocksField::from)
|
||||
.collect::<Vec<_>>();
|
||||
verify_pil(f, inputs.clone());
|
||||
// halo2 fails with "gates must contain at least one constraint"
|
||||
let inputs = vec![3, 5, 2, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7];
|
||||
gen_estark_proof(f, inputs.into_iter().map(GoldilocksField::from).collect());
|
||||
gen_estark_proof(f, inputs);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "Witness generation failed.")]
|
||||
fn test_underdetermined_zero_no_solution() {
|
||||
verify_pil("underdetermined_zero_no_solution.pil", None);
|
||||
verify_pil("pil/underdetermined_zero_no_solution.pil", vec![]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pair_lookup() {
|
||||
let f = "pair_lookup.pil";
|
||||
verify_pil(f, None);
|
||||
let f = "pil/pair_lookup.pil";
|
||||
verify_pil(f, vec![]);
|
||||
// halo2 would take too long for this
|
||||
// starky would take too long for this in debug mode
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_block_lookup_or() {
|
||||
let f = "block_lookup_or.pil";
|
||||
verify_pil(f, None);
|
||||
let f = "pil/block_lookup_or.pil";
|
||||
verify_pil(f, vec![]);
|
||||
// halo2 would take too long for this
|
||||
// starky would take too long for this in debug mode
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_halo_without_lookup() {
|
||||
let f = "halo_without_lookup.pil";
|
||||
verify_pil(f, None);
|
||||
let f = "pil/halo_without_lookup.pil";
|
||||
verify_pil(f, vec![]);
|
||||
gen_halo2_proof(f, Default::default());
|
||||
gen_estark_proof(f, Default::default());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_simple_div() {
|
||||
let f = "simple_div.pil";
|
||||
verify_pil(f, None);
|
||||
let f = "pil/simple_div.pil";
|
||||
verify_pil(f, vec![]);
|
||||
// starky would take too long for this in debug mode
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_single_line_blocks() {
|
||||
let f = "single_line_blocks.pil";
|
||||
verify_pil(f, None);
|
||||
let f = "pil/single_line_blocks.pil";
|
||||
verify_pil(f, vec![]);
|
||||
gen_estark_proof(f, Default::default());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_two_block_machine_functions() {
|
||||
let f = "two_block_machine_functions.pil";
|
||||
verify_pil(f, None);
|
||||
let f = "pil/two_block_machine_functions.pil";
|
||||
verify_pil(f, vec![]);
|
||||
gen_estark_proof(f, Default::default());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fixed_columns() {
|
||||
let f = "fixed_columns.pil";
|
||||
verify_pil(f, None);
|
||||
let f = "pil/fixed_columns.pil";
|
||||
verify_pil(f, vec![]);
|
||||
// Starky requires at least one witness column, this test has none.
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_witness_via_let() {
|
||||
verify_pil("witness_via_let.pil", None);
|
||||
verify_pil("pil/witness_via_let.pil", vec![]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn conditional_fixed_constraints() {
|
||||
verify_pil("conditional_fixed_constraints.pil", None);
|
||||
verify_pil("pil/conditional_fixed_constraints.pil", vec![]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn arith_improved() {
|
||||
let f = "arith_improved.pil";
|
||||
let f = "pil/arith_improved.pil";
|
||||
pil_analyzer::analyze::<GoldilocksField>(
|
||||
&Path::new(&format!(
|
||||
"{}/../test_data/pil/{f}",
|
||||
env!("CARGO_MANIFEST_DIR")
|
||||
))
|
||||
.canonicalize()
|
||||
.unwrap(),
|
||||
&Path::new(&format!("{}/../test_data/{f}", env!("CARGO_MANIFEST_DIR")))
|
||||
.canonicalize()
|
||||
.unwrap(),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -272,7 +187,7 @@ mod book {
|
||||
use test_log::test;
|
||||
|
||||
fn run_book_test(file: &str) {
|
||||
verify_pil(file, None);
|
||||
verify_pil(file, vec![]);
|
||||
gen_halo2_proof(file, Default::default());
|
||||
gen_estark_proof(file, Default::default());
|
||||
}
|
||||
|
||||
@@ -1,77 +1,29 @@
|
||||
use compiler::verify_asm_string;
|
||||
use number::{Bn254Field, FieldElement, GoldilocksField};
|
||||
use std::fs;
|
||||
use compiler::test_util::{gen_estark_proof, gen_halo2_proof, verify_test_file};
|
||||
use number::GoldilocksField;
|
||||
use test_log::test;
|
||||
|
||||
fn verify_asm<T: FieldElement>(file_name: &str, inputs: Vec<T>) {
|
||||
let file_name = format!(
|
||||
"{}/../test_data/std/{file_name}",
|
||||
env!("CARGO_MANIFEST_DIR")
|
||||
);
|
||||
|
||||
let contents = fs::read_to_string(&file_name).unwrap();
|
||||
|
||||
verify_asm_string(&file_name, &contents, inputs, vec![])
|
||||
}
|
||||
|
||||
fn gen_estark_proof(file_name: &str, inputs: Vec<GoldilocksField>) {
|
||||
compiler::compile_pil_or_asm(
|
||||
format!(
|
||||
"{}/../test_data/std/{file_name}",
|
||||
env!("CARGO_MANIFEST_DIR")
|
||||
)
|
||||
.as_str(),
|
||||
inputs,
|
||||
&mktemp::Temp::new_dir().unwrap(),
|
||||
true,
|
||||
Some(backend::BackendType::EStark),
|
||||
vec![],
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[cfg(feature = "halo2")]
|
||||
fn gen_halo2_proof(file_name: &str, inputs: Vec<Bn254Field>) {
|
||||
compiler::compile_pil_or_asm(
|
||||
format!(
|
||||
"{}/../test_data/std/{file_name}",
|
||||
env!("CARGO_MANIFEST_DIR")
|
||||
)
|
||||
.as_str(),
|
||||
inputs,
|
||||
&mktemp::Temp::new_dir().unwrap(),
|
||||
true,
|
||||
Some(backend::BackendType::Halo2Mock),
|
||||
vec![],
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "halo2"))]
|
||||
fn gen_halo2_proof(_file_name: &str, _inputs: Vec<Bn254Field>) {}
|
||||
|
||||
#[test]
|
||||
fn poseidon_bn254_test() {
|
||||
let f = "poseidon_bn254_test.asm";
|
||||
let f = "std/poseidon_bn254_test.asm";
|
||||
gen_halo2_proof(f, Default::default());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn poseidon_gl_test() {
|
||||
let f = "poseidon_gl_test.asm";
|
||||
verify_asm::<GoldilocksField>(f, Default::default());
|
||||
let f = "std/poseidon_gl_test.asm";
|
||||
verify_test_file::<GoldilocksField>(f, vec![], vec![]);
|
||||
gen_estark_proof(f, Default::default());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn split_bn254_test() {
|
||||
let f = "split_bn254_test.asm";
|
||||
let f = "std/split_bn254_test.asm";
|
||||
gen_halo2_proof(f, Default::default());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn split_gl_test() {
|
||||
let f = "split_gl_test.asm";
|
||||
verify_asm::<GoldilocksField>(f, Default::default());
|
||||
let f = "std/split_gl_test.asm";
|
||||
verify_test_file::<GoldilocksField>(f, vec![], vec![]);
|
||||
gen_estark_proof(f, Default::default());
|
||||
}
|
||||
|
||||
@@ -134,7 +134,10 @@ mod tests {
|
||||
f: impl Fn(BlockProcessor<T, Q>, BTreeMap<String, PolyID>, u64, usize) -> R,
|
||||
) -> R {
|
||||
let analyzed = analyze_string(src);
|
||||
let constants = generate(&analyzed);
|
||||
let constants = generate(&analyzed)
|
||||
.into_iter()
|
||||
.map(|(n, c)| (n.to_string(), c))
|
||||
.collect::<Vec<_>>();
|
||||
let fixed_data = FixedData::new(&analyzed, &constants, vec![]);
|
||||
|
||||
// No global range constraints
|
||||
|
||||
@@ -53,7 +53,7 @@ pub struct MutableState<'a, 'b, T: FieldElement, Q: QueryCallback<T>> {
|
||||
|
||||
pub struct WitnessGenerator<'a, 'b, T: FieldElement, Q: QueryCallback<T>> {
|
||||
analyzed: &'a Analyzed<T>,
|
||||
fixed_col_values: &'b [(&'a str, Vec<T>)],
|
||||
fixed_col_values: &'b [(String, Vec<T>)],
|
||||
query_callback: Q,
|
||||
external_witness_values: Vec<(&'a str, Vec<T>)>,
|
||||
}
|
||||
@@ -61,7 +61,7 @@ pub struct WitnessGenerator<'a, 'b, T: FieldElement, Q: QueryCallback<T>> {
|
||||
impl<'a, 'b, T: FieldElement, Q: QueryCallback<T>> WitnessGenerator<'a, 'b, T, Q> {
|
||||
pub fn new(
|
||||
analyzed: &'a Analyzed<T>,
|
||||
fixed_col_values: &'b [(&'a str, Vec<T>)],
|
||||
fixed_col_values: &'b [(String, Vec<T>)],
|
||||
query_callback: Q,
|
||||
) -> Self {
|
||||
WitnessGenerator {
|
||||
@@ -167,7 +167,7 @@ pub struct FixedData<'a, T> {
|
||||
impl<'a, T: FieldElement> FixedData<'a, T> {
|
||||
pub fn new(
|
||||
analyzed: &'a Analyzed<T>,
|
||||
fixed_col_values: &'a [(&str, Vec<T>)],
|
||||
fixed_col_values: &'a [(String, Vec<T>)],
|
||||
external_witness_values: Vec<(&'a str, Vec<T>)>,
|
||||
) -> Self {
|
||||
let mut external_witness_values = BTreeMap::from_iter(external_witness_values);
|
||||
|
||||
@@ -5,16 +5,17 @@ use super::circuit_builder::analyzed_to_circuit;
|
||||
use halo2_proofs::{dev::MockProver, halo2curves::bn256::Fr};
|
||||
use number::{BigInt, FieldElement};
|
||||
|
||||
// Can't depend on compiler::pipeline::GeneratedWitness because of circular dependencies...
|
||||
pub fn mock_prove<T: FieldElement>(
|
||||
pil: &Analyzed<T>,
|
||||
fixed: &[(String, Vec<T>)],
|
||||
constants: &[(String, Vec<T>)],
|
||||
witness: &[(String, Vec<T>)],
|
||||
) {
|
||||
if polyexen::expr::get_field_p::<Fr>() != T::modulus().to_arbitrary_integer() {
|
||||
panic!("powdr modulus doesn't match halo2 modulus. Make sure you are using Bn254");
|
||||
}
|
||||
|
||||
let circuit = analyzed_to_circuit(pil, fixed, witness);
|
||||
let circuit = analyzed_to_circuit(pil, constants, witness);
|
||||
|
||||
// double the row count in order to make space for the cells introduced by the backend
|
||||
// TODO: use a precise count of the extra rows needed to avoid using so many rows
|
||||
@@ -33,76 +34,53 @@ pub fn mock_prove<T: FieldElement>(
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use std::{fs, path::PathBuf};
|
||||
|
||||
use analysis::convert_asm_to_pil;
|
||||
use compiler::{inputs_to_query_callback, pipeline::Pipeline, test_util::resolve_test_file};
|
||||
use executor::witgen::unused_query_callback;
|
||||
use number::Bn254Field;
|
||||
use parser::parse_asm;
|
||||
use test_log::test;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[allow(clippy::print_stdout)]
|
||||
fn mock_prove_asm(file_name: &str, inputs: &[Bn254Field]) {
|
||||
// read and compile PIL.
|
||||
|
||||
let location = format!(
|
||||
"{}/../test_data/asm/{file_name}",
|
||||
env!("CARGO_MANIFEST_DIR")
|
||||
let mut pipeline = Pipeline::default().from_file(resolve_test_file(file_name));
|
||||
pipeline
|
||||
.generate_witness(inputs_to_query_callback(inputs.to_vec()), vec![])
|
||||
.unwrap();
|
||||
let result = pipeline.generated_witness().unwrap();
|
||||
mock_prove(
|
||||
&result.pil,
|
||||
&result.constants,
|
||||
result.witness.as_ref().unwrap(),
|
||||
);
|
||||
|
||||
let contents = fs::read_to_string(&location).unwrap();
|
||||
let parsed = parse_asm::<Bn254Field>(Some(&location), &contents).unwrap();
|
||||
let resolved = importer::resolve(Some(PathBuf::from(location)), parsed).unwrap();
|
||||
let analysed = convert_asm_to_pil(resolved).unwrap();
|
||||
let graph = airgen::compile(analysed);
|
||||
let pil = linker::link(graph).unwrap();
|
||||
|
||||
let query_callback = compiler::inputs_to_query_callback(inputs.to_vec());
|
||||
|
||||
let analyzed = pil_analyzer::analyze_string(&format!("{pil}"));
|
||||
|
||||
let fixed = executor::constant_evaluator::generate(&analyzed);
|
||||
let witness =
|
||||
executor::witgen::WitnessGenerator::new(&analyzed, &fixed, query_callback).generate();
|
||||
|
||||
let fixed = to_owned_values(fixed);
|
||||
|
||||
mock_prove(&analyzed, &fixed, &witness);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn simple_pil_halo2() {
|
||||
let content = "namespace Global(8); pol fixed z = [0]*; pol witness a; a = 0;";
|
||||
let analyzed: Analyzed<Bn254Field> = pil_analyzer::analyze_string(content);
|
||||
let fixed = executor::constant_evaluator::generate(&analyzed);
|
||||
let content = "namespace Global(8); pol fixed z = [1, 2]*; pol witness a; a = z + 1;";
|
||||
|
||||
let witness =
|
||||
executor::witgen::WitnessGenerator::new(&analyzed, &fixed, unused_query_callback())
|
||||
.generate();
|
||||
let mut pipeline = Pipeline::<Bn254Field>::default().from_pil_string(content.to_string());
|
||||
pipeline
|
||||
.generate_witness(unused_query_callback(), vec![])
|
||||
.unwrap();
|
||||
|
||||
let fixed = to_owned_values(fixed);
|
||||
|
||||
mock_prove(&analyzed, &fixed, &witness);
|
||||
let result = pipeline.generated_witness().unwrap();
|
||||
mock_prove(
|
||||
&result.pil,
|
||||
&result.constants,
|
||||
result.witness.as_ref().unwrap(),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn simple_sum() {
|
||||
let inputs = [165, 5, 11, 22, 33, 44, 55].map(From::from);
|
||||
mock_prove_asm("simple_sum.asm", &inputs);
|
||||
mock_prove_asm("asm/simple_sum.asm", &inputs);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn palindrome() {
|
||||
let inputs = [3, 11, 22, 11].map(From::from);
|
||||
mock_prove_asm("palindrome.asm", &inputs);
|
||||
}
|
||||
|
||||
fn to_owned_values<T: FieldElement>(values: Vec<(&str, Vec<T>)>) -> Vec<(String, Vec<T>)> {
|
||||
values
|
||||
.into_iter()
|
||||
.map(|(s, fields)| (s.to_string(), fields.clone()))
|
||||
.collect::<Vec<_>>()
|
||||
mock_prove_asm("asm/palindrome.asm", &inputs);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,8 +4,9 @@ mod util;
|
||||
|
||||
use backend::{Backend, BackendType, Proof};
|
||||
use clap::{CommandFactory, Parser, Subcommand};
|
||||
use compiler::inputs_to_query_callback;
|
||||
use compiler::pipeline::{Pipeline, ProofResult};
|
||||
use compiler::util::{read_poly_set, FixedPolySet, WitnessPolySet};
|
||||
use compiler::{compile_asm_string, compile_pil_or_asm, CompilationResult};
|
||||
use env_logger::fmt::Color;
|
||||
use env_logger::{Builder, Target};
|
||||
use log::LevelFilter;
|
||||
@@ -16,6 +17,7 @@ use riscv::continuations::rust_continuations;
|
||||
use riscv::{compile_riscv_asm, compile_rust};
|
||||
use std::collections::HashMap;
|
||||
use std::io::{self, BufReader, BufWriter, Read};
|
||||
use std::path::PathBuf;
|
||||
use std::{borrow::Cow, fs, io::Write, path::Path};
|
||||
use strum::{Display, EnumString, EnumVariantNames};
|
||||
|
||||
@@ -601,16 +603,16 @@ fn handle_riscv_asm<F: FieldElement>(
|
||||
unimplemented!("Running witgen with continuations is not supported yet.")
|
||||
}
|
||||
(false, false) => {
|
||||
compile_asm_string(
|
||||
file_name,
|
||||
contents,
|
||||
inputs,
|
||||
None,
|
||||
output_dir,
|
||||
force_overwrite,
|
||||
prove_with,
|
||||
vec![],
|
||||
)?;
|
||||
let mut pipeline = Pipeline::default()
|
||||
.with_output(output_dir.to_path_buf(), force_overwrite)
|
||||
.from_asm_string(contents.to_string(), Some(PathBuf::from(file_name)));
|
||||
pipeline
|
||||
.generate_witness(inputs_to_query_callback(inputs), vec![])
|
||||
.unwrap();
|
||||
pipeline.prove(BackendType::PilStarkCli).unwrap();
|
||||
if let Some(backend) = prove_with {
|
||||
pipeline.prove(backend).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
@@ -640,19 +642,27 @@ fn compile_with_csv_export<T: FieldElement>(
|
||||
let external_witness_values = strings.iter().map(AsRef::as_ref).zip(values).collect();
|
||||
|
||||
let output_dir = Path::new(&output_directory);
|
||||
let result = compile_pil_or_asm::<T>(
|
||||
&file,
|
||||
split_inputs(&inputs),
|
||||
output_dir,
|
||||
force,
|
||||
prove_with.clone(),
|
||||
external_witness_values,
|
||||
)?;
|
||||
|
||||
let mut pipeline = Pipeline::default()
|
||||
.with_output(output_dir.to_path_buf(), force)
|
||||
.from_file(PathBuf::from(file));
|
||||
pipeline
|
||||
.generate_witness(
|
||||
inputs_to_query_callback(split_inputs(&inputs)),
|
||||
external_witness_values,
|
||||
)
|
||||
.unwrap();
|
||||
let result = if let Some(backend) = &prove_with {
|
||||
pipeline.prove(backend.clone()).unwrap();
|
||||
Some(pipeline.proof().unwrap())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
if let Some(ref compilation_result) = result {
|
||||
serialize_result_witness(output_dir, compilation_result);
|
||||
|
||||
if let Some(_backend) = prove_with {
|
||||
if let Some(_backend) = &prove_with {
|
||||
write_proving_results_to_fs(
|
||||
false,
|
||||
&compilation_result.proof,
|
||||
@@ -708,7 +718,10 @@ fn read_and_prove<T: FieldElement>(
|
||||
proof_path: Option<String>,
|
||||
params: Option<String>,
|
||||
) {
|
||||
let pil = pilopt::optimize(compiler::analyze_pil::<T>(file));
|
||||
let pil = Pipeline::default()
|
||||
.from_file(file.to_path_buf())
|
||||
.optimized_pil()
|
||||
.unwrap();
|
||||
|
||||
let fixed = read_poly_set::<FixedPolySet, T>(&pil, dir);
|
||||
let witness = read_poly_set::<WitnessPolySet, T>(&pil, dir);
|
||||
@@ -741,11 +754,14 @@ fn read_and_prove<T: FieldElement>(
|
||||
fn optimize_and_output<T: FieldElement>(file: &str) {
|
||||
println!(
|
||||
"{}",
|
||||
pilopt::optimize(compiler::analyze_pil::<T>(Path::new(file)))
|
||||
Pipeline::<T>::default()
|
||||
.from_file(PathBuf::from(file))
|
||||
.optimized_pil()
|
||||
.unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
fn serialize_result_witness<T: FieldElement>(output_dir: &Path, results: &CompilationResult<T>) {
|
||||
fn serialize_result_witness<T: FieldElement>(output_dir: &Path, results: &ProofResult<T>) {
|
||||
write_constants_to_fs(&results.constants, output_dir);
|
||||
let witness = results.witness.as_ref().unwrap();
|
||||
write_commits_to_fs(witness, output_dir);
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
use std::collections::{BTreeSet, HashMap};
|
||||
use std::{
|
||||
collections::{BTreeSet, HashMap},
|
||||
path::PathBuf,
|
||||
};
|
||||
|
||||
use compiler::pipeline::Pipeline;
|
||||
use number::FieldElement;
|
||||
use riscv_executor::ExecutionTrace;
|
||||
|
||||
@@ -28,8 +32,10 @@ fn transposed_trace<F: FieldElement>(trace: &ExecutionTrace) -> HashMap<String,
|
||||
pub fn rust_continuations<F: FieldElement>(file_name: &str, contents: &str, inputs: Vec<F>) {
|
||||
let mut bootloader_inputs = default_input();
|
||||
|
||||
let program =
|
||||
compiler::compile_asm_string_to_analyzed_ast::<F>(file_name, contents, None).unwrap();
|
||||
let program = Pipeline::default()
|
||||
.from_asm_string(contents.to_string(), Some(PathBuf::from(file_name)))
|
||||
.analyzed_asm()
|
||||
.unwrap();
|
||||
|
||||
let inputs: HashMap<F, Vec<F>> = vec![(F::from(0), inputs)].into_iter().collect();
|
||||
|
||||
|
||||
@@ -1,42 +1,25 @@
|
||||
use compiler::{
|
||||
compile_asm_string, verify, write_commits_to_fs, write_constants_to_fs,
|
||||
write_constraints_to_fs, BackendType,
|
||||
};
|
||||
use compiler::{pipeline::Pipeline, test_util::verify_pipeline};
|
||||
use number::GoldilocksField;
|
||||
use riscv::bootloader::default_input;
|
||||
use std::collections::HashMap;
|
||||
use std::{collections::HashMap, path::PathBuf};
|
||||
|
||||
/// Like compiler::verify::verify_asm_string, but also runs RISCV executor.
|
||||
/// Like compiler::test_util::verify_asm_string, but also runs RISCV executor.
|
||||
pub fn verify_riscv_asm_string(file_name: &str, contents: &str, inputs: Vec<GoldilocksField>) {
|
||||
let temp_dir = mktemp::Temp::new_dir().unwrap().release();
|
||||
|
||||
let mut inputs_hash: HashMap<GoldilocksField, Vec<GoldilocksField>> = HashMap::default();
|
||||
inputs_hash.insert(0u32.into(), inputs.clone());
|
||||
|
||||
let (_, result) = compile_asm_string(
|
||||
file_name,
|
||||
contents,
|
||||
inputs.clone(),
|
||||
Some(&mut |analyzed| {
|
||||
riscv_executor::execute_ast(
|
||||
analyzed,
|
||||
&inputs_hash.clone(),
|
||||
&default_input(),
|
||||
usize::MAX,
|
||||
riscv_executor::ExecMode::Fast,
|
||||
);
|
||||
}),
|
||||
&temp_dir,
|
||||
true,
|
||||
Some(BackendType::PilStarkCli),
|
||||
vec![],
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let result = result.unwrap();
|
||||
write_constants_to_fs(&result.constants, &temp_dir);
|
||||
write_commits_to_fs(&result.witness.unwrap(), &temp_dir);
|
||||
write_constraints_to_fs(&result.constraints_serialization.unwrap(), &temp_dir);
|
||||
|
||||
verify(&temp_dir);
|
||||
let mut pipeline = Pipeline::default()
|
||||
.with_output(temp_dir.to_path_buf(), false)
|
||||
.from_asm_string(contents.to_string(), Some(PathBuf::from(file_name)));
|
||||
let analyzed = pipeline.analyzed_asm_ref().unwrap();
|
||||
riscv_executor::execute_ast(
|
||||
analyzed,
|
||||
&inputs_hash.clone(),
|
||||
&default_input(),
|
||||
usize::MAX,
|
||||
riscv_executor::ExecMode::Fast,
|
||||
);
|
||||
verify_pipeline(pipeline, inputs, vec![]);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
mod common;
|
||||
|
||||
use common::verify_riscv_asm_string;
|
||||
use compiler::verify_asm_string;
|
||||
use compiler::test_util::verify_asm_string;
|
||||
use mktemp::Temp;
|
||||
use number::GoldilocksField;
|
||||
use test_log::test;
|
||||
|
||||
@@ -7,7 +7,7 @@ namespace Sum(%N);
|
||||
pol fixed ISALMOSTLAST(i) { i == %last_row - 1 };
|
||||
pol fixed ISFIRST = [ 1, 0 ] + [0]*;
|
||||
|
||||
col witness input(i) query ("in", i);
|
||||
col witness input(i) query ("input", i);
|
||||
col witness sum;
|
||||
|
||||
ISLAST * sum' = 0;
|
||||
|
||||
Reference in New Issue
Block a user