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) -> sha256::Hash {
84 sha256::Hash::from_byte_array(self.0)
85 }
86}
87
88pub trait AsPaymentHash {
90 fn as_payment_hash(&self) -> PaymentHash;
93}
94
95impl AsPaymentHash for PaymentHash {
96 fn as_payment_hash(&self) -> PaymentHash { *self }
97}
98
99impl AsPaymentHash for Preimage {
100 fn as_payment_hash(&self) -> PaymentHash { self.compute_payment_hash() }
101}
102
103impl AsPaymentHash for Bolt11Invoice {
104 fn as_payment_hash(&self) -> PaymentHash { PaymentHash::from(*self.payment_hash()) }
105}
106
107impl AsPaymentHash for Bolt12Invoice {
108 fn as_payment_hash(&self) -> PaymentHash { self.payment_hash().into() }
109}
110
111impl AsPaymentHash for Invoice {
112 fn as_payment_hash(&self) -> PaymentHash {
113 match self {
114 Invoice::Bolt11(i) => AsPaymentHash::as_payment_hash(i),
115 Invoice::Bolt12(i) => AsPaymentHash::as_payment_hash(i),
116 }
117 }
118}
119
120impl<'a, T: AsPaymentHash> AsPaymentHash for &'a T {
121 fn as_payment_hash(&self) -> PaymentHash {
122 AsPaymentHash::as_payment_hash(*self)
123 }
124}
125
126#[derive(Debug, Clone)]
127pub enum PaymentStatus {
128 Pending,
129 Success(Preimage),
130 Failed,
131}
132
133impl fmt::Display for PaymentStatus {
134 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
135 fmt::Debug::fmt(self, f)
136 }
137}
138
139#[derive(Debug, Clone, PartialEq, Eq, Hash)]
141pub enum Invoice {
142 Bolt11(Bolt11Invoice),
143 Bolt12(Bolt12Invoice),
144}
145
146#[derive(Debug, thiserror::Error)]
147#[error("cannot parse invoice")]
148pub struct InvoiceParseError;
149
150impl FromStr for Invoice {
151 type Err = InvoiceParseError;
152
153 fn from_str(s: &str) -> Result<Self, Self::Err> {
154 if let Ok(bolt11) = Bolt11Invoice::from_str(s) {
155 Ok(Invoice::Bolt11(bolt11))
156 } else if let Ok(bolt12) = Bolt12Invoice::from_str(s) {
157 Ok(Invoice::Bolt12(bolt12))
158 } else {
159 Err(InvoiceParseError)
160 }
161 }
162}
163
164impl From<Bolt11Invoice> for Invoice {
165 fn from(invoice: Bolt11Invoice) -> Self {
166 Invoice::Bolt11(invoice)
167 }
168}
169
170impl From<Bolt12Invoice> for Invoice {
171 fn from(invoice: Bolt12Invoice) -> Self {
172 Invoice::Bolt12(invoice)
173 }
174}
175
176impl<'a> TryFrom<&'a str> for Invoice {
177 type Error = <Invoice as FromStr>::Err;
178 fn try_from(invoice: &'a str) -> Result<Self, Self::Error> {
179 FromStr::from_str(invoice)
180 }
181}
182
183impl TryFrom<String> for Invoice {
184 type Error = <Invoice as FromStr>::Err;
185 fn try_from(invoice: String) -> Result<Self, Self::Error> {
186 FromStr::from_str(&invoice)
187 }
188}
189
190impl serde::Serialize for Invoice {
191 fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
192 s.collect_str(self)
193 }
194}
195
196impl<'de> serde::Deserialize<'de> for Invoice {
197 fn deserialize<D: serde::Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
198 struct Visitor;
199 impl<'de> serde::de::Visitor<'de> for Visitor {
200 type Value = Invoice;
201 fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
202 write!(f, "a lightning invoice")
203 }
204 fn visit_str<E: serde::de::Error>(self, v: &str) -> Result<Self::Value, E> {
205 Invoice::from_str(v).map_err(serde::de::Error::custom)
206 }
207 }
208 d.deserialize_str(Visitor)
209 }
210}
211
212
213#[derive(Debug, thiserror::Error)]
214#[error("invoice amount mismatch: invoice={invoice}, user={user}")]
215pub enum CheckAmountError {
216 #[error("invalid user amount: invoice={invoice}, user={user}")]
217 InvalidUserAmount { invoice: Amount, user: Amount },
218 #[error("offer currency is not supported: {amount:?}")]
219 UnsupportedCurrency { amount: OfferAmount },
220 #[error("user amount required")]
221 UserAmountRequired,
222}
223
224#[derive(Debug, thiserror::Error)]
225#[error("invalid invoice signature: {0}")]
226pub struct CheckSignatureError(pub String);
227
228impl Invoice {
229 pub fn into_bolt11(self) -> Option<Bolt11Invoice> {
230 match self {
231 Invoice::Bolt11(invoice) => Some(invoice),
232 Invoice::Bolt12(_) => None
233 }
234 }
235
236 pub fn payment_hash(&self) -> PaymentHash {
237 match self {
238 Invoice::Bolt11(invoice) => PaymentHash::from(*invoice.payment_hash().as_byte_array()),
239 Invoice::Bolt12(invoice) => PaymentHash::from(invoice.payment_hash()),
240 }
241 }
242
243 pub fn network(&self) -> Network {
244 match self {
245 Invoice::Bolt11(invoice) => invoice.network(),
246 Invoice::Bolt12(invoice) => match invoice.chain() {
247 ChainHash::BITCOIN => Network::Bitcoin,
248 ChainHash::TESTNET3 => Network::Testnet,
249 ChainHash::TESTNET4 => Network::Testnet4,
250 ChainHash::SIGNET => Network::Signet,
251 ChainHash::REGTEST => Network::Regtest,
252 _ => panic!("unsupported network"),
253 },
254 }
255 }
256
257 pub fn get_final_amount(
261 &self,
262 user_amount: Option<Amount>,
263 ) -> Result<Amount, CheckAmountError> {
264 match self {
265 Invoice::Bolt11(invoice) => invoice.get_final_amount(user_amount),
266 Invoice::Bolt12(invoice) => invoice.get_final_amount(user_amount),
267 }
268 }
269
270 pub fn amount_msat(&self) -> Option<u64> {
271 match self {
272 Invoice::Bolt11(invoice) => invoice.amount_milli_satoshis(),
273 Invoice::Bolt12(invoice) => Some(invoice.amount_msats()),
274 }
275 }
276
277 pub fn check_signature(&self) -> Result<(), CheckSignatureError> {
278 match self {
279 Invoice::Bolt11(invoice) => invoice
280 .check_signature()
281 .map_err(|e| CheckSignatureError(e.to_string())),
282 Invoice::Bolt12(invoice) => invoice.check_signature(),
283 }
284 }
285}
286
287impl fmt::Display for Invoice {
288 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
289 match self {
290 Invoice::Bolt11(invoice) => write!(f, "{}", invoice.to_string()),
291 Invoice::Bolt12(invoice) => encode_to_fmt::<NoChecksum, _>(
292 f,
293 Hrp::parse("lni").unwrap(),
294 &invoice.bytes(),
295 )
296 .map_err(|e| match e {
297 EncodeError::Fmt(e) => e,
298 _ => fmt::Error {},
299 }),
300 }
301 }
302}
303
304fn get_invoice_final_amount(invoice_amount: Option<Amount>, user_amount: Option<Amount>) -> Result<Amount, CheckAmountError> {
308 match (invoice_amount, user_amount) {
309 (Some(invoice_amount), Some(user_amount)) => {
310 if user_amount >= invoice_amount && user_amount <= invoice_amount * 2 {
313 return Ok(user_amount);
314 }
315
316 return Err(CheckAmountError::InvalidUserAmount {
317 invoice: invoice_amount,
318 user: user_amount,
319 });
320 }
321 (Some(invoice_amount), None) => {
322 return Ok(invoice_amount);
323 }
324 (None, Some(user_amount)) => {
325 return Ok(user_amount);
326 }
327 (None, None) => {
328 return Err(CheckAmountError::UserAmountRequired);
329 }
330 }
331}
332
333pub trait Bolt11InvoiceExt: Borrow<Bolt11Invoice> {
335 fn get_final_amount(&self, user_amount: Option<Amount>) -> Result<Amount, CheckAmountError> {
339 let invoice_amount = self.borrow().amount_milli_satoshis()
340 .map(Amount::from_msat_ceil);
341
342 get_invoice_final_amount(invoice_amount, user_amount)
343 }
344}
345
346impl Bolt11InvoiceExt for Bolt11Invoice {}
347
348pub trait Bolt12InvoiceExt: Borrow<Bolt12Invoice> {
350 fn payment_hash(&self) -> PaymentHash { PaymentHash::from(self.borrow().payment_hash()) }
351
352 fn get_final_amount(&self, user_amount: Option<Amount>) -> Result<Amount, CheckAmountError> {
356 let invoice_amount = Amount::from_msat_ceil(self.borrow().amount_msats());
357 get_invoice_final_amount(Some(invoice_amount), user_amount)
358 }
359
360 fn check_signature(&self) -> Result<(), CheckSignatureError> {
361 let message = Message::from_digest(self.borrow().signable_hash());
362 let signature = self.borrow().signature();
363
364 if let Some(pubkey) = self.borrow().issuer_signing_pubkey() {
365 Ok(SECP.verify_schnorr(&signature, &message, &pubkey.into())
366 .map_err(|_| CheckSignatureError("invalid signature".to_string()))?)
367 } else {
368 Err(CheckSignatureError("no pubkey on offer, cannot verify signature".to_string()))
369 }
370 }
371
372 fn bytes(&self) -> Vec<u8> {
373 let mut bytes = Vec::new();
374 self.borrow().write(&mut bytes).expect("Writing into a Vec is infallible");
375 bytes
376 }
377
378 fn from_bytes(bytes: &[u8]) -> Result<Bolt12Invoice, Bolt12ParseError> {
379 Bolt12Invoice::try_from(bytes.to_vec())
380 }
381
382 fn validate_issuance(&self, offer: &Offer) -> Result<(), CheckSignatureError> {
383 if self.borrow().issuer_signing_pubkey() != offer.issuer_signing_pubkey() {
384 Err(CheckSignatureError("public keys mismatch".to_string()))
385 } else {
386 Ok(())
387 }
388 }
389
390 fn from_str(s: &str) -> Result<Bolt12Invoice, Bolt12ParseError> {
391 let dec = CheckedHrpstring::new::<NoChecksum>(&s)?;
392 if dec.hrp().to_lowercase() != BECH32_BOLT12_INVOICE_HRP {
393 return Err(Bolt12ParseError::InvalidBech32Hrp);
394 }
395
396 let data = dec.byte_iter().collect::<Vec<_>>();
397 Bolt12Invoice::try_from(data)
398 }
399}
400
401impl Bolt12InvoiceExt for Bolt12Invoice {}