Skip to content

Change WalletSource::sign_tx to sign_psbt #2775

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Dec 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 55 additions & 17 deletions lightning/src/events/bump_transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -343,7 +344,10 @@ pub trait CoinSelectionSource {
) -> 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
Expand All @@ -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<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
Expand Down Expand Up @@ -504,8 +511,8 @@ where
.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)
}
}

Expand Down Expand Up @@ -549,16 +556,16 @@ 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(),
sequence: Sequence::ZERO,
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
Expand Down Expand Up @@ -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,
)?;

Expand All @@ -613,15 +620,29 @@ where
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)?;
Expand Down Expand Up @@ -690,7 +711,7 @@ where
#[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,
)?;

Expand All @@ -701,13 +722,30 @@ where
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() {
Expand Down
4 changes: 3 additions & 1 deletion lightning/src/util/test_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ use crate::sign::{InMemorySigner, Recipient, EntropySource, NodeSigner, SignerPr

#[cfg(feature = "std")]
use std::time::{SystemTime, UNIX_EPOCH};
use bitcoin::psbt::PartiallySignedTransaction;
use bitcoin::Sequence;

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

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