1#![doc = include_str!("../README.md")]
19#![cfg_attr(docsrs, feature(doc_cfg))]
20#![allow(
21 unused_imports,
22 unreachable_pub,
23 unreachable_code,
24 unused_crate_dependencies,
25 dead_code,
26 unused_variables,
27 clippy::needless_pass_by_value,
28 clippy::unused_async,
29 clippy::diverging_sub_expression,
30 unused_mut,
31 clippy::let_unit_value,
32 clippy::needless_pass_by_ref_mut,
33 reason = "TODO: remove after v1.0.0"
34)]
35
36pub mod blockchain;
37pub mod config;
38pub mod constants;
39pub mod logging;
40pub mod monitor;
41pub mod version;
42
43mod p2p;
44mod rpc;
45mod tor;
46mod txpool;
47
48use std::sync::Arc;
49
50use anyhow::Context;
51use tokio::sync::{oneshot, RwLock};
52use tower::{Service, ServiceExt};
53use tracing::error;
54
55use cuprate_p2p::NetworkInterface;
56use cuprate_p2p_core::{ClearNet, Tor};
57use cuprate_txpool::service::TxpoolReadHandle;
58use cuprate_types::blockchain::BlockchainWriteRequest;
59
60use crate::{
61 blockchain::{BlockchainInterface, BlockchainManagerHandle, BlockchainSyncerHandle},
62 config::Config,
63 constants::DATABASE_CORRUPT_MSG,
64 monitor::TaskExecutor,
65 tor::initialize_tor_if_enabled,
66 txpool::IncomingTxHandler,
67};
68
69#[derive(Clone)]
76pub(crate) struct LaunchContext {
77 pub config: Arc<Config>,
79
80 pub reorg_lock: Arc<RwLock<()>>,
88
89 pub blockchain: BlockchainInterface,
91
92 pub txpool_read: TxpoolReadHandle,
94
95 pub task_executor: TaskExecutor,
97}
98
99#[must_use]
103pub struct Node {
104 pub blockchain: BlockchainInterface,
106
107 pub txpool: TxpoolReadHandle,
109
110 pub clearnet: NetworkInterface<ClearNet>,
112
113 pub tor: Option<oneshot::Receiver<NetworkInterface<Tor>>>,
115
116 pub config: Arc<Config>,
118
119 pub task_executor: TaskExecutor,
121}
122
123impl Drop for Node {
124 fn drop(&mut self) {
125 self.task_executor.trigger_shutdown();
126 }
127}
128
129impl Node {
130 pub async fn launch(config: impl Into<Arc<Config>>) -> Result<Self, anyhow::Error> {
145 let config: Arc<Config> = config.into();
146
147 let db_thread_pool = Arc::new(
149 rayon::ThreadPoolBuilder::new()
150 .num_threads(config.storage.reader_threads)
151 .build()
152 .context("failed to build rayon database thread pool")?,
153 );
154
155 let fjall_db = fjall::Database::builder(config.fjall_directory())
157 .cache_size(config.fjall_cache_size())
158 .open()
159 .context(DATABASE_CORRUPT_MSG)?;
160
161 let (mut blockchain_read_handle, mut blockchain_write_handle, _) =
162 cuprate_blockchain::service::init_with_pool(
163 &config.blockchain_config(),
164 fjall_db.clone(),
165 Arc::clone(&db_thread_pool),
166 )
167 .context(DATABASE_CORRUPT_MSG)?;
168
169 let (txpool_read_handle, txpool_write_handle) =
170 cuprate_txpool::service::init_with_pool(fjall_db, db_thread_pool)
171 .context(DATABASE_CORRUPT_MSG)?;
172
173 blockchain_write_handle
175 .ready()
176 .await?
177 .call(BlockchainWriteRequest::FlushAltBlocks)
178 .await?;
179
180 blockchain::check_add_genesis(
182 &mut blockchain_read_handle,
183 &mut blockchain_write_handle,
184 config.network(),
185 )
186 .await;
187
188 let context_svc =
190 blockchain::init_consensus(blockchain_read_handle.clone(), config.context_config())
191 .await
192 .map_err(anyhow::Error::from_boxed)?;
193
194 let (blockchain_syncer_handle, synced_tx) = BlockchainSyncerHandle::new();
196
197 let (blockchain_manager_handle, command_rx) = BlockchainManagerHandle::new();
199
200 let blockchain_interface = BlockchainInterface::new(
202 blockchain_read_handle,
203 context_svc,
204 blockchain_manager_handle,
205 blockchain_syncer_handle,
206 );
207
208 let launch_ctx = LaunchContext {
210 config,
211 reorg_lock: Arc::new(RwLock::new(())),
212 blockchain: blockchain_interface,
213 txpool_read: txpool_read_handle,
214 task_executor: TaskExecutor::new(),
215 };
216
217 let tor_enabled = !launch_ctx.config.offline && launch_ctx.config.p2p.tor_net.enabled;
219 let tor_context = initialize_tor_if_enabled(&launch_ctx).await;
220
221 let (clearnet_interface, clearnet_tx_handler_subscriber) =
223 p2p::initialize_clearnet_p2p(&launch_ctx, &tor_context).await;
224
225 let (tor_router_tx, tor_router_rx) = tor_enabled.then(oneshot::channel).unzip();
227
228 let tx_handler = IncomingTxHandler::init(
230 &launch_ctx,
231 clearnet_interface.clone(),
232 tor_router_rx,
233 txpool_write_handle,
234 )
235 .await;
236
237 if let Some(subscriber) = clearnet_tx_handler_subscriber {
239 if subscriber.send(tx_handler.clone()).is_err() {
240 unreachable!()
241 }
242 }
243
244 let (tor_tx, tor_rx) = oneshot::channel();
246
247 blockchain::init_blockchain_manager(
249 &launch_ctx,
250 clearnet_interface.clone(),
251 blockchain_write_handle,
252 tx_handler.txpool_manager.clone(),
253 synced_tx,
254 command_rx,
255 )
256 .await?;
257
258 rpc::init_rpc_servers(&launch_ctx, tx_handler.clone());
260
261 if tor_enabled {
263 p2p::initialize_tor_p2p(
264 launch_ctx.clone(),
265 tor_context,
266 tx_handler,
267 tor_tx,
268 tor_router_tx,
269 );
270 }
271
272 let LaunchContext {
273 blockchain,
274 txpool_read,
275 config,
276 task_executor,
277 ..
278 } = launch_ctx;
279
280 Ok(Self {
281 blockchain,
282 txpool: txpool_read,
283 clearnet: clearnet_interface,
284 tor: if tor_enabled { Some(tor_rx) } else { None },
285 config,
286 task_executor,
287 })
288 }
289
290 pub fn shutdown(&self) {
292 self.task_executor.trigger_shutdown();
293 }
294
295 pub async fn wait_for_shutdown(&self) {
297 self.task_executor.wait_for_shutdown().await;
298 }
299}