|
| 1 | +use crate::*; |
| 2 | + |
| 3 | +// Test the interpreter on all 4-byte combinations of non-trivial opcodes. |
| 4 | +#[test] |
| 5 | +//#[ignore = "This test is too expensive to run by default"] |
| 6 | +fn test_4opc_sequences() { |
| 7 | + use hex::FromHex; |
| 8 | + use std::io::{BufRead, BufReader}; |
| 9 | + |
| 10 | + // The test vectors are encoded in a gzipped CSV file. |
| 11 | + // Each line in the file is has the following comma-separated filelds: |
| 12 | + // 1) The hex-encoded bitcoin script |
| 13 | + // 2) The expected outcome, which is either 0 (script should fail) or 1 (script should succceed) |
| 14 | + // 3) If the expected outcome is 1 (success), then a sequence of comma-separated hex-encoded |
| 15 | + // stack items after the execution of the script follows. |
| 16 | + // |
| 17 | + // The test vectors were obtained obtained by running the script interpreter in Tapscript mode |
| 18 | + // on all 4-opcode sequences of a subset of opcodes. Notable omissions include: |
| 19 | + // * `OP_NOP_N`, `OP_SUCCESS_N`: These are trivial and including them would make the file much |
| 20 | + // larger (and test run time much longer) with little benefit. |
| 21 | + // * Opcodes dealing with checking signatures: These behave differently in Bitcoin. |
| 22 | + // * `OP_PUSHDATA_N`: Some these should be included in the future here or in a separate test. |
| 23 | + let test_vectors_raw = include_bytes!("test_vectors_4opc.csv.gz").as_ref(); |
| 24 | + let test_vectors = BufReader::new(flate2::bufread::GzDecoder::new(test_vectors_raw)); |
| 25 | + |
| 26 | + let mut fails = 0u32; |
| 27 | + for line in test_vectors.lines().map(|l| l.expect("can't get a line")) { |
| 28 | + let mut parts = line.split(','); |
| 29 | + // Load the script. |
| 30 | + let script: Script = Vec::from_hex(parts.next().expect("no script")) |
| 31 | + .expect("script not in hex format") |
| 32 | + .into(); |
| 33 | + |
| 34 | + // Load the expected outcome. It should be either 0, or 1 followed by stack items. |
| 35 | + let expected: Option<Stack> = match parts.next().expect("no desired outcome") { |
| 36 | + "0" => None, |
| 37 | + "1" => { |
| 38 | + // For successful outcome, read the expected sequence of items on the stack |
| 39 | + let stack: Vec<_> = |
| 40 | + parts.map(|s| Vec::from_hex(s).expect("hex item").into()).collect(); |
| 41 | + Some(stack.into()) |
| 42 | + } |
| 43 | + _ => unreachable!("bad format"), |
| 44 | + }; |
| 45 | + |
| 46 | + // Run the script and record mismatches between expected and actual outputs. |
| 47 | + let result = run_script(&TestContext::default(), &script, vec![].into()).ok(); |
| 48 | + if expected != result { |
| 49 | + eprintln!("FAIL {:?}: {:?} vs. {:?}", script, result, expected); |
| 50 | + fails += 1; |
| 51 | + } |
| 52 | + } |
| 53 | + |
| 54 | + // Let the test fail if we have at least one mismatch. |
| 55 | + assert!(fails == 0, "{} tests failed", fails); |
| 56 | +} |
0 commit comments