Skip to content

Commit 4dea907

Browse files
committed
Allow holder commitment and HTLC signature requests to fail
As part of the ongoing async signer work, our holder signatures must also be capable of being obtained asynchronously. We expose a new `ChannelMonitor::signer_unblocked` method to retry pending onchain claims by re-signing and rebroadcasting transactions. Unfortunately, we cannot retry said claims without them being registered first, so if we're not able to obtain the signature synchronously, we must return the transaction as unsigned and ensure it is not broadcast.
1 parent 67c140b commit 4dea907

9 files changed

+251
-41
lines changed

lightning/src/chain/channelmonitor.rs

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1769,6 +1769,25 @@ impl<Signer: WriteableEcdsaChannelSigner> ChannelMonitor<Signer> {
17691769
);
17701770
}
17711771

1772+
/// Triggers rebroadcasts of pending claims from a force-closed channel after a transaction
1773+
/// signature generation failure.
1774+
pub fn signer_unblocked<B: Deref, F: Deref, L: Deref>(
1775+
&self, broadcaster: B, fee_estimator: F, logger: &L,
1776+
)
1777+
where
1778+
B::Target: BroadcasterInterface,
1779+
F::Target: FeeEstimator,
1780+
L::Target: Logger,
1781+
{
1782+
let fee_estimator = LowerBoundedFeeEstimator::new(fee_estimator);
1783+
let mut inner = self.inner.lock().unwrap();
1784+
let logger = WithChannelMonitor::from_impl(logger, &*inner);
1785+
let current_height = inner.best_block.height;
1786+
inner.onchain_tx_handler.rebroadcast_pending_claims(
1787+
current_height, FeerateStrategy::RetryPrevious, &broadcaster, &fee_estimator, &logger,
1788+
);
1789+
}
1790+
17721791
/// Returns the descriptors for relevant outputs (i.e., those that we can spend) within the
17731792
/// transaction if they exist and the transaction has at least [`ANTI_REORG_DELAY`]
17741793
/// confirmations. For [`SpendableOutputDescriptor::DelayedPaymentOutput`] descriptors to be
@@ -1811,6 +1830,12 @@ impl<Signer: WriteableEcdsaChannelSigner> ChannelMonitor<Signer> {
18111830
pub fn set_counterparty_payment_script(&self, script: ScriptBuf) {
18121831
self.inner.lock().unwrap().counterparty_payment_script = script;
18131832
}
1833+
1834+
#[cfg(test)]
1835+
pub fn do_signer_call<F: FnMut(&Signer) -> ()>(&self, mut f: F) {
1836+
let inner = self.inner.lock().unwrap();
1837+
f(&inner.onchain_tx_handler.signer);
1838+
}
18141839
}
18151840

18161841
impl<Signer: WriteableEcdsaChannelSigner> ChannelMonitorImpl<Signer> {
@@ -3508,9 +3533,12 @@ impl<Signer: WriteableEcdsaChannelSigner> ChannelMonitorImpl<Signer> {
35083533
continue;
35093534
}
35103535
} else { None };
3511-
if let Some(htlc_tx) = self.onchain_tx_handler.get_fully_signed_htlc_tx(
3512-
&::bitcoin::OutPoint { txid, vout }, &preimage) {
3513-
holder_transactions.push(htlc_tx);
3536+
if let Some(htlc_tx) = self.onchain_tx_handler.get_maybe_signed_htlc_tx(
3537+
&::bitcoin::OutPoint { txid, vout }, &preimage
3538+
) {
3539+
if !htlc_tx.input.iter().any(|input| input.witness.is_empty()) {
3540+
holder_transactions.push(htlc_tx);
3541+
}
35143542
}
35153543
}
35163544
}

lightning/src/chain/onchaintx.rs

