Skip to content

Commit 6cf7711

Browse files
committed
Merge ElementsProject#26: Introduce 64 bit arithmetic expressions
8158647 Add CurrInp to tx value introspection (sanket1729) 1fcc865 Clean up interpreter/satisfaction logic with TxEnv struct (sanket1729) 48b47b1 Separate out new CovSatisfier from LegacySatisfier (sanket1729) 0a3a79e Add integration tests (sanket1729) 97f16a9 Introduce arithematic expressions (sanket1729) 5c91ae1 Allow interpreter to access tx and prevouts (sanket1729) 3a56d9f Change parse_num to accept generic (sanket1729) Pull request description: - Adds unit and integration tests - Does not have integration tests for issuance/re-issuance because creating those is painful on the rust side. Please carefully review the Script translation of those fragments ACKs for top commit: apoelstra: ACK 8158647 Tree-SHA512: 7cf8299533fdc0e03f321b5c3f544c4c513af9a36bade69f6c3434db942941c6bff8aa215b9660ad9526f0b5f94282c5922f0b092dfa75c424a17abaadb49b23
2 parents 0a46419 + 8158647 commit 6cf7711

File tree

22 files changed

+1680
-113
lines changed

22 files changed

+1680
-113
lines changed

examples/verify_tx.rs

Lines changed: 11 additions & 7 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();
@@ -124,11 +125,14 @@ fn main() {
124125
)
125126
.unwrap();
126127

127-
let iter = interpreter.iter_custom(Box::new(|key_sig: &KeySigPair| {
128-
let (pk, ecdsa_sig) = key_sig.as_ecdsa().expect("Ecdsa Sig");
129-
ecdsa_sig.1 == elements::EcdsaSigHashType::All
130-
&& secp.verify_ecdsa(&message, &ecdsa_sig.0, &pk.inner).is_ok()
131-
}));
128+
let iter = interpreter.iter_custom(
129+
Box::new(|key_sig: &KeySigPair| {
130+
let (pk, ecdsa_sig) = key_sig.as_ecdsa().expect("Ecdsa Sig");
131+
ecdsa_sig.1 == elements::EcdsaSigHashType::All
132+
&& secp.verify_ecdsa(&message, &ecdsa_sig.0, &pk.inner).is_ok()
133+
}),
134+
None, // txenv
135+
);
132136
println!("\nExample three");
133137
for elem in iter {
134138
let error = elem.expect_err("evaluation error");

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 & 36 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,38 +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(
63-
tx: &'tx Transaction,
64-
spent_utxos: &'ptx [TxOut],
65-
idx: u32,
66-
hash_type: EcdsaSigHashType,
67-
) -> Self {
68-
assert!(spent_utxos.len() == tx.input.len());
69-
assert!((idx as usize) < spent_utxos.len());
70-
Self {
71-
tx,
72-
idx,
73-
hash_type,
74-
script_code: None,
75-
value: None,
76-
spent_utxos: Some(spent_utxos),
77-
}
78-
}
79-
48+
impl<'tx, 'ptx> LegacyCovSatisfier<'tx, 'ptx> {
8049
/// Create a new Covsatisfier for v0 spends
8150
/// Panics if idx is out of bounds
8251
pub fn new_segwitv0(
@@ -93,7 +62,6 @@ impl<'tx, 'ptx> CovSatisfier<'tx, 'ptx> {
9362
hash_type,
9463
script_code: Some(script_code),
9564
value: Some(value),
96-
spent_utxos: None,
9765
}
9866
}
9967

@@ -111,7 +79,7 @@ impl<'tx, 'ptx> CovSatisfier<'tx, 'ptx> {
11179
}
11280
}
11381

114-
impl<'tx, 'ptx, Pk: MiniscriptKey + ToPublicKey> Satisfier<Pk> for CovSatisfier<'tx, 'ptx> {
82+
impl<'tx, 'ptx, Pk: MiniscriptKey + ToPublicKey> Satisfier<Pk> for LegacyCovSatisfier<'tx, 'ptx> {
11583
fn lookup_nversion(&self) -> Option<u32> {
11684
Some(self.tx.version)
11785
}

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/descriptor/sortedmulti.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ impl<Pk: MiniscriptKey, Ctx: ScriptContext> SortedMultiVec<Pk, Ctx> {
7878
if tree.args.is_empty() {
7979
return Err(errstr("no arguments given for sortedmulti"));
8080
}
81-
let k = expression::parse_num(tree.args[0].name)?;
81+
let k = expression::parse_num::<u32>(tree.args[0].name)?;
8282
if k > (tree.args.len() - 1) as u32 {
8383
return Err(errstr(
8484
"higher threshold than there were keys in sortedmulti",

src/expression.rs

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -230,16 +230,23 @@ impl<'a> Tree<'a> {
230230
}
231231

232232
/// Parse a string as a u32, for timelocks or thresholds
233-
pub fn parse_num(s: &str) -> Result<u32, Error> {
233+
pub fn parse_num<T: FromStr>(s: &str) -> Result<T, Error> {
234234
if s.len() > 1 {
235235
let ch = s.chars().next().unwrap();
236+
let ch = if ch == '-' {
237+
s.chars().nth(1).ok_or(Error::Unexpected(
238+
"Negative number must follow dash sign".to_string(),
239+
))?
240+
} else {
241+
ch
242+
};
236243
if !('1'..='9').contains(&ch) {
237244
return Err(Error::Unexpected(
238245
"Number must start with a digit 1-9".to_string(),
239246
));
240247
}
241248
}
242-
u32::from_str(s).map_err(|_| errstr(s))
249+
T::from_str(s).map_err(|_| errstr(s))
243250
}
244251

245252
/// Attempts to parse a terminal expression
@@ -292,11 +299,11 @@ mod tests {
292299

293300
#[test]
294301
fn test_parse_num() {
295-
assert!(parse_num("0").is_ok());
296-
assert!(parse_num("00").is_err());
297-
assert!(parse_num("0000").is_err());
298-
assert!(parse_num("06").is_err());
299-
assert!(parse_num("+6").is_err());
300-
assert!(parse_num("-6").is_err());
302+
assert!(parse_num::<u32>("0").is_ok());
303+
assert!(parse_num::<u32>("00").is_err());
304+
assert!(parse_num::<u32>("0000").is_err());
305+
assert!(parse_num::<u32>("06").is_err());
306+
assert!(parse_num::<u32>("+6").is_err());
307+
assert!(parse_num::<u32>("-6").is_err());
301308
}
302309
}

0 commit comments

Comments
 (0)