Skip to content

Commit bfc71d7

Browse files
authored
Merge pull request #19 from EthanYuan/adaptation_to_signet
feat: adaptation to btc signet
2 parents bfb9ed0 + b81fd00 commit bfc71d7

File tree

9 files changed

+138
-19
lines changed

9 files changed

+138
-19
lines changed

prover/src/dummy_service.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,8 @@ impl DummyService {
8484
header.time
8585
);
8686
let start_time: u32 = self.client.target_adjust_info.start_time().unpack();
87-
let next_target = calculate_next_target(curr_target, start_time, header.time);
87+
let next_target =
88+
calculate_next_target(curr_target, start_time, header.time, 0);
8889
log::info!(">>> calculated new target {next_target:#x}");
8990
let next_bits = next_target.to_compact_lossy();
9091
let next_target: core::Target = next_bits.into();

tests/data

Submodule data updated from 5fe47d2 to df33309

verifier/src/constants.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
11
//! Constants.
22
3-
pub const FLAG_DISABLE_DIFFICULTY_CHECK: u8 = 0b1000_0000;
3+
// Constants for the chain type flag
4+
// Specifically utilizing the two highest bits for chain type identification
5+
pub const FLAG_CHAIN_TYPE_MAINNET: u8 = 0b0000_0000; // for mainnet
6+
pub const FLAG_CHAIN_TYPE_TESTNET: u8 = 0b1000_0000; // for testnet
7+
pub const FLAG_CHAIN_TYPE_SIGNET: u8 = 0b0100_0000; // for signet

