Skip to content

Commit 2a8cdde

Browse files
Add BLIP-42 contact support for BOLT12 payments
This adds support for BLIP-42, enabling bidirectional contact establishment between payers and payees in BOLT12 payments. Sender side: - Add `send_with_contact` and `send_using_amount_with_contact` methods to `Bolt12Payment` that accept optional `ContactSecrets` and a payer offer to include in the invoice request. Receiver side: - Extend `Event::PaymentReceived` with `contact_secret` and `payer_offer` fields, which are extracted from incoming BOLT12 payments when present. The implementation is stateless - ldk-node surfaces the BLIP-42 data to the application layer without managing contact state internally. See BLIP-42: https://github.com/lightning/blips/blob/master/blip-0042.md Signed-off-by: Vincenzo Palazzo <vincenzopalazzodev@gmail.com>
1 parent bbefa73 commit 2a8cdde

7 files changed

Lines changed: 416 additions & 14 deletions

File tree

Cargo.toml

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -39,17 +39,17 @@ default = []
3939
#lightning-liquidity = { version = "0.2.0", features = ["std"] }
4040
#lightning-macros = { version = "0.2.0" }
4141

42-
lightning = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "5bf0d1e2427d759fc1ba4108ddc7e9b32e8bacfc", features = ["std"] }
43-
lightning-types = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "5bf0d1e2427d759fc1ba4108ddc7e9b32e8bacfc" }
44-
lightning-invoice = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "5bf0d1e2427d759fc1ba4108ddc7e9b32e8bacfc", features = ["std"] }
45-
lightning-net-tokio = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "5bf0d1e2427d759fc1ba4108ddc7e9b32e8bacfc" }
46-
lightning-persister = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "5bf0d1e2427d759fc1ba4108ddc7e9b32e8bacfc", features = ["tokio"] }
47-
lightning-background-processor = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "5bf0d1e2427d759fc1ba4108ddc7e9b32e8bacfc" }
48-
lightning-rapid-gossip-sync = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "5bf0d1e2427d759fc1ba4108ddc7e9b32e8bacfc" }
49-
lightning-block-sync = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "5bf0d1e2427d759fc1ba4108ddc7e9b32e8bacfc", features = ["rest-client", "rpc-client", "tokio"] }
50-
lightning-transaction-sync = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "5bf0d1e2427d759fc1ba4108ddc7e9b32e8bacfc", features = ["esplora-async-https", "time", "electrum-rustls-ring"] }
51-
lightning-liquidity = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "5bf0d1e2427d759fc1ba4108ddc7e9b32e8bacfc", features = ["std"] }
52-
lightning-macros = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "5bf0d1e2427d759fc1ba4108ddc7e9b32e8bacfc" }
42+
lightning = { git = "https://github.com/vincenzopalazzo/rust-lightning", branch = "macros/blip02-prep-v2", features = ["std"] }
43+
lightning-types = { git = "https://github.com/vincenzopalazzo/rust-lightning", branch = "macros/blip02-prep-v2" }
44+
lightning-invoice = { git = "https://github.com/vincenzopalazzo/rust-lightning", branch = "macros/blip02-prep-v2", features = ["std"] }
45+
lightning-net-tokio = { git = "https://github.com/vincenzopalazzo/rust-lightning", branch = "macros/blip02-prep-v2" }
46+
lightning-persister = { git = "https://github.com/vincenzopalazzo/rust-lightning", branch = "macros/blip02-prep-v2", features = ["tokio"] }
47+
lightning-background-processor = { git = "https://github.com/vincenzopalazzo/rust-lightning", branch = "macros/blip02-prep-v2" }
48+
lightning-rapid-gossip-sync = { git = "https://github.com/vincenzopalazzo/rust-lightning", branch = "macros/blip02-prep-v2" }
49+
lightning-block-sync = { git = "https://github.com/vincenzopalazzo/rust-lightning", branch = "macros/blip02-prep-v2", features = ["rest-client", "rpc-client", "tokio"] }
50+
lightning-transaction-sync = { git = "https://github.com/vincenzopalazzo/rust-lightning", branch = "macros/blip02-prep-v2", features = ["esplora-async-https", "time", "electrum-rustls-ring"] }
51+
lightning-liquidity = { git = "https://github.com/vincenzopalazzo/rust-lightning", branch = "macros/blip02-prep-v2", features = ["std"] }
52+
lightning-macros = { git = "https://github.com/vincenzopalazzo/rust-lightning", branch = "macros/blip02-prep-v2" }
5353

