Skip to content

Commit 1fe8418

Browse files
committed
Fill out failure codes for onion packet
Partial fullfillment for #146
1 parent cd9d680 commit 1fe8418

File tree

2 files changed

+58
-19
lines changed

2 files changed

+58
-19
lines changed

src/ln/channel.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1998,6 +1998,11 @@ impl Channel {
19981998
self.our_htlc_minimum_msat
19991999
}
20002000

2001+
/// Allowed in any state (including after shutdown)
2002+
pub fn get_their_htlc_minimum_msat(&self) -> u64 {
2003+
self.our_htlc_minimum_msat
2004+
}
2005+
20012006
pub fn get_value_satoshis(&self) -> u64 {
20022007
self.channel_value_satoshis
20032008
}

src/ln/channelmanager.rs

Lines changed: 53 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,8 @@ pub struct ChannelManager {
187187
}
188188

189189
const CLTV_EXPIRY_DELTA: u16 = 6 * 24 * 2; //TODO?
190+
const CLTV_FAR_FAR_AWAY: u16 = 6 * 24 * 7; //TODO?
191+
const FINAL_NODE_TIMEOUT: u16 = 3; //TODO?
190192

191193
macro_rules! secp_call {
192194
( $res: expr, $err_msg: expr, $action: expr ) => {
@@ -755,12 +757,15 @@ impl ChannelManager {
755757

756758
let pending_forward_info = if next_hop_data.hmac == [0; 32] {
757759
// OUR PAYMENT!
758-
if next_hop_data.data.amt_to_forward != msg.amount_msat {
759-
return_err!("Upstream node sent less than we were supposed to receive in payment", 19, &byte_utils::be64_to_array(msg.amount_msat));
760+
if (msg.cltv_expiry as u64) < self.latest_block_height.load(Ordering::Acquire) as u64 + FINAL_NODE_TIMEOUT as u64 { // final_expiry_too_soon
761+
return_err!("The CLTV expiry is too soon to handle", 17, &[0;0]);
760762
}
761763
if next_hop_data.data.outgoing_cltv_value != msg.cltv_expiry {
762764
return_err!("Upstream node set CLTV to the wrong value", 18, &byte_utils::be32_to_array(msg.cltv_expiry));
763765
}
766+
if next_hop_data.data.amt_to_forward != msg.amount_msat {
767+
return_err!("Upstream node sent less than we were supposed to receive in payment", 19, &byte_utils::be64_to_array(msg.amount_msat));
768+
}
764769

765770
// Note that we could obviously respond immediately with an update_fulfill_htlc
766771
// message, however that would leak that we are the recipient of this payment, so
@@ -822,29 +827,51 @@ impl ChannelManager {
822827
if onion_packet.is_some() { // If short_channel_id is 0 here, we'll reject them in the body here
823828
let id_option = channel_state.as_ref().unwrap().short_to_id.get(&short_channel_id).cloned();
824829
let forwarding_id = match id_option {
825-
None => {
830+
None => { // unknown_next_peer
826831
return_err!("Don't have available channel for forwarding as requested.", 0x4000 | 10, &[0;0]);
827832
},
828833
Some(id) => id.clone(),
829834
};
830-
if let Some((err, code, chan_update)) = {
835+
if let Some((err, code, chan_update)) = loop {
831836
let chan = channel_state.as_mut().unwrap().by_id.get_mut(&forwarding_id).unwrap();
832-
if !chan.is_live() {
833-
Some(("Forwarding channel is not in a ready state.", 0x1000 | 7, self.get_channel_update(chan).unwrap()))
834-
} else {
835-
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) });
836-
if fee.is_none() || msg.amount_msat < fee.unwrap() || (msg.amount_msat - fee.unwrap()) < *amt_to_forward {
837-
Some(("Prior hop has deviated from specified fees parameters or origin node has obsolete ones", 0x1000 | 12, self.get_channel_update(chan).unwrap()))
838-
} else {
839-
if (msg.cltv_expiry as u64) < (*outgoing_cltv_value) as u64 + CLTV_EXPIRY_DELTA as u64 {
840-
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()))
841-
} else {
842-
None
843-
}
844-
}
837+
838+
if !chan.is_live() { // temporary_channel_failure
839+
break Some(("Forwarding channel is not in a ready state.", 0x1000 | 7, self.get_channel_update(chan).unwrap()));
840+
}
841+
if *amt_to_forward < chan.get_their_htlc_minimum_msat() { // amount_below_minimum
842+
break Some(("HTLC amount was below the htlc_minimum_msat", 0x1000 | 11, self.get_channel_update(chan).unwrap()));
845843
}
846-
} {
847-
return_err!(err, code, &chan_update.encode_with_len()[..]);
844+
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) });
845+
if fee.is_none() || msg.amount_msat < fee.unwrap() || (msg.amount_msat - fee.unwrap()) < *amt_to_forward { // fee_insufficient
846+
break Some(("Prior hop has deviated from specified fees parameters or origin node has obsolete ones", 0x1000 | 12, self.get_channel_update(chan).unwrap()));
847+
}
848+
if (msg.cltv_expiry as u64) < (*outgoing_cltv_value) as u64 + CLTV_EXPIRY_DELTA as u64 { // incorrect_cltv_expiry
849+
break 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()));
850+
}
851+
let cur_height = self.latest_block_height.load(Ordering::Acquire) as u32 + 1;
852+
if msg.cltv_expiry <= cur_height + CLTV_EXPIRY_DELTA as u32 { // expiry_too_soon
853+
break Some(("CLTV expiry is too close", 0x1000 | 14, self.get_channel_update(chan).unwrap()));
854+
}
855+
/*
856+
if chan.is_disabled() {
857+
break Some(("Forwarding channel has been disabled.", 0x1000 | 20, self.get_channel_update(chan).unwrap()));
858+
}
859+
*/
860+
if msg.cltv_expiry > cur_height + CLTV_FAR_FAR_AWAY as u32 { // expiry_too_far
861+
break Some(("CLTV expiry is too far in the future", 0x1000 | 21, self.get_channel_update(chan).unwrap()));
862+
}
863+
break None;
864+
}
865+
{
866+
let mut res = Vec::with_capacity(8 + 128);
867+
if code == 0x1000 | 11 || code == 0x1000 | 12 {
868+
res.extend_from_slice(&byte_utils::be64_to_array(*amt_to_forward));
869+
}
870+
else if code == 0x1000 | 13 {
871+
res.extend_from_slice(&byte_utils::be32_to_array(msg.cltv_expiry));
872+
}
873+
res.extend_from_slice(&chan_update.encode_with_len()[..]);
874+
return_err!(err, code, &res[..]);
848875
}
849876
}
850877
}
@@ -1158,6 +1185,11 @@ impl ChannelManager {
11581185
self.fail_htlc_backwards_internal(self.channel_state.lock().unwrap(), payment_hash, HTLCFailReason::Reason { failure_code: 0x4000 | 15, data: Vec::new() })
11591186
}
11601187

