From b3dc63f79e7bedd19854c17e5d9809deb652e61d Mon Sep 17 00:00:00 2001 From: stringhandler Date: Tue, 25 Feb 2025 15:27:10 +0200 Subject: [PATCH] feat: add p2pool tip info and better mmproxy template handling (#6795) ## Summary by CodeRabbit - **New Features** - Introduced a new RPC method to retrieve tip information from the P2Pool service. - Added a new variable for user agent configuration in the HTTP client. - **Refactor** - Streamlined block template management with a new management structure. - Simplified method signatures and reduced parameter complexity in several service methods. - **Bug Fixes** - Improved error handling in the retrieval of node and network state metrics. - **Chores** - Updated project dependencies and made minor formatting adjustments. - Renamed modules for better clarity and organization. --- .github/workflows/ci.yml | 2 +- Cargo.lock | 1 + .../minotari_app_grpc/proto/p2pool.proto | 13 + .../minotari_merge_mining_proxy/Cargo.toml | 3 +- .../src/block_template_data.rs | 137 +------ ..._protocol.rs => block_template_manager.rs} | 344 +++++------------- .../minotari_merge_mining_proxy/src/lib.rs | 11 +- .../minotari_merge_mining_proxy/src/main.rs | 2 +- .../src/proxy/inner.rs | 92 +++-- .../src/proxy/service.rs | 2 +- .../src/run_merge_miner.rs | 6 +- .../core/src/chain_storage/lmdb_db/lmdb_db.rs | 2 +- integration_tests/src/merge_mining_proxy.rs | 4 +- 13 files changed, 198 insertions(+), 421 deletions(-) rename applications/minotari_merge_mining_proxy/src/{block_template_protocol.rs => block_template_manager.rs} (51%) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4cede9e72d..1d113b9410 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -115,7 +115,7 @@ jobs: tari-${{ runner.os }}-${{ runner.cpu-model }}-${{ env.toolchain }}-nightly - name: cargo machete run: | - cargo install cargo-machete + cargo install cargo-machete@0.7.0 cargo machete build-stable: diff --git a/Cargo.lock b/Cargo.lock index 7fa8da106a..1d926a1e2d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4071,6 +4071,7 @@ version = "1.11.5-pre.0" dependencies = [ "anyhow", "bincode", + "blake2", "borsh", "bytes 1.9.0", "chrono", diff --git a/applications/minotari_app_grpc/proto/p2pool.proto b/applications/minotari_app_grpc/proto/p2pool.proto index 61f74349eb..bb5fd3773d 100644 --- a/applications/minotari_app_grpc/proto/p2pool.proto +++ b/applications/minotari_app_grpc/proto/p2pool.proto @@ -27,10 +27,23 @@ import "base_node.proto"; import "block.proto"; service ShaP2Pool { + rpc GetTipInfo(GetTipInfoRequest) returns(GetTipInfoResponse); rpc GetNewBlock(GetNewBlockRequest) returns(GetNewBlockResponse); rpc SubmitBlock(SubmitBlockRequest) returns(tari.rpc.SubmitBlockResponse); } +message GetTipInfoRequest { +} + +message GetTipInfoResponse { + uint64 node_height = 1; + bytes node_tip_hash = 2; + uint64 p2pool_rx_height = 3; + bytes p2pool_rx_tip_hash = 4; + uint64 p2pool_sha_height = 5; + bytes p2pool_sha_tip_hash = 6; +} + message GetNewBlockRequest { tari.rpc.PowAlgo pow = 1; string coinbase_extra = 2; diff --git a/applications/minotari_merge_mining_proxy/Cargo.toml b/applications/minotari_merge_mining_proxy/Cargo.toml index 542b56469d..d1b6420599 100644 --- a/applications/minotari_merge_mining_proxy/Cargo.toml +++ b/applications/minotari_merge_mining_proxy/Cargo.toml @@ -40,7 +40,7 @@ hyper = { version = "0.14.12", features = ["default"] } jsonrpc = "0.12.0" log = { version = "0.4.8", features = ["std"] } markup5ever = "0.12.1" -monero = { version = "0.21.0" , features = ["serde"] } +monero = { version = "0.21.0", features = ["serde"] } reqwest = { version = "0.11.4", features = ["json"] } serde = { version = "1.0.136", features = ["derive"] } serde_json = "1.0.57" @@ -53,6 +53,7 @@ scraper = "0.19.0" toml = "0.8.19" rand = "0.8" regex = "1.11.1" +blake2 = "0.10" [build-dependencies] tari_features = { path = "../../common/tari_features", version = "1.11.5-pre.0" } 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 a66514369f..9a9c228c0b 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 building template data and storing them with timestamps. -use std::{collections::HashMap, convert::TryFrom, sync::Arc}; +use std::{collections::HashMap, sync::Arc}; #[cfg(not(test))] use chrono::Duration; @@ -33,47 +33,19 @@ use tari_core::{proof_of_work::monero_rx::FixedByteArray, AuxChainHashes}; use tokio::sync::RwLock; use tracing::trace; -use crate::{ - block_template_protocol::{FinalBlockTemplateData, NewBlockTemplateData}, - error::MmProxyError, -}; +use crate::{block_template_manager::FinalBlockTemplateData, error::MmProxyError}; const LOG_TARGET: &str = "minotari_mm_proxy::xmrig"; /// Structure for holding hashmap of hashes -> [BlockRepositoryItem] and [TemplateRepositoryItem]. #[derive(Debug, Clone)] -pub struct BlockTemplateRepository { +pub(crate) struct BlockTemplateRepository { blocks: Arc, BlockRepositoryItem>>>, - templates: Arc, TemplateRepositoryItem>>>, -} - -/// Structure holding [NewBlockTemplate] along with a timestamp. -#[derive(Debug, Clone)] -pub struct TemplateRepositoryItem { - pub new_block_template: NewBlockTemplateData, - pub template_with_coinbase: grpc::NewBlockTemplate, - datetime: DateTime, -} - -impl TemplateRepositoryItem { - /// Create new [Self] with current time in UTC. - pub fn new(new_block_template: NewBlockTemplateData, template_with_coinbase: grpc::NewBlockTemplate) -> Self { - Self { - new_block_template, - template_with_coinbase, - datetime: Utc::now(), - } - } - - /// Get the timestamp of creation. - pub fn datetime(&self) -> DateTime { - self.datetime - } } /// Structure holding [FinalBlockTemplateData] along with a timestamp. #[derive(Debug, Clone)] -pub struct BlockRepositoryItem { +pub(crate) struct BlockRepositoryItem { pub data: FinalBlockTemplateData, datetime: DateTime, } @@ -97,7 +69,6 @@ impl BlockTemplateRepository { pub fn new() -> Self { Self { blocks: Arc::new(RwLock::new(HashMap::new())), - templates: Arc::new(RwLock::new(HashMap::new())), } } @@ -107,60 +78,12 @@ impl BlockTemplateRepository { b.get(merge_mining_hash.as_ref()).map(|item| item.data.clone()) } - /// Return [BlockTemplateData] with the associated hash. None if the hash is not stored. - pub async fn get_new_template>( - &self, - best_block_hash: T, - ) -> Option<(NewBlockTemplateData, grpc::NewBlockTemplate)> { - let b = self.templates.read().await; - b.get(best_block_hash.as_ref()) - .map(|item| (item.new_block_template.clone(), item.template_with_coinbase.clone())) - } - /// Store [FinalBlockTemplateData] at the hash value if the key does not exist. - pub async fn save_final_block_template_if_key_unique( - &self, - merge_mining_hash: Vec, - block_template: FinalBlockTemplateData, - ) { + pub async fn save_final_block_template_if_key_unique(&self, block_template: FinalBlockTemplateData) { + let merge_mining_hash = block_template.aux_chain_mr.to_vec(); let mut b = self.blocks.write().await; - b.entry(merge_mining_hash.clone()).or_insert_with(|| { - trace!( - target: LOG_TARGET, - "Saving final block template with merge mining hash: {:?}", - hex::encode(&merge_mining_hash) - ); - BlockRepositoryItem::new(block_template) - }); - } - - /// Store [NewBlockTemplate] at the hash value if the key does not exist. - pub async fn save_new_block_template_if_key_unique( - &self, - best_block_hash: Vec, - new_block_template: NewBlockTemplateData, - template_with_coinbase: grpc::NewBlockTemplate, - ) { - let mut b = self.templates.write().await; - b.entry(best_block_hash.clone()).or_insert_with(|| { - trace!( - target: LOG_TARGET, - "Saving new block template for best block hash: {:?}", - hex::encode(&best_block_hash) - ); - TemplateRepositoryItem::new(new_block_template, template_with_coinbase) - }); - } - - /// Check if the repository contains a block template with best_previous_block_hash - pub async fn blocks_contains(&self, current_best_block_hash: FixedHash) -> Option { - let b = self.blocks.read().await; - b.values() - .find(|item| { - let header = item.data.template.tari_block.header.clone().unwrap_or_default(); - FixedHash::try_from(header.prev_hash).unwrap_or(FixedHash::default()) == current_best_block_hash - }) - .map(|val| val.data.clone()) + b.entry(merge_mining_hash) + .or_insert_with(|| BlockRepositoryItem::new(block_template)); } /// Remove any data that is older than 20 minutes. @@ -172,13 +95,6 @@ impl BlockTemplateRepository { #[cfg(not(test))] let threshold = Utc::now() - Duration::minutes(20); *b = b.drain().filter(|(_, i)| i.datetime() >= threshold).collect(); - trace!(target: LOG_TARGET, "Removing outdated new block templates"); - let mut b = self.templates.write().await; - #[cfg(test)] - let threshold = Utc::now(); - #[cfg(not(test))] - let threshold = Utc::now() - Duration::minutes(20); - *b = b.drain().filter(|(_, i)| i.datetime() >= threshold).collect(); } /// Remove a particularfinla block template for hash and return the associated [BlockRepositoryItem] if any. @@ -191,22 +107,11 @@ impl BlockTemplateRepository { let mut b = self.blocks.write().await; b.remove(hash.as_ref()) } - - /// Remove a particular new block template for hash and return the associated [BlockRepositoryItem] if any. - pub async fn remove_new_block_template>(&self, hash: T) -> Option { - trace!( - target: LOG_TARGET, - "New block template removed with best block hash {:?}", - hex::encode(hash.as_ref()) - ); - let mut b = self.templates.write().await; - b.remove(hash.as_ref()) - } } /// Setup values for the new block. #[derive(Clone, Debug)] -pub struct BlockTemplateData { +pub(crate) struct BlockTemplateData { pub monero_seed: FixedByteArray, pub tari_block: grpc::Block, pub tari_miner_data: grpc::MinerData, @@ -221,7 +126,7 @@ impl BlockTemplateData {} /// Builder for the [BlockTemplateData]. All fields have to be set to succeed. #[derive(Default)] -pub struct BlockTemplateDataBuilder { +pub(crate) struct BlockTemplateDataBuilder { monero_seed: Option, tari_block: Option, tari_miner_data: Option, @@ -312,8 +217,8 @@ impl BlockTemplateDataBuilder { } #[cfg(test)] -pub mod test { - use std::convert::TryInto; +mod test { + use std::convert::{TryFrom, TryInto}; use tari_core::{ blocks::{Block, BlockHeader}, @@ -323,7 +228,7 @@ pub mod test { use tari_utilities::ByteArray; use super::*; - use crate::block_template_protocol::AuxChainMr; + use crate::block_template_manager::AuxChainMr; fn create_block_template_data() -> FinalBlockTemplateData { let header = BlockHeader::new(100); @@ -358,25 +263,17 @@ pub mod test { #[tokio::test] async fn test_block_template_repository() { let btr = BlockTemplateRepository::new(); - let hash1 = vec![1; 32]; - let hash2 = vec![2; 32]; - let hash3 = vec![3; 32]; let block_template = create_block_template_data(); - btr.save_final_block_template_if_key_unique(hash1.clone(), block_template.clone()) - .await; - btr.save_final_block_template_if_key_unique(hash2.clone(), block_template) + let hash1 = block_template.aux_chain_mr.to_vec(); + btr.save_final_block_template_if_key_unique(block_template.clone()) .await; assert!(btr.get_final_template(hash1.clone()).await.is_some()); - assert!(btr.get_final_template(hash2.clone()).await.is_some()); - assert!(btr.get_final_template(hash3.clone()).await.is_none()); assert!(btr.remove_final_block_template(hash1.clone()).await.is_some()); assert!(btr.get_final_template(hash1.clone()).await.is_none()); - assert!(btr.get_final_template(hash2.clone()).await.is_some()); - assert!(btr.get_final_template(hash3.clone()).await.is_none()); + btr.save_final_block_template_if_key_unique(block_template).await; + assert!(btr.get_final_template(hash1.clone()).await.is_some()); btr.remove_outdated().await; assert!(btr.get_final_template(hash1).await.is_none()); - assert!(btr.get_final_template(hash2).await.is_none()); - assert!(btr.get_final_template(hash3).await.is_none()); } #[test] diff --git a/applications/minotari_merge_mining_proxy/src/block_template_protocol.rs b/applications/minotari_merge_mining_proxy/src/block_template_manager.rs similarity index 51% rename from applications/minotari_merge_mining_proxy/src/block_template_protocol.rs rename to applications/minotari_merge_mining_proxy/src/block_template_manager.rs index a46e2a2fd7..3561cfbeff 100644 --- a/applications/minotari_merge_mining_proxy/src/block_template_protocol.rs +++ b/applications/minotari_merge_mining_proxy/src/block_template_manager.rs @@ -42,7 +42,7 @@ use tari_max_size::MaxSizeBytes; use tari_utilities::{hex::Hex, ByteArray}; use crate::{ - block_template_data::{BlockTemplateData, BlockTemplateDataBuilder, BlockTemplateRepository}, + block_template_data::{BlockTemplateData, BlockTemplateDataBuilder}, common::merge_mining, config::MergeMiningProxyConfig, error::MmProxyError, @@ -51,7 +51,7 @@ use crate::{ const LOG_TARGET: &str = "minotari_mm_proxy::proxy::block_template_protocol"; /// Structure holding grpc connections. -pub struct BlockTemplateProtocol<'a> { +pub(crate) struct BlockTemplateManager<'a> { config: Arc, base_node_client: &'a mut BaseNodeGrpcClient, p2pool_client: Option, @@ -60,14 +60,14 @@ pub struct BlockTemplateProtocol<'a> { consensus_manager: ConsensusManager, } -impl<'a> BlockTemplateProtocol<'a> { - pub async fn new( +impl<'a> BlockTemplateManager<'a> { + pub fn try_create( base_node_client: &'a mut BaseNodeGrpcClient, p2pool_client: Option, config: Arc, consensus_manager: ConsensusManager, wallet_payment_address: TariAddress, - ) -> Result, MmProxyError> { + ) -> Result, MmProxyError> { let key_manager = create_memory_db_key_manager()?; Ok(Self { config, @@ -80,225 +80,106 @@ impl<'a> BlockTemplateProtocol<'a> { } } -#[allow(clippy::too_many_lines)] -impl BlockTemplateProtocol<'_> { +impl BlockTemplateManager<'_> { /// Create [FinalBlockTemplateData] with [MoneroMiningData]. pub async fn get_next_tari_block_template( mut self, monero_mining_data: MoneroMiningData, - block_templates: &BlockTemplateRepository, ) -> Result { - let best_block_hash = self.get_current_best_block_hash().await?; - let existing_block_template = block_templates.blocks_contains(best_block_hash).await; - - let mut final_block_template = existing_block_template; - let mut loop_count = 0; - loop { - if loop_count >= 10 { - warn!(target: LOG_TARGET, "Failed to get block template after {} retries", loop_count); - return Err(MmProxyError::FailedToGetBlockTemplate(format!( - "Retried {} times", - loop_count - ))); - } - if loop_count == 1 && final_block_template.is_some() { - final_block_template = None; - } - if loop_count > 0 { - tokio::time::sleep(std::time::Duration::from_millis(loop_count * 250)).await; - } - loop_count += 1; - let (final_template_data, block_height) = if let Some(data) = final_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: #{} (try {}), block hash: `{}`", - height, - loop_count, - match data.template.tari_block.header.as_ref() { - Some(h) => h.hash.to_hex(), - None => "None".to_string(), - } - ); - (data, height) - } else { - let block = match self.p2pool_client.as_mut() { - Some(client) => { - let pow_algo = PowAlgo { - pow_algo: PowAlgos::Randomx.into(), - }; - let coinbase_extra = if self.config.coinbase_extra.trim().is_empty() { - String::new() - } else { - self.config.coinbase_extra.clone() - }; - let block_result = client - .get_new_block(GetNewBlockRequest { - pow: Some(pow_algo), - coinbase_extra, - wallet_payment_address: self.wallet_payment_address.to_base58(), - }) - .await? - .into_inner(); - block_result - .block - .ok_or_else(|| MmProxyError::FailedToGetBlockTemplate("block result".to_string()))? - }, - None => { - let (block_template_with_coinbase, height) = self - .get_block_template_from_cache_or_new(block_templates, best_block_hash, &mut loop_count) - .await?; - - match self.get_new_block(block_template_with_coinbase).await { - Ok(b) => { - debug!( - target: LOG_TARGET, - "Requested new block at height: #{} (try {}), block hash: `{}`", - height, loop_count, - { - 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, - "Block lost, retrying to get new block template (try {})", loop_count - ); - continue; - }, - Err(err) => { - error!(target: LOG_TARGET, "grpc get_new_block ({})", err.to_string()); - return Err(err); - }, - } - }, + let block = match self.p2pool_client.as_mut() { + Some(client) => { + let pow_algo = PowAlgo { + pow_algo: PowAlgos::Randomx.into(), }; - - let height = block + let coinbase_extra = if self.config.coinbase_extra.trim().is_empty() { + String::new() + } else { + self.config.coinbase_extra.clone() + }; + let block_result = client + .get_new_block(GetNewBlockRequest { + pow: Some(pow_algo), + coinbase_extra, + wallet_payment_address: self.wallet_payment_address.to_base58(), + }) + .await? + .into_inner(); + block_result .block - .as_ref() - .map(|b| b.header.as_ref().map(|h| h.height).unwrap_or_default()) - .unwrap_or_default(); - - let miner_data = block - .miner_data - .as_ref() - .copied() - .ok_or_else(|| MmProxyError::GrpcResponseMissingField("miner_data"))?; + .ok_or_else(|| MmProxyError::FailedToGetBlockTemplate("block result".to_string()))? + }, + None => { + let (block_template_with_coinbase, height) = self.create_new_block_template().await?; + + 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(err) => { + error!(target: LOG_TARGET, "grpc get_new_block ({})", err.to_string()); + return Err(err); + }, + } + }, + }; - (add_monero_data(block, monero_mining_data.clone(), miner_data)?, height) - }; + let miner_data = block + .miner_data + .as_ref() + .copied() + .ok_or_else(|| MmProxyError::GrpcResponseMissingField("miner_data"))?; - block_templates - .save_final_block_template_if_key_unique( - // `aux_chain_mr` is used as the key because it is stored in the ExtraData field in the Monero - // block - final_template_data.aux_chain_mr.to_vec(), - final_template_data.clone(), - ) - .await; - block_templates - .remove_new_block_template(best_block_hash.to_vec()) - .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 (try {}).", block_height, loop_count - ); - continue; - } - info!(target: LOG_TARGET, - "Block template for height: #{} (try {}), block hash: `{}`, {}", - final_template_data - .template.tari_block - .header - .as_ref() - .map(|h| h.height) - .unwrap_or_default(), loop_count, - match final_template_data.template.tari_block.header.as_ref() { - Some(h) => h.hash.to_hex(), - None => "None".to_string(), - }, - 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); - } + add_monero_data(block, monero_mining_data.clone(), miner_data) } - async fn get_block_template_from_cache_or_new( - &mut self, - block_templates: &BlockTemplateRepository, - best_block_hash: FixedHash, - loop_count: &mut u64, - ) -> Result<(NewBlockTemplate, u64), MmProxyError> { - let (block_template_with_coinbase, height) = match block_templates.get_new_template(best_block_hash).await { - None => { - let new_template = match self.get_new_block_template().await { - Ok(val) => { - if !val.template.is_mempool_in_sync { - error!(target: LOG_TARGET, "Mempool is not in sync. Retrying to get new block template (try {})", loop_count); + async fn create_new_block_template(&mut self) -> Result<(NewBlockTemplate, u64), MmProxyError> { + let mut loop_count = 0; + loop { + let new_template = match self.get_new_block_template().await { + Ok(val) => { + if !val.template.is_mempool_in_sync { + warn!(target: LOG_TARGET, "Mempool is not in sync."); + + if loop_count > 3 { return Err(MmProxyError::FailedToGetBlockTemplate( "mempool not in sync".to_string(), )); + } else { + loop_count += 1; + continue; } - val - }, - Err(err) => { - error!(target: LOG_TARGET, "grpc get_new_block_template ({})", err.to_string()); - return Err(err); - }, - }; - 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: #{} (try {})", height, loop_count); - let (coinbase_output, coinbase_kernel) = self.get_coinbase(&new_template).await?; - - let 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 (try {})", loop_count); - - block_templates - .save_new_block_template_if_key_unique( - best_block_hash.to_vec(), - new_template.clone(), - template_with_coinbase.clone(), - ) - .await; - - (template_with_coinbase, height) - }, - Some((new_template, template_with_coinbase)) => { - let height = new_template - .template - .header - .as_ref() - .map(|h| h.height) - .unwrap_or_default(); - debug!(target: LOG_TARGET, "Used existing new block template at height: #{} (try {})", height, loop_count); - (template_with_coinbase, height) - }, - }; - Ok((block_template_with_coinbase, height)) + } + val + }, + Err(err) => { + error!(target: LOG_TARGET, "grpc get_new_block_template ({})", err.to_string()); + return Err(err); + }, + }; + 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: #{} (try {})", height, loop_count); + let (coinbase_output, coinbase_kernel) = self.get_coinbase(&new_template).await?; + + let 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 (try {})", loop_count); + + return Ok((template_with_coinbase, height)); + } } /// Get new block from base node. @@ -345,28 +226,6 @@ impl BlockTemplateProtocol<'_> { Ok(NewBlockTemplateData { template, miner_data }) } - /// Check if the height is more than the actual tip. So if still makes sense to compute block for that height. - async fn check_expected_tip(&mut self, height: u64) -> Result { - let tip = self - .base_node_client - .clone() - .get_tip_info(grpc::Empty {}) - .await? - .into_inner(); - let tip_height = tip.metadata.as_ref().map(|m| m.best_block_height).unwrap_or(0); - - if height <= tip_height { - warn!( - target: LOG_TARGET, - "Base node received next block (height={}) that has invalidated the block template (height={})", - tip_height, - height - ); - return Ok(false); - } - Ok(true) - } - /// Get coinbase transaction for the [template](NewBlockTemplateData). async fn get_coinbase( &mut self, @@ -392,21 +251,6 @@ impl BlockTemplateProtocol<'_> { .await?; Ok((coinbase_output, coinbase_kernel)) } - - 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())) - } } /// This is an interim solution to calculate the merkle root for the aux chains when multiple aux chains will be @@ -485,7 +329,7 @@ fn add_monero_data( /// Private convenience container struct for new template data #[derive(Debug, Clone)] -pub struct NewBlockTemplateData { +pub(crate) struct NewBlockTemplateData { pub template: grpc::NewBlockTemplate, pub miner_data: grpc::MinerData, } @@ -497,10 +341,10 @@ impl NewBlockTemplateData { } /// The AuxChainMerkleRoot is a 32 byte hash -pub type AuxChainMr = MaxSizeBytes<32>; +pub(crate) type AuxChainMr = MaxSizeBytes<32>; /// Final outputs for required for merge mining #[derive(Debug, Clone)] -pub struct FinalBlockTemplateData { +pub(crate) struct FinalBlockTemplateData { pub template: BlockTemplateData, pub target_difficulty: Difficulty, pub blockhashing_blob: String, @@ -511,7 +355,7 @@ pub struct FinalBlockTemplateData { /// Container struct for monero mining data inputs obtained from monerod #[derive(Clone)] -pub struct MoneroMiningData { +pub(crate) struct MoneroMiningData { pub seed_hash: FixedByteArray, pub blocktemplate_blob: String, pub difficulty: u64, diff --git a/applications/minotari_merge_mining_proxy/src/lib.rs b/applications/minotari_merge_mining_proxy/src/lib.rs index 95e975c245..90ff293bce 100644 --- a/applications/minotari_merge_mining_proxy/src/lib.rs +++ b/applications/minotari_merge_mining_proxy/src/lib.rs @@ -24,17 +24,12 @@ minotari_app_utilities::deny_non_64_bit_archs!(); mod block_template_data; -mod block_template_protocol; +mod block_template_manager; mod cli; pub use cli::Cli; mod common; mod config; mod error; -mod proxy; -mod run_merge_miner; -use run_merge_miner::start_merge_miner; mod monero_fail; - -pub async fn merge_miner(cli: Cli) -> Result<(), anyhow::Error> { - start_merge_miner(cli).await -} +mod proxy; +pub mod run_merge_miner; diff --git a/applications/minotari_merge_mining_proxy/src/main.rs b/applications/minotari_merge_mining_proxy/src/main.rs index b09dc15743..221591d7d4 100644 --- a/applications/minotari_merge_mining_proxy/src/main.rs +++ b/applications/minotari_merge_mining_proxy/src/main.rs @@ -23,7 +23,7 @@ use minotari_merge_mining_proxy::Cli; mod block_template_data; -mod block_template_protocol; +mod block_template_manager; mod cli; mod common; mod config; diff --git a/applications/minotari_merge_mining_proxy/src/proxy/inner.rs b/applications/minotari_merge_mining_proxy/src/proxy/inner.rs index d1a6d8793a..ecb5931e12 100644 --- a/applications/minotari_merge_mining_proxy/src/proxy/inner.rs +++ b/applications/minotari_merge_mining_proxy/src/proxy/inner.rs @@ -21,7 +21,6 @@ // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use std::{ - cmp, convert::TryInto, str::FromStr, sync::{ @@ -32,11 +31,12 @@ use std::{ time::Instant, }; +use blake2::{digest::Update, Blake2s256, Digest}; use borsh::BorshSerialize; use bytes::Bytes; use hyper::{header::HeaderValue, Body, Request, Response, StatusCode, Uri}; use log::error; -use minotari_app_grpc::{tari_rpc, tari_rpc::SubmitBlockRequest}; +use minotari_app_grpc::tari_rpc::{self, GetTipInfoRequest, SubmitBlockRequest}; use minotari_app_utilities::parse_miner_input::{BaseNodeGrpcClient, ShaP2PoolGrpcClient}; use monero::Hash; use rand::random; @@ -54,7 +54,7 @@ use url::Url; use crate::{ block_template_data::BlockTemplateRepository, - block_template_protocol::{BlockTemplateProtocol, MoneroMiningData}, + block_template_manager::{BlockTemplateManager, MoneroMiningData}, common::{json_rpc, monero_rpc::CoreRpcErrorCode, proxy, proxy::convert_json_to_hyper_json_response}, config::{MergeMiningProxyConfig, MonerodFallback}, error::MmProxyError, @@ -112,39 +112,61 @@ impl InnerService { )); } - let mut base_node_client = self.base_node_client.clone(); - trace!(target: LOG_TARGET, "Successful connection to base node GRPC"); + let monero_height = json["height"].as_u64().unwrap_or_default(); + let monero_hash: Vec = Hex::from_hex(json["hash"].as_str().unwrap_or_default()).unwrap_or_default(); + let base_node_height; + let base_node_hash; + let mut p2pool_height = 0; + let mut p2pool_hash = vec![]; + + if let Some(mut p2pool_client) = self.p2pool_client.clone() { + let p2pool_resp = p2pool_client.get_tip_info(GetTipInfoRequest {}).await?; + let res = p2pool_resp.into_inner(); + + base_node_height = res.node_height; + base_node_hash = res.node_tip_hash.clone(); + p2pool_height = res.p2pool_rx_height; + p2pool_hash = res.p2pool_rx_tip_hash.clone(); + } else { + let mut base_node_client = self.base_node_client.clone(); + trace!(target: LOG_TARGET, "Successful connection to base node GRPC"); - let result = - base_node_client - .get_tip_info(tari_rpc::Empty {}) - .await - .map_err(|err| MmProxyError::GrpcRequestError { + let result = base_node_client.get_tip_info(tari_rpc::Empty {}).await.map_err(|err| { + MmProxyError::GrpcRequestError { status: err, details: "get_tip_info failed".to_string(), - })?; - let height = result - .get_ref() - .metadata - .as_ref() - .map(|meta| meta.best_block_height) - .ok_or(MmProxyError::GrpcResponseMissingField("base node metadata"))?; - if result.get_ref().initial_sync_achieved != self.initial_sync_achieved.load(Ordering::SeqCst) { - self.initial_sync_achieved - .store(result.get_ref().initial_sync_achieved, Ordering::SeqCst); - debug!( - target: LOG_TARGET, - "Minotari base node initial sync status change to {}", - result.get_ref().initial_sync_achieved - ); + } + })?; + let res = result.into_inner(); + + base_node_height = res.metadata.as_ref().map(|m| m.best_block_height).unwrap_or_default(); + base_node_hash = res + .metadata + .as_ref() + .map(|m| m.best_block_hash.clone()) + .unwrap_or_default(); } - info!( target: LOG_TARGET, - "Monero height = #{}, Minotari base node height = #{}", json["height"], height + "Monero height = #{}, Minotari base node height = #{}, P2pool height: #{}", monero_height, base_node_height, p2pool_height ); - json["height"] = json!(cmp::max(json["height"].as_i64().unwrap_or_default(), height as i64)); + // Add them together. You could multiply them a factor, but xmrig will generally just check if the height or the + // hash is different. + let reported_height = p2pool_height + monero_height + base_node_height; + + // As of xmrig 6.22.0, the block hash is stored separately and does not need to match the block template they + // are mining, but if that changes in future, you might need to return the monero hash. + + let hash: Vec = Blake2s256::new() + .chain(&monero_hash) + .chain(&base_node_hash) + .chain(&p2pool_hash) + .finalize() + .to_vec(); + + json["height"] = json!(reported_height as i64); + json["hash"] = json!(&(hash).to_hex()); Ok(proxy::into_response(parts, &json)) } @@ -398,14 +420,13 @@ impl InnerService { } } - let new_block_protocol = BlockTemplateProtocol::new( + let new_block_manager = BlockTemplateManager::try_create( &mut grpc_client, self.p2pool_client.clone(), self.config.clone(), self.consensus_manager.clone(), self.wallet_payment_address.clone(), - ) - .await?; + )?; let seed_hash = FixedByteArray::from_hex(&monerod_resp["result"]["seed_hash"].to_string().replace('\"', "")) .map_err(|err| MmProxyError::InvalidMonerodResponse(format!("seed hash hex is invalid: {}", err)))?; @@ -419,10 +440,14 @@ impl InnerService { difficulty, }; - let final_block_template_data = new_block_protocol - .get_next_tari_block_template(monero_mining_data, &self.block_templates) + let final_block_template_data = new_block_manager + .get_next_tari_block_template(monero_mining_data) .await?; + self.block_templates + .save_final_block_template_if_key_unique(final_block_template_data.clone()) + .await; + 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(); @@ -1048,7 +1073,6 @@ impl InnerService { request: Request, ) -> Result, MmProxyError> { let start = Instant::now(); - debug!( target: LOG_TARGET, "[handle request] '{}' method: {}, uri: {}, headers: {:?}, body: {}", diff --git a/applications/minotari_merge_mining_proxy/src/proxy/service.rs b/applications/minotari_merge_mining_proxy/src/proxy/service.rs index f4b0b6e2e1..9a20ee974d 100644 --- a/applications/minotari_merge_mining_proxy/src/proxy/service.rs +++ b/applications/minotari_merge_mining_proxy/src/proxy/service.rs @@ -52,7 +52,7 @@ pub struct MergeMiningProxyService { } impl MergeMiningProxyService { - pub fn new( + pub fn try_create( config: MergeMiningProxyConfig, http_client: reqwest::Client, base_node_client: BaseNodeGrpcClient, diff --git a/applications/minotari_merge_mining_proxy/src/run_merge_miner.rs b/applications/minotari_merge_mining_proxy/src/run_merge_miner.rs index 273e3cab0a..2ceb1fdc90 100644 --- a/applications/minotari_merge_mining_proxy/src/run_merge_miner.rs +++ b/applications/minotari_merge_mining_proxy/src/run_merge_miner.rs @@ -111,10 +111,12 @@ pub async fn start_merge_miner(cli: Cli) -> Result<(), anyhow::Error> { } info!(target: LOG_TARGET, "Configuration: {:?}", config); + let agent = concat!("minotari_mm_proxy/", env!("CARGO_PKG_VERSION")); let client = reqwest::Client::builder() .connect_timeout(Duration::from_secs(5)) .timeout(Duration::from_secs(10)) - .pool_max_idle_per_host(25) + .user_agent(agent) + .tcp_keepalive(Duration::from_secs(60)) .build() .map_err(MmProxyError::ReqwestError)?; @@ -156,7 +158,7 @@ pub async fn start_merge_miner(cli: Cli) -> Result<(), anyhow::Error> { let listen_addr = multiaddr_to_socketaddr(&config.listener_address)?; let randomx_factory = RandomXFactory::new(config.max_randomx_vms); - let randomx_service = MergeMiningProxyService::new( + let randomx_service = MergeMiningProxyService::try_create( config, client, base_node_client, diff --git a/base_layer/core/src/chain_storage/lmdb_db/lmdb_db.rs b/base_layer/core/src/chain_storage/lmdb_db/lmdb_db.rs index 97385f0806..b4c3853b11 100644 --- a/base_layer/core/src/chain_storage/lmdb_db/lmdb_db.rs +++ b/base_layer/core/src/chain_storage/lmdb_db/lmdb_db.rs @@ -2900,7 +2900,7 @@ fn run_migrations(db: &LMDBDatabase) -> Result<(), ChainStorageError> { } let txn = db.write_transaction()?; - info!(target: LOG_TARGET, "Migrated database to version {}", MIGRATION_VERSION); + info!(target: LOG_TARGET, "Migrated database to version {}", n); lmdb_replace( &txn, &db.metadata_db, diff --git a/integration_tests/src/merge_mining_proxy.rs b/integration_tests/src/merge_mining_proxy.rs index 44d79535cd..d24560a5c8 100644 --- a/integration_tests/src/merge_mining_proxy.rs +++ b/integration_tests/src/merge_mining_proxy.rs @@ -23,7 +23,7 @@ use std::{convert::TryInto, thread}; use minotari_app_utilities::common_cli_args::CommonCliArgs; -use minotari_merge_mining_proxy::{merge_miner, Cli}; +use minotari_merge_mining_proxy::{run_merge_miner::start_merge_miner, Cli}; use minotari_wallet_grpc_client::{grpc, WalletGrpcClient}; use serde_json::{json, Value}; use tari_common::{configuration::Network, network_check::set_network_if_choice_valid}; @@ -139,7 +139,7 @@ impl MergeMiningProxyProcess { non_interactive_mode: false, }; let rt = runtime::Builder::new_multi_thread().enable_all().build().unwrap(); - if let Err(e) = rt.block_on(merge_miner(cli)) { + if let Err(e) = rt.block_on(start_merge_miner(cli)) { println!("Error running merge mining proxy : {:?}", e); panic!("Error running merge mining proxy"); }