Skip to content

Commit b2eb3e7

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

File tree

2 files changed

+50
-11
lines changed

2 files changed

+50
-11
lines changed

lightning/src/ln/channelmanager.rs

Lines changed: 48 additions & 11 deletions
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;
@@ -112,6 +113,9 @@ pub(super) enum PendingHTLCRouting {
112113
phantom_shared_secret: Option<[u8; 32]>,
113114
/// See [`RecipientOnionFields::custom_tlvs`] for more info.
114115
custom_tlvs: Vec<(u64, Vec<u8>)>,
116+
/// `Some` if this corresponds to a blinded HTLC. Used for failing backwards properly. We use an
117+
/// option to maintain compatibility with LDK versions prior to 0.0.117.
118+
blinded: Option<()>,
115119
},
116120
ReceiveKeysend {
117121
/// This was added in 0.0.116 and will break deserialization on downgrades.
@@ -121,6 +125,8 @@ pub(super) enum PendingHTLCRouting {
121125
incoming_cltv_expiry: u32, // Used to track when we should expire pending HTLCs that go unclaimed
122126
/// See [`RecipientOnionFields::custom_tlvs`] for more info.
123127
custom_tlvs: Vec<(u64, Vec<u8>)>,
128+
/// See [`Self::Receive::blinded`] docs.
129+
blinded: Option<()>,
124130
},
125131
}
126132

