Skip to content

Commit 48b47b1

Browse files
committed
Separate out new CovSatisfier from LegacySatisfier
Introspection satisfaction does not require any special satisfier.
1 parent 0a3a79e commit 48b47b1

File tree

7 files changed

+94
-77
lines changed

7 files changed

+94
-77
lines changed

src/descriptor/csfs_cov/mod.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ mod satisfy;
4848
mod script_internals;
4949
pub use self::cov::LegacyCSFSCov;
5050
pub use self::error::CovError;
51-
pub use self::satisfy::CovSatisfier;
51+
pub use self::satisfy::LegacyCovSatisfier;
5252
pub use self::script_internals::CovOperations;
5353

5454
#[cfg(test)]
@@ -219,7 +219,7 @@ mod tests {
219219

220220
// Try to satisfy the covenant part
221221
let script_code = desc.cov_script_code();
222-
let cov_sat = CovSatisfier::new_segwitv0(
222+
let cov_sat = LegacyCovSatisfier::new_segwitv0(
223223
&spend_tx,
224224
0,
225225
confidential::Value::Explicit(200_000),
@@ -412,7 +412,7 @@ mod tests {
412412
// Try to satisfy the covenant part
413413
let desc = desc.as_cov().unwrap();
414414
let script_code = desc.cov_script_code();
415-
let cov_sat = CovSatisfier::new_segwitv0(
415+
let cov_sat = LegacyCovSatisfier::new_segwitv0(
416416
&spend_tx,
417417
0,
418418
confidential::Value::Explicit(200_000),

src/descriptor/csfs_cov/satisfy.rs

Lines changed: 4 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,7 @@
1717
use elements::encode::Encodable;
1818
use elements::hashes::{sha256d, Hash};
1919
use elements::sighash::SigHashCache;
20-
use elements::{
21-
self, confidential, EcdsaSigHashType, OutPoint, Script, SigHash, Transaction, TxOut,
22-
};
20+
use elements::{self, confidential, EcdsaSigHashType, OutPoint, Script, SigHash, Transaction};
2321

2422
use super::CovError;
2523
use crate::{MiniscriptKey, Satisfier, ToPublicKey};
@@ -30,7 +28,7 @@ use crate::{MiniscriptKey, Satisfier, ToPublicKey};
3028
/// being satisfied and 'ptx denotes the lifetime
3129
/// of the previous transaction inputs
3230
#[derive(Debug, Clone, PartialEq, Eq)]
33-
pub struct CovSatisfier<'tx, 'ptx> {
31+
pub struct LegacyCovSatisfier<'tx, 'ptx> {
3432
// Common fields in Segwit and Taphash
3533
/// The transaction being spent
3634
tx: &'tx Transaction,
@@ -45,33 +43,9 @@ pub struct CovSatisfier<'tx, 'ptx> {
4543
script_code: Option<&'ptx Script>,
4644
/// The value of the output being spent
4745
value: Option<confidential::Value>,
48-
49-
// Taproot
50-
/// The utxos used in transaction
51-
/// This construction should suffice for Taproot
52-
/// related covenant spends too.
53-
spent_utxos: Option<&'ptx [TxOut]>,
5446
}
5547

56-
impl<'tx, 'ptx> CovSatisfier<'tx, 'ptx> {
57-
/// Create a new CovSatisfier for taproot spends
58-
/// **Panics**
59-
/// 1) if number of spent_utxos is not equal to
60-
/// number of transaction inputs.
61-
/// 2) if idx is out of bounds
62-
pub fn new_taproot(tx: &'tx Transaction, spent_utxos: &'ptx [TxOut], idx: u32) -> Self {
63-
assert!(spent_utxos.len() == tx.input.len());
64-
assert!((idx as usize) < spent_utxos.len());
65-
Self {
66-
tx,
67-
idx,
68-
hash_type: EcdsaSigHashType::All, // This is not used in taproot. Update PsbtSigHashType later
69-
script_code: None,
70-
value: None,
71-
spent_utxos: Some(spent_utxos),
72-
}
73-
}
74-
48+
impl<'tx, 'ptx> LegacyCovSatisfier<'tx, 'ptx> {
7549
/// Create a new Covsatisfier for v0 spends
7650
/// Panics if idx is out of bounds
7751
pub fn new_segwitv0(
@@ -88,7 +62,6 @@ impl<'tx, 'ptx> CovSatisfier<'tx, 'ptx> {
8862
hash_type,
8963
script_code: Some(script_code),
9064
value: Some(value),
91-
spent_utxos: None,
9265
}
9366
}
9467

@@ -106,15 +79,7 @@ impl<'tx, 'ptx> CovSatisfier<'tx, 'ptx> {
10679
}
10780
}
10881

109-
impl<'tx, 'ptx, Pk: MiniscriptKey + ToPublicKey> Satisfier<Pk> for CovSatisfier<'tx, 'ptx> {
110-
fn lookup_spent_utxos(&self) -> Option<&[elements::TxOut]> {
111-
self.spent_utxos
112-
}
113-
114-
fn lookup_tx(&self) -> Option<&elements::Transaction> {
115-
Some(self.tx)
116-
}
117-
82+
impl<'tx, 'ptx, Pk: MiniscriptKey + ToPublicKey> Satisfier<Pk> for LegacyCovSatisfier<'tx, 'ptx> {
11883
fn lookup_nversion(&self) -> Option<u32> {
11984
Some(self.tx.version)
12085
}

src/descriptor/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ pub use self::sh::{Sh, ShInner};
6060
pub use self::sortedmulti::SortedMultiVec;
6161
mod checksum;
6262
mod key;
63-
pub use self::csfs_cov::{CovError, CovOperations, CovSatisfier, LegacyCSFSCov};
63+
pub use self::csfs_cov::{CovError, CovOperations, LegacyCSFSCov, LegacyCovSatisfier};
6464
pub use self::key::{
6565
ConversionError, DerivedDescriptorKey, DescriptorKeyParseError, DescriptorPublicKey,
6666
DescriptorSecretKey, DescriptorXKey, InnerXKey, SinglePriv, SinglePub, SinglePubKey, Wildcard,

src/extensions/mod.rs

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use std::{fmt, hash};
66

77
use elements::script::Builder;
88
use elements::sighash::Prevouts;
9-
use elements::Transaction;
9+
use elements::{Transaction, TxOut};
1010

1111
use crate::expression::Tree;
1212
use crate::interpreter::{self, Stack};
@@ -427,3 +427,43 @@ where
427427
Ok(ext)
428428
}
429429
}
430+
431+
/// A satisfier for Covenant descriptors
432+
/// that can do transaction introspection
433+
/// 'tx denotes the lifetime of the transaction
434+
/// being satisfied and 'ptx denotes the lifetime
435+
/// of the previous transaction inputs
436+
#[derive(Debug, Clone, PartialEq, Eq)]
437+
pub struct CovSatisfier<'tx, 'ptx> {
438+
/// The transaction being spent
439+
tx: &'tx Transaction,
440+
/// Spent utxos
441+
spent_utxos: &'ptx [TxOut],
442+
/// The input index being spent
443+
idx: usize,
444+
}
445+
446+
impl<'tx, 'ptx> CovSatisfier<'tx, 'ptx> {
447+
/// Returns None when spent_utos.len() != tx.input.len()
448+
pub fn new(tx: &'tx Transaction, spent_utxos: &'ptx [TxOut], idx: usize) -> Option<Self> {
449+
if tx.input.len() != spent_utxos.len() {
450+
None
451+
} else {
452+
Some(Self {
453+
tx,
454+
spent_utxos,
455+
idx,
456+
})
457+
}
458+
}
459+
}
460+
461+
impl<'tx, 'ptx, Pk: ToPublicKey> Satisfier<Pk> for CovSatisfier<'tx, 'ptx> {
462+
fn lookup_tx(&self) -> Option<&elements::Transaction> {
463+
Some(self.tx)
464+
}
465+
466+
fn lookup_spent_utxos(&self) -> Option<&[elements::TxOut]> {
467+
Some(self.spent_utxos)
468+
}
469+
}

src/psbt/finalizer.rs

Lines changed: 31 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,14 @@
2222
use bitcoin::{self, PublicKey, XOnlyPublicKey};
2323
use elements::secp256k1_zkp::{self, Secp256k1};
2424
use elements::taproot::LeafVersion;
25-
use elements::{self, confidential, Script, Transaction};
25+
use elements::{self, confidential, Script, Transaction, TxOut};
2626

2727
use super::{sanity_check, Error, InputError, Psbt, PsbtInputSatisfier};
28-
use crate::descriptor::{CovSatisfier, LegacyCSFSCov};
29-
use crate::extensions::CovExtArgs;
28+
use crate::descriptor::{LegacyCSFSCov, LegacyCovSatisfier};
29+
use crate::extensions::{CovExtArgs, CovSatisfier};
3030
use crate::{
31-
interpreter, util, BareCtx, Descriptor, Legacy, Miniscript, MiniscriptKey, Satisfier, Segwitv0,
32-
Tap,
31+
interpreter, util, BareCtx, CovenantExt, Descriptor, Legacy, Miniscript, MiniscriptKey,
32+
Satisfier, Segwitv0, Tap,
3333
};
3434

3535
// Get the amount being spent for the psbt input
@@ -51,30 +51,31 @@ fn get_amt(psbt: &Psbt, index: usize) -> Result<confidential::Value, InputError>
5151
// descriptor from psbt because the information about all the scripts might not
5252
// be present. Also, currently the spec does not support hidden branches, so
5353
// inferring a descriptor is not possible
54-
fn construct_tap_witness(
54+
fn construct_tap_witness<S>(
5555
spk: &Script,
56-
sat: &PsbtInputSatisfier<'_>,
56+
sat: &S,
5757
allow_mall: bool,
58-
) -> Result<Vec<Vec<u8>>, InputError> {
58+
) -> Result<Vec<Vec<u8>>, InputError>
59+
where
60+
S: Satisfier<XOnlyPublicKey>,
61+
{
5962
assert!(util::is_v1_p2tr(spk));
6063

6164
// try the key spend path first
62-
if let Some(sig) =
63-
<PsbtInputSatisfier<'_> as Satisfier<XOnlyPublicKey>>::lookup_tap_key_spend_sig(sat)
64-
{
65+
if let Some(sig) = sat.lookup_tap_key_spend_sig() {
6566
return Ok(vec![sig.to_vec()]);
6667
}
6768
// Next script spends
6869
let (mut min_wit, mut min_wit_len) = (None, None);
69-
if let Some(block_map) =
70-
<PsbtInputSatisfier<'_> as Satisfier<XOnlyPublicKey>>::lookup_tap_control_block_map(sat)
71-
{
70+
if let Some(block_map) = sat.lookup_tap_control_block_map() {
7271
for (control_block, (script, ver)) in block_map {
7372
if *ver != LeafVersion::default() {
7473
// We don't know how to satisfy non default version scripts yet
7574
continue;
7675
}
77-
let ms = match Miniscript::<XOnlyPublicKey, Tap>::parse_insane(script) {
76+
let ms = match Miniscript::<XOnlyPublicKey, Tap, CovenantExt<CovExtArgs>>::parse_insane(
77+
script,
78+
) {
7879
Ok(ms) => ms,
7980
Err(..) => continue, // try another script
8081
};
@@ -390,6 +391,7 @@ fn input_sanity_checks(psbt: &Psbt, index: usize) -> Result<(), super::Error> {
390391
fn _finalize_inp(
391392
psbt: &mut Psbt,
392393
extracted_tx: &Transaction,
394+
spent_utxos: &[TxOut],
393395
index: usize,
394396
allow_mall: bool,
395397
) -> Result<(), super::Error> {
@@ -399,8 +401,10 @@ fn _finalize_inp(
399401
let psbt_sat = PsbtInputSatisfier::new(psbt, index);
400402

401403
if util::is_v1_p2tr(spk) {
404+
let cov_sat = CovSatisfier::new(&extracted_tx, spent_utxos, index)
405+
.ok_or(super::Error::InputError(InputError::MissingUtxo, index))?;
402406
// Deal with tr case separately, unfortunately we cannot infer the full descriptor for Tr
403-
let wit = construct_tap_witness(spk, &psbt_sat, allow_mall)
407+
let wit = construct_tap_witness(spk, &(psbt_sat, cov_sat), allow_mall)
404408
.map_err(|e| Error::InputError(e, index))?;
405409
(wit, Script::new())
406410
} else {
@@ -417,7 +421,7 @@ fn _finalize_inp(
417421
.ok_or(super::Error::InputError(InputError::MissingUtxo, index))?;
418422
// Codesepartor calculation
419423
let script_code = cov.cov_script_code();
420-
let cov_sat = CovSatisfier::new_segwitv0(
424+
let cov_sat = LegacyCovSatisfier::new_segwitv0(
421425
extracted_tx,
422426
index as u32,
423427
utxo.value,
@@ -477,7 +481,8 @@ pub fn finalize_input<C: secp256k1_zkp::Verification>(
477481
input_sanity_checks(psbt, index)?;
478482

479483
let extracted_tx = psbt.extract_tx()?;
480-
_finalize_inp(psbt, &extracted_tx, index, allow_mall)?;
484+
let spent_utxos = prevouts(psbt)?;
485+
_finalize_inp(psbt, &extracted_tx, &spent_utxos, index, allow_mall)?;
481486

482487
interpreter_inp_check(psbt, secp, index, genesis_hash)?;
483488
Ok(())
@@ -504,8 +509,15 @@ pub fn finalize<C: secp256k1_zkp::Verification>(
504509

505510
// Actually construct the witnesses
506511
let extracted_tx = psbt.extract_tx()?;
512+
let spent_utxos = prevouts(psbt)?;
507513
for index in 0..psbt.inputs().len() {
508-
_finalize_inp(psbt, &extracted_tx, index, /*allow_mall*/ false)?;
514+
_finalize_inp(
515+
psbt,
516+
&extracted_tx,
517+
&spent_utxos,
518+
index,
519+
/*allow_mall*/ false,
520+
)?;
509521
}
510522
// Double check everything with the interpreter
511523
// This only checks whether the script will be executed

src/psbt/mod.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ mod finalizer;
4545
pub use elements::pset as psbt;
4646

4747
pub use self::finalizer::{finalize, finalize_input, interpreter_check, interpreter_inp_check};
48-
use crate::descriptor::{CovSatisfier, Tr};
48+
use crate::descriptor::{LegacyCovSatisfier, Tr};
4949
use crate::util;
5050

5151
/// Error type for entire Psbt
@@ -300,7 +300,8 @@ pub struct PsbtInputSatisfier<'psbt> {
300300
/// Psbt Input Satisfier with Covenant support. Users should be
301301
/// using the high level [`finalizer::finalize`] API.
302302
/// The [`CovSatisfier`] should be consistent with the extracted transaction.
303-
pub type PsbtCovInputSatisfier<'psbt> = (PsbtInputSatisfier<'psbt>, CovSatisfier<'psbt, 'psbt>);
303+
pub type PsbtCovInputSatisfier<'psbt> =
304+
(PsbtInputSatisfier<'psbt>, LegacyCovSatisfier<'psbt, 'psbt>);
304305

305306
impl<'psbt> PsbtInputSatisfier<'psbt> {
306307
/// create a new PsbtInputsatisfier from

tests/test_arith.rs

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,10 @@ use elements::{
1212
};
1313
use elementsd::ElementsD;
1414
use miniscript::miniscript::iter;
15-
use miniscript::psbt::PsbtInputExt;
15+
use miniscript::psbt::{PsbtExt, PsbtInputExt};
1616
use miniscript::{Descriptor, MiniscriptKey, ToPublicKey};
1717
use rand::RngCore;
1818
mod setup;
19-
use miniscript::descriptor::CovSatisfier;
20-
use miniscript::psbt::PsbtInputSatisfier;
2119
use setup::test_util::{self, TestData, PARAMS};
2220
use setup::Call;
2321
use {actual_rand as rand, elements_miniscript as miniscript};
@@ -164,15 +162,16 @@ pub fn test_desc_satisfy(cl: &ElementsD, testdata: &TestData, desc: &str) -> Vec
164162
println!("Testing descriptor: {}", desc);
165163
// Finalize the transaction using psbt
166164
// Let miniscript do it's magic!
167-
let psbt_sat = PsbtInputSatisfier::new(&psbt, 0);
168-
let mut tx = psbt.extract_tx().unwrap();
169-
let utxos = [witness_utxo];
170-
let unsigned_tx = &tx.clone();
171-
let cov_sat = CovSatisfier::new_taproot(unsigned_tx, &utxos, 0);
172-
173-
derived_desc
174-
.satisfy(&mut tx.input[0], (psbt_sat, cov_sat))
175-
.expect("Satisfaction error");
165+
if let Err(e) = psbt.finalize_mall_mut(&secp, testdata.pubdata.genesis_hash) {
166+
// All miniscripts should satisfy
167+
panic!(
168+
"Could not satisfy non-malleably: error{} desc:{} ",
169+
e[0], desc
170+
);
171+
}
172+
let tx = psbt
173+
.extract(&secp, testdata.pubdata.genesis_hash)
174+
.expect("Extraction error");
176175

177176
// Send the transactions to bitcoin node for mining.
178177
// Regtest mode has standardness checks

0 commit comments

Comments
 (0)