use bitcoin::blockdata::constants::WITNESS_SCALE_FACTOR;
use bitcoin::blockdata::locktime::absolute::LockTime;
use bitcoin::consensus::Encodable;
+use bitcoin::psbt::PartiallySignedTransaction;
use bitcoin::secp256k1;
use bitcoin::secp256k1::Secp256k1;
use bitcoin::secp256k1::ecdsa::Signature;
) -> Result<CoinSelection, ()>;
/// Signs and provides the full witness for all inputs within the transaction known to the
/// trait (i.e., any provided via [`CoinSelectionSource::select_confirmed_utxos`]).
- fn sign_tx(&self, tx: Transaction) -> Result<Transaction, ()>;
+ ///
+ /// If your wallet does not support signing PSBTs you can call `psbt.extract_tx()` to get the
+ /// unsigned transaction and then sign it with your wallet.
+ fn sign_psbt(&self, psbt: PartiallySignedTransaction) -> Result<Transaction, ()>;
}
/// An alternative to [`CoinSelectionSource`] that can be implemented and used along [`Wallet`] to
/// Signs and provides the full [`TxIn::script_sig`] and [`TxIn::witness`] for all inputs within
/// the transaction known to the wallet (i.e., any provided via
/// [`WalletSource::list_confirmed_utxos`]).
- fn sign_tx(&self, tx: Transaction) -> Result<Transaction, ()>;
+ ///
+ /// If your wallet does not support signing PSBTs you can call `psbt.extract_tx()` to get the
+ /// unsigned transaction and then sign it with your wallet.
+ fn sign_psbt(&self, psbt: PartiallySignedTransaction) -> Result<Transaction, ()>;
}
/// A wrapper over [`WalletSource`] that implements [`CoinSelection`] by preferring UTXOs that would
.or_else(|_| do_coin_selection(true, true))
}
- fn sign_tx(&self, tx: Transaction) -> Result<Transaction, ()> {
- self.source.sign_tx(tx)
+ fn sign_psbt(&self, psbt: PartiallySignedTransaction) -> Result<Transaction, ()> {
+ self.source.sign_psbt(psbt)
}
}
}
/// Updates a transaction with the result of a successful coin selection attempt.
- fn process_coin_selection(&self, tx: &mut Transaction, mut coin_selection: CoinSelection) {
- for utxo in coin_selection.confirmed_utxos.drain(..) {
+ fn process_coin_selection(&self, tx: &mut Transaction, coin_selection: &CoinSelection) {
+ for utxo in coin_selection.confirmed_utxos.iter() {
tx.input.push(TxIn {
previous_output: utxo.outpoint,
script_sig: ScriptBuf::new(),
witness: Witness::new(),
});
}
- if let Some(change_output) = coin_selection.change_output.take() {
+ if let Some(change_output) = coin_selection.change_output.clone() {
tx.output.push(change_output);
} else if tx.output.is_empty() {
// We weren't provided a change output, likely because the input set was a perfect
log_debug!(self.logger, "Peforming coin selection for commitment package (commitment and anchor transaction) targeting {} sat/kW",
package_target_feerate_sat_per_1000_weight);
- let coin_selection = self.utxo_source.select_confirmed_utxos(
+ let coin_selection: CoinSelection = self.utxo_source.select_confirmed_utxos(
claim_id, must_spend, &[], package_target_feerate_sat_per_1000_weight,
)?;
let total_input_amount = must_spend_amount +
coin_selection.confirmed_utxos.iter().map(|utxo| utxo.output.value).sum::<u64>();
- self.process_coin_selection(&mut anchor_tx, coin_selection);
+ self.process_coin_selection(&mut anchor_tx, &coin_selection);
let anchor_txid = anchor_tx.txid();
- debug_assert_eq!(anchor_tx.output.len(), 1);
+ // construct psbt
+ let mut anchor_psbt = PartiallySignedTransaction::from_unsigned_tx(anchor_tx).unwrap();
+ // add witness_utxo to anchor input
+ anchor_psbt.inputs[0].witness_utxo = Some(anchor_descriptor.previous_utxo());
+ // add witness_utxo to remaining inputs
+ for (idx, utxo) in coin_selection.confirmed_utxos.into_iter().enumerate() {
+ // add 1 to skip the anchor input
+ let index = idx + 1;
+ debug_assert_eq!(anchor_psbt.unsigned_tx.input[index].previous_output, utxo.outpoint);
+ if utxo.output.script_pubkey.is_witness_program() {
+ anchor_psbt.inputs[index].witness_utxo = Some(utxo.output);
+ }
+ }
+
+ debug_assert_eq!(anchor_psbt.unsigned_tx.output.len(), 1);
#[cfg(debug_assertions)]
- let unsigned_tx_weight = anchor_tx.weight().to_wu() - (anchor_tx.input.len() as u64 * EMPTY_SCRIPT_SIG_WEIGHT);
+ let unsigned_tx_weight = anchor_psbt.unsigned_tx.weight().to_wu() - (anchor_psbt.unsigned_tx.input.len() as u64 * EMPTY_SCRIPT_SIG_WEIGHT);
log_debug!(self.logger, "Signing anchor transaction {}", anchor_txid);
- anchor_tx = self.utxo_source.sign_tx(anchor_tx)?;
+ anchor_tx = self.utxo_source.sign_psbt(anchor_psbt)?;
let signer = anchor_descriptor.derive_channel_signer(&self.signer_provider);
let anchor_sig = signer.sign_holder_anchor_input(&anchor_tx, 0, &self.secp)?;
#[cfg(debug_assertions)]
let must_spend_amount = must_spend.iter().map(|input| input.previous_utxo.value).sum::<u64>();
- let coin_selection = self.utxo_source.select_confirmed_utxos(
+ let coin_selection: CoinSelection = self.utxo_source.select_confirmed_utxos(
claim_id, must_spend, &htlc_tx.output, target_feerate_sat_per_1000_weight,
)?;
let total_input_amount = must_spend_amount +
coin_selection.confirmed_utxos.iter().map(|utxo| utxo.output.value).sum::<u64>();
- self.process_coin_selection(&mut htlc_tx, coin_selection);
+ self.process_coin_selection(&mut htlc_tx, &coin_selection);
+
+ // construct psbt
+ let mut htlc_psbt = PartiallySignedTransaction::from_unsigned_tx(htlc_tx).unwrap();
+ // add witness_utxo to htlc inputs
+ for (i, htlc_descriptor) in htlc_descriptors.iter().enumerate() {
+ debug_assert_eq!(htlc_psbt.unsigned_tx.input[i].previous_output, htlc_descriptor.outpoint());
+ htlc_psbt.inputs[i].witness_utxo = Some(htlc_descriptor.previous_utxo(&self.secp));
+ }
+ // add witness_utxo to remaining inputs
+ for (idx, utxo) in coin_selection.confirmed_utxos.into_iter().enumerate() {
+ // offset to skip the htlc inputs
+ let index = idx + htlc_descriptors.len();
+ debug_assert_eq!(htlc_psbt.unsigned_tx.input[index].previous_output, utxo.outpoint);
+ if utxo.output.script_pubkey.is_witness_program() {
+ htlc_psbt.inputs[index].witness_utxo = Some(utxo.output);
+ }
+ }
#[cfg(debug_assertions)]
- let unsigned_tx_weight = htlc_tx.weight().to_wu() - (htlc_tx.input.len() as u64 * EMPTY_SCRIPT_SIG_WEIGHT);
+ let unsigned_tx_weight = htlc_psbt.unsigned_tx.weight().to_wu() - (htlc_psbt.unsigned_tx.input.len() as u64 * EMPTY_SCRIPT_SIG_WEIGHT);
- log_debug!(self.logger, "Signing HTLC transaction {}", htlc_tx.txid());
- htlc_tx = self.utxo_source.sign_tx(htlc_tx)?;
+ log_debug!(self.logger, "Signing HTLC transaction {}", htlc_psbt.unsigned_tx.txid());
+ htlc_tx = self.utxo_source.sign_psbt(htlc_psbt)?;
let mut signers = BTreeMap::new();
for (idx, htlc_descriptor) in htlc_descriptors.iter().enumerate() {