Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
1e12bdf
Parse blinding point in UpdateAddHTLC
valentinewallace Mar 23, 2023
7f765a3
onion_utils: extract decrypting faiure packet into method
valentinewallace Oct 26, 2023
b70525d
Parse blinded onion errors in tests only.
valentinewallace Oct 26, 2023
1596116
Persist outbound blinding points in Channel
valentinewallace Jul 13, 2023
b645237
Store whether a forwarded HTLC is blinded in PendingHTLCRouting
valentinewallace Oct 26, 2023
ae15ba8
Persist whether an HTLC is blinded in HTLCPreviousHopData.
valentinewallace Oct 26, 2023
21ae9fd
Set HTLCPreviousHopData::blinded on intro node forward.
valentinewallace Oct 26, 2023
a2b2fb0
Parameterize Channel's htlc forward method by outbound blinding point
valentinewallace Oct 26, 2023
50c850f
Set update_add blinding point on HTLC forward
valentinewallace Oct 26, 2023
1a7254c
Parse blinded forward-as-intro onion payloads
valentinewallace Oct 26, 2023
47d34c3
Support forwarding blinded HTLCs as intro node.
valentinewallace Oct 26, 2023
d2222c8
Remove now-unused Readable impl for ReceiveTlvs
valentinewallace Oct 25, 2023
918f09c
Test blinded forward failure to calculate outbound cltv expiry
valentinewallace Sep 15, 2023
c8adb54
Test blinded forwarding payload encoded as receive error case
valentinewallace Sep 15, 2023
67d2463
Correctly fail back on outbound channel check for blinded HTLC
valentinewallace Sep 15, 2023
8c0c3a3
Extract blinded route param creation into test util
valentinewallace Oct 2, 2023
09cf484
Correctly fail back blinded inbound fwd HTLCs when adding to a Channel
valentinewallace Oct 3, 2023
b767d37
Correctly fail back downstream-failed blinded HTLCs as intro
valentinewallace Oct 9, 2023
0a45870
Test intro node blinded HTLC failing in process_pending_htlc_fwds.
valentinewallace Oct 31, 2023
4d43ccd
Test intro node failing blinded intercept HTLC.
valentinewallace Oct 31, 2023
e510e3c
Add release note for blinded HTLC backwards compat.
valentinewallace Nov 7, 2023
6af786a
Test blinding point serialization in Channel.
valentinewallace Nov 13, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 1 addition & 16 deletions lightning/src/blinded_path/payment.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -118,21 +118,6 @@ impl Writeable for ReceiveTlvs {
}
}

// This will be removed once we support forwarding blinded HTLCs, because we'll always read a
// `BlindedPaymentTlvs` instead.
impl Readable for ReceiveTlvs {
fn read<R: io::Read>(r: &mut R) -> Result<Self, DecodeError> {
_init_and_read_tlv_stream!(r, {
(12, payment_constraints, required),
(65536, payment_secret, required),
});
Ok(Self {
payment_secret: payment_secret.0.unwrap(),
payment_constraints: payment_constraints.0.unwrap()
})
}
}

impl<'a> Writeable for BlindedPaymentTlvsRef<'a> {
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
// TODO: write padding
Expand DownExpand Up@@ -187,7 +172,7 @@ pub(super) fn blinded_hops<T: secp256k1::Signing + secp256k1::Verification>(
}

/// `None` if underflow occurs.
fn amt_to_forward_msat(inbound_amt_msat: u64, payment_relay: &PaymentRelay) -> Option<u64> {
pub(crate) fn amt_to_forward_msat(inbound_amt_msat: u64, payment_relay: &PaymentRelay) -> Option<u64> {
let inbound_amt = inbound_amt_msat as u128;
let base = payment_relay.fee_base_msat as u128;
let prop = payment_relay.fee_proportional_millionths as u128;
Expand Down
329 changes: 326 additions & 3 deletions lightning/src/ln/blinded_payment_tests.rs

Large diffs are not rendered by default.

169 changes: 155 additions & 14 deletions lightning/src/ln/channel.rs

Large diffs are not rendered by default.

114 changes: 102 additions & 12 deletions lightning/src/ln/channelmanager.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -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;
Expand DownExpand Up@@ -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>,
Comment thread
valentinewallace marked this conversation as resolved.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The 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.

@valentinewallacevalentinewallaceNov 30, 2023

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The 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 BlindedForward struct itself.

},
/// 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,
Expand DownExpand Up@@ -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 {
Comment thread
valentinewallace marked this conversation as 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 {
Expand DownExpand Up@@ -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 {
Expand All@@ -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.
Expand DownExpand Up@@ -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),
}));
}
Expand DownExpand Up@@ -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
};
Expand DownExpand Up@@ -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);
Expand DownExpand Up@@ -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 {
Expand DownExpand Up@@ -4135,7 +4181,7 @@ where
}
}
}
if let PendingHTLCRouting::Forward { onion_packet, .. } = routing {
if let PendingHTLCRouting::Forward { ref onion_packet, .. } = routing {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The 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?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We will always fail to decode onion_packet if it's a phantom, because we use Recipient::Node instead of ::PhantomNode when decoding the encrypted TLVs. Not much we can do here until we add full phantom support, IIUC.

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();
Expand DownExpand Up@@ -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);
Expand All@@ -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 {
Expand DownExpand Up@@ -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());
Expand DownExpand Up@@ -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
Expand DownExpand Up@@ -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 },
Expand DownExpand Up@@ -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();
Expand DownExpand Up@@ -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 {
Expand DownExpand Up@@ -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,
Expand DownExpand Up@@ -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 {
Expand DownExpand Up@@ -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) => {
Expand DownExpand Up@@ -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),
Expand Down
5 changes: 5 additions & 0 deletions lightning/src/ln/functional_tests.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -1415,6 +1415,7 @@ fn test_fee_spike_violation_fails_htlc() {
cltv_expiry: htlc_cltv,
onion_routing_packet: onion_packet,
skimmed_fee_msat: None,
blinding_point: None,
};

nodes[1].node.handle_update_add_htlc(&nodes[0].node.get_our_node_id(), &msg);
Expand DownExpand Up@@ -1611,6 +1612,7 @@ fn test_chan_reserve_violation_inbound_htlc_outbound_channel() {
cltv_expiry: htlc_cltv,
onion_routing_packet: onion_packet,
skimmed_fee_msat: None,
blinding_point: None,
};

nodes[0].node.handle_update_add_htlc(&nodes[1].node.get_our_node_id(), &msg);
Expand DownExpand Up@@ -1789,6 +1791,7 @@ fn test_chan_reserve_violation_inbound_htlc_inbound_chan() {
cltv_expiry: htlc_cltv,
onion_routing_packet: onion_packet,
skimmed_fee_msat: None,
blinding_point: None,
};

nodes[1].node.handle_update_add_htlc(&nodes[0].node.get_our_node_id(), &msg);
Expand DownExpand Up@@ -3510,6 +3513,7 @@ fn fail_backward_pending_htlc_upon_channel_failure() {
cltv_expiry,
onion_routing_packet,
skimmed_fee_msat: None,
blinding_point: None,
};
nodes[0].node.handle_update_add_htlc(&nodes[1].node.get_our_node_id(), &update_add_htlc);
}
Expand DownExpand Up@@ -6481,6 +6485,7 @@ fn test_update_add_htlc_bolt2_receiver_check_max_htlc_limit() {
cltv_expiry: htlc_cltv,
onion_routing_packet: onion_packet.clone(),
skimmed_fee_msat: None,
blinding_point: None,
};

