1use crate::prelude::*;
2use bitcoin::hashes::sha256::Hash as Sha256Hash;
3use bitcoin::hashes::Hash;
4use bitcoin::key::Secp256k1;
5use bitcoin::secp256k1::Message as BitcoinMessage;
6use nostr_sdk::prelude::*;
7#[cfg(feature = "sqlx")]
8use sqlx::FromRow;
9#[cfg(feature = "sqlx")]
10use sqlx_crud::SqlxCrud;
11
12use std::fmt;
13use uuid::Uuid;
14
15#[derive(Debug, Deserialize, Serialize, Clone)]
17pub struct Peer {
18 pub pubkey: String,
19 pub reputation: Option<UserInfo>,
20}
21
22impl Peer {
23 pub fn new(pubkey: String, reputation: Option<UserInfo>) -> Self {
24 Self { pubkey, reputation }
25 }
26
27 pub fn from_json(json: &str) -> Result<Self, ServiceError> {
28 serde_json::from_str(json).map_err(|_| ServiceError::MessageSerializationError)
29 }
30
31 pub fn as_json(&self) -> Result<String, ServiceError> {
32 serde_json::to_string(&self).map_err(|_| ServiceError::MessageSerializationError)
33 }
34}
35
36#[derive(Debug, PartialEq, Eq, Deserialize, Serialize, Clone)]
38#[serde(rename_all = "kebab-case")]
39pub enum Action {
40 NewOrder,
41 TakeSell,
42 TakeBuy,
43 PayInvoice,
44 FiatSent,
45 FiatSentOk,
46 Release,
47 Released,
48 Cancel,
49 Canceled,
50 CooperativeCancelInitiatedByYou,
51 CooperativeCancelInitiatedByPeer,
52 DisputeInitiatedByYou,
53 DisputeInitiatedByPeer,
54 CooperativeCancelAccepted,
55 BuyerInvoiceAccepted,
56 PurchaseCompleted,
57 HoldInvoicePaymentAccepted,
58 HoldInvoicePaymentSettled,
59 HoldInvoicePaymentCanceled,
60 WaitingSellerToPay,
61 WaitingBuyerInvoice,
62 AddInvoice,
63 BuyerTookOrder,
64 Rate,
65 RateUser,
66 RateReceived,
67 CantDo,
68 Dispute,
69 AdminCancel,
70 AdminCanceled,
71 AdminSettle,
72 AdminSettled,
73 AdminAddSolver,
74 AdminTakeDispute,
75 AdminTookDispute,
76 PaymentFailed,
77 InvoiceUpdated,
78 SendDm,
79 TradePubkey,
80 RestoreSession,
81}
82
83impl fmt::Display for Action {
84 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
85 write!(f, "{self:?}")
86 }
87}
88
89#[derive(Debug, Clone, Deserialize, Serialize)]
91#[serde(rename_all = "kebab-case")]
92pub enum Message {
93 Order(MessageKind),
94 Dispute(MessageKind),
95 CantDo(MessageKind),
96 Rate(MessageKind),
97 Dm(MessageKind),
98 Restore(MessageKind),
99}
100
101impl Message {
102 pub fn new_order(
104 id: Option<Uuid>,
105 request_id: Option<u64>,
106 trade_index: Option<i64>,
107 action: Action,
108 payload: Option<Payload>,
109 ) -> Self {
110 let kind = MessageKind::new(id, request_id, trade_index, action, payload);
111 Self::Order(kind)
112 }
113
114 pub fn new_dispute(
116 id: Option<Uuid>,
117 request_id: Option<u64>,
118 trade_index: Option<i64>,
119 action: Action,
120 payload: Option<Payload>,
121 ) -> Self {
122 let kind = MessageKind::new(id, request_id, trade_index, action, payload);
123
124 Self::Dispute(kind)
125 }
126
127 pub fn new_restore(payload: Option<Payload>) -> Self {
128 let kind = MessageKind::new(None, None, None, Action::RestoreSession, payload);
129 Self::Restore(kind)
130 }
131
132 pub fn cant_do(id: Option<Uuid>, request_id: Option<u64>, payload: Option<Payload>) -> Self {
134 let kind = MessageKind::new(id, request_id, None, Action::CantDo, payload);
135
136 Self::CantDo(kind)
137 }
138
139 pub fn new_dm(
141 id: Option<Uuid>,
142 request_id: Option<u64>,
143 action: Action,
144 payload: Option<Payload>,
145 ) -> Self {
146 let kind = MessageKind::new(id, request_id, None, action, payload);
147
148 Self::Dm(kind)
149 }
150
151 pub fn from_json(json: &str) -> Result<Self, ServiceError> {
153 serde_json::from_str(json).map_err(|_| ServiceError::MessageSerializationError)
154 }
155
156 pub fn as_json(&self) -> Result<String, ServiceError> {
158 serde_json::to_string(&self).map_err(|_| ServiceError::MessageSerializationError)
159 }
160
161 pub fn get_inner_message_kind(&self) -> &MessageKind {
163 match self {
164 Message::Dispute(k)
165 | Message::Order(k)
166 | Message::CantDo(k)
167 | Message::Rate(k)
168 | Message::Dm(k)
169 | Message::Restore(k) => k,
170 }
171 }
172
173 pub fn inner_action(&self) -> Option<Action> {
175 match self {
176 Message::Dispute(a)
177 | Message::Order(a)
178 | Message::CantDo(a)
179 | Message::Rate(a)
180 | Message::Dm(a)
181 | Message::Restore(a) => Some(a.get_action()),
182 }
183 }
184
185 pub fn verify(&self) -> bool {
187 match self {
188 Message::Order(m)
189 | Message::Dispute(m)
190 | Message::CantDo(m)
191 | Message::Rate(m)
192 | Message::Dm(m)
193 | Message::Restore(m) => m.verify(),
194 }
195 }
196
197 pub fn sign(message: String, keys: &Keys) -> Signature {
198 let hash: Sha256Hash = Sha256Hash::hash(message.as_bytes());
199 let hash = hash.to_byte_array();
200 let message: BitcoinMessage = BitcoinMessage::from_digest(hash);
201
202 keys.sign_schnorr(&message)
203 }
204
205 pub fn verify_signature(message: String, pubkey: PublicKey, sig: Signature) -> bool {
206 let hash: Sha256Hash = Sha256Hash::hash(message.as_bytes());
208 let hash = hash.to_byte_array();
209 let message: BitcoinMessage = BitcoinMessage::from_digest(hash);
210
211 let secp = Secp256k1::verification_only();
213 if let Ok(xonlykey) = pubkey.xonly() {
215 xonlykey.verify(&secp, &message, &sig).is_ok()
216 } else {
217 false
218 }
219 }
220}
221
222#[derive(Debug, Clone, Deserialize, Serialize)]
224pub struct MessageKind {
225 pub version: u8,
227 pub request_id: Option<u64>,
229 pub trade_index: Option<i64>,
231 #[serde(skip_serializing_if = "Option::is_none")]
233 pub id: Option<Uuid>,
234 pub action: Action,
236 pub payload: Option<Payload>,
238}
239
240type Amount = i64;
241
242#[derive(Debug, Deserialize, Serialize, Clone)]
244pub struct PaymentFailedInfo {
245 pub payment_attempts: u32,
247 pub payment_retries_interval: u32,
249}
250
251#[cfg_attr(feature = "sqlx", derive(FromRow, SqlxCrud))]
253#[derive(Debug, Deserialize, Serialize, Clone)]
254pub struct RestoredOrdersInfo {
255 pub order_id: Uuid,
257 pub trade_index: i64,
259 pub status: String,
261}
262
263#[cfg_attr(feature = "sqlx", derive(FromRow, SqlxCrud))]
265#[derive(Debug, Deserialize, Serialize, Clone)]
266pub struct RestoredDisputesInfo {
267 pub dispute_id: Uuid,
269 pub order_id: Uuid,
271 pub trade_index: i64,
273 pub status: String,
275}
276
277#[derive(Debug, Deserialize, Serialize, Clone, Default)]
279pub struct RestoreSessionInfo {
280 #[serde(rename = "orders")]
282 pub restore_orders: Vec<RestoredOrdersInfo>,
283 #[serde(rename = "disputes")]
285 pub restore_disputes: Vec<RestoredDisputesInfo>,
286}
287
288#[derive(Debug, Deserialize, Serialize, Clone)]
290#[serde(rename_all = "snake_case")]
291pub enum Payload {
292 Order(SmallOrder),
294 PaymentRequest(Option<SmallOrder>, String, Option<Amount>),
296 TextMessage(String),
298 Peer(Peer),
300 RatingUser(u8),
302 Amount(Amount),
304 Dispute(Uuid, Option<u16>, Option<SolverDisputeInfo>),
306 CantDo(Option<CantDoReason>),
308 NextTrade(String, u32),
312 PaymentFailed(PaymentFailedInfo),
314 RestoreData(RestoreSessionInfo),
316}
317
318#[allow(dead_code)]
319impl MessageKind {
320 pub fn new(
322 id: Option<Uuid>,
323 request_id: Option<u64>,
324 trade_index: Option<i64>,
325 action: Action,
326 payload: Option<Payload>,
327 ) -> Self {
328 Self {
329 version: PROTOCOL_VER,
330 request_id,
331 trade_index,
332 id,
333 action,
334 payload,
335 }
336 }
337 pub fn from_json(json: &str) -> Result<Self, ServiceError> {
339 serde_json::from_str(json).map_err(|_| ServiceError::MessageSerializationError)
340 }
341 pub fn as_json(&self) -> Result<String, ServiceError> {
343 serde_json::to_string(&self).map_err(|_| ServiceError::MessageSerializationError)
344 }
345
346 pub fn get_action(&self) -> Action {
348 self.action.clone()
349 }
350
351 pub fn get_next_trade_key(&self) -> Result<Option<(String, u32)>, ServiceError> {
353 match &self.payload {
354 Some(Payload::NextTrade(key, index)) => Ok(Some((key.to_string(), *index))),
355 None => Ok(None),
356 _ => Err(ServiceError::InvalidPayload),
357 }
358 }
359
360 pub fn get_rating(&self) -> Result<u8, ServiceError> {
361 if let Some(Payload::RatingUser(v)) = self.payload.to_owned() {
362 if !(MIN_RATING..=MAX_RATING).contains(&v) {
363 return Err(ServiceError::InvalidRatingValue);
364 }
365 Ok(v)
366 } else {
367 Err(ServiceError::InvalidRating)
368 }
369 }
370
371 pub fn verify(&self) -> bool {
373 match &self.action {
374 Action::NewOrder => matches!(&self.payload, Some(Payload::Order(_))),
375 Action::PayInvoice | Action::AddInvoice => {
376 if self.id.is_none() {
377 return false;
378 }
379 matches!(&self.payload, Some(Payload::PaymentRequest(_, _, _)))
380 }
381 Action::TakeSell
382 | Action::TakeBuy
383 | Action::FiatSent
384 | Action::FiatSentOk
385 | Action::Release
386 | Action::Released
387 | Action::Dispute
388 | Action::AdminCancel
389 | Action::AdminCanceled
390 | Action::AdminSettle
391 | Action::AdminSettled
392 | Action::Rate
393 | Action::RateReceived
394 | Action::AdminTakeDispute
395 | Action::AdminTookDispute
396 | Action::DisputeInitiatedByYou
397 | Action::DisputeInitiatedByPeer
398 | Action::WaitingBuyerInvoice
399 | Action::PurchaseCompleted
400 | Action::HoldInvoicePaymentAccepted
401 | Action::HoldInvoicePaymentSettled
402 | Action::HoldInvoicePaymentCanceled
403 | Action::WaitingSellerToPay
404 | Action::BuyerTookOrder
405 | Action::BuyerInvoiceAccepted
406 | Action::CooperativeCancelInitiatedByYou
407 | Action::CooperativeCancelInitiatedByPeer
408 | Action::CooperativeCancelAccepted
409 | Action::Cancel
410 | Action::InvoiceUpdated
411 | Action::AdminAddSolver
412 | Action::SendDm
413 | Action::TradePubkey
414 | Action::Canceled => {
415 if self.id.is_none() {
416 return false;
417 }
418 true
419 }
420 Action::PaymentFailed => {
421 if self.id.is_none() {
422 return false;
423 }
424 matches!(&self.payload, Some(Payload::PaymentFailed(_)))
425 }
426 Action::RateUser => {
427 matches!(&self.payload, Some(Payload::RatingUser(_)))
428 }
429 Action::CantDo => {
430 matches!(&self.payload, Some(Payload::CantDo(_)))
431 }
432 Action::RestoreSession => {
433 if self.id.is_some() || self.request_id.is_some() || self.trade_index.is_some() {
434 return false;
435 }
436 matches!(&self.payload, None | Some(Payload::RestoreData(_)))
437 }
438 }
439 }
440
441 pub fn get_order(&self) -> Option<&SmallOrder> {
442 if self.action != Action::NewOrder {
443 return None;
444 }
445 match &self.payload {
446 Some(Payload::Order(o)) => Some(o),
447 _ => None,
448 }
449 }
450
451 pub fn get_payment_request(&self) -> Option<String> {
452 if self.action != Action::TakeSell
453 && self.action != Action::AddInvoice
454 && self.action != Action::NewOrder
455 {
456 return None;
457 }
458 match &self.payload {
459 Some(Payload::PaymentRequest(_, pr, _)) => Some(pr.to_owned()),
460 Some(Payload::Order(ord)) => ord.buyer_invoice.to_owned(),
461 _ => None,
462 }
463 }
464
465 pub fn get_amount(&self) -> Option<Amount> {
466 if self.action != Action::TakeSell && self.action != Action::TakeBuy {
467 return None;
468 }
469 match &self.payload {
470 Some(Payload::PaymentRequest(_, _, amount)) => *amount,
471 Some(Payload::Amount(amount)) => Some(*amount),
472 _ => None,
473 }
474 }
475
476 pub fn get_payload(&self) -> Option<&Payload> {
477 self.payload.as_ref()
478 }
479
480 pub fn has_trade_index(&self) -> (bool, i64) {
481 if let Some(index) = self.trade_index {
482 return (true, index);
483 }
484 (false, 0)
485 }
486
487 pub fn trade_index(&self) -> i64 {
488 if let Some(index) = self.trade_index {
489 return index;
490 }
491 0
492 }
493}
494
495#[cfg(test)]
496mod test {
497 use crate::message::{Action, Message, MessageKind, Payload, Peer};
498 use crate::user::UserInfo;
499 use nostr_sdk::Keys;
500 use uuid::uuid;
501
502 #[test]
503 fn test_peer_with_reputation() {
504 let reputation = UserInfo {
506 rating: 4.5,
507 reviews: 10,
508 operating_days: 30,
509 };
510 let peer = Peer::new(
511 "npub1testjsf0runcqdht5apkfcalajxkf8txdxqqk5kgm0agc38ke4vsfsgzf8".to_string(),
512 Some(reputation.clone()),
513 );
514
515 assert_eq!(
517 peer.pubkey,
518 "npub1testjsf0runcqdht5apkfcalajxkf8txdxqqk5kgm0agc38ke4vsfsgzf8"
519 );
520 assert!(peer.reputation.is_some());
521 let peer_reputation = peer.reputation.clone().unwrap();
522 assert_eq!(peer_reputation.rating, 4.5);
523 assert_eq!(peer_reputation.reviews, 10);
524 assert_eq!(peer_reputation.operating_days, 30);
525
526 let json = peer.as_json().unwrap();
528 let deserialized_peer = Peer::from_json(&json).unwrap();
529 assert_eq!(deserialized_peer.pubkey, peer.pubkey);
530 assert!(deserialized_peer.reputation.is_some());
531 let deserialized_reputation = deserialized_peer.reputation.unwrap();
532 assert_eq!(deserialized_reputation.rating, 4.5);
533 assert_eq!(deserialized_reputation.reviews, 10);
534 assert_eq!(deserialized_reputation.operating_days, 30);
535 }
536
537 #[test]
538 fn test_peer_without_reputation() {
539 let peer = Peer::new(
541 "npub1testjsf0runcqdht5apkfcalajxkf8txdxqqk5kgm0agc38ke4vsfsgzf8".to_string(),
542 None,
543 );
544
545 assert_eq!(
547 peer.pubkey,
548 "npub1testjsf0runcqdht5apkfcalajxkf8txdxqqk5kgm0agc38ke4vsfsgzf8"
549 );
550 assert!(peer.reputation.is_none());
551
552 let json = peer.as_json().unwrap();
554 let deserialized_peer = Peer::from_json(&json).unwrap();
555 assert_eq!(deserialized_peer.pubkey, peer.pubkey);
556 assert!(deserialized_peer.reputation.is_none());
557 }
558
559 #[test]
560 fn test_peer_in_message() {
561 let uuid = uuid!("308e1272-d5f4-47e6-bd97-3504baea9c23");
562
563 let reputation = UserInfo {
565 rating: 4.5,
566 reviews: 10,
567 operating_days: 30,
568 };
569 let peer_with_reputation = Peer::new(
570 "npub1testjsf0runcqdht5apkfcalajxkf8txdxqqk5kgm0agc38ke4vsfsgzf8".to_string(),
571 Some(reputation),
572 );
573 let payload_with_reputation = Payload::Peer(peer_with_reputation);
574 let message_with_reputation = Message::Order(MessageKind::new(
575 Some(uuid),
576 Some(1),
577 Some(2),
578 Action::FiatSentOk,
579 Some(payload_with_reputation),
580 ));
581
582 assert!(message_with_reputation.verify());
584 let message_json = message_with_reputation.as_json().unwrap();
585 let deserialized_message = Message::from_json(&message_json).unwrap();
586 assert!(deserialized_message.verify());
587
588 let peer_without_reputation = Peer::new(
590 "npub1testjsf0runcqdht5apkfcalajxkf8txdxqqk5kgm0agc38ke4vsfsgzf8".to_string(),
591 None,
592 );
593 let payload_without_reputation = Payload::Peer(peer_without_reputation);
594 let message_without_reputation = Message::Order(MessageKind::new(
595 Some(uuid),
596 Some(1),
597 Some(2),
598 Action::FiatSentOk,
599 Some(payload_without_reputation),
600 ));
601
602 assert!(message_without_reputation.verify());
604 let message_json = message_without_reputation.as_json().unwrap();
605 let deserialized_message = Message::from_json(&message_json).unwrap();
606 assert!(deserialized_message.verify());
607 }
608
609 #[test]
610 fn test_payment_failed_payload() {
611 let uuid = uuid!("308e1272-d5f4-47e6-bd97-3504baea9c23");
612
613 let payment_failed_info = crate::message::PaymentFailedInfo {
615 payment_attempts: 3,
616 payment_retries_interval: 60,
617 };
618
619 let payload = Payload::PaymentFailed(payment_failed_info);
620 let message = Message::Order(MessageKind::new(
621 Some(uuid),
622 Some(1),
623 Some(2),
624 Action::PaymentFailed,
625 Some(payload),
626 ));
627
628 assert!(message.verify());
630
631 let message_json = message.as_json().unwrap();
633
634 let deserialized_message = Message::from_json(&message_json).unwrap();
636 assert!(deserialized_message.verify());
637
638 if let Message::Order(kind) = deserialized_message {
640 if let Some(Payload::PaymentFailed(info)) = kind.payload {
641 assert_eq!(info.payment_attempts, 3);
642 assert_eq!(info.payment_retries_interval, 60);
643 } else {
644 panic!("Expected PaymentFailed payload");
645 }
646 } else {
647 panic!("Expected Order message");
648 }
649 }
650
651 #[test]
652 fn test_message_payload_signature() {
653 let uuid = uuid!("308e1272-d5f4-47e6-bd97-3504baea9c23");
654 let peer = Peer::new(
655 "npub1testjsf0runcqdht5apkfcalajxkf8txdxqqk5kgm0agc38ke4vsfsgzf8".to_string(),
656 None, );
658 let payload = Payload::Peer(peer);
659 let test_message = Message::Order(MessageKind::new(
660 Some(uuid),
661 Some(1),
662 Some(2),
663 Action::FiatSentOk,
664 Some(payload),
665 ));
666 assert!(test_message.verify());
667 let test_message_json = test_message.as_json().unwrap();
668 let trade_keys =
670 Keys::parse("110e43647eae221ab1da33ddc17fd6ff423f2b2f49d809b9ffa40794a2ab996c")
671 .unwrap();
672 let sig = Message::sign(test_message_json.clone(), &trade_keys);
673
674 assert!(Message::verify_signature(
675 test_message_json,
676 trade_keys.public_key(),
677 sig
678 ));
679 }
680
681 #[test]
682 fn test_restore_session_message() {
683 let restore_request_message = Message::Restore(MessageKind::new(
685 None,
686 None,
687 None,
688 Action::RestoreSession,
689 None,
690 ));
691
692 assert!(restore_request_message.verify());
694 assert_eq!(
695 restore_request_message.inner_action(),
696 Some(Action::RestoreSession)
697 );
698
699 let message_json = restore_request_message.as_json().unwrap();
701 let deserialized_message = Message::from_json(&message_json).unwrap();
702 assert!(deserialized_message.verify());
703 assert_eq!(
704 deserialized_message.inner_action(),
705 Some(Action::RestoreSession)
706 );
707
708 let restored_orders = vec![
710 crate::message::RestoredOrdersInfo {
711 order_id: uuid!("308e1272-d5f4-47e6-bd97-3504baea9c23"),
712 trade_index: 1,
713 status: "active".to_string(),
714 },
715 crate::message::RestoredOrdersInfo {
716 order_id: uuid!("408e1272-d5f4-47e6-bd97-3504baea9c24"),
717 trade_index: 2,
718 status: "success".to_string(),
719 },
720 ];
721
722 let restored_disputes = vec![crate::message::RestoredDisputesInfo {
723 dispute_id: uuid!("508e1272-d5f4-47e6-bd97-3504baea9c25"),
724 order_id: uuid!("308e1272-d5f4-47e6-bd97-3504baea9c23"),
725 trade_index: 1,
726 status: "initiated".to_string(),
727 }];
728
729 let restore_session_info = crate::message::RestoreSessionInfo {
730 restore_orders: restored_orders.clone(),
731 restore_disputes: restored_disputes.clone(),
732 };
733
734 let restore_data_payload = Payload::RestoreData(restore_session_info);
735 let restore_data_message = Message::Restore(MessageKind::new(
736 None,
737 None,
738 None,
739 Action::RestoreSession,
740 Some(restore_data_payload),
741 ));
742
743 assert!(restore_data_message.verify());
745 assert_eq!(
746 restore_data_message.inner_action(),
747 Some(Action::RestoreSession)
748 );
749
750 let message_json = restore_data_message.as_json().unwrap();
752 let deserialized_message = Message::from_json(&message_json).unwrap();
753 assert!(deserialized_message.verify());
754 assert_eq!(
755 deserialized_message.inner_action(),
756 Some(Action::RestoreSession)
757 );
758
759 if let Message::Restore(kind) = deserialized_message {
761 if let Some(Payload::RestoreData(info)) = kind.payload {
762 assert_eq!(info.restore_orders.len(), 2);
763 assert_eq!(info.restore_disputes.len(), 1);
764
765 assert_eq!(
767 info.restore_orders[0].order_id,
768 uuid!("308e1272-d5f4-47e6-bd97-3504baea9c23")
769 );
770 assert_eq!(info.restore_orders[0].trade_index, 1);
771 assert_eq!(info.restore_orders[0].status, "active");
772
773 assert_eq!(
775 info.restore_orders[1].order_id,
776 uuid!("408e1272-d5f4-47e6-bd97-3504baea9c24")
777 );
778 assert_eq!(info.restore_orders[1].trade_index, 2);
779 assert_eq!(info.restore_orders[1].status, "success");
780
781 assert_eq!(
783 info.restore_disputes[0].dispute_id,
784 uuid!("508e1272-d5f4-47e6-bd97-3504baea9c25")
785 );
786 assert_eq!(
787 info.restore_disputes[0].order_id,
788 uuid!("308e1272-d5f4-47e6-bd97-3504baea9c23")
789 );
790 assert_eq!(info.restore_disputes[0].trade_index, 1);
791 assert_eq!(info.restore_disputes[0].status, "initiated");
792 } else {
793 panic!("Expected RestoreData payload");
794 }
795 } else {
796 panic!("Expected Restore message");
797 }
798 }
799
800 #[test]
801 fn test_restore_session_message_validation() {
802 let restore_request_message = Message::Restore(MessageKind::new(
804 None,
805 None,
806 None,
807 Action::RestoreSession,
808 None, ));
810
811 assert!(restore_request_message.verify());
813
814 let wrong_payload = Payload::TextMessage("wrong payload".to_string());
816 let wrong_message = Message::Restore(MessageKind::new(
817 None,
818 None,
819 None,
820 Action::RestoreSession,
821 Some(wrong_payload),
822 ));
823
824 assert!(!wrong_message.verify());
826
827 let with_id = Message::Restore(MessageKind::new(
829 Some(uuid!("00000000-0000-0000-0000-000000000001")),
830 None,
831 None,
832 Action::RestoreSession,
833 None,
834 ));
835 assert!(!with_id.verify());
836
837 let with_request_id = Message::Restore(MessageKind::new(
839 None,
840 Some(42),
841 None,
842 Action::RestoreSession,
843 None,
844 ));
845 assert!(!with_request_id.verify());
846
847 let with_trade_index = Message::Restore(MessageKind::new(
849 None,
850 None,
851 Some(7),
852 Action::RestoreSession,
853 None,
854 ));
855 assert!(!with_trade_index.verify());
856 }
857
858 #[test]
859 fn test_restore_session_message_constructor() {
860 let restore_request_message = Message::new_restore(None);
862
863 assert!(matches!(restore_request_message, Message::Restore(_)));
864 assert!(restore_request_message.verify());
865 assert_eq!(
866 restore_request_message.inner_action(),
867 Some(Action::RestoreSession)
868 );
869
870 let restore_session_info = crate::message::RestoreSessionInfo {
872 restore_orders: vec![],
873 restore_disputes: vec![],
874 };
875 let restore_data_message =
876 Message::new_restore(Some(Payload::RestoreData(restore_session_info)));
877
878 assert!(matches!(restore_data_message, Message::Restore(_)));
879 assert!(restore_data_message.verify());
880 assert_eq!(
881 restore_data_message.inner_action(),
882 Some(Action::RestoreSession)
883 );
884 }
885}