Skip to content

Commit c91a362

Browse files
add the bolt12 invoice to the PaymentSend event
To enable proof of payment, we need to share the bolt12 invoice with the library user. This is already possible if we `manually_handle_bolt12_invoices`, but this approach requires a significant amount of work from the user. This commit adds the bolt12 invoice to the PaymentSend event when the payment is completed. This allows the user to always have the option to perform proof of payment. Link: lightningdevkit#3344 Signed-off-by: Vincenzo Palazzo <[email protected]>
1 parent 85d807b commit c91a362

File tree

5 files changed

+80
-26
lines changed

5 files changed

+80
-26
lines changed

lightning/src/events/mod.rs

+36-1
Original file line numberDiff line numberDiff line change
@@ -949,6 +949,13 @@ pub enum Event {
949949
///
950950
/// [`Route::get_total_fees`]: crate::routing::router::Route::get_total_fees
951951
fee_paid_msat: Option<u64>,
952+
/// The bolt12 invoice that was paid. `None` if the payment was a non-bolt12 payment.
953+
///
954+
/// The bolt12 invoice is useful for proof of payment because it contains the
955+
/// payment hash. A third party can verify that the payment was made by
956+
/// showing the invoice and confirming that the payment hash matches
957+
/// the hash of the payment preimage.
958+
bolt12_invoice: Option<PaidInvoice>,
952959
},
953960
/// Indicates an outbound payment failed. Individual [`Event::PaymentPathFailed`] events
954961
/// provide failure information for each path attempt in the payment, including retries.
@@ -1556,14 +1563,15 @@ impl Writeable for Event {
15561563
(13, payment_id, option),
15571564
});
15581565
},
1559-
&Event::PaymentSent { ref payment_id, ref payment_preimage, ref payment_hash, ref amount_msat, ref fee_paid_msat } => {
1566+
&Event::PaymentSent { ref payment_id, ref payment_preimage, ref payment_hash, ref amount_msat, ref fee_paid_msat, ref bolt12_invoice } => {
15601567
2u8.write(writer)?;
15611568
write_tlv_fields!(writer, {
15621569
(0, payment_preimage, required),
15631570
(1, payment_hash, required),
15641571
(3, payment_id, option),
15651572
(5, fee_paid_msat, option),
15661573
(7, amount_msat, option),
1574+
(9, bolt12_invoice, option),
15671575
});
15681576
},
15691577
&Event::PaymentPathFailed {
@@ -1898,12 +1906,14 @@ impl MaybeReadable for Event {
18981906
let mut payment_id = None;
18991907
let mut amount_msat = None;
19001908
let mut fee_paid_msat = None;
1909+
let mut bolt12_invoice = None;
19011910
read_tlv_fields!(reader, {
19021911
(0, payment_preimage, required),
19031912
(1, payment_hash, option),
19041913
(3, payment_id, option),
19051914
(5, fee_paid_msat, option),
19061915
(7, amount_msat, option),
1916+
(9, bolt12_invoice, option),
19071917
});
19081918
if payment_hash.is_none() {
19091919
payment_hash = Some(PaymentHash(Sha256::hash(&payment_preimage.0[..]).to_byte_array()));
@@ -1914,6 +1924,7 @@ impl MaybeReadable for Event {
19141924
payment_hash: payment_hash.unwrap(),
19151925
amount_msat,
19161926
fee_paid_msat,
1927+
bolt12_invoice,
19171928
}))
19181929
};
19191930
f()
@@ -2438,3 +2449,27 @@ impl<T: EventHandler> EventHandler for Arc<T> {
24382449
self.deref().handle_event(event)
24392450
}
24402451
}
2452+
2453+
/// Wrapper time to move the bolt12 invoice and the static
2454+
/// invoice across the same event as a unique type.
2455+
#[derive(Clone, Debug, PartialEq, Eq)]
2456+
pub enum PaidInvoice {
2457+
/// Bolt12 invoice
2458+
Bolt12Invoice(crate::offers::invoice::Bolt12Invoice),
2459+
#[cfg(async_payments)]
2460+
/// Static invoice
2461+
StaticInvoice(crate::offers::static_invoice::StaticInvoice),
2462+
}
2463+
2464+
#[cfg(not(async_payments))]
2465+
impl_writeable_tlv_based_enum_legacy!(PaidInvoice,
2466+
;
2467+
(0, Bolt12Invoice),
2468+
);
2469+
2470+
#[cfg(async_payments)]
2471+
impl_writeable_tlv_based_enum_legacy!(PaidInvoice,
2472+
;
2473+
(0, Bolt12Invoice),
2474+
(2, StaticInvoice)
2475+
);