for i in 0..50 {
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
1e12bdf
Parse blinding point in UpdateAddHTLC
valentinewallace Mar 23, 2023
7f765a3
onion_utils: extract decrypting faiure packet into method
valentinewallace Oct 26, 2023
b70525d
Parse blinded onion errors in tests only.
valentinewallace Oct 26, 2023
1596116
Persist outbound blinding points in Channel
valentinewallace Jul 13, 2023
b645237
Store whether a forwarded HTLC is blinded in PendingHTLCRouting
valentinewallace Oct 26, 2023
ae15ba8
Persist whether an HTLC is blinded in HTLCPreviousHopData.
valentinewallace Oct 26, 2023
21ae9fd
Set HTLCPreviousHopData::blinded on intro node forward.
valentinewallace Oct 26, 2023
a2b2fb0
Parameterize Channel's htlc forward method by outbound blinding point
valentinewallace Oct 26, 2023
50c850f
Set update_add blinding point on HTLC forward
valentinewallace Oct 26, 2023
1a7254c
Parse blinded forward-as-intro onion payloads
valentinewallace Oct 26, 2023
47d34c3
Support forwarding blinded HTLCs as intro node.
valentinewallace Oct 26, 2023
d2222c8
Remove now-unused Readable impl for ReceiveTlvs
valentinewallace Oct 25, 2023
918f09c
Test blinded forward failure to calculate outbound cltv expiry
valentinewallace Sep 15, 2023
c8adb54
Test blinded forwarding payload encoded as receive error case
valentinewallace Sep 15, 2023
67d2463
Correctly fail back on outbound channel check for blinded HTLC
valentinewallace Sep 15, 2023
8c0c3a3
Extract blinded route param creation into test util
valentinewallace Oct 2, 2023
09cf484
Correctly fail back blinded inbound fwd HTLCs when adding to a Channel
valentinewallace Oct 3, 2023
b767d37
Correctly fail back downstream-failed blinded HTLCs as intro
valentinewallace Oct 9, 2023
0a45870
Test intro node blinded HTLC failing in process_pending_htlc_fwds.
valentinewallace Oct 31, 2023
4d43ccd
Test intro node failing blinded intercept HTLC.
valentinewallace Oct 31, 2023
e510e3c
Add release note for blinded HTLC backwards compat.
valentinewallace Nov 7, 2023
6af786a
Test blinding point serialization in Channel.
valentinewallace Nov 13, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 1 addition & 16 deletions lightning/src/blinded_path/payment.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -118,21 +118,6 @@ impl Writeable for ReceiveTlvs {
}
}

// This will be removed once we support forwarding blinded HTLCs, because we'll always read a
// `BlindedPaymentTlvs` instead.
impl Readable for ReceiveTlvs {
fn read<R: io::Read>(r: &mut R) -> Result<Self, DecodeError> {
_init_and_read_tlv_stream!(r, {
(12, payment_constraints, required),
(65536, payment_secret, required),
});
Ok(Self {
payment_secret: payment_secret.0.unwrap(),
payment_constraints: payment_constraints.0.unwrap()
})
}
}

impl<'a> Writeable for BlindedPaymentTlvsRef<'a> {
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
// TODO: write padding
Expand DownExpand Up@@ -187,7 +172,7 @@ pub(super) fn blinded_hops<T: secp256k1::Signing + secp256k1::Verification>(
}

/// `None` if underflow occurs.
fn amt_to_forward_msat(inbound_amt_msat: u64, payment_relay: &PaymentRelay) -> Option<u64> {
pub(crate) fn amt_to_forward_msat(inbound_amt_msat: u64, payment_relay: &PaymentRelay) -> Option<u64> {
let inbound_amt = inbound_amt_msat as u128;
let base = payment_relay.fee_base_msat as u128;
let prop = payment_relay.fee_proportional_millionths as u128;
Expand Down
329 changes: 326 additions & 3 deletions lightning/src/ln/blinded_payment_tests.rs

Large diffs are not rendered by default.

169 changes: 155 additions & 14 deletions lightning/src/ln/channel.rs

Large diffs are not rendered by default.

114 changes: 102 additions & 12 deletions lightning/src/ln/channelmanager.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -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;
Expand DownExpand Up@@ -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>,
Comment thread
valentinewallace marked this conversation as resolved.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The 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.

@valentinewallacevalentinewallaceNov 30, 2023

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The 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 BlindedForward struct itself.

},
/// 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,
Expand DownExpand Up@@ -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 {
Comment thread
valentinewallace marked this conversation as 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 {
Expand DownExpand Up@@ -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 {
Expand All@@ -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.
Expand DownExpand Up@@ -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),
}));
}
Expand DownExpand Up@@ -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
};
Expand DownExpand Up@@ -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);
Expand DownExpand Up@@ -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 {
Expand DownExpand Up@@ -4135,7 +4181,7 @@ where
}
}
}
if let PendingHTLCRouting::Forward { onion_packet, .. } = routing {
if let PendingHTLCRouting::Forward { ref onion_packet, .. } = routing {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The 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?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We will always fail to decode onion_packet if it's a phantom, because we use Recipient::Node instead of ::PhantomNode when decoding the encrypted TLVs. Not much we can do here until we add full phantom support, IIUC.

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();
Expand DownExpand Up@@ -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);
Expand All@@ -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 {
Expand DownExpand Up@@ -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());
Expand DownExpand Up@@ -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
Expand DownExpand Up@@ -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 },
Expand DownExpand Up@@ -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();
Expand DownExpand Up@@ -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 {
Expand DownExpand Up@@ -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,
Expand DownExpand Up@@ -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 {
Expand DownExpand Up@@ -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) => {
Expand DownExpand Up@@ -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),
Expand Down
5 changes: 5 additions & 0 deletions lightning/src/ln/functional_tests.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -1415,6 +1415,7 @@ fn test_fee_spike_violation_fails_htlc() {
cltv_expiry: htlc_cltv,
onion_routing_packet: onion_packet,
skimmed_fee_msat: None,
blinding_point: None,
};

nodes[1].node.handle_update_add_htlc(&nodes[0].node.get_our_node_id(), &msg);
Expand DownExpand Up@@ -1611,6 +1612,7 @@ fn test_chan_reserve_violation_inbound_htlc_outbound_channel() {
cltv_expiry: htlc_cltv,
onion_routing_packet: onion_packet,
skimmed_fee_msat: None,
blinding_point: None,
};

nodes[0].node.handle_update_add_htlc(&nodes[1].node.get_our_node_id(), &msg);
Expand DownExpand Up@@ -1789,6 +1791,7 @@ fn test_chan_reserve_violation_inbound_htlc_inbound_chan() {
cltv_expiry: htlc_cltv,
onion_routing_packet: onion_packet,
skimmed_fee_msat: None,
blinding_point: None,
};

nodes[1].node.handle_update_add_htlc(&nodes[0].node.get_our_node_id(), &msg);
Expand DownExpand Up@@ -3510,6 +3513,7 @@ fn fail_backward_pending_htlc_upon_channel_failure() {
cltv_expiry,
onion_routing_packet,
skimmed_fee_msat: None,
blinding_point: None,
};
nodes[0].node.handle_update_add_htlc(&nodes[1].node.get_our_node_id(), &update_add_htlc);
}
Expand DownExpand Up@@ -6481,6 +6485,7 @@ fn test_update_add_htlc_bolt2_receiver_check_max_htlc_limit() {
cltv_expiry: htlc_cltv,
onion_routing_packet: onion_packet.clone(),
skimmed_fee_msat: None,
blinding_point: None,
};

