Skip to content

Commit eb3c4ab

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 eb3c4ab

10 files changed

+308
-50
lines changed

lightning/src/chain/chainmonitor.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -636,6 +636,27 @@ where C::Target: chain::Filter,
636636
)
637637
}
638638
}
639+
640+
/// Triggers rebroadcasts of pending claims from force-closed channels after a transaction
641+
/// signature generation failure.
642+
///
643+
/// `monitor_opt` can be used as a filter to only trigger them for a specific channel monitor.
644+
pub fn signer_unblocked(&self, monitor_opt: Option<OutPoint>) {
645+
let monitors = self.monitors.read().unwrap();
646+
if let Some(funding_txo) = monitor_opt {
647+
if let Some(monitor_holder) = monitors.get(&funding_txo) {
648+
monitor_holder.monitor.signer_unblocked(
649+
&*self.broadcaster, &*self.fee_estimator, &self.logger
650+
)
651+
}
652+
} else {
653+
for (_, monitor_holder) in &*monitors {
654+
monitor_holder.monitor.signer_unblocked(
655+
&*self.broadcaster, &*self.fee_estimator, &self.logger
656+
)
657+
}
658+
}
659+
}
639660
}
640661

641662
impl<ChannelSigner: WriteableEcdsaChannelSigner, C: Deref, T: Deref, F: Deref, L: Deref, P: Deref>

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: 42 additions & 25 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" };
@@ -610,7 +616,7 @@ impl<ChannelSigner: WriteableEcdsaChannelSigner> OnchainTxHandler<ChannelSigner>
610616
) {
611617
assert!(new_feerate != 0);
612618

613-
let transaction = cached_request.finalize_malleable_package(
619+
let transaction = cached_request.maybe_finalize_malleable_package(
614620
cur_height, self, output_value, self.destination_script.clone(), logger
615621
).unwrap();
616622
log_trace!(logger, "...with timer {} and feerate {}", new_timer, new_feerate);
@@ -623,7 +629,7 @@ impl<ChannelSigner: WriteableEcdsaChannelSigner> OnchainTxHandler<ChannelSigner>
623629
// which require external funding.
624630
let mut inputs = cached_request.inputs();
625631
debug_assert_eq!(inputs.len(), 1);
626-
let tx = match cached_request.finalize_untractable_package(self, logger) {
632+
let tx = match cached_request.maybe_finalize_untractable_package(self, logger) {
627633
Some(tx) => tx,
628634
None => return None,
629635
};
@@ -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: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -633,14 +633,14 @@ impl PackageSolvingData {
633633
}
634634
true
635635
}
636-
fn get_finalized_tx<Signer: WriteableEcdsaChannelSigner>(&self, outpoint: &BitcoinOutPoint, onchain_handler: &mut OnchainTxHandler<Signer>) -> Option<Transaction> {
636+
fn get_maybe_finalized_tx<Signer: WriteableEcdsaChannelSigner>(&self, outpoint: &BitcoinOutPoint, onchain_handler: &mut OnchainTxHandler<Signer>) -> Option<Transaction> {
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
}
@@ -908,7 +908,7 @@ impl PackageTemplate {
908908
}
909909
htlcs
910910
}
911-
pub(crate) fn finalize_malleable_package<L: Logger, Signer: WriteableEcdsaChannelSigner>(
911+
pub(crate) fn maybe_finalize_malleable_package<L: Logger, Signer: WriteableEcdsaChannelSigner>(
912912
&self, current_height: u32, onchain_handler: &mut OnchainTxHandler<Signer>, value: u64,
913913
destination_script: ScriptBuf, logger: &L
914914
) -> Option<Transaction> {
@@ -927,19 +927,17 @@ impl PackageTemplate {
927927
}
928928
for (i, (outpoint, out)) in self.inputs.iter().enumerate() {
929929
log_debug!(logger, "Adding claiming input for outpoint {}:{}", outpoint.txid, outpoint.vout);
930-
if !out.finalize_input(&mut bumped_tx, i, onchain_handler) { return None; }
930+
if !out.finalize_input(&mut bumped_tx, i, onchain_handler) { continue; }
931931
}
932-
log_debug!(logger, "Finalized transaction {} ready to broadcast", bumped_tx.txid());
933932
Some(bumped_tx)
934933
}
935-
pub(crate) fn finalize_untractable_package<L: Logger, Signer: WriteableEcdsaChannelSigner>(
934+
pub(crate) fn maybe_finalize_untractable_package<L: Logger, Signer: WriteableEcdsaChannelSigner>(
936935
&self, onchain_handler: &mut OnchainTxHandler<Signer>, logger: &L,
937936
) -> Option<Transaction> {
938937
debug_assert!(!self.is_malleable());
939938
if let Some((outpoint, outp)) = self.inputs.first() {
940-
if let Some(final_tx) = outp.get_finalized_tx(outpoint, onchain_handler) {
939+
if let Some(final_tx) = outp.get_maybe_finalized_tx(outpoint, onchain_handler) {
941940
log_debug!(logger, "Adding claiming input for outpoint {}:{}", outpoint.txid, outpoint.vout);
942-
log_debug!(logger, "Finalized transaction {} ready to broadcast", final_tx.txid());
943941
return Some(final_tx);
944942
}
945943
return None;
@@ -996,6 +994,7 @@ impl PackageTemplate {
996994
if self.feerate_previous != 0 {
997995
let previous_feerate = self.feerate_previous.try_into().unwrap_or(u32::max_value());
998996
match feerate_strategy {
997+
FeerateStrategy::RetryPrevious => previous_feerate,
999998
FeerateStrategy::HighestOfPreviousOrNew => cmp::max(previous_feerate, feerate_estimate),
1000999
FeerateStrategy::ForceBump => if feerate_estimate > previous_feerate {
10011000
feerate_estimate
@@ -1141,6 +1140,10 @@ where
11411140
// If old feerate inferior to actual one given back by Fee Estimator, use it to compute new fee...
11421141
let (new_fee, new_feerate) = if let Some((new_fee, new_feerate)) = compute_fee_from_spent_amounts(input_amounts, predicted_weight, fee_estimator, logger) {
11431142
match feerate_strategy {
1143+
FeerateStrategy::RetryPrevious => {
1144+
let previous_fee = previous_feerate * predicted_weight / 1000;
1145+
(previous_fee, previous_feerate)
1146+
},
11441147
FeerateStrategy::HighestOfPreviousOrNew => if new_feerate > previous_feerate {
11451148
(new_fee, new_feerate)
11461149
} else {

0 commit comments

Comments
 (0)