brk_cli/
lib.rs

1#![doc = include_str!("../README.md")]
2
3use std::{
4    fs,
5    io::Cursor,
6    path::Path,
7    thread::{self, sleep},
8    time::Duration,
9};
10
11use bitcoincore_rpc::{self, RpcApi};
12use brk_bundler::bundle;
13use brk_computer::Computer;
14use brk_indexer::Indexer;
15use brk_interface::Interface;
16use brk_parser::Parser;
17use brk_server::{Server, VERSION};
18use log::info;
19use vecdb::Exit;
20
21mod bridge;
22mod config;
23mod paths;
24mod website;
25
26use crate::{bridge::Bridge, config::Config, paths::*};
27
28pub fn main() -> color_eyre::Result<()> {
29    color_eyre::install()?;
30
31    fs::create_dir_all(dot_brk_path())?;
32
33    brk_logger::init(Some(&dot_brk_log_path()))?;
34
35    thread::Builder::new()
36        .stack_size(256 * 1024 * 1024)
37        .spawn(run)?
38        .join()
39        .unwrap()
40}
41
42pub fn run() -> color_eyre::Result<()> {
43    let config = Config::import()?;
44
45    let rpc = config.rpc()?;
46
47    let exit = Exit::new();
48    exit.set_ctrlc_handler();
49
50    let parser = Parser::new(config.blocksdir(), Some(config.brkdir()), rpc);
51
52    let mut indexer = Indexer::forced_import(&config.brkdir())?;
53
54    let wait_for_synced_node = |rpc_client: &bitcoincore_rpc::Client| -> color_eyre::Result<()> {
55        let is_synced = || -> color_eyre::Result<bool> {
56            let info = rpc_client.get_blockchain_info()?;
57            Ok(info.headers == info.blocks)
58        };
59
60        if !is_synced()? {
61            info!("Waiting for node to be synced...");
62            while !is_synced()? {
63                sleep(Duration::from_secs(1))
64            }
65        }
66
67        Ok(())
68    };
69
70    let mut computer = Computer::forced_import(&config.brkdir(), &indexer, config.fetcher())?;
71
72    tokio::runtime::Builder::new_multi_thread()
73        .enable_all()
74        .build()?
75        .block_on(async {
76            let interface = Interface::build(&indexer, &computer);
77
78            let website = config.website();
79
80            let downloads_path = config.downloads_dir();
81
82            let bundle_path = if website.is_some() {
83                let websites_dev_path = Path::new("../../websites");
84
85                let websites_path = if fs::exists(websites_dev_path)? {
86                    websites_dev_path.to_path_buf()
87                } else {
88                    let downloaded_websites_path =
89                        downloads_path.join(format!("brk-{VERSION}")).join("websites");
90
91                    if !fs::exists(&downloaded_websites_path)? {
92                        info!("Downloading websites from Github...");
93
94                        let url = format!(
95                            "https://github.com/bitcoinresearchkit/brk/archive/refs/tags/v{VERSION}.zip",
96                        );
97
98                        let response = minreq::get(url).send()?;
99                        let bytes = response.as_bytes();
100                        let cursor = Cursor::new(bytes);
101
102                        let mut zip = zip::ZipArchive::new(cursor).unwrap();
103
104                        zip.extract(downloads_path).unwrap();
105                    }
106
107                    downloaded_websites_path
108                };
109
110                interface.generate_bridge_files(website, websites_path.as_path())?;
111
112                Some(bundle(&websites_path, website.to_folder_name(), true).await?)
113            } else {
114                None
115            };
116
117            let server = Server::new(
118                interface,
119                bundle_path,
120            );
121
122            tokio::spawn(async move {
123                server.serve(true).await.unwrap();
124            });
125
126            sleep(Duration::from_secs(1));
127
128            loop {
129                wait_for_synced_node(rpc)?;
130
131                let block_count = rpc.get_block_count()?;
132
133                info!("{} blocks found.", block_count + 1);
134
135                let starting_indexes =
136                    indexer.index(&parser, rpc, &exit, config.check_collisions()).unwrap();
137
138                // dbg!(&starting_indexes);
139
140                computer.compute(&indexer, starting_indexes, &exit).unwrap();
141
142                info!("Waiting for new blocks...");
143
144                while block_count == rpc.get_block_count()? {
145                    sleep(Duration::from_secs(1))
146                }
147            }
148        })
149}