Skip to content

Commit ed30a19

Browse files
yuntaiTheBlueMatt
authored andcommitted
Error handling in decoding onion
1 parent ba30061 commit ed30a19

File tree

2 files changed

+52
-20
lines changed

2 files changed

+52
-20
lines changed

src/ln/channel.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2450,6 +2450,11 @@ impl Channel {
24502450
self.our_htlc_minimum_msat
24512451
}
24522452

2453+
/// Allowed in any state (including after shutdown)
2454+
pub fn get_their_htlc_minimum_msat(&self) -> u64 {
2455+
self.our_htlc_minimum_msat
2456+
}
2457+
24532458
pub fn get_value_satoshis(&self) -> u64 {
24542459
self.channel_value_satoshis
24552460
}

src/ln/channelmanager.rs

Lines changed: 47 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,7 @@ pub struct ChannelManager {
296296
/// the HTLC via a full update_fail_htlc/commitment_signed dance before we hit the
297297
/// CLTV_CLAIM_BUFFER point (we static assert that its at least 3 blocks more).
298298
const CLTV_EXPIRY_DELTA: u16 = 6 * 24 * 2; //TODO?
299+
const CLTV_FAR_FAR_AWAY: u32 = 6 * 24 * 7; //TODO?
299300

300301
// Check that our CLTV_EXPIRY is at least CLTV_CLAIM_BUFFER + 2*HTLC_FAIL_TIMEOUT_BLOCKS, ie that
301302
// if the next-hop peer fails the HTLC within HTLC_FAIL_TIMEOUT_BLOCKS then we'll still have
@@ -896,13 +897,17 @@ impl ChannelManager {
896897
}
897898
};
898899

