1use serde::{Deserialize, Serialize};
2use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
3
4pub const MAX_MESSAGE_SIZE: u32 = 16 * 1024 * 1024;
6
7#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
9#[serde(rename_all = "snake_case")]
10pub enum ApiResponseStatus {
11 Accepted,
12 Running,
13 Completed,
14 Failed,
15 Cancelled,
16 NotFound,
17 Error,
18}
19
20#[derive(Serialize, Deserialize, Debug, Clone)]
27pub enum MinerMessage {
28 Ready,
31
32 NewJob(MiningRequest),
35
36 JobResult(MiningResult),
38}
39
40pub async fn write_message<W: AsyncWrite + Unpin>(
44 writer: &mut W,
45 msg: &MinerMessage,
46) -> std::io::Result<()> {
47 let json = serde_json::to_vec(msg)
48 .map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e))?;
49 let len = json.len() as u32;
50 writer.write_all(&len.to_be_bytes()).await?;
51 writer.write_all(&json).await?;
52 Ok(())
53}
54
55pub async fn read_message<R: AsyncRead + Unpin>(reader: &mut R) -> std::io::Result<MinerMessage> {
60 let mut len_buf = [0u8; 4];
61 reader.read_exact(&mut len_buf).await?;
62 let len = u32::from_be_bytes(len_buf);
63
64 if len > MAX_MESSAGE_SIZE {
65 return Err(std::io::Error::new(
66 std::io::ErrorKind::InvalidData,
67 format!("Message size {} exceeds maximum {}", len, MAX_MESSAGE_SIZE),
68 ));
69 }
70
71 let mut buf = vec![0u8; len as usize];
72 reader.read_exact(&mut buf).await?;
73 serde_json::from_slice(&buf)
74 .map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e))
75}
76
77#[derive(Serialize, Deserialize, Debug, Clone)]
82pub struct MiningRequest {
83 pub job_id: String,
84 pub mining_hash: String,
86 pub difficulty: String,
88}
89
90#[derive(Serialize, Deserialize, Debug, Clone)]
92pub struct MiningResponse {
93 pub status: ApiResponseStatus,
94 pub job_id: String,
95 #[serde(skip_serializing_if = "Option::is_none")]
96 pub message: Option<String>,
97}
98
99#[derive(Serialize, Deserialize, Debug, Clone)]
101pub struct MiningResult {
102 pub status: ApiResponseStatus,
103 pub job_id: String,
104 pub nonce: Option<String>,
106 pub work: Option<String>,
109 pub hash_count: u64,
110 pub elapsed_time: f64,
111 #[serde(default, skip_serializing_if = "Option::is_none")]
113 pub miner_id: Option<u64>,
114}