From 812ac34935d439b85a5a7f836f2ebffa237595fc Mon Sep 17 00:00:00 2001 From: yaziciahmet Date: Fri, 22 Nov 2024 11:26:30 +0100 Subject: [PATCH 01/24] Carry citrea-e2e to workspace --- Cargo.lock | 1 + Cargo.toml | 2 ++ bin/citrea/Cargo.toml | 4 ++-- crates/bitcoin-da/Cargo.toml | 3 +++ crates/bitcoin-da/tests/mod.rs | 2 ++ crates/bitcoin-da/tests/service.rs | 0 crates/bitcoin-da/tests/verifier.rs | 0 7 files changed, 10 insertions(+), 2 deletions(-) create mode 100644 crates/bitcoin-da/tests/mod.rs create mode 100644 crates/bitcoin-da/tests/service.rs create mode 100644 crates/bitcoin-da/tests/verifier.rs diff --git a/Cargo.lock b/Cargo.lock index 1244b0a1a2..9221f96ec4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1225,6 +1225,7 @@ dependencies = [ "bitcoincore-rpc", "borsh", "citrea-common", + "citrea-e2e", "citrea-primitives", "crypto-bigint", "futures", diff --git a/Cargo.toml b/Cargo.toml index 1b5f902a02..0c56a36b40 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -155,5 +155,7 @@ tower-http = { version = "0.5.0", features = ["full"] } tower = { version = "0.4.13", features = ["full"] } hyper = { version = "1.4.0" } +citrea-e2e = { git = "https://github.com/chainwayxyz/citrea-e2e", rev = "5baaef3" } + [patch.crates-io] bitcoincore-rpc = { version = "0.18.0", git = "https://github.com/chainwayxyz/rust-bitcoincore-rpc.git", rev = "ca3cfa2" } diff --git a/bin/citrea/Cargo.toml b/bin/citrea/Cargo.toml index 3a05a4038d..c0777098c4 100644 --- a/bin/citrea/Cargo.toml +++ b/bin/citrea/Cargo.toml @@ -91,13 +91,13 @@ rustc_version_runtime = { workspace = true } # bitcoin-e2e dependencies bitcoin.workspace = true bitcoincore-rpc.workspace = true -citrea-e2e = { git = "https://github.com/chainwayxyz/citrea-e2e", rev = "5baaef3" } +citrea-e2e = { workspace = true } [build-dependencies] sp1-helper = { version = "3.0.0", default-features = false } [features] -default = [] # Deviate from convention by making the "native" feature active by default. This aligns with how this package is meant to be used (as a binary first, library second). +default = [] # Deviate from convention by making the "native" feature active by default. This aligns with how this package is meant to be used (). bench = ["hex"] # "sov-risc0-adapter/bench", "risc0/bench"] diff --git a/crates/bitcoin-da/Cargo.toml b/crates/bitcoin-da/Cargo.toml index f82d43e9bd..1abb57724a 100644 --- a/crates/bitcoin-da/Cargo.toml +++ b/crates/bitcoin-da/Cargo.toml @@ -40,6 +40,9 @@ sha2 = { workspace = true } bitcoincore-rpc = { workspace = true, optional = true } +[dev-dependencies] +citrea-e2e = { workspace = true } + [features] default = [] native = [ diff --git a/crates/bitcoin-da/tests/mod.rs b/crates/bitcoin-da/tests/mod.rs new file mode 100644 index 0000000000..57b8f19ab1 --- /dev/null +++ b/crates/bitcoin-da/tests/mod.rs @@ -0,0 +1,2 @@ +mod service; +mod verifier; diff --git a/crates/bitcoin-da/tests/service.rs b/crates/bitcoin-da/tests/service.rs new file mode 100644 index 0000000000..e69de29bb2 diff --git a/crates/bitcoin-da/tests/verifier.rs b/crates/bitcoin-da/tests/verifier.rs new file mode 100644 index 0000000000..e69de29bb2 From c5e452ad101aa732463b385326339033164f50b2 Mon Sep 17 00:00:00 2001 From: yaziciahmet Date: Fri, 22 Nov 2024 13:52:48 +0100 Subject: [PATCH 02/24] Implement get da service helper --- crates/bitcoin-da/tests/mod.rs | 2 - crates/bitcoin-da/tests/service.rs | 1 + crates/bitcoin-da/tests/test_utils.rs | 85 +++++++++++++++++++++++++++ crates/bitcoin-da/tests/verifier.rs | 42 +++++++++++++ 4 files changed, 128 insertions(+), 2 deletions(-) delete mode 100644 crates/bitcoin-da/tests/mod.rs create mode 100644 crates/bitcoin-da/tests/test_utils.rs diff --git a/crates/bitcoin-da/tests/mod.rs b/crates/bitcoin-da/tests/mod.rs deleted file mode 100644 index 57b8f19ab1..0000000000 --- a/crates/bitcoin-da/tests/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -mod service; -mod verifier; diff --git a/crates/bitcoin-da/tests/service.rs b/crates/bitcoin-da/tests/service.rs index e69de29bb2..8b13789179 100644 --- a/crates/bitcoin-da/tests/service.rs +++ b/crates/bitcoin-da/tests/service.rs @@ -0,0 +1 @@ + diff --git a/crates/bitcoin-da/tests/test_utils.rs b/crates/bitcoin-da/tests/test_utils.rs new file mode 100644 index 0000000000..a4a49a23f7 --- /dev/null +++ b/crates/bitcoin-da/tests/test_utils.rs @@ -0,0 +1,85 @@ +use std::path::PathBuf; +use std::sync::Arc; + +use bitcoin_da::service::{BitcoinService, BitcoinServiceConfig}; +use bitcoin_da::spec::RollupParams; +use citrea_common::tasks::manager::TaskManager; +use citrea_e2e::config::BitcoinConfig; +use citrea_e2e::node::NodeKind; +use citrea_primitives::{TO_BATCH_PROOF_PREFIX, TO_LIGHT_CLIENT_PREFIX}; + +pub async fn get_service( + task_manager: &mut TaskManager<()>, + config: &BitcoinConfig, +) -> Arc { + let node_url = format!( + "http://127.0.0.1:{}/wallet/{}", + config.rpc_port, + NodeKind::Bitcoin + ); + + let runtime_config = BitcoinServiceConfig { + node_url, + node_username: config.rpc_user.clone(), + node_password: config.rpc_password.clone(), + network: bitcoin::Network::Regtest, + da_private_key: Some( + "E9873D79C6D87DC0FB6A5778633389F4453213303DA61F20BD67FC233AA33262".to_string(), // Test key, safe to publish + ), + tx_backup_dir: get_tx_backup_dir(), + monitoring: None, + }; + + let (tx, rx) = tokio::sync::mpsc::unbounded_channel(); + + let da_service = BitcoinService::new_without_wallet_check( + runtime_config, + RollupParams { + to_batch_proof_prefix: TO_BATCH_PROOF_PREFIX.to_vec(), + to_light_client_prefix: TO_LIGHT_CLIENT_PREFIX.to_vec(), + }, + tx, + ) + .await + .expect("Error initializing BitcoinService"); + + let da_service = Arc::new(da_service); + task_manager.spawn(|tk| da_service.clone().run_da_queue(rx, tk)); + + da_service +} + +pub async fn generate_mock_txs(_service: &BitcoinService) { + todo!() +} + +pub fn get_citrea_path() -> PathBuf { + std::env::var("CITREA_E2E_TEST_BINARY").map_or_else( + |_| { + get_workspace_root() + .join("target") + .join("debug") + .join("citrea") + }, + PathBuf::from, + ) +} + +fn get_tx_backup_dir() -> String { + get_workspace_root() + .join("resources") + .join("bitcoin") + .join("inscription_txs") + .to_str() + .unwrap() + .to_string() +} + +fn get_workspace_root() -> PathBuf { + let manifest_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + manifest_dir + .ancestors() + .nth(2) + .expect("Failed to find workspace root") + .to_path_buf() +} diff --git a/crates/bitcoin-da/tests/verifier.rs b/crates/bitcoin-da/tests/verifier.rs index e69de29bb2..51777a443a 100644 --- a/crates/bitcoin-da/tests/verifier.rs +++ b/crates/bitcoin-da/tests/verifier.rs @@ -0,0 +1,42 @@ +mod test_utils; + +use async_trait::async_trait; +use citrea_common::tasks::manager::TaskManager; +use citrea_e2e::config::TestCaseConfig; +use citrea_e2e::framework::TestFramework; +use citrea_e2e::test_case::{TestCase, TestCaseRunner}; +use citrea_e2e::Result; +use test_utils::{generate_mock_txs, get_citrea_path, get_service}; + +struct BitcoinVerifierTest; + +#[async_trait] +impl TestCase for BitcoinVerifierTest { + fn test_config() -> TestCaseConfig { + // Only run bitcoin regtest + TestCaseConfig { + with_sequencer: false, + ..Default::default() + } + } + + async fn run_test(&mut self, f: &mut TestFramework) -> Result<()> { + let mut task_manager = TaskManager::default(); + let da = f.bitcoin_nodes.get(0).unwrap(); + + let da_service = get_service(&mut task_manager, &da.config).await; + generate_mock_txs(&da_service).await; + + task_manager.abort().await; + Ok(()) + } +} + +#[cfg(feature = "native")] +#[tokio::test] +async fn test_bitcoin_verifier() -> Result<()> { + TestCaseRunner::new(BitcoinVerifierTest) + .set_citrea_path(get_citrea_path()) + .run() + .await +} From b33e68e15699598e1276ebbabe87601b0aaea225 Mon Sep 17 00:00:00 2001 From: yaziciahmet Date: Sat, 23 Nov 2024 13:11:28 +0100 Subject: [PATCH 03/24] Add extra assert --- crates/bitcoin-da/src/service.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/bitcoin-da/src/service.rs b/crates/bitcoin-da/src/service.rs index 00ab6eabc2..effab7f041 100644 --- a/crates/bitcoin-da/src/service.rs +++ b/crates/bitcoin-da/src/service.rs @@ -423,6 +423,7 @@ impl BitcoinService { commit: Transaction, reveal: TxWithId, ) -> Result> { + assert!(!commit_chunks.is_empty(), "Received empty chunks"); assert_eq!( commit_chunks.len(), reveal_chunks.len(), From 3a42ecc476081cb466e7542ff98b719d2acd2f51 Mon Sep 17 00:00:00 2001 From: yaziciahmet Date: Sat, 23 Nov 2024 13:54:54 +0100 Subject: [PATCH 04/24] Implement generate_mock_txs --- crates/bitcoin-da/tests/test_utils.rs | 175 ++++++++++++++++++++++++-- crates/bitcoin-da/tests/verifier.rs | 8 +- 2 files changed, 167 insertions(+), 16 deletions(-) diff --git a/crates/bitcoin-da/tests/test_utils.rs b/crates/bitcoin-da/tests/test_utils.rs index a4a49a23f7..3f32a34cbf 100644 --- a/crates/bitcoin-da/tests/test_utils.rs +++ b/crates/bitcoin-da/tests/test_utils.rs @@ -3,29 +3,50 @@ use std::sync::Arc; use bitcoin_da::service::{BitcoinService, BitcoinServiceConfig}; use bitcoin_da::spec::RollupParams; +use bitcoincore_rpc::RpcApi; use citrea_common::tasks::manager::TaskManager; +use citrea_e2e::bitcoin::BitcoinNode; use citrea_e2e::config::BitcoinConfig; use citrea_e2e::node::NodeKind; +use citrea_e2e::traits::NodeT; use citrea_primitives::{TO_BATCH_PROOF_PREFIX, TO_LIGHT_CLIENT_PREFIX}; +use sov_rollup_interface::da::{DaData, SequencerCommitment}; +use sov_rollup_interface::services::da::DaService; + +const DEFAULT_DA_PRIVATE_KEY: &str = + "E9873D79C6D87DC0FB6A5778633389F4453213303DA61F20BD67FC233AA33262"; + +pub async fn get_default_service( + task_manager: &mut TaskManager<()>, + config: &BitcoinConfig, +) -> Arc { + get_service( + task_manager, + config, + NodeKind::Bitcoin.to_string(), + DEFAULT_DA_PRIVATE_KEY.to_string(), + TO_BATCH_PROOF_PREFIX.to_vec(), + TO_LIGHT_CLIENT_PREFIX.to_vec(), + ) + .await +} pub async fn get_service( task_manager: &mut TaskManager<()>, config: &BitcoinConfig, + wallet: String, + da_private_key: String, + to_batch_proof_prefix: Vec, + to_light_client_prefix: Vec, ) -> Arc { - let node_url = format!( - "http://127.0.0.1:{}/wallet/{}", - config.rpc_port, - NodeKind::Bitcoin - ); + let node_url = format!("http://127.0.0.1:{}/wallet/{}", config.rpc_port, wallet,); let runtime_config = BitcoinServiceConfig { node_url, node_username: config.rpc_user.clone(), node_password: config.rpc_password.clone(), network: bitcoin::Network::Regtest, - da_private_key: Some( - "E9873D79C6D87DC0FB6A5778633389F4453213303DA61F20BD67FC233AA33262".to_string(), // Test key, safe to publish - ), + da_private_key: Some(da_private_key), tx_backup_dir: get_tx_backup_dir(), monitoring: None, }; @@ -35,8 +56,8 @@ pub async fn get_service( let da_service = BitcoinService::new_without_wallet_check( runtime_config, RollupParams { - to_batch_proof_prefix: TO_BATCH_PROOF_PREFIX.to_vec(), - to_light_client_prefix: TO_LIGHT_CLIENT_PREFIX.to_vec(), + to_batch_proof_prefix, + to_light_client_prefix, }, tx, ) @@ -49,8 +70,138 @@ pub async fn get_service( da_service } -pub async fn generate_mock_txs(_service: &BitcoinService) { - todo!() +pub async fn generate_mock_txs( + da_service: &BitcoinService, + da_node: &BitcoinNode, + task_manager: &mut TaskManager<()>, +) { + println!("Generating mock txs"); + + da_service + .send_transaction(DaData::SequencerCommitment(SequencerCommitment { + merkle_root: [13; 32], + l2_start_block_number: 1002, + l2_end_block_number: 1100, + })) + .await + .expect("Failed to send transaction"); + + println!("1 generated"); + + da_service + .send_transaction(DaData::SequencerCommitment(SequencerCommitment { + merkle_root: [14; 32], + l2_start_block_number: 1101, + l2_end_block_number: 1245, + })) + .await + .expect("Failed to send transaction"); + println!("2 generated"); + + let size = 2000; + let blob = (0..size).map(|_| rand::random::()).collect::>(); + + da_service + .send_transaction(DaData::ZKProof(blob)) + .await + .expect("Failed to send transaction"); + println!("3 generated"); + + let size = 600 * 1024; + let blob = (0..size).map(|_| rand::random::()).collect::>(); + println!("wtf?"); + + da_service + .send_transaction(DaData::ZKProof(blob)) + .await + .expect("Failed to send transaction"); + println!("4 generated"); + + // seq com different namespace + let wrong_namespace_wallet = "wrong_namespace".to_string(); + create_and_fund_wallet(wrong_namespace_wallet.clone(), da_node).await; + println!("maybe?"); + get_service( + task_manager, + &da_node.config, + wrong_namespace_wallet, + DEFAULT_DA_PRIVATE_KEY.to_string(), + vec![5], + vec![6], + ) + .await + .send_transaction(DaData::SequencerCommitment(SequencerCommitment { + merkle_root: [15; 32], + l2_start_block_number: 1246, + l2_end_block_number: 1268, + })) + .await + .expect("Failed to send transaction"); + println!("5 generated"); + + let size = 1024; + let blob = (0..size).map(|_| rand::random::()).collect::>(); + + da_service + .send_transaction(DaData::ZKProof(blob)) + .await + .expect("Failed to send transaction"); + + // seq com incorrect pubkey and sig + let incorrect_pubkey_wallet = "incorrect_pubkey".to_string(); + create_and_fund_wallet(incorrect_pubkey_wallet.clone(), da_node).await; + get_service( + task_manager, + &da_node.config, + incorrect_pubkey_wallet, +"E9873D79C6D87DC0FB6A5778633389F4453213303DA61F20BD67FC233AA33263".to_string(), + TO_BATCH_PROOF_PREFIX.to_vec(), + TO_LIGHT_CLIENT_PREFIX.to_vec(), + ) + .await + .send_transaction(DaData::SequencerCommitment(SequencerCommitment { + merkle_root: [15; 32], + l2_start_block_number: 1246, + l2_end_block_number: 1268, + })) + .await + .expect("Failed to send transaction"); + + da_service + .send_transaction(DaData::SequencerCommitment(SequencerCommitment { + merkle_root: [15; 32], + l2_start_block_number: 1246, + l2_end_block_number: 1268, + })) + .await + .expect("Failed to send transaction"); + + let size = 1200 * 1024; + let blob = (0..size).map(|_| rand::random::()).collect::>(); + + da_service + .send_transaction(DaData::ZKProof(blob)) + .await + .expect("Failed to send transaction"); + + da_service + .send_transaction(DaData::SequencerCommitment(SequencerCommitment { + merkle_root: [30; 32], + l2_start_block_number: 1268, + l2_end_block_number: 1314, + })) + .await + .expect("Failed to send transaction"); +} + +async fn create_and_fund_wallet(wallet: String, da_node: &BitcoinNode) { + da_node + .client() + .create_wallet(&wallet, None, None, None, None) + .await + .unwrap(); + + da_node.fund_wallet(wallet, 1).await.unwrap(); } pub fn get_citrea_path() -> PathBuf { diff --git a/crates/bitcoin-da/tests/verifier.rs b/crates/bitcoin-da/tests/verifier.rs index 51777a443a..a0ec0c992c 100644 --- a/crates/bitcoin-da/tests/verifier.rs +++ b/crates/bitcoin-da/tests/verifier.rs @@ -6,7 +6,7 @@ use citrea_e2e::config::TestCaseConfig; use citrea_e2e::framework::TestFramework; use citrea_e2e::test_case::{TestCase, TestCaseRunner}; use citrea_e2e::Result; -use test_utils::{generate_mock_txs, get_citrea_path, get_service}; +use test_utils::{generate_mock_txs, get_citrea_path, get_default_service}; struct BitcoinVerifierTest; @@ -22,10 +22,10 @@ impl TestCase for BitcoinVerifierTest { async fn run_test(&mut self, f: &mut TestFramework) -> Result<()> { let mut task_manager = TaskManager::default(); - let da = f.bitcoin_nodes.get(0).unwrap(); + let da_node = f.bitcoin_nodes.get(0).unwrap(); - let da_service = get_service(&mut task_manager, &da.config).await; - generate_mock_txs(&da_service).await; + let da_service = get_default_service(&mut task_manager, &da_node.config).await; + generate_mock_txs(&da_service, da_node, &mut task_manager).await; task_manager.abort().await; Ok(()) From 3a77ca7583575a0eaab67d5c9a3c3cf8bdd94599 Mon Sep 17 00:00:00 2001 From: yaziciahmet Date: Sat, 23 Nov 2024 14:40:05 +0100 Subject: [PATCH 05/24] Use MAX_TXBODY_SIZE for generating aggregate zkproofs --- crates/bitcoin-da/tests/test_utils.rs | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/crates/bitcoin-da/tests/test_utils.rs b/crates/bitcoin-da/tests/test_utils.rs index 3f32a34cbf..8293a86c54 100644 --- a/crates/bitcoin-da/tests/test_utils.rs +++ b/crates/bitcoin-da/tests/test_utils.rs @@ -9,7 +9,7 @@ use citrea_e2e::bitcoin::BitcoinNode; use citrea_e2e::config::BitcoinConfig; use citrea_e2e::node::NodeKind; use citrea_e2e::traits::NodeT; -use citrea_primitives::{TO_BATCH_PROOF_PREFIX, TO_LIGHT_CLIENT_PREFIX}; +use citrea_primitives::{MAX_TXBODY_SIZE, TO_BATCH_PROOF_PREFIX, TO_LIGHT_CLIENT_PREFIX}; use sov_rollup_interface::da::{DaData, SequencerCommitment}; use sov_rollup_interface::services::da::DaService; @@ -75,8 +75,6 @@ pub async fn generate_mock_txs( da_node: &BitcoinNode, task_manager: &mut TaskManager<()>, ) { - println!("Generating mock txs"); - da_service .send_transaction(DaData::SequencerCommitment(SequencerCommitment { merkle_root: [13; 32], @@ -86,8 +84,6 @@ pub async fn generate_mock_txs( .await .expect("Failed to send transaction"); - println!("1 generated"); - da_service .send_transaction(DaData::SequencerCommitment(SequencerCommitment { merkle_root: [14; 32], @@ -96,7 +92,6 @@ pub async fn generate_mock_txs( })) .await .expect("Failed to send transaction"); - println!("2 generated"); let size = 2000; let blob = (0..size).map(|_| rand::random::()).collect::>(); @@ -105,22 +100,19 @@ pub async fn generate_mock_txs( .send_transaction(DaData::ZKProof(blob)) .await .expect("Failed to send transaction"); - println!("3 generated"); - let size = 600 * 1024; + // Invoke chunked zk proof generation + let size = MAX_TXBODY_SIZE * 4 + 1500; let blob = (0..size).map(|_| rand::random::()).collect::>(); - println!("wtf?"); da_service .send_transaction(DaData::ZKProof(blob)) .await .expect("Failed to send transaction"); - println!("4 generated"); // seq com different namespace let wrong_namespace_wallet = "wrong_namespace".to_string(); create_and_fund_wallet(wrong_namespace_wallet.clone(), da_node).await; - println!("maybe?"); get_service( task_manager, &da_node.config, @@ -137,7 +129,6 @@ pub async fn generate_mock_txs( })) .await .expect("Failed to send transaction"); - println!("5 generated"); let size = 1024; let blob = (0..size).map(|_| rand::random::()).collect::>(); @@ -154,7 +145,7 @@ pub async fn generate_mock_txs( task_manager, &da_node.config, incorrect_pubkey_wallet, -"E9873D79C6D87DC0FB6A5778633389F4453213303DA61F20BD67FC233AA33263".to_string(), + "E9873D79C6D87DC0FB6A5778633389F4453213303DA61F20BD67FC233AA33263".to_string(), TO_BATCH_PROOF_PREFIX.to_vec(), TO_LIGHT_CLIENT_PREFIX.to_vec(), ) @@ -176,7 +167,8 @@ pub async fn generate_mock_txs( .await .expect("Failed to send transaction"); - let size = 1200 * 1024; + // Invoke chunked zk proof generation + let size = MAX_TXBODY_SIZE * 8 + 2500; let blob = (0..size).map(|_| rand::random::()).collect::>(); da_service @@ -201,7 +193,8 @@ async fn create_and_fund_wallet(wallet: String, da_node: &BitcoinNode) { .await .unwrap(); - da_node.fund_wallet(wallet, 1).await.unwrap(); + // TODO: fix this somehow? + da_node.fund_wallet(wallet, 150).await.unwrap(); } pub fn get_citrea_path() -> PathBuf { From 898becd9f965daebc9bb4b20a16d1a104bdadabf Mon Sep 17 00:00:00 2001 From: yaziciahmet Date: Sat, 23 Nov 2024 14:53:34 +0100 Subject: [PATCH 06/24] Fund wallets early --- crates/bitcoin-da/tests/test_utils.rs | 85 ++++++++++++++------------- 1 file changed, 45 insertions(+), 40 deletions(-) diff --git a/crates/bitcoin-da/tests/test_utils.rs b/crates/bitcoin-da/tests/test_utils.rs index 8293a86c54..c4ade81751 100644 --- a/crates/bitcoin-da/tests/test_utils.rs +++ b/crates/bitcoin-da/tests/test_utils.rs @@ -75,6 +75,32 @@ pub async fn generate_mock_txs( da_node: &BitcoinNode, task_manager: &mut TaskManager<()>, ) { + // Funding wallet requires block generation, hence we do funding at the beginning + // to be able to write all transactions into the same block. + let wrong_prefix_wallet = "wrong_prefix".to_string(); + create_and_fund_wallet(wrong_prefix_wallet.clone(), da_node).await; + let wrong_prefix_da_service = get_service( + task_manager, + &da_node.config, + wrong_prefix_wallet, + DEFAULT_DA_PRIVATE_KEY.to_string(), + vec![5], + vec![6], + ) + .await; + + let wrong_key_wallet = "wrong_key".to_string(); + create_and_fund_wallet(wrong_key_wallet.clone(), da_node).await; + let wrong_key_da_service = get_service( + task_manager, + &da_node.config, + wrong_key_wallet, + "E9873D79C6D87DC0FB6A5778633389F4453213303DA61F20BD67FC233AA33263".to_string(), + TO_BATCH_PROOF_PREFIX.to_vec(), + TO_LIGHT_CLIENT_PREFIX.to_vec(), + ) + .await; + da_service .send_transaction(DaData::SequencerCommitment(SequencerCommitment { merkle_root: [13; 32], @@ -110,25 +136,15 @@ pub async fn generate_mock_txs( .await .expect("Failed to send transaction"); - // seq com different namespace - let wrong_namespace_wallet = "wrong_namespace".to_string(); - create_and_fund_wallet(wrong_namespace_wallet.clone(), da_node).await; - get_service( - task_manager, - &da_node.config, - wrong_namespace_wallet, - DEFAULT_DA_PRIVATE_KEY.to_string(), - vec![5], - vec![6], - ) - .await - .send_transaction(DaData::SequencerCommitment(SequencerCommitment { - merkle_root: [15; 32], - l2_start_block_number: 1246, - l2_end_block_number: 1268, - })) - .await - .expect("Failed to send transaction"); + // Sequencer commitment with wrong tx prefix + wrong_prefix_da_service + .send_transaction(DaData::SequencerCommitment(SequencerCommitment { + merkle_root: [15; 32], + l2_start_block_number: 1246, + l2_end_block_number: 1268, + })) + .await + .expect("Failed to send transaction"); let size = 1024; let blob = (0..size).map(|_| rand::random::()).collect::>(); @@ -138,25 +154,15 @@ pub async fn generate_mock_txs( .await .expect("Failed to send transaction"); - // seq com incorrect pubkey and sig - let incorrect_pubkey_wallet = "incorrect_pubkey".to_string(); - create_and_fund_wallet(incorrect_pubkey_wallet.clone(), da_node).await; - get_service( - task_manager, - &da_node.config, - incorrect_pubkey_wallet, - "E9873D79C6D87DC0FB6A5778633389F4453213303DA61F20BD67FC233AA33263".to_string(), - TO_BATCH_PROOF_PREFIX.to_vec(), - TO_LIGHT_CLIENT_PREFIX.to_vec(), - ) - .await - .send_transaction(DaData::SequencerCommitment(SequencerCommitment { - merkle_root: [15; 32], - l2_start_block_number: 1246, - l2_end_block_number: 1268, - })) - .await - .expect("Failed to send transaction"); + // Sequencer commitment with wrong key and signature + wrong_key_da_service + .send_transaction(DaData::SequencerCommitment(SequencerCommitment { + merkle_root: [15; 32], + l2_start_block_number: 1246, + l2_end_block_number: 1268, + })) + .await + .expect("Failed to send transaction"); da_service .send_transaction(DaData::SequencerCommitment(SequencerCommitment { @@ -193,8 +199,7 @@ async fn create_and_fund_wallet(wallet: String, da_node: &BitcoinNode) { .await .unwrap(); - // TODO: fix this somehow? - da_node.fund_wallet(wallet, 150).await.unwrap(); + da_node.fund_wallet(wallet, 105).await.unwrap(); } pub fn get_citrea_path() -> PathBuf { From 6b9e65b06c8f738297ee88e640e85bcd8a5a2d25 Mon Sep 17 00:00:00 2001 From: yaziciahmet Date: Sat, 23 Nov 2024 15:26:04 +0100 Subject: [PATCH 07/24] Generate and return bitcoin block --- crates/bitcoin-da/tests/test_utils.rs | 26 +++++++++++++------------- crates/bitcoin-da/tests/verifier.rs | 2 +- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/crates/bitcoin-da/tests/test_utils.rs b/crates/bitcoin-da/tests/test_utils.rs index c4ade81751..45a20fe3e5 100644 --- a/crates/bitcoin-da/tests/test_utils.rs +++ b/crates/bitcoin-da/tests/test_utils.rs @@ -2,6 +2,7 @@ use std::path::PathBuf; use std::sync::Arc; use bitcoin_da::service::{BitcoinService, BitcoinServiceConfig}; +use bitcoin_da::spec::block::BitcoinBlock; use bitcoin_da::spec::RollupParams; use bitcoincore_rpc::RpcApi; use citrea_common::tasks::manager::TaskManager; @@ -70,11 +71,14 @@ pub async fn get_service( da_service } +/// Generates mock commitment and zk proof transactions and publishes a DA block +/// with all mock transactions in it, and returns the block. Transactions also contain +/// invalid commitment and zk proof transactions. pub async fn generate_mock_txs( da_service: &BitcoinService, da_node: &BitcoinNode, task_manager: &mut TaskManager<()>, -) { +) -> BitcoinBlock { // Funding wallet requires block generation, hence we do funding at the beginning // to be able to write all transactions into the same block. let wrong_prefix_wallet = "wrong_prefix".to_string(); @@ -127,8 +131,8 @@ pub async fn generate_mock_txs( .await .expect("Failed to send transaction"); - // Invoke chunked zk proof generation - let size = MAX_TXBODY_SIZE * 4 + 1500; + // Invoke chunked zk proof generation with 2 chunks + let size = MAX_TXBODY_SIZE * 1 + 1500; let blob = (0..size).map(|_| rand::random::()).collect::>(); da_service @@ -173,8 +177,8 @@ pub async fn generate_mock_txs( .await .expect("Failed to send transaction"); - // Invoke chunked zk proof generation - let size = MAX_TXBODY_SIZE * 8 + 2500; + // Invoke chunked zk proof generation with 3 chunks + let size = MAX_TXBODY_SIZE * 2 + 2500; let blob = (0..size).map(|_| rand::random::()).collect::>(); da_service @@ -182,14 +186,10 @@ pub async fn generate_mock_txs( .await .expect("Failed to send transaction"); - da_service - .send_transaction(DaData::SequencerCommitment(SequencerCommitment { - merkle_root: [30; 32], - l2_start_block_number: 1268, - l2_end_block_number: 1314, - })) - .await - .expect("Failed to send transaction"); + // Write all txs to a block + let block_hash = da_node.generate(1).await.unwrap()[0]; + + da_service.get_block_by_hash(block_hash).await.unwrap() } async fn create_and_fund_wallet(wallet: String, da_node: &BitcoinNode) { diff --git a/crates/bitcoin-da/tests/verifier.rs b/crates/bitcoin-da/tests/verifier.rs index a0ec0c992c..b5ecfb9fba 100644 --- a/crates/bitcoin-da/tests/verifier.rs +++ b/crates/bitcoin-da/tests/verifier.rs @@ -25,7 +25,7 @@ impl TestCase for BitcoinVerifierTest { let da_node = f.bitcoin_nodes.get(0).unwrap(); let da_service = get_default_service(&mut task_manager, &da_node.config).await; - generate_mock_txs(&da_service, da_node, &mut task_manager).await; + let _da_block = generate_mock_txs(&da_service, da_node, &mut task_manager).await; task_manager.abort().await; Ok(()) From c8c734d515f7000ced298a855c587baea41ff053 Mon Sep 17 00:00:00 2001 From: yaziciahmet Date: Sat, 23 Nov 2024 16:10:31 +0100 Subject: [PATCH 08/24] Clippy --- crates/bitcoin-da/tests/test_utils.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bitcoin-da/tests/test_utils.rs b/crates/bitcoin-da/tests/test_utils.rs index 45a20fe3e5..cc8fbb3a2d 100644 --- a/crates/bitcoin-da/tests/test_utils.rs +++ b/crates/bitcoin-da/tests/test_utils.rs @@ -132,7 +132,7 @@ pub async fn generate_mock_txs( .expect("Failed to send transaction"); // Invoke chunked zk proof generation with 2 chunks - let size = MAX_TXBODY_SIZE * 1 + 1500; + let size = MAX_TXBODY_SIZE + 1500; let blob = (0..size).map(|_| rand::random::()).collect::>(); da_service From 2c33f3bf5081fb5f40794b29b1c25d21f0b1cea0 Mon Sep 17 00:00:00 2001 From: yaziciahmet Date: Sat, 23 Nov 2024 16:42:21 +0100 Subject: [PATCH 09/24] Improve funding flow --- crates/bitcoin-da/tests/test_utils.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/crates/bitcoin-da/tests/test_utils.rs b/crates/bitcoin-da/tests/test_utils.rs index cc8fbb3a2d..37af2a1dde 100644 --- a/crates/bitcoin-da/tests/test_utils.rs +++ b/crates/bitcoin-da/tests/test_utils.rs @@ -105,6 +105,9 @@ pub async fn generate_mock_txs( ) .await; + // Generate 100 blocks for wallets to get their rewards + finalize_funds(da_node).await; + da_service .send_transaction(DaData::SequencerCommitment(SequencerCommitment { merkle_root: [13; 32], @@ -192,6 +195,7 @@ pub async fn generate_mock_txs( da_service.get_block_by_hash(block_hash).await.unwrap() } +/// Creates and funds a wallet. Funds are not finalized until `finalize_funds` is called. async fn create_and_fund_wallet(wallet: String, da_node: &BitcoinNode) { da_node .client() @@ -199,7 +203,12 @@ async fn create_and_fund_wallet(wallet: String, da_node: &BitcoinNode) { .await .unwrap(); - da_node.fund_wallet(wallet, 105).await.unwrap(); + da_node.fund_wallet(wallet, 5).await.unwrap(); +} + +/// Generates 100 blocks and finalizes funds +async fn finalize_funds(da_node: &BitcoinNode) { + da_node.generate(100).await.unwrap(); } pub fn get_citrea_path() -> PathBuf { From fef7095294ce080f4c8c0522aa20a2d0107d5864 Mon Sep 17 00:00:00 2001 From: yaziciahmet Date: Sat, 23 Nov 2024 22:22:37 +0100 Subject: [PATCH 10/24] Carry verifier tests to e2e --- crates/bitcoin-da/src/verifier.rs | 5 + crates/bitcoin-da/tests/test_utils.rs | 45 +++ crates/bitcoin-da/tests/verifier.rs | 394 +++++++++++++++++++++++++- 3 files changed, 442 insertions(+), 2 deletions(-) diff --git a/crates/bitcoin-da/src/verifier.rs b/crates/bitcoin-da/src/verifier.rs index 59dc066f23..f91128a14e 100644 --- a/crates/bitcoin-da/src/verifier.rs +++ b/crates/bitcoin-da/src/verifier.rs @@ -55,6 +55,7 @@ pub enum ValidationError { InvalidBlockBits, InvalidTargetHash, InvalidTimestamp, + HeaderInclusionTxCountMismatch, } impl DaVerifier for BitcoinVerifier { @@ -78,6 +79,10 @@ impl DaVerifier for BitcoinVerifier { completeness_proof: ::CompletenessProof, namespace: DaNamespace, ) -> Result<(), Self::Error> { + if block_header.tx_count as usize != inclusion_proof.wtxids.len() { + return Err(ValidationError::HeaderInclusionTxCountMismatch); + } + // create hash set of blobs let mut blobs_iter = blobs.iter(); diff --git a/crates/bitcoin-da/tests/test_utils.rs b/crates/bitcoin-da/tests/test_utils.rs index 37af2a1dde..12230b6f9b 100644 --- a/crates/bitcoin-da/tests/test_utils.rs +++ b/crates/bitcoin-da/tests/test_utils.rs @@ -241,3 +241,48 @@ fn get_workspace_root() -> PathBuf { .expect("Failed to find workspace root") .to_path_buf() } + +// For some reason, even though macro is used, it sees it as unused +#[allow(unused)] +pub mod macros { + macro_rules! assert_panic { + // Match a single expression + ($expr:expr) => { + match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| $expr)) { + Ok(_) => panic!("Expression did not trigger panic"), + Err(_) => (), + } + }; + // Match an expression and an expected message + ($expr:expr, $expected_msg:expr) => { + match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| $expr)) { + Ok(_) => panic!("Expression did not trigger panic"), + Err(err) => { + let expected_msg = $expected_msg; + if let Some(msg) = err.downcast_ref::<&str>() { + assert!( + msg.contains(expected_msg), + "Panic message '{}' does not match expected '{}'", + msg, + expected_msg + ); + } else if let Some(msg) = err.downcast_ref::() { + assert!( + msg.contains(expected_msg), + "Panic message '{}' does not match expected '{}'", + msg, + expected_msg + ); + } else { + panic!( + "Panic occurred, but message does not match expected '{}'", + expected_msg + ); + } + } + } + }; + } + + pub(crate) use assert_panic; +} diff --git a/crates/bitcoin-da/tests/verifier.rs b/crates/bitcoin-da/tests/verifier.rs index b5ecfb9fba..fe2c3930c6 100644 --- a/crates/bitcoin-da/tests/verifier.rs +++ b/crates/bitcoin-da/tests/verifier.rs @@ -1,11 +1,19 @@ mod test_utils; use async_trait::async_trait; +use bitcoin_da::helpers::parsers::{parse_light_client_transaction, ParsedLightClientTransaction}; +use bitcoin_da::spec::blob::BlobWithSender; +use bitcoin_da::spec::RollupParams; +use bitcoin_da::verifier::{BitcoinVerifier, ValidationError}; use citrea_common::tasks::manager::TaskManager; use citrea_e2e::config::TestCaseConfig; use citrea_e2e::framework::TestFramework; use citrea_e2e::test_case::{TestCase, TestCaseRunner}; use citrea_e2e::Result; +use citrea_primitives::{TO_BATCH_PROOF_PREFIX, TO_LIGHT_CLIENT_PREFIX}; +use sov_rollup_interface::da::{DaNamespace, DaVerifier}; +use sov_rollup_interface::services::da::DaService; +use test_utils::macros::assert_panic; use test_utils::{generate_mock_txs, get_citrea_path, get_default_service}; struct BitcoinVerifierTest; @@ -24,8 +32,390 @@ impl TestCase for BitcoinVerifierTest { let mut task_manager = TaskManager::default(); let da_node = f.bitcoin_nodes.get(0).unwrap(); - let da_service = get_default_service(&mut task_manager, &da_node.config).await; - let _da_block = generate_mock_txs(&da_service, da_node, &mut task_manager).await; + let service = get_default_service(&mut task_manager, &da_node.config).await; + let block = generate_mock_txs(&service, da_node, &mut task_manager).await; + + let (b_txs, b_inclusion_proof, b_completeness_proof) = + service.extract_relevant_blobs_with_proof(&block, DaNamespace::ToBatchProver); + let (l_txs, l_inclusion_proof, l_completeness_proof) = + service.extract_relevant_blobs_with_proof(&block, DaNamespace::ToLightClientProver); + + let verifier = BitcoinVerifier::new(RollupParams { + to_batch_proof_prefix: TO_BATCH_PROOF_PREFIX.to_vec(), + to_light_client_prefix: TO_LIGHT_CLIENT_PREFIX.to_vec(), + }); + + // Correct batch proof + { + assert!(verifier + .verify_transactions( + &block.header, + &b_txs, + b_inclusion_proof.clone(), + b_completeness_proof.clone(), + DaNamespace::ToBatchProver, + ) + .is_ok()); + } + + // Correct light client proof + { + assert!(verifier + .verify_transactions( + &block.header, + &l_txs, + l_inclusion_proof.clone(), + l_completeness_proof.clone(), + DaNamespace::ToLightClientProver, + ) + .is_ok()); + } + + // Inverted namespaces should fail + { + // batch transactions with light client namespace + assert!(verifier + .verify_transactions( + &block.header, + &b_txs, + b_inclusion_proof.clone(), + b_completeness_proof.clone(), + DaNamespace::ToLightClientProver, + ) + .is_err()); + + // light client transactions with batch namespace + assert!(verifier + .verify_transactions( + &block.header, + &l_txs, + l_inclusion_proof.clone(), + l_completeness_proof.clone(), + DaNamespace::ToBatchProver, + ) + .is_err()); + } + + // Test non-segwit block + { + // TODO: do + } + + // False coinbase input witness should fail + { + // TODO: do + } + + // False coinbase script pubkey should fail + { + // TODO: do + } + + // False witness script should fail + { + // TODO: do + } + + // Different witness ids should fail + { + let mut b_inclusion_proof = b_inclusion_proof.clone(); + + b_inclusion_proof.wtxids[0] = [1; 32]; + assert!(verifier + .verify_transactions( + &block.header, + &b_txs, + b_inclusion_proof.clone(), + b_completeness_proof.clone(), + DaNamespace::ToBatchProver, + ) + .is_err()); + + b_inclusion_proof.wtxids[0] = [0; 32]; + b_inclusion_proof.wtxids[1] = [16; 32]; + assert!(verifier + .verify_transactions( + &block.header, + &b_txs, + b_inclusion_proof, + b_completeness_proof.clone(), + DaNamespace::ToBatchProver, + ) + .is_err()); + } + + // Extra tx in inclusion + { + let mut b_inclusion_proof = b_inclusion_proof.clone(); + + b_inclusion_proof.wtxids.push([5; 32]); + assert!(verifier + .verify_transactions( + &block.header, + &b_txs, + b_inclusion_proof, + b_completeness_proof.clone(), + DaNamespace::ToBatchProver, + ) + .is_err()); + } + + // Missing tx in inclusion should fail + { + let mut b_inclusion_proof = b_inclusion_proof.clone(); + + b_inclusion_proof.wtxids.pop(); + assert_eq!( + verifier.verify_transactions( + &block.header, + &b_txs, + b_inclusion_proof, + b_completeness_proof.clone(), + DaNamespace::ToBatchProver, + ), + Err(ValidationError::HeaderInclusionTxCountMismatch), + ); + } + + // Break order of inclusion should fail + { + let mut b_inclusion_proof = b_inclusion_proof.clone(); + + b_inclusion_proof.wtxids.swap(0, 1); + assert!(verifier + .verify_transactions( + &block.header, + &b_txs, + b_inclusion_proof, + b_completeness_proof.clone(), + DaNamespace::ToBatchProver, + ) + .is_err(),); + } + + // Missing tx in completeness proof should panic + { + let mut b_completeness_proof = b_completeness_proof.clone(); + + b_completeness_proof.pop(); + assert_panic!( + verifier.verify_transactions( + &block.header, + &b_txs, + b_inclusion_proof.clone(), + b_completeness_proof, + DaNamespace::ToBatchProver, + ), + "itertools: .zip_eq() reached end of one iterator before the other" + ); + } + + // Extra tx in completeness proof should panic + { + let mut b_completeness_proof = b_completeness_proof.clone(); + + b_completeness_proof.push(block.txdata[0].clone()); + assert_panic!( + verifier.verify_transactions( + &block.header, + &b_txs, + b_inclusion_proof.clone(), + b_completeness_proof, + DaNamespace::ToBatchProver, + ), + "itertools: .zip_eq() reached end of one iterator before the other" + ); + } + + // Nonrelevant tx in completeness proof should fail + { + let mut b_completeness_proof = b_completeness_proof.clone(); + + let nonrelevant_tx = block + .txdata + .iter() + .find(|tx| !b_completeness_proof.contains(tx)) + .unwrap() + .clone(); + b_completeness_proof[0] = nonrelevant_tx; + assert!(verifier + .verify_transactions( + &block.header, + &b_txs, + b_inclusion_proof.clone(), + b_completeness_proof.clone(), + DaNamespace::ToBatchProver, + ) + .is_err(),); + } + + // Break completeness proof order should fail + { + let mut b_completeness_proof = b_completeness_proof.clone(); + + b_completeness_proof.swap(1, 2); + assert!(verifier + .verify_transactions( + &block.header, + &b_txs, + b_inclusion_proof.clone(), + b_completeness_proof, + DaNamespace::ToBatchProver, + ) + .is_err(),); + } + + // Break tx order should fail + { + let mut b_txs = b_txs.clone(); + + b_txs.swap(0, 1); + assert!(verifier + .verify_transactions( + &block.header, + &b_txs, + b_inclusion_proof.clone(), + b_completeness_proof.clone(), + DaNamespace::ToBatchProver, + ) + .is_err(),); + } + + // Break tx order and completeness proof order should fail + { + let mut b_completeness_proof = b_completeness_proof.clone(); + let mut b_txs = b_txs.clone(); + + b_completeness_proof.swap(0, 1); + b_txs.swap(0, 1); + assert!(verifier + .verify_transactions( + &block.header, + &b_txs, + b_inclusion_proof.clone(), + b_completeness_proof, + DaNamespace::ToBatchProver, + ) + .is_err(),); + } + + // Missing tx should fail + { + let mut b_txs = b_txs.clone(); + + b_txs.pop(); + assert_eq!( + verifier.verify_transactions( + &block.header, + &b_txs, + b_inclusion_proof.clone(), + b_completeness_proof.clone(), + DaNamespace::ToBatchProver, + ), + Err(ValidationError::ValidBlobNotFoundInBlobs), + ); + } + + // Tamper tx content of batch proof should fail + { + let mut b_txs = b_txs.clone(); + + b_txs[0] = BlobWithSender::new(vec![2; 152], b_txs[0].sender.0.clone(), b_txs[0].hash); + assert_eq!( + verifier.verify_transactions( + &block.header, + &b_txs, + b_inclusion_proof.clone(), + b_completeness_proof.clone(), + DaNamespace::ToBatchProver, + ), + Err(ValidationError::BlobContentWasModified), + ); + } + + // Tamper tx content of light client proof should fail + { + let mut l_txs = l_txs.clone(); + + l_txs[0] = BlobWithSender::new(vec![2; 152], l_txs[0].sender.0.clone(), l_txs[0].hash); + assert_eq!( + verifier.verify_transactions( + &block.header, + &l_txs, + l_inclusion_proof.clone(), + l_completeness_proof.clone(), + DaNamespace::ToLightClientProver, + ), + Err(ValidationError::BlobContentWasModified), + ); + } + + // Tamper tx sender of batch proof should fail + { + let mut b_txs = b_txs.clone(); + + let mut blob = b_txs[0].blob.clone(); + blob.advance(blob.total_len()); + let blob = blob.accumulator().to_vec(); + + b_txs[0] = BlobWithSender::new(blob, vec![2; 33], b_txs[0].hash); + assert_eq!( + verifier.verify_transactions( + &block.header, + &b_txs, + b_inclusion_proof, + b_completeness_proof, + DaNamespace::ToBatchProver, + ), + Err(ValidationError::IncorrectSenderInBlob), + ); + } + + // Tamper tx sender of light client proof should fail + { + let mut l_txs = l_txs.clone(); + + let mut blob = l_txs[0].blob.clone(); + blob.advance(blob.total_len()); + let blob = blob.accumulator().to_vec(); + + l_txs[0] = BlobWithSender::new(blob, vec![2; 33], l_txs[0].hash); + assert_eq!( + verifier.verify_transactions( + &block.header, + &l_txs, + l_inclusion_proof.clone(), + l_completeness_proof.clone(), + DaNamespace::ToLightClientProver, + ), + Err(ValidationError::IncorrectSenderInBlob), + ); + } + + // Non-decompressed light client proof blob should fail + { + let mut l_txs = l_txs.clone(); + + let body = { + let parsed = parse_light_client_transaction(&l_completeness_proof[0]).unwrap(); + match parsed { + ParsedLightClientTransaction::Complete(complete) => complete.body, // normally we should decompress the tx body + _ => panic!("Should not select zk proof tx other than complete"), + } + }; + + l_txs[0] = BlobWithSender::new(body, l_txs[0].sender.0.clone(), l_txs[0].hash); + assert_eq!( + verifier.verify_transactions( + &block.header, + &l_txs, + l_inclusion_proof, + l_completeness_proof, + DaNamespace::ToLightClientProver, + ), + Err(ValidationError::BlobContentWasModified), + ); + } task_manager.abort().await; Ok(()) From 8477aa9fca8e4d27fb962e77512aeafce5cf614c Mon Sep 17 00:00:00 2001 From: yaziciahmet Date: Sun, 24 Nov 2024 19:08:44 +0100 Subject: [PATCH 11/24] Implement nonsegwit block test --- crates/bitcoin-da/tests/test_utils.rs | 76 +++++++++++++++++++++++++++ crates/bitcoin-da/tests/verifier.rs | 53 +++++++++++++++---- 2 files changed, 119 insertions(+), 10 deletions(-) diff --git a/crates/bitcoin-da/tests/test_utils.rs b/crates/bitcoin-da/tests/test_utils.rs index 12230b6f9b..71b43fc8a4 100644 --- a/crates/bitcoin-da/tests/test_utils.rs +++ b/crates/bitcoin-da/tests/test_utils.rs @@ -1,9 +1,13 @@ +use std::collections::HashMap; use std::path::PathBuf; use std::sync::Arc; +use bitcoin::Amount; use bitcoin_da::service::{BitcoinService, BitcoinServiceConfig}; use bitcoin_da::spec::block::BitcoinBlock; use bitcoin_da::spec::RollupParams; +use bitcoin_da::verifier::WITNESS_COMMITMENT_PREFIX; +use bitcoincore_rpc::json::{AddressType, CreateRawTransactionInput, FundRawTransactionOptions}; use bitcoincore_rpc::RpcApi; use citrea_common::tasks::manager::TaskManager; use citrea_e2e::bitcoin::BitcoinNode; @@ -195,6 +199,78 @@ pub async fn generate_mock_txs( da_service.get_block_by_hash(block_hash).await.unwrap() } +// TODO: make this work +pub async fn generate_nonsegwit_block( + da_node: &BitcoinNode, + da_service: &BitcoinService, +) -> BitcoinBlock { + let client = da_node.client(); + let address = client + .get_new_address(Some("nonsegwit_address"), Some(AddressType::Legacy)) + .await + .unwrap() + .assume_checked(); + + client.generate_to_address(5, &address).await.unwrap(); + finalize_funds(da_node).await; + + let utxos = client + .list_unspent(Some(0), None, Some(&[&address]), None, None) + .await + .unwrap(); + assert_eq!(utxos.len(), 5); + + let input = CreateRawTransactionInput { + txid: utxos[0].txid, + vout: utxos[0].vout, + sequence: None, + }; + let mut output = HashMap::new(); + output.insert(address.to_string(), utxos[0].amount / 2); + output.insert(address.to_string(), utxos[0].amount / 2); + + let raw_tx = client + .create_raw_transaction(&[input], &output, None, None) + .await + .unwrap(); + + let funded_tx = client + .fund_raw_transaction( + &raw_tx, + Some(&FundRawTransactionOptions { + change_address: Some(address.clone()), + fee_rate: Some(Amount::ONE_SAT * 1000), + ..Default::default() + }), + None, + ) + .await + .unwrap(); + + let signed_tx = client + .sign_raw_transaction_with_wallet(&funded_tx.hex, None, None) + .await + .unwrap(); + + client.send_raw_transaction(&signed_tx.hex).await.unwrap(); + + let block_hash = da_node.generate(1).await.unwrap()[0]; + + let block = da_service.get_block_by_hash(block_hash).await.unwrap(); + let txs = block.txdata.as_slice(); + + // ensure that block does not have any segwit txs + let idx = txs[0].output.iter().position(|output| { + output + .script_pubkey + .to_bytes() + .starts_with(WITNESS_COMMITMENT_PREFIX) + }); + assert_eq!(idx, None); + + block +} + /// Creates and funds a wallet. Funds are not finalized until `finalize_funds` is called. async fn create_and_fund_wallet(wallet: String, da_node: &BitcoinNode) { da_node diff --git a/crates/bitcoin-da/tests/verifier.rs b/crates/bitcoin-da/tests/verifier.rs index fe2c3930c6..a3b8bc7f86 100644 --- a/crates/bitcoin-da/tests/verifier.rs +++ b/crates/bitcoin-da/tests/verifier.rs @@ -1,8 +1,11 @@ mod test_utils; use async_trait::async_trait; +use bitcoin::hashes::Hash; +use bitcoin_da::helpers::merkle_tree::BitcoinMerkleTree; use bitcoin_da::helpers::parsers::{parse_light_client_transaction, ParsedLightClientTransaction}; use bitcoin_da::spec::blob::BlobWithSender; +use bitcoin_da::spec::proof::InclusionMultiProof; use bitcoin_da::spec::RollupParams; use bitcoin_da::verifier::{BitcoinVerifier, ValidationError}; use citrea_common::tasks::manager::TaskManager; @@ -14,7 +17,9 @@ use citrea_primitives::{TO_BATCH_PROOF_PREFIX, TO_LIGHT_CLIENT_PREFIX}; use sov_rollup_interface::da::{DaNamespace, DaVerifier}; use sov_rollup_interface::services::da::DaService; use test_utils::macros::assert_panic; -use test_utils::{generate_mock_txs, get_citrea_path, get_default_service}; +use test_utils::{ + generate_mock_txs, generate_nonsegwit_block, get_citrea_path, get_default_service, +}; struct BitcoinVerifierTest; @@ -47,28 +52,30 @@ impl TestCase for BitcoinVerifierTest { // Correct batch proof { - assert!(verifier - .verify_transactions( + assert_eq!( + verifier.verify_transactions( &block.header, &b_txs, b_inclusion_proof.clone(), b_completeness_proof.clone(), DaNamespace::ToBatchProver, - ) - .is_ok()); + ), + Ok(()), + ); } // Correct light client proof { - assert!(verifier - .verify_transactions( + assert_eq!( + verifier.verify_transactions( &block.header, &l_txs, l_inclusion_proof.clone(), l_completeness_proof.clone(), DaNamespace::ToLightClientProver, - ) - .is_ok()); + ), + Ok(()), + ); } // Inverted namespaces should fail @@ -98,7 +105,33 @@ impl TestCase for BitcoinVerifierTest { // Test non-segwit block { - // TODO: do + let nonsegwit_block = generate_nonsegwit_block(da_node, &service).await; + let txs = nonsegwit_block.txdata.as_slice(); + + let tree = BitcoinMerkleTree::new( + txs.iter() + .map(|t| t.compute_txid().to_raw_hash().to_byte_array()) + .collect(), + ); + + let inclusion_proof = InclusionMultiProof { + wtxids: txs + .iter() + .map(|t| t.compute_wtxid().to_raw_hash().to_byte_array()) + .collect(), + coinbase_tx: txs[0].clone(), + coinbase_merkle_proof: tree.get_idx_path(0), + }; + assert_eq!( + verifier.verify_transactions( + &nonsegwit_block.header, + &[], + inclusion_proof, + vec![], + DaNamespace::ToBatchProver, + ), + Ok(()) + ); } // False coinbase input witness should fail From 91e859f9481e8c044b22970985309acf52a06a8d Mon Sep 17 00:00:00 2001 From: yaziciahmet Date: Sun, 24 Nov 2024 21:47:32 +0100 Subject: [PATCH 12/24] Write extract blob tests for da service --- crates/bitcoin-da/tests/service.rs | 127 ++++++++++++++++++++++++++ crates/bitcoin-da/tests/test_utils.rs | 14 ++- 2 files changed, 140 insertions(+), 1 deletion(-) diff --git a/crates/bitcoin-da/tests/service.rs b/crates/bitcoin-da/tests/service.rs index 8b13789179..b256b73836 100644 --- a/crates/bitcoin-da/tests/service.rs +++ b/crates/bitcoin-da/tests/service.rs @@ -1 +1,128 @@ +mod test_utils; +use async_trait::async_trait; +use bitcoin::hashes::Hash; +use bitcoin_da::spec::RollupParams; +use bitcoin_da::verifier::BitcoinVerifier; +use citrea_common::tasks::manager::TaskManager; +use citrea_e2e::config::TestCaseConfig; +use citrea_e2e::framework::TestFramework; +use citrea_e2e::test_case::{TestCase, TestCaseRunner}; +use citrea_e2e::Result; +use citrea_primitives::{TO_BATCH_PROOF_PREFIX, TO_LIGHT_CLIENT_PREFIX}; +use sov_rollup_interface::da::{DaNamespace, DaVerifier}; +use sov_rollup_interface::services::da::DaService; +use test_utils::{generate_mock_txs, get_citrea_path, get_default_service}; + +struct BitcoinServiceTest; + +#[async_trait] +impl TestCase for BitcoinServiceTest { + fn test_config() -> TestCaseConfig { + // Only run bitcoin regtest + TestCaseConfig { + with_sequencer: false, + ..Default::default() + } + } + + async fn run_test(&mut self, f: &mut TestFramework) -> Result<()> { + let mut task_manager = TaskManager::default(); + let da_node = f.bitcoin_nodes.get(0).unwrap(); + + let service = get_default_service(&mut task_manager, &da_node.config).await; + let verifier = BitcoinVerifier::new(RollupParams { + to_batch_proof_prefix: TO_BATCH_PROOF_PREFIX.to_vec(), + to_light_client_prefix: TO_LIGHT_CLIENT_PREFIX.to_vec(), + }); + + let block = generate_mock_txs(&service, da_node, &mut task_manager).await; + let block_wtxids = block + .txdata + .iter() + .map(|tx| tx.compute_wtxid().as_raw_hash().to_byte_array()) + .collect::>(); + + // let sequencer_pubkey = da_node.client().get_address_info(address).await.unwrap().pubkey.unwrap().to_bytes(); + + // Extracts relevant batch proof blobs with proof correctly + { + let (txs, inclusion_proof, completeness_proof) = + service.extract_relevant_blobs_with_proof(&block, DaNamespace::ToBatchProver); + assert_eq!(inclusion_proof.wtxids.len(), 29); + assert_eq!(inclusion_proof.wtxids[1..], block_wtxids[1..]); + // 3 valid commitments, and 1 invalid commitment with wrong public key + assert_eq!(txs.len(), 4); + assert_eq!(completeness_proof.len(), 4); + + let completeness_wtxids = completeness_proof + .iter() + .map(|tx| tx.compute_wtxid().as_raw_hash().to_byte_array()); + for wtxid in completeness_wtxids { + assert!(wtxid.starts_with(TO_BATCH_PROOF_PREFIX)); + assert!(block_wtxids.contains(&wtxid)); + } + // Ensure that the produced outputs are verifiable by the verifier + assert_eq!( + verifier.verify_transactions( + &block.header, + &txs, + inclusion_proof, + completeness_proof, + DaNamespace::ToBatchProver + ), + Ok(()) + ); + } + + // Extracts relevant light client proof blobs with proof correctly + { + let (txs, inclusion_proof, completeness_proof) = + service.extract_relevant_blobs_with_proof(&block, DaNamespace::ToLightClientProver); + assert_eq!(inclusion_proof.wtxids.len(), 29); + assert_eq!(inclusion_proof.wtxids[1..], block_wtxids[1..]); + // 2 complete and 2 aggregate proofs + assert_eq!(txs.len(), 4); + assert_eq!(completeness_proof.len(), 4); + + let completeness_wtxids = completeness_proof + .iter() + .map(|tx| tx.compute_wtxid().as_raw_hash().to_byte_array()); + for wtxid in completeness_wtxids { + assert!(wtxid.starts_with(TO_LIGHT_CLIENT_PREFIX)); + assert!(block_wtxids.contains(&wtxid)); + } + // Ensure that the produced outputs are verifiable by the verifier + assert_eq!( + verifier.verify_transactions( + &block.header, + &txs, + inclusion_proof, + completeness_proof, + DaNamespace::ToLightClientProver + ), + Ok(()) + ); + } + + // Extract relevant sequencer commitments + { + // let commitments = service + // .extract_relevant_sequencer_commitments(&block, &[1, 2, 3, 4, 5]) + // .unwrap(); + // assert_eq!(commitments.len(), 3); + } + + task_manager.abort().await; + Ok(()) + } +} + +#[cfg(feature = "native")] +#[tokio::test] +async fn test_bitcoin_service() -> Result<()> { + TestCaseRunner::new(BitcoinServiceTest) + .set_citrea_path(get_citrea_path()) + .run() + .await +} diff --git a/crates/bitcoin-da/tests/test_utils.rs b/crates/bitcoin-da/tests/test_utils.rs index 71b43fc8a4..1fe2e8b7b3 100644 --- a/crates/bitcoin-da/tests/test_utils.rs +++ b/crates/bitcoin-da/tests/test_utils.rs @@ -78,6 +78,15 @@ pub async fn get_service( /// Generates mock commitment and zk proof transactions and publishes a DA block /// with all mock transactions in it, and returns the block. Transactions also contain /// invalid commitment and zk proof transactions. +/// +/// In total it generates 28 transactions. +/// - Valid commitments: 3 (6 txs) +/// - Valid complete proofs: 2 (4 txs) +/// - Valid chunked proofs: 1 with 2 chunks (6 txs) + 1 with 3 chunks (8 txs) +/// - Invalid commitment with wrong public key: 1 (2 txs) +/// - Invalid commitment with wrong prefix: 1 (2 txs) +/// +/// With coinbase transaction, returned block has total of 29 transactions. pub async fn generate_mock_txs( da_service: &BitcoinService, da_node: &BitcoinNode, @@ -196,7 +205,10 @@ pub async fn generate_mock_txs( // Write all txs to a block let block_hash = da_node.generate(1).await.unwrap()[0]; - da_service.get_block_by_hash(block_hash).await.unwrap() + let block = da_service.get_block_by_hash(block_hash).await.unwrap(); + assert_eq!(block.txdata.len(), 29); + + block } // TODO: make this work From a2c58e597a78752aeac1953b93b133eb752e558f Mon Sep 17 00:00:00 2001 From: yaziciahmet Date: Sun, 24 Nov 2024 23:02:48 +0100 Subject: [PATCH 13/24] Extract relevant proof and comm tests --- crates/bitcoin-da/tests/service.rs | 31 ++++++++++++---- crates/bitcoin-da/tests/test_utils.rs | 51 +++++++++++++++++---------- crates/bitcoin-da/tests/verifier.rs | 2 +- 3 files changed, 58 insertions(+), 26 deletions(-) diff --git a/crates/bitcoin-da/tests/service.rs b/crates/bitcoin-da/tests/service.rs index b256b73836..567260ef06 100644 --- a/crates/bitcoin-da/tests/service.rs +++ b/crates/bitcoin-da/tests/service.rs @@ -36,14 +36,15 @@ impl TestCase for BitcoinServiceTest { to_light_client_prefix: TO_LIGHT_CLIENT_PREFIX.to_vec(), }); - let block = generate_mock_txs(&service, da_node, &mut task_manager).await; + let (block, block_commitments, block_proofs) = + generate_mock_txs(&service, da_node, &mut task_manager).await; let block_wtxids = block .txdata .iter() .map(|tx| tx.compute_wtxid().as_raw_hash().to_byte_array()) .collect::>(); - // let sequencer_pubkey = da_node.client().get_address_info(address).await.unwrap().pubkey.unwrap().to_bytes(); + let pubkey; // Extracts relevant batch proof blobs with proof correctly { @@ -62,6 +63,15 @@ impl TestCase for BitcoinServiceTest { assert!(wtxid.starts_with(TO_BATCH_PROOF_PREFIX)); assert!(block_wtxids.contains(&wtxid)); } + + // Since only one of the transactions has a malformed sender, we have to find the + // tx that is not malformed, and get its public key + pubkey = if txs[0].sender == txs[1].sender || txs[0].sender == txs[2].sender { + txs[0].sender.0.clone() + } else { + txs[1].sender.0.clone() + }; + // Ensure that the produced outputs are verifiable by the verifier assert_eq!( verifier.verify_transactions( @@ -107,10 +117,19 @@ impl TestCase for BitcoinServiceTest { // Extract relevant sequencer commitments { - // let commitments = service - // .extract_relevant_sequencer_commitments(&block, &[1, 2, 3, 4, 5]) - // .unwrap(); - // assert_eq!(commitments.len(), 3); + let commitments = service + .extract_relevant_sequencer_commitments(&block, &pubkey) + .unwrap(); + assert_eq!(commitments, block_commitments); + } + + // Extract relevant zk proofs + { + let proofs = service + .extract_relevant_zk_proofs(&block, &pubkey) + .await + .unwrap(); + assert_eq!(proofs, block_proofs); } task_manager.abort().await; diff --git a/crates/bitcoin-da/tests/test_utils.rs b/crates/bitcoin-da/tests/test_utils.rs index 1fe2e8b7b3..656d5aec61 100644 --- a/crates/bitcoin-da/tests/test_utils.rs +++ b/crates/bitcoin-da/tests/test_utils.rs @@ -76,8 +76,8 @@ pub async fn get_service( } /// Generates mock commitment and zk proof transactions and publishes a DA block -/// with all mock transactions in it, and returns the block. Transactions also contain -/// invalid commitment and zk proof transactions. +/// with all mock transactions in it, and returns the block, valid commitments and proofs. +/// Transactions also contain invalid commitment and zk proof transactions. /// /// In total it generates 28 transactions. /// - Valid commitments: 3 (6 txs) @@ -91,7 +91,7 @@ pub async fn generate_mock_txs( da_service: &BitcoinService, da_node: &BitcoinNode, task_manager: &mut TaskManager<()>, -) -> BitcoinBlock { +) -> (BitcoinBlock, Vec, Vec>) { // Funding wallet requires block generation, hence we do funding at the beginning // to be able to write all transactions into the same block. let wrong_prefix_wallet = "wrong_prefix".to_string(); @@ -121,27 +121,35 @@ pub async fn generate_mock_txs( // Generate 100 blocks for wallets to get their rewards finalize_funds(da_node).await; + let mut valid_commitments = vec![]; + let mut valid_proofs = vec![]; + + let commitment = SequencerCommitment { + merkle_root: [13; 32], + l2_start_block_number: 1002, + l2_end_block_number: 1100, + }; + valid_commitments.push(commitment.clone()); da_service - .send_transaction(DaData::SequencerCommitment(SequencerCommitment { - merkle_root: [13; 32], - l2_start_block_number: 1002, - l2_end_block_number: 1100, - })) + .send_transaction(DaData::SequencerCommitment(commitment)) .await .expect("Failed to send transaction"); + let commitment = SequencerCommitment { + merkle_root: [14; 32], + l2_start_block_number: 1101, + l2_end_block_number: 1245, + }; + valid_commitments.push(commitment.clone()); da_service - .send_transaction(DaData::SequencerCommitment(SequencerCommitment { - merkle_root: [14; 32], - l2_start_block_number: 1101, - l2_end_block_number: 1245, - })) + .send_transaction(DaData::SequencerCommitment(commitment)) .await .expect("Failed to send transaction"); let size = 2000; let blob = (0..size).map(|_| rand::random::()).collect::>(); + valid_proofs.push(blob.clone()); da_service .send_transaction(DaData::ZKProof(blob)) .await @@ -151,6 +159,7 @@ pub async fn generate_mock_txs( let size = MAX_TXBODY_SIZE + 1500; let blob = (0..size).map(|_| rand::random::()).collect::>(); + valid_proofs.push(blob.clone()); da_service .send_transaction(DaData::ZKProof(blob)) .await @@ -169,6 +178,7 @@ pub async fn generate_mock_txs( let size = 1024; let blob = (0..size).map(|_| rand::random::()).collect::>(); + valid_proofs.push(blob.clone()); da_service .send_transaction(DaData::ZKProof(blob)) .await @@ -184,12 +194,14 @@ pub async fn generate_mock_txs( .await .expect("Failed to send transaction"); + let commitment = SequencerCommitment { + merkle_root: [15; 32], + l2_start_block_number: 1246, + l2_end_block_number: 1268, + }; + valid_commitments.push(commitment.clone()); da_service - .send_transaction(DaData::SequencerCommitment(SequencerCommitment { - merkle_root: [15; 32], - l2_start_block_number: 1246, - l2_end_block_number: 1268, - })) + .send_transaction(DaData::SequencerCommitment(commitment)) .await .expect("Failed to send transaction"); @@ -197,6 +209,7 @@ pub async fn generate_mock_txs( let size = MAX_TXBODY_SIZE * 2 + 2500; let blob = (0..size).map(|_| rand::random::()).collect::>(); + valid_proofs.push(blob.clone()); da_service .send_transaction(DaData::ZKProof(blob)) .await @@ -208,7 +221,7 @@ pub async fn generate_mock_txs( let block = da_service.get_block_by_hash(block_hash).await.unwrap(); assert_eq!(block.txdata.len(), 29); - block + (block, valid_commitments, valid_proofs) } // TODO: make this work diff --git a/crates/bitcoin-da/tests/verifier.rs b/crates/bitcoin-da/tests/verifier.rs index a3b8bc7f86..3f6b609146 100644 --- a/crates/bitcoin-da/tests/verifier.rs +++ b/crates/bitcoin-da/tests/verifier.rs @@ -38,7 +38,7 @@ impl TestCase for BitcoinVerifierTest { let da_node = f.bitcoin_nodes.get(0).unwrap(); let service = get_default_service(&mut task_manager, &da_node.config).await; - let block = generate_mock_txs(&service, da_node, &mut task_manager).await; + let (block, _, _) = generate_mock_txs(&service, da_node, &mut task_manager).await; let (b_txs, b_inclusion_proof, b_completeness_proof) = service.extract_relevant_blobs_with_proof(&block, DaNamespace::ToBatchProver); From 12371670b582cc64bf7155694cb27d5698c6b0a1 Mon Sep 17 00:00:00 2001 From: yaziciahmet Date: Mon, 25 Nov 2024 14:04:43 +0100 Subject: [PATCH 14/24] Make nonsegwit test work --- crates/bitcoin-da/src/helpers/parsers.rs | 2 +- crates/bitcoin-da/tests/test_utils.rs | 117 +++++++++-------------- crates/bitcoin-da/tests/verifier.rs | 4 +- 3 files changed, 48 insertions(+), 75 deletions(-) diff --git a/crates/bitcoin-da/src/helpers/parsers.rs b/crates/bitcoin-da/src/helpers/parsers.rs index 636e263076..1dd4d756a7 100644 --- a/crates/bitcoin-da/src/helpers/parsers.rs +++ b/crates/bitcoin-da/src/helpers/parsers.rs @@ -469,7 +469,7 @@ mod batch_proof { } } -#[cfg(all(test, feature = "native"))] +#[cfg(feature = "native")] pub fn parse_hex_transaction( tx_hex: &str, ) -> Result { diff --git a/crates/bitcoin-da/tests/test_utils.rs b/crates/bitcoin-da/tests/test_utils.rs index 656d5aec61..f18449a84c 100644 --- a/crates/bitcoin-da/tests/test_utils.rs +++ b/crates/bitcoin-da/tests/test_utils.rs @@ -1,13 +1,16 @@ -use std::collections::HashMap; use std::path::PathBuf; +use std::str::FromStr; use std::sync::Arc; -use bitcoin::Amount; +use bitcoin::block::{Header, Version}; +use bitcoin::hashes::Hash; +use bitcoin::{BlockHash, CompactTarget, TxMerkleNode, WitnessMerkleNode}; +use bitcoin_da::helpers::parsers::parse_hex_transaction; use bitcoin_da::service::{BitcoinService, BitcoinServiceConfig}; use bitcoin_da::spec::block::BitcoinBlock; +use bitcoin_da::spec::header::HeaderWrapper; +use bitcoin_da::spec::transaction::TransactionWrapper; use bitcoin_da::spec::RollupParams; -use bitcoin_da::verifier::WITNESS_COMMITMENT_PREFIX; -use bitcoincore_rpc::json::{AddressType, CreateRawTransactionInput, FundRawTransactionOptions}; use bitcoincore_rpc::RpcApi; use citrea_common::tasks::manager::TaskManager; use citrea_e2e::bitcoin::BitcoinNode; @@ -224,76 +227,46 @@ pub async fn generate_mock_txs( (block, valid_commitments, valid_proofs) } -// TODO: make this work -pub async fn generate_nonsegwit_block( - da_node: &BitcoinNode, - da_service: &BitcoinService, -) -> BitcoinBlock { - let client = da_node.client(); - let address = client - .get_new_address(Some("nonsegwit_address"), Some(AddressType::Legacy)) - .await - .unwrap() - .assume_checked(); - - client.generate_to_address(5, &address).await.unwrap(); - finalize_funds(da_node).await; - - let utxos = client - .list_unspent(Some(0), None, Some(&[&address]), None, None) - .await - .unwrap(); - assert_eq!(utxos.len(), 5); - - let input = CreateRawTransactionInput { - txid: utxos[0].txid, - vout: utxos[0].vout, - sequence: None, - }; - let mut output = HashMap::new(); - output.insert(address.to_string(), utxos[0].amount / 2); - output.insert(address.to_string(), utxos[0].amount / 2); - - let raw_tx = client - .create_raw_transaction(&[input], &output, None, None) - .await - .unwrap(); - - let funded_tx = client - .fund_raw_transaction( - &raw_tx, - Some(&FundRawTransactionOptions { - change_address: Some(address.clone()), - fee_rate: Some(Amount::ONE_SAT * 1000), - ..Default::default() - }), - None, +pub fn get_mock_nonsegwit_block() -> BitcoinBlock { + // There are no relevant txs + let txs = std::fs::read_to_string("test_data/mock_non_segwit_txs.txt").unwrap(); + // txs[2] is a non-segwit tx but its txid has the prefix 00 + let txs: Vec = txs + .lines() + .map(|tx| parse_hex_transaction(tx).unwrap()) + .map(Into::into) + .collect(); + + let header = HeaderWrapper::new( + Header { + version: Version::from_consensus(536870912), + prev_blockhash: BlockHash::from_str( + "6b15a2e4b17b0aabbd418634ae9410b46feaabf693eea4c8621ffe71435d24b0", + ) + .unwrap(), + merkle_root: TxMerkleNode::from_slice(&[ + 164, 71, 72, 235, 241, 189, 131, 141, 120, 210, 207, 233, 212, 171, 56, 52, 25, 40, + 83, 62, 135, 211, 81, 44, 3, 109, 10, 127, 210, 213, 124, 221, + ]) + .unwrap(), + time: 1694177029, + bits: CompactTarget::from_unprefixed_hex("207fffff").unwrap(), + nonce: 0, + }, + 6, + 2, + WitnessMerkleNode::from_str( + "a8b25755ed6e2f1df665b07e751f6acc1ff4e1ec765caa93084176e34fa5ad71", ) - .await - .unwrap(); - - let signed_tx = client - .sign_raw_transaction_with_wallet(&funded_tx.hex, None, None) - .await - .unwrap(); - - client.send_raw_transaction(&signed_tx.hex).await.unwrap(); - - let block_hash = da_node.generate(1).await.unwrap()[0]; + .unwrap() + .to_raw_hash() + .to_byte_array(), + ); - let block = da_service.get_block_by_hash(block_hash).await.unwrap(); - let txs = block.txdata.as_slice(); - - // ensure that block does not have any segwit txs - let idx = txs[0].output.iter().position(|output| { - output - .script_pubkey - .to_bytes() - .starts_with(WITNESS_COMMITMENT_PREFIX) - }); - assert_eq!(idx, None); - - block + BitcoinBlock { + header, + txdata: txs, + } } /// Creates and funds a wallet. Funds are not finalized until `finalize_funds` is called. diff --git a/crates/bitcoin-da/tests/verifier.rs b/crates/bitcoin-da/tests/verifier.rs index 3f6b609146..ee9054772d 100644 --- a/crates/bitcoin-da/tests/verifier.rs +++ b/crates/bitcoin-da/tests/verifier.rs @@ -18,7 +18,7 @@ use sov_rollup_interface::da::{DaNamespace, DaVerifier}; use sov_rollup_interface::services::da::DaService; use test_utils::macros::assert_panic; use test_utils::{ - generate_mock_txs, generate_nonsegwit_block, get_citrea_path, get_default_service, + generate_mock_txs, get_citrea_path, get_default_service, get_mock_nonsegwit_block, }; struct BitcoinVerifierTest; @@ -105,7 +105,7 @@ impl TestCase for BitcoinVerifierTest { // Test non-segwit block { - let nonsegwit_block = generate_nonsegwit_block(da_node, &service).await; + let nonsegwit_block = get_mock_nonsegwit_block(); let txs = nonsegwit_block.txdata.as_slice(); let tree = BitcoinMerkleTree::new( From 46f5cfeb7c52bb34e4cf82b052402872329fb6f5 Mon Sep 17 00:00:00 2001 From: yaziciahmet Date: Mon, 25 Nov 2024 14:31:10 +0100 Subject: [PATCH 15/24] Remove unnecessary test check --- crates/bitcoin-da/tests/service.rs | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/crates/bitcoin-da/tests/service.rs b/crates/bitcoin-da/tests/service.rs index 567260ef06..68303463bb 100644 --- a/crates/bitcoin-da/tests/service.rs +++ b/crates/bitcoin-da/tests/service.rs @@ -56,14 +56,6 @@ impl TestCase for BitcoinServiceTest { assert_eq!(txs.len(), 4); assert_eq!(completeness_proof.len(), 4); - let completeness_wtxids = completeness_proof - .iter() - .map(|tx| tx.compute_wtxid().as_raw_hash().to_byte_array()); - for wtxid in completeness_wtxids { - assert!(wtxid.starts_with(TO_BATCH_PROOF_PREFIX)); - assert!(block_wtxids.contains(&wtxid)); - } - // Since only one of the transactions has a malformed sender, we have to find the // tx that is not malformed, and get its public key pubkey = if txs[0].sender == txs[1].sender || txs[0].sender == txs[2].sender { @@ -95,13 +87,6 @@ impl TestCase for BitcoinServiceTest { assert_eq!(txs.len(), 4); assert_eq!(completeness_proof.len(), 4); - let completeness_wtxids = completeness_proof - .iter() - .map(|tx| tx.compute_wtxid().as_raw_hash().to_byte_array()); - for wtxid in completeness_wtxids { - assert!(wtxid.starts_with(TO_LIGHT_CLIENT_PREFIX)); - assert!(block_wtxids.contains(&wtxid)); - } // Ensure that the produced outputs are verifiable by the verifier assert_eq!( verifier.verify_transactions( From 130f51e1ebf5bfbc8489440e39b6bdcda9b67db6 Mon Sep 17 00:00:00 2001 From: yaziciahmet Date: Mon, 25 Nov 2024 15:24:04 +0100 Subject: [PATCH 16/24] Carry nonsegwit txs into code --- crates/bitcoin-da/tests/test_utils.rs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/crates/bitcoin-da/tests/test_utils.rs b/crates/bitcoin-da/tests/test_utils.rs index f18449a84c..5b83851457 100644 --- a/crates/bitcoin-da/tests/test_utils.rs +++ b/crates/bitcoin-da/tests/test_utils.rs @@ -229,10 +229,17 @@ pub async fn generate_mock_txs( pub fn get_mock_nonsegwit_block() -> BitcoinBlock { // There are no relevant txs - let txs = std::fs::read_to_string("test_data/mock_non_segwit_txs.txt").unwrap(); // txs[2] is a non-segwit tx but its txid has the prefix 00 + let txs = [ + "01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0f048bdb051a02e503062f503253482fffffffff0140e10d2a01000000434104808df9f502a2f1a2dd1848bade4be111b9f2e66d5c4bd6b9f1682b4d04a53bdb052ebb91ae056dc8a3cd59545265947ee61d54c49aa81981d550bf7f9167ef12ac00000000", + "01000000016a5aa0c54e24722d2cd6be99c26b3729fae9b7c27c851b080aa45c5b47c26d2d010000008a47304402201c9404cd4a8b21509834fafff15d700788ffaa842f760e1ab1dc173fa2676ec202202c7e77ad14e48320a272952db318f9f968bcefcf50123f0f40db410d6cd300bc01410491d63a7c33798ca1da6a88ea5cd8daf9c33190571ca4738306b8848466b8494619a0d217a39d4bb0c929735f9c4a1c0dea074239e153b81b9b7cfc85dd36faf8ffffffff0200ec6021000000001976a9146c11a5e60863b35f85d3911920a1aecf11f7153988ac40a8f527000000001976a914ccaf060b633fe6b1f43e2ecc8e0f17adf09d534c88ac00000000", + "01000000013377c58db37da73db2c3a269ddf410251073673983790ab5426e215f323ce00f010000008c493046022100fa6a7c25870c377080c1b5b42d216d501ac971ac09507ce079bdaf6da5b046ec022100fa885eef30ffa7a8768a30a5faac6cace724851cd53806413b10aec060bc274a0141049a162e57d5e0f96374f8ead29937ab5a90385f07678b159da4f57cb87b646c148f56a870fb779a9037fabf5fdacc753b34eb98d49e300c36e9b0f3873194e759ffffffff02002d3101000000001976a91406f1b66ffe49df7fce684df16c62f59dc9adbd3f88ac90762907000000001976a9144ef9f0e7ad583d773495722dd79ec11188b9e4fd88ac00000000", + "0100000001c9e1effb36254352bca658cfc7b06d6d358cbbffda74dc1c6fb7e25ff3fde256010000008c493046022100d871f859bf9cc2be5080194ed0c38e977e83c212be7150d7d0b65a7704bd830f022100e27f5d8922d7d977b690386457f7fb9c714241ab9d7b91bf05fbac68f2dc69b001410449f6c65c3ba451e4891f8e51e46580c7fcb87480bf5aa2f9d47644ff7b692cfa801d95f6980cef95fb49b3ec42c6ff4ed289d948f03c7f409b34647d0fedf803ffffffff0220651100000000001976a914d0b79214b73d2cba68b524ae1c0f102771e7551c88aca016f912000000001976a9142af648c077286a6c1233eb190bcd767478ced70d88ac00000000", + "01000000020792275b6ad62da82d98eaebb5de782642a06c92a80872c2cb3354da52c1ba2e000000008b483045022100fdc067f20ee84e3a4aea25638eb125b44376555ffbdd0fa05611a27a55d2610f022009b3c111bffae5e517bf957bd3f53a3306a6b5c5725418955f20afb6fbd1bb380141048cc0b94178715f03ed3d0bceb368191d0fdd7fc16d806567f6f2c45aecafb8f53e5ef849564072189b9b4f8bfe1564da776567ba359cfb0c05e839bcf65371abffffffff2316672bbcf879e3a96a2e8aab283b44529c4eec8fed798e5e435011c2b5059b010000008b48304502204caab3248930be319ba445c44398aa94e0032dfd25456d09ccd2b076737ef2f6022100eb460ca09390a67b8195fad6c4d0563d243b824ece6bafb69d21ca30542cfaa5014104952fe2d53debd645dffac11367b3888a9e5465eb9c150b98a504d7f8d4c3e98c1a6876175be17462d163e6015ce12c9c2b0a3629e1371e247109222d0b8ed5dbffffffff0230578d09000000001976a9144ef9f0e7ad583d773495722dd79ec11188b9e4fd88ac18366704000000001976a914b4f5b5a9e5119d3f0327d4ff64a1b0a97fc423d988ac00000000", + "01000000019ac1695d2e613e3bee66317bbd9ad8ec4033f596ce5c43691837e8322b3a8112010000008c49304602210083a64c8ff430ac05376ab5f940d7801796d6dd0687922af2c8f7247368a41f56022100d55326fdc50a70d992f0a04ef19e5d8edc244941d9976d609ab1b6c1ed2e92760141046992f8f0bdde46834e24df367c28233501fa8615ada18c84631b84f85eb4af10aebd92dec0a870e038fdd9820aae836edba7ba2fae915d6fc25e5727621adc1dffffffff0260011200000000001976a9147c442f8fcb7c525720ee3b587e561fffe028c16d88ac1088810e000000001976a91460b18c23c1d6139e337a306413119f6e9efda4e388ac00000000", + ]; let txs: Vec = txs - .lines() + .into_iter() .map(|tx| parse_hex_transaction(tx).unwrap()) .map(Into::into) .collect(); From e1b506b91acd92afdbf1134b7aa4b30a2973ae48 Mon Sep 17 00:00:00 2001 From: yaziciahmet Date: Mon, 25 Nov 2024 16:22:28 +0100 Subject: [PATCH 17/24] Implement rest of the service tests --- crates/bitcoin-da/tests/service.rs | 66 ++++++++++++++++++++++++++- crates/bitcoin-da/tests/test_utils.rs | 47 ++++++++++++++++++- 2 files changed, 111 insertions(+), 2 deletions(-) diff --git a/crates/bitcoin-da/tests/service.rs b/crates/bitcoin-da/tests/service.rs index 68303463bb..19f47c490e 100644 --- a/crates/bitcoin-da/tests/service.rs +++ b/crates/bitcoin-da/tests/service.rs @@ -1,7 +1,13 @@ mod test_utils; +use std::collections::HashMap; +use std::str::FromStr; + use async_trait::async_trait; use bitcoin::hashes::Hash; +use bitcoin::key::Secp256k1; +use bitcoin::secp256k1::SecretKey; +use bitcoin_da::service::get_relevant_blobs_from_txs; use bitcoin_da::spec::RollupParams; use bitcoin_da::verifier::BitcoinVerifier; use citrea_common::tasks::manager::TaskManager; @@ -12,7 +18,10 @@ use citrea_e2e::Result; use citrea_primitives::{TO_BATCH_PROOF_PREFIX, TO_LIGHT_CLIENT_PREFIX}; use sov_rollup_interface::da::{DaNamespace, DaVerifier}; use sov_rollup_interface::services::da::DaService; -use test_utils::{generate_mock_txs, get_citrea_path, get_default_service}; +use test_utils::{ + generate_mock_txs, get_citrea_path, get_default_service, get_mock_false_signature_txs_block, + DEFAULT_DA_PRIVATE_KEY, +}; struct BitcoinServiceTest; @@ -117,6 +126,61 @@ impl TestCase for BitcoinServiceTest { assert_eq!(proofs, block_proofs); } + // Batch proof tx blob signed with different private key should still be + // returned as blob with sender recovered correctly. + { + let secp = Secp256k1::new(); + let wrong_secret = SecretKey::from_str( + "E9873D79C6D87DC0FB6A5778633389F4453213303DA61F20BD67FC233AA33261", + ) + .unwrap(); + let wrong_pubkey = wrong_secret + .keypair(&secp) + .public_key() + .serialize() + .to_vec(); + + let false_sig_block = get_mock_false_signature_txs_block(); + + let (txs, _, _) = service + .extract_relevant_blobs_with_proof(&false_sig_block, DaNamespace::ToBatchProver); + // There is one tx with right prefix, but wrong signature + assert_eq!(txs.len(), 1); + assert_eq!(txs[0].sender.0, wrong_pubkey); + } + + { + let secp = bitcoin::secp256k1::Secp256k1::new(); + let secret = SecretKey::from_str(DEFAULT_DA_PRIVATE_KEY).unwrap(); + let da_pubkey = secret.keypair(&secp).public_key().serialize().to_vec(); + + let wrong_secret = SecretKey::from_str( + "E9873D79C6D87DC0FB6A5778633389F4453213303DA61F20BD67FC233AA33263", + ) + .unwrap(); + let wrong_pubkey = wrong_secret + .keypair(&secp) + .public_key() + .serialize() + .to_vec(); + + let txs = get_relevant_blobs_from_txs( + block.txdata.iter().map(|tx| tx.inner().clone()).collect(), + TO_BATCH_PROOF_PREFIX, + ); + assert_eq!(txs.len(), 4); + + // Count the number of transactions occurring per public key + let tx_count_of_pubkey = txs.into_iter().fold(HashMap::new(), |mut acc, tx| { + *acc.entry(tx.sender.0).or_insert(0) += 1; + acc + }); + // 3 valid sequencer commitments + assert_eq!(tx_count_of_pubkey.get(&da_pubkey).unwrap(), &3); + // 1 invalid sequencer commitment due to different key + assert_eq!(tx_count_of_pubkey.get(&wrong_pubkey).unwrap(), &1); + } + task_manager.abort().await; Ok(()) } diff --git a/crates/bitcoin-da/tests/test_utils.rs b/crates/bitcoin-da/tests/test_utils.rs index 5b83851457..e45d14d3ca 100644 --- a/crates/bitcoin-da/tests/test_utils.rs +++ b/crates/bitcoin-da/tests/test_utils.rs @@ -21,7 +21,7 @@ use citrea_primitives::{MAX_TXBODY_SIZE, TO_BATCH_PROOF_PREFIX, TO_LIGHT_CLIENT_ use sov_rollup_interface::da::{DaData, SequencerCommitment}; use sov_rollup_interface::services::da::DaService; -const DEFAULT_DA_PRIVATE_KEY: &str = +pub const DEFAULT_DA_PRIVATE_KEY: &str = "E9873D79C6D87DC0FB6A5778633389F4453213303DA61F20BD67FC233AA33262"; pub async fn get_default_service( @@ -227,6 +227,7 @@ pub async fn generate_mock_txs( (block, valid_commitments, valid_proofs) } +#[allow(unused)] pub fn get_mock_nonsegwit_block() -> BitcoinBlock { // There are no relevant txs // txs[2] is a non-segwit tx but its txid has the prefix 00 @@ -276,6 +277,50 @@ pub fn get_mock_nonsegwit_block() -> BitcoinBlock { } } +#[allow(unused)] +pub fn get_mock_false_signature_txs_block() -> BitcoinBlock { + let txs = [ + "020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0402aa0800ffffffff026faa040000000000160014fa5554be100ee542587688a93e7c2ac37478bdc60000000000000000266a24aa21a9ed494880ce756f69b13811200d1e358a049ac3c3dd66e4ff7e86d4c4d3aad959390120000000000000000000000000000000000000000000000000000000000000000000000000", + "020000000001015ada1242404efd013244c1361f3207d3e34a1f0c786a96b334203bcb317dc9e40100000000fdffffff025e0300000000000022512057c195448a1acba9a08b93aa31fc224988f0e1f517908ea814bd4b052dee3df8af31000000000000160014ba033fad8b4899045c892787ff716877e5f8e18102473044022066e4bafa74ad683ecee03d2a9502ed5bda1c2c791efdc91cd82f47f0a7d139e102207a52d868d9e3f2ceeafb0c01c0f6b3e5db43ec41969d5e9740ebb86f8537b0e00121034716b0a10b8e9a64acfa721fccaf7202c5156a2686332b0ec72bbb257d2dbe0600000000", + "0200000000010171f88e369556505b8c5ca67625daf351d79b9fec757ba9d1259a0b63b2338b3a0000000000fdffffff012202000000000000160014ba033fad8b4899045c892787ff716877e5f8e18103409646f749c4d980a427151c31f9e1129327d4d311caab29bfa7702d674c8aaa1c9fea6547c5c947b9745a5f3ecdeb3235d8b9b0cae6a861df2e43b245c5b16161c72059975a92015d3ca4b95d3b3faffd5003ad389ab064e1144f6a5144e4e6f56a58ad020000006340bd068f826f4ca54e7d2aa7133a0ac9f945ac0e3904ea8ed203b35f430e02d5b07ed2fb9dd6e24f7930a56de2f6772f62d0b453a68ec6a5e58ef6de0094e39d6621035c4edf0c1cc8e9d8eab292be0eee726de6ece392529d5159e75f0fd68609a4b331000d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0dea030000000000004c040000000000006808f9420000000000007721c059975a92015d3ca4b95d3b3faffd5003ad389ab064e1144f6a5144e4e6f56a5800000000", + ]; + let txs: Vec = txs + .into_iter() + .map(|tx| parse_hex_transaction(tx).unwrap()) + .map(Into::into) + .collect(); + + let header = HeaderWrapper::new( + Header { + version: Version::from_consensus(536870912), + prev_blockhash: BlockHash::from_str( + "31402555f54c3f89907c07e6d286c132f9984739f2b6b00cde195b10ac771522", + ) + .unwrap(), + merkle_root: TxMerkleNode::from_str( + "40642938a6cc6124246fd9601108f9671177c1834753162f19e073eaff751191", + ) + .unwrap(), + time: 1724665818, + bits: CompactTarget::from_unprefixed_hex("207fffff").unwrap(), + nonce: 3, + }, + 3, + 1, + WitnessMerkleNode::from_str( + "494880ce756f69b13811200d1e358a049ac3c3dd66e4ff7e86d4c4d3aad95939", + ) + .unwrap() + .as_raw_hash() + .to_byte_array(), + ); + + BitcoinBlock { + header, + txdata: txs, + } +} + /// Creates and funds a wallet. Funds are not finalized until `finalize_funds` is called. async fn create_and_fund_wallet(wallet: String, da_node: &BitcoinNode) { da_node From ab8c155574ecdb454058dde92cd41b524ff75535 Mon Sep 17 00:00:00 2001 From: yaziciahmet Date: Mon, 25 Nov 2024 20:03:16 +0100 Subject: [PATCH 18/24] Implemented all tests now --- crates/bitcoin-da/tests/test_utils.rs | 53 +++++++++- crates/bitcoin-da/tests/verifier.rs | 138 +++++++++++++++++++++++++- 2 files changed, 184 insertions(+), 7 deletions(-) diff --git a/crates/bitcoin-da/tests/test_utils.rs b/crates/bitcoin-da/tests/test_utils.rs index e45d14d3ca..186d103de4 100644 --- a/crates/bitcoin-da/tests/test_utils.rs +++ b/crates/bitcoin-da/tests/test_utils.rs @@ -4,9 +4,13 @@ use std::sync::Arc; use bitcoin::block::{Header, Version}; use bitcoin::hashes::Hash; -use bitcoin::{BlockHash, CompactTarget, TxMerkleNode, WitnessMerkleNode}; -use bitcoin_da::helpers::parsers::parse_hex_transaction; +use bitcoin::{BlockHash, CompactTarget, Transaction, TxMerkleNode, WitnessMerkleNode}; +use bitcoin_da::helpers::parsers::{ + parse_batch_proof_transaction, parse_hex_transaction, parse_light_client_transaction, + ParsedBatchProofTransaction, ParsedLightClientTransaction, VerifyParsed, +}; use bitcoin_da::service::{BitcoinService, BitcoinServiceConfig}; +use bitcoin_da::spec::blob::BlobWithSender; use bitcoin_da::spec::block::BitcoinBlock; use bitcoin_da::spec::header::HeaderWrapper; use bitcoin_da::spec::transaction::TransactionWrapper; @@ -17,6 +21,7 @@ use citrea_e2e::bitcoin::BitcoinNode; use citrea_e2e::config::BitcoinConfig; use citrea_e2e::node::NodeKind; use citrea_e2e::traits::NodeT; +use citrea_primitives::compression::decompress_blob; use citrea_primitives::{MAX_TXBODY_SIZE, TO_BATCH_PROOF_PREFIX, TO_LIGHT_CLIENT_PREFIX}; use sov_rollup_interface::da::{DaData, SequencerCommitment}; use sov_rollup_interface::services::da::DaService; @@ -368,6 +373,50 @@ fn get_workspace_root() -> PathBuf { .to_path_buf() } +#[allow(unused)] +pub enum MockData { + ToBatchProver, + ToLightClient, +} + +#[allow(unused)] +pub fn get_blob_with_sender(tx: &Transaction, ty: MockData) -> anyhow::Result { + let (blob, public_key, hash) = match ty { + MockData::ToBatchProver => { + let parsed_tx = parse_batch_proof_transaction(tx)?; + match parsed_tx { + ParsedBatchProofTransaction::SequencerCommitment(seq_com) => { + let hash = seq_com + .get_sig_verified_hash() + .expect("Invalid sighash on commitment"); + (seq_com.body, seq_com.public_key, hash) + } + } + } + MockData::ToLightClient => { + let parsed_tx = parse_light_client_transaction(tx)?; + match parsed_tx { + ParsedLightClientTransaction::Complete(complete) => { + let hash = complete + .get_sig_verified_hash() + .expect("Invalid sighash on complete zk proof"); + let blob = decompress_blob(&complete.body); + (blob, complete.public_key, hash) + } + ParsedLightClientTransaction::Aggregate(aggregate) => { + let hash = aggregate + .get_sig_verified_hash() + .expect("Invalid sighash on aggregate zk proof"); + (aggregate.body, aggregate.public_key, hash) + } + _ => unimplemented!(), + } + } + }; + + Ok(BlobWithSender::new(blob.clone(), public_key, hash)) +} + // For some reason, even though macro is used, it sees it as unused #[allow(unused)] pub mod macros { diff --git a/crates/bitcoin-da/tests/verifier.rs b/crates/bitcoin-da/tests/verifier.rs index ee9054772d..f93d5bc3da 100644 --- a/crates/bitcoin-da/tests/verifier.rs +++ b/crates/bitcoin-da/tests/verifier.rs @@ -2,12 +2,13 @@ mod test_utils; use async_trait::async_trait; use bitcoin::hashes::Hash; +use bitcoin::{ScriptBuf, Witness}; use bitcoin_da::helpers::merkle_tree::BitcoinMerkleTree; use bitcoin_da::helpers::parsers::{parse_light_client_transaction, ParsedLightClientTransaction}; use bitcoin_da::spec::blob::BlobWithSender; use bitcoin_da::spec::proof::InclusionMultiProof; use bitcoin_da::spec::RollupParams; -use bitcoin_da::verifier::{BitcoinVerifier, ValidationError}; +use bitcoin_da::verifier::{BitcoinVerifier, ValidationError, WITNESS_COMMITMENT_PREFIX}; use citrea_common::tasks::manager::TaskManager; use citrea_e2e::config::TestCaseConfig; use citrea_e2e::framework::TestFramework; @@ -18,7 +19,8 @@ use sov_rollup_interface::da::{DaNamespace, DaVerifier}; use sov_rollup_interface::services::da::DaService; use test_utils::macros::assert_panic; use test_utils::{ - generate_mock_txs, get_citrea_path, get_default_service, get_mock_nonsegwit_block, + generate_mock_txs, get_blob_with_sender, get_citrea_path, get_default_service, + get_mock_nonsegwit_block, MockData, }; struct BitcoinVerifierTest; @@ -136,17 +138,143 @@ impl TestCase for BitcoinVerifierTest { // False coinbase input witness should fail { - // TODO: do + let mut block_txs = block.txdata.clone(); + + // Malform witness + block_txs[0].input[0].witness = Witness::from_slice(&[vec![1; 32]]); + + // Recreate inclusion proof + let tree = BitcoinMerkleTree::new( + block_txs + .iter() + .map(|t| t.compute_txid().to_raw_hash().to_byte_array()) + .collect(), + ); + let mut inclusion_proof = InclusionMultiProof { + wtxids: block_txs + .iter() + .map(|t| t.compute_wtxid().to_byte_array()) + .collect(), + coinbase_tx: block_txs[0].clone(), + coinbase_merkle_proof: tree.get_idx_path(0), + }; + // Coinbase tx wtxid should be [0u8;32] + inclusion_proof.wtxids[0] = [0; 32]; + + assert!(verifier + .verify_transactions( + &block.header, + &b_txs, + inclusion_proof, + b_completeness_proof.clone(), + DaNamespace::ToBatchProver, + ) + .is_err()); } // False coinbase script pubkey should fail { - // TODO: do + let mut block_txs = block.txdata.clone(); + + let idx = block_txs[0] + .output + .iter() + .position(|output| { + output + .script_pubkey + .to_bytes() + .starts_with(WITNESS_COMMITMENT_PREFIX) + }) + .unwrap(); + // Malform coinbase script pubkey + let mut bytes = block_txs[0].output[idx].script_pubkey.to_bytes(); + bytes[0] = bytes[0].wrapping_add(1); + + block_txs[0].output[idx].script_pubkey = ScriptBuf::from_bytes(bytes); + + // Recreate inclusion proof + let tree = BitcoinMerkleTree::new( + block_txs + .iter() + .map(|t| t.compute_txid().to_raw_hash().to_byte_array()) + .collect(), + ); + let mut inclusion_proof = InclusionMultiProof { + wtxids: block_txs + .iter() + .map(|t| t.compute_wtxid().to_byte_array()) + .collect(), + coinbase_tx: block_txs[0].clone(), + coinbase_merkle_proof: tree.get_idx_path(0), + }; + // Coinbase tx wtxid should be [0u8;32] + inclusion_proof.wtxids[0] = [0; 32]; + + assert!(verifier + .verify_transactions( + &block.header, + &b_txs, + inclusion_proof, + b_completeness_proof.clone(), + DaNamespace::ToBatchProver, + ) + .is_err()); } // False witness script should fail { - // TODO: do + let mut block_txs = block.txdata.clone(); + let mut completeness_proof = b_completeness_proof.clone(); + + let relevant_tx = block_txs + .iter_mut() + .find(|tx| tx.compute_wtxid() == completeness_proof[0].compute_wtxid()) + .unwrap(); + + // Malform the witness + let mut malformed_witness = relevant_tx.input[0].witness.to_vec(); + malformed_witness[0][0] = malformed_witness[0][0].wrapping_add(1); + + completeness_proof[0].input[0].witness = Witness::from_slice(&malformed_witness); + relevant_tx.input[0].witness = Witness::from_slice(&malformed_witness); + assert_eq!( + completeness_proof[0].compute_wtxid(), + relevant_tx.compute_wtxid() + ); + + let tree = BitcoinMerkleTree::new( + block_txs + .iter() + .map(|t| t.compute_txid().to_raw_hash().to_byte_array()) + .collect(), + ); + + let mut inclusion_proof = InclusionMultiProof { + wtxids: block_txs + .iter() + .map(|t| t.compute_wtxid().to_byte_array()) + .collect(), + coinbase_tx: block_txs[0].clone(), + coinbase_merkle_proof: tree.get_idx_path(0), + }; + + // Coinbase tx wtxid should be [0u8;32] + inclusion_proof.wtxids[0] = [0; 32]; + + let txs = completeness_proof + .iter() + .filter_map(|tx| get_blob_with_sender(tx, MockData::ToBatchProver).ok()) + .collect::>(); + + assert!(verifier + .verify_transactions( + &block.header, + &txs, + inclusion_proof, + completeness_proof, + DaNamespace::ToBatchProver, + ) + .is_err()); } // Different witness ids should fail From 5688fd70712d40ca3c263b12d9259c1de538190b Mon Sep 17 00:00:00 2001 From: yaziciahmet Date: Mon, 25 Nov 2024 20:10:58 +0100 Subject: [PATCH 19/24] Clear up old tests --- crates/bitcoin-da/src/helpers/merkle_tree.rs | 7 - crates/bitcoin-da/src/helpers/mod.rs | 2 - crates/bitcoin-da/src/helpers/test_utils.rs | 154 ---- crates/bitcoin-da/src/service.rs | 552 ----------- crates/bitcoin-da/src/verifier.rs | 918 ------------------- 5 files changed, 1633 deletions(-) delete mode 100644 crates/bitcoin-da/src/helpers/test_utils.rs diff --git a/crates/bitcoin-da/src/helpers/merkle_tree.rs b/crates/bitcoin-da/src/helpers/merkle_tree.rs index 8630bad395..0b56568128 100644 --- a/crates/bitcoin-da/src/helpers/merkle_tree.rs +++ b/crates/bitcoin-da/src/helpers/merkle_tree.rs @@ -114,7 +114,6 @@ mod tests { use bitcoin::hashes::Hash; use super::*; - use crate::helpers::test_utils::get_mock_txs; #[test] fn test_merkle_root_with_proof() { @@ -144,12 +143,6 @@ mod tests { compare_merkle_tree_against_bitcoin_impl(vec![[255; 32]; 33]); compare_merkle_tree_against_bitcoin_impl(vec![[200; 32]; 2]); compare_merkle_tree_against_bitcoin_impl(vec![[99; 32]; 1]); - - let txs = get_mock_txs() - .iter() - .map(|tx| tx.compute_wtxid().to_byte_array()) - .collect(); - compare_merkle_tree_against_bitcoin_impl(txs); } fn compare_merkle_tree_against_bitcoin_impl(transactions: Vec<[u8; 32]>) { diff --git a/crates/bitcoin-da/src/helpers/mod.rs b/crates/bitcoin-da/src/helpers/mod.rs index a5eb760b0f..02bd20dc37 100644 --- a/crates/bitcoin-da/src/helpers/mod.rs +++ b/crates/bitcoin-da/src/helpers/mod.rs @@ -6,8 +6,6 @@ use sha2::{Digest, Sha256}; pub mod builders; pub mod merkle_tree; pub mod parsers; -#[cfg(test)] -pub mod test_utils; /// Type represents a typed enum for LightClient kind #[repr(u16)] diff --git a/crates/bitcoin-da/src/helpers/test_utils.rs b/crates/bitcoin-da/src/helpers/test_utils.rs deleted file mode 100644 index d53050bc40..0000000000 --- a/crates/bitcoin-da/src/helpers/test_utils.rs +++ /dev/null @@ -1,154 +0,0 @@ -use core::str::FromStr; - -use bitcoin::block::{Header, Version}; -use bitcoin::hash_types::{TxMerkleNode, WitnessMerkleNode}; -use bitcoin::hashes::Hash; -use bitcoin::{BlockHash, CompactTarget, Transaction}; -use citrea_primitives::compression::decompress_blob; -use sov_rollup_interface::da::{DaSpec, DaVerifier}; - -use super::merkle_tree; -use super::parsers::{parse_batch_proof_transaction, parse_light_client_transaction, ParserError}; -use crate::helpers::parsers::{parse_hex_transaction, VerifyParsed}; -use crate::spec::blob::BlobWithSender; -use crate::spec::header::HeaderWrapper; -use crate::spec::proof::InclusionMultiProof; -use crate::verifier::BitcoinVerifier; - -#[derive(Clone, Copy, Debug)] -pub(crate) enum MockData { - BatchProof, - LightClientProof, -} - -pub(crate) fn get_mock_txs() -> Vec { - // relevant txs are on 6, 8, 10, 12 indices - let txs = include_str!("../../test_data/mock_txs.txt"); - - txs.lines() - .map(|tx| parse_hex_transaction(tx).unwrap()) - .collect() -} - -pub(crate) fn get_blob_with_sender( - tx: &Transaction, - ty: MockData, -) -> Result { - let (blob, public_key, hash) = match ty { - MockData::BatchProof => { - let parsed_tx = parse_batch_proof_transaction(tx)?; - match parsed_tx { - super::parsers::ParsedBatchProofTransaction::SequencerCommitment(seq_com) => { - let hash = seq_com - .get_sig_verified_hash() - .expect("Invalid sighash on commitment"); - (seq_com.body, seq_com.public_key, hash) - } - } - } - MockData::LightClientProof => { - let parsed_tx = parse_light_client_transaction(tx)?; - match parsed_tx { - super::parsers::ParsedLightClientTransaction::Complete(complete) => { - let hash = complete - .get_sig_verified_hash() - .expect("Invalid sighash on complete zk proof"); - let blob = decompress_blob(&complete.body); - (blob, complete.public_key, hash) - } - super::parsers::ParsedLightClientTransaction::Aggregate(aggregate) => { - let hash = aggregate - .get_sig_verified_hash() - .expect("Invalid sighash on aggregate zk proof"); - (aggregate.body, aggregate.public_key, hash) - } - _ => unimplemented!(), - } - } - }; - - Ok(BlobWithSender::new(blob.clone(), public_key, hash)) -} - -#[allow(clippy::type_complexity)] -pub(crate) fn get_mock_data( - ty: MockData, -) -> ( - <::Spec as DaSpec>::BlockHeader, // block header - <::Spec as DaSpec>::InclusionMultiProof, // inclusion proof - <::Spec as DaSpec>::CompletenessProof, // completeness proof - Vec<<::Spec as DaSpec>::BlobTransaction>, // txs -) { - let header = HeaderWrapper::new( - Header { - version: Version::from_consensus(536870912), - prev_blockhash: BlockHash::from_str( - "426524a1b644fd8c77d32621f42a74486262bbc2eaeacf43d12cdee312885f42", - ) - .unwrap(), - merkle_root: TxMerkleNode::from_str( - "34ef858c354e8fd441e49fdc9266ca2bb760034c54b28fdb660254c2546295c8", - ) - .unwrap(), - time: 1724662940, - bits: CompactTarget::from_unprefixed_hex("207fffff").unwrap(), - nonce: 0, - }, - 36, - 1001, - WitnessMerkleNode::from_str( - "0467b591b054383ec433945d04063742f5aabb80e52a53bc2f8ded58d350a7c5", - ) - .unwrap() - .to_raw_hash() - .to_byte_array(), - ); - - let block_txs = get_mock_txs(); - - let relevant_txs_indices: &[usize] = match ty { - MockData::BatchProof => &[4, 6, 18, 28, 34], - MockData::LightClientProof => &[8, 14, 16, 32], - }; - - let completeness_proof = relevant_txs_indices - .iter() - .map(|i| block_txs[*i].clone()) - .map(Into::into) - .collect(); - - let tree = merkle_tree::BitcoinMerkleTree::new( - block_txs - .iter() - .map(|t| t.compute_txid().to_raw_hash().to_byte_array()) - .collect(), - ); - - let mut inclusion_proof = InclusionMultiProof { - wtxids: block_txs - .iter() - .map(|t| t.compute_wtxid().to_byte_array()) - .collect(), - coinbase_tx: block_txs[0].clone().into(), - coinbase_merkle_proof: tree.get_idx_path(0), - }; - - // Coinbase tx wtxid should be [0u8;32] - inclusion_proof.wtxids[0] = [0; 32]; - - let txs: Vec = relevant_txs_indices - .iter() - .filter_map(|i| get_blob_with_sender(&block_txs[*i], ty).ok()) - .collect(); - - (header, inclusion_proof, completeness_proof, txs) -} - -pub(crate) fn get_non_segwit_mock_txs() -> Vec { - // There are no relevant txs - let txs = std::fs::read_to_string("test_data/mock_non_segwit_txs.txt").unwrap(); - // txs[2] is a non-segwit tx but its txid has the prefix 00 - txs.lines() - .map(|tx| parse_hex_transaction(tx).unwrap()) - .collect() -} diff --git a/crates/bitcoin-da/src/service.rs b/crates/bitcoin-da/src/service.rs index effab7f041..24e924702a 100644 --- a/crates/bitcoin-da/src/service.rs +++ b/crates/bitcoin-da/src/service.rs @@ -1187,555 +1187,3 @@ fn calculate_witness_root(txdata: &[TransactionWrapper]) -> [u8; 32] { .collect(); BitcoinMerkleTree::new(hashes).root() } - -#[cfg(test)] -mod tests { - use core::str::FromStr; - use std::path::PathBuf; - use std::sync::Arc; - - // use futures::{Stream, StreamExt}; - use bitcoin::block::{Header, Version}; - use bitcoin::hash_types::{TxMerkleNode, WitnessMerkleNode}; - use bitcoin::hashes::Hash; - use bitcoin::secp256k1::Keypair; - use bitcoin::{BlockHash, CompactTarget}; - use citrea_common::tasks::manager::TaskManager; - use sov_rollup_interface::da::{DaNamespace, DaVerifier, SequencerCommitment}; - use sov_rollup_interface::services::da::{DaService, SlotData}; - - use super::{get_relevant_blobs_from_txs, BitcoinService}; - use crate::helpers::parsers::parse_hex_transaction; - use crate::helpers::test_utils::{get_mock_data, get_mock_txs, MockData}; - use crate::service::BitcoinServiceConfig; - use crate::spec::block::BitcoinBlock; - use crate::spec::header::HeaderWrapper; - use crate::spec::transaction::TransactionWrapper; - use crate::spec::RollupParams; - use crate::verifier::BitcoinVerifier; - - fn get_workspace_root() -> PathBuf { - let manifest_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); - manifest_dir - .ancestors() - .nth(2) - .expect("Failed to find workspace root") - .to_path_buf() - } - - fn get_tx_backup_dir() -> String { - let mut path = get_workspace_root(); - path.push("resources/bitcoin/inscription_txs"); - path.to_str().unwrap().to_string() - } - - async fn get_service(task_manager: &mut TaskManager<()>) -> Arc { - let runtime_config = BitcoinServiceConfig { - node_url: "http://localhost:38332/wallet/test".to_string(), - node_username: "chainway".to_string(), - node_password: "topsecret".to_string(), - network: bitcoin::Network::Regtest, - da_private_key: Some( - "E9873D79C6D87DC0FB6A5778633389F4453213303DA61F20BD67FC233AA33262".to_string(), // Test key, safe to publish - ), - tx_backup_dir: get_tx_backup_dir(), - monitoring: None, - }; - - let (tx, rx) = tokio::sync::mpsc::unbounded_channel(); - - let da_service = BitcoinService::new_without_wallet_check( - runtime_config, - RollupParams { - to_batch_proof_prefix: vec![1, 1], - to_light_client_prefix: vec![2, 2], - }, - tx, - ) - .await - .expect("Error initialazing BitcoinService"); - - let da_service = Arc::new(da_service); - task_manager.spawn(|tk| da_service.clone().run_da_queue(rx, tk)); - - #[allow(clippy::let_and_return)] - da_service - } - - async fn get_service_wrong_namespace( - task_manager: &mut TaskManager<()>, - ) -> Arc { - let runtime_config = BitcoinServiceConfig { - node_url: "http://localhost:38332/wallet/other".to_string(), - node_username: "chainway".to_string(), - node_password: "topsecret".to_string(), - network: bitcoin::Network::Regtest, - da_private_key: Some( - "E9873D79C6D87DC0FB6A5778633389F4453213303DA61F20BD67FC233AA33262".to_string(), // Test key, safe to publish - ), - tx_backup_dir: get_tx_backup_dir(), - monitoring: None, - }; - - let (tx, rx) = tokio::sync::mpsc::unbounded_channel(); - - let da_service = BitcoinService::new_without_wallet_check( - runtime_config, - RollupParams { - to_batch_proof_prefix: vec![5, 6], - to_light_client_prefix: vec![5, 5], - }, - tx, - ) - .await - .expect("Error initialazing BitcoinService"); - - let da_service = Arc::new(da_service); - task_manager.spawn(|tk| da_service.clone().run_da_queue(rx, tk)); - - da_service - } - - async fn get_service_correct_sig_different_public_key( - task_manager: &mut TaskManager<()>, - ) -> Arc { - let runtime_config = BitcoinServiceConfig { - node_url: "http://localhost:38332/wallet/other2".to_string(), - node_username: "chainway".to_string(), - node_password: "topsecret".to_string(), - network: bitcoin::Network::Regtest, - da_private_key: Some( - "E9873D79C6D87DC0FB6A5778633389F4453213303DA61F20BD67FC233AA33263".to_string(), // Test key, safe to publish - ), - tx_backup_dir: get_tx_backup_dir(), - monitoring: None, - }; - - let (tx, rx) = tokio::sync::mpsc::unbounded_channel(); - - let da_service = BitcoinService::new_without_wallet_check( - runtime_config, - RollupParams { - to_batch_proof_prefix: vec![1, 1], - to_light_client_prefix: vec![2, 2], - }, - tx, - ) - .await - .expect("Error initialazing BitcoinService"); - - let da_service = Arc::new(da_service); - - task_manager.spawn(|tk| da_service.clone().run_da_queue(rx, tk)); - - da_service - } - - #[tokio::test] - #[ignore] - /// A test we use to generate some data for the other tests - async fn send_transaction() { - use sov_rollup_interface::da::DaData; - - let mut task_manager = TaskManager::default(); - let da_service = get_service(&mut task_manager).await; - - da_service - .send_transaction(DaData::SequencerCommitment(SequencerCommitment { - merkle_root: [13; 32], - l2_start_block_number: 1002, - l2_end_block_number: 1100, - })) - .await - .expect("Failed to send transaction"); - - da_service - .send_transaction(DaData::SequencerCommitment(SequencerCommitment { - merkle_root: [14; 32], - l2_start_block_number: 1101, - l2_end_block_number: 1245, - })) - .await - .expect("Failed to send transaction"); - - println!("\n\nSend some BTC to this address: bcrt1qscttjdc3wypf7ttu0203sqgfz80a4q38cne693 and press enter\n\n"); - let mut s = String::new(); - std::io::stdin().read_line(&mut s).unwrap(); - - let size = 2000; - let blob = (0..size).map(|_| rand::random::()).collect::>(); - - da_service - .send_transaction(DaData::ZKProof(blob)) - .await - .expect("Failed to send transaction"); - - println!("\n\nSend some BTC to this address: bcrt1qscttjdc3wypf7ttu0203sqgfz80a4q38cne693 and press enter\n\n"); - let mut s = String::new(); - std::io::stdin().read_line(&mut s).unwrap(); - - let size = 600 * 1024; - let blob = (0..size).map(|_| rand::random::()).collect::>(); - - da_service - .send_transaction(DaData::ZKProof(blob)) - .await - .expect("Failed to send transaction"); - - // seq com different namespace - get_service_wrong_namespace(&mut task_manager) - .await - .send_transaction(DaData::SequencerCommitment(SequencerCommitment { - merkle_root: [15; 32], - l2_start_block_number: 1246, - l2_end_block_number: 1268, - })) - .await - .expect("Failed to send transaction"); - - let size = 1024; - let blob = (0..size).map(|_| rand::random::()).collect::>(); - - da_service - .send_transaction(DaData::ZKProof(blob)) - .await - .expect("Failed to send transaction"); - - // seq com incorrect pubkey and sig - get_service_correct_sig_different_public_key(&mut task_manager) - .await - .send_transaction(DaData::SequencerCommitment(SequencerCommitment { - merkle_root: [15; 32], - l2_start_block_number: 1246, - l2_end_block_number: 1268, - })) - .await - .expect("Failed to send transaction"); - - da_service - .send_transaction(DaData::SequencerCommitment(SequencerCommitment { - merkle_root: [15; 32], - l2_start_block_number: 1246, - l2_end_block_number: 1268, - })) - .await - .expect("Failed to send transaction"); - - let size = 1200 * 1024; - let blob = (0..size).map(|_| rand::random::()).collect::>(); - - da_service - .send_transaction(DaData::ZKProof(blob)) - .await - .expect("Failed to send transaction"); - - da_service - .send_transaction(DaData::SequencerCommitment(SequencerCommitment { - merkle_root: [30; 32], - l2_start_block_number: 1268, - l2_end_block_number: 1314, - })) - .await - .expect("Failed to send transaction"); - - task_manager.abort().await; - } - - #[tokio::test] - async fn extract_relevant_blobs_bp() { - let mut task_manager = TaskManager::default(); - - let da_service = get_service(&mut task_manager).await; - - let (header, _inclusion_proof, _completeness_proof, relevant_txs) = - get_mock_data(MockData::BatchProof); - - let block_txs = get_mock_txs(); - let block_txs = block_txs.into_iter().map(Into::into).collect(); - - let block = BitcoinBlock { - header, - txdata: block_txs, - }; - - let (txs, _, _) = - da_service.extract_relevant_blobs_with_proof(&block, DaNamespace::ToBatchProver); - - assert_eq!(txs, relevant_txs); - - task_manager.abort().await; - } - - #[tokio::test] - async fn extract_relevant_blobs_lcp() { - let mut task_manager = TaskManager::default(); - - let da_service = get_service(&mut task_manager).await; - let (header, _inclusion_proof, _completeness_proof, relevant_txs) = - get_mock_data(MockData::LightClientProof); - - let block_txs = get_mock_txs(); - let block_txs = block_txs.into_iter().map(Into::into).collect(); - - let block = BitcoinBlock { - header, - txdata: block_txs, - }; - - let (txs, _, _) = - da_service.extract_relevant_blobs_with_proof(&block, DaNamespace::ToLightClientProver); - - assert_eq!(txs, relevant_txs); - - task_manager.abort().await; - } - - #[tokio::test] - async fn extract_relevant_blobs_with_proof_bp() { - let mut task_manager = TaskManager::default(); - - let verifier = BitcoinVerifier::new(RollupParams { - to_batch_proof_prefix: vec![1, 1], - to_light_client_prefix: vec![2, 2], - }); - - let da_service = get_service(&mut task_manager).await; - let (header, _inclusion_proof, _completeness_proof, _relevant_txs) = - get_mock_data(MockData::BatchProof); - let block_txs = get_mock_txs(); - let block_txs = block_txs.into_iter().map(Into::into).collect(); - - let block = BitcoinBlock { - header, - txdata: block_txs, - }; - - let (txs, inclusion_proof, completeness_proof) = - da_service.extract_relevant_blobs_with_proof(&block, DaNamespace::ToBatchProver); - - assert!(verifier - .verify_transactions( - block.header(), - &txs, - inclusion_proof, - completeness_proof, - DaNamespace::ToBatchProver - ) - .is_ok()); - - task_manager.abort().await; - } - - #[tokio::test] - async fn extract_relevant_blobs_with_proof_lcp() { - let mut task_manager = TaskManager::default(); - - let da_service = get_service(&mut task_manager).await; - let (header, _inclusion_proof, _completeness_proof, relevant_txs) = - get_mock_data(MockData::LightClientProof); - - let block_txs = get_mock_txs(); - let block_txs = block_txs.into_iter().map(Into::into).collect(); - - let block = BitcoinBlock { - header, - txdata: block_txs, - }; - - let (txs, _, _) = - da_service.extract_relevant_blobs_with_proof(&block, DaNamespace::ToLightClientProver); - - assert_eq!(txs, relevant_txs); - - task_manager.abort().await; - } - - #[tokio::test] - // Ignore for now as it is not working due to mock_txs.txt being outdated - #[ignore] - async fn extract_relevant_zk_proofs() { - let mut task_manager = TaskManager::default(); - - let da_service = get_service(&mut task_manager).await; - - let secp = bitcoin::secp256k1::Secp256k1::new(); - let da_pubkey = Keypair::from_secret_key(&secp, &da_service.da_private_key.unwrap()) - .public_key() - .serialize() - .to_vec(); - - let (header, _inclusion_proof, _completeness_proof, _relevant_txs) = - get_mock_data(MockData::LightClientProof); - - let block_txs = get_mock_txs(); - let block_txs = block_txs.into_iter().map(Into::into).collect(); - - let block = BitcoinBlock { - header, - txdata: block_txs, - }; - - let proofs = da_service - .extract_relevant_zk_proofs(&block, &da_pubkey) - .await - .unwrap(); - - dbg!(proofs.len()); - - task_manager.abort().await; - } - - #[tokio::test] - async fn incorrect_private_key_signature_should_fail() { - let mut task_manager = TaskManager::default(); - - // The transaction was sent with this service and the tx data is stored in false_signature_txs.txt - let da_service = get_service(&mut task_manager).await; - let secp = bitcoin::secp256k1::Secp256k1::new(); - let da_pubkey = Keypair::from_secret_key(&secp, &da_service.da_private_key.unwrap()) - .public_key() - .serialize() - .to_vec(); - - let runtime_config = BitcoinServiceConfig { - node_url: "http://localhost:38332".to_string(), - node_username: "chainway".to_string(), - node_password: "topsecret".to_string(), - network: bitcoin::Network::Regtest, - da_private_key: Some( - "E9873D79C6D87DC0FB6A5778633389F4453213303DA61F20BD67FC233AA33261".to_string(), // Test key, safe to publish - ), - tx_backup_dir: get_tx_backup_dir(), - monitoring: None, - }; - - let (tx, _rx) = tokio::sync::mpsc::unbounded_channel(); - - let incorrect_service = BitcoinService::new_without_wallet_check( - runtime_config, - RollupParams { - to_batch_proof_prefix: vec![1, 1], - to_light_client_prefix: vec![2, 2], - }, - tx, - ) - .await - .expect("Error initialazing BitcoinService"); - - let incorrect_pub_key = - Keypair::from_secret_key(&secp, &incorrect_service.da_private_key.unwrap()) - .public_key() - .serialize() - .to_vec(); - - let header = HeaderWrapper::new( - Header { - version: Version::from_consensus(536870912), - prev_blockhash: BlockHash::from_str( - "31402555f54c3f89907c07e6d286c132f9984739f2b6b00cde195b10ac771522", - ) - .unwrap(), - merkle_root: TxMerkleNode::from_str( - "40642938a6cc6124246fd9601108f9671177c1834753162f19e073eaff751191", - ) - .unwrap(), - time: 1724665818, - bits: CompactTarget::from_unprefixed_hex("207fffff").unwrap(), - nonce: 3, - }, - 3, - 1, - WitnessMerkleNode::from_str( - "494880ce756f69b13811200d1e358a049ac3c3dd66e4ff7e86d4c4d3aad95939", - ) - .unwrap() - .as_raw_hash() - .to_byte_array(), - ); - - let txs_str = std::fs::read_to_string("test_data/false_signature_txs.txt").unwrap(); - - let txdata: Vec = txs_str - .lines() - .map(|tx| parse_hex_transaction(tx).unwrap()) - .map(Into::into) - .collect(); - - let block = BitcoinBlock { header, txdata }; - - let (txs, _, _) = - da_service.extract_relevant_blobs_with_proof(&block, DaNamespace::ToBatchProver); - - assert_ne!( - txs.first().unwrap().sender.0, - da_pubkey, - "Publickey recovered incorrectly!" - ); - - assert_eq!( - txs.first().unwrap().sender.0, - incorrect_pub_key, - "Publickey recovered incorrectly!" - ); - - task_manager.abort().await; - } - - #[tokio::test] - async fn check_signature() { - let mut task_manager = TaskManager::default(); - - let da_service = get_service(&mut task_manager).await; - let secp = bitcoin::secp256k1::Secp256k1::new(); - let da_pubkey = Keypair::from_secret_key(&secp, &da_service.da_private_key.unwrap()) - .public_key() - .serialize() - .to_vec(); - - // blob written in tx is: "01000000b60000002adbd76606f2bd4125080e6f44df7ba2d728409955c80b8438eb1828ddf23e3c12188eeac7ecf6323be0ed5668e21cc354fca90d8bca513d6c0a240c26afa7007b758bf2e7670fafaf6bf0015ce0ff5aa802306fc7e3f45762853ffc37180fe64a0000000001fea6ac5b8751120fb62fff67b54d2eac66aef307c7dde1d394dea1e09e43dd44c800000000000000135d23aee8cb15c890831ff36db170157acaac31df9bba6cd40e7329e608eabd0000000000000000"; - // tx id = 0x8a1df48198a509cd91930ff44cbb92ef46e80458b1999e16aa6923171894fba3 - // block hash = 0x4ebbe86ead2e7f397419c25b0757bea281353a0592eb692614d13f0e87c5a7ff - // the tx_hex = "020000000001012a2b5f4a9aef27067aff1bfe058076043667f0618075b94253d58c9f5b7b85d40000000000fdffffff01220200000000000016001421e826b290c95a5c65059b3a48e97a91f422d1330340f1148ce0807ebd683fad97376225ea2eea0dcef89f609e6e563bc5bb4f25c34d96e4741da9d84130ddb9b5a111703332983fdd20a461ae25c9434cde1e9d8733fd60012044e67148e60dd2ab07bb2505f2e3e9298aada763dd4635bce71bcf2f96a6691aac0063010107736f762d627463010240cc4b23d2cb3e22b2c57a59f24088764f39f7b789847e983b9ee9ce7682578c2b7dbdf4384e230c942b91ae5ce6b1ba33587f549fedee4d19e54ff3a8e54601e801032102588d202afcc1ee4ab5254c7847ec25b9a135bbda0f2bc69ee1a714749fd77dc9010400004cc41b7b01f845c786b10e90638b5cd88023081823b06c20b90040401052860738a7c6cd60c7358f581158bbf7e6bc92c7391efe57ed40c593d8a2e09839969526a688dd6cdf3e13965aeca8592c53b7e8bbce8f89ea5492b146f243b3e5a5035eae51c7ebe6b8bc3cab03487b71a7990116d8b5afdc53370e95bb16a7c0adbd8489749b96ad15ae448c2be3bb332f7dc39b6d967b026f9f591af96f3669f1f7c9cc7b1dd047a2c392bbd145daf11142776253e420f5eccc169afb55693d0febc27f0db159036821c044e67148e60dd2ab07bb2505f2e3e9298aada763dd4635bce71bcf2f96a6691a00000000"; - // let header = HeaderWrapper::new( - // Header { - // version: Version::from_consensus(536870912), - // prev_blockhash: BlockHash::from_str( - // "4ebd11342b9d9e2a23b0f14c17a12bbb4f52a9290fe6a1cf313c270d5a49c2ea", - // ) - // .unwrap(), - // merkle_root: TxMerkleNode::from_str( - // "a720804fbad45307b61958059c06f787a1ae10180ce91df2802a40023dea7e84", - // ) - // .unwrap(), - // time: 1723820296, - // bits: CompactTarget::from_unprefixed_hex("207fffff").unwrap(), - // nonce: 0, - // }, - // 3, - // 2273, - // WitnessMerkleNode::from_str( - // "ab0edbf1611637701117cfc70b878b4196be1c5e4c256609ca8b620a0838860a", - // ) - // .unwrap() - // .as_raw_hash() - // .to_byte_array(), - // ); - - let txs_str = std::fs::read_to_string("test_data/mock_txs.txt").unwrap(); - - let txdata: Vec<_> = txs_str - .lines() - .map(|tx| parse_hex_transaction(tx).unwrap()) - .collect(); - - let txs = get_relevant_blobs_from_txs(txdata, &[1]); - - assert_eq!( - txs.first().unwrap().sender.0, - da_pubkey, - "Publickey recovered incorrectly!" - ); - - task_manager.abort().await; - } -} diff --git a/crates/bitcoin-da/src/verifier.rs b/crates/bitcoin-da/src/verifier.rs index f91128a14e..4c537a215b 100644 --- a/crates/bitcoin-da/src/verifier.rs +++ b/crates/bitcoin-da/src/verifier.rs @@ -440,921 +440,3 @@ fn calculate_new_difficulty( new_target.to_be_bytes() } - -#[cfg(test)] -mod tests { - - // Transactions for testing is prepared with 2 leading zeros - // So verifier takes in [0, 0] - - use core::str::FromStr; - - use bitcoin::block::{Header, Version}; - use bitcoin::hash_types::{TxMerkleNode, WitnessMerkleNode}; - use bitcoin::hashes::Hash; - use bitcoin::{BlockHash, CompactTarget, ScriptBuf, Witness}; - use citrea_primitives::compression::decompress_blob; - use sov_rollup_interface::da::{DaNamespace, DaVerifier}; - - use super::BitcoinVerifier; - use crate::helpers::merkle_tree::BitcoinMerkleTree; - use crate::helpers::parsers::{ - parse_batch_proof_transaction, parse_light_client_transaction, ParsedBatchProofTransaction, - ParsedLightClientTransaction, - }; - use crate::helpers::test_utils::{ - get_blob_with_sender, get_mock_data, get_mock_txs, get_non_segwit_mock_txs, MockData, - }; - use crate::spec::blob::BlobWithSender; - use crate::spec::header::HeaderWrapper; - use crate::spec::proof::InclusionMultiProof; - use crate::spec::transaction::TransactionWrapper; - use crate::spec::RollupParams; - use crate::verifier::{ValidationError, WITNESS_COMMITMENT_PREFIX}; - - #[test] - fn correct_bp() { - let verifier = BitcoinVerifier::new(RollupParams { - to_batch_proof_prefix: vec![1, 1], - to_light_client_prefix: vec![2, 2], - }); - - let (block_header, inclusion_proof, completeness_proof, txs) = - get_mock_data(MockData::BatchProof); - - assert!(verifier - .verify_transactions( - &block_header, - txs.as_slice(), - inclusion_proof, - completeness_proof, - DaNamespace::ToBatchProver, - ) - .is_ok()); - } - - #[test] - fn correct_lcp() { - let verifier = BitcoinVerifier::new(RollupParams { - to_batch_proof_prefix: vec![1, 1], - to_light_client_prefix: vec![2, 2], - }); - - let (block_header, inclusion_proof, completeness_proof, txs) = - get_mock_data(MockData::LightClientProof); - - assert!(verifier - .verify_transactions( - &block_header, - txs.as_slice(), - inclusion_proof, - completeness_proof, - DaNamespace::ToLightClientProver, - ) - .is_ok()); - } - - #[test] - fn batch_in_light_client_should_fail() { - let verifier = BitcoinVerifier::new(RollupParams { - to_batch_proof_prefix: vec![1, 1], - to_light_client_prefix: vec![2, 2], - }); - - let (block_header, inclusion_proof, completeness_proof, txs) = - get_mock_data(MockData::BatchProof); - - assert_eq!( - verifier.verify_transactions( - &block_header, - txs.as_slice(), - inclusion_proof, - completeness_proof, - DaNamespace::ToLightClientProver, - ), - Err(ValidationError::RelevantTxNotInProof), - ); - } - - #[test] - fn light_client_in_batch_should_fail() { - let verifier = BitcoinVerifier::new(RollupParams { - to_batch_proof_prefix: vec![1, 1], - to_light_client_prefix: vec![2, 2], - }); - - let (block_header, inclusion_proof, completeness_proof, txs) = - get_mock_data(MockData::LightClientProof); - - assert_eq!( - verifier.verify_transactions( - &block_header, - txs.as_slice(), - inclusion_proof, - completeness_proof, - DaNamespace::ToBatchProver, - ), - Err(ValidationError::RelevantTxNotInProof), - ); - } - - #[test] - fn test_non_segwit_block() { - let verifier = BitcoinVerifier::new(RollupParams { - to_batch_proof_prefix: vec![1, 1], - to_light_client_prefix: vec![2, 2], - }); - let header = HeaderWrapper::new( - Header { - version: Version::from_consensus(536870912), - prev_blockhash: BlockHash::from_str( - "6b15a2e4b17b0aabbd418634ae9410b46feaabf693eea4c8621ffe71435d24b0", - ) - .unwrap(), - merkle_root: TxMerkleNode::from_slice(&[ - 164, 71, 72, 235, 241, 189, 131, 141, 120, 210, 207, 233, 212, 171, 56, 52, 25, - 40, 83, 62, 135, 211, 81, 44, 3, 109, 10, 127, 210, 213, 124, 221, - ]) - .unwrap(), - time: 1694177029, - bits: CompactTarget::from_unprefixed_hex("207fffff").unwrap(), - nonce: 0, - }, - 6, - 2, - WitnessMerkleNode::from_str( - "a8b25755ed6e2f1df665b07e751f6acc1ff4e1ec765caa93084176e34fa5ad71", - ) - .unwrap() - .to_raw_hash() - .to_byte_array(), - ); - - let block_txs = get_non_segwit_mock_txs(); - let block_txs: Vec = block_txs.into_iter().map(Into::into).collect(); - - // block does not have any segwit txs - let idx = block_txs[0].output.iter().position(|output| { - output - .script_pubkey - .to_bytes() - .starts_with(WITNESS_COMMITMENT_PREFIX) - }); - assert!(idx.is_none()); - - // tx with txid 00... is not relevant is in this proof - // only used so the completeness proof is not empty - let completeness_proof = vec![]; - - let tree = BitcoinMerkleTree::new( - block_txs - .iter() - .map(|t| t.compute_txid().to_raw_hash().to_byte_array()) - .collect(), - ); - - let inclusion_proof = InclusionMultiProof { - wtxids: block_txs - .iter() - .map(|t| t.compute_wtxid().to_byte_array()) - .collect(), - coinbase_tx: block_txs[0].clone(), - coinbase_merkle_proof: tree.get_idx_path(0), - }; - - // There should not be any blobs - let txs: Vec = vec![]; - - assert!(matches!( - verifier.verify_transactions( - &header, - txs.as_slice(), - inclusion_proof, - completeness_proof, - DaNamespace::ToBatchProver, - ), - Ok(()), - )); - } - - #[test] - fn false_coinbase_input_witness_should_fail() { - let verifier = BitcoinVerifier::new(RollupParams { - to_batch_proof_prefix: vec![1, 1], - to_light_client_prefix: vec![2, 2], - }); - - let header = HeaderWrapper::new( - Header { - version: Version::from_consensus(536870912), - prev_blockhash: BlockHash::from_str( - "426524a1b644fd8c77d32621f42a74486262bbc2eaeacf43d12cdee312885f42", - ) - .unwrap(), - merkle_root: TxMerkleNode::from_str( - "34ef858c354e8fd441e49fdc9266ca2bb760034c54b28fdb660254c2546295c8", - ) - .unwrap(), - time: 1724662940, - bits: CompactTarget::from_unprefixed_hex("207fffff").unwrap(), - nonce: 0, - }, - 36, - 1001, - WitnessMerkleNode::from_str( - "0467b591b054383ec433945d04063742f5aabb80e52a53bc2f8ded58d350a7c5", - ) - .unwrap() - .to_raw_hash() - .to_byte_array(), - ); - - let block_txs = get_mock_txs(); - let mut block_txs: Vec = - block_txs.into_iter().map(Into::into).collect(); - - block_txs[0].input[0].witness = Witness::from_slice(&[vec![1u8; 32]]); - - let relevant_txs_indices = [4, 6, 18, 28, 34]; - - let completeness_proof = relevant_txs_indices - .into_iter() - .map(|i| block_txs[i].clone()) - .map(Into::into) - .collect(); - - let tree = BitcoinMerkleTree::new( - block_txs - .iter() - .map(|t| t.compute_txid().to_raw_hash().to_byte_array()) - .collect(), - ); - - let mut inclusion_proof = InclusionMultiProof { - wtxids: block_txs - .iter() - .map(|t| t.compute_wtxid().to_byte_array()) - .collect(), - coinbase_tx: block_txs[0].clone(), - coinbase_merkle_proof: tree.get_idx_path(0), - }; - - // Coinbase tx wtxid should be [0u8;32] - inclusion_proof.wtxids[0] = [0; 32]; - - let txs: Vec = relevant_txs_indices - .into_iter() - .filter_map(|i| get_blob_with_sender(&block_txs[i], MockData::BatchProof).ok()) - .collect(); - - assert_eq!( - verifier.verify_transactions( - &header, - txs.as_slice(), - inclusion_proof, - completeness_proof, - DaNamespace::ToBatchProver, - ), - Err(ValidationError::IncorrectInclusionProof) - ); - } - - #[test] - fn false_coinbase_script_pubkey_should_fail() { - let verifier = BitcoinVerifier::new(RollupParams { - to_batch_proof_prefix: vec![1, 1], - to_light_client_prefix: vec![2, 2], - }); - - let header = HeaderWrapper::new( - Header { - version: Version::from_consensus(536870912), - prev_blockhash: BlockHash::from_str( - "426524a1b644fd8c77d32621f42a74486262bbc2eaeacf43d12cdee312885f42", - ) - .unwrap(), - merkle_root: TxMerkleNode::from_str( - "34ef858c354e8fd441e49fdc9266ca2bb760034c54b28fdb660254c2546295c8", - ) - .unwrap(), - time: 1724662940, - bits: CompactTarget::from_unprefixed_hex("207fffff").unwrap(), - nonce: 0, - }, - 36, - 1001, - WitnessMerkleNode::from_str( - "0467b591b054383ec433945d04063742f5aabb80e52a53bc2f8ded58d350a7c5", - ) - .unwrap() - .to_raw_hash() - .to_byte_array(), - ); - - let block_txs = get_mock_txs(); - let mut block_txs: Vec = - block_txs.into_iter().map(Into::into).collect(); - - let idx = block_txs[0] - .output - .iter() - .position(|output| { - output - .script_pubkey - .to_bytes() - .starts_with(WITNESS_COMMITMENT_PREFIX) - }) - .unwrap(); - - // the 7th byte of script pubkey is changed from 104 to 105 - block_txs[0].output[idx].script_pubkey = ScriptBuf::from_bytes(vec![ - 106, 36, 170, 33, 169, 237, 105, 181, 249, 155, 21, 242, 213, 115, 55, 123, 70, 108, - 15, 173, 14, 106, 243, 231, 186, 128, 75, 251, 178, 9, 24, 228, 200, 177, 144, 89, 95, - 182, - ]); - - let relevant_txs_indices = [4, 6, 18, 28, 34]; - - let completeness_proof = relevant_txs_indices - .into_iter() - .map(|i| block_txs[i].clone()) - .map(Into::into) - .collect(); - - let tree = BitcoinMerkleTree::new( - block_txs - .iter() - .map(|t| t.compute_txid().to_raw_hash().to_byte_array()) - .collect(), - ); - - let mut inclusion_proof = InclusionMultiProof { - wtxids: block_txs - .iter() - .map(|t| t.compute_wtxid().to_byte_array()) - .collect(), - coinbase_tx: block_txs[0].clone(), - coinbase_merkle_proof: tree.get_idx_path(0), - }; - - // Coinbase tx wtxid should be [0u8;32] - inclusion_proof.wtxids[0] = [0; 32]; - - let txs: Vec = relevant_txs_indices - .into_iter() - .filter_map(|i| get_blob_with_sender(&block_txs[i], MockData::BatchProof).ok()) - .collect(); - - assert_eq!( - verifier.verify_transactions( - &header, - txs.as_slice(), - inclusion_proof, - completeness_proof, - DaNamespace::ToBatchProver, - ), - Err(ValidationError::IncorrectInclusionProof) - ); - } - - #[test] - fn false_witness_script_should_fail() { - let verifier = BitcoinVerifier::new(RollupParams { - to_batch_proof_prefix: vec![1, 1], - to_light_client_prefix: vec![2, 2], - }); - - let header = HeaderWrapper::new( - Header { - version: Version::from_consensus(536870912), - prev_blockhash: BlockHash::from_str( - "426524a1b644fd8c77d32621f42a74486262bbc2eaeacf43d12cdee312885f42", - ) - .unwrap(), - merkle_root: TxMerkleNode::from_str( - "34ef858c354e8fd441e49fdc9266ca2bb760034c54b28fdb660254c2546295c8", - ) - .unwrap(), - time: 1724662940, - bits: CompactTarget::from_unprefixed_hex("207fffff").unwrap(), - nonce: 0, - }, - 36, - 1001, - WitnessMerkleNode::from_str( - "0467b591b054383ec433945d04063742f5aabb80e52a53bc2f8ded58d350a7c5", - ) - .unwrap() - .to_raw_hash() - .to_byte_array(), - ); - - let block_txs = get_mock_txs(); - let mut block_txs: Vec = - block_txs.into_iter().map(Into::into).collect(); - - // This is the changed witness of the 6th tx, the second byte of the second script is changed from 6b to 6c - // This creates a different wtxid, thus the verification should fail - let changed_witness = vec![ - hex::decode("9a80cec0e5697631f5833aa9e06c4254cc982abf48ef65fd38ea7c3791290a47911d99d88daa9781dc86fb2c8be70af6ee58b89f109c98c9a4bc6d69c2d8961d").unwrap(), - hex::decode("206c44322e08a288964df3af45c2a11b1fc9fdbcd03cdde61d0655fbf81948fc8aad0200000063400c7efadcdf53315064d4f54752544bd3c39f1e0242ef79b6de55eb3d0d0af15b0d497bb1dc367a74dd761ed066e67d7730dff4e5c78eff0db7b2cee4932c5ce12102588d202afcc1ee4ab5254c7847ec25b9a135bbda0f2bc69ee1a714749fd77dc931000e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e4d04000000000000dd040000000000006808f58003000000000077").unwrap(), - hex::decode("c16b44322e08a288964df3af45c2a11b1fc9fdbcd03cdde61d0655fbf81948fc8a").unwrap() - ]; - - block_txs[6].input[0].witness = Witness::from_slice(&changed_witness); - - let relevant_txs_indices = [4, 6, 18, 28, 34]; - - let completeness_proof = relevant_txs_indices - .into_iter() - .map(|i| block_txs[i].clone()) - .map(Into::into) - .collect(); - - let tree = BitcoinMerkleTree::new( - block_txs - .iter() - .map(|t| t.compute_txid().to_raw_hash().to_byte_array()) - .collect(), - ); - - let mut inclusion_proof = InclusionMultiProof { - wtxids: block_txs - .iter() - .map(|t| t.compute_wtxid().to_byte_array()) - .collect(), - coinbase_tx: block_txs[0].clone(), - coinbase_merkle_proof: tree.get_idx_path(0), - }; - - // Coinbase tx wtxid should be [0u8;32] - inclusion_proof.wtxids[0] = [0; 32]; - - let txs: Vec = relevant_txs_indices - .into_iter() - .filter_map(|i| get_blob_with_sender(&block_txs[i], MockData::BatchProof).ok()) - .collect(); - - assert_eq!( - verifier.verify_transactions( - &header, - txs.as_slice(), - inclusion_proof, - completeness_proof, - DaNamespace::ToBatchProver, - ), - Err(ValidationError::RelevantTxNotInProof) - ); - } - - // verifies it, and then changes the witness and sees that it cannot be verified - #[test] - fn different_wtxid_fails_verification() { - let verifier = BitcoinVerifier::new(RollupParams { - to_batch_proof_prefix: vec![1, 1], - to_light_client_prefix: vec![2, 2], - }); - - let (block_header, mut inclusion_proof, completeness_proof, txs) = - get_mock_data(MockData::BatchProof); - - assert!(verifier - .verify_transactions( - &block_header, - txs.as_slice(), - inclusion_proof.clone(), - completeness_proof.clone(), - DaNamespace::ToBatchProver, - ) - .is_ok()); - - // cahnging the witness txid of coinbase tx to [1; 32] will make it fail - inclusion_proof.wtxids[0] = [1; 32]; - - assert!(verifier - .verify_transactions( - &block_header, - txs.as_slice(), - inclusion_proof.clone(), - completeness_proof.clone(), - DaNamespace::ToBatchProver, - ) - .is_err()); - - inclusion_proof.wtxids[0] = [0; 32]; - - inclusion_proof.wtxids[1] = [16; 32]; - - assert!(verifier - .verify_transactions( - &block_header, - txs.as_slice(), - inclusion_proof, - completeness_proof, - DaNamespace::ToBatchProver, - ) - .is_err()); - } - - #[test] - fn extra_tx_in_inclusion() { - let verifier = BitcoinVerifier::new(RollupParams { - to_batch_proof_prefix: vec![1, 1], - to_light_client_prefix: vec![2, 2], - }); - - let (block_header, mut inclusion_proof, completeness_proof, txs) = - get_mock_data(MockData::BatchProof); - - inclusion_proof.wtxids.push([5; 32]); - - assert_eq!( - verifier.verify_transactions( - &block_header, - txs.as_slice(), - inclusion_proof, - completeness_proof, - DaNamespace::ToBatchProver, - ), - Err(ValidationError::IncorrectInclusionProof) - ); - } - - #[test] - #[should_panic(expected = "itertools: .zip_eq() reached end of one iterator before the other")] - fn missing_tx_in_inclusion() { - let verifier = BitcoinVerifier::new(RollupParams { - to_batch_proof_prefix: vec![1, 1], - to_light_client_prefix: vec![2, 2], - }); - - let (block_header, mut inclusion_proof, completeness_proof, txs) = - get_mock_data(MockData::BatchProof); - - inclusion_proof.wtxids.pop(); - - // should panic - let _ = verifier.verify_transactions( - &block_header, - txs.as_slice(), - inclusion_proof, - completeness_proof, - DaNamespace::ToBatchProver, - ); - } - - #[test] - #[should_panic(expected = "itertools: .zip_eq() reached end of one iterator before the other")] - fn empty_inclusion() { - let verifier = BitcoinVerifier::new(RollupParams { - to_batch_proof_prefix: vec![1, 1], - to_light_client_prefix: vec![2, 2], - }); - - let (block_header, mut inclusion_proof, completeness_proof, txs) = - get_mock_data(MockData::BatchProof); - - inclusion_proof.wtxids.clear(); - - // should panic - let _ = verifier.verify_transactions( - &block_header, - txs.as_slice(), - inclusion_proof, - completeness_proof, - DaNamespace::ToBatchProver, - ); - } - - #[test] - fn break_order_of_inclusion() { - let verifier = BitcoinVerifier::new(RollupParams { - to_batch_proof_prefix: vec![1, 1], - to_light_client_prefix: vec![2, 2], - }); - - let (block_header, mut inclusion_proof, completeness_proof, txs) = - get_mock_data(MockData::BatchProof); - - inclusion_proof.wtxids.swap(0, 1); - - assert_eq!( - verifier.verify_transactions( - &block_header, - txs.as_slice(), - inclusion_proof, - completeness_proof, - DaNamespace::ToBatchProver, - ), - Err(ValidationError::IncorrectInclusionProof) - ); - } - - #[test] - #[should_panic(expected = "itertools: .zip_eq() reached end of one iterator before the other")] - fn missing_tx_in_completeness_proof() { - let verifier = BitcoinVerifier::new(RollupParams { - to_batch_proof_prefix: vec![1, 1], - to_light_client_prefix: vec![2, 2], - }); - - let (block_header, inclusion_proof, mut completeness_proof, txs) = - get_mock_data(MockData::BatchProof); - - completeness_proof.pop(); - - // should panic - let _ = verifier.verify_transactions( - &block_header, - txs.as_slice(), - inclusion_proof, - completeness_proof, - DaNamespace::ToBatchProver, - ); - } - - #[test] - #[should_panic(expected = "itertools: .zip_eq() reached end of one iterator before the other")] - fn empty_completeness_proof() { - let verifier = BitcoinVerifier::new(RollupParams { - to_batch_proof_prefix: vec![1, 1], - to_light_client_prefix: vec![2, 2], - }); - - let (block_header, inclusion_proof, mut completeness_proof, txs) = - get_mock_data(MockData::BatchProof); - - completeness_proof.clear(); - - // should panic - let _ = verifier.verify_transactions( - &block_header, - txs.as_slice(), - inclusion_proof, - completeness_proof, - DaNamespace::ToBatchProver, - ); - } - - #[test] - #[should_panic(expected = "itertools: .zip_eq() reached end of one iterator before the other")] - fn non_relevant_tx_in_completeness_proof() { - let verifier = BitcoinVerifier::new(RollupParams { - to_batch_proof_prefix: vec![1, 1], - to_light_client_prefix: vec![2, 2], - }); - - let (block_header, inclusion_proof, mut completeness_proof, txs) = - get_mock_data(MockData::BatchProof); - - completeness_proof.push(get_mock_txs().get(1).unwrap().clone().into()); - - // should panic - let _ = verifier.verify_transactions( - &block_header, - txs.as_slice(), - inclusion_proof, - completeness_proof, - DaNamespace::ToBatchProver, - ); - } - - #[test] - fn break_completeness_proof_order() { - let verifier = BitcoinVerifier::new(RollupParams { - to_batch_proof_prefix: vec![1, 1], - to_light_client_prefix: vec![2, 2], - }); - - let (block_header, inclusion_proof, mut completeness_proof, txs) = - get_mock_data(MockData::BatchProof); - - completeness_proof.swap(2, 3); - - assert_eq!( - verifier.verify_transactions( - &block_header, - txs.as_slice(), - inclusion_proof, - completeness_proof, - DaNamespace::ToBatchProver, - ), - Err(ValidationError::RelevantTxNotInProof) - ); - } - - #[test] - fn break_rel_tx_order() { - let verifier = BitcoinVerifier::new(RollupParams { - to_batch_proof_prefix: vec![1, 1], - to_light_client_prefix: vec![2, 2], - }); - - let (block_header, inclusion_proof, completeness_proof, mut txs) = - get_mock_data(MockData::BatchProof); - - txs.swap(0, 1); - - assert_eq!( - verifier.verify_transactions( - &block_header, - txs.as_slice(), - inclusion_proof, - completeness_proof, - DaNamespace::ToBatchProver, - ), - Err(ValidationError::BlobWasTamperedWith) - ); - } - - #[test] - fn break_rel_tx_and_completeness_order() { - let verifier = BitcoinVerifier::new(RollupParams { - to_batch_proof_prefix: vec![1, 1], - to_light_client_prefix: vec![2, 2], - }); - - let (block_header, inclusion_proof, mut completeness_proof, mut txs) = - get_mock_data(MockData::BatchProof); - - txs.swap(0, 1); - completeness_proof.swap(0, 1); - - assert_eq!( - verifier.verify_transactions( - &block_header, - txs.as_slice(), - inclusion_proof, - completeness_proof, - DaNamespace::ToBatchProver, - ), - Err(ValidationError::RelevantTxNotInProof) - ); - } - - #[test] - fn tamper_rel_tx_content_bp() { - let verifier = BitcoinVerifier::new(RollupParams { - to_batch_proof_prefix: vec![1, 1], - to_light_client_prefix: vec![2, 2], - }); - - let (block_header, inclusion_proof, completeness_proof, mut txs) = - get_mock_data(MockData::BatchProof); - - let new_blob = vec![2; 152]; - - txs[1] = BlobWithSender::new(new_blob, txs[1].sender.0.clone(), txs[1].hash); - assert_eq!( - verifier.verify_transactions( - &block_header, - txs.as_slice(), - inclusion_proof, - completeness_proof, - DaNamespace::ToBatchProver, - ), - Err(ValidationError::BlobContentWasModified) - ); - } - - #[test] - fn tamper_rel_tx_content_lcp() { - let verifier = BitcoinVerifier::new(RollupParams { - to_batch_proof_prefix: vec![1, 1], - to_light_client_prefix: vec![2, 2], - }); - - let (block_header, inclusion_proof, completeness_proof, mut txs) = - get_mock_data(MockData::LightClientProof); - - let new_blob = vec![2; 152]; - - txs[1] = BlobWithSender::new(new_blob, txs[1].sender.0.clone(), txs[1].hash); - assert_eq!( - verifier.verify_transactions( - &block_header, - txs.as_slice(), - inclusion_proof, - completeness_proof, - DaNamespace::ToLightClientProver, - ), - Err(ValidationError::BlobContentWasModified) - ); - } - - #[test] - fn tamper_senders_bp() { - let verifier = BitcoinVerifier::new(RollupParams { - to_batch_proof_prefix: vec![1, 1], - to_light_client_prefix: vec![2, 2], - }); - - let (block_header, inclusion_proof, completeness_proof, mut txs) = - get_mock_data(MockData::BatchProof); - let tx1 = &completeness_proof[1]; - let body = { - let parsed = parse_batch_proof_transaction(tx1).unwrap(); - let ParsedBatchProofTransaction::SequencerCommitment(seq) = parsed; - seq.body - }; - txs[1] = BlobWithSender::new(body, vec![2; 33], txs[1].hash); - - assert_eq!( - verifier.verify_transactions( - &block_header, - txs.as_slice(), - inclusion_proof, - completeness_proof, - DaNamespace::ToBatchProver, - ), - Err(ValidationError::IncorrectSenderInBlob) - ); - } - - #[test] - fn tamper_senders_lcp() { - let verifier = BitcoinVerifier::new(RollupParams { - to_batch_proof_prefix: vec![1, 1], - to_light_client_prefix: vec![2, 2], - }); - - let (block_header, inclusion_proof, completeness_proof, mut txs) = - get_mock_data(MockData::LightClientProof); - let tx1 = &completeness_proof[1]; - let body = { - let parsed = parse_light_client_transaction(tx1).unwrap(); - match parsed { - ParsedLightClientTransaction::Complete(complete) => decompress_blob(&complete.body), - ParsedLightClientTransaction::Aggregate(aggregate) => aggregate.body, - _ => unimplemented!(), - } - }; - txs[1] = BlobWithSender::new(body, vec![2; 33], txs[1].hash); - - assert_eq!( - verifier.verify_transactions( - &block_header, - txs.as_slice(), - inclusion_proof, - completeness_proof, - DaNamespace::ToLightClientProver, - ), - Err(ValidationError::IncorrectSenderInBlob) - ); - } - - #[test] - fn compressed_lcp_blob_tx_should_fail() { - let verifier = BitcoinVerifier::new(RollupParams { - to_batch_proof_prefix: vec![1, 1], - to_light_client_prefix: vec![2, 2], - }); - - let (block_header, inclusion_proof, completeness_proof, mut txs) = - get_mock_data(MockData::LightClientProof); - let tx0 = &completeness_proof[0]; - let body = { - let parsed = parse_light_client_transaction(tx0).unwrap(); - match parsed { - ParsedLightClientTransaction::Complete(complete) => complete.body, // normally we should decompress the tx body here - _ => panic!("Should not select zk proof tx other than complete"), - } - }; - txs[1] = BlobWithSender::new(body, txs[1].sender.0.clone(), txs[1].hash); - - assert_eq!( - verifier.verify_transactions( - &block_header, - txs.as_slice(), - inclusion_proof, - completeness_proof, - DaNamespace::ToLightClientProver, - ), - Err(ValidationError::BlobContentWasModified) - ); - } - - #[test] - fn missing_rel_tx() { - let verifier = BitcoinVerifier::new(RollupParams { - to_batch_proof_prefix: vec![1, 1], - to_light_client_prefix: vec![2, 2], - }); - - let (block_header, inclusion_proof, completeness_proof, mut txs) = - get_mock_data(MockData::BatchProof); - - txs = vec![txs[0].clone(), txs[1].clone(), txs[2].clone()]; - - assert_eq!( - verifier.verify_transactions( - &block_header, - txs.as_slice(), - inclusion_proof, - completeness_proof, - DaNamespace::ToBatchProver, - ), - Err(ValidationError::ValidBlobNotFoundInBlobs) - ); - } -} From 0cc0f18e80275847169ed44f75e08aaba15e97a2 Mon Sep 17 00:00:00 2001 From: yaziciahmet Date: Mon, 25 Nov 2024 20:19:41 +0100 Subject: [PATCH 20/24] Fix flakiness --- crates/bitcoin-da/tests/service.rs | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/crates/bitcoin-da/tests/service.rs b/crates/bitcoin-da/tests/service.rs index 19f47c490e..6c12a5ddba 100644 --- a/crates/bitcoin-da/tests/service.rs +++ b/crates/bitcoin-da/tests/service.rs @@ -63,7 +63,13 @@ impl TestCase for BitcoinServiceTest { assert_eq!(inclusion_proof.wtxids[1..], block_wtxids[1..]); // 3 valid commitments, and 1 invalid commitment with wrong public key assert_eq!(txs.len(), 4); - assert_eq!(completeness_proof.len(), 4); + // it is >= due to the probability that one of commit transactions ended up + // with the prefix by chance (reveals are guaranteed to have a certain prefix) + assert!( + completeness_proof.len() >= 4, + "expected completeness proof to have at least 4 txs, it has {}", + completeness_proof.len() + ); // Since only one of the transactions has a malformed sender, we have to find the // tx that is not malformed, and get its public key @@ -94,7 +100,13 @@ impl TestCase for BitcoinServiceTest { assert_eq!(inclusion_proof.wtxids[1..], block_wtxids[1..]); // 2 complete and 2 aggregate proofs assert_eq!(txs.len(), 4); - assert_eq!(completeness_proof.len(), 4); + // it is >= due to the probability that one of commit transactions ended up + // with the prefix by chance (reveals are guaranteed to have a certain prefix) + assert!( + completeness_proof.len() >= 4, + "expected completeness proof to have at least 4 txs, it has {}", + completeness_proof.len() + ); // Ensure that the produced outputs are verifiable by the verifier assert_eq!( From 5f5b29d935c634a83773d5fb91ac79bebc4f2f8e Mon Sep 17 00:00:00 2001 From: yaziciahmet Date: Mon, 25 Nov 2024 20:24:08 +0100 Subject: [PATCH 21/24] Readd forgotten --- bin/citrea/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/citrea/Cargo.toml b/bin/citrea/Cargo.toml index c0777098c4..e26705b400 100644 --- a/bin/citrea/Cargo.toml +++ b/bin/citrea/Cargo.toml @@ -97,7 +97,7 @@ citrea-e2e = { workspace = true } sp1-helper = { version = "3.0.0", default-features = false } [features] -default = [] # Deviate from convention by making the "native" feature active by default. This aligns with how this package is meant to be used (). +default = [] # Deviate from convention by making the "native" feature active by default. This aligns with how this package is meant to be used (as a binary first, library second). bench = ["hex"] # "sov-risc0-adapter/bench", "risc0/bench"] From 341755599bebd53a7fa33afeb2d8e458f51a411a Mon Sep 17 00:00:00 2001 From: yaziciahmet Date: Tue, 26 Nov 2024 11:33:55 +0100 Subject: [PATCH 22/24] Assert test errors --- crates/bitcoin-da/tests/verifier.rs | 117 +++++++++++++++------------- 1 file changed, 65 insertions(+), 52 deletions(-) diff --git a/crates/bitcoin-da/tests/verifier.rs b/crates/bitcoin-da/tests/verifier.rs index f93d5bc3da..a3a05e72ac 100644 --- a/crates/bitcoin-da/tests/verifier.rs +++ b/crates/bitcoin-da/tests/verifier.rs @@ -83,26 +83,28 @@ impl TestCase for BitcoinVerifierTest { // Inverted namespaces should fail { // batch transactions with light client namespace - assert!(verifier - .verify_transactions( + assert_eq!( + verifier.verify_transactions( &block.header, &b_txs, b_inclusion_proof.clone(), b_completeness_proof.clone(), DaNamespace::ToLightClientProver, - ) - .is_err()); + ), + Err(ValidationError::RelevantTxNotInProof), + ); // light client transactions with batch namespace - assert!(verifier - .verify_transactions( + assert_eq!( + verifier.verify_transactions( &block.header, &l_txs, l_inclusion_proof.clone(), l_completeness_proof.clone(), DaNamespace::ToBatchProver, - ) - .is_err()); + ), + Err(ValidationError::RelevantTxNotInProof), + ); } // Test non-segwit block @@ -161,15 +163,16 @@ impl TestCase for BitcoinVerifierTest { // Coinbase tx wtxid should be [0u8;32] inclusion_proof.wtxids[0] = [0; 32]; - assert!(verifier - .verify_transactions( + assert_eq!( + verifier.verify_transactions( &block.header, &b_txs, inclusion_proof, b_completeness_proof.clone(), DaNamespace::ToBatchProver, - ) - .is_err()); + ), + Err(ValidationError::IncorrectInclusionProof), + ); } // False coinbase script pubkey should fail @@ -210,15 +213,16 @@ impl TestCase for BitcoinVerifierTest { // Coinbase tx wtxid should be [0u8;32] inclusion_proof.wtxids[0] = [0; 32]; - assert!(verifier - .verify_transactions( + assert_eq!( + verifier.verify_transactions( &block.header, &b_txs, inclusion_proof, b_completeness_proof.clone(), DaNamespace::ToBatchProver, - ) - .is_err()); + ), + Err(ValidationError::InvalidBlock), + ); } // False witness script should fail @@ -266,15 +270,16 @@ impl TestCase for BitcoinVerifierTest { .filter_map(|tx| get_blob_with_sender(tx, MockData::ToBatchProver).ok()) .collect::>(); - assert!(verifier - .verify_transactions( + assert_eq!( + verifier.verify_transactions( &block.header, &txs, inclusion_proof, completeness_proof, DaNamespace::ToBatchProver, - ) - .is_err()); + ), + Err(ValidationError::RelevantTxNotInProof), + ); } // Different witness ids should fail @@ -282,27 +287,29 @@ impl TestCase for BitcoinVerifierTest { let mut b_inclusion_proof = b_inclusion_proof.clone(); b_inclusion_proof.wtxids[0] = [1; 32]; - assert!(verifier - .verify_transactions( + assert_eq!( + verifier.verify_transactions( &block.header, &b_txs, b_inclusion_proof.clone(), b_completeness_proof.clone(), DaNamespace::ToBatchProver, - ) - .is_err()); + ), + Err(ValidationError::RelevantTxNotInProof), + ); b_inclusion_proof.wtxids[0] = [0; 32]; b_inclusion_proof.wtxids[1] = [16; 32]; - assert!(verifier - .verify_transactions( + assert_eq!( + verifier.verify_transactions( &block.header, &b_txs, b_inclusion_proof, b_completeness_proof.clone(), DaNamespace::ToBatchProver, - ) - .is_err()); + ), + Err(ValidationError::IncorrectInclusionProof), + ); } // Extra tx in inclusion @@ -310,15 +317,16 @@ impl TestCase for BitcoinVerifierTest { let mut b_inclusion_proof = b_inclusion_proof.clone(); b_inclusion_proof.wtxids.push([5; 32]); - assert!(verifier - .verify_transactions( + assert_eq!( + verifier.verify_transactions( &block.header, &b_txs, b_inclusion_proof, b_completeness_proof.clone(), DaNamespace::ToBatchProver, - ) - .is_err()); + ), + Err(ValidationError::HeaderInclusionTxCountMismatch), + ); } // Missing tx in inclusion should fail @@ -343,15 +351,16 @@ impl TestCase for BitcoinVerifierTest { let mut b_inclusion_proof = b_inclusion_proof.clone(); b_inclusion_proof.wtxids.swap(0, 1); - assert!(verifier - .verify_transactions( + assert_eq!( + verifier.verify_transactions( &block.header, &b_txs, b_inclusion_proof, b_completeness_proof.clone(), DaNamespace::ToBatchProver, - ) - .is_err(),); + ), + Err(ValidationError::IncorrectInclusionProof), + ); } // Missing tx in completeness proof should panic @@ -399,15 +408,16 @@ impl TestCase for BitcoinVerifierTest { .unwrap() .clone(); b_completeness_proof[0] = nonrelevant_tx; - assert!(verifier - .verify_transactions( + assert_eq!( + verifier.verify_transactions( &block.header, &b_txs, b_inclusion_proof.clone(), b_completeness_proof.clone(), DaNamespace::ToBatchProver, - ) - .is_err(),); + ), + Err(ValidationError::RelevantTxNotInProof), + ); } // Break completeness proof order should fail @@ -415,15 +425,16 @@ impl TestCase for BitcoinVerifierTest { let mut b_completeness_proof = b_completeness_proof.clone(); b_completeness_proof.swap(1, 2); - assert!(verifier - .verify_transactions( + assert_eq!( + verifier.verify_transactions( &block.header, &b_txs, b_inclusion_proof.clone(), b_completeness_proof, DaNamespace::ToBatchProver, - ) - .is_err(),); + ), + Err(ValidationError::RelevantTxNotInProof), + ); } // Break tx order should fail @@ -431,15 +442,16 @@ impl TestCase for BitcoinVerifierTest { let mut b_txs = b_txs.clone(); b_txs.swap(0, 1); - assert!(verifier - .verify_transactions( + assert_eq!( + verifier.verify_transactions( &block.header, &b_txs, b_inclusion_proof.clone(), b_completeness_proof.clone(), DaNamespace::ToBatchProver, - ) - .is_err(),); + ), + Err(ValidationError::BlobWasTamperedWith), + ); } // Break tx order and completeness proof order should fail @@ -449,15 +461,16 @@ impl TestCase for BitcoinVerifierTest { b_completeness_proof.swap(0, 1); b_txs.swap(0, 1); - assert!(verifier - .verify_transactions( + assert_eq!( + verifier.verify_transactions( &block.header, &b_txs, b_inclusion_proof.clone(), b_completeness_proof, DaNamespace::ToBatchProver, - ) - .is_err(),); + ), + Err(ValidationError::RelevantTxNotInProof), + ); } // Missing tx should fail From 99154fc0d6fc0fa94f2d0df4c7bcb42a62753317 Mon Sep 17 00:00:00 2001 From: yaziciahmet Date: Tue, 26 Nov 2024 11:47:07 +0100 Subject: [PATCH 23/24] Add mock txs merkle tree test back --- crates/bitcoin-da/src/helpers/merkle_tree.rs | 9 +++++++++ crates/bitcoin-da/test_data/false_signature_txs.txt | 3 --- crates/bitcoin-da/test_data/mock_non_segwit_txs.txt | 6 ------ 3 files changed, 9 insertions(+), 9 deletions(-) delete mode 100644 crates/bitcoin-da/test_data/false_signature_txs.txt delete mode 100644 crates/bitcoin-da/test_data/mock_non_segwit_txs.txt diff --git a/crates/bitcoin-da/src/helpers/merkle_tree.rs b/crates/bitcoin-da/src/helpers/merkle_tree.rs index 0b56568128..e875e62121 100644 --- a/crates/bitcoin-da/src/helpers/merkle_tree.rs +++ b/crates/bitcoin-da/src/helpers/merkle_tree.rs @@ -114,6 +114,7 @@ mod tests { use bitcoin::hashes::Hash; use super::*; + use crate::helpers::parsers::parse_hex_transaction; #[test] fn test_merkle_root_with_proof() { @@ -143,6 +144,14 @@ mod tests { compare_merkle_tree_against_bitcoin_impl(vec![[255; 32]; 33]); compare_merkle_tree_against_bitcoin_impl(vec![[200; 32]; 2]); compare_merkle_tree_against_bitcoin_impl(vec![[99; 32]; 1]); + + let txs = std::fs::read_to_string("test_data/mock_txs.txt") + .unwrap() + .lines() + .map(|tx_hex| parse_hex_transaction(tx_hex).unwrap()) + .map(|tx| tx.compute_wtxid().to_byte_array()) + .collect::>(); + compare_merkle_tree_against_bitcoin_impl(txs); } fn compare_merkle_tree_against_bitcoin_impl(transactions: Vec<[u8; 32]>) { diff --git a/crates/bitcoin-da/test_data/false_signature_txs.txt b/crates/bitcoin-da/test_data/false_signature_txs.txt deleted file mode 100644 index f9bb43c3e5..0000000000 --- a/crates/bitcoin-da/test_data/false_signature_txs.txt +++ /dev/null @@ -1,3 +0,0 @@ -020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0402aa0800ffffffff026faa040000000000160014fa5554be100ee542587688a93e7c2ac37478bdc60000000000000000266a24aa21a9ed494880ce756f69b13811200d1e358a049ac3c3dd66e4ff7e86d4c4d3aad959390120000000000000000000000000000000000000000000000000000000000000000000000000 -020000000001015ada1242404efd013244c1361f3207d3e34a1f0c786a96b334203bcb317dc9e40100000000fdffffff025e0300000000000022512057c195448a1acba9a08b93aa31fc224988f0e1f517908ea814bd4b052dee3df8af31000000000000160014ba033fad8b4899045c892787ff716877e5f8e18102473044022066e4bafa74ad683ecee03d2a9502ed5bda1c2c791efdc91cd82f47f0a7d139e102207a52d868d9e3f2ceeafb0c01c0f6b3e5db43ec41969d5e9740ebb86f8537b0e00121034716b0a10b8e9a64acfa721fccaf7202c5156a2686332b0ec72bbb257d2dbe0600000000 -0200000000010171f88e369556505b8c5ca67625daf351d79b9fec757ba9d1259a0b63b2338b3a0000000000fdffffff012202000000000000160014ba033fad8b4899045c892787ff716877e5f8e18103409646f749c4d980a427151c31f9e1129327d4d311caab29bfa7702d674c8aaa1c9fea6547c5c947b9745a5f3ecdeb3235d8b9b0cae6a861df2e43b245c5b16161c72059975a92015d3ca4b95d3b3faffd5003ad389ab064e1144f6a5144e4e6f56a58ad020000006340bd068f826f4ca54e7d2aa7133a0ac9f945ac0e3904ea8ed203b35f430e02d5b07ed2fb9dd6e24f7930a56de2f6772f62d0b453a68ec6a5e58ef6de0094e39d6621035c4edf0c1cc8e9d8eab292be0eee726de6ece392529d5159e75f0fd68609a4b331000d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0dea030000000000004c040000000000006808f9420000000000007721c059975a92015d3ca4b95d3b3faffd5003ad389ab064e1144f6a5144e4e6f56a5800000000 diff --git a/crates/bitcoin-da/test_data/mock_non_segwit_txs.txt b/crates/bitcoin-da/test_data/mock_non_segwit_txs.txt deleted file mode 100644 index b576f082d2..0000000000 --- a/crates/bitcoin-da/test_data/mock_non_segwit_txs.txt +++ /dev/null @@ -1,6 +0,0 @@ -01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0f048bdb051a02e503062f503253482fffffffff0140e10d2a01000000434104808df9f502a2f1a2dd1848bade4be111b9f2e66d5c4bd6b9f1682b4d04a53bdb052ebb91ae056dc8a3cd59545265947ee61d54c49aa81981d550bf7f9167ef12ac00000000 -01000000016a5aa0c54e24722d2cd6be99c26b3729fae9b7c27c851b080aa45c5b47c26d2d010000008a47304402201c9404cd4a8b21509834fafff15d700788ffaa842f760e1ab1dc173fa2676ec202202c7e77ad14e48320a272952db318f9f968bcefcf50123f0f40db410d6cd300bc01410491d63a7c33798ca1da6a88ea5cd8daf9c33190571ca4738306b8848466b8494619a0d217a39d4bb0c929735f9c4a1c0dea074239e153b81b9b7cfc85dd36faf8ffffffff0200ec6021000000001976a9146c11a5e60863b35f85d3911920a1aecf11f7153988ac40a8f527000000001976a914ccaf060b633fe6b1f43e2ecc8e0f17adf09d534c88ac00000000 -01000000013377c58db37da73db2c3a269ddf410251073673983790ab5426e215f323ce00f010000008c493046022100fa6a7c25870c377080c1b5b42d216d501ac971ac09507ce079bdaf6da5b046ec022100fa885eef30ffa7a8768a30a5faac6cace724851cd53806413b10aec060bc274a0141049a162e57d5e0f96374f8ead29937ab5a90385f07678b159da4f57cb87b646c148f56a870fb779a9037fabf5fdacc753b34eb98d49e300c36e9b0f3873194e759ffffffff02002d3101000000001976a91406f1b66ffe49df7fce684df16c62f59dc9adbd3f88ac90762907000000001976a9144ef9f0e7ad583d773495722dd79ec11188b9e4fd88ac00000000 -0100000001c9e1effb36254352bca658cfc7b06d6d358cbbffda74dc1c6fb7e25ff3fde256010000008c493046022100d871f859bf9cc2be5080194ed0c38e977e83c212be7150d7d0b65a7704bd830f022100e27f5d8922d7d977b690386457f7fb9c714241ab9d7b91bf05fbac68f2dc69b001410449f6c65c3ba451e4891f8e51e46580c7fcb87480bf5aa2f9d47644ff7b692cfa801d95f6980cef95fb49b3ec42c6ff4ed289d948f03c7f409b34647d0fedf803ffffffff0220651100000000001976a914d0b79214b73d2cba68b524ae1c0f102771e7551c88aca016f912000000001976a9142af648c077286a6c1233eb190bcd767478ced70d88ac00000000 -01000000020792275b6ad62da82d98eaebb5de782642a06c92a80872c2cb3354da52c1ba2e000000008b483045022100fdc067f20ee84e3a4aea25638eb125b44376555ffbdd0fa05611a27a55d2610f022009b3c111bffae5e517bf957bd3f53a3306a6b5c5725418955f20afb6fbd1bb380141048cc0b94178715f03ed3d0bceb368191d0fdd7fc16d806567f6f2c45aecafb8f53e5ef849564072189b9b4f8bfe1564da776567ba359cfb0c05e839bcf65371abffffffff2316672bbcf879e3a96a2e8aab283b44529c4eec8fed798e5e435011c2b5059b010000008b48304502204caab3248930be319ba445c44398aa94e0032dfd25456d09ccd2b076737ef2f6022100eb460ca09390a67b8195fad6c4d0563d243b824ece6bafb69d21ca30542cfaa5014104952fe2d53debd645dffac11367b3888a9e5465eb9c150b98a504d7f8d4c3e98c1a6876175be17462d163e6015ce12c9c2b0a3629e1371e247109222d0b8ed5dbffffffff0230578d09000000001976a9144ef9f0e7ad583d773495722dd79ec11188b9e4fd88ac18366704000000001976a914b4f5b5a9e5119d3f0327d4ff64a1b0a97fc423d988ac00000000 -01000000019ac1695d2e613e3bee66317bbd9ad8ec4033f596ce5c43691837e8322b3a8112010000008c49304602210083a64c8ff430ac05376ab5f940d7801796d6dd0687922af2c8f7247368a41f56022100d55326fdc50a70d992f0a04ef19e5d8edc244941d9976d609ab1b6c1ed2e92760141046992f8f0bdde46834e24df367c28233501fa8615ada18c84631b84f85eb4af10aebd92dec0a870e038fdd9820aae836edba7ba2fae915d6fc25e5727621adc1dffffffff0260011200000000001976a9147c442f8fcb7c525720ee3b587e561fffe028c16d88ac1088810e000000001976a91460b18c23c1d6139e337a306413119f6e9efda4e388ac00000000 From 0d39546b18422cbdb57903296c8b8fb14129777d Mon Sep 17 00:00:00 2001 From: yaziciahmet Date: Tue, 26 Nov 2024 12:45:34 +0100 Subject: [PATCH 24/24] Fix some tests that returned unexpected errors --- crates/bitcoin-da/tests/verifier.rs | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/crates/bitcoin-da/tests/verifier.rs b/crates/bitcoin-da/tests/verifier.rs index a3a05e72ac..d17c2bd064 100644 --- a/crates/bitcoin-da/tests/verifier.rs +++ b/crates/bitcoin-da/tests/verifier.rs @@ -191,7 +191,7 @@ impl TestCase for BitcoinVerifierTest { .unwrap(); // Malform coinbase script pubkey let mut bytes = block_txs[0].output[idx].script_pubkey.to_bytes(); - bytes[0] = bytes[0].wrapping_add(1); + bytes[6] = bytes[6].wrapping_add(1); block_txs[0].output[idx].script_pubkey = ScriptBuf::from_bytes(bytes); @@ -221,7 +221,7 @@ impl TestCase for BitcoinVerifierTest { b_completeness_proof.clone(), DaNamespace::ToBatchProver, ), - Err(ValidationError::InvalidBlock), + Err(ValidationError::IncorrectInclusionProof), ); } @@ -284,27 +284,30 @@ impl TestCase for BitcoinVerifierTest { // Different witness ids should fail { - let mut b_inclusion_proof = b_inclusion_proof.clone(); + let mut inclusion_proof = b_inclusion_proof.clone(); - b_inclusion_proof.wtxids[0] = [1; 32]; + // Prefix is made 1, which will look like inclusion proof + // has extra relevant transaction in it. + inclusion_proof.wtxids[0] = [1; 32]; assert_eq!( verifier.verify_transactions( &block.header, &b_txs, - b_inclusion_proof.clone(), + inclusion_proof, b_completeness_proof.clone(), DaNamespace::ToBatchProver, ), Err(ValidationError::RelevantTxNotInProof), ); - b_inclusion_proof.wtxids[0] = [0; 32]; - b_inclusion_proof.wtxids[1] = [16; 32]; + let mut inclusion_proof = b_inclusion_proof.clone(); + + inclusion_proof.wtxids[1] = [16; 32]; assert_eq!( verifier.verify_transactions( &block.header, &b_txs, - b_inclusion_proof, + inclusion_proof, b_completeness_proof.clone(), DaNamespace::ToBatchProver, ),