for i in 0..50 {
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
1e12bdf
Parse blinding point in UpdateAddHTLC
valentinewallace Mar 23, 2023
7f765a3
onion_utils: extract decrypting faiure packet into method
valentinewallace Oct 26, 2023
b70525d
Parse blinded onion errors in tests only.
valentinewallace Oct 26, 2023
1596116
Persist outbound blinding points in Channel
valentinewallace Jul 13, 2023
b645237
Store whether a forwarded HTLC is blinded in PendingHTLCRouting
valentinewallace Oct 26, 2023
ae15ba8
Persist whether an HTLC is blinded in HTLCPreviousHopData.
valentinewallace Oct 26, 2023
21ae9fd
Set HTLCPreviousHopData::blinded on intro node forward.
valentinewallace Oct 26, 2023
a2b2fb0
Parameterize Channel's htlc forward method by outbound blinding point
valentinewallace Oct 26, 2023
50c850f
Set update_add blinding point on HTLC forward
valentinewallace Oct 26, 2023
1a7254c
Parse blinded forward-as-intro onion payloads
valentinewallace Oct 26, 2023
47d34c3
Support forwarding blinded HTLCs as intro node.
valentinewallace Oct 26, 2023
d2222c8
Remove now-unused Readable impl for ReceiveTlvs
valentinewallace Oct 25, 2023
918f09c
Test blinded forward failure to calculate outbound cltv expiry
valentinewallace Sep 15, 2023
c8adb54
Test blinded forwarding payload encoded as receive error case
valentinewallace Sep 15, 2023
67d2463
Correctly fail back on outbound channel check for blinded HTLC
valentinewallace Sep 15, 2023
8c0c3a3
Extract blinded route param creation into test util
valentinewallace Oct 2, 2023
09cf484
Correctly fail back blinded inbound fwd HTLCs when adding to a Channel
valentinewallace Oct 3, 2023
b767d37
Correctly fail back downstream-failed blinded HTLCs as intro
valentinewallace Oct 9, 2023
0a45870
Test intro node blinded HTLC failing in process_pending_htlc_fwds.
valentinewallace Oct 31, 2023
4d43ccd
Test intro node failing blinded intercept HTLC.
valentinewallace Oct 31, 2023
e510e3c
Add release note for blinded HTLC backwards compat.
valentinewallace Nov 7, 2023
6af786a
Test blinding point serialization in Channel.
valentinewallace Nov 13, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 1 addition & 16 deletions lightning/src/blinded_path/payment.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -118,21 +118,6 @@ impl Writeable for ReceiveTlvs {
}
}

// This will be removed once we support forwarding blinded HTLCs, because we'll always read a
// `BlindedPaymentTlvs` instead.
impl Readable for ReceiveTlvs {
fn read<R: io::Read>(r: &mut R) -> Result<Self, DecodeError> {
_init_and_read_tlv_stream!(r, {
(12, payment_constraints, required),
(65536, payment_secret, required),
});
Ok(Self {
payment_secret: payment_secret.0.unwrap(),
payment_constraints: payment_constraints.0.unwrap()
})
}
}

impl<'a> Writeable for BlindedPaymentTlvsRef<'a> {
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
// TODO: write padding
Expand DownExpand Up@@ -187,7 +172,7 @@ pub(super) fn blinded_hops<T: secp256k1::Signing + secp256k1::Verification>(
}

/// `None` if underflow occurs.
fn amt_to_forward_msat(inbound_amt_msat: u64, payment_relay: &PaymentRelay) -> Option<u64> {
pub(crate) fn amt_to_forward_msat(inbound_amt_msat: u64, payment_relay: &PaymentRelay) -> Option<u64> {
let inbound_amt = inbound_amt_msat as u128;
let base = payment_relay.fee_base_msat as u128;
let prop = payment_relay.fee_proportional_millionths as u128;
Expand Down
329 changes: 326 additions & 3 deletions lightning/src/ln/blinded_payment_tests.rs

Large diffs are not rendered by default.

169 changes: 155 additions & 14 deletions lightning/src/ln/channel.rs

Large diffs are not rendered by default.

114 changes: 102 additions & 12 deletions lightning/src/ln/channelmanager.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -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;
Expand DownExpand Up@@ -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>,
Comment thread
valentinewallace marked this conversation as resolved.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The 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.

@valentinewallacevalentinewallaceNov 30, 2023

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The 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 BlindedForward struct itself.

},
/// 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,
Expand DownExpand Up@@ -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 {
Comment thread
valentinewallace marked this conversation as 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 {
Expand DownExpand Up@@ -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 {
Expand All@@ -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.
Expand DownExpand Up@@ -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),
}));
}
Expand DownExpand Up@@ -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
};
Expand DownExpand Up@@ -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);
Expand DownExpand Up@@ -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 {
Expand DownExpand Up@@ -4135,7 +4181,7 @@ where
}
}
}
if let PendingHTLCRouting::Forward { onion_packet, .. } = routing {
if let PendingHTLCRouting::Forward { ref onion_packet, .. } = routing {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The 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?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We will always fail to decode onion_packet if it's a phantom, because we use Recipient::Node instead of ::PhantomNode when decoding the encrypted TLVs. Not much we can do here until we add full phantom support, IIUC.

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();
Expand DownExpand Up@@ -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);
Expand All@@ -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 {
Expand DownExpand Up@@ -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());
Expand DownExpand Up@@ -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
Expand DownExpand Up@@ -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 },
Expand DownExpand Up@@ -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();
Expand DownExpand Up@@ -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 {
Expand DownExpand Up@@ -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,
Expand DownExpand Up@@ -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 {
Expand DownExpand Up@@ -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) => {
Expand DownExpand Up@@ -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),
Expand Down
5 changes: 5 additions & 0 deletions lightning/src/ln/functional_tests.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -1415,6 +1415,7 @@ fn test_fee_spike_violation_fails_htlc() {
cltv_expiry: htlc_cltv,
onion_routing_packet: onion_packet,
skimmed_fee_msat: None,
blinding_point: None,
};

nodes[1].node.handle_update_add_htlc(&nodes[0].node.get_our_node_id(), &msg);
Expand DownExpand Up@@ -1611,6 +1612,7 @@ fn test_chan_reserve_violation_inbound_htlc_outbound_channel() {
cltv_expiry: htlc_cltv,
onion_routing_packet: onion_packet,
skimmed_fee_msat: None,
blinding_point: None,
};

nodes[0].node.handle_update_add_htlc(&nodes[1].node.get_our_node_id(), &msg);
Expand DownExpand Up@@ -1789,6 +1791,7 @@ fn test_chan_reserve_violation_inbound_htlc_inbound_chan() {
cltv_expiry: htlc_cltv,
onion_routing_packet: onion_packet,
skimmed_fee_msat: None,
blinding_point: None,
};

nodes[1].node.handle_update_add_htlc(&nodes[0].node.get_our_node_id(), &msg);
Expand DownExpand Up@@ -3510,6 +3513,7 @@ fn fail_backward_pending_htlc_upon_channel_failure() {
cltv_expiry,
onion_routing_packet,
skimmed_fee_msat: None,
blinding_point: None,
};
nodes[0].node.handle_update_add_htlc(&nodes[1].node.get_our_node_id(), &update_add_htlc);
}
Expand DownExpand Up@@ -6481,6 +6485,7 @@ fn test_update_add_htlc_bolt2_receiver_check_max_htlc_limit() {
cltv_expiry: htlc_cltv,
onion_routing_packet: onion_packet.clone(),
skimmed_fee_msat: None,
blinding_point: None,
};

