ark/
lightning.rs

1use bitcoin::constants::ChainHash;
2use bitcoin::Network;
3pub use lightning::offers::offer::{Offer, Amount as OfferAmount};
4pub use lightning::offers::invoice::Bolt12Invoice;
5
6use std::fmt;
7use std::borrow::Borrow;
8use std::str::FromStr;
9
10use bitcoin::Amount;
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;
17use lightning_invoice::Bolt11Invoice;
18
19use bitcoin_ext::{BlockDelta, BlockHeight, P2TR_DUST};
20
21use crate::{musig, scripts, SECP};
22
23const BECH32_BOLT12_INVOICE_HRP: &str = "lni";
24
25/// The minimum fee we consider for an HTLC transaction.
26pub const HTLC_MIN_FEE: Amount = P2TR_DUST;
27
28
29/// A 32-byte secret preimage used for HTLC-based payments.
30#[derive(Clone, Copy, PartialEq, Eq, Hash)]
31pub struct Preimage([u8; 32]);
32impl_byte_newtype!(Preimage, 32);
33
34impl Preimage {
35	/// Generate a new random preimage.
36	pub fn random() -> Preimage {
37		Preimage(rand::random())
38	}
39
40	/// Hashes the preimage into the payment hash
41	pub fn compute_payment_hash(&self) -> PaymentHash {
42		sha256::Hash::hash(self.as_ref()).into()
43	}
44}
45
46/// The hash of a [Preimage], used to identify HTLC-based payments.
47#[derive(Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
48pub struct PaymentHash([u8; 32]);
49impl_byte_newtype!(PaymentHash, 32);
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	/// Converts this PaymentHash into a `bitcoin::hashes::sha256::Hash`.
83	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/// Construct taproot spending information for a VTXO that enables outgoing
90/// Lightning payments. This relates to the [crate::VtxoPolicy::ServerHtlcSend]
91/// policy.
92///
93/// This will build a taproot with 3 clauses:
94/// 1. The keyspend path allows Alice and Server to collaborate to spend
95/// the HTLC. The Server can use this path to revoke the HTLC if payment
96/// failed
97///
98/// 2. One leaf of the tree allows Server to spend the HTLC after the
99/// expiry, if it knows the preimage. Server can use this path if Alice
100/// tries to spend using 3rd path.
101///
102/// 3. The other leaf allows Alice to spend the HTLC after its expiry
103/// and with a delay. Alice must use this path if the server fails to
104/// provide the preimage and refuse to revoke the HTLC. It will either
105/// force the Server to reveal the preimage (by spending using 2nd path)
106/// or give Alice her money back.
107pub fn server_htlc_send_taproot(
108	payment_hash: PaymentHash,
109	server_pubkey: PublicKey,
110	user_pubkey: PublicKey,
111	exit_delta: BlockDelta,
112	htlc_expiry: BlockHeight,
113) -> TaprootSpendInfo {
114	let server_branch = scripts::hash_delay_sign(
115		payment_hash.to_sha256_hash(), exit_delta, server_pubkey.x_only_public_key().0,
116	);
117	let user_branch = scripts::delay_timelock_sign(
118		2 * exit_delta, htlc_expiry, user_pubkey.x_only_public_key().0,
119	);
120
121	let combined_pk = musig::combine_keys([user_pubkey, server_pubkey]);
122	bitcoin::taproot::TaprootBuilder::new()
123		.add_leaf(1, server_branch).unwrap()
124		.add_leaf(1, user_branch).unwrap()
125		.finalize(&SECP, combined_pk).unwrap()
126}
127
128/// Construct taproot spending information for a VTXO that enables incoming
129/// Lightning payments. This relates to the [crate::VtxoPolicy::ServerHtlcRecv]
130/// policy.
131///
132/// This will build a taproot with 3 clauses:
133/// 1. The keyspend path allows Alice and Server to collaborate to spend
134/// the HTLC. This is the expected path to be used. Server should only
135/// accept to collaborate if Alice reveals the preimage.
136///
137/// 2. One leaf of the tree allows Server to spend the HTLC after the
138/// expiry, with an exit delta delay. Server can use this path if Alice
139/// tries to spend the HTLC using the 3rd path after the HTLC expiry
140///
141/// 3. The other leaf of the tree allows Alice to spend the HTLC if she
142/// knows the preimage, but with a greater exit delta delay than Server.
143/// Alice must use this path if she revealed the preimage but Server
144/// refused to collaborate using the 1rst path.
145pub fn server_htlc_receive_taproot(
146	payment_hash: PaymentHash,
147	server_pubkey: PublicKey,
148	user_pubkey: PublicKey,
149	exit_delta: BlockDelta,
150	htlc_expiry_delta: BlockDelta,
151	htlc_expiry: BlockHeight,
152) -> TaprootSpendInfo {
153	let server_branch =
154		scripts::delay_timelock_sign(exit_delta, htlc_expiry, server_pubkey.x_only_public_key().0);
155	let user_branch = scripts::hash_delay_sign(
156		payment_hash.to_sha256_hash(),
157		exit_delta + htlc_expiry_delta,
158		user_pubkey.x_only_public_key().0,
159	);
160
161	let combined_pk = musig::combine_keys([user_pubkey, server_pubkey]);
162	bitcoin::taproot::TaprootBuilder::new()
163		.add_leaf(1, server_branch).unwrap()
164		.add_leaf(1, user_branch).unwrap()
165		.finalize(&SECP, combined_pk).unwrap()
166}
167
168
169#[derive(Debug, Clone)]
170pub enum PaymentStatus {
171	Pending,
172	Complete,
173	Failed,
174}
175
176impl fmt::Display for PaymentStatus {
177	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
178		fmt::Debug::fmt(self, f)
179	}
180}
181
182/// Enum to represent either a lightning [Bolt11Invoice] or a [Bolt12Invoice].
183#[derive(Debug, Clone, PartialEq, Eq, Hash)]
184pub enum Invoice {
185	Bolt11(Bolt11Invoice),
186	Bolt12(Bolt12Invoice),
187}
188
189#[derive(Debug, thiserror::Error)]
190#[error("cannot parse invoice")]
191pub struct InvoiceParseError;
192
193impl FromStr for Invoice {
194	type Err = InvoiceParseError;
195
196	fn from_str(s: &str) -> Result<Self, Self::Err> {
197		if let Ok(bolt11) = Bolt11Invoice::from_str(s) {
198			Ok(Invoice::Bolt11(bolt11))
199		} else if let Ok(bolt12) = Bolt12Invoice::from_str(s) {
200			Ok(Invoice::Bolt12(bolt12))
201		} else {
202			Err(InvoiceParseError)
203		}
204	}
205}
206
207impl From<Bolt11Invoice> for Invoice {
208	fn from(invoice: Bolt11Invoice) -> Self {
209		Invoice::Bolt11(invoice)
210	}
211}
212
213impl From<Bolt12Invoice> for Invoice {
214	fn from(invoice: Bolt12Invoice) -> Self {
215		Invoice::Bolt12(invoice)
216	}
217}
218
219impl serde::Serialize for Invoice {
220	fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
221		s.collect_str(self)
222	}
223}
224
225impl<'de> serde::Deserialize<'de> for Invoice {
226	fn deserialize<D: serde::Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
227		struct Visitor;
228		impl<'de> serde::de::Visitor<'de> for Visitor {
229			type Value = Invoice;
230			fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
231				write!(f, "a lightning invoice")
232			}
233			fn visit_str<E: serde::de::Error>(self, v: &str) -> Result<Self::Value, E> {
234				Invoice::from_str(v).map_err(serde::de::Error::custom)
235			}
236		}
237		d.deserialize_str(Visitor)
238	}
239}
240
241#[derive(Debug, thiserror::Error)]
242#[error("invalid invoice signature: {0}")]
243pub struct CheckSignatureError(pub String);
244
245impl Invoice {
246	pub fn into_bolt11(self) -> Option<Bolt11Invoice> {
247		match self {
248			Invoice::Bolt11(invoice) => Some(invoice),
249			Invoice::Bolt12(_) => None
250		}
251	}
252
253	pub fn payment_hash(&self) -> PaymentHash {
254		match self {
255			Invoice::Bolt11(invoice) => PaymentHash::from(*invoice.payment_hash().as_byte_array()),
256			Invoice::Bolt12(invoice) => PaymentHash::from(invoice.payment_hash()),
257		}
258	}
259
260	pub fn network(&self) -> Network {
261		match self {
262			Invoice::Bolt11(invoice) => invoice.network(),
263			Invoice::Bolt12(invoice) => match invoice.chain() {
264				ChainHash::BITCOIN => Network::Bitcoin,
265				ChainHash::TESTNET3 => Network::Testnet,
266				ChainHash::TESTNET4 => Network::Testnet4,
267				ChainHash::SIGNET => Network::Signet,
268				ChainHash::REGTEST => Network::Regtest,
269				_ => panic!("unsupported network"),
270			},
271		}
272	}
273
274	pub fn amount_msat(&self) -> Option<u64> {
275		match self {
276			Invoice::Bolt11(invoice) => invoice.amount_milli_satoshis(),
277			Invoice::Bolt12(invoice) => Some(invoice.amount_msats()),
278		}
279	}
280
281	pub fn check_signature(&self) -> Result<(), CheckSignatureError> {
282		match self {
283			Invoice::Bolt11(invoice) => invoice
284				.check_signature()
285				.map_err(|e| CheckSignatureError(e.to_string())),
286			Invoice::Bolt12(invoice) => invoice.check_signature(),
287		}
288	}
289}
290
291impl fmt::Display for Invoice {
292	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
293		match self {
294			Invoice::Bolt11(invoice) => write!(f, "{}", invoice.to_string()),
295			Invoice::Bolt12(invoice) => encode_to_fmt::<NoChecksum, _>(
296				f,
297				Hrp::parse("lni").unwrap(),
298				&invoice.bytes(),
299			)
300			.map_err(|e| match e {
301				EncodeError::Fmt(e) => e,
302				_ => fmt::Error {},
303			}),
304		}
305	}
306}
307
308pub trait Bolt12InvoiceExt: Borrow<Bolt12Invoice> {
309	fn payment_hash(&self) -> PaymentHash { PaymentHash::from(self.borrow().payment_hash()) }
310
311	fn check_signature(&self) -> Result<(), CheckSignatureError> {
312		let message = Message::from_digest(self.borrow().signable_hash());
313		let signature = self.borrow().signature();
314
315		if let Some(pubkey) = self.borrow().issuer_signing_pubkey() {
316			Ok(SECP.verify_schnorr(&signature, &message, &pubkey.into())
317				.map_err(|_| CheckSignatureError("invalid signature".to_string()))?)
318		} else {
319			Err(CheckSignatureError("no pubkey on offer, cannot verify signature".to_string()))
320		}
321	}
322
323	fn bytes(&self) -> Vec<u8> {
324		let mut bytes = Vec::new();
325		self.borrow().write(&mut bytes).expect("Writing into a Vec is infallible");
326		bytes
327	}
328
329	fn from_bytes(bytes: &[u8]) -> Result<Bolt12Invoice, Bolt12ParseError> {
330		Bolt12Invoice::try_from(bytes.to_vec())
331	}
332
333	fn validate_issuance(&self, offer: Offer) -> Result<(), CheckSignatureError> {
334		if self.borrow().issuer_signing_pubkey() != offer.issuer_signing_pubkey() {
335			Err(CheckSignatureError("public keys mismatch".to_string()))
336		} else {
337			Ok(())
338		}
339	}
340
341	fn from_str(s: &str) -> Result<Bolt12Invoice, Bolt12ParseError> {
342		let dec = CheckedHrpstring::new::<NoChecksum>(&s)?;
343		if dec.hrp().to_lowercase() != BECH32_BOLT12_INVOICE_HRP {
344			return Err(Bolt12ParseError::InvalidBech32Hrp);
345		}
346
347		let data = dec.byte_iter().collect::<Vec<_>>();
348		Bolt12Invoice::try_from(data)
349	}
350}
351
352impl Bolt12InvoiceExt for Bolt12Invoice {}