Skip to content

Commit a5aa96f

Browse files
receiver side exstract the payer_offer from a PaymentClaimable
Signed-off-by: Vincenzo Palazzo <[email protected]>
1 parent a8b98b4 commit a5aa96f

File tree

2 files changed

+117
-18
lines changed

2 files changed

+117
-18
lines changed

lightning/src/ln/offers_tests.rs

Lines changed: 86 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,8 @@ use crate::blinded_path::message::BlindedMessagePath;
5050
use crate::blinded_path::payment::{Bolt12OfferContext, Bolt12RefundContext, PaymentContext};
5151
use crate::blinded_path::message::OffersContext;
5252
use crate::events::{ClosureReason, Event, HTLCHandlingFailureType, PaidBolt12Invoice, PaymentFailureReason, PaymentPurpose};
53-
use crate::ln::channelmanager::{Bolt12PaymentError, PaymentId, RecentPaymentDetails, RecipientOnionFields, Retry, self};
53+
use crate::ln::channelmanager::{self, Bolt12PaymentError, OptionalOfferPaymentParams, PaymentId, RecentPaymentDetails, RecipientOnionFields, Retry};
54+
use crate::offers::contacts::ContactSecrets;
5455
use crate::offers::offer::Offer;
5556
use crate::types::features::Bolt12InvoiceFeatures;
5657
use crate::ln::functional_test_utils::*;
@@ -686,6 +687,8 @@ fn creates_and_pays_for_offer_using_two_hop_blinded_path() {
686687
quantity: None,
687688
payer_note_truncated: None,
688689
human_readable_name: None,
690+
contact_secret: None,
691+
payer_offer: None,
689692
},
690693
});
691694
assert_eq!(invoice_request.amount_msats(), Some(10_000_000));
@@ -844,6 +847,8 @@ fn creates_and_pays_for_offer_using_one_hop_blinded_path() {
844847
quantity: None,
845848
payer_note_truncated: None,
846849
human_readable_name: None,
850+
contact_secret: None,
851+
payer_offer: None,
847852
},
848853
});
849854
assert_eq!(invoice_request.amount_msats(), Some(10_000_000));
@@ -965,6 +970,8 @@ fn pays_for_offer_without_blinded_paths() {
965970
quantity: None,
966971
payer_note_truncated: None,
967972
human_readable_name: None,
973+
contact_secret: None,
974+
payer_offer: None,
968975
},
969976
});
970977

@@ -1232,6 +1239,8 @@ fn creates_and_pays_for_offer_with_retry() {
12321239
quantity: None,
12331240
payer_note_truncated: None,
12341241
human_readable_name: None,
1242+
contact_secret: None,
1243+
payer_offer: None,
12351244
},
12361245
});
12371246
assert_eq!(invoice_request.amount_msats(), Some(10_000_000));
@@ -1297,6 +1306,8 @@ fn pays_bolt12_invoice_asynchronously() {
12971306
quantity: None,
12981307
payer_note_truncated: None,
12991308
human_readable_name: None,
1309+
contact_secret: None,
1310+
payer_offer: None,
13001311
},
13011312
});
13021313