for i in 0..50 {
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
1e12bdf
Parse blinding point in UpdateAddHTLC
valentinewallace Mar 23, 2023
7f765a3
onion_utils: extract decrypting faiure packet into method
valentinewallace Oct 26, 2023
b70525d
Parse blinded onion errors in tests only.
valentinewallace Oct 26, 2023
1596116
Persist outbound blinding points in Channel
valentinewallace Jul 13, 2023
b645237
Store whether a forwarded HTLC is blinded in PendingHTLCRouting
valentinewallace Oct 26, 2023
ae15ba8
Persist whether an HTLC is blinded in HTLCPreviousHopData.
valentinewallace Oct 26, 2023
21ae9fd
Set HTLCPreviousHopData::blinded on intro node forward.
valentinewallace Oct 26, 2023
a2b2fb0
Parameterize Channel's htlc forward method by outbound blinding point
valentinewallace Oct 26, 2023
50c850f
Set update_add blinding point on HTLC forward
valentinewallace Oct 26, 2023
1a7254c
Parse blinded forward-as-intro onion payloads
valentinewallace Oct 26, 2023
47d34c3
Support forwarding blinded HTLCs as intro node.
valentinewallace Oct 26, 2023
d2222c8
Remove now-unused Readable impl for ReceiveTlvs
valentinewallace Oct 25, 2023
918f09c
Test blinded forward failure to calculate outbound cltv expiry
valentinewallace Sep 15, 2023
c8adb54
Test blinded forwarding payload encoded as receive error case
valentinewallace Sep 15, 2023
67d2463
Correctly fail back on outbound channel check for blinded HTLC
valentinewallace Sep 15, 2023
8c0c3a3
Extract blinded route param creation into test util
valentinewallace Oct 2, 2023
09cf484
Correctly fail back blinded inbound fwd HTLCs when adding to a Channel
valentinewallace Oct 3, 2023
b767d37
Correctly fail back downstream-failed blinded HTLCs as intro
valentinewallace Oct 9, 2023
0a45870
Test intro node blinded HTLC failing in process_pending_htlc_fwds.
valentinewallace Oct 31, 2023
4d43ccd
Test intro node failing blinded intercept HTLC.
valentinewallace Oct 31, 2023
e510e3c
Add release note for blinded HTLC backwards compat.
valentinewallace Nov 7, 2023
6af786a
Test blinding point serialization in Channel.
valentinewallace Nov 13, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 1 addition & 16 deletions lightning/src/blinded_path/payment.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -118,21 +118,6 @@ impl Writeable for ReceiveTlvs {
}
}

// This will be removed once we support forwarding blinded HTLCs, because we'll always read a
// `BlindedPaymentTlvs` instead.
impl Readable for ReceiveTlvs {
fn read<R: io::Read>(r: &mut R) -> Result<Self, DecodeError> {
_init_and_read_tlv_stream!(r, {
(12, payment_constraints, required),
(65536, payment_secret, required),
});
Ok(Self {
payment_secret: payment_secret.0.unwrap(),
payment_constraints: payment_constraints.0.unwrap()
})
}
}

impl<'a> Writeable for BlindedPaymentTlvsRef<'a> {
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
// TODO: write padding
Expand DownExpand Up@@ -187,7 +172,7 @@ pub(super) fn blinded_hops<T: secp256k1::Signing + secp256k1::Verification>(
}

/// `None` if underflow occurs.
fn amt_to_forward_msat(inbound_amt_msat: u64, payment_relay: &PaymentRelay) -> Option<u64> {
pub(crate) fn amt_to_forward_msat(inbound_amt_msat: u64, payment_relay: &PaymentRelay) -> Option<u64> {
let inbound_amt = inbound_amt_msat as u128;
let base = payment_relay.fee_base_msat as u128;
let prop = payment_relay.fee_proportional_millionths as u128;
Expand Down
329 changes: 326 additions & 3 deletions lightning/src/ln/blinded_payment_tests.rs

Large diffs are not rendered by default.

169 changes: 155 additions & 14 deletions lightning/src/ln/channel.rs

Large diffs are not rendered by default.

114 changes: 102 additions & 12 deletions lightning/src/ln/channelmanager.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -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;
Expand DownExpand Up@@ -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>,
Comment thread
valentinewallace marked this conversation as resolved.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The 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.

@valentinewallacevalentinewallaceNov 30, 2023

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The 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 BlindedForward struct itself.

},
/// 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,
Expand DownExpand Up@@ -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 {
Comment thread
valentinewallace marked this conversation as 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 {
Expand DownExpand Up@@ -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 {
Expand All@@ -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.
Expand DownExpand Up@@ -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),
}));
}
Expand DownExpand Up@@ -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
};
Expand DownExpand Up@@ -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);
Expand DownExpand Up@@ -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 {
Expand DownExpand Up@@ -4135,7 +4181,7 @@ where
}
}
}
if let PendingHTLCRouting::Forward { onion_packet, .. } = routing {
if let PendingHTLCRouting::Forward { ref onion_packet, .. } = routing {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The 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?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We will always fail to decode onion_packet if it's a phantom, because we use Recipient::Node instead of ::PhantomNode when decoding the encrypted TLVs. Not much we can do here until we add full phantom support, IIUC.

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();
Expand DownExpand Up@@ -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);
Expand All@@ -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 {
Expand DownExpand Up@@ -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());
Expand DownExpand Up@@ -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
Expand DownExpand Up@@ -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 },
Expand DownExpand Up@@ -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();
Expand DownExpand Up@@ -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 {
Expand DownExpand Up@@ -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,
Expand DownExpand Up@@ -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 {
Expand DownExpand Up@@ -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) => {
Expand DownExpand Up@@ -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),
Expand Down
5 changes: 5 additions & 0 deletions lightning/src/ln/functional_tests.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -1415,6 +1415,7 @@ fn test_fee_spike_violation_fails_htlc() {
cltv_expiry: htlc_cltv,
onion_routing_packet: onion_packet,
skimmed_fee_msat: None,
blinding_point: None,
};

nodes[1].node.handle_update_add_htlc(&nodes[0].node.get_our_node_id(), &msg);
Expand DownExpand Up@@ -1611,6 +1612,7 @@ fn test_chan_reserve_violation_inbound_htlc_outbound_channel() {
cltv_expiry: htlc_cltv,
onion_routing_packet: onion_packet,
skimmed_fee_msat: None,
blinding_point: None,
};

nodes[0].node.handle_update_add_htlc(&nodes[1].node.get_our_node_id(), &msg);
Expand DownExpand Up@@ -1789,6 +1791,7 @@ fn test_chan_reserve_violation_inbound_htlc_inbound_chan() {
cltv_expiry: htlc_cltv,
onion_routing_packet: onion_packet,
skimmed_fee_msat: None,
blinding_point: None,
};

nodes[1].node.handle_update_add_htlc(&nodes[0].node.get_our_node_id(), &msg);
Expand DownExpand Up@@ -3510,6 +3513,7 @@ fn fail_backward_pending_htlc_upon_channel_failure() {
cltv_expiry,
onion_routing_packet,
skimmed_fee_msat: None,
blinding_point: None,
};
nodes[0].node.handle_update_add_htlc(&nodes[1].node.get_our_node_id(), &update_add_htlc);
}
Expand DownExpand Up@@ -6481,6 +6485,7 @@ fn test_update_add_htlc_bolt2_receiver_check_max_htlc_limit() {
cltv_expiry: htlc_cltv,
onion_routing_packet: onion_packet.clone(),
skimmed_fee_msat: None,
blinding_point: None,
};

