bright_ln_models/
hodl_invoice.rs

1use base64::prelude::*;
2use serde::{Deserialize, Serialize};
3
4#[derive(Debug, thiserror::Error)]
5pub enum LndHodlInvoiceError {
6    #[error("Invalid HodlState")]
7    InvalidHodlState,
8    #[error("Invalid LndHodlInvoice")]
9    InvalidLndHodlInvoice,
10    #[error("InvoiceError: {0}")]
11    InvoiceError(lightning_invoice::ParseOrSemanticError),
12    #[error("Base64Error: {0}")]
13    Base64Error(#[from] base64::DecodeError),
14    #[error("SerdeJsonError: {0}")]
15    SerdeJsonError(#[from] serde_json::Error),
16}
17
18#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
19pub struct LndHodlInvoice {
20    payment_addr: String,
21    payment_request: String,
22    add_index: String,
23}
24impl LndHodlInvoice {
25    #[must_use]
26    pub fn payment_hash(&self) -> Vec<u8> {
27        self.payment_addr.as_bytes().to_vec()
28    }
29    #[must_use]
30    pub fn payment_request(&self) -> String {
31        self.payment_request.clone()
32    }
33    #[must_use]
34    pub fn payment_hash_url_safe(&self) -> String {
35        let payment_hash = self.payment_hash();
36        BASE64_URL_SAFE.encode(payment_hash)
37    }
38    /// Returns the payment request as a base64 url safe string
39    ///
40    /// # Errors
41    ///
42    /// Will return an error if the payment request is not a valid bolt11 invoice
43    pub fn r_hash_url_safe(&self) -> Result<String, LndHodlInvoiceError> {
44        let r_hash = self
45            .payment_request
46            .parse::<lightning_invoice::Bolt11Invoice>()
47            .map_err(LndHodlInvoiceError::InvoiceError)?;
48        Ok(BASE64_URL_SAFE.encode(r_hash.payment_hash()))
49    }
50    /// Returns the satoshi amount of the invoice
51    ///
52    /// # Errors
53    ///
54    /// Will return an error if the payment request is not a valid bolt11 invoice
55    pub fn sat_amount(&self) -> Result<u64, LndHodlInvoiceError> {
56        let bolt11 = self.payment_request.clone();
57        let bolt11 = bolt11
58            .parse::<lightning_invoice::Bolt11Invoice>()
59            .map_err(LndHodlInvoiceError::InvoiceError)?;
60        Ok(bolt11.amount_milli_satoshis().unwrap_or_default() / 1000)
61    }
62}
63impl std::str::FromStr for LndHodlInvoice {
64    type Err = LndHodlInvoiceError;
65    fn from_str(s: &str) -> Result<Self, Self::Err> {
66        Ok(serde_json::from_str(s)?)
67    }
68}
69impl TryFrom<LndHodlInvoice> for String {
70    type Error = LndHodlInvoiceError;
71    fn try_from(value: LndHodlInvoice) -> Result<Self, Self::Error> {
72        Ok(serde_json::to_string(&value)?)
73    }
74}
75
76#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
77#[serde(rename_all = "UPPERCASE")]
78pub enum HodlState {
79    Open,
80    Accepted,
81    Canceled,
82    Settled,
83}
84impl std::str::FromStr for HodlState {
85    type Err = LndHodlInvoiceError;
86    fn from_str(s: &str) -> Result<Self, Self::Err> {
87        Ok(serde_json::from_str(s)?)
88    }
89}
90impl AsRef<str> for HodlState {
91    fn as_ref(&self) -> &str {
92        match self {
93            Self::Open => "OPEN",
94            Self::Accepted => "ACCEPTED",
95            Self::Canceled => "CANCELED",
96            Self::Settled => "SETTLED",
97        }
98    }
99}
100
101#[derive(Debug, Clone, Serialize, Deserialize)]
102pub struct LndHodlInvoiceState {
103    settled: bool,
104    state: HodlState,
105    r_hash: String,
106    payment_request: String,
107}
108impl std::str::FromStr for LndHodlInvoiceState {
109    type Err = LndHodlInvoiceError;
110    fn from_str(s: &str) -> Result<Self, Self::Err> {
111        Ok(serde_json::from_str::<Self>(s)?)
112    }
113}
114impl TryInto<String> for LndHodlInvoiceState {
115    type Error = LndHodlInvoiceError;
116    fn try_into(self) -> Result<String, Self::Error> {
117        Ok(serde_json::to_string(&self)?)
118    }
119}
120impl LndHodlInvoiceState {
121    #[must_use]
122    pub const fn settled(&self) -> bool {
123        self.settled
124    }
125    #[must_use]
126    pub fn state(&self) -> HodlState {
127        self.state.clone()
128    }
129    #[must_use]
130    pub fn r_hash(&self) -> String {
131        self.r_hash.clone()
132    }
133    #[must_use]
134    pub fn r_hash_url_safe(&self) -> String {
135        BASE64_URL_SAFE.encode(self.r_hash.as_bytes())
136    }
137    #[must_use]
138    pub fn payment_request(&self) -> String {
139        self.payment_request.clone()
140    }
141}