Add total_value_received to ClaimableHTLC for claim validation

This is pre-work for allowing nodes to overshoot onion values and
changing validation for MPP completion. This adds a field to
`ClaimableHTLC` that is separate from the onion values, which
represents the actual received amount reported in `PaymentClaimable`
which is what we want to validate against when a user goes to claim.
This commit is contained in:
Alec Chen 2023-03-23 14:34:57 -05:00
parent 31e78ff258
commit b9f4ebdd28

View file

@ -194,7 +194,10 @@ struct ClaimableHTLC {
value: u64, value: u64,
onion_payload: OnionPayload, onion_payload: OnionPayload,
timer_ticks: u8, timer_ticks: u8,
/// The sum total of all MPP parts /// The total value received for a payment (sum of all MPP parts if the payment is a MPP).
/// Gets set to the amount reported when pushing [`Event::PaymentClaimable`].
total_value_received: Option<u64>,
/// The sender intended sum total of all MPP parts specified in the onion
total_msat: u64, total_msat: u64,
} }
@ -3272,7 +3275,7 @@ where
panic!("short_channel_id == 0 should imply any pending_forward entries are of type Receive"); panic!("short_channel_id == 0 should imply any pending_forward entries are of type Receive");
} }
}; };
let claimable_htlc = ClaimableHTLC { let mut claimable_htlc = ClaimableHTLC {
prev_hop: HTLCPreviousHopData { prev_hop: HTLCPreviousHopData {
short_channel_id: prev_short_channel_id, short_channel_id: prev_short_channel_id,
outpoint: prev_funding_outpoint, outpoint: prev_funding_outpoint,
@ -3282,6 +3285,7 @@ where
}, },
value: outgoing_amt_msat, value: outgoing_amt_msat,
timer_ticks: 0, timer_ticks: 0,
total_value_received: None,
total_msat: if let Some(data) = &payment_data { data.total_msat } else { outgoing_amt_msat }, total_msat: if let Some(data) = &payment_data { data.total_msat } else { outgoing_amt_msat },
cltv_expiry, cltv_expiry,
onion_payload, onion_payload,
@ -3326,7 +3330,7 @@ where
fail_htlc!(claimable_htlc, payment_hash); fail_htlc!(claimable_htlc, payment_hash);
continue continue
} }
let (_, htlcs) = claimable_payments.claimable_htlcs.entry(payment_hash) let (_, ref mut htlcs) = claimable_payments.claimable_htlcs.entry(payment_hash)
.or_insert_with(|| (purpose(), Vec::new())); .or_insert_with(|| (purpose(), Vec::new()));
if htlcs.len() == 1 { if htlcs.len() == 1 {
if let OnionPayload::Spontaneous(_) = htlcs[0].onion_payload { if let OnionPayload::Spontaneous(_) = htlcs[0].onion_payload {
@ -3357,11 +3361,13 @@ where
} else if total_value == $payment_data.total_msat { } else if total_value == $payment_data.total_msat {
let prev_channel_id = prev_funding_outpoint.to_channel_id(); let prev_channel_id = prev_funding_outpoint.to_channel_id();
htlcs.push(claimable_htlc); htlcs.push(claimable_htlc);
let amount_msat = htlcs.iter().map(|htlc| htlc.value).sum();
htlcs.iter_mut().for_each(|htlc| htlc.total_value_received = Some(amount_msat));
new_events.push(events::Event::PaymentClaimable { new_events.push(events::Event::PaymentClaimable {
receiver_node_id: Some(receiver_node_id), receiver_node_id: Some(receiver_node_id),
payment_hash, payment_hash,
purpose: purpose(), purpose: purpose(),
amount_msat: total_value, amount_msat,
via_channel_id: Some(prev_channel_id), via_channel_id: Some(prev_channel_id),
via_user_channel_id: Some(prev_user_channel_id), via_user_channel_id: Some(prev_user_channel_id),
}); });
@ -3415,6 +3421,8 @@ where
} }
match claimable_payments.claimable_htlcs.entry(payment_hash) { match claimable_payments.claimable_htlcs.entry(payment_hash) {
hash_map::Entry::Vacant(e) => { hash_map::Entry::Vacant(e) => {
let amount_msat = claimable_htlc.value;
claimable_htlc.total_value_received = Some(amount_msat);
let purpose = events::PaymentPurpose::SpontaneousPayment(preimage); let purpose = events::PaymentPurpose::SpontaneousPayment(preimage);
e.insert((purpose.clone(), vec![claimable_htlc])); e.insert((purpose.clone(), vec![claimable_htlc]));
let prev_channel_id = prev_funding_outpoint.to_channel_id(); let prev_channel_id = prev_funding_outpoint.to_channel_id();
@ -3960,6 +3968,7 @@ where
// provide the preimage, so worrying too much about the optimal handling isn't worth // provide the preimage, so worrying too much about the optimal handling isn't worth
// it. // it.
let mut claimable_amt_msat = 0; let mut claimable_amt_msat = 0;
let mut prev_total_msat = None;
let mut expected_amt_msat = None; let mut expected_amt_msat = None;
let mut valid_mpp = true; let mut valid_mpp = true;
let mut errs = Vec::new(); let mut errs = Vec::new();
@ -3987,14 +3996,22 @@ where
break; break;
} }
if expected_amt_msat.is_some() && expected_amt_msat != Some(htlc.total_msat) { if prev_total_msat.is_some() && prev_total_msat != Some(htlc.total_msat) {
log_error!(self.logger, "Somehow ended up with an MPP payment with different total amounts - this should not be reachable!"); log_error!(self.logger, "Somehow ended up with an MPP payment with different expected total amounts - this should not be reachable!");
debug_assert!(false); debug_assert!(false);
valid_mpp = false; valid_mpp = false;
break; break;
} }
prev_total_msat = Some(htlc.total_msat);
if expected_amt_msat.is_some() && expected_amt_msat != htlc.total_value_received {
log_error!(self.logger, "Somehow ended up with an MPP payment with different received total amounts - this should not be reachable!");
debug_assert!(false);
valid_mpp = false;
break;
}
expected_amt_msat = htlc.total_value_received;
expected_amt_msat = Some(htlc.total_msat);
if let OnionPayload::Spontaneous(_) = &htlc.onion_payload { if let OnionPayload::Spontaneous(_) = &htlc.onion_payload {
// We don't currently support MPP for spontaneous payments, so just check // We don't currently support MPP for spontaneous payments, so just check
// that there's one payment here and move on. // that there's one payment here and move on.
@ -6795,6 +6812,7 @@ impl Writeable for ClaimableHTLC {
(1, self.total_msat, required), (1, self.total_msat, required),
(2, self.value, required), (2, self.value, required),
(4, payment_data, option), (4, payment_data, option),
(5, self.total_value_received, option),
(6, self.cltv_expiry, required), (6, self.cltv_expiry, required),
(8, keysend_preimage, option), (8, keysend_preimage, option),
}); });
@ -6808,6 +6826,7 @@ impl Readable for ClaimableHTLC {
let mut value = 0; let mut value = 0;
let mut payment_data: Option<msgs::FinalOnionHopData> = None; let mut payment_data: Option<msgs::FinalOnionHopData> = None;
let mut cltv_expiry = 0; let mut cltv_expiry = 0;
let mut total_value_received = None;
let mut total_msat = None; let mut total_msat = None;
let mut keysend_preimage: Option<PaymentPreimage> = None; let mut keysend_preimage: Option<PaymentPreimage> = None;
read_tlv_fields!(reader, { read_tlv_fields!(reader, {
@ -6815,6 +6834,7 @@ impl Readable for ClaimableHTLC {
(1, total_msat, option), (1, total_msat, option),
(2, value, required), (2, value, required),
(4, payment_data, option), (4, payment_data, option),
(5, total_value_received, option),
(6, cltv_expiry, required), (6, cltv_expiry, required),
(8, keysend_preimage, option) (8, keysend_preimage, option)
}); });
@ -6842,6 +6862,7 @@ impl Readable for ClaimableHTLC {
prev_hop: prev_hop.0.unwrap(), prev_hop: prev_hop.0.unwrap(),
timer_ticks: 0, timer_ticks: 0,
value, value,
total_value_received,
total_msat: total_msat.unwrap(), total_msat: total_msat.unwrap(),
onion_payload, onion_payload,
cltv_expiry, cltv_expiry,