Skip to content

Commit 1bdb86f

Browse files
authored
Merge pull request #55 from rustaceanrob/12-8-hints-read
Migrate to new hints file format
2 parents 025125d + 46c7974 commit 1bdb86f

File tree

10 files changed

+75
-161
lines changed

10 files changed

+75
-161
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,5 @@
55
This repository is a collection of crates related to a SwiftSync node implementation. Some crates will be SwiftSync-specific, while others may have broader use cases.
66

77
- `accumulator`: A hash-based SwiftSync accumulator used to add and subtrack elements from a set.
8-
- `hintfile`: Generate a SwiftSync hintfile as the role of a server.
8+
- `hintfile`: Read a hints file from a server.
99
- `node`: Perform fast IBD using a SwiftSync hints file.

hintfile/Cargo.toml

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,4 @@ name = "hintfile"
33
version = "0.1.0"
44
edition = "2021"
55

6-
[dependencies]
7-
kernel = { workspace = true }
86

9-
configure_me = "0.4.0"
10-
11-
[build-dependencies]
12-
configure_me_codegen = "0.4.0"
13-
14-
[package.metadata.configure_me]
15-
spec = "config_spec.toml"
16-
17-
[[bin]]
18-
name = "construct"

hintfile/build.rs

Lines changed: 0 additions & 5 deletions
This file was deleted.

hintfile/config_spec.toml

Lines changed: 0 additions & 17 deletions
This file was deleted.

hintfile/src/bin/construct.rs

Lines changed: 0 additions & 75 deletions
This file was deleted.

hintfile/src/lib.rs

Lines changed: 37 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
use std::{
22
collections::BTreeMap,
3-
io::{self, Read, Write},
3+
fs::File,
4+
io::{self, Read, Seek, SeekFrom, Write},
45
};
56

6-
type BlockHash = [u8; 32];
77
type BlockHeight = u32;
8+
type FilePos = u64;
89

