Skip to content

Commit

Permalink
L1-Scan guest code to prove range of N blocks (#541)
Browse files Browse the repository at this point in the history
* sync with main

* l1_scan & l1_batch -> l1_batch

* l1 scan fix

* l1 batch test fix

* lint fix

* fix doc string
  • Loading branch information
MdTeach authored Jan 7, 2025
1 parent ab2043a commit 6a47763
Show file tree
Hide file tree
Showing 26 changed files with 257 additions and 242 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion bin/prover-client/src/hosts/native.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(())
})),
},
Expand Down
32 changes: 13 additions & 19 deletions bin/prover-client/src/operators/btc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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<L1BlockId, ProvingTaskError> {
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<Mutex<TaskTracker>>,
_db: &ProofDb,
) -> Result<Vec<ProofKey>, 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![])
}
Expand All @@ -64,15 +53,20 @@ impl ProvingOp for BtcBlockspaceOperator {
&self,
task_id: &ProofKey,
_db: &ProofDb,
) -> Result<BlockspaceProofInput, ProvingTaskError> {
let blkid = match task_id.context() {
) -> Result<BlockScanProofInput, ProvingTaskError> {
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,
})
Expand Down
18 changes: 17 additions & 1 deletion bin/prover-client/src/operators/checkpoint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
162 changes: 81 additions & 81 deletions bin/prover-client/src/operators/l1_batch.rs
Original file line number Diff line number Diff line change
@@ -1,128 +1,133 @@
use std::sync::Arc;

use bitcoin::params::MAINNET;
use bitcoin::{params::MAINNET, Block};
use strata_btcio::{
reader::query::get_verification_state,
rpc::{
traits::{Reader, Wallet},
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<BitcoinClient>,
btc_blockspace_operator: Arc<BtcBlockspaceOperator>,
rollup_params: Arc<RollupParams>,
}

impl L1BatchOperator {
pub fn new(
btc_client: Arc<BitcoinClient>,
btc_blockspace_operator: Arc<BtcBlockspaceOperator>,
) -> Self {
pub fn new(btc_client: Arc<BitcoinClient>, rollup_params: Arc<RollupParams>) -> Self {
Self {
btc_client,
btc_blockspace_operator,
rollup_params,
}
}

async fn get_block(&self, block_id: L1BlockId) -> Result<bitcoin::Block, ProvingTaskError> {
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<u64, ProvingTaskError> {
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<Vec<Block>, 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<L1BlockId, ProvingTaskError> {
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<Mutex<TaskTracker>>,
db: &ProofDb,
_db: &ProofDb,
) -> Result<Vec<ProofKey>, 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<L1BatchProofInput, ProvingTaskError> {
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(),
Expand All @@ -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(),
})
}
}
5 changes: 1 addition & 4 deletions bin/prover-client/src/operators/operator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand Down
8 changes: 4 additions & 4 deletions bin/prover-client/src/rpc_server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};

Expand Down Expand Up @@ -80,11 +80,11 @@ impl ProverClientRpc {

#[async_trait]
impl StrataProverClientApiServer for ProverClientRpc {
async fn prove_btc_block(&self, btc_block_num: u64) -> RpcResult<Vec<ProofKey>> {
async fn prove_btc_block(&self, block_id: L1BlockId) -> RpcResult<Vec<ProofKey>> {
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"))
}
Expand All @@ -110,7 +110,7 @@ impl StrataProverClientApiServer for ProverClientRpc {
.expect("failed to create task"))
}

async fn prove_l1_batch(&self, l1_range: (u64, u64)) -> RpcResult<Vec<ProofKey>> {
async fn prove_l1_batch(&self, l1_range: (L1BlockId, L1BlockId)) -> RpcResult<Vec<ProofKey>> {
Ok(self
.operator
.l1_batch_operator()
Expand Down
2 changes: 1 addition & 1 deletion crates/primitives/src/proof.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
1 change: 1 addition & 0 deletions crates/proof-impl/btc-blockspace/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ pub mod filter;
pub mod logic;
pub mod merkle;
pub mod prover;
pub mod scan;
pub mod tx;
Loading

0 comments on commit 6a47763

Please sign in to comment.