for i in 0..50 {
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
1e12bdf
Parse blinding point in UpdateAddHTLC
valentinewallace Mar 23, 2023
7f765a3
onion_utils: extract decrypting faiure packet into method
valentinewallace Oct 26, 2023
b70525d
Parse blinded onion errors in tests only.
valentinewallace Oct 26, 2023
1596116
Persist outbound blinding points in Channel
valentinewallace Jul 13, 2023
b645237
Store whether a forwarded HTLC is blinded in PendingHTLCRouting
valentinewallace Oct 26, 2023
ae15ba8
Persist whether an HTLC is blinded in HTLCPreviousHopData.
valentinewallace Oct 26, 2023
21ae9fd
Set HTLCPreviousHopData::blinded on intro node forward.
valentinewallace Oct 26, 2023
a2b2fb0
Parameterize Channel's htlc forward method by outbound blinding point
valentinewallace Oct 26, 2023
50c850f
Set update_add blinding point on HTLC forward
valentinewallace Oct 26, 2023
1a7254c
Parse blinded forward-as-intro onion payloads
valentinewallace Oct 26, 2023
47d34c3
Support forwarding blinded HTLCs as intro node.
valentinewallace Oct 26, 2023
d2222c8
Remove now-unused Readable impl for ReceiveTlvs
valentinewallace Oct 25, 2023
918f09c
Test blinded forward failure to calculate outbound cltv expiry
valentinewallace Sep 15, 2023
c8adb54
Test blinded forwarding payload encoded as receive error case
valentinewallace Sep 15, 2023
67d2463
Correctly fail back on outbound channel check for blinded HTLC
valentinewallace Sep 15, 2023
8c0c3a3
Extract blinded route param creation into test util
valentinewallace Oct 2, 2023
09cf484
Correctly fail back blinded inbound fwd HTLCs when adding to a Channel
valentinewallace Oct 3, 2023
b767d37
Correctly fail back downstream-failed blinded HTLCs as intro
valentinewallace Oct 9, 2023
0a45870
Test intro node blinded HTLC failing in process_pending_htlc_fwds.
valentinewallace Oct 31, 2023
4d43ccd
Test intro node failing blinded intercept HTLC.
valentinewallace Oct 31, 2023
e510e3c
Add release note for blinded HTLC backwards compat.
valentinewallace Nov 7, 2023
6af786a
Test blinding point serialization in Channel.
valentinewallace Nov 13, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 1 addition & 16 deletions lightning/src/blinded_path/payment.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -118,21 +118,6 @@ impl Writeable for ReceiveTlvs {
}
}

// This will be removed once we support forwarding blinded HTLCs, because we'll always read a
// `BlindedPaymentTlvs` instead.
impl Readable for ReceiveTlvs {
fn read<R: io::Read>(r: &mut R) -> Result<Self, DecodeError> {
_init_and_read_tlv_stream!(r, {
(12, payment_constraints, required),
(65536, payment_secret, required),
});
Ok(Self {
payment_secret: payment_secret.0.unwrap(),
payment_constraints: payment_constraints.0.unwrap()
})
}
}

impl<'a> Writeable for BlindedPaymentTlvsRef<'a> {
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
// TODO: write padding
Expand DownExpand Up@@ -187,7 +172,7 @@ pub(super) fn blinded_hops<T: secp256k1::Signing + secp256k1::Verification>(
}

/// `None` if underflow occurs.
fn amt_to_forward_msat(inbound_amt_msat: u64, payment_relay: &PaymentRelay) -> Option<u64> {
pub(crate) fn amt_to_forward_msat(inbound_amt_msat: u64, payment_relay: &PaymentRelay) -> Option<u64> {
let inbound_amt = inbound_amt_msat as u128;
let base = payment_relay.fee_base_msat as u128;
let prop = payment_relay.fee_proportional_millionths as u128;
Expand Down
329 changes: 326 additions & 3 deletions lightning/src/ln/blinded_payment_tests.rs

Large diffs are not rendered by default.

169 changes: 155 additions & 14 deletions lightning/src/ln/channel.rs

Large diffs are not rendered by default.

114 changes: 102 additions & 12 deletions lightning/src/ln/channelmanager.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -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;
Expand DownExpand Up@@ -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>,
Comment thread
valentinewallace marked this conversation as resolved.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The 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.

@valentinewallacevalentinewallaceNov 30, 2023

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The 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 BlindedForward struct itself.

},
/// 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,
Expand DownExpand Up@@ -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 {
Comment thread
valentinewallace marked this conversation as 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 {
Expand DownExpand Up@@ -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 {
Expand All@@ -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.
Expand DownExpand Up@@ -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),
}));
}
Expand DownExpand Up@@ -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
};
Expand DownExpand Up@@ -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);
Expand DownExpand Up@@ -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 {
Expand DownExpand Up@@ -4135,7 +4181,7 @@ where
}
}
}
if let PendingHTLCRouting::Forward { onion_packet, .. } = routing {
if let PendingHTLCRouting::Forward { ref onion_packet, .. } = routing {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The 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?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We will always fail to decode onion_packet if it's a phantom, because we use Recipient::Node instead of ::PhantomNode when decoding the encrypted TLVs. Not much we can do here until we add full phantom support, IIUC.

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();
Expand DownExpand Up@@ -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);
Expand All@@ -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 {
Expand DownExpand Up@@ -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());
Expand DownExpand Up@@ -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
Expand DownExpand Up@@ -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 },
Expand DownExpand Up@@ -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();
Expand DownExpand Up@@ -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 {
Expand DownExpand Up@@ -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,
Expand DownExpand Up@@ -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 {
Expand DownExpand Up@@ -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) => {
Expand DownExpand Up@@ -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),
Expand Down
5 changes: 5 additions & 0 deletions lightning/src/ln/functional_tests.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -1415,6 +1415,7 @@ fn test_fee_spike_violation_fails_htlc() {
cltv_expiry: htlc_cltv,
onion_routing_packet: onion_packet,
skimmed_fee_msat: None,
blinding_point: None,
};

nodes[1].node.handle_update_add_htlc(&nodes[0].node.get_our_node_id(), &msg);
Expand DownExpand Up@@ -1611,6 +1612,7 @@ fn test_chan_reserve_violation_inbound_htlc_outbound_channel() {
cltv_expiry: htlc_cltv,
onion_routing_packet: onion_packet,
skimmed_fee_msat: None,
blinding_point: None,
};

nodes[0].node.handle_update_add_htlc(&nodes[1].node.get_our_node_id(), &msg);
Expand DownExpand Up@@ -1789,6 +1791,7 @@ fn test_chan_reserve_violation_inbound_htlc_inbound_chan() {
cltv_expiry: htlc_cltv,
onion_routing_packet: onion_packet,
skimmed_fee_msat: None,
blinding_point: None,
};

nodes[1].node.handle_update_add_htlc(&nodes[0].node.get_our_node_id(), &msg);
Expand DownExpand Up@@ -3510,6 +3513,7 @@ fn fail_backward_pending_htlc_upon_channel_failure() {
cltv_expiry,
onion_routing_packet,
skimmed_fee_msat: None,
blinding_point: None,
};
nodes[0].node.handle_update_add_htlc(&nodes[1].node.get_our_node_id(), &update_add_htlc);
}
Expand DownExpand Up@@ -6481,6 +6485,7 @@ fn test_update_add_htlc_bolt2_receiver_check_max_htlc_limit() {
cltv_expiry: htlc_cltv,
onion_routing_packet: onion_packet.clone(),
skimmed_fee_msat: None,
blinding_point: None,
};

