Skip to content

Commit 7a1923a

Browse files
author
Antoine Riard
committed
Track HTLC-failure trigger tx until anti-reorg delay reached
Broadcasting a commitment tx means that we have to fail inbound HTLC in backward channel. Doing it prematurely would put us at risk in case of reorg. So we delay passing failure update upstream until solving tx mature to HTLC_FAIL_ANTI_ REORG_DELAY. Requirements differ if HTLC is a revoked/non-revoked dust/ non-revoked non-dust one. Add connect_blocks in test_utils to fix broken tests due to anti-reorg delay enforcement Remove anti-duplicate htlc update stuff in ManySimpleChannelMonitor
1 parent 591362f commit 7a1923a

File tree

3 files changed

+94
-47
lines changed

3 files changed

+94
-47
lines changed

src/ln/channelmonitor.rs

Lines changed: 73 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -169,25 +169,7 @@ impl<Key : Send + cmp::Eq + hash::Hash> ChainListener for SimpleManyChannelMonit
169169
for htlc in htlc_updated_infos.drain(..) {
170170
match pending_htlc_updated.entry(htlc.2) {
171171
hash_map::Entry::Occupied(mut e) => {
172-
// In case of reorg we may have htlc outputs solved in a different way so
173-
// we prefer to keep claims but don't store duplicate updates for a given
174-
// (payment_hash, HTLCSource) pair.
175-
// TODO: Note that we currently don't really use this as ChannelManager
176-
// will fail/claim backwards after the first block. We really should delay
177-
// a few blocks before failing backwards (but can claim backwards
178-
// immediately) as long as we have a few blocks of headroom.
179-
let mut existing_claim = false;
180-
e.get_mut().retain(|htlc_data| {
181-
if htlc.0 == htlc_data.0 {
182-
if htlc_data.1.is_some() {
183-
existing_claim = true;
184-
true
185-
} else { false }
186-
} else { true }
187-
});
188-
if !existing_claim {
189-
e.get_mut().push((htlc.0, htlc.1));
190-
}
172+
e.get_mut().push((htlc.0, htlc.1));
191173
}
192174
hash_map::Entry::Vacant(e) => {
193175
e.insert(vec![(htlc.0, htlc.1)]);
@@ -299,7 +281,6 @@ pub(crate) const HTLC_FAIL_TIMEOUT_BLOCKS: u32 = 3;
299281
/// Number of blocks we wait on seeing a confirmed HTLC-Timeout or previous revoked commitment
300282
/// transaction before we fail corresponding inbound HTLCs. This prevents us from failing backwards
301283
/// and then getting a reorg resulting in us losing money.
302-
//TODO: We currently don't actually use this...we should
303284
pub(crate) const HTLC_FAIL_ANTI_REORG_DELAY: u32 = 6;
304285

305286
#[derive(Clone, PartialEq)]
@@ -385,6 +366,8 @@ pub struct ChannelMonitor {
385366

386367
destination_script: Script,
387368

369+
htlc_updated_waiting_threshold_conf: HashMap<u32, Vec<(HTLCSource, Option<PaymentPreimage>, PaymentHash)>>,
370+
388371
// We simply modify last_block_hash in Channel's block_connected so that serialization is
389372
// consistent but hopefully the users' copy handles block_connected in a consistent way.
390373
// (we do *not*, however, update them in insert_combine to ensure any local user copies keep
@@ -414,7 +397,8 @@ impl PartialEq for ChannelMonitor {
414397
self.current_remote_commitment_number != other.current_remote_commitment_number ||
415398
self.current_local_signed_commitment_tx != other.current_local_signed_commitment_tx ||
416399
self.payment_preimages != other.payment_preimages ||
417-
self.destination_script != other.destination_script
400+
self.destination_script != other.destination_script ||
401+
self.htlc_updated_waiting_threshold_conf != other.htlc_updated_waiting_threshold_conf
418402
{
419403
false
420404
} else {
@@ -464,6 +448,8 @@ impl ChannelMonitor {
464448
payment_preimages: HashMap::new(),
465449
destination_script: destination_script,
466450

451+
htlc_updated_waiting_threshold_conf: HashMap::new(),
452+
467453
last_block_hash: Default::default(),
468454
secp_ctx: Secp256k1::new(),
469455
logger,
@@ -951,6 +937,17 @@ impl ChannelMonitor {
951937
self.last_block_hash.write(writer)?;
952938
self.destination_script.write(writer)?;
953939

940+
writer.write_all(&byte_utils::be64_to_array(self.htlc_updated_waiting_threshold_conf.len() as u64))?;
941+
for (ref target, ref updates) in self.htlc_updated_waiting_threshold_conf.iter() {
942+
writer.write_all(&byte_utils::be32_to_array(**target))?;
943+
writer.write_all(&byte_utils::be64_to_array(updates.len() as u64))?;
944+
for ref update in updates.iter() {
945+
update.0.write(writer)?;
946+
update.1.write(writer)?;
947+
update.2.write(writer)?;
948+
}
949+
}
950+
954951
Ok(())
955952
}
956953

@@ -1014,13 +1011,12 @@ impl ChannelMonitor {
10141011
/// HTLC-Success/HTLC-Timeout transactions.
10151012
/// Return updates for HTLC pending in the channel and failed automatically by the broadcast of
10161013
/// revoked remote commitment tx
1017-
fn check_spend_remote_transaction(&mut self, tx: &Transaction, height: u32) -> (Vec<Transaction>, (Sha256dHash, Vec<TxOut>), Vec<SpendableOutputDescriptor>, Vec<(HTLCSource, Option<PaymentPreimage>, PaymentHash)>) {
1014+
fn check_spend_remote_transaction(&mut self, tx: &Transaction, height: u32) -> (Vec<Transaction>, (Sha256dHash, Vec<TxOut>), Vec<SpendableOutputDescriptor>) {
10181015
// Most secp and related errors trying to create keys means we have no hope of constructing
10191016
// a spend transaction...so we return no transactions to broadcast
10201017
let mut txn_to_broadcast = Vec::new();
10211018
let mut watch_outputs = Vec::new();
10221019
let mut spendable_outputs = Vec::new();
1023-
let mut htlc_updated = Vec::new();
10241020

10251021
let commitment_txid = tx.txid(); //TODO: This is gonna be a performance bottleneck for watchtowers!
10261022
let per_commitment_option = self.remote_claimable_outpoints.get(&commitment_txid);
@@ -1029,7 +1025,7 @@ impl ChannelMonitor {
10291025
( $thing : expr ) => {
10301026
match $thing {
10311027
Ok(a) => a,
1032-
Err(_) => return (txn_to_broadcast, (commitment_txid, watch_outputs), spendable_outputs, htlc_updated)
1028+
Err(_) => return (txn_to_broadcast, (commitment_txid, watch_outputs), spendable_outputs)
10331029
}
10341030
};
10351031
}
@@ -1054,7 +1050,7 @@ impl ChannelMonitor {
10541050
};
10551051
let delayed_key = ignore_error!(chan_utils::derive_public_key(&self.secp_ctx, &PublicKey::from_secret_key(&self.secp_ctx, &per_commitment_key), &self.their_delayed_payment_base_key.unwrap()));
10561052
let a_htlc_key = match self.their_htlc_base_key {
1057-
None => return (txn_to_broadcast, (commitment_txid, watch_outputs), spendable_outputs, htlc_updated),
1053+
None => return (txn_to_broadcast, (commitment_txid, watch_outputs), spendable_outputs),
10581054
Some(their_htlc_base_key) => ignore_error!(chan_utils::derive_public_key(&self.secp_ctx, &PublicKey::from_secret_key(&self.secp_ctx, &per_commitment_key), &their_htlc_base_key)),
10591055
};
10601056

@@ -1134,7 +1130,7 @@ impl ChannelMonitor {
11341130
if transaction_output_index as usize >= tx.output.len() ||
11351131
tx.output[transaction_output_index as usize].value != htlc.amount_msat / 1000 ||
11361132
tx.output[transaction_output_index as usize].script_pubkey != expected_script.to_v0_p2wsh() {
1137-
return (txn_to_broadcast, (commitment_txid, watch_outputs), spendable_outputs, htlc_updated); // Corrupted per_commitment_data, fuck this user
1133+
return (txn_to_broadcast, (commitment_txid, watch_outputs), spendable_outputs); // Corrupted per_commitment_data, fuck this user
11381134
}
11391135
let input = TxIn {
11401136
previous_output: BitcoinOutPoint {
@@ -1174,16 +1170,22 @@ impl ChannelMonitor {
11741170
watch_outputs.append(&mut tx.output.clone());
11751171
self.remote_commitment_txn_on_chain.insert(commitment_txid, (commitment_number, tx.output.iter().map(|output| { output.script_pubkey.clone() }).collect()));
11761172

1177-
// TODO: We really should only fail backwards after our revocation claims have been
1178-
// confirmed, but we also need to do more other tracking of in-flight pre-confirm
1179-
// on-chain claims, so we can do that at the same time.
11801173
macro_rules! check_htlc_fails {
11811174
($txid: expr, $commitment_tx: expr) => {
11821175
if let Some(ref outpoints) = self.remote_claimable_outpoints.get(&$txid) {
11831176
for &(ref htlc, ref source_option) in outpoints.iter() {
11841177
if let &Some(ref source) = source_option {
1185-
log_trace!(self, "Failing HTLC with payment_hash {} from {} remote commitment tx due to broadcast of revoked remote commitment transaction", log_bytes!(htlc.payment_hash.0), $commitment_tx);
1186-
htlc_updated.push(((**source).clone(), None, htlc.payment_hash.clone()));
1178+
log_trace!(self, "Failing HTLC with payment_hash {} from {} remote commitment tx due to broadcast of revoked remote commitment transaction, waiting confirmation until {} height", log_bytes!(htlc.payment_hash.0), $commitment_tx, height + HTLC_FAIL_ANTI_REORG_DELAY - 1);
1179+
match self.htlc_updated_waiting_threshold_conf.entry(height + HTLC_FAIL_ANTI_REORG_DELAY - 1) {
1180+
hash_map::Entry::Occupied(mut entry) => {
1181+
let e = entry.get_mut();
1182+
e.retain(|ref update| update.0 != **source);
1183+
e.push(((**source).clone(), None, htlc.payment_hash.clone()));
1184+
}
1185+
hash_map::Entry::Vacant(entry) => {
1186+
entry.insert(vec![((**source).clone(), None, htlc.payment_hash.clone())]);
1187+
}
1188+
}
11871189
}
11881190
}
11891191
}
@@ -1199,7 +1201,7 @@ impl ChannelMonitor {
11991201
}
12001202
// No need to check local commitment txn, symmetric HTLCSource must be present as per-htlc data on remote commitment tx
12011203
}
1202-
if inputs.is_empty() { return (txn_to_broadcast, (commitment_txid, watch_outputs), spendable_outputs, htlc_updated); } // Nothing to be done...probably a false positive/local tx
1204+
if inputs.is_empty() { return (txn_to_broadcast, (commitment_txid, watch_outputs), spendable_outputs); } // Nothing to be done...probably a false positive/local tx
12031205

12041206
let outputs = vec!(TxOut {
12051207
script_pubkey: self.destination_script.clone(),
@@ -1238,9 +1240,6 @@ impl ChannelMonitor {
12381240

12391241
log_trace!(self, "Got broadcast of non-revoked remote commitment transaction {}", commitment_txid);
12401242

1241-
// TODO: We really should only fail backwards after our revocation claims have been
1242-
// confirmed, but we also need to do more other tracking of in-flight pre-confirm
1243-
// on-chain claims, so we can do that at the same time.
12441243
macro_rules! check_htlc_fails {
12451244
($txid: expr, $commitment_tx: expr, $id: tt) => {
12461245
if let Some(ref latest_outpoints) = self.remote_claimable_outpoints.get(&$txid) {
@@ -1261,7 +1260,16 @@ impl ChannelMonitor {
12611260
}
12621261
}
12631262
log_trace!(self, "Failing HTLC with payment_hash {} from {} remote commitment tx due to broadcast of remote commitment transaction", log_bytes!(htlc.payment_hash.0), $commitment_tx);
1264-
htlc_updated.push(((**source).clone(), None, htlc.payment_hash.clone()));
1263+
match self.htlc_updated_waiting_threshold_conf.entry(height + HTLC_FAIL_ANTI_REORG_DELAY - 1) {
1264+
hash_map::Entry::Occupied(mut entry) => {
1265+
let e = entry.get_mut();
1266+
e.retain(|ref update| update.0 != **source);
1267+
e.push(((**source).clone(), None, htlc.payment_hash.clone()));
1268+
}
1269+
hash_map::Entry::Vacant(entry) => {
1270+
entry.insert(vec![((**source).clone(), None, htlc.payment_hash.clone())]);
1271+
}
1272+
}
12651273
}
12661274
}
12671275
}
@@ -1294,7 +1302,7 @@ impl ChannelMonitor {
12941302
},
12951303
};
12961304
let a_htlc_key = match self.their_htlc_base_key {
1297-
None => return (txn_to_broadcast, (commitment_txid, watch_outputs), spendable_outputs, htlc_updated),
1305+
None => return (txn_to_broadcast, (commitment_txid, watch_outputs), spendable_outputs),
12981306
Some(their_htlc_base_key) => ignore_error!(chan_utils::derive_public_key(&self.secp_ctx, revocation_point, &their_htlc_base_key)),
12991307
};
13001308

@@ -1349,7 +1357,7 @@ impl ChannelMonitor {
13491357
if transaction_output_index as usize >= tx.output.len() ||
13501358
tx.output[transaction_output_index as usize].value != htlc.amount_msat / 1000 ||
13511359
tx.output[transaction_output_index as usize].script_pubkey != expected_script.to_v0_p2wsh() {
1352-
return (txn_to_broadcast, (commitment_txid, watch_outputs), spendable_outputs, htlc_updated); // Corrupted per_commitment_data, fuck this user
1360+
return (txn_to_broadcast, (commitment_txid, watch_outputs), spendable_outputs); // Corrupted per_commitment_data, fuck this user
13531361
}
13541362
if let Some(payment_preimage) = self.payment_preimages.get(&htlc.payment_hash) {
13551363
let input = TxIn {
@@ -1412,7 +1420,7 @@ impl ChannelMonitor {
14121420
}
14131421
}
14141422

1415-
if inputs.is_empty() { return (txn_to_broadcast, (commitment_txid, watch_outputs), spendable_outputs, htlc_updated); } // Nothing to be done...probably a false positive/local tx
1423+
if inputs.is_empty() { return (txn_to_broadcast, (commitment_txid, watch_outputs), spendable_outputs); } // Nothing to be done...probably a false positive/local tx
14161424

14171425
let outputs = vec!(TxOut {
14181426
script_pubkey: self.destination_script.clone(),
@@ -1442,7 +1450,7 @@ impl ChannelMonitor {
14421450
}
14431451
}
14441452

1445-
(txn_to_broadcast, (commitment_txid, watch_outputs), spendable_outputs, htlc_updated)
1453+
(txn_to_broadcast, (commitment_txid, watch_outputs), spendable_outputs)
14461454
}
14471455

14481456
/// Attempts to claim a remote HTLC-Success/HTLC-Timeout's outputs using the revocation key
@@ -1714,7 +1722,7 @@ impl ChannelMonitor {
17141722
}
17151723
};
17161724
if funding_txo.is_none() || (prevout.txid == funding_txo.as_ref().unwrap().0.txid && prevout.vout == funding_txo.as_ref().unwrap().0.index as u32) {
1717-
let (remote_txn, new_outputs, mut spendable_output, mut updated) = self.check_spend_remote_transaction(tx, height);
1725+
let (remote_txn, new_outputs, mut spendable_output) = self.check_spend_remote_transaction(tx, height);
17181726
txn = remote_txn;
17191727
spendable_outputs.append(&mut spendable_output);
17201728
if !new_outputs.1.is_empty() {
@@ -1733,9 +1741,6 @@ impl ChannelMonitor {
17331741
spendable_outputs.push(spendable_output);
17341742
}
17351743
}
1736-
if updated.len() > 0 {
1737-
htlc_updated.append(&mut updated);
1738-
}
17391744
} else {
17401745
if let Some(&(commitment_number, _)) = self.remote_commitment_txn_on_chain.get(&prevout.txid) {
17411746
let (tx, spendable_output) = self.check_spend_remote_htlc(tx, commitment_number);
@@ -1786,6 +1791,12 @@ impl ChannelMonitor {
17861791
}
17871792
}
17881793
}
1794+
if let Some(updates) = self.htlc_updated_waiting_threshold_conf.remove(&height) {
1795+
for update in updates {
1796+
log_trace!(self, "HTLC {} failure update has get enough confirmation to be pass upstream", log_bytes!((update.2).0));
1797+
htlc_updated.push(update);
1798+
}
1799+
}
17891800
self.last_block_hash = block_hash.clone();
17901801
(watch_outputs, spendable_outputs, htlc_updated)
17911802
}
@@ -2159,6 +2170,21 @@ impl<R: ::std::io::Read> ReadableArgs<R, Arc<Logger>> for (Sha256dHash, ChannelM
21592170
let last_block_hash: Sha256dHash = Readable::read(reader)?;
21602171
let destination_script = Readable::read(reader)?;
21612172

2173+
let waiting_threshold_conf_len: u64 = Readable::read(reader)?;
2174+
let mut htlc_updated_waiting_threshold_conf = HashMap::with_capacity(cmp::min(waiting_threshold_conf_len as usize, MAX_ALLOC_SIZE / 128));
2175+
for _ in 0..waiting_threshold_conf_len {
2176+
let height_target = Readable::read(reader)?;
2177+
let updates_len: u64 = Readable::read(reader)?;
2178+
let mut updates = Vec::with_capacity(cmp::min(updates_len as usize, MAX_ALLOC_SIZE / 128));
2179+
for _ in 0..updates_len {
2180+
let htlc_source = Readable::read(reader)?;
2181+
let preimage = Readable::read(reader)?;
2182+
let hash = Readable::read(reader)?;
2183+
updates.push((htlc_source, preimage, hash));
2184+
}
2185+
htlc_updated_waiting_threshold_conf.insert(height_target, updates);
2186+
}
2187+
21622188
Ok((last_block_hash.clone(), ChannelMonitor {
21632189
commitment_transaction_number_obscure_factor,
21642190

@@ -2182,6 +2208,9 @@ impl<R: ::std::io::Read> ReadableArgs<R, Arc<Logger>> for (Sha256dHash, ChannelM
21822208
payment_preimages,
21832209

21842210
destination_script,
2211+
2212+
htlc_updated_waiting_threshold_conf,
2213+
21852214
last_block_hash,
21862215
secp_ctx,
21872216
logger,

src/ln/functional_test_utils.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use util::errors::APIError;
1414
use util::logger::Logger;
1515
use util::config::UserConfig;
1616

17-
use bitcoin::util::hash::BitcoinHash;
17+
use bitcoin::util::hash::{BitcoinHash, Sha256dHash};
1818
use bitcoin::blockdata::block::BlockHeader;
1919
use bitcoin::blockdata::transaction::{Transaction, TxOut};
2020
use bitcoin::network::constants::Network;
@@ -46,6 +46,15 @@ pub fn confirm_transaction(chain: &chaininterface::ChainWatchInterfaceUtil, tx:
4646
}
4747
}
4848

49+
pub fn connect_blocks(chain: &chaininterface::ChainWatchInterfaceUtil, depth: u32, height: u32, parent: bool, prev_blockhash: Sha256dHash) {
50+
let mut header = BlockHeader { version: 0x2000000, prev_blockhash: if parent { prev_blockhash } else { Default::default() }, merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 };
51+
chain.block_connected_checked(&header, height + 1, &Vec::new(), &Vec::new());
52+
for i in 2..depth {
53+
header = BlockHeader { version: 0x20000000, prev_blockhash: header.bitcoin_hash(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 };
54+
chain.block_connected_checked(&header, height + i, &Vec::new(), &Vec::new());
55+
}
56+
}
57+
4958
pub struct Node {
5059
pub chain_monitor: Arc<chaininterface::ChainWatchInterfaceUtil>,
5160
pub tx_broadcaster: Arc<test_utils::TestBroadcaster>,

0 commit comments

Comments
 (0)