Lines changed: 40 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,8 @@ pub(crate) enum OnchainClaim {
212212

213213
/// Represents the different feerates a pending request can use when generating a claim.
214214
pub(crate) enum FeerateStrategy {
215+
/// We must reuse the most recently used feerate, if any.
216+
RetryPrevious,
215217
/// We must pick the highest between the most recently used and the current feerate estimate.
216218
HighestOfPreviousOrNew,
217219
/// We must force a bump of the most recently used feerate, either by using the current feerate
@@ -506,9 +508,13 @@ impl<ChannelSigner: WriteableEcdsaChannelSigner> OnchainTxHandler<ChannelSigner>
506508
}
507509
match claim {
508510
OnchainClaim::Tx(tx) => {
509-
let log_start = if bumped_feerate { "Broadcasting RBF-bumped" } else { "Rebroadcasting" };
510-
log_info!(logger, "{} onchain {}", log_start, log_tx!(tx));
511-
broadcaster.broadcast_transactions(&[&tx]);
511+
if tx.input.iter().any(|input| input.witness.is_empty()) {
512+
log_info!(logger, "Waiting for signature of unsigned onchain transaction {}", tx.txid());
513+
} else {
514+
let log_start = if bumped_feerate { "Broadcasting RBF-bumped" } else { "Rebroadcasting" };
515+
log_info!(logger, "{} onchain {}", log_start, log_tx!(tx));
516+
broadcaster.broadcast_transactions(&[&tx]);
517+
}
512518
},
513519
OnchainClaim::Event(event) => {
514520
let log_start = if bumped_feerate { "Yielding fee-bumped" } else { "Replaying" };
@@ -645,8 +651,8 @@ impl<ChannelSigner: WriteableEcdsaChannelSigner> OnchainTxHandler<ChannelSigner>
645651
let commitment_tx_feerate_sat_per_1000_weight =
646652
compute_feerate_sat_per_1000_weight(fee_sat, tx.weight().to_wu());
647653
if commitment_tx_feerate_sat_per_1000_weight >= package_target_feerate_sat_per_1000_weight {
648-
log_debug!(logger, "Pre-signed {} already has feerate {} sat/kW above required {} sat/kW",
649-
log_tx!(tx), commitment_tx_feerate_sat_per_1000_weight,
654+
log_debug!(logger, "Pre-signed commitment {} already has feerate {} sat/kW above required {} sat/kW",
655+
tx.txid(), commitment_tx_feerate_sat_per_1000_weight,
650656
package_target_feerate_sat_per_1000_weight);
651657
return Some((new_timer, 0, OnchainClaim::Tx(tx.clone())));
652658
}
@@ -785,8 +791,12 @@ impl<ChannelSigner: WriteableEcdsaChannelSigner> OnchainTxHandler<ChannelSigner>
785791
// `OnchainClaim`.
786792
let claim_id = match claim {
787793
OnchainClaim::Tx(tx) => {
788-
log_info!(logger, "Broadcasting onchain {}", log_tx!(tx));
789-
broadcaster.broadcast_transactions(&[&tx]);
794+
if tx.input.iter().any(|input| input.witness.is_empty()) {
795+
log_info!(logger, "Waiting for signature of unsigned onchain transaction {}", tx.txid());
796+
} else {
797+
log_info!(logger, "Broadcasting onchain {}", log_tx!(tx));
798+
broadcaster.broadcast_transactions(&[&tx]);
799+
}
790800
ClaimId(tx.txid().to_byte_array())
791801
},
792802
OnchainClaim::Event(claim_event) => {
@@ -978,8 +988,13 @@ impl<ChannelSigner: WriteableEcdsaChannelSigner> OnchainTxHandler<ChannelSigner>
978988
) {
979989
match bump_claim {
980990
OnchainClaim::Tx(bump_tx) => {
981-
log_info!(logger, "Broadcasting RBF-bumped onchain {}", log_tx!(bump_tx));
982-
broadcaster.broadcast_transactions(&[&bump_tx]);
991+
if bump_tx.input.iter().any(|input| input.witness.is_empty()) {
992+
log_info!(logger, "Waiting for signature of RBF-bumped unsigned onchain transaction {}",
993+
bump_tx.txid());
994+
} else {
995+
log_info!(logger, "Broadcasting RBF-bumped onchain {}", log_tx!(bump_tx));
996+
broadcaster.broadcast_transactions(&[&bump_tx]);
997+
}
983998
},
984999
OnchainClaim::Event(claim_event) => {
9851000
log_info!(logger, "Yielding RBF-bumped onchain event to spend inputs {:?}", request.outpoints());
@@ -1061,8 +1076,12 @@ impl<ChannelSigner: WriteableEcdsaChannelSigner> OnchainTxHandler<ChannelSigner>
10611076
request.set_feerate(new_feerate);
10621077
match bump_claim {
10631078
OnchainClaim::Tx(bump_tx) => {
1064-
log_info!(logger, "Broadcasting onchain {}", log_tx!(bump_tx));
1065-
broadcaster.broadcast_transactions(&[&bump_tx]);
1079+
if bump_tx.input.iter().any(|input| input.witness.is_empty()) {
1080+
log_info!(logger, "Waiting for signature of unsigned onchain transaction {}", bump_tx.txid());
1081+
} else {
1082+
log_info!(logger, "Broadcasting onchain {}", log_tx!(bump_tx));
1083+
broadcaster.broadcast_transactions(&[&bump_tx]);
1084+
}
10661085
},
10671086
OnchainClaim::Event(claim_event) => {
10681087
log_info!(logger, "Yielding onchain event after reorg to spend inputs {:?}", request.outpoints());
@@ -1115,13 +1134,10 @@ impl<ChannelSigner: WriteableEcdsaChannelSigner> OnchainTxHandler<ChannelSigner>
11151134
&self.holder_commitment.trust().built_transaction().transaction
11161135
}
11171136

1118-
//TODO: getting lastest holder transactions should be infallible and result in us "force-closing the channel", but we may
1119-
// have empty holder commitment transaction if a ChannelMonitor is asked to force-close just after OutboundV1Channel::get_funding_created,
1120-
// before providing a initial commitment transaction. For outbound channel, init ChannelMonitor at Channel::funding_signed, there is nothing
1121-
// to monitor before.
1122-
pub(crate) fn get_fully_signed_holder_tx(&mut self, funding_redeemscript: &Script) -> Transaction {
1123-
let sig = self.signer.sign_holder_commitment(&self.holder_commitment, &self.secp_ctx).expect("signing holder commitment");
1124-
self.holder_commitment.add_holder_sig(funding_redeemscript, sig)
1137+
pub(crate) fn get_maybe_signed_holder_tx(&mut self, funding_redeemscript: &Script) -> Transaction {
1138+
self.signer.sign_holder_commitment(&self.holder_commitment, &self.secp_ctx)
1139+
.map(|sig| self.holder_commitment.add_holder_sig(funding_redeemscript, sig))
1140+
.unwrap_or_else(|_| self.get_unsigned_holder_commitment_tx().clone())
11251141
}
11261142

11271143
#[cfg(any(test, feature="unsafe_revoked_tx_signing"))]
@@ -1130,7 +1146,7 @@ impl<ChannelSigner: WriteableEcdsaChannelSigner> OnchainTxHandler<ChannelSigner>
11301146
self.holder_commitment.add_holder_sig(funding_redeemscript, sig)
11311147
}
11321148

1133-
pub(crate) fn get_fully_signed_htlc_tx(&mut self, outp: &::bitcoin::OutPoint, preimage: &Option<PaymentPreimage>) -> Option<Transaction> {
1149+
pub(crate) fn get_maybe_signed_htlc_tx(&mut self, outp: &::bitcoin::OutPoint, preimage: &Option<PaymentPreimage>) -> Option<Transaction> {
11341150
let get_signed_htlc_tx = |holder_commitment: &HolderCommitmentTransaction| {
11351151
let trusted_tx = holder_commitment.trust();
11361152
if trusted_tx.txid() != outp.txid {
@@ -1158,10 +1174,11 @@ impl<ChannelSigner: WriteableEcdsaChannelSigner> OnchainTxHandler<ChannelSigner>
11581174
preimage: preimage.clone(),
11591175
counterparty_sig: counterparty_htlc_sig.clone(),
11601176
};
1161-
let htlc_sig = self.signer.sign_holder_htlc_transaction(&htlc_tx, 0, &htlc_descriptor, &self.secp_ctx).unwrap();
1162-
htlc_tx.input[0].witness = trusted_tx.build_htlc_input_witness(
1163-
htlc_idx, &counterparty_htlc_sig, &htlc_sig, preimage,
1164-
);
1177+
if let Ok(htlc_sig) = self.signer.sign_holder_htlc_transaction(&htlc_tx, 0, &htlc_descriptor, &self.secp_ctx) {
1178+
htlc_tx.input[0].witness = trusted_tx.build_htlc_input_witness(
1179+
htlc_idx, &counterparty_htlc_sig, &htlc_sig, preimage,
1180+
);
1181+
}
11651182
Some(htlc_tx)
11661183
};
11671184

lightning/src/chain/package.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -637,10 +637,10 @@ impl PackageSolvingData {
637637
match self {
638638
PackageSolvingData::HolderHTLCOutput(ref outp) => {
639639
debug_assert!(!outp.channel_type_features.supports_anchors_zero_fee_htlc_tx());
640-
return onchain_handler.get_fully_signed_htlc_tx(outpoint, &outp.preimage);
640+
onchain_handler.get_maybe_signed_htlc_tx(outpoint, &outp.preimage)
641641
}
642642
PackageSolvingData::HolderFundingOutput(ref outp) => {
643-
return Some(onchain_handler.get_fully_signed_holder_tx(&outp.funding_redeemscript));
643+
Some(onchain_handler.get_maybe_signed_holder_tx(&outp.funding_redeemscript))
644644
}
645645
_ => { panic!("API Error!"); }
646646
}
@@ -996,6 +996,7 @@ impl PackageTemplate {
996996
if self.feerate_previous != 0 {
997997
let previous_feerate = self.feerate_previous.try_into().unwrap_or(u32::max_value());
998998
match feerate_strategy {
999+
FeerateStrategy::RetryPrevious => previous_feerate,
9991000
FeerateStrategy::HighestOfPreviousOrNew => cmp::max(previous_feerate, feerate_estimate),
10001001
FeerateStrategy::ForceBump => if feerate_estimate > previous_feerate {
10011002
feerate_estimate
@@ -1141,6 +1142,10 @@ where
11411142
// If old feerate inferior to actual one given back by Fee Estimator, use it to compute new fee...
11421143
let (new_fee, new_feerate) = if let Some((new_fee, new_feerate)) = compute_fee_from_spent_amounts(input_amounts, predicted_weight, fee_estimator, logger) {
11431144
match feerate_strategy {
1145+
FeerateStrategy::RetryPrevious => {
1146+
let previous_fee = previous_feerate * predicted_weight / 1000;
1147+
(previous_fee, previous_feerate)
1148+
},
11441149
FeerateStrategy::HighestOfPreviousOrNew => if new_feerate > previous_feerate {
11451150
(new_fee, new_feerate)
11461151
} else {

lightning/src/ln/async_signer_tests.rs

Lines changed: 110 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,12 @@
1010
//! Tests for asynchronous signing. These tests verify that the channel state machine behaves
1111
//! properly with a signer implementation that asynchronously derives signatures.
1212
13-
use crate::events::{Event, MessageSendEvent, MessageSendEventsProvider};
13+
use bitcoin::{Transaction, TxOut, TxIn, Amount};
14+
use bitcoin::blockdata::locktime::absolute::LockTime;
15+
16+
use crate::chain::channelmonitor::LATENCY_GRACE_PERIOD_BLOCKS;
17+
use crate::events::bump_transaction::WalletSource;
18+
use crate::events::{Event, MessageSendEvent, MessageSendEventsProvider, ClosureReason};
1419
use crate::ln::functional_test_utils::*;
1520
use crate::ln::msgs::ChannelMessageHandler;
1621
use crate::ln::channelmanager::{PaymentId, RecipientOnionFields};
@@ -321,3 +326,107 @@ fn test_async_commitment_signature_for_peer_disconnect() {
321326
};
322327
}
323328
}
329+
330+
fn do_test_async_holder_signatures(anchors: bool) {
331+
// Ensures that we can obtain holder signatures for commitment and HTLC transactions
332+
// asynchronously by allowing their retrieval to fail and retrying via
333+
// `ChannelMonitor::signer_maybe_unblocked`.
334+
let mut config = test_default_channel_config();
335+
if anchors {
336+
config.channel_handshake_config.negotiate_anchors_zero_fee_htlc_tx = true;
337+
config.manually_accept_inbound_channels = true;
338+
}
339+
340+
let chanmon_cfgs = create_chanmon_cfgs(2);
341+
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
342+
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[Some(config), Some(config)]);
343+
let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
344+
345+
let coinbase_tx = Transaction {
346+
version: 2,
347+
lock_time: LockTime::ZERO,
348+
input: vec![TxIn { ..Default::default() }],
349+
output: vec![
350+
TxOut {
351+
value: Amount::ONE_BTC.to_sat(),
352+
script_pubkey: nodes[0].wallet_source.get_change_script().unwrap(),
353+
},
354+
],
355+
};
356+
if anchors {
357+
*nodes[0].fee_estimator.sat_per_kw.lock().unwrap() *= 2;
358+
nodes[0].wallet_source.add_utxo(bitcoin::OutPoint { txid: coinbase_tx.txid(), vout: 0 }, coinbase_tx.output[0].value);
359+
}
360+
361+
// Route an HTLC and set the signer as unavailable.
362+
let (_, _, chan_id, funding_tx) = create_announced_chan_between_nodes(&nodes, 0, 1);
363+
route_payment(&nodes[0], &[&nodes[1]], 1_000_000);
364+
365+
nodes[0].set_channel_signer_available(&nodes[1].node.get_our_node_id(), &chan_id, false);
366+
367+
// We'll connect blocks until the sender has to go onchain to time out the HTLC.
368+
connect_blocks(&nodes[0], TEST_FINAL_CLTV + LATENCY_GRACE_PERIOD_BLOCKS + 1);
369+
370+
// No transaction should be broadcast since the signer is not available yet.
371+
assert!(nodes[0].tx_broadcaster.txn_broadcast().is_empty());
372+
assert!(nodes[0].chain_monitor.chain_monitor.get_and_clear_pending_events().is_empty());
373+
374+
// Mark it as available now, we should see the signed commitment transaction.
375+
nodes[0].set_channel_signer_available(&nodes[1].node.get_our_node_id(), &chan_id, true);
376+
get_monitor!(nodes[0], chan_id).signer_unblocked(nodes[0].tx_broadcaster, nodes[0].fee_estimator, &nodes[0].logger);
377+
378+
let commitment_tx = {
379+
let mut txn = nodes[0].tx_broadcaster.txn_broadcast();
380+
if anchors {
381+
assert_eq!(txn.len(), 1);
382+
check_spends!(txn[0], funding_tx);
383+
txn.remove(0)
384+
} else {
385+
assert_eq!(txn.len(), 2);
386+
if txn[0].input[0].previous_output.txid == funding_tx.txid() {
387+
check_spends!(txn[0], funding_tx);
388+
check_spends!(txn[1], txn[0]);
389+
txn.remove(0)
390+
} else {
391+
check_spends!(txn[1], funding_tx);
392+
check_spends!(txn[0], txn[1]);
393+
txn.remove(1)
394+
}
395+
}
396+
};
397+
398+
// Mark it as unavailable again to now test the HTLC transaction. We'll mine the commitment such
399+
// that the HTLC transaction is retried.
400+
nodes[0].set_channel_signer_available(&nodes[1].node.get_our_node_id(), &chan_id, false);
401+
mine_transaction(&nodes[0], &commitment_tx);
402+
403+
check_added_monitors(&nodes[0], 1);
404+
check_closed_broadcast(&nodes[0], 1, true);
405+
check_closed_event(&nodes[0], 1, ClosureReason::CommitmentTxConfirmed, false, &[nodes[1].node.get_our_node_id()], 100_000);
406+
407+
// No HTLC transaction should be broadcast as the signer is not available yet.
408+
if anchors {
409+
handle_bump_htlc_event(&nodes[0], 1);
410+
}
411+
assert!(nodes[0].tx_broadcaster.txn_broadcast().is_empty());
412+
413+
// Mark it as available now, we should see the signed HTLC transaction.
414+
nodes[0].set_channel_signer_available(&nodes[1].node.get_our_node_id(), &chan_id, true);
415+
get_monitor!(nodes[0], chan_id).signer_unblocked(nodes[0].tx_broadcaster, nodes[0].fee_estimator, &nodes[0].logger);
416+
417+
if anchors {
418+
handle_bump_htlc_event(&nodes[0], 1);
419+
}
420+
{
421+
let mut txn = nodes[0].tx_broadcaster.txn_broadcast();
422+
assert_eq!(txn.len(), 1);
423+
check_spends!(txn[0], commitment_tx, coinbase_tx);
424+
txn.remove(0)
425+
};
426+
}
427+
428+
#[test]
429+
fn test_async_holder_signatures() {
430+
do_test_async_holder_signatures(false);
431+
do_test_async_holder_signatures(true);
432+
}

lightning/src/ln/channel.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1258,7 +1258,10 @@ pub(super) struct ChannelContext<SP: Deref> where SP::Target: SignerProvider {
12581258

12591259
/// The unique identifier used to re-derive the private key material for the channel through
12601260
/// [`SignerProvider::derive_channel_signer`].
1261+
#[cfg(not(test))]
12611262
channel_keys_id: [u8; 32],
1263+
#[cfg(test)]
1264+
pub channel_keys_id: [u8; 32],
12621265

12631266
/// If we can't release a [`ChannelMonitorUpdate`] until some external action completes, we
12641267
/// store it here and only release it to the `ChannelManager` once it asks for it.

lightning/src/ln/functional_test_utils.rs

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -487,16 +487,38 @@ impl<'a, 'b, 'c> Node<'a, 'b, 'c> {
487487
/// `release_commitment_secret` are affected by this setting.
488488
#[cfg(test)]
489489
pub fn set_channel_signer_available(&self, peer_id: &PublicKey, chan_id: &ChannelId, available: bool) {
490+
use crate::sign::ChannelSigner;
491+
log_debug!(self.logger, "Setting channel signer for {} as available={}", chan_id, available);
492+
490493
let per_peer_state = self.node.per_peer_state.read().unwrap();
491494
let chan_lock = per_peer_state.get(peer_id).unwrap().lock().unwrap();
492-
let signer = (|| {
493-
match chan_lock.channel_by_id.get(chan_id) {
494-
Some(phase) => phase.context().get_signer(),
495-
None => panic!("Couldn't find a channel with id {}", chan_id),
495+
496+
let mut channel_keys_id = None;
497+
if let Some(chan) = chan_lock.channel_by_id.get(chan_id).map(|phase| phase.context()) {
498+
chan.get_signer().as_ecdsa().unwrap().set_available(available);
499+
channel_keys_id = Some(chan.channel_keys_id);
500+
}
501+
502+
let mut monitor = None;
503+
for (funding_txo, channel_id) in self.chain_monitor.chain_monitor.list_monitors() {
504+
if *chan_id == channel_id {
505+
monitor = self.chain_monitor.chain_monitor.get_monitor(funding_txo).ok();
496506
}
497-
})();
498-
log_debug!(self.logger, "Setting channel signer for {} as available={}", chan_id, available);
499-
signer.as_ecdsa().unwrap().set_available(available);
507+
}
508+
if let Some(monitor) = monitor {
509+
monitor.do_signer_call(|signer| {
510+
channel_keys_id = channel_keys_id.or(Some(signer.inner.channel_keys_id()));
511+
signer.set_available(available)
512+
});
513+
}
514+
515+
if available {
516+
self.keys_manager.unavailable_signers.lock().unwrap()
517+
.remove(channel_keys_id.as_ref().unwrap());
518+
} else {
519+
self.keys_manager.unavailable_signers.lock().unwrap()
520+
.insert(channel_keys_id.unwrap());
521+
}
500522
}
501523
}
502524

0 commit comments

Comments
 (0)