1use crate::dispute::SolverDisputeInfo;
2use crate::error::ServiceError;
3use crate::PROTOCOL_VER;
4use crate::{error::CantDoReason, order::SmallOrder};
5use anyhow::Result;
6use bitcoin::hashes::sha256::Hash as Sha256Hash;
7use bitcoin::hashes::Hash;
8use bitcoin::key::Secp256k1;
9use bitcoin::secp256k1::Message as BitcoinMessage;
10use nostr_sdk::prelude::*;
11use serde::{Deserialize, Serialize};
12use std::fmt;
13use uuid::Uuid;
14
15pub const MAX_RATING: u8 = 5;
17pub const MIN_RATING: u8 = 1;
19
20#[derive(Debug, Deserialize, Serialize, Clone)]
22pub struct Peer {
23 pub pubkey: String,
24}
25
26impl Peer {
27 pub fn new(pubkey: String) -> Self {
28 Self { pubkey }
29 }
30
31 pub fn from_json(json: &str) -> Result<Self> {
32 Ok(serde_json::from_str(json)?)
33 }
34
35 pub fn as_json(&self) -> Result<String> {
36 Ok(serde_json::to_string(&self)?)
37 }
38}
39
40#[derive(Debug, PartialEq, Eq, Deserialize, Serialize, Clone)]
42#[serde(rename_all = "kebab-case")]
43pub enum Action {
44 NewOrder,
45 TakeSell,
46 TakeBuy,
47 PayInvoice,
48 FiatSent,
49 FiatSentOk,
50 Release,
51 Released,
52 Cancel,
53 Canceled,
54 CooperativeCancelInitiatedByYou,
55 CooperativeCancelInitiatedByPeer,
56 DisputeInitiatedByYou,
57 DisputeInitiatedByPeer,
58 CooperativeCancelAccepted,
59 BuyerInvoiceAccepted,
60 PurchaseCompleted,
61 HoldInvoicePaymentAccepted,
62 HoldInvoicePaymentSettled,
63 HoldInvoicePaymentCanceled,
64 WaitingSellerToPay,
65 WaitingBuyerInvoice,
66 AddInvoice,
67 BuyerTookOrder,
68 Rate,
69 RateUser,
70 RateReceived,
71 CantDo,
72 Dispute,
73 AdminCancel,
74 AdminCanceled,
75 AdminSettle,
76 AdminSettled,
77 AdminAddSolver,
78 AdminTakeDispute,
79 AdminTookDispute,
80 PaymentFailed,
81 InvoiceUpdated,
82 SendDm,
83 TradePubkey,
84}
85
86impl fmt::Display for Action {
87 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
88 write!(f, "{self:?}")
89 }
90}
91
92#[derive(Debug, Clone, Deserialize, Serialize)]
94#[serde(rename_all = "kebab-case")]
95pub enum Message {
96 Order(MessageKind),
97 Dispute(MessageKind),
98 CantDo(MessageKind),
99 Rate(MessageKind),
100 Dm(MessageKind),
101}
102
103impl Message {
104 pub fn new_order(
106 id: Option<Uuid>,
107 request_id: Option<u64>,
108 trade_index: Option<i64>,
109 action: Action,
110 payload: Option<Payload>,
111 ) -> Self {
112 let kind = MessageKind::new(id, request_id, trade_index, action, payload);
113 Self::Order(kind)
114 }
115
116 pub fn new_dispute(
118 id: Option<Uuid>,
119 request_id: Option<u64>,
120 trade_index: Option<i64>,
121 action: Action,
122 payload: Option<Payload>,
123 ) -> Self {
124 let kind = MessageKind::new(id, request_id, trade_index, action, payload);
125
126 Self::Dispute(kind)
127 }
128
129 pub fn cant_do(id: Option<Uuid>, request_id: Option<u64>, payload: Option<Payload>) -> Self {
131 let kind = MessageKind::new(id, request_id, None, Action::CantDo, payload);
132
133 Self::CantDo(kind)
134 }
135
136 pub fn new_dm(
138 id: Option<Uuid>,
139 request_id: Option<u64>,
140 action: Action,
141 payload: Option<Payload>,
142 ) -> Self {
143 let kind = MessageKind::new(id, request_id, None, action, payload);
144
145 Self::Dm(kind)
146 }
147
148 pub fn from_json(json: &str) -> Result<Self> {
150 Ok(serde_json::from_str(json)?)
151 }
152
153 pub fn as_json(&self) -> Result<String> {
155 Ok(serde_json::to_string(&self)?)
156 }
157
158 pub fn get_inner_message_kind(&self) -> &MessageKind {
160 match self {
161 Message::Dispute(k)
162 | Message::Order(k)
163 | Message::CantDo(k)
164 | Message::Rate(k)
165 | Message::Dm(k) => k,
166 }
167 }
168
169 pub fn inner_action(&self) -> Option<Action> {
171 match self {
172 Message::Dispute(a)
173 | Message::Order(a)
174 | Message::CantDo(a)
175 | Message::Rate(a)
176 | Message::Dm(a) => Some(a.get_action()),
177 }
178 }
179
180 pub fn verify(&self) -> bool {
182 match self {
183 Message::Order(m)
184 | Message::Dispute(m)
185 | Message::CantDo(m)
186 | Message::Rate(m)
187 | Message::Dm(m) => m.verify(),
188 }
189 }
190
191 pub fn sign(message: String, keys: &Keys) -> Signature {
192 let hash: Sha256Hash = Sha256Hash::hash(message.as_bytes());
193 let hash = hash.to_byte_array();
194 let message: BitcoinMessage = BitcoinMessage::from_digest(hash);
195
196 keys.sign_schnorr(&message)
197 }
198
199 pub fn verify_signature(message: String, pubkey: PublicKey, sig: Signature) -> bool {
200 let hash: Sha256Hash = Sha256Hash::hash(message.as_bytes());
202 let hash = hash.to_byte_array();
203 let message: BitcoinMessage = BitcoinMessage::from_digest(hash);
204 let secp = Secp256k1::verification_only();
206 pubkey.verify(&secp, &message, &sig).is_ok()
208 }
209}
210
211#[derive(Debug, Clone, Deserialize, Serialize)]
213pub struct MessageKind {
214 pub version: u8,
216 pub request_id: Option<u64>,
218 pub trade_index: Option<i64>,
220 #[serde(skip_serializing_if = "Option::is_none")]
222 pub id: Option<Uuid>,
223 pub action: Action,
225 pub payload: Option<Payload>,
227}
228
229type Amount = i64;
230
231#[derive(Debug, Deserialize, Serialize, Clone)]
233#[serde(rename_all = "snake_case")]
234pub enum Payload {
235 Order(SmallOrder),
237 PaymentRequest(Option<SmallOrder>, String, Option<Amount>),
239 TextMessage(String),
241 Peer(Peer),
243 RatingUser(u8),
245 Amount(Amount),
247 Dispute(Uuid, Option<u16>, Option<SolverDisputeInfo>),
249 CantDo(Option<CantDoReason>),
251 NextTrade(String, u32),
255}
256
257#[allow(dead_code)]
258impl MessageKind {
259 pub fn new(
261 id: Option<Uuid>,
262 request_id: Option<u64>,
263 trade_index: Option<i64>,
264 action: Action,
265 payload: Option<Payload>,
266 ) -> Self {
267 Self {
268 version: PROTOCOL_VER,
269 request_id,
270 trade_index,
271 id,
272 action,
273 payload,
274 }
275 }
276 pub fn from_json(json: &str) -> Result<Self> {
278 Ok(serde_json::from_str(json)?)
279 }
280 pub fn as_json(&self) -> Result<String> {
282 Ok(serde_json::to_string(&self)?)
283 }
284
285 pub fn get_action(&self) -> Action {
287 self.action.clone()
288 }
289
290 pub fn get_next_trade_key(&self) -> Result<Option<(String, u32)>, ServiceError> {
292 match &self.payload {
293 Some(Payload::NextTrade(key, index)) => Ok(Some((key.to_string(), *index))),
294 None => Ok(None),
295 _ => Err(ServiceError::InvalidPayload),
296 }
297 }
298
299 pub fn get_rating(&self) -> Result<u8, ServiceError> {
300 if let Some(Payload::RatingUser(v)) = self.payload.to_owned() {
301 if !(MIN_RATING..=MAX_RATING).contains(&v) {
302 return Err(ServiceError::InvalidRatingValue);
303 }
304 Ok(v)
305 } else {
306 Err(ServiceError::InvalidRating)
307 }
308 }
309
310 pub fn verify(&self) -> bool {
312 match &self.action {
313 Action::NewOrder => matches!(&self.payload, Some(Payload::Order(_))),
314 Action::PayInvoice | Action::AddInvoice => {
315 if self.id.is_none() {
316 return false;
317 }
318 matches!(&self.payload, Some(Payload::PaymentRequest(_, _, _)))
319 }
320 Action::TakeSell
321 | Action::TakeBuy
322 | Action::FiatSent
323 | Action::FiatSentOk
324 | Action::Release
325 | Action::Released
326 | Action::Dispute
327 | Action::AdminCancel
328 | Action::AdminCanceled
329 | Action::AdminSettle
330 | Action::AdminSettled
331 | Action::Rate
332 | Action::RateReceived
333 | Action::AdminTakeDispute
334 | Action::AdminTookDispute
335 | Action::DisputeInitiatedByYou
336 | Action::DisputeInitiatedByPeer
337 | Action::WaitingBuyerInvoice
338 | Action::PurchaseCompleted
339 | Action::HoldInvoicePaymentAccepted
340 | Action::HoldInvoicePaymentSettled
341 | Action::HoldInvoicePaymentCanceled
342 | Action::WaitingSellerToPay
343 | Action::BuyerTookOrder
344 | Action::BuyerInvoiceAccepted
345 | Action::CooperativeCancelInitiatedByYou
346 | Action::CooperativeCancelInitiatedByPeer
347 | Action::CooperativeCancelAccepted
348 | Action::Cancel
349 | Action::PaymentFailed
350 | Action::InvoiceUpdated
351 | Action::AdminAddSolver
352 | Action::SendDm
353 | Action::TradePubkey
354 | Action::Canceled => {
355 if self.id.is_none() {
356 return false;
357 }
358 true
359 }
360 Action::RateUser => {
361 matches!(&self.payload, Some(Payload::RatingUser(_)))
362 }
363 Action::CantDo => {
364 matches!(&self.payload, Some(Payload::CantDo(_)))
365 }
366 }
367 }
368
369 pub fn get_order(&self) -> Option<&SmallOrder> {
370 if self.action != Action::NewOrder {
371 return None;
372 }
373 match &self.payload {
374 Some(Payload::Order(o)) => Some(o),
375 _ => None,
376 }
377 }
378
379 pub fn get_payment_request(&self) -> Option<String> {
380 if self.action != Action::TakeSell
381 && self.action != Action::AddInvoice
382 && self.action != Action::NewOrder
383 {
384 return None;
385 }
386 match &self.payload {
387 Some(Payload::PaymentRequest(_, pr, _)) => Some(pr.to_owned()),
388 Some(Payload::Order(ord)) => ord.buyer_invoice.to_owned(),
389 _ => None,
390 }
391 }
392
393 pub fn get_amount(&self) -> Option<Amount> {
394 if self.action != Action::TakeSell && self.action != Action::TakeBuy {
395 return None;
396 }
397 match &self.payload {
398 Some(Payload::PaymentRequest(_, _, amount)) => *amount,
399 Some(Payload::Amount(amount)) => Some(*amount),
400 _ => None,
401 }
402 }
403
404 pub fn get_payload(&self) -> Option<&Payload> {
405 self.payload.as_ref()
406 }
407
408 pub fn has_trade_index(&self) -> (bool, i64) {
409 if let Some(index) = self.trade_index {
410 return (true, index);
411 }
412 (false, 0)
413 }
414
415 pub fn trade_index(&self) -> i64 {
416 if let Some(index) = self.trade_index {
417 return index;
418 }
419 0
420 }
421}