فهرست منبع

mmproxy: Stratum login WIP

parazyd 2 سال پیش
والد
کامیت
4c10ee8d67
5فایلهای تغییر یافته به همراه98 افزوده شده و 5 حذف شده
  1. 4 0
      Cargo.lock
  2. 1 0
      bin/darkfi-mmproxy/Cargo.toml
  3. 2 0
      bin/darkfi-mmproxy/darkfi_mmproxy.toml
  4. 27 5
      bin/darkfi-mmproxy/src/main.rs
  5. 64 0
      bin/darkfi-mmproxy/src/stratum.rs

+ 4 - 0
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"

+ 1 - 0
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"

+ 2 - 0
bin/darkfi-mmproxy/darkfi_mmproxy.toml

@@ -0,0 +1,2 @@
+# List of worker logins, comment out to allow anything.
+workers = ["x:x"]

+ 27 - 5
bin/darkfi-mmproxy/src/main.rs

@@ -16,7 +16,10 @@
  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
  */
 
-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<String>,
+
     #[structopt(long)]
     /// Set log file output
     log: Option<String>,
 }
 
 struct MiningProxy {
+    /// Worker logins
+    logins: HashMap<String, String>,
     /// JSON-RPC connection tracker
     rpc_connections: Mutex<HashSet<StoppableTaskPtr>>,
+    /// Main async executor reference
+    executor: Arc<Executor<'static>>,
 }
 
 impl MiningProxy {
-    fn new() -> Self {
-        Self { rpc_connections: Mutex::new(HashSet::new()) }
+    fn new(logins: HashMap<String, String>, executor: Arc<Executor<'static>>) -> 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<Executor<'static>>) -> 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()),

+ 64 - 0
bin/darkfi-mmproxy/src/stratum.rs

@@ -16,20 +16,84 @@
  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
  */
 
+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::<Vec<JsonValue>>().unwrap();
         if params.len() != 1 || !params[0].is_object() {
             return JsonError::new(ErrorCode::InvalidParams, None, id).into()
         }
 
+        let params = params[0].get::<HashMap<String, JsonValue>>().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::<String>() else {
+            return JsonError::new(ErrorCode::InvalidParams, None, id).into()
+        };
+
+        let Some(pass) = params["pass"].get::<String>() else {
+            return JsonError::new(ErrorCode::InvalidParams, None, id).into()
+        };
+
+        let Some(agent) = params["agent"].get::<String>() else {
+            return JsonError::new(ErrorCode::InvalidParams, None, id).into()
+        };
+
+        let Some(algos) = params["algo"].get::<Vec<JsonValue>>() 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::<String>().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!()
     }