@@ -1394,6 +1405,8 @@ fn creates_offer_with_blinded_path_using_unannounced_introduction_node() {
13941405
quantity: None,
13951406
payer_note_truncated: None,
13961407
human_readable_name: None,
1408+
contact_secret: None,
1409+
payer_offer: None,
13971410
},
13981411
});
13991412
assert_ne!(invoice_request.payer_signing_pubkey(), bob_id);
@@ -2555,28 +2568,16 @@ fn pay_offer_and_add_contacts_info_blip42() {
25552568

25562569
let payment_id = PaymentId([1; 32]);
25572570
bob.node.pay_for_offer(&offer, None, payment_id, Default::default()).unwrap();
2558-
// Probably a good place to add the information that we use for the contact secret.
2559-
// but need to double check if the sender of the invoice request still need to ask anything.
2571+
25602572
expect_recent_payment!(bob, RecentPaymentDetails::AwaitingInvoice, payment_id);
25612573
let onion_message = bob.onion_messenger.next_onion_message_for_peer(alice_id).unwrap();
25622574
alice.onion_messenger.handle_onion_message(bob_id, &onion_message);
25632575

25642576
let (invoice_request, _reply_path) = extract_invoice_request(alice, &onion_message);
25652577

2566-
let payment_context = PaymentContext::Bolt12Offer(Bolt12OfferContext {
2567-
offer_id: offer.id(),
2568-
invoice_request: InvoiceRequestFields {
2569-
payer_signing_pubkey: invoice_request.payer_signing_pubkey(),
2570-
quantity: None,
2571-
payer_note_truncated: None,
2572-
human_readable_name: None,
2573-
},
2574-
});
25752578
assert_eq!(invoice_request.amount_msats(), Some(10_000_000));
25762579
assert_ne!(invoice_request.payer_signing_pubkey(), bob_id);
25772580
assert!(invoice_request.contact_secret().is_some());
2578-
// TODO: we should check also if the contact secret is the same that we inject by bob.
2579-
25802581
assert!(invoice_request.payer_offer().is_some());
25812582

25822583
let onion_message = alice.onion_messenger.next_onion_message_for_peer(bob_id).unwrap();
@@ -2590,7 +2591,18 @@ fn pay_offer_and_add_contacts_info_blip42() {
25902591
route_bolt12_payment(bob, &[alice], &invoice);
25912592
expect_recent_payment!(bob, RecentPaymentDetails::Pending, payment_id);
25922593

2593-
let contact_info = claim_bolt12_payment(bob, &[alice], payment_context, &invoice);
2594+
let (alice_payment_purpose, alice_payment_preimage) = match get_event!(alice, Event::PaymentClaimable) {
2595+
Event::PaymentClaimable { purpose, .. } => {
2596+
let preimage = match purpose.preimage() {
2597+
Some(p) => p,
2598+
None => panic!("No preimage in PaymentClaimable"),
2599+
};
2600+
(purpose, preimage)
2601+
},
2602+
_ => panic!("No Event::PaymentClaimable for Alice"),
2603+
};
2604+
2605+
let (_, contact_info) = claim_payment(bob, &[alice], alice_payment_preimage);
25942606
expect_recent_payment!(bob, RecentPaymentDetails::Fulfilled, payment_id);
25952607

25962608
assert!(contact_info.is_some());
@@ -2599,7 +2611,63 @@ fn pay_offer_and_add_contacts_info_blip42() {
25992611
assert!(invoice_request.contact_secret().is_some());
26002612
assert_eq!(invoice_request.contact_secret().unwrap(), contact_info.contact_secrets.primary_secret());
26012613

2602-
// TODO: now should be possible that alice will be able to repay bob without that
2603-
// bob give any offer in exchange!! but there is a contact list somewhere that allow
2604-
// to run something like bob.pay_for_contact(alice_contact_name, amount);
2614+
let alice_invoice_request_fields = match alice_payment_purpose {
2615+
PaymentPurpose::Bolt12OfferPayment { payment_context, .. } => {
2616+
assert_eq!(payment_context.offer_id, offer.id());
2617+
payment_context.invoice_request
2618+
},
2619+
_ => panic!("Expected Bolt12OfferPayment purpose for Alice"),
2620+
};
2621+
2622+
assert!(alice_invoice_request_fields.contact_secret.is_some());
2623+
assert_eq!(alice_invoice_request_fields.contact_secret.unwrap(), *contact_info.contact_secrets.primary_secret());
2624+
2625+
assert!(alice_invoice_request_fields.payer_offer.is_some());
2626+
assert_eq!(alice_invoice_request_fields.payer_offer.as_ref().unwrap(), &contact_info.payer_offer);
2627+
2628+
let alice_contact_secret = alice_invoice_request_fields.contact_secret.unwrap();
2629+
let alice_payer_offer = alice_invoice_request_fields.payer_offer.unwrap();
2630+
2631+
let payment_id = PaymentId([1; 32]);
2632+
alice.node.pay_for_offer(&alice_payer_offer, Some(5_000_000), payment_id, OptionalOfferPaymentParams{
2633+
contact_secrects: Some(ContactSecrets::new(alice_contact_secret)),
2634+
..Default::default()
2635+
}).unwrap();
2636+
2637+
2638+
expect_recent_payment!(alice, RecentPaymentDetails::AwaitingInvoice, payment_id);
2639+
let onion_message = alice.onion_messenger.next_onion_message_for_peer(bob_id).unwrap();
2640+
bob.onion_messenger.handle_onion_message(alice_id, &onion_message);
2641+
2642+
let (invoice_request, _reply_path) = extract_invoice_request(bob, &onion_message);
2643+
2644+
assert_eq!(invoice_request.amount_msats(), Some(5_000_000));
2645+
assert_ne!(invoice_request.payer_signing_pubkey(), bob_id);
2646+
assert!(invoice_request.contact_secret().is_some());
2647+
assert!(invoice_request.payer_offer().is_some());
2648+
2649+
let onion_message = bob.onion_messenger.next_onion_message_for_peer(alice_id).unwrap();
2650+
bob.onion_messenger.handle_onion_message(bob_id, &onion_message);
2651+
2652+
let (invoice, _reply_path) = extract_invoice(alice, &onion_message);
2653+
assert_eq!(invoice.amount_msats(), 10_000_000);
2654+
assert_ne!(invoice.signing_pubkey(), alice_id);
2655+
assert!(!invoice.payment_paths().is_empty());
2656+
2657+
route_bolt12_payment(bob, &[alice], &invoice);
2658+
expect_recent_payment!(bob, RecentPaymentDetails::Pending, payment_id);
2659+
2660+
let (_, alice_payment_preimage) = match get_event!(alice, Event::PaymentClaimable) {
2661+
Event::PaymentClaimable { purpose, .. } => {
2662+
let preimage = match purpose.preimage() {
2663+
Some(p) => p,
2664+
None => panic!("No preimage in PaymentClaimable"),
2665+
};
2666+
(purpose, preimage)
2667+
},
2668+
_ => panic!("No Event::PaymentClaimable for Alice"),
2669+
};
2670+
2671+
let (_, _) = claim_payment(bob, &[alice], alice_payment_preimage);
2672+
expect_recent_payment!(bob, RecentPaymentDetails::Fulfilled, payment_id);
26052673
}

lightning/src/offers/invoice_request.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1094,6 +1094,12 @@ macro_rules! fields_accessor {
10941094
},
10951095
} = &$inner;
10961096

1097+
// Extract BLIP-42 contact information if present
1098+
let contact_secret = $self.contact_secret().and_then(|bytes| bytes.try_into().ok());
1099+
let payer_offer = $self
1100+
.payer_offer()
1101+
.and_then(|bytes| crate::offers::offer::Offer::try_from(bytes.to_vec()).ok());
1102+
10971103
InvoiceRequestFields {
10981104
payer_signing_pubkey: *payer_signing_pubkey,
10991105
quantity: *quantity,
@@ -1103,6 +1109,8 @@ macro_rules! fields_accessor {
11031109
// down to the nearest valid UTF-8 code point boundary.
11041110
.map(|s| UntrustedString(string_truncate_safe(s, PAYER_NOTE_LIMIT))),
11051111
human_readable_name: $self.offer_from_hrn().clone(),
1112+
contact_secret,
1113+
payer_offer,
11061114
}
11071115
}
11081116
};
@@ -1587,6 +1595,14 @@ pub struct InvoiceRequestFields {
15871595

15881596
/// The Human Readable Name which the sender indicated they were paying to.
15891597
pub human_readable_name: Option<HumanReadableName>,
1598+
1599+
/// BLIP-42: The contact secret included by the payer for contact management.
1600+
/// This allows the recipient to establish a contact relationship with the payer.
1601+
pub contact_secret: Option<[u8; 32]>,
1602+
1603+
/// BLIP-42: The payer's minimal offer included in the invoice request.
1604+
/// This is a compact offer (just node_id) to fit within payment onion size constraints.
1605+
pub payer_offer: Option<crate::offers::offer::Offer>,
15901606
}
15911607

