Skip to content

Commit 3c8467d

Browse files
committed
Handle under-coin-selecting due to an added OP_RETURN output
When we do coin selection for channel close anchor spends, we may do coin selection targeting exactly the input values we need. However, if coin selection does not include a change output, we may add an OP_RETURN output, which may cause us to end up with less fee than we wanted on the resulting package. Here we address this issue by running coin selection twice - first without seeking the extra weight of the OP_RETURN output, and again if we find that we under-selected.
1 parent 718ba16 commit 3c8467d

File tree

1 file changed

+92
-72
lines changed

1 file changed

+92
-72
lines changed

lightning/src/events/bump_transaction.rs

Lines changed: 92 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -617,85 +617,105 @@ where
617617
let mut anchor_utxo = anchor_descriptor.previous_utxo();
618618
let commitment_tx_fee_sat = Amount::from_sat(commitment_tx_fee_sat);
619619
anchor_utxo.value += commitment_tx_fee_sat;
620-
let must_spend = vec![Input {
621-
outpoint: anchor_descriptor.outpoint,
622-
previous_utxo: anchor_utxo,
623-
satisfaction_weight: commitment_tx.weight().to_wu() + ANCHOR_INPUT_WITNESS_WEIGHT + EMPTY_SCRIPT_SIG_WEIGHT,
624-
}];
625-
#[cfg(debug_assertions)]
626-
let must_spend_amount = must_spend.iter().map(|input| input.previous_utxo.value).sum::<Amount>();
627-
628-
log_debug!(self.logger, "Performing coin selection for commitment package (commitment and anchor transaction) targeting {} sat/kW",
629-
package_target_feerate_sat_per_1000_weight);
630-
let coin_selection: CoinSelection = self.utxo_source.select_confirmed_utxos(
631-
claim_id, must_spend, &[], package_target_feerate_sat_per_1000_weight,
632-
)?;
633-
634-
let mut anchor_tx = Transaction {
635-
version: Version::TWO,
636-
lock_time: LockTime::ZERO, // TODO: Use next best height.
637-
input: vec![anchor_descriptor.unsigned_tx_input()],
638-
output: vec![],
639-
};
640-
641-
#[cfg(debug_assertions)]
642-
let total_satisfaction_weight = ANCHOR_INPUT_WITNESS_WEIGHT + EMPTY_SCRIPT_SIG_WEIGHT +
643-
coin_selection.confirmed_utxos.iter().map(|utxo| utxo.satisfaction_weight).sum::<u64>();
644-
#[cfg(debug_assertions)]
645-
let total_input_amount = must_spend_amount +
646-
coin_selection.confirmed_utxos.iter().map(|utxo| utxo.output.value).sum();
647-
648-
self.process_coin_selection(&mut anchor_tx, &coin_selection);
649-
let anchor_txid = anchor_tx.compute_txid();
620+
let starting_package_and_fixed_input_satisfaction_weight =
621+
commitment_tx.weight().to_wu() + ANCHOR_INPUT_WITNESS_WEIGHT + EMPTY_SCRIPT_SIG_WEIGHT;
622+
let mut package_and_fixed_input_satisfaction_weight =
623+
starting_package_and_fixed_input_satisfaction_weight;
624+
625+
loop {
626+
let must_spend = vec![Input {
627+
outpoint: anchor_descriptor.outpoint,
628+
previous_utxo: anchor_utxo.clone(),
629+
satisfaction_weight: package_and_fixed_input_satisfaction_weight,
630+
}];
631+
#[cfg(debug_assertions)]
632+
let must_spend_amount = must_spend.iter().map(|input| input.previous_utxo.value).sum::<Amount>();
633+
634+
log_debug!(self.logger, "Performing coin selection for commitment package (commitment and anchor transaction) targeting {} sat/kW",
635+
package_target_feerate_sat_per_1000_weight);
636+
let coin_selection: CoinSelection = self.utxo_source.select_confirmed_utxos(
637+
claim_id, must_spend, &[], package_target_feerate_sat_per_1000_weight,
638+
)?;
639+
640+
let mut anchor_tx = Transaction {
641+
version: Version::TWO,
642+
lock_time: LockTime::ZERO, // TODO: Use next best height.
643+
input: vec![anchor_descriptor.unsigned_tx_input()],
644+
output: vec![],
645+
};
650646

651-
// construct psbt
652-
let mut anchor_psbt = Psbt::from_unsigned_tx(anchor_tx).unwrap();
653-
// add witness_utxo to anchor input
654-
anchor_psbt.inputs[0].witness_utxo = Some(anchor_descriptor.previous_utxo());
655-
// add witness_utxo to remaining inputs
656-
for (idx, utxo) in coin_selection.confirmed_utxos.into_iter().enumerate() {
657-
// add 1 to skip the anchor input
658-
let index = idx + 1;
659-
debug_assert_eq!(anchor_psbt.unsigned_tx.input[index].previous_output, utxo.outpoint);
660-
if utxo.output.script_pubkey.is_witness_program() {
661-
anchor_psbt.inputs[index].witness_utxo = Some(utxo.output);
647+
let total_satisfaction_weight = ANCHOR_INPUT_WITNESS_WEIGHT + EMPTY_SCRIPT_SIG_WEIGHT +
648+
coin_selection.confirmed_utxos.iter().map(|utxo| utxo.satisfaction_weight).sum::<u64>();
649+
let total_input_amount = must_spend_amount +
650+
coin_selection.confirmed_utxos.iter().map(|utxo| utxo.output.value).sum();
651+
652+
self.process_coin_selection(&mut anchor_tx, &coin_selection);
653+
let anchor_txid = anchor_tx.compute_txid();
654+
655+
// construct psbt
656+
let mut anchor_psbt = Psbt::from_unsigned_tx(anchor_tx).unwrap();
657+
// add witness_utxo to anchor input
658+
anchor_psbt.inputs[0].witness_utxo = Some(anchor_descriptor.previous_utxo());
659+
// add witness_utxo to remaining inputs
660+
for (idx, utxo) in coin_selection.confirmed_utxos.into_iter().enumerate() {
661+
// add 1 to skip the anchor input
662+
let index = idx + 1;
663+
debug_assert_eq!(anchor_psbt.unsigned_tx.input[index].previous_output, utxo.outpoint);
664+
if utxo.output.script_pubkey.is_witness_program() {
665+
anchor_psbt.inputs[index].witness_utxo = Some(utxo.output);
666+
}
662667
}
663-
}
664-
665-
debug_assert_eq!(anchor_psbt.unsigned_tx.output.len(), 1);
666-
#[cfg(debug_assertions)]
667-
let unsigned_tx_weight = anchor_psbt.unsigned_tx.weight().to_wu() - (anchor_psbt.unsigned_tx.input.len() as u64 * EMPTY_SCRIPT_SIG_WEIGHT);
668668

669-
log_debug!(self.logger, "Signing anchor transaction {}", anchor_txid);
670-
anchor_tx = self.utxo_source.sign_psbt(anchor_psbt)?;
669+
debug_assert_eq!(anchor_psbt.unsigned_tx.output.len(), 1);
670+
let unsigned_tx_weight = anchor_psbt.unsigned_tx.weight().to_wu() - (anchor_psbt.unsigned_tx.input.len() as u64 * EMPTY_SCRIPT_SIG_WEIGHT);
671671

672-
let signer = anchor_descriptor.derive_channel_signer(&self.signer_provider);
673-
let anchor_sig = signer.sign_holder_anchor_input(&anchor_tx, 0, &self.secp)?;
674-
anchor_tx.input[0].witness = anchor_descriptor.tx_input_witness(&anchor_sig);
672+
let package_fee = total_input_amount -
673+
anchor_psbt.unsigned_tx.output.iter().map(|output| output.value).sum();
674+
let package_weight = unsigned_tx_weight + total_satisfaction_weight + commitment_tx.weight().to_wu();
675+
if package_fee.to_sat() * 1000 / package_weight < package_target_feerate_sat_per_1000_weight.into() {
676+
// On the first iteration of the loop, we may undershoot the target feerate because
677+
// we had to add an OP_RETURN output in `process_coin_selection` which we didn't
678+
// select sufficient coins for. Here we detect that case and go around again
679+
// seeking additional weight.
680+
if package_and_fixed_input_satisfaction_weight == starting_package_and_fixed_input_satisfaction_weight {
681+
debug_assert!(anchor_psbt.unsigned_tx.output[0].script_pubkey.is_op_return(),
682+
"Coin selection failed to select sufficient coins for its change output");
683+
package_and_fixed_input_satisfaction_weight += anchor_psbt.unsigned_tx.output[0].weight().to_wu();
684+
continue;
685+
} else {
686+
debug_assert!(false, "Coin selection failed to select sufficient coins");
687+
}
688+
}
675689

676-
#[cfg(debug_assertions)] {
677-
let signed_tx_weight = anchor_tx.weight().to_wu();
678-
let expected_signed_tx_weight = unsigned_tx_weight + total_satisfaction_weight;
679-
// Our estimate should be within a 1% error margin of the actual weight and we should
680-
// never underestimate.
681-
assert!(expected_signed_tx_weight >= signed_tx_weight &&
682-
expected_signed_tx_weight - (expected_signed_tx_weight / 100) <= signed_tx_weight);
690+
log_debug!(self.logger, "Signing anchor transaction {}", anchor_txid);
691+
anchor_tx = self.utxo_source.sign_psbt(anchor_psbt)?;
692+
693+
let signer = anchor_descriptor.derive_channel_signer(&self.signer_provider);
694+
let anchor_sig = signer.sign_holder_anchor_input(&anchor_tx, 0, &self.secp)?;
695+
anchor_tx.input[0].witness = anchor_descriptor.tx_input_witness(&anchor_sig);
696+
697+
#[cfg(debug_assertions)] {
698+
let signed_tx_weight = anchor_tx.weight().to_wu();
699+
let expected_signed_tx_weight = unsigned_tx_weight + total_satisfaction_weight;
700+
// Our estimate should be within a 1% error margin of the actual weight and we should
701+
// never underestimate.
702+
assert!(expected_signed_tx_weight >= signed_tx_weight &&
703+
expected_signed_tx_weight - (expected_signed_tx_weight / 100) <= signed_tx_weight);
704+
705+
let expected_package_fee = Amount::from_sat(fee_for_weight(package_target_feerate_sat_per_1000_weight,
706+
signed_tx_weight + commitment_tx.weight().to_wu()));
707+
// Our fee should be within a 5% error margin of the expected fee based on the
708+
// feerate and transaction weight and we should never pay less than required.
709+
let fee_error_margin = expected_package_fee * 5 / 100;
710+
assert!(package_fee >= expected_package_fee &&
711+
package_fee - fee_error_margin <= expected_package_fee);
712+
}
683713

684-
let expected_package_fee = Amount::from_sat(fee_for_weight(package_target_feerate_sat_per_1000_weight,
685-
signed_tx_weight + commitment_tx.weight().to_wu()));
686-
let package_fee = total_input_amount -
687-
anchor_tx.output.iter().map(|output| output.value).sum();
688-
// Our fee should be within a 5% error margin of the expected fee based on the
689-
// feerate and transaction weight and we should never pay less than required.
690-
let fee_error_margin = expected_package_fee * 5 / 100;
691-
assert!(package_fee >= expected_package_fee &&
692-
package_fee - fee_error_margin <= expected_package_fee);
714+
log_info!(self.logger, "Broadcasting anchor transaction {} to bump channel close with txid {}",
715+
anchor_txid, commitment_tx.compute_txid());
716+
self.broadcaster.broadcast_transactions(&[&commitment_tx, &anchor_tx]);
717+
return Ok(());
693718
}
694-
695-
log_info!(self.logger, "Broadcasting anchor transaction {} to bump channel close with txid {}",
696-
anchor_txid, commitment_tx.compute_txid());
697-
self.broadcaster.broadcast_transactions(&[&commitment_tx, &anchor_tx]);
698-
Ok(())
699719
}
700720

701721
/// Handles a [`BumpTransactionEvent::HTLCResolution`] event variant by producing a

0 commit comments

Comments
 (0)