Skip to content

Commit f829417

Browse files
author
Antoine Riard
committed
Implement fail backward in case of detection of revoked tx
Add test_commitment_revoked_fail_backward Close #137
1 parent aed435a commit f829417

File tree

3 files changed

+94
-0
lines changed

3 files changed

+94
-0
lines changed

src/ln/channelmanager.rs

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2147,6 +2147,10 @@ impl ChainListener for ChannelManager {
21472147
hash_to_remove.push((None, htlc_with_hash.clone()));
21482148
} 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] {
21492149
hash_to_remove.push((None, htlc_with_hash.clone()));
2150+
} else if let Some(hashs) = self.monitor.is_revoked_tx(&tx) {
2151+
for hash in hashs {
2152+
hash_to_remove.push((None, hash));
2153+
}
21502154
}
21512155
}
21522156
}
@@ -4544,6 +4548,54 @@ mod tests {
45444548
assert_eq!(node_txn[2].clone().input[0].witness.last().unwrap().len(), 133);
45454549
}
45464550

4551+
#[test]
4552+
fn test_commitment_revoked_fail_backward() {
4553+
// Test that in case of a revoked commitment tx, we detect the resolution of output by justice tx
4554+
// and fail backward accordingly.
4555+
4556+
let nodes = create_network(3);
4557+
4558+
// Create some initial channels
4559+
create_announced_chan_between_nodes(&nodes, 0, 1);
4560+
let chan_2 = create_announced_chan_between_nodes(&nodes, 1, 2);
4561+
4562+
// Rebalance the network a bit by relaying one payment through all the channels...
4563+
send_payment(&nodes[0], &vec!(&nodes[1], &nodes[2])[..], 8000000);
4564+
send_payment(&nodes[0], &vec!(&nodes[1], &nodes[2])[..], 8000000);
4565+
4566+
let (payment_preimage, _payment_hash) = route_payment(&nodes[0], &vec!(&nodes[1], &nodes[2])[..], 3000000);
4567+
// Get the will-be-revoked local txn from nodes[2]
4568+
let revoked_local_txn = nodes[2].node.channel_state.lock().unwrap().by_id.get(&chan_2.2).unwrap().last_local_commitment_txn.clone();
4569+
// Revoke the old state
4570+
claim_payment(&nodes[0], &vec!(&nodes[1], &nodes[2])[..], payment_preimage);
4571+
4572+
route_payment(&nodes[0], &vec!(&nodes[1], &nodes[2])[..], 3000000);
4573+
4574+
let header = BlockHeader { version: 0x20000000, prev_blockhash: Default::default(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42};
4575+
nodes[1].chain_monitor.block_connected_with_filtering(&Block { header, txdata: vec![revoked_local_txn[0].clone()] }, 1);
4576+
{
4577+
let mut added_monitors = nodes[1].chan_monitor.added_monitors.lock().unwrap();
4578+
assert_eq!(added_monitors.len(), 1);
4579+
added_monitors.clear();
4580+
}
4581+
let events = nodes[1].node.get_and_clear_pending_events();
4582+
assert_eq!(events.len(), 2);
4583+
match events[0] {
4584+
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, .. } } => {
4585+
assert!(update_add_htlcs.is_empty());
4586+
assert!(!update_fail_htlcs.is_empty());
4587+
assert!(update_fulfill_htlcs.is_empty());
4588+
assert!(update_fail_malformed_htlcs.is_empty());
4589+
assert_eq!(nodes[0].node.get_our_node_id(), *node_id);
4590+
},
4591+
_ => panic!("Unexpected event"),
4592+
}
4593+
match events[1] {
4594+
Event::BroadcastChannelUpdate { msg: msgs::ChannelUpdate { .. } } => {},
4595+
_ => panic!("Unexpected event"),
4596+
}
4597+
}
4598+
45474599
#[test]
45484600
fn test_htlc_ignore_latest_remote_commitment() {
45494601
// Test that HTLC transactions spending the latest remote commitment transaction are simply

src/ln/channelmonitor.rs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,9 @@ 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 revoked commitment tx broadcasted by remote, we need to get current htlc offered to them on their
74+
/// last valid commitment tx to fail backward inbound htlcs in others channels belonging to payment route
75+
fn is_revoked_tx(&self, tx: &Transaction) -> Option<Vec<[u8;32]>>;
7376
}
7477

7578
/// A simple implementation of a ManyChannelMonitor and ChainListener. Can be used to create a
@@ -139,6 +142,7 @@ impl<Key : Send + cmp::Eq + hash::Hash + 'static> SimpleManyChannelMonitor<Key>
139142
monitors.insert(key, monitor);
140143
Ok(())
141144
}
145+
142146
}
143147

144148
impl ManyChannelMonitor for SimpleManyChannelMonitor<OutPoint> {
@@ -148,6 +152,20 @@ impl ManyChannelMonitor for SimpleManyChannelMonitor<OutPoint> {
148152
Err(_) => Err(ChannelMonitorUpdateErr::PermanentFailure),
149153
}
150154
}
155+
156+
fn is_revoked_tx(&self, tx: &Transaction) -> Option<Vec<[u8;32]>> {
157+
let monitors = self.monitors.lock().unwrap();
158+
let mut hash_to_remove = Vec::new();
159+
for monitor in monitors.iter() {
160+
if let Some(mut hashs) = monitor.1.is_revoked_tx(tx) {
161+
hash_to_remove.append(&mut hashs);
162+
}
163+
}
164+
if hash_to_remove.len() > 0 {
165+
return Some(hash_to_remove)
166+
}
167+
None
168+
}
151169
}
152170

153171
/// If an HTLC expires within this many blocks, don't try to claim it in a shared transaction,
@@ -1265,6 +1283,26 @@ impl ChannelMonitor {
12651283
}
12661284
false
12671285
}
1286+
1287+
pub(crate) fn is_revoked_tx(&self, tx: &Transaction) -> Option<Vec<[u8;32]>> {
1288+
let mut hash_to_remove = Vec::new();
1289+
if tx.input.len() > 0 {
1290+
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);
1291+
if commitment_number >= self.get_min_seen_secret() {
1292+
if let Some(ref local_commitment_tx) = self.current_local_signed_commitment_tx {
1293+
for &(ref htlc_output, _, _) in &local_commitment_tx.htlc_outputs {
1294+
if htlc_output.offered {
1295+
hash_to_remove.push(htlc_output.payment_hash.clone());
1296+
}
1297+
}
1298+
if hash_to_remove.len() > 0 {
1299+
return Some(hash_to_remove);
1300+
}
1301+
}
1302+
}
1303+
}
1304+
None
1305+
}
12681306
}
12691307

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

src/util/test_utils.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,10 @@ impl channelmonitor::ManyChannelMonitor for TestChannelMonitor {
5959
self.added_monitors.lock().unwrap().push((funding_txo, monitor.clone()));
6060
self.simple_monitor.add_update_monitor(funding_txo, monitor)
6161
}
62+
63+
fn is_revoked_tx(&self, tx: &Transaction) -> Option<Vec<[u8;32]>> {
64+
return self.simple_monitor.is_revoked_tx(tx);
65+
}
6266
}
6367

6468
pub struct TestBroadcaster {

0 commit comments

Comments
 (0)