Skip to content

Commit 70619e1

Browse files
Test failing phantom payments
This found multiple bugs: * we were failing back phantom payments with the wrong scid, causing them to never actually be failed backwards (L3022 in channelmanager.rs) * in any place where fail_htlc_backwards_internal was called for a phantom payment failure, we weren't encoding the onion failure as if the phantom were the one failing. Instead, we were encoding the failure as if it were coming from the second-to-last hop. This caused our failures to not be parsed properly on the payer's side. Places we were doing encoding failures incorrectly include: * on failure of a call to inbound_payment::verify * on a user call to fail_htlc_backwards Finally, drop some unnecessary panics when reading OnionHopData objects. This also enables one of the phantom failure tests because we can construct OnionHopDatas with invalid amounts
1 parent 1f6700c commit 70619e1

File tree

3 files changed

+426
-38
lines changed

3 files changed

+426
-38
lines changed

lightning/src/ln/channelmanager.rs

Lines changed: 44 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -358,14 +358,15 @@ mod inbound_payment {
358358
// our payment, which we can use to decode errors or inform the user that the payment was sent.
359359

360360
#[derive(Clone)] // See Channel::revoke_and_ack for why, tl;dr: Rust bug
361-
enum PendingHTLCRouting {
361+
pub(super) enum PendingHTLCRouting {
362362
Forward {
363363
onion_packet: msgs::OnionPacket,
364364
short_channel_id: u64, // This should be NonZero<u64> eventually when we bump MSRV
365365
},
366366
Receive {
367367
payment_data: msgs::FinalOnionHopData,
368368
incoming_cltv_expiry: u32, // Used to track when we should expire pending HTLCs that go unclaimed
369+
phantom_shared_secret: Option<[u8; 32]>,
369370
},
370371
ReceiveKeysend {
371372
payment_preimage: PaymentPreimage,
@@ -375,8 +376,8 @@ enum PendingHTLCRouting {
375376

376377
#[derive(Clone)] // See Channel::revoke_and_ack for why, tl;dr: Rust bug
377378
pub(super) struct PendingHTLCInfo {
378-
routing: PendingHTLCRouting,
379-
incoming_shared_secret: [u8; 32],
379+
pub(super) routing: PendingHTLCRouting,
380+
pub(super) incoming_shared_secret: [u8; 32],
380381
payment_hash: PaymentHash,
381382
pub(super) amt_to_forward: u64,
382383
pub(super) outgoing_cltv_value: u32,
@@ -419,6 +420,7 @@ pub(crate) struct HTLCPreviousHopData {
419420
short_channel_id: u64,
420421
htlc_id: u64,
421422
incoming_packet_shared_secret: [u8; 32],
423+
phantom_shared_secret: Option<[u8; 32]>,
422424

423425
// This field is consumed by `claim_funds_from_hop()` when updating a force-closed backwards
424426
// channel with a preimage provided by the forward channel.
@@ -2072,7 +2074,7 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
20722074
}
20732075

20742076
fn construct_recv_pending_htlc_info(&self, hop_data: msgs::OnionHopData, shared_secret: [u8; 32],
2075-
payment_hash: PaymentHash, amt_msat: u64, cltv_expiry: u32) -> Result<PendingHTLCInfo, ReceiveError>
2077+
payment_hash: PaymentHash, amt_msat: u64, cltv_expiry: u32, phantom_shared_secret: Option<[u8; 32]>) -> Result<PendingHTLCInfo, ReceiveError>
20762078
{
20772079
// final_incorrect_cltv_expiry
20782080
if hop_data.outgoing_cltv_value != cltv_expiry {
@@ -2129,6 +2131,7 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
21292131
PendingHTLCRouting::Receive {
21302132
payment_data: data,
21312133
incoming_cltv_expiry: hop_data.outgoing_cltv_value,
2134+
phantom_shared_secret,
21322135
}
21332136
} else if let Some(payment_preimage) = keysend_preimage {
21342137
// We need to check that the sender knows the keysend preimage before processing this
@@ -2232,7 +2235,7 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
22322235
let pending_forward_info = match next_hop {
22332236
onion_utils::Hop::Receive(next_hop_data) => {
22342237
// OUR PAYMENT!
2235-
match self.construct_recv_pending_htlc_info(next_hop_data, shared_secret, msg.payment_hash, msg.amount_msat, msg.cltv_expiry) {
2238+
match self.construct_recv_pending_htlc_info(next_hop_data, shared_secret, msg.payment_hash, msg.amount_msat, msg.cltv_expiry, None) {
22362239
Ok(info) => {
22372240
// Note that we could obviously respond immediately with an update_fulfill_htlc
22382241
// message, however that would leak that we are the recipient of this payment, so
@@ -3012,17 +3015,18 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
30123015
routing, incoming_shared_secret, payment_hash, amt_to_forward, outgoing_cltv_value },
30133016
prev_funding_outpoint } => {
30143017
macro_rules! fail_forward {
3015-
($msg: expr, $err_code: expr, $err_data: expr) => {
3018+
($msg: expr, $err_code: expr, $err_data: expr, $phantom_ss: expr) => {
30163019
{
30173020
log_info!(self.logger, "Failed to accept/forward incoming HTLC: {}", $msg);
30183021
let htlc_source = HTLCSource::PreviousHopData(HTLCPreviousHopData {
3019-
short_channel_id: short_chan_id,
3022+
short_channel_id: prev_short_channel_id,
30203023
outpoint: prev_funding_outpoint,
30213024
htlc_id: prev_htlc_id,
30223025
incoming_packet_shared_secret: incoming_shared_secret,
3026+
phantom_shared_secret: $phantom_ss,
30233027
});
30243028
failed_forwards.push((htlc_source, payment_hash,
3025-
HTLCFailReason::Reason { failure_code: $err_code, data: $err_data }
3029+
HTLCFailReason::Reason { failure_code: $err_code, data: $err_data }
30263030
));
30273031
continue;
30283032
}
@@ -3031,44 +3035,46 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
30313035
if let PendingHTLCRouting::Forward { onion_packet, .. } = routing {
30323036
let phantom_secret_res = self.keys_manager.get_node_secret(Recipient::PhantomNode);
30333037
if phantom_secret_res.is_ok() && fake_scid::is_valid_phantom(&self.fake_scid_rand_bytes, short_chan_id) {
3034-
let shared_secret = {
3038+
let phantom_shared_secret = {
30353039
let mut arr = [0; 32];
30363040
arr.copy_from_slice(&SharedSecret::new(&onion_packet.public_key.unwrap(), &phantom_secret_res.unwrap())[..]);
30373041
arr
30383042
};
3039-
let next_hop = match onion_utils::decode_next_hop(shared_secret, &onion_packet.hop_data, onion_packet.hmac, payment_hash) {
3043+
let next_hop = match onion_utils::decode_next_hop(phantom_shared_secret, &onion_packet.hop_data, onion_packet.hmac, payment_hash) {
30403044
Ok(res) => res,
30413045
Err(onion_utils::OnionDecodeErr::Malformed { err_msg, err_code }) => {
3042-
fail_forward!(err_msg, err_code, Vec::new());
3046+
let sha256_of_onion = Sha256::hash(&onion_packet.hop_data).into_inner();
3047+
// In this scenario, the phantom would have sent us an
3048+
// `update_fail_malformed_htlc`, meaning here we encrypt the error as
3049+
// if it came from us (the second-to-last hop) but contains the sha256
3050+
// of the onion.
3051+
fail_forward!(err_msg, err_code, sha256_of_onion.to_vec(), None);
30433052
},
30443053
Err(onion_utils::OnionDecodeErr::Relay { err_msg, err_code }) => {
3045-
fail_forward!(err_msg, err_code, Vec::new());
3054+
fail_forward!(err_msg, err_code, Vec::new(), Some(phantom_shared_secret));
30463055
},
30473056
};
30483057
match next_hop {
30493058
onion_utils::Hop::Receive(hop_data) => {
3050-
match self.construct_recv_pending_htlc_info(hop_data, shared_secret, payment_hash, amt_to_forward, outgoing_cltv_value) {
3059+
match self.construct_recv_pending_htlc_info(hop_data, incoming_shared_secret, payment_hash, amt_to_forward, outgoing_cltv_value, Some(phantom_shared_secret)) {
30513060
Ok(info) => phantom_receives.push((prev_short_channel_id, prev_funding_outpoint, vec![(info, prev_htlc_id)])),
3052-
Err(ReceiveError { err_code, err_data, msg }) => fail_forward!(msg, err_code, err_data)
3061+
Err(ReceiveError { err_code, err_data, msg }) => fail_forward!(msg, err_code, err_data, Some(phantom_shared_secret))
30533062
}
30543063
},
30553064
_ => panic!(),
30563065
}
30573066
} else {
3058-
fail_forward!(format!("Unknown short channel id {} for forward HTLC", short_chan_id), 0x4000 | 10, Vec::new());
3067+
fail_forward!(format!("Unknown short channel id {} for forward HTLC", short_chan_id), 0x4000 | 10, Vec::new(), None);
30593068
}
30603069
} else {
3061-
fail_forward!(format!("Unknown short channel id {} for forward HTLC", short_chan_id), 0x4000 | 10, Vec::new());
3070+
fail_forward!(format!("Unknown short channel id {} for forward HTLC", short_chan_id), 0x4000 | 10, Vec::new(), None);
30623071
}
30633072
},
30643073
HTLCForwardInfo::FailHTLC { .. } => {
30653074
// Channel went away before we could fail it. This implies
30663075
// the channel is now on chain and our counterparty is
30673076
// trying to broadcast the HTLC-Timeout, but that's their
30683077
// problem, not ours.
3069-
//
3070-
// `fail_htlc_backwards_internal` is never called for
3071-
// phantom payments, so this is unreachable for them.
30723078
}
30733079
}
30743080
}
@@ -3091,6 +3097,8 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
30913097
outpoint: prev_funding_outpoint,
30923098
htlc_id: prev_htlc_id,
30933099
incoming_packet_shared_secret: incoming_shared_secret,
3100+
// Phantom payments are only PendingHTLCRouting::Receive.
3101+
phantom_shared_secret: None,
30943102
});
30953103
match chan.get_mut().send_htlc(amt_to_forward, payment_hash, outgoing_cltv_value, htlc_source.clone(), onion_packet, &self.logger) {
30963104
Err(e) => {
@@ -3207,11 +3215,11 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
32073215
HTLCForwardInfo::AddHTLC { prev_short_channel_id, prev_htlc_id, forward_info: PendingHTLCInfo {
32083216
routing, incoming_shared_secret, payment_hash, amt_to_forward, .. },
32093217
prev_funding_outpoint } => {
3210-
let (cltv_expiry, onion_payload) = match routing {
3211-
PendingHTLCRouting::Receive { payment_data, incoming_cltv_expiry } =>
3212-
(incoming_cltv_expiry, OnionPayload::Invoice(payment_data)),
3218+
let (cltv_expiry, onion_payload, phantom_shared_secret) = match routing {
3219+
PendingHTLCRouting::Receive { payment_data, incoming_cltv_expiry, phantom_shared_secret } =>
3220+
(incoming_cltv_expiry, OnionPayload::Invoice(payment_data), phantom_shared_secret),
32133221
PendingHTLCRouting::ReceiveKeysend { payment_preimage, incoming_cltv_expiry } =>
3214-
(incoming_cltv_expiry, OnionPayload::Spontaneous(payment_preimage)),
3222+
(incoming_cltv_expiry, OnionPayload::Spontaneous(payment_preimage), None),
32153223
_ => {
32163224
panic!("short_channel_id == 0 should imply any pending_forward entries are of type Receive");
32173225
}
@@ -3222,6 +3230,7 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
32223230
outpoint: prev_funding_outpoint,
32233231
htlc_id: prev_htlc_id,
32243232
incoming_packet_shared_secret: incoming_shared_secret,
3233+
phantom_shared_secret,
32253234
},
32263235
value: amt_to_forward,
32273236
cltv_expiry,
@@ -3239,6 +3248,7 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
32393248
outpoint: prev_funding_outpoint,
32403249
htlc_id: $htlc.prev_hop.htlc_id,
32413250
incoming_packet_shared_secret: $htlc.prev_hop.incoming_packet_shared_secret,
3251+
phantom_shared_secret,
32423252
}), payment_hash,
32433253
HTLCFailReason::Reason { failure_code: 0x4000 | 15, data: htlc_msat_height_data }
32443254
));
@@ -3779,12 +3789,18 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
37793789
pending_events.push(path_failure);
37803790
if let Some(ev) = full_failure_ev { pending_events.push(ev); }
37813791
},
3782-
HTLCSource::PreviousHopData(HTLCPreviousHopData { short_channel_id, htlc_id, incoming_packet_shared_secret, .. }) => {
3792+
HTLCSource::PreviousHopData(HTLCPreviousHopData { short_channel_id, htlc_id, incoming_packet_shared_secret, phantom_shared_secret, .. }) => {
37833793
let err_packet = match onion_error {
37843794
HTLCFailReason::Reason { failure_code, data } => {
37853795
log_trace!(self.logger, "Failing HTLC with payment_hash {} backwards from us with code {}", log_bytes!(payment_hash.0), failure_code);
3786-
let packet = onion_utils::build_failure_packet(&incoming_packet_shared_secret, failure_code, &data[..]).encode();
3787-
onion_utils::encrypt_failure_packet(&incoming_packet_shared_secret, &packet)
3796+
if let Some(phantom_ss) = phantom_shared_secret {
3797+
let phantom_packet = onion_utils::build_failure_packet(&phantom_ss, failure_code, &data[..]).encode();
3798+
let encrypted_phantom_packet = onion_utils::encrypt_failure_packet(&phantom_ss, &phantom_packet);
3799+
onion_utils::encrypt_failure_packet(&incoming_packet_shared_secret, &encrypted_phantom_packet.data[..])
3800+
} else {
3801+
let packet = onion_utils::build_failure_packet(&incoming_packet_shared_secret, failure_code, &data[..]).encode();
3802+
onion_utils::encrypt_failure_packet(&incoming_packet_shared_secret, &packet)
3803+
}
37883804
},
37893805
HTLCFailReason::LightningError { err } => {
37903806
log_trace!(self.logger, "Failing HTLC with payment_hash {} backwards with pre-built LightningError", log_bytes!(payment_hash.0));
@@ -5979,6 +5995,7 @@ impl_writeable_tlv_based_enum!(PendingHTLCRouting,
59795995
},
59805996
(1, Receive) => {
59815997
(0, payment_data, required),
5998+
(1, phantom_shared_secret, option),
59825999
(2, incoming_cltv_expiry, required),
59836000
},
59846001
(2, ReceiveKeysend) => {
@@ -6070,6 +6087,7 @@ impl_writeable_tlv_based_enum!(PendingHTLCStatus, ;
60706087

60716088
impl_writeable_tlv_based!(HTLCPreviousHopData, {
60726089
(0, short_channel_id, required),
6090+
(1, phantom_shared_secret, option),
60736091
(2, outpoint, required),
60746092
(4, htlc_id, required),
60756093
(6, incoming_packet_shared_secret, required)

lightning/src/ln/msgs.rs

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1291,10 +1291,6 @@ impl Readable for FinalOnionHopData {
12911291

12921292
impl Writeable for OnionHopData {
12931293
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
1294-
// Note that this should never be reachable if Rust-Lightning generated the message, as we
1295-
// check values are sane long before we get here, though its possible in the future
1296-
// user-generated messages may hit this.
1297-
if self.amt_to_forward > MAX_VALUE_MSAT { panic!("We should never be sending infinite/overflow onion payments"); }
12981294
match self.format {
12991295
OnionHopDataFormat::Legacy { short_channel_id } => {
13001296
0u8.write(w)?;
@@ -1311,9 +1307,6 @@ impl Writeable for OnionHopData {
13111307
});
13121308
},
13131309
OnionHopDataFormat::FinalNode { ref payment_data, ref keysend_preimage } => {
1314-
if let Some(final_data) = payment_data {
1315-
if final_data.total_msat > MAX_VALUE_MSAT { panic!("We should never be sending infinite/overflow onion payments"); }
1316-
}
13171310
encode_varint_length_prefixed_tlv!(w, {
13181311
(2, HighZeroBytesDroppedVarInt(self.amt_to_forward), required),
13191312
(4, HighZeroBytesDroppedVarInt(self.outgoing_cltv_value), required),

0 commit comments

Comments
 (0)