X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=lightning%2Fsrc%2Fevents%2Fbump_transaction.rs;h=1b019ba349e0ae8687a190b92ebd144fe164f6f9;hb=7c9463668a4f71663746e57bdb09ee6b91797d5a;hp=8c6390302f10c945754792189f0b02f4421fd616;hpb=2659a2375eb819fe050f057b068c3e27f98a30f7;p=rust-lightning diff --git a/lightning/src/events/bump_transaction.rs b/lightning/src/events/bump_transaction.rs index 8c639030..1b019ba3 100644 --- a/lightning/src/events/bump_transaction.rs +++ b/lightning/src/events/bump_transaction.rs @@ -35,6 +35,7 @@ use bitcoin::{OutPoint, PubkeyHash, Sequence, ScriptBuf, Transaction, TxIn, TxOu 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; @@ -343,7 +344,10 @@ pub trait CoinSelectionSource { ) -> Result; /// 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; + /// + /// 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; } /// An alternative to [`CoinSelectionSource`] that can be implemented and used along [`Wallet`] to @@ -357,7 +361,10 @@ pub trait WalletSource { /// 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; + /// + /// 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; } /// A wrapper over [`WalletSource`] that implements [`CoinSelection`] by preferring UTXOs that would @@ -504,8 +511,8 @@ where .or_else(|_| do_coin_selection(true, true)) } - fn sign_tx(&self, tx: Transaction) -> Result { - self.source.sign_tx(tx) + fn sign_psbt(&self, psbt: PartiallySignedTransaction) -> Result { + self.source.sign_psbt(psbt) } } @@ -549,8 +556,8 @@ where } /// 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(), @@ -558,7 +565,7 @@ where 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 @@ -595,7 +602,7 @@ where 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, )?; @@ -613,15 +620,29 @@ where let total_input_amount = must_spend_amount + coin_selection.confirmed_utxos.iter().map(|utxo| utxo.output.value).sum::(); - 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)?; @@ -690,7 +711,7 @@ where #[cfg(debug_assertions)] let must_spend_amount = must_spend.iter().map(|input| input.previous_utxo.value).sum::(); - 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, )?; @@ -701,13 +722,30 @@ where let total_input_amount = must_spend_amount + coin_selection.confirmed_utxos.iter().map(|utxo| utxo.output.value).sum::(); - 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() {