kaspa_txscript/
lib.rs

1extern crate alloc;
2extern crate core;
3
4pub mod caches;
5mod data_stack;
6pub mod error;
7pub mod opcodes;
8pub mod result;
9pub mod script_builder;
10pub mod script_class;
11pub mod standard;
12#[cfg(feature = "wasm32-sdk")]
13pub mod wasm;
14
15use crate::caches::Cache;
16use crate::data_stack::{DataStack, Stack};
17use crate::opcodes::{deserialize_next_opcode, OpCodeImplementation};
18use itertools::Itertools;
19use kaspa_consensus_core::hashing::sighash::{calc_ecdsa_signature_hash, calc_schnorr_signature_hash, SigHashReusedValues};
20use kaspa_consensus_core::hashing::sighash_type::SigHashType;
21use kaspa_consensus_core::tx::{ScriptPublicKey, TransactionInput, UtxoEntry, VerifiableTransaction};
22use kaspa_txscript_errors::TxScriptError;
23use log::trace;
24use opcodes::codes::OpReturn;
25use opcodes::{codes, to_small_int, OpCond};
26use script_class::ScriptClass;
27
28pub mod prelude {
29    pub use super::standard::*;
30}
31pub use standard::*;
32
33pub const MAX_SCRIPT_PUBLIC_KEY_VERSION: u16 = 0;
34pub const MAX_STACK_SIZE: usize = 244;
35pub const MAX_SCRIPTS_SIZE: usize = 10_000;
36pub const MAX_SCRIPT_ELEMENT_SIZE: usize = 520;
37pub const MAX_OPS_PER_SCRIPT: i32 = 201;
38pub const MAX_TX_IN_SEQUENCE_NUM: u64 = u64::MAX;
39pub const SEQUENCE_LOCK_TIME_DISABLED: u64 = 1 << 63;
40pub const SEQUENCE_LOCK_TIME_MASK: u64 = 0x00000000ffffffff;
41pub const LOCK_TIME_THRESHOLD: u64 = 500_000_000_000;
42pub const MAX_PUB_KEYS_PER_MUTLTISIG: i32 = 20;
43
44// The last opcode that does not count toward operations.
45// Note that this includes OP_RESERVED which counts as a push operation.
46pub const NO_COST_OPCODE: u8 = 0x60;
47
48#[derive(Clone, Hash, PartialEq, Eq)]
49enum Signature {
50    Secp256k1(secp256k1::schnorr::Signature),
51    Ecdsa(secp256k1::ecdsa::Signature),
52}
53
54#[derive(Clone, Hash, PartialEq, Eq)]
55enum PublicKey {
56    Schnorr(secp256k1::XOnlyPublicKey),
57    Ecdsa(secp256k1::PublicKey),
58}
59
60// TODO: Make it pub(crate)
61#[derive(Clone, Hash, PartialEq, Eq)]
62pub struct SigCacheKey {
63    signature: Signature,
64    pub_key: PublicKey,
65    message: secp256k1::Message,
66}
67
68enum ScriptSource<'a, T: VerifiableTransaction> {
69    TxInput { tx: &'a T, input: &'a TransactionInput, id: usize, utxo_entry: &'a UtxoEntry, is_p2sh: bool },
70    StandAloneScripts(Vec<&'a [u8]>),
71}
72
73pub struct TxScriptEngine<'a, T: VerifiableTransaction> {
74    dstack: Stack,
75    astack: Stack,
76
77    script_source: ScriptSource<'a, T>,
78
79    // Outer caches for quicker calculation
80    // TODO:: make it compatible with threading
81    reused_values: &'a mut SigHashReusedValues,
82    sig_cache: &'a Cache<SigCacheKey, bool>,
83
84    cond_stack: Vec<OpCond>, // Following if stacks, and whether it is running
85
86    num_ops: i32,
87}
88
89fn parse_script<T: VerifiableTransaction>(
90    script: &[u8],
91) -> impl Iterator<Item = Result<Box<dyn OpCodeImplementation<T>>, TxScriptError>> + '_ {
92    script.iter().batching(|it| deserialize_next_opcode(it))
93}
94
95pub fn get_sig_op_count<T: VerifiableTransaction>(signature_script: &[u8], prev_script_public_key: &ScriptPublicKey) -> u64 {
96    let is_p2sh = ScriptClass::is_pay_to_script_hash(prev_script_public_key.script());
97    let script_pub_key_ops = parse_script::<T>(prev_script_public_key.script()).collect_vec();
98    if !is_p2sh {
99        return get_sig_op_count_by_opcodes(&script_pub_key_ops);
100    }
101
102    let signature_script_ops = parse_script::<T>(signature_script).collect_vec();
103    if signature_script_ops.is_empty() || signature_script_ops.iter().any(|op| op.is_err() || !op.as_ref().unwrap().is_push_opcode()) {
104        return 0;
105    }
106
107    let p2sh_script = signature_script_ops.last().expect("checked if empty above").as_ref().expect("checked if err above").get_data();
108    let p2sh_ops = parse_script::<T>(p2sh_script).collect_vec();
109    get_sig_op_count_by_opcodes(&p2sh_ops)
110}
111
112fn get_sig_op_count_by_opcodes<T: VerifiableTransaction>(opcodes: &[Result<Box<dyn OpCodeImplementation<T>>, TxScriptError>]) -> u64 {
113    // TODO: Check for overflows
114    let mut num_sigs: u64 = 0;
115    for (i, op) in opcodes.iter().enumerate() {
116        match op {
117            Ok(op) => {
118                match op.value() {
119                    codes::OpCheckSig | codes::OpCheckSigVerify | codes::OpCheckSigECDSA => num_sigs += 1,
120                    codes::OpCheckMultiSig | codes::OpCheckMultiSigVerify | codes::OpCheckMultiSigECDSA => {
121                        if i == 0 {
122                            num_sigs += MAX_PUB_KEYS_PER_MUTLTISIG as u64;
123                            continue;
124                        }
125
126                        let prev_opcode = opcodes[i - 1].as_ref().expect("they were checked before");
127                        if prev_opcode.value() >= codes::OpTrue && prev_opcode.value() <= codes::Op16 {
128                            num_sigs += to_small_int(prev_opcode) as u64;
129                        } else {
130                            num_sigs += MAX_PUB_KEYS_PER_MUTLTISIG as u64;
131                        }
132                    }
133                    _ => {} // If the opcode is not a sigop, no need to increase the count
134                }
135            }
136            Err(_) => return num_sigs,
137        }
138    }
139    num_sigs
140}
141
142/// Returns whether the passed public key script is unspendable, or guaranteed to fail at execution.
143///
144/// This allows inputs to be pruned instantly when entering the UTXO set.
145pub fn is_unspendable<T: VerifiableTransaction>(script: &[u8]) -> bool {
146    parse_script::<T>(script).enumerate().any(|(index, op)| op.is_err() || (index == 0 && op.unwrap().value() == OpReturn))
147}
148
149impl<'a, T: VerifiableTransaction> TxScriptEngine<'a, T> {
150    pub fn new(reused_values: &'a mut SigHashReusedValues, sig_cache: &'a Cache<SigCacheKey, bool>) -> Self {
151        Self {
152            dstack: vec![],
153            astack: vec![],
154            script_source: ScriptSource::StandAloneScripts(vec![]),
155            reused_values,
156            sig_cache,
157            cond_stack: vec![],
158            num_ops: 0,
159        }
160    }
161
162    pub fn from_transaction_input(
163        tx: &'a T,
164        input: &'a TransactionInput,
165        input_idx: usize,
166        utxo_entry: &'a UtxoEntry,
167        reused_values: &'a mut SigHashReusedValues,
168        sig_cache: &'a Cache<SigCacheKey, bool>,
169    ) -> Result<Self, TxScriptError> {
170        let script_public_key = utxo_entry.script_public_key.script();
171        // The script_public_key in P2SH is just validating the hash on the OpMultiSig script
172        // the user provides
173        let is_p2sh = ScriptClass::is_pay_to_script_hash(script_public_key);
174        match input_idx < tx.tx().inputs.len() {
175            true => Ok(Self {
176                dstack: Default::default(),
177                astack: Default::default(),
178                script_source: ScriptSource::TxInput { tx, input, id: input_idx, utxo_entry, is_p2sh },
179                reused_values,
180                sig_cache,
181                cond_stack: Default::default(),
182                num_ops: 0,
183            }),
184            false => Err(TxScriptError::InvalidIndex(input_idx, tx.tx().inputs.len())),
185        }
186    }
187
188    pub fn from_script(script: &'a [u8], reused_values: &'a mut SigHashReusedValues, sig_cache: &'a Cache<SigCacheKey, bool>) -> Self {
189        Self {
190            dstack: Default::default(),
191            astack: Default::default(),
192            script_source: ScriptSource::StandAloneScripts(vec![script]),
193            reused_values,
194            sig_cache,
195            cond_stack: Default::default(),
196            num_ops: 0,
197        }
198    }
199
200    #[inline]
201    pub fn is_executing(&self) -> bool {
202        return self.cond_stack.is_empty() || *self.cond_stack.last().expect("Checked not empty") == OpCond::True;
203    }
204
205    fn execute_opcode(&mut self, opcode: Box<dyn OpCodeImplementation<T>>) -> Result<(), TxScriptError> {
206        // Different from kaspad: Illegal and disabled opcode are checked on execute instead
207        // Note that this includes OP_RESERVED which counts as a push operation.
208        if !opcode.is_push_opcode() {
209            self.num_ops += 1;
210            if self.num_ops > MAX_OPS_PER_SCRIPT {
211                return Err(TxScriptError::TooManyOperations(MAX_OPS_PER_SCRIPT));
212            }
213        } else if opcode.len() > MAX_SCRIPT_ELEMENT_SIZE {
214            return Err(TxScriptError::ElementTooBig(opcode.len(), MAX_SCRIPT_ELEMENT_SIZE));
215        }
216
217        if self.is_executing() || opcode.is_conditional() {
218            if opcode.value() > 0 && opcode.value() <= 0x4e {
219                opcode.check_minimal_data_push()?;
220            }
221            opcode.execute(self)
222        } else {
223            Ok(())
224        }
225    }
226
227    fn execute_script(&mut self, script: &[u8], verify_only_push: bool) -> Result<(), TxScriptError> {
228        let script_result = parse_script(script).try_for_each(|opcode| {
229            let opcode = opcode?;
230            if opcode.is_disabled() {
231                return Err(TxScriptError::OpcodeDisabled(format!("{:?}", opcode)));
232            }
233
234            if opcode.always_illegal() {
235                return Err(TxScriptError::OpcodeReserved(format!("{:?}", opcode)));
236            }
237
238            if verify_only_push && !opcode.is_push_opcode() {
239                return Err(TxScriptError::SignatureScriptNotPushOnly);
240            }
241
242            self.execute_opcode(opcode)?;
243
244            let combined_size = self.astack.len() + self.dstack.len();
245            if combined_size > MAX_STACK_SIZE {
246                return Err(TxScriptError::StackSizeExceeded(combined_size, MAX_STACK_SIZE));
247            }
248            Ok(())
249        });
250
251        // Moving between scripts - we can't be inside an if
252        if script_result.is_ok() && !self.cond_stack.is_empty() {
253            return Err(TxScriptError::ErrUnbalancedConditional);
254        }
255
256        // Alt stack doesn't persist
257        self.astack.clear();
258        self.num_ops = 0; // number of ops is per script.
259
260        script_result
261    }
262
263    pub fn execute(&mut self) -> Result<(), TxScriptError> {
264        let (scripts, is_p2sh) = match &self.script_source {
265            ScriptSource::TxInput { input, utxo_entry, is_p2sh, .. } => {
266                if utxo_entry.script_public_key.version() > MAX_SCRIPT_PUBLIC_KEY_VERSION {
267                    trace!("The version of the scriptPublicKey is higher than the known version - the Execute function returns true.");
268                    return Ok(());
269                }
270                (vec![input.signature_script.as_slice(), utxo_entry.script_public_key.script()], *is_p2sh)
271            }
272            ScriptSource::StandAloneScripts(scripts) => (scripts.clone(), false),
273        };
274
275        // TODO: run all in same iterator?
276        // When both the signature script and public key script are empty the
277        // result is necessarily an error since the stack would end up being
278        // empty which is equivalent to a false top element. Thus, just return
279        // the relevant error now as an optimization.
280        if scripts.is_empty() {
281            return Err(TxScriptError::NoScripts);
282        }
283
284        if scripts.iter().all(|e| e.is_empty()) {
285            return Err(TxScriptError::EvalFalse);
286        }
287        if let Some(s) = scripts.iter().find(|e| e.len() > MAX_SCRIPTS_SIZE) {
288            return Err(TxScriptError::ScriptSize(s.len(), MAX_SCRIPTS_SIZE));
289        }
290
291        let mut saved_stack: Option<Vec<Vec<u8>>> = None;
292        // try_for_each quits only if an error occurred. So, we always run over all scripts if
293        // each is successful
294        scripts.iter().enumerate().filter(|(_, s)| !s.is_empty()).try_for_each(|(idx, s)| {
295            let verify_only_push =
296                idx == 0 && matches!(self.script_source, ScriptSource::TxInput { tx: _, input: _, id: _, utxo_entry: _, is_p2sh: _ });
297            // Save script in p2sh
298            if is_p2sh && idx == 1 {
299                saved_stack = Some(self.dstack.clone());
300            }
301            self.execute_script(s, verify_only_push)
302        })?;
303
304        if is_p2sh {
305            self.check_error_condition(false)?;
306            self.dstack = saved_stack.ok_or(TxScriptError::EmptyStack)?;
307            let script = self.dstack.pop().ok_or(TxScriptError::EmptyStack)?;
308            self.execute_script(script.as_slice(), false)?
309        }
310
311        self.check_error_condition(true)?;
312        Ok(())
313    }
314
315    // check_error_condition is called whenever we finish a chunk of the scripts
316    // (all original scripts, all scripts including p2sh, and maybe future extensions)
317    // returns Ok(()) if the running script has ended and was successful, leaving a true boolean
318    // on the stack. An error otherwise.
319    #[inline]
320    fn check_error_condition(&mut self, final_script: bool) -> Result<(), TxScriptError> {
321        if final_script {
322            if self.dstack.len() > 1 {
323                return Err(TxScriptError::CleanStack(self.dstack.len() - 1));
324            } else if self.dstack.is_empty() {
325                return Err(TxScriptError::EmptyStack);
326            }
327        }
328
329        let [v]: [bool; 1] = self.dstack.pop_items()?;
330        match v {
331            true => Ok(()),
332            false => Err(TxScriptError::EvalFalse),
333        }
334    }
335
336    // *** SIGNATURE SPECIFIC CODE **
337
338    fn check_pub_key_encoding(pub_key: &[u8]) -> Result<(), TxScriptError> {
339        match pub_key.len() {
340            32 => Ok(()),
341            _ => Err(TxScriptError::PubKeyFormat),
342        }
343    }
344
345    fn check_pub_key_encoding_ecdsa(pub_key: &[u8]) -> Result<(), TxScriptError> {
346        match pub_key.len() {
347            33 => Ok(()),
348            _ => Err(TxScriptError::PubKeyFormat),
349        }
350    }
351
352    fn op_check_multisig_schnorr_or_ecdsa(&mut self, ecdsa: bool) -> Result<(), TxScriptError> {
353        let [num_keys]: [i32; 1] = self.dstack.pop_items()?;
354        if num_keys < 0 {
355            return Err(TxScriptError::InvalidPubKeyCount(format!("number of pubkeys {num_keys} is negative")));
356        } else if num_keys > MAX_PUB_KEYS_PER_MUTLTISIG {
357            return Err(TxScriptError::InvalidPubKeyCount(format!("too many pubkeys {num_keys} > {MAX_PUB_KEYS_PER_MUTLTISIG}")));
358        }
359        let num_keys_usize = num_keys as usize;
360
361        self.num_ops += num_keys;
362        if self.num_ops > MAX_OPS_PER_SCRIPT {
363            return Err(TxScriptError::TooManyOperations(MAX_OPS_PER_SCRIPT));
364        }
365
366        let pub_keys = match self.dstack.len() >= num_keys_usize {
367            true => self.dstack.split_off(self.dstack.len() - num_keys_usize),
368            false => return Err(TxScriptError::InvalidStackOperation(num_keys_usize, self.dstack.len())),
369        };
370
371        let [num_sigs]: [i32; 1] = self.dstack.pop_items()?;
372        if num_sigs < 0 {
373            return Err(TxScriptError::InvalidSignatureCount(format!("number of signatures {num_sigs} is negative")));
374        } else if num_sigs > num_keys {
375            return Err(TxScriptError::InvalidSignatureCount(format!("more signatures than pubkeys {num_sigs} > {num_keys}")));
376        }
377        let num_sigs = num_sigs as usize;
378
379        let signatures = match self.dstack.len() >= num_sigs {
380            true => self.dstack.split_off(self.dstack.len() - num_sigs),
381            false => return Err(TxScriptError::InvalidStackOperation(num_sigs, self.dstack.len())),
382        };
383
384        let mut failed = false;
385        let mut pub_key_iter = pub_keys.iter();
386        'outer: for (sig_idx, signature) in signatures.iter().enumerate() {
387            if signature.is_empty() {
388                failed = true;
389                break;
390            }
391
392            let typ = *signature.last().expect("checked that is not empty");
393            let signature = &signature[..signature.len() - 1];
394            let hash_type = SigHashType::from_u8(typ).map_err(|_| TxScriptError::InvalidSigHashType(typ))?;
395
396            // Advance through the pub_keys iterator.
397            // Note every check consumes the public key
398            loop {
399                if pub_key_iter.len() < num_sigs - sig_idx {
400                    // When there are more signatures than public keys remaining,
401                    // there is no way to succeed since too many signatures are
402                    // invalid, so exit early.
403                    failed = true;
404                    break 'outer; // Break the outer signature loop
405                }
406                // SAFETY: we just checked the len
407                let pub_key = pub_key_iter.next().unwrap();
408
409                let check_signature_result = if ecdsa {
410                    self.check_ecdsa_signature(hash_type, pub_key.as_slice(), signature)
411                } else {
412                    self.check_schnorr_signature(hash_type, pub_key.as_slice(), signature)
413                };
414
415                match check_signature_result {
416                    Ok(valid) => {
417                        if valid {
418                            // Current sig is valid, we can break the inner loop and continue to next sig
419                            break;
420                        }
421                    }
422                    Err(e) => {
423                        return Err(e);
424                    }
425                }
426            }
427        }
428
429        if failed && signatures.iter().any(|sig| !sig.is_empty()) {
430            return Err(TxScriptError::NullFail);
431        }
432
433        self.dstack.push_item(!failed);
434        Ok(())
435    }
436
437    #[inline]
438    fn check_schnorr_signature(&mut self, hash_type: SigHashType, key: &[u8], sig: &[u8]) -> Result<bool, TxScriptError> {
439        match self.script_source {
440            ScriptSource::TxInput { tx, id, .. } => {
441                if sig.len() != 64 {
442                    return Err(TxScriptError::SigLength(sig.len()));
443                }
444                Self::check_pub_key_encoding(key)?;
445                let pk = secp256k1::XOnlyPublicKey::from_slice(key).map_err(TxScriptError::InvalidSignature)?;
446                let sig = secp256k1::schnorr::Signature::from_slice(sig).map_err(TxScriptError::InvalidSignature)?;
447                let sig_hash = calc_schnorr_signature_hash(tx, id, hash_type, self.reused_values);
448                let msg = secp256k1::Message::from_digest_slice(sig_hash.as_bytes().as_slice()).unwrap();
449                let sig_cache_key =
450                    SigCacheKey { signature: Signature::Secp256k1(sig), pub_key: PublicKey::Schnorr(pk), message: msg };
451
452                match self.sig_cache.get(&sig_cache_key) {
453                    Some(valid) => Ok(valid),
454                    None => {
455                        // TODO: Find a way to parallelize this part.
456                        match sig.verify(&msg, &pk) {
457                            Ok(()) => {
458                                self.sig_cache.insert(sig_cache_key, true);
459                                Ok(true)
460                            }
461                            Err(_) => {
462                                self.sig_cache.insert(sig_cache_key, false);
463                                Ok(false)
464                            }
465                        }
466                    }
467                }
468            }
469            _ => Err(TxScriptError::NotATransactionInput),
470        }
471    }
472
473    fn check_ecdsa_signature(&mut self, hash_type: SigHashType, key: &[u8], sig: &[u8]) -> Result<bool, TxScriptError> {
474        match self.script_source {
475            ScriptSource::TxInput { tx, id, .. } => {
476                if sig.len() != 64 {
477                    return Err(TxScriptError::SigLength(sig.len()));
478                }
479                Self::check_pub_key_encoding_ecdsa(key)?;
480                let pk = secp256k1::PublicKey::from_slice(key).map_err(TxScriptError::InvalidSignature)?;
481                let sig = secp256k1::ecdsa::Signature::from_compact(sig).map_err(TxScriptError::InvalidSignature)?;
482                let sig_hash = calc_ecdsa_signature_hash(tx, id, hash_type, self.reused_values);
483                let msg = secp256k1::Message::from_digest_slice(sig_hash.as_bytes().as_slice()).unwrap();
484                let sig_cache_key = SigCacheKey { signature: Signature::Ecdsa(sig), pub_key: PublicKey::Ecdsa(pk), message: msg };
485
486                match self.sig_cache.get(&sig_cache_key) {
487                    Some(valid) => Ok(valid),
488                    None => {
489                        // TODO: Find a way to parallelize this part.
490                        match sig.verify(&msg, &pk) {
491                            Ok(()) => {
492                                self.sig_cache.insert(sig_cache_key, true);
493                                Ok(true)
494                            }
495                            Err(_) => {
496                                self.sig_cache.insert(sig_cache_key, false);
497                                Ok(false)
498                            }
499                        }
500                    }
501                }
502            }
503            _ => Err(TxScriptError::NotATransactionInput),
504        }
505    }
506}
507
508#[cfg(test)]
509mod tests {
510    use std::iter::once;
511
512    use crate::opcodes::codes::{OpBlake2b, OpCheckSig, OpData1, OpData2, OpData32, OpDup, OpEqual, OpPushData1, OpTrue};
513
514    use super::*;
515    use kaspa_consensus_core::tx::{
516        PopulatedTransaction, ScriptPublicKey, Transaction, TransactionId, TransactionOutpoint, TransactionOutput,
517    };
518    use smallvec::SmallVec;
519
520    struct ScriptTestCase {
521        script: &'static [u8],
522        expected_result: Result<(), TxScriptError>,
523    }
524
525    struct KeyTestCase {
526        name: &'static str,
527        key: &'static [u8],
528        is_valid: bool,
529    }
530
531    struct VerifiableTransactionMock {}
532
533    impl VerifiableTransaction for VerifiableTransactionMock {
534        fn tx(&self) -> &Transaction {
535            unimplemented!()
536        }
537
538        fn populated_input(&self, _index: usize) -> (&TransactionInput, &UtxoEntry) {
539            unimplemented!()
540        }
541    }
542
543    fn run_test_script_cases(test_cases: Vec<ScriptTestCase>) {
544        let sig_cache = Cache::new(10_000);
545        let mut reused_values = SigHashReusedValues::new();
546
547        for test in test_cases {
548            // Ensure encapsulation of variables (no leaking between tests)
549            let input = TransactionInput {
550                previous_outpoint: TransactionOutpoint {
551                    transaction_id: TransactionId::from_bytes([
552                        0xc9, 0x97, 0xa5, 0xe5, 0x6e, 0x10, 0x41, 0x02, 0xfa, 0x20, 0x9c, 0x6a, 0x85, 0x2d, 0xd9, 0x06, 0x60, 0xa2,
553                        0x0b, 0x2d, 0x9c, 0x35, 0x24, 0x23, 0xed, 0xce, 0x25, 0x85, 0x7f, 0xcd, 0x37, 0x04,
554                    ]),
555                    index: 0,
556                },
557                signature_script: vec![],
558                sequence: 4294967295,
559                sig_op_count: 0,
560            };
561            let output = TransactionOutput { value: 1000000000, script_public_key: ScriptPublicKey::new(0, test.script.into()) };
562
563            let tx = Transaction::new(1, vec![input.clone()], vec![output.clone()], 0, Default::default(), 0, vec![]);
564            let utxo_entry = UtxoEntry::new(output.value, output.script_public_key.clone(), 0, tx.is_coinbase());
565
566            let populated_tx = PopulatedTransaction::new(&tx, vec![utxo_entry.clone()]);
567
568            let mut vm = TxScriptEngine::from_transaction_input(&populated_tx, &input, 0, &utxo_entry, &mut reused_values, &sig_cache)
569                .expect("Script creation failed");
570            assert_eq!(vm.execute(), test.expected_result);
571        }
572    }
573
574    #[test]
575    fn test_check_error_condition() {
576        let test_cases = vec![
577            ScriptTestCase {
578                script: b"\x51", // opcodes::codes::OpTrue{data: ""}
579                expected_result: Ok(()),
580            },
581            ScriptTestCase {
582                script: b"\x61", // opcodes::codes::OpNop{data: ""}
583                expected_result: Err(TxScriptError::EmptyStack),
584            },
585            ScriptTestCase {
586                script: b"\x51\x51", // opcodes::codes::OpTrue, opcodes::codes::OpTrue,
587                expected_result: Err(TxScriptError::CleanStack(1)),
588            },
589            ScriptTestCase {
590                script: b"\x00", // opcodes::codes::OpFalse{data: ""},
591                expected_result: Err(TxScriptError::EvalFalse),
592            },
593        ];
594
595        run_test_script_cases(test_cases)
596    }
597
598    #[test]
599    fn test_check_opif() {
600        let test_cases = vec![
601            ScriptTestCase {
602                script: b"\x63", // OpIf
603                expected_result: Err(TxScriptError::EmptyStack),
604            },
605            ScriptTestCase {
606                script: b"\x52\x63", // Op2, OpIf - bool for If must be 0 or 1.
607                expected_result: Err(TxScriptError::InvalidState("expected boolean".to_string())),
608            },
609            ScriptTestCase {
610                script: b"\x51\x63", // OpTrue, OpIf
611                expected_result: Err(TxScriptError::ErrUnbalancedConditional),
612            },
613            ScriptTestCase {
614                script: b"\x00\x63", // OpFalse, OpIf
615                expected_result: Err(TxScriptError::ErrUnbalancedConditional),
616            },
617            ScriptTestCase {
618                script: b"\x51\x63\x51\x68", // OpTrue, OpIf, OpTrue, OpEndIf
619                expected_result: Ok(()),
620            },
621            ScriptTestCase {
622                script: b"\x00\x63\x51\x68", // OpFalse, OpIf, OpTrue, OpEndIf
623                expected_result: Err(TxScriptError::EmptyStack),
624            },
625        ];
626
627        run_test_script_cases(test_cases)
628    }
629
630    #[test]
631    fn test_check_opelse() {
632        let test_cases = vec![
633            ScriptTestCase {
634                script: b"\x67", // OpElse
635                expected_result: Err(TxScriptError::InvalidState("condition stack empty".to_string())),
636            },
637            ScriptTestCase {
638                script: b"\x51\x63\x67", // OpTrue, OpIf, OpElse
639                expected_result: Err(TxScriptError::ErrUnbalancedConditional),
640            },
641            ScriptTestCase {
642                script: b"\x00\x63\x67", // OpFalse, OpIf, OpElse
643                expected_result: Err(TxScriptError::ErrUnbalancedConditional),
644            },
645            ScriptTestCase {
646                script: b"\x51\x63\x51\x67\x68", // OpTrue, OpIf, OpTrue, OpElse, OpEndIf
647                expected_result: Ok(()),
648            },
649            ScriptTestCase {
650                script: b"\x00\x63\x67\x51\x68", // OpFalse, OpIf, OpElse, OpTrue, OpEndIf
651                expected_result: Ok(()),
652            },
653        ];
654
655        run_test_script_cases(test_cases)
656    }
657
658    #[test]
659    fn test_check_opnotif() {
660        let test_cases = vec![
661            ScriptTestCase {
662                script: b"\x64", // OpNotIf
663                expected_result: Err(TxScriptError::EmptyStack),
664            },
665            ScriptTestCase {
666                script: b"\x51\x64", // OpTrue, OpNotIf
667                expected_result: Err(TxScriptError::ErrUnbalancedConditional),
668            },
669            ScriptTestCase {
670                script: b"\x00\x64", // OpFalse, OpNotIf
671                expected_result: Err(TxScriptError::ErrUnbalancedConditional),
672            },
673            ScriptTestCase {
674                script: b"\x51\x64\x67\x51\x68", // OpTrue, OpNotIf, OpElse, OpTrue, OpEndIf
675                expected_result: Ok(()),
676            },
677            ScriptTestCase {
678                script: b"\x51\x64\x51\x67\x00\x68", // OpTrue, OpNotIf, OpTrue, OpElse, OpFalse, OpEndIf
679                expected_result: Err(TxScriptError::EvalFalse),
680            },
681            ScriptTestCase {
682                script: b"\x00\x64\x51\x68", // OpFalse, OpIf, OpTrue, OpEndIf
683                expected_result: Ok(()),
684            },
685        ];
686
687        run_test_script_cases(test_cases)
688    }
689
690    #[test]
691    fn test_check_nestedif() {
692        let test_cases = vec![
693            ScriptTestCase {
694                script: b"\x51\x63\x00\x67\x51\x63\x51\x68\x68", // OpTrue, OpIf, OpFalse, OpElse, OpTrue, OpIf,
695                // OpTrue, OpEndIf, OpEndIf
696                expected_result: Err(TxScriptError::EvalFalse),
697            },
698            ScriptTestCase {
699                script: b"\x51\x63\x00\x67\x00\x63\x67\x51\x68\x68", // OpTrue, OpIf, OpFalse, OpElse, OpFalse, OpIf,
700                // OpElse, OpTrue, OpEndIf, OpEndIf
701                expected_result: Err(TxScriptError::EvalFalse),
702            },
703            ScriptTestCase {
704                script: b"\x51\x64\x00\x67\x51\x63\x51\x68\x68", // OpTrue, OpNotIf, OpFalse, OpElse, OpTrue, OpIf,
705                // OpTrue, OpEndIf, OpEndIf
706                expected_result: Ok(()),
707            },
708            ScriptTestCase {
709                script: b"\x51\x64\x00\x67\x00\x63\x67\x51\x68\x68", // OpTrue, OpNotIf, OpFalse, OpElse, OpFalse, OpIf,
710                // OpTrue, OpEndIf, OpEndIf
711                expected_result: Ok(()),
712            },
713            ScriptTestCase {
714                script: b"\x51\x64\x00\x67\x00\x64\x00\x67\x51\x68\x68", // OpTrue, OpNotIf, OpFalse, OpElse, OpFalse, OpNotIf,
715                // OpFalse, OpElse, OpTrue, OpEndIf, OpEndIf
716                expected_result: Err(TxScriptError::EvalFalse),
717            },
718            ScriptTestCase {
719                script: b"\x51\x00\x63\x63\x00\x68\x68", // OpTrue, OpFalse, OpIf, OpIf  OpFalse, OpEndIf, OpEndIf
720                expected_result: Ok(()),
721            },
722            ScriptTestCase {
723                script: b"\x51\x00\x63\x63\x63\x00\x67\x00\x68\x68\x68", // OpTrue, OpFalse, OpIf, OpIf  OpFalse, OpEndIf, OpEndIf
724                expected_result: Ok(()),
725            },
726            ScriptTestCase {
727                script: b"\x51\x00\x63\x63\x63\x63\x00\x67\x00\x68\x68\x68\x68", // OpTrue, OpFalse, OpIf, OpIf  OpFalse, OpEndIf, OpEndIf
728                expected_result: Ok(()),
729            },
730        ];
731
732        run_test_script_cases(test_cases)
733    }
734
735    #[test]
736    fn test_check_pub_key_encode() {
737        let test_cases = vec![
738            KeyTestCase {
739                name: "uncompressed - invalid",
740                key: &[
741                    0x04u8, 0x11, 0xdb, 0x93, 0xe1, 0xdc, 0xdb, 0x8a, 0x01, 0x6b, 0x49, 0x84, 0x0f, 0x8c, 0x53, 0xbc, 0x1e, 0xb6,
742                    0x8a, 0x38, 0x2e, 0x97, 0xb1, 0x48, 0x2e, 0xca, 0xd7, 0xb1, 0x48, 0xa6, 0x90, 0x9a, 0x5c, 0xb2, 0xe0, 0xea, 0xdd,
743                    0xfb, 0x84, 0xcc, 0xf9, 0x74, 0x44, 0x64, 0xf8, 0x2e, 0x16, 0x0b, 0xfa, 0x9b, 0x8b, 0x64, 0xf9, 0xd4, 0xc0, 0x3f,
744                    0x99, 0x9b, 0x86, 0x43, 0xf6, 0x56, 0xb4, 0x12, 0xa3,
745                ],
746                is_valid: false,
747            },
748            KeyTestCase {
749                name: "compressed - invalid",
750                key: &[
751                    0x02, 0xce, 0x0b, 0x14, 0xfb, 0x84, 0x2b, 0x1b, 0xa5, 0x49, 0xfd, 0xd6, 0x75, 0xc9, 0x80, 0x75, 0xf1, 0x2e, 0x9c,
752                    0x51, 0x0f, 0x8e, 0xf5, 0x2b, 0xd0, 0x21, 0xa9, 0xa1, 0xf4, 0x80, 0x9d, 0x3b, 0x4d,
753                ],
754                is_valid: false,
755            },
756            KeyTestCase {
757                name: "compressed - invalid",
758                key: &[
759                    0x03, 0x26, 0x89, 0xc7, 0xc2, 0xda, 0xb1, 0x33, 0x09, 0xfb, 0x14, 0x3e, 0x0e, 0x8f, 0xe3, 0x96, 0x34, 0x25, 0x21,
760                    0x88, 0x7e, 0x97, 0x66, 0x90, 0xb6, 0xb4, 0x7f, 0x5b, 0x2a, 0x4b, 0x7d, 0x44, 0x8e,
761                ],
762                is_valid: false,
763            },
764            KeyTestCase {
765                name: "hybrid - invalid",
766                key: &[
767                    0x06, 0x79, 0xbe, 0x66, 0x7e, 0xf9, 0xdc, 0xbb, 0xac, 0x55, 0xa0, 0x62, 0x95, 0xce, 0x87, 0x0b, 0x07, 0x02, 0x9b,
768                    0xfc, 0xdb, 0x2d, 0xce, 0x28, 0xd9, 0x59, 0xf2, 0x81, 0x5b, 0x16, 0xf8, 0x17, 0x98, 0x48, 0x3a, 0xda, 0x77, 0x26,
769                    0xa3, 0xc4, 0x65, 0x5d, 0xa4, 0xfb, 0xfc, 0x0e, 0x11, 0x08, 0xa8, 0xfd, 0x17, 0xb4, 0x48, 0xa6, 0x85, 0x54, 0x19,
770                    0x9c, 0x47, 0xd0, 0x8f, 0xfb, 0x10, 0xd4, 0xb8,
771                ],
772                is_valid: false,
773            },
774            KeyTestCase {
775                name: "32 bytes pubkey - Ok",
776                key: &[
777                    0x26, 0x89, 0xc7, 0xc2, 0xda, 0xb1, 0x33, 0x09, 0xfb, 0x14, 0x3e, 0x0e, 0x8f, 0xe3, 0x96, 0x34, 0x25, 0x21, 0x88,
778                    0x7e, 0x97, 0x66, 0x90, 0xb6, 0xb4, 0x7f, 0x5b, 0x2a, 0x4b, 0x7d, 0x44, 0x8e,
779                ],
780                is_valid: true,
781            },
782            KeyTestCase { name: "empty", key: &[], is_valid: false },
783        ];
784
785        for test in test_cases {
786            let check = TxScriptEngine::<PopulatedTransaction>::check_pub_key_encoding(test.key);
787            if test.is_valid {
788                assert_eq!(
789                    check,
790                    Ok(()),
791                    "checkSignatureLength test '{}' failed when it should have succeeded: {:?}",
792                    test.name,
793                    check
794                )
795            } else {
796                assert_eq!(
797                    check,
798                    Err(TxScriptError::PubKeyFormat),
799                    "checkSignatureEncoding test '{}' succeeded or failed on wrong format ({:?})",
800                    test.name,
801                    check
802                )
803            }
804        }
805    }
806
807    #[test]
808    fn test_get_sig_op_count() {
809        struct TestVector<'a> {
810            name: &'a str,
811            signature_script: &'a [u8],
812            expected_sig_ops: u64,
813            prev_script_public_key: ScriptPublicKey,
814        }
815
816        let script_hash = hex::decode("433ec2ac1ffa1b7b7d027f564529c57197f9ae88").unwrap();
817        let prev_script_pubkey_p2sh_script =
818            [OpBlake2b, OpData32].iter().copied().chain(script_hash.iter().copied()).chain(once(OpEqual));
819        let prev_script_pubkey_p2sh = ScriptPublicKey::new(0, SmallVec::from_iter(prev_script_pubkey_p2sh_script));
820
821        let tests = [
822            TestVector {
823                name: "scriptSig doesn't parse",
824                signature_script: &[OpPushData1, 0x02],
825                expected_sig_ops: 0,
826                prev_script_public_key: prev_script_pubkey_p2sh.clone(),
827            },
828            TestVector {
829                name: "scriptSig isn't push only",
830                signature_script: &[OpTrue, OpDup],
831                expected_sig_ops: 0,
832                prev_script_public_key: prev_script_pubkey_p2sh.clone(),
833            },
834            TestVector {
835                name: "scriptSig length 0",
836                signature_script: &[],
837                expected_sig_ops: 0,
838                prev_script_public_key: prev_script_pubkey_p2sh.clone(),
839            },
840            TestVector {
841                name: "No script at the end",
842                signature_script: &[OpTrue, OpTrue],
843                expected_sig_ops: 0,
844                prev_script_public_key: prev_script_pubkey_p2sh.clone(),
845            }, // No script at end but still push only.
846            TestVector {
847                name: "pushed script doesn't parse",
848                signature_script: &[OpData2, OpPushData1, 0x02],
849                expected_sig_ops: 0,
850                prev_script_public_key: prev_script_pubkey_p2sh,
851            },
852            TestVector {
853                name: "mainnet multisig transaction 487f94ffa63106f72644068765b9dc629bb63e481210f382667d4a93b69af412",
854                signature_script: &hex::decode("41eb577889fa28283709201ef5b056745c6cf0546dd31666cecd41c40a581b256e885d941b86b14d44efacec12d614e7fcabf7b341660f95bab16b71d766ab010501411c0eeef117ca485d34e4bc0cf6d5b578aa250c5d13ebff0882a7e2eeea1f31e8ecb6755696d194b1b0fcb853afab28b61f3f7cec487bd611df7e57252802f535014c875220ab64c7691713a32ea6dfced9155c5c26e8186426f0697af0db7a4b1340f992d12041ae738d66fe3d21105483e5851778ad73c5cddf0819c5e8fd8a589260d967e72065120722c36d3fac19646258481dd3661fa767da151304af514cb30af5cb5692203cd7690ecb67cbbe6cafad00a7c9133da535298ab164549e0cce2658f7b3032754ae").unwrap(),
855                prev_script_public_key: ScriptPublicKey::new(
856                    0,
857                    SmallVec::from_slice(&hex::decode("aa20f38031f61ca23d70844f63a477d07f0b2c2decab907c2e096e548b0e08721c7987").unwrap()),
858                ),
859                expected_sig_ops: 4,
860            },
861            TestVector {
862                name: "a partially parseable script public key",
863                signature_script: &[],
864                prev_script_public_key: ScriptPublicKey::new(
865                    0,
866                    SmallVec::from_slice(&[OpCheckSig,OpCheckSig, OpData1]),
867                ),
868                expected_sig_ops: 2,
869            },
870            TestVector {
871                name: "p2pk",
872                signature_script: &hex::decode("416db0c0ce824a6d076c8e73aae9987416933df768e07760829cb0685dc0a2bbb11e2c0ced0cab806e111a11cbda19784098fd25db176b6a9d7c93e5747674d32301").unwrap(),
873                prev_script_public_key: ScriptPublicKey::new(
874                    0,
875                    SmallVec::from_slice(&hex::decode("208a457ca74ade0492c44c440da1cab5b008d8449150fe2794f0d8f4cce7e8aa27ac").unwrap()),
876                ),
877                expected_sig_ops: 1,
878            },
879        ];
880
881        for test in tests {
882            assert_eq!(
883                get_sig_op_count::<VerifiableTransactionMock>(test.signature_script, &test.prev_script_public_key),
884                test.expected_sig_ops,
885                "failed for '{}'",
886                test.name
887            );
888        }
889    }
890
891    #[test]
892    fn test_is_unspendable() {
893        struct Test<'a> {
894            name: &'a str,
895            script_public_key: &'a [u8],
896            expected: bool,
897        }
898        let tests = vec![
899            Test { name: "unspendable", script_public_key: &[0x6a, 0x04, 0x74, 0x65, 0x73, 0x74], expected: true },
900            Test {
901                name: "spendable",
902                script_public_key: &[
903                    0x76, 0xa9, 0x14, 0x29, 0x95, 0xa0, 0xfe, 0x68, 0x43, 0xfa, 0x9b, 0x95, 0x45, 0x97, 0xf0, 0xdc, 0xa7, 0xa4, 0x4d,
904                    0xf6, 0xfa, 0x0b, 0x5c, 0x88, 0xac,
905                ],
906                expected: false,
907            },
908        ];
909
910        for test in tests {
911            assert_eq!(
912                is_unspendable::<VerifiableTransactionMock>(test.script_public_key),
913                test.expected,
914                "failed for '{}'",
915                test.name
916            );
917        }
918    }
919}
920
921#[cfg(test)]
922mod bitcoind_tests {
923    // Bitcoind tests
924    use serde::Deserialize;
925    use std::fs::File;
926    use std::io::BufReader;
927    use std::path::Path;
928
929    use super::*;
930    use crate::script_builder::ScriptBuilderError;
931    use kaspa_consensus_core::constants::MAX_TX_IN_SEQUENCE_NUM;
932    use kaspa_consensus_core::tx::{
933        PopulatedTransaction, ScriptPublicKey, Transaction, TransactionId, TransactionOutpoint, TransactionOutput,
934    };
935
936    #[derive(PartialEq, Eq, Debug, Clone)]
937    enum UnifiedError {
938        TxScriptError(TxScriptError),
939        ScriptBuilderError(ScriptBuilderError),
940    }
941
942    #[derive(PartialEq, Eq, Debug, Clone)]
943    struct TestError {
944        expected_result: String,
945        result: Result<(), UnifiedError>,
946    }
947
948    #[allow(dead_code)]
949    #[derive(Deserialize, Debug, Clone)]
950    #[serde(untagged)]
951    enum JsonTestRow {
952        Test(String, String, String, String),
953        TestWithComment(String, String, String, String, String),
954        Comment((String,)),
955    }
956
957    fn create_spending_transaction(sig_script: Vec<u8>, script_public_key: ScriptPublicKey) -> Transaction {
958        let coinbase = Transaction::new(
959            1,
960            vec![TransactionInput::new(
961                TransactionOutpoint::new(TransactionId::default(), 0xffffffffu32),
962                vec![0, 0],
963                MAX_TX_IN_SEQUENCE_NUM,
964                Default::default(),
965            )],
966            vec![TransactionOutput::new(0, script_public_key)],
967            Default::default(),
968            Default::default(),
969            Default::default(),
970            Default::default(),
971        );
972
973        Transaction::new(
974            1,
975            vec![TransactionInput::new(
976                TransactionOutpoint::new(coinbase.id(), 0u32),
977                sig_script,
978                MAX_TX_IN_SEQUENCE_NUM,
979                Default::default(),
980            )],
981            vec![TransactionOutput::new(0, Default::default())],
982            Default::default(),
983            Default::default(),
984            Default::default(),
985            Default::default(),
986        )
987    }
988
989    impl JsonTestRow {
990        fn test_row(&self) -> Result<(), TestError> {
991            // Parse test to objects
992            let (sig_script, script_pub_key, expected_result) = match self.clone() {
993                JsonTestRow::Test(sig_script, sig_pub_key, _, expected_result) => (sig_script, sig_pub_key, expected_result),
994                JsonTestRow::TestWithComment(sig_script, sig_pub_key, _, expected_result, _) => {
995                    (sig_script, sig_pub_key, expected_result)
996                }
997                JsonTestRow::Comment(_) => {
998                    return Ok(());
999                }
1000            };
1001
1002            let result = Self::run_test(sig_script, script_pub_key);
1003
1004            match Self::result_name(result.clone()).contains(&expected_result.as_str()) {
1005                true => Ok(()),
1006                false => Err(TestError { expected_result, result }),
1007            }
1008        }
1009
1010        fn run_test(sig_script: String, script_pub_key: String) -> Result<(), UnifiedError> {
1011            let script_sig = opcodes::parse_short_form(sig_script).map_err(UnifiedError::ScriptBuilderError)?;
1012            let script_pub_key =
1013                ScriptPublicKey::from_vec(0, opcodes::parse_short_form(script_pub_key).map_err(UnifiedError::ScriptBuilderError)?);
1014
1015            // Create transaction
1016            let tx = create_spending_transaction(script_sig, script_pub_key.clone());
1017            let entry = UtxoEntry::new(0, script_pub_key.clone(), 0, true);
1018            let populated_tx = PopulatedTransaction::new(&tx, vec![entry]);
1019
1020            // Run transaction
1021            let sig_cache = Cache::new(10_000);
1022            let mut reused_values = SigHashReusedValues::new();
1023            let mut vm = TxScriptEngine::from_transaction_input(
1024                &populated_tx,
1025                &populated_tx.tx().inputs[0],
1026                0,
1027                &populated_tx.entries[0],
1028                &mut reused_values,
1029                &sig_cache,
1030            )
1031            .map_err(UnifiedError::TxScriptError)?;
1032            vm.execute().map_err(UnifiedError::TxScriptError)
1033        }
1034
1035        /*
1036
1037        // At this point an error was expected so ensure the result of
1038        // the execution matches it.
1039        success := false
1040        for _, code := range allowedErrorCodes {
1041            if IsErrorCode(err, code) {
1042                success = true
1043                break
1044            }
1045        }
1046        if !success {
1047            var scriptErr Error
1048            if ok := errors.As(err, &scriptErr); ok {
1049                t.Errorf("%s: want error codes %v, got %v", name,
1050                    allowedErrorCodes, scriptErr.ErrorCode)
1051                continue
1052            }
1053            t.Errorf("%s: want error codes %v, got err: %v (%T)",
1054                name, allowedErrorCodes, err, err)
1055            continue
1056        }*/
1057
1058        fn result_name(result: Result<(), UnifiedError>) -> Vec<&'static str> {
1059            match result {
1060                Ok(_) => vec!["OK"],
1061                Err(ue) => match ue {
1062                    UnifiedError::TxScriptError(e) => match e {
1063                        TxScriptError::NumberTooBig(_) => vec!["UNKNOWN_ERROR"],
1064                        TxScriptError::PubKeyFormat => vec!["PUBKEYFORMAT"],
1065                        TxScriptError::EvalFalse => vec!["EVAL_FALSE"],
1066                        TxScriptError::EmptyStack => {
1067                            vec!["EMPTY_STACK", "EVAL_FALSE", "UNBALANCED_CONDITIONAL", "INVALID_ALTSTACK_OPERATION"]
1068                        }
1069                        TxScriptError::NullFail => vec!["NULLFAIL"],
1070                        TxScriptError::SigLength(_) => vec!["NULLFAIL"],
1071                        //SIG_HIGH_S
1072                        TxScriptError::InvalidSigHashType(_) => vec!["SIG_HASHTYPE"],
1073                        TxScriptError::SignatureScriptNotPushOnly => vec!["SIG_PUSHONLY"],
1074                        TxScriptError::CleanStack(_) => vec!["CLEANSTACK"],
1075                        TxScriptError::OpcodeReserved(_) => vec!["BAD_OPCODE"],
1076                        TxScriptError::MalformedPush(_, _) => vec!["BAD_OPCODE"],
1077                        TxScriptError::InvalidOpcode(_) => vec!["BAD_OPCODE"],
1078                        TxScriptError::ErrUnbalancedConditional => vec!["UNBALANCED_CONDITIONAL"],
1079                        TxScriptError::InvalidState(s) if s == "condition stack empty" => vec!["UNBALANCED_CONDITIONAL"],
1080                        //ErrInvalidStackOperation
1081                        TxScriptError::EarlyReturn => vec!["OP_RETURN"],
1082                        TxScriptError::VerifyError => vec!["VERIFY", "EQUALVERIFY"],
1083                        TxScriptError::InvalidStackOperation(_, _) => vec!["INVALID_STACK_OPERATION", "INVALID_ALTSTACK_OPERATION"],
1084                        TxScriptError::InvalidState(s) if s == "pick at an invalid location" => vec!["INVALID_STACK_OPERATION"],
1085                        TxScriptError::InvalidState(s) if s == "roll at an invalid location" => vec!["INVALID_STACK_OPERATION"],
1086                        TxScriptError::OpcodeDisabled(_) => vec!["DISABLED_OPCODE"],
1087                        TxScriptError::ElementTooBig(_, _) => vec!["PUSH_SIZE"],
1088                        TxScriptError::TooManyOperations(_) => vec!["OP_COUNT"],
1089                        TxScriptError::StackSizeExceeded(_, _) => vec!["STACK_SIZE"],
1090                        TxScriptError::InvalidPubKeyCount(_) => vec!["PUBKEY_COUNT"],
1091                        TxScriptError::InvalidSignatureCount(_) => vec!["SIG_COUNT"],
1092                        TxScriptError::NotMinimalData(_) => vec!["MINIMALDATA", "UNKNOWN_ERROR"],
1093                        //ErrNegativeLockTime
1094                        TxScriptError::UnsatisfiedLockTime(_) => vec!["UNSATISFIED_LOCKTIME"],
1095                        TxScriptError::InvalidState(s) if s == "expected boolean" => vec!["MINIMALIF"],
1096                        TxScriptError::ScriptSize(_, _) => vec!["SCRIPT_SIZE"],
1097                        _ => vec![],
1098                    },
1099                    UnifiedError::ScriptBuilderError(e) => match e {
1100                        ScriptBuilderError::ElementExceedsMaxSize(_) => vec!["PUSH_SIZE"],
1101                        _ => vec![],
1102                    },
1103                },
1104            }
1105        }
1106    }
1107
1108    #[test]
1109    fn test_bitcoind_tests() {
1110        let file = File::open(Path::new(env!("CARGO_MANIFEST_DIR")).join("test-data").join("script_tests.json"))
1111            .expect("Could not find test file");
1112        let reader = BufReader::new(file);
1113
1114        // Read the JSON contents of the file as an instance of `User`.
1115        let tests: Vec<JsonTestRow> = serde_json::from_reader(reader).expect("Failed Parsing {:?}");
1116        let mut had_errors = 0;
1117        let total_tests = tests.len();
1118        for row in tests {
1119            if let Err(error) = row.test_row() {
1120                println!("Test: {:?} failed: {:?}", row.clone(), error);
1121                had_errors += 1;
1122            }
1123        }
1124        if had_errors > 0 {
1125            panic!("{}/{} json tests failed", had_errors, total_tests)
1126        }
1127    }
1128}