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, PublicKey};
14use bitcoin::taproot::TaprootSpendInfo;
15use lightning::offers::parse::Bolt12ParseError;
16use lightning::util::ser::Writeable;
17
18use bitcoin_ext::{AmountExt, BlockDelta, BlockHeight, P2TR_DUST};
19
20use crate::{musig, scripts, SECP};
21
22const BECH32_BOLT12_INVOICE_HRP: &str = "lni";
23
24pub const HTLC_MIN_FEE: Amount = P2TR_DUST;
26
27
28#[derive(Clone, Copy, PartialEq, Eq, Hash)]
30pub struct Preimage([u8; 32]);
31impl_byte_newtype!(Preimage, 32);
32
33impl Preimage {
34 pub fn random() -> Preimage {
36 Preimage(rand::random())
37 }
38
39 pub fn compute_payment_hash(&self) -> PaymentHash {
41 sha256::Hash::hash(self.as_ref()).into()
42 }
43}
44
45#[derive(Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
47pub struct PaymentHash([u8; 32]);
48impl_byte_newtype!(PaymentHash, 32);
49
50impl From<sha256::Hash> for PaymentHash {
51 fn from(hash: sha256::Hash) -> Self {
52 PaymentHash(hash.to_byte_array())
53 }
54}
55
56impl From<Preimage> for PaymentHash {
57 fn from(preimage: Preimage) -> Self {
58 preimage.compute_payment_hash()
59 }
60}
61
62impl From<lightning::types::payment::PaymentHash> for PaymentHash {
63 fn from(hash: lightning::types::payment::PaymentHash) -> Self {
64 PaymentHash(hash.0)
65 }
66}
67
68impl<'a> From<&'a Bolt11Invoice> for PaymentHash {
69 fn from(i: &'a Bolt11Invoice) -> Self {
70 (*i.payment_hash()).into()
71 }
72}
73
74impl From<Bolt11Invoice> for PaymentHash {
75 fn from(i: Bolt11Invoice) -> Self {
76 (&i).into()
77 }
78}
79
80impl PaymentHash {
81 pub fn to_sha256_hash(&self) -> bitcoin::hashes::sha256::Hash {
83 bitcoin::hashes::sha256::Hash::from_slice(&self.0)
84 .expect("PaymentHash must be 32 bytes, which is always valid for sha256::Hash")
85 }
86}
87
88pub fn server_htlc_send_taproot(
107 payment_hash: PaymentHash,
108 server_pubkey: PublicKey,
109 user_pubkey: PublicKey,
110 exit_delta: BlockDelta,
111 htlc_expiry: BlockHeight,
112) -> TaprootSpendInfo {
113 let server_branch = scripts::hash_delay_sign(
114 payment_hash.to_sha256_hash(), exit_delta, server_pubkey.x_only_public_key().0,
115 );
116 let user_branch = scripts::delay_timelock_sign(
117 2 * exit_delta, htlc_expiry, user_pubkey.x_only_public_key().0,
118 );
119
120 let combined_pk = musig::combine_keys([user_pubkey, server_pubkey]);
121 bitcoin::taproot::TaprootBuilder::new()
122 .add_leaf(1, server_branch).unwrap()
123 .add_leaf(1, user_branch).unwrap()
124 .finalize(&SECP, combined_pk).unwrap()
125}
126
127pub fn server_htlc_receive_taproot(
145 payment_hash: PaymentHash,
146 server_pubkey: PublicKey,
147 user_pubkey: PublicKey,
148 exit_delta: BlockDelta,
149 htlc_expiry_delta: BlockDelta,
150 htlc_expiry: BlockHeight,
151) -> TaprootSpendInfo {
152 let server_branch =
153 scripts::delay_timelock_sign(exit_delta, htlc_expiry, server_pubkey.x_only_public_key().0);
154 let user_branch = scripts::hash_delay_sign(
155 payment_hash.to_sha256_hash(),
156 exit_delta + htlc_expiry_delta,
157 user_pubkey.x_only_public_key().0,
158 );
159
160 let combined_pk = musig::combine_keys([user_pubkey, server_pubkey]);
161 bitcoin::taproot::TaprootBuilder::new()
162 .add_leaf(1, server_branch).unwrap()
163 .add_leaf(1, user_branch).unwrap()
164 .finalize(&SECP, combined_pk).unwrap()
165}
166
167
168#[derive(Debug, Clone)]
169pub enum PaymentStatus {
170 Pending,
171 Complete,
172 Failed,
173}
174
175impl fmt::Display for PaymentStatus {
176 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
177 fmt::Debug::fmt(self, f)
178 }
179}
180
181#[derive(Debug, Clone, PartialEq, Eq, Hash)]
183pub enum Invoice {
184 Bolt11(Bolt11Invoice),
185 Bolt12(Bolt12Invoice),
186}
187
188#[derive(Debug, thiserror::Error)]
189#[error("cannot parse invoice")]
190pub struct InvoiceParseError;
191
192impl FromStr for Invoice {
193 type Err = InvoiceParseError;
194
195 fn from_str(s: &str) -> Result<Self, Self::Err> {
196 if let Ok(bolt11) = Bolt11Invoice::from_str(s) {
197 Ok(Invoice::Bolt11(bolt11))
198 } else if let Ok(bolt12) = Bolt12Invoice::from_str(s) {
199 Ok(Invoice::Bolt12(bolt12))
200 } else {
201 Err(InvoiceParseError)
202 }
203 }
204}
205
206impl From<Bolt11Invoice> for Invoice {
207 fn from(invoice: Bolt11Invoice) -> Self {
208 Invoice::Bolt11(invoice)
209 }
210}
211
212impl From<Bolt12Invoice> for Invoice {
213 fn from(invoice: Bolt12Invoice) -> Self {
214 Invoice::Bolt12(invoice)
215 }
216}
217
218impl<'a> TryFrom<&'a str> for Invoice {
219 type Error = <Invoice as FromStr>::Err;
220 fn try_from(invoice: &'a str) -> Result<Self, Self::Error> {
221 FromStr::from_str(invoice)
222 }
223}
224
225impl TryFrom<String> for Invoice {
226 type Error = <Invoice as FromStr>::Err;
227 fn try_from(invoice: String) -> Result<Self, Self::Error> {
228 FromStr::from_str(&invoice)
229 }
230}
231
232impl serde::Serialize for Invoice {
233 fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
234 s.collect_str(self)
235 }
236}
237
238impl<'de> serde::Deserialize<'de> for Invoice {
239 fn deserialize<D: serde::Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
240 struct Visitor;
241 impl<'de> serde::de::Visitor<'de> for Visitor {
242 type Value = Invoice;
243 fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
244 write!(f, "a lightning invoice")
245 }
246 fn visit_str<E: serde::de::Error>(self, v: &str) -> Result<Self::Value, E> {
247 Invoice::from_str(v).map_err(serde::de::Error::custom)
248 }
249 }
250 d.deserialize_str(Visitor)
251 }
252}
253
254
255#[derive(Debug, thiserror::Error)]
256#[error("invoice amount mismatch: invoice={invoice}, user={user}")]
257pub enum CheckAmountError {
258 #[error("invalid user amount: invoice={invoice}, user={user}")]
259 InvalidUserAmount { invoice: Amount, user: Amount },
260 #[error("offer currency is not supported: {amount:?}")]
261 UnsupportedCurrency { amount: OfferAmount },
262 #[error("user amount required")]
263 UserAmountRequired,
264}
265
266#[derive(Debug, thiserror::Error)]
267#[error("invalid invoice signature: {0}")]
268pub struct CheckSignatureError(pub String);
269
270impl Invoice {
271 pub fn into_bolt11(self) -> Option<Bolt11Invoice> {
272 match self {
273 Invoice::Bolt11(invoice) => Some(invoice),
274 Invoice::Bolt12(_) => None
275 }
276 }
277
278 pub fn payment_hash(&self) -> PaymentHash {
279 match self {
280 Invoice::Bolt11(invoice) => PaymentHash::from(*invoice.payment_hash().as_byte_array()),
281 Invoice::Bolt12(invoice) => PaymentHash::from(invoice.payment_hash()),
282 }
283 }
284
285 pub fn network(&self) -> Network {
286 match self {
287 Invoice::Bolt11(invoice) => invoice.network(),
288 Invoice::Bolt12(invoice) => match invoice.chain() {
289 ChainHash::BITCOIN => Network::Bitcoin,
290 ChainHash::TESTNET3 => Network::Testnet,
291 ChainHash::TESTNET4 => Network::Testnet4,
292 ChainHash::SIGNET => Network::Signet,
293 ChainHash::REGTEST => Network::Regtest,
294 _ => panic!("unsupported network"),
295 },
296 }
297 }
298
299 pub fn get_final_amount(&self, user_amount: Option<Amount>) -> Result<Amount, CheckAmountError> {
301 match self {
302 Invoice::Bolt11(invoice) => invoice.get_final_amount(user_amount),
303 Invoice::Bolt12(invoice) => invoice.get_final_amount(user_amount),
304 }
305 }
306
307 pub fn amount_msat(&self) -> Option<u64> {
308 match self {
309 Invoice::Bolt11(invoice) => invoice.amount_milli_satoshis(),
310 Invoice::Bolt12(invoice) => Some(invoice.amount_msats()),
311 }
312 }
313
314 pub fn check_signature(&self) -> Result<(), CheckSignatureError> {
315 match self {
316 Invoice::Bolt11(invoice) => invoice
317 .check_signature()
318 .map_err(|e| CheckSignatureError(e.to_string())),
319 Invoice::Bolt12(invoice) => invoice.check_signature(),
320 }
321 }
322}
323
324impl fmt::Display for Invoice {
325 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
326 match self {
327 Invoice::Bolt11(invoice) => write!(f, "{}", invoice.to_string()),
328 Invoice::Bolt12(invoice) => encode_to_fmt::<NoChecksum, _>(
329 f,
330 Hrp::parse("lni").unwrap(),
331 &invoice.bytes(),
332 )
333 .map_err(|e| match e {
334 EncodeError::Fmt(e) => e,
335 _ => fmt::Error {},
336 }),
337 }
338 }
339}
340
341fn get_invoice_final_amount(invoice_amount: Option<Amount>, user_amount: Option<Amount>) -> Result<Amount, CheckAmountError> {
345 match (invoice_amount, user_amount) {
346 (Some(invoice_amount), Some(user_amount)) => {
347 if user_amount >= invoice_amount && user_amount <= invoice_amount * 2 {
350 return Ok(user_amount);
351 }
352
353 return Err(CheckAmountError::InvalidUserAmount {
354 invoice: invoice_amount,
355 user: user_amount,
356 });
357 }
358 (Some(invoice_amount), None) => {
359 return Ok(invoice_amount);
360 }
361 (None, Some(user_amount)) => {
362 return Ok(user_amount);
363 }
364 (None, None) => {
365 return Err(CheckAmountError::UserAmountRequired);
366 }
367 }
368}
369pub trait Bolt11InvoiceExt: Borrow<Bolt11Invoice> {
370 fn get_final_amount(&self, user_amount: Option<Amount>) -> Result<Amount, CheckAmountError> {
372 let invoice_amount = self.borrow().amount_milli_satoshis()
373 .map(Amount::from_msat_ceil);
374
375 get_invoice_final_amount(invoice_amount, user_amount)
376 }
377}
378
379impl Bolt11InvoiceExt for Bolt11Invoice {}
380
381pub trait Bolt12InvoiceExt: Borrow<Bolt12Invoice> {
382 fn payment_hash(&self) -> PaymentHash { PaymentHash::from(self.borrow().payment_hash()) }
383
384 fn get_final_amount(&self, user_amount: Option<Amount>) -> Result<Amount, CheckAmountError> {
386 let invoice_amount = Amount::from_msat_ceil(self.borrow().amount_msats());
387 get_invoice_final_amount(Some(invoice_amount), user_amount)
388 }
389
390 fn check_signature(&self) -> Result<(), CheckSignatureError> {
391 let message = Message::from_digest(self.borrow().signable_hash());
392 let signature = self.borrow().signature();
393
394 if let Some(pubkey) = self.borrow().issuer_signing_pubkey() {
395 Ok(SECP.verify_schnorr(&signature, &message, &pubkey.into())
396 .map_err(|_| CheckSignatureError("invalid signature".to_string()))?)
397 } else {
398 Err(CheckSignatureError("no pubkey on offer, cannot verify signature".to_string()))
399 }
400 }
401
402 fn bytes(&self) -> Vec<u8> {
403 let mut bytes = Vec::new();
404 self.borrow().write(&mut bytes).expect("Writing into a Vec is infallible");
405 bytes
406 }
407
408 fn from_bytes(bytes: &[u8]) -> Result<Bolt12Invoice, Bolt12ParseError> {
409 Bolt12Invoice::try_from(bytes.to_vec())
410 }
411
412 fn validate_issuance(&self, offer: Offer) -> Result<(), CheckSignatureError> {
413 if self.borrow().issuer_signing_pubkey() != offer.issuer_signing_pubkey() {
414 Err(CheckSignatureError("public keys mismatch".to_string()))
415 } else {
416 Ok(())
417 }
418 }
419
420 fn from_str(s: &str) -> Result<Bolt12Invoice, Bolt12ParseError> {
421 let dec = CheckedHrpstring::new::<NoChecksum>(&s)?;
422 if dec.hrp().to_lowercase() != BECH32_BOLT12_INVOICE_HRP {
423 return Err(Bolt12ParseError::InvalidBech32Hrp);
424 }
425
426 let data = dec.byte_iter().collect::<Vec<_>>();
427 Bolt12Invoice::try_from(data)
428 }
429}
430
431impl Bolt12InvoiceExt for Bolt12Invoice {}