1use serde::{Deserialize, Serialize};
2use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
3
4pub const MAX_MESSAGE_SIZE: u32 = 1024;
9
10#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
12#[serde(rename_all = "snake_case")]
13pub enum ApiResponseStatus {
14 Accepted,
15 Running,
16 Completed,
17 Failed,
18 Cancelled,
19 NotFound,
20 Error,
21}
22
23#[derive(Serialize, Deserialize, Debug, Clone)]
30pub enum MinerMessage {
31 Ready,
34
35 NewJob(MiningRequest),
38
39 JobResult(MiningResult),
41}
42
43pub async fn write_message<W: AsyncWrite + Unpin>(
47 writer: &mut W,
48 msg: &MinerMessage,
49) -> std::io::Result<()> {
50 let json = serde_json::to_vec(msg)
51 .map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e))?;
52 let len = json.len() as u32;
53 writer.write_all(&len.to_be_bytes()).await?;
54 writer.write_all(&json).await?;
55 Ok(())
56}
57
58pub async fn read_message<R: AsyncRead + Unpin>(reader: &mut R) -> std::io::Result<MinerMessage> {
63 let mut len_buf = [0u8; 4];
64 reader.read_exact(&mut len_buf).await?;
65 let len = u32::from_be_bytes(len_buf);
66
67 if len > MAX_MESSAGE_SIZE {
68 return Err(std::io::Error::new(
69 std::io::ErrorKind::InvalidData,
70 format!("Message size {} exceeds maximum {}", len, MAX_MESSAGE_SIZE),
71 ));
72 }
73
74 let mut buf = vec![0u8; len as usize];
75 reader.read_exact(&mut buf).await?;
76 serde_json::from_slice(&buf)
77 .map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e))
78}
79
80#[derive(Serialize, Deserialize, Debug, Clone)]
85pub struct MiningRequest {
86 pub job_id: String,
87 pub mining_hash: String,
89 pub difficulty: String,
91}
92
93#[derive(Serialize, Deserialize, Debug, Clone)]
95pub struct MiningResponse {
96 pub status: ApiResponseStatus,
97 pub job_id: String,
98 #[serde(skip_serializing_if = "Option::is_none")]
99 pub message: Option<String>,
100}
101
102#[derive(Serialize, Deserialize, Debug, Clone)]
104pub struct MiningResult {
105 pub status: ApiResponseStatus,
106 pub job_id: String,
107 pub nonce: Option<String>,
109 pub work: Option<String>,
112 pub hash_count: u64,
113 pub elapsed_time: f64,
114 #[serde(default, skip_serializing_if = "Option::is_none")]
116 pub miner_id: Option<u64>,
117}