Skip to content

Commit

Permalink
Merge pull request #172 from benthecarman/sign-psbt
Browse files Browse the repository at this point in the history
Change Signer::sign_tx_input to Signer::sign_psbt_input
  • Loading branch information
Tibo-lg authored Dec 21, 2023
2 parents f9a4553 + 9b4b450 commit f303972
Show file tree
Hide file tree
Showing 7 changed files with 153 additions and 81 deletions.
37 changes: 29 additions & 8 deletions bitcoin-rpc-provider/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use std::sync::{Arc, Mutex};
use std::time::Duration;

use bitcoin::consensus::encode::Error as EncodeError;
use bitcoin::psbt::PartiallySignedTransaction;
use bitcoin::secp256k1::rand::thread_rng;
use bitcoin::secp256k1::{PublicKey, SecretKey};
use bitcoin::{
Expand Down Expand Up @@ -185,14 +186,32 @@ impl Signer for BitcoinCoreProvider {
Ok(pk.inner)
}

fn sign_tx_input(
fn sign_psbt_input(
&self,
tx: &mut Transaction,
psbt: &mut PartiallySignedTransaction,
input_index: usize,
tx_out: &TxOut,
redeem_script: Option<Script>,
) -> Result<(), ManagerError> {
let outpoint = &tx.input[input_index].previous_output;
let outpoint = &psbt.unsigned_tx.input[input_index].previous_output;
let tx_out = if let Some(input) = psbt.inputs.get(input_index) {
if let Some(wit_utxo) = &input.witness_utxo {
Ok(wit_utxo.clone())
} else if let Some(in_tx) = &input.non_witness_utxo {
Ok(in_tx.output[outpoint.vout as usize].clone())
} else {
Err(ManagerError::InvalidParameters(
"No TxOut for PSBT input".to_string(),
))
}
} else {
Err(ManagerError::InvalidParameters(
"No TxOut for PSBT input".to_string(),
))
}?;

let redeem_script = psbt
.inputs
.get(input_index)
.and_then(|i| i.redeem_script.clone());

let input = json::SignRawTransactionInput {
txid: outpoint.txid,
Expand All @@ -206,13 +225,15 @@ impl Signer for BitcoinCoreProvider {
.client
.lock()
.unwrap()
.sign_raw_transaction_with_wallet(&*tx, Some(&[input]), None)
.sign_raw_transaction_with_wallet(&psbt.unsigned_tx, Some(&[input]), None)
.map_err(rpc_err_to_manager_err)?;
let signed_tx = Transaction::consensus_decode(&mut sign_result.hex.as_slice())
.map_err(enc_err_to_manager_err)?;

tx.input[input_index].script_sig = signed_tx.input[input_index].script_sig.clone();
tx.input[input_index].witness = signed_tx.input[input_index].witness.clone();
psbt.inputs[input_index].final_script_sig =
Some(signed_tx.input[input_index].script_sig.clone());
psbt.inputs[input_index].final_script_witness =
Some(signed_tx.input[input_index].witness.clone());

Ok(())
}
Expand Down
12 changes: 8 additions & 4 deletions dlc-manager/src/channel_updater.rs
Original file line number Diff line number Diff line change
Expand Up @@ -435,7 +435,7 @@ pub fn verify_signed_channel<S: Deref>(
accepted_contract: &AcceptedContract,
sign_channel: &SignChannel,
signer: &S,
) -> Result<(SignedChannel, SignedContract), Error>
) -> Result<(SignedChannel, SignedContract, Transaction), Error>
where
S::Target: Signer,
{
Expand All @@ -458,7 +458,7 @@ where

let cet_adaptor_signatures: Vec<_> = (&sign_channel.cet_adaptor_signatures).into();

let (signed_contract, fund_tx) = verify_signed_contract_internal(
let (signed_contract, signed_fund_tx) = verify_signed_contract_internal(
secp,
accepted_contract,
&sign_channel.refund_signature,
Expand Down Expand Up @@ -493,7 +493,11 @@ where
is_offer: false,
},
update_idx: INITIAL_UPDATE_NUMBER,
fund_tx,
fund_tx: signed_contract
.accepted_contract
.dlc_transactions
.fund
.clone(),
fund_script_pubkey: accepted_contract
.dlc_transactions
.funding_script_pubkey
Expand All @@ -507,7 +511,7 @@ where
.fee_rate_per_vb,
};

Ok((signed_channel, signed_contract))
Ok((signed_channel, signed_contract, signed_fund_tx))
}

/// Creates a [`SettleOffer`] message from the given [`SignedChannel`] and parameters,
Expand Down
118 changes: 68 additions & 50 deletions dlc-manager/src/contract_updater.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
use std::ops::Deref;

use bitcoin::psbt::PartiallySignedTransaction;
use bitcoin::{consensus::Decodable, Script, Transaction, Witness};
use dlc::{DlcTransactions, PartyParams};
use dlc_messages::{
Expand Down Expand Up @@ -284,6 +285,31 @@ where
Ok((signed_contract, signed_msg))
}

fn populate_psbt(
psbt: &mut PartiallySignedTransaction,
all_funding_inputs: &[&FundingInputInfo],
) -> Result<(), Error> {
// add witness utxo to fund_psbt for all inputs
for (input_index, x) in all_funding_inputs.iter().enumerate() {
let tx = Transaction::consensus_decode(&mut x.funding_input.prev_tx.as_slice()).map_err(
|_| {
Error::InvalidParameters(
"Could not decode funding input previous tx parameter".to_string(),
)
},
)?;
let vout = x.funding_input.prev_tx_vout;
let tx_out = tx.output.get(vout as usize).ok_or_else(|| {
Error::InvalidParameters(format!("Previous tx output not found at index {}", vout))
})?;

psbt.inputs[input_index].witness_utxo = Some(tx_out.clone());
psbt.inputs[input_index].redeem_script = Some(x.funding_input.redeem_script.clone());
}

Ok(())
}

pub(crate) fn verify_accepted_and_sign_contract_internal<S: Deref>(
secp: &Secp256k1<All>,
offered_contract: &OfferedContract,
Expand All @@ -309,7 +335,8 @@ where
funding_script_pubkey,
} = dlc_transactions;

let mut fund = fund.clone();
let mut fund_psbt = PartiallySignedTransaction::from_unsigned_tx(fund.clone())
.map_err(|_| Error::InvalidState("Tried to create PSBT from signed tx".to_string()))?;
let mut cets = cets.clone();

let input_script_pubkey = input_script_pubkey.unwrap_or_else(|| funding_script_pubkey.clone());
Expand Down Expand Up @@ -392,43 +419,42 @@ where
own_signatures.extend(sigs);
}

let mut input_serial_ids: Vec<_> = offered_contract
// get all funding inputs
let mut all_funding_inputs = offered_contract
.funding_inputs_info
.iter()
.map(|x| x.funding_input.input_serial_id)
.chain(accept_params.inputs.iter().map(|x| x.serial_id))
.collect();
input_serial_ids.sort_unstable();
.chain(funding_inputs_info.iter())
.collect::<Vec<_>>();
// sort by serial id
all_funding_inputs.sort_by_key(|x| x.funding_input.input_serial_id);

populate_psbt(&mut fund_psbt, &all_funding_inputs)?;

// Vec<Witness>
let witnesses: Vec<Witness> = offered_contract
.funding_inputs_info
.iter()
.map(|x| {
let input_index = input_serial_ids
let input_index = all_funding_inputs
.iter()
.position(|y| y == &x.funding_input.input_serial_id)
.position(|y| y.funding_input == x.funding_input)
.ok_or_else(|| {
Error::InvalidState(format!(
"Could not find input for serial id {}",
x.funding_input.input_serial_id
))
})?;
let tx = Transaction::consensus_decode(&mut x.funding_input.prev_tx.as_slice())
.map_err(|_| {
Error::InvalidParameters(
"Could not decode funding input previous tx parameter".to_string(),
)
})?;
let vout = x.funding_input.prev_tx_vout;
let tx_out = tx.output.get(vout as usize).ok_or_else(|| {
Error::InvalidParameters(format!("Previous tx output not found at index {}", vout))
})?;

// pass wallet instead of privkeys
signer.sign_tx_input(&mut fund, input_index, tx_out, None)?;
signer.sign_psbt_input(&mut fund_psbt, input_index)?;

Ok(fund.input[input_index].witness.clone())
let witness = fund_psbt.inputs[input_index]
.final_script_witness
.clone()
.ok_or(Error::InvalidParameters(
"No witness from signing psbt input".to_string(),
))?;

Ok(witness)
})
.collect::<Result<Vec<_>, Error>>()?;

Expand All @@ -445,8 +471,6 @@ where
})
.collect::<Result<Vec<_>, Error>>()?;

