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 hash_str = hex::encode(hash);
194 println!("hash en sign() en core: {:?}", hash_str);
195 let message: BitcoinMessage = BitcoinMessage::from_digest(hash);
196
197 keys.sign_schnorr(&message)
198 }
199
200 pub fn verify_signature(message: String, pubkey: PublicKey, sig: Signature) -> bool {
201 let hash: Sha256Hash = Sha256Hash::hash(message.as_bytes());
203 let hash = hash.to_byte_array();
204 let message: BitcoinMessage = BitcoinMessage::from_digest(hash);
205 let secp = Secp256k1::verification_only();
207 pubkey.verify(&secp, &message, &sig).is_ok()
209 }
210}
211
212#[derive(Debug, Clone, Deserialize, Serialize)]
214pub struct MessageKind {
215 pub version: u8,
217 pub request_id: Option<u64>,
219 pub trade_index: Option<i64>,
221 #[serde(skip_serializing_if = "Option::is_none")]
223 pub id: Option<Uuid>,
224 pub action: Action,
226 pub payload: Option<Payload>,
228}
229
230type Amount = i64;
231
232#[derive(Debug, Deserialize, Serialize, Clone)]
234#[serde(rename_all = "snake_case")]
235pub enum Payload {
236 Order(SmallOrder),
238 PaymentRequest(Option<SmallOrder>, String, Option<Amount>),
240 TextMessage(String),
242 Peer(Peer),
244 RatingUser(u8),
246 Amount(Amount),
248 Dispute(Uuid, Option<u16>),
250 CantDo(Option<CantDoReason>),
252 NextTrade(String, u32),
256}
257
258#[allow(dead_code)]
259impl MessageKind {
260 pub fn new(
262 id: Option<Uuid>,
263 request_id: Option<u64>,
264 trade_index: Option<i64>,
265 action: Action,
266 payload: Option<Payload>,
267 ) -> Self {
268 Self {
269 version: PROTOCOL_VER,
270 request_id,
271 trade_index,
272 id,
273 action,
274 payload,
275 }
276 }
277 pub fn from_json(json: &str) -> Result<Self> {
279 Ok(serde_json::from_str(json)?)
280 }
281 pub fn as_json(&self) -> Result<String> {
283 Ok(serde_json::to_string(&self)?)
284 }
285
286 pub fn get_action(&self) -> Action {
288 self.action.clone()
289 }
290
291 pub fn get_next_trade_key(&self) -> Result<Option<(String, u32)>, ServiceError> {
293 match &self.payload {
294 Some(Payload::NextTrade(key, index)) => Ok(Some((key.to_string(), *index))),
295 None => Ok(None),
296 _ => Err(ServiceError::InvalidPayload),
297 }
298 }
299
300 pub fn get_rating(&self) -> Result<u8, ServiceError> {
301 if let Some(Payload::RatingUser(v)) = self.payload.to_owned() {
302 if !(MIN_RATING..=MAX_RATING).contains(&v) {
303 return Err(ServiceError::InvalidRatingValue);
304 }
305 Ok(v)
306 } else {
307 Err(ServiceError::InvalidRating)
308 }
309 }
310
311 pub fn verify(&self) -> bool {
313 match &self.action {
314 Action::NewOrder => matches!(&self.payload, Some(Payload::Order(_))),
315 Action::PayInvoice | Action::AddInvoice => {
316 if self.id.is_none() {
317 return false;
318 }
319 matches!(&self.payload, Some(Payload::PaymentRequest(_, _, _)))
320 }
321 Action::TakeSell
322 | Action::TakeBuy
323 | Action::FiatSent
324 | Action::FiatSentOk
325 | Action::Release
326 | Action::Released
327 | Action::Dispute
328 | Action::AdminCancel
329 | Action::AdminCanceled
330 | Action::AdminSettle
331 | Action::AdminSettled
332 | Action::Rate
333 | Action::RateReceived
334 | Action::AdminTakeDispute
335 | Action::AdminTookDispute
336 | Action::DisputeInitiatedByYou
337 | Action::DisputeInitiatedByPeer
338 | Action::WaitingBuyerInvoice
339 | Action::PurchaseCompleted
340 | Action::HoldInvoicePaymentAccepted
341 | Action::HoldInvoicePaymentSettled
342 | Action::HoldInvoicePaymentCanceled
343 | Action::WaitingSellerToPay
344 | Action::BuyerTookOrder
345 | Action::BuyerInvoiceAccepted
346 | Action::CooperativeCancelInitiatedByYou
347 | Action::CooperativeCancelInitiatedByPeer
348 | Action::CooperativeCancelAccepted
349 | Action::Cancel
350 | Action::PaymentFailed
351 | Action::InvoiceUpdated
352 | Action::AdminAddSolver
353 | Action::SendDm
354 | Action::TradePubkey
355 | Action::Canceled => {
356 if self.id.is_none() {
357 return false;
358 }
359 true
360 }
361 Action::RateUser => {
362 matches!(&self.payload, Some(Payload::RatingUser(_)))
363 }
364 Action::CantDo => {
365 matches!(&self.payload, Some(Payload::CantDo(_)))
366 }
367 }
368 }
369
370 pub fn get_order(&self) -> Option<&SmallOrder> {
371 if self.action != Action::NewOrder {
372 return None;
373 }
374 match &self.payload {
375 Some(Payload::Order(o)) => Some(o),
376 _ => None,
377 }
378 }
379
380 pub fn get_payment_request(&self) -> Option<String> {
381 if self.action != Action::TakeSell
382 && self.action != Action::AddInvoice
383 && self.action != Action::NewOrder
384 {
385 return None;
386 }
387 match &self.payload {
388 Some(Payload::PaymentRequest(_, pr, _)) => Some(pr.to_owned()),
389 Some(Payload::Order(ord)) => ord.buyer_invoice.to_owned(),
390 _ => None,
391 }
392 }
393
394 pub fn get_amount(&self) -> Option<Amount> {
395 if self.action != Action::TakeSell && self.action != Action::TakeBuy {
396 return None;
397 }
398 match &self.payload {
399 Some(Payload::PaymentRequest(_, _, amount)) => *amount,
400 Some(Payload::Amount(amount)) => Some(*amount),
401 _ => None,
402 }
403 }
404
405 pub fn get_payload(&self) -> Option<&Payload> {
406 self.payload.as_ref()
407 }
408
409 pub fn has_trade_index(&self) -> (bool, i64) {
410 if let Some(index) = self.trade_index {
411 return (true, index);
412 }
413 (false, 0)
414 }
415}