Skip to content

Commit 18c97b1

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 b50d4d1 commit 18c97b1

File tree

8 files changed

+254
-52
lines changed

8 files changed

+254
-52
lines changed

lightning/src/chain/channelmonitor.rs

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1749,6 +1749,25 @@ impl<Signer: WriteableEcdsaChannelSigner> ChannelMonitor<Signer> {
17491749
);
17501750
}
17511751

1752+
/// Triggers rebroadcasts of pending claims from a force-closed channel after a transaction
1753+
/// signature generation failure.
1754+
pub fn signer_unblocked<B: Deref, F: Deref, L: Deref>(
1755+
&self, broadcaster: B, fee_estimator: F, logger: &L,
1756+
)
1757+
where
1758+
B::Target: BroadcasterInterface,
1759+
F::Target: FeeEstimator,
1760+
L::Target: Logger,
1761+
{
1762+
let fee_estimator = LowerBoundedFeeEstimator::new(fee_estimator);
1763+
let mut inner = self.inner.lock().unwrap();
1764+
let logger = WithChannelMonitor::from_impl(logger, &*inner);
1765+
let current_height = inner.best_block.height;
1766+
inner.onchain_tx_handler.rebroadcast_pending_claims(
1767+
current_height, FeerateStrategy::RetryPrevious, &broadcaster, &fee_estimator, &logger,
1768+
);
1769+
}
1770+
17521771
/// Returns the descriptors for relevant outputs (i.e., those that we can spend) within the
17531772
/// transaction if they exist and the transaction has at least [`ANTI_REORG_DELAY`]
17541773
/// confirmations. For [`SpendableOutputDescriptor::DelayedPaymentOutput`] descriptors to be
@@ -1791,6 +1810,12 @@ impl<Signer: WriteableEcdsaChannelSigner> ChannelMonitor<Signer> {
17911810
pub fn set_counterparty_payment_script(&self, script: ScriptBuf) {
17921811
self.inner.lock().unwrap().counterparty_payment_script = script;
17931812
}
1813+
1814+
#[cfg(test)]
1815+
pub fn do_signer_call<F: FnMut(&Signer) -> ()>(&self, mut f: F) {
1816+
let inner = self.inner.lock().unwrap();
1817+
f(&inner.onchain_tx_handler.signer);
1818+
}
17941819
}
17951820

17961821
impl<Signer: WriteableEcdsaChannelSigner> ChannelMonitorImpl<Signer> {
@@ -3484,9 +3509,12 @@ impl<Signer: WriteableEcdsaChannelSigner> ChannelMonitorImpl<Signer> {
34843509
continue;
34853510
}
34863511
} else { None };
3487-
if let Some(htlc_tx) = self.onchain_tx_handler.get_fully_signed_htlc_tx(
3488-
&::bitcoin::OutPoint { txid, vout }, &preimage) {
3489-
holder_transactions.push(htlc_tx);
3512+
if let Some(htlc_tx) = self.onchain_tx_handler.get_maybe_signed_htlc_tx(
3513+
&::bitcoin::OutPoint { txid, vout }, &preimage
3514+
) {
3515+
if !htlc_tx.input.iter().any(|input| input.witness.is_empty()) {
3516+
holder_transactions.push(htlc_tx);
3517+
}
34903518
}
34913519
}
34923520
}

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 {

0 commit comments

Comments
 (0)