899-
//TODO: Check that msg.cltv_expiry is within acceptable bounds!
900-
901900
let pending_forward_info = if next_hop_data.hmac == [0; 32] {
902901
// OUR PAYMENT!
903-
if next_hop_data.data.amt_to_forward != msg.amount_msat {
902+
// final_expiry_too_soon
903+
if (msg.cltv_expiry as u64) < self.latest_block_height.load(Ordering::Acquire) as u64 + (CLTV_CLAIM_BUFFER + HTLC_FAIL_TIMEOUT_BLOCKS) as u64 {
904+
return_err!("The final CLTV expiry is too soon to handle", 17, &[0;0]);
905+
}
906+
// final_incorrect_htlc_amount
907+
if next_hop_data.data.amt_to_forward > msg.amount_msat {
904908
return_err!("Upstream node sent less than we were supposed to receive in payment", 19, &byte_utils::be64_to_array(msg.amount_msat));
905909
}
910+
// final_incorrect_cltv_expiry
906911
if next_hop_data.data.outgoing_cltv_value != msg.cltv_expiry {
907912
return_err!("Upstream node set CLTV to the wrong value", 18, &byte_utils::be32_to_array(msg.cltv_expiry));
908913
}
@@ -967,29 +972,49 @@ impl ChannelManager {
967972
if onion_packet.is_some() { // If short_channel_id is 0 here, we'll reject them in the body here
968973
let id_option = channel_state.as_ref().unwrap().short_to_id.get(&short_channel_id).cloned();
969974
let forwarding_id = match id_option {
970-
None => {
975+
None => { // unknown_next_peer
971976
return_err!("Don't have available channel for forwarding as requested.", 0x4000 | 10, &[0;0]);
972977
},
973978
Some(id) => id.clone(),
974979
};
975-
if let Some((err, code, chan_update)) = {
980+
if let Some((err, code, chan_update)) = loop {
976981
let chan = channel_state.as_mut().unwrap().by_id.get_mut(&forwarding_id).unwrap();
977-
if !chan.is_live() {
978-
Some(("Forwarding channel is not in a ready state.", 0x1000 | 7, self.get_channel_update(chan).unwrap()))
979-
} else {
980-
let fee = amt_to_forward.checked_mul(self.fee_proportional_millionths as u64).and_then(|prop_fee| { (prop_fee / 1000000).checked_add(chan.get_our_fee_base_msat(&*self.fee_estimator) as u64) });
981-
if fee.is_none() || msg.amount_msat < fee.unwrap() || (msg.amount_msat - fee.unwrap()) < *amt_to_forward {
982-
Some(("Prior hop has deviated from specified fees parameters or origin node has obsolete ones", 0x1000 | 12, self.get_channel_update(chan).unwrap()))
983-
} else {
984-
if (msg.cltv_expiry as u64) < (*outgoing_cltv_value) as u64 + CLTV_EXPIRY_DELTA as u64 {
985-
Some(("Forwarding node has tampered with the intended HTLC values or origin node has an obsolete cltv_expiry_delta", 0x1000 | 13, self.get_channel_update(chan).unwrap()))
986-
} else {
987-
None
988-
}
989-
}
982+
983+
if !chan.is_live() { // channel_disabled
984+
break Some(("Forwarding channel is not in a ready state.", 0x1000 | 20, Some(self.get_channel_update(chan).unwrap())));
985+
}
986+
if *amt_to_forward < chan.get_their_htlc_minimum_msat() { // amount_below_minimum
987+
break Some(("HTLC amount was below the htlc_minimum_msat", 0x1000 | 11, Some(self.get_channel_update(chan).unwrap())));
988+
}
989+
let fee = amt_to_forward.checked_mul(self.fee_proportional_millionths as u64).and_then(|prop_fee| { (prop_fee / 1000000).checked_add(chan.get_our_fee_base_msat(&*self.fee_estimator) as u64) });
990+
if fee.is_none() || msg.amount_msat < fee.unwrap() || (msg.amount_msat - fee.unwrap()) < *amt_to_forward { // fee_insufficient
991+
break Some(("Prior hop has deviated from specified fees parameters or origin node has obsolete ones", 0x1000 | 12, Some(self.get_channel_update(chan).unwrap())));
992+
}
993+
if (msg.cltv_expiry as u64) < (*outgoing_cltv_value) as u64 + CLTV_EXPIRY_DELTA as u64 { // incorrect_cltv_expiry
994+
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(chan).unwrap())));
995+
}
996+
let cur_height = self.latest_block_height.load(Ordering::Acquire) as u32 + 1;
997+
// We want to have at least HTLC_FAIL_TIMEOUT_BLOCKS to fail prior to going on chain CLAIM_BUFFER blocks before expiration
998+
if msg.cltv_expiry <= cur_height + CLTV_CLAIM_BUFFER + HTLC_FAIL_TIMEOUT_BLOCKS as u32 { // expiry_too_soon
999+
break Some(("CLTV expiry is too close", 0x1000 | 14, Some(self.get_channel_update(chan).unwrap())));
1000+
}
1001+
if msg.cltv_expiry > cur_height + CLTV_FAR_FAR_AWAY as u32 { // expiry_too_far
1002+
break Some(("CLTV expiry is too far in the future", 21, None));
1003+
}
1004+
break None;
1005+
}
1006+
{
1007+
let mut res = Vec::with_capacity(8 + 128);
1008+
if code == 0x1000 | 11 || code == 0x1000 | 12 {
1009+
res.extend_from_slice(&byte_utils::be64_to_array(msg.amount_msat));
1010+
}
1011+
else if code == 0x1000 | 13 {
1012+
res.extend_from_slice(&byte_utils::be32_to_array(msg.cltv_expiry));
1013+
}
1014+
if let Some(chan_update) = chan_update {
1015+
res.extend_from_slice(&chan_update.encode_with_len()[..]);
9901016
}
991-
} {
992-
return_err!(err, code, &chan_update.encode_with_len()[..]);
1017+
return_err!(err, code, &res[..]);
9931018
}
9941019
}
9951020
}
@@ -1328,6 +1353,8 @@ impl ChannelManager {
13281353

13291354
/// Indicates that the preimage for payment_hash is unknown after a PaymentReceived event.
13301355
pub fn fail_htlc_backwards(&self, payment_hash: &[u8; 32]) -> bool {
1356+
// TODO: Add ability to return 0x4000|16 (incorrect_payment_amount) if the amount we
1357+
// received is < expected or > 2*expected
13311358
let mut channel_state = Some(self.channel_state.lock().unwrap());
13321359
let removed_source = channel_state.as_mut().unwrap().claimable_htlcs.remove(payment_hash);
13331360
if let Some(mut sources) = removed_source {

0 commit comments

Comments
 (0)