Skip to content

Commit

Permalink
Merge pull request #19 from EthanYuan/adaptation_to_signet
Browse files Browse the repository at this point in the history
feat: adaptation to btc signet
  • Loading branch information
EthanYuan authored Jun 12, 2024
2 parents bfb9ed0 + b81fd00 commit bfc71d7
Show file tree
Hide file tree
Showing 9 changed files with 138 additions and 19 deletions.
3 changes: 2 additions & 1 deletion prover/src/dummy_service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
2 changes: 1 addition & 1 deletion tests/data
Submodule data updated from 5fe47d to df3330
6 changes: 5 additions & 1 deletion verifier/src/constants.rs
Original file line number Diff line number Diff line change
@@ -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
3 changes: 2 additions & 1 deletion verifier/src/tests/bitcoin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
1 change: 1 addition & 0 deletions verifier/src/tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
83 changes: 83 additions & 0 deletions verifier/src/tests/signet.rs
Original file line number Diff line number Diff line change
@@ -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)
}
21 changes: 20 additions & 1 deletion verifier/src/types/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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<u8> 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,
}
}
}
21 changes: 10 additions & 11 deletions verifier/src/types/extension/packed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ use bitcoin::{
use molecule::bytes::Bytes;

use crate::{
constants,
core::result::Result,
error::{BootstrapError, UpdateError, VerifyTxError},
types::{core, packed, prelude::*},
Expand Down Expand Up @@ -149,19 +148,19 @@ 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 {
Expand All @@ -172,7 +171,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.
Expand Down
17 changes: 14 additions & 3 deletions verifier/src/utilities/bitcoin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,21 @@
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.
///
/// 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;
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit bfc71d7

Please sign in to comment.