Skip to content

Commit 5acf03f

Browse files
committed
Add a payment_metadata map in blinded payment path contexts
Similar to how BOLT 11 payments can use a `payment_metadata` to provide arbitrary bytes in the invoice to be communicated back to them when receiving, its useful to be able to provide some bytes which are communicated back upon receiving a payment. Here we do so in the BOLT 12 blinded path contexts, offering a `BTreeMap<u64, Vec<u8>>` instead to enable more easily including multiple sets of data. Also note that a `Router` building a blinded path is allowed to modify the `payment_metadata` without breaking the payment. Tests by claude
1 parent 05135ae commit 5acf03f

12 files changed

Lines changed: 332 additions & 32 deletions

File tree

fuzz/src/invoice_request_deser.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ fn build_response<T: secp256k1::Signing + secp256k1::Verification>(
104104
let payment_context = PaymentContext::Bolt12Offer(Bolt12OfferContext {
105105
offer_id: OfferId([42; 32]),
106106
invoice_request: invoice_request_fields,
107+
payment_metadata: None,
107108
});
108109
let payee_tlvs = ReceiveTlvs {
109110
payment_secret: PaymentSecret([42; 32]),

fuzz/src/refund_deser.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ fn build_response<T: secp256k1::Signing + secp256k1::Verification>(
6969
) -> Result<UnsignedBolt12Invoice, Bolt12SemanticError> {
7070
let entropy_source = Randomness {};
7171
let receive_auth_key = ReceiveAuthKey([41; 32]);
72-
let payment_context = PaymentContext::Bolt12Refund(Bolt12RefundContext {});
72+
let payment_context = PaymentContext::Bolt12Refund(Bolt12RefundContext { payment_metadata: None });
7373
let payee_tlvs = ReceiveTlvs {
7474
payment_secret: PaymentSecret([42; 32]),
7575
payment_constraints: PaymentConstraints {

lightning/src/blinded_path/payment.rs

Lines changed: 74 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99

1010
//! Data structures and methods for constructing [`BlindedPaymentPath`]s to send a payment over.
1111
12+
use alloc::collections::BTreeMap;
13+
1214
use bitcoin::secp256k1::ecdh::SharedSecret;
1315
use bitcoin::secp256k1::{self, PublicKey, Secp256k1, SecretKey};
1416

@@ -29,8 +31,8 @@ use crate::types::features::BlindedHopFeatures;
2931
use crate::types::payment::PaymentSecret;
3032
use crate::types::routing::RoutingFees;
3133
use crate::util::ser::{
32-
FixedLengthReader, HighZeroBytesDroppedBigSize, LengthReadableArgs, Readable, WithoutLength,
33-
Writeable, Writer,
34+
BigSizeKeyedMap, FixedLengthReader, HighZeroBytesDroppedBigSize, LengthReadableArgs, Readable,
35+
WithoutLength, Writeable, Writer,
3436
};
3537

3638
#[allow(unused_imports)]
@@ -572,6 +574,17 @@ pub enum PaymentContext {
572574
/// [`Refund`]: crate::offers::refund::Refund
573575
Bolt12Refund(Bolt12RefundContext),
574576
}
577+
impl PaymentContext {
578+
/// Returns the additional payment metadata stored alongside this payment context, if any.
579+
pub fn payment_metadata(&self) -> Option<&BTreeMap<u64, Vec<u8>>> {
580+
match self {
581+
Self::Bolt12Offer(Bolt12OfferContext { payment_metadata, .. })
582+
| Self::AsyncBolt12Offer(AsyncBolt12OfferContext { payment_metadata, .. })
583+
| Self::Bolt12Refund(Bolt12RefundContext { payment_metadata, .. })
584+
=> payment_metadata.as_ref(),
585+
}
586+
}
587+
}
575588

576589
// Used when writing PaymentContext in Event::PaymentClaimable to avoid cloning.
577590
pub(crate) enum PaymentContextRef<'a> {
@@ -594,6 +607,22 @@ pub struct Bolt12OfferContext {
594607
/// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
595608
/// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice
596609
pub invoice_request: InvoiceRequestFields,
610+
611+
/// Additional data about this payment which is not used in LDK and can be used for any
612+
/// purpose.
613+
///
614+
/// This is analogous to the BOLT 11 [`RecipientOnionFields::payment_metadata`] (which is
615+
/// provided to payers via [`Bolt11Invoice::payment_metadata`]) and can be used any time data
616+
/// needs to be "stored" by a payment recipient for their own internal use, provided back to
617+
/// them with the payment.
618+
///
619+
/// Note that because this is included in the payment onion, its size must be tightly
620+
/// constrained. More than a few hundred bytes and the payment will be entirely unpayable (with
621+
/// limited routing options as size increases).
622+
///
623+
/// [`RecipientOnionFields::payment_metadata`]: crate::ln::outbound_payment::RecipientOnionFields::payment_metadata
624+
/// [`Bolt11Invoice::payment_metadata`]: lightning_invoice::Bolt11Invoice::payment_metadata
625+
pub payment_metadata: Option<BTreeMap<u64, Vec<u8>>>,
597626
}
598627

599628
/// The context of a payment made for a static invoice requested from a BOLT 12 [`Offer`].
@@ -606,13 +635,45 @@ pub struct AsyncBolt12OfferContext {
606635
///
607636
/// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
608637
pub offer_nonce: Nonce,
638+
639+
/// Additional data about this payment which is not used in LDK and can be used for any
640+
/// purpose.
641+
///
642+
/// This is analogous to the BOLT 11 [`RecipientOnionFields::payment_metadata`] (which is
643+
/// provided to payers via [`Bolt11Invoice::payment_metadata`]) and can be used any time data
644+
/// needs to be "stored" by a payment recipient for their own internal use, provided back to
645+
/// them with the payment.
646+
///
647+
/// Note that because this is included in the payment onion, its size must be tightly
648+
/// constrained. More than a few hundred bytes and the payment will be entirely unpayable (with
649+
/// limited routing options as size increases).
650+
///
651+
/// [`RecipientOnionFields::payment_metadata`]: crate::ln::outbound_payment::RecipientOnionFields::payment_metadata
652+
/// [`Bolt11Invoice::payment_metadata`]: lightning_invoice::Bolt11Invoice::payment_metadata
653+
pub payment_metadata: Option<BTreeMap<u64, Vec<u8>>>,
609654
}
610655

611656
/// The context of a payment made for an invoice sent for a BOLT 12 [`Refund`].
612657
///
613658
/// [`Refund`]: crate::offers::refund::Refund
614659
#[derive(Clone, Debug, Eq, PartialEq)]
615-
pub struct Bolt12RefundContext {}
660+
pub struct Bolt12RefundContext {
661+
/// Additional data about this payment which is not used in LDK and can be used for any
662+
/// purpose.
663+
///
664+
/// This is analogous to the BOLT 11 [`RecipientOnionFields::payment_metadata`] (which is
665+
/// provided to payers via [`Bolt11Invoice::payment_metadata`]) and can be used any time data
666+
/// needs to be "stored" by a payment recipient for their own internal use, provided back to
667+
/// them with the payment.
668+
///
669+
/// Note that because this is included in the payment onion, its size must be tightly
670+
/// constrained. More than a few hundred bytes and the payment will be entirely unpayable (with
671+
/// limited routing options as size increases).
672+
///
673+
/// [`RecipientOnionFields::payment_metadata`]: crate::ln::outbound_payment::RecipientOnionFields::payment_metadata
674+
/// [`Bolt11Invoice::payment_metadata`]: lightning_invoice::Bolt11Invoice::payment_metadata
675+
pub payment_metadata: Option<BTreeMap<u64, Vec<u8>>>,
676+
}
616677

617678
impl TryFrom<CounterpartyForwardingInfo> for PaymentRelay {
618679
type Error = ();
@@ -1031,14 +1092,18 @@ impl<'a> Writeable for PaymentContextRef<'a> {
10311092

10321093
impl_writeable_tlv_based!(Bolt12OfferContext, {
10331094
(0, offer_id, required),
1095+
(1, payment_metadata, (option, encoding: (BTreeMap<u64, Vec<u8>>, BigSizeKeyedMap))),
10341096
(2, invoice_request, required),
10351097
});
10361098

10371099
impl_writeable_tlv_based!(AsyncBolt12OfferContext, {
10381100
(0, offer_nonce, required),
1101+
(1, payment_metadata, (option, encoding: (BTreeMap<u64, Vec<u8>>, BigSizeKeyedMap))),
10391102
});
10401103

1041-
impl_writeable_tlv_based!(Bolt12RefundContext, {});
1104+
impl_writeable_tlv_based!(Bolt12RefundContext, {
1105+
(1, payment_metadata, (option, encoding: (BTreeMap<u64, Vec<u8>>, BigSizeKeyedMap))),
1106+
});
10421107

10431108
#[cfg(test)]
10441109
mod tests {
@@ -1097,7 +1162,7 @@ mod tests {
10971162
let recv_tlvs = ReceiveTlvs {
10981163
payment_secret: PaymentSecret([0; 32]),
10991164
payment_constraints: PaymentConstraints { max_cltv_expiry: 0, htlc_minimum_msat: 1 },
1100-
payment_context: PaymentContext::Bolt12Refund(Bolt12RefundContext {}),
1165+
payment_context: PaymentContext::Bolt12Refund(Bolt12RefundContext { payment_metadata: None }),
11011166
};
11021167
let htlc_maximum_msat = 100_000;
11031168
let blinded_payinfo =
@@ -1115,7 +1180,7 @@ mod tests {
11151180
let recv_tlvs = ReceiveTlvs {
11161181
payment_secret: PaymentSecret([0; 32]),
11171182
payment_constraints: PaymentConstraints { max_cltv_expiry: 0, htlc_minimum_msat: 1 },
1118-
payment_context: PaymentContext::Bolt12Refund(Bolt12RefundContext {}),
1183+
payment_context: PaymentContext::Bolt12Refund(Bolt12RefundContext { payment_metadata: None }),
11191184
};
11201185
let blinded_payinfo = super::compute_payinfo::<ForwardTlvs>(
11211186
&[],
@@ -1178,7 +1243,7 @@ mod tests {
11781243
let recv_tlvs = ReceiveTlvs {
11791244
payment_secret: PaymentSecret([0; 32]),
11801245
payment_constraints: PaymentConstraints { max_cltv_expiry: 0, htlc_minimum_msat: 3 },
1181-
payment_context: PaymentContext::Bolt12Refund(Bolt12RefundContext {}),
1246+
payment_context: PaymentContext::Bolt12Refund(Bolt12RefundContext { payment_metadata: None }),
11821247
};
11831248
let htlc_maximum_msat = 100_000;
11841249
let blinded_payinfo = super::compute_payinfo(
@@ -1238,7 +1303,7 @@ mod tests {
12381303
let recv_tlvs = ReceiveTlvs {
12391304
payment_secret: PaymentSecret([0; 32]),
12401305
payment_constraints: PaymentConstraints { max_cltv_expiry: 0, htlc_minimum_msat: 1 },
1241-
payment_context: PaymentContext::Bolt12Refund(Bolt12RefundContext {}),
1306+
payment_context: PaymentContext::Bolt12Refund(Bolt12RefundContext { payment_metadata: None }),
12421307
};
12431308
let htlc_minimum_msat = 3798;
12441309
assert!(super::compute_payinfo(
@@ -1309,7 +1374,7 @@ mod tests {
13091374
let recv_tlvs = ReceiveTlvs {
13101375
payment_secret: PaymentSecret([0; 32]),
13111376
payment_constraints: PaymentConstraints { max_cltv_expiry: 0, htlc_minimum_msat: 1 },
1312-
payment_context: PaymentContext::Bolt12Refund(Bolt12RefundContext {}),
1377+
payment_context: PaymentContext::Bolt12Refund(Bolt12RefundContext { payment_metadata: None }),
13131378
};
13141379

13151380
let blinded_payinfo = super::compute_payinfo(

lightning/src/ln/async_payments_tests.rs

Lines changed: 87 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
// You may not use this file except in accordance with one or both of these
88
// licenses.
99

10+
use alloc::collections::BTreeMap;
11+
1012
use crate::blinded_path::message::{
1113
BlindedMessagePath, MessageContext, NextMessageHop, OffersContext,
1214
};
@@ -299,6 +301,7 @@ fn create_static_invoice_builder<'a>(
299301
relative_expiry_secs,
300302
recipient.node.list_usable_channels(),
301303
recipient.node.test_get_peers_for_blinded_path(),
304+
None,
302305
)
303306
.unwrap()
304307
}
@@ -1150,6 +1153,88 @@ fn async_receive_flow_success() {
11501153
assert_eq!(res, Some(PaidBolt12Invoice::StaticInvoice(static_invoice)));
11511154
}
11521155

1156+
#[test]
1157+
fn async_payment_delivers_payment_metadata() {
1158+
// Test that `payment_metadata` set in the `AsyncBolt12OfferContext` of a static invoice's
1159+
// blinded payment paths is surfaced via `Event::PaymentClaimable` when the async recipient
1160+
// receives the keysend payment.
1161+
let chanmon_cfgs = create_chanmon_cfgs(3);
1162+
let node_cfgs = create_node_cfgs(3, &chanmon_cfgs);
1163+
1164+
let mut allow_priv_chan_fwds_cfg = test_default_channel_config();
1165+
allow_priv_chan_fwds_cfg.accept_forwards_to_priv_channels = true;
1166+
let node_chanmgrs =
1167+
create_node_chanmgrs(3, &node_cfgs, &[None, Some(allow_priv_chan_fwds_cfg), None]);
1168+
1169+
let nodes = create_network(3, &node_cfgs, &node_chanmgrs);
1170+
create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 1_000_000, 0);
1171+
create_unannounced_chan_between_nodes_with_value(&nodes, 1, 2, 1_000_000, 0);
1172+
1173+
let recipient_id = vec![42; 32];
1174+
let inv_server_paths =
1175+
nodes[1].node.blinded_paths_for_async_recipient(recipient_id.clone(), None).unwrap();
1176+
nodes[2].node.set_paths_to_static_invoice_server(inv_server_paths).unwrap();
1177+
expect_offer_paths_requests(&nodes[2], &[&nodes[0], &nodes[1]]);
1178+
1179+
// Configure the recipient's router to inject `payment_metadata` into the
1180+
// `AsyncBolt12OfferContext` of the static invoice's blinded payment paths. The
1181+
// `pass_static_invoice_server_messages` flow below builds the static invoice via this router,
1182+
// at which point the override is consumed.
1183+
let mut expected_metadata = BTreeMap::new();
1184+
expected_metadata.insert(0u64, vec![1, 2, 3, 4]);
1185+
expected_metadata.insert(7u64, vec![0xab, 0xcd]);
1186+
nodes[2].router.set_next_payment_context_metadata(expected_metadata.clone());
1187+
1188+
let invoice_flow_res =
1189+
pass_static_invoice_server_messages(&nodes[1], &nodes[2], recipient_id.clone());
1190+
let static_invoice = invoice_flow_res.invoice;
1191+
let offer = nodes[2].node.get_async_receive_offer().unwrap();
1192+
let amt_msat = 5000;
1193+
let payment_id = PaymentId([1; 32]);
1194+
nodes[0].node.pay_for_offer(&offer, Some(amt_msat), payment_id, Default::default()).unwrap();
1195+
let release_held_htlc_om = pass_async_payments_oms(
1196+
static_invoice.clone(),
1197+
&nodes[0],
1198+
&nodes[1],
1199+
&nodes[2],
1200+
recipient_id,
1201+
invoice_flow_res.invoice_request_path,
1202+
)
1203+
.1;
1204+
nodes[0]
1205+
.onion_messenger
1206+
.handle_onion_message(nodes[2].node.get_our_node_id(), &release_held_htlc_om);
1207+
1208+
let mut events = nodes[0].node.get_and_clear_pending_msg_events();
1209+
assert_eq!(events.len(), 1);
1210+
let ev = remove_first_msg_event_to_node(&nodes[1].node.get_our_node_id(), &mut events);
1211+
let payment_hash = extract_payment_hash(&ev);
1212+
check_added_monitors(&nodes[0], 1);
1213+
1214+
let route: &[&[&Node]] = &[&[&nodes[1], &nodes[2]]];
1215+
let args = PassAlongPathArgs::new(&nodes[0], route[0], amt_msat, payment_hash, ev)
1216+
.with_dummy_tlvs(&[DummyTlvs::default(); DEFAULT_PAYMENT_DUMMY_HOPS]);
1217+
let claimable_ev = do_pass_along_path(args).unwrap();
1218+
1219+
// Verify the `payment_metadata` we injected is surfaced via the `Bolt12OfferContext` of
1220+
// the `PaymentPurpose`. The recipient converts `AsyncBolt12OfferContext` to
1221+
// `Bolt12OfferContext` when constructing the `PaymentPurpose` for keysend payments.
1222+
match &claimable_ev {
1223+
Event::PaymentClaimable {
1224+
purpose: PaymentPurpose::Bolt12OfferPayment { payment_context, .. },
1225+
..
1226+
} => {
1227+
assert_eq!(payment_context.payment_metadata.as_ref(), Some(&expected_metadata));
1228+
},
1229+
_ => panic!("Unexpected event: {:?}", claimable_ev),
1230+
}
1231+
1232+
let keysend_preimage = extract_payment_preimage(&claimable_ev);
1233+
let (res, _) =
1234+
claim_payment_along_route(ClaimAlongRouteArgs::new(&nodes[0], route, keysend_preimage));
1235+
assert_eq!(res, Some(PaidBolt12Invoice::StaticInvoice(static_invoice)));
1236+
}
1237+
11531238
#[cfg_attr(feature = "std", ignore)]
11541239
#[test]
11551240
fn expired_static_invoice_fail() {
@@ -1591,6 +1676,7 @@ fn reject_bad_payment_secret() {
15911676
PaymentContext::AsyncBolt12Offer(AsyncBolt12OfferContext {
15921677
// We don't reach the point of checking the invreq nonce due to the invalid payment secret
15931678
offer_nonce: Nonce([i; Nonce::LENGTH]),
1679+
payment_metadata: None,
15941680
}),
15951681
u32::MAX,
15961682
)
@@ -3123,7 +3209,7 @@ fn intercepted_hold_htlc() {
31233209
.unwrap();
31243210
let mut offer_nonce = Nonce([0; Nonce::LENGTH]);
31253211
offer_nonce.0.copy_from_slice(&hardcoded_random_bytes[..Nonce::LENGTH]);
3126-
let payment_context = PaymentContext::AsyncBolt12Offer(AsyncBolt12OfferContext { offer_nonce });
3212+
let payment_context = PaymentContext::AsyncBolt12Offer(AsyncBolt12OfferContext { offer_nonce, payment_metadata: None });
31273213
let blinded_payment_path_with_jit_channel_scid = recipient
31283214
.node
31293215
.flow

lightning/src/ln/blinded_payment_tests.rs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ pub fn blinded_payment_path(
8383
htlc_minimum_msat:
8484
intro_node_min_htlc_opt.unwrap_or_else(|| channel_upds.last().unwrap().htlc_minimum_msat),
8585
},
86-
payment_context: PaymentContext::Bolt12Refund(Bolt12RefundContext {}),
86+
payment_context: PaymentContext::Bolt12Refund(Bolt12RefundContext { payment_metadata: None }),
8787
};
8888

8989
let receive_auth_key = keys_manager.get_receive_auth_key();
@@ -172,7 +172,7 @@ fn do_one_hop_blinded_path(success: bool) {
172172
max_cltv_expiry: u32::max_value(),
173173
htlc_minimum_msat: chan_upd.htlc_minimum_msat,
174174
},
175-
payment_context: PaymentContext::Bolt12Refund(Bolt12RefundContext {}),
175+
payment_context: PaymentContext::Bolt12Refund(Bolt12RefundContext { payment_metadata: None }),
176176
};
177177
let receive_auth_key = chanmon_cfgs[1].keys_manager.get_receive_auth_key();
178178

@@ -216,7 +216,7 @@ fn one_hop_blinded_path_with_dummy_hops() {
216216
max_cltv_expiry: u32::max_value(),
217217
htlc_minimum_msat: chan_upd.htlc_minimum_msat,
218218
},
219-
payment_context: PaymentContext::Bolt12Refund(Bolt12RefundContext {}),
219+
payment_context: PaymentContext::Bolt12Refund(Bolt12RefundContext { payment_metadata: None }),
220220
};
221221
let receive_auth_key = chanmon_cfgs[1].keys_manager.get_receive_auth_key();
222222
let dummy_tlvs = [DummyTlvs::default(); 2];
@@ -296,7 +296,7 @@ fn mpp_to_one_hop_blinded_path() {
296296
max_cltv_expiry: u32::max_value(),
297297
htlc_minimum_msat: chan_upd_1_3.htlc_minimum_msat,
298298
},
299-
payment_context: PaymentContext::Bolt12Refund(Bolt12RefundContext {}),
299+
payment_context: PaymentContext::Bolt12Refund(Bolt12RefundContext { payment_metadata: None }),
300300
};
301301
let receive_auth_key = chanmon_cfgs[3].keys_manager.get_receive_auth_key();
302302
let blinded_path = BlindedPaymentPath::new(
@@ -1419,7 +1419,7 @@ fn custom_tlvs_to_blinded_path() {
14191419
max_cltv_expiry: u32::max_value(),
14201420
htlc_minimum_msat: chan_upd.htlc_minimum_msat,
14211421
},
1422-
payment_context: PaymentContext::Bolt12Refund(Bolt12RefundContext {}),
1422+
payment_context: PaymentContext::Bolt12Refund(Bolt12RefundContext { payment_metadata: None }),
14231423
};
14241424
let receive_auth_key = chanmon_cfgs[1].keys_manager.get_receive_auth_key();
14251425

@@ -1473,7 +1473,7 @@ fn fails_receive_tlvs_authentication() {
14731473
max_cltv_expiry: u32::max_value(),
14741474
htlc_minimum_msat: chan_upd.htlc_minimum_msat,
14751475
},
1476-
payment_context: PaymentContext::Bolt12Refund(Bolt12RefundContext {}),
1476+
payment_context: PaymentContext::Bolt12Refund(Bolt12RefundContext { payment_metadata: None }),
14771477
};
14781478
let receive_auth_key = chanmon_cfgs[1].keys_manager.get_receive_auth_key();
14791479

@@ -1503,7 +1503,7 @@ fn fails_receive_tlvs_authentication() {
15031503
max_cltv_expiry: u32::max_value(),
15041504
htlc_minimum_msat: chan_upd.htlc_minimum_msat,
15051505
},
1506-
payment_context: PaymentContext::Bolt12Refund(Bolt12RefundContext {}),
1506+
payment_context: PaymentContext::Bolt12Refund(Bolt12RefundContext { payment_metadata: None }),
15071507
};
15081508
// Use a mismatched ReceiveAuthKey to force auth failure:
15091509
let mismatched_receive_auth_key = ReceiveAuthKey([0u8; 32]);
@@ -2286,7 +2286,7 @@ fn do_test_trampoline_single_hop_receive(success: bool) {
22862286
max_cltv_expiry: u32::max_value(),
22872287
htlc_minimum_msat: amt_msat,
22882288
},
2289-
payment_context: PaymentContext::Bolt12Refund(Bolt12RefundContext {}),
2289+
payment_context: PaymentContext::Bolt12Refund(Bolt12RefundContext { payment_metadata: None }),
22902290
};
22912291
let receive_auth_key = nodes[2].keys_manager.get_receive_auth_key();
22922292
let blinded_path = BlindedPaymentPath::new(&[], carol_node_id, receive_auth_key, payee_tlvs, u64::MAX, 0, nodes[2].keys_manager, &secp_ctx).unwrap();
@@ -2607,7 +2607,7 @@ fn do_test_trampoline_relay(blinded: bool, test_case: TrampolineTestCase) {
26072607
max_cltv_expiry: u32::max_value(),
26082608
htlc_minimum_msat: original_amt_msat,
26092609
},
2610-
payment_context: PaymentContext::Bolt12Refund(Bolt12RefundContext {}),
2610+
payment_context: PaymentContext::Bolt12Refund(Bolt12RefundContext { payment_metadata: None }),
26112611
},
26122612
original_trampoline_cltv,
26132613
excess_final_cltv,

0 commit comments

Comments
 (0)