Skip to content

Commit 20a6927

Browse files
authored
Merge pull request #411 from Evanfeenstra/keysend-custom-tlv
Feature: Keysend custom tlv
2 parents 2095d87 + eb0fb37 commit 20a6927

File tree

8 files changed

+93
-14
lines changed

8 files changed

+93
-14
lines changed

bindings/ldk_node.udl

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,8 @@ interface SpontaneousPayment {
152152
[Throws=NodeError]
153153
PaymentId send(u64 amount_msat, PublicKey node_id, SendingParameters? sending_parameters);
154154
[Throws=NodeError]
155+
PaymentId send_with_custom_tlvs(u64 amount_msat, PublicKey node_id, SendingParameters? sending_parameters, sequence<CustomTlvRecord> custom_tlvs);
156+
[Throws=NodeError]
155157
void send_probes(u64 amount_msat, PublicKey node_id);
156158
};
157159

@@ -182,6 +184,7 @@ enum NodeError {
182184
"OfferCreationFailed",
183185
"RefundCreationFailed",
184186
"PaymentSendingFailed",
187+
"InvalidCustomTlvs",
185188
"ProbeSendingFailed",
186189
"ChannelCreationFailed",
187190
"ChannelClosingFailed",
@@ -275,8 +278,8 @@ enum VssHeaderProviderError {
275278
interface Event {
276279
PaymentSuccessful(PaymentId? payment_id, PaymentHash payment_hash, PaymentPreimage? payment_preimage, u64? fee_paid_msat);
277280
PaymentFailed(PaymentId? payment_id, PaymentHash? payment_hash, PaymentFailureReason? reason);
278-
PaymentReceived(PaymentId? payment_id, PaymentHash payment_hash, u64 amount_msat);
279-
PaymentClaimable(PaymentId payment_id, PaymentHash payment_hash, u64 claimable_amount_msat, u32? claim_deadline);
281+
PaymentReceived(PaymentId? payment_id, PaymentHash payment_hash, u64 amount_msat, sequence<CustomTlvRecord> custom_records);
282+
PaymentClaimable(PaymentId payment_id, PaymentHash payment_hash, u64 claimable_amount_msat, u32? claim_deadline, sequence<CustomTlvRecord> custom_records);
280283
PaymentForwarded(ChannelId prev_channel_id, ChannelId next_channel_id, UserChannelId? prev_user_channel_id, UserChannelId? next_user_channel_id, u64? total_fee_earned_msat, u64? skimmed_fee_msat, boolean claim_from_onchain_tx, u64? outbound_amount_forwarded_msat);
281284
ChannelPending(ChannelId channel_id, UserChannelId user_channel_id, ChannelId former_temporary_channel_id, PublicKey counterparty_node_id, OutPoint funding_txo);
282285
ChannelReady(ChannelId channel_id, UserChannelId user_channel_id, PublicKey? counterparty_node_id);
@@ -362,6 +365,11 @@ dictionary SendingParameters {
362365
u8? max_channel_saturation_power_of_half;
363366
};
364367

368+
dictionary CustomTlvRecord {
369+
u64 type_num;
370+
sequence<u8> value;
371+
};
372+
365373
[Enum]
366374
interface MaxTotalRoutingFeeLimit {
367375
None ();

src/error.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ pub enum Error {
3434
RefundCreationFailed,
3535
/// Sending a payment has failed.
3636
PaymentSendingFailed,
37+
/// Sending of spontaneous payment with custom TLVs failed.
38+
InvalidCustomTlvs,
3739
/// Sending a payment probe has failed.
3840
ProbeSendingFailed,
3941
/// A channel could not be opened.
@@ -130,6 +132,7 @@ impl fmt::Display for Error {
130132
Self::OfferCreationFailed => write!(f, "Failed to create offer."),
131133
Self::RefundCreationFailed => write!(f, "Failed to create refund."),
132134
Self::PaymentSendingFailed => write!(f, "Failed to send the given payment."),
135+
Self::InvalidCustomTlvs => write!(f, "Failed to construct payment with custom TLVs."),
133136
Self::ProbeSendingFailed => write!(f, "Failed to send the given payment probe."),
134137
Self::ChannelCreationFailed => write!(f, "Failed to create channel."),
135138
Self::ChannelClosingFailed => write!(f, "Failed to close channel."),

src/event.rs

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
// http://opensource.org/licenses/MIT>, at your option. You may not use this file except in
66
// accordance with one or both of these licenses.
77

8-
use crate::types::{DynStore, Sweeper, Wallet};
8+
use crate::types::{CustomTlvRecord, DynStore, Sweeper, Wallet};
99

1010
use crate::{
1111
hex_utils, BumpTransactionEventHandler, ChannelManager, Config, Error, Graph, PeerInfo,
@@ -102,6 +102,8 @@ pub enum Event {
102102
payment_hash: PaymentHash,
103103
/// The value, in thousandths of a satoshi, that has been received.
104104
amount_msat: u64,
105+
/// Custom TLV records received on the payment
106+
custom_records: Vec<CustomTlvRecord>,
105107
},
106108
/// A payment has been forwarded.
107109
PaymentForwarded {
@@ -168,6 +170,8 @@ pub enum Event {
168170
/// The block height at which this payment will be failed back and will no longer be
169171
/// eligible for claiming.
170172
claim_deadline: Option<u32>,
173+
/// Custom TLV records attached to the payment
174+
custom_records: Vec<CustomTlvRecord>,
171175
},
172176
/// A channel has been created and is pending confirmation on-chain.
173177
ChannelPending {
@@ -224,6 +228,7 @@ impl_writeable_tlv_based_enum!(Event,
224228
(0, payment_hash, required),
225229
(1, payment_id, option),
226230
(2, amount_msat, required),
231+
(3, custom_records, optional_vec),
227232
},
228233
(3, ChannelReady) => {
229234
(0, channel_id, required),
@@ -248,6 +253,7 @@ impl_writeable_tlv_based_enum!(Event,
248253
(2, payment_id, required),
249254
(4, claimable_amount_msat, required),
250255
(6, claim_deadline, option),
256+
(7, custom_records, optional_vec),
251257
},
252258
(7, PaymentForwarded) => {
253259
(0, prev_channel_id, required),
@@ -542,7 +548,7 @@ where
542548
via_channel_id: _,
543549
via_user_channel_id: _,
544550
claim_deadline,
545-
onion_fields: _,
551+
onion_fields,
546552
counterparty_skimmed_fee_msat,
547553
} => {
548554
let payment_id = PaymentId(payment_hash.0);
@@ -644,11 +650,17 @@ where
644650
"We would have registered the preimage if we knew"
645651
);
646652

653+
let custom_records = onion_fields
654+
.map(|cf| {
655+
cf.custom_tlvs().into_iter().map(|tlv| tlv.into()).collect()
656+
})
657+
.unwrap_or_default();
647658
let event = Event::PaymentClaimable {
648659
payment_id,
649660
payment_hash,
650661
claimable_amount_msat: amount_msat,
651662
claim_deadline,
663+
custom_records,
652664
};
653665
match self.event_queue.add_event(event) {
654666
Ok(_) => return Ok(()),
@@ -799,7 +811,7 @@ where
799811
receiver_node_id: _,
800812
htlcs: _,
801813
sender_intended_total_msat: _,
802-
onion_fields: _,
814+
onion_fields,
803815
} => {
804816
let payment_id = PaymentId(payment_hash.0);
805817
log_info!(
@@ -875,6 +887,9 @@ where
875887
payment_id: Some(payment_id),
876888
payment_hash,
877889
amount_msat,
890+
custom_records: onion_fields
891+
.map(|cf| cf.custom_tlvs().into_iter().map(|tlv| tlv.into()).collect())
892+
.unwrap_or_default(),
878893
};
879894
match self.event_queue.add_event(event) {
880895
Ok(_) => return Ok(()),

src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ use types::{
140140
Broadcaster, BumpTransactionEventHandler, ChainMonitor, ChannelManager, DynStore, Graph,
141141
KeysManager, OnionMessenger, PeerManager, Router, Scorer, Sweeper, Wallet,
142142
};
143-
pub use types::{ChannelDetails, PeerDetails, UserChannelId};
143+
pub use types::{ChannelDetails, CustomTlvRecord, PeerDetails, UserChannelId};
144144

145145
use logger::{log_error, log_info, log_trace, FilesystemLogger, Logger};
146146

src/payment/spontaneous.rs

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use crate::payment::store::{
1414
PaymentDetails, PaymentDirection, PaymentKind, PaymentStatus, PaymentStore,
1515
};
1616
use crate::payment::SendingParameters;
17-
use crate::types::{ChannelManager, KeysManager};
17+
use crate::types::{ChannelManager, CustomTlvRecord, KeysManager};
1818

1919
use lightning::ln::channelmanager::{PaymentId, RecipientOnionFields, Retry, RetryableSendFailure};
2020
use lightning::ln::{PaymentHash, PaymentPreimage};
@@ -58,6 +58,21 @@ impl SpontaneousPayment {
5858
/// node-wide parameters configured via [`Config::sending_parameters`] on a per-field basis.
5959
pub fn send(
6060
&self, amount_msat: u64, node_id: PublicKey, sending_parameters: Option<SendingParameters>,
61+
) -> Result<PaymentId, Error> {
62+
self.send_inner(amount_msat, node_id, sending_parameters, None)
63+
}
64+
65+
/// Send a spontaneous payment including a list of custom TLVs.
66+
pub fn send_with_custom_tlvs(
67+
&self, amount_msat: u64, node_id: PublicKey, sending_parameters: Option<SendingParameters>,
68+
custom_tlvs: Vec<CustomTlvRecord>,
69+
) -> Result<PaymentId, Error> {
70+
self.send_inner(amount_msat, node_id, sending_parameters, Some(custom_tlvs))
71+
}
72+
73+
fn send_inner(
74+
&self, amount_msat: u64, node_id: PublicKey, sending_parameters: Option<SendingParameters>,
75+
custom_tlvs: Option<Vec<CustomTlvRecord>>,
6176
) -> Result<PaymentId, Error> {
6277
let rt_lock = self.runtime.read().unwrap();
6378
if rt_lock.is_none() {
@@ -97,7 +112,15 @@ impl SpontaneousPayment {
97112
.map(|s| route_params.payment_params.max_channel_saturation_power_of_half = s);
98113
};
99114

100-
let recipient_fields = RecipientOnionFields::spontaneous_empty();
115+
let recipient_fields = match custom_tlvs {
116+
Some(tlvs) => RecipientOnionFields::spontaneous_empty()
117+
.with_custom_tlvs(tlvs.into_iter().map(|tlv| (tlv.type_num, tlv.value)).collect())
118+
.map_err(|e| {
119+
log_error!(self.logger, "Failed to send payment with custom TLVs: {:?}", e);
120+
Error::InvalidCustomTlvs
121+
})?,
122+
None => RecipientOnionFields::spontaneous_empty(),
123+
};
101124

102125
match self.channel_manager.send_spontaneous_payment_with_retry(
103126
Some(payment_preimage),

src/types.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use crate::logger::FilesystemLogger;
1212
use crate::message_handler::NodeCustomMessageHandler;
1313

1414
use lightning::chain::chainmonitor;
15+
use lightning::impl_writeable_tlv_based;
1516
use lightning::ln::channel_state::ChannelDetails as LdkChannelDetails;
1617
use lightning::ln::msgs::RoutingMessageHandler;
1718
use lightning::ln::msgs::SocketAddress;
@@ -348,3 +349,23 @@ pub struct PeerDetails {
348349
/// Indicates whether we currently have an active connection with the peer.
349350
pub is_connected: bool,
350351
}
352+
353+
/// Custom TLV entry.
354+
#[derive(Debug, Clone, PartialEq, Eq)]
355+
pub struct CustomTlvRecord {
356+
/// Type number.
357+
pub type_num: u64,
358+
/// Serialized value.
359+
pub value: Vec<u8>,
360+
}
361+
362+
impl_writeable_tlv_based!(CustomTlvRecord, {
363+
(0, type_num, required),
364+
(2, value, required),
365+
});
366+
367+
impl From<&(u64, Vec<u8>)> for CustomTlvRecord {
368+
fn from(tlv: &(u64, Vec<u8>)) -> Self {
369+
CustomTlvRecord { type_num: tlv.0, value: tlv.1.clone() }
370+
}
371+
}

src/uniffi_types.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ pub use crate::config::{
1616
pub use crate::graph::{ChannelInfo, ChannelUpdateInfo, NodeAnnouncementInfo, NodeInfo};
1717
pub use crate::payment::store::{LSPFeeLimits, PaymentDirection, PaymentKind, PaymentStatus};
1818
pub use crate::payment::{MaxTotalRoutingFeeLimit, QrPaymentResult, SendingParameters};
19+
pub use crate::types::CustomTlvRecord;
1920

2021
pub use lightning::chain::channelmonitor::BalanceSource;
2122
pub use lightning::events::{ClosureReason, PaymentFailureReason};

tests/common/mod.rs

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,10 @@
1111
use ldk_node::config::{Config, EsploraSyncConfig};
1212
use ldk_node::io::sqlite_store::SqliteStore;
1313
use ldk_node::payment::{PaymentDirection, PaymentKind, PaymentStatus};
14-
use ldk_node::{Builder, Event, LightningBalance, LogLevel, Node, NodeError, PendingSweepBalance};
14+
use ldk_node::{
15+
Builder, CustomTlvRecord, Event, LightningBalance, LogLevel, Node, NodeError,
16+
PendingSweepBalance,
17+
};
1518

1619
use lightning::ln::msgs::SocketAddress;
1720
use lightning::ln::{PaymentHash, PaymentPreimage};
@@ -751,14 +754,18 @@ pub(crate) fn do_channel_full_cycle<E: ElectrumApi>(
751754
// Test spontaneous/keysend payments
752755
println!("\nA send_spontaneous_payment");
753756
let keysend_amount_msat = 2500_000;
754-
let keysend_payment_id =
755-
node_a.spontaneous_payment().send(keysend_amount_msat, node_b.node_id(), None).unwrap();
757+
let custom_tlvs = vec![CustomTlvRecord { type_num: 13377331, value: vec![1, 2, 3] }];
758+
let keysend_payment_id = node_a
759+
.spontaneous_payment()
760+
.send_with_custom_tlvs(keysend_amount_msat, node_b.node_id(), None, custom_tlvs.clone())
761+
.unwrap();
756762
expect_event!(node_a, PaymentSuccessful);
757-
let received_keysend_amount = match node_b.wait_next_event() {
758-
ref e @ Event::PaymentReceived { amount_msat, .. } => {
763+
let next_event = node_b.wait_next_event();
764+
let (received_keysend_amount, received_custom_records) = match next_event {
765+
ref e @ Event::PaymentReceived { amount_msat, ref custom_records, .. } => {
759766
println!("{} got event {:?}", std::stringify!(node_b), e);
760767
node_b.event_handled();
761-
amount_msat
768+
(amount_msat, custom_records)
762769
},
763770
ref e => {
764771
panic!("{} got unexpected event!: {:?}", std::stringify!(node_b), e);
@@ -772,6 +779,7 @@ pub(crate) fn do_channel_full_cycle<E: ElectrumApi>(
772779
node_a.payment(&keysend_payment_id).unwrap().kind,
773780
PaymentKind::Spontaneous { .. }
774781
));
782+
assert_eq!(received_custom_records, &custom_tlvs);
775783
assert_eq!(node_b.payment(&keysend_payment_id).unwrap().status, PaymentStatus::Succeeded);
776784
assert_eq!(node_b.payment(&keysend_payment_id).unwrap().direction, PaymentDirection::Inbound);
777785
assert_eq!(node_b.payment(&keysend_payment_id).unwrap().amount_msat, Some(keysend_amount_msat));

0 commit comments

Comments
 (0)