Skip to content

Commit 5ed672f

Browse files
committed
Refactor outgoing HTLC checks out from decode_update_add_htlc_onion
In the future, we plan to complete remove `decode_update_add_htlc_onion` and replace it with a batched variant. This refactor, while improving readability in its current form, does not feature any functional changes and allows us to reuse most of the logic in the batched variant.
1 parent 42db9a3 commit 5ed672f

File tree

1 file changed

+51
-43
lines changed

1 file changed

+51
-43
lines changed

lightning/src/ln/channelmanager.rs

Lines changed: 51 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -3094,6 +3094,48 @@ where
30943094
}
30953095
}
30963096

3097+
fn can_forward_htlc_to_outgoing_channel(
3098+
&self, chan: &mut Channel<SP>, msg: &msgs::UpdateAddHTLC, next_packet: &NextPacketDetails
3099+
) -> Result<Option<msgs::ChannelUpdate>, (&'static str, u16, Option<msgs::ChannelUpdate>)> {
3100+
if !chan.context.should_announce() && !self.default_configuration.accept_forwards_to_priv_channels {
3101+
// Note that the behavior here should be identical to the above block - we
3102+
// should NOT reveal the existence or non-existence of a private channel if
3103+
// we don't allow forwards outbound over them.
3104+
return Err(("Refusing to forward to a private channel based on our config.", 0x4000 | 10, None));
3105+
}
3106+
if chan.context.get_channel_type().supports_scid_privacy() && next_packet.outgoing_scid != chan.context.outbound_scid_alias() {
3107+
// `option_scid_alias` (referred to in LDK as `scid_privacy`) means
3108+
// "refuse to forward unless the SCID alias was used", so we pretend
3109+
// we don't have the channel here.
3110+
return Err(("Refusing to forward over real channel SCID as our counterparty requested.", 0x4000 | 10, None));
3111+
}
3112+
let chan_update_opt = self.get_channel_update_for_onion(next_packet.outgoing_scid, chan).ok();
3113+
3114+
// Note that we could technically not return an error yet here and just hope
3115+
// that the connection is reestablished or monitor updated by the time we get
3116+
// around to doing the actual forward, but better to fail early if we can and
3117+
// hopefully an attacker trying to path-trace payments cannot make this occur
3118+
// on a small/per-node/per-channel scale.
3119+
if !chan.context.is_live() { // channel_disabled
3120+
// If the channel_update we're going to return is disabled (i.e. the
3121+
// peer has been disabled for some time), return `channel_disabled`,
3122+
// otherwise return `temporary_channel_failure`.
3123+
if chan_update_opt.as_ref().map(|u| u.contents.flags & 2 == 2).unwrap_or(false) {
3124+
return Err(("Forwarding channel has been disconnected for some time.", 0x1000 | 20, chan_update_opt));
3125+
} else {
3126+
return Err(("Forwarding channel is not in a ready state.", 0x1000 | 7, chan_update_opt));
3127+
}
3128+
}
3129+
if next_packet.outgoing_amt_msat < chan.context.get_counterparty_htlc_minimum_msat() { // amount_below_minimum
3130+
return Err(("HTLC amount was below the htlc_minimum_msat", 0x1000 | 11, chan_update_opt));
3131+
}
3132+
if let Err((err, code)) = chan.htlc_satisfies_config(msg, next_packet.outgoing_amt_msat, next_packet.outgoing_cltv_value) {
3133+
return Err((err, code, chan_update_opt));
3134+
}
3135+
3136+
Ok(chan_update_opt)
3137+
}
3138+
30973139
fn htlc_failure_from_update_add_err(
30983140
&self, msg: &msgs::UpdateAddHTLC, counterparty_node_id: &PublicKey, err_msg: &'static str,
30993141
mut err_code: u16, chan_update: Option<msgs::ChannelUpdate>, is_intro_node_blinded_forward: bool,
@@ -3158,9 +3200,7 @@ where
31583200
msg, &self.node_signer, &self.logger, &self.secp_ctx
31593201
)?;
31603202

