cuprate/consensus/src/block.rs

231 lines
6.6 KiB
Rust
Raw Normal View History

2023-10-23 18:14:40 +00:00
use std::{
future::Future,
pin::Pin,
sync::Arc,
task::{Context, Poll},
};
2023-10-23 18:14:40 +00:00
use futures::FutureExt;
2023-11-11 01:55:15 +00:00
use monero_serai::{block::Block, transaction::Input};
2023-10-23 18:14:40 +00:00
use tower::{Service, ServiceExt};
use monero_consensus::{
blocks::{calculate_pow_hash, check_block, check_block_pow},
ConsensusError, HardFork,
};
2023-10-23 18:14:40 +00:00
use crate::{
context::{BlockChainContextRequest, BlockChainContextResponse},
2023-11-11 01:55:15 +00:00
helper::rayon_spawn_async,
2023-10-23 18:14:40 +00:00
transactions::{TransactionVerificationData, VerifyTxRequest, VerifyTxResponse},
ExtendedConsensusError, TxNotInPool, TxPoolRequest, TxPoolResponse,
2023-10-23 18:14:40 +00:00
};
2023-10-05 16:54:19 +00:00
2023-11-11 01:55:15 +00:00
#[derive(Debug)]
pub struct PrePreparedBlock {
pub block: Block,
pub block_blob: Vec<u8>,
pub hf_vote: HardFork,
pub hf_version: HardFork,
pub block_hash: [u8; 32],
pub pow_hash: [u8; 32],
pub miner_tx_weight: usize,
}
2023-10-23 18:14:40 +00:00
#[derive(Debug)]
pub struct VerifiedBlockInformation {
pub block: Block,
pub hf_vote: HardFork,
pub txs: Vec<Arc<TransactionVerificationData>>,
pub block_hash: [u8; 32],
pub pow_hash: [u8; 32],
pub height: u64,
pub generated_coins: u64,
pub weight: usize,
pub long_term_weight: usize,
2023-10-24 01:25:11 +00:00
pub cumulative_difficulty: u128,
2023-10-23 18:14:40 +00:00
}
2023-10-23 18:14:40 +00:00
pub enum VerifyBlockRequest {
MainChain(Block),
2023-11-11 01:55:15 +00:00
}
pub enum VerifyBlockResponse {
MainChain(VerifiedBlockInformation),
BatchSetup(Vec<PrePreparedBlock>),
}
2023-10-23 18:14:40 +00:00
// TODO: it is probably a bad idea for this to derive clone, if 2 places (RPC, P2P) receive valid but different blocks
// then they will both get approved but only one should go to main chain.
#[derive(Clone)]
pub struct BlockVerifierService<C: Clone, TxV: Clone, TxP: Clone> {
2023-10-23 18:14:40 +00:00
context_svc: C,
tx_verifier_svc: TxV,
tx_pool: TxP,
}
impl<C, TxV, TxP> BlockVerifierService<C, TxV, TxP>
2023-10-23 18:14:40 +00:00
where
C: Service<BlockChainContextRequest, Response = BlockChainContextResponse>
+ Clone
+ Send
+ 'static,
TxV: Service<VerifyTxRequest, Response = VerifyTxResponse, Error = ExtendedConsensusError>
+ Clone
+ Send
+ 'static,
TxP: Service<TxPoolRequest, Response = TxPoolResponse, Error = TxNotInPool>
2023-10-23 18:14:40 +00:00
+ Clone
+ Send
+ 'static,
{
pub fn new(
context_svc: C,
tx_verifier_svc: TxV,
tx_pool: TxP,
) -> BlockVerifierService<C, TxV, TxP> {
2023-10-23 18:14:40 +00:00
BlockVerifierService {
context_svc,
tx_verifier_svc,
tx_pool,
2023-10-23 18:14:40 +00:00
}
}
}
impl<C, TxV, TxP> Service<VerifyBlockRequest> for BlockVerifierService<C, TxV, TxP>
2023-10-23 18:14:40 +00:00
where
C: Service<
BlockChainContextRequest,
Response = BlockChainContextResponse,
Error = tower::BoxError,
> + Clone
2023-10-23 18:14:40 +00:00
+ Send
+ 'static,
C::Future: Send + 'static,
TxV: Service<VerifyTxRequest, Response = VerifyTxResponse, Error = ExtendedConsensusError>
2023-10-23 18:14:40 +00:00
+ Clone
+ Send
+ 'static,
TxV::Future: Send + 'static,
TxP: Service<TxPoolRequest, Response = TxPoolResponse, Error = TxNotInPool>
+ Clone
+ Send
+ 'static,
TxP::Future: Send + 'static,
2023-10-23 18:14:40 +00:00
{
2023-11-11 01:55:15 +00:00
type Response = VerifyBlockResponse;
type Error = ExtendedConsensusError;
2023-10-23 18:14:40 +00:00
type Future =
Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send + 'static>>;
fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
Poll::Ready(Ok(()))
2023-10-23 18:14:40 +00:00
}
fn call(&mut self, req: VerifyBlockRequest) -> Self::Future {
let context_svc = self.context_svc.clone();
let tx_verifier_svc = self.tx_verifier_svc.clone();
let tx_pool = self.tx_pool.clone();
2023-10-23 18:14:40 +00:00
async move {
match req {
VerifyBlockRequest::MainChain(block) => {
verify_main_chain_block(block, context_svc, tx_verifier_svc, tx_pool).await
2023-10-26 02:16:03 +00:00
}
2023-10-23 18:14:40 +00:00
}
}
.boxed()
}
}
async fn verify_main_chain_block<C, TxV, TxP>(
2023-10-26 02:16:03 +00:00
block: Block,
context_svc: C,
tx_verifier_svc: TxV,
tx_pool: TxP,
) -> Result<VerifyBlockResponse, ExtendedConsensusError>
2023-10-26 02:16:03 +00:00
where
C: Service<
BlockChainContextRequest,
Response = BlockChainContextResponse,
Error = tower::BoxError,
> + Send
2023-10-26 02:16:03 +00:00
+ 'static,
C::Future: Send + 'static,
TxV: Service<VerifyTxRequest, Response = VerifyTxResponse, Error = ExtendedConsensusError>,
TxP: Service<TxPoolRequest, Response = TxPoolResponse, Error = TxNotInPool>
+ Clone
+ Send
+ 'static,
2023-10-26 02:16:03 +00:00
{
tracing::debug!("getting blockchain context");
let BlockChainContextResponse::Context(checked_context) = context_svc
.oneshot(BlockChainContextRequest::Get)
2023-10-26 02:16:03 +00:00
.await
.map_err(Into::<ExtendedConsensusError>::into)?
else {
panic!("Context service returned wrong response!");
};
2023-10-26 02:16:03 +00:00
2023-11-11 01:55:15 +00:00
let context = checked_context.unchecked_blockchain_context().clone();
2023-10-26 02:16:03 +00:00
tracing::debug!("got blockchain context: {:?}", context);
let TxPoolResponse::Transactions(txs) = tx_pool
.oneshot(TxPoolRequest::Transactions(block.txs.clone()))
.await?;
2023-10-26 02:16:03 +00:00
tx_verifier_svc
.oneshot(VerifyTxRequest::Block {
txs: txs.clone(),
current_chain_height: context.chain_height,
time_for_time_lock: context.current_adjusted_timestamp_for_time_lock(),
hf: context.current_hf,
re_org_token: context.re_org_token.clone(),
2023-10-26 02:16:03 +00:00
})
.await?;
let block_weight = block.miner_tx.weight() + txs.iter().map(|tx| tx.tx_weight).sum::<usize>();
let total_fees = txs.iter().map(|tx| tx.fee).sum::<u64>();
let (hf_vote, generated_coins) = check_block(
&block,
2023-10-26 02:16:03 +00:00
total_fees,
block_weight,
block.serialize().len(),
&context.context_to_verify_block,
)
.map_err(ConsensusError::Block)?;
2023-10-26 02:16:03 +00:00
let hashing_blob = block.serialize_hashable();
// do POW test last
let chain_height = context.chain_height;
let current_hf = context.current_hf;
let pow_hash =
rayon_spawn_async(move || calculate_pow_hash(&hashing_blob, chain_height, &current_hf))
.await
.map_err(ConsensusError::Block)?;
2023-10-23 18:14:40 +00:00
check_block_pow(&pow_hash, context.next_difficulty).map_err(ConsensusError::Block)?;
2023-11-11 01:55:15 +00:00
Ok(VerifyBlockResponse::MainChain(VerifiedBlockInformation {
2023-10-23 18:14:40 +00:00
block_hash: block.hash(),
block,
txs,
pow_hash,
generated_coins,
weight: block_weight,
height: context.chain_height,
2023-10-24 01:25:11 +00:00
long_term_weight: context.next_block_long_term_weight(block_weight),
hf_vote,
2023-10-24 01:25:11 +00:00
cumulative_difficulty: context.cumulative_difficulty + context.next_difficulty,
2023-11-11 01:55:15 +00:00
}))
}