Skip to content

Commit cdd7506

Browse files
committed
Change WalletSource::sign_tx to sign_psbt
1 parent fcf47a4 commit cdd7506

File tree

2 files changed

+50
-12
lines changed

2 files changed

+50
-12
lines changed

lightning/src/events/bump_transaction.rs

Lines changed: 47 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ use bitcoin::{OutPoint, PubkeyHash, Sequence, ScriptBuf, Transaction, TxIn, TxOu
3535
use bitcoin::blockdata::constants::WITNESS_SCALE_FACTOR;
3636
use bitcoin::blockdata::locktime::absolute::LockTime;
3737
use bitcoin::consensus::Encodable;
38+
use bitcoin::psbt::PartiallySignedTransaction;
3839
use bitcoin::secp256k1;
3940
use bitcoin::secp256k1::Secp256k1;
4041
use bitcoin::secp256k1::ecdsa::Signature;
@@ -343,7 +344,10 @@ pub trait CoinSelectionSource {
343344
) -> Result<CoinSelection, ()>;
344345
/// Signs and provides the full witness for all inputs within the transaction known to the
345346
/// trait (i.e., any provided via [`CoinSelectionSource::select_confirmed_utxos`]).
346-
fn sign_tx(&self, tx: Transaction) -> Result<Transaction, ()>;
347+
///
348+
/// If your wallet does not support signing PSBTs you can call `psbt.extract_tx()` to get the
349+
/// unsigned transaction and then sign it with your wallet.
350+
fn sign_psbt(&self, tx: PartiallySignedTransaction) -> Result<Transaction, ()>;
347351
}
348352

349353
/// An alternative to [`CoinSelectionSource`] that can be implemented and used along [`Wallet`] to
@@ -357,7 +361,10 @@ pub trait WalletSource {
357361
/// Signs and provides the full [`TxIn::script_sig`] and [`TxIn::witness`] for all inputs within
358362
/// the transaction known to the wallet (i.e., any provided via
359363
/// [`WalletSource::list_confirmed_utxos`]).
360-
fn sign_tx(&self, tx: Transaction) -> Result<Transaction, ()>;
364+
///
365+
/// If your wallet does not support signing PSBTs you can call `psbt.extract_tx()` to get the
366+
/// unsigned transaction and then sign it with your wallet.
367+
fn sign_psbt(&self, psbt: PartiallySignedTransaction) -> Result<Transaction, ()>;
361368
}
362369

363370
/// A wrapper over [`WalletSource`] that implements [`CoinSelection`] by preferring UTXOs that would
@@ -504,8 +511,8 @@ where
504511
.or_else(|_| do_coin_selection(true, true))
505512
}
506513

507-
fn sign_tx(&self, tx: Transaction) -> Result<Transaction, ()> {
508-
self.source.sign_tx(tx)
514+
fn sign_psbt(&self, psbt: PartiallySignedTransaction) -> Result<Transaction, ()> {
515+
self.source.sign_psbt(psbt)
509516
}
510517
}
511518

@@ -549,16 +556,16 @@ where
549556
}
550557

