Skip to content

Commit

Permalink
Get tests passing for nested segwit
Browse files Browse the repository at this point in the history
- change commit_witness_inputs inputs param to (psbt::Input, TxIn)
- update integration tests to use nested segwit addresses
- fallback to psbtin.redeem_script if psbtin.final_script_sig is None
- don't clear sender signatures until after apply_fee
- update unit tests for nested segwit PSBTs
  • Loading branch information
spacebear21 committed Oct 10, 2024
1 parent a9b9a34 commit 8961812
Show file tree
Hide file tree
Showing 7 changed files with 97 additions and 88 deletions.
20 changes: 20 additions & 0 deletions payjoin-cli/src/app/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,3 +120,23 @@ fn read_local_cert() -> Result<Vec<u8>> {
local_cert_path.push(LOCAL_CERT_FILE);
Ok(std::fs::read(local_cert_path)?)
}

pub fn input_pair_from_list_unspent(
utxo: &bitcoincore_rpc::bitcoincore_rpc_json::ListUnspentResultEntry,
) -> (bitcoin::psbt::Input, bitcoin::TxIn) {
let psbtin = bitcoin::psbt::Input {
// TODO: non_witness_utxo for legacy support
witness_utxo: Some(bitcoin::TxOut {
value: utxo.amount,
script_pubkey: utxo.script_pub_key.clone(),
}),
redeem_script: utxo.redeem_script.clone(),
witness_script: utxo.witness_script.clone(),
..Default::default()
};
let txin = bitcoin::TxIn {
previous_output: bitcoin::OutPoint { txid: utxo.txid, vout: utxo.vout },
..Default::default()
};
(psbtin, txin)
}
9 changes: 3 additions & 6 deletions payjoin-cli/src/app/v1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ use tokio::net::TcpListener;