lightning/src/ln/functional_test_utils.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ use crate::util::test_channel_signer::SignerOp;
3535
use crate::util::test_utils;
3636
use crate::util::test_utils::{TestChainMonitor, TestScorer, TestKeysInterface};
3737
use crate::util::ser::{ReadableArgs, Writeable};
38+
use crate::offers::invoice::Bolt12Invoice;
3839

3940
use bitcoin::{Weight, WPubkeyHash};
4041
use bitcoin::amount::Amount;
@@ -3116,10 +3117,11 @@ pub fn claim_payment_along_route(args: ClaimAlongRouteArgs) {
31163117
}
31173118
}
31183119

3119-
pub fn claim_payment<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_route: &[&Node<'a, 'b, 'c>], our_payment_preimage: PaymentPreimage) {
3120+
pub fn claim_payment<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_route: &[&Node<'a, 'b, 'c>], our_payment_preimage: PaymentPreimage) -> Option<Bolt12Invoice> {
31203121
claim_payment_along_route(
31213122
ClaimAlongRouteArgs::new(origin_node, &[expected_route], our_payment_preimage)
31223123
);
3124+
None
31233125
}
31243126

31253127
pub const TEST_FINAL_CLTV: u32 = 70;

lightning/src/ln/offers_tests.rs

+14-12
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ fn route_bolt12_payment<'a, 'b, 'c>(
168168
}
169169

170170
fn claim_bolt12_payment<'a, 'b, 'c>(
171-
node: &Node<'a, 'b, 'c>, path: &[&Node<'a, 'b, 'c>], expected_payment_context: PaymentContext
171+
node: &Node<'a, 'b, 'c>, path: &[&Node<'a, 'b, 'c>], expected_payment_context: PaymentContext, invoice: &Bolt12Invoice
172172
) {
173173
let recipient = &path[path.len() - 1];
174174
let payment_purpose = match get_event!(recipient, Event::PaymentClaimable) {
@@ -188,7 +188,9 @@ fn claim_bolt12_payment<'a, 'b, 'c>(
188188
},
189189
_ => panic!("Unexpected payment purpose: {:?}", payment_purpose),
190190
}
191-
claim_payment(node, path, payment_preimage);
191+
if let Some(inv) = claim_payment(node, path, payment_preimage) {
192+
assert_eq!(inv, invoice.to_owned());
193+
};
192194
}
193195

