From d220643b1596955c499bf39df2c58c3052d92724 Mon Sep 17 00:00:00 2001 From: Hansie Odendaal <39146854+hansieodendaal@users.noreply.github.com> Date: Fri, 1 Mar 2024 11:00:07 +0200 Subject: [PATCH] feat: lazily evaluate for new random_x template (#6170) Description --- - Changed the merge mining proxy to evaluate RandomX block template changes lazily, similar to the strategy employed for the SHA3 miner. Evaluating this often is very expensive, especially during busy network times with a highly volatile mempool. - Removed _new template_ spam from logging - Fixed a bug where the command-line network argument `igor` specified for the merge mining proxy was not recognized in the network-aware 'DomainSeparatedConsensusHasher' - Updated the merge mining proxy `config.toml` to be in line with core Motivation and Context --- - With recent stress tests on `nextnet` that lasted multiple hours, RandomX mining virtually ground to a halt during the stress test, whereas SHAR3 mining was able to continue. - One merge mining proxy that was monitored for 45 min went up to 4.5 GB in memory usage. During that time the connected base node only advanced one block, while the merge mining proxy requested 428 new block templates from the base node, all of them unique. This could account for ~1 GB in memory usage as block templates are discarded after 20 minutes. How Has This Been Tested? --- - System-level testing during low network activity (_on `igor`_) - System-level stress testing: - coin split stress test (_on `igor`_) - one-side stealth transaction stress test (_on `igor`_) What process can a PR reviewer use to test or verify this change? --- Code walk-through, system-level testing Breaking Changes --- - [x] None - [ ] Requires data directory on base node to be deleted - [ ] Requires hard fork - [ ] Other - Please specify --------- Co-authored-by: SW van Heerden --- .../minotari_app_utilities/Cargo.toml | 1 - .../src/common_cli_args.rs | 2 +- .../minotari_app_utilities/src/lib.rs | 1 - .../minotari_console_wallet/src/cli.rs | 6 +- .../minotari_console_wallet/src/lib.rs | 4 +- .../minotari_merge_mining_proxy/Cargo.toml | 18 +- .../src/block_template_data.rs | 102 ++++--- .../src/block_template_protocol.rs | 250 +++++++++++------- .../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_miner/src/run_miner.rs | 15 +- applications/minotari_node/src/cli.rs | 6 +- .../src/grpc/base_node_grpc_server.rs | 1 - applications/minotari_node/src/lib.rs | 4 +- .../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/Cargo.toml | 1 + common/src/configuration/error.rs | 11 + common/src/configuration/mod.rs | 4 +- common/src/configuration/network.rs | 10 +- common/src/configuration/utils.rs | 9 +- common/src/lib.rs | 1 + .../src/network_check.rs | 7 +- 31 files changed, 444 insertions(+), 255 deletions(-) rename {applications/minotari_app_utilities => common}/src/network_check.rs (99%) diff --git a/applications/minotari_app_utilities/Cargo.toml b/applications/minotari_app_utilities/Cargo.toml index 682e361849..8ca4fbd908 100644 --- a/applications/minotari_app_utilities/Cargo.toml +++ b/applications/minotari_app_utilities/Cargo.toml @@ -9,7 +9,6 @@ license = "BSD-3-Clause" tari_common = { path = "../../common" } tari_common_types = { path = "../../base_layer/common_types" } tari_comms = { path = "../../comms/core" } -tari_features = { path = "../../common/tari_features", version = "1.0.0-pre.9" } tari_utilities = { version = "0.7" } minotari_app_grpc = { path = "../minotari_app_grpc", optional = true } 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_app_utilities/src/lib.rs b/applications/minotari_app_utilities/src/lib.rs index 1033f05499..0859c10104 100644 --- a/applications/minotari_app_utilities/src/lib.rs +++ b/applications/minotari_app_utilities/src/lib.rs @@ -22,7 +22,6 @@ pub mod common_cli_args; pub mod identity_management; -pub mod network_check; #[cfg(feature = "miner_input")] pub mod parse_miner_input; pub mod utilities; 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_console_wallet/src/lib.rs b/applications/minotari_console_wallet/src/lib.rs index 6f14065eb6..f2450261e9 100644 --- a/applications/minotari_console_wallet/src/lib.rs +++ b/applications/minotari_console_wallet/src/lib.rs @@ -48,7 +48,7 @@ pub use cli::{ }; use init::{change_password, get_base_node_peer_config, init_wallet, start_wallet, tari_splash_screen, WalletBoot}; use log::*; -use minotari_app_utilities::{common_cli_args::CommonCliArgs, consts, network_check::set_network_if_choice_valid}; +use minotari_app_utilities::{common_cli_args::CommonCliArgs, consts}; use minotari_wallet::transaction_service::config::TransactionRoutingMechanism; use recovery::{get_seed_from_seed_words, prompt_private_key_from_seed_words}; use tari_common::{ @@ -117,8 +117,6 @@ pub fn run_wallet_with_cli( consts::APP_VERSION ); - set_network_if_choice_valid(config.wallet.network)?; - let password = get_password(config, &cli); if password.is_none() { 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..cd043824c2 100644 --- a/applications/minotari_merge_mining_proxy/src/block_template_data.rs +++ b/applications/minotari_merge_mining_proxy/src/block_template_data.rs @@ -22,7 +22,7 @@ //! Provides methods for for building template data and storing them with timestamps. -use std::{collections::HashMap, sync::Arc}; +use std::{collections::HashMap, convert::TryFrom, sync::Arc}; #[cfg(not(test))] use chrono::Duration; @@ -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,28 @@ 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| { + let header = item.data.template.new_block_template.header.clone().unwrap_or_default(); + FixedHash::try_from(header.prev_hash).unwrap_or(FixedHash::default()) == current_best_block_hash + }) + .map(|val| val.data.clone()) } /// Remove any data that is older than 20 minutes. @@ -126,8 +138,9 @@ 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, } impl BlockTemplateData {} @@ -140,8 +153,9 @@ 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, } impl BlockTemplateDataBuilder { @@ -174,8 +188,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 +198,11 @@ impl BlockTemplateDataBuilder { self } + pub fn new_block_template(mut self, template: grpc::NewBlockTemplate) -> Self { + self.new_block_template = Some(template); + self + } + /// Build a new [BlockTemplateData], all the values have to be set. /// /// # Errors @@ -205,12 +224,15 @@ 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()))?; Ok(BlockTemplateData { monero_seed, @@ -218,8 +240,9 @@ 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, }) } } @@ -230,12 +253,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 +271,25 @@ pub mod test { total_fees: 100, algo: Some(grpc::PowAlgo { pow_algo: 0 }), }; + let new_block_template = grpc::NewBlockTemplate::default(); 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); + 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 +299,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 +358,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..fa0b685476 100644 --- a/applications/minotari_merge_mining_proxy/src/block_template_protocol.rs +++ b/applications/minotari_merge_mining_proxy/src/block_template_protocol.rs @@ -36,6 +36,7 @@ use tari_core::{ transaction_components::{TransactionKernel, TransactionOutput}, }, }; +use tari_utilities::hex::Hex; use crate::{ block_template_data::{BlockTemplateData, BlockTemplateDataBuilder}, @@ -78,48 +79,103 @@ impl BlockTemplateProtocol<'_> { pub async fn get_next_block_template( mut self, monero_mining_data: MoneroMiningData, + existing_block_template: Option, ) -> Result { + let mut existing_block_template = existing_block_template; 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), + }; + + ( + add_monero_data(block, monero_mining_data.clone(), new_template)?, + height, + ) + }; - let template_height = new_template.template.header.as_ref().map(|h| h.height).unwrap_or(0); - if !self.check_expected_tip(template_height).await? { + 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 ); + if existing_block_template.is_some() { + existing_block_template = None; + } 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); } } @@ -217,77 +273,80 @@ 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_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()?; + 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) + .build()?; - // 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)?; + // 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)?; - 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, "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, "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)?; + 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)?; - 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, - }) - } + 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 + ); + + 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 @@ -305,17 +364,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_miner/src/run_miner.rs b/applications/minotari_miner/src/run_miner.rs index 98c4823c43..4e856d549c 100644 --- a/applications/minotari_miner/src/run_miner.rs +++ b/applications/minotari_miner/src/run_miner.rs @@ -29,14 +29,11 @@ use minotari_app_grpc::{ tari_rpc::{base_node_client::BaseNodeClient, TransactionOutput as GrpcTransactionOutput}, tls::protocol_string, }; -use minotari_app_utilities::{ - network_check::set_network_if_choice_valid, - parse_miner_input::{ - base_node_socket_address, - verify_base_node_grpc_mining_responses, - wallet_payment_address, - BaseNodeGrpcClient, - }, +use minotari_app_utilities::parse_miner_input::{ + base_node_socket_address, + verify_base_node_grpc_mining_responses, + wallet_payment_address, + BaseNodeGrpcClient, }; use tari_common::{ exit_codes::{ExitCode, ExitError}, @@ -76,8 +73,6 @@ pub async fn start_miner(cli: Cli) -> Result<(), ExitError> { let mut config = MinerConfig::load_from(&cfg).expect("Failed to load config"); config.set_base_path(cli.common.get_base_path()); - set_network_if_choice_valid(config.network)?; - debug!(target: LOG_TARGET_FILE, "{:?}", config); let key_manager = create_memory_db_key_manager(); let wallet_payment_address = wallet_payment_address(config.wallet_payment_address.clone(), config.network) 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..3bc8ffc868 100644 --- a/applications/minotari_node/src/grpc/base_node_grpc_server.rs +++ b/applications/minotari_node/src/grpc/base_node_grpc_server.rs @@ -585,7 +585,6 @@ 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, }; diff --git a/applications/minotari_node/src/lib.rs b/applications/minotari_node/src/lib.rs index 7e4cc087d2..83fd991cdd 100644 --- a/applications/minotari_node/src/lib.rs +++ b/applications/minotari_node/src/lib.rs @@ -43,7 +43,7 @@ use commands::{cli_loop::CliLoop, command::CommandContext}; use futures::FutureExt; use log::*; use minotari_app_grpc::{authentication::ServerAuthenticationInterceptor, tls::identity::read_identity}; -use minotari_app_utilities::{common_cli_args::CommonCliArgs, network_check::set_network_if_choice_valid}; +use minotari_app_utilities::common_cli_args::CommonCliArgs; use tari_common::{ configuration::bootstrap::{grpc_default_port, ApplicationType}, exit_codes::{ExitCode, ExitError}, @@ -100,8 +100,6 @@ pub async fn run_base_node_with_cli( cli: Cli, shutdown: Shutdown, ) -> Result<(), ExitError> { - set_network_if_choice_valid(config.network())?; - #[cfg(feature = "metrics")] { metrics::install( 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 8a6631dc7e..47148fb116 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/Cargo.toml b/common/Cargo.toml index a58a2a6fe0..919f7a7a35 100644 --- a/common/Cargo.toml +++ b/common/Cargo.toml @@ -15,6 +15,7 @@ static-application-info = ["git2"] [dependencies] tari_crypto = { version = "0.20" } +tari_features = { path = "./tari_features", version = "1.0.0-pre.9"} anyhow = "1.0.53" blake2 = "0.10" diff --git a/common/src/configuration/error.rs b/common/src/configuration/error.rs index 740698874c..b347477a0d 100644 --- a/common/src/configuration/error.rs +++ b/common/src/configuration/error.rs @@ -5,6 +5,8 @@ use std::fmt; use structopt::clap::Error as ClapError; +use crate::network_check::NetworkCheckError; + #[derive(Debug)] pub struct ConfigError { pub(crate) cause: &'static str, @@ -17,6 +19,15 @@ impl ConfigError { } } +impl From for ConfigError { + fn from(err: NetworkCheckError) -> Self { + Self { + cause: "Failed to set the network", + source: Some(err.to_string()), + } + } +} + impl std::error::Error for ConfigError {} impl fmt::Display for ConfigError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 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..4e98f03ff2 100644 --- a/common/src/configuration/utils.rs +++ b/common/src/configuration/utils.rs @@ -14,6 +14,7 @@ use serde::{ use crate::{ configuration::{bootstrap::prompt, ConfigOverrideProvider, Network}, + network_check::set_network_if_choice_valid, ConfigError, LOG_TARGET, }; @@ -65,7 +66,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 +83,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 static network variable according to the user chosen network (for use with + // `get_current_or_user_setting_or_default()`) - + set_network_if_choice_valid(network)?; + if overrides.is_empty() { return Ok(cfg); } diff --git a/common/src/lib.rs b/common/src/lib.rs index 9386df0a41..8df6bddc02 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -53,6 +53,7 @@ #[cfg(any(feature = "build", feature = "static-application-info"))] pub mod build; pub mod exit_codes; +pub mod network_check; #[macro_use] mod logging; pub mod configuration; diff --git a/applications/minotari_app_utilities/src/network_check.rs b/common/src/network_check.rs similarity index 99% rename from applications/minotari_app_utilities/src/network_check.rs rename to common/src/network_check.rs index 02e798cb0d..a4465f7edb 100644 --- a/applications/minotari_app_utilities/src/network_check.rs +++ b/common/src/network_check.rs @@ -20,12 +20,13 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use tari_common::{ +use tari_features::resolver::Target; +use thiserror::Error; + +use crate::{ configuration::Network, exit_codes::{ExitCode, ExitError}, }; -use tari_features::resolver::Target; -use thiserror::Error; #[derive(Debug, Error)] pub enum NetworkCheckError {