Skip to content

Commit e9cd26d

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 f5a5729 commit e9cd26d

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,
@@ -956,6 +942,17 @@ impl ChannelMonitor {
956942
self.last_block_hash.write(writer)?;
957943
self.destination_script.write(writer)?;
958944

945+
writer.write_all(&byte_utils::be64_to_array(self.htlc_updated_waiting_threshold_conf.len() as u64))?;
946+
for (ref target, ref updates) in self.htlc_updated_waiting_threshold_conf.iter() {
947+
writer.write_all(&byte_utils::be32_to_array(**target))?;
948+
writer.write_all(&byte_utils::be64_to_array(updates.len() as u64))?;
949+
for ref update in updates.iter() {
950+
update.0.write(writer)?;
951+
update.1.write(writer)?;
952+
update.2.write(writer)?;
953+
}
954+
}
955+
959956
Ok(())
960957
}
961958

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

10301026
let commitment_txid = tx.txid(); //TODO: This is gonna be a performance bottleneck for watchtowers!
10311027
let per_commitment_option = self.remote_claimable_outpoints.get(&commitment_txid);
@@ -1034,7 +1030,7 @@ impl ChannelMonitor {
10341030
( $thing : expr ) => {
10351031
match $thing {
10361032
Ok(a) => a,
1037-
Err(_) => return (txn_to_broadcast, (commitment_txid, watch_outputs), spendable_outputs, htlc_updated)
1033+
Err(_) => return (txn_to_broadcast, (commitment_txid, watch_outputs), spendable_outputs)
10381034
}
10391035
};
10401036
}
@@ -1059,7 +1055,7 @@ impl ChannelMonitor {
10591055
};
10601056
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()));
10611057
let a_htlc_key = match self.their_htlc_base_key {
1062-
None => return (txn_to_broadcast, (commitment_txid, watch_outputs), spendable_outputs, htlc_updated),
1058+
None => return (txn_to_broadcast, (commitment_txid, watch_outputs), spendable_outputs),
10631059
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)),
10641060
};
10651061

