1use bitcoin::constants::ChainHash;
2use bitcoin::key::Keypair;
3use bitcoin::{secp256k1, Network};
4pub use lightning::offers::invoice::Bolt12Invoice;
5pub use lightning_invoice::Bolt11Invoice;
6pub use lightning::offers::offer::{Amount as OfferAmount, Offer};
7
8use std::fmt;
9use std::borrow::Borrow;
10use std::io::Write as _;
11use std::str::FromStr;
12
13use bitcoin::Amount;
14use bitcoin::bech32::{encode_to_fmt, EncodeError, Hrp, NoChecksum, primitives::decode::CheckedHrpstring};
15use bitcoin::hashes::{sha256, Hash};
16use bitcoin::secp256k1::{rand, schnorr, Message, PublicKey};
17use bitcoin::taproot::TaprootSpendInfo;
18use lightning::offers::parse::Bolt12ParseError;
19use lightning::util::ser::Writeable;
20
21use bitcoin_ext::{BlockDelta, BlockHeight, P2TR_DUST};
22
23use crate::{musig, scripts, Vtxo, VtxoId, SECP};
24
25const BECH32_BOLT12_INVOICE_HRP: &str = "lni";
26
27pub const HTLC_MIN_FEE: Amount = P2TR_DUST;
29
30
31#[derive(Clone, Copy, PartialEq, Eq, Hash)]
33pub struct Preimage([u8; 32]);
34impl_byte_newtype!(Preimage, 32);
35
36impl Preimage {
37 pub fn random() -> Preimage {
39 Preimage(rand::random())
40 }
41
42 pub fn compute_payment_hash(&self) -> PaymentHash {
44 sha256::Hash::hash(self.as_ref()).into()
45 }
46}
47
48#[derive(Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
50pub struct PaymentHash([u8; 32]);
51impl_byte_newtype!(PaymentHash, 32);
52
53impl From<sha256::Hash> for PaymentHash {
54 fn from(hash: sha256::Hash) -> Self {
55 PaymentHash(hash.to_byte_array())
56 }
57}
58
59impl From<Preimage> for PaymentHash {
60 fn from(preimage: Preimage) -> Self {
61 preimage.compute_payment_hash()
62 }
63}
64
65impl From<lightning::types::payment::PaymentHash> for PaymentHash {
66 fn from(hash: lightning::types::payment::PaymentHash) -> Self {
67 PaymentHash(hash.0)
68 }
69}
70
71impl<'a> From<&'a Bolt11Invoice> for PaymentHash {
72 fn from(i: &'a Bolt11Invoice) -> Self {
73 (*i.payment_hash()).into()
74 }
75}
76
77impl From<Bolt11Invoice> for PaymentHash {
78 fn from(i: Bolt11Invoice) -> Self {
79 (&i).into()
80 }
81}
82
83impl PaymentHash {
84 pub fn to_sha256_hash(&self) -> bitcoin::hashes::sha256::Hash {
86 bitcoin::hashes::sha256::Hash::from_slice(&self.0)
87 .expect("PaymentHash must be 32 bytes, which is always valid for sha256::Hash")
88 }
89}
90
91#[derive(Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
92pub struct LightningReceiveChallenge(PaymentHash);
93
94impl LightningReceiveChallenge {
95 const CHALLENGE_MESSAGE_PREFIX: &'static [u8; 32] = b"Lightning receive VTXO challenge";
96
97 pub fn new(value: PaymentHash) -> Self {
98 Self(value)
99 }
100
101 fn as_signable_message(&self, vtxo_id: VtxoId) -> Message {
105 let mut engine = sha256::Hash::engine();
106 engine.write_all(Self::CHALLENGE_MESSAGE_PREFIX).unwrap();
107 engine.write_all(&self.0.to_byte_array()).unwrap();
108 engine.write_all(&vtxo_id.to_bytes()).unwrap();
109
110 let hash = sha256::Hash::from_engine(engine).to_byte_array();
111 Message::from_digest(hash)
112 }
113
114 pub fn sign_with(
115 &self,
116 vtxo_id: VtxoId,
117 vtxo_keypair: Keypair,
118 ) -> schnorr::Signature {
119 SECP.sign_schnorr(
120 &LightningReceiveChallenge::as_signable_message(self, vtxo_id),
121 &vtxo_keypair,
122 )
123 }
124
125 pub fn verify_input_vtxo_sig(
126 &self,
127 vtxo: &Vtxo,
128 sig: &schnorr::Signature,
129 ) -> Result<(), secp256k1::Error> {
130 SECP.verify_schnorr(
131 sig,
132 &LightningReceiveChallenge::as_signable_message(self, vtxo.id()),
133 &vtxo.user_pubkey().x_only_public_key().0,
134 )
135 }
136}
137
138pub fn server_htlc_send_taproot(
157 payment_hash: PaymentHash,
158 server_pubkey: PublicKey,
159 user_pubkey: PublicKey,
160 exit_delta: BlockDelta,
161 htlc_expiry: BlockHeight,
162) -> TaprootSpendInfo {
163 let server_branch = scripts::hash_delay_sign(
164 payment_hash.to_sha256_hash(), exit_delta, server_pubkey.x_only_public_key().0,
165 );
166 let user_branch = scripts::delay_timelock_sign(
167 2 * exit_delta, htlc_expiry, user_pubkey.x_only_public_key().0,
168 );
169
170 let combined_pk = musig::combine_keys([user_pubkey, server_pubkey]);
171 bitcoin::taproot::TaprootBuilder::new()
172 .add_leaf(1, server_branch).unwrap()
173 .add_leaf(1, user_branch).unwrap()
174 .finalize(&SECP, combined_pk).unwrap()
175}
176
177pub fn server_htlc_receive_taproot(
195 payment_hash: PaymentHash,
196 server_pubkey: PublicKey,
197 user_pubkey: PublicKey,
198 exit_delta: BlockDelta,
199 htlc_expiry_delta: BlockDelta,
200 htlc_expiry: BlockHeight,
201) -> TaprootSpendInfo {
202 let server_branch =
203 scripts::delay_timelock_sign(exit_delta, htlc_expiry, server_pubkey.x_only_public_key().0);
204 let user_branch = scripts::hash_delay_sign(
205 payment_hash.to_sha256_hash(),
206 exit_delta + htlc_expiry_delta,
207 user_pubkey.x_only_public_key().0,
208 );
209
210 let combined_pk = musig::combine_keys([user_pubkey, server_pubkey]);
211 bitcoin::taproot::TaprootBuilder::new()
212 .add_leaf(1, server_branch).unwrap()
213 .add_leaf(1, user_branch).unwrap()
214 .finalize(&SECP, combined_pk).unwrap()
215}
216
217
218#[derive(Debug, Clone)]
219pub enum PaymentStatus {
220 Pending,
221 Complete,
222 Failed,
223}
224
225impl fmt::Display for PaymentStatus {
226 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
227 fmt::Debug::fmt(self, f)
228 }
229}
230
231#[derive(Debug, Clone, PartialEq, Eq, Hash)]
233pub enum Invoice {
234 Bolt11(Bolt11Invoice),
235 Bolt12(Bolt12Invoice),
236}
237
238#[derive(Debug, thiserror::Error)]
239#[error("cannot parse invoice")]
240pub struct InvoiceParseError;
241
242impl FromStr for Invoice {
243 type Err = InvoiceParseError;
244
245 fn from_str(s: &str) -> Result<Self, Self::Err> {
246 if let Ok(bolt11) = Bolt11Invoice::from_str(s) {
247 Ok(Invoice::Bolt11(bolt11))
248 } else if let Ok(bolt12) = Bolt12Invoice::from_str(s) {
249 Ok(Invoice::Bolt12(bolt12))
250 } else {
251 Err(InvoiceParseError)
252 }
253 }
254}
255
256impl From<Bolt11Invoice> for Invoice {
257 fn from(invoice: Bolt11Invoice) -> Self {
258 Invoice::Bolt11(invoice)
259 }
260}
261
262impl From<Bolt12Invoice> for Invoice {
263 fn from(invoice: Bolt12Invoice) -> Self {
264 Invoice::Bolt12(invoice)
265 }
266}
267
268impl<'a> TryFrom<&'a str> for Invoice {
269 type Error = <Invoice as FromStr>::Err;
270 fn try_from(invoice: &'a str) -> Result<Self, Self::Error> {
271 FromStr::from_str(invoice)
272 }
273}
274
275impl TryFrom<String> for Invoice {
276 type Error = <Invoice as FromStr>::Err;
277 fn try_from(invoice: String) -> Result<Self, Self::Error> {
278 FromStr::from_str(&invoice)
279 }
280}
281
282impl serde::Serialize for Invoice {
283 fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
284 s.collect_str(self)
285 }
286}
287
288impl<'de> serde::Deserialize<'de> for Invoice {
289 fn deserialize<D: serde::Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
290 struct Visitor;
291 impl<'de> serde::de::Visitor<'de> for Visitor {
292 type Value = Invoice;
293 fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
294 write!(f, "a lightning invoice")
295 }
296 fn visit_str<E: serde::de::Error>(self, v: &str) -> Result<Self::Value, E> {
297 Invoice::from_str(v).map_err(serde::de::Error::custom)
298 }
299 }
300 d.deserialize_str(Visitor)
301 }
302}
303
304#[derive(Debug, thiserror::Error)]
305#[error("invalid invoice signature: {0}")]
306pub struct CheckSignatureError(pub String);
307
308impl Invoice {
309 pub fn into_bolt11(self) -> Option<Bolt11Invoice> {
310 match self {
311 Invoice::Bolt11(invoice) => Some(invoice),
312 Invoice::Bolt12(_) => None
313 }
314 }
315
316 pub fn payment_hash(&self) -> PaymentHash {
317 match self {
318 Invoice::Bolt11(invoice) => PaymentHash::from(*invoice.payment_hash().as_byte_array()),
319 Invoice::Bolt12(invoice) => PaymentHash::from(invoice.payment_hash()),
320 }
321 }
322
323 pub fn network(&self) -> Network {
324 match self {
325 Invoice::Bolt11(invoice) => invoice.network(),
326 Invoice::Bolt12(invoice) => match invoice.chain() {
327 ChainHash::BITCOIN => Network::Bitcoin,
328 ChainHash::TESTNET3 => Network::Testnet,
329 ChainHash::TESTNET4 => Network::Testnet4,
330 ChainHash::SIGNET => Network::Signet,
331 ChainHash::REGTEST => Network::Regtest,
332 _ => panic!("unsupported network"),
333 },
334 }
335 }
336
337 pub fn amount_msat(&self) -> Option<u64> {
338 match self {
339 Invoice::Bolt11(invoice) => invoice.amount_milli_satoshis(),
340 Invoice::Bolt12(invoice) => Some(invoice.amount_msats()),
341 }
342 }
343
344 pub fn check_signature(&self) -> Result<(), CheckSignatureError> {
345 match self {
346 Invoice::Bolt11(invoice) => invoice
347 .check_signature()
348 .map_err(|e| CheckSignatureError(e.to_string())),
349 Invoice::Bolt12(invoice) => invoice.check_signature(),
350 }
351 }
352}
353
354impl fmt::Display for Invoice {
355 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
356 match self {
357 Invoice::Bolt11(invoice) => write!(f, "{}", invoice.to_string()),
358 Invoice::Bolt12(invoice) => encode_to_fmt::<NoChecksum, _>(
359 f,
360 Hrp::parse("lni").unwrap(),
361 &invoice.bytes(),
362 )
363 .map_err(|e| match e {
364 EncodeError::Fmt(e) => e,
365 _ => fmt::Error {},
366 }),
367 }
368 }
369}
370
371pub trait Bolt12InvoiceExt: Borrow<Bolt12Invoice> {
372 fn payment_hash(&self) -> PaymentHash { PaymentHash::from(self.borrow().payment_hash()) }
373
374 fn check_signature(&self) -> Result<(), CheckSignatureError> {
375 let message = Message::from_digest(self.borrow().signable_hash());
376 let signature = self.borrow().signature();
377
378 if let Some(pubkey) = self.borrow().issuer_signing_pubkey() {
379 Ok(SECP.verify_schnorr(&signature, &message, &pubkey.into())
380 .map_err(|_| CheckSignatureError("invalid signature".to_string()))?)
381 } else {
382 Err(CheckSignatureError("no pubkey on offer, cannot verify signature".to_string()))
383 }
384 }
385
386 fn bytes(&self) -> Vec<u8> {
387 let mut bytes = Vec::new();
388 self.borrow().write(&mut bytes).expect("Writing into a Vec is infallible");
389 bytes
390 }
391
392 fn from_bytes(bytes: &[u8]) -> Result<Bolt12Invoice, Bolt12ParseError> {
393 Bolt12Invoice::try_from(bytes.to_vec())
394 }
395
396 fn validate_issuance(&self, offer: Offer) -> Result<(), CheckSignatureError> {
397 if self.borrow().issuer_signing_pubkey() != offer.issuer_signing_pubkey() {
398 Err(CheckSignatureError("public keys mismatch".to_string()))
399 } else {
400 Ok(())
401 }
402 }
403
404 fn from_str(s: &str) -> Result<Bolt12Invoice, Bolt12ParseError> {
405 let dec = CheckedHrpstring::new::<NoChecksum>(&s)?;
406 if dec.hrp().to_lowercase() != BECH32_BOLT12_INVOICE_HRP {
407 return Err(Bolt12ParseError::InvalidBech32Hrp);
408 }
409
410 let data = dec.byte_iter().collect::<Vec<_>>();
411 Bolt12Invoice::try_from(data)
412 }
413}
414
415impl Bolt12InvoiceExt for Bolt12Invoice {}