verifier/src/tests/bitcoin.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,8 @@ fn main_chain_targets_and_chainwork() {
5151
match (height + 1) % interval {
5252
0 => {
5353
assert!(prev_height + interval - 1 == height);
54-
let next_target = calculate_next_target(curr_target, start_time, header.time);
54+
let next_target =
55+
calculate_next_target(curr_target, start_time, header.time, 0);
5556
log::info!(">>> calculated new target {next_target:#x}");
5657
next_bits = next_target.to_compact_lossy();
5758
let next_target: Target = next_bits.into();

verifier/src/tests/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use log::LevelFilter;
66
mod bitcoin;
77

88
pub(crate) mod data;
9+
pub(crate) mod signet;
910
pub(crate) mod testnet;
1011
pub(crate) mod utilities;
1112

verifier/src/tests/signet.rs

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
use std::fs::read_to_string;
2+
3+
use alloc::format;
4+
use ckb_jsonrpc_types::TransactionView;
5+
use ckb_types::packed::WitnessArgs;
6+
use serde_json::from_str as from_json_str;
7+
8+
use crate::{
9+
error::UpdateError,
10+
molecule::prelude::*,
11+
tests,
12+
types::{
13+
core,
14+
packed::{SpvClient, SpvUpdate},
15+
prelude::*,
16+
},
17+
};
18+
19+
// This case shows that:
20+
// - For the signet network, `header.bits` should be the same as `new_info.1`.
21+
// To run this test, use the following command:
22+
// `cargo test --package ckb-bitcoin-spv-verifier --lib -- tests::signet::signet_verify_new_client_error_197568 --exact --show-output`
23+
#[test]
24+
fn signet_verify_new_client_error_197568() {
25+
let ret = verify_new_client_common(
26+
"tx-0528-error-check-header-target-adjust-info.json",
27+
1, // cell_dep_index
28+
);
29+
assert!(ret.is_err());
30+
}
31+
32+
// This case shows that:
33+
// - For the signet network, target max should be Target::MAX_ATTAINABLE_SIGNET.
34+
// To run this test, use the following command:
35+
// `cargo test --package ckb-bitcoin-spv-verifier --lib -- tests::signet::signet_verify_new_client_error_header_197567 --exact --show-output`
36+
#[test]
37+
fn signet_verify_new_client_error_header_197567() {
38+
let ret = verify_new_client_common(
39+
"tx-0xd663a1dfdfbf9a4824950c44c0d5f5e65f6b1ba4710a0308edecadeaed3ac549.json",
40+
2, // cell_dep_index
41+
);
42+
assert!(ret.is_err());
43+
}
44+
45+
// To run this test, use the following command:
46+
// `cargo test --package ckb-bitcoin-spv-verifier --lib -- tests::signet::signet_verify_new_client_normal --exact --show-output`
47+
#[test]
48+
fn signet_verify_new_client_normal() {
49+
let ret = verify_new_client_common(
50+
"tx-0x01d827b049778ffb53532d8263009512a696647bde4acc7f10f39ded14c066ab.json",
51+
2, // cell_dep_index
52+
);
53+
assert!(ret.is_ok());
54+
}
55+
56+
fn verify_new_client_common(tx_file: &str, cell_dep_index: usize) -> Result<(), UpdateError> {
57+
tests::setup();
58+
59+
let path = tests::data::find_bin_file("signet", tx_file);
60+
let tx = read_to_string(path).unwrap();
61+
let tx: TransactionView = from_json_str(&tx).unwrap();
62+
63+
let witnesses = tx.inner.witnesses;
64+
let witness_args = WitnessArgs::from_slice(witnesses[0].as_bytes()).unwrap();
65+
let spv_update_bin = witness_args.output_type().to_opt().unwrap().raw_data();
66+
let spv_update = SpvUpdate::from_slice(&spv_update_bin).unwrap();
67+
68+
let client_bin = tx.inner.outputs_data[1].clone();
69+
let client = SpvClient::from_slice(client_bin.as_bytes()).unwrap();
70+
71+
let cell_dep = tx.inner.cell_deps[cell_dep_index].out_point.clone();
72+
let path =
73+
tests::data::find_bin_file("signet", format!("tx-0x{}.json", cell_dep.tx_hash).as_str());
74+
let previous_tx = read_to_string(path).unwrap();
75+
let previous_tx: TransactionView = from_json_str(&previous_tx).unwrap();
76+
let cell_dep_data_bin = &previous_tx.inner.outputs_data[cell_dep.index.value() as usize];
77+
let cell_dep_client = SpvClient::from_slice(cell_dep_data_bin.as_bytes()).unwrap();
78+
79+
let mut cell_dep_client: core::SpvClient = cell_dep_client.unpack();
80+
cell_dep_client.id = client.id().into();
81+
let input_client = cell_dep_client.pack();
82+
input_client.verify_new_client(&client, spv_update, 64)
83+
}

verifier/src/types/core.rs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ pub use bitcoin_hashes::sha256d::Hash;
2323
pub use molecule::bytes::Bytes;
2424
pub use primitive_types::U256;
2525

26-
use crate::types::packed;
26+
use crate::{constants::*, types::packed};
2727

2828
//
2929
// Proofs
@@ -116,3 +116,22 @@ impl fmt::Display for SpvClient {
116116
)
117117
}
118118
}
119+
120+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
121+
pub enum BitcoinChainType {
122+
Mainnet,
123+
Testnet,
124+
Signet,
125+
Other, // For future use.
126+
}
127+
128+
impl From<u8> for BitcoinChainType {
129+
fn from(flags: u8) -> Self {
130+
match flags & 0b1100_0000 {
131+
FLAG_CHAIN_TYPE_MAINNET => BitcoinChainType::Mainnet,
132+
FLAG_CHAIN_TYPE_TESTNET => BitcoinChainType::Testnet,
133+
FLAG_CHAIN_TYPE_SIGNET => BitcoinChainType::Signet,
134+
_ => BitcoinChainType::Other,
135+
}
136+
}
137+
}

verifier/src/types/extension/packed.rs

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ use bitcoin::{
99
use molecule::bytes::Bytes;
1010

1111
use crate::{
12-
constants,
1312
core::result::Result,
1413
error::{BootstrapError, UpdateError, VerifyTxError},
1514
types::{core, packed, prelude::*},
@@ -149,19 +148,19 @@ impl packed::SpvClient {
149148
expect {expected} but got {actual}"
150149
);
151150
});
152-
if flags & constants::FLAG_DISABLE_DIFFICULTY_CHECK == 0 {
151+
152+
// For mainnet and signet, `header.bits` should be as the same as `new_info.1`.
153+
// But for testnet, it could be not.
154+
if core::BitcoinChainType::Testnet != flags.into() {
153155
return Err(UpdateError::Difficulty);
154156
}
155157
}
156158
// Check POW.
157-
new_tip_block_hash = if flags & constants::FLAG_DISABLE_DIFFICULTY_CHECK == 0 {
158-
header
159-
.validate_pow(new_info.1.into())
160-
.map_err(|_| UpdateError::Pow)?
161-
} else {
162-
header.block_hash()
163-
}
164-
.into();
159+
new_tip_block_hash = header
160+
.validate_pow(header.bits.into())
161+
.map_err(|_| UpdateError::Pow)?
162+
.into();
163+
165164
// Update the target adjust info.
166165
{
167166
match (new_max_height + 1) % DIFFCHANGE_INTERVAL {
@@ -172,7 +171,7 @@ impl packed::SpvClient {
172171
// - But for testnet, it could be not.
173172
let prev_target = header.bits.into();
174173
let next_target =
175-
calculate_next_target(prev_target, new_info.0, header.time);
174+
calculate_next_target(prev_target, new_info.0, header.time, flags);
176175
new_info.1 = next_target.to_compact_lossy();
177176
}
178177
// Current block is the first block for a new difficulty.

verifier/src/utilities/bitcoin.rs

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,21 @@
55
use bitcoin::{blockdata::constants::DIFFCHANGE_TIMESPAN, pow::Target};
66
use primitive_types::U256;
77

8+
use crate::types::core::BitcoinChainType;
9+
810
/// Calculates the next target.
911
///
1012
/// N.B. The end time is not the block time of the next 2016-th header.
1113
///
1214
/// Ref:
1315
/// - [What is the Target in Bitcoin?](https://learnmeabitcoin.com/technical/target)
1416
/// - [`CalculateNextWorkRequired(..)` in Bitcoin source code](https://github.com/bitcoin/bitcoin/blob/v26.0/src/pow.cpp#L49)
15-
pub fn calculate_next_target(prev_target: Target, start_time: u32, end_time: u32) -> Target {
17+
pub fn calculate_next_target(
18+
prev_target: Target,
19+
start_time: u32,
20+
end_time: u32,
21+
flags: u8,
22+
) -> Target {
1623
let expected = DIFFCHANGE_TIMESPAN;
1724
let actual = {
1825
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
4047
};
4148

4249
let target = Target::from_le_bytes(le_bytes);
43-
if target > Target::MAX {
50+
let max_target = match flags.into() {
51+
BitcoinChainType::Signet => Target::MAX_ATTAINABLE_SIGNET,
52+
_ => Target::MAX,
53+
};
54+
if target > max_target {
4455
trace!("fallback to the max target");
45-
Target::MAX
56+
max_target
4657
} else {
4758
trace!("use the calculated target");
4859
target

0 commit comments

Comments
 (0)