Skip to content

Commit

Permalink
Add an infallible mode to virtual processor build_block_template()
Browse files Browse the repository at this point in the history
  • Loading branch information
tiram88 committed Sep 20, 2023
1 parent 14da8a2 commit e7b6551
Show file tree
Hide file tree
Showing 10 changed files with 64 additions and 26 deletions.
4 changes: 2 additions & 2 deletions consensus/core/src/api/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use std::sync::Arc;

use crate::{
acceptance_data::AcceptanceData,
block::{Block, BlockTemplate},
block::{Block, BlockTemplate, BuildMode},
block_count::BlockCount,
blockstatus::BlockStatus,
coinbase::MinerData,
Expand All @@ -27,7 +27,7 @@ pub type BlockValidationFuture = BoxFuture<'static, BlockProcessResult<BlockStat
/// Abstracts the consensus external API
#[allow(unused_variables)]
pub trait ConsensusApi: Send + Sync {
fn build_block_template(&self, miner_data: MinerData, txs: Vec<Transaction>) -> Result<BlockTemplate, RuleError> {
fn build_block_template(&self, miner_data: MinerData, txs: Vec<Transaction>, mode: BuildMode) -> Result<BlockTemplate, RuleError> {
unimplemented!()
}

Expand Down
12 changes: 12 additions & 0 deletions consensus/core/src/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,3 +85,15 @@ impl BlockTemplate {
Self { block, miner_data, coinbase_has_red_reward, selected_parent_timestamp, selected_parent_daa_score }
}
}

/// Block template build mode
#[derive(Clone, Copy, Debug)]
pub enum BuildMode {
/// Block template build succeeds only if all the transactions are successfully validated.
///
/// When invalid transactions are found, the build fails with `BlockRuleError::InvalidTransactionsInNewBlock`.
Standard,

/// Block template build always succeeds. The built block contains only the validated transactions.
Infallible,
}
6 changes: 3 additions & 3 deletions consensus/src/consensus/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ use crate::{
use kaspa_consensus_core::{
acceptance_data::AcceptanceData,
api::{BlockValidationFuture, ConsensusApi},
block::{Block, BlockTemplate},
block::{Block, BlockTemplate, BuildMode},
block_count::BlockCount,
blockhash::BlockHashExtensions,
blockstatus::BlockStatus,
Expand Down Expand Up @@ -353,8 +353,8 @@ impl Consensus {
}

impl ConsensusApi for Consensus {
fn build_block_template(&self, miner_data: MinerData, txs: Vec<Transaction>) -> Result<BlockTemplate, RuleError> {
self.virtual_processor.build_block_template(miner_data, txs)
fn build_block_template(&self, miner_data: MinerData, txs: Vec<Transaction>, mode: BuildMode) -> Result<BlockTemplate, RuleError> {
self.virtual_processor.build_block_template(miner_data, txs, mode)
}

fn validate_and_insert_block(&self, block: Block) -> BlockValidationFuture {
Expand Down
31 changes: 27 additions & 4 deletions consensus/src/pipeline/virtual_processor/processor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ use crate::{
};
use kaspa_consensus_core::{
acceptance_data::AcceptanceData,
block::{BlockTemplate, MutableBlock},
block::{BlockTemplate, BuildMode, MutableBlock},
blockstatus::BlockStatus::{StatusDisqualifiedFromChain, StatusUTXOValid},
coinbase::MinerData,
config::genesis::GenesisBlock,
Expand Down Expand Up @@ -787,14 +787,28 @@ impl VirtualStateProcessor {
Ok(())
}

pub fn build_block_template(&self, miner_data: MinerData, txs: Vec<Transaction>) -> Result<BlockTemplate, RuleError> {
pub fn build_block_template(
&self,
miner_data: MinerData,
mut txs: Vec<Transaction>,
mode: BuildMode,
) -> Result<BlockTemplate, RuleError> {
// TODO: tests
let virtual_read = self.virtual_stores.read();
let virtual_state = virtual_read.state.get().unwrap();
let virtual_utxo_view = &virtual_read.utxo_set;

// Validate the transactions in virtual's utxo context
self.validate_block_template_transactions(&txs, &virtual_state, virtual_utxo_view)?;
match mode {
BuildMode::Standard => {
// Validate the transactions in virtual's utxo context
self.validate_block_template_transactions(&txs, &virtual_state, virtual_utxo_view)?;
}

BuildMode::Infallible => {
// Discard invalid transactions in virtual's utxo context
txs = self.get_valid_block_template_transactions(txs, &virtual_state, virtual_utxo_view);
}
}

// At this point we can safely drop the read lock
drop(virtual_read);
Expand Down Expand Up @@ -824,6 +838,15 @@ impl VirtualStateProcessor {
}
}

fn get_valid_block_template_transactions(
&self,
txs: Vec<Transaction>,
virtual_state: &VirtualState,
utxo_view: &impl UtxoView,
) -> Vec<Transaction> {
txs.into_iter().filter(|tx| self.validate_block_template_transaction(tx, virtual_state, utxo_view).is_ok()).collect_vec()
}

pub(crate) fn build_block_template_from_virtual_state(
&self,
virtual_state: Arc<VirtualState>,
Expand Down
4 changes: 2 additions & 2 deletions consensus/src/pipeline/virtual_processor/tests.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::{consensus::test_consensus::TestConsensus, model::services::reachability::ReachabilityService};
use kaspa_consensus_core::{
api::ConsensusApi,
block::{Block, BlockTemplate, MutableBlock},
block::{Block, BlockTemplate, BuildMode, MutableBlock},
blockhash,
blockstatus::BlockStatus,
coinbase::MinerData,
Expand Down Expand Up @@ -78,7 +78,7 @@ 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(), Default::default()).unwrap();
let mut t = self.consensus.build_block_template(self.miner_data.clone(), Default::default(), BuildMode::Standard).unwrap();
t.block.header.timestamp = timestamp;
t.block.header.nonce = nonce;
t.block.header.finalize();
Expand Down
5 changes: 3 additions & 2 deletions mining/src/block_template/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use super::{errors::BuilderResult, policy::Policy};
use crate::{block_template::selector::TransactionsSelector, model::candidate_tx::CandidateTransaction};
use kaspa_consensus_core::{
api::ConsensusApi,
block::BlockTemplate,
block::{BlockTemplate, BuildMode},
coinbase::MinerData,
merkle::calc_hash_merkle_root,
tx::{TransactionId, COINBASE_TRANSACTION_INDEX},
Expand Down Expand Up @@ -92,11 +92,12 @@ impl BlockTemplateBuilder {
&mut self,
consensus: &dyn ConsensusApi,
miner_data: &MinerData,
mode: BuildMode,
) -> BuilderResult<BlockTemplate> {
let _sw = Stopwatch::<20>::with_threshold("build_block_template op");
debug!("Considering {} transactions for a new block template", self.selector.len());
let block_txs = self.selector.select_transactions();
Ok(consensus.build_block_template(miner_data.clone(), block_txs)?)
Ok(consensus.build_block_template(miner_data.clone(), block_txs, mode)?)
}

pub(crate) fn update_transactions(&mut self, transactions: Vec<CandidateTransaction>) {
Expand Down
12 changes: 4 additions & 8 deletions mining/src/manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ use crate::{
use itertools::Itertools;
use kaspa_consensus_core::{
api::ConsensusApi,
block::BlockTemplate,
block::{BlockTemplate, BuildMode},
coinbase::MinerData,
errors::{block::RuleError as BlockRuleError, tx::TxRuleError},
tx::{MutableTransaction, Transaction, TransactionId, TransactionOutput},
Expand Down Expand Up @@ -89,13 +89,9 @@ impl MiningManager {
let mut block_template_builder = BlockTemplateBuilder::new(self.config.maximum_mass_per_block, transactions);
loop {
attempts += 1;

// TODO: consider a parameter forcing the consensus to build a template with the remaining successfully validated transactions
//
// let force_build = attempts == self.config.maximum_build_block_template_attempts;
// match block_template_builder.build_block_template(consensus, miner_data, force_build) {

match block_template_builder.build_block_template(consensus, miner_data) {
let mode =
if attempts < self.config.maximum_build_block_template_attempts { BuildMode::Standard } else { BuildMode::Infallible };
match block_template_builder.build_block_template(consensus, miner_data, mode) {
Ok(block_template) => {
let block_template = cache_lock.set_immutable_cached_template(block_template);
match attempts {
Expand Down
3 changes: 2 additions & 1 deletion mining/src/manager_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ mod tests {
use kaspa_addresses::{Address, Prefix, Version};
use kaspa_consensus_core::{
api::ConsensusApi,
block::BuildMode,
coinbase::MinerData,
constants::{MAX_TX_IN_SEQUENCE_NUM, SOMPI_PER_KASPA, TX_VERSION},
errors::tx::{TxResult, TxRuleError},
Expand Down Expand Up @@ -847,7 +848,7 @@ mod tests {

// Build a fresh template for coinbase2 as a reference
let mut builder = mining_manager.block_template_builder(transactions);
let result = builder.build_block_template(consensus, &miner_data_2);
let result = builder.build_block_template(consensus, &miner_data_2, BuildMode::Standard);
assert!(result.is_ok(), "build block template failed for miner data 2");
let expected_template = result.unwrap();

Expand Down
9 changes: 7 additions & 2 deletions mining/src/testutils/consensus_mock.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use super::coinbase_mock::CoinbaseManagerMock;
use kaspa_consensus_core::{
api::ConsensusApi,
block::{BlockTemplate, MutableBlock},
block::{BlockTemplate, BuildMode, MutableBlock},
coinbase::MinerData,
constants::BLOCK_VERSION,
errors::{
Expand Down Expand Up @@ -72,7 +72,12 @@ impl ConsensusMock {
}

impl ConsensusApi for ConsensusMock {
fn build_block_template(&self, miner_data: MinerData, mut txs: Vec<Transaction>) -> Result<BlockTemplate, RuleError> {
fn build_block_template(
&self,
miner_data: MinerData,
mut txs: Vec<Transaction>,
_: BuildMode,
) -> Result<BlockTemplate, RuleError> {
let coinbase_manager = CoinbaseManagerMock::new();
let coinbase = coinbase_manager.expected_coinbase_transaction(miner_data.clone());
txs.insert(0, coinbase.tx);
Expand Down
4 changes: 2 additions & 2 deletions simpa/src/simulator/miner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
use kaspa_consensus_core::block::{Block, BuildMode};
use kaspa_consensus_core::coinbase::MinerData;
use kaspa_consensus_core::sign::sign;
use kaspa_consensus_core::subnets::SUBNETWORK_ID_NATIVE;
Expand Down Expand Up @@ -89,7 +89,7 @@ impl Miner {
let session = self.consensus.acquire_session();
let mut block_template = self
.consensus
.build_block_template(self.miner_data.clone(), txs)
.build_block_template(self.miner_data.clone(), txs, BuildMode::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
Expand Down

0 comments on commit e7b6551

Please sign in to comment.