Skip to content

Commit ca12473

Browse files
channelmanager: add multi-node receive support
Now if we receive an onion that we're intended to forward to one of our fake hops, we'll use its secret to unwrap the onion a second time and receive the payment
1 parent 499897d commit ca12473

File tree

4 files changed

+319
-22
lines changed

4 files changed

+319
-22
lines changed

lightning-invoice/src/utils.rs

Lines changed: 214 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,99 @@ use payment::{Payer, Router};
55

66
use bech32::ToBase32;
77
use bitcoin_hashes::Hash;
8+
use bitcoin_hashes::sha256::Hash as Sha256;
89
use lightning::chain;
910
use lightning::chain::chaininterface::{BroadcasterInterface, FeeEstimator};
1011
use lightning::chain::keysinterface::{Sign, KeysInterface};
1112
use lightning::ln::{PaymentHash, PaymentPreimage, PaymentSecret};
12-
use lightning::ln::channelmanager::{ChannelDetails, ChannelManager, PaymentId, PaymentSendFailure, MIN_FINAL_CLTV_EXPIRY};
13+
use lightning::ln::channelmanager::{ChannelDetails, ChannelManager, PaymentId, PaymentSendFailure, MIN_FINAL_CLTV_EXPIRY, MIN_CLTV_EXPIRY_DELTA};
1314
use lightning::ln::msgs::LightningError;
1415
use lightning::routing::scoring::Score;
1516
use lightning::routing::network_graph::{NetworkGraph, RoutingFees};
1617
use lightning::routing::router::{Route, RouteHint, RouteHintHop, RouteParameters, find_route};
1718
use lightning::util::logger::Logger;
19+
use secp256k1;
20+
use secp256k1::{Secp256k1, SecretKey};
1821
use secp256k1::key::PublicKey;
1922
use std::convert::TryInto;
2023
use std::ops::Deref;
2124

