Skip to content

Commit 8cc9410

Browse files
authored
Merge pull request #932 from TheBlueMatt/2021-05-broadcast-locktime-delay
Broadcast transactions only after their timelock is up
2 parents df829a8 + d51d606 commit 8cc9410

File tree

11 files changed

+480
-309
lines changed

11 files changed

+480
-309
lines changed

fuzz/src/full_stack.rs

Lines changed: 1 addition & 2 deletions
Large diffs are not rendered by default.

lightning-background-processor/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,7 @@ mod tests {
238238
fn create_nodes(num_nodes: usize, persist_dir: String) -> Vec<Node> {
239239
let mut nodes = Vec::new();
240240
for i in 0..num_nodes {
241-
let tx_broadcaster = Arc::new(test_utils::TestBroadcaster{txn_broadcasted: Mutex::new(Vec::new())});
241+
let tx_broadcaster = Arc::new(test_utils::TestBroadcaster{txn_broadcasted: Mutex::new(Vec::new()), blocks: Arc::new(Mutex::new(Vec::new()))});
242242
let fee_estimator = Arc::new(test_utils::TestFeeEstimator { sat_per_kw: 253 });
243243
let chain_source = Arc::new(test_utils::TestChainSource::new(Network::Testnet));
244244
let logger = Arc::new(test_utils::TestLogger::with_id(format!("node {}", i)));

lightning/src/chain/channelmonitor.rs

Lines changed: 29 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1387,7 +1387,7 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
13871387
macro_rules! claim_htlcs {
13881388
($commitment_number: expr, $txid: expr) => {
13891389
let htlc_claim_reqs = self.get_counterparty_htlc_output_claim_reqs($commitment_number, $txid, None);
1390-
self.onchain_tx_handler.update_claims_view(&Vec::new(), htlc_claim_reqs, None, broadcaster, fee_estimator, logger);
1390+
self.onchain_tx_handler.update_claims_view(&Vec::new(), htlc_claim_reqs, self.best_block.height(), broadcaster, fee_estimator, logger);
13911391
}
13921392
}
13931393
if let Some(txid) = self.current_counterparty_commitment_txid {
@@ -1410,10 +1410,10 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
14101410
// holder commitment transactions.
14111411
if self.broadcasted_holder_revokable_script.is_some() {
14121412
let (claim_reqs, _) = self.get_broadcasted_holder_claims(&self.current_holder_commitment_tx, 0);
1413-
self.onchain_tx_handler.update_claims_view(&Vec::new(), claim_reqs, None, broadcaster, fee_estimator, logger);
1413+
self.onchain_tx_handler.update_claims_view(&Vec::new(), claim_reqs, self.best_block.height(), broadcaster, fee_estimator, logger);
14141414
if let Some(ref tx) = self.prev_holder_signed_commitment_tx {
14151415
let (claim_reqs, _) = self.get_broadcasted_holder_claims(&tx, 0);
1416-
self.onchain_tx_handler.update_claims_view(&Vec::new(), claim_reqs, None, broadcaster, fee_estimator, logger);
1416+
self.onchain_tx_handler.update_claims_view(&Vec::new(), claim_reqs, self.best_block.height(), broadcaster, fee_estimator, logger);
14171417
}
14181418
}
14191419
}
@@ -1786,14 +1786,17 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
17861786

17871787
for &(ref htlc, _, _) in holder_tx.htlc_outputs.iter() {
17881788
if let Some(transaction_output_index) = htlc.transaction_output_index {
1789-
let htlc_output = HolderHTLCOutput::build(if !htlc.offered {
1790-
if let Some(preimage) = self.payment_preimages.get(&htlc.payment_hash) {
1791-
Some(preimage.clone())
1789+
let htlc_output = if htlc.offered {
1790+
HolderHTLCOutput::build_offered(htlc.amount_msat, htlc.cltv_expiry)
17921791
} else {
1793-
// We can't build an HTLC-Success transaction without the preimage
1794-
continue;
1795-
}
1796-
} else { None }, htlc.amount_msat);
1792+
let payment_preimage = if let Some(preimage) = self.payment_preimages.get(&htlc.payment_hash) {
1793+
preimage.clone()
1794+
} else {
1795+
// We can't build an HTLC-Success transaction without the preimage
1796+
continue;
1797+
};
1798+
HolderHTLCOutput::build_accepted(payment_preimage, htlc.amount_msat)
1799+
};
17971800
let htlc_package = PackageTemplate::build_package(holder_tx.txid, transaction_output_index, PackageSolvingData::HolderHTLCOutput(htlc_output), height, false, height);
17981801
claim_requests.push(htlc_package);
17991802
}
@@ -1896,32 +1899,40 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
18961899
self.holder_tx_signed = true;
18971900
let commitment_tx = self.onchain_tx_handler.get_fully_signed_holder_tx(&self.funding_redeemscript);
18981901
let txid = commitment_tx.txid();
1899-
let mut res = vec![commitment_tx];
1902+
let mut holder_transactions = vec![commitment_tx];
19001903
for htlc in self.current_holder_commitment_tx.htlc_outputs.iter() {
19011904
if let Some(vout) = htlc.0.transaction_output_index {
19021905
let preimage = if !htlc.0.offered {
19031906
if let Some(preimage) = self.payment_preimages.get(&htlc.0.payment_hash) { Some(preimage.clone()) } else {
19041907
// We can't build an HTLC-Success transaction without the preimage
19051908
continue;
19061909
}
1910+
} else if htlc.0.cltv_expiry > self.best_block.height() + 1 {
1911+
// Don't broadcast HTLC-Timeout transactions immediately as they don't meet the
1912+
// current locktime requirements on-chain. We will broadcast them in
1913+
// `block_confirmed` when `would_broadcast_at_height` returns true.
1914+
// Note that we add + 1 as transactions are broadcastable when they can be
1915+
// confirmed in the next block.
1916+
continue;
19071917
} else { None };
19081918
if let Some(htlc_tx) = self.onchain_tx_handler.get_fully_signed_htlc_tx(
19091919
&::bitcoin::OutPoint { txid, vout }, &preimage) {
1910-
res.push(htlc_tx);
1920+
holder_transactions.push(htlc_tx);
19111921
}
19121922
}
19131923
}
19141924
// We throw away the generated waiting_first_conf data as we aren't (yet) confirmed and we don't actually know what the caller wants to do.
19151925
// The data will be re-generated and tracked in check_spend_holder_transaction if we get a confirmation.
1916-
return res;
1926+
holder_transactions
19171927
}
19181928

19191929
#[cfg(any(test,feature = "unsafe_revoked_tx_signing"))]
1930+
/// Note that this includes possibly-locktimed-in-the-future transactions!
19201931
fn unsafe_get_latest_holder_commitment_txn<L: Deref>(&mut self, logger: &L) -> Vec<Transaction> where L::Target: Logger {
19211932
log_trace!(logger, "Getting signed copy of latest holder commitment transaction!");
19221933
let commitment_tx = self.onchain_tx_handler.get_fully_signed_copy_holder_tx(&self.funding_redeemscript);
19231934
let txid = commitment_tx.txid();
1924-
let mut res = vec![commitment_tx];
1935+
let mut holder_transactions = vec![commitment_tx];
19251936
for htlc in self.current_holder_commitment_tx.htlc_outputs.iter() {
19261937
if let Some(vout) = htlc.0.transaction_output_index {
19271938
let preimage = if !htlc.0.offered {
@@ -1932,11 +1943,11 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
19321943
} else { None };
19331944
if let Some(htlc_tx) = self.onchain_tx_handler.unsafe_get_fully_signed_htlc_tx(
19341945
&::bitcoin::OutPoint { txid, vout }, &preimage) {
1935-
res.push(htlc_tx);
1946+
holder_transactions.push(htlc_tx);
19361947
}
19371948
}
19381949
}
1939-
return res
1950+
holder_transactions
19401951
}
19411952

19421953
pub fn block_connected<B: Deref, F: Deref, L: Deref>(&mut self, header: &BlockHeader, txdata: &TransactionData, height: u32, broadcaster: B, fee_estimator: F, logger: L) -> Vec<TransactionOutputs>
@@ -2141,7 +2152,7 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
21412152
}
21422153
}
21432154

2144-
self.onchain_tx_handler.update_claims_view(&txn_matched, claimable_outpoints, Some(height), &&*broadcaster, &&*fee_estimator, &&*logger);
2155+
self.onchain_tx_handler.update_claims_view(&txn_matched, claimable_outpoints, height, &&*broadcaster, &&*fee_estimator, &&*logger);
21452156

21462157
// Determine new outputs to watch by comparing against previously known outputs to watch,
21472158
// updating the latter in the process.
@@ -2918,7 +2929,7 @@ mod tests {
29182929
fn test_prune_preimages() {
29192930
let secp_ctx = Secp256k1::new();
29202931
let logger = Arc::new(TestLogger::new());
2921-
let broadcaster = Arc::new(TestBroadcaster{txn_broadcasted: Mutex::new(Vec::new())});
2932+
let broadcaster = Arc::new(TestBroadcaster{txn_broadcasted: Mutex::new(Vec::new()), blocks: Arc::new(Mutex::new(Vec::new()))});
29222933
let fee_estimator = Arc::new(TestFeeEstimator { sat_per_kw: 253 });
29232934

29242935
let dummy_key = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap());

lightning/src/chain/onchaintx.rs

Lines changed: 58 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ use util::ser::{Readable, ReadableArgs, Writer, Writeable, VecWriter};
3333
use util::byte_utils;
3434

3535
use prelude::*;
36+
use alloc::collections::BTreeMap;
3637
use std::collections::HashMap;
3738
use core::cmp;
3839
use core::ops::Deref;
@@ -165,9 +166,9 @@ pub struct OnchainTxHandler<ChannelSigner: Sign> {
165166
#[cfg(not(test))]
166167
claimable_outpoints: HashMap<BitcoinOutPoint, (Txid, u32)>,
167168

168-
onchain_events_awaiting_threshold_conf: Vec<OnchainEventEntry>,
169+
locktimed_packages: BTreeMap<u32, Vec<PackageTemplate>>,
169170

170-
latest_height: u32,
171+
onchain_events_awaiting_threshold_conf: Vec<OnchainEventEntry>,
171172

172173
pub(super) secp_ctx: Secp256k1<secp256k1::All>,
173174
}
@@ -207,6 +208,15 @@ impl<ChannelSigner: Sign> OnchainTxHandler<ChannelSigner> {
207208
claim_and_height.1.write(writer)?;
208209
}
209210

211+
writer.write_all(&byte_utils::be64_to_array(self.locktimed_packages.len() as u64))?;
212+
for (ref locktime, ref packages) in self.locktimed_packages.iter() {
213+
locktime.write(writer)?;
214+
writer.write_all(&byte_utils::be64_to_array(packages.len() as u64))?;
215+
for ref package in packages.iter() {
216+
package.write(writer)?;
217+
}
218+
}
219+
210220
writer.write_all(&byte_utils::be64_to_array(self.onchain_events_awaiting_threshold_conf.len() as u64))?;
211221
for ref entry in self.onchain_events_awaiting_threshold_conf.iter() {
212222
entry.txid.write(writer)?;
@@ -222,7 +232,6 @@ impl<ChannelSigner: Sign> OnchainTxHandler<ChannelSigner> {
222232
}
223233
}
224234
}
225-
self.latest_height.write(writer)?;
226235

227236
write_tlv_fields!(writer, {}, {});
228237
Ok(())
@@ -267,6 +276,19 @@ impl<'a, K: KeysInterface> ReadableArgs<&'a K> for OnchainTxHandler<K::Signer> {
267276
let height = Readable::read(reader)?;
268277
claimable_outpoints.insert(outpoint, (ancestor_claim_txid, height));
269278
}
279+
280+
let locktimed_packages_len: u64 = Readable::read(reader)?;
281+
let mut locktimed_packages = BTreeMap::new();
282+
for _ in 0..locktimed_packages_len {
283+
let locktime = Readable::read(reader)?;
284+
let packages_len: u64 = Readable::read(reader)?;
285+
let mut packages = Vec::with_capacity(cmp::min(packages_len as usize, MAX_ALLOC_SIZE / std::mem::size_of::<PackageTemplate>()));
286+
for _ in 0..packages_len {
287+
packages.push(Readable::read(reader)?);
288+
}
289+
locktimed_packages.insert(locktime, packages);
290+
}
291+
270292
let waiting_threshold_conf_len: u64 = Readable::read(reader)?;
271293
let mut onchain_events_awaiting_threshold_conf = Vec::with_capacity(cmp::min(waiting_threshold_conf_len as usize, MAX_ALLOC_SIZE / 128));
272294
for _ in 0..waiting_threshold_conf_len {
@@ -289,7 +311,6 @@ impl<'a, K: KeysInterface> ReadableArgs<&'a K> for OnchainTxHandler<K::Signer> {
289311
};
290312
onchain_events_awaiting_threshold_conf.push(OnchainEventEntry { txid, height, event });
291313
}
292-
let latest_height = Readable::read(reader)?;
293314

294315
read_tlv_fields!(reader, {}, {});
295316

@@ -305,9 +326,9 @@ impl<'a, K: KeysInterface> ReadableArgs<&'a K> for OnchainTxHandler<K::Signer> {
305326
signer,
306327
channel_transaction_parameters: channel_parameters,
307328
claimable_outpoints,
329+
locktimed_packages,
308330
pending_claim_requests,
309331
onchain_events_awaiting_threshold_conf,
310-
latest_height,
311332
secp_ctx,
312333
})
313334
}
@@ -325,8 +346,8 @@ impl<ChannelSigner: Sign> OnchainTxHandler<ChannelSigner> {
325346
channel_transaction_parameters: channel_parameters,
326347
pending_claim_requests: HashMap::new(),
327348
claimable_outpoints: HashMap::new(),
349+
locktimed_packages: BTreeMap::new(),
328350
onchain_events_awaiting_threshold_conf: Vec::new(),
329-
latest_height: 0,
330351

331352
secp_ctx,
332353
}
@@ -345,10 +366,9 @@ impl<ChannelSigner: Sign> OnchainTxHandler<ChannelSigner> {
345366
// Compute new height timer to decide when we need to regenerate a new bumped version of the claim tx (if we
346367
// didn't receive confirmation of it before, or not enough reorg-safe depth on top of it).
347368
let new_timer = Some(cached_request.get_height_timer(height));
348-
let amt = cached_request.package_amount();
349369
if cached_request.is_malleable() {
350370
let predicted_weight = cached_request.package_weight(&self.destination_script);
351-
if let Some((output_value, new_feerate)) = cached_request.compute_package_output(predicted_weight, amt, fee_estimator, logger) {
371+
if let Some((output_value, new_feerate)) = cached_request.compute_package_output(predicted_weight, fee_estimator, logger) {
352372
assert!(new_feerate != 0);
353373

354374
let transaction = cached_request.finalize_package(self, output_value, self.destination_script.clone(), logger).unwrap();
@@ -360,8 +380,7 @@ impl<ChannelSigner: Sign> OnchainTxHandler<ChannelSigner> {
360380
// Note: Currently, amounts of holder outputs spending witnesses aren't used
361381
// as we can't malleate spending package to increase their feerate. This
362382
// should change with the remaining anchor output patchset.
363-
debug_assert!(amt == 0);
364-
if let Some(transaction) = cached_request.finalize_package(self, amt, self.destination_script.clone(), logger) {
383+
if let Some(transaction) = cached_request.finalize_package(self, 0, self.destination_script.clone(), logger) {
365384
return Some((None, 0, transaction));
366385
}
367386
}
@@ -372,15 +391,11 @@ impl<ChannelSigner: Sign> OnchainTxHandler<ChannelSigner> {
372391
/// for this channel, provide new relevant on-chain transactions and/or new claim requests.
373392
/// Formerly this was named `block_connected`, but it is now also used for claiming an HTLC output
374393
/// if we receive a preimage after force-close.
375-
pub(crate) fn update_claims_view<B: Deref, F: Deref, L: Deref>(&mut self, txn_matched: &[&Transaction], requests: Vec<PackageTemplate>, latest_height: Option<u32>, broadcaster: &B, fee_estimator: &F, logger: &L)
394+
pub(crate) fn update_claims_view<B: Deref, F: Deref, L: Deref>(&mut self, txn_matched: &[&Transaction], requests: Vec<PackageTemplate>, height: u32, broadcaster: &B, fee_estimator: &F, logger: &L)
376395
where B::Target: BroadcasterInterface,
377396
F::Target: FeeEstimator,
378397
L::Target: Logger,
379398
{
380-
let height = match latest_height {
381-
Some(h) => h,
382-
None => self.latest_height,
383-
};
384399
log_trace!(logger, "Updating claims view at height {} with {} matched transactions and {} claim requests", height, txn_matched.len(), requests.len());
385400
let mut preprocessed_requests = Vec::with_capacity(requests.len());
386401
let mut aggregated_request = None;
@@ -389,7 +404,26 @@ impl<ChannelSigner: Sign> OnchainTxHandler<ChannelSigner> {
389404
// <= CLTV_SHARED_CLAIM_BUFFER) and they don't require an immediate nLockTime (aggregable).
390405
for req in requests {
391406
// Don't claim a outpoint twice that would be bad for privacy and may uselessly lock a CPFP input for a while
392-
if let Some(_) = self.claimable_outpoints.get(req.outpoints()[0]) { log_trace!(logger, "Bouncing off outpoint {}:{}, already registered its claiming request", req.outpoints()[0].txid, req.outpoints()[0].vout); } else {
407+
if let Some(_) = self.claimable_outpoints.get(req.outpoints()[0]) {
408+
log_trace!(logger, "Ignoring second claim for outpoint {}:{}, already registered its claiming request", req.outpoints()[0].txid, req.outpoints()[0].vout);
409+
} else {
410+
let timelocked_equivalent_package = self.locktimed_packages.iter().map(|v| v.1.iter()).flatten()
411+
.find(|locked_package| locked_package.outpoints() == req.outpoints());
412+
if let Some(package) = timelocked_equivalent_package {
413+
log_trace!(logger, "Ignoring second claim for outpoint {}:{}, we already have one which we're waiting on a timelock at {} for.",
414+
req.outpoints()[0].txid, req.outpoints()[0].vout, package.package_timelock());
415+
continue;
416+
}
417+
418+
if req.package_timelock() > height + 1 {
419+
log_debug!(logger, "Delaying claim of package until its timelock at {} (current height {}), the following outpoints are spent:", req.package_timelock(), height);
420+
for outpoint in req.outpoints() {
421+
log_debug!(logger, " Outpoint {}", outpoint);
422+
}
423+
self.locktimed_packages.entry(req.package_timelock()).or_insert(Vec::new()).push(req);
424+
continue;
425+
}
426+
393427
log_trace!(logger, "Test if outpoint can be aggregated with expiration {} against {}", req.timelock(), height + CLTV_SHARED_CLAIM_BUFFER);
394428
if req.timelock() <= height + CLTV_SHARED_CLAIM_BUFFER || !req.aggregable() {
395429
// Don't aggregate if outpoint package timelock is soon or marked as non-aggregable
@@ -405,6 +439,14 @@ impl<ChannelSigner: Sign> OnchainTxHandler<ChannelSigner> {
405439
preprocessed_requests.push(req);
406440
}
407441

442+
// Claim everything up to and including height + 1
443+
let remaining_locked_packages = self.locktimed_packages.split_off(&(height + 2));
444+
for (pop_height, mut entry) in self.locktimed_packages.iter_mut() {
445+
log_trace!(logger, "Restoring delayed claim of package(s) at their timelock at {}.", pop_height);
446+
preprocessed_requests.append(&mut entry);
447+
}
448+
self.locktimed_packages = remaining_locked_packages;
449+
408450
// Generate claim transactions and track them to bump if necessary at
409451
// height timer expiration (i.e in how many blocks we're going to take action).
410452
for mut req in preprocessed_requests {

0 commit comments

Comments
 (0)