use std::path::PathBuf;
use std::sync::Arc;
use zisk_prover_backend::{
AsmOptions, BackendProverOpts, ExecuteClient, ExecuteOutput, GuestProgram, ProverClientBuilder,
};
use crate::{ExecutorKind, Result, SdkError, ZiskHints, ZiskStdin};
pub struct EmbeddedExecuteOnlyBuilder {
executor: ExecutorKind,
asm_options: Option<AsmOptions>,
verbose: u8,
}
impl Default for EmbeddedExecuteOnlyBuilder {
fn default() -> Self {
Self { executor: ExecutorKind::Emulator, asm_options: None, verbose: 0 }
}
}
impl EmbeddedExecuteOnlyBuilder {
#[must_use]
pub fn new() -> Self {
Self::default()
}
pub(crate) fn from_parts(executor: ExecutorKind, asm_options: Option<AsmOptions>) -> Self {
Self { executor, asm_options, verbose: 0 }
}
#[must_use]
pub fn emulator(mut self) -> Self {
self.executor = ExecutorKind::Emulator;
self
}
#[must_use]
pub fn assembly(mut self) -> Self {
self.executor = ExecutorKind::Assembly;
self
}
#[must_use]
pub fn asm_options(mut self, opts: AsmOptions) -> Self {
self.asm_options = Some(opts);
self
}
#[must_use]
pub fn asm_cache_dir(mut self, path: impl Into<PathBuf>) -> Self {
let mut opts = self.asm_options.take().unwrap_or_default();
opts = opts.asm_path(path.into());
self.asm_options = Some(opts);
self
}
#[must_use]
pub fn verbose(mut self, v: u8) -> Self {
self.verbose = v;
self
}
pub fn build(self) -> Result<EmbeddedExecuteOnlyClient> {
crate::client::ensure_single_instance();
let mut backend_opts = BackendProverOpts::default().verbose(self.verbose);
if let Some(asm_opts) = self.asm_options {
backend_opts = backend_opts.with_asm_options(asm_opts);
}
let prover: Box<dyn ExecuteClient + Send + Sync> = match self.executor {
ExecutorKind::Emulator => Box::new(
ProverClientBuilder::new()
.emu()
.with_prover_options(backend_opts)
.execute_only()
.build()
.map_err(SdkError::backend)?,
),
ExecutorKind::Assembly => Box::new(
ProverClientBuilder::new()
.asm()
.with_prover_options(backend_opts)
.execute_only()
.build()
.map_err(SdkError::backend)?,
),
};
Ok(EmbeddedExecuteOnlyClient { prover: Arc::from(prover), executor: self.executor })
}
}
pub struct EmbeddedExecuteOnlyClient {
prover: Arc<dyn ExecuteClient + Send + Sync>,
executor: ExecutorKind,
}
impl Clone for EmbeddedExecuteOnlyClient {
fn clone(&self) -> Self {
Self { prover: Arc::clone(&self.prover), executor: self.executor }
}
}
impl EmbeddedExecuteOnlyClient {
pub fn executor(&self) -> ExecutorKind {
self.executor
}
pub fn setup(&self, program: &GuestProgram, with_hints: bool) -> Result<()> {
self.prover.setup(program, with_hints).map_err(SdkError::backend)
}
pub fn execute(
&self,
program: &GuestProgram,
stdin: ZiskStdin,
hints: Option<ZiskHints>,
) -> Result<ExecuteOutput> {
if hints.is_some() && self.executor == ExecutorKind::Emulator {
return Err(SdkError::UnsupportedExecutor(
"Hints require the Assembly executor".to_string(),
));
}
self.prover
.execute(program, stdin.into_inner(), hints.map(ZiskHints::into_inner))
.map_err(SdkError::backend)
}
}