Skip to content

Commit 1fcc865

Browse files
committed
Clean up interpreter/satisfaction logic with TxEnv struct
1 parent 48b47b1 commit 1fcc865

File tree

9 files changed

+67
-63
lines changed

9 files changed

+67
-63
lines changed

examples/verify_tx.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ use std::str::FromStr;
2020

2121
use elements::encode::Decodable;
2222
use elements::{confidential, secp256k1_zkp};
23+
use miniscript::TxEnv;
2324

2425
use crate::miniscript::interpreter::KeySigPair; // secp256k1 re-exported from rust-bitcoin
2526
fn main() {
@@ -96,12 +97,12 @@ fn main() {
9697
spent_utxo.value = amount;
9798
// Create a spend utxos, since it is a segwit spend we don't really need all prevouts. Fill dummy data for this example instead
9899
let utxos = [spent_utxo, elements::TxOut::default()];
99-
let prevouts = elements::sighash::Prevouts::All(&utxos);
100+
let env = TxEnv::new(&transaction, &utxos, 0).expect("Input len == witness utxo len");
100101
// segwit spends don't require genesis hash
101102
let genesis_hash = elements::BlockHash::default();
102103

103104
println!("\nExample two");
104-
for elem in interpreter.iter(&secp, &transaction, 0, &prevouts, genesis_hash) {
105+
for elem in interpreter.iter(&secp, &env, genesis_hash) {
105106
match elem.expect("no evaluation error") {
106107
miniscript::interpreter::SatisfiedConstraint::PublicKey { key_sig } => {
107108
let (key, sig) = key_sig.as_ecdsa().unwrap();
@@ -130,8 +131,7 @@ fn main() {
130131
ecdsa_sig.1 == elements::EcdsaSigHashType::All
131132
&& secp.verify_ecdsa(&message, &ecdsa_sig.0, &pk.inner).is_ok()
132133
}),
133-
None, // tx
134-
None, //prevouts
134+
None, // txenv
135135
);
136136
println!("\nExample three");
137137
for elem in iter {

src/extensions/arith.rs

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use elements::opcodes::all::*;
77
use elements::sighash::Prevouts;
88
use elements::{opcodes, script, Transaction};
99

10-
use super::{ExtParam, ParseableExt};
10+
use super::{ExtParam, ParseableExt, TxEnv};
1111
use crate::expression::{FromTree, Tree};
1212
use crate::miniscript::context::ScriptContextError;
1313
use crate::miniscript::lex::{Token as Tk, TokenIter};
@@ -908,14 +908,13 @@ impl ParseableExt for Arith {
908908
fn evaluate<'intp, 'txin>(
909909
&'intp self,
910910
stack: &mut interpreter::Stack<'txin>,
911-
tx: Option<&Transaction>,
912-
prevouts: Option<&Prevouts<'txin>>,
911+
txenv: Option<&TxEnv>,
913912
) -> Result<bool, interpreter::Error> {
914-
let (tx, utxos) = match (tx, prevouts) {
915-
(Some(tx), Some(&Prevouts::All(utxos))) => (tx, utxos),
916-
_ => return Err(interpreter::Error::ArithError(EvalError::TxEnvNotPresent)),
917-
};
918-
match self.eval(tx, utxos) {
913+
let txenv = txenv
914+
.as_ref()
915+
.ok_or(interpreter::Error::ArithError(EvalError::TxEnvNotPresent))?;
916+
917+
match self.eval(txenv.tx(), txenv.spent_utxos()) {
919918
Ok(true) => {
920919
stack.push(interpreter::Element::Satisfied);
921920
Ok(true)

src/extensions/csfs.rs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,9 @@ use std::str::FromStr;
77
use bitcoin::hashes::hex::{FromHex, ToHex};
88
use bitcoin::XOnlyPublicKey;
99
use elements::hashes::hex;
10-
use elements::sighash::Prevouts;
11-
use elements::{self, opcodes, secp256k1_zkp, Transaction};
10+
use elements::{self, opcodes, secp256k1_zkp};
1211

13-
use super::{ArgFromStr, CovExtArgs, ExtParam, ParseableExt};
12+
use super::{ArgFromStr, CovExtArgs, ExtParam, ParseableExt, TxEnv};
1413
use crate::miniscript::context::ScriptContextError;
1514
use crate::miniscript::lex::{Token as Tk, TokenIter};
1615
use crate::miniscript::limits::MAX_STANDARD_P2WSH_STACK_ITEM_SIZE;
@@ -275,8 +274,7 @@ impl ParseableExt for CheckSigFromStack<CovExtArgs> {
275274
fn evaluate<'intp, 'txin>(
276275
&'intp self,
277276
stack: &mut interpreter::Stack<'txin>,
278-
_tx: Option<&Transaction>,
279-
_prevouts: Option<&Prevouts<'txin>>,
277+
_txenv: Option<&TxEnv>,
280278
) -> Result<bool, interpreter::Error> {
281279
let sig = stack[0].try_push()?;
282280

src/extensions/mod.rs

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
use std::{fmt, hash};
66

77
use elements::script::Builder;
8-
use elements::sighash::Prevouts;
98
use elements::{Transaction, TxOut};
109

1110
use crate::expression::Tree;
@@ -109,8 +108,7 @@ pub trait ParseableExt:
109108
fn evaluate<'intp, 'txin>(
110109
&'intp self,
111110
stack: &mut Stack<'txin>,
112-
tx: Option<&Transaction>,
113-
prevouts: Option<&Prevouts<'txin>>,
111+
txenv: Option<&TxEnv>,
114112
) -> Result<bool, interpreter::Error>;
115113

116114
/// Encoding of the current fragment
@@ -201,8 +199,7 @@ impl ParseableExt for NoExt {
201199
fn evaluate<'intp, 'txin>(
202200
&'intp self,
203201
_stack: &mut Stack<'txin>,
204-
_tx: Option<&Transaction>,
205-
_prevouts: Option<&Prevouts<'txin>>,
202+
_txenv: Option<&TxEnv>,
206203
) -> Result<bool, interpreter::Error> {
207204
match *self {}
208205
}
@@ -368,10 +365,9 @@ impl ParseableExt for CovenantExt<CovExtArgs> {
368365
fn evaluate<'intp, 'txin>(
369366
&self,
370367
stack: &mut Stack<'txin>,
371-
tx: Option<&Transaction>,
372-
prevouts: Option<&Prevouts<'txin>>,
368+
txenv: Option<&TxEnv>,
373369
) -> Result<bool, interpreter::Error> {
374-
all_arms_fn!(self, ParseableExt, evaluate, stack, tx, prevouts,)
370+
all_arms_fn!(self, ParseableExt, evaluate, stack, txenv,)
375371
}
376372

377373
fn push_to_builder(&self, builder: Builder) -> Builder {
@@ -434,7 +430,7 @@ where
434430
/// being satisfied and 'ptx denotes the lifetime
435431
/// of the previous transaction inputs
436432
#[derive(Debug, Clone, PartialEq, Eq)]
437-
pub struct CovSatisfier<'tx, 'ptx> {
433+
pub struct TxEnv<'tx, 'ptx> {
438434
/// The transaction being spent
439435
tx: &'tx Transaction,
440436
/// Spent utxos
@@ -443,7 +439,7 @@ pub struct CovSatisfier<'tx, 'ptx> {
443439
idx: usize,
444440
}
445441

446-
impl<'tx, 'ptx> CovSatisfier<'tx, 'ptx> {
442+
impl<'tx, 'ptx> TxEnv<'tx, 'ptx> {
447443
/// Returns None when spent_utos.len() != tx.input.len()
448444
pub fn new(tx: &'tx Transaction, spent_utxos: &'ptx [TxOut], idx: usize) -> Option<Self> {
449445
if tx.input.len() != spent_utxos.len() {
@@ -456,9 +452,24 @@ impl<'tx, 'ptx> CovSatisfier<'tx, 'ptx> {
456452
})
457453
}
458454
}
455+
456+
/// Obtains the tx
457+
pub fn tx(&self) -> &Transaction {
458+
self.tx
459+
}
460+
461+
/// Obtains the spend utxos
462+
pub fn spent_utxos(&self) -> &[TxOut] {
463+
self.spent_utxos
464+
}
465+
466+
/// Obtains the current input index
467+
pub fn idx(&self) -> usize {
468+
self.idx
469+
}
459470
}
460471

461-
impl<'tx, 'ptx, Pk: ToPublicKey> Satisfier<Pk> for CovSatisfier<'tx, 'ptx> {
472+
impl<'tx, 'ptx, Pk: ToPublicKey> Satisfier<Pk> for TxEnv<'tx, 'ptx> {
462473
fn lookup_tx(&self) -> Option<&elements::Transaction> {
463474
Some(self.tx)
464475
}

src/extensions/outputs_pref.rs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,8 @@ use std::fmt;
77
use elements::encode::serialize;
88
use elements::hashes::hex::{FromHex, ToHex};
99
use elements::hashes::{sha256d, Hash};
10-
use elements::sighash::Prevouts;
11-
use elements::{self, Transaction};
1210

13-
use super::{ExtParam, ParseableExt};
11+
use super::{ExtParam, ParseableExt, TxEnv};
1412
use crate::descriptor::CovError;
1513
use crate::miniscript::astelem::StackCtxOperations;
1614
use crate::miniscript::context::ScriptContextError;
@@ -246,8 +244,7 @@ impl ParseableExt for LegacyOutputsPref {
246244
fn evaluate<'intp, 'txin>(
247245
&'intp self,
248246
stack: &mut interpreter::Stack<'txin>,
249-
_tx: Option<&Transaction>,
250-
_prevouts: Option<&Prevouts<'txin>>,
247+
_txenv: Option<&TxEnv>,
251248
) -> Result<bool, interpreter::Error> {
252249
// Hash Outputs is at index 3
253250
let hash_outputs = stack[3];

src/extensions/tx_ver.rs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,8 @@
55
use std::fmt;
66

77
use elements::encode::serialize;
8-
use elements::sighash::Prevouts;
9-
use elements::{self, Transaction};
108

11-
use super::{ExtParam, ParseableExt};
9+
use super::{ExtParam, ParseableExt, TxEnv};
1210
use crate::descriptor::CovError;
1311
use crate::miniscript::astelem::StackCtxOperations;
1412
use crate::miniscript::lex::{Token as Tk, TokenIter};
@@ -169,8 +167,7 @@ impl ParseableExt for LegacyVerEq {
169167
fn evaluate<'intp, 'txin>(
170168
&'intp self,
171169
stack: &mut interpreter::Stack<'txin>,
172-
_tx: Option<&Transaction>,
173-
_prevouts: Option<&Prevouts<'txin>>,
170+
_txenv: Option<&TxEnv>,
174171
) -> Result<bool, interpreter::Error> {
175172
// Version is at index 11
176173
let ver = stack[11];

src/interpreter/mod.rs

Lines changed: 21 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,9 @@ use std::str::FromStr;
2424

2525
use bitcoin;
2626
use elements::hashes::{hash160, ripemd160, sha256, sha256d, Hash, HashEngine};
27-
use elements::sighash::Prevouts;
28-
use elements::{self, secp256k1_zkp, sighash, EcdsaSigHashType, SigHash, Transaction};
27+
use elements::{self, secp256k1_zkp, sighash, EcdsaSigHashType, SigHash};
2928

30-
use crate::extensions::{CovExtArgs, ParseableExt};
29+
use crate::extensions::{CovExtArgs, ParseableExt, TxEnv};
3130
use crate::miniscript::context::NoChecks;
3231
use crate::miniscript::ScriptContext;
3332
use crate::{util, Descriptor, ElementsSig, Miniscript, Terminal, ToPublicKey};
@@ -234,8 +233,7 @@ where
234233
pub fn iter_custom<'iter>(
235234
&'iter self,
236235
verify_sig: Box<dyn FnMut(&KeySigPair) -> bool + 'iter>,
237-
tx: Option<&'txin Transaction>,
238-
prevouts: Option<&'iter Prevouts<'txin>>,
236+
txenv: Option<&'txin TxEnv<'txin, 'txin>>,
239237
) -> Iter<'txin, 'iter, Ext> {
240238
Iter {
241239
verify_sig,
@@ -268,8 +266,7 @@ where
268266
None
269267
},
270268
has_errored: false,
271-
tx: tx,
272-
prevouts: prevouts,
269+
txenv: txenv,
273270
}
274271
}
275272

@@ -380,21 +377,27 @@ where
380377
pub fn iter<'iter, C: secp256k1_zkp::Verification>(
381378
&'iter self,
382379
secp: &'iter secp256k1_zkp::Secp256k1<C>,
383-
tx: &'txin elements::Transaction,
384-
input_idx: usize,
385-
prevouts: &'iter sighash::Prevouts<'_>, // actually a 'prevouts, but 'prevouts: 'iter
386-
genesis_hash: elements::BlockHash, // required for sighash computation in BIP341
380+
txenv: &'txin TxEnv, // actually a 'prevouts, but 'prevouts: 'iter
381+
genesis_hash: elements::BlockHash, // required for sighash computation in BIP341
387382
) -> Iter<'txin, 'iter, Ext> {
388383
self.iter_custom(
389-
Box::new(move |sig| self.verify_sig(secp, tx, input_idx, prevouts, genesis_hash, sig)),
390-
Some(tx),
391-
Some(prevouts),
384+
Box::new(move |sig| {
385+
self.verify_sig(
386+
secp,
387+
txenv.tx(),
388+
txenv.idx(),
389+
&sighash::Prevouts::All(txenv.spent_utxos()),
390+
genesis_hash,
391+
sig,
392+
)
393+
}),
394+
Some(txenv),
392395
)
393396
}
394397

395398
/// Creates an iterator over the satisfied spending conditions without checking signatures
396399
pub fn iter_assume_sigs<'iter>(&'iter self) -> Iter<'txin, 'iter, Ext> {
397-
self.iter_custom(Box::new(|_| true), None, None)
400+
self.iter_custom(Box::new(|_| true), None)
398401
}
399402

400403
/// Outputs a "descriptor" string which reproduces the spent coins
@@ -626,8 +629,7 @@ where
626629
public_key: Option<&'intp BitcoinKey>,
627630
state: Vec<NodeEvaluationState<'intp, Ext>>,
628631
stack: Stack<'txin>,
629-
tx: Option<&'txin Transaction>,
630-
prevouts: Option<&'intp Prevouts<'txin>>,
632+
txenv: Option<&'txin TxEnv<'txin, 'txin>>,
631633
age: u32,
632634
lock_time: u32,
633635
cov: Option<&'intp BitcoinKey>,
@@ -755,7 +757,7 @@ where
755757
}
756758
}
757759
Terminal::Ext(ref ext) => {
758-
let res = ext.evaluate(&mut self.stack, self.tx, self.prevouts);
760+
let res = ext.evaluate(&mut self.stack, self.txenv);
759761
match res {
760762
Ok(true) => {
761763
return Some(Ok(SatisfiedConstraint::Ext {
@@ -1308,8 +1310,7 @@ mod tests {
13081310
lock_time: 1002,
13091311
cov: None,
13101312
has_errored: false,
1311-
tx: None,
1312-
prevouts: None,
1313+
txenv: None,
13131314
}
13141315
}
13151316

src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ use elements::secp256k1_zkp::Secp256k1;
152152
use elements::{opcodes, script, secp256k1_zkp};
153153

154154
pub use crate::descriptor::{Descriptor, DescriptorPublicKey};
155-
pub use crate::extensions::{CovenantExt, Extension, NoExt};
155+
pub use crate::extensions::{CovenantExt, Extension, NoExt, TxEnv};
156156
pub use crate::interpreter::Interpreter;
157157
pub use crate::miniscript::context::{BareCtx, Legacy, ScriptContext, Segwitv0, Tap};
158158
pub use crate::miniscript::decode::Terminal;

src/psbt/finalizer.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ use elements::{self, confidential, Script, Transaction, TxOut};
2626

2727
use super::{sanity_check, Error, InputError, Psbt, PsbtInputSatisfier};
2828
use crate::descriptor::{LegacyCSFSCov, LegacyCovSatisfier};
29-
use crate::extensions::{CovExtArgs, CovSatisfier};
29+
use crate::extensions::{CovExtArgs, TxEnv};
3030
use crate::{
3131
interpreter, util, BareCtx, CovenantExt, Descriptor, Legacy, Miniscript, MiniscriptKey,
3232
Satisfier, Segwitv0, Tap,
@@ -303,9 +303,10 @@ pub fn _interpreter_inp_check<C: secp256k1_zkp::Verification>(
303303
.map_err(|e| Error::InputError(InputError::Interpreter(e), index))?;
304304

305305
let prevouts = prevouts(psbt)?;
306-
let prevouts = elements::sighash::Prevouts::All(&prevouts);
306+
let env = TxEnv::new(tx, &prevouts, index)
307+
.ok_or(Error::InputError(InputError::MissingUtxo, index))?;
307308
if let Some(error) = interpreter
308-
.iter(secp, tx, index, &prevouts, genesis_hash)
309+
.iter(secp, &env, genesis_hash)
309310
.filter_map(Result::err)
310311
.next()
311312
{
@@ -401,7 +402,7 @@ fn _finalize_inp(
401402
let psbt_sat = PsbtInputSatisfier::new(psbt, index);
402403

403404
if util::is_v1_p2tr(spk) {
404-
let cov_sat = CovSatisfier::new(&extracted_tx, spent_utxos, index)
405+
let cov_sat = TxEnv::new(&extracted_tx, spent_utxos, index)
405406
.ok_or(super::Error::InputError(InputError::MissingUtxo, index))?;
406407
// Deal with tr case separately, unfortunately we cannot infer the full descriptor for Tr
407408
let wit = construct_tap_witness(spk, &(psbt_sat, cov_sat), allow_mall)

0 commit comments

Comments
 (0)