Skip to content

Commit

Permalink
feat(merge mining proxy): check achieved Monero difficulty before sub…
Browse files Browse the repository at this point in the history
…mitting to Tari basenode (#4019)

Description
---
This checks the achieved difficulty in the merge_mining_proxy before it submits a block to the tari base_node. 
This can be disabled by a config option.
  • Loading branch information
SWvheerden authored Apr 12, 2022
1 parent e59e657 commit b09fa76
Show file tree
Hide file tree
Showing 6 changed files with 91 additions and 51 deletions.
4 changes: 4 additions & 0 deletions applications/tari_merge_mining_proxy/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ pub struct MergeMiningProxyConfig {
pub listener_address: Multiaddr,
pub submit_to_origin: bool,
pub wait_for_initial_sync_at_startup: bool,
pub check_tari_difficulty_before_submit: bool,
pub max_randomx_vms: usize,
}

impl Default for MergeMiningProxyConfig {
Expand All @@ -52,6 +54,8 @@ impl Default for MergeMiningProxyConfig {
listener_address: "/ip4/127.0.0.1/tcp/18081".parse().unwrap(),
submit_to_origin: true,
wait_for_initial_sync_at_startup: true,
check_tari_difficulty_before_submit: true,
max_randomx_vms: 5,
}
}
}
Expand Down
4 changes: 3 additions & 1 deletion applications/tari_merge_mining_proxy/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ use tari_app_grpc::tari_rpc as grpc;
use tari_app_utilities::consts;
use tari_common::{initialize_logging, load_configuration, DefaultConfigLoader};
use tari_comms::utils::multiaddr::multiaddr_to_socketaddr;
use tari_core::proof_of_work::randomx_factory::RandomXFactory;
use tokio::time::Duration;

