Skip to content

Commit 54ca54d

Browse files
committed
BOLT 12 variants of PaymentPurpose
In order to provide more context in PaymentClaimable and PaymentClaimed events, introduce new variants of PaymentPurpose for use with BOLT 12 payments. Separate variants are used for offers and refunds. An unknown variant is used for backwards compatibility and ease of testing, but is otherwise not publicly constructable.
1 parent ef3ec8f commit 54ca54d

File tree

4 files changed

+150
-17
lines changed

4 files changed

+150
-17
lines changed

lightning/src/blinded_path/payment.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,12 @@ pub enum PaymentContext {
121121
Bolt12Refund(Bolt12RefundContext),
122122
}
123123

124+
// Used when writing PaymentContext in Event::PaymentClaimable to avoid cloning.
125+
pub(crate) enum PaymentContextRef<'a> {
126+
Bolt12Offer(&'a Bolt12OfferContext),
127+
Bolt12Refund(&'a Bolt12RefundContext),
128+
}
129+
124130
/// An unknown payment context.
125131
#[derive(Clone, Debug, Eq, PartialEq)]
126132
pub struct UnknownPaymentContext(());
@@ -372,6 +378,23 @@ impl_writeable_tlv_based_enum!(PaymentContext,
372378
(2, Bolt12Refund),
373379
);
374380

381+
impl<'a> Writeable for PaymentContextRef<'a> {
382+
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
383+
match self {
384+
PaymentContextRef::Bolt12Offer(context) => {
385+
1u8.write(w)?;
386+
context.write(w)?;
387+
},
388+
PaymentContextRef::Bolt12Refund(context) => {
389+
2u8.write(w)?;
390+
context.write(w)?;
391+
},
392+
}
393+
394+
Ok(())
395+
}
396+
}
397+
375398
impl Writeable for UnknownPaymentContext {
376399
fn write<W: Writer>(&self, _w: &mut W) -> Result<(), io::Error> {
377400
Ok(())

lightning/src/events/mod.rs

Lines changed: 96 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ pub mod bump_transaction;
1818

1919
pub use bump_transaction::BumpTransactionEvent;
2020

21+
use crate::blinded_path::payment::{Bolt12OfferContext, Bolt12RefundContext, PaymentContext, PaymentContextRef};
2122
use crate::sign::SpendableOutputDescriptor;
2223
use crate::ln::channelmanager::{InterceptId, PaymentId, RecipientOnionFields};
2324
use crate::ln::channel::FUNDING_CONF_DEADLINE_BLOCKS;
@@ -70,6 +71,46 @@ pub enum PaymentPurpose {
7071
/// [`ChannelManager::create_inbound_payment_for_hash`]: crate::ln::channelmanager::ChannelManager::create_inbound_payment_for_hash
7172
payment_secret: PaymentSecret,
7273
},
74+
/// A payment for a BOLT 12 [`Offer`].
75+
///
76+
/// [`Offer`]: crate::offers::offer::Offer
77+
Bolt12OfferPayment {
78+
/// The preimage to the payment hash. If provided, this can be handed directly to
79+
/// [`ChannelManager::claim_funds`].
80+
///
81+
/// [`ChannelManager::claim_funds`]: crate::ln::channelmanager::ChannelManager::claim_funds
82+
payment_preimage: Option<PaymentPreimage>,
83+
/// The secret used to authenticate the sender to the recipient, preventing a number of
84+
/// de-anonymization attacks while routing a payment.
85+
///
86+
/// See [`PaymentPurpose::Bolt11InvoicePayment::payment_secret`] for further details.
87+
payment_secret: PaymentSecret,
88+
/// The context of the payment such as information about the corresponding [`Offer`] and
89+
/// [`InvoiceRequest`].
90+
///
91+
/// [`Offer`]: crate::offers::offer::Offer
92+
/// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
93+
payment_context: Bolt12OfferContext,
94+
},
95+
/// A payment for a BOLT 12 [`Refund`].
96+
///
97+
/// [`Refund`]: crate::offers::refund::Refund
98+
Bolt12RefundPayment {
99+
/// The preimage to the payment hash. If provided, this can be handed directly to
100+
/// [`ChannelManager::claim_funds`].
101+
///
102+
/// [`ChannelManager::claim_funds`]: crate::ln::channelmanager::ChannelManager::claim_funds
103+
payment_preimage: Option<PaymentPreimage>,
104+
/// The secret used to authenticate the sender to the recipient, preventing a number of
105+
/// de-anonymization attacks while routing a payment.
106+
///
107+
/// See [`PaymentPurpose::Bolt11InvoicePayment::payment_secret`] for further details.
108+
payment_secret: PaymentSecret,
109+
/// The context of the payment such as information about the corresponding [`Refund`].
110+
///
111+
/// [`Refund`]: crate::offers::refund::Refund
112+
payment_context: Bolt12RefundContext,
113+
},
73114
/// Because this is a spontaneous payment, the payer generated their own preimage rather than us
74115
/// (the payee) providing a preimage.
75116
SpontaneousPayment(PaymentPreimage),
@@ -80,13 +121,17 @@ impl PaymentPurpose {
80121
pub fn preimage(&self) -> Option<PaymentPreimage> {
81122
match self {
82123
PaymentPurpose::Bolt11InvoicePayment { payment_preimage, .. } => *payment_preimage,
124+
PaymentPurpose::Bolt12OfferPayment { payment_preimage, .. } => *payment_preimage,
125+
PaymentPurpose::Bolt12RefundPayment { payment_preimage, .. } => *payment_preimage,
83126
PaymentPurpose::SpontaneousPayment(preimage) => Some(*preimage),
84127
}
85128
}
86129

87130
pub(crate) fn is_keysend(&self) -> bool {
88131
match self {
89132
PaymentPurpose::Bolt11InvoicePayment { .. } => false,
133+
PaymentPurpose::Bolt12OfferPayment { .. } => false,
134+
PaymentPurpose::Bolt12RefundPayment { .. } => false,
90135
PaymentPurpose::SpontaneousPayment(..) => true,
91136
}
92137
}
@@ -96,7 +141,18 @@ impl_writeable_tlv_based_enum!(PaymentPurpose,
96141
(0, Bolt11InvoicePayment) => {
97142
(0, payment_preimage, option),
98143
(2, payment_secret, required),
99-
};
144+
},
145+
(4, Bolt12OfferPayment) => {
146+
(0, payment_preimage, option),
147+
(2, payment_secret, required),
148+
(4, payment_context, required),
149+
},
150+
(6, Bolt12RefundPayment) => {
151+
(0, payment_preimage, option),
152+
(2, payment_secret, required),
153+
(4, payment_context, required),
154+
},
155+
;
100156
(2, SpontaneousPayment)
101157
);
102158

@@ -1065,13 +1121,28 @@ impl Writeable for Event {
10651121
1u8.write(writer)?;
10661122
let mut payment_secret = None;
10671123
let payment_preimage;
1124+
let mut payment_context = None;
10681125
match &purpose {
10691126
PaymentPurpose::Bolt11InvoicePayment {
10701127
payment_preimage: preimage, payment_secret: secret
10711128
} => {
10721129
payment_secret = Some(secret);
10731130
payment_preimage = *preimage;
10741131
},
1132+
PaymentPurpose::Bolt12OfferPayment {
1133+
payment_preimage: preimage, payment_secret: secret, payment_context: context
1134+
} => {
1135+
payment_secret = Some(secret);
1136+
payment_preimage = *preimage;
1137+
payment_context = Some(PaymentContextRef::Bolt12Offer(context));
1138+
},
1139+
PaymentPurpose::Bolt12RefundPayment {
1140+
payment_preimage: preimage, payment_secret: secret, payment_context: context
1141+
} => {
1142+
payment_secret = Some(secret);
1143+
payment_preimage = *preimage;
1144+
payment_context = Some(PaymentContextRef::Bolt12Refund(context));
1145+
},
10751146
PaymentPurpose::SpontaneousPayment(preimage) => {
10761147
payment_preimage = Some(*preimage);
10771148
}
@@ -1090,6 +1161,7 @@ impl Writeable for Event {
10901161
(8, payment_preimage, option),
10911162
(9, onion_fields, option),
10921163
(10, skimmed_fee_opt, option),
1164+
(11, payment_context, option),
10931165
});
10941166
},
10951167
&Event::PaymentSent { ref payment_id, ref payment_preimage, ref payment_hash, ref fee_paid_msat } => {
@@ -1320,6 +1392,7 @@ impl MaybeReadable for Event {
13201392
let mut claim_deadline = None;
13211393
let mut via_user_channel_id = None;
13221394
let mut onion_fields = None;
1395+
let mut payment_context = None;
13231396
read_tlv_fields!(reader, {
13241397
(0, payment_hash, required),
13251398
(1, receiver_node_id, option),
@@ -1332,11 +1405,30 @@ impl MaybeReadable for Event {
13321405
(8, payment_preimage, option),
13331406
(9, onion_fields, option),
13341407
(10, counterparty_skimmed_fee_msat_opt, option),
1408+
(11, payment_context, option),
13351409
});
13361410
let purpose = match payment_secret {
1337-
Some(secret) => PaymentPurpose::Bolt11InvoicePayment {
1338-
payment_preimage,
1339-
payment_secret: secret
1411+
Some(secret) => match payment_context {
1412+
Some(PaymentContext::Unknown(_)) | None => {
1413+
PaymentPurpose::Bolt11InvoicePayment {
1414+
payment_preimage,
1415+
payment_secret: secret,
1416+
}
1417+
},
1418+
Some(PaymentContext::Bolt12Offer(context)) => {
1419+
PaymentPurpose::Bolt12OfferPayment {
1420+
payment_preimage,
1421+
payment_secret: secret,
1422+
payment_context: context,
1423+
}
1424+
},
1425+
Some(PaymentContext::Bolt12Refund(context)) => {
1426+
PaymentPurpose::Bolt12RefundPayment {
1427+
payment_preimage,
1428+
payment_secret: secret,
1429+
payment_context: context,
1430+
}
1431+
},
13401432
},
13411433
None if payment_preimage.is_some() => PaymentPurpose::SpontaneousPayment(payment_preimage.unwrap()),
13421434
None => return Err(msgs::DecodeError::InvalidValue),

lightning/src/ln/channelmanager.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1477,6 +1477,8 @@ where
14771477
/// println!("Claiming spontaneous payment {}", payment_hash);
14781478
/// channel_manager.claim_funds(payment_preimage);
14791479
/// },
1480+
/// // ...
1481+
/// # _ => {},
14801482
/// },
14811483
/// Event::PaymentClaimed { payment_hash, amount_msat, .. } => {
14821484
/// assert_eq!(payment_hash, known_payment_hash);
@@ -8877,10 +8879,9 @@ where
88778879
/// This differs from [`create_inbound_payment_for_hash`] only in that it generates the
88788880
/// [`PaymentHash`] and [`PaymentPreimage`] for you.
88798881
///
8880-
/// The [`PaymentPreimage`] will ultimately be returned to you in the [`PaymentClaimable`], which
8881-
/// will have the [`PaymentClaimable::purpose`] be [`PaymentPurpose::Bolt11InvoicePayment`] with
8882-
/// its [`PaymentPurpose::Bolt11InvoicePayment::payment_preimage`] field filled in. That should
8883-
/// then be passed directly to [`claim_funds`].
8882+
/// The [`PaymentPreimage`] will ultimately be returned to you in the [`PaymentClaimable`] event, which
8883+
/// will have the [`PaymentClaimable::purpose`] return `Some` for [`PaymentPurpose::preimage`]. That
8884+
/// should then be passed directly to [`claim_funds`].
88848885
///
88858886
/// See [`create_inbound_payment_for_hash`] for detailed documentation on behavior and requirements.
88868887
///
@@ -8900,8 +8901,7 @@ where
89008901
/// [`claim_funds`]: Self::claim_funds
89018902
/// [`PaymentClaimable`]: events::Event::PaymentClaimable
89028903
/// [`PaymentClaimable::purpose`]: events::Event::PaymentClaimable::purpose
8903-
/// [`PaymentPurpose::Bolt11InvoicePayment`]: events::PaymentPurpose::Bolt11InvoicePayment
8904-
/// [`PaymentPurpose::Bolt11InvoicePayment::payment_preimage`]: events::PaymentPurpose::Bolt11InvoicePayment::payment_preimage
8904+
/// [`PaymentPurpose::preimage`]: events::PaymentPurpose::preimage
89058905
/// [`create_inbound_payment_for_hash`]: Self::create_inbound_payment_for_hash
89068906
pub fn create_inbound_payment(&self, min_value_msat: Option<u64>, invoice_expiry_delta_secs: u32,
89078907
min_final_cltv_expiry_delta: Option<u16>) -> Result<(PaymentHash, PaymentSecret), ()> {

lightning/src/ln/functional_test_utils.rs

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2133,6 +2133,14 @@ pub fn check_payment_claimable(
21332133
assert_eq!(&expected_payment_preimage, payment_preimage);
21342134
assert_eq!(expected_payment_secret, *payment_secret);
21352135
},
2136+
PaymentPurpose::Bolt12OfferPayment { payment_preimage, payment_secret, .. } => {
2137+
assert_eq!(&expected_payment_preimage, payment_preimage);
2138+
assert_eq!(expected_payment_secret, *payment_secret);
2139+
},
2140+
PaymentPurpose::Bolt12RefundPayment { payment_preimage, payment_secret, .. } => {
2141+
assert_eq!(&expected_payment_preimage, payment_preimage);
2142+
assert_eq!(expected_payment_secret, *payment_secret);
2143+
},
21362144
_ => {},
21372145
}
21382146
},
@@ -2611,6 +2619,16 @@ pub fn do_pass_along_path<'a, 'b, 'c>(args: PassAlongPathArgs) -> Option<Event>
26112619
assert_eq!(our_payment_secret.unwrap(), *payment_secret);
26122620
assert_eq!(Some(*payment_secret), onion_fields.as_ref().unwrap().payment_secret);
26132621
},
2622+
PaymentPurpose::Bolt12OfferPayment { payment_preimage, payment_secret, .. } => {
2623+
assert_eq!(expected_preimage, *payment_preimage);
2624+
assert_eq!(our_payment_secret.unwrap(), *payment_secret);
2625+
assert_eq!(Some(*payment_secret), onion_fields.as_ref().unwrap().payment_secret);
2626+
},
2627+
PaymentPurpose::Bolt12RefundPayment { payment_preimage, payment_secret, .. } => {
2628+
assert_eq!(expected_preimage, *payment_preimage);
2629+
assert_eq!(our_payment_secret.unwrap(), *payment_secret);
2630+
assert_eq!(Some(*payment_secret), onion_fields.as_ref().unwrap().payment_secret);
2631+
},
26142632
PaymentPurpose::SpontaneousPayment(payment_preimage) => {
26152633
assert_eq!(expected_preimage.unwrap(), *payment_preimage);
26162634
assert_eq!(our_payment_secret, onion_fields.as_ref().unwrap().payment_secret);
@@ -2763,14 +2781,12 @@ pub fn pass_claimed_payment_along_route<'a, 'b, 'c, 'd>(args: ClaimAlongRouteArg
27632781
let mut fwd_amt_msat = 0;
27642782
match claim_event[0] {
27652783
Event::PaymentClaimed {
2766-
purpose: PaymentPurpose::SpontaneousPayment(preimage),
2784+
purpose: PaymentPurpose::SpontaneousPayment(preimage)
2785+
| PaymentPurpose::Bolt11InvoicePayment { payment_preimage: Some(preimage), .. }
2786+
| PaymentPurpose::Bolt12OfferPayment { payment_preimage: Some(preimage), .. }
2787+
| PaymentPurpose::Bolt12RefundPayment { payment_preimage: Some(preimage), .. },
27672788
amount_msat,
27682789
ref htlcs,
2769-
.. }
2770-
| Event::PaymentClaimed {
2771-
purpose: PaymentPurpose::Bolt11InvoicePayment { payment_preimage: Some(preimage), ..},
2772-
ref htlcs,
2773-
amount_msat,
27742790
..
27752791
} => {
27762792
assert_eq!(preimage, our_payment_preimage);
@@ -2780,7 +2796,9 @@ pub fn pass_claimed_payment_along_route<'a, 'b, 'c, 'd>(args: ClaimAlongRouteArg
27802796
fwd_amt_msat = amount_msat;
27812797
},
27822798
Event::PaymentClaimed {
2783-
purpose: PaymentPurpose::Bolt11InvoicePayment { .. },
2799+
purpose: PaymentPurpose::Bolt11InvoicePayment { .. }
2800+
| PaymentPurpose::Bolt12OfferPayment { .. }
2801+
| PaymentPurpose::Bolt12RefundPayment { .. },
27842802
payment_hash,
27852803
amount_msat,
27862804
ref htlcs,

0 commit comments

Comments
 (0)