@@ -2758,18 +2764,34 @@ where
27582764
amt_msat: u64, cltv_expiry: u32, phantom_shared_secret: Option<[u8; 32]>, allow_underpay: bool,
27592765
counterparty_skimmed_fee_msat: Option<u64>,
27602766
) -> Result<PendingHTLCInfo, InboundOnionErr> {
2761-
let (payment_data, keysend_preimage, custom_tlvs, onion_amt_msat, outgoing_cltv_value, payment_metadata) = match hop_data {
2767+
let (
2768+
payment_data, keysend_preimage, custom_tlvs, onion_amt_msat, outgoing_cltv_value, payment_metadata, blinded
2769+
) = match hop_data {
27622770
msgs::InboundOnionPayload::Receive {
27632771
payment_data, keysend_preimage, custom_tlvs, amt_msat, outgoing_cltv_value, payment_metadata, ..
27642772
} =>
2765-
(payment_data, keysend_preimage, custom_tlvs, amt_msat, outgoing_cltv_value, payment_metadata),
2766-
msgs::InboundOnionPayload::Forward { .. } =>
2773+
(payment_data, keysend_preimage, custom_tlvs, amt_msat, outgoing_cltv_value, payment_metadata, None),
2774+
msgs::InboundOnionPayload::BlindedReceive {
2775+
amt_msat, total_msat, outgoing_cltv_value, payment_secret, intro_node_blinding_point, ..
2776+
} => {
2777+
let payment_data = msgs::FinalOnionHopData { payment_secret, total_msat };
2778+
let blinded = if intro_node_blinding_point.is_none() { Some(()) } else { None };
2779+
(Some(payment_data), None, Vec::new(), amt_msat, outgoing_cltv_value, None, blinded)
2780+
}
2781+
msgs::InboundOnionPayload::Forward { .. } => {
27672782
return Err(InboundOnionErr {
27682783
err_code: 0x4000|22,
27692784
err_data: Vec::new(),
27702785
msg: "Got non final data with an HMAC of 0",
2771-
}),
2772-
_ => todo!()
2786+
})
2787+
},
2788+
msgs::InboundOnionPayload::BlindedForward { .. } => {
2789+
return Err(InboundOnionErr {
2790+
msg: "Got blinded non final data with an HMAC of 0",
2791+
err_code: INVALID_ONION_BLINDING,
2792+
err_data: Vec::new(), // filled in in construct_pending_htlc_status
2793+
})
2794+
},
27732795
};
27742796
// final_incorrect_cltv_expiry
27752797
if outgoing_cltv_value > cltv_expiry {
@@ -2834,6 +2856,7 @@ where
28342856
payment_metadata,
28352857
incoming_cltv_expiry: outgoing_cltv_value,
28362858
custom_tlvs,
2859+
blinded,
28372860
}
28382861
} else if let Some(data) = payment_data {
28392862
PendingHTLCRouting::Receive {
@@ -2842,6 +2865,7 @@ where
28422865
incoming_cltv_expiry: outgoing_cltv_value,
28432866
phantom_shared_secret,
28442867
custom_tlvs,
2868+
blinded,
28452869
}
28462870
} else {
28472871
return Err(InboundOnionErr {
@@ -2882,8 +2906,15 @@ where
28822906
return_malformed_err!("invalid ephemeral pubkey", 0x8000 | 0x4000 | 6);
28832907
}
28842908

2909+
let blinded_node_id_tweak = msg.blinding_point.map(|bp| {
2910+
let blinded_tlvs_ss = self.node_signer.ecdh(
2911+
Recipient::Node, &bp, None).unwrap().secret_bytes();
2912+
let mut hmac = HmacEngine::<Sha256>::new(b"blinded_node_id");
2913+
hmac.input(blinded_tlvs_ss.as_ref());
2914+
Scalar::from_be_bytes(Hmac::from_engine(hmac).into_inner()).unwrap()
2915+
});
28852916
let shared_secret = self.node_signer.ecdh(
2886-
Recipient::Node, &msg.onion_routing_packet.public_key.unwrap(), None
2917+
Recipient::Node, &msg.onion_routing_packet.public_key.unwrap(), blinded_node_id_tweak.as_ref()
28872918
).unwrap().secret_bytes();
28882919

28892920
if msg.onion_routing_packet.version != 0 {
@@ -4042,14 +4073,18 @@ where
40424073
}
40434074
}) => {
40444075
let (cltv_expiry, onion_payload, payment_data, phantom_shared_secret, mut onion_fields) = match routing {
4045-
PendingHTLCRouting::Receive { payment_data, payment_metadata, incoming_cltv_expiry, phantom_shared_secret, custom_tlvs } => {
4076+
PendingHTLCRouting::Receive {
4077+
payment_data, payment_metadata, incoming_cltv_expiry, phantom_shared_secret, custom_tlvs, ..
4078+
} => {
40464079
let _legacy_hop_data = Some(payment_data.clone());
40474080
let onion_fields = RecipientOnionFields { payment_secret: Some(payment_data.payment_secret),
40484081
payment_metadata, custom_tlvs };
40494082
(incoming_cltv_expiry, OnionPayload::Invoice { _legacy_hop_data },
40504083
Some(payment_data), phantom_shared_secret, onion_fields)
40514084
},
4052-
PendingHTLCRouting::ReceiveKeysend { payment_data, payment_preimage, payment_metadata, incoming_cltv_expiry, custom_tlvs } => {
4085+
PendingHTLCRouting::ReceiveKeysend {
4086+
payment_data, payment_preimage, payment_metadata, incoming_cltv_expiry, custom_tlvs, ..
4087+
} => {
40534088
let onion_fields = RecipientOnionFields {
40544089
payment_secret: payment_data.as_ref().map(|data| data.payment_secret),
40554090
payment_metadata,
@@ -7917,6 +7952,7 @@ impl_writeable_tlv_based_enum!(PendingHTLCRouting,
79177952
(1, phantom_shared_secret, option),
79187953
(2, incoming_cltv_expiry, required),
79197954
(3, payment_metadata, option),
7955+
(4, blinded, option),
79207956
(5, custom_tlvs, optional_vec),
79217957
},
79227958
(2, ReceiveKeysend) => {
@@ -7925,6 +7961,7 @@ impl_writeable_tlv_based_enum!(PendingHTLCRouting,
79257961
(3, payment_metadata, option),
79267962
(4, payment_data, option), // Added in 0.0.116
79277963
(5, custom_tlvs, optional_vec),
7964+
(6, blinded, option),
79287965
},
79297966
;);
79307967

lightning/src/ln/onion_utils.rs

Lines changed: 2 additions & 0 deletions
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() {

0 commit comments

Comments
 (0)