From 6a47763fd7a095046e9c5ac1da5ce77c87ca72d4 Mon Sep 17 00:00:00 2001 From: Abishek Bashyal Date: Tue, 7 Jan 2025 10:15:22 +0545 Subject: [PATCH] L1-Scan guest code to prove range of N blocks (#541) * sync with main * l1_scan & l1_batch -> l1_batch * l1 scan fix * l1 batch test fix * lint fix * fix doc string --- Cargo.lock | 1 + bin/prover-client/src/hosts/native.rs | 2 +- bin/prover-client/src/operators/btc.rs | 32 ++-- bin/prover-client/src/operators/checkpoint.rs | 18 +- bin/prover-client/src/operators/l1_batch.rs | 162 +++++++++--------- bin/prover-client/src/operators/operator.rs | 5 +- bin/prover-client/src/rpc_server.rs | 8 +- crates/primitives/src/proof.rs | 2 +- crates/proof-impl/btc-blockspace/src/lib.rs | 1 + crates/proof-impl/btc-blockspace/src/logic.rs | 62 ++----- .../proof-impl/btc-blockspace/src/prover.rs | 6 +- crates/proof-impl/btc-blockspace/src/scan.rs | 37 ++++ crates/proof-impl/l1-batch/src/logic.rs | 33 ++-- crates/proof-impl/l1-batch/src/prover.rs | 27 ++- crates/rpc/prover-client-api/Cargo.toml | 1 + crates/rpc/prover-client-api/src/lib.rs | 5 +- crates/zkvm/hosts/src/native.rs | 2 +- .../tests/prover_l1_batch_dispatch.py | 29 +++- functional-tests/tests/prover_l1_dispatch.py | 12 +- functional-tests/utils/utils.py | 5 + provers/risc0/guest-l1-batch/src/main.rs | 6 +- provers/sp1/build.rs | 1 - provers/sp1/guest-l1-batch/src/main.rs | 4 +- provers/tests/src/btc.rs | 7 +- provers/tests/src/generators.rs | 3 +- provers/tests/src/l1_batch.rs | 28 ++- 26 files changed, 257 insertions(+), 242 deletions(-) create mode 100644 crates/proof-impl/btc-blockspace/src/scan.rs diff --git a/Cargo.lock b/Cargo.lock index aeaaeddec..6a5e6a75d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -13492,6 +13492,7 @@ dependencies = [ "jsonrpsee", "strata-primitives", "strata-rpc-types", + "strata-state", ] [[package]] diff --git a/bin/prover-client/src/hosts/native.rs b/bin/prover-client/src/hosts/native.rs index 86aa10a13..b7d823466 100644 --- a/bin/prover-client/src/hosts/native.rs +++ b/bin/prover-client/src/hosts/native.rs @@ -30,7 +30,7 @@ pub fn get_host(id: &ProofContext) -> NativeHost { }, ProofContext::L1Batch(..) => NativeHost { process_proof: Arc::new(Box::new(move |zkvm: &NativeMachine| { - process_l1_batch_proof(zkvm, &MOCK_VK); + process_l1_batch_proof(zkvm); Ok(()) })), }, diff --git a/bin/prover-client/src/operators/btc.rs b/bin/prover-client/src/operators/btc.rs index 4707a14b4..94973d89e 100644 --- a/bin/prover-client/src/operators/btc.rs +++ b/bin/prover-client/src/operators/btc.rs @@ -5,7 +5,7 @@ use strata_primitives::{ params::RollupParams, proof::{ProofContext, ProofKey}, }; -use strata_proofimpl_btc_blockspace::{logic::BlockspaceProofInput, prover::BtcBlockspaceProver}; +use strata_proofimpl_btc_blockspace::{logic::BlockScanProofInput, prover::BtcBlockspaceProver}; use strata_rocksdb::prover::db::ProofDb; use strata_state::l1::L1BlockId; use tokio::sync::Mutex; @@ -32,30 +32,19 @@ impl BtcBlockspaceOperator { rollup_params, } } - - /// Retrieves the [`L1BlockId`] for a given block number. - pub async fn get_id(&self, block_num: u64) -> Result { - Ok(self - .btc_client - .get_block_hash(block_num) - .await - .inspect_err(|_| error!(%block_num, "Failed to fetch BTC BlockId")) - .map_err(|e| ProvingTaskError::RpcError(e.to_string()))? - .into()) - } } impl ProvingOp for BtcBlockspaceOperator { type Prover = BtcBlockspaceProver; - type Params = u64; + type Params = L1BlockId; async fn create_task( &self, - block_num: u64, + block_id: Self::Params, task_tracker: Arc>, _db: &ProofDb, ) -> Result, ProvingTaskError> { - let context = ProofContext::BtcBlockspace(self.get_id(block_num).await?); + let context = ProofContext::BtcBlockspace(block_id); let mut task_tracker = task_tracker.lock().await; task_tracker.create_tasks(context, vec![]) } @@ -64,15 +53,20 @@ impl ProvingOp for BtcBlockspaceOperator { &self, task_id: &ProofKey, _db: &ProofDb, - ) -> Result { - let blkid = match task_id.context() { + ) -> Result { + let block_id = match task_id.context() { ProofContext::BtcBlockspace(id) => *id, _ => return Err(ProvingTaskError::InvalidInput("BtcBlockspace".to_string())), }; - let block = self.btc_client.get_block(&blkid.into()).await.unwrap(); + let block = self + .btc_client + .get_block(&block_id.into()) + .await + .inspect_err(|_| error!(%block_id, "Failed to fetch BTC BlockId")) + .map_err(|e| ProvingTaskError::RpcError(e.to_string()))?; - Ok(BlockspaceProofInput { + Ok(BlockScanProofInput { rollup_params: self.rollup_params.as_ref().clone(), block, }) diff --git a/bin/prover-client/src/operators/checkpoint.rs b/bin/prover-client/src/operators/checkpoint.rs index 85a6b796a..e8d621729 100644 --- a/bin/prover-client/src/operators/checkpoint.rs +++ b/bin/prover-client/src/operators/checkpoint.rs @@ -111,9 +111,25 @@ impl ProvingOp for CheckpointOperator { let ckp_proof_id = ProofContext::Checkpoint(ckp_idx); + // Doing the manual block idx to id transformation. Will be removed once checkpoint_info + // include the range in terms of block_id. + // https://alpenlabs.atlassian.net/browse/STR-756 + let start_l1_block_id = self + .l1_batch_operator + .get_block_at(checkpoint_info.l1_range.0) + .await?; + let end_l1_block_id = self + .l1_batch_operator + .get_block_at(checkpoint_info.l1_range.1) + .await?; + let l1_batch_keys = self .l1_batch_operator - .create_task(checkpoint_info.l1_range, task_tracker.clone(), db) + .create_task( + (start_l1_block_id, end_l1_block_id), + task_tracker.clone(), + db, + ) .await?; let l1_batch_id = l1_batch_keys .first() diff --git a/bin/prover-client/src/operators/l1_batch.rs b/bin/prover-client/src/operators/l1_batch.rs index eb30e7490..b271e652f 100644 --- a/bin/prover-client/src/operators/l1_batch.rs +++ b/bin/prover-client/src/operators/l1_batch.rs @@ -1,6 +1,6 @@ use std::sync::Arc; -use bitcoin::params::MAINNET; +use bitcoin::{params::MAINNET, Block}; use strata_btcio::{ reader::query::get_verification_state, rpc::{ @@ -8,121 +8,126 @@ use strata_btcio::{ BitcoinClient, }, }; -use strata_db::traits::ProofDatabase; -use strata_primitives::proof::{ProofContext, ProofKey}; +use strata_primitives::{ + params::RollupParams, + proof::{ProofContext, ProofKey}, +}; use strata_proofimpl_l1_batch::{L1BatchProofInput, L1BatchProver}; use strata_rocksdb::prover::db::ProofDb; +use strata_state::l1::L1BlockId; use tokio::sync::Mutex; use tracing::error; -use super::{btc::BtcBlockspaceOperator, ProvingOp}; -use crate::{errors::ProvingTaskError, hosts, task_tracker::TaskTracker}; +use super::ProvingOp; +use crate::{errors::ProvingTaskError, task_tracker::TaskTracker}; /// A struct that implements the [`ProvingOp`] trait for L1 Batch Proof generation. /// -/// It is responsible for managing the data and tasks required to generate proofs for L1 Batch. It +/// It is responsible for managing the data to generate proofs for L1 Batch. It /// fetches the necessary inputs for the [`L1BatchProver`] by: -/// -/// - Utilizing the [`BtcBlockspaceOperator`] to create and manage proving tasks for BTC Blockspace. -/// The resulting BTC Blockspace proofs are incorporated as part of the input for the CL STF -/// proof. +/// - Fetching the Bitcoin blocks and verification state for the given block range. /// - Interfacing with the Bitcoin Client to fetch additional required information for batch proofs. #[derive(Debug, Clone)] pub struct L1BatchOperator { btc_client: Arc, - btc_blockspace_operator: Arc, + rollup_params: Arc, } impl L1BatchOperator { - pub fn new( - btc_client: Arc, - btc_blockspace_operator: Arc, - ) -> Self { + pub fn new(btc_client: Arc, rollup_params: Arc) -> Self { Self { btc_client, - btc_blockspace_operator, + rollup_params, } } + + async fn get_block(&self, block_id: L1BlockId) -> Result { + self.btc_client + .get_block(&block_id.into()) + .await + .inspect_err(|_| error!(%block_id, "Failed to fetch BTC block")) + .map_err(|e| ProvingTaskError::RpcError(e.to_string())) + } + + async fn get_block_height(&self, block_id: L1BlockId) -> Result { + let block = self.get_block(block_id).await?; + + let block_height = self + .btc_client + .get_transaction(&block.coinbase().expect("expect coinbase tx").compute_txid()) + .await + .map_err(|e| ProvingTaskError::RpcError(e.to_string()))? + .block_height(); + + Ok(block_height) + } + + /// Retrieves the specified number of ancestor block IDs for the given block ID. + async fn get_block_ancestors( + &self, + block_id: L1BlockId, + n_ancestors: u64, + ) -> Result, ProvingTaskError> { + let mut ancestors = Vec::with_capacity(n_ancestors as usize); + let mut block_id = block_id; + for _ in 0..=n_ancestors { + let block = self.get_block(block_id).await?; + block_id = block.header.prev_blockhash.into(); + ancestors.push(block); + } + + Ok(ancestors) + } + + /// Retrieves the block ID at the specified height. + /// + /// Note: This function will be removed once checkpoint_info includes the block ID range. + /// Currently, it requires a manual L1 block index-to-ID conversion by the checkpoint operator. + // https://alpenlabs.atlassian.net/browse/STR-756 + pub async fn get_block_at(&self, height: u64) -> Result { + let block_hash = self + .btc_client + .get_block_hash(height) + .await + .map_err(|e| ProvingTaskError::RpcError(e.to_string()))?; + Ok(block_hash.into()) + } } impl ProvingOp for L1BatchOperator { type Prover = L1BatchProver; - type Params = (u64, u64); + type Params = (L1BlockId, L1BlockId); async fn create_task( &self, - params: (u64, u64), + params: Self::Params, task_tracker: Arc>, - db: &ProofDb, + _db: &ProofDb, ) -> Result, ProvingTaskError> { - let (start_height, end_height) = params; - - let len = (end_height - start_height) as usize + 1; - let mut btc_deps = Vec::with_capacity(len); - - let start_blkid = self.btc_blockspace_operator.get_id(start_height).await?; - let end_blkid = self.btc_blockspace_operator.get_id(end_height).await?; + let (start_blkid, end_blkid) = params; let l1_batch_proof_id = ProofContext::L1Batch(start_blkid, end_blkid); - for height in start_height..=end_height { - let blkid = self.btc_blockspace_operator.get_id(height).await?; - let proof_id = ProofContext::BtcBlockspace(blkid); - self.btc_blockspace_operator - .create_task(height, task_tracker.clone(), db) - .await?; - btc_deps.push(proof_id); - } - - db.put_proof_deps(l1_batch_proof_id, btc_deps.clone()) - .map_err(ProvingTaskError::DatabaseError)?; - let mut task_tracker = task_tracker.lock().await; - task_tracker.create_tasks(l1_batch_proof_id, btc_deps) + task_tracker.create_tasks(l1_batch_proof_id, vec![]) } async fn fetch_input( &self, task_id: &ProofKey, - db: &ProofDb, + _db: &ProofDb, ) -> Result { - let (start_blkid, _) = match task_id.context() { - ProofContext::L1Batch(start, end) => (*start, end), + let (start_block_id, end_block_id) = match task_id.context() { + ProofContext::L1Batch(start, end) => (*start, *end), _ => return Err(ProvingTaskError::InvalidInput("L1Batch".to_string())), }; - let deps = db - .get_proof_deps(*task_id.context()) - .map_err(ProvingTaskError::DatabaseError)? - .ok_or(ProvingTaskError::DependencyNotFound(*task_id))?; - - let mut batch = Vec::new(); - for proof_id in deps { - let proof_key = ProofKey::new(proof_id, *task_id.host()); - let proof = db - .get_proof(proof_key) - .map_err(ProvingTaskError::DatabaseError)? - .ok_or(ProvingTaskError::ProofNotFound(proof_key))?; - batch.push(proof); - } + let start_height = self.get_block_height(start_block_id).await?; + let end_height = self.get_block_height(end_block_id).await?; + let num_blocks = end_height - start_height; - let start_block = self - .btc_client - .get_block(&start_blkid.into()) - .await - .inspect_err(|_| error!(%start_blkid, "Failed to fetch BTC block")) - .map_err(|e| ProvingTaskError::RpcError(e.to_string()))?; - - let start_height = self - .btc_client - .get_transaction( - &start_block - .coinbase() - .expect("expect coinbase tx") - .compute_txid(), - ) - .await - .map_err(|e| ProvingTaskError::RpcError(e.to_string()))? - .block_height(); + // Get ancestor blocks and reverse to oldest-first order + let mut blocks = self.get_block_ancestors(end_block_id, num_blocks).await?; + blocks.reverse(); let state = get_verification_state( self.btc_client.as_ref(), @@ -132,15 +137,10 @@ impl ProvingOp for L1BatchOperator { .await .map_err(|e| ProvingTaskError::RpcError(e.to_string()))?; - let blockspace_vk = hosts::get_verification_key(&ProofKey::new( - ProofContext::BtcBlockspace(start_blkid), - *task_id.host(), - )); - Ok(L1BatchProofInput { - batch, + blocks, state, - blockspace_vk, + rollup_params: self.rollup_params.as_ref().clone(), }) } } diff --git a/bin/prover-client/src/operators/operator.rs b/bin/prover-client/src/operators/operator.rs index d31b22f76..50268d685 100644 --- a/bin/prover-client/src/operators/operator.rs +++ b/bin/prover-client/src/operators/operator.rs @@ -64,10 +64,7 @@ impl ProofOperator { // Create each operator using the respective clients. let btc_blockspace_operator = BtcBlockspaceOperator::new(btc_client.clone(), rollup_params.clone()); - let l1_batch_operator = L1BatchOperator::new( - btc_client.clone(), - Arc::new(btc_blockspace_operator.clone()), - ); + let l1_batch_operator = L1BatchOperator::new(btc_client.clone(), rollup_params.clone()); let evm_ee_operator = EvmEeOperator::new(evm_ee_client.clone()); let cl_stf_operator = ClStfOperator::new( cl_client.clone(), diff --git a/bin/prover-client/src/rpc_server.rs b/bin/prover-client/src/rpc_server.rs index 1939b42a2..628001e38 100644 --- a/bin/prover-client/src/rpc_server.rs +++ b/bin/prover-client/src/rpc_server.rs @@ -9,7 +9,7 @@ use strata_primitives::buf::Buf32; use strata_prover_client_rpc_api::StrataProverClientApiServer; use strata_rocksdb::prover::db::ProofDb; use strata_rpc_types::ProofKey; -use strata_state::id::L2BlockId; +use strata_state::{id::L2BlockId, l1::L1BlockId}; use tokio::sync::{oneshot, Mutex}; use tracing::{info, warn}; @@ -80,11 +80,11 @@ impl ProverClientRpc { #[async_trait] impl StrataProverClientApiServer for ProverClientRpc { - async fn prove_btc_block(&self, btc_block_num: u64) -> RpcResult> { + async fn prove_btc_block(&self, block_id: L1BlockId) -> RpcResult> { Ok(self .operator .btc_operator() - .create_task(btc_block_num, self.task_tracker.clone(), &self.db) + .create_task(block_id, self.task_tracker.clone(), &self.db) .await .expect("failed to create task")) } @@ -110,7 +110,7 @@ impl StrataProverClientApiServer for ProverClientRpc { .expect("failed to create task")) } - async fn prove_l1_batch(&self, l1_range: (u64, u64)) -> RpcResult> { + async fn prove_l1_batch(&self, l1_range: (L1BlockId, L1BlockId)) -> RpcResult> { Ok(self .operator .l1_batch_operator() diff --git a/crates/primitives/src/proof.rs b/crates/primitives/src/proof.rs index 388bdfad4..b7bea3030 100644 --- a/crates/primitives/src/proof.rs +++ b/crates/primitives/src/proof.rs @@ -48,7 +48,7 @@ pub enum RollupVerifyingKey { Deserialize, )] pub enum ProofContext { - /// Identifier for the L1 block height used in a Bitcoin blockspace proof. + /// Identifier representing a Bitcoin L1 block for blockscan proof BtcBlockspace(L1BlockId), /// Identifier for a batch of L1 blocks being proven. diff --git a/crates/proof-impl/btc-blockspace/src/lib.rs b/crates/proof-impl/btc-blockspace/src/lib.rs index c65eed287..ab8859192 100644 --- a/crates/proof-impl/btc-blockspace/src/lib.rs +++ b/crates/proof-impl/btc-blockspace/src/lib.rs @@ -5,4 +5,5 @@ pub mod filter; pub mod logic; pub mod merkle; pub mod prover; +pub mod scan; pub mod tx; diff --git a/crates/proof-impl/btc-blockspace/src/logic.rs b/crates/proof-impl/btc-blockspace/src/logic.rs index 67c06be8f..f1533af94 100644 --- a/crates/proof-impl/btc-blockspace/src/logic.rs +++ b/crates/proof-impl/btc-blockspace/src/logic.rs @@ -2,72 +2,32 @@ use bitcoin::{consensus::deserialize, Block}; use borsh::{BorshDeserialize, BorshSerialize}; -use strata_primitives::{buf::Buf32, params::RollupParams}; +use strata_primitives::params::RollupParams; use strata_state::{batch::BatchCheckpoint, tx::DepositInfo}; use strata_zkvm::ZkVmEnv; -use crate::{block::check_merkle_root, filter::extract_relevant_info}; - -#[derive(Debug)] -pub struct BlockspaceProofInput { - pub block: Block, - pub rollup_params: RollupParams, - // TODO: add hintings and other necessary params -} +use crate::scan::process_blockscan; +/// Defines the result of scanning an L1 block. +/// Includes protocol-relevant data posted on L1 block. #[derive(Debug, BorshSerialize, BorshDeserialize)] -pub struct BlockspaceProofOutput { +pub struct BlockScanResult { pub header_raw: Vec, pub deposits: Vec, pub prev_checkpoint: Option, - pub rollup_params_commitment: Buf32, } -pub fn process_blockspace_proof(input: &BlockspaceProofInput) -> BlockspaceProofOutput { - let BlockspaceProofInput { - block, - rollup_params, - } = input; - assert!(check_merkle_root(block)); - // assert!(check_witness_commitment(block)); - - let (deposits, prev_checkpoint) = extract_relevant_info(block, rollup_params); - let rollup_params_commitment = rollup_params.compute_hash(); - - BlockspaceProofOutput { - header_raw: bitcoin::consensus::serialize(&block.header), - deposits, - prev_checkpoint, - rollup_params_commitment, - } +/// Represents the input data required for generating an L1Scan proof. +#[derive(Debug)] +pub struct BlockScanProofInput { + pub block: Block, + pub rollup_params: RollupParams, } pub fn process_blockspace_proof_outer(zkvm: &impl ZkVmEnv) { let rollup_params: RollupParams = zkvm.read_serde(); let serialized_block = zkvm.read_buf(); let block: Block = deserialize(&serialized_block).unwrap(); - let input = BlockspaceProofInput { - block, - rollup_params, - }; - let output = process_blockspace_proof(&input); + let output = process_blockscan(&block, &rollup_params); zkvm.commit_borsh(&output); } - -#[cfg(test)] -mod tests { - use strata_test_utils::{bitcoin::get_btc_chain, l2::gen_params}; - - use super::{process_blockspace_proof, BlockspaceProofInput}; - #[test] - fn test_process_blockspace_proof() { - let params = gen_params(); - let rollup_params = params.rollup(); - let btc_block = get_btc_chain().get_block(40321).clone(); - let input = BlockspaceProofInput { - block: btc_block, - rollup_params: rollup_params.clone(), - }; - let _ = process_blockspace_proof(&input); - } -} diff --git a/crates/proof-impl/btc-blockspace/src/prover.rs b/crates/proof-impl/btc-blockspace/src/prover.rs index c598dd882..d1d2cce51 100644 --- a/crates/proof-impl/btc-blockspace/src/prover.rs +++ b/crates/proof-impl/btc-blockspace/src/prover.rs @@ -3,13 +3,13 @@ use strata_zkvm::{ ProofType, PublicValues, ZkVmHost, ZkVmInputBuilder, ZkVmInputResult, ZkVmProver, ZkVmResult, }; -use crate::logic::{BlockspaceProofInput, BlockspaceProofOutput}; +use crate::logic::{BlockScanProofInput, BlockScanResult}; pub struct BtcBlockspaceProver; impl ZkVmProver for BtcBlockspaceProver { - type Input = BlockspaceProofInput; - type Output = BlockspaceProofOutput; + type Input = BlockScanProofInput; + type Output = BlockScanResult; fn proof_type() -> ProofType { ProofType::Compressed diff --git a/crates/proof-impl/btc-blockspace/src/scan.rs b/crates/proof-impl/btc-blockspace/src/scan.rs new file mode 100644 index 000000000..2edfc8837 --- /dev/null +++ b/crates/proof-impl/btc-blockspace/src/scan.rs @@ -0,0 +1,37 @@ +//! This module contains the logic to process a blockscan and extract relevant information from it. + +use bitcoin::{consensus::serialize, Block}; +use strata_primitives::params::RollupParams; + +use crate::{block::check_merkle_root, filter::extract_relevant_info, logic::BlockScanResult}; + +/// Scans a Bitcoin block to extract rollup-relevant data, including checkpoints and deposits. +pub fn process_blockscan(block: &Block, rollup_params: &RollupParams) -> BlockScanResult { + assert!(check_merkle_root(block)); + + // TODO: Assert witness commitment check + // https://alpenlabs.atlassian.net/browse/STR-758 + // assert!(check_witness_commitment(block)); + + let (deposits, prev_checkpoint) = extract_relevant_info(block, rollup_params); + + BlockScanResult { + header_raw: serialize(&block.header), + deposits, + prev_checkpoint, + } +} + +#[cfg(test)] +mod tests { + use strata_test_utils::{bitcoin::get_btc_chain, l2::gen_params}; + + use super::process_blockscan; + #[test] + fn test_process_blockspace_proof() { + let params = gen_params(); + let rollup_params = params.rollup(); + let btc_block = get_btc_chain().get_block(40321).clone(); + let _ = process_blockscan(&btc_block, rollup_params); + } +} diff --git a/crates/proof-impl/l1-batch/src/logic.rs b/crates/proof-impl/l1-batch/src/logic.rs index 204801ee6..4e02037e0 100644 --- a/crates/proof-impl/l1-batch/src/logic.rs +++ b/crates/proof-impl/l1-batch/src/logic.rs @@ -1,7 +1,7 @@ -use bitcoin::{block::Header, consensus::deserialize}; +use bitcoin::{consensus::deserialize, Block}; use borsh::{BorshDeserialize, BorshSerialize}; -use strata_primitives::buf::Buf32; -use strata_proofimpl_btc_blockspace::logic::BlockspaceProofOutput; +use strata_primitives::{buf::Buf32, params::RollupParams}; +use strata_proofimpl_btc_blockspace::scan::process_blockscan; use strata_state::{ batch::BatchCheckpoint, l1::{get_btc_params, HeaderVerificationState, HeaderVerificationStateSnapshot}, @@ -9,6 +9,7 @@ use strata_state::{ }; use strata_zkvm::ZkVmEnv; +/// Represents the public parameters of the L1BlockScan batch proof. #[derive(Debug, BorshSerialize, BorshDeserialize)] pub struct L1BatchProofOutput { pub deposits: Vec, @@ -24,32 +25,26 @@ impl L1BatchProofOutput { } } -pub fn process_l1_batch_proof(zkvm: &impl ZkVmEnv, btc_blockspace_vk: &[u32; 8]) { +pub fn process_l1_batch_proof(zkvm: &impl ZkVmEnv) { let mut state: HeaderVerificationState = zkvm.read_borsh(); + let rollup_params: RollupParams = zkvm.read_serde(); let num_inputs: u32 = zkvm.read_serde(); assert!(num_inputs > 0); let initial_snapshot = state.compute_snapshot(); let mut deposits = Vec::new(); let mut prev_checkpoint = None; - let mut rollup_params_commitment = None; for _ in 0..num_inputs { - let blkpo: BlockspaceProofOutput = zkvm.read_verified_borsh(btc_blockspace_vk); - let header: Header = deserialize(&blkpo.header_raw).unwrap(); - - state.check_and_update_continuity(&header, &get_btc_params()); - deposits.extend(blkpo.deposits); - prev_checkpoint = prev_checkpoint.or(blkpo.prev_checkpoint); - - // Ensure that the rollup parameters used are same for all blocks - if let Some(filters_comm) = rollup_params_commitment { - assert_eq!(blkpo.rollup_params_commitment, filters_comm); - } else { - rollup_params_commitment = Some(blkpo.rollup_params_commitment); - } + let serialized_block = zkvm.read_buf(); + let block: Block = deserialize(&serialized_block).unwrap(); + let blockscan_result = process_blockscan(&block, &rollup_params); + state.check_and_update_continuity(&block.header, &get_btc_params()); + deposits.extend(blockscan_result.deposits); + prev_checkpoint = prev_checkpoint.or(blockscan_result.prev_checkpoint); } + let final_snapshot = state.compute_snapshot(); let output = L1BatchProofOutput { @@ -57,7 +52,7 @@ pub fn process_l1_batch_proof(zkvm: &impl ZkVmEnv, btc_blockspace_vk: &[u32; 8]) prev_checkpoint, initial_snapshot, final_snapshot, - rollup_params_commitment: rollup_params_commitment.unwrap(), + rollup_params_commitment: rollup_params.compute_hash(), }; zkvm.commit_borsh(&output); diff --git a/crates/proof-impl/l1-batch/src/prover.rs b/crates/proof-impl/l1-batch/src/prover.rs index 93b9cb36e..d91657fa5 100644 --- a/crates/proof-impl/l1-batch/src/prover.rs +++ b/crates/proof-impl/l1-batch/src/prover.rs @@ -1,17 +1,16 @@ -use borsh::{BorshDeserialize, BorshSerialize}; +use bitcoin::{consensus::serialize, Block}; +use strata_primitives::params::RollupParams; use strata_state::l1::HeaderVerificationState; -use strata_zkvm::{ - AggregationInput, ProofReceipt, PublicValues, VerificationKey, ZkVmInputResult, ZkVmProver, - ZkVmResult, -}; +use strata_zkvm::{PublicValues, ZkVmInputResult, ZkVmProver, ZkVmResult}; use crate::logic::L1BatchProofOutput; -#[derive(Debug, BorshSerialize, BorshDeserialize)] +#[derive(Debug)] +// #[derive(Debug, BorshSerialize, BorshDeserialize)] pub struct L1BatchProofInput { - pub batch: Vec, + pub blocks: Vec, pub state: HeaderVerificationState, - pub blockspace_vk: VerificationKey, + pub rollup_params: RollupParams, } pub struct L1BatchProver; @@ -30,15 +29,11 @@ impl ZkVmProver for L1BatchProver { { let mut input_builder = B::new(); input_builder.write_borsh(&input.state)?; + input_builder.write_serde(&input.rollup_params)?; - let len = input.batch.len() as u32; - input_builder.write_serde(&len)?; - - for proof in &input.batch { - input_builder.write_proof(&AggregationInput::new( - proof.clone(), - input.blockspace_vk.clone(), - ))?; + input_builder.write_serde(&input.blocks.len())?; + for block in &input.blocks { + input_builder.write_buf(&serialize(block))?; } input_builder.build() diff --git a/crates/rpc/prover-client-api/Cargo.toml b/crates/rpc/prover-client-api/Cargo.toml index c2a598019..3e491816e 100644 --- a/crates/rpc/prover-client-api/Cargo.toml +++ b/crates/rpc/prover-client-api/Cargo.toml @@ -15,6 +15,7 @@ rustdoc.all = "warn" [dependencies] strata-primitives.workspace = true strata-rpc-types.workspace = true +strata-state.workspace = true jsonrpsee = { workspace = true, features = ["server", "macros"] } diff --git a/crates/rpc/prover-client-api/src/lib.rs b/crates/rpc/prover-client-api/src/lib.rs index 99f546588..c6c5d1941 100644 --- a/crates/rpc/prover-client-api/src/lib.rs +++ b/crates/rpc/prover-client-api/src/lib.rs @@ -3,6 +3,7 @@ use jsonrpsee::{core::RpcResult, proc_macros::rpc}; use strata_primitives::{buf::Buf32, l2::L2BlockId}; use strata_rpc_types::ProofKey; +use strata_state::l1::L1BlockId; /// RPCs related to information about the client itself. #[cfg_attr(not(feature = "client"), rpc(server, namespace = "dev_strata"))] @@ -10,7 +11,7 @@ use strata_rpc_types::ProofKey; pub trait StrataProverClientApi { /// Start proving the given btc block #[method(name = "proveBtcBlock")] - async fn prove_btc_block(&self, el_block_num: u64) -> RpcResult>; + async fn prove_btc_block(&self, block_id: L1BlockId) -> RpcResult>; /// Start proving the given el block #[method(name = "proveElBlocks")] @@ -25,7 +26,7 @@ pub trait StrataProverClientApi { /// Start proving the given l1 Batch #[method(name = "proveL1Batch")] - async fn prove_l1_batch(&self, l1_range: (u64, u64)) -> RpcResult>; + async fn prove_l1_batch(&self, l1_range: (L1BlockId, L1BlockId)) -> RpcResult>; /// Start proving the given l2 batch #[method(name = "proveL2Batch")] diff --git a/crates/zkvm/hosts/src/native.rs b/crates/zkvm/hosts/src/native.rs index 155422741..753384d23 100644 --- a/crates/zkvm/hosts/src/native.rs +++ b/crates/zkvm/hosts/src/native.rs @@ -27,7 +27,7 @@ static BTC_BLOCKSPACE_HOST: LazyLock = std::sync::LazyLock::new(|| N /// A native host for [`ProofVm::L1Batch`] prover. static L1_BATCH_HOST: LazyLock = std::sync::LazyLock::new(|| NativeHost { process_proof: Arc::new(Box::new(move |zkvm: &NativeMachine| { - process_l1_batch_proof(zkvm, &MOCK_VK); + process_l1_batch_proof(zkvm); Ok(()) })), }); diff --git a/functional-tests/tests/prover_l1_batch_dispatch.py b/functional-tests/tests/prover_l1_batch_dispatch.py index 25bd7af9d..e22fdb97c 100644 --- a/functional-tests/tests/prover_l1_batch_dispatch.py +++ b/functional-tests/tests/prover_l1_batch_dispatch.py @@ -1,9 +1,16 @@ import time import flexitest +from bitcoinlib.services.bitcoind import BitcoindClient from envs import testenv -from utils import wait_for_proof_with_time_out +from utils import bytes_to_big_endian, wait_for_proof_with_time_out + +# Parameters defining therange of L1 blocks to be proven. +L1_PROVER_PARAMS = { + "START_BLOCK_HEIGHT": 1, + "END_BLOCK_HEIGHT": 3, +} @flexitest.register @@ -12,16 +19,26 @@ def __init__(self, ctx: flexitest.InitContext): ctx.set_env("prover") def main(self, ctx: flexitest.RunContext): + btc = ctx.get_service("bitcoin") prover_client = ctx.get_service("prover_client") + + btcrpc: BitcoindClient = btc.create_rpc() prover_client_rpc = prover_client.create_rpc() - # Wait for the some block building + # Allow time for blocks to build time.sleep(5) - task_ids = prover_client_rpc.dev_strata_proveL1Batch((1, 2)) + start_block_hash = bytes_to_big_endian( + btcrpc.proxy.getblockhash(L1_PROVER_PARAMS["START_BLOCK_HEIGHT"]) + ) + end_block_hash = bytes_to_big_endian( + btcrpc.proxy.getblockhash(L1_PROVER_PARAMS["END_BLOCK_HEIGHT"]) + ) + + task_ids = prover_client_rpc.dev_strata_proveL1Batch((start_block_hash, end_block_hash)) task_id = task_ids[0] - self.debug(f"using task id: {task_id}") + self.debug(f"Using task id: {task_id}") assert task_id is not None - time_out = 10 * 60 - wait_for_proof_with_time_out(prover_client_rpc, task_id, time_out=time_out) + proof_timeout_seconds = 10 * 60 + wait_for_proof_with_time_out(prover_client_rpc, task_id, time_out=proof_timeout_seconds) diff --git a/functional-tests/tests/prover_l1_dispatch.py b/functional-tests/tests/prover_l1_dispatch.py index bb9c1e5b7..2b22295a4 100644 --- a/functional-tests/tests/prover_l1_dispatch.py +++ b/functional-tests/tests/prover_l1_dispatch.py @@ -1,9 +1,10 @@ import time import flexitest +from bitcoinlib.services.bitcoind import BitcoindClient from envs import testenv -from utils import wait_for_proof_with_time_out +from utils import bytes_to_big_endian, wait_for_proof_with_time_out @flexitest.register @@ -12,14 +13,21 @@ def __init__(self, ctx: flexitest.InitContext): ctx.set_env("prover") def main(self, ctx: flexitest.RunContext): + btc = ctx.get_service("bitcoin") prover_client = ctx.get_service("prover_client") + + btcrpc: BitcoindClient = btc.create_rpc() prover_client_rpc = prover_client.create_rpc() # Wait for the Prover Manager setup time.sleep(5) # Dispatch the prover task - task_ids = prover_client_rpc.dev_strata_proveBtcBlock(1) + block_height = 1 + blockhash = btcrpc.proxy.getblockhash(block_height) + print(block_height, blockhash) + + task_ids = prover_client_rpc.dev_strata_proveBtcBlock(bytes_to_big_endian(blockhash)) self.debug(f"got task ids: {task_ids}") task_id = task_ids[0] self.debug(f"using task id: {task_id}") diff --git a/functional-tests/utils/utils.py b/functional-tests/utils/utils.py index 3b023393a..461ce16c3 100644 --- a/functional-tests/utils/utils.py +++ b/functional-tests/utils/utils.py @@ -518,3 +518,8 @@ def cl_slot_to_block_id(seqrpc, slot): def el_slot_to_block_id(rethrpc, block_num): """Get EL block hash from block number using Ethereum RPC.""" return rethrpc.eth_getBlockByNumber(hex(block_num), False)["hash"] + + +def bytes_to_big_endian(hash): + """Reverses the byte order of a hexadecimal string to produce big-endian format.""" + return "".join(reversed([hash[i : i + 2] for i in range(0, len(hash), 2)])) diff --git a/provers/risc0/guest-l1-batch/src/main.rs b/provers/risc0/guest-l1-batch/src/main.rs index dccd352a9..949171310 100644 --- a/provers/risc0/guest-l1-batch/src/main.rs +++ b/provers/risc0/guest-l1-batch/src/main.rs @@ -1,10 +1,6 @@ use strata_proofimpl_l1_batch::process_l1_batch_proof; use strata_risc0_adapter::Risc0ZkVmEnv; -// TODO: replace this with vks file that'll generated by build.rs script similar to how things are -// implemented for sp1-guest-builder -pub const GUEST_BTC_BLOCKSPACE_ELF_ID: &[u32; 8] = &[0, 0, 0, 0, 0, 0, 0, 0]; - fn main() { - process_l1_batch_proof(&Risc0ZkVmEnv, GUEST_BTC_BLOCKSPACE_ELF_ID); + process_l1_batch_proof(&Risc0ZkVmEnv); } diff --git a/provers/sp1/build.rs b/provers/sp1/build.rs index 84d50f7b7..0b474d409 100644 --- a/provers/sp1/build.rs +++ b/provers/sp1/build.rs @@ -25,7 +25,6 @@ const CHECKPOINT: &str = "guest-checkpoint"; /// Returns a map of program dependencies. fn get_program_dependencies() -> HashMap<&'static str, Vec<&'static str>> { let mut dependencies = HashMap::new(); - dependencies.insert(L1_BATCH, vec![BTC_BLOCKSPACE]); dependencies.insert(CL_STF, vec![EVM_EE_STF]); dependencies.insert(CL_AGG, vec![CL_STF]); dependencies.insert(CHECKPOINT, vec![L1_BATCH, CL_AGG]); diff --git a/provers/sp1/guest-l1-batch/src/main.rs b/provers/sp1/guest-l1-batch/src/main.rs index 1ad581fa9..4e3fc5695 100644 --- a/provers/sp1/guest-l1-batch/src/main.rs +++ b/provers/sp1/guest-l1-batch/src/main.rs @@ -1,8 +1,6 @@ use strata_proofimpl_l1_batch::process_l1_batch_proof; use strata_sp1_adapter::Sp1ZkVmEnv; -mod vks; - fn main() { - process_l1_batch_proof(&Sp1ZkVmEnv, vks::GUEST_BTC_BLOCKSPACE_ELF_ID); + process_l1_batch_proof(&Sp1ZkVmEnv); } diff --git a/provers/tests/src/btc.rs b/provers/tests/src/btc.rs index 99b549c3c..663e49e3a 100644 --- a/provers/tests/src/btc.rs +++ b/provers/tests/src/btc.rs @@ -1,5 +1,5 @@ use bitcoin::Block; -use strata_proofimpl_btc_blockspace::{logic::BlockspaceProofInput, prover::BtcBlockspaceProver}; +use strata_proofimpl_btc_blockspace::{logic::BlockScanProofInput, prover::BtcBlockspaceProver}; use strata_test_utils::l2::gen_params; use strata_zkvm::{ZkVmHost, ZkVmResult}; @@ -21,10 +21,10 @@ impl ProofGenerator for BtcBlockProofGenerator { type P = BtcBlockspaceProver; type H = H; - fn get_input(&self, block: &Block) -> ZkVmResult { + fn get_input(&self, block: &Block) -> ZkVmResult { let params = gen_params(); let rollup_params = params.rollup(); - let input = BlockspaceProofInput { + let input = BlockScanProofInput { block: block.clone(), rollup_params: rollup_params.clone(), }; @@ -49,7 +49,6 @@ mod tests { fn test_proof(generator: &BtcBlockProofGenerator) { let btc_chain = get_btc_chain(); let block = btc_chain.get_block(40321); - let _ = generator.get_proof(block).unwrap(); } diff --git a/provers/tests/src/generators.rs b/provers/tests/src/generators.rs index 899e82149..09c3313c5 100644 --- a/provers/tests/src/generators.rs +++ b/provers/tests/src/generators.rs @@ -39,8 +39,7 @@ impl TestProverGenerators { // TODO: refactor deeper to remove clones. // Likely not critical right now due to its being used in tests and perf CI. let btc_prover = BtcBlockProofGenerator::new(host_provider(ProofVm::BtcProving)); - let l1_batch_prover = - L1BatchProofGenerator::new(btc_prover.clone(), host_provider(ProofVm::L1Batch)); + let l1_batch_prover = L1BatchProofGenerator::new(host_provider(ProofVm::L1Batch)); let el_prover = ElProofGenerator::new(host_provider(ProofVm::ELProving)); let cl_prover = ClProofGenerator::new(el_prover.clone(), host_provider(ProofVm::CLProving)); let l2_batch_prover = diff --git a/provers/tests/src/l1_batch.rs b/provers/tests/src/l1_batch.rs index beef53e5e..ea777b701 100644 --- a/provers/tests/src/l1_batch.rs +++ b/provers/tests/src/l1_batch.rs @@ -1,22 +1,18 @@ use bitcoin::params::MAINNET; use strata_proofimpl_l1_batch::{L1BatchProofInput, L1BatchProver}; -use strata_test_utils::bitcoin::get_btc_chain; +use strata_test_utils::{bitcoin::get_btc_chain, l2::gen_params}; use strata_zkvm::{ZkVmHost, ZkVmResult}; -use super::{btc::BtcBlockProofGenerator, ProofGenerator}; +use super::ProofGenerator; #[derive(Clone)] pub struct L1BatchProofGenerator { - btc_proof_generator: BtcBlockProofGenerator, host: H, } impl L1BatchProofGenerator { - pub fn new(btc_proof_generator: BtcBlockProofGenerator, host: H) -> Self { - Self { - btc_proof_generator, - host, - } + pub fn new(host: H) -> Self { + Self { host } } } @@ -27,24 +23,24 @@ impl ProofGenerator for L1BatchProofGenerator { fn get_input(&self, heights: &(u32, u32)) -> ZkVmResult { let (start_height, end_height) = *heights; - let btc_chain = get_btc_chain(); + let params = gen_params(); + let rollup_params = params.rollup().clone(); let state = btc_chain.get_verification_state(start_height, &MAINNET.clone().into()); - let mut batch = vec![]; + let mut blocks = Vec::new(); for height in start_height..=end_height { - let block = btc_chain.get_block(height); - let btc_proof = self.btc_proof_generator.get_proof(block)?; - batch.push(btc_proof); + let block = btc_chain.get_block(height).clone(); + blocks.push(block); } let input = L1BatchProofInput { + blocks, state, - batch, - blockspace_vk: self.btc_proof_generator.get_host().get_verification_key(), + rollup_params, }; - // dbg!(&input.blockspace_vk); + Ok(input) }