Skip to content

Commit e5479ce

Browse files
committed
Refactor outgoing channel lookup 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 5ed672f commit e5479ce

File tree

1 file changed

+76
-67
lines changed

1 file changed

+76
-67
lines changed

lightning/src/ln/channelmanager.rs

Lines changed: 76 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -3096,7 +3096,7 @@ where
30963096

30973097
fn can_forward_htlc_to_outgoing_channel(
30983098
&self, chan: &mut Channel<SP>, msg: &msgs::UpdateAddHTLC, next_packet: &NextPacketDetails
3099-
) -> Result<Option<msgs::ChannelUpdate>, (&'static str, u16, Option<msgs::ChannelUpdate>)> {
3099+
) -> Result<(), (&'static str, u16, Option<msgs::ChannelUpdate>)> {
31003100
if !chan.context.should_announce() && !self.default_configuration.accept_forwards_to_priv_channels {
31013101
// Note that the behavior here should be identical to the above block - we
31023102
// should NOT reveal the existence or non-existence of a private channel if
@@ -3109,7 +3109,6 @@ where
31093109
// we don't have the channel here.
31103110
return Err(("Refusing to forward over real channel SCID as our counterparty requested.", 0x4000 | 10, None));
31113111
}
3112-
let chan_update_opt = self.get_channel_update_for_onion(next_packet.outgoing_scid, chan).ok();
31133112

31143113
// Note that we could technically not return an error yet here and just hope
31153114
// that the connection is reestablished or monitor updated by the time we get
@@ -3120,20 +3119,87 @@ where
31203119
// If the channel_update we're going to return is disabled (i.e. the
31213120
// peer has been disabled for some time), return `channel_disabled`,
31223121
// otherwise return `temporary_channel_failure`.
3122+
let chan_update_opt = self.get_channel_update_for_onion(next_packet.outgoing_scid, chan).ok();
31233123
if chan_update_opt.as_ref().map(|u| u.contents.flags & 2 == 2).unwrap_or(false) {
31243124
return Err(("Forwarding channel has been disconnected for some time.", 0x1000 | 20, chan_update_opt));
31253125
} else {
31263126
return Err(("Forwarding channel is not in a ready state.", 0x1000 | 7, chan_update_opt));
31273127
}
31283128
}
31293129
if next_packet.outgoing_amt_msat < chan.context.get_counterparty_htlc_minimum_msat() { // amount_below_minimum
3130+
let chan_update_opt = self.get_channel_update_for_onion(next_packet.outgoing_scid, chan).ok();
31303131
return Err(("HTLC amount was below the htlc_minimum_msat", 0x1000 | 11, chan_update_opt));
31313132
}
31323133
if let Err((err, code)) = chan.htlc_satisfies_config(msg, next_packet.outgoing_amt_msat, next_packet.outgoing_cltv_value) {
3134+
let chan_update_opt = self.get_channel_update_for_onion(next_packet.outgoing_scid, chan).ok();
31333135
return Err((err, code, chan_update_opt));
31343136
}
31353137

3136-
Ok(chan_update_opt)
3138+
Ok(())
3139+
}
3140+
3141+
/// Executes a callback `C` that returns some value `X` on the channel found with the given
3142+
/// `scid`. `None` is returned when the channel is not found.
3143+
fn do_channel_callback<X, C: Fn(&mut Channel<SP>) -> X>(
3144+
&self, scid: u64, callback: C,
3145+
) -> Option<X> {
3146+
let (counterparty_node_id, channel_id) = match self.short_to_chan_info.read().unwrap().get(&scid).cloned() {
3147+
None => return None,
3148+
Some((cp_id, id)) => (cp_id, id),
3149+
};
3150+
let per_peer_state = self.per_peer_state.read().unwrap();
3151+
let peer_state_mutex_opt = per_peer_state.get(&counterparty_node_id);
3152+
if peer_state_mutex_opt.is_none() {
3153+
return None;
3154+
}
3155+
let mut peer_state_lock = peer_state_mutex_opt.unwrap().lock().unwrap();
3156+
let peer_state = &mut *peer_state_lock;
3157+
match peer_state.channel_by_id.get_mut(&channel_id).and_then(
3158+
|chan_phase| if let ChannelPhase::Funded(chan) = chan_phase { Some(chan) } else { None }
3159+
) {
3160+
None => None,
3161+
Some(chan) => Some(callback(chan)),
3162+
}
3163+
}
3164+
3165+
fn can_forward_htlc(
3166+
&self, msg: &msgs::UpdateAddHTLC, next_packet_details: &NextPacketDetails
3167+
) -> Result<(), (&'static str, u16, Option<msgs::ChannelUpdate>)> {
3168+
match self.do_channel_callback(next_packet_details.outgoing_scid, |chan: &mut Channel<SP>| {
3169+
self.can_forward_htlc_to_outgoing_channel(chan, msg, next_packet_details)
3170+
}) {
3171+
Some(Ok(())) => {},
3172+
Some(Err(e)) => return Err(e),
3173+
None => {
3174+
// If we couldn't find the channel info for the scid, it may be a phantom or
3175+
// intercept forward.
3176+
if (self.default_configuration.accept_intercept_htlcs &&
3177+
fake_scid::is_valid_intercept(&self.fake_scid_rand_bytes, next_packet_details.outgoing_scid, &self.chain_hash)) ||
3178+
fake_scid::is_valid_phantom(&self.fake_scid_rand_bytes, next_packet_details.outgoing_scid, &self.chain_hash)
3179+
{} else {
3180+
return Err(("Don't have available channel for forwarding as requested.", 0x4000 | 10, None));
3181+
}
3182+
}
3183+
}
3184+
3185+
let cur_height = self.best_block.read().unwrap().height + 1;
3186+
if let Err((err_msg, err_code)) = check_incoming_htlc_cltv(
3187+
cur_height, next_packet_details.outgoing_cltv_value, msg.cltv_expiry
3188+
) {
3189+
let chan_update_opt = self.do_channel_callback(next_packet_details.outgoing_scid, |chan: &mut Channel<SP>| {
3190+
self.get_channel_update_for_onion(next_packet_details.outgoing_scid, chan).ok()
3191+
}).flatten();
3192+
if err_code & 0x1000 != 0 && chan_update_opt.is_none() {
3193+
// We really should set `incorrect_cltv_expiry` here but as we're not
3194+
// forwarding over a real channel we can't generate a channel_update
3195+
// for it. Instead we just return a generic temporary_node_failure.
3196+
return Err((err_msg, 0x2000 | 2, None));
3197+
}
3198+
let chan_update_opt = if err_code & 0x1000 != 0 { chan_update_opt } else { None };
3199+
return Err((err_msg, err_code, chan_update_opt));
3200+
}
3201+
3202+
Ok(())
31373203
}
31383204