@@ -1139,7 +1135,7 @@ impl ChannelMonitor {
11391135
if transaction_output_index as usize >= tx.output.len() ||
11401136
tx.output[transaction_output_index as usize].value != htlc.amount_msat / 1000 ||
11411137
tx.output[transaction_output_index as usize].script_pubkey != expected_script.to_v0_p2wsh() {
1142-
return (txn_to_broadcast, (commitment_txid, watch_outputs), spendable_outputs, htlc_updated); // Corrupted per_commitment_data, fuck this user
1138+
return (txn_to_broadcast, (commitment_txid, watch_outputs), spendable_outputs); // Corrupted per_commitment_data, fuck this user
11431139
}
11441140
let input = TxIn {
11451141
previous_output: BitcoinOutPoint {
@@ -1179,16 +1175,22 @@ impl ChannelMonitor {
11791175
watch_outputs.append(&mut tx.output.clone());
11801176
self.remote_commitment_txn_on_chain.insert(commitment_txid, (commitment_number, tx.output.iter().map(|output| { output.script_pubkey.clone() }).collect()));
11811177

1182-
// TODO: We really should only fail backwards after our revocation claims have been
1183-
// confirmed, but we also need to do more other tracking of in-flight pre-confirm
1184-
// on-chain claims, so we can do that at the same time.
11851178
macro_rules! check_htlc_fails {
11861179
($txid: expr, $commitment_tx: expr) => {
11871180
if let Some(ref outpoints) = self.remote_claimable_outpoints.get($txid) {
11881181
for &(ref htlc, ref source_option) in outpoints.iter() {
11891182
if let &Some(ref source) = source_option {
1190-
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);
1191-
htlc_updated.push(((**source).clone(), None, htlc.payment_hash.clone()));
1183+
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);
1184+
match self.htlc_updated_waiting_threshold_conf.entry(height + HTLC_FAIL_ANTI_REORG_DELAY - 1) {
1185+
hash_map::Entry::Occupied(mut entry) => {
1186+
let e = entry.get_mut();
1187+
e.retain(|ref update| update.0 != **source);
1188+
e.push(((**source).clone(), None, htlc.payment_hash.clone()));
1189+
}
1190+
hash_map::Entry::Vacant(entry) => {
1191+
entry.insert(vec![((**source).clone(), None, htlc.payment_hash.clone())]);
1192+
}
1193+
}
11921194
}
11931195
}
11941196
}
@@ -1204,7 +1206,7 @@ impl ChannelMonitor {
12041206
}
12051207
// No need to check local commitment txn, symmetric HTLCSource must be present as per-htlc data on remote commitment tx
12061208
}
1207-
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
1209+
if inputs.is_empty() { return (txn_to_broadcast, (commitment_txid, watch_outputs), spendable_outputs); } // Nothing to be done...probably a false positive/local tx
12081210

12091211
let outputs = vec!(TxOut {
12101212
script_pubkey: self.destination_script.clone(),
@@ -1243,9 +1245,6 @@ impl ChannelMonitor {
12431245

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

1246-
// TODO: We really should only fail backwards after our revocation claims have been
1247-
// confirmed, but we also need to do more other tracking of in-flight pre-confirm
1248-
// on-chain claims, so we can do that at the same time.
12491248
macro_rules! check_htlc_fails {
12501249
($txid: expr, $commitment_tx: expr, $id: tt) => {
12511250
if let Some(ref latest_outpoints) = self.remote_claimable_outpoints.get($txid) {
@@ -1266,7 +1265,16 @@ impl ChannelMonitor {
12661265
}
12671266
}
12681267
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);
1269-
htlc_updated.push(((**source).clone(), None, htlc.payment_hash.clone()));
1268+
match self.htlc_updated_waiting_threshold_conf.entry(height + HTLC_FAIL_ANTI_REORG_DELAY - 1) {
1269+
hash_map::Entry::Occupied(mut entry) => {
1270+
let e = entry.get_mut();
1271+
e.retain(|ref update| update.0 != **source);
1272+
e.push(((**source).clone(), None, htlc.payment_hash.clone()));
1273+
}
1274+
hash_map::Entry::Vacant(entry) => {
1275+
entry.insert(vec![((**source).clone(), None, htlc.payment_hash.clone())]);
1276+
}
1277+
}
12701278
}
12711279
}
12721280
}
@@ -1299,7 +1307,7 @@ impl ChannelMonitor {
12991307
},
13001308
};
13011309
let a_htlc_key = match self.their_htlc_base_key {
1302-
None => return (txn_to_broadcast, (commitment_txid, watch_outputs), spendable_outputs, htlc_updated),
1310+
None => return (txn_to_broadcast, (commitment_txid, watch_outputs), spendable_outputs),
13031311
Some(their_htlc_base_key) => ignore_error!(chan_utils::derive_public_key(&self.secp_ctx, revocation_point, &their_htlc_base_key)),
13041312
};
13051313

@@ -1354,7 +1362,7 @@ impl ChannelMonitor {
13541362
if transaction_output_index as usize >= tx.output.len() ||
13551363
tx.output[transaction_output_index as usize].value != htlc.amount_msat / 1000 ||
13561364
tx.output[transaction_output_index as usize].script_pubkey != expected_script.to_v0_p2wsh() {
1357-
return (txn_to_broadcast, (commitment_txid, watch_outputs), spendable_outputs, htlc_updated); // Corrupted per_commitment_data, fuck this user
1365+
return (txn_to_broadcast, (commitment_txid, watch_outputs), spendable_outputs); // Corrupted per_commitment_data, fuck this user
13581366
}
13591367
if let Some(payment_preimage) = self.payment_preimages.get(&htlc.payment_hash) {
13601368
let input = TxIn {
@@ -1417,7 +1425,7 @@ impl ChannelMonitor {
14171425
}
14181426
}
14191427

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

14221430
let outputs = vec!(TxOut {
14231431
script_pubkey: self.destination_script.clone(),
@@ -1447,7 +1455,7 @@ impl ChannelMonitor {
14471455
}
14481456
}
14491457

1450-
(txn_to_broadcast, (commitment_txid, watch_outputs), spendable_outputs, htlc_updated)
1458+
(txn_to_broadcast, (commitment_txid, watch_outputs), spendable_outputs)
14511459
}
14521460

14531461
/// Attempts to claim a remote HTLC-Success/HTLC-Timeout's outputs using the revocation key
@@ -1719,7 +1727,7 @@ impl ChannelMonitor {
17191727
}
17201728
};
17211729
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) {
1722-
let (remote_txn, new_outputs, mut spendable_output, mut updated) = self.check_spend_remote_transaction(tx, height);
1730+
let (remote_txn, new_outputs, mut spendable_output) = self.check_spend_remote_transaction(tx, height);
17231731
txn = remote_txn;
17241732
spendable_outputs.append(&mut spendable_output);
17251733
if !new_outputs.1.is_empty() {
@@ -1738,9 +1746,6 @@ impl ChannelMonitor {
17381746
spendable_outputs.push(spendable_output);
17391747
}
17401748
}
1741-
if updated.len() > 0 {
1742-
htlc_updated.append(&mut updated);
1743-
}
17441749
} else {
17451750
if let Some(&(commitment_number, _)) = self.remote_commitment_txn_on_chain.get(&prevout.txid) {
17461751
let (tx, spendable_output) = self.check_spend_remote_htlc(tx, commitment_number);
@@ -1791,6 +1796,12 @@ impl ChannelMonitor {
17911796
}
17921797
}
17931798
}
1799+
if let Some(updates) = self.htlc_updated_waiting_threshold_conf.remove(&height) {
1800+
for update in updates {
1801+
log_trace!(self, "HTLC {} failure update has get enough confirmation to be pass upstream", log_bytes!((update.2).0));
1802+
htlc_updated.push(update);
1803+
}
1804+
}
17941805
self.last_block_hash = block_hash.clone();
17951806
(watch_outputs, spendable_outputs, htlc_updated)
17961807
}
@@ -2191,6 +2202,21 @@ impl<R: ::std::io::Read> ReadableArgs<R, Arc<Logger>> for (Sha256dHash, ChannelM
21912202
let last_block_hash: Sha256dHash = Readable::read(reader)?;
21922203
let destination_script = Readable::read(reader)?;
21932204

2205+
let waiting_threshold_conf_len: u64 = Readable::read(reader)?;
2206+
let mut htlc_updated_waiting_threshold_conf = HashMap::with_capacity(cmp::min(waiting_threshold_conf_len as usize, MAX_ALLOC_SIZE / 128));
2207+
for _ in 0..waiting_threshold_conf_len {
2208+
let height_target = Readable::read(reader)?;
2209+
let updates_len: u64 = Readable::read(reader)?;
2210+
let mut updates = Vec::with_capacity(cmp::min(updates_len as usize, MAX_ALLOC_SIZE / 128));
2211+
for _ in 0..updates_len {
2212+
let htlc_source = Readable::read(reader)?;
2213+
let preimage = Readable::read(reader)?;
2214+
let hash = Readable::read(reader)?;
2215+
updates.push((htlc_source, preimage, hash));
2216+
}
2217+
htlc_updated_waiting_threshold_conf.insert(height_target, updates);
2218+
}
2219+
21942220
Ok((last_block_hash.clone(), ChannelMonitor {
21952221
commitment_transaction_number_obscure_factor,
21962222

@@ -2214,6 +2240,9 @@ impl<R: ::std::io::Read> ReadableArgs<R, Arc<Logger>> for (Sha256dHash, ChannelM
22142240
payment_preimages,
22152241

22162242
destination_script,
2243+
2244+
htlc_updated_waiting_threshold_conf,
2245+
22172246
last_block_hash,
22182247
secp_ctx,
22192248
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)