+
+ /// Returns the index of the revokeable output, i.e. the `to_local` output sending funds to
+ /// the broadcaster, in the built transaction, if any exists.
+ ///
+ /// There are two cases where this may return `None`:
+ /// - The balance of the revokeable output is below the dust limit (only found on commitments
+ /// early in the channel's lifetime, i.e. before the channel reserve is met).
+ /// - This commitment was created before LDK 0.0.117. In this case, the
+ /// commitment transaction previously didn't contain enough information to locate the
+ /// revokeable output.
+ pub fn revokeable_output_index(&self) -> Option<usize> {
+ let revokeable_redeemscript = get_revokeable_redeemscript(
+ &self.keys.revocation_key,
+ self.to_broadcaster_delay?,
+ &self.keys.broadcaster_delayed_payment_key,
+ );
+ let revokeable_p2wsh = revokeable_redeemscript.to_v0_p2wsh();
+ let outputs = &self.inner.built.transaction.output;
+ outputs.iter().enumerate()
+ .find(|(_, out)| out.script_pubkey == revokeable_p2wsh)
+ .map(|(idx, _)| idx)
+ }
+
+ /// Helper method to build an unsigned justice transaction spending the revokeable
+ /// `to_local` output to a destination script. Fee estimation accounts for the expected
+ /// revocation witness data that will be added when signed.
+ ///
+ /// This method will error if the given fee rate results in a fee greater than the value
+ /// of the output being spent, or if there exists no revokeable `to_local` output on this
+ /// commitment transaction. See [`Self::revokeable_output_index`] for more details.
+ ///
+ /// The built transaction will allow fee bumping with RBF, and this method takes
+ /// `feerate_per_kw` as an input such that multiple copies of a justice transaction at different
+ /// fee rates may be built.
+ pub fn build_to_local_justice_tx(&self, feerate_per_kw: u64, destination_script: Script)
+ -> Result<Transaction, ()> {
+ let output_idx = self.revokeable_output_index().ok_or(())?;
+ let input = vec![TxIn {
+ previous_output: OutPoint {
+ txid: self.trust().txid(),
+ vout: output_idx as u32,
+ },
+ script_sig: Script::new(),
+ sequence: Sequence::ENABLE_RBF_NO_LOCKTIME,
+ witness: Witness::new(),
+ }];
+ let value = self.inner.built.transaction.output[output_idx].value;
+ let output = vec![TxOut {
+ script_pubkey: destination_script,
+ value,
+ }];
+ let mut justice_tx = Transaction {
+ version: 2,
+ lock_time: PackedLockTime::ZERO,
+ input,
+ output,
+ };
+ let weight = justice_tx.weight() as u64 + WEIGHT_REVOKED_OUTPUT;
+ let fee = fee_for_weight(feerate_per_kw as u32, weight);
+ justice_tx.output[0].value = value.checked_sub(fee).ok_or(())?;
+ Ok(justice_tx)
+ }
+