Skip to content

Commit 74bc9e2

Browse files
Merge pull request #2540 from valentinewallace/2023-08-blinded-errors
Route blinding: support forwarding as the intro node
2 parents f07f4b9 + 6af786a commit 74bc9e2

10 files changed

+757
-70
lines changed

lightning/src/blinded_path/payment.rs

+1-16
Original file line numberDiff line numberDiff line change
@@ -118,21 +118,6 @@ impl Writeable for ReceiveTlvs {
118118
}
119119
}
120120

121-
// This will be removed once we support forwarding blinded HTLCs, because we'll always read a
122-
// `BlindedPaymentTlvs` instead.
123-
impl Readable for ReceiveTlvs {
124-
fn read<R: io::Read>(r: &mut R) -> Result<Self, DecodeError> {
125-
_init_and_read_tlv_stream!(r, {
126-
(12, payment_constraints, required),
127-
(65536, payment_secret, required),
128-
});
129-
Ok(Self {
130-
payment_secret: payment_secret.0.unwrap(),
131-
payment_constraints: payment_constraints.0.unwrap()
132-
})
133-
}
134-
}
135-
136121
impl<'a> Writeable for BlindedPaymentTlvsRef<'a> {
137122
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
138123
// TODO: write padding
@@ -187,7 +172,7 @@ pub(super) fn blinded_hops<T: secp256k1::Signing + secp256k1::Verification>(
187172
}
188173

189174
/// `None` if underflow occurs.
190-
fn amt_to_forward_msat(inbound_amt_msat: u64, payment_relay: &PaymentRelay) -> Option<u64> {
175+
pub(crate) fn amt_to_forward_msat(inbound_amt_msat: u64, payment_relay: &PaymentRelay) -> Option<u64> {
191176
let inbound_amt = inbound_amt_msat as u128;
192177
let base = payment_relay.fee_base_msat as u128;
193178
let prop = payment_relay.fee_proportional_millionths as u128;

lightning/src/ln/blinded_payment_tests.rs

+326-3
Large diffs are not rendered by default.

lightning/src/ln/channel.rs

+155-14
Large diffs are not rendered by default.

lightning/src/ln/channelmanager.rs

+102-12
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ use crate::routing::scoring::{ProbabilisticScorer, ProbabilisticScoringFeeParame
5353
use crate::ln::onion_payment::{check_incoming_htlc_cltv, create_recv_pending_htlc_info, create_fwd_pending_htlc_info, decode_incoming_update_add_htlc_onion, InboundOnionErr, NextPacketDetails};
5454
use crate::ln::msgs;
5555
use crate::ln::onion_utils;
56-
use crate::ln::onion_utils::HTLCFailReason;
56+
use crate::ln::onion_utils::{HTLCFailReason, INVALID_ONION_BLINDING};
5757
use crate::ln::msgs::{ChannelMessageHandler, DecodeError, LightningError};
5858
#[cfg(test)]
5959
use crate::ln::outbound_payment;
@@ -119,6 +119,8 @@ pub enum PendingHTLCRouting {
119119
/// The SCID from the onion that we should forward to. This could be a real SCID or a fake one
120120
/// generated using `get_fake_scid` from the scid_utils::fake_scid module.
121121
short_channel_id: u64, // This should be NonZero<u64> eventually when we bump MSRV
122+
/// Set if this HTLC is being forwarded within a blinded path.
123+
blinded: Option<BlindedForward>,
122124
},
123125
/// An HTLC paid to an invoice (supposedly) generated by us.
124126
/// At this point, we have not checked that the invoice being paid was actually generated by us,
@@ -155,6 +157,28 @@ pub enum PendingHTLCRouting {
155157
},
156158
}
157159

160+
/// Information used to forward or fail this HTLC that is being forwarded within a blinded path.
161+
#[derive(Clone, Copy, Hash, PartialEq, Eq)]
162+
pub struct BlindedForward {
163+
/// The `blinding_point` that was set in the inbound [`msgs::UpdateAddHTLC`], or in the inbound
164+
/// onion payload if we're the introduction node. Useful for calculating the next hop's
165+
/// [`msgs::UpdateAddHTLC::blinding_point`].
166+
pub inbound_blinding_point: PublicKey,
167+
// Another field will be added here when we support forwarding as a non-intro node.
168+
}
169+
170+
impl PendingHTLCRouting {
171+
// Used to override the onion failure code and data if the HTLC is blinded.
172+
fn blinded_failure(&self) -> Option<BlindedFailure> {
173+
// TODO: needs update when we support receiving to multi-hop blinded paths
174+
if let Self::Forward { blinded: Some(_), .. } = self {
175+
Some(BlindedFailure::FromIntroductionNode)
176+
} else {
177+
None
178+
}
179+
}
180+
}
181+
158182
/// Full details of an incoming HTLC, including routing info.
159183
#[derive(Clone)] // See Channel::revoke_and_ack for why, tl;dr: Rust bug
160184
pub struct PendingHTLCInfo {
@@ -213,6 +237,13 @@ pub(super) enum HTLCForwardInfo {
213237
},
214238
}
215239

240+
// Used for failing blinded HTLCs backwards correctly.
241+
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
242+
enum BlindedFailure {
243+
FromIntroductionNode,
244+
// Another variant will be added here for non-intro nodes.
245+
}
246+
216247
/// Tracks the inbound corresponding to an outbound HTLC
217248
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
218249
pub(crate) struct HTLCPreviousHopData {
@@ -222,6 +253,7 @@ pub(crate) struct HTLCPreviousHopData {
222253
htlc_id: u64,
223254
incoming_packet_shared_secret: [u8; 32],
224255
phantom_shared_secret: Option<[u8; 32]>,
256+
blinded_failure: Option<BlindedFailure>,
225257

226258
// This field is consumed by `claim_funds_from_hop()` when updating a force-closed backwards
227259
// channel with a preimage provided by the forward channel.
@@ -2945,14 +2977,24 @@ where
29452977
msg, &self.node_signer, &self.logger, &self.secp_ctx
29462978
)?;
29472979

2980+
let is_blinded = match next_hop {
2981+
onion_utils::Hop::Forward {
2982+
next_hop_data: msgs::InboundOnionPayload::BlindedForward { .. }, ..
2983+
} => true,
2984+
_ => false, // TODO: update this when we support receiving to multi-hop blinded paths
2985+
};
2986+
29482987
macro_rules! return_err {
29492988
($msg: expr, $err_code: expr, $data: expr) => {
29502989
{
29512990
log_info!(self.logger, "Failed to accept/forward incoming HTLC: {}", $msg);
2991+
let (err_code, err_data) = if is_blinded {
2992+
(INVALID_ONION_BLINDING, &[0; 32][..])
2993+
} else { ($err_code, $data) };
29522994
return Err(HTLCFailureMsg::Relay(msgs::UpdateFailHTLC {
29532995
channel_id: msg.channel_id,
29542996
htlc_id: msg.htlc_id,
2955-
reason: HTLCFailReason::reason($err_code, $data.to_vec())
2997+
reason: HTLCFailReason::reason(err_code, err_data.to_vec())
29562998
.get_encrypted_failure_packet(&shared_secret, &None),
29572999
}));
29583000
}
@@ -4013,8 +4055,10 @@ where
40134055
})?;
40144056

40154057
let routing = match payment.forward_info.routing {
4016-
PendingHTLCRouting::Forward { onion_packet, .. } => {
4017-
PendingHTLCRouting::Forward { onion_packet, short_channel_id: next_hop_scid }
4058+
PendingHTLCRouting::Forward { onion_packet, blinded, .. } => {
4059+
PendingHTLCRouting::Forward {
4060+
onion_packet, blinded, short_channel_id: next_hop_scid
4061+
}
40184062
},
40194063
_ => unreachable!() // Only `PendingHTLCRouting::Forward`s are intercepted
40204064
};
@@ -4058,6 +4102,7 @@ where
40584102
htlc_id: payment.prev_htlc_id,
40594103
incoming_packet_shared_secret: payment.forward_info.incoming_shared_secret,
40604104
phantom_shared_secret: None,
4105+
blinded_failure: payment.forward_info.routing.blinded_failure(),
40614106
});
40624107

40634108
let failure_reason = HTLCFailReason::from_failure_code(0x4000 | 10);
@@ -4106,6 +4151,7 @@ where
41064151
htlc_id: prev_htlc_id,
41074152
incoming_packet_shared_secret: incoming_shared_secret,
41084153
phantom_shared_secret: $phantom_ss,
4154+
blinded_failure: routing.blinded_failure(),
41094155
});
41104156

41114157
let reason = if $next_hop_unknown {
@@ -4135,7 +4181,7 @@ where
41354181
}
41364182
}
41374183
}
4138-
if let PendingHTLCRouting::Forward { onion_packet, .. } = routing {
4184+
if let PendingHTLCRouting::Forward { ref onion_packet, .. } = routing {
41394185
let phantom_pubkey_res = self.node_signer.get_node_id(Recipient::PhantomNode);
41404186
if phantom_pubkey_res.is_ok() && fake_scid::is_valid_phantom(&self.fake_scid_rand_bytes, short_chan_id, &self.chain_hash) {
41414187
let phantom_shared_secret = self.node_signer.ecdh(Recipient::PhantomNode, &onion_packet.public_key.unwrap(), None).unwrap().secret_bytes();
@@ -4210,7 +4256,9 @@ where
42104256
prev_short_channel_id, prev_htlc_id, prev_funding_outpoint, prev_user_channel_id,
42114257
forward_info: PendingHTLCInfo {
42124258
incoming_shared_secret, payment_hash, outgoing_amt_msat, outgoing_cltv_value,
4213-
routing: PendingHTLCRouting::Forward { onion_packet, .. }, skimmed_fee_msat, ..
4259+
routing: PendingHTLCRouting::Forward {
4260+
onion_packet, blinded, ..
4261+
}, skimmed_fee_msat, ..
42144262
},
42154263
}) => {
42164264
log_trace!(self.logger, "Adding HTLC from short id {} with payment_hash {} to channel with short id {} after delay", prev_short_channel_id, &payment_hash, short_chan_id);
@@ -4222,10 +4270,19 @@ where
42224270
incoming_packet_shared_secret: incoming_shared_secret,
42234271
// Phantom payments are only PendingHTLCRouting::Receive.
42244272
phantom_shared_secret: None,
4273+
blinded_failure: blinded.map(|_| BlindedFailure::FromIntroductionNode),
4274+
});
4275+
let next_blinding_point = blinded.and_then(|b| {
4276+
let encrypted_tlvs_ss = self.node_signer.ecdh(
4277+
Recipient::Node, &b.inbound_blinding_point, None
4278+
).unwrap().secret_bytes();
4279+
onion_utils::next_hop_pubkey(
4280+
&self.secp_ctx, b.inbound_blinding_point, &encrypted_tlvs_ss
4281+
).ok()
42254282
});
42264283
if let Err(e) = chan.queue_add_htlc(outgoing_amt_msat,
42274284
payment_hash, outgoing_cltv_value, htlc_source.clone(),
4228-
onion_packet, skimmed_fee_msat, &self.fee_estimator,
4285+
onion_packet, skimmed_fee_msat, next_blinding_point, &self.fee_estimator,
42294286
&self.logger)
42304287
{
42314288
if let ChannelError::Ignore(msg) = e {
@@ -4276,6 +4333,7 @@ where
42764333
skimmed_fee_msat, ..
42774334
}
42784335
}) => {
4336+
let blinded_failure = routing.blinded_failure();
42794337
let (cltv_expiry, onion_payload, payment_data, phantom_shared_secret, mut onion_fields) = match routing {
42804338
PendingHTLCRouting::Receive { payment_data, payment_metadata, incoming_cltv_expiry, phantom_shared_secret, custom_tlvs } => {
42814339
let _legacy_hop_data = Some(payment_data.clone());
@@ -4305,6 +4363,7 @@ where
43054363
htlc_id: prev_htlc_id,
43064364
incoming_packet_shared_secret: incoming_shared_secret,
43074365
phantom_shared_secret,
4366+
blinded_failure,
43084367
},
43094368
// We differentiate the received value from the sender intended value
43104369
// if possible so that we don't prematurely mark MPP payments complete
@@ -4335,6 +4394,7 @@ where
43354394
htlc_id: $htlc.prev_hop.htlc_id,
43364395
incoming_packet_shared_secret: $htlc.prev_hop.incoming_packet_shared_secret,
43374396
phantom_shared_secret,
4397+
blinded_failure: None,
43384398
}), payment_hash,
43394399
HTLCFailReason::reason(0x4000 | 15, htlc_msat_height_data),
43404400
HTLCDestination::FailedPayment { payment_hash: $payment_hash },
@@ -5098,9 +5158,23 @@ where
50985158
&self.pending_events, &self.logger)
50995159
{ self.push_pending_forwards_ev(); }
51005160
},
5101-
HTLCSource::PreviousHopData(HTLCPreviousHopData { ref short_channel_id, ref htlc_id, ref incoming_packet_shared_secret, ref phantom_shared_secret, ref outpoint, .. }) => {
5102-
log_trace!(self.logger, "Failing HTLC with payment_hash {} backwards from us with {:?}", &payment_hash, onion_error);
5103-
let err_packet = onion_error.get_encrypted_failure_packet(incoming_packet_shared_secret, phantom_shared_secret);
5161+
HTLCSource::PreviousHopData(HTLCPreviousHopData {
5162+
ref short_channel_id, ref htlc_id, ref incoming_packet_shared_secret,
5163+
ref phantom_shared_secret, ref outpoint, ref blinded_failure, ..
5164+
}) => {
5165+
log_trace!(self.logger, "Failing {}HTLC with payment_hash {} backwards from us: {:?}",
5166+
if blinded_failure.is_some() { "blinded " } else { "" }, &payment_hash, onion_error);
5167+
let err_packet = match blinded_failure {
5168+
Some(BlindedFailure::FromIntroductionNode) => {
5169+
let blinded_onion_error = HTLCFailReason::reason(INVALID_ONION_BLINDING, vec![0; 32]);
5170+
blinded_onion_error.get_encrypted_failure_packet(
5171+
incoming_packet_shared_secret, phantom_shared_secret
5172+
)
5173+
},
5174+
None => {
5175+
onion_error.get_encrypted_failure_packet(incoming_packet_shared_secret, phantom_shared_secret)
5176+
}
5177+
};
51045178

51055179
let mut push_forward_ev = false;
51065180
let mut forward_htlcs = self.forward_htlcs.lock().unwrap();
@@ -6381,8 +6455,12 @@ where
63816455
// but if we've sent a shutdown and they haven't acknowledged it yet, we just
63826456
// want to reject the new HTLC and fail it backwards instead of forwarding.
63836457
match pending_forward_info {
6384-
PendingHTLCStatus::Forward(PendingHTLCInfo { ref incoming_shared_secret, .. }) => {
6385-
let reason = if (error_code & 0x1000) != 0 {
6458+
PendingHTLCStatus::Forward(PendingHTLCInfo {
6459+
ref incoming_shared_secret, ref routing, ..
6460+
}) => {
6461+
let reason = if routing.blinded_failure().is_some() {
6462+
HTLCFailReason::reason(INVALID_ONION_BLINDING, vec![0; 32])
6463+
} else if (error_code & 0x1000) != 0 {
63866464
let (real_code, error_data) = self.get_htlc_inbound_temp_fail_err_and_data(error_code, chan);
63876465
HTLCFailReason::reason(real_code, error_data)
63886466
} else {
@@ -6584,6 +6662,7 @@ where
65846662
htlc_id: prev_htlc_id,
65856663
incoming_packet_shared_secret: forward_info.incoming_shared_secret,
65866664
phantom_shared_secret: None,
6665+
blinded_failure: forward_info.routing.blinded_failure(),
65876666
});
65886667

65896668
failed_intercept_forwards.push((htlc_source, forward_info.payment_hash,
@@ -8180,6 +8259,7 @@ where
81808259
incoming_packet_shared_secret: htlc.forward_info.incoming_shared_secret,
81818260
phantom_shared_secret: None,
81828261
outpoint: htlc.prev_funding_outpoint,
8262+
blinded_failure: htlc.forward_info.routing.blinded_failure(),
81838263
});
81848264

81858265
let requested_forward_scid /* intercept scid */ = match htlc.forward_info.routing {
@@ -9143,9 +9223,14 @@ impl_writeable_tlv_based!(PhantomRouteHints, {
91439223
(6, real_node_pubkey, required),
91449224
});
91459225

9226+
impl_writeable_tlv_based!(BlindedForward, {
9227+
(0, inbound_blinding_point, required),
9228+
});
9229+
91469230
impl_writeable_tlv_based_enum!(PendingHTLCRouting,
91479231
(0, Forward) => {
91489232
(0, onion_packet, required),
9233+
(1, blinded, option),
91499234
(2, short_channel_id, required),
91509235
},
91519236
(1, Receive) => {
@@ -9247,10 +9332,15 @@ impl_writeable_tlv_based_enum!(PendingHTLCStatus, ;
92479332
(1, Fail),
92489333
);
92499334

9335+
impl_writeable_tlv_based_enum!(BlindedFailure,
9336+
(0, FromIntroductionNode) => {}, ;
9337+
);
9338+
92509339
impl_writeable_tlv_based!(HTLCPreviousHopData, {
92519340
(0, short_channel_id, required),
92529341
(1, phantom_shared_secret, option),
92539342
(2, outpoint, required),
9343+
(3, blinded_failure, option),
92549344
(4, htlc_id, required),
92559345
(6, incoming_packet_shared_secret, required),
92569346
(7, user_channel_id, option),

lightning/src/ln/functional_tests.rs

+5
Original file line numberDiff line numberDiff line change
@@ -1415,6 +1415,7 @@ fn test_fee_spike_violation_fails_htlc() {
14151415
cltv_expiry: htlc_cltv,
14161416
onion_routing_packet: onion_packet,
14171417
skimmed_fee_msat: None,
1418+
blinding_point: None,
14181419
};
14191420

14201421
nodes[1].node.handle_update_add_htlc(&nodes[0].node.get_our_node_id(), &msg);
@@ -1611,6 +1612,7 @@ fn test_chan_reserve_violation_inbound_htlc_outbound_channel() {
16111612
cltv_expiry: htlc_cltv,
16121613
onion_routing_packet: onion_packet,
16131614
skimmed_fee_msat: None,
1615+
blinding_point: None,
16141616
};
16151617

16161618
nodes[0].node.handle_update_add_htlc(&nodes[1].node.get_our_node_id(), &msg);
@@ -1789,6 +1791,7 @@ fn test_chan_reserve_violation_inbound_htlc_inbound_chan() {
17891791
cltv_expiry: htlc_cltv,
17901792
onion_routing_packet: onion_packet,
17911793
skimmed_fee_msat: None,
1794+
blinding_point: None,
17921795
};
17931796

17941797
nodes[1].node.handle_update_add_htlc(&nodes[0].node.get_our_node_id(), &msg);
@@ -3510,6 +3513,7 @@ fn fail_backward_pending_htlc_upon_channel_failure() {
35103513
cltv_expiry,
35113514
onion_routing_packet,
35123515
skimmed_fee_msat: None,
3516+
blinding_point: None,
35133517
};
35143518
nodes[0].node.handle_update_add_htlc(&nodes[1].node.get_our_node_id(), &update_add_htlc);
35153519
}
@@ -6481,6 +6485,7 @@ fn test_update_add_htlc_bolt2_receiver_check_max_htlc_limit() {
64816485
cltv_expiry: htlc_cltv,
64826486
onion_routing_packet: onion_packet.clone(),
64836487
skimmed_fee_msat: None,
6488+
blinding_point: None,
64846489
};
64856490

64866491
for i in 0..50 {

0 commit comments

Comments
 (0)