Skip to content

Commit 2cdf5fb

Browse files
Implement utilities for keysending to private nodes
1 parent a67dd7f commit 2cdf5fb

File tree

3 files changed

+55
-1
lines changed

3 files changed

+55
-1
lines changed

lightning/src/ln/features.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -420,6 +420,18 @@ impl InvoiceFeatures {
420420
pub(crate) fn to_context<C: sealed::Context>(&self) -> Features<C> {
421421
self.to_context_internal()
422422
}
423+
424+
/// Getting a route for a keysend payment to a private node requires providing the payee's
425+
/// features (since they were not announced in a node announcement). However, keysend payments
426+
/// don't have an invoice to pull the payee's features from, so this method is provided for use in
427+
/// [`get_keysend_route`], thus omitting the need for payers to manually construct an
428+
/// `InvoiceFeatures` for [`get_route`].
429+
///
430+
/// [`get_keysend_route`]: crate::routing::router::get_keysend_route
431+
/// [`get_route`]: crate::routing::router::get_route
432+
pub(crate) fn for_keysend() -> InvoiceFeatures {
433+
InvoiceFeatures::empty().set_variable_length_onion_optional()
434+
}
423435
}
424436

425437
impl ToBase32 for InvoiceFeatures {

lightning/src/ln/functional_tests.rs

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ use ln::channel::{COMMITMENT_TX_BASE_WEIGHT, COMMITMENT_TX_WEIGHT_PER_HTLC};
2222
use ln::channelmanager::{ChannelManager, ChannelManagerReadArgs, RAACommitmentOrder, PaymentSendFailure, BREAKDOWN_TIMEOUT, MIN_CLTV_EXPIRY_DELTA};
2323
use ln::channel::{Channel, ChannelError};
2424
use ln::{chan_utils, onion_utils};
25-
use routing::router::{Route, RouteHop, get_route};
25+
use routing::router::{Route, RouteHop, get_route, get_keysend_route};
2626
use ln::features::{ChannelFeatures, InitFeatures, InvoiceFeatures, NodeFeatures};
2727
use ln::msgs;
2828
use ln::msgs::{ChannelMessageHandler,RoutingMessageHandler,HTLCFailChannelUpdate, ErrorAction};
@@ -9419,3 +9419,33 @@ fn test_keysend_payments_to_public_node() {
94199419
pass_along_path(&nodes[0], &path, 10000, payment_hash, PaymentSecret([0; 32]), event, true, Some(test_preimage));
94209420
claim_payment(&nodes[0], &path, test_preimage);
94219421
}
9422+
9423+
#[test]
9424+
fn test_keysend_payments_to_private_node() {
9425+
let chanmon_cfgs = create_chanmon_cfgs(2);
9426+
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
9427+
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
9428+
let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
9429+
9430+
let payer_pubkey = nodes[0].node.get_our_node_id();
9431+
let payee_pubkey = nodes[1].node.get_our_node_id();
9432+
nodes[0].node.peer_connected(&payee_pubkey, &msgs::Init { features: InitFeatures::known() });
9433+
nodes[1].node.peer_connected(&payer_pubkey, &msgs::Init { features: InitFeatures::known() });
9434+
9435+
let _chan = create_chan_between_nodes(&nodes[0], &nodes[1], InitFeatures::known(), InitFeatures::known());
9436+
let network_graph = nodes[0].net_graph_msg_handler.network_graph.read().unwrap();
9437+
let first_hops = nodes[0].node.list_usable_channels();
9438+
let route = get_keysend_route(&payer_pubkey, &network_graph, &payee_pubkey,
9439+
Some(&first_hops.iter().collect::<Vec<_>>()), &vec![], 10000, 40,
9440+
nodes[0].logger).unwrap();
9441+
9442+
let test_preimage = PaymentPreimage([42; 32]);
9443+
let payment_hash = nodes[0].node.send_spontaneous_payment(&route, Some(test_preimage)).unwrap();
9444+
check_added_monitors!(nodes[0], 1);
9445+
let mut events = nodes[0].node.get_and_clear_pending_msg_events();
9446+
assert_eq!(events.len(), 1);
9447+
let event = events.pop().unwrap();
9448+
let path = vec![&nodes[1]];
9449+
pass_along_path(&nodes[0], &path, 10000, payment_hash, PaymentSecret([0; 32]), event, true, Some(test_preimage));
9450+
claim_payment(&nodes[0], &path, test_preimage);
9451+
}

lightning/src/routing/router.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,18 @@ fn compute_fees(amount_msat: u64, channel_fees: RoutingFees) -> Option<u64> {
327327
}
328328
}
329329

330+
/// Gets a keysend route from us (payer) to the given target node (payee). This is needed because
331+
/// keysend payments do not have an invoice from which to pull the payee's supported features, which
332+
/// makes it tricky to otherwise supply the `payee_features` parameter of `get_route`.
333+
pub fn get_keysend_route<L: Deref>(our_node_id: &PublicKey, network: &NetworkGraph, payee:
334+
&PublicKey, first_hops: Option<&[&ChannelDetails]>, last_hops: &[&RouteHint],
335+
final_value_msat: u64, final_cltv: u32, logger: L) -> Result<Route,
336+
LightningError> where L::Target: Logger {
337+
let invoice_features = InvoiceFeatures::for_keysend();
338+
get_route(our_node_id, network, payee, Some(invoice_features), first_hops, last_hops,
339+
final_value_msat, final_cltv, logger)
340+
}
341+
330342
/// Gets a route from us (payer) to the given target node (payee).
331343
///
332344
/// If the payee provided features in their invoice, they should be provided via payee_features.

0 commit comments

Comments
 (0)