Skip to content

Commit e3a305c

Browse files
authored
Merge pull request #1447 from andozw/seana.20220422.avoid-storing-FinalOnionHopData
Avoid storing a full FinalOnionHopData in OnionPayload::Invoice
2 parents 9bdce47 + fc77c57 commit e3a305c

File tree

2 files changed

+68
-52
lines changed

2 files changed

+68
-52
lines changed

lightning/src/ln/channelmanager.rs

+67-51
Original file line numberDiff line numberDiff line change
@@ -159,20 +159,26 @@ pub(crate) struct HTLCPreviousHopData {
159159
}
160160

161161
enum OnionPayload {
162-
/// Contains a total_msat (which may differ from value if this is a Multi-Path Payment) and a
163-
/// payment_secret which prevents path-probing attacks and can associate different HTLCs which
164-
/// are part of the same payment.
165-
Invoice(msgs::FinalOnionHopData),
162+
/// Indicates this incoming onion payload is for the purpose of paying an invoice.
163+
Invoice {
164+
/// This is only here for backwards-compatibility in serialization, in the future it can be
165+
/// removed, breaking clients running 0.0.106 and earlier.
166+
_legacy_hop_data: msgs::FinalOnionHopData,
167+
},
166168
/// Contains the payer-provided preimage.
167169
Spontaneous(PaymentPreimage),
168170
}
169171

172+
/// HTLCs that are to us and can be failed/claimed by the user
170173
struct ClaimableHTLC {
171174
prev_hop: HTLCPreviousHopData,
172175
cltv_expiry: u32,
176+
/// The amount (in msats) of this MPP part
173177
value: u64,
174178
onion_payload: OnionPayload,
175179
timer_ticks: u8,
180+
/// The sum total of all MPP parts
181+
total_msat: u64,
176182
}
177183

178184
/// A payment identifier used to uniquely identify a payment to LDK.
@@ -3096,11 +3102,13 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
30963102
HTLCForwardInfo::AddHTLC { prev_short_channel_id, prev_htlc_id, forward_info: PendingHTLCInfo {
30973103
routing, incoming_shared_secret, payment_hash, amt_to_forward, .. },
30983104
prev_funding_outpoint } => {
3099-
let (cltv_expiry, onion_payload, phantom_shared_secret) = match routing {
3100-
PendingHTLCRouting::Receive { payment_data, incoming_cltv_expiry, phantom_shared_secret } =>
3101-
(incoming_cltv_expiry, OnionPayload::Invoice(payment_data), phantom_shared_secret),
3105+
let (cltv_expiry, onion_payload, payment_data, phantom_shared_secret) = match routing {
3106+
PendingHTLCRouting::Receive { payment_data, incoming_cltv_expiry, phantom_shared_secret } => {
3107+
let _legacy_hop_data = payment_data.clone();
3108+
(incoming_cltv_expiry, OnionPayload::Invoice { _legacy_hop_data }, Some(payment_data), phantom_shared_secret)
3109+
},
31023110
PendingHTLCRouting::ReceiveKeysend { payment_preimage, incoming_cltv_expiry } =>
3103-
(incoming_cltv_expiry, OnionPayload::Spontaneous(payment_preimage), None),
3111+
(incoming_cltv_expiry, OnionPayload::Spontaneous(payment_preimage), None, None),
31043112
_ => {
31053113
panic!("short_channel_id == 0 should imply any pending_forward entries are of type Receive");
31063114
}
@@ -3115,6 +3123,7 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
31153123
},
31163124
value: amt_to_forward,
31173125
timer_ticks: 0,
3126+
total_msat: if let Some(data) = &payment_data { data.total_msat } else { amt_to_forward },
31183127
cltv_expiry,
31193128
onion_payload,
31203129
};
@@ -3138,7 +3147,7 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
31383147
}
31393148