for i in 0..50 {
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
1e12bdf
Parse blinding point in UpdateAddHTLC
valentinewallace Mar 23, 2023
7f765a3
onion_utils: extract decrypting faiure packet into method
valentinewallace Oct 26, 2023
b70525d
Parse blinded onion errors in tests only.
valentinewallace Oct 26, 2023
1596116
Persist outbound blinding points in Channel
valentinewallace Jul 13, 2023
b645237
Store whether a forwarded HTLC is blinded in PendingHTLCRouting
valentinewallace Oct 26, 2023
ae15ba8
Persist whether an HTLC is blinded in HTLCPreviousHopData.
valentinewallace Oct 26, 2023
21ae9fd
Set HTLCPreviousHopData::blinded on intro node forward.
valentinewallace Oct 26, 2023
a2b2fb0
Parameterize Channel's htlc forward method by outbound blinding point
valentinewallace Oct 26, 2023
50c850f
Set update_add blinding point on HTLC forward
valentinewallace Oct 26, 2023
1a7254c
Parse blinded forward-as-intro onion payloads
valentinewallace Oct 26, 2023
47d34c3
Support forwarding blinded HTLCs as intro node.
valentinewallace Oct 26, 2023
d2222c8
Remove now-unused Readable impl for ReceiveTlvs
valentinewallace Oct 25, 2023
918f09c
Test blinded forward failure to calculate outbound cltv expiry
valentinewallace Sep 15, 2023
c8adb54
Test blinded forwarding payload encoded as receive error case
valentinewallace Sep 15, 2023
67d2463
Correctly fail back on outbound channel check for blinded HTLC
valentinewallace Sep 15, 2023
8c0c3a3
Extract blinded route param creation into test util
valentinewallace Oct 2, 2023
09cf484
Correctly fail back blinded inbound fwd HTLCs when adding to a Channel
valentinewallace Oct 3, 2023
b767d37
Correctly fail back downstream-failed blinded HTLCs as intro
valentinewallace Oct 9, 2023
0a45870
Test intro node blinded HTLC failing in process_pending_htlc_fwds.
valentinewallace Oct 31, 2023
4d43ccd
Test intro node failing blinded intercept HTLC.
valentinewallace Oct 31, 2023
e510e3c
Add release note for blinded HTLC backwards compat.
valentinewallace Nov 7, 2023
6af786a
Test blinding point serialization in Channel.
valentinewallace Nov 13, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 1 addition & 16 deletions lightning/src/blinded_path/payment.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -118,21 +118,6 @@ impl Writeable for ReceiveTlvs {
}
}

// This will be removed once we support forwarding blinded HTLCs, because we'll always read a
// `BlindedPaymentTlvs` instead.
impl Readable for ReceiveTlvs {
fn read<R: io::Read>(r: &mut R) -> Result<Self, DecodeError> {
_init_and_read_tlv_stream!(r, {
(12, payment_constraints, required),
(65536, payment_secret, required),
});
Ok(Self {
payment_secret: payment_secret.0.unwrap(),
payment_constraints: payment_constraints.0.unwrap()
})
}
}

impl<'a> Writeable for BlindedPaymentTlvsRef<'a> {
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
// TODO: write padding
Expand DownExpand Up@@ -187,7 +172,7 @@ pub(super) fn blinded_hops<T: secp256k1::Signing + secp256k1::Verification>(
}

/// `None` if underflow occurs.
fn amt_to_forward_msat(inbound_amt_msat: u64, payment_relay: &PaymentRelay) -> Option<u64> {
pub(crate) fn amt_to_forward_msat(inbound_amt_msat: u64, payment_relay: &PaymentRelay) -> Option<u64> {
let inbound_amt = inbound_amt_msat as u128;
let base = payment_relay.fee_base_msat as u128;
let prop = payment_relay.fee_proportional_millionths as u128;
Expand Down
329 changes: 326 additions & 3 deletions lightning/src/ln/blinded_payment_tests.rs

Large diffs are not rendered by default.

169 changes: 155 additions & 14 deletions lightning/src/ln/channel.rs

Large diffs are not rendered by default.

114 changes: 102 additions & 12 deletions lightning/src/ln/channelmanager.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -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;
Expand DownExpand Up@@ -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>,
Comment thread
valentinewallace marked this conversation as resolved.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The 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.

@valentinewallacevalentinewallaceNov 30, 2023

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The 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 BlindedForward struct itself.

},
/// 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,
Expand DownExpand Up@@ -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 {
Comment thread
valentinewallace marked this conversation as 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 {
Expand DownExpand Up@@ -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 {
Expand All@@ -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.
Expand DownExpand Up@@ -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),
}));
}
Expand DownExpand Up@@ -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
};
Expand DownExpand Up@@ -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);
Expand DownExpand Up@@ -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 {
Expand DownExpand Up@@ -4135,7 +4181,7 @@ where
}
}
}
if let PendingHTLCRouting::Forward { onion_packet, .. } = routing {
if let PendingHTLCRouting::Forward { ref onion_packet, .. } = routing {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The 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?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We will always fail to decode onion_packet if it's a phantom, because we use Recipient::Node instead of ::PhantomNode when decoding the encrypted TLVs. Not much we can do here until we add full phantom support, IIUC.

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();
Expand DownExpand Up@@ -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);
Expand All@@ -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 {
Expand DownExpand Up@@ -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());
Expand DownExpand Up@@ -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
Expand DownExpand Up@@ -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 },
Expand DownExpand Up@@ -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();
Expand DownExpand Up@@ -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 {
Expand DownExpand Up@@ -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,
Expand DownExpand Up@@ -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 {
Expand DownExpand Up@@ -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) => {
Expand DownExpand Up@@ -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),
Expand Down
5 changes: 5 additions & 0 deletions lightning/src/ln/functional_tests.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -1415,6 +1415,7 @@ fn test_fee_spike_violation_fails_htlc() {
cltv_expiry: htlc_cltv,
onion_routing_packet: onion_packet,
skimmed_fee_msat: None,
blinding_point: None,
};

nodes[1].node.handle_update_add_htlc(&nodes[0].node.get_our_node_id(), &msg);
Expand DownExpand Up@@ -1611,6 +1612,7 @@ fn test_chan_reserve_violation_inbound_htlc_outbound_channel() {
cltv_expiry: htlc_cltv,
onion_routing_packet: onion_packet,
skimmed_fee_msat: None,
blinding_point: None,
};

nodes[0].node.handle_update_add_htlc(&nodes[1].node.get_our_node_id(), &msg);
Expand DownExpand Up@@ -1789,6 +1791,7 @@ fn test_chan_reserve_violation_inbound_htlc_inbound_chan() {
cltv_expiry: htlc_cltv,
onion_routing_packet: onion_packet,
skimmed_fee_msat: None,
blinding_point: None,
};

nodes[1].node.handle_update_add_htlc(&nodes[0].node.get_our_node_id(), &msg);
Expand DownExpand Up@@ -3510,6 +3513,7 @@ fn fail_backward_pending_htlc_upon_channel_failure() {
cltv_expiry,
onion_routing_packet,
skimmed_fee_msat: None,
blinding_point: None,
};
nodes[0].node.handle_update_add_htlc(&nodes[1].node.get_our_node_id(), &update_add_htlc);
}
Expand DownExpand Up@@ -6481,6 +6485,7 @@ fn test_update_add_htlc_bolt2_receiver_check_max_htlc_limit() {
cltv_expiry: htlc_cltv,
onion_routing_packet: onion_packet.clone(),
skimmed_fee_msat: None,
blinding_point: None,
};