910
pub fn write_compact_size<W: Write>(value: u64, writer: &mut W) -> Result<(), io::Error> {
1011
match value {
@@ -45,43 +46,46 @@ pub fn read_compact_size<R: Read>(reader: &mut R) -> Result<u64, io::Error> {
4546

4647
#[derive(Debug)]
4748
pub struct Hints {
48-
map: BTreeMap<BlockHeight, Vec<u64>>,
49-
assume_valid: BlockHash,
49+
map: BTreeMap<BlockHeight, FilePos>,
50+
file: File,
5051
stop_height: BlockHeight,
5152
}
5253

5354
impl Hints {
5455
// # Panics
5556
//
5657
// Panics when expected data is not present, or the hintfile overflows the maximum blockheight
57-
pub fn from_file<R: Read>(reader: &mut R) -> Self {
58+
pub fn from_file(mut file: File) -> Self {
5859
let mut map = BTreeMap::new();
59-
let mut height = 1;
60+
let mut magic = [0; 4];
61+
file.read_exact(&mut magic).unwrap();
62+
assert_eq!(magic, [0x55, 0x54, 0x58, 0x4f]);
63+
let mut ver = [0; 1];
64+
file.read_exact(&mut ver).unwrap();
65+
if u8::from_le_bytes(ver) != 0x00 {
66+
panic!("Unsupported file version.");
67+
}
6068
let mut stop_height = [0; 4];
61-
reader.read_exact(&mut stop_height).expect("empty file");
62-
let mut assume_valid = [0; 32];
63-
reader.read_exact(&mut assume_valid).expect("empty file");
64-
while let Ok(count) = read_compact_size(reader) {
65-
// panics on 32 bit machines
66-
let mut offsets = Vec::with_capacity(count as usize);
67-
for _ in 0..count {
68-
offsets.push(read_compact_size(reader).expect("unexpected end of hintfile"));
69-
}
70-
map.insert(height, offsets);
71-
height += 1;
69+
file.read_exact(&mut stop_height).expect("empty file");
70+
let stop_height = BlockHeight::from_le_bytes(stop_height);
71+
for _ in 1..=stop_height {
72+
let mut height = [0; 4];
73+
file.read_exact(&mut height)
74+
.expect("expected kv pair does not exist.");
75+
let height = BlockHeight::from_le_bytes(height);
76+
let mut file_pos = [0; 8];
77+
file.read_exact(&mut file_pos)
78+
.expect("expected kv pair does not exist.");
79+
let file_pos = FilePos::from_le_bytes(file_pos);
80+
map.insert(height, file_pos);
7281
}
7382
Self {
7483
map,
75-
assume_valid,
76-
stop_height: BlockHeight::from_le_bytes(stop_height),
84+
file,
85+
stop_height,
7786
}
7887
}
7988

80-
/// Get the last hash encoded in the hintfile.
81-
pub fn stop_hash(&self) -> BlockHash {
82-
self.assume_valid
83-
}
84-
8589
/// Get the stop height of the hint file.
8690
pub fn stop_height(&self) -> BlockHeight {
8791
self.stop_height
@@ -91,15 +95,20 @@ impl Hints {
9195
///
9296
/// If there are no offset present at that height, aka an overflow, or the entry has already
9397
/// been fetched.
94-
pub fn get_indexes(&self, height: BlockHeight) -> Vec<u64> {
95-
let offsets = self
98+
pub fn get_indexes(&mut self, height: BlockHeight) -> Vec<u64> {
99+
let file_pos = self
96100
.map
97101
.get(&height)
98102
.cloned()
99103
.expect("block height overflow");
100-
let mut indexes = Vec::with_capacity(offsets.len());
104+
self.file
105+
.seek(SeekFrom::Start(file_pos))
106+
.expect("missing file position.");
107+
let num_unspents = read_compact_size(&mut self.file).expect("unexpected missing hints.");
108+
let mut indexes = Vec::new();
101109
let mut prev = 0;
102-
for offset in offsets {
110+
for _ in 0..num_unspents {
111+
let offset = read_compact_size(&mut self.file).expect("unexpected missing hints.");
103112
let next = prev + offset;
104113
indexes.push(next);
105114
prev = next;

node/README.md

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,23 @@
22

33
This binary implements a SwiftSync client that downloads blocks in parallel from multiple peers, references a hint file, and updates an accumulator. Once the client has reached the stop hash specified in the hint file, the accumulator state is reported as verified or false. For more information, read the [SwiftSync specification](https://gist.github.com/RubenSomsen/a61a37d14182ccd78760e477c78133cd).
44

5-
You will need a `.hints` file locally to run this binary. There is one committed to by the repository as a `zip` file. You may uncompress it with the `unzip` tool.
5+
You will need a `.hints` file locally to run this binary. You may download one from a server:
66

7-
```
8-
sudo apt-get install unzip
9-
```
7+
For Signet:
108

9+
```bash
10+
curl -o signet.hints http://utxohints.store/hints/signet
1111
```
12-
unzip bitcoin.hints.zip
12+
13+
Or for Bitcoin:
14+
15+
```bash
16+
curl -o bitcoin.hints http://utxohints.store/hints/bitcoin
1317
```
1418

1519
To build the Bitcoin kernel, you will need the following on Ubuntu:
1620

17-
```
21+
```bash
1822
sudo apt-get install build-essential cmake pkgconf python3 libevent-dev libboost-dev
1923
```
2024

@@ -24,7 +28,7 @@ Finally, you will need Rust and cargo installed, you may download them from [her
2428

2529
To start fast IBD:
2630

27-
```
31+
```bash
2832
cargo run --bin ibd --release -- <args>
2933
```
3034

node/bitcoin.hints.zip

-89.1 MB
Binary file not shown.

node/src/bin/ibd.rs

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use std::{
55
time::{Duration, Instant},
66
};
77

8-
use bitcoin::{consensus, BlockHash, Network};
8+
use bitcoin::Network;
99
use hintfile::Hints;
1010
use kernel::{ChainstateManager, ChainstateManagerOptions, ContextBuilder};
1111

@@ -41,17 +41,16 @@ fn main() {
4141
tracing::subscriber::set_global_default(subscriber).unwrap();
4242
let hintfile_start_time = Instant::now();
4343
tracing::info!("Reading in {hint_path}");
44-
let mut hintfile = File::open(hint_path).expect("invalid hintfile path");
45-
let hints = Arc::new(Hints::from_file(&mut hintfile));
44+
let hintfile = File::open(hint_path).expect("invalid hintfile path");
45+
let hints = Hints::from_file(hintfile);
46+
let stop_height = hints.stop_height();
4647
elapsed_time(hintfile_start_time);
4748
tracing::info!("Syncing to height {}", hints.stop_height());
4849
let block_file_path = blocks_dir.map(PathBuf::from);
4950
if let Some(block_file_path) = block_file_path.as_ref() {
5051
std::fs::create_dir(block_file_path).expect("could not create block file directory");
5152
}
52-
let stop_hash =
53-
consensus::deserialize::<BlockHash>(&hints.stop_hash()).expect("stop hash is not valid");
54-
tracing::info!("Assume valid hash: {stop_hash}");
53+
tracing::info!("Assume valid height: {stop_height}");
5554
tracing::info!("Finding peers with DNS");
5655
let dns_start_time = Instant::now();
5756
let peers = bootstrap_dns(network);
@@ -68,7 +67,13 @@ fn main() {
6867
let tip = chainman.best_header().height();
6968
tracing::info!("Kernel best header: {tip}");
7069
let chain = Arc::new(chainman);
71-
sync_block_headers(stop_hash, &peers, Arc::clone(&chain), network, timeout_conf);
70+
sync_block_headers(
71+
stop_height,
72+
&peers,
73+
Arc::clone(&chain),
74+
network,
75+
timeout_conf,
76+
);
7277
tracing::info!("Assume valid height: {}", chain.best_header().height());
7378
let (tx, rx) = channel();
7479
let main_routine_time = Instant::now();
@@ -78,6 +83,7 @@ fn main() {
7883
let mut tasks = Vec::new();
7984
let hashes = hashes_from_chain(Arc::clone(&chain));
8085
let arc_hashes = Arc::new(Mutex::new(hashes));
86+
let hints = Arc::new(Mutex::new(hints));
8187
for task_id in 0..task_num {
8288
let chain = Arc::clone(&chain);
8389
let tx = tx.clone();
@@ -93,7 +99,7 @@ fn main() {
9399
network,
94100
block_file_path,
95101
chain,
96-
&hints,
102+
hints,
97103
peers,
98104
tx,
99105
hashes,

node/src/lib.rs

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ pub fn bootstrap_dns(network: Network) -> Vec<SocketAddr> {
7474
}
7575

7676
pub fn sync_block_headers(
77-
stop_hash: BlockHash,
77+
stop_height: u32,
7878
hosts: &[SocketAddr],
7979
chainman: Arc<ChainstateManager>,
8080
network: Network,
@@ -99,9 +99,10 @@ pub fn sync_block_headers(
9999
Err(_) => continue,
100100
};
101101
tracing::info!("Connection established");
102-
let curr = chainman.best_header().block_hash().hash;
103-
let locator = BlockHash::from_byte_array(curr);
104-
if locator.eq(&stop_hash) {
102+
let curr = chainman.best_header();
103+
let locator = BlockHash::from_byte_array(curr.block_hash().hash);
104+
let curr_height = curr.height() as u32;
105+
if curr_height.eq(&stop_height) {
105106
tracing::info!("Using existing header state");
106107
return;
107108
}
@@ -124,7 +125,8 @@ pub fn sync_block_headers(
124125
chainman
125126
.process_new_block_headers(&consensus::serialize(&header), true)
126127
.expect("process headers failed");
127-
if header.block_hash().eq(&stop_hash) {
128+
let curr_height = chainman.best_header().height() as u32;
129+
if curr_height.eq(&stop_height) {
128130
tracing::info!("Done syncing block headers");
129131
if let Some(message_rate) =
130132
metrics.message_rate(p2p::TimedMessage::BlockHeaders)
@@ -175,7 +177,7 @@ pub fn get_blocks_for_range(
175177
network: Network,
176178
block_dir: Option<PathBuf>,
177179
chain: Arc<ChainstateManager>,
178-
hints: &Hints,
180+
hints: Arc<Mutex<Hints>>,
179181
peers: Arc<Mutex<Vec<SocketAddr>>>,
180182
updater: Sender<AccumulatorUpdate>,
181183
hashes: Arc<Mutex<Vec<Vec<BlockHash>>>>,
@@ -231,8 +233,10 @@ pub fn get_blocks_for_range(
231233
.block_index_by_hash(kernal_hash)
232234
.expect("header is in best chain.");
233235
let block_height = block_index.height().unsigned_abs();
234-
let unspent_indexes: HashSet<u64> =
235-
hints.get_indexes(block_height).into_iter().collect();
236+
let unspent_indexes: HashSet<u64> = {
237+
let mut hint_ref = hints.lock().unwrap();
238+
hint_ref.get_indexes(block_height).into_iter().collect()
239+
};
236240
if let Some(block_dir) = block_dir.as_ref() {
237241
let file_path = block_dir.join(format!("{hash}.block"));
238242
let file = File::create_new(file_path);

0 commit comments

Comments
 (0)