Skip to content

Commit 30f5bff

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 2354e2c commit 30f5bff

File tree

6 files changed

+69
-27
lines changed

6 files changed

+69
-27
lines changed

lightning/src/events/mod.rs

+20-1
Original file line numberDiff line numberDiff line change
@@ -1563,14 +1563,15 @@ impl Writeable for Event {
15631563
(13, payment_id, option),
15641564
});
15651565
},
1566-
&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 } => {
15671567
2u8.write(writer)?;
15681568
write_tlv_fields!(writer, {
15691569
(0, payment_preimage, required),
15701570
(1, payment_hash, required),
15711571
(3, payment_id, option),
15721572
(5, fee_paid_msat, option),
15731573
(7, amount_msat, option),
1574+
(9, bolt12_invoice, option),
15741575
});
15751576
},
15761577
&Event::PaymentPathFailed {
@@ -1905,12 +1906,14 @@ impl MaybeReadable for Event {
19051906
let mut payment_id = None;
19061907
let mut amount_msat = None;
19071908
let mut fee_paid_msat = None;
1909+
let mut bolt12_invoice = None;
19081910
read_tlv_fields!(reader, {
19091911
(0, payment_preimage, required),
19101912
(1, payment_hash, option),
19111913
(3, payment_id, option),
19121914
(5, fee_paid_msat, option),
19131915
(7, amount_msat, option),
1916+
(9, bolt12_invoice, option),
19141917
});
19151918
if payment_hash.is_none() {
19161919
payment_hash = Some(PaymentHash(Sha256::hash(&payment_preimage.0[..]).to_byte_array()));
@@ -1921,6 +1924,7 @@ impl MaybeReadable for Event {
19211924
payment_hash: payment_hash.unwrap(),
19221925
amount_msat,
19231926
fee_paid_msat,
1927+
bolt12_invoice,
19241928
}))
19251929
};
19261930
f()
@@ -2445,3 +2449,18 @@ impl<T: EventHandler> EventHandler for Arc<T> {
24452449
self.deref().handle_event(event)
24462450
}
24472451
}
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+
/// Static invoice
2460+
StaticInvoice(crate::offers::static_invoice::StaticInvoice),
2461+
}
2462+
2463+
impl_writeable_tlv_based_enum!(PaidInvoice,
2464+
{0, Bolt12Invoice} => (),
2465+
{2, StaticInvoice} => (),
2466+
);

lightning/src/ln/functional_test_utils.rs

