Skip to content

Commit 7ec16e5

Browse files
authored
Merge pull request #441 from TheBlueMatt/2020-01-mpp
Multi-Path Payments
2 parents e8a8fd0 + 59b1bf6 commit 7ec16e5

14 files changed

+1394
-799
lines changed

fuzz/src/chanmon_consistency.rs

Lines changed: 56 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ use lightning::chain::chaininterface::{BroadcasterInterface,ConfirmationTarget,C
2727
use lightning::chain::keysinterface::{KeysInterface, InMemoryChannelKeys};
2828
use lightning::ln::channelmonitor;
2929
use lightning::ln::channelmonitor::{ChannelMonitor, ChannelMonitorUpdateErr, HTLCUpdate};
30-
use lightning::ln::channelmanager::{ChannelManager, PaymentHash, PaymentPreimage, ChannelManagerReadArgs};
30+
use lightning::ln::channelmanager::{ChannelManager, PaymentHash, PaymentPreimage, PaymentSecret, ChannelManagerReadArgs};
3131
use lightning::ln::router::{Route, RouteHop};
3232
use lightning::ln::features::{ChannelFeatures, InitFeatures, NodeFeatures};
3333
use lightning::ln::msgs::{CommitmentUpdate, ChannelMessageHandler, ErrorAction, UpdateAddHTLC, Init};
@@ -408,25 +408,25 @@ pub fn do_test(data: &[u8]) {
408408
($source: expr, $dest: expr) => { {
409409
let payment_hash = Sha256::hash(&[payment_id; 1]);
410410
payment_id = payment_id.wrapping_add(1);
411-
if let Err(_) = $source.send_payment(Route {
412-
hops: vec![RouteHop {
411+
if let Err(_) = $source.send_payment(&Route {
412+
paths: vec![vec![RouteHop {
413413
pubkey: $dest.0.get_our_node_id(),
414414
node_features: NodeFeatures::empty(),
415415
short_channel_id: $dest.1,
416416
channel_features: ChannelFeatures::empty(),
417417
fee_msat: 5000000,
418418
cltv_expiry_delta: 200,
419-
}],
420-
}, PaymentHash(payment_hash.into_inner())) {
419+
}]],
420+
}, PaymentHash(payment_hash.into_inner()), &None) {
421421
// Probably ran out of funds
422422
test_return!();
423423
}
424424
} };
425425
($source: expr, $middle: expr, $dest: expr) => { {
426426
let payment_hash = Sha256::hash(&[payment_id; 1]);
427427
payment_id = payment_id.wrapping_add(1);
428-
if let Err(_) = $source.send_payment(Route {
429-
hops: vec![RouteHop {
428+
if let Err(_) = $source.send_payment(&Route {
429+
paths: vec![vec![RouteHop {
430430
pubkey: $middle.0.get_our_node_id(),
431431
node_features: NodeFeatures::empty(),
432432
short_channel_id: $middle.1,
@@ -440,8 +440,50 @@ pub fn do_test(data: &[u8]) {
440440
channel_features: ChannelFeatures::empty(),
441441
fee_msat: 5000000,
442442
cltv_expiry_delta: 200,
443-
}],
444-
}, PaymentHash(payment_hash.into_inner())) {
443+
}]],
444+
}, PaymentHash(payment_hash.into_inner()), &None) {
445+
// Probably ran out of funds
446+
test_return!();
447+
}
448+
} }
449+
}
450+
macro_rules! send_payment_with_secret {
451+
($source: expr, $middle: expr, $dest: expr) => { {
452+
let payment_hash = Sha256::hash(&[payment_id; 1]);
453+
payment_id = payment_id.wrapping_add(1);
454+
let payment_secret = Sha256::hash(&[payment_id; 1]);
455+
payment_id = payment_id.wrapping_add(1);
456+
if let Err(_) = $source.send_payment(&Route {
457+
paths: vec![vec![RouteHop {
458+
pubkey: $middle.0.get_our_node_id(),
459+
node_features: NodeFeatures::empty(),
460+
short_channel_id: $middle.1,
461+
channel_features: ChannelFeatures::empty(),
462+
fee_msat: 50000,
463+
cltv_expiry_delta: 100,
464+
},RouteHop {
465+
pubkey: $dest.0.get_our_node_id(),
466+
node_features: NodeFeatures::empty(),
467+
short_channel_id: $dest.1,
468+
channel_features: ChannelFeatures::empty(),
469+
fee_msat: 5000000,
470+
cltv_expiry_delta: 200,
471+
}],vec![RouteHop {
472+
pubkey: $middle.0.get_our_node_id(),
473+
node_features: NodeFeatures::empty(),
474+
short_channel_id: $middle.1,
475+
channel_features: ChannelFeatures::empty(),
476+
fee_msat: 50000,
477+
cltv_expiry_delta: 100,
478+
},RouteHop {
479+
pubkey: $dest.0.get_our_node_id(),
480+
node_features: NodeFeatures::empty(),
481+
short_channel_id: $dest.1,
482+
channel_features: ChannelFeatures::empty(),
483+
fee_msat: 5000000,
484+
cltv_expiry_delta: 200,
485+
}]],
486+
}, PaymentHash(payment_hash.into_inner()), &Some(PaymentSecret(payment_secret.into_inner()))) {
445487
// Probably ran out of funds
446488
test_return!();
447489
}
@@ -599,12 +641,12 @@ pub fn do_test(data: &[u8]) {
599641
});
600642
for event in events.drain(..) {
601643
match event {
602-
events::Event::PaymentReceived { payment_hash, .. } => {
644+
events::Event::PaymentReceived { payment_hash, payment_secret, .. } => {
603645
if claim_set.insert(payment_hash.0) {
604646
if $fail {
605-
assert!(nodes[$node].fail_htlc_backwards(&payment_hash));
647+
assert!(nodes[$node].fail_htlc_backwards(&payment_hash, &payment_secret));
606648
} else {
607-
assert!(nodes[$node].claim_funds(PaymentPreimage(payment_hash.0), 5_000_000));
649+
assert!(nodes[$node].claim_funds(PaymentPreimage(payment_hash.0), &payment_secret, 5_000_000));
608650
}
609651
}
610652
},
@@ -734,6 +776,8 @@ pub fn do_test(data: &[u8]) {
734776
nodes[2] = node_c.clone();
735777
monitor_c = new_monitor_c;
736778
},
779+
0x22 => send_payment_with_secret!(nodes[0], (&nodes[1], chan_a), (&nodes[2], chan_b)),
780+
0x23 => send_payment_with_secret!(nodes[2], (&nodes[1], chan_b), (&nodes[0], chan_a)),
737781
// 0x24 defined above
738782
_ => test_return!(),
739783
}

fuzz/src/full_stack.rs

Lines changed: 32 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ use lightning::chain::chaininterface::{BroadcasterInterface,ConfirmationTarget,C
2222
use lightning::chain::transaction::OutPoint;
2323
use lightning::chain::keysinterface::{InMemoryChannelKeys, KeysInterface};
2424
use lightning::ln::channelmonitor;
25-
use lightning::ln::channelmanager::{ChannelManager, PaymentHash, PaymentPreimage};
25+
use lightning::ln::channelmanager::{ChannelManager, PaymentHash, PaymentPreimage, PaymentSecret};
2626
use lightning::ln::peer_handler::{MessageHandler,PeerManager,SocketDescriptor};
2727
use lightning::ln::router::Router;
2828
use lightning::util::events::{EventsProvider,Event};
@@ -343,7 +343,7 @@ pub fn do_test(data: &[u8], logger: &Arc<dyn Logger>) {
343343
}, our_network_key, &[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 0], Arc::clone(&logger)));
344344

345345
let mut should_forward = false;
346-
let mut payments_received: Vec<(PaymentHash, u64)> = Vec::new();
346+
let mut payments_received: Vec<(PaymentHash, Option<PaymentSecret>, u64)> = Vec::new();
347347
let mut payments_sent = 0;
348348
let mut pending_funding_generation: Vec<([u8; 32], u64, Script)> = Vec::new();
349349
let mut pending_funding_signatures = HashMap::new();
@@ -401,7 +401,28 @@ pub fn do_test(data: &[u8], logger: &Arc<dyn Logger>) {
401401
sha.input(&payment_hash.0[..]);
402402
payment_hash.0 = Sha256::from_engine(sha).into_inner();
403403
payments_sent += 1;
404-
match channelmanager.send_payment(route, payment_hash) {
404+
match channelmanager.send_payment(&route, payment_hash, &None) {
405+
Ok(_) => {},
406+
Err(_) => return,
407+
}
408+
},
409+
15 => {
410+
let value = slice_to_be24(get_slice!(3)) as u64;
411+
let mut route = match router.get_route(&get_pubkey!(), None, &Vec::new(), value, 42) {
412+
Ok(route) => route,
413+
Err(_) => return,
414+
};
415+
route.paths.push(route.paths[0].clone());
416+
let mut payment_hash = PaymentHash([0; 32]);
417+
payment_hash.0[0..8].copy_from_slice(&be64_to_array(payments_sent));
418+
let mut sha = Sha256::engine();
419+
sha.input(&payment_hash.0[..]);
420+
payment_hash.0 = Sha256::from_engine(sha).into_inner();
421+
payments_sent += 1;
422+
let mut payment_secret = PaymentSecret([0; 32]);
423+
payment_secret.0[0..8].copy_from_slice(&be64_to_array(payments_sent));
424+
payments_sent += 1;
425+
match channelmanager.send_payment(&route, payment_hash, &Some(payment_secret)) {
405426
Ok(_) => {},
406427
Err(_) => return,
407428
}
@@ -428,23 +449,23 @@ pub fn do_test(data: &[u8], logger: &Arc<dyn Logger>) {
428449
}
429450
},
430451
8 => {
431-
for (payment, amt) in payments_received.drain(..) {
452+
for (payment, payment_secret, amt) in payments_received.drain(..) {
432453
// SHA256 is defined as XOR of all input bytes placed in the first byte, and 0s
433454
// for the remaining bytes. Thus, if not all remaining bytes are 0s we cannot
434455
// fulfill this HTLC, but if they are, we can just take the first byte and
435456
// place that anywhere in our preimage.
436457
if &payment.0[1..] != &[0; 31] {
437-
channelmanager.fail_htlc_backwards(&payment);
458+
channelmanager.fail_htlc_backwards(&payment, &payment_secret);
438459
} else {
439460
let mut payment_preimage = PaymentPreimage([0; 32]);
440461
payment_preimage.0[0] = payment.0[0];
441-
channelmanager.claim_funds(payment_preimage, amt);
462+
channelmanager.claim_funds(payment_preimage, &payment_secret, amt);
442463
}
443464
}
444465
},
445466
9 => {
446-
for (payment, _) in payments_received.drain(..) {
447-
channelmanager.fail_htlc_backwards(&payment);
467+
for (payment, payment_secret, _) in payments_received.drain(..) {
468+
channelmanager.fail_htlc_backwards(&payment, &payment_secret);
448469
}
449470
},
450471
10 => {
@@ -513,6 +534,7 @@ pub fn do_test(data: &[u8], logger: &Arc<dyn Logger>) {
513534
channels.sort_by(|a, b| { a.channel_id.cmp(&b.channel_id) });
514535
channelmanager.force_close_channel(&channels[channel_id].channel_id);
515536
},
537+
// 15 is above
516538
_ => return,
517539
}
518540
loss_detector.handler.process_events();
@@ -524,9 +546,9 @@ pub fn do_test(data: &[u8], logger: &Arc<dyn Logger>) {
524546
Event::FundingBroadcastSafe { funding_txo, .. } => {
525547
pending_funding_relay.push(pending_funding_signatures.remove(&funding_txo).unwrap());
526548
},
527-
Event::PaymentReceived { payment_hash, amt } => {
549+
Event::PaymentReceived { payment_hash, payment_secret, amt } => {
528550
//TODO: enhance by fetching random amounts from fuzz input?
529-
payments_received.push((payment_hash, amt));
551+
payments_received.push((payment_hash, payment_secret, amt));
530552
},
531553
Event::PaymentSent {..} => {},
532554
Event::PaymentFailed {..} => {},

0 commit comments

Comments
 (0)