Skip to main content

cuprated/
blockchain.rs

1//! Blockchain
2//!
3//! Contains the blockchain manager, syncer and an interface to mutate the blockchain.
4use tower::{BoxError, Service, ServiceExt};
5
6use cuprate_blockchain::service::{BlockchainReadHandle, BlockchainWriteHandle};
7use cuprate_consensus::{
8    generate_genesis_block, BlockchainContext, BlockchainContextService, ContextConfig,
9};
10use cuprate_cryptonight::cryptonight_hash_v0;
11use cuprate_p2p_core::{client::PeerSyncCallback, Network};
12use cuprate_pruning::PruningSeed;
13use cuprate_types::{
14    blockchain::{BlockchainReadRequest, BlockchainWriteRequest},
15    VerifiedBlockInformation,
16};
17
18mod chain_service;
19mod error;
20mod fast_sync;
21pub mod interface;
22mod manager;
23mod syncer;
24mod types;
25
26pub use error::IncomingBlockError;
27pub use fast_sync::get_fast_sync_hashes;
28pub use interface::BlockchainManagerHandle;
29pub use syncer::BlockchainSyncerHandle;
30pub use types::ConsensusBlockchainReadHandle;
31
32pub(crate) use manager::init_blockchain_manager;
33
34/// The interface to the blockchain.
35#[derive(Clone)]
36pub struct BlockchainInterface {
37    /// A read handle to the blockchain database.
38    read: BlockchainReadHandle,
39    /// The blockchain context service.
40    context_svc: BlockchainContextService,
41    /// A handle to the blockchain manager.
42    manager: BlockchainManagerHandle,
43    /// A handle to the blockchain syncer.
44    syncer: BlockchainSyncerHandle,
45}
46
47impl BlockchainInterface {
48    pub(crate) const fn new(
49        read: BlockchainReadHandle,
50        context_svc: BlockchainContextService,
51        manager: BlockchainManagerHandle,
52        syncer: BlockchainSyncerHandle,
53    ) -> Self {
54        Self {
55            read,
56            context_svc,
57            manager,
58            syncer,
59        }
60    }
61
62    /// Returns a read handle to the blockchain database.
63    pub fn read(&self) -> BlockchainReadHandle {
64        self.read.clone()
65    }
66
67    /// Returns the current [`BlockchainContext`].
68    pub fn context(&mut self) -> &BlockchainContext {
69        self.context_svc.blockchain_context()
70    }
71
72    /// Returns a handle to the blockchain manager.
73    pub fn manager(&self) -> BlockchainManagerHandle {
74        self.manager.clone()
75    }
76
77    /// Returns a handle to the blockchain syncer.
78    pub fn syncer(&self) -> BlockchainSyncerHandle {
79        self.syncer.clone()
80    }
81
82    pub fn pruning_seed(&self) -> PruningSeed {
83        self.read.blockchain.pruning_seed()
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) -> anyhow::Result<()> {
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        .call(BlockchainReadRequest::ChainHeight)
109        .await
110        .is_ok()
111    {
112        return Ok(());
113    }
114
115    let genesis = generate_genesis_block(network);
116
117    assert_eq!(genesis.miner_transaction().prefix().outputs.len(), 1);
118    assert_eq!(genesis.transactions.len(), 0);
119
120    blockchain_write_handle
121        .ready()
122        .await?
123        .call(BlockchainWriteRequest::WriteBlock(
124            VerifiedBlockInformation {
125                block_blob: genesis.serialize(),
126                txs: vec![],
127                block_hash: genesis.hash(),
128                pow_hash: cryptonight_hash_v0(&genesis.serialize_pow_hash()),
129                height: 0,
130                generated_coins: genesis.miner_transaction().prefix().outputs[0]
131                    .amount
132                    .unwrap(),
133                weight: genesis.miner_transaction().weight(),
134                long_term_weight: genesis.miner_transaction().weight(),
135                cumulative_difficulty: 1,
136                block: genesis,
137            },
138        ))
139        .await?;
140
141    Ok(())
142}
143
144/// Initializes the consensus services.
145pub async fn init_consensus(
146    blockchain_read_handle: BlockchainReadHandle,
147    context_config: ContextConfig,
148) -> Result<BlockchainContextService, BoxError> {
149    let read_handle = ConsensusBlockchainReadHandle::new(blockchain_read_handle, BoxError::from);
150
151    let ctx_service =
152        cuprate_consensus::initialize_blockchain_context(context_config, read_handle.clone())
153            .await?;
154
155    Ok(ctx_service)
156}