551558
/// Updates a transaction with the result of a successful coin selection attempt.
552-
fn process_coin_selection(&self, tx: &mut Transaction, mut coin_selection: CoinSelection) {
553-
for utxo in coin_selection.confirmed_utxos.drain(..) {
559+
fn process_coin_selection(&self, tx: &mut Transaction, coin_selection: &CoinSelection) {
560+
for utxo in coin_selection.confirmed_utxos.iter() {
554561
tx.input.push(TxIn {
555562
previous_output: utxo.outpoint,
556563
script_sig: ScriptBuf::new(),
557564
sequence: Sequence::ZERO,
558565
witness: Witness::new(),
559566
});
560567
}
561-
if let Some(change_output) = coin_selection.change_output.take() {
568+
if let Some(change_output) = coin_selection.change_output.clone() {
562569
tx.output.push(change_output);
563570
} else if tx.output.is_empty() {
564571
// We weren't provided a change output, likely because the input set was a perfect
@@ -613,15 +620,28 @@ where
613620
let total_input_amount = must_spend_amount +
614621
coin_selection.confirmed_utxos.iter().map(|utxo| utxo.output.value).sum::<u64>();
615622

616-
self.process_coin_selection(&mut anchor_tx, coin_selection);
623+
self.process_coin_selection(&mut anchor_tx, &coin_selection);
617624
let anchor_txid = anchor_tx.txid();
618625

626+
// construct psbt
627+
let mut anchor_psbt = PartiallySignedTransaction::from_unsigned_tx(anchor_tx.clone()).unwrap();
628+
// add witness_utxo to anchor input
629+
anchor_psbt.inputs[0].witness_utxo = Some(anchor_descriptor.previous_utxo());
630+
// add witness_utxo to remaining inputs
631+
for (idx, utxo) in coin_selection.confirmed_utxos.into_iter().enumerate() {
632+
// add 1 to skip the anchor input
633+
let index = idx + 1;
634+
debug_assert_eq!(anchor_psbt.unsigned_tx.input[index].previous_output, utxo.outpoint);
635+
anchor_psbt.inputs[index].witness_utxo = Some(utxo.output);
636+
}
637+
619638
debug_assert_eq!(anchor_tx.output.len(), 1);
620639
#[cfg(debug_assertions)]
621640
let unsigned_tx_weight = anchor_tx.weight().to_wu() - (anchor_tx.input.len() as u64 * EMPTY_SCRIPT_SIG_WEIGHT);
622641

623642
log_debug!(self.logger, "Signing anchor transaction {}", anchor_txid);
624-
anchor_tx = self.utxo_source.sign_tx(anchor_tx)?;
643+
debug_assert!(anchor_psbt.inputs.iter().all(|i| i.witness_utxo.is_some()));
644+
anchor_tx = self.utxo_source.sign_psbt(anchor_psbt)?;
625645

626646
let signer = anchor_descriptor.derive_channel_signer(&self.signer_provider);
627647
let anchor_sig = signer.sign_holder_anchor_input(&anchor_tx, 0, &self.secp)?;
@@ -701,13 +721,29 @@ where
701721
let total_input_amount = must_spend_amount +
702722
coin_selection.confirmed_utxos.iter().map(|utxo| utxo.output.value).sum::<u64>();
703723

704-
self.process_coin_selection(&mut htlc_tx, coin_selection);
724+
self.process_coin_selection(&mut htlc_tx, &coin_selection);
725+
726+
// construct psbt
727+
let mut htlc_psbt = PartiallySignedTransaction::from_unsigned_tx(htlc_tx.clone()).unwrap();
728+
// add witness_utxo to htlc inputs
729+
for (i, htlc_descriptor) in htlc_descriptors.iter().enumerate() {
730+
debug_assert_eq!(htlc_psbt.unsigned_tx.input[i].previous_output, htlc_descriptor.outpoint());
731+
htlc_psbt.inputs[i].witness_utxo = Some(htlc_descriptor.previous_utxo(&self.secp));
732+
}
733+
// add witness_utxo to remaining inputs
734+
for (idx, utxo) in coin_selection.confirmed_utxos.into_iter().enumerate() {
735+
// offset to skip the htlc inputs
736+
let index = idx + htlc_descriptors.len();
737+
debug_assert_eq!(htlc_psbt.unsigned_tx.input[index].previous_output, utxo.outpoint);
738+
htlc_psbt.inputs[index].witness_utxo = Some(utxo.output);
739+
}
705740

706741
#[cfg(debug_assertions)]
707742
let unsigned_tx_weight = htlc_tx.weight().to_wu() - (htlc_tx.input.len() as u64 * EMPTY_SCRIPT_SIG_WEIGHT);
708743

709744
log_debug!(self.logger, "Signing HTLC transaction {}", htlc_tx.txid());
710-
htlc_tx = self.utxo_source.sign_tx(htlc_tx)?;
745+
debug_assert!(htlc_psbt.inputs.iter().all(|i| i.witness_utxo.is_some()));
746+
htlc_tx = self.utxo_source.sign_psbt(htlc_psbt)?;
711747

712748
let mut signers = BTreeMap::new();
713749
for (idx, htlc_descriptor) in htlc_descriptors.iter().enumerate() {

lightning/src/util/test_utils.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ use crate::sign::{InMemorySigner, Recipient, EntropySource, NodeSigner, SignerPr
7171

7272
#[cfg(feature = "std")]
7373
use std::time::{SystemTime, UNIX_EPOCH};
74+
use bitcoin::psbt::PartiallySignedTransaction;
7475
use bitcoin::Sequence;
7576

7677
pub fn pubkey(byte: u8) -> PublicKey {
@@ -1410,7 +1411,8 @@ impl WalletSource for TestWalletSource {
14101411
Ok(ScriptBuf::new_p2pkh(&public_key.pubkey_hash()))
14111412
}
14121413

1413-
fn sign_tx(&self, mut tx: Transaction) -> Result<Transaction, ()> {
1414+
fn sign_psbt(&self, mut psbt: PartiallySignedTransaction) -> Result<Transaction, ()> {
1415+
let mut tx = psbt.extract_tx();
14141416
let utxos = self.utxos.borrow();
14151417
for i in 0..tx.input.len() {
14161418
if let Some(utxo) = utxos.iter().find(|utxo| utxo.outpoint == tx.input[i].previous_output) {

0 commit comments

Comments
 (0)