15921608
/// The maximum number of characters included in [`InvoiceRequestFields::payer_note_truncated`].
@@ -1599,11 +1615,17 @@ pub const PAYER_NOTE_LIMIT: usize = 8;
15991615

16001616
impl Writeable for InvoiceRequestFields {
16011617
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), io::Error> {
1618+
let payer_offer_bytes = self.payer_offer.as_ref().map(|offer| {
1619+
use core::convert::AsRef;
1620+
offer.as_ref().to_vec()
1621+
});
16021622
write_tlv_fields!(writer, {
16031623
(0, self.payer_signing_pubkey, required),
16041624
(1, self.human_readable_name, option),
16051625
(2, self.quantity.map(|v| HighZeroBytesDroppedBigSize(v)), option),
16061626
(4, self.payer_note_truncated.as_ref().map(|s| WithoutLength(&s.0)), option),
1627+
(6, self.contact_secret, option),
1628+
(8, payer_offer_bytes.as_ref().map(|v| WithoutLength(&v[..])), option),
16071629
});
16081630
Ok(())
16091631
}
@@ -1616,13 +1638,20 @@ impl Readable for InvoiceRequestFields {
16161638
(1, human_readable_name, option),
16171639
(2, quantity, (option, encoding: (u64, HighZeroBytesDroppedBigSize))),
16181640
(4, payer_note_truncated, (option, encoding: (String, WithoutLength))),
1641+
(6, contact_secret, option),
1642+
(8, payer_offer_bytes, (option, encoding: (Vec<u8>, WithoutLength))),
16191643
});
16201644

1645+
let payer_offer =
1646+
payer_offer_bytes.and_then(|bytes| crate::offers::offer::Offer::try_from(bytes).ok());
1647+
16211648
Ok(InvoiceRequestFields {
16221649
payer_signing_pubkey: payer_signing_pubkey.0.unwrap(),
16231650
quantity,
16241651
payer_note_truncated: payer_note_truncated.map(|s| UntrustedString(s)),
16251652
human_readable_name,
1653+
contact_secret,
1654+
payer_offer,
16261655
})
16271656
}
16281657
}
@@ -3199,6 +3228,8 @@ mod tests {
31993228
quantity: Some(1),
32003229
payer_note_truncated: Some(UntrustedString(expected_payer_note)),
32013230
human_readable_name: None,
3231+
contact_secret: None,
3232+
payer_offer: None,
32023233
}
32033234
);
32043235

0 commit comments

Comments
 (0)