use crate::{
Expand Down Expand Up @@ -91,13 +92,14 @@ async fn main() -> Result<(), anyhow::Error> {
println!("Connecting to wallet at {}", wallet);
let wallet_client = grpc::wallet_client::WalletClient::connect(format!("http://{}", wallet)).await?;
let listen_addr = multiaddr_to_socketaddr(&config.listener_address)?;

let randomx_factory = RandomXFactory::new(config.max_randomx_vms);
let xmrig_service = MergeMiningProxyService::new(
config,
client,
base_node_client,
wallet_client,
BlockTemplateRepository::new(),
randomx_factory,
);
let service = make_service_fn(|_conn| future::ready(Result::<_, Infallible>::Ok(xmrig_service.clone())));

Expand Down
115 changes: 68 additions & 47 deletions applications/tari_merge_mining_proxy/src/proxy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

use std::{
cmp,
convert::TryInto,
future::Future,
pin::Pin,
sync::{
Expand All @@ -40,7 +41,12 @@ use jsonrpc::error::StandardError;
use reqwest::{ResponseBuilderExt, Url};
use serde_json as json;
use tari_app_grpc::tari_rpc as grpc;
use tari_core::proof_of_work::{monero_rx, monero_rx::FixedByteArray};
use tari_core::proof_of_work::{
monero_difficulty,
monero_rx,
monero_rx::FixedByteArray,
randomx_factory::RandomXFactory,
};
use tari_utilities::hex::Hex;
use tracing::{debug, error, info, instrument, trace, warn};

Expand Down Expand Up @@ -70,6 +76,7 @@ impl MergeMiningProxyService {
base_node_client: grpc::base_node_client::BaseNodeClient<tonic::transport::Channel>,
wallet_client: grpc::wallet_client::WalletClient<tonic::transport::Channel>,
block_templates: BlockTemplateRepository,
randomx_factory: RandomXFactory,
) -> Self {
debug!(target: LOG_TARGET, "Config: {:?}", config);
Self {
Expand All @@ -82,6 +89,7 @@ impl MergeMiningProxyService {
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)),
randomx_factory,
},
}
}
Expand Down Expand Up @@ -152,6 +160,7 @@ struct InnerService {
initial_sync_achieved: Arc<AtomicBool>,
current_monerod_server: Arc<RwLock<Option<String>>>,
last_assigned_monerod_server: Arc<RwLock<Option<String>>>,
randomx_factory: RandomXFactory,
}

impl InnerService {
Expand Down Expand Up @@ -260,59 +269,71 @@ impl InnerService {
let height = header_mut.height;
header_mut.pow.as_mut().unwrap().pow_data = monero_rx::serialize(&monero_data);

let tari_header = header_mut.clone().try_into().map_err(MmProxyError::ConversionError)?;
let mut base_node_client = self.base_node_client.clone();
let start = Instant::now();
match base_node_client.submit_block(block_data.tari_block).await {
Ok(resp) => {
if self.config.submit_to_origin {
json_resp = json_rpc::success_response(
request["id"].as_i64(),
json!({ "status": "OK", "untrusted": !self.initial_sync_achieved.load(Ordering::Relaxed) }),
);
let resp = resp.into_inner();
json_resp = append_aux_chain_data(
json_resp,
json!({"id": TARI_CHAIN_ID, "block_hash": resp.block_hash.to_hex()}),
);
let achieved_target = if self.config.check_tari_difficulty_before_submit {
trace!(target: LOG_TARGET, "Starting calculate achieved tari difficultly");
let diff = monero_difficulty(&tari_header, &self.randomx_factory)?;
trace!(target: LOG_TARGET, "Finished calculate achieved tari difficultly");
diff.as_u64()
} else {
block_data.tari_difficulty
};

if achieved_target >= block_data.tari_difficulty {
match base_node_client.submit_block(block_data.tari_block).await {
Ok(resp) => {
if self.config.submit_to_origin {
json_resp = json_rpc::success_response(
request["id"].as_i64(),
json!({ "status": "OK", "untrusted": !self.initial_sync_achieved.load(Ordering::Relaxed) }),
);
let resp = resp.into_inner();
json_resp = append_aux_chain_data(
json_resp,
json!({"id": TARI_CHAIN_ID, "block_hash": resp.block_hash.to_hex()}),
);
debug!(
target: LOG_TARGET,
"Submitted block #{} to Tari node in {:.0?} (SubmitBlock)",
height,
start.elapsed()
);
} else {
// self-select related, do not change.
json_resp = json_rpc::default_block_accept_response(request["id"].as_i64());
trace!(
target: LOG_TARGET,
"pool merged mining proxy_submit_to_origin({}) json_resp: {}",
self.config.submit_to_origin,
json_resp
);
}
self.block_templates.remove(&hash).await;
},
Err(err) => {
debug!(
target: LOG_TARGET,
"Submitted block #{} to Tari node in {:.0?} (SubmitBlock)",
"Problem submitting block #{} to Tari node, responded in {:.0?} (SubmitBlock): {}",
height,
start.elapsed()
);
} else {
// self-select related, do not change.
json_resp = json_rpc::default_block_accept_response(request["id"].as_i64());
trace!(
target: LOG_TARGET,
"pool merged mining proxy_submit_to_origin({}) json_resp: {}",
self.config.submit_to_origin,
json_resp
start.elapsed(),
err
);
}
self.block_templates.remove(&hash).await;
},
Err(err) => {
debug!(
target: LOG_TARGET,
"Problem submitting block #{} to Tari node, responded in {:.0?} (SubmitBlock): {}",
height,
start.elapsed(),
err
);

if !self.config.submit_to_origin {
// When "submit to origin" is turned off the block is never submitted to monerod, and so we need
// to construct an error message here.
json_resp = json_rpc::error_response(
request["id"].as_i64(),
CoreRpcErrorCode::BlockNotAccepted.into(),
"Block not accepted",
None,
);
}
},
}
if !self.config.submit_to_origin {
// When "submit to origin" is turned off the block is never submitted to monerod, and so we
// need to construct an error message here.
json_resp = json_rpc::error_response(
request["id"].as_i64(),
CoreRpcErrorCode::BlockNotAccepted.into(),
"Block not accepted",
None,
);
}
},
}
};
self.block_templates.remove_outdated().await;
}

Expand Down
13 changes: 11 additions & 2 deletions base_layer/core/src/proof_of_work/randomx_factory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

use std::{
collections::HashMap,
fmt,
sync::{Arc, RwLock},
time::Instant,
};
Expand Down Expand Up @@ -78,7 +79,7 @@ unsafe impl Send for RandomXVMInstance {}
unsafe impl Sync for RandomXVMInstance {}

// Thread safe impl of the inner impl
#[derive(Clone)]
#[derive(Clone, Debug)]
pub struct RandomXFactory {
inner: Arc<RwLock<RandomXFactoryInner>>,
}
Expand Down Expand Up @@ -115,7 +116,6 @@ impl RandomXFactory {
inner.get_flags()
}
}

struct RandomXFactoryInner {
flags: RandomXFlag,
vms: HashMap<Vec<u8>, (Instant, RandomXVMInstance)>,
Expand Down Expand Up @@ -173,6 +173,15 @@ impl RandomXFactoryInner {
}
}

impl fmt::Debug for RandomXFactoryInner {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("RandomXFactory")
.field("flags", &self.flags)
.field("max_vms", &self.max_vms)
.finish()
}
}

#[cfg(test)]
mod test {
use super::*;
Expand Down
2 changes: 1 addition & 1 deletion base_layer/core/src/validation/header_validator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ impl HeaderValidator {
impl<TBackend: BlockchainBackend> HeaderValidation<TBackend> for HeaderValidator {
/// The consensus checks that are done (in order of cheapest to verify to most expensive):
/// 1. Is the block timestamp within the Future Time Limit (FTL)?
/// 1. Is the Proof of Work valid?
/// 1. Is the Proof of Work struct valid? Note it does not check the actual PoW here
/// 1. Is the achieved difficulty of this block >= the target difficulty for this block?
fn validate(
Expand Down
4 changes: 4 additions & 0 deletions common/config/presets/merge_mining_proxy.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ listener_address = "/ip4/127.0.0.1/tcp/18081"
# pool does that, then this setting should be "false". (default = true).
submit_to_origin = true

# When mining for tari, you might want to check the achieved difficulty of the mined tari block before submitting
# This setting this can be disabled to allow you to always submit tari blocks even if the difficulty does not meet the required.
check_tari_difficulty_before_submit = true

# The merge mining proxy can either wait for the base node to achieve initial sync at startup before it enables mining,
# or not. If merge mining starts before the base node has achieved initial sync, those Tari mined blocks will not be
# accepted. (Default value = true; will wait for base node initial sync).
Expand Down

0 comments on commit b09fa76

Please sign in to comment.