diff --git a/Makefile b/Makefile index c818f8f65..8452e84a3 100644 --- a/Makefile +++ b/Makefile @@ -63,6 +63,13 @@ darkirc: RUST_TARGET="$(RUST_TARGET)" \ RUSTFLAGS="$(RUSTFLAGS)" +darkfi-mmproxy: + $(MAKE) -C bin/darkfi-mmproxy \ + PREFIX="$(PREFIX)" \ + CARGO="$(CARGO)" \ + RUST_TARGET="$(RUST_TARGET)" \ + RUSTFLAGS="$(RUSTFLAGS)" + genev: $(MAKE) -C bin/genev/genev-cli @@ -110,6 +117,7 @@ clean: $(MAKE) -C bin/zkas clean $(MAKE) -C bin/darkfid clean $(MAKE) -C bin/darkfid2 clean + $(MAKE) -C bin/darkfi-mmproxy clean $(MAKE) -C bin/faucetd clean $(MAKE) -C bin/darkirc clean $(MAKE) -C bin/genev/genev-cli clean diff --git a/bin/darkfi-mmproxy/Makefile b/bin/darkfi-mmproxy/Makefile index 491800c2f..e7fbef1b7 100644 --- a/bin/darkfi-mmproxy/Makefile +++ b/bin/darkfi-mmproxy/Makefile @@ -6,6 +6,9 @@ PREFIX = $(HOME)/.cargo # Cargo binary CARGO = cargo +nightly +# Compile target +RUST_TARGET = $(shell rustc -Vv | grep '^host: ' | cut -d' ' -f2) + SRC = \ Cargo.toml \ ../../Cargo.toml \ @@ -17,7 +20,7 @@ BIN = ../../darkfi-mmproxy all: $(BIN) $(BIN): $(SRC) - $(CARGO) build $(TARGET_PRFX)$(RUST_TARGET) --release --package darkfi-mmproxy + RUSTFLAGS="$(RUSTFLAGS)" $(CARGO) build --target=$(RUST_TARGET) --release --package darkfi-mmproxy cp -f ../../target/$(RUST_TARGET)/release/darkfi-mmproxy $@ clean: diff --git a/bin/darkfi-mmproxy/src/main.rs b/bin/darkfi-mmproxy/src/main.rs index 6b38159df..c25ef23f6 100644 --- a/bin/darkfi-mmproxy/src/main.rs +++ b/bin/darkfi-mmproxy/src/main.rs @@ -115,12 +115,9 @@ impl MiningProxy { }; // Test that monerod RPC is reachable - match TcpStream::connect(monerod.rpc.socket_addrs(|| None)?[0]).await { - Ok(_) => {} - Err(e) => { - error!("Failed connecting to monerod RPC: {}", e); - return Err(e.into()) - } + if let Err(e) = TcpStream::connect(monerod.rpc.socket_addrs(|| None)?[0]).await { + error!("Failed connecting to monerod RPC: {}", e); + return Err(e.into()) } let workers = Arc::new(RwLock::new(HashMap::new())); diff --git a/bin/darkfi-mmproxy/src/stratum.rs b/bin/darkfi-mmproxy/src/stratum.rs index 1d61218e4..208ccdff3 100644 --- a/bin/darkfi-mmproxy/src/stratum.rs +++ b/bin/darkfi-mmproxy/src/stratum.rs @@ -180,6 +180,7 @@ async fn getblocktemplate(endpoint: &Url, wallet_address: &monero::Address) -> R ); // Get block template from monerod + info!(target: "stratum::getblocktemplate", "[STRATUM] Sending getblocktemplate to monero"); let rep = match monerod_request(endpoint, req).await { Ok(v) => v, Err(e) => { @@ -213,16 +214,19 @@ async fn getblocktemplate(endpoint: &Url, wallet_address: &monero::Address) -> R .to_string(); // Needed because hex::decode doesn't accept odd-length - if difficulty_hex.len() % 2 == 0 { + if difficulty_hex.len() % 2 != 0 { difficulty_hex = format!("0{}", difficulty_hex); } - // TODO: Check if this is little or big-endian let difficulty_raw = hex::decode(&difficulty_hex).unwrap(); - let difficulty = BigUint::from_radix_le(&difficulty_raw, 16).unwrap(); + let difficulty = BigUint::from_radix_be(&difficulty_raw, 16).unwrap(); - // Calculate the target - let target = (BigUint::from_bytes_be(&[0xFF; 32]) / &difficulty).to_str_radix(16); + // Calculate the target. XMRig expects the 64 least significant bits. + let target_raw = BigUint::from_bytes_be(&[0xFF; 32]) / &difficulty; + // This iterator is ordered least significant first + let target_lsb: u64 = target_raw.iter_u64_digits().take(1).next().unwrap(); + let target = hex::encode(target_lsb.to_be_bytes()); + assert!(target.len() == 16); info!(target: "stratum::getblocktemplate", "[STRATUM] Difficulty: {}", difficulty_hex); info!(target: "stratum::getblocktemplate", "[STRATUM] Target: {}", target); @@ -303,7 +307,7 @@ impl MiningProxy { const POLL_INTERVAL: Duration = Duration::from_secs(60); // Comfy wait for settling the Stratum login RPC call - sleep(2).await; + sleep(5).await; // In this loop, we'll be getting the block template for mining. // At the beginning of the loop, we'll perform a getblocktemplate, @@ -313,7 +317,10 @@ impl MiningProxy { // order to get the next mining job. loop { // Get the workers lock and the worker reference + debug!(target: "stratum::job_task", "Acquiring workers write lock..."); let mut workers_ptr = workers.write().await; + debug!(target: "stratum::job_task", "Acquired workers write lock"); + let Some(worker) = workers_ptr.get_mut(&uuid) else { info!( target: "stratum::job_task", @@ -359,6 +366,7 @@ impl MiningProxy { break } + debug!(target: "stratum::job_task", "Dropped workers write lock"); drop(workers_ptr); // Now poll or wait for a trigger for a new job. @@ -588,54 +596,79 @@ impl MiningProxy { if !params.contains_key("id") || !params.contains_key("job_id") || !params.contains_key("nonce") || - !params.contains_key("result") + !params.contains_key("result") || + !params.contains_key("algo") { return JsonError::new(InvalidParams, None, id).into() } // Validate all the parameters let Some(worker_uuid) = params["id"].get::() else { - return JsonError::new(InvalidParams, Some("Invalid \"id\" field".to_string()), id) + error!(target: "stratum::submit", "[STRATUM] Missing \"id\" field for stratum::submit"); + return JsonError::new(InvalidParams, Some("Missing \"id\" field".to_string()), id) .into() }; let Ok(worker_uuid) = Uuid::try_from(worker_uuid.as_str()) else { + error!(target: "stratum::submit", "[STRATUM] Invalid \"id\" field for stratum::submit"); return JsonError::new(InvalidParams, Some("Invalid \"id\" field".to_string()), id) .into() }; let Some(job_id) = params["job_id"].get::() else { - return JsonError::new(InvalidParams, Some("Invalid \"job_id\" field".to_string()), id) + error!(target: "stratum::submit", "[STRATUM] Missing \"job_id\" field for stratum::submit"); + return JsonError::new(InvalidParams, Some("Missing \"job_id\" field".to_string()), id) .into() }; let Ok(job_id) = blake3::Hash::from_str(job_id) else { + error!(target: "stratum::submit", "[STRATUM] Invalid \"job_id\" field for stratum::submit"); return JsonError::new(InvalidParams, Some("Invalid \"job_id\" field".to_string()), id) .into() }; let Some(nonce) = params["nonce"].get::() else { - return JsonError::new(InvalidParams, Some("Invalid \"nonce\" field".to_string()), id) + error!(target: "stratum::submit", "[STRATUM] Missing \"nonce\" field for stratum::submit"); + return JsonError::new(InvalidParams, Some("Missing \"nonce\" field".to_string()), id) .into() }; let Ok(nonce) = u32::from_str_radix(nonce, 16) else { + error!(target: "stratum::submit", "[STRATUM] Invalid \"nonce\" field for stratum::submit"); return JsonError::new(InvalidParams, Some("Invalid \"nonce\" field".to_string()), id) .into() }; let Some(_result) = params["result"].get::() else { + error!(target: "stratum::submit", "[STRATUM] Missing \"result\" field for stratum::submit"); return JsonError::new(InvalidParams, Some("Invalid \"result\" field".to_string()), id) .into() }; + let Some(algo) = params["algo"].get::() else { + error!(target: "stratum::submit", "[STRATUM] Missing \"algo\" field for stratum::submit"); + return JsonError::new(InvalidParams, Some("Missing \"algo\" field".to_string()), id) + .into() + }; + + if algo != RANDOMX_ALGO { + error!(target: "stratum::submit", "[STRATUM] Invalid \"algo\" field for stratum::submit"); + return JsonError::new(InvalidParams, Some("Invalid \"algo\" field".to_string()), id) + .into() + } + // Get the worker reference and confirm this is submitted for the current job + debug!(target: "stratum::submit", "Acquiring workers read lock..."); let workers_ptr = self.workers.read().await; + debug!(target: "stratum::submit", "Acquired workers read lock"); + let Some(worker) = workers_ptr.get(&worker_uuid) else { + error!(target: "stratum::submit", "[STRATUM] Unknown worker UUID for stratum::submit"); return JsonError::new(InvalidParams, Some("Unknown worker UUID".to_string()), id).into() }; if worker.mining_job.job_id != job_id { + error!(target: "stratum::submit", "[STRATUM] Job ID mismatch for stratum::submit"); return JsonError::new(InvalidParams, Some("Job ID mismatch".to_string()), id).into() }