stratum-server 3.0.0-beta-2

The server code for the Rust Stratum (v1) implementation
Documentation
use crate::{Error, Result};
use async_std::sync::{Arc, Mutex};
use extended_primitives::Buffer;
use serde::{Deserialize, Serialize};
use std::fmt;

pub const EX_MAGIC_NUMBER: u8 = 0x7F;

#[derive(Clone, Default)]
pub struct ReadyIndicator(Arc<Mutex<bool>>);

impl ReadyIndicator {
    pub fn new(ready: bool) -> Self {
        ReadyIndicator(Arc::new(Mutex::new(ready)))
    }

    pub async fn ready(&self) {
        *self.0.lock().await = true;
    }

    pub async fn not_ready(&self) {
        *self.0.lock().await = false;
    }

    pub async fn inner(&self) -> bool {
        *self.0.lock().await
    }
}

#[derive(Serialize, Deserialize, Clone, Debug)]
#[serde(untagged)]
pub enum ID {
    Num(u64),
    Str(String),
    Null(serde_json::Value),
}

impl std::fmt::Display for ID {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            ID::Num(ref e) => write!(f, "{}", e),
            ID::Str(ref e) => write!(f, "{}", e),
            ID::Null(ref _e) => write!(f, "null"),
        }
    }
}

#[derive(Clone, Debug)]
pub enum MessageValue {
    StratumV1(serde_json::map::Map<String, serde_json::Value>),
    ExMessage(ExMessageGeneric),
}

#[derive(Clone, Eq, PartialEq, Debug)]
pub enum MessageTypes {
    RegisterWorker,
    SubmitShare,
    SubmitShareWithTime,
    SubmitShareWithVersion,
    SubmitShareWithTimeAndVersion,
    UnregisterWorker,
    MiningSetDiff,

    Unknown(u8),
}

impl MessageTypes {
    pub fn from_u8(cmd: u8) -> Self {
        match cmd {
            0x01 => MessageTypes::RegisterWorker,
            0x02 => MessageTypes::SubmitShare,
            0x03 => MessageTypes::SubmitShareWithTime,
            0x04 => MessageTypes::UnregisterWorker,
            0x05 => MessageTypes::MiningSetDiff,
            //@note not sure why these are so far after the originals. Makes me think there are
            //other messages we are missing here, but can figure that out later.
            0x12 => MessageTypes::SubmitShareWithVersion,
            0x13 => MessageTypes::SubmitShareWithTimeAndVersion,
            _ => MessageTypes::Unknown(cmd),
        }
    }

    pub fn to_string(&self) -> String {
        match *self {
            MessageTypes::RegisterWorker => String::from("exMessageRegisterWorker"),
            MessageTypes::SubmitShare => String::from("exMessageSubmitShare"),
            MessageTypes::SubmitShareWithTime => String::from("exMessageSubmitShare"),
            MessageTypes::UnregisterWorker => String::from("exMessageUnregisterWorker"),
            MessageTypes::MiningSetDiff => String::from("exMessageMiningSetDiff"),
            MessageTypes::SubmitShareWithVersion => String::from("exMessageSubmitShare"),
            MessageTypes::SubmitShareWithTimeAndVersion => String::from("exMessageSubmitShare"),
            _ => String::from(""),
        }
    }
}

// ex-message: BTC Agent Messages
//   magic_number	uint8_t		magic number for Ex-Message, always 0x7F
//   type/cmd		uint8_t		message type
//   length			uint16_t	message length (include header self)
//   message_body	uint8_t[]	message body
#[derive(Clone, Debug)]
pub struct ExMessageGeneric {
    pub magic_number: u8,
    pub cmd: MessageTypes,
    pub length: u16,
    pub body: Buffer,
}

impl ExMessageGeneric {
    pub fn from_buffer(buffer: &mut Buffer) -> Result<Self> {
        let magic_number = buffer.read_u8().map_err(|_| Error::BrokenExHeader)?;
        let cmd = buffer.read_u8().map_err(|_| Error::BrokenExHeader)?;
        let length = buffer.read_u16().map_err(|_| Error::BrokenExHeader)?;

        let body = buffer.clone();

        let cmd = MessageTypes::from_u8(cmd);

        //@todo I think we need some kind of sanity checks with length and the body here, but I'd
        //rather let a few bad messages slip than weed out good ones rn.

        if length as usize != body.len() {
            return Err(Error::BrokenExHeader);
        }

        match cmd {
            MessageTypes::Unknown(_) => {
                //@todo might be better to do unknown ExMethod, but we'll see.
                return Err(Error::BrokenExHeader);
            }
            _ => {}
        }

        Ok(ExMessageGeneric {
            magic_number,
            cmd,
            length,
            body,
        })
    }
}