Skip to content

Commit 077df33

Browse files
authored
Merge pull request #3593 from vincenzopalazzo/macros/proof-of-payment-bolt12
[RFC] Implement a way to do BOLT 12 Proof of Payment
2 parents 06fb142 + 57772d3 commit 077df33

12 files changed

+194
-64
lines changed

lightning/src/events/mod.rs

+35-2
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,12 @@ use crate::blinded_path::payment::{Bolt12OfferContext, Bolt12RefundContext, Paym
2323
use crate::chain::transaction;
2424
use crate::ln::channelmanager::{InterceptId, PaymentId, RecipientOnionFields};
2525
use crate::ln::channel::FUNDING_CONF_DEADLINE_BLOCKS;
26+
use crate::offers::invoice::Bolt12Invoice;
27+
use crate::offers::static_invoice::StaticInvoice;
2628
use crate::types::features::ChannelTypeFeatures;
2729
use crate::ln::msgs;
2830
use crate::ln::types::ChannelId;
2931
use crate::types::payment::{PaymentPreimage, PaymentHash, PaymentSecret};
30-
use crate::offers::invoice::Bolt12Invoice;
3132
use crate::onion_message::messenger::Responder;
3233
use crate::routing::gossip::NetworkUpdate;
3334
use crate::routing::router::{BlindedTail, Path, RouteHop, RouteParameters};
@@ -949,6 +950,18 @@ pub enum Event {
949950
///
950951
/// [`Route::get_total_fees`]: crate::routing::router::Route::get_total_fees
951952
fee_paid_msat: Option<u64>,
953+
/// The BOLT 12 invoice that was paid. `None` if the payment was a non BOLT 12 payment.
954+
///
955+
/// The BOLT 12 invoice is useful for proof of payment because it contains the
956+
/// payment hash. A third party can verify that the payment was made by
957+
/// showing the invoice and confirming that the payment hash matches
958+
/// the hash of the payment preimage.
959+
///
960+
/// However, the [`PaidBolt12Invoice`] can also be of type [`StaticInvoice`], which
961+
/// is a special [`Bolt12Invoice`] where proof of payment is not possible.
962+
///
963+
/// [`StaticInvoice`]: crate::offers::static_invoice::StaticInvoice
964+
bolt12_invoice: Option<PaidBolt12Invoice>,
952965
},
953966
/// Indicates an outbound payment failed. Individual [`Event::PaymentPathFailed`] events
954967
/// provide failure information for each path attempt in the payment, including retries.
@@ -1556,14 +1569,15 @@ impl Writeable for Event {
15561569
(13, payment_id, option),
15571570
});
15581571
},
1559-
&Event::PaymentSent { ref payment_id, ref payment_preimage, ref payment_hash, ref amount_msat, ref fee_paid_msat } => {
1572+
&Event::PaymentSent { ref payment_id, ref payment_preimage, ref payment_hash, ref amount_msat, ref fee_paid_msat, ref bolt12_invoice } => {
15601573
2u8.write(writer)?;
15611574
write_tlv_fields!(writer, {
15621575
(0, payment_preimage, required),
15631576
(1, payment_hash, required),
15641577
(3, payment_id, option),
15651578
(5, fee_paid_msat, option),
15661579
(7, amount_msat, option),
1580+
(9, bolt12_invoice, option),
15671581
});
15681582
},
15691583
&Event::PaymentPathFailed {
@@ -1898,12 +1912,14 @@ impl MaybeReadable for Event {
18981912
let mut payment_id = None;
18991913
let mut amount_msat = None;
19001914
let mut fee_paid_msat = None;
1915+
let mut bolt12_invoice = None;
19011916
read_tlv_fields!(reader, {
19021917
(0, payment_preimage, required),
19031918
(1, payment_hash, option),
19041919
(3, payment_id, option),
19051920
(5, fee_paid_msat, option),
19061921
(7, amount_msat, option),
1922+
(9, bolt12_invoice, option),
19071923
});
19081924
if payment_hash.is_none() {
19091925
payment_hash = Some(PaymentHash(Sha256::hash(&payment_preimage.0[..]).to_byte_array()));
@@ -1914,6 +1930,7 @@ impl MaybeReadable for Event {
19141930
payment_hash: payment_hash.unwrap(),
19151931
amount_msat,
19161932
fee_paid_msat,
1933+
bolt12_invoice,
19171934
}))
19181935
};
19191936
f()
@@ -2438,3 +2455,19 @@ impl<T: EventHandler> EventHandler for Arc<T> {
24382455
self.deref().handle_event(event)
24392456
}
24402457
}
2458+
2459+
/// The BOLT 12 invoice that was paid, surfaced in [`Event::PaymentSent::bolt12_invoice`].
2460+
#[derive(Clone, Debug, PartialEq, Eq)]
2461+
pub enum PaidBolt12Invoice {
2462+
/// The BOLT 12 invoice specified by the BOLT 12 specification,
2463+
/// allowing the user to perform proof of payment.
2464+
Bolt12Invoice(Bolt12Invoice),
2465+
/// The Static invoice, used in the async payment specification update proposal,
2466+
/// where the user cannot perform proof of payment.
2467+
StaticInvoice(StaticInvoice),
2468+
}
2469+
2470+
impl_writeable_tlv_based_enum!(PaidBolt12Invoice,
2471+
{0, Bolt12Invoice} => (),
2472+
{2, StaticInvoice} => (),
2473+
);

