Skip to content

Commit b364170

Browse files
Support receiving payments to blinded paths.
Error handling will be completed in later commit(s).
1 parent f2b52f7 commit b364170

File tree

5 files changed

+93
-11
lines changed

5 files changed

+93
-11
lines changed
+60
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
// This file is Copyright its original authors, visible in version control
2+
// history.
3+
//
4+
// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
5+
// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
6+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
7+
// You may not use this file except in accordance with one or both of these
8+
// licenses.
9+
10+
use bitcoin::secp256k1::Secp256k1;
11+
use crate::blinded_path::BlindedPath;
12+
use crate::blinded_path::payment::{PaymentConstraints, ReceiveTlvs};
13+
use crate::ln::channelmanager::{PaymentId, RecipientOnionFields};
14+
use crate::ln::functional_test_utils::*;
15+
use crate::ln::outbound_payment::Retry;
16+
use crate::prelude::*;
17+
use crate::routing::router::{PaymentParameters, RouteParameters};
18+
19+
#[test]
20+
fn one_hop_blinded_path() {
21+
do_one_hop_blinded_path(true);
22+
do_one_hop_blinded_path(false);
23+
}
24+
25+
fn do_one_hop_blinded_path(success: bool) {
26+
let chanmon_cfgs = create_chanmon_cfgs(2);
27+
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
28+
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
29+
let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
30+
let chan_upd = create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 1_000_000, 0).0.contents;
31+
32+
let amt_msat = 5000;
33+
let (payment_preimage, payment_hash, payment_secret) = get_payment_preimage_hash(&nodes[1], Some(amt_msat), None);
34+
let payee_tlvs = ReceiveTlvs {
35+
payment_secret,
36+
payment_constraints: PaymentConstraints {
37+
max_cltv_expiry: u32::max_value(),
38+
htlc_minimum_msat: chan_upd.htlc_minimum_msat,
39+
},
40+
};
41+
let mut secp_ctx = Secp256k1::new();
42+
let blinded_path = BlindedPath::new_for_payment(
43+
&[], nodes[1].node.get_our_node_id(), payee_tlvs, chan_upd.htlc_maximum_msat,
44+
&chanmon_cfgs[1].keys_manager, &secp_ctx
45+
).unwrap();
46+
47+
let route_params = RouteParameters {
48+
payment_params: PaymentParameters::blinded(vec![blinded_path]),
49+
final_value_msat: amt_msat
50+
};
51+
nodes[0].node.send_payment(payment_hash, RecipientOnionFields::spontaneous_empty(),
52+
PaymentId(payment_hash.0), route_params, Retry::Attempts(0)).unwrap();
53+
check_added_monitors(&nodes[0], 1);
54+
pass_along_route(&nodes[0], &[&[&nodes[1]]], amt_msat, payment_hash, payment_secret);
55+
if success {
56+
claim_payment(&nodes[0], &[&nodes[1]], payment_preimage);
57+
} else {
58+
fail_payment(&nodes[0], &[&nodes[1]], payment_hash);
59+
}
60+
}

lightning/src/ln/channelmanager.rs

+28-7
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,12 @@ use bitcoin::blockdata::transaction::Transaction;
2222
use bitcoin::blockdata::constants::{genesis_block, ChainHash};
2323
use bitcoin::network::constants::Network;
2424

25-
use bitcoin::hashes::Hash;
25+
use bitcoin::hashes::{Hash, HashEngine};
26+
use bitcoin::hashes::hmac::{Hmac, HmacEngine};
2627
use bitcoin::hashes::sha256::Hash as Sha256;
2728
use bitcoin::hash_types::{BlockHash, Txid};
2829

29-
use bitcoin::secp256k1::{SecretKey,PublicKey};
30+
use bitcoin::secp256k1::{PublicKey, Scalar, SecretKey};
3031
use bitcoin::secp256k1::Secp256k1;
3132
use bitcoin::{LockTime, secp256k1, Sequence};
3233

