Skip to content

Commit 343e65f

Browse files
committed
Derive weight from witness for p2wsh inputs
If witness is available for p2wsh inputs we should be using the size of the witness to get expected input weight. Doing this incidentally enables p2wsh inputs for the payjoin sender. Related issue: payjoin#659
1 parent 26eff9b commit 343e65f

File tree

3 files changed

+71
-15
lines changed

3 files changed

+71
-15
lines changed

payjoin/src/core/psbt/mod.rs

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,27 @@ impl InternalInputPair<'_> {
206206
}
207207
}
208208
P2wpkh => Ok(InputWeightPrediction::P2WPKH_MAX),
209-
P2wsh => Err(InputWeightError::NotSupported),
209+
P2wsh =>
210+
if !self.txin.witness.is_empty() {
211+
Ok(InputWeightPrediction::new(
212+
0,
213+
self.txin.witness.iter().map(|el| el.len()).collect::<Vec<_>>(),
214+
))
215+
} else {
216+
let iwp = self
217+
.psbtin
218+
.final_script_witness
219+
.as_ref()
220+
.filter(|w| !w.is_empty())
221+
.map(|w| {
222+
InputWeightPrediction::new(
223+
0,
224+
w.iter().map(|el| el.len()).collect::<Vec<_>>(),
225+
)
226+
})
227+
.ok_or(InputWeightError::NotSupported)?;
228+
Ok(iwp)
229+
},
210230
P2tr => Ok(InputWeightPrediction::P2TR_KEY_DEFAULT_SIGHASH),
211231
_ => Err(AddressTypeError::UnknownAddressType.into()),
212232
}?;
@@ -245,6 +265,8 @@ pub(crate) enum InternalPsbtInputError {
245265
AddressType(AddressTypeError),
246266
InvalidScriptPubKey(AddressType),
247267
WeightError(InputWeightError),
268+
/// Weight was provided but can be calculated from available information
269+
ProvidedUnnecessaryWeight,
248270
}
249271

250272
impl fmt::Display for InternalPsbtInputError {
@@ -256,6 +278,7 @@ impl fmt::Display for InternalPsbtInputError {
256278
Self::AddressType(_) => write!(f, "invalid address type"),
257279
Self::InvalidScriptPubKey(e) => write!(f, "provided script was not a valid type of {e}"),
258280
Self::WeightError(e) => write!(f, "{e}"),
281+
Self::ProvidedUnnecessaryWeight => write!(f, "weight was provided but can be calculated from available information"),
259282
}
260283
}
261284
}
@@ -269,6 +292,7 @@ impl std::error::Error for InternalPsbtInputError {
269292
Self::AddressType(error) => Some(error),
270293
Self::InvalidScriptPubKey(_) => None,
271294
Self::WeightError(error) => Some(error),
295+
Self::ProvidedUnnecessaryWeight => None,
272296
}
273297
}
274298
}

payjoin/src/core/receive/mod.rs

