Skip to content

Commit e9cc2b1

Browse files
Don't send channel updates for private chans on error
This commit also adds additional checks for the second-to-last (phantom) hop for phantom payments.
1 parent 99a67cf commit e9cc2b1

File tree

1 file changed

+39
-33
lines changed

1 file changed

+39
-33
lines changed

lightning/src/ln/channelmanager.rs

Lines changed: 39 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -2293,53 +2293,59 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
22932293
if let &PendingHTLCRouting::Forward { ref short_channel_id, .. } = routing {
22942294
let id_option = channel_state.as_ref().unwrap().short_to_id.get(&short_channel_id).cloned();
22952295
if let Some((err, code, chan_update)) = loop {
2296-
let forwarding_id = match id_option {
2296+
let forwarding_id_opt = match id_option {
22972297
None => { // unknown_next_peer
22982298
// Note that this is likely a timing oracle for detecting whether an scid is a
22992299
// phantom.
23002300
if fake_scid::is_valid_phantom(&self.fake_scid_rand_bytes, *short_channel_id) {
2301-
break None
2301+
None
2302+
} else {
2303+
break Some(("Don't have available channel for forwarding as requested.", 0x4000 | 10, None));
23022304
}
2303-
break Some(("Don't have available channel for forwarding as requested.", 0x4000 | 10, None));
23042305
},
2305-
Some(id) => id.clone(),
2306+
Some(id) => Some(id.clone()),
23062307
};
2308+
let (chan_update_opt, forwardee_cltv_expiry_delta) = if let Some(forwarding_id) = forwarding_id_opt {
2309+
let chan = channel_state.as_mut().unwrap().by_id.get_mut(&forwarding_id).unwrap();
2310+
// Leave channel updates as None for private channels.
2311+
let chan_update_opt = if chan.should_announce() {
2312+
Some(self.get_channel_update_for_unicast(chan).unwrap()) } else { None };
2313+
if !chan.should_announce() && !self.default_configuration.accept_forwards_to_priv_channels {
2314+
// Note that the behavior here should be identical to the above block - we
2315+
// should NOT reveal the existence or non-existence of a private channel if
2316+
// we don't allow forwards outbound over them.
2317+
break Some(("Don't have available channel for forwarding as requested.", 0x4000 | 10, None));
2318+
}
23072319

2308-
let chan = channel_state.as_mut().unwrap().by_id.get_mut(&forwarding_id).unwrap();
2309-
2310-
if !chan.should_announce() && !self.default_configuration.accept_forwards_to_priv_channels {
2311-
// Note that the behavior here should be identical to the above block - we
2312-
// should NOT reveal the existence or non-existence of a private channel if
2313-
// we don't allow forwards outbound over them.
2314-
break Some(("Don't have available channel for forwarding as requested.", 0x4000 | 10, None));
2315-
}
2320+
// Note that we could technically not return an error yet here and just hope
2321+
// that the connection is reestablished or monitor updated by the time we get
2322+
// around to doing the actual forward, but better to fail early if we can and
2323+
// hopefully an attacker trying to path-trace payments cannot make this occur
2324+
// on a small/per-node/per-channel scale.
2325+
if !chan.is_live() { // channel_disabled
2326+
break Some(("Forwarding channel is not in a ready state.", 0x1000 | 20, chan_update_opt));
2327+
}
2328+
if *amt_to_forward < chan.get_counterparty_htlc_minimum_msat() { // amount_below_minimum
2329+
break Some(("HTLC amount was below the htlc_minimum_msat", 0x1000 | 11, chan_update_opt));
2330+
}
2331+
let fee = amt_to_forward.checked_mul(chan.get_fee_proportional_millionths() as u64)
2332+
.and_then(|prop_fee| { (prop_fee / 1000000)
2333+
.checked_add(chan.get_outbound_forwarding_fee_base_msat() as u64) });
2334+
if fee.is_none() || msg.amount_msat < fee.unwrap() || (msg.amount_msat - fee.unwrap()) < *amt_to_forward { // fee_insufficient
2335+
break Some(("Prior hop has deviated from specified fees parameters or origin node has obsolete ones", 0x1000 | 12, chan_update_opt));
2336+
}
2337+
(chan_update_opt, chan.get_cltv_expiry_delta())
2338+
} else { (None, MIN_CLTV_EXPIRY_DELTA) };
23162339

2317-
// Note that we could technically not return an error yet here and just hope
2318-
// that the connection is reestablished or monitor updated by the time we get
2319-
// around to doing the actual forward, but better to fail early if we can and
2320-
// hopefully an attacker trying to path-trace payments cannot make this occur
2321-
// on a small/per-node/per-channel scale.
2322-
if !chan.is_live() { // channel_disabled
2323-
break Some(("Forwarding channel is not in a ready state.", 0x1000 | 20, Some(self.get_channel_update_for_unicast(chan).unwrap())));
2324-
}
2325-
if *amt_to_forward < chan.get_counterparty_htlc_minimum_msat() { // amount_below_minimum
2326-
break Some(("HTLC amount was below the htlc_minimum_msat", 0x1000 | 11, Some(self.get_channel_update_for_unicast(chan).unwrap())));
2327-
}
2328-
let fee = amt_to_forward.checked_mul(chan.get_fee_proportional_millionths() as u64)
2329-
.and_then(|prop_fee| { (prop_fee / 1000000)
2330-
.checked_add(chan.get_outbound_forwarding_fee_base_msat() as u64) });
2331-
if fee.is_none() || msg.amount_msat < fee.unwrap() || (msg.amount_msat - fee.unwrap()) < *amt_to_forward { // fee_insufficient
2332-
break Some(("Prior hop has deviated from specified fees parameters or origin node has obsolete ones", 0x1000 | 12, Some(self.get_channel_update_for_unicast(chan).unwrap())));
2333-
}
2334-
if (msg.cltv_expiry as u64) < (*outgoing_cltv_value) as u64 + chan.get_cltv_expiry_delta() as u64 { // incorrect_cltv_expiry
2335-
break Some(("Forwarding node has tampered with the intended HTLC values or origin node has an obsolete cltv_expiry_delta", 0x1000 | 13, Some(self.get_channel_update_for_unicast(chan).unwrap())));
2340+
if (msg.cltv_expiry as u64) < (*outgoing_cltv_value) as u64 + forwardee_cltv_expiry_delta as u64 { // incorrect_cltv_expiry
2341+
break Some(("Forwarding node has tampered with the intended HTLC values or origin node has an obsolete cltv_expiry_delta", 0x1000 | 13, chan_update_opt));
23362342
}
23372343
let cur_height = self.best_block.read().unwrap().height() + 1;
23382344
// Theoretically, channel counterparty shouldn't send us a HTLC expiring now,
23392345
// but we want to be robust wrt to counterparty packet sanitization (see
23402346
// HTLC_FAIL_BACK_BUFFER rationale).
23412347
if msg.cltv_expiry <= cur_height + HTLC_FAIL_BACK_BUFFER as u32 { // expiry_too_soon
2342-
break Some(("CLTV expiry is too close", 0x1000 | 14, Some(self.get_channel_update_for_unicast(chan).unwrap())));
2348+
break Some(("CLTV expiry is too close", 0x1000 | 14, chan_update_opt));
23432349
}
23442350
if msg.cltv_expiry > cur_height + CLTV_FAR_FAR_AWAY as u32 { // expiry_too_far
23452351
break Some(("CLTV expiry is too far in the future", 21, None));
@@ -2353,7 +2359,7 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
23532359
// but there is no need to do that, and since we're a bit conservative with our
23542360
// risk threshold it just results in failing to forward payments.
23552361
if (*outgoing_cltv_value) as u64 <= (cur_height + LATENCY_GRACE_PERIOD_BLOCKS) as u64 {
2356-
break Some(("Outgoing CLTV value is too soon", 0x1000 | 14, Some(self.get_channel_update_for_unicast(chan).unwrap())));
2362+
break Some(("Outgoing CLTV value is too soon", 0x1000 | 14, chan_update_opt));
23572363
}
23582364

23592365
break None;

0 commit comments

Comments
 (0)