#![allow(missing_docs)]
use crate::orphan_broker::OrphanBroker;
use crate::{LonelyBlock, ProcessBlockRequest};
use ckb_channel::{Receiver, Request, select};
use ckb_error::{Error, InternalErrorKind};
use ckb_logger::{self, debug, error, info, warn};
use ckb_shared::block_status::BlockStatus;
use ckb_shared::shared::Shared;
use ckb_stop_handler::new_crossbeam_exit_rx;
use ckb_types::core::BlockView;
use ckb_verification::{BlockVerifier, NonContextualBlockTxsVerifier};
use ckb_verification_traits::Verifier;
pub(crate) struct ChainService {
shared: Shared,
process_block_rx: Receiver<ProcessBlockRequest>,
orphan_broker: OrphanBroker,
}
impl ChainService {
pub(crate) fn new(
shared: Shared,
process_block_rx: Receiver<ProcessBlockRequest>,
consume_orphan: OrphanBroker,
) -> ChainService {
ChainService {
shared,
process_block_rx,
orphan_broker: consume_orphan,
}
}
pub(crate) fn start_process_block(self) {
let signal_receiver = new_crossbeam_exit_rx();
let clean_expired_orphan_timer =
crossbeam::channel::tick(std::time::Duration::from_secs(60));
loop {
select! {
recv(self.process_block_rx) -> msg => match msg {
Ok(Request { responder, arguments: lonely_block }) => {
let _trace_now = minstant::Instant::now();
self.asynchronous_process_block(lonely_block);
if let Some(handle) = ckb_metrics::handle(){
handle.ckb_chain_async_process_block_duration.observe(_trace_now.elapsed().as_secs_f64())
}
let _ = responder.send(());
},
_ => {
error!("process_block_receiver closed");
break;
},
},
recv(clean_expired_orphan_timer) -> _ => {
self.orphan_broker.clean_expired_orphans();
},
recv(signal_receiver) -> _ => {
info!("ChainService received exit signal, exit now");
break;
}
}
}
}
fn non_contextual_verify(&self, block: &BlockView) -> Result<(), Error> {
let consensus = self.shared.consensus();
BlockVerifier::new(consensus).verify(block).map_err(|e| {
debug!("[process_block] BlockVerifier error {:?}", e);
e
})?;
NonContextualBlockTxsVerifier::new(consensus)
.verify(block)
.map_err(|e| {
debug!(
"[process_block] NonContextualBlockTxsVerifier error {:?}",
e
);
e
})
.map(|_| ())
}
fn asynchronous_process_block(&self, lonely_block: LonelyBlock) {
let block_number = lonely_block.block().number();
let block_hash = lonely_block.block().hash();
if block_number < 1 {
if self.shared.genesis_hash() != block_hash {
warn!(
"receive 0 number block: 0-{}, expect genesis hash: {}",
block_hash,
self.shared.genesis_hash()
);
self.shared
.insert_block_status(lonely_block.block().hash(), BlockStatus::BLOCK_INVALID);
let error = InternalErrorKind::System
.other("Invalid genesis block received")
.into();
lonely_block.execute_callback(Err(error));
} else {
warn!("receive 0 number block: 0-{}", block_hash);
lonely_block.execute_callback(Ok(false));
}
return;
}
if lonely_block.switch().is_none()
|| matches!(lonely_block.switch(), Some(switch) if !switch.disable_non_contextual())
{
let result = self.non_contextual_verify(lonely_block.block());
if let Err(err) = result {
error!(
"block {}-{} verify failed: {:?}",
block_number, block_hash, err
);
self.shared
.insert_block_status(lonely_block.block().hash(), BlockStatus::BLOCK_INVALID);
lonely_block.execute_callback(Err(err));
return;
}
}
if let Err(err) = self.insert_block(&lonely_block) {
error!(
"insert block {}-{} failed: {:?}",
block_number, block_hash, err
);
self.shared.block_status_map().remove(&block_hash);
lonely_block.execute_callback(Err(err));
return;
}
self.orphan_broker.process_lonely_block(lonely_block.into());
}
fn insert_block(&self, lonely_block: &LonelyBlock) -> Result<(), ckb_error::Error> {
let db_txn = self.shared.store().begin_transaction();
db_txn.insert_block(lonely_block.block())?;
db_txn.commit()?;
Ok(())
}
}