Skip to content

Commit 67d878d

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 9f53d5c commit 67d878d

File tree

3 files changed

+94
-46
lines changed

3 files changed

+94
-46
lines changed

src/ln/channelmonitor.rs

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

310291
#[derive(Clone, PartialEq)]
@@ -390,6 +371,8 @@ pub struct ChannelMonitor {
390371

391372
destination_script: Script,
392373

374+
htlc_updated_waiting_threshold_conf: HashMap<u32, Vec<(HTLCSource, Option<PaymentPreimage>, PaymentHash)>>,
375+
393376
// We simply modify last_block_hash in Channel's block_connected so that serialization is
394377
// consistent but hopefully the users' copy handles block_connected in a consistent way.
395378
// (we do *not*, however, update them in insert_combine to ensure any local user copies keep
@@ -419,7 +402,8 @@ impl PartialEq for ChannelMonitor {
419402
self.current_remote_commitment_number != other.current_remote_commitment_number ||
420403
self.current_local_signed_commitment_tx != other.current_local_signed_commitment_tx ||
421404
self.payment_preimages != other.payment_preimages ||
422-
self.destination_script != other.destination_script
405+
self.destination_script != other.destination_script ||
406+
self.htlc_updated_waiting_threshold_conf != other.htlc_updated_waiting_threshold_conf
423407
{
424408
false
425409
} else {
@@ -469,6 +453,8 @@ impl ChannelMonitor {
469453
payment_preimages: HashMap::new(),
470454
destination_script: destination_script,
471455

456+
htlc_updated_waiting_threshold_conf: HashMap::new(),
457+
472458
last_block_hash: Default::default(),
473459
secp_ctx: Secp256k1::new(),
474460
logger,
@@ -946,6 +932,17 @@ impl ChannelMonitor {
946932
self.last_block_hash.write(writer)?;
947933
self.destination_script.write(writer)?;
948934

935+
writer.write_all(&byte_utils::be64_to_array(self.htlc_updated_waiting_threshold_conf.len() as u64))?;
936+
for (ref target, ref updates) in self.htlc_updated_waiting_threshold_conf.iter() {
937+
writer.write_all(&byte_utils::be32_to_array(**target))?;
938+
writer.write_all(&byte_utils::be64_to_array(updates.len() as u64))?;
939+
for ref update in updates.iter() {
940+
update.0.write(writer)?;
941+
update.1.write(writer)?;
942+
update.2.write(writer)?;
943+
}
944+
}
945+
949946
Ok(())
950947
}
951948

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

10201016
let commitment_txid = tx.txid(); //TODO: This is gonna be a performance bottleneck for watchtowers!
10211017
let per_commitment_option = self.remote_claimable_outpoints.get(&commitment_txid);
@@ -1024,7 +1020,7 @@ impl ChannelMonitor {
10241020
( $thing : expr ) => {
10251021
match $thing {
10261022
Ok(a) => a,
1027-
Err(_) => return (txn_to_broadcast, (commitment_txid, watch_outputs), spendable_outputs, htlc_updated)
1023+
Err(_) => return (txn_to_broadcast, (commitment_txid, watch_outputs), spendable_outputs)
10281024
}
10291025
};
10301026
}
@@ -1049,7 +1045,7 @@ impl ChannelMonitor {
10491045
};
10501046
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()));
10511047
let a_htlc_key = match self.their_htlc_base_key {
1052-
None => return (txn_to_broadcast, (commitment_txid, watch_outputs), spendable_outputs, htlc_updated),
1048+
None => return (txn_to_broadcast, (commitment_txid, watch_outputs), spendable_outputs),
10531049
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)),
10541050
};
10551051

