Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

20 changes: 10 additions & 10 deletions binaries/cuprated/src/blockchain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,18 @@ use cuprate_types::{
VerifiedBlockInformation,
};

use crate::constants::PANIC_CRITICAL_SERVICE_ERROR;

mod chain_service;
mod error;
mod fast_sync;
pub mod interface;
mod manager;
mod syncer;
mod types;

pub use error::IncomingBlockError;
pub use fast_sync::get_fast_sync_hashes;
pub use interface::BlockchainManagerHandle;
pub use manager::IncomingBlockOk;
pub use syncer::BlockchainSyncerHandle;
pub use types::ConsensusBlockchainReadHandle;

Expand Down Expand Up @@ -100,17 +101,16 @@ pub async fn check_add_genesis(
blockchain_read_handle: &mut BlockchainReadHandle,
blockchain_write_handle: &mut BlockchainWriteHandle,
network: Network,
) {
) -> anyhow::Result<()> {
// Try to get the chain height, will fail if the genesis block is not in the DB.
if blockchain_read_handle
.ready()
.await
.expect(PANIC_CRITICAL_SERVICE_ERROR)
.await?
.call(BlockchainReadRequest::ChainHeight)
.await
.is_ok()
{
return;
return Ok(());
}

let genesis = generate_genesis_block(network);
Expand All @@ -120,8 +120,7 @@ pub async fn check_add_genesis(

blockchain_write_handle
.ready()
.await
.expect(PANIC_CRITICAL_SERVICE_ERROR)
.await?
.call(BlockchainWriteRequest::WriteBlock(
VerifiedBlockInformation {
block_blob: genesis.serialize(),
Expand All @@ -138,8 +137,9 @@ pub async fn check_add_genesis(
block: genesis,
},
))
.await
.expect(PANIC_CRITICAL_SERVICE_ERROR);
.await?;

Ok(())
}

/// Initializes the consensus services.
Expand Down
66 changes: 66 additions & 0 deletions binaries/cuprated/src/blockchain/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
//! Error types for the blockchain manager interface.

use cuprate_blockchain::BlockchainError;
use cuprate_consensus::{block::BlockVerificationError, ExtendedConsensusError};
use cuprate_consensus_rules::ConsensusError;
use cuprate_txpool::TxPoolError;

use crate::monitor::FatalError;

/// An error returned from [`BlockchainManagerHandle::handle_incoming_block`](super::interface::BlockchainManagerHandle::handle_incoming_block).
#[derive(Debug, thiserror::Error)]
pub enum IncomingBlockError {
/// The peer sent us an invalid block.
#[error("Block verification failed: {inner}")]
Validation {
/// Whether the block's proof-of-work was verified before the failure.
pow_valid: bool,
/// The consensus rule that was broken.
inner: ConsensusError,
},

/// We cannot recover; shut the node down.
#[error(transparent)]
Fatal(#[from] FatalError),

/// We are missing the block's parent.
#[error("The block has an unknown parent.")]
Orphan,

/// Some transactions in the block were unknown.
///
/// The inner values are the block hash and the indexes of the missing txs in the block.
#[error("Unknown transactions in block.")]
UnknownTransactions([u8; 32], Vec<usize>),

/// The block claimed more transactions than it contained.
#[error("Too many transactions given for block.")]
TooManyTxs,

/// The blockchain manager command channel is closed.
#[error("The blockchain manager command channel is closed.")]
ChannelClosed,
}

impl From<BlockVerificationError> for IncomingBlockError {
fn from(error: BlockVerificationError) -> Self {
let BlockVerificationError { pow_valid, inner } = error;

match inner {
ExtendedConsensusError::FatalError(error) => Self::Fatal(error),
ExtendedConsensusError::ConsensusError(inner) => Self::Validation { pow_valid, inner },
}
}
}

impl From<BlockchainError> for IncomingBlockError {
fn from(e: BlockchainError) -> Self {
Self::Fatal(e.into())
}
}

impl From<TxPoolError> for IncomingBlockError {
fn from(v: TxPoolError) -> Self {
Self::Fatal(v.into())
}
}
55 changes: 12 additions & 43 deletions binaries/cuprated/src/blockchain/interface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,17 @@ use monero_oxide::{block::Block, transaction::Transaction};
use tokio::sync::{mpsc, oneshot};
use tower::{Service, ServiceExt};

use cuprate_blockchain::service::BlockchainReadHandle;
use cuprate_consensus::transactions::new_tx_verification_data;
use cuprate_blockchain::{service::BlockchainReadHandle, BlockchainError};
use cuprate_consensus::{block::BlockVerificationError, transactions::new_tx_verification_data};
use cuprate_txpool::service::{
interface::{TxpoolReadRequest, TxpoolReadResponse},
TxpoolReadHandle,
};
use cuprate_types::blockchain::{BlockchainReadRequest, BlockchainResponse};

use crate::{
blockchain::manager::{BlockchainManagerCommand, IncomingBlockOk},
constants::PANIC_CRITICAL_SERVICE_ERROR,
use crate::blockchain::{
manager::{BlockchainManagerCommand, IncomingBlockOk},
IncomingBlockError,
};

/// Handle for the blockchain manager.
Expand All @@ -42,25 +42,6 @@ pub struct BlockchainManagerHandle {
blocks_being_handled: Arc<Mutex<HashSet<[u8; 32]>>>,
}

/// An error that can be returned from [`BlockchainManagerHandle::handle_incoming_block`].
#[derive(Debug, thiserror::Error)]
pub enum IncomingBlockError {
/// Some transactions in the block were unknown.
///
/// The inner values are the block hash and the indexes of the missing txs in the block.
#[error("Unknown transactions in block.")]
UnknownTransactions([u8; 32], Vec<usize>),
/// We are missing the block's parent.
#[error("The block has an unknown parent.")]
Orphan,
/// The block was invalid.
#[error(transparent)]
InvalidBlock(anyhow::Error),
/// The blockchain manager command channel is closed.
#[error("The blockchain manager command channel is closed.")]
ChannelClosed,
}

impl BlockchainManagerHandle {
/// Create a new handle and command receiver pair.
pub(crate) fn new() -> (Self, mpsc::Receiver<BlockchainManagerCommand>) {
Expand Down Expand Up @@ -98,34 +79,24 @@ impl BlockchainManagerHandle {
txpool_read_handle: &mut TxpoolReadHandle,
) -> Result<IncomingBlockOk, IncomingBlockError> {
if given_txs.len() > block.transactions.len() {
return Err(IncomingBlockError::InvalidBlock(anyhow::anyhow!(
"Too many transactions given for block"
)));
return Err(IncomingBlockError::TooManyTxs);
}

if !block_exists(block.header.previous, blockchain_read_handle)
.await
.expect(PANIC_CRITICAL_SERVICE_ERROR)
{
if !block_exists(block.header.previous, blockchain_read_handle).await? {
return Err(IncomingBlockError::Orphan);
}

let block_hash = block.hash();

if block_exists(block_hash, blockchain_read_handle)
.await
.expect(PANIC_CRITICAL_SERVICE_ERROR)
{
if block_exists(block_hash, blockchain_read_handle).await? {
return Ok(IncomingBlockOk::AlreadyHave);
}

let TxpoolReadResponse::TxsForBlock { mut txs, missing } = txpool_read_handle
.ready()
.await
.expect(PANIC_CRITICAL_SERVICE_ERROR)
.await?
.call(TxpoolReadRequest::TxsForBlock(block.transactions.clone()))
.await
.expect(PANIC_CRITICAL_SERVICE_ERROR)
.await?
else {
unreachable!()
};
Expand All @@ -144,8 +115,7 @@ impl BlockchainManagerHandle {

txs.insert(
needed_hash,
new_tx_verification_data(tx)
.map_err(|e| IncomingBlockError::InvalidBlock(e.into()))?,
new_tx_verification_data(tx).map_err(BlockVerificationError::invalid_pow)?,
);
}
}
Expand Down Expand Up @@ -185,7 +155,6 @@ impl BlockchainManagerHandle {
response_rx
.await
.map_err(|_| IncomingBlockError::ChannelClosed)?
.map_err(IncomingBlockError::InvalidBlock)
}

/// Pop blocks from the top of the blockchain.
Expand All @@ -211,7 +180,7 @@ impl BlockchainManagerHandle {
async fn block_exists(
block_hash: [u8; 32],
blockchain_read_handle: &mut BlockchainReadHandle,
) -> Result<bool, anyhow::Error> {
) -> Result<bool, BlockchainError> {
let BlockchainResponse::FindBlock(chain) = blockchain_read_handle
.ready()
.await?
Expand Down
43 changes: 21 additions & 22 deletions binaries/cuprated/src/blockchain/manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ use tracing::error;
use cuprate_blockchain::service::{BlockchainReadHandle, BlockchainWriteHandle};
use cuprate_consensus::{
BlockChainContextRequest, BlockChainContextResponse, BlockchainContextService,
ExtendedConsensusError,
};
use cuprate_p2p::{
block_downloader::{self, BlockBatch},
Expand All @@ -27,7 +26,7 @@ use crate::{
blockchain::{
chain_service::ChainService, syncer::BlockchainSyncer, types::ConsensusBlockchainReadHandle,
},
constants::PANIC_CRITICAL_SERVICE_ERROR,
monitor::FatalError,
txpool::TxpoolManagerHandle,
LaunchContext,
};
Expand Down Expand Up @@ -66,15 +65,18 @@ pub(crate) async fn init_blockchain_manager(
launch_ctx.config.offline,
);

launch_ctx.task_executor.spawn(syncer.run(
launch_ctx.blockchain.context_svc(),
ChainService(launch_ctx.blockchain.read(), fast_sync_hashes),
clearnet_interface.clone(),
batch_tx,
Arc::clone(&stop_current_block_downloader),
block_downloader_config,
shutdown_token.clone(),
));
launch_ctx.task_executor.spawn_critical(
"blockchain syncer",
syncer.run(
launch_ctx.blockchain.context_svc(),
ChainService(launch_ctx.blockchain.read(), fast_sync_hashes),
clearnet_interface.clone(),
batch_tx,
Arc::clone(&stop_current_block_downloader),
block_downloader_config,
shutdown_token.clone(),
),
);

let manager = BlockchainManager {
blockchain_write_handle,
Expand All @@ -90,9 +92,10 @@ pub(crate) async fn init_blockchain_manager(
fast_sync_hashes,
};

launch_ctx
.task_executor
.spawn(manager.run(batch_rx, command_rx, shutdown_token));
launch_ctx.task_executor.spawn_critical(
"blockchain manager",
manager.run(batch_rx, command_rx, shutdown_token),
);

Ok(())
}
Expand Down Expand Up @@ -132,29 +135,25 @@ impl BlockchainManager {
mut block_batch_rx: mpsc::Receiver<(BlockBatch, Arc<OwnedSemaphorePermit>)>,
mut command_rx: mpsc::Receiver<BlockchainManagerCommand>,
shutdown_token: CancellationToken,
) {
) -> Result<(), FatalError> {
loop {
tokio::select! {
biased;
() = shutdown_token.cancelled() => {
break;
}
Some((batch, permit)) = block_batch_rx.recv() => {
self.handle_incoming_block_batch(
batch,
).await;
self.handle_incoming_block_batch(batch).await?;

drop(permit);
}
Some(incoming_command) = command_rx.recv() => {
self.handle_command(incoming_command).await;
}
else => {
break;
self.handle_command(incoming_command).await?;
}
}
}

tracing::info!("Blockchain manager shut down.");
Ok(())
}
}
4 changes: 3 additions & 1 deletion binaries/cuprated/src/blockchain/manager/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ use tokio::sync::oneshot;

use cuprate_types::TransactionVerificationData;

use crate::blockchain::IncomingBlockError;

/// The blockchain manager commands.
#[expect(clippy::large_enum_variant)]
pub enum BlockchainManagerCommand {
Expand All @@ -16,7 +18,7 @@ pub enum BlockchainManagerCommand {
/// All the transactions defined in [`Block::transactions`].
prepped_txs: HashMap<[u8; 32], TransactionVerificationData>,
/// The channel to send the response down.
response_tx: oneshot::Sender<Result<IncomingBlockOk, anyhow::Error>>,
response_tx: oneshot::Sender<Result<IncomingBlockOk, IncomingBlockError>>,
},
/// Pop blocks from the top of the blockchain.
PopBlocks {
Expand Down
Loading
Loading