Skip to content

Commit e9b02dc

Browse files
committed
WIP: started implementing onion failure handling
1 parent e00cbc1 commit e9b02dc

File tree

4 files changed

+319
-67
lines changed

4 files changed

+319
-67
lines changed

fuzz/fuzz_targets/router_target.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ pub fn do_test(data: &[u8]) {
187187
},
188188
1 => {
189189
let short_channel_id = slice_to_be64(get_slice!(8));
190-
router.handle_htlc_fail_channel_update(&msgs::HTLCFailChannelUpdate::ChannelClosed {short_channel_id});
190+
router.handle_htlc_fail_channel_update(&msgs::HTLCFailChannelUpdate::ChannelClosed {short_channel_id, is_permanent: false});
191191
},
192192
_ => return,
193193
}

src/ln/channelmanager.rs

Lines changed: 122 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1345,11 +1345,6 @@ impl ChannelManager {
13451345
} else { false }
13461346
}
13471347

1348-
/// Indicates that the amount for payment_hash is incorrect after a PaymentReceived event.
1349-
pub fn fail_htlc_backwards_incorrect_payment_amount(&self, payment_hash: &[u8; 32]) -> bool {
1350-
self.fail_htlc_backwards_internal(self.channel_state.lock().unwrap(), payment_hash, HTLCFailReason::Reason { failure_code: 0x4000 | 16, data: Vec::new() })
1351-
}
1352-
13531348
/// Fails an HTLC backwards to the sender of it to us.
13541349
/// Note that while we take a channel_state lock as input, we do *not* assume consistency here.
13551350
/// There are several callsites that do stupid things like loop over a list of payment_hashes
@@ -1358,6 +1353,8 @@ impl ChannelManager {
13581353
/// still-available channels.
13591354
fn fail_htlc_backwards_internal(&self, mut channel_state: MutexGuard<ChannelHolder>, source: HTLCSource, payment_hash: &[u8; 32], onion_error: HTLCFailReason) {
13601355
match source {
1356+
//YK TODO: not sure this can happen here as htlc_bacwards_interanl is
1357+
//always dervied by forwarding process
13611358
HTLCSource::OutboundRoute { .. } => {
13621359
mem::drop(channel_state);
13631360

@@ -1778,7 +1775,113 @@ impl ChannelManager {
17781775
Ok(())
17791776
}
17801777

1778+
// Process onion peacket processed in only in the origin node. Returns update
1779+
// for router and boolean flag indicating if payment can be retried
1780+
fn process_onion_failure(&self, route: &Route, mut packet_decrypted: Vec<u8>, session_priv: &SecretKey) -> (Option<msgs::HTLCFailChannelUpdate>, bool) {
1781+
use ln::router::{BADONION, PERM, NODE, UPDATE, OnionFailureCode};
1782+
let mut res = None;
1783+
1784+
// Handle packed channel/node updates for passing back for the route handler
1785+
Self::construct_onion_keys_callback(&self.secp_ctx, &route, &session_priv, |shared_secret, _, _, route_hop| {
1786+
if res.is_some() { return; }
1787+
1788+
let ammag = ChannelManager::gen_ammag_from_shared_secret(&shared_secret);
1789+
1790+
let mut decryption_tmp = Vec::with_capacity(packet_decrypted.len());
1791+
decryption_tmp.resize(packet_decrypted.len(), 0);
1792+
let mut chacha = ChaCha20::new(&ammag, &[0u8; 8]);
1793+
chacha.process(&packet_decrypted, &mut decryption_tmp[..]);
1794+
packet_decrypted = decryption_tmp;
1795+
1796+
let is_from_final_node = route.hops.last().unwrap().pubkey == route_hop.pubkey;
1797+
1798+
match msgs::DecodedOnionErrorPacket::read(&mut Cursor::new(&packet_decrypted)) {
1799+
Err(e) => {
1800+
// TODO: what to do?
1801+
},
1802+
Ok(ref err_packet) if err_packet.failuremsg.len() < 2 => {
1803+
// TODO: what to do?
1804+
}
1805+
Ok(ref err_packet) if err_packet.failuremsg.len() >= 2 => {
1806+
let um = ChannelManager::gen_um_from_shared_secret(&shared_secret);
1807+
1808+
let mut hmac = Hmac::new(Sha256::new(), &um);
1809+
hmac.input(&err_packet.encode()[32..]);
1810+
let mut calc_tag = [0u8; 32];
1811+
hmac.raw_result(&mut calc_tag);
1812+
1813+
if crypto::util::fixed_time_eq(&calc_tag, &err_packet.hmac) {
1814+
let error_code = OnionFailureCode::from_code(byte_utils::slice_to_be16(&err_packet.failuremsg[0..2]));
1815+
if error_code.is_none() {
1816+
// bogus node sending invalid code
1817+
res = Some((Some(msgs::HTLCFailChannelUpdate::NodeFailure {
1818+
node_id: route_hop.pubkey,
1819+
is_permanent: true,
1820+
}), false));
1821+
return;
1822+
}
1823+
let error_code = error_code.unwrap();
1824+
1825+
if !error_code.is_from_valid_node(is_from_final_node) {
1826+
// final node sent an error code that it MUST not send or
1827+
// intermediate node sent an error code that it MUST not
1828+
// consider it as bogus
1829+
res = Some((Some(msgs::HTLCFailChannelUpdate::NodeFailure {
1830+
node_id: route_hop.pubkey,
1831+
is_permanent: true,
1832+
}), false));
1833+
return;
1834+
}
1835+
1836+
if is_from_final_node {
1837+
res = Some((None, !error_code.is_flag_set(PERM)));
1838+
return;
1839+
}
1840+
1841+
if error_code.is_flag_set(NODE) {
1842+
res = Some((Some(msgs::HTLCFailChannelUpdate::NodeFailure {
1843+
node_id: route_hop.pubkey,
1844+
is_permanent: error_code.is_flag_set(PERM),
1845+
}), !error_code.is_flag_set(PERM)));
1846+
} else if error_code.is_flag_set(UPDATE) {
1847+
// TODO: parse additinoal parameter and implement
1848+
// AND the channel_update is valid and more recent than the channel_update used to send the payment
1849+
// if channel_update should NOT have caused the failure:
1850+
// MAY treat the channel_update as invalid.
1851+
if err_packet.failuremsg.len() >= 4 {
1852+
let update_len = byte_utils::slice_to_be16(&err_packet.failuremsg[2..4]) as usize;
1853+
if err_packet.failuremsg.len() >= 4 + update_len {
1854+
if let Ok(chan_update) = msgs::ChannelUpdate::read(&mut Cursor::new(&err_packet.failuremsg[4..4 + update_len])) {
1855+
res = Some((Some(msgs::HTLCFailChannelUpdate::ChannelUpdateMessage {
1856+
msg: chan_update,
1857+
}), true));
1858+
return;
1859+
}
1860+
}
1861+
}
1862+
// malformed
1863+
res = Some((Some(msgs::HTLCFailChannelUpdate::NodeFailure {
1864+
node_id: route_hop.pubkey,
1865+
is_permanent: true,
1866+
}), false));
1867+
return;
1868+
} else {
1869+
debug_assert!(error_code.is_flag_set(PERM));
1870+
res = Some((Some(msgs::HTLCFailChannelUpdate::ChannelClosed {
1871+
short_channel_id: route_hop.short_channel_id,
1872+
is_permanent: true,
1873+
}), false));
1874+
}
1875+
}
1876+
},
1877+
_ => { unreachable!() }
1878+
}
1879+
});
1880+
res.unwrap()
1881+
}
1882+
17811883
fn internal_update_fail_htlc(&self, their_node_id: &PublicKey, msg: &msgs::UpdateFailHTLC) -> Result<Option<msgs::HTLCFailChannelUpdate>, MsgHandleErrInternal> {
1884+
17821885
let mut channel_state = self.channel_state.lock().unwrap();
17831886
let htlc_source = match channel_state.by_id.get_mut(&msg.channel_id) {
17841887
Some(chan) => {
@@ -1792,62 +1895,15 @@ impl ChannelManager {
17921895
None => return Err(MsgHandleErrInternal::send_err_msg_no_close("Failed to find corresponding channel", msg.channel_id))
17931896
}?;
17941897

1795-
match htlc_source {
1796-
&HTLCSource::OutboundRoute { ref route, ref session_priv, .. } => {
1797-
// Handle packed channel/node updates for passing back for the route handler
1798-
let mut packet_decrypted = msg.reason.data.clone();
1799-
let mut res = None;
1800-
Self::construct_onion_keys_callback(&self.secp_ctx, &route, &session_priv, |shared_secret, _, _, route_hop| {
1801-
if res.is_some() { return; }
1802-
1803-
let ammag = ChannelManager::gen_ammag_from_shared_secret(&shared_secret);
1804-
1805-
let mut decryption_tmp = Vec::with_capacity(packet_decrypted.len());
1806-
decryption_tmp.resize(packet_decrypted.len(), 0);
1807-
let mut chacha = ChaCha20::new(&ammag, &[0u8; 8]);
1808-
chacha.process(&packet_decrypted, &mut decryption_tmp[..]);
1809-
packet_decrypted = decryption_tmp;
1810-
1811-
if let Ok(err_packet) = msgs::DecodedOnionErrorPacket::read(&mut Cursor::new(&packet_decrypted)) {
1812-
if err_packet.failuremsg.len() >= 2 {
1813-
let um = ChannelManager::gen_um_from_shared_secret(&shared_secret);
1814-
1815-
let mut hmac = Hmac::new(Sha256::new(), &um);
1816-
hmac.input(&err_packet.encode()[32..]);
1817-
let mut calc_tag = [0u8; 32];
1818-
hmac.raw_result(&mut calc_tag);
1819-
if crypto::util::fixed_time_eq(&calc_tag, &err_packet.hmac) {
1820-
const UNKNOWN_CHAN: u16 = 0x4000|10;
1821-
const TEMP_CHAN_FAILURE: u16 = 0x4000|7;
1822-
match byte_utils::slice_to_be16(&err_packet.failuremsg[0..2]) {
1823-
TEMP_CHAN_FAILURE => {
1824-
if err_packet.failuremsg.len() >= 4 {
1825-
let update_len = byte_utils::slice_to_be16(&err_packet.failuremsg[2..4]) as usize;
1826-
if err_packet.failuremsg.len() >= 4 + update_len {
1827-
if let Ok(chan_update) = msgs::ChannelUpdate::read(&mut Cursor::new(&err_packet.failuremsg[4..4 + update_len])) {
1828-
res = Some(msgs::HTLCFailChannelUpdate::ChannelUpdateMessage {
1829-
msg: chan_update,
1830-
});
1831-
}
1832-
}
1833-
}
1834-
},
1835-
UNKNOWN_CHAN => {
1836-
// No such next-hop. We know this came from the
1837-
// current node as the HMAC validated.
1838-
res = Some(msgs::HTLCFailChannelUpdate::ChannelClosed {
1839-
short_channel_id: route_hop.short_channel_id
1840-
});
1841-
},
1842-
_ => {}, //TODO: Enumerate all of these!
1843-
}
1844-
}
1845-
}
1846-
}
1847-
}).unwrap();
1848-
Ok(res)
1849-
},
1850-
_ => { Ok(None) },
1898+
// we are the originating node and update route information
1899+
if let &HTLCSource::OutboundRoute { ref route, ref session_priv } = htlc_source {
1900+
let (update, _payment_retry) = self.process_onion_failure(route, msg.reason.data.clone(), session_priv);
1901+
if _payment_retry {
1902+
// TODO
1903+
}
1904+
Ok(update)
1905+
} else {
1906+
Ok(None)
18511907
}
18521908
}
18531909

@@ -4224,6 +4280,9 @@ mod tests {
42244280
assert_eq!(nodes[0].node.list_channels().len(), 0);
42254281
assert_eq!(nodes[1].node.list_channels().len(), 1);
42264282

4283+
let header = BlockHeader { version: 0x20000000, prev_blockhash: Default::default(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 };
4284+
nodes[4].chain_monitor.block_connected_with_filtering(&Block { header, txdata: vec![] }, 1);
4285+
42274286
// One pending HTLC is discarded by the force-close:
42284287
let payment_preimage_1 = route_payment(&nodes[1], &vec!(&nodes[2], &nodes[3])[..], 3000000).0;
42294288

@@ -4277,8 +4336,6 @@ mod tests {
42774336
assert_eq!(nodes[2].node.list_channels().len(), 0);
42784337
assert_eq!(nodes[3].node.list_channels().len(), 1);
42794338

4280-
let header = BlockHeader { version: 0x20000000, prev_blockhash: Default::default(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 };
4281-
nodes[4].chain_monitor.block_connected_with_filtering(&Block { header, txdata: vec![] }, 1);
42824339
// One pending HTLC to time out:
42834340
let payment_preimage_2 = route_payment(&nodes[3], &vec!(&nodes[4])[..], 3000000).0;
42844341

@@ -4844,7 +4901,7 @@ mod tests {
48444901
let as_chan = a_channel_lock.by_id.get(&chan_announcement.3).unwrap();
48454902
let bs_chan = b_channel_lock.by_id.get(&chan_announcement.3).unwrap();
48464903

4847-
let _ = nodes[0].router.handle_htlc_fail_channel_update(&msgs::HTLCFailChannelUpdate::ChannelClosed { short_channel_id : as_chan.get_short_channel_id().unwrap() } );
4904+
let _ = nodes[0].router.handle_htlc_fail_channel_update(&msgs::HTLCFailChannelUpdate::ChannelClosed { short_channel_id : as_chan.get_short_channel_id().unwrap(), is_permanent: false } );
48484905

48494906
let as_bitcoin_key = PublicKey::from_secret_key(&secp_ctx, &as_chan.get_local_keys().funding_key);
48504907
let bs_bitcoin_key = PublicKey::from_secret_key(&secp_ctx, &bs_chan.get_local_keys().funding_key);
@@ -4891,7 +4948,7 @@ mod tests {
48914948
let unsigned_msg = dummy_unsigned_msg!();
48924949
sign_msg!(unsigned_msg);
48934950
assert_eq!(nodes[0].router.handle_channel_announcement(&chan_announcement).unwrap(), true);
4894-
let _ = nodes[0].router.handle_htlc_fail_channel_update(&msgs::HTLCFailChannelUpdate::ChannelClosed { short_channel_id : as_chan.get_short_channel_id().unwrap() } );
4951+
let _ = nodes[0].router.handle_htlc_fail_channel_update(&msgs::HTLCFailChannelUpdate::ChannelClosed { short_channel_id : as_chan.get_short_channel_id().unwrap(), is_permanent: false } );
48954952

48964953
// Configured with Network::Testnet
48974954
let mut unsigned_msg = dummy_unsigned_msg!();

src/ln/msgs.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -484,7 +484,19 @@ pub enum HTLCFailChannelUpdate {
484484
ChannelClosed {
485485
/// The short_channel_id which has now closed.
486486
short_channel_id: u64,
487+
/// when this true, this channel should be permanently removed from the
488+
/// consideration. Otherwise, this channel can be restored as new channel_update is received
489+
is_permanent: bool,
487490
},
491+
/// We received an error which indicated only that a node has failed
492+
NodeFailure {
493+
/// The node_id that has failed.
494+
node_id: PublicKey,
495+
/// when this true, node should be permanently removed from the
496+
/// consideration. Otherwise, the channels connected to this node can be
497+
/// restored as new channel_update is received
498+
is_permanent: bool,
499+
}
488500
}
489501

490502
/// A trait to describe an object which can receive channel messages.

0 commit comments

Comments
 (0)