1#![expect(
2 unused_crate_dependencies,
3 reason = "binary shares same Cargo.toml as library"
4)]
5use std::{fs::write, sync::Arc};
6
7use clap::Parser;
8use futures::TryStreamExt;
9use tower::{Service, ServiceExt};
10
11use cuprate_blockchain::{config::Config, service::BlockchainReadHandle, DbResult};
12use cuprate_hex::Hex;
13use cuprate_types::{
14 blockchain::{BlockchainReadRequest, BlockchainResponse},
15 Chain,
16};
17
18use cuprate_fast_sync::FAST_SYNC_BATCH_LEN;
19use cuprate_helper::fs::CUPRATE_DATA_DIR;
20
21async fn read_batch(
22 handle: &mut BlockchainReadHandle,
23 height_from: usize,
24) -> DbResult<Vec<[u8; 32]>> {
25 let request = BlockchainReadRequest::BlockHashInRange(
26 height_from..(height_from + FAST_SYNC_BATCH_LEN),
27 Chain::Main,
28 );
29 let response_channel = handle.ready().await?.call(request);
30 let response = response_channel.await?;
31
32 let BlockchainResponse::BlockHashInRange(block_ids) = response else {
33 unreachable!()
34 };
35
36 Ok(block_ids)
37}
38
39#[derive(Parser)]
40#[command(version, about, long_about = None)]
41struct Args {
42 #[arg(long)]
43 height: usize,
44}
45
46#[tokio::main]
47async fn main() {
48 let args = Args::parse();
49 tracing_subscriber::fmt().init();
50
51 let height_target = args.height;
52
53 let config = Config::default();
55
56 let fjall_dir = CUPRATE_DATA_DIR.to_path_buf().join("fjall");
57
58 let fjall = fjall::Database::builder(fjall_dir).open().unwrap();
59
60 let thread_pool = Arc::new(rayon::ThreadPoolBuilder::new().build().unwrap());
61
62 let (read_handle, _, _) =
63 cuprate_blockchain::service::init_with_pool(&config, fjall, thread_pool).unwrap();
64
65 let time = std::time::Instant::now();
66
67 let fut = (0..(height_target / FAST_SYNC_BATCH_LEN) * FAST_SYNC_BATCH_LEN)
68 .step_by(FAST_SYNC_BATCH_LEN)
69 .map(|height| {
70 let mut read_handle = read_handle.clone();
71 async move {
72 println!("height: {height}");
73
74 if let Ok(block_ids) = read_batch(&mut read_handle, height).await {
75 let hash = hash_of_hashes(block_ids.as_slice());
76 Ok(Hex(hash))
77 } else {
78 println!("Failed to read next batch from database");
79 Err("Failed to read next batch from database")
80 }
81 }
82 })
83 .collect::<futures::stream::FuturesOrdered<_>>();
84
85 let hashes_of_hashes = fut.try_collect::<Vec<_>>().await.unwrap();
86
87 drop(read_handle);
88
89 write(
90 "fast_sync_hashes.json",
91 serde_json::to_string_pretty(&hashes_of_hashes).unwrap(),
92 )
93 .unwrap();
94
95 println!(
96 "Generated hashes up to block height {} in {} milliseconds.",
97 hashes_of_hashes.len() * FAST_SYNC_BATCH_LEN,
98 time.elapsed().as_millis()
99 );
100}
101
102pub fn hash_of_hashes(hashes: &[[u8; 32]]) -> [u8; 32] {
103 blake3::hash(hashes.concat().as_slice()).into()
104}