lightning/src/ln/async_payments_tests.rs

+6-3
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use crate::blinded_path::message::{MessageContext, OffersContext};
1111
use crate::blinded_path::payment::PaymentContext;
1212
use crate::blinded_path::payment::{AsyncBolt12OfferContext, BlindedPaymentTlvs};
1313
use crate::chain::channelmonitor::{HTLC_FAIL_BACK_BUFFER, LATENCY_GRACE_PERIOD_BLOCKS};
14-
use crate::events::{Event, HTLCDestination, PaymentFailureReason};
14+
use crate::events::{Event, HTLCDestination, PaidBolt12Invoice, PaymentFailureReason};
1515
use crate::ln::blinded_payment_tests::{fail_blinded_htlc_backwards, get_blinded_route_parameters};
1616
use crate::ln::channelmanager::{PaymentId, RecipientOnionFields};
1717
use crate::ln::functional_test_utils::*;
@@ -420,7 +420,7 @@ fn async_receive_flow_success() {
420420
.pay_for_offer(&offer, None, Some(amt_msat), None, payment_id, Retry::Attempts(0), params)
421421
.unwrap();
422422
let release_held_htlc_om =
423-
pass_async_payments_oms(static_invoice, &nodes[0], &nodes[1], &nodes[2]).1;
423+
pass_async_payments_oms(static_invoice.clone(), &nodes[0], &nodes[1], &nodes[2]).1;
424424
nodes[0]
425425
.onion_messenger
426426
.handle_onion_message(nodes[2].node.get_our_node_id(), &release_held_htlc_om);
@@ -441,7 +441,10 @@ fn async_receive_flow_success() {
441441
let args = PassAlongPathArgs::new(&nodes[0], route[0], amt_msat, payment_hash, ev)
442442
.with_payment_preimage(keysend_preimage);
443443
do_pass_along_path(args);
444-
claim_payment_along_route(ClaimAlongRouteArgs::new(&nodes[0], route, keysend_preimage));
444+
let res =
445+
claim_payment_along_route(ClaimAlongRouteArgs::new(&nodes[0], route, keysend_preimage));
446+
assert!(res.is_some());
447+
assert_eq!(res, Some(PaidBolt12Invoice::StaticInvoice(static_invoice)));
445448
}
446449

447450
#[cfg_attr(feature = "std", ignore)]

lightning/src/ln/functional_test_utils.rs

+16-10
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
use crate::chain::{BestBlock, ChannelMonitorUpdateStatus, Confirm, Listen, Watch};
1414
use crate::chain::channelmonitor::ChannelMonitor;
1515
use crate::chain::transaction::OutPoint;
16-
use crate::events::{ClaimedHTLC, ClosureReason, Event, HTLCDestination, PathFailure, PaymentPurpose, PaymentFailureReason};
16+
use crate::events::{ClaimedHTLC, ClosureReason, Event, HTLCDestination, PaidBolt12Invoice, PathFailure, PaymentFailureReason, PaymentPurpose};
1717
use crate::events::bump_transaction::{BumpTransactionEvent, BumpTransactionEventHandler, Wallet, WalletSource};
1818
use crate::ln::types::ChannelId;
1919
use crate::types::payment::{PaymentPreimage, PaymentHash, PaymentSecret};
@@ -2317,7 +2317,7 @@ macro_rules! expect_payment_claimed {
23172317
pub fn expect_payment_sent<CM: AChannelManager, H: NodeHolder<CM=CM>>(node: &H,
23182318
expected_payment_preimage: PaymentPreimage, expected_fee_msat_opt: Option<Option<u64>>,
23192319
expect_per_path_claims: bool, expect_post_ev_mon_update: bool,
2320-
) {
2320+
) -> Option<PaidBolt12Invoice> {
23212321
let events = node.node().get_and_clear_pending_events();
23222322
let expected_payment_hash = PaymentHash(
23232323
bitcoin::hashes::sha256::Hash::hash(&expected_payment_preimage.0).to_byte_array());
@@ -2329,8 +2329,10 @@ pub fn expect_payment_sent<CM: AChannelManager, H: NodeHolder<CM=CM>>(node: &H,
23292329
if expect_post_ev_mon_update {
23302330
check_added_monitors(node, 1);
23312331
}
2332+
// We return the invoice because some test may want to check the invoice details.
2333+
let invoice;
23322334
let expected_payment_id = match events[0] {
2333-
Event::PaymentSent { ref payment_id, ref payment_preimage, ref payment_hash, ref amount_msat, ref fee_paid_msat } => {
2335+
Event::PaymentSent { ref payment_id, ref payment_preimage, ref payment_hash, ref amount_msat, ref fee_paid_msat, ref bolt12_invoice } => {
23342336
assert_eq!(expected_payment_preimage, *payment_preimage);
23352337
assert_eq!(expected_payment_hash, *payment_hash);
23362338
assert!(amount_msat.is_some());
@@ -2339,6 +2341,7 @@ pub fn expect_payment_sent<CM: AChannelManager, H: NodeHolder<CM=CM>>(node: &H,
23392341
} else {
23402342
assert!(fee_paid_msat.is_some());
23412343
}
2344+
invoice = bolt12_invoice.clone();
23422345
payment_id.unwrap()
23432346
},
23442347
_ => panic!("Unexpected event"),
@@ -2354,19 +2357,20 @@ pub fn expect_payment_sent<CM: AChannelManager, H: NodeHolder<CM=CM>>(node: &H,
23542357
}
23552358
}
23562359
}
2360+
invoice
23572361
}
23582362

23592363
#[macro_export]
23602364
macro_rules! expect_payment_sent {
23612365
($node: expr, $expected_payment_preimage: expr) => {
2362-
$crate::expect_payment_sent!($node, $expected_payment_preimage, None::<u64>, true);
2366+
$crate::expect_payment_sent!($node, $expected_payment_preimage, None::<u64>, true)
23632367
};
23642368
($node: expr, $expected_payment_preimage: expr, $expected_fee_msat_opt: expr) => {
2365-
$crate::expect_payment_sent!($node, $expected_payment_preimage, $expected_fee_msat_opt, true);
2369+
$crate::expect_payment_sent!($node, $expected_payment_preimage, $expected_fee_msat_opt, true)
23662370
};
23672371
($node: expr, $expected_payment_preimage: expr, $expected_fee_msat_opt: expr, $expect_paths: expr) => {
23682372
$crate::ln::functional_test_utils::expect_payment_sent(&$node, $expected_payment_preimage,
2369-
$expected_fee_msat_opt.map(|o| Some(o)), $expect_paths, true);
2373+
$expected_fee_msat_opt.map(|o| Some(o)), $expect_paths, true)
23702374
}
23712375
}
23722376

@@ -3130,20 +3134,22 @@ pub fn pass_claimed_payment_along_route(args: ClaimAlongRouteArgs) -> u64 {
31303134

31313135
expected_total_fee_msat
31323136
}
3133-
pub fn claim_payment_along_route(args: ClaimAlongRouteArgs) {
3137+
pub fn claim_payment_along_route(args: ClaimAlongRouteArgs) -> Option<PaidBolt12Invoice> {
31343138
let origin_node = args.origin_node;
31353139
let payment_preimage = args.payment_preimage;
31363140
let skip_last = args.skip_last;
31373141
let expected_total_fee_msat = do_claim_payment_along_route(args);
31383142
if !skip_last {
3139-
expect_payment_sent!(origin_node, payment_preimage, Some(expected_total_fee_msat));
3143+
expect_payment_sent!(origin_node, payment_preimage, Some(expected_total_fee_msat))
3144+
} else {
3145+
None
31403146
}
31413147
}
31423148

3143-
pub fn claim_payment<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_route: &[&Node<'a, 'b, 'c>], our_payment_preimage: PaymentPreimage) {
3149+
pub fn claim_payment<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_route: &[&Node<'a, 'b, 'c>], our_payment_preimage: PaymentPreimage) -> Option<PaidBolt12Invoice> {
31443150
claim_payment_along_route(
31453151
ClaimAlongRouteArgs::new(origin_node, &[expected_route], our_payment_preimage)
3146-
);
3152+
)
31473153
}
31483154

31493155
pub const TEST_FINAL_CLTV: u32 = 70;

lightning/src/ln/offers_tests.rs

+17-13
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ use crate::blinded_path::IntroductionNode;
4747
use crate::blinded_path::message::BlindedMessagePath;
4848
use crate::blinded_path::payment::{Bolt12OfferContext, Bolt12RefundContext, PaymentContext};
4949
use crate::blinded_path::message::OffersContext;
50-
use crate::events::{ClosureReason, Event, HTLCDestination, PaymentFailureReason, PaymentPurpose};
50+
use crate::events::{ClosureReason, Event, HTLCDestination, PaidBolt12Invoice, PaymentFailureReason, PaymentPurpose};
5151
use crate::ln::channelmanager::{Bolt12PaymentError, MAX_SHORT_LIVED_RELATIVE_EXPIRY, PaymentId, RecentPaymentDetails, RecipientOnionFields, Retry, self};
5252
use crate::types::features::Bolt12InvoiceFeatures;
5353
use crate::ln::functional_test_utils::*;
@@ -167,7 +167,7 @@ fn route_bolt12_payment<'a, 'b, 'c>(
167167
}
168168

169169
fn claim_bolt12_payment<'a, 'b, 'c>(
170-
node: &Node<'a, 'b, 'c>, path: &[&Node<'a, 'b, 'c>], expected_payment_context: PaymentContext
170+
node: &Node<'a, 'b, 'c>, path: &[&Node<'a, 'b, 'c>], expected_payment_context: PaymentContext, invoice: &Bolt12Invoice
171171
) {
172172
let recipient = &path[path.len() - 1];
173173
let payment_purpose = match get_event!(recipient, Event::PaymentClaimable) {
@@ -187,7 +187,11 @@ fn claim_bolt12_payment<'a, 'b, 'c>(
187187
},
188188
_ => panic!("Unexpected payment purpose: {:?}", payment_purpose),
189189
}
190-
claim_payment(node, path, payment_preimage);
190+
if let Some(inv) = claim_payment(node, path, payment_preimage) {
191+
assert_eq!(inv, PaidBolt12Invoice::Bolt12Invoice(invoice.to_owned()));
192+
} else {
193+
panic!("Expected PaidInvoice::Bolt12Invoice");
194+
};
191195
}
192196

