diff --git a/Cargo.toml b/Cargo.toml index f92c05b76d..8fed89ad35 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,7 +33,7 @@ members = [ "applications/minotari_ledger_wallet/comms", "applications/minotari_ledger_wallet/common", "integration_tests", - "hashing", + "hashing" ] # Add here until we move to edition=2021 diff --git a/applications/minotari_merge_mining_proxy/Cargo.toml b/applications/minotari_merge_mining_proxy/Cargo.toml index a669847631..89731cce8d 100644 --- a/applications/minotari_merge_mining_proxy/Cargo.toml +++ b/applications/minotari_merge_mining_proxy/Cargo.toml @@ -12,14 +12,20 @@ default = [] [dependencies] minotari_app_grpc = { path = "../minotari_app_grpc" } -minotari_app_utilities = { path = "../minotari_app_utilities", features = ["miner_input"] } +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"] } -tari_key_manager = { path = "../../base_layer/key_manager", features = ["key_manager_service"] } +tari_core = { path = "../../base_layer/core", default-features = false, features = [ + "transactions", +] } +tari_key_manager = { path = "../../base_layer/key_manager", features = [ + "key_manager_service", +] } tari_max_size = { path = "../../infrastructure/max_size" } tari_utilities = { version = "0.7" } @@ -48,7 +54,7 @@ url = "2.1.1" scraper = "0.19.0" [build-dependencies] -tari_features = { path = "../../common/tari_features", version = "1.3.0-pre.0"} +tari_features = { path = "../../common/tari_features", version = "1.3.0-pre.0" } [dev-dependencies] -markup5ever = "0.11.0" \ No newline at end of file +markup5ever = "0.11.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 e013b05872..712945fec9 100644 --- a/applications/minotari_merge_mining_proxy/src/block_template_data.rs +++ b/applications/minotari_merge_mining_proxy/src/block_template_data.rs @@ -104,15 +104,7 @@ impl BlockTemplateRepository { /// Return [BlockTemplateData] with the associated hash. None if the hash is not stored. pub async fn get_final_template>(&self, merge_mining_hash: T) -> Option { let b = self.blocks.read().await; - b.get(merge_mining_hash.as_ref()).map(|item| { - trace!( - target: LOG_TARGET, - "Retrieving block template at height #{} with merge mining hash: {:?}", - item.data.clone().template.new_block_template.header.unwrap_or_default().height, - hex::encode(merge_mining_hash.as_ref()) - ); - item.data.clone() - }) + 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. @@ -165,7 +157,7 @@ impl BlockTemplateRepository { let b = self.blocks.read().await; b.values() .find(|item| { - let header = item.data.template.new_block_template.header.clone().unwrap_or_default(); + 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()) @@ -223,7 +215,6 @@ pub struct BlockTemplateData { pub tari_merge_mining_hash: FixedHash, #[allow(dead_code)] pub aux_chain_hashes: AuxChainHashes, - pub new_block_template: grpc::NewBlockTemplate, } impl BlockTemplateData {} @@ -238,7 +229,6 @@ pub struct BlockTemplateDataBuilder { tari_difficulty: Option, tari_merge_mining_hash: Option, aux_chain_hashes: AuxChainHashes, - new_block_template: Option, } impl BlockTemplateDataBuilder { @@ -281,11 +271,6 @@ 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 @@ -313,9 +298,6 @@ impl BlockTemplateDataBuilder { 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, @@ -325,7 +307,6 @@ impl BlockTemplateDataBuilder { tari_difficulty, tari_merge_mining_hash, aux_chain_hashes: self.aux_chain_hashes, - new_block_template, }) } } @@ -355,7 +336,6 @@ 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()) @@ -363,8 +343,7 @@ pub mod test { .monero_difficulty(123456) .tari_difficulty(12345) .tari_merge_mining_hash(hash) - .aux_hashes(AuxChainHashes::try_from(vec![monero::Hash::from_slice(hash.as_slice())]).unwrap()) - .new_block_template(new_block_template); + .aux_hashes(AuxChainHashes::try_from(vec![monero::Hash::from_slice(hash.as_slice())]).unwrap()); let block_template_data = btdb.build().unwrap(); FinalBlockTemplateData { template: block_template_data, 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 291a4adbdb..07621fae0a 100644 --- a/applications/minotari_merge_mining_proxy/src/block_template_protocol.rs +++ b/applications/minotari_merge_mining_proxy/src/block_template_protocol.rs @@ -24,7 +24,8 @@ use std::{cmp, convert::TryFrom, sync::Arc}; use log::*; -use minotari_app_utilities::parse_miner_input::BaseNodeGrpcClient; +use minotari_app_grpc::tari_rpc::{GetNewBlockRequest, MinerData, NewBlockTemplate}; +use minotari_app_utilities::parse_miner_input::{BaseNodeGrpcClient, ShaP2PoolGrpcClient}; use minotari_node_grpc_client::grpc; use tari_common_types::{tari_address::TariAddress, types::FixedHash}; use tari_core::{ @@ -53,6 +54,7 @@ const LOG_TARGET: &str = "minotari_mm_proxy::proxy::block_template_protocol"; pub struct BlockTemplateProtocol<'a> { config: Arc, base_node_client: &'a mut BaseNodeGrpcClient, + p2pool_client: Option, key_manager: MemoryDbKeyManager, wallet_payment_address: TariAddress, consensus_manager: ConsensusManager, @@ -61,6 +63,7 @@ pub struct BlockTemplateProtocol<'a> { impl<'a> BlockTemplateProtocol<'a> { pub async fn new( base_node_client: &'a mut BaseNodeGrpcClient, + p2pool_client: Option, config: Arc, consensus_manager: ConsensusManager, wallet_payment_address: TariAddress, @@ -69,6 +72,7 @@ impl<'a> BlockTemplateProtocol<'a> { Ok(Self { config, base_node_client, + p2pool_client, key_manager, wallet_payment_address, consensus_manager, @@ -124,87 +128,59 @@ impl BlockTemplateProtocol<'_> { ); (data, height) } else { - let (new_template, block_template_with_coinbase, height) = match block_templates - .get_new_template(best_block_hash) - .await - { + let block = match self.p2pool_client.as_mut() { + Some(client) => { + let block_result = client.get_new_block(GetNewBlockRequest::default()).await?.into_inner(); + block_result + .block + .ok_or_else(|| MmProxyError::FailedToGetBlockTemplate("block result".to_string()))? + }, None => { - let new_template = match self.get_new_block_template().await { - Ok(val) => val, + 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_template ({})", err.to_string()); + error!(target: LOG_TARGET, "grpc get_new_block ({})", 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; - - (new_template, 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); - (new_template, template_with_coinbase, height) + } }, }; - let block = 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, - "Chain tip has progressed past template height {}. Fetching a new block template (try {}).", - height, loop_count - ); - continue; - }, - Err(err) => { - error!(target: LOG_TARGET, "grpc get_new_block ({})", err.to_string()); - return Err(err); - }, - }; + let height = block + .block + .as_ref() + .map(|b| b.header.as_ref().map(|h| h.height).unwrap_or_default()) + .unwrap_or_default(); - ( - add_monero_data(block, monero_mining_data.clone(), new_template)?, - height, - ) + let miner_data = block + .miner_data + .as_ref() + .cloned() + .ok_or_else(|| MmProxyError::GrpcResponseMissingField("miner_data"))?; + + (add_monero_data(block, monero_mining_data.clone(), miner_data)?, height) }; block_templates @@ -229,7 +205,7 @@ impl BlockTemplateProtocol<'_> { info!(target: LOG_TARGET, "Block template for height: #{} (try {}), block hash: `{}`, {}", final_template_data - .template.new_block_template + .template.tari_block .header .as_ref() .map(|h| h.height) @@ -250,6 +226,58 @@ impl BlockTemplateProtocol<'_> { } } + 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) => 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)) + } + /// Get new block from base node. async fn get_new_block( &mut self, @@ -377,26 +405,25 @@ pub fn calculate_aux_chain_merkle_root(hashes: AuxChainHashes) -> Result<(monero fn add_monero_data( tari_block_result: grpc::GetNewBlockResult, monero_mining_data: MoneroMiningData, - template_data: NewBlockTemplateData, + miner_data: MinerData, ) -> Result { let merge_mining_hash = FixedHash::try_from(tari_block_result.merge_mining_hash.clone()) .map_err(|e| MmProxyError::ConversionError(e.to_string()))?; let aux_chain_hashes = AuxChainHashes::try_from(vec![monero::Hash::from_slice(merge_mining_hash.as_slice())])?; - let tari_difficulty = template_data.miner_data.target_difficulty; + let tari_difficulty = 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) + .tari_miner_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 diff --git a/applications/minotari_merge_mining_proxy/src/config.rs b/applications/minotari_merge_mining_proxy/src/config.rs index ed76f3f692..30badfa0d3 100644 --- a/applications/minotari_merge_mining_proxy/src/config.rs +++ b/applications/minotari_merge_mining_proxy/src/config.rs @@ -92,6 +92,8 @@ pub struct MergeMiningProxyConfig { pub wallet_payment_address: String, /// Range proof type - revealed_value or bullet_proof_plus: (default = revealed_value) pub range_proof_type: RangeProofType, + /// Use p2pool to submit and get block templates + pub p2pool_enabled: bool, } impl Default for MergeMiningProxyConfig { @@ -118,6 +120,7 @@ impl Default for MergeMiningProxyConfig { config_dir: PathBuf::from("config/merge_mining_proxy"), wallet_payment_address: TariAddress::default().to_base58(), range_proof_type: RangeProofType::RevealedValue, + p2pool_enabled: false, } } } diff --git a/applications/minotari_merge_mining_proxy/src/proxy.rs b/applications/minotari_merge_mining_proxy/src/proxy.rs index c0d76cca54..1ce4d302a2 100644 --- a/applications/minotari_merge_mining_proxy/src/proxy.rs +++ b/applications/minotari_merge_mining_proxy/src/proxy.rs @@ -39,7 +39,8 @@ use bytes::Bytes; use hyper::{header::HeaderValue, service::Service, Body, Method, Request, Response, StatusCode, Uri}; use json::json; use jsonrpc::error::StandardError; -use minotari_app_utilities::parse_miner_input::BaseNodeGrpcClient; +use minotari_app_grpc::tari_rpc::SubmitBlockRequest; +use minotari_app_utilities::parse_miner_input::{BaseNodeGrpcClient, ShaP2PoolGrpcClient}; use minotari_node_grpc_client::grpc; use reqwest::{ResponseBuilderExt, Url}; use serde_json as json; @@ -76,6 +77,7 @@ impl MergeMiningProxyService { config: MergeMiningProxyConfig, http_client: reqwest::Client, base_node_client: BaseNodeGrpcClient, + p2pool_client: Option, block_templates: BlockTemplateRepository, randomx_factory: RandomXFactory, wallet_payment_address: TariAddress, @@ -88,6 +90,7 @@ impl MergeMiningProxyService { block_templates, http_client, base_node_client, + p2pool_client, initial_sync_achieved: Arc::new(AtomicBool::new(false)), current_monerod_server: Arc::new(RwLock::new(None)), last_assigned_monerod_server: Arc::new(RwLock::new(None)), @@ -160,6 +163,7 @@ struct InnerService { block_templates: BlockTemplateRepository, http_client: reqwest::Client, base_node_client: BaseNodeGrpcClient, + p2pool_client: Option, initial_sync_achieved: Arc, current_monerod_server: Arc>>, last_assigned_monerod_server: Arc>>, @@ -292,6 +296,7 @@ impl InnerService { .try_into() .map_err(MmProxyError::ConversionError)?; let mut base_node_client = self.base_node_client.clone(); + let p2pool_client = self.p2pool_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"); @@ -313,8 +318,29 @@ impl InnerService { }; let height = tari_header_mut.height; + info!( + target: LOG_TARGET, + "Checking if we must submit block #{} to Minotari node with achieved target {} and expected target: {}", + height, + achieved_target, + block_data.template.tari_difficulty + ); if achieved_target >= block_data.template.tari_difficulty { - match base_node_client.submit_block(block_data.template.tari_block).await { + let resp = match p2pool_client { + Some(mut client) => { + info!(target: LOG_TARGET, "Submiting to p2pool"); + client + .submit_block(SubmitBlockRequest { + block: Some(block_data.template.tari_block), + + wallet_payment_address: self.wallet_payment_address.to_hex(), + }) + .await + }, + None => base_node_client.submit_block(block_data.template.tari_block).await, + }; + + match resp { Ok(resp) => { if self.config.submit_to_origin { json_resp = json_rpc::success_response( @@ -451,6 +477,7 @@ impl InnerService { let new_block_protocol = BlockTemplateProtocol::new( &mut grpc_client, + self.p2pool_client.clone(), self.config.clone(), self.consensus_manager.clone(), self.wallet_payment_address.clone(), 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 34ceb2a856..5f226bbe0e 100644 --- a/applications/minotari_merge_mining_proxy/src/run_merge_miner.rs +++ b/applications/minotari_merge_mining_proxy/src/run_merge_miner.rs @@ -25,12 +25,13 @@ use std::{convert::Infallible, str::FromStr}; use futures::future; use hyper::{service::make_service_fn, Server}; use log::*; -use minotari_app_grpc::tls::protocol_string; +use minotari_app_grpc::{tari_rpc::sha_p2_pool_client::ShaP2PoolClient, tls::protocol_string}; use minotari_app_utilities::parse_miner_input::{ base_node_socket_address, verify_base_node_grpc_mining_responses, wallet_payment_address, BaseNodeGrpcClient, + ShaP2PoolGrpcClient, }; use minotari_node_grpc_client::{grpc, grpc::base_node_client::BaseNodeClient}; use minotari_wallet_grpc_client::ClientAuthenticationInterceptor; @@ -82,6 +83,18 @@ pub async fn start_merge_miner(cli: Cli) -> Result<(), anyhow::Error> { return Err(e.into()); }, }; + + let p2pool_client = if config.p2pool_enabled { + Some(connect_sha_p2pool(&config).await.map_err(|e| { + error!(target: LOG_TARGET, "Could not connect to p2pool node: {}", e); + let msg = "Could not connect to p2pool node. \nIs the p2pool node's gRPC running? Try running it with \ + `--enable-grpc` or enable it in the config."; + println!("{}", msg); + e + })?) + } else { + None + }; if let Err(e) = verify_base_node_responses(&mut base_node_client).await { if let MmProxyError::BaseNodeNotResponding(_) = e { error!(target: LOG_TARGET, "{}", e.to_string()); @@ -101,6 +114,7 @@ pub async fn start_merge_miner(cli: Cli) -> Result<(), anyhow::Error> { config, client, base_node_client, + p2pool_client, BlockTemplateRepository::new(), randomx_factory, wallet_payment_address, @@ -175,3 +189,39 @@ async fn connect_base_node(config: &MergeMiningProxyConfig) -> Result Result { + // TODO: Merge this code in the sha miner + let socketaddr = base_node_socket_address(config.base_node_grpc_address.clone(), config.network)?; + let base_node_addr = format!( + "{}{}", + protocol_string(config.base_node_grpc_tls_domain_name.is_some()), + socketaddr, + ); + + info!(target: LOG_TARGET, "👛 Connecting to p2pool node at {}", base_node_addr); + let mut endpoint = Endpoint::from_str(&base_node_addr)?; + + if let Some(domain_name) = config.base_node_grpc_tls_domain_name.as_ref() { + let pem = tokio::fs::read(config.config_dir.join(&config.base_node_grpc_ca_cert_filename)) + .await + .map_err(|e| MmProxyError::TlsConnectionError(e.to_string()))?; + let ca = Certificate::from_pem(pem); + + let tls = ClientTlsConfig::new().ca_certificate(ca).domain_name(domain_name); + endpoint = endpoint + .tls_config(tls) + .map_err(|e| MmProxyError::TlsConnectionError(e.to_string()))?; + } + + let channel = endpoint + .connect() + .await + .map_err(|e| MmProxyError::TlsConnectionError(e.to_string()))?; + let node_conn = ShaP2PoolClient::with_interceptor( + channel, + ClientAuthenticationInterceptor::create(&config.base_node_grpc_authentication)?, + ); + + Ok(node_conn) +} diff --git a/src/nodeenv b/src/nodeenv new file mode 160000 index 0000000000..a6585e9a63 --- /dev/null +++ b/src/nodeenv @@ -0,0 +1 @@ +Subproject commit a6585e9a63e1601c4a37f3a1bb8fd0722dd6b51c