diff --git a/bin/citrea/src/rollup/bitcoin.rs b/bin/citrea/src/rollup/bitcoin.rs index 4b4b70e07..d82e8e3bb 100644 --- a/bin/citrea/src/rollup/bitcoin.rs +++ b/bin/citrea/src/rollup/bitcoin.rs @@ -26,8 +26,8 @@ use sov_rollup_interface::da::DaVerifier; use sov_rollup_interface::services::da::SenderWithNotifier; use sov_state::ZkStorage; use sov_stf_runner::ProverGuestRunConfig; -use tokio::sync::broadcast; use tokio::sync::mpsc::unbounded_channel; +use tokio::sync::{broadcast, Mutex}; use tracing::instrument; use crate::guests::{ @@ -246,6 +246,7 @@ impl RollupBlueprint for BitcoinRollup { da_service: &Arc, da_verifier: Self::DaVerifier, ledger_db: LedgerDB, + proof_sampling_number: usize, ) -> Self::ProverService { let vm = Risc0BonsaiHost::new(ledger_db.clone()); // let vm = SP1Host::new( @@ -260,10 +261,13 @@ impl RollupBlueprint for BitcoinRollup { ProverGuestRunConfig::Skip => ProofGenMode::Skip, ProverGuestRunConfig::Simulate => { let stf_verifier = StateTransitionVerifier::new(zk_stf, da_verifier); - ProofGenMode::Simulate(stf_verifier) + ProofGenMode::Simulate(Arc::new(Mutex::new(stf_verifier))) } ProverGuestRunConfig::Execute => ProofGenMode::Execute, - ProverGuestRunConfig::Prove => ProofGenMode::Prove, + ProverGuestRunConfig::Prove => ProofGenMode::ProveWithSampling, + ProverGuestRunConfig::ProveWithFakeProofs => { + ProofGenMode::ProveWithSamplingWithFakeProofs(proof_sampling_number) + } }; ParallelProverService::new_from_env( diff --git a/bin/citrea/src/rollup/mock.rs b/bin/citrea/src/rollup/mock.rs index 72f128679..311e8c4fa 100644 --- a/bin/citrea/src/rollup/mock.rs +++ b/bin/citrea/src/rollup/mock.rs @@ -20,7 +20,7 @@ use sov_modules_stf_blueprint::StfBlueprint; use sov_prover_storage_manager::ProverStorageManager; use sov_state::ZkStorage; use sov_stf_runner::ProverGuestRunConfig; -use tokio::sync::broadcast; +use tokio::sync::{broadcast, Mutex}; use crate::guests::{BATCH_PROOF_LATEST_MOCK_GUESTS, LIGHT_CLIENT_LATEST_MOCK_GUESTS}; use crate::{CitreaRollupBlueprint, Network}; @@ -179,6 +179,7 @@ impl RollupBlueprint for MockDemoRollup { da_service: &Arc, da_verifier: Self::DaVerifier, ledger_db: LedgerDB, + proof_sampling_number: usize, ) -> Self::ProverService { let vm = Risc0BonsaiHost::new(ledger_db.clone()); @@ -189,10 +190,13 @@ impl RollupBlueprint for MockDemoRollup { ProverGuestRunConfig::Skip => ProofGenMode::Skip, ProverGuestRunConfig::Simulate => { let stf_verifier = StateTransitionVerifier::new(zk_stf, da_verifier); - ProofGenMode::Simulate(stf_verifier) + ProofGenMode::Simulate(Arc::new(Mutex::new(stf_verifier))) } ProverGuestRunConfig::Execute => ProofGenMode::Execute, - ProverGuestRunConfig::Prove => ProofGenMode::Prove, + ProverGuestRunConfig::Prove => ProofGenMode::ProveWithSampling, + ProverGuestRunConfig::ProveWithFakeProofs => { + ProofGenMode::ProveWithSamplingWithFakeProofs(proof_sampling_number) + } }; ParallelProverService::new(da_service.clone(), vm, proof_mode, zk_storage, 1, ledger_db) diff --git a/bin/citrea/src/rollup/mod.rs b/bin/citrea/src/rollup/mod.rs index a1f1a69d6..8b4c8fb07 100644 --- a/bin/citrea/src/rollup/mod.rs +++ b/bin/citrea/src/rollup/mod.rs @@ -296,6 +296,7 @@ pub trait CitreaRollupBlueprint: RollupBlueprint { &da_service, da_verifier, ledger_db.clone(), + prover_config.proof_sampling_number, ) .await; @@ -421,6 +422,7 @@ pub trait CitreaRollupBlueprint: RollupBlueprint { &da_service, da_verifier, ledger_db.clone(), + prover_config.proof_sampling_number, ) .await; diff --git a/crates/batch-prover/src/da_block_handler.rs b/crates/batch-prover/src/da_block_handler.rs index 9c6b93659..5e910f9bc 100644 --- a/crates/batch-prover/src/da_block_handler.rs +++ b/crates/batch-prover/src/da_block_handler.rs @@ -25,7 +25,7 @@ use sov_rollup_interface::services::da::{DaService, SlotData}; use sov_rollup_interface::soft_confirmation::SignedSoftConfirmation; use sov_rollup_interface::spec::SpecId; use sov_rollup_interface::zk::ZkvmHost; -use sov_stf_runner::ProverService; +use sov_stf_runner::{ProverGuestRunConfig, ProverService}; use tokio::select; use tokio::sync::{mpsc, Mutex}; use tokio::time::{sleep, Duration}; @@ -238,11 +238,18 @@ where l1_block.header().height(), ); - // if proof_sampling_number is 0, then we always prove and submit - // otherwise we submit and prove with a probability of 1/proof_sampling_number - let should_prove = self.prover_config.proof_sampling_number == 0 - || rand::thread_rng().gen_range(0..self.prover_config.proof_sampling_number) == 0; - + let should_prove = match self.prover_config.proving_mode { + ProverGuestRunConfig::ProveWithFakeProofs => { + // Unconditionally call `prove_l1()` + true + } + _ => { + // Call `prove_l1()` with a probability + self.prover_config.proof_sampling_number == 0 + || rand::thread_rng().gen_range(0..self.prover_config.proof_sampling_number) + == 0 + } + }; if should_prove { if l1_height >= self.skip_submission_until_l1 { prove_l1::( diff --git a/crates/prover-services/src/lib.rs b/crates/prover-services/src/lib.rs index a23f701a5..5fd74c1c9 100644 --- a/crates/prover-services/src/lib.rs +++ b/crates/prover-services/src/lib.rs @@ -1,11 +1,17 @@ +use std::sync::Arc; + use citrea_stf::verifier::StateTransitionVerifier; use sov_rollup_interface::services::da::DaService; use sov_rollup_interface::stf::StateTransitionFunction; use sov_rollup_interface::zk::ZkvmHost; +use tokio::sync::Mutex; mod parallel; pub use parallel::*; +type Simulator = + Arc>>; + pub enum ProofGenMode where Da: DaService, @@ -15,10 +21,39 @@ where /// Skips proving. Skip, /// The simulator runs the rollup verifier logic without even emulating the zkVM - Simulate(StateTransitionVerifier), + Simulate(Simulator), /// The executor runs the rollup verification logic in the zkVM, but does not actually /// produce a zk proof Execute, /// The prover runs the rollup verification logic in the zkVM and produces a zk proof - Prove, + ProveWithSampling, + /// The prover runs the rollup verification logic in the zkVM and produces a zk/fake proof + ProveWithSamplingWithFakeProofs( + /// Average number of _REAL_ commitments to prove + /// If proof_sampling_number is 0, then we always produce real proofs + /// Otherwise we prove with a probability of 1/proof_sampling_number, + /// but produce fake proofs with a probability of (1-1/proof_sampling_number). + /// + /// proof_sampling_number: + usize, + ), +} + +impl Clone for ProofGenMode +where + Da: DaService, + Vm: ZkvmHost, + Stf: StateTransitionFunction, +{ + fn clone(&self) -> Self { + match self { + Self::Skip => Self::Skip, + Self::Execute => Self::Execute, + Self::ProveWithSampling => Self::ProveWithSampling, + Self::ProveWithSamplingWithFakeProofs(proof_sampling_number) => { + Self::ProveWithSamplingWithFakeProofs(*proof_sampling_number) + } + Self::Simulate(simulate) => Self::Simulate(Arc::clone(simulate)), + } + } } diff --git a/crates/prover-services/src/parallel/mod.rs b/crates/prover-services/src/parallel/mod.rs index 7d9423082..60a8a7ad4 100644 --- a/crates/prover-services/src/parallel/mod.rs +++ b/crates/prover-services/src/parallel/mod.rs @@ -1,8 +1,8 @@ -use std::ops::DerefMut; use std::sync::Arc; use async_trait::async_trait; use futures::future; +use rand::Rng; use sov_db::ledger_db::LedgerDB; use sov_rollup_interface::da::DaData; use sov_rollup_interface::services::da::DaService; @@ -28,7 +28,7 @@ where { thread_pool: rayon::ThreadPool, - proof_mode: Arc>>, + proof_mode: ProofGenMode, da_service: Arc, vm: Vm, @@ -69,9 +69,18 @@ where ProofGenMode::Execute => { tracing::info!("Prover is configured to execute proving"); } - ProofGenMode::Prove => { + ProofGenMode::ProveWithSampling => { tracing::info!("Prover is configured to prove"); } + ProofGenMode::ProveWithSamplingWithFakeProofs(proof_sampling_number) => { + if proof_sampling_number == 0 { + tracing::info!("Prover is configured to always prove"); + } else { + tracing::info!( + "Prover is configured to prove with fake proofs with 1/{proof_sampling_number} sampling" + ); + } + } }; let thread_pool = rayon::ThreadPoolBuilder::new() @@ -81,7 +90,7 @@ where Ok(Self { thread_pool, - proof_mode: Arc::new(Mutex::new(proof_mode)), + proof_mode, da_service, vm, zk_storage, @@ -203,7 +212,7 @@ where async fn prove(&self, elf: Vec) -> anyhow::Result> { let mut proof_queue = self.proof_queue.lock().await; - if let ProofGenMode::Skip = *self.proof_mode.lock().await { + if let ProofGenMode::Skip = self.proof_mode { tracing::debug!("Skipped proving {} proofs", proof_queue.len()); proof_queue.clear(); return Ok(vec![]); @@ -247,7 +256,7 @@ fn make_proof( mut vm: Vm, elf: Vec, zk_storage: Stf::PreState, - proof_mode: Arc>>, + proof_mode: ProofGenMode, ) -> Result where Da: DaService, @@ -255,21 +264,35 @@ where Stf: StateTransitionFunction + Send + Sync, Stf::PreState: Send + Sync, { - let mut proof_mode = proof_mode.blocking_lock(); - match proof_mode.deref_mut() { + match proof_mode { ProofGenMode::Skip => Ok(Vec::default()), - ProofGenMode::Simulate(ref mut verifier) => verifier - .run_sequencer_commitments_in_da_slot(vm.simulate_with_hints(), zk_storage) - .map(|_| Vec::default()) - .map_err(|e| anyhow::anyhow!("Guest execution must succeed but failed with {:?}", e)), - // If not skip or simulate, we have to drop the lock manually to allow parallel proving + ProofGenMode::Simulate(verifier) => { + let mut verifier = verifier.blocking_lock(); + verifier + .run_sequencer_commitments_in_da_slot(vm.simulate_with_hints(), zk_storage) + .map(|_| Vec::default()) + .map_err(|e| { + anyhow::anyhow!("Guest execution must succeed but failed with {:?}", e) + }) + } ProofGenMode::Execute => { drop(proof_mode); vm.run(elf, false) } - ProofGenMode::Prove => { + ProofGenMode::ProveWithSampling => { drop(proof_mode); + // `make_proof` is called with a probability in this case. + // When it's called, we have to produce a real proof. vm.run(elf, true) } + ProofGenMode::ProveWithSamplingWithFakeProofs(proof_sampling_number) => { + drop(proof_mode); + // `make_proof` is called unconditionally in this case. + // When it's called, we have to calculate the probabiliry for a proof + // and produce a real proof if we are lucky. If unlucky - produce a fake proof. + let with_prove = proof_sampling_number == 0 + || rand::thread_rng().gen_range(0..proof_sampling_number) == 0; + vm.run(elf, with_prove) + } } } diff --git a/crates/sovereign-sdk/full-node/db/sov-db/src/ledger_db/mod.rs b/crates/sovereign-sdk/full-node/db/sov-db/src/ledger_db/mod.rs index f5438f4f8..b42a82ffe 100644 --- a/crates/sovereign-sdk/full-node/db/sov-db/src/ledger_db/mod.rs +++ b/crates/sovereign-sdk/full-node/db/sov-db/src/ledger_db/mod.rs @@ -349,9 +349,13 @@ impl SharedLedgerOps for LedgerDB { match commitments { // If there were other commitments, upsert Some(mut commitments) => { - commitments.push(commitment); - self.db - .put::(&SlotNumber(height), &commitments) + if !commitments.contains(&commitment) { + commitments.push(commitment); + self.db + .put::(&SlotNumber(height), &commitments) + } else { + Ok(()) + } } // Else insert None => self diff --git a/crates/sovereign-sdk/full-node/sov-stf-runner/src/prover_service/mod.rs b/crates/sovereign-sdk/full-node/sov-stf-runner/src/prover_service/mod.rs index 224418904..6ff15c577 100644 --- a/crates/sovereign-sdk/full-node/sov-stf-runner/src/prover_service/mod.rs +++ b/crates/sovereign-sdk/full-node/sov-stf-runner/src/prover_service/mod.rs @@ -16,6 +16,8 @@ pub enum ProverGuestRunConfig { Execute, /// Run the rollup verifier and create a SNARK of execution. Prove, + /// Run the rollup verifier and create a SNARK or a fake proof of execution. + ProveWithFakeProofs, } impl<'de> Deserialize<'de> for ProverGuestRunConfig { @@ -29,6 +31,7 @@ impl<'de> Deserialize<'de> for ProverGuestRunConfig { "simulate" => Ok(ProverGuestRunConfig::Simulate), "execute" => Ok(ProverGuestRunConfig::Execute), "prove" => Ok(ProverGuestRunConfig::Prove), + "prove-with-fakes" => Ok(ProverGuestRunConfig::ProveWithFakeProofs), _ => Err(serde::de::Error::custom("invalid prover guest run config")), } } diff --git a/crates/sovereign-sdk/module-system/sov-modules-rollup-blueprint/src/lib.rs b/crates/sovereign-sdk/module-system/sov-modules-rollup-blueprint/src/lib.rs index 67896e73e..56b6f6917 100644 --- a/crates/sovereign-sdk/module-system/sov-modules-rollup-blueprint/src/lib.rs +++ b/crates/sovereign-sdk/module-system/sov-modules-rollup-blueprint/src/lib.rs @@ -146,6 +146,7 @@ pub trait RollupBlueprint: Sized + Send + Sync { da_service: &Arc, da_verifier: Self::DaVerifier, ledger_db: LedgerDB, + proof_sampling_number: usize, ) -> Self::ProverService; /// Creates instance of [`Self::StorageManager`].