Skip to content

Commit b52a5f3

Browse files
vincenzopalazzojkczyzclaude
committed
Include payment_nonce in PaymentSent for payer proof construction
Adds PaymentSent nonce tracking, PaidBolt12Invoice encapsulation, SignFn/sign_message integration, encode_tlv_stream! serialization, UnsignedPayerProof builder pattern, and review feedback (naming conventions, TLV validation, even-type range tests). Co-Authored-By: Jeffrey Czyz <jkczyz@gmail.com> Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent c32f7e6 commit b52a5f3

14 files changed

Lines changed: 785 additions & 489 deletions

fuzz/src/process_onion_failure.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ fn do_test<Out: test_logger::Output>(data: &[u8], out: Out) {
122122
first_hop_htlc_msat: 0,
123123
payment_id,
124124
bolt12_invoice: None,
125+
payment_nonce: None,
125126
};
126127

127128
let failure_len = get_u16!();

lightning/src/events/mod.rs

Lines changed: 18 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ use crate::ln::outbound_payment::RecipientOnionFields;
3131
use crate::ln::types::ChannelId;
3232
use crate::offers::invoice::Bolt12Invoice;
3333
use crate::offers::invoice_request::InvoiceRequest;
34+
use crate::offers::nonce::Nonce;
35+
use crate::offers::payer_proof::{Bolt12InvoiceType, PaidBolt12Invoice};
3436
use crate::offers::static_invoice::StaticInvoice;
3537
use crate::onion_message::messenger::Responder;
3638
use crate::routing::gossip::NetworkUpdate;
@@ -1089,17 +1091,13 @@ pub enum Event {
10891091
///
10901092
/// [`Route::get_total_fees`]: crate::routing::router::Route::get_total_fees
10911093
fee_paid_msat: Option<u64>,
1092-
/// The BOLT 12 invoice that was paid. `None` if the payment was a non BOLT 12 payment.
1094+
/// The paid BOLT 12 invoice bundled with the data needed to construct a
1095+
/// [`PayerProof`], which selectively discloses invoice fields to prove payment to a
1096+
/// third party.
10931097
///
1094-
/// The BOLT 12 invoice is useful for proof of payment because it contains the
1095-
/// payment hash. A third party can verify that the payment was made by
1096-
/// showing the invoice and confirming that the payment hash matches
1097-
/// the hash of the payment preimage.
1098+
/// `None` for non-BOLT 12 payments.
10981099
///
1099-
/// However, the [`PaidBolt12Invoice`] can also be of type [`StaticInvoice`], which
1100-
/// is a special [`Bolt12Invoice`] where proof of payment is not possible.
1101-
///
1102-
/// [`StaticInvoice`]: crate::offers::static_invoice::StaticInvoice
1100+
/// [`PayerProof`]: crate::offers::payer_proof::PayerProof
11031101
bolt12_invoice: Option<PaidBolt12Invoice>,
11041102
},
11051103
/// Indicates an outbound payment failed. Individual [`Event::PaymentPathFailed`] events
@@ -1975,13 +1973,16 @@ impl Writeable for Event {
19751973
ref bolt12_invoice,
19761974
} => {
19771975
2u8.write(writer)?;
1976+
let invoice_type = bolt12_invoice.as_ref().map(|paid| paid.invoice_type());
1977+
let payment_nonce = bolt12_invoice.as_ref().and_then(|paid| paid.nonce());
19781978
write_tlv_fields!(writer, {
19791979
(0, payment_preimage, required),
19801980
(1, payment_hash, required),
19811981
(3, payment_id, option),
19821982
(5, fee_paid_msat, option),
19831983
(7, amount_msat, option),
1984-
(9, bolt12_invoice, option),
1984+
(9, invoice_type, option),
1985+
(11, payment_nonce, option),
19851986
});
19861987
},
19871988
&Event::PaymentPathFailed {
@@ -2473,20 +2474,25 @@ impl MaybeReadable for Event {
24732474
let mut payment_id = None;
24742475
let mut amount_msat = None;
24752476
let mut fee_paid_msat = None;
2476-
let mut bolt12_invoice = None;
2477+
let mut invoice_type: Option<Bolt12InvoiceType> = None;
2478+
let mut payment_nonce: Option<Nonce> = None;
24772479
read_tlv_fields!(reader, {
24782480
(0, payment_preimage, required),
24792481
(1, payment_hash, option),
24802482
(3, payment_id, option),
24812483
(5, fee_paid_msat, option),
24822484
(7, amount_msat, option),
2483-
(9, bolt12_invoice, option),
2485+
(9, invoice_type, option),
2486+
(11, payment_nonce, option),
24842487
});
24852488
if payment_hash.is_none() {
24862489
payment_hash = Some(PaymentHash(
24872490
Sha256::hash(&payment_preimage.0[..]).to_byte_array(),
24882491
));
24892492
}
2493+
let bolt12_invoice = invoice_type.map(|invoice| {
2494+
PaidBolt12Invoice::new(invoice, payment_preimage, payment_nonce)
2495+
});
24902496
Ok(Some(Event::PaymentSent {
24912497
payment_id,
24922498
payment_preimage,
@@ -3146,19 +3152,3 @@ impl<T: EventHandler> EventHandler for Arc<T> {
31463152
self.deref().handle_event(event)
31473153
}
31483154
}
3149-
3150-
/// The BOLT 12 invoice that was paid, surfaced in [`Event::PaymentSent::bolt12_invoice`].
3151-
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
3152-
pub enum PaidBolt12Invoice {
3153-
/// The BOLT 12 invoice specified by the BOLT 12 specification,
3154-
/// allowing the user to perform proof of payment.
3155-
Bolt12Invoice(Bolt12Invoice),
3156-
/// The Static invoice, used in the async payment specification update proposal,
3157-
/// where the user cannot perform proof of payment.
3158-
StaticInvoice(StaticInvoice),
3159-
}
3160-
3161-
impl_writeable_tlv_based_enum!(PaidBolt12Invoice,
3162-
{0, Bolt12Invoice} => (),
3163-
{2, StaticInvoice} => (),
3164-
);

lightning/src/ln/async_payments_tests.rs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use crate::blinded_path::payment::{AsyncBolt12OfferContext, BlindedPaymentTlvs};
1414
use crate::blinded_path::payment::{DummyTlvs, PaymentContext};
1515
use crate::chain::channelmonitor::{HTLC_FAIL_BACK_BUFFER, LATENCY_GRACE_PERIOD_BLOCKS};
1616
use crate::events::{
17-
Event, EventsProvider, HTLCHandlingFailureReason, HTLCHandlingFailureType, PaidBolt12Invoice,
17+
Event, EventsProvider, HTLCHandlingFailureReason, HTLCHandlingFailureType,
1818
PaymentFailureReason, PaymentPurpose,
1919
};
2020
use crate::ln::blinded_payment_tests::{fail_blinded_htlc_backwards, get_blinded_route_parameters};
@@ -988,7 +988,7 @@ fn ignore_duplicate_invoice() {
988988
let keysend_preimage = extract_payment_preimage(&claimable_ev);
989989
let (res, _) =
990990
claim_payment_along_route(ClaimAlongRouteArgs::new(sender, route, keysend_preimage));
991-
assert_eq!(res, Some(PaidBolt12Invoice::StaticInvoice(static_invoice.clone())));
991+
assert_eq!(res.as_ref().and_then(|paid| paid.static_invoice()), Some(&static_invoice));
992992

993993
// After paying the static invoice, check that regular invoice received from async recipient is ignored.
994994
match sender.onion_messenger.peel_onion_message(&invoice_om) {
@@ -1073,7 +1073,7 @@ fn ignore_duplicate_invoice() {
10731073

10741074
// After paying invoice, check that static invoice is ignored.
10751075
let res = claim_payment(sender, route[0], payment_preimage);
1076-
assert_eq!(res, Some(PaidBolt12Invoice::Bolt12Invoice(invoice)));
1076+
assert_eq!(res.as_ref().and_then(|paid| paid.bolt12_invoice()), Some(&invoice));
10771077

10781078
sender.onion_messenger.handle_onion_message(always_online_node_id, &static_invoice_om);
10791079
let async_pmts_msgs = AsyncPaymentsMessageHandler::release_pending_messages(sender.node);
@@ -1144,7 +1144,7 @@ fn async_receive_flow_success() {
11441144
let keysend_preimage = extract_payment_preimage(&claimable_ev);
11451145
let (res, _) =
11461146
claim_payment_along_route(ClaimAlongRouteArgs::new(&nodes[0], route, keysend_preimage));
1147-
assert_eq!(res, Some(PaidBolt12Invoice::StaticInvoice(static_invoice)));
1147+
assert_eq!(res.as_ref().and_then(|paid| paid.static_invoice()), Some(&static_invoice));
11481148
}
11491149

11501150
#[cfg_attr(feature = "std", ignore)]
@@ -2384,7 +2384,7 @@ fn refresh_static_invoices_for_used_offers() {
23842384
let claimable_ev = do_pass_along_path(args).unwrap();
23852385
let keysend_preimage = extract_payment_preimage(&claimable_ev);
23862386
let res = claim_payment_along_route(ClaimAlongRouteArgs::new(sender, route, keysend_preimage));
2387-
assert_eq!(res.0, Some(PaidBolt12Invoice::StaticInvoice(updated_invoice)));
2387+
assert_eq!(res.0.as_ref().and_then(|paid| paid.static_invoice()), Some(&updated_invoice));
23882388
}
23892389

23902390
#[cfg_attr(feature = "std", ignore)]
@@ -2719,7 +2719,7 @@ fn invoice_server_is_not_channel_peer() {
27192719
let claimable_ev = do_pass_along_path(args).unwrap();
27202720
let keysend_preimage = extract_payment_preimage(&claimable_ev);
27212721
let res = claim_payment_along_route(ClaimAlongRouteArgs::new(sender, route, keysend_preimage));
2722-
assert_eq!(res.0, Some(PaidBolt12Invoice::StaticInvoice(invoice)));
2722+
assert_eq!(res.0.as_ref().and_then(|paid| paid.static_invoice()), Some(&invoice));
27232723
}
27242724

27252725
#[test]
@@ -2962,7 +2962,7 @@ fn async_payment_e2e() {
29622962
let keysend_preimage = extract_payment_preimage(&claimable_ev);
29632963
let (res, _) =
29642964
claim_payment_along_route(ClaimAlongRouteArgs::new(sender, route, keysend_preimage));
2965-
assert_eq!(res, Some(PaidBolt12Invoice::StaticInvoice(static_invoice)));
2965+
assert_eq!(res.as_ref().and_then(|paid| paid.static_invoice()), Some(&static_invoice));
29662966
}
29672967

29682968
#[test]
@@ -3199,7 +3199,7 @@ fn intercepted_hold_htlc() {
31993199
let keysend_preimage = extract_payment_preimage(&claimable_ev);
32003200
let (res, _) =
32013201
claim_payment_along_route(ClaimAlongRouteArgs::new(sender, route, keysend_preimage));
3202-
assert_eq!(res, Some(PaidBolt12Invoice::StaticInvoice(static_invoice)));
3202+
assert_eq!(res.as_ref().and_then(|paid| paid.static_invoice()), Some(&static_invoice));
32033203
}
32043204

32053205
#[test]
@@ -3449,5 +3449,5 @@ fn release_htlc_races_htlc_onion_decode() {
34493449
let keysend_preimage = extract_payment_preimage(&claimable_ev);
34503450
let (res, _) =
34513451
claim_payment_along_route(ClaimAlongRouteArgs::new(sender, route, keysend_preimage));
3452-
assert_eq!(res, Some(PaidBolt12Invoice::StaticInvoice(static_invoice)));
3452+
assert_eq!(res.as_ref().and_then(|paid| paid.static_invoice()), Some(&static_invoice));
34533453
}

lightning/src/ln/channel.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16535,6 +16535,7 @@ mod tests {
1653516535
first_hop_htlc_msat: 548,
1653616536
payment_id: PaymentId([42; 32]),
1653716537
bolt12_invoice: None,
16538+
payment_nonce: None,
1653816539
},
1653916540
skimmed_fee_msat: None,
1654016541
blinding_point: None,
@@ -16986,6 +16987,7 @@ mod tests {
1698616987
first_hop_htlc_msat: 0,
1698716988
payment_id: PaymentId([42; 32]),
1698816989
bolt12_invoice: None,
16990+
payment_nonce: None,
1698916991
};
1699016992
let dummy_outbound_output = OutboundHTLCOutput {
1699116993
htlc_id: 0,

0 commit comments

Comments
 (0)