for i in 0..50 {
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
1e12bdf
Parse blinding point in UpdateAddHTLC
valentinewallace Mar 23, 2023
7f765a3
onion_utils: extract decrypting faiure packet into method
valentinewallace Oct 26, 2023
b70525d
Parse blinded onion errors in tests only.
valentinewallace Oct 26, 2023
1596116
Persist outbound blinding points in Channel
valentinewallace Jul 13, 2023
b645237
Store whether a forwarded HTLC is blinded in PendingHTLCRouting
valentinewallace Oct 26, 2023
ae15ba8
Persist whether an HTLC is blinded in HTLCPreviousHopData.
valentinewallace Oct 26, 2023
21ae9fd
Set HTLCPreviousHopData::blinded on intro node forward.
valentinewallace Oct 26, 2023
a2b2fb0
Parameterize Channel's htlc forward method by outbound blinding point
valentinewallace Oct 26, 2023
50c850f
Set update_add blinding point on HTLC forward
valentinewallace Oct 26, 2023
1a7254c
Parse blinded forward-as-intro onion payloads
valentinewallace Oct 26, 2023
47d34c3
Support forwarding blinded HTLCs as intro node.
valentinewallace Oct 26, 2023
d2222c8
Remove now-unused Readable impl for ReceiveTlvs
valentinewallace Oct 25, 2023
918f09c
Test blinded forward failure to calculate outbound cltv expiry
valentinewallace Sep 15, 2023
c8adb54
Test blinded forwarding payload encoded as receive error case
valentinewallace Sep 15, 2023
67d2463
Correctly fail back on outbound channel check for blinded HTLC
valentinewallace Sep 15, 2023
8c0c3a3
Extract blinded route param creation into test util
valentinewallace Oct 2, 2023
09cf484
Correctly fail back blinded inbound fwd HTLCs when adding to a Channel
valentinewallace Oct 3, 2023
b767d37
Correctly fail back downstream-failed blinded HTLCs as intro
valentinewallace Oct 9, 2023
0a45870
Test intro node blinded HTLC failing in process_pending_htlc_fwds.
valentinewallace Oct 31, 2023
4d43ccd
Test intro node failing blinded intercept HTLC.
valentinewallace Oct 31, 2023
e510e3c
Add release note for blinded HTLC backwards compat.
valentinewallace Nov 7, 2023
6af786a
Test blinding point serialization in Channel.
valentinewallace Nov 13, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 1 addition & 16 deletions lightning/src/blinded_path/payment.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -118,21 +118,6 @@ impl Writeable for ReceiveTlvs {
}
}

// This will be removed once we support forwarding blinded HTLCs, because we'll always read a
// `BlindedPaymentTlvs` instead.
impl Readable for ReceiveTlvs {
fn read<R: io::Read>(r: &mut R) -> Result<Self, DecodeError> {
_init_and_read_tlv_stream!(r, {
(12, payment_constraints, required),
(65536, payment_secret, required),
});
Ok(Self {
payment_secret: payment_secret.0.unwrap(),
payment_constraints: payment_constraints.0.unwrap()
})
}
}

impl<'a> Writeable for BlindedPaymentTlvsRef<'a> {
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
// TODO: write padding
Expand DownExpand Up@@ -187,7 +172,7 @@ pub(super) fn blinded_hops<T: secp256k1::Signing + secp256k1::Verification>(
}

/// `None` if underflow occurs.
fn amt_to_forward_msat(inbound_amt_msat: u64, payment_relay: &PaymentRelay) -> Option<u64> {
pub(crate) fn amt_to_forward_msat(inbound_amt_msat: u64, payment_relay: &PaymentRelay) -> Option<u64> {
let inbound_amt = inbound_amt_msat as u128;
let base = payment_relay.fee_base_msat as u128;
let prop = payment_relay.fee_proportional_millionths as u128;
Expand Down
329 changes: 326 additions & 3 deletions lightning/src/ln/blinded_payment_tests.rs

Large diffs are not rendered by default.

169 changes: 155 additions & 14 deletions lightning/src/ln/channel.rs

Large diffs are not rendered by default.

114 changes: 102 additions & 12 deletions lightning/src/ln/channelmanager.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -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;
Expand DownExpand Up@@ -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>,
Comment thread
valentinewallace marked this conversation as resolved.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The 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.

@valentinewallacevalentinewallaceNov 30, 2023

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The 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 BlindedForward struct itself.

},
/// 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,
Expand DownExpand Up@@ -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 {
Comment thread
valentinewallace marked this conversation as 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 {
Expand DownExpand Up@@ -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 {
Expand All@@ -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.
Expand DownExpand Up@@ -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),
}));
}
Expand DownExpand Up@@ -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
};
Expand DownExpand Up@@ -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);
Expand DownExpand Up@@ -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 {
Expand DownExpand Up@@ -4135,7 +4181,7 @@ where
}
}
}
if let PendingHTLCRouting::Forward { onion_packet, .. } = routing {
if let PendingHTLCRouting::Forward { ref onion_packet, .. } = routing {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The 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?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We will always fail to decode onion_packet if it's a phantom, because we use Recipient::Node instead of ::PhantomNode when decoding the encrypted TLVs. Not much we can do here until we add full phantom support, IIUC.

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();
Expand DownExpand Up@@ -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);
Expand All@@ -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 {
Expand DownExpand Up@@ -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());
Expand DownExpand Up@@ -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
Expand DownExpand Up@@ -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 },
Expand DownExpand Up@@ -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();
Expand DownExpand Up@@ -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 {
Expand DownExpand Up@@ -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,
Expand DownExpand Up@@ -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 {
Expand DownExpand Up@@ -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) => {
Expand DownExpand Up@@ -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),
Expand Down
5 changes: 5 additions & 0 deletions lightning/src/ln/functional_tests.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -1415,6 +1415,7 @@ fn test_fee_spike_violation_fails_htlc() {
cltv_expiry: htlc_cltv,
onion_routing_packet: onion_packet,
skimmed_fee_msat: None,
blinding_point: None,
};

nodes[1].node.handle_update_add_htlc(&nodes[0].node.get_our_node_id(), &msg);
Expand DownExpand Up@@ -1611,6 +1612,7 @@ fn test_chan_reserve_violation_inbound_htlc_outbound_channel() {
cltv_expiry: htlc_cltv,
onion_routing_packet: onion_packet,
skimmed_fee_msat: None,
blinding_point: None,
};

nodes[0].node.handle_update_add_htlc(&nodes[1].node.get_our_node_id(), &msg);
Expand DownExpand Up@@ -1789,6 +1791,7 @@ fn test_chan_reserve_violation_inbound_htlc_inbound_chan() {
cltv_expiry: htlc_cltv,
onion_routing_packet: onion_packet,
skimmed_fee_msat: None,
blinding_point: None,
};

nodes[1].node.handle_update_add_htlc(&nodes[0].node.get_our_node_id(), &msg);
Expand DownExpand Up@@ -3510,6 +3513,7 @@ fn fail_backward_pending_htlc_upon_channel_failure() {
cltv_expiry,
onion_routing_packet,
skimmed_fee_msat: None,
blinding_point: None,
};
nodes[0].node.handle_update_add_htlc(&nodes[1].node.get_our_node_id(), &update_add_htlc);
}
Expand DownExpand Up@@ -6481,6 +6485,7 @@ fn test_update_add_htlc_bolt2_receiver_check_max_htlc_limit() {
cltv_expiry: htlc_cltv,
onion_routing_packet: onion_packet.clone(),
skimmed_fee_msat: None,
blinding_point: None,
};

