Skip to content

Commit 0e18482

Browse files
author
Antoine Riard
committed
Implement fail backward in case of detection of revoked tx
Refactor block_connected to ease output resolution Add test_commitment_revoked_fail_backward Close #137
1 parent 72eabd2 commit 0e18482

File tree

2 files changed

+74
-23
lines changed

2 files changed

+74
-23
lines changed

src/ln/channelmanager.rs

Lines changed: 60 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2058,7 +2058,6 @@ impl ChainListener for ChannelManager {
20582058
fn block_connected(&self, header: &BlockHeader, height: u32, txn_matched: &[&Transaction], indexes_of_txn_matched: &[u32]) {
20592059
let mut new_events = Vec::new();
20602060
let mut failed_channels = Vec::new();
2061-
let mut hash_to_remove = Vec::new();
20622061
{
20632062
let mut channel_lock = self.channel_state.lock().unwrap();
20642063
let channel_state = channel_lock.borrow_parts();
@@ -2122,33 +2121,25 @@ impl ChainListener for ChannelManager {
21222121
true
21232122
});
21242123

2125-
for tx in txn_matched {
2126-
if let Some(payments_data) = self.monitor.is_resolving_output(&tx) {
2127-
for payment_data in payments_data {
2128-
hash_to_remove.push((payment_data.0, payment_data.1));
2129-
}
2130-
}
2131-
}
21322124
}
21332125
for failure in failed_channels.drain(..) {
21342126
self.finish_force_close_channel(failure);
21352127
}
21362128

21372129
{
21382130
let mut channel_state = Some(self.channel_state.lock().unwrap());
2139-
for (payment_preimage, payment_hash) in hash_to_remove {
2140-
if let Some(preimage) = payment_preimage {
2141-
if channel_state.is_none() { channel_state = Some(self.channel_state.lock().unwrap());}
2142-
if let Some(mut entry) = channel_state.as_mut().unwrap().claimable_htlcs.remove(&payment_hash) {
2143-
for source in entry.drain(..) {
2144-
self.claim_funds_internal(channel_state.take().unwrap(), HTLCSource::PreviousHopData(source), preimage);
2145-
}
2146-
}
2147-
} else {
2148-
if channel_state.is_none() { channel_state = Some(self.channel_state.lock().unwrap());}
2149-
if let Some(mut entry) = channel_state.as_mut().unwrap().claimable_htlcs.remove(&payment_hash) {
2150-
for source in entry.drain(..) {
2151-
self.fail_htlc_backwards_internal(channel_state.take().unwrap(), HTLCSource::PreviousHopData(source), &payment_hash, HTLCFailReason::Reason { failure_code: 0x1000 | 14, data: Vec::new() });
2131+
for tx in txn_matched {
2132+
if let Some(payments_data) = self.monitor.is_resolving_output(&tx) {
2133+
for payment_data in payments_data {
2134+
if channel_state.is_none() { channel_state = Some(self.channel_state.lock().unwrap()); }
2135+
if let Some(mut entry) = channel_state.as_mut().unwrap().claimable_htlcs.remove(&payment_data.1) {
2136+
for source in entry.drain(..) {
2137+
if let Some(preimage) = payment_data.0 {
2138+
self.claim_funds_internal(channel_state.take().unwrap(), HTLCSource::PreviousHopData(source), preimage);
2139+
} else {
2140+
self.fail_htlc_backwards_internal(channel_state.take().unwrap(), HTLCSource::PreviousHopData(source), &payment_data.1, HTLCFailReason::Reason { failure_code: 0x1000 | 14, data: Vec::new() });
2141+
}
2142+
}
21522143
}
21532144
}
21542145
}
@@ -4523,6 +4514,54 @@ mod tests {
45234514
assert_eq!(node_txn[2].clone().input[0].witness.last().unwrap().len(), 133);
45244515
}
45254516

4517+
#[test]
4518+
fn test_commitment_revoked_fail_backward() {
4519+
// Test that in case of a revoked commitment tx, we detect the resolution of output by justice tx
4520+
// and fail backward accordingly.
4521+
4522+
let nodes = create_network(3);
4523+
4524+
// Create some initial channels
4525+
create_announced_chan_between_nodes(&nodes, 0, 1);
4526+
let chan_2 = create_announced_chan_between_nodes(&nodes, 1, 2);
4527+
4528+
// Rebalance the network a bit by relaying one payment through all the channels...
4529+
send_payment(&nodes[0], &vec!(&nodes[1], &nodes[2])[..], 8000000);
4530+
send_payment(&nodes[0], &vec!(&nodes[1], &nodes[2])[..], 8000000);
4531+
4532+
let (payment_preimage, _payment_hash) = route_payment(&nodes[0], &vec!(&nodes[1], &nodes[2])[..], 3000000);
4533+
// Get the will-be-revoked local txn from nodes[2]
4534+
let revoked_local_txn = nodes[2].node.channel_state.lock().unwrap().by_id.get(&chan_2.2).unwrap().last_local_commitment_txn.clone();
4535+
// Revoke the old state
4536+
claim_payment(&nodes[0], &vec!(&nodes[1], &nodes[2])[..], payment_preimage);
4537+
4538+
route_payment(&nodes[0], &vec!(&nodes[1], &nodes[2])[..], 3000000);
4539+
4540+
let header = BlockHeader { version: 0x20000000, prev_blockhash: Default::default(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42};
4541+
nodes[1].chain_monitor.block_connected_with_filtering(&Block { header, txdata: vec![revoked_local_txn[0].clone()] }, 1);
4542+
{
4543+
let mut added_monitors = nodes[1].chan_monitor.added_monitors.lock().unwrap();
4544+
assert_eq!(added_monitors.len(), 1);
4545+
added_monitors.clear();
4546+
}
4547+
let events = nodes[1].node.get_and_clear_pending_events();
4548+
assert_eq!(events.len(), 2);
4549+
match events[0] {
4550+
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, .. } } => {
4551+
assert!(update_add_htlcs.is_empty());
4552+
assert!(!update_fail_htlcs.is_empty());
4553+
assert!(update_fulfill_htlcs.is_empty());
4554+
assert!(update_fail_malformed_htlcs.is_empty());
4555+
assert_eq!(nodes[0].node.get_our_node_id(), *node_id);
4556+
},
4557+
_ => panic!("Unexpected event"),
4558+
}
4559+
match events[1] {
4560+
Event::BroadcastChannelUpdate { msg: msgs::ChannelUpdate { .. } } => {},
4561+
_ => panic!("Unexpected event"),
4562+
}
4563+
}
4564+
45264565
#[test]
45274566
fn test_htlc_ignore_latest_remote_commitment() {
45284567
// Test that HTLC transactions spending the latest remote commitment transaction are simply

src/ln/channelmonitor.rs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,8 @@ pub trait ManyChannelMonitor: Send + Sync {
7070
/// ChainWatchInterfaces such that the provided monitor receives block_connected callbacks with
7171
/// any spends of it.
7272
fn add_update_monitor(&self, funding_txo: OutPoint, monitor: ChannelMonitor) -> Result<(), ChannelMonitorUpdateErr>;
73-
/// In case of commitment tx broadcasted onchain, we need to detect resolution of its outputs by a success tx
74-
/// (HTLC-Success or preimage tx) and extract preimages from them to claim backward inbound htlcs in others channels
73+
/// In case of commitment tx broadcasted onchain, we need to detect resolution of its outputs by a success tx, a timeout tx
74+
/// or if it's a revoked one and pass state backward to advance inbound htlcs in others channels
7575
/// belonging to same payment route
7676
fn is_resolving_output(&self, tx: &Transaction) -> Option<Vec<(Option<[u8;32]>, [u8;32])>>;
7777
}
@@ -143,6 +143,7 @@ impl<Key : Send + cmp::Eq + hash::Hash + 'static> SimpleManyChannelMonitor<Key>
143143
monitors.insert(key, monitor);
144144
Ok(())
145145
}
146+
146147
}
147148

148149
impl ManyChannelMonitor for SimpleManyChannelMonitor<OutPoint> {
@@ -1283,6 +1284,16 @@ impl ChannelMonitor {
12831284
pub(crate) fn is_resolving_output(&self, tx: &Transaction) -> Option<Vec<(Option<[u8;32]>, [u8;32])>> {
12841285
let mut hash_to_remove = Vec::new();
12851286
if tx.input.len() > 0 {
1287+
let commitment_number = 0xffffffffffff - ((((tx.input[0].sequence as u64 & 0xffffff) << 3*8) | (tx.lock_time as u64 & 0xffffff)) ^ self.commitment_transaction_number_obscure_factor);
1288+
if commitment_number >= self.get_min_seen_secret() {
1289+
if let Some(ref local_commitment_tx) = self.current_local_signed_commitment_tx {
1290+
for &(ref htlc_output, _, _) in &local_commitment_tx.htlc_outputs {
1291+
if htlc_output.offered {
1292+
hash_to_remove.push((None, htlc_output.payment_hash.clone()));
1293+
}
1294+
}
1295+
}
1296+
}
12861297
for input in &tx.input {
12871298
let mut payment_data = (None, None);
12881299
if let Some(ref current_local_signed_commitment_tx) = self.current_local_signed_commitment_tx {
@@ -1335,6 +1346,7 @@ impl ChannelMonitor {
13351346
}
13361347
None
13371348
}
1349+
13381350
}
13391351

13401352
impl<R: ::std::io::Read> Readable<R> for ChannelMonitor {

0 commit comments

Comments
 (0)