194196
fn extract_offer_nonce<'a, 'b, 'c>(node: &Node<'a, 'b, 'c>, message: &OnionMessage) -> Nonce {
@@ -597,7 +599,7 @@ fn creates_and_pays_for_offer_using_two_hop_blinded_path() {
597599
route_bolt12_payment(david, &[charlie, bob, alice], &invoice);
598600
expect_recent_payment!(david, RecentPaymentDetails::Pending, payment_id);
599601

600-
claim_bolt12_payment(david, &[charlie, bob, alice], payment_context);
602+
claim_bolt12_payment(david, &[charlie, bob, alice], payment_context, &invoice);
601603
expect_recent_payment!(david, RecentPaymentDetails::Fulfilled, payment_id);
602604
}
603605

@@ -680,7 +682,7 @@ fn creates_and_pays_for_refund_using_two_hop_blinded_path() {
680682
route_bolt12_payment(david, &[charlie, bob, alice], &invoice);
681683
expect_recent_payment!(david, RecentPaymentDetails::Pending, payment_id);
682684

683-
claim_bolt12_payment(david, &[charlie, bob, alice], payment_context);
685+
claim_bolt12_payment(david, &[charlie, bob, alice], payment_context, &invoice);
684686
expect_recent_payment!(david, RecentPaymentDetails::Fulfilled, payment_id);
685687
}
686688

@@ -747,7 +749,7 @@ fn creates_and_pays_for_offer_using_one_hop_blinded_path() {
747749
route_bolt12_payment(bob, &[alice], &invoice);
748750
expect_recent_payment!(bob, RecentPaymentDetails::Pending, payment_id);
749751

750-
claim_bolt12_payment(bob, &[alice], payment_context);
752+
claim_bolt12_payment(bob, &[alice], payment_context, &invoice);
751753
expect_recent_payment!(bob, RecentPaymentDetails::Fulfilled, payment_id);
752754
}
753755

@@ -803,7 +805,7 @@ fn creates_and_pays_for_refund_using_one_hop_blinded_path() {
803805
route_bolt12_payment(bob, &[alice], &invoice);
804806
expect_recent_payment!(bob, RecentPaymentDetails::Pending, payment_id);
805807

806-
claim_bolt12_payment(bob, &[alice], payment_context);
808+
claim_bolt12_payment(bob, &[alice], payment_context, &invoice);
807809
expect_recent_payment!(bob, RecentPaymentDetails::Fulfilled, payment_id);
808810
}
809811

@@ -857,7 +859,7 @@ fn pays_for_offer_without_blinded_paths() {
857859
route_bolt12_payment(bob, &[alice], &invoice);
858860
expect_recent_payment!(bob, RecentPaymentDetails::Pending, payment_id);
859861

860-
claim_bolt12_payment(bob, &[alice], payment_context);
862+
claim_bolt12_payment(bob, &[alice], payment_context, &invoice);
861863
expect_recent_payment!(bob, RecentPaymentDetails::Fulfilled, payment_id);
862864
}
863865

@@ -900,7 +902,7 @@ fn pays_for_refund_without_blinded_paths() {
900902
route_bolt12_payment(bob, &[alice], &invoice);
901903
expect_recent_payment!(bob, RecentPaymentDetails::Pending, payment_id);
902904

903-
claim_bolt12_payment(bob, &[alice], payment_context);
905+
claim_bolt12_payment(bob, &[alice], payment_context, &invoice);
904906
expect_recent_payment!(bob, RecentPaymentDetails::Fulfilled, payment_id);
905907
}
906908

@@ -1138,7 +1140,7 @@ fn creates_and_pays_for_offer_with_retry() {
11381140
}
11391141
route_bolt12_payment(bob, &[alice], &invoice);
11401142
expect_recent_payment!(bob, RecentPaymentDetails::Pending, payment_id);
1141-
claim_bolt12_payment(bob, &[alice], payment_context);
1143+
claim_bolt12_payment(bob, &[alice], payment_context, &invoice);
11421144
expect_recent_payment!(bob, RecentPaymentDetails::Fulfilled, payment_id);
11431145
}
11441146

