use core::str;
use serde::{Deserialize, Serialize};
use crate::{
command::{Command, CommandLevel},
error::Result,
redacted::Redacted,
};
#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct PoolConfig {
pub pool: String,
pub worker: String,
pub passwd: Redacted<String>,
}
impl PoolConfig {
pub fn new(
pool: impl Into<String>,
worker: impl Into<String>,
passwd: impl Into<String>,
) -> Self {
Self {
pool: pool.into(),
worker: worker.into(),
passwd: Redacted(passwd.into()),
}
}
}
#[derive(Debug, Default, Clone, PartialEq, Eq)]
pub struct SetMinerPools {
pub pools: [PoolConfig; 3],
}
impl SetMinerPools {
pub fn new(pools: [PoolConfig; 3]) -> Self {
Self { pools }
}
}
impl From<[PoolConfig; 3]> for SetMinerPools {
fn from(pools: [PoolConfig; 3]) -> Self {
Self { pools }
}
}
impl Command for SetMinerPools {
type Response = String;
const CMD_NAME: &'static str = "set.miner.pools";
const LEVEL: CommandLevel = CommandLevel::Encrypted;
fn params(&self) -> Result<Option<impl serde::Serialize>> {
Ok(Some(&self.pools))
}
}
#[cfg(test)]
mod tests {
use crate::account::Account;
use crate::auth_data::AuthData;
use super::*;
const RESPONSE_JSON: &str =
r#"{"code":0,"when":1692685512,"msg":"ok","desc":"set.miner.pools"}"#;
#[test]
fn request_with_pools_is_encrypted() {
let pools = [
PoolConfig {
pool: "stratum+tcp://1.1.1.1:3333".to_string(),
worker: "test.worker".to_string(),
passwd: Redacted("x".to_string()),
},
PoolConfig::default(),
PoolConfig::default(),
];
let cmd = SetMinerPools::new(pools);
assert!(cmd.is_secured());
assert!(cmd.is_encrypted());
let auth = AuthData::new::<SetMinerPools>(Account::Super, "pass", "salt").unwrap();
let encoded = cmd.encode(Some(auth)).unwrap();
let json: serde_json::Value = serde_json::from_slice(&encoded.0).unwrap();
assert_eq!(json["cmd"], "set.miner.pools");
assert_eq!(json["account"], "super");
assert!(json["ts"].as_u64().unwrap() > 0);
assert_eq!(json["token"].as_str().unwrap().len(), 8);
let param = json["param"].as_str().unwrap();
assert!(!param.contains("stratum"));
assert!(!param.contains("worker"));
assert!(!base64_light::base64_decode(param).is_empty());
}
#[test]
fn pool_credentials_are_redacted_but_serialize_for_encryption() {
let pool = PoolConfig::new("stratum+tcp://localhost:3333", "worker", "pool-secret");
assert!(!format!("{pool:?}").contains("pool-secret"));
let json = serde_json::to_value(&pool).unwrap();
assert_eq!(json["passwd"], "pool-secret");
}
#[test]
fn request_without_auth_returns_error() {
let cmd = SetMinerPools::default();
let result = cmd.encode(None);
assert!(result.is_err());
}
#[test]
fn deserialize_response_fixture() {
let raw = RESPONSE_JSON.as_bytes();
let response = SetMinerPools::parse_response(raw).unwrap();
assert!(response.code.is_success());
assert_eq!(response.desc, "set.miner.pools");
assert_eq!(response.when, 1692685512);
assert_eq!(response.msg, "ok");
}
}