Skip to content

Commit

Permalink
feat: add merge mining proxy support for p2pool (#6474)
Browse files Browse the repository at this point in the history
Adds merge mining support for p2pool

---------

Co-authored-by: SW van Heerden <[email protected]>
  • Loading branch information
stringhandler and SWvheerden authored Aug 30, 2024
1 parent da5b443 commit bed32d5
Show file tree
Hide file tree
Showing 8 changed files with 205 additions and 112 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
16 changes: 11 additions & 5 deletions applications/minotari_merge_mining_proxy/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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" }

Expand Down Expand Up @@ -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"
markup5ever = "0.11.0"
Original file line number Diff line number Diff line change
Expand Up @@ -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<T: AsRef<[u8]>>(&self, merge_mining_hash: T) -> Option<FinalBlockTemplateData> {
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.
Expand Down Expand Up @@ -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())
Expand Down Expand Up @@ -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 {}
Expand All @@ -238,7 +229,6 @@ pub struct BlockTemplateDataBuilder {
tari_difficulty: Option<u64>,
tari_merge_mining_hash: Option<FixedHash>,
aux_chain_hashes: AuxChainHashes,
new_block_template: Option<grpc::NewBlockTemplate>,
}

impl BlockTemplateDataBuilder {
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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,
Expand All @@ -325,7 +307,6 @@ impl BlockTemplateDataBuilder {
tari_difficulty,
tari_merge_mining_hash,
aux_chain_hashes: self.aux_chain_hashes,
new_block_template,
})
}
}
Expand Down Expand Up @@ -355,16 +336,14 @@ 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_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,
Expand Down
185 changes: 106 additions & 79 deletions applications/minotari_merge_mining_proxy/src/block_template_protocol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::{
Expand Down Expand Up @@ -53,6 +54,7 @@ const LOG_TARGET: &str = "minotari_mm_proxy::proxy::block_template_protocol";
pub struct BlockTemplateProtocol<'a> {
config: Arc<MergeMiningProxyConfig>,
base_node_client: &'a mut BaseNodeGrpcClient,
p2pool_client: Option<ShaP2PoolGrpcClient>,
key_manager: MemoryDbKeyManager,
wallet_payment_address: TariAddress,
consensus_manager: ConsensusManager,
Expand All @@ -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<ShaP2PoolGrpcClient>,
config: Arc<MergeMiningProxyConfig>,
consensus_manager: ConsensusManager,
wallet_payment_address: TariAddress,
Expand All @@ -69,6 +72,7 @@ impl<'a> BlockTemplateProtocol<'a> {
Ok(Self {
config,
base_node_client,
p2pool_client,
key_manager,
wallet_payment_address,
consensus_manager,
Expand Down Expand Up @@ -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
Expand All @@ -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)
Expand All @@ -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,
Expand Down Expand Up @@ -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<FinalBlockTemplateData, MmProxyError> {
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
Expand Down
Loading

0 comments on commit bed32d5

Please sign in to comment.