-
Notifications
You must be signed in to change notification settings - Fork 409
Route blinding: support forwarding as the intro node #2540
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
1e12bdf
7f765a3
b70525d
1596116
b645237
ae15ba8
21ae9fd
a2b2fb0
50c850f
1a7254c
47d34c3
d2222c8
918f09c
c8adb54
67d2463
8c0c3a3
09cf484
b767d37
0a45870
4d43ccd
e510e3c
6af786a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -53,7 +53,7 @@ use crate::routing::scoring::{ProbabilisticScorer, ProbabilisticScoringFeeParame | |
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}; | ||
use crate::ln::msgs; | ||
use crate::ln::onion_utils; | ||
use crate::ln::onion_utils::HTLCFailReason; | ||
use crate::ln::onion_utils::{HTLCFailReason, INVALID_ONION_BLINDING}; | ||
use crate::ln::msgs::{ChannelMessageHandler, DecodeError, LightningError}; | ||
#[cfg(test)] | ||
use crate::ln::outbound_payment; | ||
|
@@ -119,6 +119,8 @@ pub enum PendingHTLCRouting { | |
/// The SCID from the onion that we should forward to. This could be a real SCID or a fake one | ||
/// generated using `get_fake_scid` from the scid_utils::fake_scid module. | ||
short_channel_id: u64, // This should be NonZero<u64> eventually when we bump MSRV | ||
/// Set if this HTLC is being forwarded within a blinded path. | ||
blinded: Option<BlindedForward>, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah, this is public now, we should include a bit more details about why this is here and what its used for. Happy to just do that in #2762. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good point, doing it in #2762 sgtm. FWIW, there are more detailed docs on the |
||
}, | ||
/// An HTLC paid to an invoice (supposedly) generated by us. | ||
/// At this point, we have not checked that the invoice being paid was actually generated by us, | ||
|
@@ -155,6 +157,28 @@ pub enum PendingHTLCRouting { | |
}, | ||
} | ||
|
||
/// Information used to forward or fail this HTLC that is being forwarded within a blinded path. | ||
#[derive(Clone, Copy, Hash, PartialEq, Eq)] | ||
pub struct BlindedForward { | ||
/// The `blinding_point` that was set in the inbound [`msgs::UpdateAddHTLC`], or in the inbound | ||
/// onion payload if we're the introduction node. Useful for calculating the next hop's | ||
/// [`msgs::UpdateAddHTLC::blinding_point`]. | ||
pub inbound_blinding_point: PublicKey, | ||
// Another field will be added here when we support forwarding as a non-intro node. | ||
} | ||
|
||
impl PendingHTLCRouting { | ||
// Used to override the onion failure code and data if the HTLC is blinded. | ||
fn blinded_failure(&self) -> Option<BlindedFailure> { | ||
// TODO: needs update when we support receiving to multi-hop blinded paths | ||
if let Self::Forward { blinded: Some(_), .. } = self { | ||
Some(BlindedFailure::FromIntroductionNode) | ||
} else { | ||
valentinewallace marked this conversation as resolved.
Show resolved
Hide resolved
|
||
None | ||
} | ||
} | ||
} | ||
|
||
/// Full details of an incoming HTLC, including routing info. | ||
#[derive(Clone)] // See Channel::revoke_and_ack for why, tl;dr: Rust bug | ||
pub struct PendingHTLCInfo { | ||
|
@@ -213,6 +237,13 @@ pub(super) enum HTLCForwardInfo { | |
}, | ||
} | ||
|
||
// Used for failing blinded HTLCs backwards correctly. | ||
#[derive(Clone, Debug, Hash, PartialEq, Eq)] | ||
enum BlindedFailure { | ||
FromIntroductionNode, | ||
// Another variant will be added here for non-intro nodes. | ||
} | ||
|
||
/// Tracks the inbound corresponding to an outbound HTLC | ||
#[derive(Clone, Debug, Hash, PartialEq, Eq)] | ||
pub(crate) struct HTLCPreviousHopData { | ||
|
@@ -222,6 +253,7 @@ pub(crate) struct HTLCPreviousHopData { | |
htlc_id: u64, | ||
incoming_packet_shared_secret: [u8; 32], | ||
phantom_shared_secret: Option<[u8; 32]>, | ||
blinded_failure: Option<BlindedFailure>, | ||
|
||
// This field is consumed by `claim_funds_from_hop()` when updating a force-closed backwards | ||
// channel with a preimage provided by the forward channel. | ||
|
@@ -2945,14 +2977,24 @@ where | |
msg, &self.node_signer, &self.logger, &self.secp_ctx | ||
)?; | ||
|
||
let is_blinded = match next_hop { | ||
onion_utils::Hop::Forward { | ||
next_hop_data: msgs::InboundOnionPayload::BlindedForward { .. }, .. | ||
} => true, | ||
_ => false, // TODO: update this when we support receiving to multi-hop blinded paths | ||
}; | ||
|
||
macro_rules! return_err { | ||
($msg: expr, $err_code: expr, $data: expr) => { | ||
{ | ||
log_info!(self.logger, "Failed to accept/forward incoming HTLC: {}", $msg); | ||
let (err_code, err_data) = if is_blinded { | ||
(INVALID_ONION_BLINDING, &[0; 32][..]) | ||
} else { ($err_code, $data) }; | ||
return Err(HTLCFailureMsg::Relay(msgs::UpdateFailHTLC { | ||
channel_id: msg.channel_id, | ||
htlc_id: msg.htlc_id, | ||
reason: HTLCFailReason::reason($err_code, $data.to_vec()) | ||
reason: HTLCFailReason::reason(err_code, err_data.to_vec()) | ||
.get_encrypted_failure_packet(&shared_secret, &None), | ||
})); | ||
} | ||
|
@@ -4013,8 +4055,10 @@ where | |
})?; | ||
|
||
let routing = match payment.forward_info.routing { | ||
PendingHTLCRouting::Forward { onion_packet, .. } => { | ||
PendingHTLCRouting::Forward { onion_packet, short_channel_id: next_hop_scid } | ||
PendingHTLCRouting::Forward { onion_packet, blinded, .. } => { | ||
PendingHTLCRouting::Forward { | ||
onion_packet, blinded, short_channel_id: next_hop_scid | ||
} | ||
}, | ||
_ => unreachable!() // Only `PendingHTLCRouting::Forward`s are intercepted | ||
}; | ||
|
@@ -4058,6 +4102,7 @@ where | |
htlc_id: payment.prev_htlc_id, | ||
incoming_packet_shared_secret: payment.forward_info.incoming_shared_secret, | ||
phantom_shared_secret: None, | ||
blinded_failure: payment.forward_info.routing.blinded_failure(), | ||
}); | ||
|
||
let failure_reason = HTLCFailReason::from_failure_code(0x4000 | 10); | ||
|
@@ -4106,6 +4151,7 @@ where | |
htlc_id: prev_htlc_id, | ||
incoming_packet_shared_secret: incoming_shared_secret, | ||
phantom_shared_secret: $phantom_ss, | ||
blinded_failure: routing.blinded_failure(), | ||
}); | ||
|
||
let reason = if $next_hop_unknown { | ||
|
@@ -4135,7 +4181,7 @@ where | |
} | ||
} | ||
} | ||
if let PendingHTLCRouting::Forward { onion_packet, .. } = routing { | ||
if let PendingHTLCRouting::Forward { ref onion_packet, .. } = routing { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Don't we need to handle the route blinding stuff here? If we have a forward to a phantom that takes a blinded path we should support overriding the error type, even though its not something we're gonna use right now its pretty trivial to do and we might as well? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We will always fail to decode |
||
let phantom_pubkey_res = self.node_signer.get_node_id(Recipient::PhantomNode); | ||
if phantom_pubkey_res.is_ok() && fake_scid::is_valid_phantom(&self.fake_scid_rand_bytes, short_chan_id, &self.chain_hash) { | ||
let phantom_shared_secret = self.node_signer.ecdh(Recipient::PhantomNode, &onion_packet.public_key.unwrap(), None).unwrap().secret_bytes(); | ||
|
@@ -4210,7 +4256,9 @@ where | |
prev_short_channel_id, prev_htlc_id, prev_funding_outpoint, prev_user_channel_id, | ||
forward_info: PendingHTLCInfo { | ||
incoming_shared_secret, payment_hash, outgoing_amt_msat, outgoing_cltv_value, | ||
routing: PendingHTLCRouting::Forward { onion_packet, .. }, skimmed_fee_msat, .. | ||
routing: PendingHTLCRouting::Forward { | ||
onion_packet, blinded, .. | ||
}, skimmed_fee_msat, .. | ||
}, | ||
}) => { | ||
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 | |
incoming_packet_shared_secret: incoming_shared_secret, | ||
// Phantom payments are only PendingHTLCRouting::Receive. | ||
phantom_shared_secret: None, | ||
blinded_failure: blinded.map(|_| BlindedFailure::FromIntroductionNode), | ||
}); | ||
let next_blinding_point = blinded.and_then(|b| { | ||
let encrypted_tlvs_ss = self.node_signer.ecdh( | ||
Recipient::Node, &b.inbound_blinding_point, None | ||
).unwrap().secret_bytes(); | ||
onion_utils::next_hop_pubkey( | ||
&self.secp_ctx, b.inbound_blinding_point, &encrypted_tlvs_ss | ||
).ok() | ||
}); | ||
if let Err(e) = chan.queue_add_htlc(outgoing_amt_msat, | ||
payment_hash, outgoing_cltv_value, htlc_source.clone(), | ||
onion_packet, skimmed_fee_msat, &self.fee_estimator, | ||
onion_packet, skimmed_fee_msat, next_blinding_point, &self.fee_estimator, | ||
&self.logger) | ||
{ | ||
if let ChannelError::Ignore(msg) = e { | ||
|
@@ -4276,6 +4333,7 @@ where | |
skimmed_fee_msat, .. | ||
} | ||
}) => { | ||
let blinded_failure = routing.blinded_failure(); | ||
let (cltv_expiry, onion_payload, payment_data, phantom_shared_secret, mut onion_fields) = match routing { | ||
PendingHTLCRouting::Receive { payment_data, payment_metadata, incoming_cltv_expiry, phantom_shared_secret, custom_tlvs } => { | ||
let _legacy_hop_data = Some(payment_data.clone()); | ||
|
@@ -4305,6 +4363,7 @@ where | |
htlc_id: prev_htlc_id, | ||
incoming_packet_shared_secret: incoming_shared_secret, | ||
phantom_shared_secret, | ||
blinded_failure, | ||
}, | ||
// We differentiate the received value from the sender intended value | ||
// if possible so that we don't prematurely mark MPP payments complete | ||
|
@@ -4335,6 +4394,7 @@ where | |
htlc_id: $htlc.prev_hop.htlc_id, | ||
incoming_packet_shared_secret: $htlc.prev_hop.incoming_packet_shared_secret, | ||
phantom_shared_secret, | ||
blinded_failure: None, | ||
}), payment_hash, | ||
HTLCFailReason::reason(0x4000 | 15, htlc_msat_height_data), | ||
HTLCDestination::FailedPayment { payment_hash: $payment_hash }, | ||
|
@@ -5098,9 +5158,23 @@ where | |
&self.pending_events, &self.logger) | ||
{ self.push_pending_forwards_ev(); } | ||
}, | ||
HTLCSource::PreviousHopData(HTLCPreviousHopData { ref short_channel_id, ref htlc_id, ref incoming_packet_shared_secret, ref phantom_shared_secret, ref outpoint, .. }) => { | ||
log_trace!(self.logger, "Failing HTLC with payment_hash {} backwards from us with {:?}", &payment_hash, onion_error); | ||
let err_packet = onion_error.get_encrypted_failure_packet(incoming_packet_shared_secret, phantom_shared_secret); | ||
HTLCSource::PreviousHopData(HTLCPreviousHopData { | ||
ref short_channel_id, ref htlc_id, ref incoming_packet_shared_secret, | ||
ref phantom_shared_secret, ref outpoint, ref blinded_failure, .. | ||
}) => { | ||
log_trace!(self.logger, "Failing {}HTLC with payment_hash {} backwards from us: {:?}", | ||
if blinded_failure.is_some() { "blinded " } else { "" }, &payment_hash, onion_error); | ||
let err_packet = match blinded_failure { | ||
Some(BlindedFailure::FromIntroductionNode) => { | ||
let blinded_onion_error = HTLCFailReason::reason(INVALID_ONION_BLINDING, vec![0; 32]); | ||
blinded_onion_error.get_encrypted_failure_packet( | ||
incoming_packet_shared_secret, phantom_shared_secret | ||
) | ||
}, | ||
None => { | ||
onion_error.get_encrypted_failure_packet(incoming_packet_shared_secret, phantom_shared_secret) | ||
} | ||
}; | ||
|
||
let mut push_forward_ev = false; | ||
let mut forward_htlcs = self.forward_htlcs.lock().unwrap(); | ||
|
@@ -6381,8 +6455,12 @@ where | |
// but if we've sent a shutdown and they haven't acknowledged it yet, we just | ||
// want to reject the new HTLC and fail it backwards instead of forwarding. | ||
match pending_forward_info { | ||
PendingHTLCStatus::Forward(PendingHTLCInfo { ref incoming_shared_secret, .. }) => { | ||
let reason = if (error_code & 0x1000) != 0 { | ||
PendingHTLCStatus::Forward(PendingHTLCInfo { | ||
ref incoming_shared_secret, ref routing, .. | ||
}) => { | ||
let reason = if routing.blinded_failure().is_some() { | ||
HTLCFailReason::reason(INVALID_ONION_BLINDING, vec![0; 32]) | ||
} else if (error_code & 0x1000) != 0 { | ||
let (real_code, error_data) = self.get_htlc_inbound_temp_fail_err_and_data(error_code, chan); | ||
HTLCFailReason::reason(real_code, error_data) | ||
} else { | ||
|
@@ -6584,6 +6662,7 @@ where | |
htlc_id: prev_htlc_id, | ||
incoming_packet_shared_secret: forward_info.incoming_shared_secret, | ||
phantom_shared_secret: None, | ||
blinded_failure: forward_info.routing.blinded_failure(), | ||
}); | ||
|
||
failed_intercept_forwards.push((htlc_source, forward_info.payment_hash, | ||
|
@@ -8180,6 +8259,7 @@ where | |
incoming_packet_shared_secret: htlc.forward_info.incoming_shared_secret, | ||
phantom_shared_secret: None, | ||
outpoint: htlc.prev_funding_outpoint, | ||
blinded_failure: htlc.forward_info.routing.blinded_failure(), | ||
}); | ||
|
||
let requested_forward_scid /* intercept scid */ = match htlc.forward_info.routing { | ||
|
@@ -9143,9 +9223,14 @@ impl_writeable_tlv_based!(PhantomRouteHints, { | |
(6, real_node_pubkey, required), | ||
}); | ||
|
||
impl_writeable_tlv_based!(BlindedForward, { | ||
(0, inbound_blinding_point, required), | ||
}); | ||
|
||
impl_writeable_tlv_based_enum!(PendingHTLCRouting, | ||
(0, Forward) => { | ||
(0, onion_packet, required), | ||
(1, blinded, option), | ||
(2, short_channel_id, required), | ||
}, | ||
(1, Receive) => { | ||
|
@@ -9247,10 +9332,15 @@ impl_writeable_tlv_based_enum!(PendingHTLCStatus, ; | |
(1, Fail), | ||
); | ||
|
||
impl_writeable_tlv_based_enum!(BlindedFailure, | ||
(0, FromIntroductionNode) => {}, ; | ||
); | ||
|
||
impl_writeable_tlv_based!(HTLCPreviousHopData, { | ||
(0, short_channel_id, required), | ||
(1, phantom_shared_secret, option), | ||
(2, outpoint, required), | ||
(3, blinded_failure, option), | ||
(4, htlc_id, required), | ||
(6, incoming_packet_shared_secret, required), | ||
(7, user_channel_id, option), | ||
|
Uh oh!
There was an error while loading. Please reload this page.