193197
fn extract_offer_nonce<'a, 'b, 'c>(node: &Node<'a, 'b, 'c>, message: &OnionMessage) -> Nonce {
@@ -591,7 +595,7 @@ fn creates_and_pays_for_offer_using_two_hop_blinded_path() {
591595
route_bolt12_payment(david, &[charlie, bob, alice], &invoice);
592596
expect_recent_payment!(david, RecentPaymentDetails::Pending, payment_id);
593597

594-
claim_bolt12_payment(david, &[charlie, bob, alice], payment_context);
598+
claim_bolt12_payment(david, &[charlie, bob, alice], payment_context, &invoice);
595599
expect_recent_payment!(david, RecentPaymentDetails::Fulfilled, payment_id);
596600
}
597601

@@ -674,7 +678,7 @@ fn creates_and_pays_for_refund_using_two_hop_blinded_path() {
674678
route_bolt12_payment(david, &[charlie, bob, alice], &invoice);
675679
expect_recent_payment!(david, RecentPaymentDetails::Pending, payment_id);
676680

677-
claim_bolt12_payment(david, &[charlie, bob, alice], payment_context);
681+
claim_bolt12_payment(david, &[charlie, bob, alice], payment_context, &invoice);
678682
expect_recent_payment!(david, RecentPaymentDetails::Fulfilled, payment_id);
679683
}
680684

@@ -741,7 +745,7 @@ fn creates_and_pays_for_offer_using_one_hop_blinded_path() {
741745
route_bolt12_payment(bob, &[alice], &invoice);
742746
expect_recent_payment!(bob, RecentPaymentDetails::Pending, payment_id);
743747

744-
claim_bolt12_payment(bob, &[alice], payment_context);
748+
claim_bolt12_payment(bob, &[alice], payment_context, &invoice);
745749
expect_recent_payment!(bob, RecentPaymentDetails::Fulfilled, payment_id);
746750
}
747751

@@ -797,7 +801,7 @@ fn creates_and_pays_for_refund_using_one_hop_blinded_path() {
797801
route_bolt12_payment(bob, &[alice], &invoice);
798802
expect_recent_payment!(bob, RecentPaymentDetails::Pending, payment_id);
799803

800-
claim_bolt12_payment(bob, &[alice], payment_context);
804+
claim_bolt12_payment(bob, &[alice], payment_context, &invoice);
801805
expect_recent_payment!(bob, RecentPaymentDetails::Fulfilled, payment_id);
802806
}
803807

@@ -851,7 +855,7 @@ fn pays_for_offer_without_blinded_paths() {
851855
route_bolt12_payment(bob, &[alice], &invoice);
852856
expect_recent_payment!(bob, RecentPaymentDetails::Pending, payment_id);
853857

854-
claim_bolt12_payment(bob, &[alice], payment_context);
858+
claim_bolt12_payment(bob, &[alice], payment_context, &invoice);
855859
expect_recent_payment!(bob, RecentPaymentDetails::Fulfilled, payment_id);
856860
}
857861

@@ -894,7 +898,7 @@ fn pays_for_refund_without_blinded_paths() {
894898
route_bolt12_payment(bob, &[alice], &invoice);
895899
expect_recent_payment!(bob, RecentPaymentDetails::Pending, payment_id);
896900

897-
claim_bolt12_payment(bob, &[alice], payment_context);
901+
claim_bolt12_payment(bob, &[alice], payment_context, &invoice);
898902
expect_recent_payment!(bob, RecentPaymentDetails::Fulfilled, payment_id);
899903
}
900904

@@ -1132,7 +1136,7 @@ fn creates_and_pays_for_offer_with_retry() {
11321136
}
11331137
route_bolt12_payment(bob, &[alice], &invoice);
11341138
expect_recent_payment!(bob, RecentPaymentDetails::Pending, payment_id);
1135-
claim_bolt12_payment(bob, &[alice], payment_context);
1139+
claim_bolt12_payment(bob, &[alice], payment_context, &invoice);
11361140
expect_recent_payment!(bob, RecentPaymentDetails::Fulfilled, payment_id);
11371141
}
11381142

