Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
370 changes: 344 additions & 26 deletions Cargo.lock

Large diffs are not rendered by default.

8 changes: 8 additions & 0 deletions crates/aggchain-proof-builder/sindri.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"$schema": "https://sindri.app/api/v1/sindri-manifest-schema.json",
"name": "pessimistic-proof",
"circuitType": "sp1",
"provingScheme": "plonk",
"sp1Version": "5.0.0",
"elfPath": "elf/riscv32im-succinct-zkvm-elf"
}

Large diffs are not rendered by default.

43 changes: 42 additions & 1 deletion crates/aggchain-proof-builder/src/tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ pub fn load_aggchain_prover_inputs_json(
mod aggchain_proof_builder {
use std::time::Duration;

use prover_config::{NetworkProverConfig, ProverType};
use prover_config::{NetworkProverConfig, ProverType, SindriProverConfig};
use prover_executor::Executor;
use tower::{buffer::Buffer, Service, ServiceExt};

Expand All @@ -49,6 +49,22 @@ mod aggchain_proof_builder {
Ok(prover)
}

fn init_sindri_prover() -> Result<ProverService, anyhow::Error> {
let executor = Executor::new(
&ProverType::SindriProver(SindriProverConfig {
proving_timeout: Duration::from_secs(3600),
proving_request_timeout: Some(Duration::from_secs(600)),
project_name: "pessimistic-proof".to_string(),
project_tag: "latest".to_string(),
}),
&None,
crate::AGGCHAIN_PROOF_ELF,
);
let executor = tower::ServiceBuilder::new().service(executor).boxed();
let prover = Buffer::new(executor, 10);
Ok(prover)
}

#[tokio::test]
#[ignore = "requires network key, run manually"]
async fn execute_aggchain_program_test() -> Result<(), Box<dyn std::error::Error>> {
Expand All @@ -73,4 +89,29 @@ mod aggchain_proof_builder {

Ok(())
}

#[tokio::test]
#[ignore = "requires Sindri API key, run manually"]
async fn execute_aggchain_program_sindri_test() -> Result<(), Box<dyn std::error::Error>> {
let mut prover = init_sindri_prover()?;

let aggchain_prover_inputs: AggchainProverInputs = load_aggchain_prover_inputs_json(
"src/tests/data/aggchain_prover_inputs_001_lpb_1_eb_4.json",
)?;

let prover_executor::Response { proof } = prover
.ready()
.await
.map_err(Error::ProverServiceReadyError)?
.call(prover_executor::Request {
stdin: aggchain_prover_inputs.stdin,
proof_type: prover_executor::ProofType::Plonk,
})
.await
.map_err(|error| Error::ProverFailedToExecute(anyhow::Error::from_boxed(error)))?;

println!("Sindri prover executor successfully returned response: {proof:?}");

Ok(())
}
}
48 changes: 48 additions & 0 deletions crates/prover-config/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ pub enum ProverType {
NetworkProver(NetworkProverConfig),
CpuProver(CpuProverConfig),
MockProver(MockProverConfig),
SindriProver(SindriProverConfig),
}

impl Default for ProverType {
Expand Down Expand Up @@ -129,6 +130,45 @@ impl Default for MockProverConfig {
}
}

#[serde_as]
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
#[serde(rename_all = "kebab-case")]
pub struct SindriProverConfig {
#[serde_as(as = "Option<crate::with::HumanDuration>")]
pub proving_request_timeout: Option<Duration>,

#[serde(default = "default_local_proving_timeout")]
#[serde(with = "crate::with::HumanDuration")]
pub proving_timeout: Duration,

#[serde(default = "default_sindri_project_name")]
pub project_name: String,

#[serde(default = "default_sindri_project_tag")]
pub project_tag: String,
}

impl SindriProverConfig {
// This constant represents the number of second added to the proving_timeout
pub const DEFAULT_PROVING_TIMEOUT_PADDING: Duration = Duration::from_secs(1);

pub fn get_proving_request_timeout(&self) -> Duration {
self.proving_request_timeout
.unwrap_or_else(|| self.proving_timeout + Self::DEFAULT_PROVING_TIMEOUT_PADDING)
}
}

impl Default for SindriProverConfig {
fn default() -> Self {
Self {
proving_request_timeout: None,
proving_timeout: default_network_proving_timeout(),
project_name: default_sindri_project_name(),
project_tag: default_sindri_project_tag(),
}
}
}

pub const fn default_max_concurrency_limit() -> usize {
100
}
Expand All @@ -147,3 +187,11 @@ fn default_sp1_cluster_endpoint() -> Url {
Url::from_str(DEFAULT_SP1_CLUSTER_ENDPOINT).unwrap(),
)
}

fn default_sindri_project_name() -> String {
"pessimistic-proof".to_string()
}

fn default_sindri_project_tag() -> String {
"latest".to_string()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@

[primary-prover.sindri-prover]
proving-request-timeout = "5m"
proving-timeout = "10m"
project-name = "pessimistic-proof"
project-tag = "latest"
20 changes: 19 additions & 1 deletion crates/prover-config/tests/validate_deserialize.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use pretty_assertions::assert_eq;
use prover_config::{CpuProverConfig, MockProverConfig, NetworkProverConfig, ProverType};
use prover_config::{
CpuProverConfig, MockProverConfig, NetworkProverConfig, ProverType, SindriProverConfig,
};
use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
Expand Down Expand Up @@ -77,3 +79,19 @@ fn mock_prover() {
})
);
}

