Skip to content

Commit 8b2d14e

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 lightningdevkit#137
1 parent bbf99aa commit 8b2d14e

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
@@ -2517,7 +2517,6 @@ impl ChainListener for ChannelManager {
25172517
fn block_connected(&self, header: &BlockHeader, height: u32, txn_matched: &[&Transaction], indexes_of_txn_matched: &[u32]) {
25182518
let _ = self.total_consistency_lock.read().unwrap();
25192519
let mut failed_channels = Vec::new();
2520-
let mut hash_to_remove = Vec::new();
25212520
{
25222521
let mut channel_lock = self.channel_state.lock().unwrap();
25232522
let channel_state = channel_lock.borrow_parts();
@@ -2586,32 +2585,24 @@ impl ChainListener for ChannelManager {
25862585
true
25872586
});
25882587

2589-
for tx in txn_matched {
2590-
if let Some(payments_data) = self.monitor.is_resolving_output(&tx) {
2591-
for payment_data in payments_data {
2592-
hash_to_remove.push((payment_data.0, payment_data.1));
2593-
}
2594-
}
2595-
}
25962588
}
25972589
for failure in failed_channels.drain(..) {
25982590
self.finish_force_close_channel(failure);
25992591
}
26002592
{
26012593
let mut channel_state = Some(self.channel_state.lock().unwrap());
2602-
for (payment_preimage, payment_hash) in hash_to_remove {
2603-
if let Some(preimage) = payment_preimage {
2604-
if channel_state.is_none() { channel_state = Some(self.channel_state.lock().unwrap());}
2605-
if let Some(mut entry) = channel_state.as_mut().unwrap().claimable_htlcs.remove(&payment_hash) {
2606-
for source in entry.drain(..) {
2607-
self.claim_funds_internal(channel_state.take().unwrap(), HTLCSource::PreviousHopData(source), preimage);
2608-
}
2609-
}
2610-
} else {
2611-
if channel_state.is_none() { channel_state = Some(self.channel_state.lock().unwrap());}
2612-
if let Some(mut entry) = channel_state.as_mut().unwrap().claimable_htlcs.remove(&payment_hash) {
2613-
for source in entry.drain(..) {
2614-
self.fail_htlc_backwards_internal(channel_state.take().unwrap(), HTLCSource::PreviousHopData(source), &payment_hash, HTLCFailReason::Reason { failure_code: 0x1000 | 14, data: Vec::new() });
2594+
for tx in txn_matched {
2595+
if let Some(payments_data) = self.monitor.is_resolving_output(&tx) {
2596+
for payment_data in payments_data {
2597+
if channel_state.is_none() { channel_state = Some(self.channel_state.lock().unwrap()); }
2598+
if let Some(mut entry) = channel_state.as_mut().unwrap().claimable_htlcs.remove(&payment_data.1) {
2599+
for source in entry.drain(..) {
2600+
if let Some(preimage) = payment_data.0 {
2601+
self.claim_funds_internal(channel_state.take().unwrap(), HTLCSource::PreviousHopData(source), preimage);
2602+
} else {
2603+
self.fail_htlc_backwards_internal(channel_state.take().unwrap(), HTLCSource::PreviousHopData(source), &payment_data.1, HTLCFailReason::Reason { failure_code: 0x1000 | 14, data: Vec::new() });
2604+
}
2605+
}
26152606
}
26162607
}
26172608
}
@@ -6276,6 +6267,54 @@ mod tests {
62766267
assert_eq!(node_txn[2].clone().input[0].witness.last().unwrap().len(), 133);
62776268
}
62786269

6270+
#[test]
6271+
fn test_commitment_revoked_fail_backward() {
6272+
// Test that in case of a revoked commitment tx, we detect the resolution of output by justice tx
6273+
// and fail backward accordingly.
6274+
6275+
let nodes = create_network(3);
6276+
6277+
// Create some initial channels
6278+
create_announced_chan_between_nodes(&nodes, 0, 1);
6279+
let chan_2 = create_announced_chan_between_nodes(&nodes, 1, 2);
6280+
6281+
// Rebalance the network a bit by relaying one payment through all the channels...
6282+
send_payment(&nodes[0], &vec!(&nodes[1], &nodes[2])[..], 8000000);
6283+
send_payment(&nodes[0], &vec!(&nodes[1], &nodes[2])[..], 8000000);
6284+
6285+
let (payment_preimage, _payment_hash) = route_payment(&nodes[0], &vec!(&nodes[1], &nodes[2])[..], 3000000);
6286+
// Get the will-be-revoked local txn from nodes[2]
6287+
let revoked_local_txn = nodes[2].node.channel_state.lock().unwrap().by_id.get(&chan_2.2).unwrap().last_local_commitment_txn.clone();
6288+
// Revoke the old state
6289+
claim_payment(&nodes[0], &vec!(&nodes[1], &nodes[2])[..], payment_preimage);
6290+
6291+
route_payment(&nodes[0], &vec!(&nodes[1], &nodes[2])[..], 3000000);
6292+
6293+
let header = BlockHeader { version: 0x20000000, prev_blockhash: Default::default(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42};
6294+
nodes[1].chain_monitor.block_connected_with_filtering(&Block { header, txdata: vec![revoked_local_txn[0].clone()] }, 1);
6295+
{
6296+
let mut added_monitors = nodes[1].chan_monitor.added_monitors.lock().unwrap();
6297+
assert_eq!(added_monitors.len(), 1);
6298+
added_monitors.clear();
6299+
}
6300+
let events = nodes[1].node.get_and_clear_pending_msg_events();
6301+
assert_eq!(events.len(), 2);
6302+
match events[0] {
6303+
MessageSendEvent::BroadcastChannelUpdate { msg: msgs::ChannelUpdate { .. } } => {},
6304+
_ => panic!("Unexpected event"),
6305+
}
6306+
match events[1] {
6307+
MessageSendEvent::UpdateHTLCs { ref node_id, updates: msgs::CommitmentUpdate { ref update_add_htlcs, ref update_fail_htlcs, ref update_fulfill_htlcs, ref update_fail_malformed_htlcs, .. } } => {
6308+
assert!(update_add_htlcs.is_empty());
6309+
assert!(!update_fail_htlcs.is_empty());
6310+
assert!(update_fulfill_htlcs.is_empty());
6311+
assert!(update_fail_malformed_htlcs.is_empty());
6312+
assert_eq!(nodes[0].node.get_our_node_id(), *node_id);
6313+
},
6314+
_ => panic!("Unexpected event"),
6315+
}
6316+
}
6317+
62796318
#[test]
62806319
fn test_htlc_ignore_latest_remote_commitment() {
62816320
// 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
@@ -89,8 +89,8 @@ pub trait ManyChannelMonitor: Send + Sync {
8989
/// ChainWatchInterfaces such that the provided monitor receives block_connected callbacks with
9090
/// any spends of it.
9191
fn add_update_monitor(&self, funding_txo: OutPoint, monitor: ChannelMonitor) -> Result<(), ChannelMonitorUpdateErr>;
92-
/// In case of commitment tx broadcasted onchain, we need to detect resolution of its outputs by a success tx
93-
/// (HTLC-Success or preimage tx) and extract preimages from them to claim backward inbound htlcs in others channels
92+
/// In case of commitment tx broadcasted onchain, we need to detect resolution of its outputs by a success tx, a timeout tx
93+
/// or if it's a revoked one and pass state backward to advance inbound htlcs in others channels
9494
/// belonging to same payment route
9595
fn is_resolving_output(&self, tx: &Transaction) -> Option<Vec<(Option<[u8;32]>, [u8;32])>>;
9696
}
@@ -184,6 +184,7 @@ impl<Key : Send + cmp::Eq + hash::Hash + 'static> SimpleManyChannelMonitor<Key>
184184
monitors.insert(key, monitor);
185185
Ok(())
186186
}
187+
187188
}
188189

189190
impl ManyChannelMonitor for SimpleManyChannelMonitor<OutPoint> {
@@ -1502,6 +1503,16 @@ impl ChannelMonitor {
15021503
pub(crate) fn is_resolving_output(&self, tx: &Transaction) -> Option<Vec<(Option<[u8;32]>, [u8;32])>> {
15031504
let mut hash_to_remove = Vec::new();
15041505
if tx.input.len() > 0 {
1506+
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);
1507+
if commitment_number >= self.get_min_seen_secret() {
1508+
if let Some(ref local_commitment_tx) = self.current_local_signed_commitment_tx {
1509+
for &(ref htlc_output, _, _) in &local_commitment_tx.htlc_outputs {
1510+
if htlc_output.offered {
1511+
hash_to_remove.push((None, htlc_output.payment_hash.clone()));
1512+
}
1513+
}
1514+
}
1515+
}
15051516
for input in &tx.input {
15061517
let mut payment_data = (None, None);
15071518
if let Some(ref current_local_signed_commitment_tx) = self.current_local_signed_commitment_tx {
@@ -1552,6 +1563,7 @@ impl ChannelMonitor {
15521563
}
15531564
None
15541565
}
1566+
15551567
}
15561568

15571569
const MAX_ALLOC_SIZE: usize = 64*1024;

0 commit comments

Comments
 (0)