diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 350fdb8..cc61052 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -10,6 +10,8 @@ on: env: CARGO_TERM_COLOR: always + STONE_SDK_VERSION: v0.3.0 + STONE_INSTALL_DIR: ./dependencies/stone jobs: build: @@ -47,18 +49,32 @@ jobs: key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} restore-keys: ${{ runner.os }}-cargo- - - name: Log in to Github container registry - run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin - - - name: Set cache image environment variables + - name: Set Stone SDK version in context + id: set-env-sdk-version run: | - # Uppercase characters are not allowed in Docker tags - cache_image=$(echo ghcr.io/${GITHUB_REPOSITORY}/build-cache | tr '[:upper:]' '[:lower:]') - echo "STONE_PROVER_DOCKER_CACHE=$(echo ${cache_image})" >> $GITHUB_ENV + echo "STONE_SDK_VERSION=${STONE_SDK_VERSION}" >> $GITHUB_ENV + echo "STONE_INSTALL_DIR=${STONE_INSTALL_DIR}" >> $GITHUB_ENV + + - name: Cache Stone prover and verifier + id: cache-stone + uses: actions/cache@v4 + with: + path: ${{ env.STONE_INSTALL_DIR }} + key: stone-${{ runner.os }}-${{ env.STONE_SDK_VERSION }} + - name: Download Stone + if: steps.cache-stone.outputs.cache-hit != 'true' + run: | + mkdir -p "${STONE_INSTALL_DIR}" + wget https://github.com/Moonsong-Labs/stone-prover-sdk/releases/download/${STONE_SDK_VERSION}/cpu_air_prover -O "${STONE_INSTALL_DIR}/cpu_air_prover" + wget https://github.com/Moonsong-Labs/stone-prover-sdk/releases/download/${STONE_SDK_VERSION}/cpu_air_verifier -O "${STONE_INSTALL_DIR}/cpu_air_verifier" - - name: Download Docker cache image (if available) - run: docker pull ${STONE_PROVER_DOCKER_CACHE} || true + - name: Set Stone in PATH + run: | + INSTALL_DIR=$(readlink -f ${STONE_INSTALL_DIR}) + echo "${INSTALL_DIR}" >> $GITHUB_PATH + chmod +x ${INSTALL_DIR}/cpu_air_prover + chmod +x ${INSTALL_DIR}/cpu_air_verifier - name: Build run: cargo build --verbose @@ -71,11 +87,3 @@ jobs: - name: Run tests run: cargo test --verbose - - - name: Push the image to the cache - # It's not possible to push packages from fork PRs. - if: github.event.pull_request.head.repo.full_name == github.repository - run: | - docker tag stone-prover-build:latest ${STONE_PROVER_DOCKER_CACHE} - docker push ${STONE_PROVER_DOCKER_CACHE} - diff --git a/Cargo.toml b/Cargo.toml index 4c69250..9a7c5a7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,14 +1,15 @@ [workspace] resolver = "2" -members = ["integration-tests", "madara-prover-common", "madara-prover-rpc-client", "madara-prover-rpc-server", "stone-prover", "test-cases", "test-fixtures", "integration-tests/evm-test"] +members = ["integration-tests", "madara-prover-rpc-client", "madara-prover-rpc-server", "test-cases", "test-fixtures", "integration-tests/evm-test"] [workspace.dependencies] -cairo-vm = { git = "https://github.com/Moonsong-Labs/cairo-vm", rev = "b2f69d230416129a84ad8237ccc13d088992f74b", features=["extensive_hints"] } +cairo-vm = { git = "https://github.com/Moonsong-Labs/cairo-vm", rev = "b2f69d230416129a84ad8237ccc13d088992f74b", features = ["extensive_hints"] } prost = "0.12.1" serde = { version = "1.0.192", features = ["derive"] } serde_json = "1.0.108" stark_evm_adapter = "0.1.5" +stone-prover-sdk = { git = "https://github.com/Moonsong-Labs/stone-prover-sdk", tag = "v0.3.0" } tempfile = "3.8.1" thiserror = "1.0.50" tokio = { version = "1.34.0", features = ["macros", "process", "rt-multi-thread"] } diff --git a/README.md b/README.md index 7d3bb2a..40a8793 100644 --- a/README.md +++ b/README.md @@ -42,11 +42,9 @@ pub async fn prove_cairo_program() -> Result((), Box) { ## Project structure * `integration-tests`: Integration tests. -* `madara-prover-common`: Types and functions used in both the server and client. * `madara-prover-rpc-client`: Prover API client. * `madara-prover-rpc-server`: Prover API server. * `protocols`: Protocol buffers are stored here. -* `stone-prover`: Rust wrapper for the Stone prover. * `test-cases`: Cairo programs used as test cases. * `test-fixtures`: Shared test fixtures and utilities. diff --git a/integration-tests/Cargo.toml b/integration-tests/Cargo.toml index 52f26f9..9a4c069 100644 --- a/integration-tests/Cargo.toml +++ b/integration-tests/Cargo.toml @@ -8,12 +8,12 @@ publish = false [dev-dependencies] rand = "0.8.5" -madara-prover-common = { path = "../madara-prover-common" } madara-prover-rpc-client = { path = "../madara-prover-rpc-client" } madara-prover-rpc-server = { path = "../madara-prover-rpc-server" } evm-adapter = { path = "../integration-tests/evm-test" } rstest = { workspace = true } serde_json = { workspace = true } +stone-prover-sdk = { workspace = true } test-cases = { path = "../test-cases" } test-fixtures = { path = "../test-fixtures" } tokio = { workspace = true } diff --git a/integration-tests/tests/integration/test_starknet_prover.rs b/integration-tests/tests/integration/test_starknet_prover.rs index 7fadb9a..e37cada 100644 --- a/integration-tests/tests/integration/test_starknet_prover.rs +++ b/integration-tests/tests/integration/test_starknet_prover.rs @@ -5,8 +5,8 @@ mod tests { use crate::integration::toolkit::{starknet_prover_client_server, RpcServer}; use madara_prover_rpc_client::services::starknet_prover::execute_and_prove; use rstest::rstest; - use madara_prover_common::models::Proof; - use madara_prover_common::toolkit::read_json_from_file; + use stone_prover_sdk::models::Proof; + use stone_prover_sdk::json::read_json_from_file; use madara_prover_rpc_client::services::starknet_prover::starknet_prover_proto::starknet_prover_client::StarknetProverClient; use test_cases::get_test_case_file_path; diff --git a/integration-tests/tests/integration/toolkit.rs b/integration-tests/tests/integration/toolkit.rs index 7ff4eb2..e434c11 100644 --- a/integration-tests/tests/integration/toolkit.rs +++ b/integration-tests/tests/integration/toolkit.rs @@ -11,7 +11,6 @@ use tokio::net::UnixStream; use tokio::task::JoinHandle; use tonic::transport::{Endpoint, Uri}; use tower::service_fn; -use test_fixtures::prover_in_path; pub type RpcServer = JoinHandle>; @@ -56,15 +55,12 @@ async fn rpc_client_server( } #[fixture] -pub async fn prover_client_server( - #[from(prover_in_path)] _path: (), -) -> (ProverClient, RpcServer) { +pub async fn prover_client_server() -> (ProverClient, RpcServer) { rpc_client_server(ProverClient::new).await } #[fixture] pub async fn starknet_prover_client_server( - #[from(prover_in_path)] _path: (), ) -> (StarknetProverClient, RpcServer) { rpc_client_server(StarknetProverClient::new).await } diff --git a/madara-prover-common/Cargo.toml b/madara-prover-common/Cargo.toml deleted file mode 100644 index 75f9f14..0000000 --- a/madara-prover-common/Cargo.toml +++ /dev/null @@ -1,17 +0,0 @@ -[package] -name = "madara-prover-common" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -cairo-vm = { workspace = true } -serde = { workspace = true, features = ["derive"] } -serde_json = { workspace = true } -stark_evm_adapter = { workspace = true } -tempfile = { workspace = true } - -[dev-dependencies] -rstest = { workspace = true } -test-cases = { path = "../test-cases" } \ No newline at end of file diff --git a/madara-prover-common/src/lib.rs b/madara-prover-common/src/lib.rs deleted file mode 100644 index 180e38a..0000000 --- a/madara-prover-common/src/lib.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod models; -pub mod toolkit; diff --git a/madara-prover-common/src/models.rs b/madara-prover-common/src/models.rs deleted file mode 100644 index e61433d..0000000 --- a/madara-prover-common/src/models.rs +++ /dev/null @@ -1,186 +0,0 @@ -use cairo_vm::air_private_input::AirPrivateInputSerializable; -use stark_evm_adapter::annotation_parser::SplitProofs; -use std::collections::HashMap; -use std::path::PathBuf; - -use serde::{Deserialize, Serialize}; - -#[derive(Serialize, Deserialize, Debug)] -pub struct CachedLdeConfig { - pub store_full_lde: bool, - pub use_fft_for_eval: bool, -} - -#[derive(Serialize, Deserialize, Debug)] -pub struct ProverConfig { - pub cached_lde_config: CachedLdeConfig, - pub constraint_polynomial_task_size: i32, - pub n_out_of_memory_merkle_layers: i32, - pub table_prover_n_tasks_per_segment: i32, -} - -impl Default for ProverConfig { - fn default() -> Self { - Self { - cached_lde_config: CachedLdeConfig { - store_full_lde: false, - use_fft_for_eval: false, - }, - constraint_polynomial_task_size: 256, - n_out_of_memory_merkle_layers: 1, - table_prover_n_tasks_per_segment: 32, - } - } -} - -#[derive(Serialize, Deserialize, Debug)] -pub struct FriParameters { - pub fri_step_list: Vec, - pub last_layer_degree_bound: u32, - pub n_queries: u32, - pub proof_of_work_bits: u32, -} - -#[derive(Serialize, Deserialize, Debug)] -pub struct StarkParameters { - pub fri: FriParameters, - pub log_n_cosets: i32, -} - -#[derive(Serialize, Deserialize, Debug)] -pub struct ProverParameters { - pub field: String, - pub stark: StarkParameters, - pub use_extension_field: bool, -} - -#[derive(Serialize, Deserialize, Eq, PartialEq, Debug)] -pub enum Layout { - #[serde(rename = "plain")] - Plain, - #[serde(rename = "small")] - Small, - #[serde(rename = "dex")] - Dex, - #[serde(rename = "recursive")] - Recursive, - #[serde(rename = "starknet")] - Starknet, - #[serde(rename = "recursive_large_output")] - RecursiveLargeOutput, - #[serde(rename = "all_solidity")] - AllSolidity, - #[serde(rename = "starknet_with_keccak")] - StarknetWithKeccak, -} - -#[derive(Serialize, Deserialize, Debug, PartialEq)] -pub struct MemorySegmentAddresses { - pub begin_addr: u32, - pub stop_ptr: u32, -} - -#[derive(Serialize, Deserialize, Debug, PartialEq)] -pub struct PublicMemoryEntry { - pub address: u32, - pub value: String, - pub page: u32, -} - -#[derive(Serialize, Deserialize, Debug, PartialEq)] -pub struct PublicInput { - pub layout: Layout, - pub rc_min: u32, - pub rc_max: u32, - pub n_steps: u32, - pub memory_segments: HashMap, - pub public_memory: Vec, - pub dynamic_params: Option>, -} - -// TODO: implement Deserialize in cairo-vm types. -impl<'a> TryFrom> for PublicInput { - type Error = serde_json::Error; - - /// Converts a Cairo VM `PublicInput` object into our format. - /// - /// Cairo VM provides an opaque public input struct that does not expose any of its members - /// and only implements `Serialize`. Our only solution for now is to serialize this struct - /// and deserialize it into our own format. - fn try_from(value: cairo_vm::air_public_input::PublicInput<'a>) -> Result { - // Cairo VM PublicInput does not expose members, so we are stuck with this poor - // excuse of a conversion function for now. - let public_input_str = serde_json::to_string(&value)?; - serde_json::from_str::(&public_input_str) - } -} - -#[derive(Serialize, Deserialize, Debug)] -pub struct ProofVersion { - commit_hash: String, - proof_hash: String, - statement_name: String, -} - -#[derive(Serialize, Deserialize, Debug)] -pub struct Proof { - pub private_input: AirPrivateInputSerializable, - pub proof_hex: String, - pub proof_parameters: ProverParameters, - pub prover_config: ProverConfig, - pub public_input: PublicInput, - pub split_proofs: Option, -} - -#[derive(Debug)] -pub struct ProverWorkingDirectory { - pub dir: tempfile::TempDir, - pub public_input_file: PathBuf, - pub private_input_file: PathBuf, - pub _memory_file: PathBuf, - pub _trace_file: PathBuf, - pub prover_config_file: PathBuf, - pub prover_parameter_file: PathBuf, - pub proof_file: PathBuf, - pub annotations_file: Option, - pub extra_annotations_file: Option, -} - -/// A struct representing the annotations artifacts generated by running the verifier with -/// --annotation_file and --extra_output_file -/// TODO: this is intermediate and probably doesn't need to be exposed (esp. serialized) -#[derive(Serialize, Deserialize, Debug)] -pub struct ProofAnnotations { - pub annotation_file: PathBuf, - pub extra_output_file: PathBuf, -} - -#[cfg(test)] -mod tests { - use test_cases::load_test_case_file; - - use super::*; - - /// Sanity check: verify that we can deserialize a public input JSON file. - #[test] - fn deserialize_public_input() { - let public_input_str = load_test_case_file("fibonacci/fibonacci_public_input.json"); - let public_input: PublicInput = serde_json::from_str(&public_input_str) - .expect("Failed to deserialize public input fixture"); - - // We don't check all fields, just ensure that we can deserialize the fixture - assert_eq!(public_input.layout, Layout::StarknetWithKeccak); - assert_eq!(public_input.n_steps, 32768); - assert_eq!(public_input.dynamic_params, None); - } - - #[test] - fn deserialize_solver_parameters() { - let parameters_str = load_test_case_file("fibonacci/cpu_air_params.json"); - let parameters: ProverParameters = serde_json::from_str(¶meters_str) - .expect("Failed to deserialize prover parameters fixture"); - - // We don't check all fields, just ensure that we can deserialize the fixture - assert!(!parameters.use_extension_field); - } -} diff --git a/madara-prover-common/src/toolkit.rs b/madara-prover-common/src/toolkit.rs deleted file mode 100644 index b900299..0000000 --- a/madara-prover-common/src/toolkit.rs +++ /dev/null @@ -1,24 +0,0 @@ -use std::fs::File; -use std::path::Path; - -use serde::de::DeserializeOwned; -use serde::Serialize; - -pub fn read_json_from_file>( - path: P, -) -> Result { - let file = File::open(path)?; - let mut reader = std::io::BufReader::new(file); - - let obj: T = serde_json::from_reader(&mut reader)?; - Ok(obj) -} - -pub fn write_json_to_file>( - obj: T, - path: P, -) -> Result<(), std::io::Error> { - let mut file = File::create(path)?; - serde_json::to_writer(&mut file, &obj)?; - Ok(()) -} diff --git a/madara-prover-rpc-client/Cargo.toml b/madara-prover-rpc-client/Cargo.toml index 5670b86..36305c3 100644 --- a/madara-prover-rpc-client/Cargo.toml +++ b/madara-prover-rpc-client/Cargo.toml @@ -7,9 +7,9 @@ edition = "2021" [dependencies] cairo-vm = { workspace = true } -madara-prover-common = { path = "../madara-prover-common" } prost = { workspace = true } serde_json = { workspace = true } +stone-prover-sdk = { workspace = true } tokio = { workspace = true } tonic = { workspace = true } diff --git a/madara-prover-rpc-client/src/services/prover.rs b/madara-prover-rpc-client/src/services/prover.rs index 6f1eee6..a003267 100644 --- a/madara-prover-rpc-client/src/services/prover.rs +++ b/madara-prover-rpc-client/src/services/prover.rs @@ -1,7 +1,7 @@ use cairo_vm::air_private_input::AirPrivateInput; use tonic::Status; -use madara_prover_common::models::{Proof, ProverConfig, ProverParameters, PublicInput}; +use stone_prover_sdk::models::{Proof, ProverConfig, ProverParameters, PublicInput}; use prover_proto::prover_client::ProverClient; use prover_proto::{ExecutionRequest, ExecutionResponse, ProverRequest, ProverResponse}; diff --git a/madara-prover-rpc-client/src/services/starknet_prover.rs b/madara-prover-rpc-client/src/services/starknet_prover.rs index da8ca89..3e872d1 100644 --- a/madara-prover-rpc-client/src/services/starknet_prover.rs +++ b/madara-prover-rpc-client/src/services/starknet_prover.rs @@ -1,7 +1,7 @@ use tonic::Status; -use madara_prover_common::models::Proof; use starknet_prover_proto::{StarknetExecutionRequest, StarknetProverResponse}; +use stone_prover_sdk::models::Proof; use crate::services::starknet_prover::starknet_prover_proto::starknet_prover_client::StarknetProverClient; diff --git a/madara-prover-rpc-server/Cargo.toml b/madara-prover-rpc-server/Cargo.toml index 7bcf8ba..0c42ab2 100644 --- a/madara-prover-rpc-server/Cargo.toml +++ b/madara-prover-rpc-server/Cargo.toml @@ -13,9 +13,8 @@ path = "src/lib.rs" [dependencies] cairo-vm = { workspace = true } -madara-prover-common = { path = "../madara-prover-common" } prost = { workspace = true } -stone-prover = { path = "../stone-prover" } +stone-prover-sdk = { workspace = true } thiserror = { workspace = true } tokio = { workspace = true } tonic = { workspace = true } diff --git a/madara-prover-rpc-server/src/cairo.rs b/madara-prover-rpc-server/src/cairo.rs index ec2625e..b69fc05 100644 --- a/madara-prover-rpc-server/src/cairo.rs +++ b/madara-prover-rpc-server/src/cairo.rs @@ -12,7 +12,7 @@ use cairo_vm::vm::vm_core::VirtualMachine; use thiserror::Error; use tonic::Status; -use madara_prover_common::models::PublicInput; +use stone_prover_sdk::models::PublicInput; #[derive(Error, Debug)] pub enum ExecutionError { diff --git a/madara-prover-rpc-server/src/evm_adapter.rs b/madara-prover-rpc-server/src/evm_adapter.rs index 41fb7dc..a80ecec 100644 --- a/madara-prover-rpc-server/src/evm_adapter.rs +++ b/madara-prover-rpc-server/src/evm_adapter.rs @@ -1,11 +1,11 @@ use std::path::Path; -use madara_prover_common::toolkit::read_json_from_file; use stark_evm_adapter::{ annotated_proof::AnnotatedProof, annotation_parser::{split_fri_merkle_statements, SplitProofs}, }; use std::io::BufRead; +use stone_prover_sdk::json::read_json_from_file; use thiserror::Error; #[derive(Debug, Error)] @@ -69,7 +69,7 @@ mod tests { &annotations_file, &extra_annotations_file, ) - .unwrap(); + .unwrap(); assert!(split_proofs.merkle_statements.len() > 0); assert!(split_proofs.fri_merkle_statements.len() > 0); diff --git a/madara-prover-rpc-server/src/services/common.rs b/madara-prover-rpc-server/src/services/common.rs index 8730dff..a2ab961 100644 --- a/madara-prover-rpc-server/src/services/common.rs +++ b/madara-prover-rpc-server/src/services/common.rs @@ -1,13 +1,15 @@ -use crate::cairo::ExecutionArtifacts; -use crate::evm_adapter; -use madara_prover_common::models::{ +use stone_prover_sdk::error::{ProverError, VerifierError}; +use stone_prover_sdk::fri::generate_prover_parameters; +use stone_prover_sdk::models::{ Proof, ProofAnnotations, ProverConfig, ProverParameters, ProverWorkingDirectory, }; -use stone_prover::error::{ProverError, VerifierError}; -use stone_prover::fri::generate_prover_parameters; -use stone_prover::prover::{run_prover_async, run_verifier_async}; +use stone_prover_sdk::prover::run_prover_async; +use stone_prover_sdk::verifier::run_verifier_with_annotations_async; use tonic::Status; +use crate::cairo::ExecutionArtifacts; +use crate::evm_adapter; + pub async fn call_prover( execution_artifacts: &ExecutionArtifacts, prover_config: &ProverConfig, @@ -33,12 +35,17 @@ pub async fn call_verifier( working_dir.annotations_file = Some(annotations_file.clone()); working_dir.extra_annotations_file = Some(extra_annotations_file.clone()); - run_verifier_async( + run_verifier_with_annotations_async( working_dir.proof_file.as_path(), &annotations_file, &extra_annotations_file, ) - .await + .await?; + + Ok(ProofAnnotations { + annotation_file: annotations_file, + extra_output_file: extra_annotations_file, + }) } pub fn format_prover_error(e: ProverError) -> Status { @@ -96,8 +103,8 @@ pub async fn verify_and_annotate_proof( ) -> Result<(), Status> { let _ = // TODO: return type seems worthless here call_verifier(working_dir) - .await - .map_err(format_verifier_error)?; + .await + .map_err(format_verifier_error)?; let proof_file_path = working_dir.proof_file.as_path(); let annotations_file_path = working_dir diff --git a/madara-prover-rpc-server/src/services/prover.rs b/madara-prover-rpc-server/src/services/prover.rs index faec2c2..d215b60 100644 --- a/madara-prover-rpc-server/src/services/prover.rs +++ b/madara-prover-rpc-server/src/services/prover.rs @@ -10,8 +10,8 @@ use crate::services::prover::prover_proto::prover_server::Prover; use crate::services::prover::prover_proto::{ ExecutionRequest, ExecutionResponse, ProverRequest, ProverResponse, }; -use madara_prover_common::models::{Proof, ProverConfig, ProverWorkingDirectory}; -use stone_prover::error::ProverError; +use stone_prover_sdk::error::ProverError; +use stone_prover_sdk::models::{Proof, ProverConfig, ProverWorkingDirectory}; pub mod prover_proto { tonic::include_proto!("prover"); diff --git a/madara-prover-rpc-server/src/services/starknet_prover.rs b/madara-prover-rpc-server/src/services/starknet_prover.rs index c09bbb5..fe980e1 100644 --- a/madara-prover-rpc-server/src/services/starknet_prover.rs +++ b/madara-prover-rpc-server/src/services/starknet_prover.rs @@ -19,8 +19,8 @@ use cairo_vm::vm::vm_core::VirtualMachine; use cairo_vm::{any_box, Felt252}; use tonic::{Request, Response, Status}; -use madara_prover_common::models::{Proof, ProverConfig, ProverWorkingDirectory}; -use stone_prover::error::ProverError; +use stone_prover_sdk::error::ProverError; +use stone_prover_sdk::models::{Proof, ProverConfig, ProverWorkingDirectory}; use crate::cairo::{extract_execution_artifacts, ExecutionArtifacts, ExecutionError}; use crate::services::common::{ diff --git a/madara-prover-rpc-server/tests/test_run_bootloader.rs b/madara-prover-rpc-server/tests/test_run_bootloader.rs index ab1b60f..bb0c7ba 100644 --- a/madara-prover-rpc-server/tests/test_run_bootloader.rs +++ b/madara-prover-rpc-server/tests/test_run_bootloader.rs @@ -7,9 +7,9 @@ mod tests { use cairo_vm::types::program::Program; use cairo_vm::vm::runners::cairo_pie::CairoPie; use rstest::{fixture, rstest}; + use stone_prover_sdk::json::read_json_from_file; + use stone_prover_sdk::models::PublicInput; - use madara_prover_common::models::PublicInput; - use madara_prover_common::toolkit::read_json_from_file; use madara_prover_rpc_server::cairo::ExecutionArtifacts; use madara_prover_rpc_server::services::starknet_prover::run_bootloader_in_proof_mode; use test_cases::{get_test_case_file_path, load_test_case_file}; diff --git a/stone-prover/Cargo.toml b/stone-prover/Cargo.toml deleted file mode 100644 index d1be8f6..0000000 --- a/stone-prover/Cargo.toml +++ /dev/null @@ -1,19 +0,0 @@ -[package] -name = "stone-prover" -version = "0.1.0" -edition = "2021" -description = "A Rust wrapper around StarkWare's Stone Prover." - -[dependencies] -cairo-vm = { workspace = true } -madara-prover-common = { path = "../madara-prover-common" } -serde = { workspace = true, features = ["derive"] } -serde_json = { workspace = true } -tempfile = { workspace = true } -thiserror = { workspace = true } -tokio = { workspace = true } - -[dev-dependencies] -rstest = { workspace = true } -test-cases = { path = "../test-cases" } -test-fixtures = { path = "../test-fixtures" } diff --git a/stone-prover/build.rs b/stone-prover/build.rs deleted file mode 100644 index cb2337b..0000000 --- a/stone-prover/build.rs +++ /dev/null @@ -1,131 +0,0 @@ -/// Builds the Stone Prover C++ submodule to make it callable from the wrapper. -use std::path::{Path, PathBuf}; - -#[derive(Debug)] -enum CommandError { - /// The command failed with a non-zero return code. - CommandFailed(std::process::Output), - /// The command could not be launched. - IoError(std::io::Error), -} - -impl From for CommandError { - fn from(value: std::io::Error) -> Self { - Self::IoError(value) - } -} - -/// Run any shell command line and retrieve its output. -fn run_command(command: &str) -> Result { - let output = std::process::Command::new("sh") - .arg("-c") - .arg(command) - .output()?; - - if !output.status.success() { - return Err(CommandError::CommandFailed(output)); - } - Ok(output) -} - -/// Copy a file from a running Docker container. -fn copy_file_from_container( - container_name: &str, - container_file: &Path, - target: &Path, -) -> Result<(), CommandError> { - let docker_copy_command = format!( - "docker cp -L {container_name}:{} {}", - container_file.to_string_lossy(), - target.to_string_lossy() - ); - let _ = run_command(&docker_copy_command); - Ok(()) -} - -/// Copy the prover and verifier binary files from the prover build container. -fn copy_prover_files_from_container( - container_name: &str, - output_dir: &Path, -) -> Result<(), CommandError> { - copy_file_from_container(container_name, Path::new("/bin/cpu_air_prover"), output_dir)?; - copy_file_from_container( - container_name, - Path::new("/bin/cpu_air_verifier"), - output_dir, - )?; - - Ok(()) -} - -fn make_docker_build_command(repo_dir: &Path, image_name: &str) -> String { - let mut docker_build_command = format!( - "docker build -t {image_name} {}", - repo_dir.to_string_lossy() - ); - - // Check if the platform is Mac - #[cfg(target_os = "macos")] - { - // Add build args - docker_build_command.push_str(" --build-arg CMAKE_ARGS=-DNO_AVX=1"); - } - - // Check if a cache image exists. Used by the CI/CD pipeline. - if let Ok(cache_image) = std::env::var("STONE_PROVER_DOCKER_CACHE") { - docker_build_command.push_str(&format!(" --cache-from {cache_image}")); - } - - docker_build_command -} - -/// Build the Stone Prover and copy binaries to `output_dir`. -/// -/// The prover repository contains a Dockerfile to build the prover. This function: -/// 1. Builds the Dockerfile -/// 2. Starts a container based on the generated image -/// 3. Extracts the binaries from the container -/// 4. Stops the container. -fn build_stone_prover(repo_dir: &Path, output_dir: &Path) { - // Build the Stone Prover build Docker image - let image_name = "stone-prover-build:latest"; - let docker_build_command = make_docker_build_command(repo_dir, image_name); - run_command(&docker_build_command).expect("Failed to build Stone Prover using Dockerfile"); - - // Run a container based on the Docker image - let docker_create_command = format!("docker create {image_name}"); - let docker_create_output = run_command(&docker_create_command) - .expect("Failed to start container to copy prover files"); - let container_name = String::from_utf8_lossy(&docker_create_output.stdout) - .trim() - .to_owned(); - println!("Started container {container_name}"); - - // Copy the files - let copy_result = copy_prover_files_from_container(&container_name, output_dir); - - // Stop the container - let docker_delete_command = format!("docker rm {container_name}"); - run_command(&docker_delete_command).expect("Failed to stop and delete prover build container"); - - // Handle a potential error during copy - if let Err(e) = copy_result { - panic!( - "Failed to copy files from the prover build container: {:?}", - e - ); - } -} - -fn main() { - let out_dir = PathBuf::from(std::env::var("OUT_DIR").unwrap()); - // Copy the prover and verifier to the root of the target directory - // to make them easy to find for all the crates in the workspace. - let target_dir = out_dir.join("../../..").canonicalize().unwrap(); - - let stone_prover_repo_dir = Path::new("./dependencies/stone-prover"); - build_stone_prover(stone_prover_repo_dir, target_dir.as_path()); - - // Rerun if the submodule is updated - println!("cargo:rerun-if-changed=../.git/modules/stone-prover/dependencies/stone-prover/HEAD"); -} diff --git a/stone-prover/dependencies/stone-prover b/stone-prover/dependencies/stone-prover deleted file mode 160000 index 00b274b..0000000 --- a/stone-prover/dependencies/stone-prover +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 00b274b55c82077184be4c0758f7bed18950eaba diff --git a/stone-prover/src/error.rs b/stone-prover/src/error.rs deleted file mode 100644 index 8f74a1e..0000000 --- a/stone-prover/src/error.rs +++ /dev/null @@ -1,19 +0,0 @@ -use thiserror::Error; - -#[derive(Error, Debug)] -pub enum ProverError { - #[error("prover could not be launched")] - IoError(#[from] std::io::Error), - #[error("prover run failed")] - CommandError(std::process::Output), - #[error("the format of a JSON file is invalid")] - SerdeError(#[from] serde_json::Error), -} - -#[derive(Error, Debug)] -pub enum VerifierError { - #[error("verifier could not be launched")] - IoError(#[from] std::io::Error), - #[error("verifier run failed")] - CommandError(std::process::Output), -} diff --git a/stone-prover/src/fri.rs b/stone-prover/src/fri.rs deleted file mode 100644 index 9820589..0000000 --- a/stone-prover/src/fri.rs +++ /dev/null @@ -1,84 +0,0 @@ -use madara_prover_common::models::{FriParameters, ProverParameters, StarkParameters}; - -/// Implements ceil(log2(x)). -fn ceil_log2(x: u32) -> u32 { - let mut log = x.ilog2(); - if !x.is_power_of_two() { - log += 1; - } - log -} - -/// Computes the FRI steps list based on the number of Cairo steps of the program. -/// -/// This computation is based on the documentation of the Stone prover: -/// # log₂(#steps) + 4 = log₂(last_layer_degree_bound) + ∑fri_step_list -/// # log₂(#steps) = log₂(last_layer_degree_bound) + ∑fri_step_list - 4 -/// # ∑fri_step_list = log₂(#steps) + 4 - log₂(last_layer_degree_bound) -/// -/// * `nb_steps`: Number of Cairo steps of the program. -/// * `last_layer_degree_bound`: Last layer degree bound. -/// -/// Returns The FRI steps list. -pub fn compute_fri_steps(nb_steps: u32, last_layer_degree_bound: u32) -> Vec { - let nb_steps_log = ceil_log2(nb_steps); - let last_layer_degree_bound_log = ceil_log2(last_layer_degree_bound); - - let sigma_fri_step_list = nb_steps_log + 4 - last_layer_degree_bound_log; - let quotient = (sigma_fri_step_list / 4) as usize; - let remainder = sigma_fri_step_list % 4; - - let mut fri_steps = vec![4; quotient]; - if remainder > 0 { - fri_steps.push(remainder); - } - - fri_steps -} - -/// Generates prover parameters based on program parameters. -/// -/// * `nb_steps`: Number of Cairo steps of the program. -/// * `last_layer_degree_bound`: Last layer degree bound. -pub fn generate_prover_parameters(nb_steps: u32, last_layer_degree_bound: u32) -> ProverParameters { - let fri_steps = compute_fri_steps(nb_steps, last_layer_degree_bound); - ProverParameters { - field: "PrimeField0".to_string(), - stark: StarkParameters { - fri: FriParameters { - fri_step_list: fri_steps, - last_layer_degree_bound, - n_queries: 18, - proof_of_work_bits: 24, - }, - log_n_cosets: 4, - }, - use_extension_field: false, - } -} - -#[cfg(test)] -mod tests { - use super::*; - use rstest::rstest; - - #[rstest] - #[case(2, 1)] - #[case(32, 5)] - #[case(1000, 10)] - #[case(524288, 19)] - fn test_ceil_log2(#[case] x: u32, #[case] expected: u32) { - let log = ceil_log2(x); - assert_eq!(log, expected); - } - - #[rstest] - #[case(32768, vec ! [4, 4, 4, 1])] - #[case(524288, vec ! [4, 4, 4, 4, 1])] - #[case(768, vec ! [4, 4])] - fn test_compute_fri_step_list(#[case] nb_steps: u32, #[case] expected: Vec) { - let last_layer_degree_bound = 64; - let step_list = compute_fri_steps(nb_steps, last_layer_degree_bound); - assert_eq!(step_list, expected); - } -} diff --git a/stone-prover/src/lib.rs b/stone-prover/src/lib.rs deleted file mode 100644 index 1d251a8..0000000 --- a/stone-prover/src/lib.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub mod error; -pub mod fri; -pub mod prover; diff --git a/stone-prover/src/prover.rs b/stone-prover/src/prover.rs deleted file mode 100644 index a2dd6b9..0000000 --- a/stone-prover/src/prover.rs +++ /dev/null @@ -1,356 +0,0 @@ -use cairo_vm::air_private_input::AirPrivateInput; -use std::path::Path; - -use tempfile::tempdir; - -use madara_prover_common::models::{ - Proof, ProofAnnotations, ProverConfig, ProverParameters, ProverWorkingDirectory, PublicInput, -}; -use madara_prover_common::toolkit::{read_json_from_file, write_json_to_file}; - -use crate::error::{ProverError, VerifierError}; - -/// Call the Stone Prover from the command line. -/// -/// Input files must be prepared by the caller. -/// -/// * `public_input_file`: Path to the public input file. -/// * `private_input_file`: Path to the private input file. The private input file points to -/// the memory and trace files. -/// * `prover_config_file`: Path to the prover configuration file. Contains application-agnostic -/// configuration values for the prover. -/// * `parameter_file`: Path to the prover parameters file. Contains application-specific -/// configuration values for the prover (ex: FRI steps). -/// * `output_file`: Path to the proof file. This function will write the generated proof -/// as JSON to this file. -pub fn run_prover_from_command_line( - public_input_file: &Path, - private_input_file: &Path, - prover_config_file: &Path, - prover_parameter_file: &Path, - output_file: &Path, -) -> Result<(), ProverError> { - let output = std::process::Command::new("cpu_air_prover") - .arg("--out-file") - .arg(output_file) - .arg("--public-input-file") - .arg(public_input_file) - .arg("--private-input-file") - .arg(private_input_file) - .arg("--prover-config-file") - .arg(prover_config_file) - .arg("--parameter-file") - .arg(prover_parameter_file) - .output()?; - - if !output.status.success() { - return Err(ProverError::CommandError(output)); - } - - Ok(()) -} - -/// Call the Stone Prover from the command line, asynchronously. -/// -/// Input files must be prepared by the caller. -/// -/// * `public_input_file`: Path to the public input file. -/// * `private_input_file`: Path to the private input file. The private input file points to -/// the memory and trace files. -/// * `prover_config_file`: Path to the prover configuration file. Contains application-agnostic -/// configuration values for the prover. -/// * `parameter_file`: Path to the prover parameters file. Contains application-specific -/// configuration values for the prover (ex: FRI steps). -/// * `output_file`: Path to the proof file. This function will write the generated proof -/// as JSON to this file. -pub async fn run_prover_from_command_line_async( - public_input_file: &Path, - private_input_file: &Path, - prover_config_file: &Path, - parameter_file: &Path, - output_file: &Path, -) -> Result<(), ProverError> { - let output = tokio::process::Command::new("cpu_air_prover") - .arg("--out-file") - .arg(output_file) - .arg("--public-input-file") - .arg(public_input_file) - .arg("--private-input-file") - .arg(private_input_file) - .arg("--prover-config-file") - .arg(prover_config_file) - .arg("--parameter-file") - .arg(parameter_file) - .output() - .await?; - - if !output.status.success() { - return Err(ProverError::CommandError(output)); - } - - Ok(()) -} - -/// Call the Stone Verifier from the command line, asynchronously. -/// -/// Input files must be prepared by the caller. -/// -/// * `in_file`: Path to the proof generated from the prover. Corresponds to its "--out-file". -/// * `annotation_file`: Path to the annotations file, which will be generated as output. -/// * `extra_output_file`: Path to the extra annotations file, which will be generated as output. -pub async fn run_verifier_from_command_line_async( - in_file: &Path, - annotation_file: &Path, - extra_output_file: &Path, -) -> Result<(), VerifierError> { - let output = tokio::process::Command::new("cpu_air_verifier") - .arg("--in_file") - .arg(in_file) - .arg("--annotation_file") - .arg(annotation_file) - .arg("--extra_output_file") - .arg(extra_output_file) - .output() - .await?; - - if !output.status.success() { - return Err(VerifierError::CommandError(output)); - } - - Ok(()) -} - -fn prepare_prover_files( - public_input: &PublicInput, - private_input: &AirPrivateInput, - memory: &Vec, - trace: &Vec, - prover_config: &ProverConfig, - parameters: &ProverParameters, -) -> Result { - let tmp_dir = tempdir()?; - - let tmp_dir_path = tmp_dir.path(); - - let public_input_file = tmp_dir_path.join("public_input.json"); - let private_input_file = tmp_dir_path.join("private_input.json"); - let memory_file = tmp_dir_path.join("memory.bin"); - let prover_config_file = tmp_dir_path.join("prover_config_file.json"); - let prover_parameter_file = tmp_dir_path.join("parameters.json"); - let trace_file = tmp_dir_path.join("trace.bin"); - let proof_file = tmp_dir_path.join("proof.json"); - - // Write public input and config/parameters files - write_json_to_file(public_input, &public_input_file)?; - write_json_to_file(prover_config, &prover_config_file)?; - write_json_to_file(parameters, &prover_parameter_file)?; - - // Write private input file - let private_input_serializable = private_input.to_serializable( - trace_file.to_string_lossy().to_string(), - memory_file.to_string_lossy().to_string(), - ); - write_json_to_file(private_input_serializable, &private_input_file)?; - - // Write memory and trace files - std::fs::write(&memory_file, memory)?; - std::fs::write(&trace_file, trace)?; - - Ok(ProverWorkingDirectory { - dir: tmp_dir, - public_input_file, - private_input_file, - _memory_file: memory_file, - _trace_file: trace_file, - prover_config_file, - prover_parameter_file, - proof_file, - annotations_file: None, - extra_annotations_file: None, - }) -} - -/// Run the Stone Prover on the specified program execution. -/// -/// This function abstracts the method used to call the prover. At the moment we invoke -/// the prover as a subprocess but other methods can be implemented (ex: FFI). -/// -/// * `public_input`: the public prover input generated by the Cairo program. -/// * `private_input`: the private prover input generated by the Cairo program. -/// * `memory`: the memory output of the Cairo program. -/// * `trace`: the execution trace of the Cairo program. -/// * `prover_config`: prover configuration. -/// * `parameters`: prover parameters for the Cairo program. -pub fn run_prover( - public_input: &PublicInput, - private_input: &AirPrivateInput, - memory: &Vec, - trace: &Vec, - prover_config: &ProverConfig, - parameters: &ProverParameters, -) -> Result { - let prover_working_dir = prepare_prover_files( - public_input, - private_input, - memory, - trace, - prover_config, - parameters, - )?; - - // Call the prover - run_prover_from_command_line( - &prover_working_dir.public_input_file, - &prover_working_dir.private_input_file, - &prover_working_dir.prover_config_file, - &prover_working_dir.prover_parameter_file, - &prover_working_dir.proof_file, - )?; - - // Load the proof from the generated JSON proof file - let proof = read_json_from_file(&prover_working_dir.proof_file)?; - Ok(proof) -} - -/// Run the Stone Prover on the specified program execution, asynchronously. -/// -/// The main difference from the synchronous implementation is that the prover process -/// is spawned asynchronously using `tokio::process::Command`. -/// -/// This function abstracts the method used to call the prover. At the moment we invoke -/// the prover as a subprocess but other methods can be implemented (ex: FFI). -/// -/// * `public_input`: the public prover input generated by the Cairo program. -/// * `private_input`: the private prover input generated by the Cairo program. -/// * `memory`: the memory output of the Cairo program. -/// * `trace`: the execution trace of the Cairo program. -/// * `prover_config`: prover configuration. -/// * `parameters`: prover parameters for the Cairo program. -pub async fn run_prover_async( - public_input: &PublicInput, - private_input: &AirPrivateInput, - memory: &Vec, - trace: &Vec, - prover_config: &ProverConfig, - parameters: &ProverParameters, -) -> Result<(Proof, ProverWorkingDirectory), ProverError> { - let prover_working_dir = prepare_prover_files( - public_input, - private_input, - memory, - trace, - prover_config, - parameters, - )?; - - // Call the prover - run_prover_from_command_line_async( - &prover_working_dir.public_input_file, - &prover_working_dir.private_input_file, - &prover_working_dir.prover_config_file, - &prover_working_dir.prover_parameter_file, - &prover_working_dir.proof_file, - ) - .await?; - - // Load the proof from the generated JSON proof file - let proof = read_json_from_file(&prover_working_dir.proof_file)?; - Ok((proof, prover_working_dir)) -} - -/// Run the Stone Verifier on the specified program execution, asynchronously. -/// -/// The main difference from the synchronous implementation is that the verifier process -/// is spawned asynchronously using `tokio::process::Command`. -/// -/// This function abstracts the method used to call the verifier. At the moment we invoke -/// the verifier as a subprocess but other methods can be implemented (ex: FFI). -/// -/// * `in_file`: Path to the proof generated from the prover. Corresponds to its "--out-file". -/// * `annotation_file`: Path to the annotations file, which will be generated as output. -/// * `extra_output_file`: Path to the extra annotations file, which will be generated as output. -pub async fn run_verifier_async( - in_file: &Path, - annotation_file: &Path, - extra_output_file: &Path, -) -> Result { - // Call the verifier - run_verifier_from_command_line_async(in_file, annotation_file, extra_output_file).await?; - - let annotations = ProofAnnotations { - annotation_file: annotation_file.into(), - extra_output_file: extra_output_file.into(), - }; - Ok(annotations) -} - -#[cfg(test)] -mod test { - use rstest::rstest; - use tempfile::NamedTempFile; - - use test_fixtures::{ - parsed_prover_test_case, prover_cli_test_case, prover_in_path, read_proof_file, - ParsedProverTestCase, ProverCliTestCase, - }; - - use super::*; - - /// Check that the Stone Prover command-line wrapper works. - #[rstest] - fn test_run_prover_from_command_line( - prover_cli_test_case: ProverCliTestCase, - #[from(prover_in_path)] _path: (), - ) { - let output_file = NamedTempFile::new().expect("Creating output file failed"); - run_prover_from_command_line( - &prover_cli_test_case.public_input_file, - &prover_cli_test_case.private_input_file.path(), - &prover_cli_test_case.prover_config_file, - &prover_cli_test_case.prover_parameter_file, - output_file.path(), - ) - .unwrap(); - - let proof = read_proof_file(output_file.path()); - assert_eq!(proof.proof_hex, prover_cli_test_case.proof.proof_hex); - } - - #[rstest] - fn test_run_prover( - parsed_prover_test_case: ParsedProverTestCase, - #[from(prover_in_path)] _path: (), - ) { - let proof = run_prover( - &parsed_prover_test_case.public_input, - &parsed_prover_test_case.private_input, - &parsed_prover_test_case.memory, - &parsed_prover_test_case.trace, - &parsed_prover_test_case.prover_config, - &parsed_prover_test_case.prover_parameters, - ) - .unwrap(); - - assert_eq!(proof.proof_hex, parsed_prover_test_case.proof.proof_hex); - } - - #[rstest] - #[tokio::test] - async fn test_run_prover_async( - parsed_prover_test_case: ParsedProverTestCase, - #[from(prover_in_path)] _path: (), - ) { - let proof = run_prover_async( - &parsed_prover_test_case.public_input, - &parsed_prover_test_case.private_input, - &parsed_prover_test_case.memory, - &parsed_prover_test_case.trace, - &parsed_prover_test_case.prover_config, - &parsed_prover_test_case.prover_parameters, - ) - .await - .unwrap(); - - assert_eq!(proof.0.proof_hex, parsed_prover_test_case.proof.proof_hex); - } -} diff --git a/test-fixtures/Cargo.toml b/test-fixtures/Cargo.toml index 0729adf..4c5c318 100644 --- a/test-fixtures/Cargo.toml +++ b/test-fixtures/Cargo.toml @@ -8,8 +8,8 @@ publish = false [dependencies] cairo-vm = { workspace = true } -madara-prover-common = { path = "../madara-prover-common" } rstest = { workspace = true } serde_json = { workspace = true } +stone-prover-sdk = { workspace = true } tempfile = { workspace = true } test-cases = { path = "../test-cases" } diff --git a/test-fixtures/src/lib.rs b/test-fixtures/src/lib.rs index 287cfcc..c80b5da 100644 --- a/test-fixtures/src/lib.rs +++ b/test-fixtures/src/lib.rs @@ -8,22 +8,10 @@ use cairo_vm::Felt252; use rstest::fixture; use tempfile::NamedTempFile; -use madara_prover_common::models::{Proof, ProverConfig, ProverParameters, PublicInput}; -use madara_prover_common::toolkit::read_json_from_file; +use stone_prover_sdk::json::read_json_from_file; +use stone_prover_sdk::models::{Proof, ProverConfig, ProverParameters, PublicInput}; use test_cases::get_test_case_file_path; -#[fixture] -pub fn prover_in_path() { - // Add build dir to path for the duration of the test - let path = std::env::var("PATH").unwrap_or_default(); - let build_dir = Path::new(env!("OUT_DIR")); - // This will find the root of the target directory where the prover binaries - // are put after compilation. - let target_dir = build_dir.join("../../..").canonicalize().unwrap(); - - std::env::set_var("PATH", format!("{}:{path}", target_dir.to_string_lossy())); -} - /// Reads and deserializes a JSON proof file. pub fn read_proof_file>(proof_file: P) -> Proof { let proof: Proof = read_json_from_file(proof_file).expect("Could not open proof file");