Skip to content

Commit

Permalink
spontaneous payment send_with_custom_tlv
Browse files Browse the repository at this point in the history
  • Loading branch information
Evanfeenstra committed Dec 2, 2024
1 parent c605781 commit aae4ed1
Show file tree
Hide file tree
Showing 8 changed files with 93 additions and 14 deletions.
12 changes: 10 additions & 2 deletions bindings/ldk_node.udl
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,8 @@ interface SpontaneousPayment {
[Throws=NodeError]
PaymentId send(u64 amount_msat, PublicKey node_id, SendingParameters? sending_parameters);
[Throws=NodeError]
PaymentId send_with_custom_tlvs(u64 amount_msat, PublicKey node_id, SendingParameters? sending_parameters, sequence<CustomTlvRecord> custom_tlvs);
[Throws=NodeError]
void send_probes(u64 amount_msat, PublicKey node_id);
};

Expand Down Expand Up @@ -182,6 +184,7 @@ enum NodeError {
"OfferCreationFailed",
"RefundCreationFailed",
"PaymentSendingFailed",
"InvalidCustomTlvs",
"ProbeSendingFailed",
"ChannelCreationFailed",
"ChannelClosingFailed",
Expand Down Expand Up @@ -275,8 +278,8 @@ enum VssHeaderProviderError {
interface Event {
PaymentSuccessful(PaymentId? payment_id, PaymentHash payment_hash, PaymentPreimage? payment_preimage, u64? fee_paid_msat);
PaymentFailed(PaymentId? payment_id, PaymentHash? payment_hash, PaymentFailureReason? reason);
PaymentReceived(PaymentId? payment_id, PaymentHash payment_hash, u64 amount_msat);
PaymentClaimable(PaymentId payment_id, PaymentHash payment_hash, u64 claimable_amount_msat, u32? claim_deadline);
PaymentReceived(PaymentId? payment_id, PaymentHash payment_hash, u64 amount_msat, sequence<CustomTlvRecord>? custom_records);
PaymentClaimable(PaymentId payment_id, PaymentHash payment_hash, u64 claimable_amount_msat, u32? claim_deadline, sequence<CustomTlvRecord>? custom_records);
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);
ChannelPending(ChannelId channel_id, UserChannelId user_channel_id, ChannelId former_temporary_channel_id, PublicKey counterparty_node_id, OutPoint funding_txo);
ChannelReady(ChannelId channel_id, UserChannelId user_channel_id, PublicKey? counterparty_node_id);
Expand Down Expand Up @@ -362,6 +365,11 @@ dictionary SendingParameters {
u8? max_channel_saturation_power_of_half;
};

dictionary CustomTlvRecord {
u64 kind;
sequence<u8> value;
};

[Enum]
interface MaxTotalRoutingFeeLimit {
None ();
Expand Down
3 changes: 3 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ pub enum Error {
RefundCreationFailed,
/// Sending a payment has failed.
PaymentSendingFailed,
/// Sending of spontaneous payment with custom TLVs failed.
InvalidCustomTlvs,
/// Sending a payment probe has failed.
ProbeSendingFailed,
/// A channel could not be opened.
Expand Down Expand Up @@ -130,6 +132,7 @@ impl fmt::Display for Error {
Self::OfferCreationFailed => write!(f, "Failed to create offer."),
Self::RefundCreationFailed => write!(f, "Failed to create refund."),
Self::PaymentSendingFailed => write!(f, "Failed to send the given payment."),
Self::InvalidCustomTlvs => write!(f, "Failed to construct payment with custom TLVs."),
Self::ProbeSendingFailed => write!(f, "Failed to send the given payment probe."),
Self::ChannelCreationFailed => write!(f, "Failed to create channel."),
Self::ChannelClosingFailed => write!(f, "Failed to close channel."),
Expand Down
21 changes: 18 additions & 3 deletions src/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
// http://opensource.org/licenses/MIT>, at your option. You may not use this file except in
// accordance with one or both of these licenses.

use crate::types::{DynStore, Sweeper, Wallet};
use crate::types::{CustomTlvRecord, DynStore, Sweeper, Wallet};

use crate::{
hex_utils, BumpTransactionEventHandler, ChannelManager, Config, Error, Graph, PeerInfo,
Expand Down Expand Up @@ -102,6 +102,8 @@ pub enum Event {
payment_hash: PaymentHash,
/// The value, in thousandths of a satoshi, that has been received.
amount_msat: u64,
/// Custom TLV records received on the payment
custom_records: Vec<CustomTlvRecord>,
},
/// A payment has been forwarded.
PaymentForwarded {
Expand Down Expand Up @@ -168,6 +170,8 @@ pub enum Event {
/// The block height at which this payment will be failed back and will no longer be
/// eligible for claiming.
claim_deadline: Option<u32>,
/// Custom TLV records attached to the payment
custom_records: Vec<CustomTlvRecord>,
},
/// A channel has been created and is pending confirmation on-chain.
ChannelPending {
Expand Down Expand Up @@ -224,6 +228,7 @@ impl_writeable_tlv_based_enum!(Event,
(0, payment_hash, required),
(1, payment_id, option),
(2, amount_msat, required),
(3, custom_records, optional_vec),
},
(3, ChannelReady) => {
(0, channel_id, required),
Expand All @@ -248,6 +253,7 @@ impl_writeable_tlv_based_enum!(Event,
(2, payment_id, required),
(4, claimable_amount_msat, required),
(6, claim_deadline, option),
(8, custom_records, optional_vec),
},
(7, PaymentForwarded) => {
(0, prev_channel_id, required),
Expand Down Expand Up @@ -542,7 +548,7 @@ where
via_channel_id: _,
via_user_channel_id: _,
claim_deadline,
onion_fields: _,
onion_fields,
counterparty_skimmed_fee_msat,
} => {
let payment_id = PaymentId(payment_hash.0);
Expand Down Expand Up @@ -644,11 +650,17 @@ where
"We would have registered the preimage if we knew"
);

let custom_records = onion_fields
.map(|cf| {
cf.custom_tlvs().into_iter().map(|tlv| tlv.into()).collect()
})
.unwrap_or_default();
let event = Event::PaymentClaimable {
payment_id,
payment_hash,
claimable_amount_msat: amount_msat,
claim_deadline,
custom_records,
};
match self.event_queue.add_event(event) {
Ok(_) => return Ok(()),
Expand Down Expand Up @@ -799,7 +811,7 @@ where
receiver_node_id: _,
htlcs: _,
sender_intended_total_msat: _,
onion_fields: _,
onion_fields,
} => {
let payment_id = PaymentId(payment_hash.0);
log_info!(
Expand Down Expand Up @@ -875,6 +887,9 @@ where
payment_id: Some(payment_id),
payment_hash,
amount_msat,
custom_records: onion_fields
.map(|cf| cf.custom_tlvs().into_iter().map(|tlv| tlv.into()).collect())
.unwrap_or_default(),
};
match self.event_queue.add_event(event) {
Ok(_) => return Ok(()),
Expand Down
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ use types::{
Broadcaster, BumpTransactionEventHandler, ChainMonitor, ChannelManager, DynStore, Graph,
KeysManager, OnionMessenger, PeerManager, Router, Scorer, Sweeper, Wallet,
};
pub use types::{ChannelDetails, PeerDetails, UserChannelId};
pub use types::{ChannelDetails, PeerDetails, CustomTlvRecord, UserChannelId};

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

Expand Down
27 changes: 25 additions & 2 deletions src/payment/spontaneous.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use crate::payment::store::{
PaymentDetails, PaymentDirection, PaymentKind, PaymentStatus, PaymentStore,
};
use crate::payment::SendingParameters;
use crate::types::{ChannelManager, KeysManager};
use crate::types::{ChannelManager, CustomTlvRecord, KeysManager};

use lightning::ln::channelmanager::{PaymentId, RecipientOnionFields, Retry, RetryableSendFailure};
use lightning::ln::{PaymentHash, PaymentPreimage};
Expand Down Expand Up @@ -58,6 +58,21 @@ impl SpontaneousPayment {
/// node-wide parameters configured via [`Config::sending_parameters`] on a per-field basis.
pub fn send(
&self, amount_msat: u64, node_id: PublicKey, sending_parameters: Option<SendingParameters>,
) -> Result<PaymentId, Error> {
self.send_inner(amount_msat, node_id, sending_parameters, None)
}

/// Send a spontaneous payment including a list of custom TLVs.
pub fn send_with_custom_tlvs(
&self, amount_msat: u64, node_id: PublicKey, sending_parameters: Option<SendingParameters>,
custom_tlvs: Vec<CustomTlvRecord>,
) -> Result<PaymentId, Error> {
self.send_inner(amount_msat, node_id, sending_parameters, Some(custom_tlvs))
}

fn send_inner(
&self, amount_msat: u64, node_id: PublicKey, sending_parameters: Option<SendingParameters>,
custom_tlvs: Option<Vec<CustomTlvRecord>>,
) -> Result<PaymentId, Error> {
let rt_lock = self.runtime.read().unwrap();
if rt_lock.is_none() {
Expand Down Expand Up @@ -97,7 +112,15 @@ impl SpontaneousPayment {
.map(|s| route_params.payment_params.max_channel_saturation_power_of_half = s);
};

let recipient_fields = RecipientOnionFields::spontaneous_empty();
let recipient_fields = match custom_tlvs {
Some(tlvs) => RecipientOnionFields::spontaneous_empty()
.with_custom_tlvs(tlvs.into_iter().map(|tlv| (tlv.kind, tlv.value)).collect())
.map_err(|e| {
log_error!(self.logger, "Failed to send payment with custom TLVs: {:?}", e);
Error::InvalidCustomTlvs
})?,
None => RecipientOnionFields::spontaneous_empty(),
};

match self.channel_manager.send_spontaneous_payment_with_retry(
Some(payment_preimage),
Expand Down
21 changes: 21 additions & 0 deletions src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use crate::logger::FilesystemLogger;
use crate::message_handler::NodeCustomMessageHandler;

use lightning::chain::chainmonitor;
use lightning::impl_writeable_tlv_based;
use lightning::ln::channel_state::ChannelDetails as LdkChannelDetails;
use lightning::ln::msgs::RoutingMessageHandler;
use lightning::ln::msgs::SocketAddress;
Expand Down Expand Up @@ -348,3 +349,23 @@ pub struct PeerDetails {
/// Indicates whether we currently have an active connection with the peer.
pub is_connected: bool,
}

/// Custom TLV entry.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CustomTlvRecord {
/// Type number.
pub kind: u64,
/// Serialized value.
pub value: Vec<u8>,
}

impl_writeable_tlv_based!(CustomTlvRecord, {
(0, kind, required),
(1, value, required),
});

impl From<&(u64, Vec<u8>)> for CustomTlvRecord {
fn from(tlv: &(u64, Vec<u8>)) -> Self {
CustomTlvRecord { kind: tlv.0, value: tlv.1.clone() }
}
}
1 change: 1 addition & 0 deletions src/uniffi_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ pub use crate::config::{
pub use crate::graph::{ChannelInfo, ChannelUpdateInfo, NodeAnnouncementInfo, NodeInfo};
pub use crate::payment::store::{LSPFeeLimits, PaymentDirection, PaymentKind, PaymentStatus};
pub use crate::payment::{MaxTotalRoutingFeeLimit, QrPaymentResult, SendingParameters};
pub use crate::types::CustomTlvRecord;

pub use lightning::chain::channelmonitor::BalanceSource;
pub use lightning::events::{ClosureReason, PaymentFailureReason};
Expand Down
20 changes: 14 additions & 6 deletions tests/common/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@
use ldk_node::config::{Config, EsploraSyncConfig};
use ldk_node::io::sqlite_store::SqliteStore;
use ldk_node::payment::{PaymentDirection, PaymentKind, PaymentStatus};
use ldk_node::{Builder, Event, LightningBalance, LogLevel, Node, NodeError, PendingSweepBalance};
use ldk_node::{
Builder, CustomTlvRecord, Event, LightningBalance, LogLevel, Node, NodeError,
PendingSweepBalance,
};

use lightning::ln::msgs::SocketAddress;
use lightning::ln::{PaymentHash, PaymentPreimage};
Expand Down Expand Up @@ -751,14 +754,18 @@ pub(crate) fn do_channel_full_cycle<E: ElectrumApi>(
// Test spontaneous/keysend payments
println!("\nA send_spontaneous_payment");
let keysend_amount_msat = 2500_000;
let keysend_payment_id =
node_a.spontaneous_payment().send(keysend_amount_msat, node_b.node_id(), None).unwrap();
let custom_tlvs = vec![CustomTlvRecord { kind: 13377331, value: vec![1, 2, 3] }];
let keysend_payment_id = node_a
.spontaneous_payment()
.send_with_custom_tlvs(keysend_amount_msat, node_b.node_id(), None, custom_tlvs.clone())
.unwrap();
expect_event!(node_a, PaymentSuccessful);
let received_keysend_amount = match node_b.wait_next_event() {
ref e @ Event::PaymentReceived { amount_msat, .. } => {
let next_event = node_b.wait_next_event();
let (received_keysend_amount, received_custom_records) = match next_event {
ref e @ Event::PaymentReceived { amount_msat, ref custom_records, .. } => {
println!("{} got event {:?}", std::stringify!(node_b), e);
node_b.event_handled();
amount_msat
(amount_msat, custom_records)
},
ref e => {
panic!("{} got unexpected event!: {:?}", std::stringify!(node_b), e);
Expand All @@ -772,6 +779,7 @@ pub(crate) fn do_channel_full_cycle<E: ElectrumApi>(
node_a.payment(&keysend_payment_id).unwrap().kind,
PaymentKind::Spontaneous { .. }
));
assert_eq!(received_custom_records, &custom_tlvs);
assert_eq!(node_b.payment(&keysend_payment_id).unwrap().status, PaymentStatus::Succeeded);
assert_eq!(node_b.payment(&keysend_payment_id).unwrap().direction, PaymentDirection::Inbound);
assert_eq!(node_b.payment(&keysend_payment_id).unwrap().amount_msat, Some(keysend_amount_msat));
Expand Down

0 comments on commit aae4ed1

Please sign in to comment.