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