1188+
/// Indicates that the amount for payment_hash is incorrect after a PaymentReceived event.
1189+
pub fn fail_htlc_backwards_incorrect_payment_amount(&self, payment_hash: &[u8; 32]) -> bool {
1190+
self.fail_htlc_backwards_internal(self.channel_state.lock().unwrap(), payment_hash, HTLCFailReason::Reason { failure_code: 0x4000 | 16, data: Vec::new() })
1191+
}
1192+
11611193
/// Fails an HTLC backwards to the sender of it to us.
11621194
/// Note that while we take a channel_state lock as input, we do *not* assume consistency here.
11631195
/// There are several callsites that do stupid things like loop over a list of payment_hashes
@@ -3130,6 +3162,8 @@ mod tests {
31303162
assert_eq!(nodes[2].node.list_channels().len(), 0);
31313163
assert_eq!(nodes[3].node.list_channels().len(), 1);
31323164

3165+
let header = BlockHeader { version: 0x20000000, prev_blockhash: Default::default(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 };
3166+
nodes[4].chain_monitor.block_connected_with_filtering(&Block { header, txdata: vec![] }, 1);
31333167
// One pending HTLC to time out:
31343168
let payment_preimage_2 = route_payment(&nodes[3], &vec!(&nodes[4])[..], 3000000).0;
31353169

0 commit comments

Comments
 (0)