Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Always prove to produce proofs for BatchProofs #1540

Open
wants to merge 10 commits into
base: nightly
Choose a base branch
from
10 changes: 7 additions & 3 deletions bin/citrea/src/rollup/bitcoin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::{
Expand Down Expand Up @@ -246,6 +246,7 @@ impl RollupBlueprint for BitcoinRollup {
da_service: &Arc<Self::DaService>,
da_verifier: Self::DaVerifier,
ledger_db: LedgerDB,
proof_sampling_number: usize,
) -> Self::ProverService {
let vm = Risc0BonsaiHost::new(ledger_db.clone());
// let vm = SP1Host::new(
Expand All @@ -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(
Expand Down
10 changes: 7 additions & 3 deletions bin/citrea/src/rollup/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -179,6 +179,7 @@ impl RollupBlueprint for MockDemoRollup {
da_service: &Arc<Self::DaService>,
da_verifier: Self::DaVerifier,
ledger_db: LedgerDB,
proof_sampling_number: usize,
) -> Self::ProverService {
let vm = Risc0BonsaiHost::new(ledger_db.clone());

Expand All @@ -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)
Expand Down
2 changes: 2 additions & 0 deletions bin/citrea/src/rollup/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,7 @@ pub trait CitreaRollupBlueprint: RollupBlueprint {
&da_service,
da_verifier,
ledger_db.clone(),
prover_config.proof_sampling_number,
)
.await;

Expand Down Expand Up @@ -421,6 +422,7 @@ pub trait CitreaRollupBlueprint: RollupBlueprint {
&da_service,
da_verifier,
ledger_db.clone(),
prover_config.proof_sampling_number,
)
.await;

Expand Down
19 changes: 13 additions & 6 deletions crates/batch-prover/src/da_block_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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::<Da, Ps, Vm, DB, StateRoot, Witness, Tx>(
Expand Down
39 changes: 37 additions & 2 deletions crates/prover-services/src/lib.rs
Original file line number Diff line number Diff line change
@@ -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<Stf, DaVerifier, VmGuest> =
Arc<Mutex<StateTransitionVerifier<Stf, DaVerifier, VmGuest>>>;

pub enum ProofGenMode<Da, Vm, Stf>
where
Da: DaService,
Expand All @@ -15,10 +21,39 @@ where
/// Skips proving.
Skip,
/// The simulator runs the rollup verifier logic without even emulating the zkVM
Simulate(StateTransitionVerifier<Stf, Da::Verifier, Vm::Guest>),
Simulate(Simulator<Stf, Da::Verifier, Vm::Guest>),
yaziciahmet marked this conversation as resolved.
Show resolved Hide resolved
/// 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<Da, Vm, Stf> Clone for ProofGenMode<Da, Vm, Stf>
where
Da: DaService,
Vm: ZkvmHost,
Stf: StateTransitionFunction<Da::Spec>,
{
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)),
}
}
}
51 changes: 38 additions & 13 deletions crates/prover-services/src/parallel/mod.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -27,7 +27,7 @@ where
{
thread_pool: rayon::ThreadPool,

proof_mode: Arc<Mutex<ProofGenMode<Da, Vm, Stf>>>,
proof_mode: ProofGenMode<Da, Vm, Stf>,

da_service: Arc<Da>,
vm: Vm,
Expand Down Expand Up @@ -68,9 +68,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()
Expand All @@ -80,7 +89,7 @@ where

Ok(Self {
thread_pool,
proof_mode: Arc::new(Mutex::new(proof_mode)),
proof_mode,
da_service,
vm,
zk_storage,
Expand Down Expand Up @@ -190,7 +199,7 @@ where

async fn prove(&self, elf: Vec<u8>) -> anyhow::Result<Vec<Proof>> {
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![]);
Expand Down Expand Up @@ -234,22 +243,38 @@ fn make_proof<Da, Vm, Stf>(
mut vm: Vm,
elf: Vec<u8>,
zk_storage: Stf::PreState,
proof_mode: Arc<Mutex<ProofGenMode<Da, Vm, Stf>>>,
proof_mode: ProofGenMode<Da, Vm, Stf>,
) -> Result<Proof, anyhow::Error>
where
Da: DaService,
Vm: ZkvmHost,
Stf: StateTransitionFunction<Da::Spec> + 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)),
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 => vm.run(elf, false),
ProofGenMode::Prove => vm.run(elf, true),
ProofGenMode::ProveWithSampling => {
// `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) => {
// `make_proof` is called unconditionally in this case.
Copy link
Contributor

@yaziciahmet yaziciahmet Dec 6, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only nit I have is that ProveWithSampling is a bit confusing to me, because in the eyes of ProverService it is just Prove, and sampling happens above in the call stack in da_block_handler. So i think we should just call them Prove and ProveWithSampling respectively, so its obvious from the ProofGenMode whether ProverService should do any sampling itself.

I am not going to block the PR for this nit, but would love to see this change.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, what if instead of sampling on per proof basis for ProveWithSamplingWithFakeProofs, we sample per DA block. Meaning, we again decide the sampling on da_block_handler level, but what we decide is to whether proofs in current DA should be run in execute or prove mode. It would simplify the flow quite a bit. Just throwing this out as a thought. WDYT?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We talked with Esad, the approach doesn't matter much in this PR. We may open a new issue as a followup

// 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)
}
}
}
10 changes: 7 additions & 3 deletions crates/sovereign-sdk/full-node/db/sov-db/src/ledger_db/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::<CommitmentsByNumber>(&SlotNumber(height), &commitments)
if !commitments.contains(&commitment) {
yaziciahmet marked this conversation as resolved.
Show resolved Hide resolved
commitments.push(commitment);
self.db
.put::<CommitmentsByNumber>(&SlotNumber(height), &commitments)
} else {
Ok(())
}
}
// Else insert
None => self
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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),
yaziciahmet marked this conversation as resolved.
Show resolved Hide resolved
_ => Err(serde::de::Error::custom("invalid prover guest run config")),
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ pub trait RollupBlueprint: Sized + Send + Sync {
da_service: &Arc<Self::DaService>,
da_verifier: Self::DaVerifier,
ledger_db: LedgerDB,
proof_sampling_number: usize,
) -> Self::ProverService;

/// Creates instance of [`Self::StorageManager`].
Expand Down
Loading