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