Lines changed: 44 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -61,15 +61,13 @@ impl InputPair {
6161
let raw = InternalInputPair { txin: &txin, psbtin: &psbtin };
6262
raw.validate_utxo()?;
6363

64-
let expected_weight = match raw.expected_input_weight() {
65-
Ok(weight) => weight,
66-
Err(InputWeightError::NotSupported) =>
67-
if let Some(weight) = expected_weight {
68-
weight
69-
} else {
70-
return Err(InternalPsbtInputError::from(InputWeightError::NotSupported).into());
71-
},
72-
Err(e) => return Err(InternalPsbtInputError::from(e).into()),
64+
let expected_weight = match (raw.expected_input_weight(), expected_weight) {
65+
(Ok(_), Some(_)) => {
66+
return Err(InternalPsbtInputError::ProvidedUnnecessaryWeight.into());
67+
}
68+
(Ok(weight), None) => weight,
69+
(Err(InputWeightError::NotSupported), Some(expected_weight)) => expected_weight,
70+
(Err(e), _) => return Err(InternalPsbtInputError::from(e).into()),
7371
};
7472

7573
let input_pair = Self { expected_weight, txin, psbtin };
@@ -246,7 +244,9 @@ mod tests {
246244
use bitcoin::key::{PublicKey, WPubkeyHash};
247245
use bitcoin::secp256k1::Secp256k1;
248246
use bitcoin::transaction::InputWeightPrediction;
249-
use bitcoin::{Amount, PubkeyHash, ScriptBuf, ScriptHash, Txid, WScriptHash, XOnlyPublicKey};
247+
use bitcoin::{
248+
witness, Amount, PubkeyHash, ScriptBuf, ScriptHash, Txid, WScriptHash, XOnlyPublicKey,
249+
};
250250
use payjoin_test_utils::{DUMMY20, DUMMY32};
251251

252252
use super::*;
@@ -457,7 +457,7 @@ mod tests {
457457

458458
let p2wsh_pair = InputPair::new(
459459
TxIn { previous_output: outpoint, sequence, ..Default::default() },
460-
psbt::Input { witness_utxo: Some(p2wsh_txout), ..Default::default() },
460+
psbt::Input { witness_utxo: Some(p2wsh_txout.clone()), ..Default::default() },
461461
None,
462462
);
463463
// P2wsh is not supported when expected weight is not provided
@@ -475,7 +475,39 @@ mod tests {
475475
.err()
476476
.unwrap(),
477477
PsbtInputError::from(InvalidScriptPubKey(AddressType::P2wsh))
478-
)
478+
);
479+
480+
let mut dummy_witness = witness::Witness::new();
481+
dummy_witness.push(DUMMY32);
482+
let txin = TxIn {
483+
previous_output: outpoint,
484+
witness: dummy_witness.clone(),
485+
..Default::default()
486+
};
487+
let input_weight = Weight::from_non_witness_data_size(txin.base_size() as u64)
488+
+ Weight::from_witness_data_size(dummy_witness.size() as u64);
489+
490+
// Add the witness straight to the txin
491+
let psbtin = psbt::Input { witness_utxo: Some(p2wsh_txout.clone()), ..Default::default() };
492+
let p2wsh_pair = InputPair::new(txin, psbtin, None).expect("witness is provided for p2wsh");
493+
assert_eq!(p2wsh_pair.expected_weight, input_weight);
494+
// Same check but add the witness to the psbtin
495+
let txin = TxIn { previous_output: outpoint, ..Default::default() };
496+
let psbtin = psbt::Input {
497+
witness_utxo: Some(p2wsh_txout),
498+
final_script_witness: Some(dummy_witness),
499+
..Default::default()
500+
};
501+
let p2wsh_pair = InputPair::new(txin.clone(), psbtin.clone(), None)
502+
.expect("witness is provided for p2wsh");
503+
assert_eq!(p2wsh_pair.expected_weight, input_weight);
504+
505+
// Should error out if expected weight is provided and witness is provided
506+
let p2wsh_pair = InputPair::new(txin, psbtin, Some(expected_weight));
507+
assert_eq!(
508+
p2wsh_pair.err().unwrap(),
509+
PsbtInputError::from(InternalPsbtInputError::ProvidedUnnecessaryWeight)
510+
);
479511
}
480512

481513
#[test]

payjoin/src/core/receive/v1/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1231,13 +1231,13 @@ pub(crate) mod test {
12311231
let input_pair_1 = InputPair::new(
12321232
TxIn { previous_output: ot1, sequence: Sequence::MAX, ..Default::default() },
12331233
Input { witness_utxo: Some(txout.clone()), ..Default::default() },
1234-
Some(Weight::from_wu(777)),
1234+
None,
12351235
)
12361236
.unwrap();
12371237
let input_pair_2 = InputPair::new(
12381238
TxIn { previous_output: ot2, sequence: Sequence::MAX, ..Default::default() },
12391239
Input { witness_utxo: Some(txout), ..Default::default() },
1240-
Some(Weight::from_wu(777)),
1240+
None,
12411241
)
12421242
.unwrap();
12431243

0 commit comments

Comments
 (0)