Skip to content

Commit f60f577

Browse files
committed
hints: Update file format to JIT reads
Currently breaks the writing side, but allows for parsing from Bitcoin Core PR #34004
1 parent 025125d commit f60f577

File tree

3 files changed

+63
-44
lines changed

3 files changed

+63
-44
lines changed

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/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)