From 00b8554aa2959f8ffda30bea030653752ece32c7 Mon Sep 17 00:00:00 2001 From: Hansie Odendaal Date: Mon, 26 Feb 2024 11:05:51 +0200 Subject: [PATCH] Lazily evaluate RandomX template - Changed the merge mining proxy to lazily evaluate RandomX block template changes. Greedily evaluating this is very expensive, especially during busy network times with a highly volatile mempool. - Fixed a bug where the command-line network argument 'igor' specified for the merge mining proxy was not recognized in the network-aware 'DomainSeparatedConsensusHasher' --- .../minotari_app_grpc/proto/base_node.proto | 1 + .../src/common_cli_args.rs | 2 +- .../minotari_console_wallet/src/cli.rs | 6 +- .../minotari_merge_mining_proxy/Cargo.toml | 18 +- .../src/block_template_data.rs | 110 +++++-- .../src/block_template_protocol.rs | 277 ++++++++++++------ .../minotari_merge_mining_proxy/src/cli.rs | 6 +- .../minotari_merge_mining_proxy/src/error.rs | 2 + .../minotari_merge_mining_proxy/src/proxy.rs | 94 ++++-- applications/minotari_miner/src/cli.rs | 6 +- applications/minotari_node/src/cli.rs | 6 +- .../src/grpc/base_node_grpc_server.rs | 10 +- .../comms_interface/inbound_handlers.rs | 27 +- .../consensus/consensus_encoding/hashing.rs | 37 ++- .../src/proof_of_work/monero_rx/helpers.rs | 48 +-- .../core/src/proof_of_work/monero_rx/mod.rs | 2 +- .../core/src/transactions/aggregated_body.rs | 2 +- .../transaction_components/test.rs | 4 +- .../core/tests/tests/block_validation.rs | 14 +- base_layer/tari_mining_helper_ffi/src/lib.rs | 4 +- common/src/configuration/mod.rs | 4 +- common/src/configuration/network.rs | 10 +- common/src/configuration/utils.rs | 8 +- 23 files changed, 464 insertions(+), 234 deletions(-) diff --git a/applications/minotari_app_grpc/proto/base_node.proto b/applications/minotari_app_grpc/proto/base_node.proto index 57aabd60f8..0a70cdceb2 100644 --- a/applications/minotari_app_grpc/proto/base_node.proto +++ b/applications/minotari_app_grpc/proto/base_node.proto @@ -173,6 +173,7 @@ message NewBlockTemplateResponse { NewBlockTemplate new_block_template = 1; bool initial_sync_achieved = 3; MinerData miner_data = 4; + bytes best_previous_block_hash = 5; } /// return type of NewBlockTemplateRequest diff --git a/applications/minotari_app_utilities/src/common_cli_args.rs b/applications/minotari_app_utilities/src/common_cli_args.rs index 78a0f2e366..57fd52d402 100644 --- a/applications/minotari_app_utilities/src/common_cli_args.rs +++ b/applications/minotari_app_utilities/src/common_cli_args.rs @@ -105,7 +105,7 @@ impl CommonCliArgs { } impl ConfigOverrideProvider for CommonCliArgs { - fn get_config_property_overrides(&self, _default_network: Network) -> Vec<(String, String)> { + fn get_config_property_overrides(&self, _network: &mut Network) -> Vec<(String, String)> { let mut overrides = self.config_property_overrides.clone(); overrides.push(( "common.base_path".to_string(), diff --git a/applications/minotari_console_wallet/src/cli.rs b/applications/minotari_console_wallet/src/cli.rs index 2bad5ae327..83bf2d20e8 100644 --- a/applications/minotari_console_wallet/src/cli.rs +++ b/applications/minotari_console_wallet/src/cli.rs @@ -91,9 +91,9 @@ pub struct Cli { } impl ConfigOverrideProvider for Cli { - fn get_config_property_overrides(&self, default_network: Network) -> Vec<(String, String)> { - let mut overrides = self.common.get_config_property_overrides(default_network); - let network = self.common.network.unwrap_or(default_network); + fn get_config_property_overrides(&self, network: &mut Network) -> Vec<(String, String)> { + let mut overrides = self.common.get_config_property_overrides(network); + *network = self.common.network.unwrap_or(*network); overrides.push(("wallet.network".to_string(), network.to_string())); overrides.push(("wallet.override_from".to_string(), network.to_string())); overrides.push(("p2p.seeds.override_from".to_string(), network.to_string())); diff --git a/applications/minotari_merge_mining_proxy/Cargo.toml b/applications/minotari_merge_mining_proxy/Cargo.toml index 0bf57d7419..3ac3ef917e 100644 --- a/applications/minotari_merge_mining_proxy/Cargo.toml +++ b/applications/minotari_merge_mining_proxy/Cargo.toml @@ -11,33 +11,33 @@ edition = "2018" default = [] [dependencies] +minotari_app_grpc = { path = "../minotari_app_grpc" } +minotari_app_utilities = { path = "../minotari_app_utilities", features = ["miner_input"] } +minotari_node_grpc_client = { path = "../../clients/rust/base_node_grpc_client" } +minotari_wallet_grpc_client = { path = "../../clients/rust/wallet_grpc_client" } tari_common = { path = "../../common" } tari_common_types = { path = "../../base_layer/common_types" } tari_comms = { path = "../../comms/core" } tari_core = { path = "../../base_layer/core", default-features = false, features = ["transactions"] } -minotari_app_utilities = { path = "../minotari_app_utilities", features = ["miner_input"] } -tari_utilities = { version = "0.7" } -minotari_node_grpc_client = { path = "../../clients/rust/base_node_grpc_client" } -minotari_wallet_grpc_client = { path = "../../clients/rust/wallet_grpc_client" } -minotari_app_grpc = { path = "../minotari_app_grpc" } tari_key_manager = { path = "../../base_layer/key_manager", features = ["key_manager_service"] } +tari_utilities = { version = "0.7" } anyhow = "1.0.53" -crossterm = { version = "0.25.0" } bincode = "1.3.1" borsh = "1.2" bytes = "1.1" -chrono = { version = "0.4.6", default-features = false } +chrono = { version = "0.4.19", default-features = false } clap = { version = "3.2", features = ["derive", "env"] } config = { version = "0.13.0" } -futures = "0.3.5" +crossterm = { version = "0.25.0" } +futures = { version = "^0.3.16", features = ["async-await"] } hex = "0.4.2" hyper = "0.14.12" jsonrpc = "0.12.0" log = { version = "0.4.8", features = ["std"] } monero = { version = "0.20.0" } reqwest = { version = "0.11.4", features = ["json"] } -serde = { version = "1.0.106", features = ["derive"] } +serde = { version = "1.0.136", features = ["derive"] } serde_json = "1.0.57" thiserror = "1.0.26" tokio = { version = "1.23", features = ["macros"] } diff --git a/applications/minotari_merge_mining_proxy/src/block_template_data.rs b/applications/minotari_merge_mining_proxy/src/block_template_data.rs index d88598dda4..7ec3725369 100644 --- a/applications/minotari_merge_mining_proxy/src/block_template_data.rs +++ b/applications/minotari_merge_mining_proxy/src/block_template_data.rs @@ -33,7 +33,7 @@ use tari_core::proof_of_work::monero_rx::FixedByteArray; use tokio::sync::RwLock; use tracing::trace; -use crate::error::MmProxyError; +use crate::{block_template_protocol::FinalBlockTemplateData, error::MmProxyError}; const LOG_TARGET: &str = "minotari_mm_proxy::xmrig"; @@ -46,13 +46,13 @@ pub struct BlockTemplateRepository { /// Structure holding [BlockTemplateData] along with a timestamp. #[derive(Debug, Clone)] pub struct BlockTemplateRepositoryItem { - pub data: BlockTemplateData, + pub data: FinalBlockTemplateData, datetime: DateTime, } impl BlockTemplateRepositoryItem { /// Create new [Self] with current time in UTC. - pub fn new(block_template: BlockTemplateData) -> Self { + pub fn new(block_template: FinalBlockTemplateData) -> Self { Self { data: block_template, datetime: Utc::now(), @@ -73,7 +73,7 @@ impl BlockTemplateRepository { } /// Return [BlockTemplateData] with the associated hash. None if the hash is not stored. - pub async fn get>(&self, hash: T) -> Option { + pub async fn get>(&self, hash: T) -> Option { trace!( target: LOG_TARGET, "Retrieving blocktemplate with merge mining hash: {:?}", @@ -83,16 +83,25 @@ impl BlockTemplateRepository { b.get(hash.as_ref()).map(|item| item.data.clone()) } - /// Store [BlockTemplateData] at the hash value. - pub async fn save(&self, hash: Vec, block_template: BlockTemplateData) { - trace!( - target: LOG_TARGET, - "Saving blocktemplate with merge mining hash: {:?}", - hex::encode(&hash) - ); + /// Store [BlockTemplateData] at the hash value if the key does not exist. + pub async fn save_if_key_unique(&self, hash: Vec, block_template: FinalBlockTemplateData) { let mut b = self.blocks.write().await; - let repository_item = BlockTemplateRepositoryItem::new(block_template); - b.insert(hash, repository_item); + b.entry(hash.clone()).or_insert_with(|| { + trace!( + target: LOG_TARGET, + "Saving blocktemplate with merge mining hash: {:?}", + hex::encode(&hash) + ); + BlockTemplateRepositoryItem::new(block_template) + }); + } + + /// Check if the repository contains a block template with best_previous_block_hash + pub async fn contains(&self, current_best_block_hash: FixedHash) -> Option { + let b = self.blocks.read().await; + b.values() + .find(|item| item.data.template.best_previous_block_hash == current_best_block_hash) + .map(|val| val.data.clone()) } /// Remove any data that is older than 20 minutes. @@ -126,8 +135,10 @@ pub struct BlockTemplateData { pub tari_miner_data: grpc::MinerData, pub monero_difficulty: u64, pub tari_difficulty: u64, - pub tari_hash: FixedHash, + pub tari_merge_mining_hash: FixedHash, pub aux_chain_hashes: Vec, + pub new_block_template: grpc::NewBlockTemplate, + pub best_previous_block_hash: FixedHash, } impl BlockTemplateData {} @@ -140,8 +151,10 @@ pub struct BlockTemplateDataBuilder { tari_miner_data: Option, monero_difficulty: Option, tari_difficulty: Option, - tari_hash: Option, + tari_merge_mining_hash: Option, aux_chain_hashes: Vec, + new_block_template: Option, + best_previous_block_hash: Option, } impl BlockTemplateDataBuilder { @@ -174,8 +187,8 @@ impl BlockTemplateDataBuilder { self } - pub fn tari_hash(mut self, hash: FixedHash) -> Self { - self.tari_hash = Some(hash); + pub fn tari_merge_mining_hash(mut self, hash: FixedHash) -> Self { + self.tari_merge_mining_hash = Some(hash); self } @@ -184,6 +197,16 @@ impl BlockTemplateDataBuilder { self } + pub fn new_block_template(mut self, template: grpc::NewBlockTemplate) -> Self { + self.new_block_template = Some(template); + self + } + + pub fn best_previous_block_hash(mut self, hash: FixedHash) -> Self { + self.best_previous_block_hash = Some(hash); + self + } + /// Build a new [BlockTemplateData], all the values have to be set. /// /// # Errors @@ -205,12 +228,18 @@ impl BlockTemplateDataBuilder { let tari_difficulty = self .tari_difficulty .ok_or_else(|| MmProxyError::MissingDataError("tari_difficulty not provided".to_string()))?; - let tari_hash = self - .tari_hash + let tari_merge_mining_hash = self + .tari_merge_mining_hash .ok_or_else(|| MmProxyError::MissingDataError("tari_hash not provided".to_string()))?; if self.aux_chain_hashes.is_empty() { return Err(MmProxyError::MissingDataError("aux chain hashes are empty".to_string())); }; + let new_block_template = self + .new_block_template + .ok_or_else(|| MmProxyError::MissingDataError("new_block_template not provided".to_string()))?; + let best_previous_block_hash = self + .best_previous_block_hash + .ok_or_else(|| MmProxyError::MissingDataError("best_previous_block_hash not provided".to_string()))?; Ok(BlockTemplateData { monero_seed, @@ -218,8 +247,10 @@ impl BlockTemplateDataBuilder { tari_miner_data, monero_difficulty, tari_difficulty, - tari_hash, + tari_merge_mining_hash, aux_chain_hashes: self.aux_chain_hashes, + new_block_template, + best_previous_block_hash, }) } } @@ -230,12 +261,14 @@ pub mod test { use tari_core::{ blocks::{Block, BlockHeader}, + proof_of_work::Difficulty, transactions::aggregated_body::AggregateBody, }; + use tari_utilities::ByteArray; use super::*; - fn create_block_template_data() -> BlockTemplateData { + fn create_block_template_data() -> FinalBlockTemplateData { let header = BlockHeader::new(100); let body = AggregateBody::empty(); let block = Block::new(header, body); @@ -246,15 +279,27 @@ pub mod test { total_fees: 100, algo: Some(grpc::PowAlgo { pow_algo: 0 }), }; + let new_block_template = grpc::NewBlockTemplate::default(); + let best_block_hash = FixedHash::zero(); let btdb = BlockTemplateDataBuilder::new() .monero_seed(FixedByteArray::new()) .tari_block(block.try_into().unwrap()) .tari_miner_data(miner_data) .monero_difficulty(123456) .tari_difficulty(12345) - .tari_hash(hash) - .aux_hashes(vec![monero::Hash::from_slice(hash.as_slice())]); - btdb.build().unwrap() + .tari_merge_mining_hash(hash) + .aux_hashes(vec![monero::Hash::from_slice(hash.as_slice())]) + .new_block_template(new_block_template) + .best_previous_block_hash(best_block_hash); + let block_template_data = btdb.build().unwrap(); + FinalBlockTemplateData { + template: block_template_data, + target_difficulty: Difficulty::from_u64(12345).unwrap(), + blockhashing_blob: "no blockhashing_blob data".to_string(), + blocktemplate_blob: "no blocktemplate_blob data".to_string(), + aux_chain_hashes: vec![monero::Hash::from_slice(hash.as_slice())], + aux_chain_mr: hash.to_vec(), + } } #[tokio::test] @@ -264,8 +309,8 @@ pub mod test { let hash2 = vec![2; 32]; let hash3 = vec![3; 32]; let block_template = create_block_template_data(); - btr.save(hash1.clone(), block_template.clone()).await; - btr.save(hash2.clone(), block_template).await; + btr.save_if_key_unique(hash1.clone(), block_template.clone()).await; + btr.save_if_key_unique(hash2.clone(), block_template).await; assert!(btr.get(hash1.clone()).await.is_some()); assert!(btr.get(hash2.clone()).await.is_some()); assert!(btr.get(hash3.clone()).await.is_none()); @@ -323,10 +368,13 @@ pub mod test { #[test] pub fn ok_block_template_data_builder() { let build = create_block_template_data(); - assert!(build.monero_seed.is_empty()); - assert_eq!(build.tari_block.header.unwrap().version, 100); - assert_eq!(build.tari_miner_data.target_difficulty, 600000); - assert_eq!(build.monero_difficulty, 123456); - assert_eq!(build.tari_difficulty, 12345); + assert!(build.template.monero_seed.is_empty()); + assert_eq!(build.template.tari_block.header.unwrap().version, 100); + assert_eq!(build.template.tari_miner_data.target_difficulty, 600000); + assert_eq!(build.template.monero_difficulty, 123456); + assert_eq!(build.template.tari_difficulty, 12345); + assert_eq!(build.blockhashing_blob, "no blockhashing_blob data".to_string()); + assert_eq!(build.blocktemplate_blob, "no blocktemplate_blob data".to_string()); + assert_eq!(build.target_difficulty, Difficulty::from_u64(12345).unwrap()); } } diff --git a/applications/minotari_merge_mining_proxy/src/block_template_protocol.rs b/applications/minotari_merge_mining_proxy/src/block_template_protocol.rs index 04e59852e4..9e2e72f38c 100644 --- a/applications/minotari_merge_mining_proxy/src/block_template_protocol.rs +++ b/applications/minotari_merge_mining_proxy/src/block_template_protocol.rs @@ -21,13 +21,18 @@ // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. //! Methods for seting up a new block. -use std::{cmp, convert::TryFrom, sync::Arc}; +use std::{ + cmp, + convert::{TryFrom, TryInto}, + sync::Arc, +}; use log::*; use minotari_app_utilities::parse_miner_input::BaseNodeGrpcClient; use minotari_node_grpc_client::grpc; use tari_common_types::{tari_address::TariAddress, types::FixedHash}; use tari_core::{ + blocks::BlockHeader, consensus::ConsensusManager, proof_of_work::{monero_rx, monero_rx::FixedByteArray, Difficulty}, transactions::{ @@ -36,6 +41,7 @@ use tari_core::{ transaction_components::{TransactionKernel, TransactionOutput}, }, }; +use tari_utilities::hex::Hex; use crate::{ block_template_data::{BlockTemplateData, BlockTemplateDataBuilder}, @@ -78,48 +84,99 @@ impl BlockTemplateProtocol<'_> { pub async fn get_next_block_template( mut self, monero_mining_data: MoneroMiningData, + existing_block_template: Option, ) -> Result { loop { - let new_template = self.get_new_block_template().await?; - let (coinbase_output, coinbase_kernel) = self.get_coinbase(&new_template).await?; + let (final_template_data, block_height) = if let Some(data) = existing_block_template.clone() { + let height = data + .template + .tari_block + .header + .as_ref() + .map(|h| h.height) + .unwrap_or_default(); + debug!( + target: LOG_TARGET, + "Used existing block template and block for height: #{}, block hash: `{}`", + height, + match data.template.tari_block.header.as_ref() { + Some(h) => h.hash.to_hex(), + None => "None".to_string(), + } + ); + (data, height) + } else { + let new_template = self.get_new_block_template().await?; + let height = new_template + .template + .header + .as_ref() + .map(|h| h.height) + .unwrap_or_default(); + debug!(target: LOG_TARGET, "Requested new block template at height: #{}", height); + let (coinbase_output, coinbase_kernel) = self.get_coinbase(&new_template).await?; + + let block_template_with_coinbase = + merge_mining::add_coinbase(&coinbase_output, &coinbase_kernel, new_template.template.clone())?; + debug!(target: LOG_TARGET, "Added coinbase to new block template"); + let block = match self.get_new_block(block_template_with_coinbase).await { + Ok(b) => { + debug!( + target: LOG_TARGET, + "Requested new block at height: #{}, block hash: `{}`", + height, + { + let block_header = b.block.as_ref().map(|b| b.header.as_ref()).unwrap_or_default(); + block_header.map(|h| h.hash.clone()).unwrap_or_default().to_hex() + }, + ); + b + }, + Err(MmProxyError::FailedPreconditionBlockLostRetry) => { + debug!( + target: LOG_TARGET, + "Chain tip has progressed past template height {}. Fetching a new block template.", + new_template.template.header.as_ref().map(|h| h.height).unwrap_or(0) + ); + continue; + }, + Err(err) => return Err(err), + }; - let template_height = new_template.template.header.as_ref().map(|h| h.height).unwrap_or(0); - if !self.check_expected_tip(template_height).await? { + ( + add_monero_data(block, monero_mining_data.clone(), new_template)?, + height, + ) + }; + + if !self.check_expected_tip(block_height).await? { debug!( target: LOG_TARGET, - "Chain tip has progressed past template height {}. Fetching a new block template.", template_height + "Chain tip has progressed past template height {}. Fetching a new block template.", block_height ); continue; } - - debug!(target: LOG_TARGET, "Added coinbase to new block template"); - let block_template_with_coinbase = - merge_mining::add_coinbase(&coinbase_output, &coinbase_kernel, new_template.template.clone())?; - info!( - target: LOG_TARGET, - "Received new block template from Minotari base node for height #{}", - new_template - .template + info!(target: LOG_TARGET, + "Block template for height: #{}, block hash: `{}`, {}", + final_template_data + .template.new_block_template .header .as_ref() .map(|h| h.height) .unwrap_or_default(), - ); - let block = match self.get_new_block(block_template_with_coinbase).await { - Ok(b) => b, - Err(MmProxyError::FailedPreconditionBlockLostRetry) => { - debug!( - target: LOG_TARGET, - "Chain tip has progressed past template height {}. Fetching a new block template.", - template_height - ); - continue; + match final_template_data.template.tari_block.header.as_ref() { + Some(h) => h.hash.to_hex(), + None => "None".to_string(), }, - Err(err) => return Err(err), - }; - - let final_block = self.add_monero_data(block, monero_mining_data, new_template)?; - return Ok(final_block); + match final_template_data.template.tari_block.body.as_ref() { + Some(b) => format!( + "inputs: `{}`, outputs: `{}`, kernels: `{}`", + b.inputs.len(), b.outputs.len(), b.kernels.len() + ), + None => "inputs: `0`, outputs: `0`, kernels: `0`".to_string(), + } + ); + return Ok(final_template_data); } } @@ -147,6 +204,7 @@ impl BlockTemplateProtocol<'_> { miner_data, new_block_template: template, initial_sync_achieved, + best_previous_block_hash, } = self .base_node_client .get_new_block_template(grpc::NewBlockTemplateRequest { @@ -168,6 +226,7 @@ impl BlockTemplateProtocol<'_> { template, miner_data, initial_sync_achieved, + best_previous_block_hash, }) } @@ -217,77 +276,101 @@ impl BlockTemplateProtocol<'_> { .await?; Ok((coinbase_output, coinbase_kernel)) } +} - /// Build the [FinalBlockTemplateData] from [template](NewBlockTemplateData) and with - /// [tari](grpc::GetNewBlockResult) and [monero data](MoneroMiningData). - fn add_monero_data( - &self, - tari_block: grpc::GetNewBlockResult, - monero_mining_data: MoneroMiningData, - template_data: NewBlockTemplateData, - ) -> Result { - debug!(target: LOG_TARGET, "New block received from Minotari: {:?}", tari_block); +/// This is an interim solution to calculate the merkle root for the aux chains when multiple aux chains will be +/// merge mined with Monero. It needs to be replaced with a more general solution in the future. +pub fn calculate_aux_chain_merkle_root(hashes: Vec) -> Result<(monero::Hash, u32), MmProxyError> { + if hashes.is_empty() { + Err(MmProxyError::MissingDataError( + "No aux chain hashes provided".to_string(), + )) + } else if hashes.len() == 1 { + Ok((hashes[0], 0)) + } else { + unimplemented!("Multiple aux chains for Monero is not supported yet, only Tari."); + } +} + +/// Build the [FinalBlockTemplateData] from [template](NewBlockTemplateData) and with +/// [tari](grpc::GetNewBlockResult) and [monero data](MoneroMiningData). +fn add_monero_data( + tari_block_result: grpc::GetNewBlockResult, + monero_mining_data: MoneroMiningData, + template_data: NewBlockTemplateData, +) -> Result { + let merge_mining_hash = FixedHash::try_from(tari_block_result.merge_mining_hash.clone()) + .map_err(|e| MmProxyError::ConversionError(e.to_string()))?; + let tari_block_grpc = tari_block_result + .block + .as_ref() + .ok_or(MmProxyError::GrpcResponseMissingField("block"))?; + let tari_header_grpc = tari_block_grpc + .header + .as_ref() + .ok_or(MmProxyError::GrpcResponseMissingField("header"))?; + let tari_header: BlockHeader = tari_header_grpc + .clone() + .try_into() + .map_err(MmProxyError::ConversionError)?; + debug!( + target: LOG_TARGET, + "merge_mining_hash: `{}`, tari_header.merge_mining_hash(): `{}`", + hex::encode(merge_mining_hash), tari_header.merge_mining_hash().to_hex() + ); + + let aux_chain_hashes = vec![monero::Hash::from_slice(merge_mining_hash.as_slice())]; + let tari_difficulty = template_data.miner_data.target_difficulty; + let block_template_data = BlockTemplateDataBuilder::new() + .tari_block( + tari_block_result + .block + .ok_or(MmProxyError::GrpcResponseMissingField("block"))?, + ) + .tari_miner_data(template_data.miner_data) + .monero_seed(monero_mining_data.seed_hash) + .monero_difficulty(monero_mining_data.difficulty) + .tari_difficulty(tari_difficulty) + .tari_merge_mining_hash(merge_mining_hash) + .aux_hashes(aux_chain_hashes.clone()) + .new_block_template(template_data.template) + .best_previous_block_hash( + FixedHash::try_from(template_data.best_previous_block_hash) + .map_err(|e| MmProxyError::ConversionError(e.to_string()))?, + ) + .build()?; - let tari_difficulty = template_data.miner_data.target_difficulty; - let block_template_data = BlockTemplateDataBuilder::new() - .tari_block( - tari_block - .block - .ok_or(MmProxyError::GrpcResponseMissingField("block"))?, - ) - .tari_miner_data(template_data.miner_data) - .monero_seed(monero_mining_data.seed_hash) - .monero_difficulty(monero_mining_data.difficulty) - .tari_difficulty(tari_difficulty) - .tari_hash( - FixedHash::try_from(tari_block.merge_mining_hash.clone()) - .map_err(|e| MmProxyError::MissingDataError(e.to_string()))?, - ) - .aux_hashes(vec![monero::Hash::from_slice(&tari_block.merge_mining_hash)]) - .build()?; + // Deserialize the block template blob + debug!(target: LOG_TARGET, "Deseriale Monero block template blob into Monero block",); + let mut monero_block = monero_rx::deserialize_monero_block_from_hex(&monero_mining_data.blocktemplate_blob)?; - // Deserialize the block template blob - debug!(target: LOG_TARGET, "Deserializing Blocktemplate Blob into Monero Block",); - let mut monero_block = monero_rx::deserialize_monero_block_from_hex(&monero_mining_data.blocktemplate_blob)?; + debug!(target: LOG_TARGET, "Insert aux chain merkle root (merge_mining_hash) into Monero block"); + let aux_chain_mr = calculate_aux_chain_merkle_root(aux_chain_hashes.clone())?.0; + monero_rx::insert_aux_chain_mr_and_info_into_block(&mut monero_block, aux_chain_mr.to_bytes(), 1, 0)?; - debug!(target: LOG_TARGET, "Insert Merged Mining Tag",); - // Add the Tari merge mining tag to the retrieved block template - // We need to send the MR al all aux chains, but a single chain, aka minotari only, means we only need the tari - // hash - let aux_chain_mr = tari_block.merge_mining_hash.clone(); - monero_rx::insert_merge_mining_tag_and_aux_chain_merkle_root_into_block( - &mut monero_block, - &aux_chain_mr, - 1, - 0, - )?; + debug!(target: LOG_TARGET, "Create blockhashing blob from blocktemplate blob",); + // Must be done after the aux_chain_mr is inserted since it will affect the hash of the miner tx + let blockhashing_blob = monero_rx::create_blockhashing_blob_from_block(&monero_block)?; + let blocktemplate_blob = monero_rx::serialize_monero_block_to_hex(&monero_block)?; - debug!(target: LOG_TARGET, "Creating blockhashing blob from blocktemplate blob",); - // Must be done after the tag is inserted since it will affect the hash of the miner tx - let blockhashing_blob = monero_rx::create_blockhashing_blob_from_block(&monero_block)?; - let blocktemplate_blob = monero_rx::serialize_monero_block_to_hex(&monero_block)?; + let monero_difficulty = monero_mining_data.difficulty; + let mining_difficulty = cmp::min(monero_difficulty, tari_difficulty); + info!( + target: LOG_TARGET, + "Difficulties: Minotari ({}), Monero({}), Selected({})", + tari_difficulty, + monero_mining_data.difficulty, + mining_difficulty + ); - let monero_difficulty = monero_mining_data.difficulty; - let mining_difficulty = cmp::min(monero_difficulty, tari_difficulty); - info!( - target: LOG_TARGET, - "Difficulties: Minotari ({}), Monero({}), Selected({})", - tari_difficulty, - monero_mining_data.difficulty, - mining_difficulty - ); - let merge_mining_hash = FixedHash::try_from(tari_block.merge_mining_hash.clone()) - .map_err(|e| MmProxyError::MissingDataError(e.to_string()))?; - Ok(FinalBlockTemplateData { - template: block_template_data, - target_difficulty: Difficulty::from_u64(mining_difficulty)?, - blockhashing_blob, - blocktemplate_blob, - merge_mining_hash, - aux_chain_hashes: vec![monero::Hash::from_slice(&tari_block.merge_mining_hash)], - aux_chain_mr: tari_block.merge_mining_hash, - }) - } + Ok(FinalBlockTemplateData { + template: block_template_data, + target_difficulty: Difficulty::from_u64(mining_difficulty)?, + blockhashing_blob, + blocktemplate_blob, + aux_chain_hashes, + aux_chain_mr: aux_chain_mr.to_bytes().to_vec(), + }) } /// Private convenience container struct for new template data @@ -296,6 +379,7 @@ struct NewBlockTemplateData { pub template: grpc::NewBlockTemplate, pub miner_data: grpc::MinerData, pub initial_sync_achieved: bool, + pub best_previous_block_hash: Vec, } impl NewBlockTemplateData { @@ -305,17 +389,18 @@ impl NewBlockTemplateData { } /// Final outputs for required for merge mining +#[derive(Debug, Clone)] pub struct FinalBlockTemplateData { pub template: BlockTemplateData, pub target_difficulty: Difficulty, pub blockhashing_blob: String, pub blocktemplate_blob: String, - pub merge_mining_hash: FixedHash, pub aux_chain_hashes: Vec, pub aux_chain_mr: Vec, } /// Container struct for monero mining data inputs obtained from monerod +#[derive(Clone)] pub struct MoneroMiningData { pub seed_hash: FixedByteArray, pub blocktemplate_blob: String, diff --git a/applications/minotari_merge_mining_proxy/src/cli.rs b/applications/minotari_merge_mining_proxy/src/cli.rs index f2cf7faa04..14e42769cd 100644 --- a/applications/minotari_merge_mining_proxy/src/cli.rs +++ b/applications/minotari_merge_mining_proxy/src/cli.rs @@ -35,9 +35,9 @@ pub struct Cli { } impl ConfigOverrideProvider for Cli { - fn get_config_property_overrides(&self, default_network: Network) -> Vec<(String, String)> { - let mut overrides = self.common.get_config_property_overrides(default_network); - let network = self.common.network.unwrap_or(default_network); + fn get_config_property_overrides(&self, network: &mut Network) -> Vec<(String, String)> { + let mut overrides = self.common.get_config_property_overrides(network); + *network = self.common.network.unwrap_or(*network); overrides.push(("merge_mining_proxy.override_from".to_string(), network.to_string())); overrides.push(("merge_mining_proxy.network".to_string(), network.to_string())); overrides diff --git a/applications/minotari_merge_mining_proxy/src/error.rs b/applications/minotari_merge_mining_proxy/src/error.rs index 27b81a8ca2..2b3ad90a55 100644 --- a/applications/minotari_merge_mining_proxy/src/error.rs +++ b/applications/minotari_merge_mining_proxy/src/error.rs @@ -111,6 +111,8 @@ pub enum MmProxyError { ParseInputError(#[from] ParseInputError), #[error("Base node not responding to gRPC requests: {0}")] BaseNodeNotResponding(String), + #[error("Unexpected missing data: {0}")] + UnexpectedMissingData(String), } impl From for MmProxyError { diff --git a/applications/minotari_merge_mining_proxy/src/proxy.rs b/applications/minotari_merge_mining_proxy/src/proxy.rs index 4c9edf8aba..bd2e71b0ad 100644 --- a/applications/minotari_merge_mining_proxy/src/proxy.rs +++ b/applications/minotari_merge_mining_proxy/src/proxy.rs @@ -22,7 +22,7 @@ use std::{ cmp, - convert::TryInto, + convert::{TryFrom, TryInto}, future::Future, pin::Pin, sync::{ @@ -43,7 +43,7 @@ use minotari_app_utilities::parse_miner_input::BaseNodeGrpcClient; use minotari_node_grpc_client::grpc; use reqwest::{ResponseBuilderExt, Url}; use serde_json as json; -use tari_common_types::tari_address::TariAddress; +use tari_common_types::{tari_address::TariAddress, types::FixedHash}; use tari_core::{ consensus::ConsensusManager, proof_of_work::{monero_rx, monero_rx::FixedByteArray, randomx_difficulty, randomx_factory::RandomXFactory}, @@ -226,7 +226,6 @@ impl InnerService { let (parts, mut json_resp) = monerod_resp.into_parts(); debug!(target: LOG_TARGET, "handle_submit_block: submit request #{}", request); - debug!(target: LOG_TARGET, "Params received: #{:?}", request["params"]); let params = match request["params"].as_array() { Some(v) => v, None => { @@ -242,8 +241,6 @@ impl InnerService { }, }; - let gen_hash = *self.consensus_manager.get_genesis_block().hash(); - for param in params.iter().filter_map(|p| p.as_str()) { let monero_block = monero_rx::deserialize_monero_block_from_hex(param)?; debug!(target: LOG_TARGET, "Monero block: {}", monero_block); @@ -270,36 +267,53 @@ impl InnerService { }; let monero_data = monero_rx::construct_monero_data( monero_block, - block_data.monero_seed.clone(), + block_data.template.monero_seed.clone(), block_data.aux_chain_hashes.clone(), - block_data.tari_hash, + block_data.template.tari_merge_mining_hash, )?; debug!(target: LOG_TARGET, "Monero PoW Data: {:?}", monero_data); - let header_mut = block_data.tari_block.header.as_mut().unwrap(); - let height = header_mut.height; - BorshSerialize::serialize(&monero_data, &mut header_mut.pow.as_mut().unwrap().pow_data) + let tari_header_mut = block_data + .template + .tari_block + .header + .as_mut() + .ok_or(MmProxyError::UnexpectedMissingData("tari_block.header".to_string()))?; + let pow_mut = tari_header_mut + .pow + .as_mut() + .ok_or(MmProxyError::UnexpectedMissingData("tari_block.header.pow".to_string()))?; + BorshSerialize::serialize(&monero_data, &mut pow_mut.pow_data) .map_err(|err| MmProxyError::ConversionError(err.to_string()))?; - let tari_header = header_mut.clone().try_into().map_err(MmProxyError::ConversionError)?; + let tari_header = tari_header_mut + .clone() + .try_into() + .map_err(MmProxyError::ConversionError)?; let mut base_node_client = self.base_node_client.clone(); let start = Instant::now(); let achieved_target = if self.config.check_tari_difficulty_before_submit { trace!(target: LOG_TARGET, "Starting calculate achieved Tari difficultly"); - let diff = randomx_difficulty(&tari_header, &self.randomx_factory, &gen_hash, &self.consensus_manager)?; + let diff = randomx_difficulty( + &tari_header, + &self.randomx_factory, + self.consensus_manager.get_genesis_block().hash(), + &self.consensus_manager, + )?; trace!( target: LOG_TARGET, "Finished calculate achieved Tari difficultly - achieved {} vs. target {}", - diff.as_u64(), - block_data.tari_difficulty + diff, + block_data.template.tari_difficulty ); diff.as_u64() } else { - block_data.tari_difficulty + block_data.template.tari_difficulty }; - if achieved_target >= block_data.tari_difficulty { - match base_node_client.submit_block(block_data.tari_block).await { + let height = tari_header_mut.height; + if achieved_target >= block_data.template.tari_difficulty { + match base_node_client.submit_block(block_data.template.tari_block).await { Ok(resp) => { if self.config.submit_to_origin { json_resp = json_rpc::success_response( @@ -361,6 +375,21 @@ impl InnerService { Ok(proxy::into_response(parts, &json_resp)) } + async fn get_current_best_block_hash(&self) -> Result { + let tip = self + .base_node_client + .clone() + .get_tip_info(grpc::Empty {}) + .await? + .into_inner(); + let best_block_hash = tip + .metadata + .as_ref() + .map(|m| m.best_block_hash.clone()) + .unwrap_or(Vec::default()); + FixedHash::try_from(best_block_hash).map_err(|e| MmProxyError::ConversionError(e.to_string())) + } + #[allow(clippy::too_many_lines)] async fn handle_get_block_template( &self, @@ -404,7 +433,6 @@ impl InnerService { let mut grpc_client = self.base_node_client.clone(); // Add merge mining tag on blocktemplate request - debug!(target: LOG_TARGET, "Requested new block template from Minotari base node"); if !self.initial_sync_achieved.load(Ordering::SeqCst) { let grpc::TipInfoResponse { initial_sync_achieved, @@ -455,12 +483,21 @@ impl InnerService { difficulty, }; - let final_block_template_data = new_block_protocol.get_next_block_template(monero_mining_data).await?; + let existing_block_template = if let Ok(best_block_hash) = self.get_current_best_block_hash().await { + self.block_templates.contains(best_block_hash).await + } else { + None + }; + + let final_block_template_data = new_block_protocol + .get_next_block_template(monero_mining_data, existing_block_template) + .await?; - monerod_resp["result"]["blocktemplate_blob"] = final_block_template_data.blocktemplate_blob.into(); - monerod_resp["result"]["blockhashing_blob"] = final_block_template_data.blockhashing_blob.into(); + monerod_resp["result"]["blocktemplate_blob"] = final_block_template_data.blocktemplate_blob.clone().into(); + monerod_resp["result"]["blockhashing_blob"] = final_block_template_data.blockhashing_blob.clone().into(); monerod_resp["result"]["difficulty"] = final_block_template_data.target_difficulty.as_u64().into(); + let tari_difficulty = final_block_template_data.template.tari_difficulty; let tari_height = final_block_template_data .template .tari_block @@ -468,9 +505,9 @@ impl InnerService { .as_ref() .map(|h| h.height) .unwrap_or(0); + let aux_chain_mr = hex::encode(final_block_template_data.aux_chain_mr.clone()); let block_reward = final_block_template_data.template.tari_miner_data.reward; let total_fees = final_block_template_data.template.tari_miner_data.total_fees; - let mining_hash = final_block_template_data.merge_mining_hash; let monerod_resp = add_aux_data( monerod_resp, json!({ "base_difficulty": final_block_template_data.template.monero_difficulty }), @@ -479,18 +516,19 @@ impl InnerService { monerod_resp, json!({ "id": TARI_CHAIN_ID, - "difficulty": final_block_template_data.template.tari_difficulty, + "difficulty": tari_difficulty, "height": tari_height, - // The merge mining hash, before the final block hash can be calculated - "mining_hash": mining_hash.to_hex(), + // The aux chain merkle root, before the final block hash can be calculated + "mining_hash": aux_chain_mr, "miner_reward": block_reward + total_fees, }), ); self.block_templates - .save( - final_block_template_data.aux_chain_mr, - final_block_template_data.template, + .save_if_key_unique( + // `aux_chain_mr` is used as the key because it is stored in the ExtraData field in the Monero block + final_block_template_data.aux_chain_mr.clone(), + final_block_template_data, ) .await; diff --git a/applications/minotari_miner/src/cli.rs b/applications/minotari_miner/src/cli.rs index d1c976eff1..1c5f339acc 100644 --- a/applications/minotari_miner/src/cli.rs +++ b/applications/minotari_miner/src/cli.rs @@ -42,9 +42,9 @@ pub struct Cli { } impl ConfigOverrideProvider for Cli { - fn get_config_property_overrides(&self, default_network: Network) -> Vec<(String, String)> { - let mut overrides = self.common.get_config_property_overrides(default_network); - let network = self.common.network.unwrap_or(default_network); + fn get_config_property_overrides(&self, network: &mut Network) -> Vec<(String, String)> { + let mut overrides = self.common.get_config_property_overrides(network); + *network = self.common.network.unwrap_or(*network); overrides.push(("miner.network".to_string(), network.to_string())); overrides } diff --git a/applications/minotari_node/src/cli.rs b/applications/minotari_node/src/cli.rs index f6de3e7c9f..d180fed2e5 100644 --- a/applications/minotari_node/src/cli.rs +++ b/applications/minotari_node/src/cli.rs @@ -52,9 +52,9 @@ pub struct Cli { } impl ConfigOverrideProvider for Cli { - fn get_config_property_overrides(&self, default_network: Network) -> Vec<(String, String)> { - let mut overrides = self.common.get_config_property_overrides(default_network); - let network = self.common.network.unwrap_or(default_network); + fn get_config_property_overrides(&self, network: &mut Network) -> Vec<(String, String)> { + let mut overrides = self.common.get_config_property_overrides(network); + *network = self.common.network.unwrap_or(*network); overrides.push(("base_node.network".to_string(), network.to_string())); overrides.push(("base_node.override_from".to_string(), network.to_string())); overrides.push(("p2p.seeds.override_from".to_string(), network.to_string())); diff --git a/applications/minotari_node/src/grpc/base_node_grpc_server.rs b/applications/minotari_node/src/grpc/base_node_grpc_server.rs index e01bdbe248..28384122db 100644 --- a/applications/minotari_node/src/grpc/base_node_grpc_server.rs +++ b/applications/minotari_node/src/grpc/base_node_grpc_server.rs @@ -570,6 +570,14 @@ impl tari_rpc::base_node_server::BaseNode for BaseNodeGrpcServer { ); obscure_error_if_true(report_error_flag, Status::internal(e.to_string())) })?; + let metadata = handler.get_metadata().await.map_err(|e| { + warn!( + target: LOG_TARGET, + "Could not get metadata for new block template: {}", + e.to_string() + ); + obscure_error_if_true(report_error_flag, Status::internal(e.to_string())) + })?; let status_watch = self.state_machine_handle.get_status_info_watch(); let pow = algo as i32; @@ -585,8 +593,8 @@ impl tari_rpc::base_node_server::BaseNode for BaseNodeGrpcServer { .try_into() .map_err(|e| obscure_error_if_true(report_error_flag, Status::internal(e)))?, ), - initial_sync_achieved: status_watch.borrow().bootstrapped, + best_previous_block_hash: metadata.best_block_hash().to_vec(), }; debug!(target: LOG_TARGET, "Sending GetNewBlockTemplate response to client"); diff --git a/base_layer/core/src/base_node/comms_interface/inbound_handlers.rs b/base_layer/core/src/base_node/comms_interface/inbound_handlers.rs index d5fd4f932b..3a092bb022 100644 --- a/base_layer/core/src/base_node/comms_interface/inbound_handlers.rs +++ b/base_layer/core/src/base_node/comms_interface/inbound_handlers.rs @@ -292,33 +292,38 @@ where B: BlockchainBackend + 'static let prev_hash = header.prev_hash; let height = header.height; + let block = header.into_builder().with_transactions(transactions).build(); + let block_hash = block.hash(); let block_template = NewBlockTemplate::from_block( - header.into_builder().with_transactions(transactions).build(), + block, self.get_target_difficulty_for_next_block(request.algo, constants, prev_hash) .await?, self.consensus_manager.get_block_reward_at(height), )?; - debug!(target: LOG_TARGET, "New template block: {}", block_template); - debug!( - target: LOG_TARGET, - "New block template requested at height {}, weight: {}", + debug!(target: LOG_TARGET, + "New block template requested and prepared at height: #{}, target difficulty: {}, block hash: `{}`, weight: {}, {}", block_template.header.height, + block_template.target_difficulty, + block_hash.to_hex(), block_template .body .calculate_weight(constants.transaction_weight_params()) - .map_err(|e| CommsInterfaceError::InternalError(e.to_string()))? + .map_err(|e| CommsInterfaceError::InternalError(e.to_string()))?, + block_template.body.to_counts_string() ); - trace!(target: LOG_TARGET, "{}", block_template); + Ok(NodeCommsResponse::NewBlockTemplate(block_template)) }, NodeCommsRequest::GetNewBlock(block_template) => { - debug!(target: LOG_TARGET, "Prepared block: {}", block_template); + let height = block_template.header.height; + let target_difficulty = block_template.target_difficulty; let block = self.blockchain_db.prepare_new_block(block_template).await?; let constants = self.consensus_manager.consensus_constants(block.header.height); - debug!( - target: LOG_TARGET, - "Prepared new block from template (hash: {}, weight: {}, {})", + debug!(target: LOG_TARGET, + "Prepared block: #{}, target difficulty: {}, block hash: `{}`, weight: {}, {}", + height, + target_difficulty, block.hash().to_hex(), block .body diff --git a/base_layer/core/src/consensus/consensus_encoding/hashing.rs b/base_layer/core/src/consensus/consensus_encoding/hashing.rs index 6418f280b3..b215268af5 100644 --- a/base_layer/core/src/consensus/consensus_encoding/hashing.rs +++ b/base_layer/core/src/consensus/consensus_encoding/hashing.rs @@ -42,7 +42,7 @@ where D: Default { #[allow(clippy::new_ret_no_self)] pub fn new(label: &'static str) -> ConsensusHasher { - Self::new_with_network(label, Network::get_current_or_default()) + Self::new_with_network(label, Network::get_current_or_user_setting_or_default()) } pub fn new_with_network(label: &'static str, network: Network) -> ConsensusHasher { @@ -174,7 +174,7 @@ mod tests { #[test] fn it_hashes_using_the_domain_hasher() { - let network = Network::get_current_or_default(); + let network = Network::get_current_or_user_setting_or_default(); // Script is chosen because the consensus encoding impl for TariScript has 2 writes let mut hasher = Blake2b::::default(); @@ -190,7 +190,7 @@ mod tests { #[test] fn it_adds_to_hash_challenge_in_complete_chunks() { - let network = Network::get_current_or_default(); + let network = Network::get_current_or_user_setting_or_default(); // Script is chosen because the consensus encoding impl for TariScript has 2 writes let test_subject = script!(Nop); @@ -215,4 +215,35 @@ mod tests { assert_ne!(blake_hash.as_slice(), default_consensus_hash.as_slice()); } + + #[test] + fn it_uses_the_network_environment_variable_if_set() { + let label = "test"; + let input = [1u8; 32]; + + for network in [ + Network::MainNet, + Network::StageNet, + Network::NextNet, + Network::LocalNet, + Network::Igor, + Network::Esmeralda, + ] { + // Generate a specific network hash + let hash_specify_network = + DomainSeparatedConsensusHasher::>::new_with_network(label, network) + .chain(&input) + .finalize(); + + // Generate an inferred network hash + std::env::set_var("TARI_NETWORK", network.as_key_str()); + let inferred_network_hash = DomainSeparatedConsensusHasher::>::new(label) + .chain(&input) + .finalize(); + std::env::remove_var("TARI_NETWORK"); + + // They should be equal + assert_eq!(hash_specify_network, inferred_network_hash); + } + } } diff --git a/base_layer/core/src/proof_of_work/monero_rx/helpers.rs b/base_layer/core/src/proof_of_work/monero_rx/helpers.rs index 0ec008c626..dc4aea7399 100644 --- a/base_layer/core/src/proof_of_work/monero_rx/helpers.rs +++ b/base_layer/core/src/proof_of_work/monero_rx/helpers.rs @@ -57,10 +57,10 @@ pub const LOG_TARGET: &str = "c::pow::monero_rx"; pub fn randomx_difficulty( header: &BlockHeader, randomx_factory: &RandomXFactory, - gen_hash: &FixedHash, + genesis_block_hash: &FixedHash, consensus: &ConsensusManager, ) -> Result { - let monero_pow_data = verify_header(header, gen_hash, consensus)?; + let monero_pow_data = verify_header(header, genesis_block_hash, consensus)?; debug!(target: LOG_TARGET, "Valid Monero data: {}", monero_pow_data); let blockhashing_blob = monero_pow_data.to_blockhashing_blob(); let vm = randomx_factory.create(monero_pow_data.randomx_key())?; @@ -100,7 +100,7 @@ fn parse_extra_field_truncate_on_error(raw_extra_field: &RawExtraField) -> Extra /// If these assertions pass, a valid `MoneroPowData` instance is returned pub fn verify_header( header: &BlockHeader, - gen_hash: &FixedHash, + genesis_block_hash: &FixedHash, consensus: &ConsensusManager, ) -> Result { let monero_data = MoneroPowData::from_header(header, consensus)?; @@ -110,6 +110,7 @@ pub fn verify_header( warn!(target: LOG_TARGET, "Error deserializing, Monero extra field"); ex_field }); + debug!(target: LOG_TARGET, "Extra field: {:?}", extra_field); // Check that the Tari MM hash is found in the Monero coinbase transaction // and that only 1 Tari header is found @@ -128,7 +129,7 @@ pub fn verify_header( depth, &merge_mining_hash, &expected_merge_mining_hash, - gen_hash, + genesis_block_hash, ) } } @@ -303,21 +304,21 @@ pub fn create_ordered_transaction_hashes_from_block(block: &monero::Block) -> Ve .collect() } -/// Inserts merge mining hash into a Monero block -pub fn insert_merge_mining_tag_and_aux_chain_merkle_root_into_block>( +/// Inserts aux chain merkle root and info into a Monero block +pub fn insert_aux_chain_mr_and_info_into_block>( block: &mut monero::Block, - hash: T, + aux_chain_mr: T, aux_chain_count: u8, aux_nonce: u32, ) -> Result<(), MergeMineError> { if aux_chain_count == 0 { return Err(MergeMineError::ZeroAuxChains); } - if hash.as_ref().len() != monero::Hash::len_bytes() { + if aux_chain_mr.as_ref().len() != monero::Hash::len_bytes() { return Err(MergeMineError::HashingError(format!( "Expected source to be {} bytes, but it was {} bytes", monero::Hash::len_bytes(), - hash.as_ref().len() + aux_chain_mr.as_ref().len() ))); } // When we insert the merge mining tag, we need to make sure that the extra field is valid. @@ -337,17 +338,18 @@ pub fn insert_merge_mining_tag_and_aux_chain_merkle_root_into_block(&bytes[..]).unwrap(); let block_header = BlockHeader::new(0); let hash = block_header.merge_mining_hash(); - insert_merge_mining_tag_and_aux_chain_merkle_root_into_block(&mut block, hash, 1, 0).unwrap(); + insert_aux_chain_mr_and_info_into_block(&mut block, hash, 1, 0).unwrap(); let coinbase = block.miner_tx.clone(); let extra = coinbase.prefix.extra; @@ -561,7 +563,7 @@ mod test { validator_node_size: 0, }; let hash = block_header.merge_mining_hash(); - insert_merge_mining_tag_and_aux_chain_merkle_root_into_block(&mut block, hash, 1, 0).unwrap(); + insert_aux_chain_mr_and_info_into_block(&mut block, hash, 1, 0).unwrap(); let hashes = create_ordered_transaction_hashes_from_block(&block); assert_eq!(hashes.len(), block.tx_hashes.len() + 1); let root = tree_hash(&hashes).unwrap(); @@ -663,7 +665,7 @@ mod test { validator_node_size: 0, }; let hash = block_header.merge_mining_hash(); - insert_merge_mining_tag_and_aux_chain_merkle_root_into_block(&mut block, hash, 1, 0).unwrap(); + insert_aux_chain_mr_and_info_into_block(&mut block, hash, 1, 0).unwrap(); let count = 1 + (u16::try_from(block.tx_hashes.len()).unwrap()); let mut hashes = Vec::with_capacity(count as usize); hashes.push(block.miner_tx.hash()); @@ -809,7 +811,7 @@ mod test { validator_node_size: 0, }; let hash = Hash::null(); - insert_merge_mining_tag_and_aux_chain_merkle_root_into_block(&mut block, hash, 1, 0).unwrap(); + insert_aux_chain_mr_and_info_into_block(&mut block, hash, 1, 0).unwrap(); let count = 1 + (u16::try_from(block.tx_hashes.len()).unwrap()); let mut hashes = Vec::with_capacity(count as usize); let mut proof = Vec::with_capacity(count as usize); @@ -885,7 +887,7 @@ mod test { validator_node_size: 0, }; let hash = block_header.merge_mining_hash(); - insert_merge_mining_tag_and_aux_chain_merkle_root_into_block(&mut block, hash, 1, 0).unwrap(); + insert_aux_chain_mr_and_info_into_block(&mut block, hash, 1, 0).unwrap(); #[allow(clippy::redundant_clone)] let mut block_header2 = block_header.clone(); block_header2.version = 1; @@ -893,7 +895,7 @@ mod test { assert!(extract_aux_merkle_root_from_block(&block).is_ok()); // Try via the API - this will fail because more than one merge mining tag is not allowed - assert!(insert_merge_mining_tag_and_aux_chain_merkle_root_into_block(&mut block, hash2, 1, 0).is_err()); + assert!(insert_aux_chain_mr_and_info_into_block(&mut block, hash2, 1, 0).is_err()); // Now bypass the API - this will effectively allow us to insert more than one merge mining tag, // like trying to sneek it in. Later on, when we call `verify_header(&block_header)`, it should fail. @@ -1000,7 +1002,7 @@ mod test { // Now insert the merge mining tag - this would also clean up the extra field and remove the invalid sub-fields let hash = block_header.merge_mining_hash(); - insert_merge_mining_tag_and_aux_chain_merkle_root_into_block(&mut block, hash, 1, 0).unwrap(); + insert_aux_chain_mr_and_info_into_block(&mut block, hash, 1, 0).unwrap(); assert!(ExtraField::try_parse(&block.miner_tx.prefix.extra.clone()).is_ok()); // Verify that the merge mining tag is there @@ -1041,7 +1043,7 @@ mod test { validator_node_size: 0, }; let hash = block_header.merge_mining_hash(); - insert_merge_mining_tag_and_aux_chain_merkle_root_into_block(&mut block, hash, 1, 0).unwrap(); + insert_aux_chain_mr_and_info_into_block(&mut block, hash, 1, 0).unwrap(); let count = 1 + (u16::try_from(block.tx_hashes.len()).unwrap()); let mut hashes = Vec::with_capacity(count as usize); let mut proof = Vec::with_capacity(count as usize); @@ -1174,7 +1176,7 @@ mod test { validator_node_size: 0, }; let hash = block_header.merge_mining_hash(); - insert_merge_mining_tag_and_aux_chain_merkle_root_into_block(&mut block, hash, 1, 0).unwrap(); + insert_aux_chain_mr_and_info_into_block(&mut block, hash, 1, 0).unwrap(); let count = 1 + (u16::try_from(block.tx_hashes.len()).unwrap()); let mut hashes = Vec::with_capacity(count as usize); let mut proof = Vec::with_capacity(count as usize); diff --git a/base_layer/core/src/proof_of_work/monero_rx/mod.rs b/base_layer/core/src/proof_of_work/monero_rx/mod.rs index 7d29264add..24701323e0 100644 --- a/base_layer/core/src/proof_of_work/monero_rx/mod.rs +++ b/base_layer/core/src/proof_of_work/monero_rx/mod.rs @@ -29,7 +29,7 @@ pub use helpers::{ create_ordered_transaction_hashes_from_block, deserialize_monero_block_from_hex, extract_aux_merkle_root_from_block, - insert_merge_mining_tag_and_aux_chain_merkle_root_into_block, + insert_aux_chain_mr_and_info_into_block, randomx_difficulty, serialize_monero_block_to_hex, verify_header, diff --git a/base_layer/core/src/transactions/aggregated_body.rs b/base_layer/core/src/transactions/aggregated_body.rs index 69b7a75887..1c6fce86c5 100644 --- a/base_layer/core/src/transactions/aggregated_body.rs +++ b/base_layer/core/src/transactions/aggregated_body.rs @@ -351,7 +351,7 @@ impl AggregateBody { /// Lists the number of inputs, outputs, and kernels in the block pub fn to_counts_string(&self) -> String { format!( - "{} input(s), {} output(s), {} kernel(s)", + "input(s): {}, output(s): {}, kernel(s): {}", self.inputs.len(), self.outputs.len(), self.kernels.len() diff --git a/base_layer/core/src/transactions/transaction_components/test.rs b/base_layer/core/src/transactions/transaction_components/test.rs index 66328e9dc0..067ca86aef 100644 --- a/base_layer/core/src/transactions/transaction_components/test.rs +++ b/base_layer/core/src/transactions/transaction_components/test.rs @@ -281,7 +281,7 @@ async fn sender_signature_verification() { #[test] fn kernel_hash() { #[cfg(tari_target_network_mainnet)] - if let Network::MainNet = Network::get_current_or_default() { + if let Network::MainNet = Network::get_current_or_user_setting_or_default() { eprintln!("This test is configured for stagenet only"); return; } @@ -327,7 +327,7 @@ fn kernel_metadata() { .build() .unwrap(); #[cfg(tari_target_network_mainnet)] - match Network::get_current_or_default() { + match Network::get_current_or_user_setting_or_default() { Network::MainNet => { eprintln!("This test is configured for stagenet only"); }, diff --git a/base_layer/core/tests/tests/block_validation.rs b/base_layer/core/tests/tests/block_validation.rs index 891c1d57bd..f2fe92c946 100644 --- a/base_layer/core/tests/tests/block_validation.rs +++ b/base_layer/core/tests/tests/block_validation.rs @@ -131,19 +131,19 @@ async fn test_monero_blocks() { let mut block_1 = db.prepare_new_block(block_1_t).unwrap(); // Now we have block 1, lets add monero data to it - add_monero_data(&mut block_1, seed1); + add_monero_test_data(&mut block_1, seed1); let cb_1 = assert_block_add_result_added(&db.add_block(Arc::new(block_1)).unwrap()); // Now lets add a second faulty block using the same seed hash let (block_2_t, _) = chain_block_with_new_coinbase(&cb_1, vec![], &cm, None, &key_manager).await; let mut block_2 = db.prepare_new_block(block_2_t).unwrap(); - add_monero_data(&mut block_2, seed1); + add_monero_test_data(&mut block_2, seed1); let cb_2 = assert_block_add_result_added(&db.add_block(Arc::new(block_2)).unwrap()); // Now lets add a third faulty block using the same seed hash. This should fail. let (block_3_t, _) = chain_block_with_new_coinbase(&cb_2, vec![], &cm, None, &key_manager).await; let mut block_3 = db.prepare_new_block(block_3_t).unwrap(); let mut block_3_broken = block_3.clone(); - add_monero_data(&mut block_3_broken, seed1); + add_monero_test_data(&mut block_3_broken, seed1); match db.add_block(Arc::new(block_3_broken)) { Err(ChainStorageError::ValidationError { source: ValidationError::BlockHeaderError(BlockHeaderValidationError::OldSeedHash), @@ -171,7 +171,7 @@ async fn test_monero_blocks() { }, }; // now lets fix the seed, and try again - add_monero_data(&mut block_3, seed2); + add_monero_test_data(&mut block_3, seed2); // lets break the nonce count let hash1 = block_3.hash(); block_3.header.nonce = 1; @@ -194,14 +194,14 @@ async fn test_monero_blocks() { assert_block_add_result_added(&db.add_block(Arc::new(block_3.clone())).unwrap()); } -fn add_monero_data(tblock: &mut Block, seed_key: &str) { +fn add_monero_test_data(tblock: &mut Block, seed_key: &str) { let blocktemplate_blob = "0c0c8cd6a0fa057fe21d764e7abf004e975396a2160773b93712bf6118c3b4959ddd8ee0f76aad0000000002e1ea2701ffa5ea2701d5a299e2abb002028eb3066ced1b2cc82ea046f3716a48e9ae37144057d5fb48a97f941225a1957b2b0106225b7ec0a6544d8da39abe68d8bd82619b4a7c5bdae89c3783b256a8fa47820208f63aa86d2e857f070000" .to_string(); let bytes = hex::decode(blocktemplate_blob).unwrap(); let mut mblock = monero_rx::deserialize::(&bytes[..]).unwrap(); let hash = monero::Hash::from_slice(tblock.header.merge_mining_hash().as_slice()); - monero_rx::insert_merge_mining_tag_and_aux_chain_merkle_root_into_block(&mut mblock, hash, 1, 0).unwrap(); + monero_rx::insert_aux_chain_mr_and_info_into_block(&mut mblock, hash, 1, 0).unwrap(); let hashes = monero_rx::create_ordered_transaction_hashes_from_block(&mblock); let merkle_root = monero_rx::tree_hash(&hashes).unwrap(); let coinbase_merkle_proof = monero_rx::create_merkle_proof(&hashes, &hashes[0]).unwrap(); @@ -240,7 +240,7 @@ fn add_monero_data(tblock: &mut Block, seed_key: &str) { } fn add_bad_monero_data(tblock: &mut Block, seed_key: &str) { - add_monero_data(tblock, seed_key); + add_monero_test_data(tblock, seed_key); // Add some "garbage" bytes to the end of the pow_data tblock.header.pow.pow_data.extend([1u8; 100]); } diff --git a/base_layer/tari_mining_helper_ffi/src/lib.rs b/base_layer/tari_mining_helper_ffi/src/lib.rs index 5aebef1f0e..32badb6120 100644 --- a/base_layer/tari_mining_helper_ffi/src/lib.rs +++ b/base_layer/tari_mining_helper_ffi/src/lib.rs @@ -387,7 +387,7 @@ mod tests { #[test] fn detect_change_in_consensus_encoding() { #[cfg(tari_target_network_mainnet)] - let (nonce, difficulty) = match Network::get_current_or_default() { + let (nonce, difficulty) = match Network::get_current_or_user_setting_or_default() { Network::MainNet => (9205754023158580549, Difficulty::from_u64(1015).unwrap()), Network::StageNet => (12022341430563186162, Difficulty::from_u64(1011).unwrap()), _ => panic!("Invalid network for mainnet target"), @@ -411,7 +411,7 @@ mod tests { // Use this to generate new NONCE and DIFFICULTY // Use ONLY if you know encoding has changed let (difficulty, nonce) = generate_nonce_with_min_difficulty(min_difficulty()).unwrap(); - let network = Network::get_current_or_default(); + let network = Network::get_current_or_user_setting_or_default(); eprintln!("network = {network:?}"); eprintln!("nonce = {:?}", nonce); eprintln!("difficulty = {:?}", difficulty); diff --git a/common/src/configuration/mod.rs b/common/src/configuration/mod.rs index 44f30f90c8..bcb57ab324 100644 --- a/common/src/configuration/mod.rs +++ b/common/src/configuration/mod.rs @@ -69,13 +69,13 @@ pub fn socket_or_multi(addr: &str) -> Result { /// Implement this trait to specify custom configuration overrides for a network when loading the config pub trait ConfigOverrideProvider { - fn get_config_property_overrides(&self, default_network: Network) -> Vec<(String, String)>; + fn get_config_property_overrides(&self, network: &mut Network) -> Vec<(String, String)>; } pub struct NoConfigOverrides; impl ConfigOverrideProvider for NoConfigOverrides { - fn get_config_property_overrides(&self, _default_network: Network) -> Vec<(String, String)> { + fn get_config_property_overrides(&self, _network: &mut Network) -> Vec<(String, String)> { Vec::new() } } diff --git a/common/src/configuration/network.rs b/common/src/configuration/network.rs index c754a913c6..bcd6cc3ef6 100644 --- a/common/src/configuration/network.rs +++ b/common/src/configuration/network.rs @@ -49,10 +49,16 @@ pub enum Network { } impl Network { - pub fn get_current_or_default() -> Self { + pub fn get_current_or_user_setting_or_default() -> Self { match CURRENT_NETWORK.get() { Some(&network) => network, - None => Network::default(), + None => { + // Check to see if the network has been set by the environment, otherwise use the default + match std::env::var("TARI_NETWORK") { + Ok(network) => Network::from_str(network.as_str()).unwrap_or(Network::default()), + Err(_) => Network::default(), + } + }, } } diff --git a/common/src/configuration/utils.rs b/common/src/configuration/utils.rs index 4985b23c6b..e03ea67bbd 100644 --- a/common/src/configuration/utils.rs +++ b/common/src/configuration/utils.rs @@ -65,7 +65,7 @@ pub fn load_configuration_with_overrides, TOverride: ConfigOverri .build() .map_err(|ce| ConfigError::new("Could not build config", Some(ce.to_string())))?; - let network = match cfg.get_string("network") { + let mut network = match cfg.get_string("network") { Ok(network) => { Network::from_str(&network).map_err(|e| ConfigError::new("Invalid network", Some(e.to_string())))? }, @@ -82,7 +82,11 @@ pub fn load_configuration_with_overrides, TOverride: ConfigOverri }; info!(target: LOG_TARGET, "Configuration file loaded."); - let overrides = overrides.get_config_property_overrides(network); + let overrides = overrides.get_config_property_overrides(&mut network); + // Set the network environment variable according to the chosen network (this is to ensure that + // `get_current_or_user_setting_or_default()` uses the environment variable if the static network is not set) + std::env::set_var("TARI_NETWORK", network.as_key_str()); + if overrides.is_empty() { return Ok(cfg); }