mirror of
https://github.com/lightningdevkit/rust-lightning.git
synced 2025-02-25 15:20:24 +01:00
Change WalletSource::sign_tx to sign_psbt
This commit is contained in:
parent
fcf47a429c
commit
489b408520
2 changed files with 58 additions and 18 deletions
|
@ -35,6 +35,7 @@ use bitcoin::{OutPoint, PubkeyHash, Sequence, ScriptBuf, Transaction, TxIn, TxOu
|
||||||
use bitcoin::blockdata::constants::WITNESS_SCALE_FACTOR;
|
use bitcoin::blockdata::constants::WITNESS_SCALE_FACTOR;
|
||||||
use bitcoin::blockdata::locktime::absolute::LockTime;
|
use bitcoin::blockdata::locktime::absolute::LockTime;
|
||||||
use bitcoin::consensus::Encodable;
|
use bitcoin::consensus::Encodable;
|
||||||
|
use bitcoin::psbt::PartiallySignedTransaction;
|
||||||
use bitcoin::secp256k1;
|
use bitcoin::secp256k1;
|
||||||
use bitcoin::secp256k1::Secp256k1;
|
use bitcoin::secp256k1::Secp256k1;
|
||||||
use bitcoin::secp256k1::ecdsa::Signature;
|
use bitcoin::secp256k1::ecdsa::Signature;
|
||||||
|
@ -343,7 +344,10 @@ pub trait CoinSelectionSource {
|
||||||
) -> Result<CoinSelection, ()>;
|
) -> Result<CoinSelection, ()>;
|
||||||
/// Signs and provides the full witness for all inputs within the transaction known to the
|
/// 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`]).
|
/// 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
|
/// 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
|
/// 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
|
/// the transaction known to the wallet (i.e., any provided via
|
||||||
/// [`WalletSource::list_confirmed_utxos`]).
|
/// [`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
|
/// A wrapper over [`WalletSource`] that implements [`CoinSelection`] by preferring UTXOs that would
|
||||||
|
@ -504,8 +511,8 @@ where
|
||||||
.or_else(|_| do_coin_selection(true, true))
|
.or_else(|_| do_coin_selection(true, true))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sign_tx(&self, tx: Transaction) -> Result<Transaction, ()> {
|
fn sign_psbt(&self, psbt: PartiallySignedTransaction) -> Result<Transaction, ()> {
|
||||||
self.source.sign_tx(tx)
|
self.source.sign_psbt(psbt)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -549,8 +556,8 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Updates a transaction with the result of a successful coin selection attempt.
|
/// Updates a transaction with the result of a successful coin selection attempt.
|
||||||
fn process_coin_selection(&self, tx: &mut Transaction, mut coin_selection: CoinSelection) {
|
fn process_coin_selection(&self, tx: &mut Transaction, coin_selection: &CoinSelection) {
|
||||||
for utxo in coin_selection.confirmed_utxos.drain(..) {
|
for utxo in coin_selection.confirmed_utxos.iter() {
|
||||||
tx.input.push(TxIn {
|
tx.input.push(TxIn {
|
||||||
previous_output: utxo.outpoint,
|
previous_output: utxo.outpoint,
|
||||||
script_sig: ScriptBuf::new(),
|
script_sig: ScriptBuf::new(),
|
||||||
|
@ -558,7 +565,7 @@ where
|
||||||
witness: Witness::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);
|
tx.output.push(change_output);
|
||||||
} else if tx.output.is_empty() {
|
} else if tx.output.is_empty() {
|
||||||
// We weren't provided a change output, likely because the input set was a perfect
|
// 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",
|
log_debug!(self.logger, "Peforming coin selection for commitment package (commitment and anchor transaction) targeting {} sat/kW",
|
||||||
package_target_feerate_sat_per_1000_weight);
|
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,
|
claim_id, must_spend, &[], package_target_feerate_sat_per_1000_weight,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
@ -613,15 +620,29 @@ where
|
||||||
let total_input_amount = must_spend_amount +
|
let total_input_amount = must_spend_amount +
|
||||||
coin_selection.confirmed_utxos.iter().map(|utxo| utxo.output.value).sum::<u64>();
|
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();
|
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)]
|
#[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);
|
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 signer = anchor_descriptor.derive_channel_signer(&self.signer_provider);
|
||||||
let anchor_sig = signer.sign_holder_anchor_input(&anchor_tx, 0, &self.secp)?;
|
let anchor_sig = signer.sign_holder_anchor_input(&anchor_tx, 0, &self.secp)?;
|
||||||
|
@ -690,7 +711,7 @@ where
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
let must_spend_amount = must_spend.iter().map(|input| input.previous_utxo.value).sum::<u64>();
|
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,
|
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 +
|
let total_input_amount = must_spend_amount +
|
||||||
coin_selection.confirmed_utxos.iter().map(|utxo| utxo.output.value).sum::<u64>();
|
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)]
|
#[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());
|
log_debug!(self.logger, "Signing HTLC transaction {}", htlc_psbt.unsigned_tx.txid());
|
||||||
htlc_tx = self.utxo_source.sign_tx(htlc_tx)?;
|
htlc_tx = self.utxo_source.sign_psbt(htlc_psbt)?;
|
||||||
|
|
||||||
let mut signers = BTreeMap::new();
|
let mut signers = BTreeMap::new();
|
||||||
for (idx, htlc_descriptor) in htlc_descriptors.iter().enumerate() {
|
for (idx, htlc_descriptor) in htlc_descriptors.iter().enumerate() {
|
||||||
|
|
|
@ -71,6 +71,7 @@ use crate::sign::{InMemorySigner, Recipient, EntropySource, NodeSigner, SignerPr
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
use std::time::{SystemTime, UNIX_EPOCH};
|
use std::time::{SystemTime, UNIX_EPOCH};
|
||||||
|
use bitcoin::psbt::PartiallySignedTransaction;
|
||||||
use bitcoin::Sequence;
|
use bitcoin::Sequence;
|
||||||
|
|
||||||
pub fn pubkey(byte: u8) -> PublicKey {
|
pub fn pubkey(byte: u8) -> PublicKey {
|
||||||
|
@ -1410,7 +1411,8 @@ impl WalletSource for TestWalletSource {
|
||||||
Ok(ScriptBuf::new_p2pkh(&public_key.pubkey_hash()))
|
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();
|
let utxos = self.utxos.borrow();
|
||||||
for i in 0..tx.input.len() {
|
for i in 0..tx.input.len() {
|
||||||
if let Some(utxo) = utxos.iter().find(|utxo| utxo.outpoint == tx.input[i].previous_output) {
|
if let Some(utxo) = utxos.iter().find(|utxo| utxo.outpoint == tx.input[i].previous_output) {
|
||||||
|
|
Loading…
Add table
Reference in a new issue