#![deny(clippy::print_stdout)] #[cfg(feature = "halo2")] mod halo2_impl; mod pilstark; use ast::analyzed::Analyzed; use number::{DegreeType, FieldElement}; use std::{io, marker::PhantomData}; use strum::{Display, EnumString, EnumVariantNames}; #[derive(Clone, EnumString, EnumVariantNames, Display, Copy)] pub enum BackendType { #[cfg(feature = "halo2")] #[strum(serialize = "halo2")] Halo2, #[cfg(feature = "halo2")] #[strum(serialize = "halo2-mock")] Halo2Mock, #[strum(serialize = "estark")] EStark, #[strum(serialize = "pil-stark-cli")] PilStarkCli, } impl BackendType { pub fn factory(&self) -> &'static dyn BackendFactory { #[cfg(feature = "halo2")] const HALO2_FACTORY: WithSetupFactory = WithSetupFactory(PhantomData); #[cfg(feature = "halo2")] const HALO2_MOCK_FACTORY: WithoutSetupFactory = WithoutSetupFactory(PhantomData); const ESTARK_FACTORY: WithoutSetupFactory = WithoutSetupFactory(PhantomData); const PIL_STARK_CLI_FACTORY: WithoutSetupFactory = WithoutSetupFactory(PhantomData); match self { #[cfg(feature = "halo2")] BackendType::Halo2 => &HALO2_FACTORY, #[cfg(feature = "halo2")] BackendType::Halo2Mock => &HALO2_MOCK_FACTORY, BackendType::PilStarkCli => &PIL_STARK_CLI_FACTORY, BackendType::EStark => &ESTARK_FACTORY, } } } /// Factory for backends without setup. struct WithoutSetupFactory(PhantomData); /// Factory implementation for backends without setup. impl + 'static> BackendFactory for WithoutSetupFactory { fn create(&self, degree: DegreeType) -> Box> { Box::new(ConcreteBackendWithoutSetup(B::new(degree))) } fn create_from_setup(&self, _input: &mut dyn io::Read) -> Result>, Error> { Err(Error::NoSetupAvailable) } } /// Concrete dynamic dispatch Backend object, for backends without setup. struct ConcreteBackendWithoutSetup(B); /// Concrete implementation for backends with setup. impl> Backend for ConcreteBackendWithoutSetup { fn prove( &self, pil: &Analyzed, fixed: &[(String, Vec)], witness: &[(String, Vec)], prev_proof: Option, ) -> (Option, Option) { self.0.prove(pil, fixed, witness, prev_proof) } fn write_setup(&self, _output: &mut dyn io::Write) -> Result<(), Error> { Err(Error::NoSetupAvailable) } } /// Factory for backends with setup. struct WithSetupFactory(PhantomData); /// Factory implementation for backends with setup. impl + 'static> BackendFactory for WithSetupFactory { fn create(&self, degree: DegreeType) -> Box> { Box::new(ConcreteBackendWithSetup(B::new(degree))) } fn create_from_setup(&self, input: &mut dyn io::Read) -> Result>, Error> { Ok(Box::new(ConcreteBackendWithSetup(B::new_from_setup( input, )?))) } } /// Concrete dynamic dispatch Backend object, for backends with setup. struct ConcreteBackendWithSetup(B); /// Concrete implementation for backends with setup. impl> Backend for ConcreteBackendWithSetup { fn prove( &self, pil: &Analyzed, fixed: &[(String, Vec)], witness: &[(String, Vec)], prev_proof: Option, ) -> (Option, Option) { self.0.prove(pil, fixed, witness, prev_proof) } fn write_setup(&self, output: &mut dyn io::Write) -> Result<(), Error> { Ok(self.0.write_setup(output)?) } } #[derive(thiserror::Error, Debug)] pub enum Error { #[error("input/output error")] IO(#[from] std::io::Error), #[error("the backend has not setup operations")] NoSetupAvailable, } pub type Proof = Vec; /* Bellow are the public interface traits. They are implemented in this module, wrapping the traits implemented by each backend. */ /// Dynamic interface for a backend. pub trait Backend { /// Perform the proving. /// /// If prev_proof is provided, proof aggregation is performed. /// /// Returns the generated proof, and the string serialization of the /// constraints. fn prove( &self, pil: &Analyzed, fixed: &[(String, Vec)], witness: &[(String, Vec)], prev_proof: Option, ) -> (Option, Option); /// Write the prover setup to a file, so that it can be loaded later. fn write_setup(&self, output: &mut dyn io::Write) -> Result<(), Error>; } /// Dynamic interface for a backend factory. pub trait BackendFactory { /// Maybe perform the setup, and create a new backend object. fn create(&self, degree: DegreeType) -> Box>; /// Create a backend object from a prover setup loaded from a file. fn create_from_setup(&self, input: &mut dyn io::Read) -> Result>, Error>; } /* Below are the traits implemented by the backends. */ /// Trait implemented by all backends. trait BackendImpl { fn new(degree: DegreeType) -> Self; fn prove( &self, pil: &Analyzed, fixed: &[(String, Vec)], witness: &[(String, Vec)], prev_proof: Option, ) -> (Option, Option); } /// Trait implemented by backends that have a setup phase that must be saved to /// a file. trait BackendImplWithSetup where Self: Sized + BackendImpl, { /// Create a backend object from a setup loaded from a file. fn new_from_setup(input: &mut dyn io::Read) -> Result; /// Write the setup to a file. fn write_setup(&self, output: &mut dyn io::Write) -> Result<(), io::Error>; }