diff --git a/consensus/core/src/api/mod.rs b/consensus/core/src/api/mod.rs index 7c244b914..12b4a2d03 100644 --- a/consensus/core/src/api/mod.rs +++ b/consensus/core/src/api/mod.rs @@ -199,7 +199,7 @@ pub trait ConsensusApi: Send + Sync { unimplemented!() } - fn calc_transaction_hash_merkle_root(&self, txs: &[Transaction], pov_daa_score: u64) -> Hash { + fn calc_transaction_hash_merkle_root(&self, txs: &[Arc], pov_daa_score: u64) -> Hash { unimplemented!() } diff --git a/consensus/core/src/block.rs b/consensus/core/src/block.rs index cbd76b42d..41b084e2f 100644 --- a/consensus/core/src/block.rs +++ b/consensus/core/src/block.rs @@ -5,7 +5,7 @@ use crate::{ BlueWorkType, }; use kaspa_hashes::Hash; -use kaspa_utils::mem_size::MemSizeEstimator; +use kaspa_utils::{arc::ArcExtensions, mem_size::MemSizeEstimator}; use std::sync::Arc; /// A mutable block structure where header and transactions within can still be mutated. @@ -25,7 +25,19 @@ impl MutableBlock { } pub fn to_immutable(self) -> Block { - Block::new(self.header, self.transactions) + Block::new(self.header, self.transactions.into_iter().map(Arc::new).collect()) + } +} + +impl From for MutableBlock { + fn from(block: Block) -> Self { + Self { header: block.header.unwrap_or_clone(), transactions: block.transactions.iter().map(|tx| (**tx).clone()).collect() } + } +} + +impl From for Block { + fn from(mutable_block: MutableBlock) -> Self { + mutable_block.to_immutable() } } @@ -35,15 +47,15 @@ impl MutableBlock { #[derive(Debug, Clone)] pub struct Block { pub header: Arc
, - pub transactions: Arc>, + pub transactions: Arc>>, } impl Block { - pub fn new(header: Header, txs: Vec) -> Self { + pub fn new(header: Header, txs: Vec>) -> Self { Self { header: Arc::new(header), transactions: Arc::new(txs) } } - pub fn from_arcs(header: Arc
, transactions: Arc>) -> Self { + pub fn from_arcs(header: Arc
, transactions: Arc>>) -> Self { Self { header, transactions } } @@ -79,7 +91,7 @@ impl MemSizeEstimator for Block { size_of::() + self.header.estimate_mem_bytes() + size_of::>() - + self.transactions.iter().map(Transaction::estimate_mem_bytes).sum::() + + self.transactions.iter().map(|tx| tx.estimate_mem_bytes()).sum::() } } @@ -88,7 +100,7 @@ pub trait TemplateTransactionSelector { /// Expected to return a batch of transactions which were not previously selected. /// The batch will typically contain sufficient transactions to fill the block /// mass (along with the previously unrejected txs), or will drain the selector - fn select_transactions(&mut self) -> Vec; + fn select_transactions(&mut self) -> Vec>; /// Should be used to report invalid transactions obtained from the *most recent* /// `select_transactions` call. Implementors should use this call to internally @@ -114,7 +126,7 @@ pub enum TemplateBuildMode { /// A block template for miners. #[derive(Debug, Clone)] pub struct BlockTemplate { - pub block: MutableBlock, + pub block: Block, pub miner_data: MinerData, pub coinbase_has_red_reward: bool, pub selected_parent_timestamp: u64, @@ -126,7 +138,7 @@ pub struct BlockTemplate { impl BlockTemplate { pub fn new( - block: MutableBlock, + block: Block, miner_data: MinerData, coinbase_has_red_reward: bool, selected_parent_timestamp: u64, diff --git a/consensus/core/src/config/genesis.rs b/consensus/core/src/config/genesis.rs index 9f9ea21e5..5cfa2331f 100644 --- a/consensus/core/src/config/genesis.rs +++ b/consensus/core/src/config/genesis.rs @@ -1,3 +1,5 @@ +use std::sync::Arc; + use crate::{block::Block, header::Header, subnets::SUBNETWORK_ID_COINBASE, tx::Transaction}; use kaspa_hashes::{Hash, ZERO_HASH}; use kaspa_muhash::EMPTY_MUHASH; @@ -17,8 +19,8 @@ pub struct GenesisBlock { } impl GenesisBlock { - pub fn build_genesis_transactions(&self) -> Vec { - vec![Transaction::new(0, Vec::new(), Vec::new(), 0, SUBNETWORK_ID_COINBASE, 0, self.coinbase_payload.to_vec())] + pub fn build_genesis_transactions(&self) -> Vec> { + vec![Arc::new(Transaction::new(0, Vec::new(), Vec::new(), 0, SUBNETWORK_ID_COINBASE, 0, self.coinbase_payload.to_vec()))] } } @@ -231,7 +233,7 @@ mod tests { fn test_genesis_hashes() { [GENESIS, TESTNET_GENESIS, TESTNET11_GENESIS, SIMNET_GENESIS, DEVNET_GENESIS].into_iter().for_each(|genesis| { let block: Block = (&genesis).into(); - assert_hashes_eq(calc_hash_merkle_root(block.transactions.iter(), false), block.header.hash_merkle_root); + assert_hashes_eq(calc_hash_merkle_root(block.transactions.iter().map(|tx| &(**tx)), false), block.header.hash_merkle_root); assert_hashes_eq(block.hash(), genesis.hash); }); } diff --git a/consensus/src/consensus/mod.rs b/consensus/src/consensus/mod.rs index eca78ee2a..ad124c57e 100644 --- a/consensus/src/consensus/mod.rs +++ b/consensus/src/consensus/mod.rs @@ -742,9 +742,9 @@ impl ConsensusApi for Consensus { self.services.coinbase_manager.modify_coinbase_payload(payload, miner_data) } - fn calc_transaction_hash_merkle_root(&self, txs: &[Transaction], pov_daa_score: u64) -> Hash { + fn calc_transaction_hash_merkle_root(&self, txs: &[Arc], pov_daa_score: u64) -> Hash { let storage_mass_activated = self.config.storage_mass_activation.is_active(pov_daa_score); - calc_hash_merkle_root(txs.iter(), storage_mass_activated) + calc_hash_merkle_root(txs.iter().map(|tx| &**tx), storage_mass_activated) } fn validate_pruning_proof( diff --git a/consensus/src/consensus/test_consensus.rs b/consensus/src/consensus/test_consensus.rs index 87790d093..e09e7776d 100644 --- a/consensus/src/consensus/test_consensus.rs +++ b/consensus/src/consensus/test_consensus.rs @@ -146,10 +146,10 @@ impl TestConsensus { &self, hash: Hash, parents: Vec, - txs: Vec, + txs: Vec>, ) -> impl Future> { let miner_data = MinerData::new(ScriptPublicKey::from_vec(0, vec![]), vec![]); - self.validate_and_insert_block(self.build_utxo_valid_block_with_parents(hash, parents, miner_data, txs).to_immutable()) + self.validate_and_insert_block(self.build_utxo_valid_block_with_parents(hash, parents, miner_data, txs.clone()).to_immutable()) .virtual_state_task } @@ -164,11 +164,10 @@ impl TestConsensus { hash: Hash, parents: Vec, miner_data: MinerData, - txs: Vec, + txs: Vec>, ) -> MutableBlock { - let mut template = self.block_builder.build_block_template_with_parents(parents, miner_data, txs).unwrap(); - template.block.header.hash = hash; - template.block + let template = self.block_builder.build_block_template_with_parents(parents, miner_data, txs).unwrap(); + MutableBlock::new((*template.block.header).clone(), template.block.transactions.iter().map(|tx| (**tx).clone()).collect()) } pub fn build_block_with_parents_and_transactions( diff --git a/consensus/src/model/stores/block_transactions.rs b/consensus/src/model/stores/block_transactions.rs index 53aa487e0..c66d41582 100644 --- a/consensus/src/model/stores/block_transactions.rs +++ b/consensus/src/model/stores/block_transactions.rs @@ -49,16 +49,16 @@ impl AsRef<[u8]> for BlockTransactionFullAccessKey { } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] -struct BlockBody(Arc>); +struct BlockBody(Arc>>); pub trait BlockTransactionsStoreReader { - fn get(&self, block_hash: Hash) -> Result>, StoreError>; - fn get_at_index(&self, block_hash: Hash, index: TransactionIndexType) -> Result; + fn get(&self, block_hash: Hash) -> Result>>, StoreError>; + fn get_at_index(&self, block_hash: Hash, index: TransactionIndexType) -> Result, StoreError>; } pub trait BlockTransactionsStore: BlockTransactionsStoreReader { // This is append only - fn insert(&self, hash: Hash, transactions: Arc>) -> Result<(), StoreError>; + fn insert(&self, hash: Hash, transactions: Arc>>) -> Result<(), StoreError>; fn delete(&self, hash: Hash) -> Result<(), StoreError>; } @@ -83,7 +83,7 @@ impl MemSizeEstimator for BlockBody { #[derive(Clone)] pub struct DbBlockTransactionsStore { db: Arc, - access: CachedDbAccess, + access: CachedDbAccess>, cache: Cache, } @@ -104,7 +104,12 @@ impl DbBlockTransactionsStore { Ok(self.cache.contains_key(&hash) || self.access.has_bucket(hash.as_bytes().as_ref())?) } - pub fn insert_batch(&self, batch: &mut WriteBatch, hash: Hash, transactions: Arc>) -> Result<(), StoreError> { + pub fn insert_batch( + &self, + batch: &mut WriteBatch, + hash: Hash, + transactions: Arc>>, + ) -> Result<(), StoreError> { if self.cache.contains_key(&hash) || self.access.has_bucket(hash.as_bytes().as_ref())? { return Err(StoreError::HashAlreadyExists(hash)); } @@ -127,7 +132,7 @@ impl DbBlockTransactionsStore { } impl BlockTransactionsStoreReader for DbBlockTransactionsStore { - fn get(&self, hash: Hash) -> Result>, StoreError> { + fn get(&self, hash: Hash) -> Result>>, StoreError> { self.cache .get(&hash) .map(|block_transactions| block_transactions.0.clone()) @@ -139,22 +144,22 @@ impl BlockTransactionsStoreReader for DbBlockTransactionsStore { } } - fn get_at_index(&self, block_hash: Hash, index: TransactionIndexType) -> Result { - if let Some(block_transactions) = self.cache.get(&block_hash) { - return Ok(block_transactions.0[index as usize].clone()); + fn get_at_index(&self, block_hash: Hash, index: TransactionIndexType) -> Result, StoreError> { + Ok(if let Some(block_transactions) = self.cache.get(&block_hash) { + block_transactions.0[index as usize].clone() } else { - self.access.read(BlockTransactionFullAccessKey::new(&block_hash, index)) - } + self.access.read(BlockTransactionFullAccessKey::new(&block_hash, index))? + }) } } impl BlockTransactionsStore for DbBlockTransactionsStore { - fn insert(&self, hash: Hash, transactions: Arc>) -> Result<(), StoreError> { + fn insert(&self, hash: Hash, transactions: Arc>>) -> Result<(), StoreError> { if self.access.has_bucket(hash.as_bytes().as_ref())? { return Err(StoreError::HashAlreadyExists(hash)); } self.cache.insert(hash, BlockBody(transactions.clone())); - self.access.write_many_without_cache( + self.access.write_many( DirectDbWriter::new(&self.db), &mut transactions .iter() diff --git a/consensus/src/pipeline/body_processor/body_validation_in_isolation.rs b/consensus/src/pipeline/body_processor/body_validation_in_isolation.rs index 4c6139846..57ee988f9 100644 --- a/consensus/src/pipeline/body_processor/body_validation_in_isolation.rs +++ b/consensus/src/pipeline/body_processor/body_validation_in_isolation.rs @@ -29,7 +29,7 @@ impl BlockBodyProcessor { } fn check_hash_merkle_root(block: &Block, storage_mass_activated: bool) -> BlockProcessResult<()> { - let calculated = calc_hash_merkle_root(block.transactions.iter(), storage_mass_activated); + let calculated = calc_hash_merkle_root(block.transactions.iter().map(|tx| &**tx), storage_mass_activated); if calculated != block.header.hash_merkle_root { return Err(RuleError::BadMerkleRoot(block.header.hash_merkle_root, calculated)); } diff --git a/consensus/src/pipeline/body_processor/processor.rs b/consensus/src/pipeline/body_processor/processor.rs index ebb11a200..f10f53d1f 100644 --- a/consensus/src/pipeline/body_processor/processor.rs +++ b/consensus/src/pipeline/body_processor/processor.rs @@ -233,7 +233,7 @@ impl BlockBodyProcessor { Ok(mass) } - fn commit_body(self: &Arc, hash: Hash, parents: &[Hash], transactions: Arc>) { + fn commit_body(self: &Arc, hash: Hash, parents: &[Hash], transactions: Arc>>) { let mut batch = WriteBatch::default(); // This is an append only store so it requires no lock. diff --git a/consensus/src/pipeline/virtual_processor/processor.rs b/consensus/src/pipeline/virtual_processor/processor.rs index 1f0c4ff38..b0cdeed4d 100644 --- a/consensus/src/pipeline/virtual_processor/processor.rs +++ b/consensus/src/pipeline/virtual_processor/processor.rs @@ -50,7 +50,7 @@ use crate::{ use kaspa_consensus_core::{ acceptance_data::AcceptanceData, api::args::{TransactionValidationArgs, TransactionValidationBatchArgs}, - block::{BlockTemplate, MutableBlock, TemplateBuildMode, TemplateTransactionSelector}, + block::{Block, BlockTemplate, MutableBlock, TemplateBuildMode, TemplateTransactionSelector}, blockstatus::BlockStatus::{StatusDisqualifiedFromChain, StatusUTXOValid}, coinbase::MinerData, config::{genesis::GenesisBlock, params::ForkActivation}, @@ -83,7 +83,7 @@ use super::errors::{PruningImportError, PruningImportResult}; use crossbeam_channel::{Receiver as CrossbeamReceiver, Sender as CrossbeamSender}; use itertools::Itertools; use kaspa_consensus_core::tx::ValidatedTransaction; -use kaspa_utils::binary_heap::BinaryHeapExtensions; +use kaspa_utils::{arc::ArcExtensions, binary_heap::BinaryHeapExtensions}; use parking_lot::{RwLock, RwLockUpgradableReadGuard}; use rand::{seq::SliceRandom, Rng}; use rayon::{ @@ -879,7 +879,7 @@ impl VirtualStateProcessor { fn validate_block_template_transactions_in_parallel( &self, - txs: &[Transaction], + txs: &[Arc], virtual_state: &VirtualState, utxo_view: &V, ) -> Vec> { @@ -977,7 +977,7 @@ impl VirtualStateProcessor { pub(crate) fn validate_block_template_transactions( &self, - txs: &[Transaction], + txs: &[Arc], virtual_state: &VirtualState, utxo_view: &impl UtxoView, ) -> Result<(), RuleError> { @@ -999,7 +999,7 @@ impl VirtualStateProcessor { &self, virtual_state: Arc, miner_data: MinerData, - mut txs: Vec, + mut txs: Vec>, calculated_fees: Vec, ) -> Result { // [`calc_block_parents`] can use deep blocks below the pruning point for this calculation, so we @@ -1018,13 +1018,13 @@ impl VirtualStateProcessor { &virtual_state.mergeset_non_daa, ) .unwrap(); - txs.insert(0, coinbase.tx); + txs.insert(0, Arc::new(coinbase.tx)); let version = BLOCK_VERSION; let parents_by_level = self.parents_manager.calc_block_parents(pruning_info.pruning_point, &virtual_state.parents); // Hash according to hardfork activation let storage_mass_activated = self.storage_mass_activation.is_active(virtual_state.daa_score); - let hash_merkle_root = calc_hash_merkle_root(txs.iter(), storage_mass_activated); + let hash_merkle_root = calc_hash_merkle_root(txs.iter().map(|tx| &**tx), storage_mass_activated); let accepted_id_merkle_root = kaspa_merkle::calc_merkle_root(virtual_state.accepted_tx_ids.iter().copied()); let utxo_commitment = virtual_state.multiset.clone().finalize(); @@ -1048,7 +1048,7 @@ impl VirtualStateProcessor { let selected_parent_timestamp = self.headers_store.get_timestamp(selected_parent_hash).unwrap(); let selected_parent_daa_score = self.headers_store.get_daa_score(selected_parent_hash).unwrap(); Ok(BlockTemplate::new( - MutableBlock::new(header, txs), + Block::new(header, txs), miner_data, coinbase.has_red_reward, selected_parent_timestamp, diff --git a/consensus/src/pipeline/virtual_processor/test_block_builder.rs b/consensus/src/pipeline/virtual_processor/test_block_builder.rs index 2654a6a5f..7857a7c45 100644 --- a/consensus/src/pipeline/virtual_processor/test_block_builder.rs +++ b/consensus/src/pipeline/virtual_processor/test_block_builder.rs @@ -34,7 +34,7 @@ impl TestBlockBuilder { &self, parents: Vec, miner_data: MinerData, - txs: Vec, + txs: Vec>, ) -> Result { // // In the context of this method "pov virtual" is the virtual block which has `parents` as tips and not the actual virtual diff --git a/consensus/src/pipeline/virtual_processor/tests.rs b/consensus/src/pipeline/virtual_processor/tests.rs index 20eea9e57..a22186565 100644 --- a/consensus/src/pipeline/virtual_processor/tests.rs +++ b/consensus/src/pipeline/virtual_processor/tests.rs @@ -10,20 +10,20 @@ use kaspa_consensus_core::{ BlockHashSet, }; use kaspa_hashes::Hash; -use std::{collections::VecDeque, thread::JoinHandle}; +use std::{collections::VecDeque, sync::Arc, thread::JoinHandle}; struct OnetimeTxSelector { - txs: Option>, + txs: Option>>, } impl OnetimeTxSelector { - fn new(txs: Vec) -> Self { + fn new(txs: Vec>) -> Self { Self { txs: Some(txs) } } } impl TemplateTransactionSelector for OnetimeTxSelector { - fn select_transactions(&mut self) -> Vec { + fn select_transactions(&mut self) -> Vec> { self.txs.take().unwrap() } @@ -85,7 +85,7 @@ impl TestContext { self.current_tips.clear(); while let Some(t) = self.current_templates.pop_front() { self.current_tips.insert(t.block.header.hash); - self.validate_and_insert_block(t.block.to_immutable()).await; + self.validate_and_insert_block(t.block).await; } self } @@ -110,9 +110,11 @@ impl TestContext { TemplateBuildMode::Standard, ) .unwrap(); - t.block.header.timestamp = timestamp; - t.block.header.nonce = nonce; - t.block.header.finalize(); + let mut mutable_block = MutableBlock::from(t.block); + mutable_block.header.timestamp = timestamp; + mutable_block.header.nonce = nonce; + mutable_block.header.finalize(); + t.block = mutable_block.into(); t } diff --git a/consensus/src/pipeline/virtual_processor/utxo_validation.rs b/consensus/src/pipeline/virtual_processor/utxo_validation.rs index 4a62a4ae8..26d0370b4 100644 --- a/consensus/src/pipeline/virtual_processor/utxo_validation.rs +++ b/consensus/src/pipeline/virtual_processor/utxo_validation.rs @@ -33,7 +33,7 @@ use kaspa_utils::refs::Refs; use rayon::prelude::*; use smallvec::{smallvec, SmallVec}; -use std::{iter::once, ops::Deref}; +use std::{iter::once, ops::Deref, sync::Arc}; /// A context for processing the UTXO state of a block with respect to its selected parent. /// Note this can also be the virtual block. @@ -217,7 +217,7 @@ impl VirtualStateProcessor { /// which passed the validation along with their original index within the containing block pub(crate) fn validate_transactions_in_parallel<'a, V: UtxoView + Sync>( &self, - txs: &'a Vec, + txs: &'a Arc>>, utxo_view: &V, pov_daa_score: u64, flags: TxValidationFlags, @@ -237,7 +237,7 @@ impl VirtualStateProcessor { /// calculate the muhash in parallel for valid transactions pub(crate) fn validate_transactions_with_muhash_in_parallel<'a, V: UtxoView + Sync>( &self, - txs: &'a Vec, + txs: &'a Arc>>, utxo_view: &V, pov_daa_score: u64, flags: TxValidationFlags, diff --git a/consensus/src/test_helpers.rs b/consensus/src/test_helpers.rs index c119c6d6d..584742a2c 100644 --- a/consensus/src/test_helpers.rs +++ b/consensus/src/test_helpers.rs @@ -1,3 +1,5 @@ +use std::sync::Arc; + use kaspa_consensus_core::{ block::Block, header::Header, @@ -125,8 +127,13 @@ pub fn generate_random_transaction(rng: &mut SmallRng, input_amount: usize, outp } ///Note: generate_random_transactions is filled with random data, it does not represent consensus-valid transactions! -pub fn generate_random_transactions(rng: &mut SmallRng, amount: usize, input_amount: usize, output_amount: usize) -> Vec { - Vec::from_iter((0..amount).map(move |_| generate_random_transaction(rng, input_amount, output_amount))) +pub fn generate_random_transactions( + rng: &mut SmallRng, + amount: usize, + input_amount: usize, + output_amount: usize, +) -> Vec> { + (0..amount).map(move |_| Arc::new(generate_random_transaction(rng, input_amount, output_amount))).collect() } ///Note: generate_random_transactions is filled with random data, it does not represent consensus-valid transaction input! diff --git a/database/src/access.rs b/database/src/access.rs index 381974c62..bab791950 100644 --- a/database/src/access.rs +++ b/database/src/access.rs @@ -193,7 +193,6 @@ where { self.db .iterator_opt(IteratorMode::Start, read_opts) - .into_iter() .map(|item| match item { Ok((_, value_bytes)) => match bincode::deserialize::(value_bytes.as_ref()) { Ok(value) => Ok(value), diff --git a/mining/src/block_template/builder.rs b/mining/src/block_template/builder.rs index 6f0dbe674..737d27100 100644 --- a/mining/src/block_template/builder.rs +++ b/mining/src/block_template/builder.rs @@ -1,11 +1,15 @@ +use std::sync::Arc; + use super::errors::BuilderResult; use kaspa_consensus_core::{ api::ConsensusApi, block::{BlockTemplate, TemplateBuildMode, TemplateTransactionSelector}, coinbase::MinerData, + header::Header, tx::COINBASE_TRANSACTION_INDEX, }; use kaspa_core::time::{unix_now, Stopwatch}; +use kaspa_utils::arc::ArcExtensions; pub(crate) struct BlockTemplateBuilder {} @@ -97,7 +101,7 @@ impl BlockTemplateBuilder { let mut block_template = block_template_to_modify.clone(); // The first transaction is always the coinbase transaction - let coinbase_tx = &mut block_template.block.transactions[COINBASE_TRANSACTION_INDEX]; + let coinbase_tx = &mut (*(block_template.block.transactions.clone()[COINBASE_TRANSACTION_INDEX].clone())).clone(); let new_payload = consensus.modify_coinbase_payload(coinbase_tx.payload.clone(), new_miner_data)?; coinbase_tx.payload = new_payload; if block_template.coinbase_has_red_reward { @@ -105,15 +109,18 @@ impl BlockTemplateBuilder { coinbase_tx.outputs.last_mut().unwrap().script_public_key = new_miner_data.script_public_key.clone(); } // Update the hash merkle root according to the modified transactions - block_template.block.header.hash_merkle_root = - consensus.calc_transaction_hash_merkle_root(&block_template.block.transactions, block_template.block.header.daa_score); + let old_timestamp = block_template.block.header.timestamp; + let mut new_header = block_template.block.header.unwrap_or_clone(); + new_header.hash_merkle_root = consensus + .calc_transaction_hash_merkle_root(&block_template.block.transactions, new_header.daa_score); let new_timestamp = unix_now(); - if new_timestamp > block_template.block.header.timestamp { + if new_timestamp > old_timestamp { // Only if new time stamp is later than current, update the header. Otherwise, // we keep the previous time as built by internal consensus median time logic - block_template.block.header.timestamp = new_timestamp; + new_header.timestamp = new_timestamp; } - block_template.block.header.finalize(); + new_header.finalize(); + block_template.block.header = Arc::new(new_header); block_template.miner_data = new_miner_data.clone(); Ok(block_template) } diff --git a/mining/src/block_template/selector.rs b/mining/src/block_template/selector.rs index 6acacb22d..da14b107a 100644 --- a/mining/src/block_template/selector.rs +++ b/mining/src/block_template/selector.rs @@ -1,6 +1,6 @@ use kaspa_core::{time::Stopwatch, trace}; use rand::Rng; -use std::collections::HashMap; +use std::{collections::HashMap, sync::Arc}; use crate::model::candidate_tx::CandidateTransaction; @@ -103,7 +103,7 @@ impl RebalancingWeightedTransactionSelector { /// select_transactions loops over the candidate transactions /// and appends the ones that will be included in the next block into /// selected_txs. - pub fn select_transactions(&mut self) -> Vec { + pub fn select_transactions(&mut self) -> Vec> { let _sw = Stopwatch::<15>::with_threshold("select_transaction op"); let mut rng = rand::thread_rng(); @@ -195,9 +195,9 @@ impl RebalancingWeightedTransactionSelector { self.get_transactions() } - fn get_transactions(&self) -> Vec { + fn get_transactions(&self) -> Vec> { // These transactions leave the selector so we clone - self.selected_txs.iter().map(|x| self.transactions[*x].tx.as_ref().clone()).collect() + self.selected_txs.iter().map(|x| self.transactions[*x].tx.clone()).collect() } fn reset_selection(&mut self) { @@ -226,7 +226,7 @@ impl RebalancingWeightedTransactionSelector { } impl TemplateTransactionSelector for RebalancingWeightedTransactionSelector { - fn select_transactions(&mut self) -> Vec { + fn select_transactions(&mut self) -> Vec> { self.select_transactions() } diff --git a/mining/src/manager.rs b/mining/src/manager.rs index 32893312a..148c6050c 100644 --- a/mining/src/manager.rs +++ b/mining/src/manager.rs @@ -241,7 +241,7 @@ impl MiningManager { )); let miner_data: MinerData = MinerData::new(script_public_key, vec![]); - let BlockTemplate { block: kaspa_consensus_core::block::MutableBlock { transactions, .. }, calculated_fees, .. } = + let BlockTemplate { block: kaspa_consensus_core::block::Block { transactions, .. }, calculated_fees, .. } = self.get_block_template(consensus, &miner_data)?; let Some(Stats { max, median, min }) = feerate_stats(transactions, calculated_fees) else { @@ -1039,7 +1039,7 @@ struct Stats { /// Returns an `Option` containing the maximum, median, and minimum fee /// rates if the input vectors are valid. Returns `None` if the vectors are /// empty or if the lengths are inconsistent. -fn feerate_stats(transactions: Vec, calculated_fees: Vec) -> Option { +fn feerate_stats(transactions: Vec>, calculated_fees: Vec) -> Option { if calculated_fees.is_empty() { return None; } @@ -1058,7 +1058,7 @@ fn feerate_stats(transactions: Vec, calculated_fees: Vec) -> O .iter() // skip coinbase tx .skip(1) - .map(Transaction::mass)) + .map( |tx| tx.mass())) .map(|(fee, mass)| fee as f64 / mass as f64) .collect_vec(); feerates.sort_unstable_by(f64::total_cmp); diff --git a/mining/src/mempool/model/frontier/selectors.rs b/mining/src/mempool/model/frontier/selectors.rs index a30ecc145..7372d0796 100644 --- a/mining/src/mempool/model/frontier/selectors.rs +++ b/mining/src/mempool/model/frontier/selectors.rs @@ -88,7 +88,7 @@ impl SequenceSelector { } impl TemplateTransactionSelector for SequenceSelector { - fn select_transactions(&mut self) -> Vec { + fn select_transactions(&mut self) -> Vec> { // Remove selections from the previous round if any for selection in self.selected_vec.drain(..) { self.input_sequence.inner.remove(&selection.priority_index); @@ -106,7 +106,7 @@ impl TemplateTransactionSelector for SequenceSelector { } self.total_selected_mass += tx.mass; self.selected_vec.push(SequenceSelectorSelection { tx_id: tx.tx.id(), mass: tx.mass, priority_index }); - transactions.push(tx.tx.as_ref().clone()) + transactions.push(tx.tx.clone()) } transactions } @@ -145,9 +145,9 @@ impl TakeAllSelector { } impl TemplateTransactionSelector for TakeAllSelector { - fn select_transactions(&mut self) -> Vec { + fn select_transactions(&mut self) -> Vec> { // Drain on the first call so that subsequent calls return nothing - self.txs.drain(..).map(|tx| tx.as_ref().clone()).collect() + self.txs.drain(..).map(|tx| tx).collect() } fn reject_selection(&mut self, _tx_id: TransactionId) { diff --git a/mining/src/testutils/consensus_mock.rs b/mining/src/testutils/consensus_mock.rs index 28d3f5897..697b418a8 100644 --- a/mining/src/testutils/consensus_mock.rs +++ b/mining/src/testutils/consensus_mock.rs @@ -4,7 +4,7 @@ use kaspa_consensus_core::{ args::{TransactionValidationArgs, TransactionValidationBatchArgs}, ConsensusApi, }, - block::{BlockTemplate, MutableBlock, TemplateBuildMode, TemplateTransactionSelector, VirtualStateApproxId}, + block::{Block, BlockTemplate, MutableBlock, TemplateBuildMode, TemplateTransactionSelector, VirtualStateApproxId}, coinbase::MinerData, constants::BLOCK_VERSION, errors::{ @@ -84,7 +84,7 @@ impl ConsensusApi for ConsensusMock { let mut txs = tx_selector.select_transactions(); let coinbase_manager = CoinbaseManagerMock::new(); let coinbase = coinbase_manager.expected_coinbase_transaction(miner_data.clone()); - txs.insert(0, coinbase.tx); + txs.insert(0, Arc::new(coinbase.tx)); let now = unix_now(); let hash_merkle_root = self.calc_transaction_hash_merkle_root(&txs, 0); let header = Header::new_finalized( @@ -101,7 +101,7 @@ impl ConsensusApi for ConsensusMock { 0, ZERO_HASH, ); - let mutable_block = MutableBlock::new(header, txs); + let mutable_block = Block::new(header, txs); Ok(BlockTemplate::new(mutable_block, miner_data, coinbase.has_red_reward, now, 0, ZERO_HASH, vec![])) } @@ -181,7 +181,7 @@ impl ConsensusApi for ConsensusMock { Ok(coinbase_manager.modify_coinbase_payload(payload, miner_data)) } - fn calc_transaction_hash_merkle_root(&self, txs: &[Transaction], _pov_daa_score: u64) -> Hash { - calc_hash_merkle_root(txs.iter(), false) + fn calc_transaction_hash_merkle_root(&self, txs: &[Arc], _pov_daa_score: u64) -> Hash { + calc_hash_merkle_root(txs.iter().map(|tx| &**tx), false) } } diff --git a/rpc/core/src/convert/tx.rs b/rpc/core/src/convert/tx.rs index 9b69ca168..33fe073cf 100644 --- a/rpc/core/src/convert/tx.rs +++ b/rpc/core/src/convert/tx.rs @@ -1,5 +1,7 @@ //! Conversion of Transaction related types +use std::sync::Arc; + use crate::{RpcError, RpcResult, RpcTransaction, RpcTransactionInput, RpcTransactionOutput}; use kaspa_consensus_core::tx::{Transaction, TransactionInput, TransactionOutput}; @@ -24,6 +26,12 @@ impl From<&Transaction> for RpcTransaction { } } +impl From> for RpcTransaction { + fn from(item: Arc) -> Self { + Self::from(item.as_ref()) + } +} + impl From<&TransactionOutput> for RpcTransactionOutput { fn from(item: &TransactionOutput) -> Self { Self { @@ -75,6 +83,13 @@ impl TryFrom for Transaction { } } +impl TryFrom for Arc { + type Error = RpcError; + fn try_from(item: RpcTransaction) -> RpcResult { + Ok(Arc::new(Transaction::try_from(item)?)) + } +} + impl TryFrom for TransactionOutput { type Error = RpcError; fn try_from(item: RpcTransactionOutput) -> RpcResult { diff --git a/simpa/src/simulator/miner.rs b/simpa/src/simulator/miner.rs index a9a4a3423..2d0ecba06 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, TemplateBuildMode, TemplateTransactionSelector}; +use kaspa_consensus_core::block::{self, Block, TemplateBuildMode, TemplateTransactionSelector}; use kaspa_consensus_core::coinbase::MinerData; use kaspa_consensus_core::mass::{Kip9Version, MassCalculator}; use kaspa_consensus_core::sign::sign; @@ -14,6 +14,7 @@ use kaspa_consensus_core::tx::{ }; use kaspa_consensus_core::utxo::utxo_view::UtxoView; use kaspa_core::trace; +use kaspa_utils::arc::ArcExtensions; use kaspa_utils::sim::{Environment, Process, Resumption, Suspension}; use rand::rngs::ThreadRng; use rand::Rng; @@ -118,18 +119,22 @@ impl Miner { } fn build_new_block(&mut self, timestamp: u64) -> Block { + let txs = self.build_txs(); let nonce = self.id; let session = self.consensus.acquire_session(); + let mut block_template = self .consensus .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 - block_template.block.header.nonce = nonce; - block_template.block.header.finalize(); - block_template.block.to_immutable() + + let new_header = &mut block_template.block.header.unwrap_or_clone(); + new_header.timestamp = timestamp; // Use simulation time rather than real time + new_header.nonce = nonce; + new_header.finalize(); + Block::from_arcs(Arc::new(new_header), block_template.block.transactions) } fn build_txs(&mut self) -> Vec {