Skip to content

Fix keysend not being sent in OutboundPayment::send_payment_internal #2475

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 1 addition & 68 deletions lightning/src/ln/functional_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ use crate::ln::channel::{DISCONNECT_PEER_AWAITING_RESPONSE_TICKS, ChannelError};
use crate::ln::{chan_utils, onion_utils};
use crate::ln::chan_utils::{OFFERED_HTLC_SCRIPT_WEIGHT, htlc_success_tx_weight, htlc_timeout_tx_weight, HTLCOutputInCommitment};
use crate::routing::gossip::{NetworkGraph, NetworkUpdate};
use crate::routing::router::{Path, PaymentParameters, Route, RouteHop, RouteParameters, find_route, get_route};
use crate::routing::router::{Path, PaymentParameters, Route, RouteHop, get_route};
use crate::ln::features::{ChannelFeatures, ChannelTypeFeatures, NodeFeatures};
use crate::ln::msgs;
use crate::ln::msgs::{ChannelMessageHandler, RoutingMessageHandler, ErrorAction};
Expand Down Expand Up @@ -9408,73 +9408,6 @@ fn test_inconsistent_mpp_params() {
expect_payment_sent(&nodes[0], our_payment_preimage, Some(None), true);
}

#[test]
fn test_keysend_payments_to_public_node() {
let chanmon_cfgs = create_chanmon_cfgs(2);
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
let nodes = create_network(2, &node_cfgs, &node_chanmgrs);

let _chan = create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 100000, 10001);
let network_graph = nodes[0].network_graph.clone();
let payer_pubkey = nodes[0].node.get_our_node_id();
let payee_pubkey = nodes[1].node.get_our_node_id();
let route_params = RouteParameters {
payment_params: PaymentParameters::for_keysend(payee_pubkey, 40, false),
final_value_msat: 10000,
};
let scorer = test_utils::TestScorer::new();
let random_seed_bytes = chanmon_cfgs[1].keys_manager.get_secure_random_bytes();
let route = find_route(&payer_pubkey, &route_params, &network_graph, None, nodes[0].logger, &scorer, &(), &random_seed_bytes).unwrap();

let test_preimage = PaymentPreimage([42; 32]);
let payment_hash = nodes[0].node.send_spontaneous_payment(&route, Some(test_preimage),
RecipientOnionFields::spontaneous_empty(), PaymentId(test_preimage.0)).unwrap();
check_added_monitors!(nodes[0], 1);
let mut events = nodes[0].node.get_and_clear_pending_msg_events();
assert_eq!(events.len(), 1);
let event = events.pop().unwrap();
let path = vec![&nodes[1]];
pass_along_path(&nodes[0], &path, 10000, payment_hash, None, event, true, Some(test_preimage));
claim_payment(&nodes[0], &path, test_preimage);
}

#[test]
fn test_keysend_payments_to_private_node() {
let chanmon_cfgs = create_chanmon_cfgs(2);
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
let nodes = create_network(2, &node_cfgs, &node_chanmgrs);

let payer_pubkey = nodes[0].node.get_our_node_id();
let payee_pubkey = nodes[1].node.get_our_node_id();

let _chan = create_chan_between_nodes(&nodes[0], &nodes[1]);
let route_params = RouteParameters {
payment_params: PaymentParameters::for_keysend(payee_pubkey, 40, false),
final_value_msat: 10000,
};
let network_graph = nodes[0].network_graph.clone();
let first_hops = nodes[0].node.list_usable_channels();
let scorer = test_utils::TestScorer::new();
let random_seed_bytes = chanmon_cfgs[1].keys_manager.get_secure_random_bytes();
let route = find_route(
&payer_pubkey, &route_params, &network_graph, Some(&first_hops.iter().collect::<Vec<_>>()),
nodes[0].logger, &scorer, &(), &random_seed_bytes
).unwrap();

let test_preimage = PaymentPreimage([42; 32]);
let payment_hash = nodes[0].node.send_spontaneous_payment(&route, Some(test_preimage),
RecipientOnionFields::spontaneous_empty(), PaymentId(test_preimage.0)).unwrap();
check_added_monitors!(nodes[0], 1);
let mut events = nodes[0].node.get_and_clear_pending_msg_events();
assert_eq!(events.len(), 1);
let event = events.pop().unwrap();
let path = vec![&nodes[1]];
pass_along_path(&nodes[0], &path, 10000, payment_hash, None, event, true, Some(test_preimage));
claim_payment(&nodes[0], &path, test_preimage);
}

