diff --git a/consensus/core/src/api/mod.rs b/consensus/core/src/api/mod.rs index c11ea8317..880a27172 100644 --- a/consensus/core/src/api/mod.rs +++ b/consensus/core/src/api/mod.rs @@ -4,7 +4,7 @@ use std::sync::Arc; use crate::{ acceptance_data::AcceptanceData, - block::{Block, BlockTemplate, TemplateTransactionSelector}, + block::{Block, BlockTemplate, TemplateBuildMode, TemplateTransactionSelector}, block_count::BlockCount, blockstatus::BlockStatus, coinbase::MinerData, @@ -31,6 +31,7 @@ pub trait ConsensusApi: Send + Sync { &self, miner_data: MinerData, tx_selector: Box, + build_mode: TemplateBuildMode, ) -> Result { unimplemented!() } diff --git a/consensus/core/src/block.rs b/consensus/core/src/block.rs index 74aa42186..77a363196 100644 --- a/consensus/core/src/block.rs +++ b/consensus/core/src/block.rs @@ -84,6 +84,18 @@ pub trait TemplateTransactionSelector { fn is_successful(&self) -> bool; } +/// Block template build mode +#[derive(Clone, Copy, Debug)] +pub enum TemplateBuildMode { + /// Block template build can possibly fail if `TemplateTransactionSelector::is_successful` deems the operation unsuccessful. + /// + /// In such a case, the build fails with `BlockRuleError::InvalidTransactionsInNewBlock`. + Standard, + + /// Block template build always succeeds. The built block contains only the validated transactions. + Infallible, +} + /// A block template for miners. #[derive(Debug, Clone)] pub struct BlockTemplate { diff --git a/consensus/src/consensus/mod.rs b/consensus/src/consensus/mod.rs index 0071ea07f..dd82c09d6 100644 --- a/consensus/src/consensus/mod.rs +++ b/consensus/src/consensus/mod.rs @@ -41,7 +41,7 @@ use crate::{ use kaspa_consensus_core::{ acceptance_data::AcceptanceData, api::{BlockValidationFuture, ConsensusApi}, - block::{Block, BlockTemplate, TemplateTransactionSelector}, + block::{Block, BlockTemplate, TemplateBuildMode, TemplateTransactionSelector}, block_count::BlockCount, blockhash::BlockHashExtensions, blockstatus::BlockStatus, @@ -357,8 +357,9 @@ impl ConsensusApi for Consensus { &self, miner_data: MinerData, tx_selector: Box, + build_mode: TemplateBuildMode, ) -> Result { - self.virtual_processor.build_block_template(miner_data, tx_selector) + self.virtual_processor.build_block_template(miner_data, tx_selector, build_mode) } fn validate_and_insert_block(&self, block: Block) -> BlockValidationFuture { diff --git a/consensus/src/pipeline/virtual_processor/processor.rs b/consensus/src/pipeline/virtual_processor/processor.rs index 2bde3b4c4..47d0cf679 100644 --- a/consensus/src/pipeline/virtual_processor/processor.rs +++ b/consensus/src/pipeline/virtual_processor/processor.rs @@ -48,7 +48,7 @@ use crate::{ }; use kaspa_consensus_core::{ acceptance_data::AcceptanceData, - block::{BlockTemplate, MutableBlock, TemplateTransactionSelector}, + block::{BlockTemplate, MutableBlock, TemplateBuildMode, TemplateTransactionSelector}, blockstatus::BlockStatus::{StatusDisqualifiedFromChain, StatusUTXOValid}, coinbase::MinerData, config::genesis::GenesisBlock, @@ -791,6 +791,7 @@ impl VirtualStateProcessor { &self, miner_data: MinerData, mut tx_selector: Box, + build_mode: TemplateBuildMode, ) -> Result { // // TODO: tests @@ -835,8 +836,9 @@ impl VirtualStateProcessor { // Check whether this was an overall successful selection episode. We pass this decision // to the selector implementation which has the broadest picture and can use mempool config // and context - if !tx_selector.is_successful() { - return Err(RuleError::InvalidTransactionsInNewBlock(invalid_transactions)); + match (build_mode, tx_selector.is_successful()) { + (TemplateBuildMode::Standard, false) => return Err(RuleError::InvalidTransactionsInNewBlock(invalid_transactions)), + (TemplateBuildMode::Standard, true) | (TemplateBuildMode::Infallible, _) => {} } // At this point we can safely drop the read lock diff --git a/consensus/src/pipeline/virtual_processor/tests.rs b/consensus/src/pipeline/virtual_processor/tests.rs index e0b43b1b8..fc6cb73da 100644 --- a/consensus/src/pipeline/virtual_processor/tests.rs +++ b/consensus/src/pipeline/virtual_processor/tests.rs @@ -1,7 +1,7 @@ use crate::{consensus::test_consensus::TestConsensus, model::services::reachability::ReachabilityService}; use kaspa_consensus_core::{ api::ConsensusApi, - block::{Block, BlockTemplate, MutableBlock, TemplateTransactionSelector}, + block::{Block, BlockTemplate, MutableBlock, TemplateBuildMode, TemplateTransactionSelector}, blockhash, blockstatus::BlockStatus, coinbase::MinerData, @@ -104,7 +104,11 @@ impl TestContext { pub fn build_block_template(&self, nonce: u64, timestamp: u64) -> BlockTemplate { let mut t = self .consensus - .build_block_template(self.miner_data.clone(), Box::new(OnetimeTxSelector::new(Default::default()))) + .build_block_template( + self.miner_data.clone(), + Box::new(OnetimeTxSelector::new(Default::default())), + TemplateBuildMode::Standard, + ) .unwrap(); t.block.header.timestamp = timestamp; t.block.header.nonce = nonce; diff --git a/mining/src/block_template/builder.rs b/mining/src/block_template/builder.rs index a3468e8e2..de3428a74 100644 --- a/mining/src/block_template/builder.rs +++ b/mining/src/block_template/builder.rs @@ -1,7 +1,11 @@ use super::{errors::BuilderResult, policy::Policy}; use crate::{block_template::selector::TransactionsSelector, model::candidate_tx::CandidateTransaction}; use kaspa_consensus_core::{ - api::ConsensusApi, block::BlockTemplate, coinbase::MinerData, merkle::calc_hash_merkle_root, tx::COINBASE_TRANSACTION_INDEX, + api::ConsensusApi, + block::{BlockTemplate, TemplateBuildMode}, + coinbase::MinerData, + merkle::calc_hash_merkle_root, + tx::COINBASE_TRANSACTION_INDEX, }; use kaspa_core::{ debug, @@ -86,11 +90,12 @@ impl BlockTemplateBuilder { consensus: &dyn ConsensusApi, miner_data: &MinerData, transactions: Vec, + build_mode: TemplateBuildMode, ) -> BuilderResult { let _sw = Stopwatch::<20>::with_threshold("build_block_template op"); debug!("Considering {} transactions for a new block template", transactions.len()); let selector = Box::new(TransactionsSelector::new(self.policy.clone(), transactions)); - Ok(consensus.build_block_template(miner_data.clone(), selector)?) + Ok(consensus.build_block_template(miner_data.clone(), selector, build_mode)?) } /// modify_block_template clones an existing block template, modifies it to the requested coinbase data and updates the timestamp diff --git a/mining/src/block_template/selector.rs b/mining/src/block_template/selector.rs index 369d3450d..9b34d964c 100644 --- a/mining/src/block_template/selector.rs +++ b/mining/src/block_template/selector.rs @@ -250,7 +250,7 @@ impl TemplateTransactionSelector for TransactionsSelector { fn is_successful(&self) -> bool { // TODO: comment + constants - self.transactions.is_empty() + self.overall_rejections == 0 || (self.total_mass as f64) > self.policy.max_block_mass as f64 * 0.8 || (self.overall_rejections as f64) < self.transactions.len() as f64 * 0.2 } diff --git a/mining/src/manager.rs b/mining/src/manager.rs index f7fe03a77..9a081bff0 100644 --- a/mining/src/manager.rs +++ b/mining/src/manager.rs @@ -21,7 +21,7 @@ use crate::{ use itertools::Itertools; use kaspa_consensus_core::{ api::ConsensusApi, - block::BlockTemplate, + block::{BlockTemplate, TemplateBuildMode}, coinbase::MinerData, errors::{block::RuleError as BlockRuleError, tx::TxRuleError}, tx::{MutableTransaction, Transaction, TransactionId, TransactionOutput}, @@ -90,7 +90,12 @@ impl MiningManager { let transactions = self.block_candidate_transactions(); let block_template_builder = BlockTemplateBuilder::new(self.config.maximum_mass_per_block); - match block_template_builder.build_block_template(consensus, miner_data, transactions) { + let build_mode = if attempts < self.config.maximum_build_block_template_attempts { + TemplateBuildMode::Standard + } else { + TemplateBuildMode::Infallible + }; + match block_template_builder.build_block_template(consensus, miner_data, transactions, build_mode) { Ok(block_template) => { let block_template = cache_lock.set_immutable_cached_template(block_template); match attempts { diff --git a/mining/src/manager_tests.rs b/mining/src/manager_tests.rs index ddcc05be1..776ef017f 100644 --- a/mining/src/manager_tests.rs +++ b/mining/src/manager_tests.rs @@ -16,6 +16,7 @@ mod tests { use kaspa_addresses::{Address, Prefix, Version}; use kaspa_consensus_core::{ api::ConsensusApi, + block::TemplateBuildMode, coinbase::MinerData, constants::{MAX_TX_IN_SEQUENCE_NUM, SOMPI_PER_KASPA, TX_VERSION}, errors::tx::{TxResult, TxRuleError}, @@ -847,7 +848,7 @@ mod tests { // Build a fresh template for coinbase2 as a reference let builder = mining_manager.block_template_builder(); - let result = builder.build_block_template(consensus, &miner_data_2, transactions); + let result = builder.build_block_template(consensus, &miner_data_2, transactions, TemplateBuildMode::Standard); assert!(result.is_ok(), "build block template failed for miner data 2"); let expected_template = result.unwrap(); diff --git a/mining/src/testutils/consensus_mock.rs b/mining/src/testutils/consensus_mock.rs index c1f5cda4c..ecf5319e0 100644 --- a/mining/src/testutils/consensus_mock.rs +++ b/mining/src/testutils/consensus_mock.rs @@ -1,7 +1,7 @@ use super::coinbase_mock::CoinbaseManagerMock; use kaspa_consensus_core::{ api::ConsensusApi, - block::{BlockTemplate, MutableBlock, TemplateTransactionSelector}, + block::{BlockTemplate, MutableBlock, TemplateBuildMode, TemplateTransactionSelector}, coinbase::MinerData, constants::BLOCK_VERSION, errors::{ @@ -76,6 +76,7 @@ impl ConsensusApi for ConsensusMock { &self, miner_data: MinerData, mut tx_selector: Box, + _build_mode: TemplateBuildMode, ) -> Result { let mut txs = tx_selector.select_transactions(); let coinbase_manager = CoinbaseManagerMock::new(); diff --git a/simpa/src/simulator/miner.rs b/simpa/src/simulator/miner.rs index 2cdf1af4b..2f144fc66 100644 --- a/simpa/src/simulator/miner.rs +++ b/simpa/src/simulator/miner.rs @@ -4,7 +4,7 @@ use kaspa_consensus::consensus::Consensus; use kaspa_consensus::model::stores::virtual_state::VirtualStateStoreReader; use kaspa_consensus::params::Params; use kaspa_consensus_core::api::ConsensusApi; -use kaspa_consensus_core::block::{Block, TemplateTransactionSelector}; +use kaspa_consensus_core::block::{Block, TemplateBuildMode, TemplateTransactionSelector}; use kaspa_consensus_core::coinbase::MinerData; use kaspa_consensus_core::sign::sign; use kaspa_consensus_core::subnets::SUBNETWORK_ID_NATIVE; @@ -113,7 +113,7 @@ impl Miner { let session = self.consensus.acquire_session(); let mut block_template = self .consensus - .build_block_template(self.miner_data.clone(), Box::new(OnetimeTxSelector::new(txs))) + .build_block_template(self.miner_data.clone(), Box::new(OnetimeTxSelector::new(txs)), TemplateBuildMode::Standard) .expect("simulation txs are selected in sync with virtual state and are expected to be valid"); drop(session); block_template.block.header.timestamp = timestamp; // Use simulation time rather than real time