#[test]
fn sindri_prover() {
let input = "./tests/fixtures/validate_config/prover_config_sindri_prover.toml";
let config: TestConfig = toml::from_str(&std::fs::read_to_string(input).unwrap()).unwrap();

assert_eq!(
config.primary_prover,
ProverType::SindriProver(SindriProverConfig {
proving_request_timeout: Some(std::time::Duration::from_secs(300)),
proving_timeout: std::time::Duration::from_secs(600),
project_name: "pessimistic-proof".to_string(),
project_tag: "latest".to_string(),
})
);
}
1 change: 1 addition & 0 deletions crates/prover-executor/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ prover-engine.workspace = true
prover-logger.workspace = true
prover-config.workspace = true

sindri = { version = "0.3.1", features = ["sp1-v5"]}
sp1-sdk = { workspace = true, features = ["native-gnark"] }
sp1-prover = { workspace = true, features = ["native-gnark"] }

Expand Down
101 changes: 101 additions & 0 deletions crates/prover-executor/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use std::{
pub use error::Error;
use futures::{Future, TryFutureExt};
use prover_config::{CpuProverConfig, ProverType};
use sindri::{client::SindriClient, integrations::sp1_v5::SP1ProofInfo, JobStatus, ProofInput};
use sp1_sdk::{
network::{prover::NetworkProver, FulfillmentStrategy},
CpuProver, Prover, ProverClient, SP1ProofWithPublicValues, SP1ProvingKey, SP1Stdin,
Expand All @@ -20,6 +21,8 @@ use tower::{
};
use tracing::{debug, error, info};

use crate::error::ProofVerificationError;

#[cfg(test)]
mod tests;

Expand Down Expand Up @@ -153,6 +156,25 @@ impl Executor {
),
)
}
ProverType::SindriProver(sindri_prover_config) => {
debug!("Creating Sindri prover executor...");
let mut sindri_client = SindriClient::default();
sindri_client.polling_options.timeout = Some(sindri_prover_config.proving_timeout);
let verification_key = Self::compute_program_vkey(program);
(
verification_key.clone(),
Self::build_network_service(
sindri_prover_config.get_proving_request_timeout(),
SindriExecutor {
prover: Arc::new(sindri_client),
verification_key,
timeout: sindri_prover_config.proving_timeout,
project_name: sindri_prover_config.project_name.clone(),
project_tag: sindri_prover_config.project_tag.clone(),
},
),
)
}
}
}

Expand Down Expand Up @@ -357,3 +379,82 @@ impl Service<Request> for NetworkExecutor {
Box::pin(fut)
}
}

#[derive(Clone)]
struct SindriExecutor {
prover: Arc<SindriClient>,
verification_key: SP1VerifyingKey,
timeout: Duration,
project_name: String,
project_tag: String,
}

impl Service<Request> for SindriExecutor {
type Response = Response;

type Error = Error;

type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send>>;

fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
Poll::Ready(Ok(()))
}

fn call(&mut self, req: Request) -> Self::Future {
let prover = self.prover.clone();
let stdin = req.stdin;
let verification_key = self.verification_key.clone();
let timeout = self.timeout;
let project_name = self.project_name.clone();
let project_tag = self.project_tag.clone();

debug!("Proving with Sindri prover with timeout: {:?}", timeout);
let fut = async move {
debug!("Starting the proving of the requested MultiBatchHeader");

// Convert Sp1Stdin type to the Sindri client's ProofInput type
let proof_input = ProofInput::try_from(stdin)
.map_err(|error| Error::ProverFailed(error.to_string()))?;

// Submit the proof request, and poll until the job is completed or a
// timeout occurs. The Sindri client was passed the timeout parameter
// upon creation
let proof_response = prover
.prove_circuit(
&format!("{project_name}:{project_tag}"),
proof_input,
None,
None,
None,
)
.await
.map_err(|error| Error::ProverFailed(error.to_string()))?;

// If the Sindri job completed with an error, retrieve the error message
if proof_response.status == JobStatus::Failed {
return Err(Error::ProverFailed(
proof_response.error.flatten().unwrap_or(
"Sindri job was marked as failed. No error message was provided."
.to_string(),
),
));
}
// Convert the proof response to a SP1 proof with public values
let proof = proof_response
.to_sp1_proof_with_public()
.map_err(|error| Error::ProverFailed(error.to_string()))?;

debug!("Proving completed. Verifying the proof...");
proof_response
.verify_sp1_proof_locally(&verification_key)
.map_err(|error| {
Error::ProofVerificationFailed(ProofVerificationError::Plonk(error.to_string()))
})?;

debug!("Proof verification completed successfully");
Ok(Response { proof })
};

Box::pin(fut)
}
}
28 changes: 28 additions & 0 deletions scripts/make/Makefile.elf.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,31 @@ args = [
"-vv",
"-paggchain-proof-builder",
]

[tasks.install-sindri-cli]
description = "Install Sindri CLI"
command = "cargo"
args = [
"install",
"sindri-cli@0.3.1",
"--force",
"--locked",
]

[tasks.ap-elf-sindri-upload]
description = "Upload ELF file to Sindri"
dependencies = [
"ap-elf",
"install-sindri-cli"
]
cwd = "crates/aggchain-proof-builder"
command = "cargo"
args = [
"sindri",
"deploy",
".",
"--api-key",
"${SINDRI_API_KEY}",
#"--tags",
#"${SINDRI_PROJECT_TAG}",
]