diff --git a/Cargo.lock b/Cargo.lock
index a8a71ca2d61e..bff84f766287 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1612,6 +1612,7 @@ dependencies = [
"structopt",
"structopt-toml",
"url",
+ "uuid",
]
[[package]]
@@ -6653,6 +6654,9 @@ name = "uuid"
version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "79daa5ed5740825c40b389c5e50312b9c86df53fccd33f281df655642b43869d"
+dependencies = [
+ "getrandom 0.2.10",
+]
[[package]]
name = "valuable"
diff --git a/bin/darkfi-mmproxy/Cargo.toml b/bin/darkfi-mmproxy/Cargo.toml
index 3fb024e40924..97e40febb645 100644
--- a/bin/darkfi-mmproxy/Cargo.toml
+++ b/bin/darkfi-mmproxy/Cargo.toml
@@ -17,6 +17,7 @@ log = "0.4.20"
# Encoding
url = "2.4.1"
+uuid = {version = "1.4.1", features = ["v4"]}
# Daemon
easy-parallel = "3.3.0"
diff --git a/bin/darkfi-mmproxy/darkfi_mmproxy.toml b/bin/darkfi-mmproxy/darkfi_mmproxy.toml
index e69de29bb2d1..db1554ede8aa 100644
--- a/bin/darkfi-mmproxy/darkfi_mmproxy.toml
+++ b/bin/darkfi-mmproxy/darkfi_mmproxy.toml
@@ -0,0 +1,2 @@
+# List of worker logins, comment out to allow anything.
+workers = ["x:x"]
\ No newline at end of file
diff --git a/bin/darkfi-mmproxy/src/main.rs b/bin/darkfi-mmproxy/src/main.rs
index ad168bf14503..157c75e2e658 100644
--- a/bin/darkfi-mmproxy/src/main.rs
+++ b/bin/darkfi-mmproxy/src/main.rs
@@ -16,7 +16,10 @@
* along with this program. If not, see .
*/
-use std::{collections::HashSet, sync::Arc};
+use std::{
+ collections::{HashMap, HashSet},
+ sync::Arc,
+};
use darkfi::{
async_daemonize, cli_desc,
@@ -61,19 +64,27 @@ struct Args {
/// JSON-RPC server listen URL
rpc_listen: Url,
+ #[structopt(long)]
+ /// List of worker logins
+ workers: Vec,
+
#[structopt(long)]
/// Set log file output
log: Option,
}
struct MiningProxy {
+ /// Worker logins
+ logins: HashMap,
/// JSON-RPC connection tracker
rpc_connections: Mutex>,
+ /// Main async executor reference
+ executor: Arc>,
}
impl MiningProxy {
- fn new() -> Self {
- Self { rpc_connections: Mutex::new(HashSet::new()) }
+ fn new(logins: HashMap, executor: Arc>) -> Self {
+ Self { logins, rpc_connections: Mutex::new(HashSet::new()), executor }
}
}
@@ -136,9 +147,20 @@ impl RequestHandler for MiningProxy {
async_daemonize!(realmain);
async fn realmain(args: Args, ex: Arc>) -> Result<()> {
- info!("Starting JSON-RPC server");
- let mmproxy = Arc::new(MiningProxy::new());
+ // Parse worker logins
+ let mut logins = HashMap::new();
+ for worker in args.workers {
+ let mut split = worker.split(':');
+ let user = split.next().unwrap().to_string();
+ let pass = split.next().unwrap().to_string();
+ info!("Whitelisting worker \"{}:{}\"", user, pass);
+ logins.insert(user, pass);
+ }
+
+ let mmproxy = Arc::new(MiningProxy::new(logins, ex.clone()));
let mmproxy_ = Arc::clone(&mmproxy);
+
+ info!("Starting JSON-RPC server");
let rpc_task = StoppableTask::new();
rpc_task.clone().start(
listen_and_serve(args.rpc_listen, mmproxy.clone(), None, ex.clone()),
diff --git a/bin/darkfi-mmproxy/src/stratum.rs b/bin/darkfi-mmproxy/src/stratum.rs
index c2137882e8cf..0d63abc339bf 100644
--- a/bin/darkfi-mmproxy/src/stratum.rs
+++ b/bin/darkfi-mmproxy/src/stratum.rs
@@ -16,20 +16,84 @@
* along with this program. If not, see .
*/
+use std::collections::HashMap;
+
use darkfi::rpc::{
jsonrpc::{ErrorCode, JsonError, JsonResult},
util::JsonValue,
};
+use uuid::Uuid;
use super::MiningProxy;
+/// Algo string representing Monero's RandomX
+pub const RANDOMX_ALGO: &str = "rx/0";
+
impl MiningProxy {
+ /// Stratum login method. `darkfi-mmproxy` will check that it is a valid worker
+ /// login, and will also search for `RANDOMX_ALGO`.
+ /// TODO: More proper error codes
pub async fn stratum_login(&self, id: u16, params: JsonValue) -> JsonResult {
let params = params.get::>().unwrap();
if params.len() != 1 || !params[0].is_object() {
return JsonError::new(ErrorCode::InvalidParams, None, id).into()
}
+ let params = params[0].get::>().unwrap();
+
+ if !params.contains_key("login") ||
+ !params.contains_key("pass") ||
+ !params.contains_key("agent") ||
+ !params.contains_key("algo")
+ {
+ return JsonError::new(ErrorCode::InvalidParams, None, id).into()
+ }
+
+ let Some(login) = params["login"].get::() else {
+ return JsonError::new(ErrorCode::InvalidParams, None, id).into()
+ };
+
+ let Some(pass) = params["pass"].get::() else {
+ return JsonError::new(ErrorCode::InvalidParams, None, id).into()
+ };
+
+ let Some(agent) = params["agent"].get::() else {
+ return JsonError::new(ErrorCode::InvalidParams, None, id).into()
+ };
+
+ let Some(algos) = params["algo"].get::>() else {
+ return JsonError::new(ErrorCode::InvalidParams, None, id).into()
+ };
+
+ // We'll only support rx/0 algo.
+ let mut found_xmr_algo = false;
+ for algo in algos {
+ if !algo.is_string() {
+ return JsonError::new(ErrorCode::InvalidParams, None, id).into()
+ }
+
+ if algo.get::().unwrap() == RANDOMX_ALGO {
+ found_xmr_algo = true;
+ break
+ }
+ }
+
+ if !found_xmr_algo {
+ return JsonError::new(ErrorCode::InvalidParams, None, id).into()
+ }
+
+ // Check valid login
+ let Some(known_pass) = self.logins.get(login) else {
+ return JsonError::new(ErrorCode::InvalidParams, None, id).into()
+ };
+
+ if known_pass != pass {
+ return JsonError::new(ErrorCode::InvalidParams, None, id).into()
+ }
+
+ // Login success, generate UUID
+ let uuid = Uuid::new_v4();
+
todo!()
}