1use bitcoin_hashes::hex::FromHex;
2use serde_json::{
3 Value,
4 Value::{Array as JArrary, Bool as JBool, Number as JNumber, String as JString},
5};
6use std::convert::{TryFrom, TryInto};
7use std::fmt;
8
9use crate::{
10 error::Error,
11 json_rpc::{Message, Notification, Response},
12 methods::ParsingMethodError,
13 utils::{Extranonce, HexBytes, HexU32Be, MerkleNode, PrevHash},
14};
15
16#[derive(Debug, Clone)]
41pub struct Notify<'a> {
42 pub job_id: String,
43 pub prev_hash: PrevHash<'a>,
44 pub coin_base1: HexBytes,
45 pub coin_base2: HexBytes,
46 pub merkle_branch: Vec<MerkleNode<'a>>,
47 pub version: HexU32Be,
48 pub bits: HexU32Be,
49 pub time: HexU32Be,
50 pub clean_jobs: bool,
51}
52
53impl<'a> fmt::Display for Notify<'a> {
54 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
55 write!(
56 f,
57 "Notify {{ job_id: {}, prev_hash: {}, coin_base1: {}, coin_base2: {}, merkle_branch: ",
58 self.job_id, self.prev_hash, self.coin_base1, self.coin_base2,
59 )?;
60
61 match self.merkle_branch.len() {
62 0 => write!(f, "[]")?,
63 1 => write!(f, "[{}]", self.merkle_branch[0])?,
64 2 => write!(f, "[{}, {}]", self.merkle_branch[0], self.merkle_branch[1])?,
65 _ => write!(
66 f,
67 "[{}, ..., {}]",
68 self.merkle_branch.first().unwrap(),
69 self.merkle_branch.last().unwrap()
70 )?,
71 }
72
73 write!(
74 f,
75 " ({} nodes), version: {}, bits: {}, time: {}, clean_jobs: {} }}",
76 self.merkle_branch.len(),
77 self.version,
78 self.bits,
79 self.time,
80 self.clean_jobs,
81 )
82 }
83}
84
85impl From<Notify<'_>> for Message {
86 fn from(notify: Notify) -> Self {
87 let prev_hash: Value = notify.prev_hash.into();
88 let coin_base1: Value = notify.coin_base1.into();
89 let coin_base2: Value = notify.coin_base2.into();
90 let mut merkle_branch: Vec<Value> = vec![];
91 for mb in notify.merkle_branch {
92 let mb: Value = mb.into();
93 merkle_branch.push(mb);
94 }
95 let merkle_branch = JArrary(merkle_branch);
96 let version: Value = notify.version.into();
97 let bits: Value = notify.bits.into();
98 let time: Value = notify.time.into();
99 Message::Notification(Notification {
100 method: "mining.notify".to_string(),
101 params: (&[
102 notify.job_id.into(),
103 prev_hash,
104 coin_base1,
105 coin_base2,
106 merkle_branch,
107 version,
108 bits,
109 time,
110 notify.clean_jobs.into(),
111 ][..])
112 .into(),
113 })
114 }
115}
116
117impl TryFrom<Notification> for Notify<'_> {
118 type Error = ParsingMethodError;
119
120 #[allow(clippy::many_single_char_names)]
121 fn try_from(msg: Notification) -> Result<Self, Self::Error> {
122 let params = msg
123 .params
124 .as_array()
125 .ok_or_else(|| ParsingMethodError::not_array_from_value(msg.params.clone()))?;
126 let (
127 job_id,
128 prev_hash,
129 coin_base1,
130 coin_base2,
131 merkle_branch_,
132 version,
133 bits,
134 time,
135 clean_jobs,
136 ) = match ¶ms[..] {
137 [JString(a), JString(b), JString(c), JString(d), JArrary(e), JString(f), JString(g), JString(h), JBool(i)] => {
138 (
139 a.into(),
140 b.as_str().try_into()?,
141 c.as_str().try_into()?,
142 d.as_str().try_into()?,
143 e,
144 f.as_str().try_into()?,
145 g.as_str().try_into()?,
146 h.as_str().try_into()?,
147 *i,
148 )
149 }
150 _ => {
151 return Err(ParsingMethodError::wrong_args_from_value(
152 msg.params.clone(),
153 ))
154 }
155 };
156 let mut merkle_branch = vec![];
157 for h in merkle_branch_ {
158 let h: MerkleNode = h
159 .as_str()
160 .ok_or_else(|| ParsingMethodError::not_string_from_value(h.clone()))?
161 .try_into()?;
162
163 merkle_branch.push(h);
164 }
165 let notify = Notify {
166 job_id,
167 prev_hash,
168 coin_base1,
169 coin_base2,
170 merkle_branch,
171 version,
172 bits,
173 time,
174 clean_jobs,
175 };
176 Ok(notify.clone())
177 }
178}
179
180#[derive(Debug, Clone)]
187pub struct SetDifficulty {
188 pub value: f64,
189}
190
191impl fmt::Display for SetDifficulty {
192 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
193 write!(f, "{}", self.value)
194 }
195}
196
197impl From<SetDifficulty> for Message {
198 fn from(sd: SetDifficulty) -> Self {
199 let value: Value = sd.value.into();
200 Message::Notification(Notification {
201 method: "mining.set_difficulty".to_string(),
202 params: (&[value][..]).into(),
203 })
204 }
205}
206
207impl TryFrom<Notification> for SetDifficulty {
208 type Error = ParsingMethodError;
209
210 fn try_from(msg: Notification) -> Result<Self, Self::Error> {
211 let params = msg
212 .params
213 .as_array()
214 .ok_or_else(|| ParsingMethodError::not_array_from_value(msg.params.clone()))?;
215 let (value,) = match ¶ms[..] {
216 [a] => (a
217 .as_f64()
218 .ok_or_else(|| ParsingMethodError::not_float_from_value(a.clone()))?,),
219 _ => return Err(ParsingMethodError::wrong_args_from_value(msg.params)),
220 };
221 Ok(SetDifficulty { value })
222 }
223}
224
225#[derive(Debug, Clone)]
235pub struct SetExtranonce<'a> {
236 pub extra_nonce1: Extranonce<'a>,
237 pub extra_nonce2_size: usize,
238}
239
240impl fmt::Display for SetExtranonce<'_> {
241 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
242 write!(
243 f,
244 "SetExtranonce {{ extra_nonce1: {}, extra_nonce2_size: {} }}",
245 self.extra_nonce1, self.extra_nonce2_size
246 )
247 }
248}
249
250impl From<SetExtranonce<'_>> for Message {
251 fn from(se: SetExtranonce) -> Self {
252 let extra_nonce1: Value = se.extra_nonce1.into();
253 let extra_nonce2_size: Value = se.extra_nonce2_size.into();
254 Message::Notification(Notification {
255 method: "mining.set_extranonce".to_string(),
256 params: (&[extra_nonce1, extra_nonce2_size][..]).into(),
257 })
258 }
259}
260
261impl TryFrom<Notification> for SetExtranonce<'_> {
262 type Error = ParsingMethodError;
263
264 fn try_from(msg: Notification) -> Result<Self, Self::Error> {
265 let params = msg
266 .params
267 .as_array()
268 .ok_or_else(|| ParsingMethodError::not_array_from_value(msg.params.clone()))?;
269 let (extra_nonce1, extra_nonce2_size) = match ¶ms[..] {
270 [JString(a), JNumber(b)] => (
271 Extranonce::try_from(Vec::<u8>::from_hex(a)?)?,
272 b.as_u64()
273 .ok_or_else(|| ParsingMethodError::not_unsigned_from_value(b.clone()))?
274 as usize,
275 ),
276 _ => return Err(ParsingMethodError::wrong_args_from_value(msg.params)),
277 };
278 Ok(SetExtranonce {
279 extra_nonce1,
280 extra_nonce2_size,
281 })
282 }
283}
284
285#[derive(Debug, Clone)]
286pub struct SetVersionMask {
288 version_mask: HexU32Be,
289}
290
291impl fmt::Display for SetVersionMask {
292 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
293 write!(
294 f,
295 "SetVersionMask {{ version_mask: {} }}",
296 self.version_mask
297 )
298 }
299}
300
301impl From<SetVersionMask> for Message {
302 fn from(sv: SetVersionMask) -> Self {
303 let version_mask: Value = sv.version_mask.into();
304 Message::Notification(Notification {
305 method: "mining.set_version".to_string(),
306 params: (&[version_mask][..]).into(),
307 })
308 }
309}
310
311impl TryFrom<Notification> for SetVersionMask {
312 type Error = ParsingMethodError;
313
314 fn try_from(msg: Notification) -> Result<Self, Self::Error> {
315 let params = msg
316 .params
317 .as_array()
318 .ok_or_else(|| ParsingMethodError::not_array_from_value(msg.params.clone()))?;
319 let version_mask = match ¶ms[..] {
320 [JString(a)] => a.as_str().try_into()?,
321 _ => return Err(ParsingMethodError::wrong_args_from_value(msg.params)),
322 };
323 Ok(SetVersionMask { version_mask })
324 }
325}
326
327#[derive(Debug, Clone)]
331pub struct GeneralResponse {
332 pub id: u64,
333 result: bool,
334}
335
336impl fmt::Display for GeneralResponse {
337 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
338 write!(
339 f,
340 "GeneralResponse {{ id: {}, result: {} }}",
341 self.id, self.result
342 )
343 }
344}
345
346impl GeneralResponse {
347 pub fn into_authorize(self, prev_request_name: String) -> Authorize {
348 Authorize {
349 id: self.id,
350 authorized: self.result,
351 prev_request_name,
352 }
353 }
354 pub fn into_submit(self) -> Submit {
355 Submit {
356 id: self.id,
357 is_ok: self.result,
358 }
359 }
360}
361
362impl TryFrom<&Response> for GeneralResponse {
363 type Error = ParsingMethodError;
364
365 fn try_from(msg: &Response) -> Result<Self, Self::Error> {
366 let id = msg.id;
367 let result = msg.result.as_bool().ok_or_else(|| {
368 ParsingMethodError::ImpossibleToParseResultField(Box::new(msg.clone()))
369 })?;
370 Ok(GeneralResponse { id, result })
371 }
372}
373
374#[derive(Debug, Clone)]
375pub struct Authorize {
376 pub id: u64,
377 authorized: bool,
378 pub prev_request_name: String,
379}
380
381impl fmt::Display for Authorize {
382 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
383 write!(
384 f,
385 "Authorize {{ id: {}, authorized: {}, prev_request_name: {} }}",
386 self.id, self.authorized, self.prev_request_name
387 )
388 }
389}
390
391impl Authorize {
392 pub fn is_ok(&self) -> bool {
393 self.authorized
394 }
395
396 pub fn user_name(self) -> String {
397 self.prev_request_name
398 }
399}
400
401#[derive(Debug, Clone)]
402pub struct Submit {
403 pub id: u64,
404 is_ok: bool,
405}
406
407impl fmt::Display for Submit {
408 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
409 write!(f, "Submit {{ id: {}, is_ok: {} }}", self.id, self.is_ok)
410 }
411}
412
413impl Submit {
414 pub fn is_ok(&self) -> bool {
415 self.is_ok
416 }
417}
418
419#[derive(Debug, Clone)]
438pub struct Subscribe<'a> {
439 pub id: u64,
440 pub extra_nonce1: Extranonce<'a>,
441 pub extra_nonce2_size: usize,
442 pub subscriptions: Vec<(String, String)>,
443}
444
445impl fmt::Display for Subscribe<'_> {
446 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
447 write!(
448 f,
449 "Subscribe {{ id: {}, extra_nonce1: {}, extra_nonce2_size: {}, subscriptions: {:?} }}",
450 self.id, self.extra_nonce1, self.extra_nonce2_size, self.subscriptions
451 )
452 }
453}
454
455impl From<Subscribe<'_>> for Message {
456 fn from(su: Subscribe) -> Self {
457 let extra_nonce1: Value = su.extra_nonce1.into();
458 let extra_nonce2_size: Value = su.extra_nonce2_size.into();
459 let subscriptions: Vec<Value> = su
460 .subscriptions
461 .iter()
462 .map(|x| JArrary(vec![JString(x.0.clone()), JString(x.1.clone())]))
463 .collect();
464 let subscriptions: Value = subscriptions.into();
465 Message::OkResponse(Response {
466 id: su.id,
467 error: None,
468 result: (&[subscriptions, extra_nonce1, extra_nonce2_size][..]).into(),
469 })
470 }
471}
472
473impl TryFrom<&Response> for Subscribe<'_> {
474 type Error = ParsingMethodError;
475
476 fn try_from(msg: &Response) -> Result<Self, Self::Error> {
477 let id = msg.id;
478 let params = msg.result.as_array().ok_or_else(|| {
479 ParsingMethodError::ImpossibleToParseResultField(Box::new(msg.clone()))
480 })?;
481 let (subscriptions_, extra_nonce1, extra_nonce2_size) = match ¶ms[..] {
482 [JArrary(a), JString(b), JNumber(c)] => (
483 a,
484 b.as_str().try_into()?,
486 c.as_u64().ok_or_else(|| {
487 ParsingMethodError::ImpossibleToParseAsU64(Box::new(c.clone()))
488 })? as usize,
489 ),
490 _ => return Err(ParsingMethodError::UnexpectedArrayParams(params.clone())),
491 };
492 let mut subscriptions: Vec<(String, String)> = vec![];
493 for s in subscriptions_ {
494 let s = s.as_array().unwrap();
496 if s.len() != 2 {
497 return Err(ParsingMethodError::UnexpectedArrayParams(params.clone()));
498 };
499 let s = (
500 s[0].as_str()
501 .ok_or_else(|| ParsingMethodError::UnexpectedArrayParams(params.clone()))?
502 .to_string(),
503 s[1].as_str()
504 .ok_or_else(|| ParsingMethodError::UnexpectedArrayParams(params.clone()))?
505 .to_string(),
506 );
507 subscriptions.push(s);
508 }
509 Ok(Subscribe {
510 id,
511 extra_nonce1,
512 extra_nonce2_size,
513 subscriptions,
514 })
515 }
516}
517
518#[derive(Debug, Clone)]
519pub struct Configure {
520 pub id: u64,
521 pub version_rolling: Option<VersionRollingParams>,
522 pub minimum_difficulty: Option<bool>,
523}
524
525impl fmt::Display for Configure {
526 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
527 write!(
528 f,
529 "Configure {{ id: {}, version_rolling: {:?}, minimum_difficulty: {:?} }}",
530 self.id, self.version_rolling, self.minimum_difficulty
531 )
532 }
533}
534
535impl Configure {
536 pub fn version_rolling_mask(&self) -> Option<HexU32Be> {
537 match &self.version_rolling {
538 None => None,
539 Some(a) => {
540 if a.version_rolling {
541 Some(a.version_rolling_mask.clone())
542 } else {
543 None
544 }
545 }
546 }
547 }
548 pub fn version_rolling_min_bit(&self) -> Option<HexU32Be> {
549 match &self.version_rolling {
550 None => None,
551 Some(a) => {
552 if a.version_rolling {
553 Some(a.version_rolling_min_bit_count.clone())
554 } else {
555 None
556 }
557 }
558 }
559 }
560}
561
562impl From<Configure> for Message {
563 fn from(co: Configure) -> Self {
564 let mut params = serde_json::Map::new();
565 if let Some(version_rolling_) = co.version_rolling {
566 let mut version_rolling: serde_json::Map<String, Value> = version_rolling_.into();
567 params.append(&mut version_rolling);
568 };
569 if let Some(min_diff) = co.minimum_difficulty {
570 let minimum_difficulty: Value = min_diff.into();
571 params.insert("minimum-difficulty".to_string(), minimum_difficulty);
572 };
573 Message::OkResponse(Response {
574 id: co.id,
575 error: None,
576 result: serde_json::Value::Object(params),
577 })
578 }
579}
580
581impl TryFrom<&Response> for Configure {
582 type Error = ParsingMethodError;
583
584 fn try_from(msg: &Response) -> Result<Self, ParsingMethodError> {
585 let id = msg.id;
586 let params = msg.result.as_object().ok_or_else(|| {
587 ParsingMethodError::ImpossibleToParseResultField(Box::new(msg.clone()))
588 })?;
589
590 let version_rolling_ = params.get("version-rolling");
591 let version_rolling_mask = params.get("version-rolling.mask");
592 let version_rolling_min_bit_count = params.get("version-rolling.min-bit-count");
593 let minimum_difficulty = params.get("minimum-difficulty");
594
595 let version_rolling: Option<VersionRollingParams>;
601 if version_rolling_.is_some() && version_rolling_mask.is_some() {
602 let vr: bool = version_rolling_
603 .unwrap()
604 .as_bool()
605 .ok_or_else(|| ParsingMethodError::UnexpectedObjectParams(params.clone()))?;
606
607 let version_rolling_mask: HexU32Be = version_rolling_mask
608 .unwrap()
609 .as_str()
610 .ok_or_else(|| ParsingMethodError::UnexpectedObjectParams(params.clone()))?
611 .try_into()?;
612
613 let version_rolling_min_bit_count: HexU32Be = match version_rolling_min_bit_count {
616 Some(version_rolling_min_bit_count) => version_rolling_min_bit_count
617 .as_str()
618 .ok_or_else(|| ParsingMethodError::UnexpectedObjectParams(params.clone()))?
619 .try_into()?,
620 None => HexU32Be(0),
621 };
622
623 version_rolling = Some(VersionRollingParams {
624 version_rolling: vr,
625 version_rolling_mask,
626 version_rolling_min_bit_count,
627 });
628 } else if version_rolling_.is_none()
629 && version_rolling_mask.is_none()
630 && version_rolling_min_bit_count.is_none()
631 {
632 version_rolling = None;
633 } else {
634 return Err(ParsingMethodError::UnexpectedObjectParams(params.clone()));
635 };
636
637 let minimum_difficulty = match minimum_difficulty {
638 Some(a) => Some(
639 a.as_bool()
640 .ok_or_else(|| ParsingMethodError::UnexpectedObjectParams(params.clone()))?,
641 ),
642 None => None,
643 };
644
645 Ok(Configure {
646 id,
647 version_rolling,
648 minimum_difficulty,
649 })
650 }
651}
652
653#[derive(Debug, Clone)]
654pub struct VersionRollingParams {
655 pub version_rolling: bool,
656 pub version_rolling_mask: HexU32Be,
657 pub version_rolling_min_bit_count: HexU32Be,
658}
659
660impl fmt::Display for VersionRollingParams {
661 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
662 write!(
663 f,
664 "VersionRollingParams {{ version_rolling: {}, version_rolling_mask: {}, version_rolling_min_bit_count: {} }}",
665 self.version_rolling,
666 self.version_rolling_mask,
667 self.version_rolling_min_bit_count
668 )
669 }
670}
671
672#[test]
673fn configure_response_parsing_all_fields() {
674 let client_response_str = r#"{"id":0,
675 "result":{
676 "version-rolling":true,
677 "version-rolling.mask":"1fffe000",
678 "version-rolling.min-bit-count":"00000005",
679 "minimum-difficulty":false
680 }
681 }"#;
682 let client_response = serde_json::from_str(client_response_str).unwrap();
683 let server_configure = Configure::try_from(&client_response).unwrap();
684 println!("{server_configure:?}");
685
686 let version_rolling = server_configure.version_rolling.unwrap();
687 assert!(version_rolling.version_rolling);
688 assert_eq!(version_rolling.version_rolling_mask, HexU32Be(0x1fffe000));
689 assert_eq!(version_rolling.version_rolling_min_bit_count, HexU32Be(5));
690
691 assert_eq!(server_configure.minimum_difficulty, Some(false));
692}
693
694#[test]
695fn configure_response_parsing_no_vr_min_bit_count() {
696 let client_response_str = r#"{"id":0,
697 "result":{
698 "version-rolling":true,
699 "version-rolling.mask":"1fffe000",
700 "minimum-difficulty":false
701 }
702 }"#;
703 let client_response = serde_json::from_str(client_response_str).unwrap();
704 let server_configure = Configure::try_from(&client_response).unwrap();
705 println!("{server_configure:?}");
706
707 let version_rolling = server_configure.version_rolling.unwrap();
708 assert!(version_rolling.version_rolling);
709 assert_eq!(version_rolling.version_rolling_mask, HexU32Be(0x1fffe000));
710 assert_eq!(version_rolling.version_rolling_min_bit_count, HexU32Be(0));
711
712 assert_eq!(server_configure.minimum_difficulty, Some(false));
713}
714
715impl VersionRollingParams {
716 pub fn new(
717 version_rolling_mask: HexU32Be,
718 version_rolling_min_bit_count: HexU32Be,
719 ) -> Result<Self, Error<'static>> {
720 let negotiated_mask = HexU32Be(version_rolling_mask.clone() & 0x1FFFE000);
722
723 let version_head_ok = negotiated_mask.0 >> 29 == 0;
724 let version_tail_ok = negotiated_mask.0 << 19 == 0;
725 if version_head_ok && version_tail_ok {
726 Ok(VersionRollingParams {
727 version_rolling: true,
728 version_rolling_mask: negotiated_mask,
729 version_rolling_min_bit_count,
730 })
731 } else {
732 Err(Error::InvalidVersionMask(version_rolling_mask))
733 }
734 }
735}
736
737impl From<VersionRollingParams> for serde_json::Map<String, Value> {
738 fn from(vp: VersionRollingParams) -> Self {
739 let version_rolling: Value = vp.version_rolling.into();
740 let version_rolling_mask: Value = vp.version_rolling_mask.into();
741 let version_rolling_min_bit_count: Value = vp.version_rolling_min_bit_count.into();
742 let mut params = serde_json::Map::new();
743 params.insert("version-rolling".to_string(), version_rolling);
744 params.insert("version-rolling.mask".to_string(), version_rolling_mask);
745 params.insert(
746 "version-rolling.min-bit-count".to_string(),
747 version_rolling_min_bit_count,
748 );
749 params
750 }
751}