Skip to content

Commit f35adc7

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 9b8e1fb commit f35adc7

10 files changed

+323
-50
lines changed

lightning/src/chain/chainmonitor.rs

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

640661
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> {
@@ -3526,9 +3551,12 @@ impl<Signer: WriteableEcdsaChannelSigner> ChannelMonitorImpl<Signer> {
35263551
continue;
35273552
}
35283553
} else { None };
3529-
if let Some(htlc_tx) = self.onchain_tx_handler.get_fully_signed_htlc_tx(
3530-
&::bitcoin::OutPoint { txid, vout }, &preimage) {
3531-
holder_transactions.push(htlc_tx);
3554+
if let Some(htlc_tx) = self.onchain_tx_handler.get_maybe_signed_htlc_tx(
3555+
&::bitcoin::OutPoint { txid, vout }, &preimage
3556+
) {
3557+
if !htlc_tx.input.iter().any(|input| input.witness.is_empty()) {
3558+
holder_transactions.push(htlc_tx);
3559+
}
35323560
}
35333561
}
35343562
}

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) => {
@@ -980,8 +990,13 @@ impl<ChannelSigner: WriteableEcdsaChannelSigner> OnchainTxHandler<ChannelSigner>
980990
) {
981991
match bump_claim {
982992
OnchainClaim::Tx(bump_tx) => {
983-
log_info!(logger, "Broadcasting RBF-bumped onchain {}", log_tx!(bump_tx));
984-
broadcaster.broadcast_transactions(&[&bump_tx]);
993+
if bump_tx.input.iter().any(|input| input.witness.is_empty()) {
994+
log_info!(logger, "Waiting for signature of RBF-bumped unsigned onchain transaction {}",
995+
bump_tx.txid());
996+
} else {
997+
log_info!(logger, "Broadcasting RBF-bumped onchain {}", log_tx!(bump_tx));
998+
broadcaster.broadcast_transactions(&[&bump_tx]);
999+
}
9851000
},
9861001
OnchainClaim::Event(claim_event) => {
9871002
log_info!(logger, "Yielding RBF-bumped onchain event to spend inputs {:?}", request.outpoints());
@@ -1063,8 +1078,12 @@ impl<ChannelSigner: WriteableEcdsaChannelSigner> OnchainTxHandler<ChannelSigner>
10631078
request.set_feerate(new_feerate);
10641079
match bump_claim {
10651080
OnchainClaim::Tx(bump_tx) => {
1066-
log_info!(logger, "Broadcasting onchain {}", log_tx!(bump_tx));
1067-
broadcaster.broadcast_transactions(&[&bump_tx]);
1081+
if bump_tx.input.iter().any(|input| input.witness.is_empty()) {
1082+
log_info!(logger, "Waiting for signature of unsigned onchain transaction {}", bump_tx.txid());
1083+
} else {
1084+
log_info!(logger, "Broadcasting onchain {}", log_tx!(bump_tx));
1085+
broadcaster.broadcast_transactions(&[&bump_tx]);
1086+
}
10681087
},
10691088
OnchainClaim::Event(claim_event) => {
10701089
log_info!(logger, "Yielding onchain event after reorg to spend inputs {:?}", request.outpoints());
@@ -1117,13 +1136,10 @@ impl<ChannelSigner: WriteableEcdsaChannelSigner> OnchainTxHandler<ChannelSigner>
11171136
&self.holder_commitment.trust().built_transaction().transaction
11181137
}
11191138

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

11291145
#[cfg(any(test, feature="unsafe_revoked_tx_signing"))]
@@ -1132,7 +1148,7 @@ impl<ChannelSigner: WriteableEcdsaChannelSigner> OnchainTxHandler<ChannelSigner>
11321148
self.holder_commitment.add_holder_sig(funding_redeemscript, sig)
11331149
}
11341150

1135-
pub(crate) fn get_fully_signed_htlc_tx(&mut self, outp: &::bitcoin::OutPoint, preimage: &Option<PaymentPreimage>) -> Option<Transaction> {
1151+
pub(crate) fn get_maybe_signed_htlc_tx(&mut self, outp: &::bitcoin::OutPoint, preimage: &Option<PaymentPreimage>) -> Option<Transaction> {
11361152
let get_signed_htlc_tx = |holder_commitment: &HolderCommitmentTransaction| {
11371153
let trusted_tx = holder_commitment.trust();
11381154
if trusted_tx.txid() != outp.txid {
@@ -1160,10 +1176,11 @@ impl<ChannelSigner: WriteableEcdsaChannelSigner> OnchainTxHandler<ChannelSigner>
11601176
preimage: preimage.clone(),
11611177
counterparty_sig: counterparty_htlc_sig.clone(),
11621178
};
1163-
let htlc_sig = self.signer.sign_holder_htlc_transaction(&htlc_tx, 0, &htlc_descriptor, &self.secp_ctx).unwrap();
1164-
htlc_tx.input[0].witness = trusted_tx.build_htlc_input_witness(
1165-
htlc_idx, &counterparty_htlc_sig, &htlc_sig, preimage,
1166-
);
1179+
if let Ok(htlc_sig) = self.signer.sign_holder_htlc_transaction(&htlc_tx, 0, &htlc_descriptor, &self.secp_ctx) {
1180+
htlc_tx.input[0].witness = trusted_tx.build_htlc_input_witness(
1181+
htlc_idx, &counterparty_htlc_sig, &htlc_sig, preimage,
1182+
);
1183+
}
11671184
Some(htlc_tx)
11681185
};
11691186

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)