31393205
fn htlc_failure_from_update_add_err(
@@ -3208,71 +3274,14 @@ where
32083274

32093275
// Perform outbound checks here instead of in [`Self::construct_pending_htlc_info`] because we
32103276
// can't hold the outbound peer state lock at the same time as the inbound peer state lock.
3211-
if let Some((err, code, chan_update)) = loop {
3212-
let id_option = self.short_to_chan_info.read().unwrap().get(&next_packet_details.outgoing_scid).cloned();
3213-
let forwarding_chan_info_opt = match id_option {
3214-
None => { // unknown_next_peer
3215-
// Note that this is likely a timing oracle for detecting whether an scid is a
3216-
// phantom or an intercept.
3217-
if (self.default_configuration.accept_intercept_htlcs &&
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)
3220-
{
3221-
None
3222-
} else {
3223-
break Some(("Don't have available channel for forwarding as requested.", 0x4000 | 10, None));
3224-
}
3225-
},
3226-
Some((cp_id, id)) => Some((cp_id.clone(), id.clone())),
3227-
};
3228-
let chan_update_opt = if let Some((counterparty_node_id, forwarding_id)) = forwarding_chan_info_opt {
3229-
let per_peer_state = self.per_peer_state.read().unwrap();
3230-
let peer_state_mutex_opt = per_peer_state.get(&counterparty_node_id);
3231-
if peer_state_mutex_opt.is_none() {
3232-
break Some(("Don't have available channel for forwarding as requested.", 0x4000 | 10, None));
3233-
}
3234-
let mut peer_state_lock = peer_state_mutex_opt.unwrap().lock().unwrap();
3235-
let peer_state = &mut *peer_state_lock;
3236-
let chan = match peer_state.channel_by_id.get_mut(&forwarding_id).map(
3237-
|chan_phase| if let ChannelPhase::Funded(chan) = chan_phase { Some(chan) } else { None }
3238-
).flatten() {
3239-
None => {
3240-
// Channel was removed. The short_to_chan_info and channel_by_id maps
3241-
// have no consistency guarantees.
3242-
break Some(("Don't have available channel for forwarding as requested.", 0x4000 | 10, None));
3243-
},
3244-
Some(chan) => chan
3245-
};
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),
3249-
}
3250-
} else {
3251-
None
3252-
};
3253-
3254-
let cur_height = self.best_block.read().unwrap().height + 1;
3255-
3256-
if let Err((err_msg, code)) = check_incoming_htlc_cltv(
3257-
cur_height, next_packet_details.outgoing_cltv_value, msg.cltv_expiry
3258-
) {
3259-
if code & 0x1000 != 0 && chan_update_opt.is_none() {
3260-
// We really should set `incorrect_cltv_expiry` here but as we're not
3261-
// forwarding over a real channel we can't generate a channel_update
3262-
// for it. Instead we just return a generic temporary_node_failure.
3263-
break Some((err_msg, 0x2000 | 2, None))
3264-
}
3265-
let chan_update_opt = if code & 0x1000 != 0 { chan_update_opt } else { None };
3266-
break Some((err_msg, code, chan_update_opt));
3267-
}
3277+
let _ = self.can_forward_htlc(&msg, &next_packet_details).map_err(|e| {
3278+
let (err_msg, err_code, chan_update_opt) = e;
3279+
self.htlc_failure_from_update_add_err(
3280+
msg, counterparty_node_id, err_msg, err_code, chan_update_opt,
3281+
next_hop.is_intro_node_blinded_forward(), &shared_secret
3282+
)
3283+
})?;
32683284

3269-
break None;
3270-
}
3271-
{
3272-
return Err(self.htlc_failure_from_update_add_err(
3273-
msg, counterparty_node_id, err, code, chan_update, next_hop.is_intro_node_blinded_forward(), &shared_secret
3274-
));
3275-
}
32763285
Ok((next_hop, shared_secret, Some(next_packet_details.next_packet_pubkey)))
32773286
}
32783287

0 commit comments

Comments
 (0)