for i in 0..50 {
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
1e12bdf
Parse blinding point in UpdateAddHTLC
valentinewallace Mar 23, 2023
7f765a3
onion_utils: extract decrypting faiure packet into method
valentinewallace Oct 26, 2023
b70525d
Parse blinded onion errors in tests only.
valentinewallace Oct 26, 2023
1596116
Persist outbound blinding points in Channel
valentinewallace Jul 13, 2023
b645237
Store whether a forwarded HTLC is blinded in PendingHTLCRouting
valentinewallace Oct 26, 2023
ae15ba8
Persist whether an HTLC is blinded in HTLCPreviousHopData.
valentinewallace Oct 26, 2023
21ae9fd
Set HTLCPreviousHopData::blinded on intro node forward.
valentinewallace Oct 26, 2023
a2b2fb0
Parameterize Channel's htlc forward method by outbound blinding point
valentinewallace Oct 26, 2023
50c850f
Set update_add blinding point on HTLC forward
valentinewallace Oct 26, 2023
1a7254c
Parse blinded forward-as-intro onion payloads
valentinewallace Oct 26, 2023
47d34c3
Support forwarding blinded HTLCs as intro node.
valentinewallace Oct 26, 2023
d2222c8
Remove now-unused Readable impl for ReceiveTlvs
valentinewallace Oct 25, 2023
918f09c
Test blinded forward failure to calculate outbound cltv expiry
valentinewallace Sep 15, 2023
c8adb54
Test blinded forwarding payload encoded as receive error case
valentinewallace Sep 15, 2023
67d2463
Correctly fail back on outbound channel check for blinded HTLC
valentinewallace Sep 15, 2023
8c0c3a3
Extract blinded route param creation into test util
valentinewallace Oct 2, 2023
09cf484
Correctly fail back blinded inbound fwd HTLCs when adding to a Channel
valentinewallace Oct 3, 2023
b767d37
Correctly fail back downstream-failed blinded HTLCs as intro
valentinewallace Oct 9, 2023
0a45870
Test intro node blinded HTLC failing in process_pending_htlc_fwds.
valentinewallace Oct 31, 2023
4d43ccd
Test intro node failing blinded intercept HTLC.
valentinewallace Oct 31, 2023
e510e3c
Add release note for blinded HTLC backwards compat.
valentinewallace Nov 7, 2023
6af786a
Test blinding point serialization in Channel.
valentinewallace Nov 13, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 1 addition & 16 deletions lightning/src/blinded_path/payment.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -118,21 +118,6 @@ impl Writeable for ReceiveTlvs {
}
}

// This will be removed once we support forwarding blinded HTLCs, because we'll always read a
// `BlindedPaymentTlvs` instead.
impl Readable for ReceiveTlvs {
fn read<R: io::Read>(r: &mut R) -> Result<Self, DecodeError> {
_init_and_read_tlv_stream!(r, {
(12, payment_constraints, required),
(65536, payment_secret, required),
});
Ok(Self {
payment_secret: payment_secret.0.unwrap(),
payment_constraints: payment_constraints.0.unwrap()
})
}
}

impl<'a> Writeable for BlindedPaymentTlvsRef<'a> {
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
// TODO: write padding
Expand DownExpand Up@@ -187,7 +172,7 @@ pub(super) fn blinded_hops<T: secp256k1::Signing + secp256k1::Verification>(
}

/// `None` if underflow occurs.
fn amt_to_forward_msat(inbound_amt_msat: u64, payment_relay: &PaymentRelay) -> Option<u64> {
pub(crate) fn amt_to_forward_msat(inbound_amt_msat: u64, payment_relay: &PaymentRelay) -> Option<u64> {
let inbound_amt = inbound_amt_msat as u128;
let base = payment_relay.fee_base_msat as u128;
let prop = payment_relay.fee_proportional_millionths as u128;
Expand Down
329 changes: 326 additions & 3 deletions lightning/src/ln/blinded_payment_tests.rs

Large diffs are not rendered by default.

169 changes: 155 additions & 14 deletions lightning/src/ln/channel.rs

Large diffs are not rendered by default.

114 changes: 102 additions & 12 deletions lightning/src/ln/channelmanager.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -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;
Expand DownExpand Up@@ -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>,
Comment thread
valentinewallace marked this conversation as resolved.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The 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.

@valentinewallacevalentinewallaceNov 30, 2023

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The 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 BlindedForward struct itself.

},
/// 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,
Expand DownExpand Up@@ -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 {
Comment thread
valentinewallace marked this conversation as 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 {
Expand DownExpand Up@@ -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 {
Expand All@@ -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.
Expand DownExpand Up@@ -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),
}));
}
Expand DownExpand Up@@ -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
};
Expand DownExpand Up@@ -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);
Expand DownExpand Up@@ -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 {
Expand DownExpand Up@@ -4135,7 +4181,7 @@ where
}
}
}
if let PendingHTLCRouting::Forward { onion_packet, .. } = routing {
if let PendingHTLCRouting::Forward { ref onion_packet, .. } = routing {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The 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?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We will always fail to decode onion_packet if it's a phantom, because we use Recipient::Node instead of ::PhantomNode when decoding the encrypted TLVs. Not much we can do here until we add full phantom support, IIUC.

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();
Expand DownExpand Up@@ -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);
Expand All@@ -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 {
Expand DownExpand Up@@ -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());
Expand DownExpand Up@@ -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
Expand DownExpand Up@@ -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 },
Expand DownExpand Up@@ -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();
Expand DownExpand Up@@ -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 {
Expand DownExpand Up@@ -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,
Expand DownExpand Up@@ -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 {
Expand DownExpand Up@@ -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) => {
Expand DownExpand Up@@ -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),
Expand Down
5 changes: 5 additions & 0 deletions lightning/src/ln/functional_tests.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -1415,6 +1415,7 @@ fn test_fee_spike_violation_fails_htlc() {
cltv_expiry: htlc_cltv,
onion_routing_packet: onion_packet,
skimmed_fee_msat: None,
blinding_point: None,
};

nodes[1].node.handle_update_add_htlc(&nodes[0].node.get_our_node_id(), &msg);
Expand DownExpand Up@@ -1611,6 +1612,7 @@ fn test_chan_reserve_violation_inbound_htlc_outbound_channel() {
cltv_expiry: htlc_cltv,
onion_routing_packet: onion_packet,
skimmed_fee_msat: None,
blinding_point: None,
};

nodes[0].node.handle_update_add_htlc(&nodes[1].node.get_our_node_id(), &msg);
Expand DownExpand Up@@ -1789,6 +1791,7 @@ fn test_chan_reserve_violation_inbound_htlc_inbound_chan() {
cltv_expiry: htlc_cltv,
onion_routing_packet: onion_packet,
skimmed_fee_msat: None,
blinding_point: None,
};

nodes[1].node.handle_update_add_htlc(&nodes[0].node.get_our_node_id(), &msg);
Expand DownExpand Up@@ -3510,6 +3513,7 @@ fn fail_backward_pending_htlc_upon_channel_failure() {
cltv_expiry,
onion_routing_packet,
skimmed_fee_msat: None,
blinding_point: None,
};
nodes[0].node.handle_update_add_htlc(&nodes[1].node.get_our_node_id(), &update_add_htlc);
}
Expand DownExpand Up@@ -6481,6 +6485,7 @@ fn test_update_add_htlc_bolt2_receiver_check_max_htlc_limit() {
cltv_expiry: htlc_cltv,
onion_routing_packet: onion_packet.clone(),
skimmed_fee_msat: None,
blinding_point: None,
};

for i in 0..50 {
Expand Down
Loading