Skip to main content

cuprated/
blockchain.rs

1//! Blockchain
2//!
3//! Contains the blockchain manager, syncer and an interface to mutate the blockchain.
4use std::sync::Arc;
5
6use futures::FutureExt;
7use tokio::sync::{mpsc, Notify};
8use tower::{BoxError, Service, ServiceExt};
9
10use cuprate_blockchain::service::{BlockchainReadHandle, BlockchainWriteHandle};
11use cuprate_consensus::{
12    generate_genesis_block, BlockchainContext, BlockchainContextService, ContextConfig,
13};
14use cuprate_cryptonight::cryptonight_hash_v0;
15use cuprate_p2p::{block_downloader::BlockDownloaderConfig, NetworkInterface};
16use cuprate_p2p_core::{client::PeerSyncCallback, ClearNet, Network};
17use cuprate_types::{
18    blockchain::{BlockchainReadRequest, BlockchainWriteRequest},
19    VerifiedBlockInformation,
20};
21
22use crate::constants::PANIC_CRITICAL_SERVICE_ERROR;
23
24mod chain_service;
25mod fast_sync;
26pub mod interface;
27mod manager;
28mod syncer;
29mod types;
30
31pub use fast_sync::get_fast_sync_hashes;
32pub use interface::BlockchainManagerHandle;
33pub use syncer::BlockchainSyncerHandle;
34pub use types::ConsensusBlockchainReadHandle;
35
36pub(crate) use manager::init_blockchain_manager;
37
38/// The interface to the blockchain.
39#[derive(Clone)]
40pub struct BlockchainInterface {
41    /// A read handle to the blockchain database.
42    read: BlockchainReadHandle,
43    /// The blockchain context service.
44    context_svc: BlockchainContextService,
45    /// A handle to the blockchain manager.
46    manager: BlockchainManagerHandle,
47    /// A handle to the blockchain syncer.
48    syncer: BlockchainSyncerHandle,
49}
50
51impl BlockchainInterface {
52    pub(crate) const fn new(
53        read: BlockchainReadHandle,
54        context_svc: BlockchainContextService,
55        manager: BlockchainManagerHandle,
56        syncer: BlockchainSyncerHandle,
57    ) -> Self {
58        Self {
59            read,
60            context_svc,
61            manager,
62            syncer,
63        }
64    }
65
66    /// Returns a read handle to the blockchain database.
67    pub fn read(&self) -> BlockchainReadHandle {
68        self.read.clone()
69    }
70
71    /// Returns the current [`BlockchainContext`].
72    pub fn context(&mut self) -> &BlockchainContext {
73        self.context_svc.blockchain_context()
74    }
75
76    /// Returns a handle to the blockchain manager.
77    pub fn manager(&self) -> BlockchainManagerHandle {
78        self.manager.clone()
79    }
80
81    /// Returns a handle to the blockchain syncer.
82    pub fn syncer(&self) -> BlockchainSyncerHandle {
83        self.syncer.clone()
84    }
85
86    /// Returns the blockchain context service.
87    pub(crate) fn context_svc(&self) -> BlockchainContextService {
88        self.context_svc.clone()
89    }
90
91    /// Creates a [`PeerSyncCallback`] that filters and wakes the syncer.
92    pub(crate) fn peer_sync_callback(&self) -> PeerSyncCallback {
93        self.syncer
94            .callback(self.context_svc.clone(), self.manager.clone())
95    }
96}
97
98/// Checks if the genesis block is in the blockchain and adds it if not.
99pub async fn check_add_genesis(
100    blockchain_read_handle: &mut BlockchainReadHandle,
101    blockchain_write_handle: &mut BlockchainWriteHandle,
102    network: Network,
103) {
104    // Try to get the chain height, will fail if the genesis block is not in the DB.
105    if blockchain_read_handle
106        .ready()
107        .await
108        .expect(PANIC_CRITICAL_SERVICE_ERROR)
109        .call(BlockchainReadRequest::ChainHeight)
110        .await
111        .is_ok()
112    {
113        return;
114    }
115
116    let genesis = generate_genesis_block(network);
117
118    assert_eq!(genesis.miner_transaction().prefix().outputs.len(), 1);
119    assert!(genesis.transactions.is_empty());
120
121    blockchain_write_handle
122        .ready()
123        .await
124        .expect(PANIC_CRITICAL_SERVICE_ERROR)
125        .call(BlockchainWriteRequest::WriteBlock(
126            VerifiedBlockInformation {
127                block_blob: genesis.serialize(),
128                txs: vec![],
129                block_hash: genesis.hash(),
130                pow_hash: cryptonight_hash_v0(&genesis.serialize_pow_hash()),
131                height: 0,
132                generated_coins: genesis.miner_transaction().prefix().outputs[0]
133                    .amount
134                    .unwrap(),
135                weight: genesis.miner_transaction().weight(),
136                long_term_weight: genesis.miner_transaction().weight(),
137                cumulative_difficulty: 1,
138                block: genesis,
139            },
140        ))
141        .await
142        .expect(PANIC_CRITICAL_SERVICE_ERROR);
143}
144
145/// Initializes the consensus services.
146pub async fn init_consensus(
147    blockchain_read_handle: BlockchainReadHandle,
148    context_config: ContextConfig,
149) -> Result<BlockchainContextService, BoxError> {
150    let read_handle = ConsensusBlockchainReadHandle::new(blockchain_read_handle, BoxError::from);
151
152    let ctx_service =
153        cuprate_consensus::initialize_blockchain_context(context_config, read_handle.clone())
154            .await?;
155
156    Ok(ctx_service)
157}