@@ -49,7 +50,7 @@ use crate::routing::router::{BlindedTail, DefaultRouter, InFlightHtlcs, Path, Pa
4950
use crate::routing::scoring::{ProbabilisticScorer, ProbabilisticScoringFeeParameters};
5051
use crate::ln::msgs;
5152
use crate::ln::onion_utils;
52-
use crate::ln::onion_utils::HTLCFailReason;
53+
use crate::ln::onion_utils::{HTLCFailReason, INVALID_ONION_BLINDING};
5354
use crate::ln::msgs::{ChannelMessageHandler, DecodeError, LightningError};
5455
#[cfg(test)]
5556
use crate::ln::outbound_payment;
@@ -2763,13 +2764,26 @@ where
27632764
payment_data, keysend_preimage, custom_tlvs, amt_msat, outgoing_cltv_value, payment_metadata, ..
27642765
} =>
27652766
(payment_data, keysend_preimage, custom_tlvs, amt_msat, outgoing_cltv_value, payment_metadata),
2766-
msgs::InboundOnionPayload::Forward { .. } =>
2767+
msgs::InboundOnionPayload::BlindedReceive {
2768+
amt_msat, total_msat, outgoing_cltv_value, payment_secret, ..
2769+
} => {
2770+
let payment_data = msgs::FinalOnionHopData { payment_secret, total_msat };
2771+
(Some(payment_data), None, Vec::new(), amt_msat, outgoing_cltv_value, None)
2772+
}
2773+
msgs::InboundOnionPayload::Forward { .. } => {
27672774
return Err(InboundOnionErr {
27682775
err_code: 0x4000|22,
27692776
err_data: Vec::new(),
27702777
msg: "Got non final data with an HMAC of 0",
2771-
}),
2772-
_ => todo!()
2778+
})
2779+
},
2780+
msgs::InboundOnionPayload::BlindedForward { .. } => {
2781+
return Err(InboundOnionErr {
2782+
msg: "Got blinded non final data with an HMAC of 0",
2783+
err_code: INVALID_ONION_BLINDING,
2784+
err_data: vec![0; 32],
2785+
})
2786+
},
27732787
};
27742788
// final_incorrect_cltv_expiry
27752789
if outgoing_cltv_value > cltv_expiry {
@@ -2882,8 +2896,15 @@ where
28822896
return_malformed_err!("invalid ephemeral pubkey", 0x8000 | 0x4000 | 6);
28832897
}
28842898

2899+
let blinded_node_id_tweak = msg.blinding_point.map(|bp| {
2900+
let blinded_tlvs_ss = self.node_signer.ecdh(
2901+
Recipient::Node, &bp, None).unwrap().secret_bytes();
2902+
let mut hmac = HmacEngine::<Sha256>::new(b"blinded_node_id");
2903+
hmac.input(blinded_tlvs_ss.as_ref());
2904+
Scalar::from_be_bytes(Hmac::from_engine(hmac).into_inner()).unwrap()
2905+
});
28852906
let shared_secret = self.node_signer.ecdh(
2886-
Recipient::Node, &msg.onion_routing_packet.public_key.unwrap(), None
2907+
Recipient::Node, &msg.onion_routing_packet.public_key.unwrap(), blinded_node_id_tweak.as_ref()
28872908
).unwrap().secret_bytes();
28882909

28892910
if msg.onion_routing_packet.version != 0 {

lightning/src/ln/mod.rs

+3
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ pub mod wire;
4343
// without the node parameter being mut. This is incorrect, and thus newer rustcs will complain
4444
// about an unnecessary mut. Thus, we silence the unused_mut warning in two test modules below.
4545

46+
#[cfg(test)]
47+
#[allow(unused_mut)]
48+
mod blinded_payment_tests;
4649
#[cfg(test)]
4750
#[allow(unused_mut)]
4851
mod functional_tests;

lightning/src/ln/onion_utils.rs

+2
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,8 @@ pub(super) fn build_onion_payloads(path: &Path, total_msat: u64, mut recipient_o
240240
/// the hops can be of variable length.
241241
pub(crate) const ONION_DATA_LEN: usize = 20*65;
242242

243+
pub(super) const INVALID_ONION_BLINDING: u16 = 0x8000 | 0x4000 | 24;
244+
243245
#[inline]
244246
fn shift_slice_right(arr: &mut [u8], amt: usize) {
245247
for i in (amt..arr.len()).rev() {

lightning/src/ln/outbound_payment.rs

-4
Original file line numberDiff line numberDiff line change
@@ -1075,10 +1075,6 @@ impl OutboundPayments {
10751075
path_errs.push(Err(APIError::InvalidRoute{err: "Path didn't go anywhere/had bogus size".to_owned()}));
10761076
continue 'path_check;
10771077
}
1078-
if path.blinded_tail.is_some() {
1079-
path_errs.push(Err(APIError::InvalidRoute{err: "Sending to blinded paths isn't supported yet".to_owned()}));
1080-
continue 'path_check;
1081-
}
10821078
let dest_hop_idx = if path.blinded_tail.is_some() && path.blinded_tail.as_ref().unwrap().hops.len() > 1 {
10831079
usize::max_value() } else { path.hops.len() - 1 };
10841080
for (idx, hop) in path.hops.iter().enumerate() {

0 commit comments

Comments
 (0)