Skip to content

Commit

Permalink
feat!: add aux chain support for merge mining (#5976)
Browse files Browse the repository at this point in the history
Description
---
This adds aux chain support for merge mining monero

Fixes: #5975 

Motivation and Context
---
This allows tari to be merged mined with multiple other chains, up to
253 other chains.
See:
https://github.com/SChernykh/p2pool/blob/merge-mining/docs/MERGE_MINING.MD#proposed-rpc-api

How Has This Been Tested?
---
  • Loading branch information
SWvheerden committed Nov 29, 2023
1 parent 30d15fa commit 6723dc7
Show file tree
Hide file tree
Showing 18 changed files with 772 additions and 119 deletions.
32 changes: 28 additions & 4 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions applications/minotari_merge_mining_proxy/Cargo.toml
Expand Up @@ -35,6 +35,7 @@ hex = "0.4.2"
hyper = "0.14.12"
jsonrpc = "0.12.0"
log = { version = "0.4.8", features = ["std"] }
monero = { version = "0.18" }
reqwest = { version = "0.11.4", features = ["json"] }
serde = { version = "1.0.106", features = ["derive"] }
serde_json = "1.0.57"
Expand Down
Expand Up @@ -28,6 +28,7 @@ use std::{collections::HashMap, sync::Arc};
use chrono::Duration;
use chrono::{self, DateTime, Utc};
use minotari_node_grpc_client::grpc;
use tari_common_types::types::FixedHash;
use tari_core::proof_of_work::monero_rx::FixedByteArray;
use tokio::sync::RwLock;
use tracing::trace;
Expand Down Expand Up @@ -125,6 +126,8 @@ pub struct BlockTemplateData {
pub tari_miner_data: grpc::MinerData,
pub monero_difficulty: u64,
pub tari_difficulty: u64,
pub tari_hash: FixedHash,
pub aux_chain_hashes: Vec<monero::Hash>,
}

impl BlockTemplateData {}
Expand All @@ -137,6 +140,8 @@ pub struct BlockTemplateDataBuilder {
tari_miner_data: Option<grpc::MinerData>,
monero_difficulty: Option<u64>,
tari_difficulty: Option<u64>,
tari_hash: Option<FixedHash>,
aux_chain_hashes: Vec<monero::Hash>,
}

impl BlockTemplateDataBuilder {
Expand Down Expand Up @@ -169,6 +174,16 @@ impl BlockTemplateDataBuilder {
self
}

pub fn tari_hash(mut self, hash: FixedHash) -> Self {
self.tari_hash = Some(hash);
self
}

pub fn aux_hashes(mut self, aux_chain_hashes: Vec<monero::Hash>) -> Self {
self.aux_chain_hashes = aux_chain_hashes;
self
}

/// Build a new [BlockTemplateData], all the values have to be set.
///
/// # Errors
Expand All @@ -190,13 +205,21 @@ 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
.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()));
};

Ok(BlockTemplateData {
monero_seed,
tari_block,
tari_miner_data,
monero_difficulty,
tari_difficulty,
tari_hash,
aux_chain_hashes: self.aux_chain_hashes,
})
}
}
Expand All @@ -216,6 +239,7 @@ pub mod test {
let header = BlockHeader::new(100);
let body = AggregateBody::empty();
let block = Block::new(header, body);
let hash = block.hash();
let miner_data = grpc::MinerData {
reward: 10000,
target_difficulty: 600000,
Expand All @@ -227,7 +251,9 @@ pub mod test {
.tari_block(block.try_into().unwrap())
.tari_miner_data(miner_data)
.monero_difficulty(123456)
.tari_difficulty(12345);
.tari_difficulty(12345)
.tari_hash(hash)
.aux_hashes(vec![monero::Hash::from_slice(hash.as_slice())]);
btdb.build().unwrap()
}

Expand Down
Expand Up @@ -21,13 +21,12 @@
// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

//! Methods for seting up a new block.

use std::{cmp, str::FromStr, sync::Arc};
use std::{cmp, convert::TryFrom, str::FromStr, sync::Arc};

use log::*;
use minotari_app_grpc::{authentication::ClientAuthenticationInterceptor, tari_rpc::base_node_client::BaseNodeClient};
use minotari_node_grpc_client::grpc;
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, Difficulty},
Expand Down Expand Up @@ -61,11 +60,11 @@ impl<'a> BlockTemplateProtocol<'a> {
pub async fn new(
base_node_client: &'a mut BaseNodeClient<InterceptedService<Channel, ClientAuthenticationInterceptor>>,
config: Arc<MergeMiningProxyConfig>,
consensus_manager: ConsensusManager,
) -> Result<BlockTemplateProtocol<'a>, MmProxyError> {
let key_manager = create_memory_db_key_manager();
let wallet_payment_address = TariAddress::from_str(&config.wallet_payment_address)
.map_err(|err| MmProxyError::WalletPaymentAddress(err.to_string()))?;
let consensus_manager = ConsensusManager::builder(config.network).build()?;
Ok(Self {
config,
base_node_client,
Expand Down Expand Up @@ -242,6 +241,11 @@ impl BlockTemplateProtocol<'_> {
.monero_seed(monero_mining_data.seed_hash)
.monero_difficulty(monero_mining_data.difficulty)
.tari_difficulty(tari_difficulty)
.tari_hash(
FixedHash::try_from(tari_block.merge_mining_hash.clone())
.map_err(|e| MmProxyError::MissingDataError(e.to_string()))?,
)
.aux_hashes(vec![monero::Hash::from_slice(&tari_block.merge_mining_hash)])
.build()?;

// Deserialize the block template blob
Expand All @@ -250,7 +254,15 @@ impl BlockTemplateProtocol<'_> {

debug!(target: LOG_TARGET, "Insert Merged Mining Tag",);
// Add the Tari merge mining tag to the retrieved block template
monero_rx::insert_merge_mining_tag_into_block(&mut monero_block, &tari_block.merge_mining_hash)?;
// 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, "Creating blockhashing blob from blocktemplate blob",);
// Must be done after the tag is inserted since it will affect the hash of the miner tx
Expand All @@ -266,12 +278,16 @@ impl BlockTemplateProtocol<'_> {
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: tari_block.merge_mining_hash,
merge_mining_hash,
aux_chain_hashes: vec![monero::Hash::from_slice(&tari_block.merge_mining_hash)],
aux_chain_mr: tari_block.merge_mining_hash,
})
}
}
Expand All @@ -296,7 +312,9 @@ pub struct FinalBlockTemplateData {
pub target_difficulty: Difficulty,
pub blockhashing_blob: String,
pub blocktemplate_blob: String,
pub merge_mining_hash: Vec<u8>,
pub merge_mining_hash: FixedHash,
pub aux_chain_hashes: Vec<monero::Hash>,
pub aux_chain_mr: Vec<u8>,
}