#[test]
fn test_double_partial_claim() {
// Test what happens if a node receives a payment, generates a PaymentClaimable event, the HTLCs
Expand Down
2 changes: 1 addition & 1 deletion lightning/src/ln/outbound_payment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -684,7 +684,7 @@ impl OutboundPayments {
Some(route_params.payment_params.clone()), entropy_source, best_block_height)
.map_err(|_| RetryableSendFailure::DuplicatePayment)?;

let res = self.pay_route_internal(&route, payment_hash, recipient_onion, None, payment_id, None,
let res = self.pay_route_internal(&route, payment_hash, recipient_onion, keysend_preimage, payment_id, None,
onion_session_privs, node_signer, best_block_height, &send_payment_along_path);
log_info!(logger, "Result sending payment with id {}: {:?}", log_bytes!(payment_id.0), res);
if let Err(e) = res {
Expand Down
56 changes: 56 additions & 0 deletions lightning/src/ln/payment_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,62 @@ fn mpp_receive_timeout() {
do_mpp_receive_timeout(false);
}

#[test]
fn test_keysend_payments() {
do_test_keysend_payments(false, false);
do_test_keysend_payments(false, true);
do_test_keysend_payments(true, false);
do_test_keysend_payments(true, true);
}

fn do_test_keysend_payments(public_node: bool, with_retry: bool) {
let chanmon_cfgs = create_chanmon_cfgs(2);
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
let nodes = create_network(2, &node_cfgs, &node_chanmgrs);

if public_node {
create_announced_chan_between_nodes(&nodes, 0, 1);
} else {
create_chan_between_nodes(&nodes[0], &nodes[1]);
}
let payer_pubkey = nodes[0].node.get_our_node_id();
let payee_pubkey = nodes[1].node.get_our_node_id();
let route_params = RouteParameters {
payment_params: PaymentParameters::for_keysend(payee_pubkey, 40, false),
final_value_msat: 10000,
};

let network_graph = nodes[0].network_graph.clone();
let channels = nodes[0].node.list_usable_channels();
let first_hops = channels.iter().collect::<Vec<_>>();
let first_hops = if public_node { None } else { Some(first_hops.as_slice()) };

let scorer = test_utils::TestScorer::new();
let random_seed_bytes = chanmon_cfgs[1].keys_manager.get_secure_random_bytes();
let route = find_route(
&payer_pubkey, &route_params, &network_graph, first_hops,
nodes[0].logger, &scorer, &(), &random_seed_bytes
).unwrap();

let test_preimage = PaymentPreimage([42; 32]);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The real issue is that the test just reused the preimage instead of extracting it from the event. Can you put the preimage and send in a {}, that way the claiming logic further down has no access to it at all.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that's okay since pass_along_path checks that the preimage from Event::PaymentClaimable matches the expected one passed in?

I had mainly noticed that a problem was that we never tested a full payment using spontaneous_with_retry which uses OutboundPayment::send_payment_internal whereas the method without retries doesn't

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not really a big deal, I wrote up more of what I was suggesting as #2481, which I'm fine with not merging if anyone disagrees, but I like the explicit scope to demonstrate information lifetime.

let payment_hash = if with_retry {
nodes[0].node.send_spontaneous_payment_with_retry(Some(test_preimage),
RecipientOnionFields::spontaneous_empty(), PaymentId(test_preimage.0),
route_params, Retry::Attempts(1)).unwrap()
} else {
nodes[0].node.send_spontaneous_payment(&route, Some(test_preimage),
RecipientOnionFields::spontaneous_empty(), PaymentId(test_preimage.0)).unwrap()
};
check_added_monitors!(nodes[0], 1);
let mut events = nodes[0].node.get_and_clear_pending_msg_events();
assert_eq!(events.len(), 1);
let event = events.pop().unwrap();
let path = vec![&nodes[1]];
pass_along_path(&nodes[0], &path, 10000, payment_hash, None, event, true, Some(test_preimage));
claim_payment(&nodes[0], &path, test_preimage);
}

#[test]
fn test_mpp_keysend() {
let mut mpp_keysend_config = test_default_channel_config();
Expand Down