Skip to content

Commit

Permalink
Only yield DelayedPaymentOutput descriptors once their delay expires
Browse files Browse the repository at this point in the history
Otherwise, we could give users a descriptor ahead of time that will
result in an invalid transaction spend/broadcast.
  • Loading branch information
wpaulino committed Sep 29, 2023
1 parent b19abe4 commit 1f9ede3
Show file tree
Hide file tree
Showing 2 changed files with 20 additions and 8 deletions.
15 changes: 10 additions & 5 deletions lightning/src/chain/channelmonitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1672,6 +1672,8 @@ impl<Signer: WriteableEcdsaChannelSigner> ChannelMonitor<Signer> {

/// Returns the descriptors for relevant outputs (i.e., those that we can spend) within the
/// transaction if they exist and the transaction has at least [`ANTI_REORG_DELAY`]
/// confirmations. For [`SpendableOutputDescriptor::DelayedPaymentOutput`] descriptors to be
/// returned, the transaction must have at least `max(ANTI_REORG_DELAY, to_self_delay)`
/// confirmations.
///
/// Descriptors returned by this method are primarily exposed via [`Event::SpendableOutputs`]
Expand All @@ -1690,11 +1692,14 @@ impl<Signer: WriteableEcdsaChannelSigner> ChannelMonitor<Signer> {
pub fn get_spendable_outputs(&self, tx: &Transaction, confirmation_height: u32) -> Vec<SpendableOutputDescriptor> {
let inner = self.inner.lock().unwrap();
let current_height = inner.best_block.height;
if current_height.saturating_sub(ANTI_REORG_DELAY) + 1 >= confirmation_height {
inner.get_spendable_outputs(tx)
} else {
Vec::new()
}
inner.get_spendable_outputs(tx).into_iter().filter(|descriptor| {
let mut conf_threshold = current_height.saturating_sub(ANTI_REORG_DELAY) + 1;
if let SpendableOutputDescriptor::DelayedPaymentOutput(descriptor) = descriptor {
conf_threshold = cmp::min(conf_threshold,
current_height.saturating_sub(descriptor.to_self_delay as u32) + 1);
}
conf_threshold >= confirmation_height
}).collect()
}
}

Expand Down
13 changes: 10 additions & 3 deletions lightning/src/ln/monitor_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -637,7 +637,7 @@ fn test_balances_on_local_commitment_htlcs() {
// First confirm the commitment transaction on nodes[0], which should leave us with three
// claimable balances.
let node_a_commitment_claimable = nodes[0].best_block_info().1 + BREAKDOWN_TIMEOUT as u32;
mine_transaction(&nodes[0], &as_txn[0]);
let commitment_tx_conf_height_a = block_from_scid(&mine_transaction(&nodes[0], &as_txn[0]));
check_added_monitors!(nodes[0], 1);
check_closed_broadcast!(nodes[0], true);
check_closed_event!(nodes[0], 1, ClosureReason::CommitmentTxConfirmed, [nodes[1].node.get_our_node_id()], 1000000);
Expand Down Expand Up @@ -729,13 +729,20 @@ fn test_balances_on_local_commitment_htlcs() {

// Connect blocks until the commitment transaction's CSV expires, providing us the relevant
// `SpendableOutputs` event and removing the claimable balance entry.
connect_blocks(&nodes[0], node_a_commitment_claimable - nodes[0].best_block_info().1);
connect_blocks(&nodes[0], node_a_commitment_claimable - nodes[0].best_block_info().1 - 1);
assert!(get_monitor!(nodes[0], chan_id)
.get_spendable_outputs(&as_txn[0], commitment_tx_conf_height_a).is_empty());
connect_blocks(&nodes[0], 1);
assert_eq!(vec![Balance::ClaimableAwaitingConfirmations {
amount_satoshis: 10_000,
confirmation_height: node_a_htlc_claimable,
}],
nodes[0].chain_monitor.chain_monitor.get_monitor(funding_outpoint).unwrap().get_claimable_balances());
test_spendable_output(&nodes[0], &as_txn[0]);
let to_self_spendable_output = test_spendable_output(&nodes[0], &as_txn[0]);
assert_eq!(
get_monitor!(nodes[0], chan_id).get_spendable_outputs(&as_txn[0], commitment_tx_conf_height_a),
to_self_spendable_output
);

// Connect blocks until the HTLC-Timeout's CSV expires, providing us the relevant
// `SpendableOutputs` event and removing the claimable balance entry.
Expand Down

0 comments on commit 1f9ede3

Please sign in to comment.