use super::config::AppConfig;
use super::App as AppTrait;
use crate::app::http_agent;
use crate::app::{http_agent, input_pair_from_list_unspent};
use crate::db::Database;
#[cfg(feature = "danger-local-https")]
pub const LOCAL_CERT_FILE: &str = "localhost.der";
Expand Down Expand Up @@ -396,13 +396,10 @@ fn try_contributing_inputs(
.find(|i| i.txid == selected_outpoint.txid && i.vout == selected_outpoint.vout)
.context("This shouldn't happen. Failed to retrieve the privacy preserving utxo from those we provided to the seclector.")?;
log::debug!("selected utxo: {:#?}", selected_utxo);
let txo_to_contribute = bitcoin::TxOut {
value: selected_utxo.amount,
script_pubkey: selected_utxo.script_pub_key.clone(),
};
let input_pair = input_pair_from_list_unspent(selected_utxo);

Ok(payjoin
.contribute_witness_inputs(vec![(selected_outpoint, txo_to_contribute)])
.contribute_witness_inputs(vec![input_pair])
.expect("This shouldn't happen. Failed to contribute inputs.")
.commit_inputs())
}
Expand Down
7 changes: 2 additions & 5 deletions payjoin-cli/src/app/v2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -375,13 +375,10 @@ fn try_contributing_inputs(
.find(|i| i.txid == selected_outpoint.txid && i.vout == selected_outpoint.vout)
.context("This shouldn't happen. Failed to retrieve the privacy preserving utxo from those we provided to the seclector.")?;
log::debug!("selected utxo: {:#?}", selected_utxo);
let txo_to_contribute = bitcoin::TxOut {
value: selected_utxo.amount,
script_pubkey: selected_utxo.script_pub_key.clone(),
};
let input_pair = input_pair_from_list_unspent(selected_utxo);

Ok(payjoin
.contribute_witness_inputs(vec![(selected_outpoint, txo_to_contribute)])
.contribute_witness_inputs(vec![input_pair])
.expect("This shouldn't happen. Failed to contribute inputs.")
.commit_inputs())
}
Expand Down
15 changes: 11 additions & 4 deletions payjoin/src/psbt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -195,17 +195,24 @@ impl<'a> InputPair<'a> {
// Get the input weight prediction corresponding to spending an output of this address type
let iwp = match self.address_type()? {
P2pkh => Ok(InputWeightPrediction::P2PKH_COMPRESSED_MAX),
P2sh =>
match self.psbtin.final_script_sig.as_ref().and_then(|s| redeem_script(s.as_ref()))
{
P2sh => {
let redeem_script = if let Some(ref script_sig) = self.psbtin.final_script_sig {
redeem_script(script_sig.as_ref())
} else if let Some(ref script) = self.psbtin.redeem_script {
Some(script.as_ref())
} else {
None
};
match redeem_script {
// Nested segwit p2wpkh.
Some(script) if script.is_witness_program() && script.is_p2wpkh() =>
Ok(NESTED_P2WPKH_MAX),
// Other script or witness program.
Some(_) => Err(InputWeightError::NotSupported),
// No redeem script provided. Cannot determine the script type.
None => Err(InputWeightError::NotFinalized),
},
}
}
P2wpkh => Ok(InputWeightPrediction::P2WPKH_MAX),
P2wsh => Err(InputWeightError::NotSupported),
P2tr => Ok(InputWeightPrediction::P2TR_KEY_DEFAULT_SIGHASH),
Expand Down
70 changes: 28 additions & 42 deletions payjoin/src/receive/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ use error::{
};
use optional_parameters::Params;

use crate::psbt::PsbtExt;
use crate::psbt::{InputPair, PsbtExt};

pub trait Headers {
fn get_header(&self, key: &str) -> Option<&str>;
Expand Down Expand Up @@ -577,7 +577,7 @@ impl WantsInputs {
/// Any excess input amount is added to the change_vout output indicated previously.
pub fn contribute_witness_inputs(
self,
inputs: impl IntoIterator<Item = (OutPoint, TxOut)>,
inputs: impl IntoIterator<Item = (bitcoin::psbt::Input, bitcoin::TxIn)>,
) -> Result<WantsInputs, InputContributionError> {
let mut payjoin_psbt = self.payjoin_psbt.clone();
// The payjoin proposal must not introduce mixed input sequence numbers
Expand All @@ -592,21 +592,15 @@ impl WantsInputs {
// Insert contributions at random indices for privacy
let mut rng = rand::thread_rng();
let mut receiver_input_amount = Amount::ZERO;
for (outpoint, txo) in inputs.into_iter() {
receiver_input_amount += txo.value;
for (psbtin, txin) in inputs.into_iter() {
receiver_input_amount +=
InputPair { txin: &txin, psbtin: &psbtin }.previous_txout().expect("txout").value;
let index = rng.gen_range(0..=self.payjoin_psbt.unsigned_tx.input.len());
payjoin_psbt.inputs.insert(
index,
bitcoin::psbt::Input { witness_utxo: Some(txo), ..Default::default() },
);
payjoin_psbt.unsigned_tx.input.insert(
index,
bitcoin::TxIn {
previous_output: outpoint,
sequence: original_sequence,
..Default::default()
},
);
payjoin_psbt.inputs.insert(index, psbtin);
payjoin_psbt
.unsigned_tx
.input
.insert(index, bitcoin::TxIn { sequence: original_sequence, ..txin });
}

// Add the receiver change amount to the receiver change output, if applicable
Expand Down Expand Up @@ -838,14 +832,14 @@ impl ProvisionalProposal {
min_feerate_sat_per_vb: Option<FeeRate>,
max_feerate_sat_per_vb: FeeRate,
) -> Result<PayjoinProposal, Error> {
for i in self.sender_input_indexes() {
let mut psbt = self.apply_fee(min_feerate_sat_per_vb, max_feerate_sat_per_vb)?.clone();
for i in 0..psbt.inputs.len() {
log::trace!("Clearing sender script signatures for input {}", i);
self.payjoin_psbt.inputs[i].final_script_sig = None;
self.payjoin_psbt.inputs[i].final_script_witness = None;
self.payjoin_psbt.inputs[i].tap_key_sig = None;
psbt.inputs[i].final_script_sig = None;
psbt.inputs[i].final_script_witness = None;
psbt.inputs[i].tap_key_sig = None;
}
let psbt = self.apply_fee(min_feerate_sat_per_vb, max_feerate_sat_per_vb)?;
let psbt = wallet_process_psbt(psbt)?;
let psbt = wallet_process_psbt(&psbt)?;
let payjoin_proposal = self.prepare_psbt(psbt)?;
Ok(payjoin_proposal)
}
Expand Down Expand Up @@ -874,6 +868,7 @@ impl PayjoinProposal {
mod test {
use std::str::FromStr;

use bitcoin::hashes::serde::Deserialize;
use bitcoin::hashes::Hash;
use bitcoin::{Address, Network, ScriptBuf};
use rand::rngs::StdRng;
Expand Down Expand Up @@ -958,23 +953,9 @@ mod test {
// Specify excessive fee rate in sender params
proposal.params.min_feerate = FeeRate::from_sat_per_vb_unchecked(1000);
// Input contribution for the receiver, from the BIP78 test vector
let input: (OutPoint, TxOut) = (
OutPoint {
txid: "833b085de288cda6ff614c6e8655f61e7ae4f84604a2751998dc25a0d1ba278f"
.parse()
.unwrap(),
vout: 1,
},
TxOut {
value: Amount::from_sat(2000000),
// HACK: The script pubkey in the original test vector is a nested p2sh witness
// script, which is not correctly supported in our current weight calculations.
// To get around this limitation, this test uses a native segwit script instead.
script_pubkey: ScriptBuf::new_p2wpkh(&bitcoin::WPubkeyHash::hash(
"00145f806655e5924c9204c2d51be5394f4bf9eda210".as_bytes(),
)),
},
);
let proposal_psbt = Psbt::from_str("cHNidP8BAJwCAAAAAo8nutGgJdyYGXWiBEb45Hoe9lWGbkxh/6bNiOJdCDuDAAAAAAD+////jye60aAl3JgZdaIERvjkeh72VYZuTGH/ps2I4l0IO4MBAAAAAP7///8CJpW4BQAAAAAXqRQd6EnwadJ0FQ46/q6NcutaawlEMIcACT0AAAAAABepFHdAltvPSGdDwi9DR+m0af6+i2d6h9MAAAAAAAEBIICEHgAAAAAAF6kUyPLL+cphRyyI5GTUazV0hF2R2NWHAQcXFgAUX4BmVeWSTJIEwtUb5TlPS/ntohABCGsCRzBEAiBnu3tA3yWlT0WBClsXXS9j69Bt+waCs9JcjWtNjtv7VgIge2VYAaBeLPDB6HGFlpqOENXMldsJezF9Gs5amvDQRDQBIQJl1jz1tBt8hNx2owTm+4Du4isx0pmdKNMNIjjaMHFfrQAAAA==").unwrap();
let input: (bitcoin::psbt::Input, bitcoin::TxIn) =
(proposal_psbt.inputs[1].clone(), proposal_psbt.unsigned_tx.input[1].clone());
let mut payjoin = proposal
.assume_interactive_receiver()
.check_inputs_not_owned(|_| Ok(false))
Expand Down Expand Up @@ -1021,13 +1002,18 @@ mod test {

// Input weight for a single nested P2WPKH (nested segwit) receiver input
let nested_p2wpkh_proposal = ProvisionalProposal {
original_psbt: Psbt::from_str("cHNidP8BAHECAAAAAX57euL5j6xOst5JB/e/gp58RihmmpxXpsc2hEKKcVFkAAAAAAD9////AhAnAAAAAAAAFgAUtjrU62JOASAnPQ4e30wBM/Exk7ZM0QKVAAAAABYAFL6xh6gjSHmznJnPMbolG7wbGuwtAAAAAAABAIYCAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/////wQCqgAA/////wIA+QKVAAAAABepFOyefe4gjXozL4pzi5vcPrjMeCJwhwAAAAAAAAAAJmokqiGp7eL2HD9x0d79P6mZ36NpU3VcaQaJeZlitIvr2DaXToz5AAAAAAEBIAD5ApUAAAAAF6kU7J597iCNejMvinOLm9w+uMx4InCHAQcXFgAUd6fhKfAd+JIJGpIGkMfMpjd/26sBCGsCRzBEAiBaCDgIrTw5bB1VZrB8RPycgKGNPw/YS6P+psUyxOUwgwIgbJkcbHlMoZxG7vBOVWnQQWayDTSvub6L20dDo1R5SS8BIQK2GCTydo2dJXC6C5wcSKzQ2pCsSygXa0+cMlJrRRnKtwAAIgIC0VgJvaoW2/lbq5atJhxfcgVzs6/gnpafsJHbz+ei484YDOqFk1QAAIABAACAAAAAgAEAAAACAAAAAA==").unwrap(),
payjoin_psbt: Psbt::from_str("cHNidP8BAJoCAAAAAn57euL5j6xOst5JB/e/gp58RihmmpxXpsc2hEKKcVFkAAAAAAD9////VinByqmVDo3wPNB9LnNELJoJ0g+hOdWiTSXzWEUVtiAAAAAAAP3///8CEBkGKgEAAAAWABSZUDn7eqenP01ziWRBnTCrpwwD6vHQApUAAAAAFgAUvrGHqCNIebOcmc8xuiUbvBsa7C0AAAAAAAEAhgIAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/////BAKqAAD/////AgD5ApUAAAAAF6kU7J597iCNejMvinOLm9w+uMx4InCHAAAAAAAAAAAmaiSqIant4vYcP3HR3v0/qZnfo2lTdVxpBol5mWK0i+vYNpdOjPkAAAAAAQEgAPkClQAAAAAXqRTsnn3uII16My+Kc4ub3D64zHgicIcAAQCEAgAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////8CYAD/////AgDyBSoBAAAAF6kUx/+ZHBBBZ+6E/US1N2Oe7IDItXiHAAAAAAAAAAAmaiSqIant4vYcP3HR3v0/qZnfo2lTdVxpBol5mWK0i+vYNpdOjPkAAAAAAQEgAPIFKgEAAAAXqRTH/5kcEEFn7oT9RLU3Y57sgMi1eIcBBxcWABRDVkPBhZHK7tVQqp2uWqQC/GGTCgEIawJHMEQCIEv8/8VpUz0dK4MCcVzS7zoyt+hPRvWwLskZBuaurnFiAiBIuyt1IRaHqFSspDbjDNM607nrDQz4lmDnekNqMNn07AEhAp1Ol7vKvG2Oi8RSrsb7uSPTET83/YXuknx63PhfCG/zAAAA").unwrap(),
original_psbt: Psbt::from_str("cHNidP8BAHECAAAAAeOsT9cRWRz3te+bgmtweG1vDLkdSH4057NuoodDNPFWAAAAAAD9////AhAnAAAAAAAAFgAUtp3bPFM/YWThyxD5Cc9OR4mb8tdMygUqAQAAABYAFODlplDoE6EGlZvmqoUngBgsu8qCAAAAAAABAIUCAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/////wMBZwD/////AgDyBSoBAAAAF6kU2JnIn4Mmcb5kuF3EYeFei8IB43qHAAAAAAAAAAAmaiSqIant4vYcP3HR3v0/qZnfo2lTdVxpBol5mWK0i+vYNpdOjPkAAAAAAQEgAPIFKgEAAAAXqRTYmcifgyZxvmS4XcRh4V6LwgHjeocBBxcWABSPGoPK1yl60X4Z9OfA7IQPUWCgVwEIawJHMEQCICZG3s2cbulPnLTvK4TwlKhsC+cem8tD2GjZZ3eMJD7FAiADh/xwv0ib8ksOrj1M27DYLiw7WFptxkMkE2YgiNMRVgEhAlDMm5DA8kU+QGiPxEWUyV1S8+XGzUOepUOck257ZOhkAAAiAgP+oMbeca66mt+UtXgHm6v/RIFEpxrwG7IvPDim5KWHpBgfVHrXVAAAgAEAAIAAAACAAQAAAAAAAAAA").unwrap(),
payjoin_psbt: Psbt::from_str("cHNidP8BAJoCAAAAAuXYOTUaVRiB8cPPhEXzcJ72/SgZOPEpPx5pkG0fNeGCAAAAAAD9////46xP1xFZHPe175uCa3B4bW8MuR1IfjTns26ih0M08VYAAAAAAP3///8CEBkGKgEAAAAWABQHuuu4H4fbQWV51IunoJLUtmMTfEzKBSoBAAAAFgAU4OWmUOgToQaVm+aqhSeAGCy7yoIAAAAAAAEBIADyBSoBAAAAF6kUQ4BssmVBS3r0s95c6dl1DQCHCR+HAQQWABQbDc333XiiOeEXroP523OoYNb1aAABAIUCAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/////wMBZwD/////AgDyBSoBAAAAF6kU2JnIn4Mmcb5kuF3EYeFei8IB43qHAAAAAAAAAAAmaiSqIant4vYcP3HR3v0/qZnfo2lTdVxpBol5mWK0i+vYNpdOjPkAAAAAAQEgAPIFKgEAAAAXqRTYmcifgyZxvmS4XcRh4V6LwgHjeocBBxcWABSPGoPK1yl60X4Z9OfA7IQPUWCgVwEIawJHMEQCICZG3s2cbulPnLTvK4TwlKhsC+cem8tD2GjZZ3eMJD7FAiADh/xwv0ib8ksOrj1M27DYLiw7WFptxkMkE2YgiNMRVgEhAlDMm5DA8kU+QGiPxEWUyV1S8+XGzUOepUOck257ZOhkAAAA").unwrap(),
params: Params::default(),
change_vout: 0
};
// Currently nested segwit is not supported, see https://github.com/payjoin/rust-payjoin/issues/358
assert!(nested_p2wpkh_proposal.additional_input_weight().is_err());
assert_eq!(
nested_p2wpkh_proposal
.additional_input_weight()
.expect("should calculate input weight"),
Weight::from_wu(364)
);

// Input weight for a single P2WPKH (native segwit) receiver input
let p2wpkh_proposal = ProvisionalProposal {
Expand Down
2 changes: 1 addition & 1 deletion payjoin/src/receive/v2/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -468,7 +468,7 @@ impl WantsInputs {
/// Any excess input amount is added to the change_vout output indicated previously.
pub fn contribute_witness_inputs(
self,
inputs: impl IntoIterator<Item = (OutPoint, TxOut)>,
inputs: impl IntoIterator<Item = (bitcoin::psbt::Input, bitcoin::TxIn)>,
) -> Result<WantsInputs, InputContributionError> {
let inner = self.inner.contribute_witness_inputs(inputs)?;
Ok(WantsInputs { inner, context: self.context })
Expand Down
62 changes: 32 additions & 30 deletions payjoin/tests/integration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -639,15 +639,10 @@ mod integration {
.iter()
.find(|i| i.txid == selected_outpoint.txid && i.vout == selected_outpoint.vout)
.unwrap();
let txo_to_contribute = bitcoin::TxOut {
value: selected_utxo.amount,
script_pubkey: selected_utxo.script_pub_key.clone(),
};
let input_pair = input_pair_from_list_unspent(selected_utxo);

let payjoin = payjoin
.contribute_witness_inputs(vec![(selected_outpoint, txo_to_contribute)])
.unwrap()
.commit_inputs();
let payjoin =
payjoin.contribute_witness_inputs(vec![input_pair]).unwrap().commit_inputs();

// Sign and finalize the proposal PSBT
let payjoin_proposal = payjoin
Expand Down Expand Up @@ -753,7 +748,7 @@ mod integration {
let (bitcoind, sender, receiver) = init_bitcoind_sender_receiver()?;
// Generate more UTXOs for the receiver
let receiver_address =
receiver.get_new_address(None, Some(AddressType::Bech32))?.assume_checked();
receiver.get_new_address(None, Some(AddressType::P2shSegwit))?.assume_checked();
bitcoind.client.generate_to_address(199, &receiver_address)?;
let receiver_utxos = receiver.list_unspent(None, None, None, None, None).unwrap();
assert_eq!(100, receiver_utxos.len(), "receiver doesn't have enough UTXOs");
Expand Down Expand Up @@ -796,17 +791,7 @@ mod integration {
.script_pubkey(),
}];
let drain_script = outputs[0].script_pubkey.clone();
let inputs = receiver_utxos
.iter()
.map(|utxo| {
let outpoint = OutPoint { txid: utxo.txid, vout: utxo.vout };
let txo = bitcoin::TxOut {
value: utxo.amount,
script_pubkey: utxo.script_pub_key.clone(),
};
(outpoint, txo)
})
.collect();
let inputs = receiver_utxos.iter().map(input_pair_from_list_unspent).collect();
let response = handle_v1_pj_request(
req,
headers,
Expand Down Expand Up @@ -953,10 +938,10 @@ mod integration {
let bitcoind = bitcoind::BitcoinD::with_conf(bitcoind_exe, &conf)?;
let receiver = bitcoind.create_wallet("receiver")?;
let receiver_address =
receiver.get_new_address(None, Some(AddressType::Bech32))?.assume_checked();
receiver.get_new_address(None, Some(AddressType::P2shSegwit))?.assume_checked();
let sender = bitcoind.create_wallet("sender")?;
let sender_address =
sender.get_new_address(None, Some(AddressType::Bech32))?.assume_checked();
sender.get_new_address(None, Some(AddressType::P2shSegwit))?.assume_checked();
bitcoind.client.generate_to_address(1, &receiver_address)?;
bitcoind.client.generate_to_address(101, &sender_address)?;

Expand Down Expand Up @@ -1008,7 +993,7 @@ mod integration {
receiver: &bitcoincore_rpc::Client,
custom_outputs: Option<Vec<TxOut>>,
drain_script: Option<&bitcoin::Script>,
custom_inputs: Option<Vec<(OutPoint, TxOut)>>,
custom_inputs: Option<Vec<(bitcoin::psbt::Input, bitcoin::TxIn)>>,
) -> String {
// Receiver receive payjoin proposal, IRL it will be an HTTP request (over ssl or onion)
let proposal = payjoin::receive::UncheckedProposal::from_request(
Expand All @@ -1030,7 +1015,7 @@ mod integration {
receiver: &bitcoincore_rpc::Client,
custom_outputs: Option<Vec<TxOut>>,
drain_script: Option<&bitcoin::Script>,
custom_inputs: Option<Vec<(OutPoint, TxOut)>>,
custom_inputs: Option<Vec<(bitcoin::psbt::Input, bitcoin::TxIn)>>,
) -> payjoin::receive::PayjoinProposal {
// in a payment processor where the sender could go offline, this is where you schedule to broadcast the original_tx
let _to_broadcast_in_failure_case = proposal.extract_tx_to_schedule_broadcast();
Expand Down Expand Up @@ -1100,11 +1085,8 @@ mod integration {
.iter()
.find(|i| i.txid == selected_outpoint.txid && i.vout == selected_outpoint.vout)
.unwrap();
let txo_to_contribute = bitcoin::TxOut {
value: selected_utxo.amount,
script_pubkey: selected_utxo.script_pub_key.clone(),
};
vec![(selected_outpoint, txo_to_contribute)]
let input_pair = input_pair_from_list_unspent(selected_utxo);
vec![input_pair]
}
};

Expand Down Expand Up @@ -1147,11 +1129,31 @@ mod integration {

fn predicted_tx_weight(tx: &bitcoin::Transaction) -> Weight {
bitcoin::transaction::predict_weight(
vec![bitcoin::transaction::InputWeightPrediction::P2WPKH_MAX; tx.input.len()],
vec![bitcoin::transaction::InputWeightPrediction::new(23, [72, 33]); tx.input.len()],
tx.script_pubkey_lens(),
)
}

fn input_pair_from_list_unspent(
utxo: &bitcoind::bitcoincore_rpc::bitcoincore_rpc_json::ListUnspentResultEntry,
) -> (bitcoin::psbt::Input, bitcoin::TxIn) {
let psbtin = bitcoin::psbt::Input {
// TODO: non_witness_utxo for legacy support
witness_utxo: Some(TxOut {
value: utxo.amount,
script_pubkey: utxo.script_pub_key.clone(),
}),
redeem_script: utxo.redeem_script.clone(),
witness_script: utxo.witness_script.clone(),
..Default::default()
};
let txin = bitcoin::TxIn {
previous_output: OutPoint { txid: utxo.txid, vout: utxo.vout },
..Default::default()
};
(psbtin, txin)
}

struct HeaderMock(HashMap<String, String>);

impl payjoin::receive::Headers for HeaderMock {
Expand Down

0 comments on commit 8961812

Please sign in to comment.