Skip to main content

create_fs_file/
create.rs

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    // TODO: use the cuprated config here?
54    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}