Skip to content

Commit c836395

Browse files
vincenzopalazzojkczyzclaude
committed
Refactor payer proof construction and address review feedback
Encapsulate invoice, preimage, and nonce in PaidBolt12Invoice and surface it in PaymentSent. Rework builder to return UnsignedPayerProof with SignFn/sign_message integration, use encode_tlv_stream! for serialization, move helpers to DisclosedFields methods, and address naming conventions and TLV validation feedback. Co-Authored-By: Jeffrey Czyz <jkczyz@gmail.com> Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent c32f7e6 commit c836395

File tree

14 files changed

+787
-490
lines changed

14 files changed

+787
-490
lines changed

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: 19 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@ 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;
36+
pub use crate::offers::payer_proof::PaidBolt12Invoice;
3437
use crate::offers::static_invoice::StaticInvoice;
3538
use crate::onion_message::messenger::Responder;
3639
use crate::routing::gossip::NetworkUpdate;
@@ -1089,17 +1092,13 @@ pub enum Event {
10891092
///
10901093
/// [`Route::get_total_fees`]: crate::routing::router::Route::get_total_fees
10911094
fee_paid_msat: Option<u64>,
1092-
/// The BOLT 12 invoice that was paid. `None` if the payment was a non BOLT 12 payment.
1095+
/// The paid BOLT 12 invoice bundled with the data needed to construct a
1096+
/// [`PayerProof`], which selectively discloses invoice fields to prove payment to a
1097+
/// third party.
10931098
///
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.
1099+
/// `None` for non-BOLT 12 payments.
10981100
///
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
1101+
/// [`PayerProof`]: crate::offers::payer_proof::PayerProof
11031102
bolt12_invoice: Option<PaidBolt12Invoice>,
11041103
},
11051104
/// Indicates an outbound payment failed. Individual [`Event::PaymentPathFailed`] events
@@ -1975,13 +1974,16 @@ impl Writeable for Event {
19751974
ref bolt12_invoice,
19761975
} => {
19771976
2u8.write(writer)?;
1977+
let invoice_type = bolt12_invoice.as_ref().map(|paid| paid.invoice_type());
1978+
let payment_nonce = bolt12_invoice.as_ref().and_then(|paid| paid.nonce());
19781979
write_tlv_fields!(writer, {
19791980
(0, payment_preimage, required),
19801981
(1, payment_hash, required),
19811982
(3, payment_id, option),
19821983
(5, fee_paid_msat, option),
19831984
(7, amount_msat, option),
1984-
(9, bolt12_invoice, option),
1985+
(9, invoice_type, option),
1986+
(11, payment_nonce, option),
19851987
});
19861988
},
19871989
&Event::PaymentPathFailed {
@@ -2473,20 +2475,25 @@ impl MaybeReadable for Event {
24732475
let mut payment_id = None;
24742476
let mut amount_msat = None;
24752477
let mut fee_paid_msat = None;
2476-
let mut bolt12_invoice = None;
2478+
let mut invoice_type: Option<Bolt12InvoiceType> = None;
2479+
let mut payment_nonce: Option<Nonce> = None;
24772480
read_tlv_fields!(reader, {
24782481
(0, payment_preimage, required),
24792482
(1, payment_hash, option),
24802483
(3, payment_id, option),
24812484
(5, fee_paid_msat, option),
24822485
(7, amount_msat, option),
2483-
(9, bolt12_invoice, option),
2486+
(9, invoice_type, option),
2487+
(11, payment_nonce, option),
24842488
});
24852489
if payment_hash.is_none() {
24862490
payment_hash = Some(PaymentHash(
24872491
Sha256::hash(&payment_preimage.0[..]).to_byte_array(),
24882492
));
24892493
}
2494+
let bolt12_invoice = invoice_type.map(|invoice| {
2495+
PaidBolt12Invoice::new(invoice, payment_preimage, payment_nonce)
2496+
});
24902497
Ok(Some(Event::PaymentSent {
24912498
payment_id,
24922499
payment_preimage,
@@ -3146,19 +3153,3 @@ impl<T: EventHandler> EventHandler for Arc<T> {
31463153
self.deref().handle_event(event)
31473154
}
31483155
}
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)