5454
bdk_chain = { version = "0.23.0", default-features = false, features = ["std"] }
5555
bdk_esplora = { version = "0.22.0", default-features = false, features = ["async-https-rustls", "tokio"]}
@@ -82,7 +82,7 @@ prost = { version = "0.11.6", default-features = false}
8282
winapi = { version = "0.3", features = ["winbase"] }
8383

8484
[dev-dependencies]
85-
lightning = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "5bf0d1e2427d759fc1ba4108ddc7e9b32e8bacfc", features = ["std", "_test_utils"] }
85+
lightning = { git = "https://github.com/vincenzopalazzo/rust-lightning", branch = "macros/blip02-prep-v2", features = ["std", "_test_utils"] }
8686
proptest = "1.0.0"
8787
regex = "1.5.6"
8888
criterion = { version = "0.7.0", features = ["async_tokio"] }

bindings/ldk_node.udl

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,12 @@ interface Bolt12Payment {
227227
PaymentId send([ByRef]Offer offer, u64? quantity, string? payer_note, RouteParametersConfig? route_parameters);
228228
[Throws=NodeError]
229229
PaymentId send_using_amount([ByRef]Offer offer, u64 amount_msat, u64? quantity, string? payer_note, RouteParametersConfig? route_parameters);
230+
/// Send a payment with BLIP-42 contact information for contact establishment.
231+
[Throws=NodeError]
232+
PaymentId send_with_contact([ByRef]Offer offer, u64? quantity, string? payer_note, RouteParametersConfig? route_parameters, ContactSecrets? contact_secrets, Offer? payer_offer);
233+
/// Send a payment with an explicit amount and BLIP-42 contact information.
234+
[Throws=NodeError]
235+
PaymentId send_using_amount_with_contact([ByRef]Offer offer, u64 amount_msat, u64? quantity, string? payer_note, RouteParametersConfig? route_parameters, ContactSecrets? contact_secrets, Offer? payer_offer);
230236
[Throws=NodeError]
231237
Offer receive(u64 amount_msat, [ByRef]string description, u32? expiry_secs, u64? quantity);
232238
[Throws=NodeError]
@@ -402,7 +408,7 @@ enum VssHeaderProviderError {
402408
interface Event {
403409
PaymentSuccessful(PaymentId? payment_id, PaymentHash payment_hash, PaymentPreimage? payment_preimage, u64? fee_paid_msat);
404410
PaymentFailed(PaymentId? payment_id, PaymentHash? payment_hash, PaymentFailureReason? reason);
405-
PaymentReceived(PaymentId? payment_id, PaymentHash payment_hash, u64 amount_msat, sequence<CustomTlvRecord> custom_records);
411+
PaymentReceived(PaymentId? payment_id, PaymentHash payment_hash, u64 amount_msat, sequence<CustomTlvRecord> custom_records, sequence<u8>? contact_secret, string? payer_offer);
406412
PaymentClaimable(PaymentId payment_id, PaymentHash payment_hash, u64 claimable_amount_msat, u32? claim_deadline, sequence<CustomTlvRecord> custom_records);
407413
PaymentForwarded(ChannelId prev_channel_id, ChannelId next_channel_id, UserChannelId?
408414
prev_user_channel_id, UserChannelId? next_user_channel_id, PublicKey? prev_node_id, PublicKey? next_node_id, u64? total_fee_earned_msat, u64? skimmed_fee_msat, boolean claim_from_onchain_tx, u64? outbound_amount_forwarded_msat);
@@ -763,6 +769,18 @@ dictionary RouteHintHop {
763769
RoutingFees fees;
764770
};
765771

772+
/// BLIP-42 Contact secrets used for mutual authentication in payments.
773+
///
774+
/// When sending payments with contact information, the primary secret is sent to establish
775+
/// the contact relationship. Additional remote secrets can be stored for recognizing
776+
/// payments from contacts who independently added us.
777+
dictionary ContactSecrets {
778+
/// The primary secret (32 bytes) used when sending payments to identify ourselves.
779+
sequence<u8> primary_secret;
780+
/// Additional secrets received from contacts for recognizing their payments.
781+
sequence<sequence<u8>> additional_remote_secrets;
782+
};
783+
766784
[Traits=(Debug, Display, Eq)]
767785
interface Bolt11Invoice {
768786
[Throws=NodeError, Name=from_str]

src/event.rs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,25 @@ pub enum Event {
106106
amount_msat: u64,
107107
/// Custom TLV records received on the payment
108108
custom_records: Vec<CustomTlvRecord>,
109+
/// BLIP-42: The contact secret sent by the payer.
110+
///
111+
/// If present, this indicates the payer wants to establish a contact relationship.
112+
/// The recipient can use this secret along with `payer_offer` to add the payer as a contact.
113+
///
114+
/// See [BLIP-42](https://github.com/lightning/blips/blob/master/blip-0042.md) for more details.
115+
///
116+
/// Will only be `Some` for BOLT12 payments where the payer included contact information.
117+
/// The value is 32 bytes.
118+
contact_secret: Option<Vec<u8>>,
119+
/// BLIP-42: The payer's BOLT12 offer.
120+
///
121+
/// If present, this is the payer's offer that can be used to pay them back or establish
122+
/// bidirectional contact. This is typically a compact offer with minimal blinded paths.
123+
///
124+
/// See [BLIP-42](https://github.com/lightning/blips/blob/master/blip-0042.md) for more details.
125+
///
126+
/// Will only be `Some` for BOLT12 payments where the payer included their offer.
127+
payer_offer: Option<String>,
109128
},
110129
/// A payment has been forwarded.
111130
PaymentForwarded {
@@ -275,6 +294,9 @@ impl_writeable_tlv_based_enum!(Event,
275294
(1, payment_id, option),
276295
(2, amount_msat, required),
277296
(3, custom_records, optional_vec),
297+
// BLIP-42 contact fields
298+
(5, contact_secret, option),
299+
(7, payer_offer, option),
278300
},
279301
(3, ChannelReady) => {
280302
(0, channel_id, required),
@@ -938,6 +960,21 @@ where
938960
amount_msat,
939961
);
940962

963+
// Extract BLIP-42 contact fields from Bolt12 payments
964+
let (contact_secret, payer_offer) = match &purpose {
965+
PaymentPurpose::Bolt12OfferPayment { payment_context, .. } => {
966+
let contact_secret =
967+
payment_context.invoice_request.contact_secret.map(|s| s.to_vec());
968+
let payer_offer = payment_context
969+
.invoice_request
970+
.payer_offer
971+
.as_ref()
972+
.map(|offer| offer.to_string());
973+
(contact_secret, payer_offer)
974+
},
975+
_ => (None, None),
976+
};
977+
941978
let update = match purpose {
942979
PaymentPurpose::Bolt11InvoicePayment {
943980
payment_preimage,
@@ -1008,6 +1045,8 @@ where
10081045
custom_records: onion_fields
10091046
.map(|cf| cf.custom_tlvs().into_iter().map(|tlv| tlv.into()).collect())
10101047
.unwrap_or_default(),
1048+
contact_secret,
1049+
payer_offer,
10111050
};
10121051
match self.event_queue.add_event(event).await {
10131052
Ok(_) => return Ok(()),

src/lib.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,10 @@ use types::{
161161
Broadcaster, BumpTransactionEventHandler, ChainMonitor, ChannelManager, DynStore, Graph,
162162
KeysManager, OnionMessenger, PaymentStore, PeerManager, Router, Scorer, Sweeper, Wallet,
163163
};
164-
pub use types::{ChannelDetails, CustomTlvRecord, PeerDetails, SyncAndAsyncKVStore, UserChannelId};
164+
pub use types::{
165+
ChannelDetails, ContactSecrets, CustomTlvRecord, PeerDetails, SyncAndAsyncKVStore,
166+
UserChannelId,
167+
};
165168
pub use {
166169
bip39, bitcoin, lightning, lightning_invoice, lightning_liquidity, lightning_types, tokio,
167170
vss_client,

0 commit comments

Comments
 (0)