Skip to content

Commit 7529f7e

Browse files
author
Antoine Riard
committed
Fail back dust HTLC of local commitment tx after enough confirmations
Add test_failure_delay_htlc_local_commitment and test_no_failure_dust_htlc_local_commitment Move some bits of check_spend_remote as we need to fail dust HTLCs which can be spread on both prev/lastest local commitment tx
1 parent f7cb000 commit 7529f7e

File tree

2 files changed

+202
-14
lines changed

2 files changed

+202
-14
lines changed

src/ln/channelmonitor.rs

Lines changed: 61 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1738,42 +1738,90 @@ impl ChannelMonitor {
17381738
/// Attempts to claim any claimable HTLCs in a commitment transaction which was not (yet)
17391739
/// revoked using data in local_claimable_outpoints.
17401740
/// Should not be used if check_spend_revoked_transaction succeeds.
1741-
fn check_spend_local_transaction(&self, tx: &Transaction, _height: u32) -> (Vec<Transaction>, Vec<SpendableOutputDescriptor>, (Sha256dHash, Vec<TxOut>)) {
1741+
fn check_spend_local_transaction(&mut self, tx: &Transaction, height: u32) -> (Vec<Transaction>, Vec<SpendableOutputDescriptor>, (Sha256dHash, Vec<TxOut>)) {
17421742
let commitment_txid = tx.txid();
1743-
// TODO: If we find a match here we need to fail back HTLCs that weren't included in the
1744-
// broadcast commitment transaction, either because they didn't meet dust or because they
1745-
// weren't yet included in our commitment transaction(s).
1743+
let mut local_txn = Vec::new();
1744+
let mut spendable_outputs = Vec::new();
1745+
let mut watch_outputs = Vec::new();
1746+
1747+
macro_rules! wait_threshold_conf {
1748+
($height: expr, $source: expr, $update: expr, $commitment_tx: expr, $payment_hash: expr) => {
1749+
log_info!(self, "Failing HTLC with payment_hash {} from {} local commitment tx due to broadcast of transaction, waiting confirmation (at height{})", log_bytes!($payment_hash.0), $commitment_tx, height + HTLC_FAIL_ANTI_REORG_DELAY - 1);
1750+
match self.htlc_updated_waiting_threshold_conf.entry($height + HTLC_FAIL_ANTI_REORG_DELAY - 1) {
1751+
hash_map::Entry::Occupied(mut entry) => {
1752+
let e = entry.get_mut();
1753+
e.retain(|ref update| update.0 != $source);
1754+
e.push(($source, $update, $payment_hash));
1755+
}
1756+
hash_map::Entry::Vacant(entry) => {
1757+
entry.insert(vec![($source, $update, $payment_hash)]);
1758+
}
1759+
}
1760+
}
1761+
}
1762+
1763+
macro_rules! append_onchain_update {
1764+
($updates: expr) => {
1765+
local_txn.append(&mut $updates.0);
1766+
spendable_outputs.append(&mut $updates.1);
1767+
watch_outputs.append(&mut $updates.2);
1768+
}
1769+
}
1770+
1771+
// HTLCs set may differ between last and previous local commitment txn, in case of one them hitting chain, ensure we cancel all HTLCs backward
1772+
let mut is_local_tx = false;
1773+
17461774
if let &Some(ref local_tx) = &self.current_local_signed_commitment_tx {
17471775
if local_tx.txid == commitment_txid {
1776+
is_local_tx = true;
17481777
log_trace!(self, "Got latest local commitment tx broadcast, searching for available HTLCs to claim");
17491778
match self.key_storage {
17501779
Storage::Local { ref delayed_payment_base_key, ref latest_per_commitment_point, .. } => {
1751-
let (local_txn, spendable_outputs, watch_outputs) = self.broadcast_by_local_state(local_tx, latest_per_commitment_point, &Some(*delayed_payment_base_key));
1752-
return (local_txn, spendable_outputs, (commitment_txid, watch_outputs));
1780+
append_onchain_update!(self.broadcast_by_local_state(local_tx, latest_per_commitment_point, &Some(*delayed_payment_base_key)));
17531781
},
17541782
Storage::Watchtower { .. } => {
1755-
let (local_txn, spendable_outputs, watch_outputs) = self.broadcast_by_local_state(local_tx, &None, &None);
1756-
return (local_txn, spendable_outputs, (commitment_txid, watch_outputs));
1783+
append_onchain_update!(self.broadcast_by_local_state(local_tx, &None, &None));
17571784
}
17581785
}
17591786
}
17601787
}
17611788
if let &Some(ref local_tx) = &self.prev_local_signed_commitment_tx {
17621789
if local_tx.txid == commitment_txid {
1790+
is_local_tx = true;
17631791
log_trace!(self, "Got previous local commitment tx broadcast, searching for available HTLCs to claim");
17641792
match self.key_storage {
17651793
Storage::Local { ref delayed_payment_base_key, ref prev_latest_per_commitment_point, .. } => {
1766-
let (local_txn, spendable_outputs, watch_outputs) = self.broadcast_by_local_state(local_tx, prev_latest_per_commitment_point, &Some(*delayed_payment_base_key));
1767-
return (local_txn, spendable_outputs, (commitment_txid, watch_outputs));
1794+
append_onchain_update!(self.broadcast_by_local_state(local_tx, prev_latest_per_commitment_point, &Some(*delayed_payment_base_key)));
17681795
},
17691796
Storage::Watchtower { .. } => {
1770-
let (local_txn, spendable_outputs, watch_outputs) = self.broadcast_by_local_state(local_tx, &None, &None);
1771-
return (local_txn, spendable_outputs, (commitment_txid, watch_outputs));
1797+
append_onchain_update!(self.broadcast_by_local_state(local_tx, &None, &None));
17721798
}
17731799
}
17741800
}
17751801
}
1776-
(Vec::new(), Vec::new(), (commitment_txid, Vec::new()))
1802+
1803+
macro_rules! fail_dust_htlcs_after_threshold_conf {
1804+
($local_tx: expr) => {
1805+
for &(ref htlc, _, ref source) in &$local_tx.htlc_outputs {
1806+
if htlc.transaction_output_index.is_none() {
1807+
if let &Some(ref source) = source {
1808+
wait_threshold_conf!(height, source.clone(), None, "lastest", htlc.payment_hash.clone());
1809+
}
1810+
}
1811+
}
1812+
}
1813+
}
1814+
1815+
if is_local_tx {
1816+
if let &Some(ref local_tx) = &self.current_local_signed_commitment_tx {
1817+
fail_dust_htlcs_after_threshold_conf!(local_tx);
1818+
}
1819+
if let &Some(ref local_tx) = &self.prev_local_signed_commitment_tx {
1820+
fail_dust_htlcs_after_threshold_conf!(local_tx);
1821+
}
1822+
}
1823+
1824+
(local_txn, spendable_outputs, (commitment_txid, watch_outputs))
17771825
}
17781826

17791827
/// Generate a spendable output event when closing_transaction get registered onchain.

src/ln/functional_tests.rs

Lines changed: 141 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ use bitcoin::util::bip143;
2727
use bitcoin::util::address::Address;
2828
use bitcoin::util::bip32::{ChildNumber, ExtendedPubKey, ExtendedPrivKey};
2929
use bitcoin::blockdata::block::{Block, BlockHeader};
30-
use bitcoin::blockdata::transaction::{Transaction, TxOut, TxIn, SigHashType};
30+
use bitcoin::blockdata::transaction::{Transaction, TxOut, TxIn, SigHashType, OutPoint as BitcoinOutPoint};
3131
use bitcoin::blockdata::script::{Builder, Script};
3232
use bitcoin::blockdata::opcodes;
3333
use bitcoin::blockdata::constants::genesis_block;
@@ -5513,3 +5513,143 @@ fn test_update_fulfill_htlc_bolt2_after_malformed_htlc_message_must_forward_upda
55135513

55145514
check_added_monitors!(nodes[1], 1);
55155515
}
5516+
5517+
fn do_test_failure_delay_dust_htlc_local_commitment(announce_latest: bool) {
5518+
// Dust-HTLC failure updates must be delayed until failure-trigger tx (in this case local commitment) reach HTLC_FAIL_ANTI_REORG_DELAY
5519+
// We can have at most two valid local commitment tx, so both cases must be covered, and both txs must be checked to get them all as
5520+
// HTLC could have been removed from lastest local commitment tx but still valid until we get remote RAA
5521+
5522+
let nodes = create_network(2);
5523+
let chan =create_announced_chan_between_nodes(&nodes, 0, 1);
5524+
5525+
let bs_dust_limit = nodes[1].node.channel_state.lock().unwrap().by_id.get(&chan.2).unwrap().our_dust_limit_satoshis;
5526+
5527+
// We route 2 dust-HTLCs between A and B
5528+
let (_, payment_hash_1) = route_payment(&nodes[0], &[&nodes[1]], bs_dust_limit*1000);
5529+
let (_, payment_hash_2) = route_payment(&nodes[0], &[&nodes[1]], bs_dust_limit*1000);
5530+
route_payment(&nodes[0], &[&nodes[1]], 1000000);
5531+
5532+
// Cache one local commitment tx as previous
5533+
let as_prev_commitment_tx = nodes[0].node.channel_state.lock().unwrap().by_id.get(&chan.2).unwrap().last_local_commitment_txn.clone();
5534+
5535+
// Fail one HTLC to prune it in the will-be-latest-local commitment tx
5536+
assert!(nodes[1].node.fail_htlc_backwards(&payment_hash_2));
5537+
check_added_monitors!(nodes[1], 0);
5538+
expect_pending_htlcs_forwardable!(nodes[1]);
5539+
check_added_monitors!(nodes[1], 1);
5540+
5541+
let remove = get_htlc_update_msgs!(nodes[1], nodes[0].node.get_our_node_id());
5542+
nodes[0].node.handle_update_fail_htlc(&nodes[1].node.get_our_node_id(), &remove.update_fail_htlcs[0]).unwrap();
5543+
nodes[0].node.handle_commitment_signed(&nodes[1].node.get_our_node_id(), &remove.commitment_signed).unwrap();
5544+
check_added_monitors!(nodes[0], 1);
5545+
5546+
// Cache one local commitment tx as lastest
5547+
let as_last_commitment_tx = nodes[0].node.channel_state.lock().unwrap().by_id.get(&chan.2).unwrap().last_local_commitment_txn.clone();
5548+
5549+
let events = nodes[0].node.get_and_clear_pending_msg_events();
5550+
match events[0] {
5551+
MessageSendEvent::SendRevokeAndACK { node_id, .. } => {
5552+
assert_eq!(node_id, nodes[1].node.get_our_node_id());
5553+
},
5554+
_ => panic!("Unexpected event"),
5555+
}
5556+
match events[1] {
5557+
MessageSendEvent::UpdateHTLCs { node_id, .. } => {
5558+
assert_eq!(node_id, nodes[1].node.get_our_node_id());
5559+
},
5560+
_ => panic!("Unexpected event"),
5561+
}
5562+
5563+
assert_ne!(as_prev_commitment_tx, as_last_commitment_tx);
5564+
// Fail the 2 dust-HTLCs, move their failure in maturation buffer (htlc_updated_waiting_threshold_conf)
5565+
let header = BlockHeader { version: 0x20000000, prev_blockhash: Default::default(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 };
5566+
if announce_latest {
5567+
nodes[0].chain_monitor.block_connected_checked(&header, 1, &[&as_last_commitment_tx[0]], &[1; 1]);
5568+
} else {
5569+
nodes[0].chain_monitor.block_connected_checked(&header, 1, &[&as_prev_commitment_tx[0]], &[1; 1]);
5570+
}
5571+
5572+
let events = nodes[0].node.get_and_clear_pending_msg_events();
5573+
assert_eq!(events.len(), 1);
5574+
match events[0] {
5575+
MessageSendEvent::BroadcastChannelUpdate { .. } => {},
5576+
_ => panic!("Unexpected event"),
5577+
}
5578+
5579+
assert_eq!(nodes[0].node.get_and_clear_pending_events().len(), 0);
5580+
connect_blocks(&nodes[0].chain_monitor, HTLC_FAIL_ANTI_REORG_DELAY - 1, 1, true, header.bitcoin_hash());
5581+
let events = nodes[0].node.get_and_clear_pending_events();
5582+
// Only 2 PaymentFailed events should show up, over-dust HTLC has to be failed by timeout tx
5583+
assert_eq!(events.len(), 2);
5584+
let mut first_failed = false;
5585+
for event in events {
5586+
match event {
5587+
Event::PaymentFailed { payment_hash, .. } => {
5588+
if payment_hash == payment_hash_1 {
5589+
assert!(!first_failed);
5590+
first_failed = true;
5591+
} else {
5592+
assert_eq!(payment_hash, payment_hash_2);
5593+
}
5594+
}
5595+
_ => panic!("Unexpected event"),
5596+
}
5597+
}
5598+
}
5599+
5600+
#[test]
5601+
fn test_failure_delay_dust_htlc_local_commitment() {
5602+
do_test_failure_delay_dust_htlc_local_commitment(true);
5603+
do_test_failure_delay_dust_htlc_local_commitment(false);
5604+
}
5605+
5606+
#[test]
5607+
fn test_no_failure_dust_htlc_local_commitment() {
5608+
// Transaction filters for failing back dust htlc based on local commitment txn infos has been
5609+
// prone to error, we test here that a dummy transaction don't fail them.
5610+
5611+
let nodes = create_network(2);
5612+
let chan = create_announced_chan_between_nodes(&nodes, 0, 1);
5613+
5614+
// Rebalance a bit
5615+
send_payment(&nodes[0], &vec!(&nodes[1])[..], 8000000);
5616+
5617+
let as_dust_limit = nodes[0].node.channel_state.lock().unwrap().by_id.get(&chan.2).unwrap().our_dust_limit_satoshis;
5618+
let bs_dust_limit = nodes[1].node.channel_state.lock().unwrap().by_id.get(&chan.2).unwrap().our_dust_limit_satoshis;
5619+
5620+
// We route 2 dust-HTLCs between A and B
5621+
let (preimage_1, _) = route_payment(&nodes[0], &[&nodes[1]], bs_dust_limit*1000);
5622+
let (preimage_2, _) = route_payment(&nodes[1], &[&nodes[0]], as_dust_limit*1000);
5623+
5624+
// Build a dummy invalid transaction trying to spend a commitment tx
5625+
let input = TxIn {
5626+
previous_output: BitcoinOutPoint { txid: chan.3.txid(), vout: 0 },
5627+
script_sig: Script::new(),
5628+
sequence: 0,
5629+
witness: Vec::new(),
5630+
};
5631+
5632+
let outp = TxOut {
5633+
script_pubkey: Builder::new().push_opcode(opcodes::all::OP_RETURN).into_script(),
5634+
value: 10000,
5635+
};
5636+
5637+
let dummy_tx = Transaction {
5638+
version: 2,
5639+
lock_time: 0,
5640+
input: vec![input],
5641+
output: vec![outp]
5642+
};
5643+
5644+
let header = BlockHeader { version: 0x20000000, prev_blockhash: Default::default(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 };
5645+
nodes[0].chan_monitor.simple_monitor.block_connected(&header, 1, &[&dummy_tx], &[1;1]);
5646+
assert_eq!(nodes[0].node.get_and_clear_pending_events().len(), 0);
5647+
assert_eq!(nodes[0].node.get_and_clear_pending_msg_events().len(), 0);
5648+
// We broadcast a few more block to check everything is all right
5649+
connect_blocks(&nodes[0].chain_monitor, 20, 1, true, header.bitcoin_hash());
5650+
assert_eq!(nodes[0].node.get_and_clear_pending_events().len(), 0);
5651+
assert_eq!(nodes[0].node.get_and_clear_pending_msg_events().len(), 0);
5652+
5653+
claim_payment(&nodes[0], &vec!(&nodes[1])[..], preimage_1);
5654+
claim_payment(&nodes[1], &vec!(&nodes[0])[..], preimage_2);
5655+
}

0 commit comments

Comments
 (0)