input_serial_ids.sort_unstable();

let offer_refund_signature = dlc::util::get_raw_sig_for_tx_input(
secp,
refund,
Expand All @@ -457,7 +481,7 @@ where
)?;

let dlc_transactions = DlcTransactions {
fund,
fund: fund.clone(),
cets,
refund: refund.clone(),
funding_script_pubkey: funding_script_pubkey.clone(),
Expand Down Expand Up @@ -565,63 +589,57 @@ where
)?;
}

let mut input_serials: Vec<_> = offered_contract
let fund_tx = &accepted_contract.dlc_transactions.fund;
let mut fund_psbt = PartiallySignedTransaction::from_unsigned_tx(fund_tx.clone())
.map_err(|_| Error::InvalidState("Tried to create PSBT from signed tx".to_string()))?;

// get all funding inputs
let mut all_funding_inputs = offered_contract
.funding_inputs_info
.iter()
.chain(accepted_contract.funding_inputs.iter())
.map(|x| x.funding_input.input_serial_id)
.collect();
input_serials.sort_unstable();
.collect::<Vec<_>>();
// sort by serial id
all_funding_inputs.sort_by_key(|x| x.funding_input.input_serial_id);

let mut fund_tx = accepted_contract.dlc_transactions.fund.clone();
populate_psbt(&mut fund_psbt, &all_funding_inputs)?;