/// Container struct for monero mining data inputs obtained from monerod
Expand Down
39 changes: 25 additions & 14 deletions applications/minotari_merge_mining_proxy/src/proxy.rs
Expand Up @@ -43,11 +43,9 @@ use minotari_node_grpc_client::{grpc, grpc::base_node_client::BaseNodeClient};
use minotari_wallet_grpc_client::ClientAuthenticationInterceptor;
use reqwest::{ResponseBuilderExt, Url};
use serde_json as json;
use tari_core::proof_of_work::{
monero_rx,
monero_rx::FixedByteArray,
randomx_difficulty,
randomx_factory::RandomXFactory,
use tari_core::{
consensus::ConsensusManager,
proof_of_work::{monero_rx, monero_rx::FixedByteArray, randomx_difficulty, randomx_factory::RandomXFactory},
};
use tari_utilities::hex::Hex;
use tonic::{codegen::InterceptedService, transport::Channel};
Expand Down Expand Up @@ -79,9 +77,10 @@ impl MergeMiningProxyService {
base_node_client: BaseNodeClient<InterceptedService<Channel, ClientAuthenticationInterceptor>>,
block_templates: BlockTemplateRepository,
randomx_factory: RandomXFactory,
) -> Self {
) -> Result<Self, MmProxyError> {
debug!(target: LOG_TARGET, "Config: {:?}", config);
Self {
let consensus_manager = ConsensusManager::builder(config.network).build()?;
Ok(Self {
inner: InnerService {
config: Arc::new(config),
block_templates,
Expand All @@ -91,8 +90,9 @@ impl MergeMiningProxyService {
current_monerod_server: Arc::new(RwLock::new(None)),
last_assigned_monerod_server: Arc::new(RwLock::new(None)),
randomx_factory,
consensus_manager,
},
}
})
}
}

Expand Down Expand Up @@ -161,6 +161,7 @@ struct InnerService {
current_monerod_server: Arc<RwLock<Option<String>>>,
last_assigned_monerod_server: Arc<RwLock<Option<String>>>,
randomx_factory: RandomXFactory,
consensus_manager: ConsensusManager,
}

impl InnerService {
Expand Down Expand Up @@ -238,10 +239,12 @@ 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);
let hash = monero_rx::extract_tari_hash_from_block(&monero_block)?.ok_or_else(|| {
let hash = monero_rx::extract_aux_merkle_root_from_block(&monero_block)?.ok_or_else(|| {
MmProxyError::MissingDataError("Could not find Minotari header in coinbase".to_string())
})?;

Expand All @@ -262,8 +265,12 @@ impl InnerService {
continue;
},
};

let monero_data = monero_rx::construct_monero_data(monero_block, block_data.monero_seed.clone())?;
let monero_data = monero_rx::construct_monero_data(
monero_block,
block_data.monero_seed.clone(),
block_data.aux_chain_hashes.clone(),
block_data.tari_hash,
)?;

debug!(target: LOG_TARGET, "Monero PoW Data: {:?}", monero_data);

Expand All @@ -276,7 +283,7 @@ impl InnerService {
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)?;
let diff = randomx_difficulty(&tari_header, &self.randomx_factory, &gen_hash)?;
trace!(
target: LOG_TARGET,
"Finished calculate achieved Tari difficultly - achieved {} vs. target {}",
Expand Down Expand Up @@ -425,7 +432,8 @@ impl InnerService {
}
}

let new_block_protocol = BlockTemplateProtocol::new(&mut grpc_client, self.config.clone()).await?;
let new_block_protocol =
BlockTemplateProtocol::new(&mut grpc_client, self.config.clone(), self.consensus_manager.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)))?;
Expand Down Expand Up @@ -472,7 +480,10 @@ impl InnerService {
);

self.block_templates
.save(mining_hash, final_block_template_data.template)
.save(
final_block_template_data.aux_chain_mr,
final_block_template_data.template,
)
.await;

debug!(target: LOG_TARGET, "Returning template result: {}", monerod_resp);
Expand Down
Expand Up @@ -87,7 +87,7 @@ pub async fn start_merge_miner(cli: Cli) -> Result<(), anyhow::Error> {
base_node_client,
BlockTemplateRepository::new(),
randomx_factory,
);
)?;
let service = make_service_fn(|_conn| future::ready(Result::<_, Infallible>::Ok(randomx_service.clone())));

match Server::try_bind(&listen_addr) {
Expand Down

0 comments on commit 6723dc7

Please sign in to comment.