Skip to content

Commit

Permalink
Merge pull request #2775 from benthecarman/sign-psbt
Browse files Browse the repository at this point in the history
Change WalletSource::sign_tx to sign_psbt
  • Loading branch information
TheBlueMatt committed Dec 12, 2023
2 parents 4b35697 + 489b408 commit 68e25c6
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 18 deletions.
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 @@ -1417,7 +1418,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

0 comments on commit 68e25c6

Please sign in to comment.