+1
Original file line numberDiff line numberDiff line change
@@ -2307,6 +2307,7 @@ pub fn expect_payment_sent<CM: AChannelManager, H: NodeHolder<CM=CM>>(node: &H,
23072307
check_added_monitors(node, 1);
23082308
}
23092309
// We return the invoice because some test may want to check the invoice details.
2310+
#[allow(unused_assignments)]
23102311
let mut invoice = None;
23112312
let expected_payment_id = match events[0] {
23122313
Event::PaymentSent { ref payment_id, ref payment_preimage, ref payment_hash, ref amount_msat, ref fee_paid_msat, ref bolt12_invoice } => {

lightning/src/ln/offers_tests.rs

+11-11
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) {
@@ -601,7 +601,7 @@ fn creates_and_pays_for_offer_using_two_hop_blinded_path() {
601601
route_bolt12_payment(david, &[charlie, bob, alice], &invoice);
602602
expect_recent_payment!(david, RecentPaymentDetails::Pending, payment_id);
603603

604-
claim_bolt12_payment(david, &[charlie, bob, alice], payment_context);
604+
claim_bolt12_payment(david, &[charlie, bob, alice], payment_context, &invoice);
605605
expect_recent_payment!(david, RecentPaymentDetails::Fulfilled, payment_id);
606606
}
607607

@@ -684,7 +684,7 @@ fn creates_and_pays_for_refund_using_two_hop_blinded_path() {
684684
route_bolt12_payment(david, &[charlie, bob, alice], &invoice);
685685
expect_recent_payment!(david, RecentPaymentDetails::Pending, payment_id);
686686

687-
claim_bolt12_payment(david, &[charlie, bob, alice], payment_context);
687+
claim_bolt12_payment(david, &[charlie, bob, alice], payment_context, &invoice);
688688
expect_recent_payment!(david, RecentPaymentDetails::Fulfilled, payment_id);
689689
}
690690

@@ -751,7 +751,7 @@ fn creates_and_pays_for_offer_using_one_hop_blinded_path() {
751751
route_bolt12_payment(bob, &[alice], &invoice);
752752
expect_recent_payment!(bob, RecentPaymentDetails::Pending, payment_id);
753753

754-
claim_bolt12_payment(bob, &[alice], payment_context);
754+
claim_bolt12_payment(bob, &[alice], payment_context, &invoice);
755755
expect_recent_payment!(bob, RecentPaymentDetails::Fulfilled, payment_id);
756756
}
757757

@@ -807,7 +807,7 @@ fn creates_and_pays_for_refund_using_one_hop_blinded_path() {
807807
route_bolt12_payment(bob, &[alice], &invoice);
808808
expect_recent_payment!(bob, RecentPaymentDetails::Pending, payment_id);
809809

810-
claim_bolt12_payment(bob, &[alice], payment_context);
810+
claim_bolt12_payment(bob, &[alice], payment_context, &invoice);
811811
expect_recent_payment!(bob, RecentPaymentDetails::Fulfilled, payment_id);
812812
}
813813

@@ -861,7 +861,7 @@ fn pays_for_offer_without_blinded_paths() {
861861
route_bolt12_payment(bob, &[alice], &invoice);
862862
expect_recent_payment!(bob, RecentPaymentDetails::Pending, payment_id);
863863

864-
claim_bolt12_payment(bob, &[alice], payment_context);
864+
claim_bolt12_payment(bob, &[alice], payment_context, &invoice);
865865
expect_recent_payment!(bob, RecentPaymentDetails::Fulfilled, payment_id);
866866
}
867867

@@ -904,7 +904,7 @@ fn pays_for_refund_without_blinded_paths() {
904904
route_bolt12_payment(bob, &[alice], &invoice);
905905
expect_recent_payment!(bob, RecentPaymentDetails::Pending, payment_id);
906906

907-
claim_bolt12_payment(bob, &[alice], payment_context);
907+
claim_bolt12_payment(bob, &[alice], payment_context, &invoice);
908908
expect_recent_payment!(bob, RecentPaymentDetails::Fulfilled, payment_id);
909909
}
910910

@@ -1142,7 +1142,7 @@ fn creates_and_pays_for_offer_with_retry() {
11421142
}
11431143
route_bolt12_payment(bob, &[alice], &invoice);
11441144
expect_recent_payment!(bob, RecentPaymentDetails::Pending, payment_id);
1145-
claim_bolt12_payment(bob, &[alice], payment_context);
1145+
claim_bolt12_payment(bob, &[alice], payment_context, &invoice);
11461146
expect_recent_payment!(bob, RecentPaymentDetails::Fulfilled, payment_id);
11471147
}
11481148

@@ -1213,7 +1213,7 @@ fn pays_bolt12_invoice_asynchronously() {
12131213
route_bolt12_payment(bob, &[alice], &invoice);
12141214
expect_recent_payment!(bob, RecentPaymentDetails::Pending, payment_id);
12151215

1216-
claim_bolt12_payment(bob, &[alice], payment_context);
1216+
claim_bolt12_payment(bob, &[alice], payment_context, &invoice);
12171217
expect_recent_payment!(bob, RecentPaymentDetails::Fulfilled, payment_id);
12181218

12191219
assert_eq!(
@@ -1293,7 +1293,7 @@ fn creates_offer_with_blinded_path_using_unannounced_introduction_node() {
12931293
route_bolt12_payment(bob, &[alice], &invoice);
12941294
expect_recent_payment!(bob, RecentPaymentDetails::Pending, payment_id);
12951295

1296-
claim_bolt12_payment(bob, &[alice], payment_context);
1296+
claim_bolt12_payment(bob, &[alice], payment_context, &invoice);
12971297
expect_recent_payment!(bob, RecentPaymentDetails::Fulfilled, payment_id);
12981298
}
12991299

@@ -2149,7 +2149,7 @@ fn fails_paying_invoice_more_than_once() {
21492149
assert!(david.node.get_and_clear_pending_msg_events().is_empty());
21502150

21512151
// Complete paying the first invoice
2152-
claim_bolt12_payment(david, &[charlie, bob, alice], payment_context);
2152+
claim_bolt12_payment(david, &[charlie, bob, alice], payment_context, &invoice1);
21532153
expect_recent_payment!(david, RecentPaymentDetails::Fulfilled, payment_id);
21542154
}
21552155

lightning/src/ln/outbound_payment.rs

+14-14
Original file line numberDiff line numberDiff line change
@@ -15,29 +15,27 @@ 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};
19-
use crate::types::payment::{PaymentHash, PaymentPreimage, PaymentSecret};
18+
use crate::events::{self, PaidInvoice, PaymentFailureReason};
2019
use crate::ln::channel_state::ChannelDetails;
2120
use crate::ln::channelmanager::{EventCompletionAction, HTLCSource, PaymentId};
22-
use crate::types::features::Bolt12InvoiceFeatures;
2321
use crate::ln::onion_utils;
2422
use crate::ln::onion_utils::{DecodedOnionFailure, HTLCFailReason};
2523
use crate::offers::invoice::Bolt12Invoice;
2624
use crate::offers::invoice_request::InvoiceRequest;
2725
use crate::offers::nonce::Nonce;
28-
use crate::routing::router::{BlindedTail, InFlightHtlcs, RouteParametersConfig, Path, PaymentParameters, Route, RouteParameters, Router};
26+
use crate::offers::static_invoice::StaticInvoice;
27+
use crate::routing::router::{BlindedTail, InFlightHtlcs, Path, PaymentParameters, Route, RouteParameters, RouteParametersConfig, Router};
2928
use crate::sign::{EntropySource, NodeSigner, Recipient};
29+
use crate::types::features::Bolt12InvoiceFeatures;
30+
use crate::types::payment::{PaymentHash, PaymentPreimage, PaymentSecret};
3031
use crate::util::errors::APIError;
3132
use crate::util::logger::Logger;
3233
#[cfg(feature = "std")]
3334
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 BOLT 12 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

+17-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,9 @@ 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::{
37+
CursorReadable, Iterable, LengthLimitedRead, LengthReadable, WithoutLength, Writeable, Writer,
38+
};
3739
use crate::util::string::PrintableString;
3840
use bitcoin::address::Address;
3941
use bitcoin::constants::ChainHash;
@@ -70,6 +72,14 @@ pub struct StaticInvoice {
7072
signature: Signature,
7173
}
7274

75+
impl PartialEq for StaticInvoice {
76+
fn eq(&self, other: &Self) -> bool {
77+
self.bytes.eq(&other.bytes)
78+
}
79+
}
80+
81+
impl Eq for StaticInvoice {}
82+
7383
/// The contents of a [`StaticInvoice`] for responding to an [`Offer`].
7484
///
7585
/// [`Offer`]: crate::offers::offer::Offer
@@ -534,6 +544,12 @@ impl Writeable for StaticInvoice {
534544
WithoutLength(&self.bytes).write(writer)
535545
}
536546
}
547+
impl LengthReadable for StaticInvoice {
548+
fn read_from_fixed_length_buffer<R: LengthLimitedRead>(r: &mut R) -> Result<Self, DecodeError> {
549+
let bytes: WithoutLength<Vec<u8>> = LengthReadable::read_from_fixed_length_buffer(r)?;
550+
Self::try_from(bytes.0).map_err(|_| DecodeError::InvalidValue)
551+
}
552+
}
537553

538554
impl TryFrom<Vec<u8>> for StaticInvoice {
539555
type Error = Bolt12ParseError;

lightning/src/offers/test_utils.rs

+6
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,10 @@
1212
use bitcoin::secp256k1::schnorr::Signature;
1313
use bitcoin::secp256k1::{Keypair, PublicKey, Secp256k1, SecretKey};
1414

15+
use crate::blinded_path::message::BlindedMessagePath;
1516
use crate::blinded_path::payment::{BlindedPayInfo, BlindedPaymentPath};
1617
use crate::blinded_path::BlindedHop;
18+
use crate::ln::inbound_payment::ExpandedKey;
1719
use crate::offers::merkle::TaggedHash;
1820
use crate::sign::EntropySource;
1921
use crate::types::features::BlindedHopFeatures;
@@ -23,6 +25,10 @@ use core::time::Duration;
2325
#[allow(unused_imports)]
2426
use crate::prelude::*;
2527

28+
use super::nonce::Nonce;
29+
use super::offer::OfferBuilder;
30+
use super::static_invoice::{StaticInvoice, StaticInvoiceBuilder};
31+
2632
pub(crate) fn fail_sign<T: AsRef<TaggedHash>>(_message: &T) -> Result<Signature, ()> {
2733
Err(())
2834
}

0 commit comments

Comments
 (0)