3161-
let NextPacketDetails {
3162-
next_packet_pubkey, outgoing_amt_msat, outgoing_scid, outgoing_cltv_value
3163-
} = match next_packet_details_opt {
3203+
let next_packet_details = match next_packet_details_opt {
31643204
Some(next_packet_details) => next_packet_details,
31653205
// it is a receive, so no need for outbound checks
31663206
None => return Ok((next_hop, shared_secret, None)),
@@ -3169,14 +3209,14 @@ where
31693209
// Perform outbound checks here instead of in [`Self::construct_pending_htlc_info`] because we
31703210
// can't hold the outbound peer state lock at the same time as the inbound peer state lock.
31713211
if let Some((err, code, chan_update)) = loop {
3172-
let id_option = self.short_to_chan_info.read().unwrap().get(&outgoing_scid).cloned();
3212+
let id_option = self.short_to_chan_info.read().unwrap().get(&next_packet_details.outgoing_scid).cloned();
31733213
let forwarding_chan_info_opt = match id_option {
31743214
None => { // unknown_next_peer
31753215
// Note that this is likely a timing oracle for detecting whether an scid is a
31763216
// phantom or an intercept.
31773217
if (self.default_configuration.accept_intercept_htlcs &&
3178-
fake_scid::is_valid_intercept(&self.fake_scid_rand_bytes, outgoing_scid, &self.chain_hash)) ||
3179-
fake_scid::is_valid_phantom(&self.fake_scid_rand_bytes, outgoing_scid, &self.chain_hash)
3218+
fake_scid::is_valid_intercept(&self.fake_scid_rand_bytes, next_packet_details.outgoing_scid, &self.chain_hash)) ||
3219+
fake_scid::is_valid_phantom(&self.fake_scid_rand_bytes, next_packet_details.outgoing_scid, &self.chain_hash)
31803220
{
31813221
None
31823222
} else {
@@ -3203,50 +3243,18 @@ where
32033243
},
32043244
Some(chan) => chan
32053245
};
3206-
if !chan.context.should_announce() && !self.default_configuration.accept_forwards_to_priv_channels {
3207-
// Note that the behavior here should be identical to the above block - we
3208-
// should NOT reveal the existence or non-existence of a private channel if
3209-
// we don't allow forwards outbound over them.
3210-
break Some(("Refusing to forward to a private channel based on our config.", 0x4000 | 10, None));
3211-
}
3212-
if chan.context.get_channel_type().supports_scid_privacy() && outgoing_scid != chan.context.outbound_scid_alias() {
3213-
// `option_scid_alias` (referred to in LDK as `scid_privacy`) means
3214-
// "refuse to forward unless the SCID alias was used", so we pretend
3215-
// we don't have the channel here.
3216-
break Some(("Refusing to forward over real channel SCID as our counterparty requested.", 0x4000 | 10, None));
3217-
}
3218-
let chan_update_opt = self.get_channel_update_for_onion(outgoing_scid, chan).ok();
3219-
3220-
// Note that we could technically not return an error yet here and just hope
3221-
// that the connection is reestablished or monitor updated by the time we get
3222-
// around to doing the actual forward, but better to fail early if we can and
3223-
// hopefully an attacker trying to path-trace payments cannot make this occur
3224-
// on a small/per-node/per-channel scale.
3225-
if !chan.context.is_live() { // channel_disabled
3226-
// If the channel_update we're going to return is disabled (i.e. the
3227-
// peer has been disabled for some time), return `channel_disabled`,
3228-
// otherwise return `temporary_channel_failure`.
3229-
if chan_update_opt.as_ref().map(|u| u.contents.flags & 2 == 2).unwrap_or(false) {
3230-
break Some(("Forwarding channel has been disconnected for some time.", 0x1000 | 20, chan_update_opt));
3231-
} else {
3232-
break Some(("Forwarding channel is not in a ready state.", 0x1000 | 7, chan_update_opt));
3233-
}
3234-
}
3235-
if outgoing_amt_msat < chan.context.get_counterparty_htlc_minimum_msat() { // amount_below_minimum
3236-
break Some(("HTLC amount was below the htlc_minimum_msat", 0x1000 | 11, chan_update_opt));
3237-
}
3238-
if let Err((err, code)) = chan.htlc_satisfies_config(&msg, outgoing_amt_msat, outgoing_cltv_value) {
3239-
break Some((err, code, chan_update_opt));
3246+
match self.can_forward_htlc_to_outgoing_channel(chan, msg, &next_packet_details) {
3247+
Ok(chan_update_opt) => chan_update_opt,
3248+
Err(e) => break Some(e),
32403249
}
3241-
chan_update_opt
32423250
} else {
32433251
None
32443252
};
32453253

32463254
let cur_height = self.best_block.read().unwrap().height + 1;
32473255

32483256
if let Err((err_msg, code)) = check_incoming_htlc_cltv(
3249-
cur_height, outgoing_cltv_value, msg.cltv_expiry
3257+
cur_height, next_packet_details.outgoing_cltv_value, msg.cltv_expiry
32503258
) {
32513259
if code & 0x1000 != 0 && chan_update_opt.is_none() {
32523260
// We really should set `incorrect_cltv_expiry` here but as we're not
@@ -3265,7 +3273,7 @@ where
32653273
msg, counterparty_node_id, err, code, chan_update, next_hop.is_intro_node_blinded_forward(), &shared_secret
32663274
));
32673275
}
3268-
Ok((next_hop, shared_secret, Some(next_packet_pubkey)))
3276+
Ok((next_hop, shared_secret, Some(next_packet_details.next_packet_pubkey)))
32693277
}
32703278

32713279
fn construct_pending_htlc_status<'a>(

0 commit comments

Comments
 (0)