for (funding_input, funding_signatures) in offered_contract
.funding_inputs_info
.iter()
.zip(funding_signatures.funding_signatures.iter())
{
let input_index = input_serials
let input_index = all_funding_inputs
.iter()
.position(|x| x == &funding_input.funding_input.input_serial_id)
.position(|x| x.funding_input == funding_input.funding_input)
.ok_or_else(|| {
Error::InvalidState(format!(
"Could not find input for serial id {}",
funding_input.funding_input.input_serial_id
))
})?;

fund_tx.input[input_index].witness = Witness::from_vec(
fund_psbt.inputs[input_index].final_script_witness = Some(Witness::from_vec(
funding_signatures
.witness_elements
.iter()
.map(|x| x.witness.clone())
.collect(),
);
));
}

for funding_input_info in &accepted_contract.funding_inputs {
let input_index = input_serials
for funding_input in &accepted_contract.funding_inputs {
let input_index = all_funding_inputs
.iter()
.position(|x| x == &funding_input_info.funding_input.input_serial_id)
.position(|x| x.funding_input == funding_input.funding_input)
.ok_or_else(|| {
Error::InvalidState(format!(
"Could not find input for serial id {}",
funding_input_info.funding_input.input_serial_id,
funding_input.funding_input.input_serial_id
))
})?;
let tx =
Transaction::consensus_decode(&mut funding_input_info.funding_input.prev_tx.as_slice())
.map_err(|_| {
Error::InvalidParameters(
"Could not decode funding input previous tx parameter".to_string(),
)
})?;
let vout = funding_input_info.funding_input.prev_tx_vout;
let tx_out = tx.output.get(vout as usize).ok_or_else(|| {
Error::InvalidParameters(format!("Previous tx output not found at index {}", vout))
})?;

signer.sign_tx_input(&mut fund_tx, input_index, tx_out, None)?;
signer.sign_psbt_input(&mut fund_psbt, input_index)?;
}

let signed_contract = SignedContract {
Expand All @@ -632,7 +650,7 @@ where
channel_id,
};

Ok((signed_contract, fund_tx))
Ok((signed_contract, fund_psbt.extract_tx()))
}

/// Signs and return the CET that can be used to close the given contract.
Expand Down
7 changes: 3 additions & 4 deletions dlc-manager/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ pub mod manager;
pub mod payout_curve;
mod utils;

use bitcoin::psbt::PartiallySignedTransaction;
use bitcoin::{Address, Block, OutPoint, Script, Transaction, TxOut, Txid};
use chain_monitor::ChainMonitor;
use channel::offered_channel::OfferedChannel;
Expand Down Expand Up @@ -78,12 +79,10 @@ impl Time for SystemTimeProvider {
/// Provides signing related functionalities.
pub trait Signer {
/// Signs a transaction input
fn sign_tx_input(
fn sign_psbt_input(
&self,
tx: &mut Transaction,
psbt: &mut PartiallySignedTransaction,
input_index: usize,
tx_out: &TxOut,
redeem_script: Option<Script>,
) -> Result<(), Error>;
/// Get the secret key associated with the provided public key.
fn get_secret_key_for_pubkey(&self, pubkey: &PublicKey) -> Result<SecretKey, Error>;
Expand Down
4 changes: 2 additions & 2 deletions dlc-manager/src/manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1390,7 +1390,7 @@ where
Some(*peer_id)
)?;

let (signed_channel, signed_contract) = {
let (signed_channel, signed_contract, signed_fund_tx) = {
let res = verify_signed_channel(
&self.secp,
&accepted_channel,
Expand Down Expand Up @@ -1430,7 +1430,7 @@ where
unreachable!();
}

self.blockchain.send_transaction(&signed_channel.fund_tx)?;
self.blockchain.send_transaction(&signed_fund_tx)?;

self.store.upsert_channel(
Channel::Signed(signed_channel),
Expand Down
11 changes: 5 additions & 6 deletions mocks/src/mock_wallet.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use std::rc::Rc;

use bitcoin::psbt::PartiallySignedTransaction;
use bitcoin::{Address, PackedLockTime, Script, Transaction, TxOut};
use dlc_manager::{error::Error, Blockchain, Signer, Utxo, Wallet};
use secp256k1_zkp::{rand::seq::SliceRandom, SecretKey};
Expand Down Expand Up @@ -45,13 +46,11 @@ impl MockWallet {
}

impl Signer for MockWallet {
fn sign_tx_input(
fn sign_psbt_input(
&self,
_tx: &mut bitcoin::Transaction,
_input_index: usize,
_tx_out: &bitcoin::TxOut,
_redeem_script: Option<bitcoin::Script>,
) -> Result<(), dlc_manager::error::Error> {
_psbt: &mut PartiallySignedTransaction,
_idx: usize,
) -> Result<(), Error> {
Ok(())
}

Expand Down
Loading

0 comments on commit f303972

Please sign in to comment.