|
7 | 7 | // You may not use this file except in accordance with one or both of these
|
8 | 8 | // licenses.
|
9 | 9 |
|
10 |
| -//! Convenient utilities for paying Lightning invoices. |
11 |
| -
|
12 |
| -use bitcoin::hashes::Hash; |
13 |
| -use lightning_invoice::Bolt11Invoice; |
14 |
| - |
15 |
| -use crate::ln::channelmanager::RecipientOnionFields; |
16 |
| -use crate::routing::router::{PaymentParameters, RouteParameters}; |
17 |
| -use crate::types::payment::PaymentHash; |
18 |
| - |
19 |
| -/// Builds the necessary parameters to pay or pre-flight probe the given variable-amount |
20 |
| -/// (also known as 'zero-amount') [`Bolt11Invoice`] using |
21 |
| -/// [`ChannelManager::send_payment`] or [`ChannelManager::send_preflight_probes`]. |
22 |
| -/// |
23 |
| -/// Prior to paying, you must ensure that the [`Bolt11Invoice::payment_hash`] is unique and the |
24 |
| -/// same [`PaymentHash`] has never been paid before. |
25 |
| -/// |
26 |
| -/// Will always succeed unless the invoice has an amount specified, in which case |
27 |
| -/// [`payment_parameters_from_invoice`] should be used. |
28 |
| -/// |
29 |
| -/// [`ChannelManager::send_payment`]: crate::ln::channelmanager::ChannelManager::send_payment |
30 |
| -/// [`ChannelManager::send_preflight_probes`]: crate::ln::channelmanager::ChannelManager::send_preflight_probes |
31 |
| -pub fn payment_parameters_from_variable_amount_invoice( |
32 |
| - invoice: &Bolt11Invoice, amount_msat: u64, |
33 |
| -) -> Result<(PaymentHash, RecipientOnionFields, RouteParameters), ()> { |
34 |
| - if invoice.amount_milli_satoshis().is_some() { |
35 |
| - Err(()) |
36 |
| - } else { |
37 |
| - Ok(params_from_invoice(invoice, amount_msat)) |
38 |
| - } |
39 |
| -} |
40 |
| - |
41 |
| -/// Builds the necessary parameters to pay or pre-flight probe the given [`Bolt11Invoice`] using |
42 |
| -/// [`ChannelManager::send_payment`] or [`ChannelManager::send_preflight_probes`]. |
43 |
| -/// |
44 |
| -/// Prior to paying, you must ensure that the [`Bolt11Invoice::payment_hash`] is unique and the |
45 |
| -/// same [`PaymentHash`] has never been paid before. |
46 |
| -/// |
47 |
| -/// Will always succeed unless the invoice has no amount specified, in which case |
48 |
| -/// [`payment_parameters_from_variable_amount_invoice`] should be used. |
49 |
| -/// |
50 |
| -/// [`ChannelManager::send_payment`]: crate::ln::channelmanager::ChannelManager::send_payment |
51 |
| -/// [`ChannelManager::send_preflight_probes`]: crate::ln::channelmanager::ChannelManager::send_preflight_probes |
52 |
| -pub fn payment_parameters_from_invoice( |
53 |
| - invoice: &Bolt11Invoice, |
54 |
| -) -> Result<(PaymentHash, RecipientOnionFields, RouteParameters), ()> { |
55 |
| - if let Some(amount_msat) = invoice.amount_milli_satoshis() { |
56 |
| - Ok(params_from_invoice(invoice, amount_msat)) |
57 |
| - } else { |
58 |
| - Err(()) |
59 |
| - } |
60 |
| -} |
61 |
| - |
62 |
| -fn params_from_invoice( |
63 |
| - invoice: &Bolt11Invoice, amount_msat: u64, |
64 |
| -) -> (PaymentHash, RecipientOnionFields, RouteParameters) { |
65 |
| - let payment_hash = PaymentHash((*invoice.payment_hash()).to_byte_array()); |
66 |
| - |
67 |
| - let mut recipient_onion = RecipientOnionFields::secret_only(*invoice.payment_secret()); |
68 |
| - recipient_onion.payment_metadata = invoice.payment_metadata().map(|v| v.clone()); |
69 |
| - |
70 |
| - let mut payment_params = PaymentParameters::from_node_id( |
71 |
| - invoice.recover_payee_pub_key(), |
72 |
| - invoice.min_final_cltv_expiry_delta() as u32, |
73 |
| - ) |
74 |
| - .with_route_hints(invoice.route_hints()) |
75 |
| - .unwrap(); |
76 |
| - if let Some(expiry) = invoice.expires_at() { |
77 |
| - payment_params = payment_params.with_expiry_time(expiry.as_secs()); |
78 |
| - } |
79 |
| - if let Some(features) = invoice.features() { |
80 |
| - payment_params = payment_params.with_bolt11_features(features.clone()).unwrap(); |
81 |
| - } |
82 |
| - |
83 |
| - let route_params = RouteParameters::from_payment_params_and_value(payment_params, amount_msat); |
84 |
| - (payment_hash, recipient_onion, route_params) |
85 |
| -} |
| 10 | +//! Tests for verifying the correct end-to-end handling of BOLT11 payments, including metadata propagation. |
86 | 11 |
|
87 | 12 | #[cfg(test)]
|
88 | 13 | mod tests {
|
89 |
| - use super::*; |
90 | 14 | use crate::events::Event;
|
91 | 15 | use crate::ln::channelmanager::{PaymentId, Retry};
|
92 | 16 | use crate::ln::functional_test_utils::*;
|
93 | 17 | use crate::ln::msgs::ChannelMessageHandler;
|
94 | 18 | use crate::ln::outbound_payment::Bolt11PaymentError;
|
95 |
| - use crate::routing::router::{Payee, RouteParametersConfig}; |
| 19 | + use crate::routing::router::RouteParametersConfig; |
96 | 20 | use crate::sign::{NodeSigner, Recipient};
|
97 |
| - use crate::types::payment::PaymentSecret; |
98 | 21 | use bitcoin::hashes::sha256::Hash as Sha256;
|
99 |
| - use bitcoin::secp256k1::{PublicKey, Secp256k1, SecretKey}; |
100 |
| - use lightning_invoice::{Currency, InvoiceBuilder}; |
| 22 | + use bitcoin::hashes::Hash; |
| 23 | + use lightning_invoice::{Bolt11Invoice, Currency, InvoiceBuilder}; |
101 | 24 | use std::time::SystemTime;
|
102 | 25 |
|
103 |
| - #[test] |
104 |
| - fn invoice_test() { |
105 |
| - let payment_hash = Sha256::hash(&[0; 32]); |
106 |
| - let private_key = SecretKey::from_slice(&[42; 32]).unwrap(); |
107 |
| - let secp_ctx = Secp256k1::new(); |
108 |
| - let public_key = PublicKey::from_secret_key(&secp_ctx, &private_key); |
109 |
| - |
110 |
| - let timestamp = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap(); |
111 |
| - let invoice = InvoiceBuilder::new(Currency::Bitcoin) |
112 |
| - .description("test".into()) |
113 |
| - .payment_hash(payment_hash) |
114 |
| - .payment_secret(PaymentSecret([0; 32])) |
115 |
| - .duration_since_epoch(timestamp) |
116 |
| - .min_final_cltv_expiry_delta(144) |
117 |
| - .amount_milli_satoshis(128) |
118 |
| - .build_signed(|hash| secp_ctx.sign_ecdsa_recoverable(hash, &private_key)) |
119 |
| - .unwrap(); |
120 |
| - |
121 |
| - assert!(payment_parameters_from_variable_amount_invoice(&invoice, 42).is_err()); |
122 |
| - |
123 |
| - let (hash, onion, params) = payment_parameters_from_invoice(&invoice).unwrap(); |
124 |
| - assert_eq!(&hash.0[..], &payment_hash[..]); |
125 |
| - assert_eq!(onion.payment_secret, Some(PaymentSecret([0; 32]))); |
126 |
| - assert_eq!(params.final_value_msat, 128); |
127 |
| - match params.payment_params.payee { |
128 |
| - Payee::Clear { node_id, .. } => { |
129 |
| - assert_eq!(node_id, public_key); |
130 |
| - }, |
131 |
| - _ => panic!(), |
132 |
| - } |
133 |
| - } |
134 |
| - |
135 |
| - #[test] |
136 |
| - fn zero_value_invoice_test() { |
137 |
| - let payment_hash = Sha256::hash(&[0; 32]); |
138 |
| - let private_key = SecretKey::from_slice(&[42; 32]).unwrap(); |
139 |
| - let secp_ctx = Secp256k1::new(); |
140 |
| - let public_key = PublicKey::from_secret_key(&secp_ctx, &private_key); |
141 |
| - |
142 |
| - let timestamp = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap(); |
143 |
| - let invoice = InvoiceBuilder::new(Currency::Bitcoin) |
144 |
| - .description("test".into()) |
145 |
| - .payment_hash(payment_hash) |
146 |
| - .payment_secret(PaymentSecret([0; 32])) |
147 |
| - .duration_since_epoch(timestamp) |
148 |
| - .min_final_cltv_expiry_delta(144) |
149 |
| - .build_signed(|hash| secp_ctx.sign_ecdsa_recoverable(hash, &private_key)) |
150 |
| - .unwrap(); |
151 |
| - |
152 |
| - assert!(payment_parameters_from_invoice(&invoice).is_err()); |
153 |
| - |
154 |
| - let (hash, onion, params) = |
155 |
| - payment_parameters_from_variable_amount_invoice(&invoice, 42).unwrap(); |
156 |
| - assert_eq!(&hash.0[..], &payment_hash[..]); |
157 |
| - assert_eq!(onion.payment_secret, Some(PaymentSecret([0; 32]))); |
158 |
| - assert_eq!(params.final_value_msat, 42); |
159 |
| - match params.payment_params.payee { |
160 |
| - Payee::Clear { node_id, .. } => { |
161 |
| - assert_eq!(node_id, public_key); |
162 |
| - }, |
163 |
| - _ => panic!(), |
164 |
| - } |
165 |
| - } |
166 |
| - |
167 | 26 | #[test]
|
168 | 27 | fn payment_metadata_end_to_end_for_invoice_with_amount() {
|
169 | 28 | // Test that a payment metadata read from an invoice passed to `pay_invoice` makes it all
|
|
0 commit comments