From 7c55c5580e5bf39bd64cbef8198f3aae70ab3652 Mon Sep 17 00:00:00 2001 From: EthanYuan Date: Sat, 8 Jun 2024 00:01:55 +0800 Subject: [PATCH 1/3] re-define flags for different btc chain type --- prover/src/dummy_service.rs | 3 ++- verifier/src/constants.rs | 6 +++++- verifier/src/tests/bitcoin.rs | 3 ++- verifier/src/types/core.rs | 21 ++++++++++++++++++++- verifier/src/types/extension/packed.rs | 22 +++++++++++----------- verifier/src/utilities/bitcoin.rs | 17 ++++++++++++++--- 6 files changed, 54 insertions(+), 18 deletions(-) diff --git a/prover/src/dummy_service.rs b/prover/src/dummy_service.rs index 9910a6d..ac2b655 100644 --- a/prover/src/dummy_service.rs +++ b/prover/src/dummy_service.rs @@ -84,7 +84,8 @@ impl DummyService { header.time ); let start_time: u32 = self.client.target_adjust_info.start_time().unpack(); - let next_target = calculate_next_target(curr_target, start_time, header.time); + let next_target = + calculate_next_target(curr_target, start_time, header.time, 0); log::info!(">>> calculated new target {next_target:#x}"); let next_bits = next_target.to_compact_lossy(); let next_target: core::Target = next_bits.into(); diff --git a/verifier/src/constants.rs b/verifier/src/constants.rs index 1b6d767..fcda13c 100644 --- a/verifier/src/constants.rs +++ b/verifier/src/constants.rs @@ -1,3 +1,7 @@ //! Constants. -pub const FLAG_DISABLE_DIFFICULTY_CHECK: u8 = 0b1000_0000; +// Constants for the chain type flag +// Specifically utilizing the two highest bits for chain type identification +pub const FLAG_CHAIN_TYPE_MAINNET: u8 = 0b0000_0000; // for mainnet +pub const FLAG_CHAIN_TYPE_TESTNET: u8 = 0b1000_0000; // for testnet +pub const FLAG_CHAIN_TYPE_SIGNET: u8 = 0b0100_0000; // for signet diff --git a/verifier/src/tests/bitcoin.rs b/verifier/src/tests/bitcoin.rs index d77e459..7334a4d 100644 --- a/verifier/src/tests/bitcoin.rs +++ b/verifier/src/tests/bitcoin.rs @@ -51,7 +51,8 @@ fn main_chain_targets_and_chainwork() { match (height + 1) % interval { 0 => { assert!(prev_height + interval - 1 == height); - let next_target = calculate_next_target(curr_target, start_time, header.time); + let next_target = + calculate_next_target(curr_target, start_time, header.time, 0); log::info!(">>> calculated new target {next_target:#x}"); next_bits = next_target.to_compact_lossy(); let next_target: Target = next_bits.into(); diff --git a/verifier/src/types/core.rs b/verifier/src/types/core.rs index 65a347f..00e5926 100644 --- a/verifier/src/types/core.rs +++ b/verifier/src/types/core.rs @@ -23,7 +23,7 @@ pub use bitcoin_hashes::sha256d::Hash; pub use molecule::bytes::Bytes; pub use primitive_types::U256; -use crate::types::packed; +use crate::{constants::*, types::packed}; // // Proofs @@ -116,3 +116,22 @@ impl fmt::Display for SpvClient { ) } } + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum BitcoinChainType { + Mainnet, + Testnet, + Signet, + Other, // For future use. +} + +impl From for BitcoinChainType { + fn from(flags: u8) -> Self { + match flags & 0b1100_0000 { + FLAG_CHAIN_TYPE_MAINNET => BitcoinChainType::Mainnet, + FLAG_CHAIN_TYPE_TESTNET => BitcoinChainType::Testnet, + FLAG_CHAIN_TYPE_SIGNET => BitcoinChainType::Signet, + _ => BitcoinChainType::Other, + } + } +} diff --git a/verifier/src/types/extension/packed.rs b/verifier/src/types/extension/packed.rs index 83d061f..d99a9b9 100644 --- a/verifier/src/types/extension/packed.rs +++ b/verifier/src/types/extension/packed.rs @@ -9,7 +9,6 @@ use bitcoin::{ use molecule::bytes::Bytes; use crate::{ - constants, core::result::Result, error::{BootstrapError, UpdateError, VerifyTxError}, types::{core, packed, prelude::*}, @@ -149,19 +148,20 @@ impl packed::SpvClient { expect {expected} but got {actual}" ); }); - if flags & constants::FLAG_DISABLE_DIFFICULTY_CHECK == 0 { + + // For mainnet and signet, `header.bits` should be as the same as `new_info.1`. + // But for testnet, it could be not. + if core::BitcoinChainType::Testnet != flags.into() { return Err(UpdateError::Difficulty); } } + // Check POW. - new_tip_block_hash = if flags & constants::FLAG_DISABLE_DIFFICULTY_CHECK == 0 { - header - .validate_pow(new_info.1.into()) - .map_err(|_| UpdateError::Pow)? - } else { - header.block_hash() - } - .into(); + new_tip_block_hash = header + .validate_pow(header.bits.into()) + .map_err(|_| UpdateError::Pow)? + .into(); + // Update the target adjust info. { match (new_max_height + 1) % DIFFCHANGE_INTERVAL { @@ -172,7 +172,7 @@ impl packed::SpvClient { // - But for testnet, it could be not. let prev_target = header.bits.into(); let next_target = - calculate_next_target(prev_target, new_info.0, header.time); + calculate_next_target(prev_target, new_info.0, header.time, flags); new_info.1 = next_target.to_compact_lossy(); } // Current block is the first block for a new difficulty. diff --git a/verifier/src/utilities/bitcoin.rs b/verifier/src/utilities/bitcoin.rs index ec5fb44..da33070 100644 --- a/verifier/src/utilities/bitcoin.rs +++ b/verifier/src/utilities/bitcoin.rs @@ -5,6 +5,8 @@ use bitcoin::{blockdata::constants::DIFFCHANGE_TIMESPAN, pow::Target}; use primitive_types::U256; +use crate::types::core::BitcoinChainType; + /// Calculates the next target. /// /// N.B. The end time is not the block time of the next 2016-th header. @@ -12,7 +14,12 @@ use primitive_types::U256; /// Ref: /// - [What is the Target in Bitcoin?](https://learnmeabitcoin.com/technical/target) /// - [`CalculateNextWorkRequired(..)` in Bitcoin source code](https://github.com/bitcoin/bitcoin/blob/v26.0/src/pow.cpp#L49) -pub fn calculate_next_target(prev_target: Target, start_time: u32, end_time: u32) -> Target { +pub fn calculate_next_target( + prev_target: Target, + start_time: u32, + end_time: u32, + flags: u8, +) -> Target { let expected = DIFFCHANGE_TIMESPAN; let actual = { let mut actual = end_time - start_time; @@ -40,9 +47,13 @@ pub fn calculate_next_target(prev_target: Target, start_time: u32, end_time: u32 }; let target = Target::from_le_bytes(le_bytes); - if target > Target::MAX { + let max_target = match flags.into() { + BitcoinChainType::Signet => Target::MAX_ATTAINABLE_SIGNET, + _ => Target::MAX, + }; + if target > max_target { trace!("fallback to the max target"); - Target::MAX + max_target } else { trace!("use the calculated target"); target From 8634a660212109b691e1995d876b5b78ab14fc5c Mon Sep 17 00:00:00 2001 From: EthanYuan Date: Sat, 8 Jun 2024 00:02:23 +0800 Subject: [PATCH 2/3] add tests for signet. --- verifier/src/tests/mod.rs | 1 + verifier/src/tests/signet.rs | 83 ++++++++++++++++++++++++++ verifier/src/types/extension/packed.rs | 1 - 3 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 verifier/src/tests/signet.rs diff --git a/verifier/src/tests/mod.rs b/verifier/src/tests/mod.rs index 388e1a4..9408622 100644 --- a/verifier/src/tests/mod.rs +++ b/verifier/src/tests/mod.rs @@ -6,6 +6,7 @@ use log::LevelFilter; mod bitcoin; pub(crate) mod data; +pub(crate) mod signet; pub(crate) mod testnet; pub(crate) mod utilities; diff --git a/verifier/src/tests/signet.rs b/verifier/src/tests/signet.rs new file mode 100644 index 0000000..c89d654 --- /dev/null +++ b/verifier/src/tests/signet.rs @@ -0,0 +1,83 @@ +use std::fs::read_to_string; + +use alloc::format; +use ckb_jsonrpc_types::TransactionView; +use ckb_types::packed::WitnessArgs; +use serde_json::from_str as from_json_str; + +use crate::{ + error::UpdateError, + molecule::prelude::*, + tests, + types::{ + core, + packed::{SpvClient, SpvUpdate}, + prelude::*, + }, +}; + +// This case shows that: +// - For the signet network, `header.bits` should be the same as `new_info.1`. +// To run this test, use the following command: +// `cargo test --package ckb-bitcoin-spv-verifier --lib -- tests::signet::signet_verify_new_client_error_197568 --exact --show-output` +#[test] +fn signet_verify_new_client_error_197568() { + let ret = verify_new_client_common( + "tx-0528-error-check-header-target-adjust-info.json", + 1, // cell_dep_index + ); + assert!(ret.is_err()); +} + +// This case shows that: +// - For the signet network, target max should be Target::MAX_ATTAINABLE_SIGNET. +// To run this test, use the following command: +// `cargo test --package ckb-bitcoin-spv-verifier --lib -- tests::signet::signet_verify_new_client_error_header_197567 --exact --show-output` +#[test] +fn signet_verify_new_client_error_header_197567() { + let ret = verify_new_client_common( + "tx-0xd663a1dfdfbf9a4824950c44c0d5f5e65f6b1ba4710a0308edecadeaed3ac549.json", + 2, // cell_dep_index + ); + assert!(ret.is_err()); +} + +// To run this test, use the following command: +// `cargo test --package ckb-bitcoin-spv-verifier --lib -- tests::signet::signet_verify_new_client_normal --exact --show-output` +#[test] +fn signet_verify_new_client_normal() { + let ret = verify_new_client_common( + "tx-0x01d827b049778ffb53532d8263009512a696647bde4acc7f10f39ded14c066ab.json", + 2, // cell_dep_index + ); + assert!(ret.is_ok()); +} + +fn verify_new_client_common(tx_file: &str, cell_dep_index: usize) -> Result<(), UpdateError> { + tests::setup(); + + let path = tests::data::find_bin_file("signet", tx_file); + let tx = read_to_string(path).unwrap(); + let tx: TransactionView = from_json_str(&tx).unwrap(); + + let witnesses = tx.inner.witnesses; + let witness_args = WitnessArgs::from_slice(witnesses[0].as_bytes()).unwrap(); + let spv_update_bin = witness_args.output_type().to_opt().unwrap().raw_data(); + let spv_update = SpvUpdate::from_slice(&spv_update_bin).unwrap(); + + let client_bin = tx.inner.outputs_data[1].clone(); + let client = SpvClient::from_slice(client_bin.as_bytes()).unwrap(); + + let cell_dep = tx.inner.cell_deps[cell_dep_index].out_point.clone(); + let path = + tests::data::find_bin_file("signet", format!("tx-0x{}.json", cell_dep.tx_hash).as_str()); + let previous_tx = read_to_string(path).unwrap(); + let previous_tx: TransactionView = from_json_str(&previous_tx).unwrap(); + let cell_dep_data_bin = &previous_tx.inner.outputs_data[cell_dep.index.value() as usize]; + let cell_dep_client = SpvClient::from_slice(cell_dep_data_bin.as_bytes()).unwrap(); + + let mut cell_dep_client: core::SpvClient = cell_dep_client.unpack(); + cell_dep_client.id = client.id().into(); + let input_client = cell_dep_client.pack(); + input_client.verify_new_client(&client, spv_update, 64) +} diff --git a/verifier/src/types/extension/packed.rs b/verifier/src/types/extension/packed.rs index d99a9b9..890bb60 100644 --- a/verifier/src/types/extension/packed.rs +++ b/verifier/src/types/extension/packed.rs @@ -155,7 +155,6 @@ impl packed::SpvClient { return Err(UpdateError::Difficulty); } } - // Check POW. new_tip_block_hash = header .validate_pow(header.bits.into()) From b81fd00d4b1db127d84ce4c4caed687f126a3234 Mon Sep 17 00:00:00 2001 From: EthanYuan Date: Tue, 11 Jun 2024 12:12:26 +0800 Subject: [PATCH 3/3] update sub module - data --- tests/data | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/data b/tests/data index 5fe47d2..df33309 160000 --- a/tests/data +++ b/tests/data @@ -1 +1 @@ -Subproject commit 5fe47d2fa6296de64e2a358ea814755aa62741c8 +Subproject commit df333093dae580ff94388bc33040375a5cb630dd