@@ -1203,7 +1207,7 @@ fn pays_bolt12_invoice_asynchronously() {
12031207
route_bolt12_payment(bob, &[alice], &invoice);
12041208
expect_recent_payment!(bob, RecentPaymentDetails::Pending, payment_id);
12051209

1206-
claim_bolt12_payment(bob, &[alice], payment_context);
1210+
claim_bolt12_payment(bob, &[alice], payment_context, &invoice);
12071211
expect_recent_payment!(bob, RecentPaymentDetails::Fulfilled, payment_id);
12081212

12091213
assert_eq!(
@@ -1283,7 +1287,7 @@ fn creates_offer_with_blinded_path_using_unannounced_introduction_node() {
12831287
route_bolt12_payment(bob, &[alice], &invoice);
12841288
expect_recent_payment!(bob, RecentPaymentDetails::Pending, payment_id);
12851289

1286-
claim_bolt12_payment(bob, &[alice], payment_context);
1290+
claim_bolt12_payment(bob, &[alice], payment_context, &invoice);
12871291
expect_recent_payment!(bob, RecentPaymentDetails::Fulfilled, payment_id);
12881292
}
12891293

@@ -2139,7 +2143,7 @@ fn fails_paying_invoice_more_than_once() {
21392143
assert!(david.node.get_and_clear_pending_msg_events().is_empty());
21402144

21412145
// Complete paying the first invoice
2142-
claim_bolt12_payment(david, &[charlie, bob, alice], payment_context);
2146+
claim_bolt12_payment(david, &[charlie, bob, alice], payment_context, &invoice1);
21432147
expect_recent_payment!(david, RecentPaymentDetails::Fulfilled, payment_id);
21442148
}
21452149

0 commit comments

Comments
 (0)