@@ -1129,7 +1125,7 @@ impl ChannelMonitor {
11291125
if transaction_output_index as usize >= tx.output.len() ||
11301126
tx.output[transaction_output_index as usize].value != htlc.amount_msat / 1000 ||
11311127
tx.output[transaction_output_index as usize].script_pubkey != expected_script.to_v0_p2wsh() {
1132-
return (txn_to_broadcast, (commitment_txid, watch_outputs), spendable_outputs, htlc_updated); // Corrupted per_commitment_data, fuck this user
1128+
return (txn_to_broadcast, (commitment_txid, watch_outputs), spendable_outputs); // Corrupted per_commitment_data, fuck this user
11331129
}
11341130
let input = TxIn {
11351131
previous_output: BitcoinOutPoint {
@@ -1169,16 +1165,22 @@ impl ChannelMonitor {
11691165
watch_outputs.append(&mut tx.output.clone());
11701166
self.remote_commitment_txn_on_chain.insert(commitment_txid, (commitment_number, tx.output.iter().map(|output| { output.script_pubkey.clone() }).collect()));
11711167

1172-
// TODO: We really should only fail backwards after our revocation claims have been
1173-
// confirmed, but we also need to do more other tracking of in-flight pre-confirm
1174-
// on-chain claims, so we can do that at the same time.
11751168
macro_rules! check_htlc_fails {
11761169
($txid: expr, $commitment_tx: expr) => {
11771170
if let Some(ref outpoints) = self.remote_claimable_outpoints.get($txid) {
11781171
for &(ref htlc, ref source_option) in outpoints.iter() {
11791172
if let &Some(ref source) = source_option {
1180-
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);
1181-
htlc_updated.push(((**source).clone(), None, htlc.payment_hash.clone()));
1173+
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);
1174+
match self.htlc_updated_waiting_threshold_conf.entry(height + HTLC_FAIL_ANTI_REORG_DELAY - 1) {
1175+
hash_map::Entry::Occupied(mut entry) => {
1176+
let e = entry.get_mut();
1177+
e.retain(|ref update| update.0 != **source);
1178+
e.push(((**source).clone(), None, htlc.payment_hash.clone()));
1179+
}
1180+
hash_map::Entry::Vacant(entry) => {
1181+
entry.insert(vec![((**source).clone(), None, htlc.payment_hash.clone())]);
1182+
}
1183+
}
11821184
}
11831185
}
11841186
}
@@ -1194,7 +1196,7 @@ impl ChannelMonitor {
11941196
}
11951197
// No need to check local commitment txn, symmetric HTLCSource must be present as per-htlc data on remote commitment tx
11961198
}
1197-
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
1199+
if inputs.is_empty() { return (txn_to_broadcast, (commitment_txid, watch_outputs), spendable_outputs); } // Nothing to be done...probably a false positive/local tx
11981200

11991201
let outputs = vec!(TxOut {
12001202
script_pubkey: self.destination_script.clone(),
@@ -1233,9 +1235,6 @@ impl ChannelMonitor {
12331235

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

1236-
// TODO: We really should only fail backwards after our revocation claims have been
1237-
// confirmed, but we also need to do more other tracking of in-flight pre-confirm
1238-
// on-chain claims, so we can do that at the same time.
12391238
macro_rules! check_htlc_fails {
12401239
($txid: expr, $commitment_tx: expr, $id: tt) => {
12411240
if let Some(ref latest_outpoints) = self.remote_claimable_outpoints.get($txid) {
@@ -1256,7 +1255,16 @@ impl ChannelMonitor {
12561255
}
12571256
}
12581257
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);
1259-
htlc_updated.push(((**source).clone(), None, htlc.payment_hash.clone()));
1258+
match self.htlc_updated_waiting_threshold_conf.entry(height + HTLC_FAIL_ANTI_REORG_DELAY - 1) {
1259+
hash_map::Entry::Occupied(mut entry) => {
1260+
let e = entry.get_mut();
1261+
e.retain(|ref update| update.0 != **source);
1262+
e.push(((**source).clone(), None, htlc.payment_hash.clone()));
1263+
}
1264+
hash_map::Entry::Vacant(entry) => {
1265+
entry.insert(vec![((**source).clone(), None, htlc.payment_hash.clone())]);
1266+
}
1267+
}
12601268
}
12611269
}
12621270
}
@@ -1289,7 +1297,7 @@ impl ChannelMonitor {
12891297
},
12901298
};
12911299
let a_htlc_key = match self.their_htlc_base_key {
1292-
None => return (txn_to_broadcast, (commitment_txid, watch_outputs), spendable_outputs, htlc_updated),
1300+
None => return (txn_to_broadcast, (commitment_txid, watch_outputs), spendable_outputs),
12931301
Some(their_htlc_base_key) => ignore_error!(chan_utils::derive_public_key(&self.secp_ctx, revocation_point, &their_htlc_base_key)),
12941302
};
12951303

@@ -1344,7 +1352,7 @@ impl ChannelMonitor {
13441352
if transaction_output_index as usize >= tx.output.len() ||
13451353
tx.output[transaction_output_index as usize].value != htlc.amount_msat / 1000 ||
13461354
tx.output[transaction_output_index as usize].script_pubkey != expected_script.to_v0_p2wsh() {
1347-
return (txn_to_broadcast, (commitment_txid, watch_outputs), spendable_outputs, htlc_updated); // Corrupted per_commitment_data, fuck this user
1355+
return (txn_to_broadcast, (commitment_txid, watch_outputs), spendable_outputs); // Corrupted per_commitment_data, fuck this user
13481356
}
13491357
if let Some(payment_preimage) = self.payment_preimages.get(&htlc.payment_hash) {
13501358
let input = TxIn {
@@ -1407,7 +1415,7 @@ impl ChannelMonitor {
14071415
}
14081416
}
14091417

1410-
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
1418+
if inputs.is_empty() { return (txn_to_broadcast, (commitment_txid, watch_outputs), spendable_outputs); } // Nothing to be done...probably a false positive/local tx
14111419

14121420
let outputs = vec!(TxOut {
14131421
script_pubkey: self.destination_script.clone(),
@@ -1437,7 +1445,7 @@ impl ChannelMonitor {
14371445
}
14381446
}
14391447

1440-
(txn_to_broadcast, (commitment_txid, watch_outputs), spendable_outputs, htlc_updated)
1448+
(txn_to_broadcast, (commitment_txid, watch_outputs), spendable_outputs)
14411449
}
14421450

14431451
/// Attempts to claim a remote HTLC-Success/HTLC-Timeout's outputs using the revocation key
@@ -1709,7 +1717,7 @@ impl ChannelMonitor {
17091717
}
17101718
};
17111719
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) {
1712-
let (remote_txn, new_outputs, mut spendable_output, mut updated) = self.check_spend_remote_transaction(tx, height);
1720+
let (remote_txn, new_outputs, mut spendable_output) = self.check_spend_remote_transaction(tx, height);
17131721
txn = remote_txn;
17141722
spendable_outputs.append(&mut spendable_output);
17151723
if !new_outputs.1.is_empty() {
@@ -1728,9 +1736,6 @@ impl ChannelMonitor {
17281736
spendable_outputs.push(spendable_output);
17291737
}
17301738
}
1731-
if updated.len() > 0 {
1732-
htlc_updated.append(&mut updated);
1733-
}
17341739
} else {
17351740
if let Some(&(commitment_number, _)) = self.remote_commitment_txn_on_chain.get(&prevout.txid) {
17361741
let (tx, spendable_output) = self.check_spend_remote_htlc(tx, commitment_number);
@@ -1781,6 +1786,12 @@ impl ChannelMonitor {
17811786
}
17821787
}
17831788
}
1789+
if let Some(updates) = self.htlc_updated_waiting_threshold_conf.remove(&height) {
1790+
for update in updates {
1791+
log_trace!(self, "HTLC {} failure update has get enough confirmation to be pass upstream", log_bytes!((update.2).0));
1792+
htlc_updated.push(update);
1793+
}
1794+
}
17841795
self.last_block_hash = block_hash.clone();
17851796
(watch_outputs, spendable_outputs, htlc_updated)
17861797
}
@@ -2181,6 +2192,21 @@ impl<R: ::std::io::Read> ReadableArgs<R, Arc<Logger>> for (Sha256dHash, ChannelM
21812192
let last_block_hash: Sha256dHash = Readable::read(reader)?;
21822193
let destination_script = Readable::read(reader)?;
21832194

2195+
let waiting_threshold_conf_len: u64 = Readable::read(reader)?;
2196+
let mut htlc_updated_waiting_threshold_conf = HashMap::with_capacity(cmp::min(waiting_threshold_conf_len as usize, MAX_ALLOC_SIZE / 128));
2197+
for _ in 0..waiting_threshold_conf_len {
2198+
let height_target = Readable::read(reader)?;
2199+
let updates_len: u64 = Readable::read(reader)?;
2200+
let mut updates = Vec::with_capacity(cmp::min(updates_len as usize, MAX_ALLOC_SIZE / 128));
2201+
for _ in 0..updates_len {
2202+
let htlc_source = Readable::read(reader)?;
2203+
let preimage = Readable::read(reader)?;
2204+
let hash = Readable::read(reader)?;
2205+
updates.push((htlc_source, preimage, hash));
2206+
}
2207+
htlc_updated_waiting_threshold_conf.insert(height_target, updates);
2208+
}
2209+
21842210
Ok((last_block_hash.clone(), ChannelMonitor {
21852211
commitment_transaction_number_obscure_factor,
21862212

@@ -2204,6 +2230,9 @@ impl<R: ::std::io::Read> ReadableArgs<R, Arc<Logger>> for (Sha256dHash, ChannelM
22042230
payment_preimages,
22052231

22062232
destination_script,
2233+
2234+
htlc_updated_waiting_threshold_conf,
2235+
22072236
last_block_hash,
22082237
secp_ctx,
22092238
logger,

src/ln/functional_test_utils.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ use bitcoin::blockdata::transaction::{Transaction, TxOut};
2020
use bitcoin::network::constants::Network;
2121

2222
use bitcoin_hashes::sha256::Hash as Sha256;
23+
use bitcoin_hashes::sha256d::Hash as Sha256d;
2324
use bitcoin_hashes::Hash;
2425

2526
use secp256k1::Secp256k1;
@@ -46,6 +47,15 @@ pub fn confirm_transaction(chain: &chaininterface::ChainWatchInterfaceUtil, tx:
4647
}
4748
}
4849

50+
pub fn connect_blocks(chain: &chaininterface::ChainWatchInterfaceUtil, depth: u32, height: u32, parent: bool, prev_blockhash: Sha256d) {
51+
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 };
52+
chain.block_connected_checked(&header, height + 1, &Vec::new(), &Vec::new());
53+
for i in 2..depth {
54+
header = BlockHeader { version: 0x20000000, prev_blockhash: header.bitcoin_hash(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 };
55+
chain.block_connected_checked(&header, height + i, &Vec::new(), &Vec::new());
56+
}
57+
}
58+
4959
pub struct Node {
5060
pub chain_monitor: Arc<chaininterface::ChainWatchInterfaceUtil>,
5161
pub tx_broadcaster: Arc<test_utils::TestBroadcaster>,

0 commit comments

Comments
 (0)