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