31403149
macro_rules! check_total_value {
3141-
($payment_data_total_msat: expr, $payment_secret: expr, $payment_preimage: expr) => {{
3150+
($payment_data: expr, $payment_preimage: expr) => {{
31423151
let mut payment_received_generated = false;
31433152
let htlcs = channel_state.claimable_htlcs.entry(payment_hash)
31443153
.or_insert(Vec::new());
@@ -3153,28 +3162,28 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
31533162
for htlc in htlcs.iter() {
31543163
total_value += htlc.value;
31553164
match &htlc.onion_payload {
3156-
OnionPayload::Invoice(htlc_payment_data) => {
3157-
if htlc_payment_data.total_msat != $payment_data_total_msat {
3165+
OnionPayload::Invoice { .. } => {
3166+
if htlc.total_msat != $payment_data.total_msat {
31583167
log_trace!(self.logger, "Failing HTLCs with payment_hash {} as the HTLCs had inconsistent total values (eg {} and {})",
3159-
log_bytes!(payment_hash.0), $payment_data_total_msat, htlc_payment_data.total_msat);
3168+
log_bytes!(payment_hash.0), $payment_data.total_msat, htlc.total_msat);
31603169
total_value = msgs::MAX_VALUE_MSAT;
31613170
}
31623171
if total_value >= msgs::MAX_VALUE_MSAT { break; }
31633172
},
31643173
_ => unreachable!(),
31653174
}
31663175
}
3167-
if total_value >= msgs::MAX_VALUE_MSAT || total_value > $payment_data_total_msat {
3176+
if total_value >= msgs::MAX_VALUE_MSAT || total_value > $payment_data.total_msat {
31683177
log_trace!(self.logger, "Failing HTLCs with payment_hash {} as the total value {} ran over expected value {} (or HTLCs were inconsistent)",
3169-
log_bytes!(payment_hash.0), total_value, $payment_data_total_msat);
3178+
log_bytes!(payment_hash.0), total_value, $payment_data.total_msat);
31703179
fail_htlc!(claimable_htlc);
3171-
} else if total_value == $payment_data_total_msat {
3180+
} else if total_value == $payment_data.total_msat {
31723181
htlcs.push(claimable_htlc);
31733182
new_events.push(events::Event::PaymentReceived {
31743183
payment_hash,
31753184
purpose: events::PaymentPurpose::InvoicePayment {
31763185
payment_preimage: $payment_preimage,
3177-
payment_secret: $payment_secret,
3186+
payment_secret: $payment_data.payment_secret,
31783187
},
31793188
amt: total_value,
31803189
});
@@ -3199,17 +3208,16 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
31993208
match payment_secrets.entry(payment_hash) {
32003209
hash_map::Entry::Vacant(_) => {
32013210
match claimable_htlc.onion_payload {
3202-
OnionPayload::Invoice(ref payment_data) => {
3203-
let payment_preimage = match inbound_payment::verify(payment_hash, payment_data.clone(), self.highest_seen_timestamp.load(Ordering::Acquire) as u64, &self.inbound_payment_key, &self.logger) {
3211+
OnionPayload::Invoice { .. } => {
3212+
let payment_data = payment_data.unwrap();
3213+
let payment_preimage = match inbound_payment::verify(payment_hash, &payment_data, self.highest_seen_timestamp.load(Ordering::Acquire) as u64, &self.inbound_payment_key, &self.logger) {
32043214
Ok(payment_preimage) => payment_preimage,
32053215
Err(()) => {
32063216
fail_htlc!(claimable_htlc);
32073217
continue
32083218
}
32093219
};
3210-
let payment_data_total_msat = payment_data.total_msat;
3211-
let payment_secret = payment_data.payment_secret.clone();
3212-
check_total_value!(payment_data_total_msat, payment_secret, payment_preimage);
3220+
check_total_value!(payment_data, payment_preimage);
32133221
},
32143222
OnionPayload::Spontaneous(preimage) => {
32153223
match channel_state.claimable_htlcs.entry(payment_hash) {
@@ -3230,14 +3238,12 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
32303238
}
32313239
},
32323240
hash_map::Entry::Occupied(inbound_payment) => {
3233-
let payment_data =
3234-
if let OnionPayload::Invoice(ref data) = claimable_htlc.onion_payload {
3235-
data.clone()
3236-
} else {
3237-
log_trace!(self.logger, "Failing new keysend HTLC with payment_hash {} because we already have an inbound payment with the same payment hash", log_bytes!(payment_hash.0));
3238-
fail_htlc!(claimable_htlc);
3239-
continue
3240-
};
3241+
if payment_data.is_none() {
3242+
log_trace!(self.logger, "Failing new keysend HTLC with payment_hash {} because we already have an inbound payment with the same payment hash", log_bytes!(payment_hash.0));
3243+
fail_htlc!(claimable_htlc);
3244+
continue
3245+
};
3246+
let payment_data = payment_data.unwrap();
32413247
if inbound_payment.get().payment_secret != payment_data.payment_secret {
32423248
log_trace!(self.logger, "Failing new HTLC with payment_hash {} as it didn't match our expected payment secret.", log_bytes!(payment_hash.0));
32433249
fail_htlc!(claimable_htlc);
@@ -3246,7 +3252,7 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
32463252
log_bytes!(payment_hash.0), payment_data.total_msat, inbound_payment.get().min_value_msat.unwrap());
32473253
fail_htlc!(claimable_htlc);
32483254
} else {
3249-
let payment_received_generated = check_total_value!(payment_data.total_msat, payment_data.payment_secret, inbound_payment.get().payment_preimage);
3255+
let payment_received_generated = check_total_value!(payment_data, inbound_payment.get().payment_preimage);
32503256
if payment_received_generated {
32513257
inbound_payment.remove_entry();
32523258
}
@@ -3465,10 +3471,10 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
34653471
debug_assert!(false);
34663472
return false;
34673473
}
3468-
if let OnionPayload::Invoice(ref final_hop_data) = htlcs[0].onion_payload {
3474+
if let OnionPayload::Invoice { .. } = htlcs[0].onion_payload {
34693475
// Check if we've received all the parts we need for an MPP (the value of the parts adds to total_msat).
34703476
// In this case we're not going to handle any timeouts of the parts here.
3471-
if final_hop_data.total_msat == htlcs.iter().fold(0, |total, htlc| total + htlc.value) {
3477+
if htlcs[0].total_msat == htlcs.iter().fold(0, |total, htlc| total + htlc.value) {
34723478
return true;
34733479
} else if htlcs.into_iter().any(|htlc| {
34743480
htlc.timer_ticks += 1;
@@ -6069,20 +6075,21 @@ impl_writeable_tlv_based!(HTLCPreviousHopData, {
60696075
impl Writeable for ClaimableHTLC {
60706076
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), io::Error> {
60716077
let payment_data = match &self.onion_payload {
6072-
OnionPayload::Invoice(data) => Some(data.clone()),
6078+
OnionPayload::Invoice { _legacy_hop_data } => Some(_legacy_hop_data),
60736079
_ => None,
60746080
};
60756081
let keysend_preimage = match self.onion_payload {
6076-
OnionPayload::Invoice(_) => None,
6082+
OnionPayload::Invoice { .. } => None,
60776083
OnionPayload::Spontaneous(preimage) => Some(preimage.clone()),
60786084
};
6079-
write_tlv_fields!
6080-
(writer,
6081-
{
6082-
(0, self.prev_hop, required), (2, self.value, required),
6083-
(4, payment_data, option), (6, self.cltv_expiry, required),
6084-
(8, keysend_preimage, option),
6085-
});
6085+
write_tlv_fields!(writer, {
6086+
(0, self.prev_hop, required),
6087+
(1, self.total_msat, required),
6088+
(2, self.value, required),
6089+
(4, payment_data, option),
6090+
(6, self.cltv_expiry, required),
6091+
(8, keysend_preimage, option),
6092+
});
60866093
Ok(())
60876094
}
60886095
}
@@ -6093,32 +6100,41 @@ impl Readable for ClaimableHTLC {
60936100
let mut value = 0;
60946101
let mut payment_data: Option<msgs::FinalOnionHopData> = None;
60956102
let mut cltv_expiry = 0;
6103+
let mut total_msat = None;
60966104
let mut keysend_preimage: Option<PaymentPreimage> = None;
6097-
read_tlv_fields!
6098-
(reader,
6099-
{
6100-
(0, prev_hop, required), (2, value, required),
6101-
(4, payment_data, option), (6, cltv_expiry, required),
6102-
(8, keysend_preimage, option)
6103-
});
6105+
read_tlv_fields!(reader, {
6106+
(0, prev_hop, required),
6107+
(1, total_msat, option),
6108+
(2, value, required),
6109+
(4, payment_data, option),
6110+
(6, cltv_expiry, required),
6111+
(8, keysend_preimage, option)
6112+
});
61046113
let onion_payload = match keysend_preimage {
61056114
Some(p) => {
61066115
if payment_data.is_some() {
61076116
return Err(DecodeError::InvalidValue)
61086117
}
6118+
if total_msat.is_none() {
6119+
total_msat = Some(value);
6120+
}
61096121
OnionPayload::Spontaneous(p)
61106122
},
61116123
None => {
61126124
if payment_data.is_none() {
61136125
return Err(DecodeError::InvalidValue)
61146126
}
6115-
OnionPayload::Invoice(payment_data.unwrap())
6127+
if total_msat.is_none() {
6128+
total_msat = Some(payment_data.as_ref().unwrap().total_msat);
6129+
}
6130+
OnionPayload::Invoice { _legacy_hop_data: payment_data.unwrap() }
61166131
},
61176132
};
61186133
Ok(Self {
61196134
prev_hop: prev_hop.0.unwrap(),
61206135
timer_ticks: 0,
61216136
value,
6137+
total_msat: total_msat.unwrap(),
61226138
onion_payload,
61236139
cltv_expiry,
61246140
})
@@ -7319,15 +7335,15 @@ mod tests {
73197335
// payment verification fails as expected.
73207336
let mut bad_payment_hash = payment_hash.clone();
73217337
bad_payment_hash.0[0] += 1;
7322-
match inbound_payment::verify(bad_payment_hash, payment_data.clone(), nodes[0].node.highest_seen_timestamp.load(Ordering::Acquire) as u64, &nodes[0].node.inbound_payment_key, &nodes[0].logger) {
7338+
match inbound_payment::verify(bad_payment_hash, &payment_data, nodes[0].node.highest_seen_timestamp.load(Ordering::Acquire) as u64, &nodes[0].node.inbound_payment_key, &nodes[0].logger) {
73237339
Ok(_) => panic!("Unexpected ok"),
73247340
Err(()) => {
73257341
nodes[0].logger.assert_log_contains("lightning::ln::inbound_payment".to_string(), "Failing HTLC with user-generated payment_hash".to_string(), 1);
73267342
}
73277343
}
73287344

73297345
// Check that using the original payment hash succeeds.
7330-
assert!(inbound_payment::verify(payment_hash, payment_data, nodes[0].node.highest_seen_timestamp.load(Ordering::Acquire) as u64, &nodes[0].node.inbound_payment_key, &nodes[0].logger).is_ok());
7346+
assert!(inbound_payment::verify(payment_hash, &payment_data, nodes[0].node.highest_seen_timestamp.load(Ordering::Acquire) as u64, &nodes[0].node.inbound_payment_key, &nodes[0].logger).is_ok());
73317347
}
73327348
}
73337349

lightning/src/ln/inbound_payment.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,7 @@ fn construct_payment_secret(iv_bytes: &[u8; IV_LEN], metadata_bytes: &[u8; METAD
200200
/// [`KeysInterface::get_inbound_payment_key_material`]: crate::chain::keysinterface::KeysInterface::get_inbound_payment_key_material
201201
/// [`create_inbound_payment`]: crate::ln::channelmanager::ChannelManager::create_inbound_payment
202202
/// [`create_inbound_payment_for_hash`]: crate::ln::channelmanager::ChannelManager::create_inbound_payment_for_hash
203-
pub(super) fn verify<L: Deref>(payment_hash: PaymentHash, payment_data: msgs::FinalOnionHopData, highest_seen_timestamp: u64, keys: &ExpandedKey, logger: &L) -> Result<Option<PaymentPreimage>, ()>
203+
pub(super) fn verify<L: Deref>(payment_hash: PaymentHash, payment_data: &msgs::FinalOnionHopData, highest_seen_timestamp: u64, keys: &ExpandedKey, logger: &L) -> Result<Option<PaymentPreimage>, ()>
204204
where L::Target: Logger
205205
{
206206
let (iv_bytes, metadata_bytes) = decrypt_metadata(payment_data.payment_secret, keys);

0 commit comments

Comments
 (0)