Skip to content

Commit

Permalink
Upgradeable batch proof method id lists in light client circuit (#1630)
Browse files Browse the repository at this point in the history
Co-authored-by: eyusufatik <[email protected]>
Co-authored-by: yaziciahmet <[email protected]>
Co-authored-by: Ahmet Yazıcı <[email protected]>
  • Loading branch information
4 people authored Dec 25, 2024
1 parent 0da7227 commit a9d35a6
Show file tree
Hide file tree
Showing 27 changed files with 982 additions and 149 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ byteorder = { version = "1.5.0", default-features = false }
bytes = { version = "1.2.1", default-features = false }
chrono = { version = "0.4.37", default-features = false }
clap = { version = "4.4.10", features = ["derive"] }
const-hex = "1.12"
crypto-bigint = { version = "0.5.5" }
digest = { version = "0.10.6", default-features = false, features = ["alloc"] }
derive_more = { version = "0.99.11", default-features = false }
Expand Down
4 changes: 2 additions & 2 deletions bin/citrea/src/rollup/bitcoin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ use sov_modules_api::{Address, SpecId, Zkvm};
use sov_modules_rollup_blueprint::RollupBlueprint;
use sov_prover_storage_manager::{ProverStorageManager, SnapshotManager};
use sov_rollup_interface::da::DaVerifier;
use sov_rollup_interface::services::da::SenderWithNotifier;
use sov_rollup_interface::services::da::TxRequestWithNotifier;
use sov_state::ProverStorage;
use sov_stf_runner::ProverGuestRunConfig;
use tokio::sync::broadcast;
Expand Down Expand Up @@ -118,7 +118,7 @@ impl RollupBlueprint for BitcoinRollup {
require_wallet_check: bool,
task_manager: &mut TaskManager<()>,
) -> Result<Arc<Self::DaService>, anyhow::Error> {
let (tx, rx) = unbounded_channel::<SenderWithNotifier<TxidWrapper>>();
let (tx, rx) = unbounded_channel::<TxRequestWithNotifier<TxidWrapper>>();

let bitcoin_service = if require_wallet_check {
BitcoinService::new_with_wallet_check(
Expand Down
4 changes: 2 additions & 2 deletions bin/citrea/tests/bitcoin_e2e/batch_prover_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ use citrea_e2e::traits::NodeT;
use citrea_e2e::Result;
use citrea_primitives::{TO_BATCH_PROOF_PREFIX, TO_LIGHT_CLIENT_PREFIX};
use sov_ledger_rpc::LedgerRpcClient;
use sov_rollup_interface::da::{DaData, SequencerCommitment};
use sov_rollup_interface::da::{DaTxRequest, SequencerCommitment};
use sov_rollup_interface::rpc::VerifiedBatchProofResponse;
use tokio::time::sleep;

Expand Down Expand Up @@ -273,7 +273,7 @@ impl TestCase for SkipPreprovenCommitmentsTest {
// Send the same commitment that was already proven.
bitcoin_da_service
.send_transaction_with_fee_rate(
DaData::SequencerCommitment(commitments.first().unwrap().clone()),
DaTxRequest::SequencerCommitment(commitments.first().unwrap().clone()),
1,
)
.await
Expand Down
252 changes: 251 additions & 1 deletion bin/citrea/tests/bitcoin_e2e/light_client_test.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,25 @@
use std::sync::Arc;
use std::time::Duration;

use alloy_primitives::U64;
use async_trait::async_trait;
use bitcoin_da::service::FINALITY_DEPTH;
use bitcoin_da::service::{BitcoinService, BitcoinServiceConfig, FINALITY_DEPTH};
use bitcoin_da::spec::RollupParams;
use citrea_batch_prover::rpc::BatchProverRpcClient;
use citrea_batch_prover::GroupCommitments;
use citrea_common::tasks::manager::TaskManager;
use citrea_e2e::config::{
BatchProverConfig, LightClientProverConfig, SequencerConfig, SequencerMempoolConfig,
TestCaseConfig,
};
use citrea_e2e::framework::TestFramework;
use citrea_e2e::node::NodeKind;
use citrea_e2e::test_case::{TestCase, TestCaseRunner};
use citrea_e2e::Result;
use citrea_light_client_prover::rpc::LightClientProverRpcClient;
use citrea_primitives::{TO_BATCH_PROOF_PREFIX, TO_LIGHT_CLIENT_PREFIX};
use sov_ledger_rpc::LedgerRpcClient;
use sov_rollup_interface::da::{BatchProofMethodId, DaTxRequest};

use super::batch_prover_test::wait_for_zkproofs;
use super::get_citrea_path;
Expand Down Expand Up @@ -433,3 +439,247 @@ async fn test_light_client_proving_multiple_proofs() -> Result<()> {
.run()
.await
}

#[derive(Default)]
struct LightClientBatchProofMethodIdUpdateTest {
task_manager: TaskManager<()>,
}

#[async_trait]
impl TestCase for LightClientBatchProofMethodIdUpdateTest {
fn test_config() -> TestCaseConfig {
TestCaseConfig {
with_sequencer: true,
with_batch_prover: true,
with_light_client_prover: true,
..Default::default()
}
}

fn sequencer_config() -> SequencerConfig {
SequencerConfig {
min_soft_confirmations_per_commitment: 2,
da_update_interval_ms: 500,
..Default::default()
}
}

fn batch_prover_config() -> BatchProverConfig {
BatchProverConfig {
enable_recovery: false,
..Default::default()
}
}

fn light_client_prover_config() -> LightClientProverConfig {
LightClientProverConfig {
enable_recovery: false,
initial_da_height: 171,
..Default::default()
}
}

async fn cleanup(&self) -> Result<()> {
self.task_manager.abort().await;
Ok(())
}

async fn run_test(&mut self, f: &mut TestFramework) -> Result<()> {
let da = f.bitcoin_nodes.get(0).unwrap();
let sequencer = f.sequencer.as_ref().unwrap();
let batch_prover = f.batch_prover.as_ref().unwrap();
let light_client_prover = f.light_client_prover.as_ref().unwrap();

let da_config = &da.config;
let bitcoin_da_service_config = BitcoinServiceConfig {
node_url: format!(
"http://127.0.0.1:{}/wallet/{}",
da_config.rpc_port,
NodeKind::Bitcoin
),
node_username: da_config.rpc_user.clone(),
node_password: da_config.rpc_password.clone(),
network: bitcoin::Network::Regtest,
da_private_key: Some(
// This is a random private key matching guest's METHOD_ID_UPGRADE_AUTHORITY
"79122E48DF1A002FB6584B2E94D0D50F95037416C82DAF280F21CD67D17D9077".to_string(),
),
tx_backup_dir: Self::test_config()
.dir
.join("tx_backup_dir")
.display()
.to_string(),
monitoring: Default::default(),
mempool_space_url: None,
};
let (tx, rx) = tokio::sync::mpsc::unbounded_channel();

let bitcoin_da_service = Arc::new(
BitcoinService::new_with_wallet_check(
bitcoin_da_service_config,
RollupParams {
to_light_client_prefix: TO_LIGHT_CLIENT_PREFIX.to_vec(),
to_batch_proof_prefix: TO_BATCH_PROOF_PREFIX.to_vec(),
},
tx,
)
.await
.unwrap(),
);

self.task_manager
.spawn(|tk| bitcoin_da_service.clone().run_da_queue(rx, tk));

let min_soft_confirmations_per_commitment =
sequencer.min_soft_confirmations_per_commitment();

// publish min_soft_confirmations_per_commitment confirmations
for _ in 0..min_soft_confirmations_per_commitment {
sequencer.client.send_publish_batch_request().await?;
}
sequencer
.wait_for_l2_height(min_soft_confirmations_per_commitment, None)
.await?;

// Wait for commitment tx to be submitted to DA
da.wait_mempool_len(2, Some(TEN_MINS)).await?;

// Finalize the DA block which contains the commitment tx
da.generate(FINALITY_DEPTH).await?;

let commitment_l1_height = da.get_finalized_height().await?;

// Wait for batch prover to generate proof for commitment
batch_prover
.wait_for_l1_height(commitment_l1_height, Some(TEN_MINS))
.await
.unwrap();

// Assert that commitment is queryable
let commitments = batch_prover
.client
.http_client()
.get_sequencer_commitments_on_slot_by_number(U64::from(commitment_l1_height))
.await
.unwrap()
.unwrap();
assert_eq!(commitments.len(), 1);

// Ensure that batch proof is submitted to DA
da.wait_mempool_len(2, Some(TEN_MINS)).await?;

// Finalize the DA block which contains the batch proof tx
da.generate(FINALITY_DEPTH).await?;

let batch_proof_l1_height = da.get_finalized_height().await?;

// Wait for light client prover to process batch proofs.
light_client_prover
.wait_for_l1_height(batch_proof_l1_height, Some(TEN_MINS))
.await
.unwrap();

// Expect light client prover to have generated light client proof
let lcp = light_client_prover
.client
.http_client()
.get_light_client_proof_by_l1_height(batch_proof_l1_height)
.await?;
let lcp_output = lcp.unwrap().light_client_proof_output;
// Verify the current batch proof method ids
assert_eq!(
lcp_output.batch_proof_method_ids,
vec![(0, citrea_risc0_batch_proof::BATCH_PROOF_BITCOIN_ID)],
);

// Send BatchProofMethodId transaction to da
let new_batch_proof_method_id = [1u32; 8];
bitcoin_da_service
.send_transaction_with_fee_rate(
DaTxRequest::BatchProofMethodId(BatchProofMethodId {
method_id: new_batch_proof_method_id,
activation_l2_height: 100,
}),
1,
)
.await
.unwrap();

// Ensure that method id tx is submitted to DA
da.wait_mempool_len(2, Some(TEN_MINS)).await?;

// Finalize the DA block which contains the method id tx
da.generate(FINALITY_DEPTH).await?;

let method_id_l1_height = da.get_finalized_height().await?;

// Wait for light client prover to process method id update
light_client_prover
.wait_for_l1_height(method_id_l1_height, Some(TEN_MINS))
.await
.unwrap();

// Assert that 1 l1 block before method id tx, still has the same batch proof method ids
let lcp = light_client_prover
.client
.http_client()
.get_light_client_proof_by_l1_height(method_id_l1_height - 1)
.await?;
let lcp_output = lcp.unwrap().light_client_proof_output;
// Verify the current batch proof method ids
assert_eq!(
lcp_output.batch_proof_method_ids,
vec![(0, citrea_risc0_batch_proof::BATCH_PROOF_BITCOIN_ID)],
);

// Assert that method ids are updated
let lcp = light_client_prover
.client
.http_client()
.get_light_client_proof_by_l1_height(method_id_l1_height)
.await?;
let lcp_output = lcp.unwrap().light_client_proof_output;
// Verify the current batch proof method ids
assert_eq!(
lcp_output.batch_proof_method_ids,
vec![
(0, citrea_risc0_batch_proof::BATCH_PROOF_BITCOIN_ID),
(100, new_batch_proof_method_id)
],
);

// Generate one more empty l1 block
da.generate(1).await?;

// Wait for light client to process it
light_client_prover
.wait_for_l1_height(method_id_l1_height + 1, None)
.await
.unwrap();

// Verify that previously updated method ids are being used
let lcp = light_client_prover
.client
.http_client()
.get_light_client_proof_by_l1_height(method_id_l1_height + 1)
.await?;
let lcp_output = lcp.unwrap().light_client_proof_output;
assert_eq!(
lcp_output.batch_proof_method_ids,
vec![
(0, citrea_risc0_batch_proof::BATCH_PROOF_BITCOIN_ID),
(100, new_batch_proof_method_id)
],
);

Ok(())
}
}

#[tokio::test]
async fn test_light_client_batch_proof_method_id_update() -> Result<()> {
TestCaseRunner::new(LightClientBatchProofMethodIdUpdateTest::default())
.set_citrea_path(get_citrea_path())
.run()
.await
}
8 changes: 4 additions & 4 deletions bin/citrea/tests/bitcoin_e2e/sequencer_commitments.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use citrea_primitives::TO_BATCH_PROOF_PREFIX;
use rs_merkle::algorithms::Sha256;
use rs_merkle::MerkleTree;
use sov_ledger_rpc::LedgerRpcClient;
use sov_rollup_interface::da::{BlobReaderTrait, DaData};
use sov_rollup_interface::da::{BlobReaderTrait, DaTxRequest};
use sov_rollup_interface::rpc::SequencerCommitmentResponse;
use tokio::time::sleep;

Expand Down Expand Up @@ -309,11 +309,11 @@ impl SequencerSendCommitmentsToDaTest {

let data = BlobReaderTrait::full_data(&mut blob);

let commitment = DaData::try_from_slice(data).unwrap();
let commitment = DaTxRequest::try_from_slice(data).unwrap();

matches!(commitment, DaData::SequencerCommitment(_));
matches!(commitment, DaTxRequest::SequencerCommitment(_));

let DaData::SequencerCommitment(commitment) = commitment else {
let DaTxRequest::SequencerCommitment(commitment) = commitment else {
panic!("Expected SequencerCommitment, got {:?}", commitment);
};

Expand Down
8 changes: 4 additions & 4 deletions bin/citrea/tests/test_helpers/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use sov_mock_da::{MockAddress, MockBlock, MockDaConfig, MockDaService};
use sov_modules_api::default_signature::private_key::DefaultPrivateKey;
use sov_modules_api::PrivateKey;
use sov_modules_rollup_blueprint::RollupBlueprint as _;
use sov_rollup_interface::da::{BlobReaderTrait, DaData, SequencerCommitment};
use sov_rollup_interface::da::{BlobReaderTrait, DaTxRequest, SequencerCommitment};
use sov_rollup_interface::services::da::{DaService, SlotData};
use sov_rollup_interface::zk::Proof;
use sov_rollup_interface::Network;
Expand Down Expand Up @@ -362,10 +362,10 @@ fn extract_da_data(
.extract_relevant_blobs(&block)
.into_iter()
.for_each(|mut tx| {
let data = DaData::try_from_slice(tx.full_data());
if let Ok(DaData::SequencerCommitment(seq_com)) = data {
let data = DaTxRequest::try_from_slice(tx.full_data());
if let Ok(DaTxRequest::SequencerCommitment(seq_com)) = data {
sequencer_commitments.push(seq_com);
} else if let Ok(DaData::ZKProof(proof)) = data {
} else if let Ok(DaTxRequest::ZKProof(proof)) = data {
zk_proofs.push(proof);
} else {
tracing::warn!(
Expand Down
Loading

0 comments on commit a9d35a6

Please sign in to comment.