@@ -1209,7 +1211,7 @@ fn pays_bolt12_invoice_asynchronously() {
12091211
route_bolt12_payment(bob, &[alice], &invoice);
12101212
expect_recent_payment!(bob, RecentPaymentDetails::Pending, payment_id);
12111213

1212-
claim_bolt12_payment(bob, &[alice], payment_context);
1214+
claim_bolt12_payment(bob, &[alice], payment_context, &invoice);
12131215
expect_recent_payment!(bob, RecentPaymentDetails::Fulfilled, payment_id);
12141216

12151217
assert_eq!(
@@ -1289,7 +1291,7 @@ fn creates_offer_with_blinded_path_using_unannounced_introduction_node() {
12891291
route_bolt12_payment(bob, &[alice], &invoice);
12901292
expect_recent_payment!(bob, RecentPaymentDetails::Pending, payment_id);
12911293

1292-
claim_bolt12_payment(bob, &[alice], payment_context);
1294+
claim_bolt12_payment(bob, &[alice], payment_context, &invoice);
12931295
expect_recent_payment!(bob, RecentPaymentDetails::Fulfilled, payment_id);
12941296
}
12951297

@@ -2145,7 +2147,7 @@ fn fails_paying_invoice_more_than_once() {
21452147
assert!(david.node.get_and_clear_pending_msg_events().is_empty());
21462148

21472149
// Complete paying the first invoice
2148-
claim_bolt12_payment(david, &[charlie, bob, alice], payment_context);
2150+
claim_bolt12_payment(david, &[charlie, bob, alice], payment_context, &invoice1);
21492151
expect_recent_payment!(david, RecentPaymentDetails::Fulfilled, payment_id);
21502152
}
21512153

lightning/src/ln/outbound_payment.rs

+11-11
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ use bitcoin::secp256k1::{self, Secp256k1, SecretKey};
1515
use lightning_invoice::Bolt11Invoice;
1616

1717
use crate::blinded_path::{IntroductionNode, NodeIdLookUp};
18-
use crate::events::{self, PaymentFailureReason};
18+
use crate::events::{self, PaymentFailureReason, PaidInvoice};
19+
use crate::offers::static_invoice::StaticInvoice;
1920
use crate::types::payment::{PaymentHash, PaymentPreimage, PaymentSecret};
2021
use crate::ln::channel_state::ChannelDetails;
2122
use crate::ln::channelmanager::{EventCompletionAction, HTLCSource, PaymentId};
@@ -34,10 +35,7 @@ use crate::util::time::Instant;
3435
use crate::util::ser::ReadableArgs;
3536

3637
#[cfg(async_payments)]
37-
use {
38-
crate::offers::invoice::{DerivedSigningPubkey, InvoiceBuilder},
39-
crate::offers::static_invoice::StaticInvoice,
40-
};
38+
use crate::offers::invoice::{DerivedSigningPubkey, InvoiceBuilder};
4139

4240
use core::fmt::{self, Display, Formatter};
4341
use core::ops::Deref;
@@ -108,7 +106,7 @@ pub(crate) enum PendingOutboundPayment {
108106
invoice_request: Option<InvoiceRequest>,
109107
// Storing the bolt12 invoice here to allow Proof of Payment after
110108
// the payment is made.
111-
bolt12_invoice: Option<Bolt12Invoice>,
109+
bolt12_invoice: Option<PaidInvoice>,
112110
custom_tlvs: Vec<(u64, Vec<u8>)>,
113111
pending_amt_msat: u64,
114112
/// Used to track the fee paid. Present iff the payment was serialized on 0.0.103+.
@@ -158,7 +156,7 @@ impl_writeable_tlv_based!(RetryableInvoiceRequest, {
158156
});
159157

160158
impl PendingOutboundPayment {
161-
fn bolt12_invoice(&self) -> Option<&Bolt12Invoice> {
159+
fn bolt12_invoice(&self) -> Option<&PaidInvoice> {
162160
match self {
163161
PendingOutboundPayment::Retryable { bolt12_invoice, .. } => bolt12_invoice.as_ref(),
164162
_ => None,
@@ -967,8 +965,9 @@ impl OutboundPayments {
967965
if let Some(max_fee_msat) = params_config.max_total_routing_fee_msat {
968966
route_params.max_total_routing_fee_msat = Some(max_fee_msat);
969967
}
968+
let invoice = PaidInvoice::Bolt12Invoice(invoice.clone());
970969
self.send_payment_for_bolt12_invoice_internal(
971-
payment_id, payment_hash, None, None, Some(invoice), route_params, retry_strategy, router, first_hops,
970+
payment_id, payment_hash, None, None, Some(&invoice), route_params, retry_strategy, router, first_hops,
972971
inflight_htlcs, entropy_source, node_signer, node_id_lookup, secp_ctx, best_block_height,
973972
logger, pending_events, send_payment_along_path
974973
)
@@ -979,7 +978,7 @@ impl OutboundPayments {
979978
>(
980979
&self, payment_id: PaymentId, payment_hash: PaymentHash,
981980
keysend_preimage: Option<PaymentPreimage>, invoice_request: Option<&InvoiceRequest>,
982-
bolt12_invoice: Option<&Bolt12Invoice>,
981+
bolt12_invoice: Option<&PaidInvoice>,
983982
mut route_params: RouteParameters, retry_strategy: Retry, router: &R,
984983
first_hops: Vec<ChannelDetails>, inflight_htlcs: IH, entropy_source: &ES, node_signer: &NS,
985984
node_id_lookup: &NL, secp_ctx: &Secp256k1<secp256k1::All>, best_block_height: u32, logger: &L,
@@ -1726,7 +1725,7 @@ impl OutboundPayments {
17261725
&self, payment_hash: PaymentHash, recipient_onion: RecipientOnionFields, payment_id: PaymentId,
17271726
keysend_preimage: Option<PaymentPreimage>, route: &Route, retry_strategy: Option<Retry>,
17281727
payment_params: Option<PaymentParameters>, entropy_source: &ES, best_block_height: u32,
1729-
bolt12_invoice: Option<Bolt12Invoice>
1728+
bolt12_invoice: Option<PaidInvoice>
17301729
) -> Result<Vec<[u8; 32]>, PaymentSendFailure> where ES::Target: EntropySource {
17311730
let mut pending_outbounds = self.pending_outbound_payments.lock().unwrap();
17321731
match pending_outbounds.entry(payment_id) {
@@ -1745,7 +1744,7 @@ impl OutboundPayments {
17451744
fn create_pending_payment<ES: Deref>(
17461745
payment_hash: PaymentHash, recipient_onion: RecipientOnionFields,
17471746
keysend_preimage: Option<PaymentPreimage>, invoice_request: Option<InvoiceRequest>,
1748-
bolt12_invoice: Option<Bolt12Invoice>, route: &Route, retry_strategy: Option<Retry>,
1747+
bolt12_invoice: Option<PaidInvoice>, route: &Route, retry_strategy: Option<Retry>,
17491748
payment_params: Option<PaymentParameters>, entropy_source: &ES, best_block_height: u32
17501749
) -> (PendingOutboundPayment, Vec<[u8; 32]>)
17511750
where
@@ -2028,6 +2027,7 @@ impl OutboundPayments {
20282027
payment_hash,
20292028
amount_msat,
20302029
fee_paid_msat,
2030+
bolt12_invoice: payment.get().bolt12_invoice().cloned(),
20312031
}, Some(ev_completion_action.clone())));
20322032
payment.get_mut().mark_fulfilled();
20332033
}

lightning/src/offers/static_invoice.rs

+16-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ use crate::offers::offer::{
3333
};
3434
use crate::offers::parse::{Bolt12ParseError, Bolt12SemanticError, ParsedMessage};
3535
use crate::types::features::{Bolt12InvoiceFeatures, OfferFeatures};
36-
use crate::util::ser::{CursorReadable, Iterable, WithoutLength, Writeable, Writer};
36+
use crate::util::ser::{CursorReadable, Iterable, Readable, WithoutLength, Writeable, Writer};
3737
use crate::util::string::PrintableString;
3838
use bitcoin::address::Address;
3939
use bitcoin::constants::ChainHash;
@@ -70,6 +70,14 @@ pub struct StaticInvoice {
7070
signature: Signature,
7171
}
7272

73+
impl PartialEq for StaticInvoice {
74+
fn eq(&self, other: &Self) -> bool {
75+
self.bytes.eq(&other.bytes)
76+
}
77+
}
78+
79+
impl Eq for StaticInvoice {}
80+
7381
/// The contents of a [`StaticInvoice`] for responding to an [`Offer`].
7482
///
7583
/// [`Offer`]: crate::offers::offer::Offer
@@ -535,6 +543,13 @@ impl Writeable for StaticInvoice {
535543
}
536544
}
537545

546+
impl Readable for StaticInvoice {
547+
fn read<R: io::Read>(reader: &mut R) -> Result<Self, DecodeError> {
548+
let bytes: WithoutLength<Vec<u8>> = Readable::read(reader)?;
549+
Self::try_from(bytes.0).map_err(|_| DecodeError::InvalidValue)
550+
}
551+
}
552+
538553
impl TryFrom<Vec<u8>> for StaticInvoice {
539554
type Error = Bolt12ParseError;
540555

0 commit comments

Comments
 (0)