Skip to content

Commit d7f04d8

Browse files
author
Antoine Riard
committed
Add timeout detection in block_connected to fail htlc
backward Add test for onchain resolution of channel with a timeout, passing expiration backward
1 parent db555c9 commit d7f04d8

File tree

1 file changed

+116
-7
lines changed

1 file changed

+116
-7
lines changed

src/ln/channelmanager.rs

Lines changed: 116 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2005,13 +2005,17 @@ impl ChainListener for ChannelManager {
20052005
for (arr, vec) in payment_preimage.iter_mut().zip(tx.input[0].witness[3].iter()) {
20062006
*arr = *vec;
20072007
}
2008-
hash_to_remove.push((payment_preimage, htlc_with_hash.clone()));
2008+
hash_to_remove.push((Some(payment_preimage), htlc_with_hash.clone()));
20092009
} else if tx.input.len() > 0 && tx.input[0].witness.len() == 3 && tx.input[0].witness[2].len() == 133 && payment_hash160 == tx.input[0].witness[2][109..129] {
20102010
let mut payment_preimage = [0; 32];
20112011
for (arr, vec) in payment_preimage.iter_mut().zip(tx.input[0].witness[1].iter()) {
20122012
*arr = *vec;
20132013
}
2014-
hash_to_remove.push((payment_preimage, htlc_with_hash.clone()));
2014+
hash_to_remove.push((Some(payment_preimage), htlc_with_hash.clone()));
2015+
} else if tx.input.len() > 0 && tx.input[0].witness.len() == 5 && tx.input[0].witness[4].len() == 133 && payment_hash160 == tx.input[0].witness[4][109..129] {
2016+
hash_to_remove.push((None, htlc_with_hash.clone()));
2017+
} else if tx.input.len() > 0 && tx.input[0].witness.len() == 3 && tx.input[0].witness[2].len() == 138 && payment_hash160 == tx.input[0].witness[2][69..89] {
2018+
hash_to_remove.push((None, htlc_with_hash.clone()));
20152019
}
20162020
}
20172021
}
@@ -2022,11 +2026,20 @@ impl ChainListener for ChannelManager {
20222026

20232027
{
20242028
let mut channel_state = Some(self.channel_state.lock().unwrap());
2025-
for (preimage, hash) in hash_to_remove {
2026-
if channel_state.is_none() { channel_state = Some(self.channel_state.lock().unwrap());}
2027-
if let Some(mut entry) = channel_state.as_mut().unwrap().claimable_htlcs.remove_entry(&hash) {
2028-
for source in entry.1.drain(..) {
2029-
self.claim_funds_internal(channel_state.take().unwrap(), HTLCSource::PreviousHopData(source), preimage);
2029+
for (payment_preimage, payment_hash) in hash_to_remove {
2030+
if let Some(preimage) = payment_preimage {
2031+
if channel_state.is_none() { channel_state = Some(self.channel_state.lock().unwrap());}
2032+
if let Some(mut entry) = channel_state.as_mut().unwrap().claimable_htlcs.remove(&payment_hash) {
2033+
for source in entry.drain(..) {
2034+
self.claim_funds_internal(channel_state.take().unwrap(), HTLCSource::PreviousHopData(source), preimage);
2035+
}
2036+
}
2037+
} else {
2038+
if channel_state.is_none() { channel_state = Some(self.channel_state.lock().unwrap());}
2039+
if let Some(mut entry) = channel_state.as_mut().unwrap().claimable_htlcs.remove(&payment_hash) {
2040+
for source in entry.drain(..) {
2041+
self.fail_htlc_backwards_internal(channel_state.take().unwrap(), HTLCSource::PreviousHopData(source), &payment_hash, HTLCFailReason::Reason { failure_code: 0x1000 | 14, data: Vec::new() });
2042+
}
20302043
}
20312044
}
20322045
}
@@ -3569,6 +3582,102 @@ mod tests {
35693582
}
35703583
}
35713584

3585+
#[test]
3586+
fn test_htlc_on_chain_timeout() {
3587+
// Test that in case of an unilateral close onchain, we detect the state of output thanks to
3588+
// ChainWatchInterface and timeout the HTLC bacward accordingly. So here we test that ChannelManager is
3589+
// broadcasting the right event to other nodes in payment path.
3590+
// A ------------------> B ----------------------> C (timeout)
3591+
// A's commitment tx C's commitment tx
3592+
// \ \
3593+
// B's HTLC timeout tx B's timeout tx
3594+
3595+
let nodes = create_network(3);
3596+
3597+
// Create some intial channels
3598+
let chan_1 = create_announced_chan_between_nodes(&nodes, 0, 1);
3599+
let chan_2 = create_announced_chan_between_nodes(&nodes, 1, 2);
3600+
3601+
// Rebalance the network a bit by relaying one payment thorugh all the channels...
3602+
send_payment(&nodes[0], &vec!(&nodes[1], &nodes[2])[..], 8000000);
3603+
send_payment(&nodes[0], &vec!(&nodes[1], &nodes[2])[..], 8000000);
3604+
3605+
let (payment_preimage, payment_hash) = route_payment(&nodes[0], &vec!(&nodes[1], &nodes[2]), 3000000);
3606+
let header = BlockHeader { version: 0x20000000, prev_blockhash: Default::default(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42};
3607+
3608+
// Brodacast legit commitment tx from C on B's chain
3609+
let commitment_tx = nodes[2].node.channel_state.lock().unwrap().by_id.get(&chan_2.2).unwrap().last_local_commitment_txn.clone();
3610+
nodes[2].node.fail_htlc_backwards(&payment_hash);
3611+
{
3612+
let mut added_monitors = nodes[2].chan_monitor.added_monitors.lock().unwrap();
3613+
assert_eq!(added_monitors.len(), 1);
3614+
added_monitors.clear();
3615+
}
3616+
let events = nodes[2].node.get_and_clear_pending_events();
3617+
assert_eq!(events.len(), 1);
3618+
match events[0] {
3619+
Event::UpdateHTLCs { ref node_id, updates: msgs::CommitmentUpdate { ref update_add_htlcs, ref update_fulfill_htlcs, ref update_fail_htlcs, ref update_fail_malformed_htlcs, .. } } => {
3620+
assert!(update_add_htlcs.is_empty());
3621+
assert!(!update_fail_htlcs.is_empty());
3622+
assert!(update_fulfill_htlcs.is_empty());
3623+
assert!(update_fail_malformed_htlcs.is_empty());
3624+
assert_eq!(nodes[1].node.get_our_node_id(), *node_id);
3625+
},
3626+
_ => panic!("Unexpected event"),
3627+
};
3628+
nodes[2].chain_monitor.block_connected_with_filtering(&Block { header, txdata: vec![commitment_tx[0].clone()]}, 1);
3629+
let events = nodes[2].node.get_and_clear_pending_events();
3630+
assert_eq!(events.len(), 1);
3631+
match events[0] {
3632+
Event::BroadcastChannelUpdate { msg: msgs::ChannelUpdate { .. } } => {},
3633+
_ => panic!("Unexpected event"),
3634+
}
3635+
let node_txn = nodes[2].tx_broadcaster.txn_broadcasted.lock().unwrap().clone();
3636+
3637+
// Broadcast timeout transaction by B on received output fron C's commitment tx on B's chain
3638+
// Verify that B's ChannelManager is able to detect that HTLC is timeout by its own tx and react backward in consequence
3639+
nodes[1].chain_monitor.block_connected_with_filtering(&Block { header, txdata: node_txn}, 200);
3640+
let node_txn = nodes[1].tx_broadcaster.txn_broadcasted.lock().unwrap().clone(); //TODO: shouldn't be a HTLC-Timeout but actually that's one... => implement lacking parts in check_spend_remote_transaction
3641+
nodes[1].chain_monitor.block_connected_with_filtering(&Block { header, txdata: node_txn}, 1);
3642+
let events = nodes[1].node.get_and_clear_pending_events();
3643+
assert_eq!(events.len(), 2);
3644+
match events[0] {
3645+
Event::BroadcastChannelUpdate { msg: msgs::ChannelUpdate { .. } } => {},
3646+
_ => panic!("Unexpected event"),
3647+
}
3648+
match events[1] {
3649+
Event::UpdateHTLCs { ref node_id, updates: msgs::CommitmentUpdate { ref update_add_htlcs, ref update_fail_htlcs, ref update_fulfill_htlcs, ref update_fail_malformed_htlcs, .. } } => {
3650+
assert!(update_add_htlcs.is_empty());
3651+
assert!(!update_fail_htlcs.is_empty());
3652+
assert!(update_fulfill_htlcs.is_empty());
3653+
assert!(update_fail_malformed_htlcs.is_empty());
3654+
assert_eq!(nodes[0].node.get_our_node_id(), *node_id);
3655+
},
3656+
_ => panic!("Unexpected event"),
3657+
};
3658+
3659+
// Broadcast legit commitment tx from A on B's chain
3660+
// Broadcast HTLC Timeout tx by B on offered output from A commitment tx on A's chain
3661+
let commitment_tx = nodes[0].node.channel_state.lock().unwrap().by_id.get(&chan_1.2).unwrap().last_local_commitment_txn.clone();
3662+
nodes[1].chain_monitor.block_connected_with_filtering(&Block { header, txdata: vec![commitment_tx[0].clone()]}, 1);
3663+
let events = nodes[1].node.get_and_clear_pending_events();
3664+
assert_eq!(events.len(), 1);
3665+
match events[0] {
3666+
Event::BroadcastChannelUpdate { msg: msgs::ChannelUpdate { .. } } => {},
3667+
_ => panic!("Unexpected event"),
3668+
}
3669+
let node_txn = nodes[1].tx_broadcaster.txn_broadcasted.lock().unwrap().clone();
3670+
3671+
// Verify that A's ChannelManager is able to detect that HTLC is timeout by a HTLC Timeout tx and react backward in consequence
3672+
nodes[0].chain_monitor.block_connected_with_filtering(&Block { header, txdata: node_txn }, 1);
3673+
let events = nodes[0].node.get_and_clear_pending_events();
3674+
assert_eq!(events.len(), 1);
3675+
match events[0] {
3676+
Event::BroadcastChannelUpdate { msg: msgs::ChannelUpdate { .. } } => {},
3677+
_ => panic!("Unexpected event"),
3678+
}
3679+
}
3680+
35723681
fn test_htlc_ignore_latest_remote_commitment() {
35733682
// Test that HTLC transactions spending the latest remote commitment transaction are simply
35743683
// ignored if we cannot claim them. This originally tickled an invalid unwrap().

0 commit comments

Comments
 (0)