1use bitcoin::constants::ChainHash;
2pub use lightning::offers::invoice::Bolt12Invoice;
3pub use lightning_invoice::Bolt11Invoice;
4pub use lightning::offers::offer::{Amount as OfferAmount, Offer};
5
6use std::fmt;
7use std::borrow::Borrow;
8use std::str::FromStr;
9
10use bitcoin::{Amount, Network};
11use bitcoin::bech32::{encode_to_fmt, EncodeError, Hrp, NoChecksum, primitives::decode::CheckedHrpstring};
12use bitcoin::hashes::{sha256, Hash};
13use bitcoin::secp256k1::Message;
14use lightning::offers::parse::Bolt12ParseError;
15use lightning::util::ser::Writeable;
16
17use bitcoin_ext::{AmountExt, P2TR_DUST};
18
19use crate::SECP;
20
21const BECH32_BOLT12_INVOICE_HRP: &str = "lni";
22
23pub const HTLC_MIN_FEE: Amount = P2TR_DUST;
25
26pub const PREIMAGE_SIZE: usize = 32;
27pub const PAYMENT_HASH_SIZE: usize = 32;
28
29#[derive(Clone, Copy, PartialEq, Eq, Hash)]
31pub struct Preimage([u8; PREIMAGE_SIZE]);
32impl_byte_newtype!(Preimage, PREIMAGE_SIZE);
33
34impl Preimage {
35 pub fn random() -> Preimage {
37 Preimage(rand::random())
38 }
39
40 pub fn compute_payment_hash(&self) -> PaymentHash {
42 sha256::Hash::hash(self.as_ref()).into()
43 }
44}
45
46#[derive(Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
48pub struct PaymentHash([u8; PAYMENT_HASH_SIZE]);
49impl_byte_newtype!(PaymentHash, PAYMENT_HASH_SIZE);
50
51impl From<sha256::Hash> for PaymentHash {
52 fn from(hash: sha256::Hash) -> Self {
53 PaymentHash(hash.to_byte_array())
54 }
55}
56
57impl From<Preimage> for PaymentHash {
58 fn from(preimage: Preimage) -> Self {
59 preimage.compute_payment_hash()
60 }
61}
62
63impl From<lightning::types::payment::PaymentHash> for PaymentHash {
64 fn from(hash: lightning::types::payment::PaymentHash) -> Self {
65 PaymentHash(hash.0)
66 }
67}
68
69impl<'a> From<&'a Bolt11Invoice> for PaymentHash {
70 fn from(i: &'a Bolt11Invoice) -> Self {
71 (*i.payment_hash()).into()
72 }
73}
74
75impl From<Bolt11Invoice> for PaymentHash {
76 fn from(i: Bolt11Invoice) -> Self {
77 (&i).into()
78 }
79}
80
81impl PaymentHash {
82 pub fn to_sha256_hash(&self) -> bitcoin::hashes::sha256::Hash {
84 bitcoin::hashes::sha256::Hash::from_slice(&self.0)
85 .expect("PaymentHash must be 32 bytes, which is always valid for sha256::Hash")
86 }
87}
88
89#[derive(Debug, Clone)]
90pub enum PaymentStatus {
91 Pending,
92 Success(Preimage),
93 Failed,
94}
95
96impl fmt::Display for PaymentStatus {
97 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
98 fmt::Debug::fmt(self, f)
99 }
100}
101
102#[derive(Debug, Clone, PartialEq, Eq, Hash)]
104pub enum Invoice {
105 Bolt11(Bolt11Invoice),
106 Bolt12(Bolt12Invoice),
107}
108
109#[derive(Debug, thiserror::Error)]
110#[error("cannot parse invoice")]
111pub struct InvoiceParseError;
112
113impl FromStr for Invoice {
114 type Err = InvoiceParseError;
115
116 fn from_str(s: &str) -> Result<Self, Self::Err> {
117 if let Ok(bolt11) = Bolt11Invoice::from_str(s) {
118 Ok(Invoice::Bolt11(bolt11))
119 } else if let Ok(bolt12) = Bolt12Invoice::from_str(s) {
120 Ok(Invoice::Bolt12(bolt12))
121 } else {
122 Err(InvoiceParseError)
123 }
124 }
125}
126
127impl From<Bolt11Invoice> for Invoice {
128 fn from(invoice: Bolt11Invoice) -> Self {
129 Invoice::Bolt11(invoice)
130 }
131}
132
133impl From<Bolt12Invoice> for Invoice {
134 fn from(invoice: Bolt12Invoice) -> Self {
135 Invoice::Bolt12(invoice)
136 }
137}
138
139impl<'a> TryFrom<&'a str> for Invoice {
140 type Error = <Invoice as FromStr>::Err;
141 fn try_from(invoice: &'a str) -> Result<Self, Self::Error> {
142 FromStr::from_str(invoice)
143 }
144}
145
146impl TryFrom<String> for Invoice {
147 type Error = <Invoice as FromStr>::Err;
148 fn try_from(invoice: String) -> Result<Self, Self::Error> {
149 FromStr::from_str(&invoice)
150 }
151}
152
153impl serde::Serialize for Invoice {
154 fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
155 s.collect_str(self)
156 }
157}
158
159impl<'de> serde::Deserialize<'de> for Invoice {
160 fn deserialize<D: serde::Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
161 struct Visitor;
162 impl<'de> serde::de::Visitor<'de> for Visitor {
163 type Value = Invoice;
164 fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
165 write!(f, "a lightning invoice")
166 }
167 fn visit_str<E: serde::de::Error>(self, v: &str) -> Result<Self::Value, E> {
168 Invoice::from_str(v).map_err(serde::de::Error::custom)
169 }
170 }
171 d.deserialize_str(Visitor)
172 }
173}
174
175
176#[derive(Debug, thiserror::Error)]
177#[error("invoice amount mismatch: invoice={invoice}, user={user}")]
178pub enum CheckAmountError {
179 #[error("invalid user amount: invoice={invoice}, user={user}")]
180 InvalidUserAmount { invoice: Amount, user: Amount },
181 #[error("offer currency is not supported: {amount:?}")]
182 UnsupportedCurrency { amount: OfferAmount },
183 #[error("user amount required")]
184 UserAmountRequired,
185}
186
187#[derive(Debug, thiserror::Error)]
188#[error("invalid invoice signature: {0}")]
189pub struct CheckSignatureError(pub String);
190
191impl Invoice {
192 pub fn into_bolt11(self) -> Option<Bolt11Invoice> {
193 match self {
194 Invoice::Bolt11(invoice) => Some(invoice),
195 Invoice::Bolt12(_) => None
196 }
197 }
198
199 pub fn payment_hash(&self) -> PaymentHash {
200 match self {
201 Invoice::Bolt11(invoice) => PaymentHash::from(*invoice.payment_hash().as_byte_array()),
202 Invoice::Bolt12(invoice) => PaymentHash::from(invoice.payment_hash()),
203 }
204 }
205
206 pub fn network(&self) -> Network {
207 match self {
208 Invoice::Bolt11(invoice) => invoice.network(),
209 Invoice::Bolt12(invoice) => match invoice.chain() {
210 ChainHash::BITCOIN => Network::Bitcoin,
211 ChainHash::TESTNET3 => Network::Testnet,
212 ChainHash::TESTNET4 => Network::Testnet4,
213 ChainHash::SIGNET => Network::Signet,
214 ChainHash::REGTEST => Network::Regtest,
215 _ => panic!("unsupported network"),
216 },
217 }
218 }
219
220 pub fn get_final_amount(&self, user_amount: Option<Amount>) -> Result<Amount, CheckAmountError> {
224 match self {
225 Invoice::Bolt11(invoice) => invoice.get_final_amount(user_amount),
226 Invoice::Bolt12(invoice) => invoice.get_final_amount(user_amount),
227 }
228 }
229
230 pub fn amount_msat(&self) -> Option<u64> {
231 match self {
232 Invoice::Bolt11(invoice) => invoice.amount_milli_satoshis(),
233 Invoice::Bolt12(invoice) => Some(invoice.amount_msats()),
234 }
235 }
236
237 pub fn check_signature(&self) -> Result<(), CheckSignatureError> {
238 match self {
239 Invoice::Bolt11(invoice) => invoice
240 .check_signature()
241 .map_err(|e| CheckSignatureError(e.to_string())),
242 Invoice::Bolt12(invoice) => invoice.check_signature(),
243 }
244 }
245}
246
247impl fmt::Display for Invoice {
248 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
249 match self {
250 Invoice::Bolt11(invoice) => write!(f, "{}", invoice.to_string()),
251 Invoice::Bolt12(invoice) => encode_to_fmt::<NoChecksum, _>(
252 f,
253 Hrp::parse("lni").unwrap(),
254 &invoice.bytes(),
255 )
256 .map_err(|e| match e {
257 EncodeError::Fmt(e) => e,
258 _ => fmt::Error {},
259 }),
260 }
261 }
262}
263
264fn get_invoice_final_amount(invoice_amount: Option<Amount>, user_amount: Option<Amount>) -> Result<Amount, CheckAmountError> {
268 match (invoice_amount, user_amount) {
269 (Some(invoice_amount), Some(user_amount)) => {
270 if user_amount >= invoice_amount && user_amount <= invoice_amount * 2 {
273 return Ok(user_amount);
274 }
275
276 return Err(CheckAmountError::InvalidUserAmount {
277 invoice: invoice_amount,
278 user: user_amount,
279 });
280 }
281 (Some(invoice_amount), None) => {
282 return Ok(invoice_amount);
283 }
284 (None, Some(user_amount)) => {
285 return Ok(user_amount);
286 }
287 (None, None) => {
288 return Err(CheckAmountError::UserAmountRequired);
289 }
290 }
291}
292
293pub trait Bolt11InvoiceExt: Borrow<Bolt11Invoice> {
295 fn get_final_amount(&self, user_amount: Option<Amount>) -> Result<Amount, CheckAmountError> {
299 let invoice_amount = self.borrow().amount_milli_satoshis()
300 .map(Amount::from_msat_ceil);
301
302 get_invoice_final_amount(invoice_amount, user_amount)
303 }
304}
305
306impl Bolt11InvoiceExt for Bolt11Invoice {}
307
308pub trait Bolt12InvoiceExt: Borrow<Bolt12Invoice> {
310 fn payment_hash(&self) -> PaymentHash { PaymentHash::from(self.borrow().payment_hash()) }
311
312 fn get_final_amount(&self, user_amount: Option<Amount>) -> Result<Amount, CheckAmountError> {
316 let invoice_amount = Amount::from_msat_ceil(self.borrow().amount_msats());
317 get_invoice_final_amount(Some(invoice_amount), user_amount)
318 }
319
320 fn check_signature(&self) -> Result<(), CheckSignatureError> {
321 let message = Message::from_digest(self.borrow().signable_hash());
322 let signature = self.borrow().signature();
323
324 if let Some(pubkey) = self.borrow().issuer_signing_pubkey() {
325 Ok(SECP.verify_schnorr(&signature, &message, &pubkey.into())
326 .map_err(|_| CheckSignatureError("invalid signature".to_string()))?)
327 } else {
328 Err(CheckSignatureError("no pubkey on offer, cannot verify signature".to_string()))
329 }
330 }
331
332 fn bytes(&self) -> Vec<u8> {
333 let mut bytes = Vec::new();
334 self.borrow().write(&mut bytes).expect("Writing into a Vec is infallible");
335 bytes
336 }
337
338 fn from_bytes(bytes: &[u8]) -> Result<Bolt12Invoice, Bolt12ParseError> {
339 Bolt12Invoice::try_from(bytes.to_vec())
340 }
341
342 fn validate_issuance(&self, offer: &Offer) -> Result<(), CheckSignatureError> {
343 if self.borrow().issuer_signing_pubkey() != offer.issuer_signing_pubkey() {
344 Err(CheckSignatureError("public keys mismatch".to_string()))
345 } else {
346 Ok(())
347 }
348 }
349
350 fn from_str(s: &str) -> Result<Bolt12Invoice, Bolt12ParseError> {
351 let dec = CheckedHrpstring::new::<NoChecksum>(&s)?;
352 if dec.hrp().to_lowercase() != BECH32_BOLT12_INVOICE_HRP {
353 return Err(Bolt12ParseError::InvalidBech32Hrp);
354 }
355
356 let data = dec.byte_iter().collect::<Vec<_>>();
357 Bolt12Invoice::try_from(data)
358 }
359}
360
361impl Bolt12InvoiceExt for Bolt12Invoice {}