25+
/// TODO docs after interface is settled
26+
/// TODO instead of passing in the secret, pass in an InvoiceSigner trait object that ChannelManager
27+
/// implements, maybe?
28+
pub fn create_multi_receive_invoice(amt_msat: Option<u64>, description: String, payment_hash: PaymentHash, payment_secret: PaymentSecret, channels: &[(u64, PublicKey, &ChannelDetails)], fake_node_secret: SecretKey, network: Currency) ->
29+
Result<Invoice, SignOrCreationError<()>>
30+
{
31+
let fake_node_pubkey = PublicKey::from_secret_key(&Secp256k1::new(), &fake_node_secret);
32+
let mut route_hints = vec![];
33+
for (fake_scid, real_node_pubkey, channel) in channels {
34+
let short_channel_id = match channel.short_channel_id {
35+
Some(id) => id,
36+
None => continue,
37+
};
38+
let forwarding_info = match &channel.counterparty.forwarding_info {
39+
Some(info) => info.clone(),
40+
None => continue,
41+
};
42+
route_hints.push(RouteHint(vec![
43+
RouteHintHop {
44+
src_node_id: channel.counterparty.node_id,
45+
short_channel_id,
46+
fees: RoutingFees {
47+
base_msat: forwarding_info.fee_base_msat,
48+
proportional_millionths: forwarding_info.fee_proportional_millionths,
49+
},
50+
cltv_expiry_delta: forwarding_info.cltv_expiry_delta,
51+
htlc_minimum_msat: None,
52+
htlc_maximum_msat: None,
53+
},
54+
RouteHintHop {
55+
src_node_id: *real_node_pubkey,
56+
short_channel_id: *fake_scid,
57+
fees: RoutingFees {
58+
base_msat: 0,
59+
proportional_millionths: 0,
60+
},
61+
cltv_expiry_delta: MIN_CLTV_EXPIRY_DELTA,
62+
htlc_minimum_msat: None,
63+
htlc_maximum_msat: None,
64+
}
65+
])
66+
);
67+
}
68+
// TODO handle empty route hints
69+
// TODO comment why mpp isn't enabled
70+
let mut invoice = InvoiceBuilder::new(network)
71+
.description(description)
72+
.current_timestamp()
73+
.payee_pub_key(fake_node_pubkey)
74+
.payment_hash(Hash::from_slice(&payment_hash.0).unwrap())
75+
.payment_secret(payment_secret)
76+
.min_final_cltv_expiry(MIN_FINAL_CLTV_EXPIRY.into());
77+
if let Some(amt) = amt_msat {
78+
invoice = invoice.amount_milli_satoshis(amt);
79+
}
80+
for hint in route_hints {
81+
invoice = invoice.private_route(hint);
82+
}
83+
84+
let raw_invoice = match invoice.build_raw() {
85+
Ok(inv) => inv,
86+
Err(e) => return Err(SignOrCreationError::CreationError(e))
87+
};
88+
let hrp_str = raw_invoice.hrp.to_string();
89+
let hrp_bytes = hrp_str.as_bytes();
90+
let data_without_signature = raw_invoice.data.to_base32();
91+
let invoice_preimage = RawInvoice::construct_invoice_preimage(hrp_bytes, &data_without_signature);
92+
let secp_ctx = Secp256k1::new();
93+
let invoice_preimage_msg = secp256k1::Message::from_slice(&Sha256::hash(&invoice_preimage)).unwrap();
94+
let signed_raw_invoice = raw_invoice.sign(|_| Ok(secp_ctx.sign_recoverable(&invoice_preimage_msg, &fake_node_secret)));
95+
match signed_raw_invoice {
96+
Ok(inv) => Ok(Invoice::from_signed(inv).unwrap()),
97+
Err(e) => Err(SignOrCreationError::SignError(e))
98+
}
99+
}
100+
22101
/// Utility to construct an invoice. Generally, unless you want to do something like a custom
23102
/// cltv_expiry, this is what you should be using to create an invoice. The reason being, this
24103
/// method stores the invoice's payment secret and preimage in `ChannelManager`, so (a) the user
@@ -155,16 +234,22 @@ where
155234
}
156235

157236
#[cfg(test)]
158-
mod test {
237+
mod tests {
159238
use {Currency, Description, InvoiceDescription};
160-
use lightning::ln::PaymentHash;
161-
use lightning::ln::channelmanager::MIN_FINAL_CLTV_EXPIRY;
239+
use lightning::{expect_payment_sent, get_revoke_commit_msgs};
240+
use lightning::chain::keysinterface::KeyMaterial;
241+
use lightning::ln::{PaymentPreimage, PaymentHash};
242+
use lightning::ln::channelmanager::{ChannelDetails, MIN_FINAL_CLTV_EXPIRY};
162243
use lightning::ln::functional_test_utils::*;
163244
use lightning::ln::features::InitFeatures;
164245
use lightning::ln::msgs::ChannelMessageHandler;
165246
use lightning::routing::router::{Payee, RouteParameters, find_route};
166-
use lightning::util::events::MessageSendEventsProvider;
247+
use lightning::util::events::{Event, MessageSendEvent, MessageSendEventsProvider, PaymentPurpose};
167248
use lightning::util::test_utils;
249+
use bitcoin_hashes::sha256::Hash as Sha256;
250+
use bitcoin_hashes::Hash;
251+
use secp256k1::PublicKey;
252+
168253
#[test]
169254
fn test_from_channelmanager() {
170255
let chanmon_cfgs = create_chanmon_cfgs(2);
@@ -215,4 +300,128 @@ mod test {
215300
let events = nodes[1].node.get_and_clear_pending_msg_events();
216301
assert_eq!(events.len(), 2);
217302
}
303+
304+
#[test]
305+
fn test_multi_node_receive() {
306+
do_test_multi_node_receive(true);
307+
do_test_multi_node_receive(false);
308+
}
309+
310+
fn do_test_multi_node_receive(user_generated_pmt_hash: bool) {
311+
let chanmon_cfgs = create_chanmon_cfgs(3);
312+
let node_cfgs = create_node_cfgs(3, &chanmon_cfgs);
313+
let node_chanmgrs = create_node_chanmgrs(3, &node_cfgs, &[None, None, None]);
314+
let nodes = create_network(3, &node_cfgs, &node_chanmgrs);
315+
let chan_0_1 = create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 100000, 10001, InitFeatures::known(), InitFeatures::known());
316+
nodes[0].node.handle_channel_update(&nodes[1].node.get_our_node_id(), &chan_0_1.1);
317+
nodes[1].node.handle_channel_update(&nodes[0].node.get_our_node_id(), &chan_0_1.0);
318+
let chan_0_2 = create_announced_chan_between_nodes_with_value(&nodes, 0, 2, 100000, 10001, InitFeatures::known(), InitFeatures::known());
319+
nodes[0].node.handle_channel_update(&nodes[2].node.get_our_node_id(), &chan_0_2.1);
320+
nodes[2].node.handle_channel_update(&nodes[0].node.get_our_node_id(), &chan_0_2.0);
321+
let phantom_key_material = KeyMaterial([42; 32]);
322+
let phantom_scid = 42;
323+
let phantom_secret = nodes[1].node.create_fake_last_hop(phantom_key_material, phantom_scid);
324+
nodes[2].node.create_fake_last_hop(phantom_key_material, phantom_scid);
325+
326+
let (mut payment_preimage_opt, payment_hash, payment_secret) = {
327+
if user_generated_pmt_hash {
328+
let payment_preimage = PaymentPreimage([1; 32]);
329+
let payment_hash = PaymentHash(Sha256::hash(&payment_preimage.0[..]).into_inner());
330+
let payment_secret = nodes[1].node.create_inbound_payment_for_hash_fake_last_hop(payment_hash, Some(10_000), 3600, phantom_scid).unwrap();
331+
(Some(payment_preimage), payment_hash, payment_secret)
332+
} else {
333+
let (payment_hash, payment_secret) = nodes[1].node.create_inbound_payment_for_fake_last_hop(Some(10_000), 3600, phantom_scid).unwrap();
334+
(None, payment_hash, payment_secret)
335+
}
336+
};
337+
let channels_1 = nodes[1].node.list_channels();
338+
let channels_2 = nodes[2].node.list_channels();
339+
let mut route_hints = channels_1.iter().map(|e| (phantom_scid, nodes[1].node.get_our_node_id(), e)).collect::<Vec<(u64, PublicKey, &ChannelDetails)>>();
340+
for channel in channels_2.iter() {
341+
route_hints.push((phantom_scid, nodes[2].node.get_our_node_id(), &channel));
342+
}
343+
let invoice = ::utils::create_multi_receive_invoice(Some(10_000), "test".to_string(), payment_hash, payment_secret, &route_hints, phantom_secret, Currency::BitcoinTestnet).unwrap();
344+
345+
assert_eq!(invoice.min_final_cltv_expiry(), MIN_FINAL_CLTV_EXPIRY as u64);
346+
assert_eq!(invoice.description(), InvoiceDescription::Direct(&Description("test".to_string())));
347+
assert_eq!(invoice.route_hints().len(), 2);
348+
// assert!(!invoice.features().unwrap().supports_basic_mpp()); todo get this work?
349+
350+
let payee = Payee::from_node_id(invoice.recover_payee_pub_key())
351+
.with_features(invoice.features().unwrap().clone())
352+
.with_route_hints(invoice.route_hints());
353+
let params = RouteParameters {
354+
payee,
355+
final_value_msat: invoice.amount_milli_satoshis().unwrap(),
356+
final_cltv_expiry_delta: invoice.min_final_cltv_expiry() as u32,
357+
};
358+
let first_hops = nodes[0].node.list_usable_channels();
359+
let network_graph = node_cfgs[0].network_graph;
360+
let logger = test_utils::TestLogger::new();
361+
let scorer = test_utils::TestScorer::with_fixed_penalty(0);
362+
let route = find_route(
363+
&nodes[0].node.get_our_node_id(), &params, network_graph,
364+
Some(&first_hops.iter().collect::<Vec<_>>()), &logger, &scorer,
365+
).unwrap();
366+
let payment_event = {
367+
let mut payment_hash = PaymentHash([0; 32]);
368+
payment_hash.0.copy_from_slice(&invoice.payment_hash().as_ref()[0..32]);
369+
nodes[0].node.send_payment(&route, payment_hash, &Some(invoice.payment_secret().clone())).unwrap();
370+
let mut added_monitors = nodes[0].chain_monitor.added_monitors.lock().unwrap();
371+
assert_eq!(added_monitors.len(), 1);
372+
added_monitors.clear();
373+
374+
let mut events = nodes[0].node.get_and_clear_pending_msg_events();
375+
assert_eq!(events.len(), 1);
376+
SendEvent::from_event(events.remove(0))
377+
};
378+
nodes[1].node.handle_update_add_htlc(&nodes[0].node.get_our_node_id(), &payment_event.msgs[0]);
379+
nodes[1].node.handle_commitment_signed(&nodes[0].node.get_our_node_id(), &payment_event.commitment_msg);
380+
check_added_monitors!(nodes[1], 1);
381+
let (raa_1, cs_1) = get_revoke_commit_msgs!(nodes[1], nodes[0].node.get_our_node_id());
382+
check_added_monitors!(nodes[1], 0);
383+
assert!(nodes[1].node.get_and_clear_pending_msg_events().is_empty());
384+
nodes[0].node.handle_revoke_and_ack(&nodes[1].node.get_our_node_id(), &raa_1);
385+
assert!(nodes[0].node.get_and_clear_pending_msg_events().is_empty());
386+
check_added_monitors!(nodes[0], 1);
387+
nodes[0].node.handle_commitment_signed(&nodes[1].node.get_our_node_id(), &cs_1);
388+
let raa_0 = {
389+
let events = nodes[0].node.get_and_clear_pending_msg_events();
390+
assert_eq!(events.len(), 1);
391+
match events[0] {
392+
MessageSendEvent::SendRevokeAndACK { ref node_id, ref msg } => {
393+
assert_eq!(*node_id, nodes[1].node.get_our_node_id());
394+
(*msg).clone()
395+
},
396+
_ => panic!("Unexpected event"),
397+
}
398+
};
399+
check_added_monitors!(nodes[0], 1);
400+
nodes[1].node.handle_revoke_and_ack(&nodes[0].node.get_our_node_id(), &raa_0);
401+
402+
let events = nodes[1].node.get_and_clear_pending_events();
403+
assert_eq!(events.len(), 1);
404+
match events[0] {
405+
Event::PendingHTLCsForwardable { .. } => { },
406+
_ => panic!("Unexpected event"),
407+
};
408+
nodes[1].node.process_pending_htlc_forwards();
409+
let events = nodes[1].node.get_and_clear_pending_events();
410+
assert_eq!(events.len(), 1);
411+
match events[0] {
412+
Event::PaymentReceived {
413+
purpose: PaymentPurpose::InvoicePayment { payment_preimage, payment_secret: secret }, ..
414+
} => {
415+
assert_eq!(payment_secret, secret);
416+
if let Some(preimage) = payment_preimage {
417+
payment_preimage_opt = Some(preimage);
418+
}
419+
},
420+
_ => panic!("Unexpected event"),
421+
}
422+
check_added_monitors!(nodes[1], 1);
423+
424+
do_claim_payment_along_route(&nodes[0], &vec!(&vec!(&nodes[1])[..]), false, payment_preimage_opt.unwrap());
425+
expect_payment_sent!(nodes[0], payment_preimage_opt.unwrap(), Some(0));
426+
}
218427
}

0 commit comments

Comments
 (0)