From 6cdbe1909cdd10a59889e04e7c91b81afbd607b7 Mon Sep 17 00:00:00 2001 From: LLFourn Date: Thu, 12 Nov 2026 11:21:56 +1100 Subject: [PATCH] feat(chain): validate unconfirmed transaction input scripts Check input scripts while canonicalizing transactions eligible through observation, against full parents or floating prevouts. Reject Invalid candidates and their descendants; retain Valid and Unknown candidates. Anchored and explicitly assumed transactions bypass these checks. Add the pure-Rust, no_std bdk_script_verify crate and TxGraph::validate_tx with per-input results and floating-prevout provenance. Memoize results only for the lifetime of one canonicalization task. Expose a caller-owned VerificationCache for verify_input and a whole-transaction convenience API. Reuse transaction-wide signature hashes within each copy while keeping per-input dependency reports independent. Keep complete witness variants by wtxid and persist them through changesets and SQLite schema v4. During canonicalization, prefer a Valid whole copy over Unknown and exclude a txid only if all copies are Invalid. Carry the selected copy into the view, so junk witnesses cannot hide a retained valid payment. Document the ordinary graph-query preference and the floating-prevout and unsigned-draft tradeoffs in the ADR. Validate the verifier with Core vectors, signed spends, generated and deterministic differential tests against a pinned libbitcoinkernel. Retain attribution for the code adapted from rbitcoin. --- .github/workflows/code_coverage.yml | 5 +- .github/workflows/cont_integration.yml | 29 + Cargo.toml | 1 + crates/chain/Cargo.toml | 3 +- crates/chain/src/canonical_task.rs | 144 +- crates/chain/src/lib.rs | 1 + crates/chain/src/rusqlite_impl.rs | 160 +- crates/chain/src/tx_graph.rs | 195 +- crates/chain/src/validation.rs | 102 + crates/chain/tests/common/tx_template.rs | 41 +- crates/chain/tests/test_canonical_view.rs | 49 +- crates/chain/tests/test_indexed_tx_graph.rs | 35 +- crates/chain/tests/test_input_validation.rs | 1189 +++++++ crates/chain/tests/test_tx_graph.rs | 35 +- crates/chain/tests/test_tx_graph_conflicts.rs | 10 +- crates/script_verify/Cargo.toml | 30 + crates/script_verify/README.md | 46 + crates/script_verify/src/crypto.rs | 133 + crates/script_verify/src/interpreter.rs | 1186 +++++++ crates/script_verify/src/lib.rs | 243 ++ crates/script_verify/src/scriptnum.rs | 51 + crates/script_verify/src/sighash.rs | 257 ++ crates/script_verify/src/tests.rs | 246 ++ crates/script_verify/tests/core_vectors.rs | 553 ++++ crates/script_verify/tests/data/README.md | 7 + .../tests/data/bip341_wallet_vectors.json | 452 +++ .../tests/data/script_tests.json | 2743 +++++++++++++++++ .../script_verify/tests/data/tx_invalid.json | 397 +++ crates/script_verify/tests/data/tx_valid.json | 528 ++++ crates/script_verify/tests/differential.rs | 2642 ++++++++++++++++ docs/input-validation.md | 96 + justfile | 4 + 32 files changed, 11469 insertions(+), 144 deletions(-) create mode 100644 crates/chain/src/validation.rs create mode 100644 crates/chain/tests/test_input_validation.rs create mode 100644 crates/script_verify/Cargo.toml create mode 100644 crates/script_verify/README.md create mode 100644 crates/script_verify/src/crypto.rs create mode 100644 crates/script_verify/src/interpreter.rs create mode 100644 crates/script_verify/src/lib.rs create mode 100644 crates/script_verify/src/scriptnum.rs create mode 100644 crates/script_verify/src/sighash.rs create mode 100644 crates/script_verify/src/tests.rs create mode 100644 crates/script_verify/tests/core_vectors.rs create mode 100644 crates/script_verify/tests/data/README.md create mode 100644 crates/script_verify/tests/data/bip341_wallet_vectors.json create mode 100644 crates/script_verify/tests/data/script_tests.json create mode 100644 crates/script_verify/tests/data/tx_invalid.json create mode 100644 crates/script_verify/tests/data/tx_valid.json create mode 100644 crates/script_verify/tests/differential.rs create mode 100644 docs/input-validation.md diff --git a/.github/workflows/code_coverage.yml b/.github/workflows/code_coverage.yml index afc088f83e..2af3da8579 100644 --- a/.github/workflows/code_coverage.yml +++ b/.github/workflows/code_coverage.yml @@ -20,8 +20,9 @@ jobs: uses: actions/checkout@v7 with: persist-credentials: false - - name: Install lcov tools - run: sudo apt-get install lcov -y + # Boost is for `bdk_script_verify`'s `bitcoinkernel` dev-dependency, which builds Bitcoin Core. + - name: Install lcov tools and Boost + run: sudo apt-get update && sudo apt-get install -y lcov libboost-dev - name: Install Rust toolchain uses: actions-rust-lang/setup-rust-toolchain@v1 with: diff --git a/.github/workflows/cont_integration.yml b/.github/workflows/cont_integration.yml index e4e788f067..704d644d04 100644 --- a/.github/workflows/cont_integration.yml +++ b/.github/workflows/cont_integration.yml @@ -65,6 +65,32 @@ jobs: - name: Test (all features) run: cargo test --all-features + build-script-verify: + needs: prepare + name: bdk_script_verify + runs-on: ubuntu-latest + defaults: + run: + working-directory: ./crates/script_verify + steps: + - name: Checkout + uses: actions/checkout@v7 + with: + persist-credentials: false + - uses: actions-rust-lang/setup-rust-toolchain@v1 + with: + toolchain: ${{ needs.prepare.outputs.rust_version }} + cache: true + # The differential tests' `bitcoinkernel` dev-dependency builds Bitcoin Core, which needs Boost. + - name: Install Boost + run: sudo apt-get update && sudo apt-get install -y libboost-dev + - name: Build (default features) + run: cargo build + - name: Build (no default features) + run: cargo build --no-default-features + - name: Test (all features) + run: cargo test --all-features + build-chain: needs: prepare name: bdk_chain @@ -316,6 +342,9 @@ jobs: toolchain: ${{ needs.prepare.outputs.rust_version }} cache: true components: clippy + # `--all-targets` builds `bdk_script_verify`'s `bitcoinkernel` dev-dependency, which needs Boost. + - name: Install Boost + run: sudo apt-get update && sudo apt-get install -y libboost-dev - name: Check clippy run: cargo clippy --all-features --all-targets -- -D warnings diff --git a/Cargo.toml b/Cargo.toml index fc0253b16c..edd5abb033 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,6 +3,7 @@ resolver = "2" members = [ "crates/chain", "crates/core", + "crates/script_verify", "crates/file_store", "crates/electrum", "crates/esplora", diff --git a/crates/chain/Cargo.toml b/crates/chain/Cargo.toml index 36ff3847db..8b656da471 100644 --- a/crates/chain/Cargo.toml +++ b/crates/chain/Cargo.toml @@ -18,6 +18,7 @@ workspace = true [dependencies] bitcoin = { version = "0.32.0", default-features = false } bdk_core = { path = "../core", version = "0.6.2", default-features = false } +bdk_script_verify = { path = "../script_verify", version = "0.1.0", default-features = false } serde = { version = "1", optional = true, default-features = false, features = ["derive", "rc", "alloc"] } miniscript = { version = "13.0.0", optional = true, default-features = false } @@ -33,7 +34,7 @@ tempfile = "3" [features] default = ["std", "miniscript"] -std = ["bitcoin/std", "miniscript?/std", "bdk_core/std"] +std = ["bitcoin/std", "miniscript?/std", "bdk_core/std", "bdk_script_verify/std"] serde = ["dep:serde", "bitcoin/serde", "miniscript?/serde", "bdk_core/serde"] hashbrown = ["bdk_core/hashbrown"] rusqlite = ["std", "dep:rusqlite", "serde"] diff --git a/crates/chain/src/canonical_task.rs b/crates/chain/src/canonical_task.rs index 693a3229bb..56fa046e4d 100644 --- a/crates/chain/src/canonical_task.rs +++ b/crates/chain/src/canonical_task.rs @@ -1,5 +1,6 @@ use crate::collections::{HashMap, HashSet, VecDeque}; use crate::tx_graph::{TxAncestors, TxDescendants}; +use crate::validation::TxValidity; use crate::{Anchor, CanonicalTxs, TxGraph}; use alloc::boxed::Box; use alloc::collections::BTreeSet; @@ -46,6 +47,9 @@ pub struct CanonicalParams { /// /// In case of conflicting transactions within `assume_canonical`, transactions that appear /// later in the list (have higher index) have precedence. + /// + /// This overrides script validation: these transactions and their ancestors are canonical + /// even if [`TxGraph::validate_tx`](crate::TxGraph::validate_tx) finds them invalid. pub assume_canonical: Vec, } @@ -56,6 +60,11 @@ pub struct CanonicalParams { /// (via [`CanonicalReason`](crate::CanonicalReason)). The output is a [`CanonicalTxs`] which can /// then be further processed by [`CanonicalViewTask`](crate::CanonicalViewTask) to resolve reasons /// into [`ChainPosition`](crate::ChainPosition)s. +/// +/// For unconfirmed candidates, retained copies are checked in the graph's witness preference +/// order. A Valid copy is preferred over Unknown; only an all-Invalid set is rejected. The output +/// carries the selected whole copy, which may differ from [`TxGraph::get_tx`]. Anchored and assumed +/// transactions retain the graph's preferred copy without script checks. pub struct CanonicalTask<'g, A> { tx_graph: &'g TxGraph, chain_tip: BlockId, @@ -67,6 +76,11 @@ pub struct CanonicalTask<'g, A> { canonical: CanonicalMap, not_canonical: NotCanonicalSet, + /// The selected eligible copy, or None if all copies are Invalid. The immutable graph makes + /// these results reusable even after the walk that computed them is undone. + selected_copies: HashMap>>, + #[cfg(test)] + validations: usize, // Store canonical transactions in order canonical_order: Vec, @@ -253,6 +267,9 @@ impl<'g, A: Anchor> CanonicalTask<'g, A> { canonical: HashMap::new(), not_canonical: HashSet::new(), + selected_copies: HashMap::new(), + #[cfg(test)] + validations: 0, canonical_order: Vec::new(), current_stage: CanonicalStage::default(), @@ -267,9 +284,9 @@ impl<'g, A: Anchor> CanonicalTask<'g, A> { let starting_txid = txid; let mut is_starting_tx = true; - // We keep track of changes made so far so that we can undo it later in case we detect that - // `tx` double spends itself. - let mut detected_self_double_spend = false; + // We keep track of changes made so far so that we can undo them if `tx` turns out not to be + // canonical: it double spends itself, or it or an ancestor has invalid scripts. + let mut rejected = false; let mut undo_not_canonical = Vec::::new(); let mut staged_canonical = Vec::<(Txid, Arc, CanonicalReason)>::new(); @@ -294,6 +311,43 @@ impl<'g, A: Anchor> CanonicalTask<'g, A> { Entry::Vacant(entry) => entry, }; + // Validation guards against acting on unconfirmed transactions that can never + // confirm. An anchor in the best chain or an explicit assumption outranks it. An + // unknown verdict does not bar it: the graph may just lack a parent. + let tx = if this_reason.is_unconfirmed() { + let selected = self.selected_copies.entry(this_txid).or_insert_with(|| { + let mut copies = self.tx_graph.tx_copies(this_txid).collect::>(); + copies.sort_by_cached_key(|copy| { + ( + crate::tx_graph::witness_preference(copy), + copy.compute_wtxid(), + ) + }); + let mut unknown = None; + for copy in copies { + #[cfg(test)] + { + self.validations += 1; + } + match self.tx_graph.validate_tx(©).validity() { + TxValidity::Valid => return Some(copy), + TxValidity::Unknown if unknown.is_none() => unknown = Some(copy), + TxValidity::Unknown | TxValidity::Invalid => {} + } + } + unknown + }); + match selected { + Some(copy) => copy.clone(), + None => { + rejected = true; + return None; + } + } + } else { + tx + }; + // Prune conflicts // // Any conflicts with a canonical tx can be added to `not_canonical`. Descendants @@ -316,7 +370,7 @@ impl<'g, A: Anchor> CanonicalTask<'g, A> { if self.not_canonical.contains(&this_txid) { // Early exit if self-double-spend is detected. - detected_self_double_spend = true; + rejected = true; return None; } @@ -327,7 +381,7 @@ impl<'g, A: Anchor> CanonicalTask<'g, A> { ) .run_until_finished(); - if detected_self_double_spend { + if rejected { // Undo changes for (txid, _, _) in staged_canonical { self.canonical.remove(&txid); @@ -443,6 +497,15 @@ impl CanonicalReason { pub fn is_assumed(&self) -> bool { matches!(self, CanonicalReason::Assumed { .. }) } + + /// Whether this reason leaves the transaction unconfirmed, which is when script validity + /// decides it. Exhaustive so that a new reason has to make that choice. + fn is_unconfirmed(&self) -> bool { + match self { + CanonicalReason::Assumed { .. } | CanonicalReason::Anchor { .. } => false, + CanonicalReason::ObservedIn { .. } => true, + } + } } #[cfg(test)] @@ -451,7 +514,7 @@ mod tests { use super::*; use crate::local_chain::LocalChain; use crate::ChainPosition; - use bitcoin::{hashes::Hash, BlockHash, TxIn, TxOut}; + use bitcoin::{hashes::Hash, opcodes, BlockHash, OutPoint, ScriptBuf, TxIn, TxOut}; #[test] fn test_canonicalization_task_sans_io() { @@ -503,4 +566,73 @@ mod tests { // Should be confirmed (anchored) assert!(matches!(canon_tx.pos, ChainPosition::Confirmed { .. })); } + + #[test] + fn a_valid_ancestor_of_a_rejected_candidate_is_checked_once() { + let chain = LocalChain::from_blocks([(0, BlockHash::all_zeros())].into()).unwrap(); + let anyone = ScriptBuf::from_bytes(vec![opcodes::OP_TRUE.to_u8()]); + let unspendable = ScriptBuf::new_op_return([]); + let txout = |script_pubkey: &ScriptBuf| TxOut { + value: bitcoin::Amount::from_sat(1000), + script_pubkey: script_pubkey.clone(), + }; + let spend = |prevouts: &[OutPoint], outputs: usize| bitcoin::Transaction { + version: bitcoin::transaction::Version::TWO, + lock_time: bitcoin::absolute::LockTime::ZERO, + input: prevouts + .iter() + .map(|&previous_output| TxIn { + previous_output, + ..TxIn::default() + }) + .collect(), + output: vec![txout(&anyone); outputs], + }; + + let mut tx_graph = TxGraph::::default(); + let anyone_coin = OutPoint::new(Txid::from_byte_array([1; 32]), 0); + let unspendable_coin = OutPoint::new(Txid::from_byte_array([2; 32]), 0); + let _ = tx_graph.insert_txout(anyone_coin, txout(&anyone)); + let _ = tx_graph.insert_txout(unspendable_coin, txout(&unspendable)); + let shared = spend(&[anyone_coin], 2); + let invalid = spend(&[unspendable_coin], 1); + let rejected = spend( + &[ + OutPoint::new(shared.compute_txid(), 0), + OutPoint::new(invalid.compute_txid(), 0), + ], + 1, + ); + let later = spend(&[OutPoint::new(shared.compute_txid(), 1)], 1); + for tx in [&shared, &invalid, &rejected, &later] { + let _ = tx_graph.insert_tx(tx.clone()); + } + let mut bogus_shared = shared.clone(); + bogus_shared.input[0].witness = bitcoin::Witness::from_slice(&[vec![0]]); + let _ = tx_graph.insert_tx(bogus_shared); + assert_ne!(*tx_graph.get_tx(shared.compute_txid()).unwrap(), shared); + let _ = tx_graph.insert_seen_at(rejected.compute_txid(), 2); + let _ = tx_graph.insert_seen_at(later.compute_txid(), 1); + + let mut task = CanonicalTask::new( + &tx_graph, + chain.tip().block_id(), + CanonicalParams::default(), + ); + assert!(task.next_query().is_none(), "nothing is anchored"); + + assert_eq!( + task.validations, 5, + "each copy is checked once, including both copies of the shared ancestor" + ); + assert_eq!( + task.selected_copies[&shared.compute_txid()].as_deref(), + Some(&shared) + ); + let mut canonical = task.canonical.keys().copied().collect::>(); + canonical.sort(); + let mut expected = vec![shared.compute_txid(), later.compute_txid()]; + expected.sort(); + assert_eq!(canonical, expected); + } } diff --git a/crates/chain/src/lib.rs b/crates/chain/src/lib.rs index 41ed7cc098..55a77e54ba 100644 --- a/crates/chain/src/lib.rs +++ b/crates/chain/src/lib.rs @@ -48,6 +48,7 @@ mod canonical; pub use canonical::*; mod canonical_view_task; pub use canonical_view_task::*; +pub mod validation; #[doc(hidden)] pub mod example_utils; diff --git a/crates/chain/src/rusqlite_impl.rs b/crates/chain/src/rusqlite_impl.rs index ad89f429ec..bbcf27f0d3 100644 --- a/crates/chain/src/rusqlite_impl.rs +++ b/crates/chain/src/rusqlite_impl.rs @@ -82,6 +82,20 @@ impl ToSql for Impl { } } +impl FromSql for Impl { + fn column_result(value: ValueRef<'_>) -> FromSqlResult { + bitcoin::Wtxid::from_str(value.as_str()?) + .map(Self) + .map_err(from_sql_error) + } +} + +impl ToSql for Impl { + fn to_sql(&self) -> rusqlite::Result> { + Ok(self.to_string().into()) + } +} + impl FromSql for Impl { fn column_result(value: ValueRef<'_>) -> FromSqlResult { bitcoin::BlockHash::from_str(value.as_str()?) @@ -196,8 +210,12 @@ fn to_sql_error(err: E) -> rusqli impl tx_graph::ChangeSet { /// Schema name for [`tx_graph::ChangeSet`]. pub const SCHEMA_NAME: &'static str = "bdk_txgraph"; - /// Name of table that stores full transactions and `last_seen` timestamps. + /// Name of table that stores per-txid timestamps. Before schema v4 it also held one full + /// transaction per txid. pub const TXS_TABLE_NAME: &'static str = "bdk_txs"; + /// Name of table that stores every received copy of each full transaction, keyed by wtxid + /// (copies of a txid differ only in witnesses). + pub const TX_COPIES_TABLE_NAME: &'static str = "bdk_tx_copies"; /// Name of table that stores floating txouts. pub const TXOUTS_TABLE_NAME: &'static str = "bdk_txouts"; /// Name of table that stores [`Anchor`]s. @@ -274,6 +292,45 @@ impl tx_graph::ChangeSet { ) } + /// Migrates schema v3 to v4, which moves full transactions into a table keyed by wtxid. + /// + /// `TxGraph` keeps every copy of a txid, and which one it exposes can change as data arrives, + /// so one stored transaction per txid would lose copies. This is Rust rather than an SQL script + /// because SQLite cannot compute a wtxid. + fn migrate_v3_to_v4(db_tx: &rusqlite::Transaction) -> rusqlite::Result<()> { + db_tx.execute_batch(&format!( + "CREATE TABLE {} ( \ + wtxid TEXT PRIMARY KEY NOT NULL, \ + txid TEXT NOT NULL REFERENCES {} (txid), \ + raw_tx BLOB NOT NULL \ + ) STRICT", + Self::TX_COPIES_TABLE_NAME, + Self::TXS_TABLE_NAME, + ))?; + let mut select = db_tx.prepare(&format!( + "SELECT raw_tx FROM {} WHERE raw_tx IS NOT NULL", + Self::TXS_TABLE_NAME, + ))?; + let mut insert = db_tx.prepare(&format!( + "INSERT INTO {}(wtxid, txid, raw_tx) VALUES(:wtxid, :txid, :raw_tx)", + Self::TX_COPIES_TABLE_NAME, + ))?; + let rows = + select.query_map([], |row| row.get::<_, Impl>("raw_tx"))?; + for row in rows { + let tx = row?.0; + insert.execute(named_params! { + ":wtxid": Impl(tx.compute_wtxid()), + ":txid": Impl(tx.compute_txid()), + ":raw_tx": Impl(tx), + })?; + } + db_tx.execute_batch(&format!( + "ALTER TABLE {} DROP COLUMN raw_tx", + Self::TXS_TABLE_NAME + )) + } + /// Initialize sqlite tables. pub fn init_sqlite_tables(db_tx: &rusqlite::Transaction) -> rusqlite::Result<()> { migrate_schema( @@ -285,7 +342,12 @@ impl tx_graph::ChangeSet { &Self::schema_v2(), &Self::schema_v3(), ], - ) + )?; + if schema_version(db_tx, Self::SCHEMA_NAME)? == Some(3) { + set_schema_version(db_tx, Self::SCHEMA_NAME, 4)?; + Self::migrate_v3_to_v4(db_tx)?; + } + Ok(()) } /// Construct a [`TxGraph`] from an sqlite database. @@ -295,23 +357,29 @@ impl tx_graph::ChangeSet { let mut changeset = Self::default(); let mut statement = db_tx.prepare(&format!( - "SELECT txid, raw_tx, first_seen, last_seen, last_evicted FROM {}", + "SELECT raw_tx FROM {}", + Self::TX_COPIES_TABLE_NAME, + ))?; + let row_iter = + statement.query_map([], |row| row.get::<_, Impl>("raw_tx"))?; + for row in row_iter { + changeset.txs.insert(Arc::new(row?.0)); + } + + let mut statement = db_tx.prepare(&format!( + "SELECT txid, first_seen, last_seen, last_evicted FROM {}", Self::TXS_TABLE_NAME, ))?; let row_iter = statement.query_map([], |row| { Ok(( row.get::<_, Impl>("txid")?, - row.get::<_, Option>>("raw_tx")?, row.get::<_, Option>("first_seen")?, row.get::<_, Option>("last_seen")?, row.get::<_, Option>("last_evicted")?, )) })?; for row in row_iter { - let (Impl(txid), tx, first_seen, last_seen, last_evicted) = row?; - if let Some(Impl(tx)) = tx { - changeset.txs.insert(Arc::new(tx)); - } + let (Impl(txid), first_seen, last_seen, last_evicted) = row?; if let Some(first_seen) = first_seen { changeset.first_seen.insert(txid, first_seen); } @@ -376,13 +444,20 @@ impl tx_graph::ChangeSet { /// /// Remember to call [`Self::init_sqlite_tables`] beforehand. pub fn persist_to_sqlite(&self, db_tx: &rusqlite::Transaction) -> rusqlite::Result<()> { - let mut statement = db_tx.prepare_cached(&format!( - "INSERT INTO {}(txid, raw_tx) VALUES(:txid, :raw_tx) ON CONFLICT(txid) DO UPDATE SET raw_tx=:raw_tx", + let mut statement_txid = db_tx.prepare_cached(&format!( + "INSERT OR IGNORE INTO {}(txid) VALUES(:txid)", Self::TXS_TABLE_NAME, ))?; + let mut statement = db_tx.prepare_cached(&format!( + "INSERT OR IGNORE INTO {}(wtxid, txid, raw_tx) VALUES(:wtxid, :txid, :raw_tx)", + Self::TX_COPIES_TABLE_NAME, + ))?; for tx in &self.txs { + let txid = Impl(tx.compute_txid()); + statement_txid.execute(named_params! { ":txid": txid })?; statement.execute(named_params! { - ":txid": Impl(tx.compute_txid()), + ":wtxid": Impl(tx.compute_wtxid()), + ":txid": txid, ":raw_tx": Impl(tx.as_ref().clone()), })?; } @@ -730,7 +805,7 @@ mod test { } #[test] - fn v0_to_v3_schema_migration_is_backward_compatible() -> anyhow::Result<()> { + fn v0_to_v4_schema_migration_is_backward_compatible() -> anyhow::Result<()> { type ChangeSet = tx_graph::ChangeSet; let mut conn = rusqlite::Connection::open_in_memory()?; @@ -799,19 +874,10 @@ mod test { } } - // Apply v1, v2, v3 sqlite schema to tables with data + // Apply v1, v2, v3, v4 sqlite schema to tables with data { let db_tx = conn.transaction()?; - migrate_schema( - &db_tx, - ChangeSet::SCHEMA_NAME, - &[ - &ChangeSet::schema_v0(), - &ChangeSet::schema_v1(), - &ChangeSet::schema_v2(), - &ChangeSet::schema_v3(), - ], - )?; + ChangeSet::init_sqlite_tables(&db_tx)?; db_tx.commit()?; } @@ -826,6 +892,54 @@ mod test { Ok(()) } + #[test] + fn v4_migration_moves_transactions_into_copies() -> anyhow::Result<()> { + type ChangeSet = tx_graph::ChangeSet; + let mut conn = rusqlite::Connection::open_in_memory()?; + let v0_to_v3 = [ + ChangeSet::schema_v0(), + ChangeSet::schema_v1(), + ChangeSet::schema_v2(), + ChangeSet::schema_v3(), + ]; + let tx = bitcoin::Transaction { + version: transaction::Version::TWO, + lock_time: absolute::LockTime::ZERO, + input: vec![TxIn::default()], + output: vec![TxOut::NULL], + }; + let txid = tx.compute_txid(); + + // As persisted before v4: one raw tx per txid in the txs table. + { + let db_tx = conn.transaction()?; + let v0_to_v3 = v0_to_v3.iter().map(String::as_str).collect::>(); + migrate_schema(&db_tx, ChangeSet::SCHEMA_NAME, &v0_to_v3)?; + db_tx.execute( + &format!( + "INSERT INTO {}(txid, raw_tx, last_seen) VALUES(:txid, :raw_tx, 7)", + ChangeSet::TXS_TABLE_NAME + ), + named_params! { ":txid": Impl(txid), ":raw_tx": Impl(tx.clone()) }, + )?; + db_tx.commit()?; + } + + let db_tx = conn.transaction()?; + ChangeSet::init_sqlite_tables(&db_tx)?; + let mut other_copy = tx.clone(); + other_copy.input[0].witness = bitcoin::Witness::from_slice(&[[1u8]]); + ChangeSet { + txs: [Arc::new(other_copy.clone())].into(), + ..Default::default() + } + .persist_to_sqlite(&db_tx)?; + let changeset = ChangeSet::from_sqlite(&db_tx)?; + assert_eq!(changeset.txs, [Arc::new(tx), Arc::new(other_copy)].into()); + assert_eq!(changeset.last_seen, [(txid, 7)].into()); + Ok(()) + } + #[test] fn can_persist_first_seen() -> anyhow::Result<()> { use bitcoin::hashes::Hash; diff --git a/crates/chain/src/tx_graph.rs b/crates/chain/src/tx_graph.rs index 3c6ab69e59..308bd7e8b8 100644 --- a/crates/chain/src/tx_graph.rs +++ b/crates/chain/src/tx_graph.rs @@ -129,6 +129,7 @@ //! [`canonical_task`]: TxGraph::canonical_task use crate::collections::*; +use crate::validation::{InputValidity, TxValidation}; use crate::CanonicalParams; use crate::CanonicalTask; use crate::{Anchor, BlockId, Merge}; @@ -137,7 +138,8 @@ use alloc::sync::Arc; use alloc::vec::Vec; use bdk_core::ConfirmationBlockTime; pub use bdk_core::TxUpdate; -use bitcoin::{Amount, OutPoint, SignedAmount, Transaction, TxOut, Txid}; +use bdk_script_verify::{DependsOn, Error as VerifyError}; +use bitcoin::{Amount, OutPoint, SignedAmount, Transaction, TxOut, Txid, Wtxid}; use core::fmt::{self, Formatter}; use core::ops::{Deref, RangeInclusive}; @@ -185,6 +187,10 @@ pub struct TxGraph { txs_by_highest_conf_heights: BTreeSet<(u32, Txid)>, txs_by_last_seen: BTreeSet<(u64, Txid)>, + /// Every received copy of each txid that has more than one (they differ only in witnesses). + /// `txs` holds the copy resolved from these. + copies: HashMap>>, + // The following fields exist so that methods can return references to empty sets. // FIXME: This can be removed once `HashSet::new` and `BTreeSet::new` are const fns. empty_outspends: HashSet, @@ -202,6 +208,7 @@ impl Default for TxGraph { last_evicted: Default::default(), txs_by_highest_conf_heights: Default::default(), txs_by_last_seen: Default::default(), + copies: Default::default(), empty_outspends: Default::default(), empty_anchors: Default::default(), } @@ -361,6 +368,30 @@ impl TxGraph { }) } + /// Every received copy of every full transaction. The graph's own transactions are resolved + /// from these, so replaying them rebuilds it. + fn received_txs(&self) -> impl Iterator> + '_ { + self.full_txs() + .flat_map(|node| match self.copies.get(&node.txid) { + Some(copies) => copies.values().cloned().collect::>(), + None => vec![node.tx], + }) + } + + /// Every received copy of the full transaction `txid`. Copies share a txid and differ only in + /// their witnesses. + /// + /// Graph queries expose one copy per txid, chosen as [`insert_tx`](Self::insert_tx) describes. + /// Canonicalization additionally considers script validity and may select a different copy. + pub fn tx_copies(&self, txid: Txid) -> impl Iterator> + '_ { + match (self.copies.get(&txid), self.txs.get(&txid)) { + (Some(copies), _) => copies.values().cloned().collect(), + (None, Some(TxNodeInternal::Whole(tx))) => vec![tx.clone()], + _ => vec![], + } + .into_iter() + } + /// Iterate over graph transactions with no anchors or last-seen. pub fn txs_with_no_anchor_or_last_seen( &self, @@ -585,6 +616,85 @@ impl TxGraph { pub fn is_empty(&self) -> bool { self.txs.is_empty() } + + /// Checks the scripts of each input of `tx`, which need not be in the graph, against the + /// outputs it spends, under today's consensus script rules. + /// + /// Each spent output is taken from the graph's full transaction if it has one, and otherwise + /// from its floating txout, and each verdict says whether it relied on a floating txout. An + /// input depending on an output the graph has neither for is [`InputValidity::Unknown`]. + /// + /// [`InputValidity::Unknown`]: crate::validation::InputValidity::Unknown + pub fn validate_tx(&self, tx: &Transaction) -> TxValidation { + let prevouts = tx + .input + .iter() + .map(|txin| { + let outpoint = txin.previous_output; + match self.txs.get(&outpoint.txid)? { + TxNodeInternal::Whole(prev) => { + Some((prev.output.get(outpoint.vout as usize)?, false)) + } + TxNodeInternal::Partial(txouts) => Some((txouts.get(&outpoint.vout)?, true)), + } + }) + .collect::>(); + let txouts = prevouts + .iter() + .map(|p| p.map(|(txout, _)| txout)) + .collect::>(); + let floating = |vin: usize| matches!(prevouts[vin], Some((_, true))); + let any_floating = (0..tx.input.len()).any(floating); + let inputs = + bdk_script_verify::verify_transaction(tx, &txouts, bdk_script_verify::Flags::CONSENSUS) + .into_iter() + .enumerate() + .map(|(vin, verification)| { + let relies_on_floating = match verification.depends_on { + DependsOn::OwnPrevout => floating(vin), + DependsOn::AllPrevouts => any_floating, + }; + match verification.result { + Ok(()) => InputValidity::Valid { relies_on_floating }, + Err(VerifyError::Script(error)) => InputValidity::Invalid { + error, + relies_on_floating, + }, + Err(VerifyError::MissingPrevouts(inputs)) => InputValidity::Unknown { + missing: inputs + .into_iter() + .map(|i| tx.input[i].previous_output) + .collect(), + }, + } + }) + .collect(); + TxValidation { inputs } + } + + /// Exposes the preferred copy of `txid`. The choice depends only on the copies, never on the + /// order they arrived in. + fn resolve(&mut self, txid: Txid) { + let Some(copies) = self.copies.get(&txid) else { + return; + }; + let best = copies + .iter() + .min_by_key(|(wtxid, tx)| (witness_preference(tx), **wtxid)) + .map(|(_, tx)| tx.clone()) + .expect("copies are only recorded in pairs or more"); + self.txs.insert(txid, TxNodeInternal::Whole(best)); + } +} + +pub(crate) fn witness_preference(tx: &Transaction) -> (usize, usize) { + let empty = tx + .input + .iter() + .filter(|input| input.witness.is_empty()) + .count(); + let size = tx.input.iter().map(|input| input.witness.size()).sum(); + (empty, size) } impl TxGraph { @@ -656,55 +766,19 @@ impl TxGraph { /// further transaction insertions (given that the newly introduced transaction shares the same /// txid as the original transaction). /// - /// The witnesses of the newly introduced transaction will be merged with the witnesses of the - /// original transaction in a way where: + /// Every copy is kept, and the graph exposes one of them as the transaction, never mixing + /// witnesses across copies. It prefers, in order: + /// + /// * Fewer empty witnesses. + /// * Smaller witnesses in total. + /// * The lexicographically smaller wtxid. /// - /// * A non-empty witness has precedence over an empty witness. - /// * A smaller witness has precedence over a larger witness. - /// * If the witness sizes are the same, we prioritize the two witnesses with lexicographical - /// order. + /// Scripts are not checked here. For unconfirmed candidates, canonicalization prefers a Valid + /// copy over Unknown and excludes the txid only if all copies are Invalid. Within each validity + /// class it uses the order above, and its view carries that selected copy. A new copy + /// is in the returned [`ChangeSet`] even if it is not preferred. Use + /// [`tx_copies`](Self::tx_copies) to see every copy. pub fn insert_tx>>(&mut self, tx: T) -> ChangeSet { - // This returns `Some` only if the merged tx is different to the `original_tx`. - fn _merge_tx_witnesses( - original_tx: &Arc, - other_tx: &Arc, - ) -> Option> { - debug_assert_eq!( - original_tx.input.len(), - other_tx.input.len(), - "tx input count must be the same" - ); - let merged_input = Iterator::zip(original_tx.input.iter(), other_tx.input.iter()) - .map(|(original_txin, other_txin)| { - let original_key = core::cmp::Reverse(( - original_txin.witness.is_empty(), - original_txin.witness.size(), - &original_txin.witness, - )); - let other_key = core::cmp::Reverse(( - other_txin.witness.is_empty(), - other_txin.witness.size(), - &other_txin.witness, - )); - if original_key > other_key { - original_txin.clone() - } else { - other_txin.clone() - } - }) - .collect::>(); - if merged_input == original_tx.input { - return None; - } - if merged_input == other_tx.input { - return Some(other_tx.clone()); - } - Some(Arc::new(Transaction { - input: merged_input, - ..(**original_tx).clone() - })) - } - let tx: Arc = tx.into(); let txid = tx.compute_txid(); let mut changeset = ChangeSet::::default(); @@ -712,12 +786,22 @@ impl TxGraph { let tx_node = self.txs.entry(txid).or_default(); match tx_node { TxNodeInternal::Whole(existing_tx) => { - if existing_tx.as_ref() != tx.as_ref() { - // Allowing updating witnesses of txs. - if let Some(merged_tx) = _merge_tx_witnesses(existing_tx, &tx) { - *existing_tx = merged_tx.clone(); - changeset.txs.insert(merged_tx); + let is_new_copy = match self.copies.get_mut(&txid) { + Some(copies) => copies.insert(tx.compute_wtxid(), tx.clone()).is_none(), + None if existing_tx.as_ref() != tx.as_ref() => { + let copies = [ + (existing_tx.compute_wtxid(), existing_tx.clone()), + (tx.compute_wtxid(), tx.clone()), + ] + .into(); + self.copies.insert(txid, copies); + true } + None => false, + }; + if is_new_copy { + changeset.txs.insert(tx); + self.resolve(txid); } } partial_tx => { @@ -953,7 +1037,7 @@ impl TxGraph { /// Determines the [`ChangeSet`] between `self` and an empty [`TxGraph`]. pub fn initial_changeset(&self) -> ChangeSet { ChangeSet { - txs: self.full_txs().map(|tx_node| tx_node.tx).collect(), + txs: self.received_txs().collect(), txouts: self .floating_txouts() .map(|(op, txout)| (op, txout.clone())) @@ -1059,6 +1143,9 @@ impl TxGraph { #[must_use] pub struct ChangeSet { /// Added transactions. + /// + /// Several may share a txid, differing only in their witnesses. A store must keep every one: + /// which copy the graph exposes can change as more data arrives. pub txs: BTreeSet>, /// Added txouts. pub txouts: BTreeMap, diff --git a/crates/chain/src/validation.rs b/crates/chain/src/validation.rs new file mode 100644 index 0000000000..ea4f16f447 --- /dev/null +++ b/crates/chain/src/validation.rs @@ -0,0 +1,102 @@ +//! Script validation of transaction inputs. +//! +//! Validation exists to keep a provably invalid unconfirmed transaction out of the canonical set: +//! one a chain source injects to spend a wallet's on-chain outputs, so that they look spent or the +//! wallet replaces against a transaction that can never confirm. Canonicalization prefers a Valid +//! whole copy over Unknown and rejects a txid only when every retained copy is +//! [`TxValidity::Invalid`]. Valid and Unknown are both eligible, so nothing has to be proven valid. +//! +//! [`TxGraph::validate_tx`] checks each input of a transaction, in the graph or not, under today's +//! consensus script rules, the ones an unconfirmed transaction must meet. Each spent output comes +//! from the graph's full transaction when it has one, and otherwise from its floating txout. A +//! floating txout is the chain source's word, not proof, so each verdict records whether it relied +//! on one. +//! +//! Canonicalization runs the same check on each transaction that would be canonical only by being +//! seen. A txid with only Invalid copies, or one spending from it, is never picked that way. A +//! transaction confirmed in the best chain, or assumed canonical, is picked whatever its scripts, +//! since an anchor's trust is a matter of proof of work. Nothing is stored between +//! canonicalizations. +//! +//! See `docs/input-validation.md` in the repository for the model. +//! +//! [`TxGraph::validate_tx`]: crate::TxGraph::validate_tx + +use alloc::vec::Vec; +use bitcoin::OutPoint; + +pub use bdk_script_verify::ScriptError; + +/// The script validity of a whole transaction. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum TxValidity { + /// Every input's scripts passed. + Valid, + /// No input failed, but some could not be checked because the graph has neither a full + /// transaction nor a floating txout for outputs they depend on. + Unknown, + /// At least one input's scripts failed. + Invalid, +} + +/// The script validity of one input. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum InputValidity { + /// The input's scripts passed. + Valid { + /// Whether the check read a floating txout rather than a full transaction's output. + relies_on_floating: bool, + }, + /// The input's scripts failed against the spent outputs the graph has. It is conclusive only + /// as far as those outputs are: a floating txout is the chain source's word. + Invalid { + /// Why the scripts failed. + error: ScriptError, + /// Whether the check read a floating txout rather than a full transaction's output. + relies_on_floating: bool, + }, + /// The input cannot be checked until the graph has these spent outputs, as a full transaction + /// or a floating txout. + Unknown { + /// The spent outputs the graph has no data for. + /// + /// This is what the check stopped on, not necessarily everything it needs: a taproot + /// input missing its own prevout lists only that, and may then need the others. + missing: Vec, + }, +} + +/// The result of [`TxGraph::validate_tx`](crate::TxGraph::validate_tx). +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct TxValidation { + /// The validity of each input, in input order. + pub inputs: Vec, +} + +impl TxValidation { + /// The validity of the whole transaction: [`TxValidity::Invalid`] if any input is invalid, + /// otherwise [`TxValidity::Unknown`] if any input is unknown. + pub fn validity(&self) -> TxValidity { + let mut validity = TxValidity::Valid; + for input in &self.inputs { + match input { + InputValidity::Invalid { .. } => return TxValidity::Invalid, + InputValidity::Unknown { .. } => validity = TxValidity::Unknown, + InputValidity::Valid { .. } => {} + } + } + validity + } + + /// Whether any input's Valid or Invalid verdict relied on a floating txout. An Unknown input + /// has no verdict, so it relies on nothing. + pub fn relies_on_floating(&self) -> bool { + self.inputs.iter().any(|input| match input { + InputValidity::Valid { relies_on_floating } + | InputValidity::Invalid { + relies_on_floating, .. + } => *relies_on_floating, + InputValidity::Unknown { .. } => false, + }) + } +} diff --git a/crates/chain/tests/common/tx_template.rs b/crates/chain/tests/common/tx_template.rs index 7bdac78a50..0fc15004d9 100644 --- a/crates/chain/tests/common/tx_template.rs +++ b/crates/chain/tests/common/tx_template.rs @@ -1,15 +1,26 @@ #![cfg(feature = "miniscript")] -use bdk_testenv::utils::DESCRIPTORS; use rand::distributions::{Alphanumeric, DistString}; use std::collections::HashMap; use bdk_chain::{spk_txout::SpkTxOutIndex, tx_graph::TxGraph, Anchor, CanonicalParams}; use bitcoin::{ - locktime::absolute::LockTime, secp256k1::Secp256k1, transaction, Amount, OutPoint, ScriptBuf, - Sequence, Transaction, TxIn, TxOut, Txid, Witness, + locktime::absolute::LockTime, opcodes, script::Builder, transaction, Amount, OutPoint, + ScriptBuf, Sequence, Transaction, TxIn, TxOut, Txid, Witness, }; -use miniscript::Descriptor; + +/// Canonicalization checks the scripts of unconfirmed transactions, and the templates' spends are +/// unsigned, so every output must be spendable by an empty scriptSig. +fn anyone_can_spend(index: Option) -> ScriptBuf { + match index { + None => Builder::new().push_opcode(opcodes::OP_TRUE).into_script(), + Some(index) => Builder::new() + .push_int(index.into()) + .push_opcode(opcodes::all::OP_DROP) + .push_opcode(opcodes::OP_TRUE) + .into_script(), + } +} /// Template for creating a transaction in `TxGraph`. /// @@ -64,18 +75,10 @@ pub struct TxTemplateEnv<'a, A> { pub fn init_graph<'a, A: Anchor + Clone + 'a>( tx_templates: impl IntoIterator>, ) -> TxTemplateEnv<'a, A> { - let (descriptor, _) = - Descriptor::parse_descriptor(&Secp256k1::signing_only(), DESCRIPTORS[2]).unwrap(); let mut tx_graph = TxGraph::::default(); let mut indexer = SpkTxOutIndex::default(); (0..10).for_each(|index| { - indexer.insert_spk( - index, - descriptor - .at_derivation_index(index) - .unwrap() - .script_pubkey(), - ); + indexer.insert_spk(index, anyone_can_spend(Some(index))); }); let mut txid_to_name = HashMap::<&'a str, Txid>::new(); @@ -123,15 +126,9 @@ pub fn init_graph<'a, A: Anchor + Clone + 'a>( output: tx_tmp .outputs .iter() - .map(|output| match &output.spk_index { - None => TxOut { - value: Amount::from_sat(output.value), - script_pubkey: ScriptBuf::new(), - }, - Some(index) => TxOut { - value: Amount::from_sat(output.value), - script_pubkey: indexer.spk_at_index(index).unwrap(), - }, + .map(|output| TxOut { + value: Amount::from_sat(output.value), + script_pubkey: anyone_can_spend(output.spk_index), }) .collect(), }; diff --git a/crates/chain/tests/test_canonical_view.rs b/crates/chain/tests/test_canonical_view.rs index 9b17d3d0b0..a90e7410e9 100644 --- a/crates/chain/tests/test_canonical_view.rs +++ b/crates/chain/tests/test_canonical_view.rs @@ -10,6 +10,11 @@ use bdk_testenv::{hash, utils::new_tx}; use bitcoin::{key::Secp256k1, Amount, BlockHash, OutPoint, ScriptBuf, Transaction, TxIn, TxOut}; use miniscript::Descriptor; +/// Canonicalization checks the scripts of unconfirmed transactions, and these spends are unsigned. +fn anyone_can_spend() -> ScriptBuf { + ScriptBuf::from_bytes(vec![bitcoin::opcodes::OP_TRUE.to_u8()]) +} + /// Builds an `is_settled` predicate requiring at least `min_confirmations` confirmations. fn settled( tip_height: u32, @@ -48,7 +53,7 @@ fn test_is_settled_boundary() { }], output: vec![TxOut { value: Amount::from_sat(50_000), - script_pubkey: ScriptBuf::new(), + script_pubkey: anyone_can_spend(), }], ..new_tx(0) }; @@ -70,7 +75,7 @@ fn test_is_settled_boundary() { }], output: vec![TxOut { value: Amount::from_sat(50_000), - script_pubkey: ScriptBuf::new(), + script_pubkey: anyone_can_spend(), }], ..new_tx(1) }; @@ -154,7 +159,7 @@ fn test_min_confirmations_with_untrusted_tx() { }], output: vec![TxOut { value: Amount::from_sat(25_000), - script_pubkey: ScriptBuf::new(), + script_pubkey: anyone_can_spend(), }], ..new_tx(0) }; @@ -176,7 +181,7 @@ fn test_min_confirmations_with_untrusted_tx() { }], output: vec![TxOut { value: Amount::from_sat(25_000), - script_pubkey: ScriptBuf::new(), + script_pubkey: anyone_can_spend(), }], ..new_tx(1) }; @@ -260,15 +265,15 @@ fn test_min_confirmations_multiple_transactions() { output: vec![ TxOut { value: Amount::from_sat(10_000), - script_pubkey: ScriptBuf::new(), + script_pubkey: anyone_can_spend(), }, TxOut { value: Amount::from_sat(20_000), - script_pubkey: ScriptBuf::new(), + script_pubkey: anyone_can_spend(), }, TxOut { value: Amount::from_sat(30_000), - script_pubkey: ScriptBuf::new(), + script_pubkey: anyone_can_spend(), }, ], ..new_tx(0) @@ -291,7 +296,7 @@ fn test_min_confirmations_multiple_transactions() { }], output: vec![TxOut { value: Amount::from_sat(10_000), - script_pubkey: ScriptBuf::new(), + script_pubkey: anyone_can_spend(), }], ..new_tx(1) }; @@ -315,7 +320,7 @@ fn test_min_confirmations_multiple_transactions() { }], output: vec![TxOut { value: Amount::from_sat(20_000), - script_pubkey: ScriptBuf::new(), + script_pubkey: anyone_can_spend(), }], ..new_tx(2) }; @@ -339,7 +344,7 @@ fn test_min_confirmations_multiple_transactions() { }], output: vec![TxOut { value: Amount::from_sat(30_000), - script_pubkey: ScriptBuf::new(), + script_pubkey: anyone_can_spend(), }], ..new_tx(3) }; @@ -412,7 +417,7 @@ fn test_balance_taint_propagates_through_unconfirmed_ancestry() { let chain = LocalChain::from_blocks(blocks).unwrap(); let mut tx_graph = TxGraph::::default(); - let owned_spk = ScriptBuf::new(); + let owned_spk = anyone_can_spend(); // A confirmed coin we own. let coin = Transaction { @@ -538,7 +543,7 @@ fn test_balance_is_settled_is_authoritative_for_unconfirmed() { }], output: vec![TxOut { value: Amount::from_sat(50_000), - script_pubkey: ScriptBuf::new(), + script_pubkey: anyone_can_spend(), }], ..new_tx(1) }; @@ -569,7 +574,7 @@ fn test_balance_taint_stops_at_settled_ancestor() { .collect(); let chain = LocalChain::from_blocks(blocks).unwrap(); let mut tx_graph = TxGraph::::default(); - let owned_spk = ScriptBuf::new(); + let owned_spk = anyone_can_spend(); // A *settled* (confirmed) tx that itself spends a third-party coin — `does_taint` would flag // it. @@ -651,7 +656,7 @@ fn test_classify_immature_and_settled() { .collect(); let chain = LocalChain::from_blocks(blocks).unwrap(); let mut tx_graph = TxGraph::::default(); - let spk = ScriptBuf::new(); + let spk = anyone_can_spend(); // Coinbase confirmed at height 1; far below `COINBASE_MATURITY` -> immature. let coinbase = Transaction { @@ -725,7 +730,7 @@ fn test_balance_taint_shared_ancestor() { [(0, hash!("g")), (1, hash!("tip"))].into_iter().collect(); let chain = LocalChain::from_blocks(blocks).unwrap(); let mut tx_graph = TxGraph::::default(); - let owned_spk = ScriptBuf::new(); + let owned_spk = anyone_can_spend(); // Unconfirmed, funded by a third party -> taints itself. Two outputs. let foreign = Transaction { @@ -815,7 +820,7 @@ fn test_classify_skips_spent_and_unknown() { [(0, hash!("g")), (1, hash!("tip"))].into_iter().collect(); let chain = LocalChain::from_blocks(blocks).unwrap(); let mut tx_graph = TxGraph::::default(); - let spk = ScriptBuf::new(); + let spk = anyone_can_spend(); // `parent`'s output is spent by `child`. let parent = Transaction { @@ -881,7 +886,7 @@ fn test_immature_coinbase_stays_immature_when_unsettled() { }], output: vec![TxOut { value: Amount::from_sat(50_000), - script_pubkey: ScriptBuf::new(), + script_pubkey: anyone_can_spend(), }], ..new_tx(0) }; @@ -928,7 +933,7 @@ fn test_mature_coinbase_is_settled_not_immature() { }], output: vec![TxOut { value: Amount::from_sat(50_000), - script_pubkey: ScriptBuf::new(), + script_pubkey: anyone_can_spend(), }], ..new_tx(0) }; @@ -974,7 +979,7 @@ fn test_unsettled_unknown_when_parent_root_missing() { }], output: vec![TxOut { value: Amount::from_sat(50_000), - script_pubkey: ScriptBuf::new(), + script_pubkey: anyone_can_spend(), }], ..new_tx(1) }; @@ -987,7 +992,7 @@ fn test_unsettled_unknown_when_parent_root_missing() { }], output: vec![TxOut { value: Amount::from_sat(50_000), - script_pubkey: ScriptBuf::new(), + script_pubkey: anyone_can_spend(), }], ..new_tx(2) }; @@ -1035,7 +1040,7 @@ fn test_evicted_stale_anchored_tx_not_canonical() { }], output: vec![TxOut { value: Amount::from_sat(50_000), - script_pubkey: ScriptBuf::new(), + script_pubkey: anyone_can_spend(), }], ..new_tx(1) }; @@ -1069,7 +1074,7 @@ fn test_classify_locked() { [(0, hash!("g")), (1, hash!("tip"))].into_iter().collect(); let chain = LocalChain::from_blocks(blocks).unwrap(); let mut tx_graph = TxGraph::::default(); - let spk = ScriptBuf::new(); + let spk = anyone_can_spend(); let tx = Transaction { input: vec![TxIn { diff --git a/crates/chain/tests/test_indexed_tx_graph.rs b/crates/chain/tests/test_indexed_tx_graph.rs index c6e9443cec..3d8bc31650 100644 --- a/crates/chain/tests/test_indexed_tx_graph.rs +++ b/crates/chain/tests/test_indexed_tx_graph.rs @@ -365,6 +365,37 @@ fn reindex_reaches_fixed_point() { ); } +/// Signs the only input of `tx`, a key-path spend of `descriptor`, a `tr(tprv/path/*)`, at `index`. +fn sign_tr_key_spend(tx: &mut Transaction, prevout: &TxOut, descriptor: &str, index: u32) { + use bitcoin::bip32::{ChildNumber, DerivationPath, Xpriv}; + use bitcoin::hashes::Hash; + use bitcoin::key::TapTweak; + use bitcoin::sighash::{Prevouts, SighashCache, TapSighashType}; + + let secp = Secp256k1::new(); + let (xprv, path) = descriptor + .strip_prefix("tr(") + .and_then(|d| d.strip_suffix("/*)")) + .and_then(|d| d.split_once('/')) + .unwrap(); + let path = DerivationPath::from_str(&format!("m/{path}")) + .unwrap() + .child(ChildNumber::from_normal_idx(index).unwrap()); + let keypair = Xpriv::from_str(xprv) + .unwrap() + .derive_priv(&secp, &path) + .unwrap() + .to_keypair(&secp) + .tap_tweak(&secp, None) + .to_keypair(); + let sighash = SighashCache::new(&*tx) + .taproot_key_spend_signature_hash(0, &Prevouts::All(&[prevout]), TapSighashType::Default) + .unwrap(); + let message = bitcoin::secp256k1::Message::from_digest(sighash.to_byte_array()); + let sig = secp.sign_schnorr_no_aux_rand(&message, &keypair); + tx.input[0].witness = bitcoin::Witness::from_slice(&[sig.as_ref()]); +} + /// Ensure consistency IndexedTxGraph list_* and balance methods. These methods lists /// relevant txouts and utxos from the information fetched from a LocalChain. /// @@ -468,7 +499,7 @@ fn test_list_owned_txouts() { }; // tx3 spends tx2 and gives a change back in keychain_1. Confirmed at Block 2. - let tx3 = Transaction { + let mut tx3 = Transaction { input: vec![TxIn { previous_output: OutPoint::new(tx2.compute_txid(), 0), ..Default::default() @@ -479,6 +510,8 @@ fn test_list_owned_txouts() { }], ..new_tx(3) }; + // Below block 2 tx3 is unconfirmed, so its scripts are checked. + sign_tr_key_spend(&mut tx3, &tx2.output[0], DESCRIPTORS[3], 0); // tx4 is unconfirmed and pays one of our addresses, but it spends a third-party // coin we don't own. That foreign input is what makes its ancestry untrusted. diff --git a/crates/chain/tests/test_input_validation.rs b/crates/chain/tests/test_input_validation.rs new file mode 100644 index 0000000000..e4f03f9716 --- /dev/null +++ b/crates/chain/tests/test_input_validation.rs @@ -0,0 +1,1189 @@ +use bdk_chain::local_chain::LocalChain; +use bdk_chain::validation::{InputValidity, TxValidity}; +use bdk_chain::{CanonicalParams, CanonicalView, ConfirmationBlockTime, TxGraph}; +use bdk_testenv::hash; +use bitcoin::hashes::Hash; +use bitcoin::key::{Keypair, TapTweak}; +use bitcoin::script::{Builder, PushBytesBuf}; +use bitcoin::secp256k1::{Message, Secp256k1, SecretKey}; +use bitcoin::sighash::{EcdsaSighashType, Prevouts, SighashCache, TapSighashType}; +use bitcoin::taproot::{LeafVersion, TaprootBuilder}; +use bitcoin::{ + absolute, opcodes, transaction, Amount, CompressedPublicKey, OutPoint, ScriptBuf, Sequence, + TapLeafHash, Transaction, TxIn, TxOut, Txid, Witness, +}; + +#[derive(Clone, Copy)] +enum Kind { + P2wpkh, + P2pkh, + P2shMultisig, + P2trKey, + P2trScript, +} + +#[derive(Clone)] +struct Coin { + kind: Kind, + key: u8, + outpoint: OutPoint, + txout: TxOut, + /// The whole transaction creating the coin, whose txid authenticates `txout`. + parent: Transaction, +} + +fn secret(key: u8) -> SecretKey { + SecretKey::from_slice(&[key; 32]).unwrap() +} + +fn pubkey(key: u8) -> CompressedPublicKey { + CompressedPublicKey(secret(key).public_key(&Secp256k1::new())) +} + +fn keypair(key: u8) -> Keypair { + Keypair::from_secret_key(&Secp256k1::new(), &secret(key)) +} + +fn multisig_script(key: u8) -> ScriptBuf { + Builder::new() + .push_int(2) + .push_key(&pubkey(key).into()) + .push_key(&pubkey(key + 1).into()) + .push_int(2) + .push_opcode(opcodes::all::OP_CHECKMULTISIG) + .into_script() +} + +fn leaf_script(key: u8) -> ScriptBuf { + Builder::new() + .push_x_only_key(&keypair(key).x_only_public_key().0) + .push_opcode(opcodes::all::OP_CHECKSIG) + .into_script() +} + +/// The script tree's internal key is `key + 1` so that only the script path can spend. +fn tap_tree(key: u8) -> bitcoin::taproot::TaprootSpendInfo { + TaprootBuilder::new() + .add_leaf(0, leaf_script(key)) + .unwrap() + .finalize(&Secp256k1::new(), keypair(key + 1).x_only_public_key().0) + .unwrap() +} + +fn script_pubkey(kind: Kind, key: u8) -> ScriptBuf { + let secp = Secp256k1::new(); + match kind { + Kind::P2wpkh => ScriptBuf::new_p2wpkh(&pubkey(key).wpubkey_hash()), + Kind::P2pkh => ScriptBuf::new_p2pkh(&bitcoin::PublicKey::from(pubkey(key)).pubkey_hash()), + Kind::P2shMultisig => ScriptBuf::new_p2sh(&multisig_script(key).script_hash()), + Kind::P2trKey => ScriptBuf::new_p2tr(&secp, keypair(key).x_only_public_key().0, None), + Kind::P2trScript => ScriptBuf::new_p2tr_tweaked(tap_tree(key).output_key()), + } +} + +/// A coin created by a parent transaction that spends an output no graph has. +fn coin(kind: Kind, key: u8, sats: u64) -> Coin { + let parent = Transaction { + version: transaction::Version::TWO, + lock_time: absolute::LockTime::ZERO, + input: vec![TxIn { + previous_output: OutPoint::new(Txid::hash(&[kind as u8, key]), 0), + ..Default::default() + }], + output: vec![pay(kind, key, sats)], + }; + output_coin(&parent, 0, kind, key) +} + +fn output_coin(tx: &Transaction, vout: u32, kind: Kind, key: u8) -> Coin { + Coin { + kind, + key, + outpoint: OutPoint::new(tx.compute_txid(), vout), + txout: tx.output[vout as usize].clone(), + parent: tx.clone(), + } +} + +/// Builds and signs a transaction spending `coins` to `outputs`. +fn spend(coins: &[Coin], outputs: Vec) -> Transaction { + let secp = Secp256k1::new(); + let mut tx = Transaction { + version: transaction::Version::TWO, + lock_time: absolute::LockTime::ZERO, + input: coins + .iter() + .map(|c| TxIn { + previous_output: c.outpoint, + sequence: Sequence::ENABLE_RBF_NO_LOCKTIME, + ..Default::default() + }) + .collect(), + output: outputs, + }; + let prevouts = coins.iter().map(|c| c.txout.clone()).collect::>(); + let mut cache = SighashCache::new(tx.clone()); + let ecdsa = |digest: [u8; 32], key: u8| { + let sig = secp.sign_ecdsa(&Message::from_digest(digest), &secret(key)); + bitcoin::ecdsa::Signature::sighash_all(sig) + }; + for (vin, c) in coins.iter().enumerate() { + let txin = &mut tx.input[vin]; + match c.kind { + Kind::P2wpkh => { + let hash = cache + .p2wpkh_signature_hash( + vin, + &c.txout.script_pubkey, + c.txout.value, + EcdsaSighashType::All, + ) + .unwrap(); + let sig = ecdsa(hash.to_byte_array(), c.key); + txin.witness = Witness::p2wpkh(&sig, &pubkey(c.key).0); + } + Kind::P2pkh => { + let hash = cache + .legacy_signature_hash(vin, &c.txout.script_pubkey, 1) + .unwrap(); + let sig = ecdsa(hash.to_byte_array(), c.key); + txin.script_sig = Builder::new() + .push_slice(PushBytesBuf::try_from(sig.to_vec()).unwrap()) + .push_key(&pubkey(c.key).into()) + .into_script(); + } + Kind::P2shMultisig => { + let redeem = multisig_script(c.key); + let hash = cache.legacy_signature_hash(vin, &redeem, 1).unwrap(); + let sig1 = ecdsa(hash.to_byte_array(), c.key); + let sig2 = ecdsa(hash.to_byte_array(), c.key + 1); + txin.script_sig = Builder::new() + .push_int(0) + .push_slice(PushBytesBuf::try_from(sig1.to_vec()).unwrap()) + .push_slice(PushBytesBuf::try_from(sig2.to_vec()).unwrap()) + .push_slice(PushBytesBuf::try_from(redeem.to_bytes()).unwrap()) + .into_script(); + } + Kind::P2trKey => { + let hash = cache + .taproot_key_spend_signature_hash( + vin, + &Prevouts::All(&prevouts), + TapSighashType::Default, + ) + .unwrap(); + let tweaked = keypair(c.key).tap_tweak(&secp, None).to_keypair(); + let sig = secp.sign_schnorr_no_aux_rand( + &Message::from_digest(hash.to_byte_array()), + &tweaked, + ); + txin.witness = Witness::from_slice(&[sig.as_ref()]); + } + Kind::P2trScript => { + let script = leaf_script(c.key); + let leaf_hash = TapLeafHash::from_script(&script, LeafVersion::TapScript); + let hash = cache + .taproot_script_spend_signature_hash( + vin, + &Prevouts::All(&prevouts), + leaf_hash, + TapSighashType::Default, + ) + .unwrap(); + let sig = secp.sign_schnorr_no_aux_rand( + &Message::from_digest(hash.to_byte_array()), + &keypair(c.key), + ); + let control = tap_tree(c.key) + .control_block(&(script.clone(), LeafVersion::TapScript)) + .unwrap(); + txin.witness = Witness::from_slice(&[ + sig.as_ref().to_vec(), + script.to_bytes(), + control.serialize(), + ]); + } + } + } + tx +} + +fn pay(kind: Kind, key: u8, sats: u64) -> TxOut { + TxOut { + value: Amount::from_sat(sats), + script_pubkey: script_pubkey(kind, key), + } +} + +/// Spends `coin` with a signature over different outputs, so the scripts fail. +fn bad_spend(coin: &Coin, outputs: Vec) -> Transaction { + let mut other = outputs.clone(); + other[0].value += Amount::from_sat(1); + let mut tx = spend(core::slice::from_ref(coin), other); + tx.output = outputs; + tx +} + +type Graph = TxGraph; + +fn insert_coin(graph: &mut Graph, coin: &Coin) { + let _ = graph.insert_tx(coin.parent.clone()); +} + +fn chain() -> LocalChain { + LocalChain::from_blocks( + [(0, hash!("0")), (1, hash!("1")), (2, hash!("2"))] + .into_iter() + .collect(), + ) + .unwrap() +} + +fn anchor(chain: &LocalChain, height: u32) -> ConfirmationBlockTime { + ConfirmationBlockTime { + block_id: chain.get(height).unwrap().block_id(), + confirmation_time: height as u64, + } +} + +fn view( + chain: &LocalChain, + graph: &Graph, + params: CanonicalParams, +) -> CanonicalView { + chain.canonical_view(graph, chain.tip().block_id(), params) +} + +fn validity(graph: &Graph, tx: &Transaction) -> TxValidity { + graph.validate_tx(tx).validity() +} + +const VALID: InputValidity = InputValidity::Valid { + relies_on_floating: false, +}; + +const VALID_ON_FLOATING: InputValidity = InputValidity::Valid { + relies_on_floating: true, +}; + +fn inputs(graph: &Graph, tx: &Transaction) -> Vec { + graph.validate_tx(tx).inputs +} + +const KINDS: [Kind; 5] = [ + Kind::P2wpkh, + Kind::P2pkh, + Kind::P2shMultisig, + Kind::P2trKey, + Kind::P2trScript, +]; + +#[test] +fn every_spend_type_validates() { + let coins = KINDS + .iter() + .enumerate() + .map(|(i, &kind)| coin(kind, 10 * (i as u8 + 1), 10_000)) + .collect::>(); + let tx = spend(&coins, vec![pay(Kind::P2wpkh, 1, 40_000)]); + let mut graph = Graph::default(); + coins.iter().for_each(|c| insert_coin(&mut graph, c)); + assert_eq!(validity(&graph, &tx), TxValidity::Valid); + assert_eq!(inputs(&graph, &tx), vec![VALID; KINDS.len()]); + + let mut tampered = tx.clone(); + tampered.lock_time = absolute::LockTime::from_consensus(1); + assert_eq!(validity(&graph, &tampered), TxValidity::Invalid); + for (vin, validity) in inputs(&graph, &tampered).iter().enumerate() { + assert!( + matches!(validity, InputValidity::Invalid { .. }), + "input {vin}: {validity:?}" + ); + } +} + +#[test] +fn validate_tx_checks_a_tx_the_graph_does_not_have() { + let present = coin(Kind::P2trKey, 1, 10_000); + let also_present = coin(Kind::P2wpkh, 2, 10_000); + let missing = coin(Kind::P2wpkh, 3, 10_000); + let tx = spend( + &[present.clone(), also_present.clone(), missing.clone()], + vec![pay(Kind::P2wpkh, 4, 29_000)], + ); + let mut graph = Graph::default(); + insert_coin(&mut graph, &present); + insert_coin(&mut graph, &also_present); + let before = graph.clone(); + + // The taproot input signs every prevout, so it waits on the missing one too. + assert_eq!( + inputs(&graph, &tx), + vec![ + InputValidity::Unknown { + missing: vec![missing.outpoint] + }, + VALID, + InputValidity::Unknown { + missing: vec![missing.outpoint] + }, + ] + ); + assert_eq!(validity(&graph, &tx), TxValidity::Unknown); + assert_eq!(graph.get_tx(tx.compute_txid()), None); + assert_eq!(graph, before); + + // A failing input is conclusive whatever else is missing. + let mut bad = tx.clone(); + bad.input[1].witness = Witness::from_slice(&[vec![1; 9], pubkey(2).to_bytes().to_vec()]); + let per_input = inputs(&graph, &bad); + assert!(matches!(per_input[1], InputValidity::Invalid { .. })); + assert_eq!( + per_input[2], + InputValidity::Unknown { + missing: vec![missing.outpoint] + } + ); + assert_eq!(validity(&graph, &bad), TxValidity::Invalid); + + insert_coin(&mut graph, &missing); + assert_eq!(inputs(&graph, &tx), vec![VALID; 3]); +} + +#[test] +fn unconfirmed_invalid_candidate_never_wins_on_last_seen() { + let chain = chain(); + let c = coin(Kind::P2wpkh, 1, 10_000); + let good = spend(core::slice::from_ref(&c), vec![pay(Kind::P2wpkh, 2, 9_000)]); + let bad = bad_spend(&c, vec![pay(Kind::P2wpkh, 3, 9_000)]); + let (good_txid, bad_txid) = (good.compute_txid(), bad.compute_txid()); + + let mut graph = Graph::default(); + insert_coin(&mut graph, &c); + let _ = graph.insert_tx(good); + let _ = graph.insert_tx(bad); + let _ = graph.insert_seen_at(good_txid, 1); + let _ = graph.insert_seen_at(bad_txid, 100); + + let view = view(&chain, &graph, Default::default()); + assert!(view.tx(bad_txid).is_none()); + assert!(view.tx(good_txid).is_some()); + assert!(view.txout(OutPoint::new(bad_txid, 0)).is_none()); +} + +/// Confirmed transactions are out of validation's scope: an anchor in the best chain is trusted +/// as it is without validation, and so is what it pulls in. +#[test] +fn anchored_invalid_tx_and_its_ancestry_stay_canonical() { + let chain = chain(); + let c = coin(Kind::P2trKey, 1, 10_000); + let bad = bad_spend(&c, vec![pay(Kind::P2trKey, 2, 9_000)]); + let child = spend( + &[output_coin(&bad, 0, Kind::P2trKey, 2)], + vec![pay(Kind::P2wpkh, 3, 8_000)], + ); + let (bad_txid, child_txid) = (bad.compute_txid(), child.compute_txid()); + let mut graph = Graph::default(); + insert_coin(&mut graph, &c); + let _ = graph.insert_tx(bad.clone()); + let _ = graph.insert_tx(child); + assert_eq!(validity(&graph, &bad), TxValidity::Invalid); + + let mut anchored_bad = graph.clone(); + let _ = anchored_bad.insert_anchor(bad_txid, anchor(&chain, 1)); + let canonical = view(&chain, &anchored_bad, Default::default()); + assert!(canonical.tx(bad_txid).is_some()); + + let mut anchored_child = graph; + let _ = anchored_child.insert_anchor(child_txid, anchor(&chain, 2)); + let canonical = view(&chain, &anchored_child, Default::default()); + assert!(canonical.tx(child_txid).is_some()); + assert!(canonical.tx(bad_txid).is_some()); +} + +#[test] +fn assumed_invalid_tx_and_its_ancestry_stay_canonical() { + let chain = chain(); + let c = coin(Kind::P2wpkh, 1, 10_000); + let good = spend(core::slice::from_ref(&c), vec![pay(Kind::P2wpkh, 2, 9_000)]); + let bad = bad_spend(&c, vec![pay(Kind::P2wpkh, 3, 9_000)]); + let child = spend( + &[output_coin(&bad, 0, Kind::P2wpkh, 3)], + vec![pay(Kind::P2wpkh, 4, 8_000)], + ); + let [good_txid, bad_txid, child_txid] = [&good, &bad, &child].map(|tx| tx.compute_txid()); + let mut graph = Graph::default(); + insert_coin(&mut graph, &c); + for tx in [good, bad, child] { + let _ = graph.insert_tx(tx); + } + let _ = graph.insert_seen_at(good_txid, 100); + let _ = graph.insert_seen_at(bad_txid, 1); + + for assumed in [bad_txid, child_txid] { + let params = CanonicalParams { + assume_canonical: vec![assumed], + }; + let view = view(&chain, &graph, params); + assert!(view.tx(bad_txid).is_some(), "assuming {assumed}"); + assert!(view.tx(good_txid).is_none(), "assuming {assumed}"); + } +} + +/// A wallet's pending spend of a confirmed or assumed transaction must stay in view even if the +/// parent is one the verifier calls Invalid: only the child is unconfirmed, and it is valid. +#[test] +fn unconfirmed_spend_of_a_trusted_invalid_tx_stays_canonical() { + let chain = chain(); + let c = coin(Kind::P2trKey, 1, 10_000); + let bad = bad_spend(&c, vec![pay(Kind::P2trKey, 2, 9_000)]); + let child = spend( + &[output_coin(&bad, 0, Kind::P2trKey, 2)], + vec![pay(Kind::P2wpkh, 3, 8_000)], + ); + let (bad_txid, child_txid) = (bad.compute_txid(), child.compute_txid()); + let mut graph = Graph::default(); + insert_coin(&mut graph, &c); + let _ = graph.insert_tx(bad.clone()); + let _ = graph.insert_tx(child.clone()); + let _ = graph.insert_seen_at(child_txid, 5); + assert_eq!(validity(&graph, &bad), TxValidity::Invalid); + assert_eq!(validity(&graph, &child), TxValidity::Valid); + + let mut anchored = graph.clone(); + let _ = anchored.insert_anchor(bad_txid, anchor(&chain, 1)); + let assumed = CanonicalParams { + assume_canonical: vec![bad_txid], + }; + for (graph, params) in [(&anchored, CanonicalParams::default()), (&graph, assumed)] { + let canonical = view(&chain, graph, params); + assert!(canonical.tx(bad_txid).is_some()); + assert!(canonical.tx(child_txid).is_some()); + } +} + +#[test] +fn descendants_of_an_unconfirmed_invalid_tx_are_not_canonical() { + let chain = chain(); + let c = coin(Kind::P2wpkh, 1, 10_000); + let parent = bad_spend(&c, vec![pay(Kind::P2wpkh, 2, 9_000)]); + let child = spend( + &[output_coin(&parent, 0, Kind::P2wpkh, 2)], + vec![pay(Kind::P2wpkh, 3, 8_000)], + ); + let grandchild = spend( + &[output_coin(&child, 0, Kind::P2wpkh, 3)], + vec![pay(Kind::P2wpkh, 4, 7_000)], + ); + let honest = spend(core::slice::from_ref(&c), vec![pay(Kind::P2wpkh, 5, 9_000)]); + let txs = [&parent, &child, &grandchild, &honest]; + let txids = txs.map(|tx| tx.compute_txid()); + + let mut graph = Graph::default(); + insert_coin(&mut graph, &c); + for tx in txs { + let _ = graph.insert_tx(tx.clone()); + } + // The descendants are seen last, so only validation keeps them out. + let _ = graph.insert_seen_at(txids[2], 60); + let _ = graph.insert_seen_at(txids[1], 50); + let _ = graph.insert_seen_at(txids[3], 1); + + // Script validity is per transaction; ancestry is canonicalization's concern. + assert_eq!(validity(&graph, &parent), TxValidity::Invalid); + assert_eq!(validity(&graph, &child), TxValidity::Valid); + assert_eq!(validity(&graph, &grandchild), TxValidity::Valid); + + let view = view(&chain, &graph, Default::default()); + for excluded in &txids[..3] { + assert!(view.tx(*excluded).is_none()); + } + assert!(view.tx(txids[3]).is_some()); + let outpoints = txids.iter().map(|&txid| ((), OutPoint::new(txid, 0))); + let utxos = view + .filter_unspent_outpoints(outpoints) + .map(|(_, txo)| txo.outpoint.txid) + .collect::>(); + assert_eq!(utxos, vec![txids[3]]); +} + +/// An anchor in a block the best chain no longer has confirms nothing, so the transaction is +/// unconfirmed again and checked as one, whether or not it was ever seen in the mempool. +#[test] +fn reorged_out_tx_is_checked_again() { + let chain = chain(); + let reorged = LocalChain::from_blocks( + [(0, hash!("0")), (1, hash!("1'")), (2, hash!("2'"))] + .into_iter() + .collect(), + ) + .unwrap(); + let c = coin(Kind::P2wpkh, 1, 10_000); + let other = coin(Kind::P2wpkh, 2, 10_000); + let bad = bad_spend(&c, vec![pay(Kind::P2wpkh, 3, 9_000)]); + let good = spend( + core::slice::from_ref(&other), + vec![pay(Kind::P2wpkh, 4, 9_000)], + ); + let (bad_txid, good_txid) = (bad.compute_txid(), good.compute_txid()); + + for seen in [false, true] { + let mut graph = Graph::default(); + insert_coin(&mut graph, &c); + insert_coin(&mut graph, &other); + for tx in [&bad, &good] { + let _ = graph.insert_tx(tx.clone()); + let _ = graph.insert_anchor(tx.compute_txid(), anchor(&chain, 1)); + if seen { + let _ = graph.insert_seen_at(tx.compute_txid(), 1); + } + } + let before = view(&chain, &graph, Default::default()); + assert!(before.tx(bad_txid).is_some(), "seen: {seen}"); + + let after = view(&reorged, &graph, Default::default()); + assert!(after.tx(bad_txid).is_none(), "seen: {seen}"); + assert!(after.tx(good_txid).is_some(), "seen: {seen}"); + } +} + +#[test] +fn unknown_tx_stays_eligible() { + let chain = chain(); + let c = coin(Kind::P2trKey, 1, 10_000); + let tx = spend(core::slice::from_ref(&c), vec![pay(Kind::P2wpkh, 2, 9_000)]); + let txid = tx.compute_txid(); + let mut graph = Graph::default(); + let _ = graph.insert_tx(tx.clone()); + let _ = graph.insert_seen_at(txid, 1); + assert_eq!( + inputs(&graph, &tx), + vec![InputValidity::Unknown { + missing: vec![c.outpoint] + }] + ); + let view = view(&chain, &graph, Default::default()); + assert!(view.tx(txid).is_some()); + assert!(view.txout(OutPoint::new(txid, 0)).is_some()); +} + +/// Nothing is remembered between canonicalizations, so a verdict always reflects the graph as it +/// is when canonicalizing. +#[test] +fn a_bad_spend_is_excluded_once_its_parent_arrives() { + let chain = chain(); + let c = coin(Kind::P2wpkh, 1, 10_000); + let bad = bad_spend(&c, vec![pay(Kind::P2wpkh, 3, 9_000)]); + let txid = bad.compute_txid(); + + let mut graph = Graph::default(); + let _ = graph.insert_tx(bad.clone()); + let _ = graph.insert_seen_at(txid, 1); + assert_eq!(validity(&graph, &bad), TxValidity::Unknown); + assert!(view(&chain, &graph, Default::default()).tx(txid).is_some()); + + insert_coin(&mut graph, &c); + assert_eq!(validity(&graph, &bad), TxValidity::Invalid); + assert!(view(&chain, &graph, Default::default()).tx(txid).is_none()); +} + +/// A wallet output whose receiving transaction the graph lacks is known only as the chain source's +/// floating txout, and a forged spend of it cannot carry a valid signature for the wallet's script. +#[test] +fn an_invalid_spend_of_an_output_known_only_as_a_floating_txout_is_excluded() { + let chain = chain(); + let c = coin(Kind::P2wpkh, 1, 10_000); + let good = spend(core::slice::from_ref(&c), vec![pay(Kind::P2wpkh, 2, 9_000)]); + let bad = bad_spend(&c, vec![pay(Kind::P2wpkh, 3, 9_000)]); + let (good_txid, bad_txid) = (good.compute_txid(), bad.compute_txid()); + + let mut graph = Graph::default(); + let _ = graph.insert_txout(c.outpoint, c.txout.clone()); + let _ = graph.insert_tx(good.clone()); + let _ = graph.insert_tx(bad.clone()); + let _ = graph.insert_seen_at(good_txid, 1); + let _ = graph.insert_seen_at(bad_txid, 100); + + assert_eq!(inputs(&graph, &good), vec![VALID_ON_FLOATING]); + assert!(matches!( + inputs(&graph, &bad)[..], + [InputValidity::Invalid { + relies_on_floating: true, + .. + }] + )); + let view = view(&chain, &graph, Default::default()); + assert!(view.tx(bad_txid).is_none()); + assert!(view.tx(good_txid).is_some()); +} + +/// A taproot signature's hash commits to every spent output unless it is ANYONECANPAY, so only then +/// does the input's verdict rest on the other inputs' prevouts. +#[test] +fn taproot_provenance_follows_the_prevouts_its_sighash_read() { + let tap = coin(Kind::P2trKey, 1, 10_000); + let seg = coin(Kind::P2wpkh, 2, 10_000); + let tx = spend( + &[tap.clone(), seg.clone()], + vec![pay(Kind::P2wpkh, 3, 19_000)], + ); + let mut graph = Graph::default(); + insert_coin(&mut graph, &tap); + let _ = graph.insert_txout(seg.outpoint, seg.txout.clone()); + assert_eq!(inputs(&graph, &tx), vec![VALID_ON_FLOATING; 2]); + + // A signature that fails before any hash is computed read only its own prevout. + let mut early = tx.clone(); + early.input[0].witness = Witness::from_slice(&[vec![1; 9]]); + assert!(matches!( + inputs(&graph, &early)[..], + [ + InputValidity::Invalid { + relies_on_floating: false, + .. + }, + VALID_ON_FLOATING + ] + )); + + let secp = Secp256k1::new(); + let mut acp = tx.clone(); + let hash = SighashCache::new(&acp) + .taproot_key_spend_signature_hash( + 0, + &Prevouts::One(0, &tap.txout), + TapSighashType::AllPlusAnyoneCanPay, + ) + .unwrap(); + let tweaked = keypair(tap.key).tap_tweak(&secp, None).to_keypair(); + let sig = secp.sign_schnorr_no_aux_rand(&Message::from_digest(hash.to_byte_array()), &tweaked); + let mut sig = sig.as_ref().to_vec(); + sig.push(TapSighashType::AllPlusAnyoneCanPay as u8); + acp.input[0].witness = Witness::from_slice(&[sig]); + assert_eq!(inputs(&graph, &acp), vec![VALID, VALID_ON_FLOATING]); +} + +#[test] +fn shared_hashes_do_not_leak_floating_provenance_between_inputs() { + let coins = [ + coin(Kind::P2trKey, 1, 10_000), + coin(Kind::P2trKey, 2, 10_000), + coin(Kind::P2wpkh, 3, 10_000), + coin(Kind::P2pkh, 4, 10_000), + ]; + let mut tx = spend(&coins, vec![pay(Kind::P2wpkh, 5, 39_000)]); + let secp = Secp256k1::new(); + let ty = TapSighashType::AllPlusAnyoneCanPay; + let hash = SighashCache::new(&tx) + .taproot_key_spend_signature_hash(1, &Prevouts::One(1, &coins[1].txout), ty) + .unwrap(); + let tweaked = keypair(coins[1].key).tap_tweak(&secp, None).to_keypair(); + let sig = bitcoin::taproot::Signature { + signature: secp + .sign_schnorr_no_aux_rand(&Message::from_digest(hash.to_byte_array()), &tweaked), + sighash_type: ty, + }; + tx.input[1].witness = Witness::from_slice(&[sig.to_vec()]); + + for wrong in [false, true] { + let mut graph = Graph::default(); + let mut floating = coins[0].txout.clone(); + if wrong { + floating.value += Amount::from_sat(1); + } + let _ = graph.insert_txout(coins[0].outpoint, floating); + for coin in &coins[1..3] { + insert_coin(&mut graph, coin); + } + let incomplete = inputs(&graph, &tx); + assert_eq!( + incomplete[0], + InputValidity::Unknown { + missing: vec![coins[3].outpoint] + } + ); + assert_eq!(incomplete[1..3], [VALID, VALID]); + assert_eq!( + incomplete[3], + InputValidity::Unknown { + missing: vec![coins[3].outpoint] + } + ); + + insert_coin(&mut graph, &coins[3]); + let complete = inputs(&graph, &tx); + if wrong { + assert!(matches!( + complete[0], + InputValidity::Invalid { + relies_on_floating: true, + .. + } + )); + } else { + assert_eq!(complete[0], VALID_ON_FLOATING); + } + assert_eq!(complete[1..], [VALID, VALID, VALID]); + } +} + +/// Compare the old per-input cache lifetime with the graph's shared verification path. +/// Signing and graph construction are excluded; timings are evidence, not pass/fail thresholds. +#[test] +#[ignore = "manual release-mode signature-hash scaling measurement"] +#[allow(clippy::print_stdout)] // This ignored benchmark reports timings for manual comparison. +fn signature_hash_scaling() { + use std::{hint::black_box, time::Instant}; + for (label, kind) in [("segwit-v0", Kind::P2wpkh), ("taproot", Kind::P2trKey)] { + for n in [1, 16, 128, 1024] { + let coins = (0..n) + .map(|i| coin(kind, 1, 10_000 + i as u64)) + .collect::>(); + let tx = spend(&coins, vec![pay(Kind::P2wpkh, 2, 1_000)]); + let prevouts = coins + .iter() + .map(|coin| Some(&coin.txout)) + .collect::>(); + let mut graph = Graph::default(); + for coin in &coins { + insert_coin(&mut graph, coin); + } + assert_eq!(validity(&graph, &tx), TxValidity::Valid); + for vin in 0..n { + assert!(bdk_script_verify::verify_input( + &mut bdk_script_verify::VerificationCache::new(&tx, &prevouts), + vin, + bdk_script_verify::Flags::CONSENSUS + ) + .result + .is_ok()); + } + let rounds = if n < 128 { 100 } else { 10 }; + let start = Instant::now(); + for _ in 0..rounds { + for vin in 0..n { + black_box(bdk_script_verify::verify_input( + &mut bdk_script_verify::VerificationCache::new( + black_box(&tx), + black_box(&prevouts), + ), + vin, + bdk_script_verify::Flags::CONSENSUS, + )); + } + } + let independent = start.elapsed().as_secs_f64() * 1e6 / f64::from(rounds); + let start = Instant::now(); + for _ in 0..rounds { + black_box(graph.validate_tx(black_box(&tx))); + } + let shared = start.elapsed().as_secs_f64() * 1e6 / f64::from(rounds); + std::println!("{label}: inputs={n}, independent_us={independent:.1}, graph_shared_us={shared:.1}, speedup={:.2}", independent / shared); + } + } +} + +/// Floating data that a check read before stopping on a missing prevout gave no verdict to rely on. +#[test] +fn an_unknown_input_relies_on_nothing() { + let tap = coin(Kind::P2trKey, 1, 10_000); + let seg = coin(Kind::P2wpkh, 2, 10_000); + let tx = spend( + &[tap.clone(), seg.clone()], + vec![pay(Kind::P2wpkh, 3, 19_000)], + ); + let mut graph = Graph::default(); + let _ = graph.insert_txout(tap.outpoint, tap.txout.clone()); + let unknown = InputValidity::Unknown { + missing: vec![seg.outpoint], + }; + assert_eq!(inputs(&graph, &tx), vec![unknown.clone(), unknown]); + assert_eq!(validity(&graph, &tx), TxValidity::Unknown); + assert!(!graph.validate_tx(&tx).relies_on_floating()); +} + +#[test] +fn a_full_parent_takes_precedence_over_a_floating_txout() { + let c = coin(Kind::P2wpkh, 1, 10_000); + let tx = spend(core::slice::from_ref(&c), vec![pay(Kind::P2wpkh, 2, 9_000)]); + let mut wrong = c.txout.clone(); + wrong.value += Amount::from_sat(1); + + // A lying amount fails a genuine segwit signature, and the verdict says whose word it took. + let mut graph = Graph::default(); + let _ = graph.insert_txout(c.outpoint, wrong); + assert!(matches!( + inputs(&graph, &tx)[..], + [InputValidity::Invalid { + relies_on_floating: true, + .. + }] + )); + assert!(graph.validate_tx(&tx).relies_on_floating()); + + insert_coin(&mut graph, &c); + assert_eq!(inputs(&graph, &tx), vec![VALID]); + assert!(!graph.validate_tx(&tx).relies_on_floating()); +} + +#[test] +fn aggregate_and_per_input_validity() { + let signed = coin(Kind::P2wpkh, 1, 10_000); + let missing = coin(Kind::P2wpkh, 2, 10_000); + let mut tx = spend( + &[signed.clone(), missing.clone()], + vec![pay(Kind::P2wpkh, 3, 19_000)], + ); + let mut graph = Graph::default(); + insert_coin(&mut graph, &signed); + assert_eq!(validity(&graph, &tx), TxValidity::Unknown); + assert_eq!( + inputs(&graph, &tx), + vec![ + VALID, + InputValidity::Unknown { + missing: vec![missing.outpoint] + }, + ] + ); + + // One failing input makes the transaction invalid even while another is unknown. + tx.input[0].witness = Witness::from_slice(&[vec![1; 9], pubkey(1).to_bytes().to_vec()]); + assert_eq!(validity(&graph, &tx), TxValidity::Invalid); + let per_input = inputs(&graph, &tx); + assert!(matches!(per_input[0], InputValidity::Invalid { .. })); + assert_eq!( + per_input[1], + InputValidity::Unknown { + missing: vec![missing.outpoint] + } + ); +} + +fn graph_with(txs: &[&Transaction]) -> Graph { + let mut graph = Graph::default(); + for tx in txs { + let _ = graph.insert_tx((*tx).clone()); + } + graph +} + +/// A valid spend of `c`, plus bogus copies whose witnesses are smaller than the valid one's and +/// sort below and above each other. +fn valid_and_bogus_copies(c: &Coin) -> (Transaction, [Transaction; 2]) { + let valid = spend(core::slice::from_ref(c), vec![pay(Kind::P2wpkh, 2, 9_000)]); + let bogus = |fill: u8| { + let mut tx = valid.clone(); + tx.input[0].witness = Witness::from_slice(&[vec![fill; 9], pubkey(1).to_bytes().to_vec()]); + tx + }; + let bogus = [bogus(0x00), bogus(0xff)]; + (valid, bogus) +} + +/// A source must not hide a real payment by supplying a smaller junk witness for the same txid. +#[test] +fn junk_witness_cannot_exclude_a_valid_transaction_or_its_descendants() { + let chain = chain(); + let c = coin(Kind::P2wpkh, 1, 10_000); + let (valid, [bogus, _]) = valid_and_bogus_copies(&c); + let txid = valid.compute_txid(); + let child = spend( + &[output_coin(&valid, 0, Kind::P2wpkh, 2)], + vec![pay(Kind::P2wpkh, 3, 8_000)], + ); + for parent_seen in [false, true] { + let mut graph = graph_with(&[&c.parent, &valid, &child]); + let _ = graph.insert_seen_at(child.compute_txid(), 1); + if parent_seen { + let _ = graph.insert_seen_at(txid, 2); + } + let before = view(&chain, &graph, Default::default()); + assert_eq!(*before.tx(txid).unwrap().tx, valid); + assert!(before.tx(child.compute_txid()).is_some()); + + let changeset = graph.insert_tx(bogus.clone()); + assert!(changeset.txs.contains(&bogus)); + assert_eq!(*graph.get_tx(txid).unwrap(), bogus); + let after = view(&chain, &graph, Default::default()); + assert_eq!(*after.tx(txid).unwrap().tx, valid); + assert_eq!(*after.tx(child.compute_txid()).unwrap().tx, child); + } +} + +#[test] +fn all_invalid_copies_are_excluded_unless_anchored_or_assumed() { + let chain = chain(); + let c = coin(Kind::P2wpkh, 1, 10_000); + let (valid, bogus) = valid_and_bogus_copies(&c); + let txid = valid.compute_txid(); + let child = spend( + &[output_coin(&valid, 0, Kind::P2wpkh, 2)], + vec![pay(Kind::P2wpkh, 3, 8_000)], + ); + let child_txid = child.compute_txid(); + let mut graph = graph_with(&[&c.parent, &bogus[0], &bogus[1], &child]); + let _ = graph.insert_seen_at(txid, 1); + let _ = graph.insert_seen_at(child_txid, 2); + let canonical = view(&chain, &graph, Default::default()); + assert!(canonical.tx(txid).is_none()); + assert!(canonical.tx(child_txid).is_none()); + + // Bypassed ancestry must keep the graph's exact preferred copy, even with a valid alternative. + let _ = graph.insert_tx(valid.clone()); + let preferred = graph.get_tx(txid).unwrap(); + assert_ne!(*preferred, valid); + let assumed = view( + &chain, + &graph, + CanonicalParams { + assume_canonical: vec![child_txid], + }, + ); + assert_eq!(assumed.tx(txid).unwrap().tx, preferred); + let _ = graph.insert_anchor(child_txid, anchor(&chain, 1)); + let anchored = view(&chain, &graph, Default::default()); + assert_eq!(anchored.tx(txid).unwrap().tx, preferred); +} + +#[test] +fn unknown_copy_survives_an_invalid_alternative_and_is_rechecked_with_parents() { + let coins = [coin(Kind::P2wpkh, 1, 10_000), coin(Kind::P2wpkh, 3, 10_000)]; + let signed = spend(&coins, vec![pay(Kind::P2wpkh, 5, 19_000)]); + let txid = signed.compute_txid(); + let mut bogus = signed.clone(); + bogus.input[0].witness = Witness::from_slice(&[vec![0; 9], pubkey(1).to_bytes().to_vec()]); + let mut graph = graph_with(&[&coins[0].parent, &signed, &bogus]); + let _ = graph.insert_seen_at(txid, 1); + assert_eq!(validity(&graph, &signed), TxValidity::Unknown); + assert_eq!(validity(&graph, &bogus), TxValidity::Invalid); + assert_eq!( + *view(&chain(), &graph, Default::default()) + .tx(txid) + .unwrap() + .tx, + signed + ); + + insert_coin(&mut graph, &coins[1]); + assert_eq!(validity(&graph, &signed), TxValidity::Valid); + assert_eq!( + *view(&chain(), &graph, Default::default()) + .tx(txid) + .unwrap() + .tx, + signed + ); +} + +#[test] +fn eligible_copies_use_deterministic_witness_order() { + let script = Builder::new() + .push_opcode(opcodes::all::OP_DROP) + .push_int(1) + .into_script(); + let mut c = coin(Kind::P2wpkh, 1, 10_000); + c.parent.output[0].script_pubkey = script.to_p2wsh(); + let tx = Transaction { + version: transaction::Version::TWO, + lock_time: absolute::LockTime::ZERO, + input: vec![TxIn { + previous_output: OutPoint::new(c.parent.compute_txid(), 0), + ..Default::default() + }], + output: vec![pay(Kind::P2wpkh, 2, 9_000)], + }; + let copies = [vec![0; 2], vec![1], vec![2]].map(|data| { + let mut copy = tx.clone(); + copy.input[0].witness = Witness::from_slice(&[data, script.as_bytes().to_vec()]); + copy + }); + let expected = copies[1..] + .iter() + .min_by_key(|tx| tx.compute_wtxid()) + .unwrap(); + for with_parent in [false, true] { + for order in [[0, 1, 2], [2, 0, 1], [1, 2, 0]] { + let mut graph = Graph::default(); + if with_parent { + let _ = graph.insert_tx(c.parent.clone()); + } + for i in order { + let _ = graph.insert_tx(copies[i].clone()); + assert_eq!( + validity(&graph, &copies[i]), + if with_parent { + TxValidity::Valid + } else { + TxValidity::Unknown + } + ); + } + let _ = graph.insert_seen_at(tx.compute_txid(), 1); + let canonical = view(&chain(), &graph, Default::default()); + assert_eq!(*canonical.tx(tx.compute_txid()).unwrap().tx, *expected); + } + } +} + +#[test] +fn every_arrival_order_resolves_to_the_same_copy() { + let c = coin(Kind::P2wpkh, 1, 10_000); + let (valid, bogus) = valid_and_bogus_copies(&c); + let txid = valid.compute_txid(); + let items = [&c.parent, &valid, &bogus[0], &bogus[1]]; + let mut expected = None; + for a in 0..4 { + for b in (0..4).filter(|&b| b != a) { + for c_ in (0..4).filter(|&c_| c_ != a && c_ != b) { + let d = 6 - a - b - c_; + let mut graph = graph_with(&[items[a], items[b], items[c_], items[d]]); + let _ = graph.insert_seen_at(txid, 1); + assert_eq!(graph.tx_copies(txid).count(), 3); + assert_eq!( + *view(&chain(), &graph, Default::default()) + .tx(txid) + .unwrap() + .tx, + valid + ); + assert_eq!(*expected.get_or_insert_with(|| graph.clone()), graph); + let mut reloaded = Graph::default(); + reloaded.apply_changeset(graph.initial_changeset()); + assert_eq!( + *view(&chain(), &reloaded, Default::default()) + .tx(txid) + .unwrap() + .tx, + valid + ); + } + } + } +} + +#[test] +fn copies_are_never_spliced() { + let coins = [coin(Kind::P2wpkh, 1, 10_000), coin(Kind::P2wpkh, 3, 10_000)]; + let signed = spend(&coins, vec![pay(Kind::P2wpkh, 5, 19_000)]); + let txid = signed.compute_txid(); + let partial = |vin: usize| { + let mut tx = signed.clone(); + tx.input[vin].witness = Witness::new(); + tx + }; + let (first_signed, second_signed) = (partial(1), partial(0)); + let received = [first_signed.clone(), second_signed.clone()]; + + let mut graph = Graph::default(); + coins.iter().for_each(|c| insert_coin(&mut graph, c)); + let _ = graph.insert_tx(first_signed.clone()); + let _ = graph.insert_tx(second_signed.clone()); + let chosen = graph.get_tx(txid).unwrap(); + assert!(received.contains(&chosen)); + assert_eq!(validity(&graph, &chosen), TxValidity::Invalid); + let copies = graph.tx_copies(txid).collect::>(); + assert_eq!(copies.len(), 2); + assert!(copies.iter().all(|copy| received.contains(copy))); + + let _ = graph.insert_tx(signed.clone()); + assert_eq!(*graph.get_tx(txid).unwrap(), signed); + assert_eq!(graph.tx_copies(txid).count(), 3); +} + +#[test] +fn tx_update_has_one_copy_per_txid() { + let c = coin(Kind::P2wpkh, 1, 10_000); + let (valid, bogus) = valid_and_bogus_copies(&c); + let txid = valid.compute_txid(); + let graph = graph_with(&[&bogus[0], &valid, &bogus[1], &c.parent]); + assert_eq!(graph.tx_copies(txid).count(), 3); + let chosen = graph.get_tx(txid).unwrap(); + let update = bdk_chain::TxUpdate::from(graph); + let txs = update + .txs + .iter() + .filter(|tx| tx.compute_txid() == txid) + .collect::>(); + assert_eq!(txs, vec![&chosen]); +} + +#[cfg(feature = "rusqlite")] +#[test] +fn every_copy_survives_sqlite() { + type ChangeSet = bdk_chain::tx_graph::ChangeSet; + let c = coin(Kind::P2wpkh, 1, 10_000); + let (valid, [bogus, _]) = valid_and_bogus_copies(&c); + + let mut graph = Graph::default(); + let mut conn = rusqlite::Connection::open_in_memory().unwrap(); + let db = conn.transaction().unwrap(); + ChangeSet::init_sqlite_tables(&db).unwrap(); + for tx in [c.parent.clone(), valid, bogus] { + graph.insert_tx(tx).persist_to_sqlite(&db).unwrap(); + } + + let mut reloaded = Graph::default(); + reloaded.apply_changeset(ChangeSet::from_sqlite(&db).unwrap()); + assert_eq!(reloaded, graph); + + db.commit().unwrap(); + + let mut conn = rusqlite::Connection::open_in_memory().unwrap(); + let fresh = conn.transaction().unwrap(); + ChangeSet::init_sqlite_tables(&fresh).unwrap(); + fresh + .execute_batch("DELETE FROM bdk_tx_copies; DELETE FROM bdk_txs;") + .unwrap(); + graph.initial_changeset().persist_to_sqlite(&fresh).unwrap(); + let mut reloaded = Graph::default(); + reloaded.apply_changeset(ChangeSet::from_sqlite(&fresh).unwrap()); + assert_eq!(reloaded, graph); +} + +#[cfg(feature = "rusqlite")] +#[test] +fn copies_staged_together_survive_a_reload() { + use bdk_chain::Merge; + type ChangeSet = bdk_chain::tx_graph::ChangeSet; + let c = coin(Kind::P2wpkh, 1, 10_000); + let (valid, bogus_copies) = valid_and_bogus_copies(&c); + for bogus in bogus_copies { + let mut graph = Graph::default(); + let mut staged = graph.insert_tx(bogus.clone()); + staged.merge(graph.insert_tx(c.parent.clone())); + staged.merge(graph.insert_tx(valid.clone())); + staged.merge(graph.insert_seen_at(valid.compute_txid(), 1)); + + let mut conn = rusqlite::Connection::open_in_memory().unwrap(); + let db = conn.transaction().unwrap(); + ChangeSet::init_sqlite_tables(&db).unwrap(); + staged.persist_to_sqlite(&db).unwrap(); + + let mut reloaded = Graph::default(); + reloaded.apply_changeset(ChangeSet::from_sqlite(&db).unwrap()); + assert_eq!(reloaded, graph); + assert_eq!( + *view(&chain(), &reloaded, Default::default()) + .tx(valid.compute_txid()) + .unwrap() + .tx, + valid + ); + + let mut parent_last = Graph::default(); + let mut changeset = reloaded.initial_changeset(); + changeset + .txs + .retain(|tx| tx.compute_txid() != c.parent.compute_txid()); + parent_last.apply_changeset(changeset); + let _ = parent_last.insert_tx(c.parent.clone()); + assert_eq!( + *view(&chain(), &parent_last, Default::default()) + .tx(valid.compute_txid()) + .unwrap() + .tx, + valid + ); + } +} diff --git a/crates/chain/tests/test_tx_graph.rs b/crates/chain/tests/test_tx_graph.rs index 24b7b947de..ffdb0a0eba 100644 --- a/crates/chain/tests/test_tx_graph.rs +++ b/crates/chain/tests/test_tx_graph.rs @@ -348,14 +348,26 @@ fn insert_tx_witness_precedence() { assert_eq!( changeset_insert_signed, ChangeSet { - txs: [Arc::new(signed_tx)].into(), + txs: [Arc::new(signed_tx.clone())].into(), ..Default::default() } ); - assert!(changeset_insert_unsigned.is_empty()); + // The graph records every copy, the losing one too. + assert_eq!( + changeset_insert_unsigned, + ChangeSet { + txs: [Arc::new(unsigned_tx.clone())].into(), + ..Default::default() + } + ); + assert_eq!( + *tx_graph.get_tx(signed_tx.compute_txid()).unwrap(), + signed_tx + ); } - // Smaller witness displaces larger witness and witnesses must not get cleared. + // A copy with an empty witness does not displace a fully witnessed one, even if its other + // witnesses are smaller, and witnesses are never mixed across copies. { let previous_output_2 = OutPoint::new(hash!("prev"), 3); let small_wit = Witness::from_slice(&[vec![0u8; 10]]); @@ -401,7 +413,7 @@ fn insert_tx_witness_precedence() { let mut tx_graph = TxGraph::::default(); let changeset_small = tx_graph.insert_tx(tx_small.clone()); - let changeset_large = tx_graph.insert_tx(tx_large); + let changeset_large = tx_graph.insert_tx(tx_large.clone()); assert_eq!( changeset_small, ChangeSet { @@ -409,7 +421,13 @@ fn insert_tx_witness_precedence() { ..Default::default() } ); - assert!(changeset_large.is_empty()); + assert_eq!( + changeset_large, + ChangeSet { + txs: [Arc::new(tx_large)].into(), + ..Default::default() + } + ); let tx = tx_graph .get_tx(tx_small.compute_txid()) .expect("tx must exist"); @@ -942,17 +960,18 @@ fn test_chain_spends() { let tip = local_chain.tip(); // The parent tx contains 2 outputs. Which are spent by one confirmed and one unconfirmed tx. - // The parent tx is confirmed at block 95. + // The parent tx is confirmed at block 95. The unconfirmed spend's scripts are checked, so the + // outputs are anyone-can-spend. let tx_0 = Transaction { input: vec![], output: vec![ TxOut { value: Amount::from_sat(10_000), - script_pubkey: ScriptBuf::new(), + script_pubkey: ScriptBuf::from_bytes(vec![bitcoin::opcodes::OP_TRUE.to_u8()]), }, TxOut { value: Amount::from_sat(20_000), - script_pubkey: ScriptBuf::new(), + script_pubkey: ScriptBuf::from_bytes(vec![bitcoin::opcodes::OP_TRUE.to_u8()]), }, ], ..new_tx(0) diff --git a/crates/chain/tests/test_tx_graph_conflicts.rs b/crates/chain/tests/test_tx_graph_conflicts.rs index 2f6f1fd6bb..5f2c8149b2 100644 --- a/crates/chain/tests/test_tx_graph_conflicts.rs +++ b/crates/chain/tests/test_tx_graph_conflicts.rs @@ -111,12 +111,12 @@ fn test_tx_conflict_handling() { ..Default::default() }, ], - // the txgraph is going to pick tx_conflict_2 because of higher lexicographical txid - exp_chain_txs: HashSet::from(["tx1", "tx_conflict_2"]), - exp_chain_txouts: HashSet::from([("tx1", 0), ("tx_conflict_2", 0)]), - exp_unspents: HashSet::from([("tx_conflict_2", 0)]), + // the txgraph is going to pick tx_conflict_1 because of higher lexicographical txid + exp_chain_txs: HashSet::from(["tx1", "tx_conflict_1"]), + exp_chain_txouts: HashSet::from([("tx1", 0), ("tx_conflict_1", 0)]), + exp_unspents: HashSet::from([("tx_conflict_1", 0)]), exp_balance: Balance { - trusted_pending: Amount::from_sat(30000), + trusted_pending: Amount::from_sat(20000), ..Default::default() }, }, diff --git a/crates/script_verify/Cargo.toml b/crates/script_verify/Cargo.toml new file mode 100644 index 0000000000..292bc970d8 --- /dev/null +++ b/crates/script_verify/Cargo.toml @@ -0,0 +1,30 @@ +[package] +name = "bdk_script_verify" +version = "0.1.0" +edition = "2021" +rust-version = "1.85.0" +homepage = "https://bitcoindevkit.org" +repository = "https://github.com/bitcoindevkit/bdk" +documentation = "https://docs.rs/bdk_script_verify" +description = "Pure-Rust Bitcoin input script and witness verification." +license = "MIT OR Apache-2.0" +readme = "README.md" + +[lints] +workspace = true + +[dependencies] +bitcoin = { version = "0.32.0", default-features = false } + +[dev-dependencies] +bitcoin = { version = "0.32.0", features = ["rand-std"] } +bitcoinkernel = "=0.3.2" +# Pinned because this crate vendors the Core source the oracle runs, which the tests name. +libbitcoinkernel-sys = "=0.4.1" +miniscript = { version = "13.0.0", features = ["compiler"] } +proptest = "1.2.0" +serde_json = "1" + +[features] +default = ["std"] +std = ["bitcoin/std"] diff --git a/crates/script_verify/README.md b/crates/script_verify/README.md new file mode 100644 index 0000000000..c9f37190e4 --- /dev/null +++ b/crates/script_verify/README.md @@ -0,0 +1,46 @@ +# BDK Script Verify + +Pure-Rust verification of a transaction input's scriptSig, scriptPubKey and witness against the +output it spends, under Bitcoin's consensus script rules (P2SH, BIP66, BIP65, BIP112, BIP141, +BIP143, BIP147, BIP341 and BIP342). + +It checks scripts only. It does not check amounts, locktimes against the chain, double spends or +any other transaction or block rule. + +Use `verify_transaction` to check every input of a transaction. It shares signature-hash caches +and lazily prepared Taproot prevouts within that call, while returning a separate result and +prevout-dependency report for each input. To control input order or verify only selected inputs, +create a `VerificationCache` and pass it to `verify_input` for each check. The cache borrows immutable +transaction and prevout data and cannot be reused with another witness copy or changed prevouts. +The whole-transaction API discards its cache when it returns. This avoids repeated transaction-wide +hashing for segwit and Taproot. +Legacy signature hashes and scripts with input-specific work retain their own costs. + +## Provenance + +These parts are adapted from [rbitcoin](https://github.com/reardencode/rbitcoin) at commit +`49210ea82130474a4df366e253d1346a16369fdb`: + +| Here | From rbitcoin | +|------|---------------| +| `src/interpreter.rs` (the interpreter) | `crates/rbitcoin-consensus/src/script/interpreter.rs` | +| `src/crypto.rs` (signature encoding checks, ECDSA verify) | the `crypto` module of `crates/rbitcoin-consensus/src/script/mod.rs` | +| `src/sighash.rs` (raw-hashtype BIP143) | `bip143_signature_hash` in `crates/rbitcoin-consensus/src/script/mod.rs` | +| `src/interpreter.rs` `verify_taproot_commitment` | `verify_control_commitment` in `crates/rbitcoin-consensus/src/script/p2tr.rs` | +| `src/scriptnum.rs` | `crates/rbitcoin-primitives/src/scriptnum.rs` | + +rbitcoin is licensed under MIT OR Apache-2.0, "Copyright (c) 2026 rbitcoin contributors". Those +files carry no per-file headers. Each adapted file says so at the top, and +`verify_taproot_commitment` in its doc comment. The adaptation removes the +block-validation job, precompute and thread-pool plumbing and the policy (standardness) flags. It +replaces rbitcoin's typed fast paths with Bitcoin Core's `VerifyScript` flow run through the one +interpreter. + +## License + +Licensed under either of + +- Apache License, Version 2.0 ([LICENSE-APACHE](../../LICENSE-APACHE) or ) +- MIT license ([LICENSE-MIT](../../LICENSE-MIT) or ) + +at your option. diff --git a/crates/script_verify/src/crypto.rs b/crates/script_verify/src/crypto.rs new file mode 100644 index 0000000000..a242e55f27 --- /dev/null +++ b/crates/script_verify/src/crypto.rs @@ -0,0 +1,133 @@ +// Adapted from the `crypto` module of rbitcoin `crates/rbitcoin-consensus/src/script/mod.rs` at +// 49210ea82130474a4df366e253d1346a16369fdb (MIT OR Apache-2.0, rbitcoin contributors). + +use bitcoin::hashes::{hash160, sha1, sha256, Hash}; +use bitcoin::secp256k1::{ecdsa, schnorr, Message, Parity, PublicKey, Secp256k1, VerifyOnly}; +use bitcoin::taproot::{TapNodeHash, TapTweakHash}; +use bitcoin::XOnlyPublicKey; + +/// Splits a signature push into its DER part and the **raw** sighash byte. +/// +/// The raw byte is kept because legacy and BIP143 sighashes commit to it unnormalized: mainnet has +/// spends signed with hashtype 0. DER is always parsed laxly, as Core does, because libsecp's +/// strict parser recovers the wrong (R, S) for some pre-BIP66 encodings. BIP66 strictness is +/// enforced separately by [`is_valid_signature_encoding`]. +pub fn parse_der_sig(sig: &[u8]) -> Option<(ecdsa::Signature, u32)> { + let (&sighash_byte, der) = sig.split_last()?; + let sig = ecdsa::Signature::from_der_lax(der).ok()?; + Some((sig, u32::from(sighash_byte))) +} + +/// BIP66 strict DER encoding of a full signature push, including its trailing hashtype byte. +pub fn is_valid_signature_encoding(sig: &[u8]) -> bool { + // 0x30 [total-length] 0x02 [R-length] [R] 0x02 [S-length] [S] [sighash] + if sig.len() < 9 || sig.len() > 73 { + return false; + } + if sig[0] != 0x30 { + return false; + } + if sig[1] as usize != sig.len().wrapping_sub(3) { + return false; + } + if sig[2] != 0x02 { + return false; + } + let len_r = sig[3] as usize; + if len_r == 0 { + return false; + } + if 5 + len_r >= sig.len() { + return false; + } + if sig[4 + len_r] != 0x02 { + return false; + } + let len_s = sig[5 + len_r] as usize; + if len_s == 0 { + return false; + } + if len_r + len_s + 7 != sig.len() { + return false; + } + if sig[4] & 0x80 != 0 { + return false; + } + if len_r > 1 && sig[4] == 0x00 && (sig[5] & 0x80) == 0 { + return false; + } + let s0 = 6 + len_r; + if sig[s0] & 0x80 != 0 { + return false; + } + if len_s > 1 && sig[s0] == 0x00 && (sig[s0 + 1] & 0x80) == 0 { + return false; + } + true +} + +/// Signature and key checks, sharing one secp256k1 verification context. +pub struct Verifier { + secp: Secp256k1, +} + +impl Verifier { + pub fn new() -> Self { + Self { + secp: Secp256k1::verification_only(), + } + } + + /// ECDSA verification under consensus rules. + /// + /// libsecp256k1 rejects high-S signatures but high S was never a consensus failure (BIP146 is + /// unactivated), so S is normalized first as Core does. + pub fn verify_ecdsa( + &self, + sighash: [u8; 32], + sig: &ecdsa::Signature, + pubkey: &PublicKey, + ) -> bool { + let mut normalized = *sig; + normalized.normalize_s(); + self.secp + .verify_ecdsa(&Message::from_digest(sighash), &normalized, pubkey) + .is_ok() + } + + /// BIP340 verification. + pub fn verify_schnorr( + &self, + sighash: [u8; 32], + sig: &schnorr::Signature, + pubkey: &XOnlyPublicKey, + ) -> bool { + self.secp + .verify_schnorr(sig, &Message::from_digest(sighash), pubkey) + .is_ok() + } + + /// BIP341: whether `output_key`, with `parity`, is `internal` tweaked by `merkle_root`. + pub fn verify_taproot_tweak( + &self, + internal: &XOnlyPublicKey, + merkle_root: TapNodeHash, + output_key: &XOnlyPublicKey, + parity: Parity, + ) -> bool { + let tweak = TapTweakHash::from_key_and_tweak(*internal, Some(merkle_root)).to_scalar(); + internal.tweak_add_check(&self.secp, output_key, parity, tweak) + } +} + +pub fn hash160(data: &[u8]) -> [u8; 20] { + hash160::Hash::hash(data).to_byte_array() +} + +pub fn sha256(data: &[u8]) -> [u8; 32] { + sha256::Hash::hash(data).to_byte_array() +} + +pub fn sha1(data: &[u8]) -> [u8; 20] { + sha1::Hash::hash(data).to_byte_array() +} diff --git a/crates/script_verify/src/interpreter.rs b/crates/script_verify/src/interpreter.rs new file mode 100644 index 0000000000..60ced1a398 --- /dev/null +++ b/crates/script_verify/src/interpreter.rs @@ -0,0 +1,1186 @@ +// Adapted from rbitcoin `crates/rbitcoin-consensus/src/script/interpreter.rs` at +// 49210ea82130474a4df366e253d1346a16369fdb (MIT OR Apache-2.0, rbitcoin contributors). +// +// Consensus rules only. rbitcoin's policy flags (MINIMALDATA, NULLFAIL, LOW_S, STRICTENC, +// WITNESS_PUBKEYTYPE, CONST_SCRIPTCODE, v0 MINIMALIF) are removed. + +use alloc::borrow::Cow; +use alloc::vec; +use alloc::vec::Vec; +use core::cell::Cell; + +use bitcoin::hashes::{ripemd160, sha256d, Hash}; +use bitcoin::script::{read_scriptbool, Instruction, Script, ScriptBuf}; +use bitcoin::secp256k1::{schnorr, PublicKey}; +use bitcoin::sighash::TapSighashType; +use bitcoin::{Amount, PubkeyHash, Sequence, TapLeafHash, TxOut, Witness, XOnlyPublicKey}; + +use crate::sighash::Sighasher; +use crate::{crypto, scriptnum, Fail, Flags}; + +const MAX_STACK_SIZE: usize = 1000; +const MAX_SCRIPT_ELEMENT_SIZE: usize = 520; +const MAX_SCRIPT_SIZE_LEGACY: usize = 10_000; +const MAX_OPS_LEGACY: usize = 201; +const MAX_PUBKEYS_PER_MULTISIG: i64 = 20; +const TAPSCRIPT_VALIDATION_WEIGHT_OFFSET: i64 = 50; +const TAPSCRIPT_VALIDATION_WEIGHT_PER_SIGOP: i64 = 50; + +const OP_PUSHBYTES_75: u8 = 0x4b; +const OP_PUSHDATA1: u8 = 0x4c; +const OP_PUSHDATA2: u8 = 0x4d; +const OP_PUSHDATA4: u8 = 0x4e; +const OP_VERIF: u8 = 0x65; +const OP_VERNOTIF: u8 = 0x66; +const OP_CODESEPARATOR: u8 = 0xab; + +type Stack = Vec>; + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +enum SigVersion { + Base, + WitnessV0, + TapScript, +} + +/// The facts a BIP342 signature hash needs beyond the transaction. +struct TapscriptData<'w> { + leaf_hash: TapLeafHash, + annex: Option<&'w [u8]>, +} + +struct EvalContext<'a, 'c> { + sighasher: &'c Sighasher<'a>, + verifier: &'c crypto::Verifier, + flags: Flags, + input_index: usize, + amount: Amount, + script_code: &'c Script, + sig_version: SigVersion, + /// BIP342: instruction index (not byte offset) of the last executed OP_CODESEPARATOR. + codeseparator_pos: Cell, + /// Base and witness v0: byte offset just past the last executed OP_CODESEPARATOR, which starts + /// the scriptCode that signatures commit to. + codeseparator_script_off: Cell>, + validation_weight_left: Cell, + tapscript: Option>, +} + +impl<'a, 'c> EvalContext<'a, 'c> { + fn new( + sighasher: &'c Sighasher<'a>, + verifier: &'c crypto::Verifier, + flags: Flags, + input_index: usize, + amount: Amount, + script_code: &'c Script, + sig_version: SigVersion, + ) -> Self { + let validation_weight = if sig_version == SigVersion::TapScript { + let witness = &sighasher.tx().input[input_index].witness; + TAPSCRIPT_VALIDATION_WEIGHT_OFFSET + + bitcoin::consensus::encode::serialize(witness).len() as i64 + } else { + 0 + }; + Self { + sighasher, + verifier, + flags, + input_index, + amount, + script_code, + sig_version, + codeseparator_pos: Cell::new(0xFFFF_FFFF), + codeseparator_script_off: Cell::new(None), + validation_weight_left: Cell::new(validation_weight), + tapscript: None, + } + } + + fn with_tapscript(mut self, data: TapscriptData<'c>) -> Self { + self.tapscript = Some(data); + self + } +} + +fn is_disabled_legacy(code: u8) -> bool { + // OP_CAT, OP_SUBSTR, OP_LEFT, OP_RIGHT, OP_INVERT, OP_AND, OP_OR, OP_XOR, OP_2MUL, OP_2DIV, + // OP_MUL, OP_DIV, OP_MOD, OP_LSHIFT, OP_RSHIFT + matches!( + code, + 0x7e | 0x7f | 0x80 | 0x81 | 0x83 | 0x84 | 0x85 | 0x86 | 0x8d | 0x8e | 0x95..=0x99 + ) +} + +fn is_op_success(code: u8) -> bool { + matches!( + code, + 80 | 98 | 126..=129 | 131..=134 | 137 | 138 | 141 | 142 | 149..=153 | 187..=254 + ) +} + +/// BIP342: an OP_SUCCESSx anywhere makes the script valid before any limit applies, so this is a +/// separate walk from evaluation. A truncated push stops the walk; evaluation then fails on it. +fn tapscript_has_op_success(script: &Script) -> bool { + let bytes = script.as_bytes(); + let mut i = 0usize; + while i < bytes.len() { + let op = bytes[i]; + if (1..=OP_PUSHBYTES_75).contains(&op) { + i = i.saturating_add(1).saturating_add(op as usize); + continue; + } + match op { + OP_PUSHDATA1 => { + if i + 1 >= bytes.len() { + break; + } + i = i.saturating_add(2).saturating_add(bytes[i + 1] as usize); + } + OP_PUSHDATA2 => { + if i + 2 >= bytes.len() { + break; + } + let n = u16::from_le_bytes([bytes[i + 1], bytes[i + 2]]) as usize; + i = i.saturating_add(3).saturating_add(n); + } + OP_PUSHDATA4 => { + if i + 4 >= bytes.len() { + break; + } + let n = u32::from_le_bytes([bytes[i + 1], bytes[i + 2], bytes[i + 3], bytes[i + 4]]) + as usize; + i = i.saturating_add(5).saturating_add(n); + } + _ => { + if is_op_success(op) { + return true; + } + i += 1; + } + } + } + false +} + +/// Witness and tapscript: exactly one true element (implicit CLEANSTACK). +fn require_clean_true(stack: &[Vec]) -> Result<(), Fail> { + if stack.len() != 1 { + return Err(Fail::Script("CLEANSTACK")); + } + if !read_scriptbool(&stack[0]) { + return Err(Fail::Script("EVAL_FALSE")); + } + Ok(()) +} + +/// Legacy: a non-empty stack with a true top. BIP62 CLEANSTACK never became consensus. +fn require_true_top(stack: &[Vec]) -> Result<(), Fail> { + match stack.last() { + Some(top) if read_scriptbool(top) => Ok(()), + _ => Err(Fail::Script("EVAL_FALSE")), + } +} + +/// Core's `ConditionStack`: the depth plus the index of the first false entry, so that checking +/// whether we are executing is O(1). Tapscript has no opcode limit, so scanning a `Vec` per +/// opcode would be quadratic in nesting depth. +#[derive(Default)] +struct ConditionStack { + size: usize, + first_false: Option, +} + +impl ConditionStack { + fn is_empty(&self) -> bool { + self.size == 0 + } + + fn all_true(&self) -> bool { + self.first_false.is_none() + } + + fn push(&mut self, value: bool) { + if self.first_false.is_none() && !value { + self.first_false = Some(self.size); + } + self.size += 1; + } + + fn pop(&mut self) { + self.size -= 1; + if self.first_false == Some(self.size) { + self.first_false = None; + } + } + + fn toggle_top(&mut self) { + let top = self.size - 1; + match self.first_false { + None => self.first_false = Some(top), + Some(pos) if pos == top => self.first_false = None, + Some(_) => {} + } + } +} + +fn eval_script(script: &Script, stack: &mut Stack, ctx: &EvalContext<'_, '_>) -> Result<(), Fail> { + let tapscript = ctx.sig_version == SigVersion::TapScript; + if !tapscript && script.len() > MAX_SCRIPT_SIZE_LEGACY { + return Err(Fail::Script("SCRIPT_SIZE")); + } + + let mut altstack: Stack = Vec::new(); + let mut cond_stack = ConditionStack::default(); + let mut op_count = 0usize; + let mut opcode_pos: u32 = 0; + for item in script.instruction_indices() { + let (byte_index, ins) = item.map_err(|_| Fail::Script("BAD_OPCODE"))?; + let this_pos = opcode_pos; + opcode_pos = opcode_pos.saturating_add(1); + let executing = cond_stack.all_true(); + + let code = match ins { + Instruction::PushBytes(b) => { + // Core enforces the element size even in unexecuted branches. + if b.len() > MAX_SCRIPT_ELEMENT_SIZE { + return Err(Fail::Script("PUSH_SIZE")); + } + if executing { + push(stack, altstack.len(), b.as_bytes().to_vec())?; + } + continue; + } + Instruction::Op(op) => op.to_u8(), + }; + + // Opcodes above OP_16 count toward the legacy limit even when skipped. + if !tapscript && code > 0x60 { + op_count += 1; + if op_count > MAX_OPS_LEGACY { + return Err(Fail::Script("OP_COUNT")); + } + } + + // Flow control runs even when skipped: it is structure, not value. + match code { + 0x63 | 0x64 => { + let mut cond = false; + if executing { + let v = pop(stack)?; + if tapscript && !(v.is_empty() || v == [0x01]) { + return Err(Fail::Script("TAPSCRIPT_MINIMALIF")); + } + cond = read_scriptbool(&v) == (code == 0x63); + } + cond_stack.push(executing && cond); + continue; + } + 0x67 => { + if cond_stack.is_empty() { + return Err(Fail::Script("UNBALANCED_CONDITIONAL")); + } + cond_stack.toggle_top(); + continue; + } + 0x68 => { + if cond_stack.is_empty() { + return Err(Fail::Script("UNBALANCED_CONDITIONAL")); + } + cond_stack.pop(); + continue; + } + _ => {} + } + + // Core fails these even in unexecuted branches. + if code == OP_VERIF || code == OP_VERNOTIF { + return Err(Fail::Script("BAD_OPCODE")); + } + if !tapscript && is_disabled_legacy(code) { + return Err(Fail::Script("DISABLED_OPCODE")); + } + + if !executing { + continue; + } + + let alt = altstack.len(); + match code { + 0x4f => push(stack, alt, vec![0x81])?, + n @ 0x51..=0x60 => push(stack, alt, vec![n - 0x50])?, + 0x61 => {} + 0x69 => { + if !read_scriptbool(&pop(stack)?) { + return Err(Fail::Script("VERIFY")); + } + } + 0x6a => return Err(Fail::Script("OP_RETURN")), + 0x6b => { + let v = pop(stack)?; + altstack.push(v); + } + 0x6c => { + let v = altstack + .pop() + .ok_or(Fail::Script("INVALID_ALTSTACK_OPERATION"))?; + push(stack, altstack.len(), v)?; + } + 0x6d => { + pop(stack)?; + pop(stack)?; + } + 0x6e => { + require_n(stack, 2)?; + let a = stack[stack.len() - 2].clone(); + let b = stack[stack.len() - 1].clone(); + push(stack, alt, a)?; + push(stack, alt, b)?; + } + 0x6f => { + require_n(stack, 3)?; + let a = stack[stack.len() - 3].clone(); + let b = stack[stack.len() - 2].clone(); + let c = stack[stack.len() - 1].clone(); + push(stack, alt, a)?; + push(stack, alt, b)?; + push(stack, alt, c)?; + } + 0x70 => { + require_n(stack, 4)?; + let a = stack[stack.len() - 4].clone(); + let b = stack[stack.len() - 3].clone(); + push(stack, alt, a)?; + push(stack, alt, b)?; + } + 0x71 => { + require_n(stack, 6)?; + let n = stack.len(); + let x1 = stack[n - 6].clone(); + let x2 = stack[n - 5].clone(); + for i in 0..4 { + stack[n - 6 + i] = stack[n - 4 + i].clone(); + } + stack[n - 2] = x1; + stack[n - 1] = x2; + } + 0x72 => { + require_n(stack, 4)?; + let n = stack.len(); + stack.swap(n - 4, n - 2); + stack.swap(n - 3, n - 1); + } + 0x73 => { + require_n(stack, 1)?; + let top = stack[stack.len() - 1].clone(); + if read_scriptbool(&top) { + push(stack, alt, top)?; + } + } + 0x74 => { + let depth = stack.len() as i64; + push(stack, alt, scriptnum::encode(depth))?; + } + 0x75 => { + pop(stack)?; + } + 0x76 => { + require_n(stack, 1)?; + let v = stack[stack.len() - 1].clone(); + push(stack, alt, v)?; + } + 0x77 => { + require_n(stack, 2)?; + let top = pop(stack)?; + pop(stack)?; + push(stack, alt, top)?; + } + 0x78 => { + require_n(stack, 2)?; + let v = stack[stack.len() - 2].clone(); + push(stack, alt, v)?; + } + 0x79 | 0x7a => { + let n = scriptnum::decode(&pop(stack)?)?; + if n < 0 || n as usize >= stack.len() { + return Err(Fail::Script("INVALID_STACK_OPERATION")); + } + let idx = stack.len() - 1 - n as usize; + let v = if code == 0x79 { + stack[idx].clone() + } else { + stack.remove(idx) + }; + push(stack, altstack.len(), v)?; + } + 0x7b => { + require_n(stack, 3)?; + let n = stack.len(); + stack.swap(n - 3, n - 2); + stack.swap(n - 2, n - 1); + } + 0x7c => { + require_n(stack, 2)?; + let n = stack.len(); + stack.swap(n - 1, n - 2); + } + 0x7d => { + require_n(stack, 2)?; + let v = stack[stack.len() - 1].clone(); + push(stack, alt, v)?; + let n = stack.len(); + stack.swap(n - 3, n - 2); + } + 0x82 => { + require_n(stack, 1)?; + let size = stack[stack.len() - 1].len() as i64; + push(stack, alt, scriptnum::encode(size))?; + } + 0x87 | 0x88 => { + let a = pop(stack)?; + let b = pop(stack)?; + if code == 0x87 { + push(stack, alt, bool_encode(a == b))?; + } else if a != b { + return Err(Fail::Script("EQUALVERIFY")); + } + } + 0x8b | 0x8c | 0x8f | 0x90 | 0x91 | 0x92 => { + let v = scriptnum::decode(&pop(stack)?)?; + let out = match code { + 0x8b => scriptnum::encode(v + 1), + 0x8c => scriptnum::encode(v - 1), + 0x8f => scriptnum::encode(-v), + 0x90 => scriptnum::encode(v.abs()), + 0x91 => bool_encode(v == 0), + _ => bool_encode(v != 0), + }; + push(stack, alt, out)?; + } + 0x93..=0x94 | 0x9a..=0xa4 => { + let b = scriptnum::decode(&pop(stack)?)?; + let a = scriptnum::decode(&pop(stack)?)?; + let out = match code { + 0x93 => scriptnum::encode(a + b), + 0x94 => scriptnum::encode(a - b), + 0x9a => bool_encode(a != 0 && b != 0), + 0x9b => bool_encode(a != 0 || b != 0), + 0x9c => bool_encode(a == b), + 0x9d => { + if a != b { + return Err(Fail::Script("NUMEQUALVERIFY")); + } + continue; + } + 0x9e => bool_encode(a != b), + 0x9f => bool_encode(a < b), + 0xa0 => bool_encode(a > b), + 0xa1 => bool_encode(a <= b), + 0xa2 => bool_encode(a >= b), + 0xa3 => scriptnum::encode(a.min(b)), + _ => scriptnum::encode(a.max(b)), + }; + push(stack, alt, out)?; + } + 0xa5 => { + let max = scriptnum::decode(&pop(stack)?)?; + let min = scriptnum::decode(&pop(stack)?)?; + let x = scriptnum::decode(&pop(stack)?)?; + push(stack, alt, bool_encode(x >= min && x < max))?; + } + 0xa6 => { + let v = pop(stack)?; + push( + stack, + alt, + ripemd160::Hash::hash(&v).to_byte_array().to_vec(), + )?; + } + 0xa7 => { + let v = pop(stack)?; + push(stack, alt, crypto::sha1(&v).to_vec())?; + } + 0xa8 => { + let v = pop(stack)?; + push(stack, alt, crypto::sha256(&v).to_vec())?; + } + 0xa9 => { + let v = pop(stack)?; + push(stack, alt, crypto::hash160(&v).to_vec())?; + } + 0xaa => { + let v = pop(stack)?; + push(stack, alt, sha256d::Hash::hash(&v).to_byte_array().to_vec())?; + } + OP_CODESEPARATOR => { + ctx.codeseparator_pos.set(this_pos); + ctx.codeseparator_script_off + .set(Some(byte_index.saturating_add(1))); + } + 0xac | 0xad => op_checksig(stack, alt, ctx, code == 0xad)?, + 0xae | 0xaf => { + if tapscript { + return Err(Fail::Script("TAPSCRIPT_CHECKMULTISIG")); + } + op_checkmultisig(stack, alt, ctx, code == 0xaf, &mut op_count)?; + } + 0xba if tapscript => op_checksigadd(stack, alt, ctx)?, + 0xb1 => { + if !ctx.flags.checklocktimeverify { + continue; + } + require_n(stack, 1)?; + let locktime = scriptnum::decode_width(&stack[stack.len() - 1], 5)?; + check_locktime(ctx, locktime)?; + } + 0xb2 => { + if !ctx.flags.checksequenceverify { + continue; + } + require_n(stack, 1)?; + let csv = scriptnum::decode_width(&stack[stack.len() - 1], 5)?; + if csv < 0 { + return Err(Fail::Script("NEGATIVE_LOCKTIME")); + } + // The disable flag makes CSV a NOP, checked before the version gate. + if csv as u32 & (1 << 31) != 0 { + continue; + } + check_sequence(ctx, csv as u32)?; + } + 0xb0 | 0xb3..=0xb9 => {} + _ => return Err(Fail::Script("BAD_OPCODE")), + } + } + + if !cond_stack.is_empty() { + return Err(Fail::Script("UNBALANCED_CONDITIONAL")); + } + Ok(()) +} + +/// One input being verified, and the facts every script it runs shares. +struct Spend<'a, 'c> { + sighasher: &'c Sighasher<'a>, + verifier: &'c crypto::Verifier, + input_index: usize, + prevout: &'c TxOut, + flags: Flags, +} + +impl<'a, 'c> Spend<'a, 'c> { + fn witness(&self) -> &'a Witness { + &self.sighasher.tx().input[self.input_index].witness + } + + fn ctx(&self, script_code: &'c Script, sig_version: SigVersion) -> EvalContext<'a, 'c> { + EvalContext::new( + self.sighasher, + self.verifier, + self.flags, + self.input_index, + self.prevout.value, + script_code, + sig_version, + ) + } +} + +/// Bitcoin Core's `VerifyScript`: runs input `input_index`'s scriptSig, the scriptPubKey of +/// `prevout` and its witness under `flags`. +pub fn verify_script( + sighasher: &Sighasher<'_>, + input_index: usize, + prevout: &TxOut, + flags: Flags, +) -> Result<(), Fail> { + let verifier = crypto::Verifier::new(); + let spend = Spend { + sighasher, + verifier: &verifier, + input_index, + prevout, + flags, + }; + let txin = &sighasher.tx().input[input_index]; + let script_sig = txin.script_sig.as_script(); + let spk = prevout.script_pubkey.as_script(); + + let mut stack = Stack::new(); + eval_script( + script_sig, + &mut stack, + &spend.ctx(script_sig, SigVersion::Base), + )?; + let p2sh_stack = (flags.p2sh && spk.is_p2sh()).then(|| stack.clone()); + eval_script(spk, &mut stack, &spend.ctx(spk, SigVersion::Base))?; + require_true_top(&stack)?; + + let mut had_witness = false; + if flags.witness { + if let Some((version, program)) = witness_program(spk) { + had_witness = true; + if !script_sig.is_empty() { + return Err(Fail::Script("WITNESS_MALLEATED")); + } + verify_witness_program(&spend, version, program, false)?; + } + } + + if let Some(mut stack) = p2sh_stack { + if !script_sig.is_push_only() { + return Err(Fail::Script("SIG_PUSHONLY")); + } + let redeem = stack + .pop() + .expect("the scriptPubKey's hash check passed so the stack was not empty"); + let redeem = Script::from_bytes(&redeem); + eval_script(redeem, &mut stack, &spend.ctx(redeem, SigVersion::Base))?; + require_true_top(&stack)?; + if flags.witness { + if let Some((version, program)) = witness_program(redeem) { + had_witness = true; + if Some(script_sig.as_bytes()) != single_push(redeem.as_bytes()).as_deref() { + return Err(Fail::Script("WITNESS_MALLEATED_P2SH")); + } + verify_witness_program(&spend, version, program, true)?; + } + } + } + + if flags.witness && !had_witness && !txin.witness.is_empty() { + return Err(Fail::Script("WITNESS_UNEXPECTED")); + } + Ok(()) +} + +fn witness_program(script: &Script) -> Option<(u8, &[u8])> { + let version = script.witness_version()?; + Some((version.to_num(), &script.as_bytes()[2..])) +} + +/// Bitcoin Core's `VerifyWitnessProgram`. +fn verify_witness_program( + spend: &Spend<'_, '_>, + version: u8, + program: &[u8], + is_p2sh: bool, +) -> Result<(), Fail> { + let mut stack: Stack = spend.witness().iter().map(<[u8]>::to_vec).collect(); + match (version, program.len()) { + (0, 32) => { + let script = stack + .pop() + .ok_or(Fail::Script("WITNESS_PROGRAM_WITNESS_EMPTY"))?; + if crypto::sha256(&script) != program { + return Err(Fail::Script("WITNESS_PROGRAM_MISMATCH")); + } + let script = Script::from_bytes(&script); + execute_witness_script(stack, script, &spend.ctx(script, SigVersion::WitnessV0)) + } + (0, 20) => { + if stack.len() != 2 { + return Err(Fail::Script("WITNESS_PROGRAM_MISMATCH")); + } + let hash = PubkeyHash::from_slice(program).expect("20 bytes"); + let script = ScriptBuf::new_p2pkh(&hash); + execute_witness_script(stack, &script, &spend.ctx(&script, SigVersion::WitnessV0)) + } + (0, _) => Err(Fail::Script("WITNESS_PROGRAM_WRONG_LENGTH")), + (1, 32) if !is_p2sh && spend.flags.taproot => verify_taproot(spend, program, stack), + // Unknown witness versions and lengths, including pay-to-anchor, are reserved for + // upgrades and pass. + _ => Ok(()), + } +} + +fn verify_taproot(spend: &Spend<'_, '_>, output_key: &[u8], mut stack: Stack) -> Result<(), Fail> { + let witness = spend.witness(); + if stack.is_empty() { + return Err(Fail::Script("WITNESS_PROGRAM_WITNESS_EMPTY")); + } + let annex = match witness.last() { + Some(last) if witness.len() >= 2 && last.first() == Some(&0x50) => { + stack.pop(); + Some(last) + } + _ => None, + }; + if stack.len() == 1 { + return check_schnorr( + spend.sighasher, + spend.verifier, + spend.input_index, + &stack[0], + output_key, + annex, + None, + ); + } + let control = stack.pop().expect("at least two elements"); + stack.pop().expect("at least two elements"); + // Borrow the script from the witness, not the stack, so it outlives the evaluation context. + let script = Script::from_bytes( + witness + .nth(stack.len()) + .expect("the script is the element after the remaining stack"), + ); + let (leaf_version, leaf_hash) = + verify_taproot_commitment(spend.verifier, &control, output_key, script)?; + // Unknown leaf versions are reserved for upgrades and pass without running the script. + if leaf_version != 0xc0 { + return Ok(()); + } + let ctx = spend + .ctx(script, SigVersion::TapScript) + .with_tapscript(TapscriptData { leaf_hash, annex }); + execute_witness_script(stack, script, &ctx) +} + +/// Checks the BIP341 commitment of a script path spend. Returns the leaf version and leaf hash. +/// +/// Adapted from rbitcoin `p2tr.rs` (see the crate README). The leaf version byte is used raw +/// because rust-bitcoin's `LeafVersion` rejects 0x50, which consensus allows as a future version. +fn verify_taproot_commitment( + verifier: &crypto::Verifier, + control: &[u8], + output_key: &[u8], + script: &Script, +) -> Result<(u8, TapLeafHash), Fail> { + use bitcoin::consensus::Encodable; + use bitcoin::hashes::HashEngine; + use bitcoin::secp256k1::Parity; + use bitcoin::taproot::TapNodeHash; + + const BASE: usize = 33; + const NODE: usize = 32; + const MAX_NODES: usize = 128; + let extra = control.len().checked_sub(BASE); + let nodes = match extra { + Some(extra) if extra % NODE == 0 && extra / NODE <= MAX_NODES => extra / NODE, + _ => return Err(Fail::Script("TAPROOT_WRONG_CONTROL_SIZE")), + }; + let leaf_version = control[0] & 0xfe; + let parity = if control[0] & 1 == 0 { + Parity::Even + } else { + Parity::Odd + }; + let mismatch = || Fail::Script("WITNESS_PROGRAM_MISMATCH"); + let internal = XOnlyPublicKey::from_slice(&control[1..BASE]).map_err(|_| mismatch())?; + let output_key = XOnlyPublicKey::from_slice(output_key).map_err(|_| mismatch())?; + + let mut eng = TapLeafHash::engine(); + eng.input(&[leaf_version]); + script + .consensus_encode(&mut eng) + .expect("hash engines do not error"); + let leaf_hash = TapLeafHash::from_engine(eng); + let mut node = TapNodeHash::from_byte_array(leaf_hash.to_byte_array()); + for sibling in control[BASE..].chunks_exact(NODE).take(nodes) { + let sibling = TapNodeHash::from_byte_array(sibling.try_into().expect("32 bytes")); + node = TapNodeHash::from_node_hashes(node, sibling); + } + if !verifier.verify_taproot_tweak(&internal, node, &output_key, parity) { + return Err(mismatch()); + } + Ok((leaf_version, leaf_hash)) +} + +/// Bitcoin Core's `ExecuteWitnessScript`. +fn execute_witness_script( + mut stack: Stack, + script: &Script, + ctx: &EvalContext<'_, '_>, +) -> Result<(), Fail> { + if ctx.sig_version == SigVersion::TapScript { + if tapscript_has_op_success(script) { + return Ok(()); + } + if stack.len() > MAX_STACK_SIZE { + return Err(Fail::Script("STACK_SIZE")); + } + } + if stack + .iter() + .any(|item| item.len() > MAX_SCRIPT_ELEMENT_SIZE) + { + return Err(Fail::Script("PUSH_SIZE")); + } + eval_script(script, &mut stack, ctx)?; + require_clean_true(&stack) +} + +fn check_locktime(ctx: &EvalContext<'_, '_>, locktime: i64) -> Result<(), Fail> { + const THRESHOLD: i64 = 500_000_000; + if locktime < 0 { + return Err(Fail::Script("NEGATIVE_LOCKTIME")); + } + let tx = ctx.sighasher.tx(); + let tx_lock = tx.lock_time.to_consensus_u32() as i64; + if (locktime >= THRESHOLD) != (tx_lock >= THRESHOLD) || locktime > tx_lock { + return Err(Fail::Script("UNSATISFIED_LOCKTIME")); + } + if tx.input[ctx.input_index].sequence.is_final() { + return Err(Fail::Script("UNSATISFIED_LOCKTIME")); + } + Ok(()) +} + +fn check_sequence(ctx: &EvalContext<'_, '_>, csv: u32) -> Result<(), Fail> { + const DISABLE: u32 = 1 << 31; + const TYPE: u32 = 1 << 22; + const MASK: u32 = 0x0000_ffff | TYPE; + let tx = ctx.sighasher.tx(); + // Core compares the version unsigned. + if (tx.version.0 as u32) < 2 { + return Err(Fail::Script("UNSATISFIED_LOCKTIME")); + } + let seq = Sequence::to_consensus_u32(tx.input[ctx.input_index].sequence); + if seq & DISABLE != 0 { + return Err(Fail::Script("UNSATISFIED_LOCKTIME")); + } + let (seq, csv) = (seq & MASK, csv & MASK); + if (seq ^ csv) & TYPE != 0 || (csv & 0xffff) > (seq & 0xffff) { + return Err(Fail::Script("UNSATISFIED_LOCKTIME")); + } + Ok(()) +} + +fn op_checksig( + stack: &mut Stack, + alt_len: usize, + ctx: &EvalContext<'_, '_>, + verify: bool, +) -> Result<(), Fail> { + let pubkey = pop(stack)?; + let sig = pop(stack)?; + if ctx.sig_version == SigVersion::TapScript { + let success = tapscript_check_sig(&sig, &pubkey, ctx)?; + if verify && !success { + return Err(Fail::Script("CHECKSIGVERIFY")); + } + if !verify { + // BIP342 pushes the empty vector on failure, not 0x00. + push(stack, alt_len, bool_encode(success))?; + } + return Ok(()); + } + let script_code = if ctx.sig_version == SigVersion::Base { + legacy_script_code(script_code_bytes(ctx), &[&sig]) + } else { + Cow::Borrowed(script_code_bytes(ctx)) + }; + let ok = ecdsa_sig_matches(&sig, &pubkey, ctx, &script_code)?; + if verify { + if !ok { + return Err(Fail::Script("CHECKSIGVERIFY")); + } + } else { + push(stack, alt_len, bool_encode(ok))?; + } + Ok(()) +} + +/// BIP342 OP_CHECKSIGADD: `sig n pubkey` becomes `n` or `n+1`. +fn op_checksigadd( + stack: &mut Stack, + alt_len: usize, + ctx: &EvalContext<'_, '_>, +) -> Result<(), Fail> { + require_n(stack, 3)?; + let pubkey = pop(stack)?; + let n = scriptnum::decode(&pop(stack)?)?; + let sig = pop(stack)?; + let success = tapscript_check_sig(&sig, &pubkey, ctx)?; + push(stack, alt_len, scriptnum::encode(n + i64::from(success))) +} + +/// Core's `EvalChecksigTapscript`. Returns whether the signature counts as a success. An empty +/// signature is a soft failure; any other failing signature fails the script. +fn tapscript_check_sig(sig: &[u8], pubkey: &[u8], ctx: &EvalContext<'_, '_>) -> Result { + if !sig.is_empty() { + let left = ctx.validation_weight_left.get() - TAPSCRIPT_VALIDATION_WEIGHT_PER_SIGOP; + ctx.validation_weight_left.set(left); + if left < 0 { + return Err(Fail::Script("TAPSCRIPT_VALIDATION_WEIGHT")); + } + } + if pubkey.is_empty() { + return Err(Fail::Script("TAPSCRIPT_EMPTY_PUBKEY")); + } + if sig.is_empty() { + return Ok(false); + } + // Unknown public key types succeed, reserved for soft forks. + if pubkey.len() != 32 { + return Ok(true); + } + let tapscript = ctx + .tapscript + .as_ref() + .expect("tapscript evaluation always carries its leaf data"); + let leaf = Some((tapscript.leaf_hash, ctx.codeseparator_pos.get())); + check_schnorr( + ctx.sighasher, + ctx.verifier, + ctx.input_index, + sig, + pubkey, + tapscript.annex, + leaf, + )?; + Ok(true) +} + +/// BIP340/341 Schnorr check shared by the key path and tapscript. Any failure fails the script. +fn check_schnorr( + sighasher: &Sighasher<'_>, + verifier: &crypto::Verifier, + input_index: usize, + sig: &[u8], + pubkey: &[u8], + annex: Option<&[u8]>, + leaf: Option<(TapLeafHash, u32)>, +) -> Result<(), Fail> { + let (sig, ty) = match sig.len() { + 64 => (sig, TapSighashType::Default), + // An explicit 0x00 must use the 64-byte form. + 65 if sig[64] != 0x00 => { + let ty = TapSighashType::from_consensus_u8(sig[64]) + .map_err(|_| Fail::Script("SCHNORR_SIG_HASHTYPE"))?; + (&sig[..64], ty) + } + 65 => return Err(Fail::Script("SCHNORR_SIG_HASHTYPE")), + _ => return Err(Fail::Script("SCHNORR_SIG_SIZE")), + }; + let pubkey = XOnlyPublicKey::from_slice(pubkey).map_err(|_| Fail::Script("SCHNORR_SIG"))?; + let sig = schnorr::Signature::from_slice(sig).expect("64 bytes"); + let sighash = sighasher.taproot(input_index, annex, leaf, ty)?; + if !verifier.verify_schnorr(sighash, &sig, &pubkey) { + return Err(Fail::Script("SCHNORR_SIG")); + } + Ok(()) +} + +fn op_checkmultisig( + stack: &mut Stack, + alt_len: usize, + ctx: &EvalContext<'_, '_>, + verify: bool, + op_count: &mut usize, +) -> Result<(), Fail> { + // Pop order matches Core: n, n keys (top = last pushed), m, m sigs, dummy. + let n = scriptnum::decode(&pop(stack)?)?; + if !(0..=MAX_PUBKEYS_PER_MULTISIG).contains(&n) { + return Err(Fail::Script("PUBKEY_COUNT")); + } + *op_count += n as usize; + if *op_count > MAX_OPS_LEGACY { + return Err(Fail::Script("OP_COUNT")); + } + let pubkeys = (0..n).map(|_| pop(stack)).collect::, _>>()?; + let m = scriptnum::decode(&pop(stack)?)?; + if m < 0 || m > n { + return Err(Fail::Script("SIG_COUNT")); + } + let sigs = (0..m).map(|_| pop(stack)).collect::, _>>()?; + let dummy = pop(stack)?; + if ctx.flags.nulldummy && !dummy.is_empty() { + return Err(Fail::Script("SIG_NULLDUMMY")); + } + + let script_code = if ctx.sig_version == SigVersion::Base { + let sig_refs = sigs.iter().map(Vec::as_slice).collect::>(); + legacy_script_code(script_code_bytes(ctx), &sig_refs) + } else { + Cow::Borrowed(script_code_bytes(ctx)) + }; + + // Start from the last pushed sig and key. The key always advances; the sig only on a match. + // Encodings are only checked for pairs actually tried. + let (mut isig, mut ikey) = (0usize, 0usize); + let mut success = true; + while success && isig < sigs.len() { + if ecdsa_sig_matches(&sigs[isig], &pubkeys[ikey], ctx, &script_code)? { + isig += 1; + } + ikey += 1; + if sigs.len() - isig > pubkeys.len() - ikey { + success = false; + } + } + + if verify { + if !success { + return Err(Fail::Script("CHECKMULTISIGVERIFY")); + } + } else { + push(stack, alt_len, bool_encode(success))?; + } + Ok(()) +} + +/// Base and witness v0 scriptCode: what follows the last executed OP_CODESEPARATOR. +fn script_code_bytes<'s>(ctx: &'s EvalContext<'_, '_>) -> &'s [u8] { + let full = ctx.script_code.as_bytes(); + match ctx.codeseparator_script_off.get() { + Some(off) => &full[off..], + None => full, + } +} + +/// Core's legacy scriptCode: FindAndDelete each signature push and drop every OP_CODESEPARATOR. +/// +/// An empty signature is `CScript() << vchSig`, the push `0x00`, so it deletes every OP_0. +fn legacy_script_code<'s>(script: &'s [u8], sigs: &[&[u8]]) -> Cow<'s, [u8]> { + let needles = sigs + .iter() + .map(|sig| { + let mut needle = Vec::with_capacity(sig.len() + 5); + encode_push(&mut needle, sig); + needle + }) + .collect::>(); + let mut out: Option> = None; + let mut i = 0usize; + while i < script.len() { + if let Some(needle) = needles.iter().find(|n| script[i..].starts_with(n)) { + out.get_or_insert_with(|| script[..i].to_vec()); + i += needle.len(); + continue; + } + let op = script[i]; + match instruction_end(script, i) { + None => { + if let Some(buf) = out.as_mut() { + buf.extend_from_slice(&script[i..]); + } + break; + } + Some(end) => { + if op == OP_CODESEPARATOR { + out.get_or_insert_with(|| script[..i].to_vec()); + } else if let Some(buf) = out.as_mut() { + buf.extend_from_slice(&script[i..end]); + } + i = end; + } + } + } + out.map(Cow::Owned).unwrap_or(Cow::Borrowed(script)) +} + +fn encode_push(out: &mut Vec, data: &[u8]) { + let n = data.len(); + if n < usize::from(OP_PUSHDATA1) { + out.push(n as u8); + } else if n <= 0xff { + out.push(OP_PUSHDATA1); + out.push(n as u8); + } else if n <= 0xffff { + out.push(OP_PUSHDATA2); + out.extend_from_slice(&(n as u16).to_le_bytes()); + } else { + out.push(OP_PUSHDATA4); + out.extend_from_slice(&(n as u32).to_le_bytes()); + } + out.extend_from_slice(data); +} + +/// End of the instruction at `i`, or `None` when its push length is truncated. A short payload +/// ends at the last byte of the script. +fn instruction_end(script: &[u8], i: usize) -> Option { + let op = script[i]; + let mut j = i + 1; + let n = match op { + 1..=OP_PUSHBYTES_75 => op as usize, + OP_PUSHDATA1 => { + let n = *script.get(j)? as usize; + j += 1; + n + } + OP_PUSHDATA2 => { + let b = script.get(j..j + 2)?; + j += 2; + u16::from_le_bytes([b[0], b[1]]) as usize + } + OP_PUSHDATA4 => { + let b = script.get(j..j + 4)?; + j += 4; + u32::from_le_bytes([b[0], b[1], b[2], b[3]]) as usize + } + _ => 0, + }; + Some((j + n).min(script.len())) +} + +/// One ECDSA signature/key comparison. Under BIP66 a badly encoded signature fails the script; +/// otherwise anything that does not verify is just `false`. +fn ecdsa_sig_matches( + sig: &[u8], + pubkey: &[u8], + ctx: &EvalContext<'_, '_>, + script_code: &[u8], +) -> Result { + if sig.is_empty() { + return Ok(false); + } + if ctx.flags.dersig && !crypto::is_valid_signature_encoding(sig) { + return Err(Fail::Script("SIG_DER")); + } + let Some((sig, raw_ty)) = crypto::parse_der_sig(sig) else { + return Ok(false); + }; + let Ok(pubkey) = PublicKey::from_slice(pubkey) else { + return Ok(false); + }; + let sighash = match ctx.sig_version { + SigVersion::Base => ctx.sighasher.legacy(ctx.input_index, script_code, raw_ty), + _ => ctx.sighasher.segwit_v0( + ctx.input_index, + Script::from_bytes(script_code), + ctx.amount, + raw_ty, + ), + }; + Ok(ctx.verifier.verify_ecdsa(sighash, &sig, &pubkey)) +} + +fn push(stack: &mut Stack, alt_len: usize, v: Vec) -> Result<(), Fail> { + if stack.len() + alt_len + 1 > MAX_STACK_SIZE { + return Err(Fail::Script("STACK_SIZE")); + } + stack.push(v); + Ok(()) +} + +fn pop(stack: &mut Stack) -> Result, Fail> { + stack.pop().ok_or(Fail::Script("INVALID_STACK_OPERATION")) +} + +fn require_n(stack: &[Vec], n: usize) -> Result<(), Fail> { + if stack.len() < n { + return Err(Fail::Script("INVALID_STACK_OPERATION")); + } + Ok(()) +} + +fn bool_encode(b: bool) -> Vec { + if b { + vec![1] + } else { + vec![] + } +} + +/// Bytes of the minimal `CScript() << data` push for data up to 75 bytes. +fn single_push(data: &[u8]) -> Option> { + if data.len() > usize::from(OP_PUSHBYTES_75) { + return None; + } + let mut out = Vec::with_capacity(data.len() + 1); + encode_push(&mut out, data); + Some(out) +} diff --git a/crates/script_verify/src/lib.rs b/crates/script_verify/src/lib.rs new file mode 100644 index 0000000000..af7cd83501 --- /dev/null +++ b/crates/script_verify/src/lib.rs @@ -0,0 +1,243 @@ +//! Pure-Rust verification of a transaction input's scripts and witness under Bitcoin's consensus +//! script rules. +//! +//! [`verify_input`] runs one input's scriptSig, the scriptPubKey it spends and its witness, as +//! Bitcoin Core's `VerifyScript` does. Spent outputs that are not known are passed as `None`. The +//! result then tells apart a script that failed from one whose outcome depends on data that is +//! missing. +//! [`verify_transaction`] checks all inputs while reusing transaction-wide signature hashes. +//! +//! Only scripts are checked: not amounts, locktimes against the chain, double spends or any other +//! transaction or block rule. +#![no_std] +#![warn(missing_docs)] + +extern crate alloc; +#[cfg(any(feature = "std", test))] +extern crate std; + +mod crypto; +mod interpreter; +mod scriptnum; +mod sighash; + +#[cfg(test)] +mod tests; + +use alloc::vec; +use alloc::vec::Vec; +use core::fmt; + +use bitcoin::{Transaction, TxOut}; + +use sighash::Sighasher; + +/// Which script rules apply. +/// +/// Each flag is one of Bitcoin Core's consensus `SCRIPT_VERIFY_*` flags. Policy (standardness) +/// flags are not supported. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub struct Flags { + /// BIP16 pay-to-script-hash. + pub p2sh: bool, + /// BIP66 strict DER signatures. + pub dersig: bool, + /// BIP65 `OP_CHECKLOCKTIMEVERIFY`. + pub checklocktimeverify: bool, + /// BIP112 `OP_CHECKSEQUENCEVERIFY`. + pub checksequenceverify: bool, + /// BIP141/BIP143 segregated witness. Requires `p2sh`. + pub witness: bool, + /// BIP147 empty `OP_CHECKMULTISIG` dummy. + pub nulldummy: bool, + /// BIP341/BIP342 taproot. Requires `witness`. + pub taproot: bool, +} + +impl Flags { + /// Every consensus rule active at the tip of the Bitcoin main chain. + pub const CONSENSUS: Self = Self { + p2sh: true, + dersig: true, + checklocktimeverify: true, + checksequenceverify: true, + witness: true, + nulldummy: true, + taproot: true, + }; +} + +impl Default for Flags { + fn default() -> Self { + Self::CONSENSUS + } +} + +/// The outcome of [`verify_input`]. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Verification { + /// `Ok` if the input's scripts pass. + pub result: Result<(), Error>, + /// Which entries of the `prevouts` argument the result depends on. + /// + /// The same transaction, flags and values of these entries always give the same result. + pub depends_on: DependsOn, +} + +/// The spent outputs a [`Verification`] depends on. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum DependsOn { + /// Only the output spent by the verified input. + OwnPrevout, + /// The outputs spent by every input of the transaction, as a BIP341 signature hash commits to + /// them all. + AllPrevouts, +} + +/// Why an input did not verify. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum Error { + /// The outcome depends on spent outputs that were passed as `None`. Holds their input indices. + /// + /// This is not a failure: the input may verify once they are known. + MissingPrevouts(Vec), + /// The scripts conclusively failed. + Script(ScriptError), +} + +/// A conclusive script failure, named after Bitcoin Core's `SCRIPT_ERR_*` code. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct ScriptError(&'static str); + +impl ScriptError { + /// Bitcoin Core's name for the failure, without its `SCRIPT_ERR_` prefix. + pub fn code(&self) -> &'static str { + self.0 + } +} + +impl fmt::Display for ScriptError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(self.0) + } +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Error::MissingPrevouts(inputs) => write!(f, "missing prevouts of inputs {inputs:?}"), + Error::Script(e) => write!(f, "script failed: {e}"), + } + } +} + +#[cfg(feature = "std")] +impl std::error::Error for Error {} + +#[derive(Debug)] +enum Fail { + Script(&'static str), + Missing(Vec), +} + +impl From for Error { + fn from(fail: Fail) -> Self { + match fail { + Fail::Script(code) => Error::Script(ScriptError(code)), + Fail::Missing(inputs) => Error::MissingPrevouts(inputs), + } + } +} + +/// Reusable signature-hash data for one transaction and its spent outputs. +/// +/// Borrows immutable data so cached hashes cannot be reused with a different transaction or +/// prevouts. Pass the same cache to [`verify_input`] for each input, in any order. Input verdicts +/// and dependency reports are not cached. +pub struct VerificationCache<'a> { + sighasher: Sighasher<'a>, +} + +impl<'a> VerificationCache<'a> { + /// Creates an empty cache. `prevouts[i]` is the output spent by input `i`, or `None`. + /// Hashes and complete Taproot prevout data are prepared lazily when needed. + /// + /// # Panics + /// + /// If `prevouts.len() != tx.input.len()`. + pub fn new(tx: &'a Transaction, prevouts: &'a [Option<&'a TxOut>]) -> Self { + assert_eq!(prevouts.len(), tx.input.len(), "one prevout per input"); + Self { + sighasher: Sighasher::new(tx, prevouts), + } + } +} + +/// Verifies the scripts of `input_index`, reusing the supplied cache. +/// +/// Only the prevout entries named by [`Verification::depends_on`] affect this input's result, +/// independently of earlier calls. Coinbase inputs always pass. +/// +/// ``` +/// # use bdk_script_verify::{verify_input, Flags, VerificationCache}; +/// # use bitcoin::{Transaction, TxOut}; +/// # fn check(tx: &Transaction, prevouts: &[Option<&TxOut>]) { +/// let mut cache = VerificationCache::new(tx, prevouts); +/// for vin in 0..tx.input.len() { +/// let verification = verify_input(&mut cache, vin, Flags::CONSENSUS); +/// assert!(verification.result.is_ok()); +/// } +/// # } +/// ``` +/// +/// # Panics +/// +/// If `input_index` is out of range for the cache's transaction. +pub fn verify_input( + cache: &mut VerificationCache<'_>, + input_index: usize, + flags: Flags, +) -> Verification { + let sighasher = &cache.sighasher; + assert!( + input_index < sighasher.tx().input.len(), + "input index out of range" + ); + sighasher.reset_dependencies(); + let tx = sighasher.tx(); + let result = if tx.is_coinbase() { + Ok(()) + } else { + match sighasher.prevout(input_index) { + None => Err(Fail::Missing(vec![input_index])), + Some(prevout) => interpreter::verify_script(sighasher, input_index, prevout, flags), + } + }; + Verification { + result: result.map_err(Error::from), + depends_on: if sighasher.read_all_prevouts() { + DependsOn::AllPrevouts + } else { + DependsOn::OwnPrevout + }, + } +} + +/// Verifies every input with one cache, returning results in input order. +/// +/// Equivalent to creating a [`VerificationCache`] and calling [`verify_input`] for each input. +/// Missing prevouts are represented by `None`; all inputs are checked even if an earlier one fails. +/// +/// # Panics +/// +/// If `prevouts.len() != tx.input.len()`. +pub fn verify_transaction( + tx: &Transaction, + prevouts: &[Option<&TxOut>], + flags: Flags, +) -> Vec { + let mut cache = VerificationCache::new(tx, prevouts); + (0..tx.input.len()) + .map(|vin| verify_input(&mut cache, vin, flags)) + .collect() +} diff --git a/crates/script_verify/src/scriptnum.rs b/crates/script_verify/src/scriptnum.rs new file mode 100644 index 0000000000..e7a839dbda --- /dev/null +++ b/crates/script_verify/src/scriptnum.rs @@ -0,0 +1,51 @@ +// Adapted from rbitcoin `crates/rbitcoin-primitives/src/scriptnum.rs` at +// 49210ea82130474a4df366e253d1346a16369fdb (MIT OR Apache-2.0, rbitcoin contributors). + +use alloc::vec::Vec; + +use crate::Fail; + +pub fn encode(mut n: i64) -> Vec { + if n == 0 { + return Vec::new(); + } + let neg = n < 0; + if neg { + n = -n; + } + let mut out = Vec::new(); + while n > 0 { + out.push((n & 0xff) as u8); + n >>= 8; + } + if out.last().map(|b| b & 0x80 != 0).unwrap_or(false) { + out.push(if neg { 0x80 } else { 0x00 }); + } else if neg { + let last = out.last_mut().expect("n != 0 so out is non-empty"); + *last |= 0x80; + } + out +} + +pub fn decode(v: &[u8]) -> Result { + decode_width(v, 4) +} + +/// `max_len` is 4 for arithmetic. CLTV and CSV use 5 so that every `u32` encodes as positive. +pub fn decode_width(v: &[u8], max_len: usize) -> Result { + if v.len() > max_len { + return Err(Fail::Script("SCRIPTNUM_OVERFLOW")); + } + let Some(&last) = v.last() else { + return Ok(0); + }; + let mut result: i64 = 0; + for (i, &b) in v.iter().enumerate() { + result |= (b as i64) << (8 * i); + } + if last & 0x80 != 0 { + result &= !(0x80i64 << (8 * (v.len() - 1))); + result = -result; + } + Ok(result) +} diff --git a/crates/script_verify/src/sighash.rs b/crates/script_verify/src/sighash.rs new file mode 100644 index 0000000000..702abdf265 --- /dev/null +++ b/crates/script_verify/src/sighash.rs @@ -0,0 +1,257 @@ +// BIP143 adapted from `bip143_signature_hash` in rbitcoin +// `crates/rbitcoin-consensus/src/script/mod.rs` at 49210ea82130474a4df366e253d1346a16369fdb (MIT OR +// Apache-2.0, rbitcoin contributors). + +use alloc::vec::Vec; +use core::cell::{Cell, OnceCell, RefCell}; + +use bitcoin::consensus::Encodable; +use bitcoin::hashes::{sha256d, Hash, HashEngine}; +use bitcoin::sighash::{Annex, EcdsaSighashType, Prevouts, SighashCache, TapSighashType}; +use bitcoin::{Amount, Script, TapLeafHash, Transaction, TxOut}; + +use crate::Fail; + +/// Signature hashing for the inputs of one transaction, plus a record of which spent outputs the +/// hashes committed to. +pub struct Sighasher<'a> { + tx: &'a Transaction, + prevouts: &'a [Option<&'a TxOut>], + cache: RefCell>, + bip143: OnceCell, + all_prevouts: OnceCell, Vec>>, + read_all_prevouts: Cell, +} + +struct Bip143Midstate { + prevouts: [u8; 32], + sequences: [u8; 32], + outputs: [u8; 32], +} + +impl<'a> Sighasher<'a> { + pub fn new(tx: &'a Transaction, prevouts: &'a [Option<&'a TxOut>]) -> Self { + #[cfg(test)] + work::record(|counts| counts.contexts += 1); + Self { + tx, + prevouts, + cache: RefCell::new(SighashCache::new(tx)), + bip143: OnceCell::new(), + all_prevouts: OnceCell::new(), + read_all_prevouts: Cell::new(false), + } + } + + pub fn tx(&self) -> &'a Transaction { + self.tx + } + + pub fn prevout(&self, input_index: usize) -> Option<&'a TxOut> { + self.prevouts[input_index] + } + + pub fn read_all_prevouts(&self) -> bool { + self.read_all_prevouts.get() + } + + pub fn reset_dependencies(&self) { + self.read_all_prevouts.set(false); + } + + /// `script_code` must already have its `OP_CODESEPARATOR`s and FindAndDelete matches removed. + pub fn legacy(&self, input_index: usize, script_code: &[u8], raw_ty: u32) -> [u8; 32] { + self.cache + .borrow() + .legacy_signature_hash(input_index, Script::from_bytes(script_code), raw_ty) + .expect("input index is in range") + .to_byte_array() + } + + /// BIP143 with the raw `nHashType`. + /// + /// rust-bitcoin's segwit v0 sighash takes an [`EcdsaSighashType`], which serializes a + /// normalized type. Consensus commits to the raw byte, and mainnet has non-standard ones (e.g. + /// `0x65`). + pub fn segwit_v0( + &self, + input_index: usize, + script_code: &Script, + amount: Amount, + raw_ty: u32, + ) -> [u8; 32] { + use EcdsaSighashType::*; + let tx = self.tx; + let mapped = EcdsaSighashType::from_consensus(raw_ty); + let anyone_can_pay = matches!( + mapped, + AllPlusAnyoneCanPay | NonePlusAnyoneCanPay | SinglePlusAnyoneCanPay + ); + let base = match mapped { + None | NonePlusAnyoneCanPay => None, + Single | SinglePlusAnyoneCanPay => Single, + _ => All, + }; + let zero = [0u8; 32]; + let mid = self.bip143.get_or_init(|| { + #[cfg(test)] + work::record(|counts| counts.bip143 += 1); + Bip143Midstate::new(tx) + }); + + let hash_prevouts = if anyone_can_pay { zero } else { mid.prevouts }; + let hash_sequence = if !anyone_can_pay && base == All { + mid.sequences + } else { + zero + }; + let hash_outputs = if base == All { + mid.outputs + } else if base == Single && input_index < tx.output.len() { + let mut eng = sha256d::Hash::engine(); + tx.output[input_index] + .consensus_encode(&mut eng) + .expect("hash engines do not error"); + sha256d::Hash::from_engine(eng).to_byte_array() + } else { + zero + }; + + let txin = &tx.input[input_index]; + let mut eng = sha256d::Hash::engine(); + tx.version + .consensus_encode(&mut eng) + .expect("hash engines do not error"); + eng.input(&hash_prevouts); + eng.input(&hash_sequence); + txin.previous_output + .consensus_encode(&mut eng) + .expect("hash engines do not error"); + script_code + .consensus_encode(&mut eng) + .expect("hash engines do not error"); + amount + .consensus_encode(&mut eng) + .expect("hash engines do not error"); + txin.sequence + .consensus_encode(&mut eng) + .expect("hash engines do not error"); + eng.input(&hash_outputs); + tx.lock_time + .consensus_encode(&mut eng) + .expect("hash engines do not error"); + eng.input(&raw_ty.to_le_bytes()); + sha256d::Hash::from_engine(eng).to_byte_array() + } + + /// BIP341 signature hash. `leaf` is `None` on the key path. + /// + /// Unless the type is ANYONECANPAY the hash commits to every input's spent output, so a missing + /// one leaves the result undetermined rather than invalid. + pub fn taproot( + &self, + input_index: usize, + annex: Option<&[u8]>, + leaf: Option<(TapLeafHash, u32)>, + ty: TapSighashType, + ) -> Result<[u8; 32], Fail> { + use TapSighashType::*; + let annex = annex.map(|a| Annex::new(a).expect("caller checked the 0x50 tag")); + let anyone_can_pay = matches!( + ty, + AllPlusAnyoneCanPay | NonePlusAnyoneCanPay | SinglePlusAnyoneCanPay + ); + let mut cache = self.cache.borrow_mut(); + let hash = if anyone_can_pay { + let own = + self.prevouts[input_index].expect("own prevout is checked before scripts run"); + cache.taproot_signature_hash( + input_index, + &Prevouts::One(input_index, own), + annex, + leaf, + ty, + ) + } else { + self.read_all_prevouts.set(true); + let all = self.all_prevouts.get_or_init(|| { + #[cfg(test)] + work::record(|counts| counts.taproot_prevouts += 1); + let missing = self + .prevouts + .iter() + .enumerate() + .filter(|(_, p)| p.is_none()) + .map(|(i, _)| i) + .collect::>(); + if missing.is_empty() { + Ok(self.prevouts.iter().flatten().copied().collect()) + } else { + Err(missing) + } + }); + let all = all + .as_ref() + .map_err(|missing| Fail::Missing(missing.clone()))?; + cache.taproot_signature_hash(input_index, &Prevouts::All(all), annex, leaf, ty) + }; + // The only reachable error is SIGHASH_SINGLE without a matching output, which Core reports + // as a bad hashtype. + hash.map(|h| h.to_byte_array()) + .map_err(|_| Fail::Script("SCHNORR_SIG_HASHTYPE")) + } +} + +impl Bip143Midstate { + fn new(tx: &Transaction) -> Self { + let mut prevouts = sha256d::Hash::engine(); + let mut sequences = sha256d::Hash::engine(); + for txin in &tx.input { + txin.previous_output + .consensus_encode(&mut prevouts) + .expect("hash engines do not error"); + txin.sequence + .consensus_encode(&mut sequences) + .expect("hash engines do not error"); + } + let mut outputs = sha256d::Hash::engine(); + for txout in &tx.output { + txout + .consensus_encode(&mut outputs) + .expect("hash engines do not error"); + } + Self { + prevouts: sha256d::Hash::from_engine(prevouts).to_byte_array(), + sequences: sha256d::Hash::from_engine(sequences).to_byte_array(), + outputs: sha256d::Hash::from_engine(outputs).to_byte_array(), + } + } +} + +#[cfg(test)] +pub(crate) mod work { + use core::cell::Cell; + + #[derive(Clone, Copy, Default, Debug, PartialEq, Eq)] + pub struct Counts { + pub contexts: usize, + pub bip143: usize, + pub taproot_prevouts: usize, + } + + std::thread_local! { + static COUNTS: Cell = Cell::new(Counts::default()); + } + + pub fn record(update: impl FnOnce(&mut Counts)) { + COUNTS.with(|cell| { + let mut counts = cell.get(); + update(&mut counts); + cell.set(counts); + }); + } + + pub fn take() -> Counts { + COUNTS.with(|cell| cell.replace(Counts::default())) + } +} diff --git a/crates/script_verify/src/tests.rs b/crates/script_verify/src/tests.rs new file mode 100644 index 0000000000..0f6d3a0535 --- /dev/null +++ b/crates/script_verify/src/tests.rs @@ -0,0 +1,246 @@ +use super::*; +use crate::sighash::work::{self, Counts}; +use bitcoin::hashes::Hash; +use bitcoin::key::{Keypair, TapTweak}; +use bitcoin::script::Builder; +use bitcoin::secp256k1::{Message, Secp256k1, SecretKey}; +use bitcoin::sighash::{EcdsaSighashType, Prevouts, SighashCache, TapSighashType}; +use bitcoin::{ + absolute, transaction, Amount, CompressedPublicKey, OutPoint, ScriptBuf, TxIn, Txid, Witness, +}; + +#[derive(Clone, Copy)] +enum Kind { + Segwit, + Taproot, + TaprootAnyoneCanPay, + Legacy, +} + +fn signed(kinds: &[Kind]) -> (Transaction, Vec) { + let secp = Secp256k1::new(); + let secret = SecretKey::from_slice(&[1; 32]).unwrap(); + let keypair = Keypair::from_secret_key(&secp, &secret); + let pubkey = CompressedPublicKey(secret.public_key(&secp)); + let prevouts = kinds + .iter() + .map(|kind| TxOut { + value: Amount::from_sat(10_000), + script_pubkey: match kind { + Kind::Segwit => ScriptBuf::new_p2wpkh(&pubkey.wpubkey_hash()), + Kind::Taproot | Kind::TaprootAnyoneCanPay => { + ScriptBuf::new_p2tr(&secp, keypair.x_only_public_key().0, None) + } + Kind::Legacy => { + ScriptBuf::new_p2pkh(&bitcoin::PublicKey::from(pubkey).pubkey_hash()) + } + }, + }) + .collect::>(); + let mut tx = Transaction { + version: transaction::Version::TWO, + lock_time: absolute::LockTime::ZERO, + input: (0..kinds.len()) + .map(|i| TxIn { + previous_output: OutPoint::new(Txid::from_byte_array([2; 32]), i as u32), + ..TxIn::default() + }) + .collect(), + output: vec![TxOut { + value: Amount::from_sat(1_000), + script_pubkey: ScriptBuf::new(), + }], + }; + for (vin, kind) in kinds.iter().enumerate() { + match kind { + Kind::Segwit | Kind::Legacy => { + let mut cache = SighashCache::new(&tx); + let hash = match kind { + Kind::Segwit => cache + .p2wpkh_signature_hash( + vin, + &prevouts[vin].script_pubkey, + prevouts[vin].value, + EcdsaSighashType::All, + ) + .unwrap() + .to_byte_array(), + _ => cache + .legacy_signature_hash( + vin, + &prevouts[vin].script_pubkey, + EcdsaSighashType::All as u32, + ) + .unwrap() + .to_byte_array(), + }; + let sig = bitcoin::ecdsa::Signature::sighash_all( + secp.sign_ecdsa(&Message::from_digest(hash), &secret), + ); + match kind { + Kind::Segwit => tx.input[vin].witness = Witness::p2wpkh(&sig, &pubkey.0), + _ => { + tx.input[vin].script_sig = Builder::new() + .push_slice(sig.serialize()) + .push_key(&pubkey.into()) + .into_script() + } + } + } + Kind::Taproot | Kind::TaprootAnyoneCanPay => { + let ty = if matches!(kind, Kind::Taproot) { + TapSighashType::Default + } else { + TapSighashType::AllPlusAnyoneCanPay + }; + let hash = SighashCache::new(&tx) + .taproot_key_spend_signature_hash(vin, &Prevouts::All(&prevouts), ty) + .unwrap(); + let tweaked = keypair.tap_tweak(&secp, None).to_keypair(); + let sig = bitcoin::taproot::Signature { + signature: secp.sign_schnorr_no_aux_rand( + &Message::from_digest(hash.to_byte_array()), + &tweaked, + ), + sighash_type: ty, + }; + tx.input[vin].witness = Witness::from_slice(&[sig.to_vec()]); + } + } + } + (tx, prevouts) +} + +fn check_parity(tx: &Transaction, prevouts: &[Option<&TxOut>]) -> Vec { + let shared = verify_transaction(tx, prevouts, Flags::CONSENSUS); + let independent = (0..tx.input.len()) + .map(|vin| { + verify_input( + &mut VerificationCache::new(tx, prevouts), + vin, + Flags::CONSENSUS, + ) + }) + .collect::>(); + assert_eq!(shared, independent); + let mut context = VerificationCache::new(tx, prevouts); + for vin in (0..tx.input.len()) + .rev() + .chain(0..tx.input.len()) + .chain((0..tx.input.len()).rev()) + { + assert_eq!( + verify_input(&mut context, vin, Flags::CONSENSUS), + independent[vin] + ); + } + shared +} + +#[test] +fn transaction_api_reuses_shared_work() { + for kind in [Kind::Segwit, Kind::Taproot] { + for n in [1, 8, 64] { + let (tx, spent) = signed(&vec![kind; n]); + let prevouts = spent.iter().map(Some).collect::>(); + work::take(); + let shared = verify_transaction(&tx, &prevouts, Flags::CONSENSUS); + assert!(shared.iter().all(|v| v.result.is_ok())); + assert_eq!( + work::take(), + Counts { + contexts: 1, + bip143: usize::from(matches!(kind, Kind::Segwit)), + taproot_prevouts: usize::from(matches!(kind, Kind::Taproot)), + } + ); + for (vin, expected) in shared.iter().enumerate() { + assert_eq!( + &verify_input( + &mut VerificationCache::new(&tx, &prevouts), + vin, + Flags::CONSENSUS + ), + expected + ); + } + assert_eq!( + work::take(), + Counts { + contexts: n, + bip143: if matches!(kind, Kind::Segwit) { n } else { 0 }, + taproot_prevouts: if matches!(kind, Kind::Taproot) { n } else { 0 }, + } + ); + } + } +} + +#[test] +fn dependencies_are_per_input_even_after_errors_or_missing_prevouts() { + let (tx, spent) = signed(&[ + Kind::Taproot, + Kind::TaprootAnyoneCanPay, + Kind::Segwit, + Kind::Legacy, + ]); + let prevouts = spent.iter().map(Some).collect::>(); + let results = check_parity(&tx, &prevouts); + assert!(results.iter().all(|v| v.result.is_ok())); + assert_eq!( + results.iter().map(|v| v.depends_on).collect::>(), + vec![ + DependsOn::AllPrevouts, + DependsOn::OwnPrevout, + DependsOn::OwnPrevout, + DependsOn::OwnPrevout + ] + ); + + let mut missing = prevouts.clone(); + missing[3] = None; + let results = check_parity(&tx, &missing); + assert_eq!(results[0].result, Err(Error::MissingPrevouts(vec![3]))); + assert_eq!(results[0].depends_on, DependsOn::AllPrevouts); + assert!(results[1].result.is_ok()); + assert_eq!(results[1].depends_on, DependsOn::OwnPrevout); + assert_eq!(results[3].result, Err(Error::MissingPrevouts(vec![3]))); + assert_eq!(results[3].depends_on, DependsOn::OwnPrevout); + + let mut wrong = spent.clone(); + wrong[0].value += Amount::from_sat(1); + let wrong_refs = wrong.iter().map(Some).collect::>(); + let results = check_parity(&tx, &wrong_refs); + assert!(matches!(results[0].result, Err(Error::Script(_)))); + assert_eq!(results[0].depends_on, DependsOn::AllPrevouts); + assert!(results[1..] + .iter() + .all(|v| v.result.is_ok() && v.depends_on == DependsOn::OwnPrevout)); + + let mut junk = tx.clone(); + junk.input[1].witness = Witness::from_slice(&[vec![0; 9]]); + let results = check_parity(&junk, &prevouts); + assert!(matches!(results[1].result, Err(Error::Script(_)))); + assert_eq!(results[1].depends_on, DependsOn::OwnPrevout); + assert_eq!(tx.compute_txid(), junk.compute_txid()); + assert_ne!(tx.compute_wtxid(), junk.compute_wtxid()); + assert!(check_parity(&tx, &prevouts) + .iter() + .all(|v| v.result.is_ok())); +} + +#[test] +fn anyonecanpay_does_not_prepare_other_prevouts() { + let (tx, spent) = signed(&[Kind::TaprootAnyoneCanPay, Kind::TaprootAnyoneCanPay]); + work::take(); + let results = verify_transaction(&tx, &[Some(&spent[0]), None], Flags::CONSENSUS); + assert!(results[0].result.is_ok()); + assert_eq!(results[0].depends_on, DependsOn::OwnPrevout); + assert_eq!( + work::take(), + Counts { + contexts: 1, + ..Counts::default() + } + ); +} diff --git a/crates/script_verify/tests/core_vectors.rs b/crates/script_verify/tests/core_vectors.rs new file mode 100644 index 0000000000..e4a5b31967 --- /dev/null +++ b/crates/script_verify/tests/core_vectors.rs @@ -0,0 +1,553 @@ +//! Runs Bitcoin Core's script and transaction test vectors (see `tests/data/README.md`). +//! +//! Core's own harness checks that dropping flags from a passing row keeps it passing. So a passing +//! row is run under its consensus flags only, while a failing row is run only when all its flags +//! are consensus flags, since a policy flag may be what fails it. + +use std::collections::BTreeMap; + +use bdk_script_verify::{Error, Flags}; +use bitcoin::absolute::LockTime; +use bitcoin::hashes::Hash; +use bitcoin::secp256k1::{Secp256k1, SecretKey}; +use bitcoin::taproot::{LeafVersion, TaprootBuilder}; +use bitcoin::transaction::Version; +use bitcoin::{Amount, OutPoint, ScriptBuf, Sequence, Transaction, TxIn, TxOut, Txid, Witness}; +use serde_json::Value; + +const OP_NAMES: [&str; 90] = [ + "NOP", + "VER", + "IF", + "NOTIF", + "VERIF", + "VERNOTIF", + "ELSE", + "ENDIF", + "VERIFY", + "RETURN", + "TOALTSTACK", + "FROMALTSTACK", + "2DROP", + "2DUP", + "3DUP", + "2OVER", + "2ROT", + "2SWAP", + "IFDUP", + "DEPTH", + "DROP", + "DUP", + "NIP", + "OVER", + "PICK", + "ROLL", + "ROT", + "SWAP", + "TUCK", + "CAT", + "SUBSTR", + "LEFT", + "RIGHT", + "SIZE", + "INVERT", + "AND", + "OR", + "XOR", + "EQUAL", + "EQUALVERIFY", + "RESERVED1", + "RESERVED2", + "1ADD", + "1SUB", + "2MUL", + "2DIV", + "NEGATE", + "ABS", + "NOT", + "0NOTEQUAL", + "ADD", + "SUB", + "MUL", + "DIV", + "MOD", + "LSHIFT", + "RSHIFT", + "BOOLAND", + "BOOLOR", + "NUMEQUAL", + "NUMEQUALVERIFY", + "NUMNOTEQUAL", + "LESSTHAN", + "GREATERTHAN", + "LESSTHANOREQUAL", + "GREATERTHANOREQUAL", + "MIN", + "MAX", + "WITHIN", + "RIPEMD160", + "SHA1", + "SHA256", + "HASH160", + "HASH256", + "CODESEPARATOR", + "CHECKSIG", + "CHECKSIGVERIFY", + "CHECKMULTISIG", + "CHECKMULTISIGVERIFY", + "NOP1", + "CHECKLOCKTIMEVERIFY", + "CHECKSEQUENCEVERIFY", + "NOP4", + "NOP5", + "NOP6", + "NOP7", + "NOP8", + "NOP9", + "NOP10", + "CHECKSIGADD", +]; + +fn opcode(name: &str) -> u8 { + let name = name.strip_prefix("OP_").unwrap_or(name); + match name { + "RESERVED" => 0x50, + "INVALIDOPCODE" => 0xff, + _ => { + let i = OP_NAMES + .iter() + .position(|n| *n == name) + .unwrap_or_else(|| panic!("unknown opcode {name}")); + 0x61 + i as u8 + } + } +} + +fn scriptnum(mut n: i64) -> Vec { + let neg = n < 0; + n = n.abs(); + let mut out = Vec::new(); + while n > 0 { + out.push((n & 0xff) as u8); + n >>= 8; + } + if out.last().is_some_and(|b| b & 0x80 != 0) { + out.push(if neg { 0x80 } else { 0 }); + } else if neg { + *out.last_mut().unwrap() |= 0x80; + } + out +} + +fn push(out: &mut Vec, data: &[u8]) { + let n = data.len(); + if n < 0x4c { + out.push(n as u8); + } else if n <= 0xff { + out.extend([0x4c, n as u8]); + } else if n <= 0xffff { + out.push(0x4d); + out.extend((n as u16).to_le_bytes()); + } else { + out.push(0x4e); + out.extend((n as u32).to_le_bytes()); + } + out.extend_from_slice(data); +} + +/// Bitcoin Core's `ParseScript`. +fn parse_script(asm: &str) -> ScriptBuf { + let mut out = Vec::new(); + for word in asm.split_whitespace() { + let is_num = word.bytes().all(|b| b.is_ascii_digit()) + || (word.len() > 1 + && word.starts_with('-') + && word[1..].bytes().all(|b| b.is_ascii_digit())); + if is_num { + let n: i64 = word.parse().unwrap(); + match n { + 0 => out.push(0x00), + -1 => out.push(0x4f), + 1..=16 => out.push(0x50 + n as u8), + _ => push(&mut out, &scriptnum(n)), + } + } else if let Some(hex) = word.strip_prefix("0x").filter(|h| !h.is_empty()) { + out.extend(decode_hex(hex)); + } else if word.len() >= 2 && word.starts_with('\'') && word.ends_with('\'') { + push(&mut out, &word.as_bytes()[1..word.len() - 1]); + } else { + out.push(opcode(word)); + } + } + ScriptBuf::from_bytes(out) +} + +fn decode_hex(hex: &str) -> Vec { + (0..hex.len()) + .step_by(2) + .map(|i| u8::from_str_radix(&hex[i..i + 2], 16).unwrap()) + .collect() +} + +/// The consensus part of a Core flag list, and whether it also named policy flags. +fn parse_flags(list: &str) -> (Flags, bool) { + let mut flags = Flags { + p2sh: false, + dersig: false, + checklocktimeverify: false, + checksequenceverify: false, + witness: false, + nulldummy: false, + taproot: false, + }; + let mut policy = false; + for name in list.split(',').filter(|n| !n.is_empty() && *n != "NONE") { + match name { + "P2SH" => flags.p2sh = true, + "DERSIG" => flags.dersig = true, + "CHECKLOCKTIMEVERIFY" => flags.checklocktimeverify = true, + "CHECKSEQUENCEVERIFY" => flags.checksequenceverify = true, + "WITNESS" => flags.witness = true, + "NULLDUMMY" => flags.nulldummy = true, + "TAPROOT" => flags.taproot = true, + _ => policy = true, + } + } + (flags, policy) +} + +/// Core's `#CONTROLBLOCK#` and `#TAPROOTOUTPUT#`: a tree whose only leaf is `leaf`. The internal +/// key differs from Core's, which no row depends on. +fn single_leaf_taproot(leaf: ScriptBuf) -> (ScriptBuf, Vec) { + let secp = Secp256k1::new(); + let (internal_key, _) = SecretKey::from_slice(&[1; 32]) + .unwrap() + .x_only_public_key(&secp); + let info = TaprootBuilder::new() + .add_leaf(0, leaf.clone()) + .unwrap() + .finalize(&secp, internal_key) + .unwrap(); + let control_block = info.control_block(&(leaf, LeafVersion::TapScript)).unwrap(); + ( + ScriptBuf::new_p2tr_tweaked(info.output_key()), + control_block.serialize(), + ) +} + +#[test] +fn core_script_tests() { + let rows: Vec = serde_json::from_str(include_str!("data/script_tests.json")).unwrap(); + let (mut ran, mut skipped) = (0, 0); + for row in rows { + let row = row.as_array().unwrap(); + if row.len() == 1 { + continue; + } + let mut taproot_output = None; + let (witness, amount, row) = match row[0].as_array() { + Some(wit) => { + let (amount, elements) = wit.split_last().unwrap(); + let mut items: Vec> = Vec::new(); + for element in elements { + let element = element.as_str().unwrap(); + if let Some(asm) = element.strip_prefix("#SCRIPT#") { + items.push(parse_script(asm).into_bytes()); + } else if element == "#CONTROLBLOCK#" { + let (output, control_block) = single_leaf_taproot(ScriptBuf::from_bytes( + items.last().unwrap().clone(), + )); + taproot_output = Some(output); + items.push(control_block); + } else { + items.push(decode_hex(element)); + } + } + let sats = (amount.as_f64().unwrap() * 1e8).round() as u64; + ( + Witness::from_slice(&items), + Amount::from_sat(sats), + &row[1..], + ) + } + None => (Witness::new(), Amount::ZERO, &row[..]), + }; + let strs = row.iter().filter_map(Value::as_str).collect::>(); + let (script_sig, spk, flag_list, expected) = (strs[0], strs[1], strs[2], strs[3]); + let (flags, policy) = parse_flags(flag_list); + let expect_ok = expected == "OK"; + if !expect_ok && policy { + skipped += 1; + continue; + } + + let credit = Transaction { + version: Version::ONE, + lock_time: LockTime::ZERO, + input: vec![TxIn { + previous_output: OutPoint::null(), + script_sig: ScriptBuf::from_bytes(vec![0x00, 0x00]), + sequence: Sequence::MAX, + witness: Witness::new(), + }], + output: vec![TxOut { + value: amount, + script_pubkey: match spk { + "0x51 0x20 #TAPROOTOUTPUT#" => taproot_output.take().unwrap(), + _ => parse_script(spk), + }, + }], + }; + let spend = Transaction { + version: Version::ONE, + lock_time: LockTime::ZERO, + input: vec![TxIn { + previous_output: OutPoint::new(credit.compute_txid(), 0), + script_sig: parse_script(script_sig), + sequence: Sequence::MAX, + witness: witness.clone(), + }], + output: vec![TxOut { + value: amount, + script_pubkey: ScriptBuf::new(), + }], + }; + let v = bdk_script_verify::verify_input( + &mut bdk_script_verify::VerificationCache::new(&spend, &[Some(&credit.output[0])]), + 0, + flags, + ); + match (&v.result, expect_ok) { + (Ok(()), true) | (Err(Error::Script(_)), false) => {} + _ => panic!("{row:?}: expected {expected}, got {:?}", v.result), + } + ran += 1; + } + assert!(ran >= 761, "ran {ran}, skipped {skipped}"); +} + +/// A `tx_valid.json` or `tx_invalid.json` row: the transaction, the outputs it spends and its +/// flag list. +fn parse_tx_row(row: &[Value]) -> (Transaction, Vec, &str) { + let mut spent = BTreeMap::new(); + for p in row[0].as_array().unwrap() { + let p = p.as_array().unwrap(); + let txid: Txid = p[0].as_str().unwrap().parse().unwrap(); + let vout = p[1].as_i64().unwrap() as u32; + let outpoint = if vout == u32::MAX && txid == Txid::all_zeros() { + OutPoint::null() + } else { + OutPoint::new(txid, vout) + }; + let value = p.get(3).and_then(Value::as_u64).unwrap_or(0); + spent.insert( + outpoint, + TxOut { + value: Amount::from_sat(value), + script_pubkey: parse_script(p[2].as_str().unwrap()), + }, + ); + } + let tx: Transaction = + bitcoin::consensus::deserialize(&decode_hex(row[1].as_str().unwrap())).unwrap(); + let prevouts = tx + .input + .iter() + .map(|txin| spent[&txin.previous_output].clone()) + .collect(); + (tx, prevouts, row[2].as_str().unwrap()) +} + +fn verify_tx(tx: &Transaction, prevouts: &[TxOut], flags: Flags) -> Vec> { + let prevouts = prevouts.iter().map(Some).collect::>(); + bdk_script_verify::verify_transaction(tx, &prevouts, flags) + .into_iter() + .enumerate() + .map(|(vin, verification)| { + assert_eq!( + verification, + bdk_script_verify::verify_input( + &mut bdk_script_verify::VerificationCache::new(tx, &prevouts), + vin, + flags + ) + ); + verification.result + }) + .collect() +} + +#[test] +fn core_tx_valid() { + let rows: Vec = serde_json::from_str(include_str!("data/tx_valid.json")).unwrap(); + let mut ran = 0; + for row in rows { + let row = row.as_array().unwrap(); + if !row[0].is_array() { + continue; + } + let (tx, prevouts, flag_list) = parse_tx_row(row); + let (excluded, _) = parse_flags(flag_list); + let flags = Flags { + p2sh: !excluded.p2sh, + dersig: !excluded.dersig, + checklocktimeverify: !excluded.checklocktimeverify, + checksequenceverify: !excluded.checksequenceverify, + witness: !excluded.witness, + nulldummy: !excluded.nulldummy, + taproot: !excluded.taproot, + }; + for (vin, result) in verify_tx(&tx, &prevouts, flags).into_iter().enumerate() { + assert_eq!(result, Ok(()), "{row:?} input {vin}"); + } + ran += 1; + } + assert!(ran > 100, "ran {ran}"); +} + +/// Unlike `tx_valid.json`, a row's flags here are the ones applied, and Core checks they are +/// minimal: the row fails under them, still fails with any added, and passes with any one removed. +#[test] +fn core_tx_invalid() { + let rows: Vec = serde_json::from_str(include_str!("data/tx_invalid.json")).unwrap(); + let (mut ran, mut skipped) = (0, 0); + for row in rows { + let row = row.as_array().unwrap(); + if !row[0].is_array() { + continue; + } + let (tx, prevouts, flag_list) = parse_tx_row(row); + // BADTX rows fail `CheckTransaction`, which is not script validation. + if flag_list == "BADTX" { + skipped += 1; + continue; + } + let (flags, policy) = parse_flags(flag_list); + if policy { + skipped += 1; + continue; + } + + for applied in [flags, Flags::CONSENSUS] { + let results = verify_tx(&tx, &prevouts, applied); + assert!( + results.iter().any(|r| matches!(r, Err(Error::Script(_)))), + "{row:?} under {applied:?}: {results:?}" + ); + } + for fewer in without_each_flag(flags) { + let results = verify_tx(&tx, &prevouts, fewer); + assert!( + results.iter().all(Result::is_ok), + "{row:?} under {fewer:?}: {results:?}" + ); + } + ran += 1; + } + assert!(ran >= 70, "ran {ran}, skipped {skipped}"); +} + +/// `flags` with each set flag removed in turn, dropping what then lacks its prerequisite as Core's +/// `TrimFlags` does. +fn without_each_flag(flags: Flags) -> Vec { + let mut out = Vec::new(); + let fields: [fn(&mut Flags) -> &mut bool; 7] = [ + |f| &mut f.p2sh, + |f| &mut f.dersig, + |f| &mut f.checklocktimeverify, + |f| &mut f.checksequenceverify, + |f| &mut f.witness, + |f| &mut f.nulldummy, + |f| &mut f.taproot, + ]; + for field in fields { + let mut fewer = flags; + if !std::mem::take(field(&mut fewer)) { + continue; + } + fewer.witness &= fewer.p2sh; + if !out.contains(&fewer) { + out.push(fewer); + } + } + out +} + +#[test] +fn bip341_key_path_vectors() { + use bdk_script_verify::DependsOn; + + let vectors: Value = + serde_json::from_str(include_str!("data/bip341_wallet_vectors.json")).unwrap(); + let case = &vectors["keyPathSpending"][0]; + let tx: Transaction = bitcoin::consensus::deserialize(&decode_hex( + case["auxiliary"]["fullySignedTx"].as_str().unwrap(), + )) + .unwrap(); + let spent = case["given"]["utxosSpent"] + .as_array() + .unwrap() + .iter() + .map(|u| TxOut { + value: Amount::from_sat(u["amountSats"].as_u64().unwrap()), + script_pubkey: ScriptBuf::from_bytes(decode_hex(u["scriptPubKey"].as_str().unwrap())), + }) + .collect::>(); + let all = spent.iter().map(Some).collect::>(); + + for input in case["inputSpending"].as_array().unwrap() { + let vin = input["given"]["txinIndex"].as_u64().unwrap() as usize; + let hash_type = input["given"]["hashType"].as_u64().unwrap(); + let anyone_can_pay = hash_type & 0x80 != 0; + + let v = bdk_script_verify::verify_input( + &mut bdk_script_verify::VerificationCache::new(&tx, &all), + vin, + Flags::CONSENSUS, + ); + assert_eq!(v.result, Ok(()), "input {vin}"); + + // Without another input's prevout only ANYONECANPAY signatures are decided. + let other = (vin + 1) % tx.input.len(); + let mut partial = all.clone(); + partial[other] = None; + let v = bdk_script_verify::verify_input( + &mut bdk_script_verify::VerificationCache::new(&tx, &partial), + vin, + Flags::CONSENSUS, + ); + if anyone_can_pay { + assert_eq!(v.result, Ok(()), "input {vin}"); + assert_eq!(v.depends_on, DependsOn::OwnPrevout); + } else { + assert_eq!( + v.result, + Err(Error::MissingPrevouts(vec![other])), + "input {vin}" + ); + assert_eq!(v.depends_on, DependsOn::AllPrevouts); + } + + // A wrong amount for another input invalidates exactly the signatures committing to it. + let mut wrong = spent.clone(); + wrong[other].value += Amount::from_sat(1); + let wrong = wrong.iter().map(Some).collect::>(); + let v = bdk_script_verify::verify_input( + &mut bdk_script_verify::VerificationCache::new(&tx, &wrong), + vin, + Flags::CONSENSUS, + ); + assert_eq!(v.result.is_ok(), anyone_can_pay, "input {vin}"); + + let mut tampered = tx.clone(); + let mut sig = tampered.input[vin].witness.nth(0).unwrap().to_vec(); + sig[0] ^= 1; + tampered.input[vin].witness = Witness::from_slice(&[sig]); + let v = bdk_script_verify::verify_input( + &mut bdk_script_verify::VerificationCache::new(&tampered, &all), + vin, + Flags::CONSENSUS, + ); + assert!(matches!(v.result, Err(Error::Script(_))), "input {vin}"); + } +} diff --git a/crates/script_verify/tests/data/README.md b/crates/script_verify/tests/data/README.md new file mode 100644 index 0000000000..21e91badd0 --- /dev/null +++ b/crates/script_verify/tests/data/README.md @@ -0,0 +1,7 @@ +# Bitcoin Core test vectors + +`script_tests.json`, `tx_valid.json`, `tx_invalid.json` and `bip341_wallet_vectors.json` are copied unmodified from Bitcoin Core's +`src/test/data/` at commit `633044f14362108b782b37408385b6e024eaa41b`. + +They are MIT licensed: "Copyright (c) 2009-2026 The Bitcoin Core developers" +(). diff --git a/crates/script_verify/tests/data/bip341_wallet_vectors.json b/crates/script_verify/tests/data/bip341_wallet_vectors.json new file mode 100644 index 0000000000..11261b00ba --- /dev/null +++ b/crates/script_verify/tests/data/bip341_wallet_vectors.json @@ -0,0 +1,452 @@ +{ + "version": 1, + "scriptPubKey": [ + { + "given": { + "internalPubkey": "d6889cb081036e0faefa3a35157ad71086b123b2b144b649798b494c300a961d", + "scriptTree": null + }, + "intermediary": { + "merkleRoot": null, + "tweak": "b86e7be8f39bab32a6f2c0443abbc210f0edac0e2c53d501b36b64437d9c6c70", + "tweakedPubkey": "53a1f6e454df1aa2776a2814a721372d6258050de330b3c6d10ee8f4e0dda343" + }, + "expected": { + "scriptPubKey": "512053a1f6e454df1aa2776a2814a721372d6258050de330b3c6d10ee8f4e0dda343", + "bip350Address": "bc1p2wsldez5mud2yam29q22wgfh9439spgduvct83k3pm50fcxa5dps59h4z5" + } + }, + { + "given": { + "internalPubkey": "187791b6f712a8ea41c8ecdd0ee77fab3e85263b37e1ec18a3651926b3a6cf27", + "scriptTree": { + "id": 0, + "script": "20d85a959b0290bf19bb89ed43c916be835475d013da4b362117393e25a48229b8ac", + "leafVersion": 192 + } + }, + "intermediary": { + "leafHashes": [ + "5b75adecf53548f3ec6ad7d78383bf84cc57b55a3127c72b9a2481752dd88b21" + ], + "merkleRoot": "5b75adecf53548f3ec6ad7d78383bf84cc57b55a3127c72b9a2481752dd88b21", + "tweak": "cbd8679ba636c1110ea247542cfbd964131a6be84f873f7f3b62a777528ed001", + "tweakedPubkey": "147c9c57132f6e7ecddba9800bb0c4449251c92a1e60371ee77557b6620f3ea3" + }, + "expected": { + "scriptPubKey": "5120147c9c57132f6e7ecddba9800bb0c4449251c92a1e60371ee77557b6620f3ea3", + "bip350Address": "bc1pz37fc4cn9ah8anwm4xqqhvxygjf9rjf2resrw8h8w4tmvcs0863sa2e586", + "scriptPathControlBlocks": [ + "c1187791b6f712a8ea41c8ecdd0ee77fab3e85263b37e1ec18a3651926b3a6cf27" + ] + } + }, + { + "given": { + "internalPubkey": "93478e9488f956df2396be2ce6c5cced75f900dfa18e7dabd2428aae78451820", + "scriptTree": { + "id": 0, + "script": "20b617298552a72ade070667e86ca63b8f5789a9fe8731ef91202a91c9f3459007ac", + "leafVersion": 192 + } + }, + "intermediary": { + "leafHashes": [ + "c525714a7f49c28aedbbba78c005931a81c234b2f6c99a73e4d06082adc8bf2b" + ], + "merkleRoot": "c525714a7f49c28aedbbba78c005931a81c234b2f6c99a73e4d06082adc8bf2b", + "tweak": "6af9e28dbf9d6aaf027696e2598a5b3d056f5fd2355a7fd5a37a0e5008132d30", + "tweakedPubkey": "e4d810fd50586274face62b8a807eb9719cef49c04177cc6b76a9a4251d5450e" + }, + "expected": { + "scriptPubKey": "5120e4d810fd50586274face62b8a807eb9719cef49c04177cc6b76a9a4251d5450e", + "bip350Address": "bc1punvppl2stp38f7kwv2u2spltjuvuaayuqsthe34hd2dyy5w4g58qqfuag5", + "scriptPathControlBlocks": [ + "c093478e9488f956df2396be2ce6c5cced75f900dfa18e7dabd2428aae78451820" + ] + } + }, + { + "given": { + "internalPubkey": "ee4fe085983462a184015d1f782d6a5f8b9c2b60130aff050ce221ecf3786592", + "scriptTree": [ + { + "id": 0, + "script": "20387671353e273264c495656e27e39ba899ea8fee3bb69fb2a680e22093447d48ac", + "leafVersion": 192 + }, + { + "id": 1, + "script": "06424950333431", + "leafVersion": 250 + } + ] + }, + "intermediary": { + "leafHashes": [ + "8ad69ec7cf41c2a4001fd1f738bf1e505ce2277acdcaa63fe4765192497f47a7", + "f224a923cd0021ab202ab139cc56802ddb92dcfc172b9212261a539df79a112a" + ], + "merkleRoot": "6c2dc106ab816b73f9d07e3cd1ef2c8c1256f519748e0813e4edd2405d277bef", + "tweak": "9e0517edc8259bb3359255400b23ca9507f2a91cd1e4250ba068b4eafceba4a9", + "tweakedPubkey": "712447206d7a5238acc7ff53fbe94a3b64539ad291c7cdbc490b7577e4b17df5" + }, + "expected": { + "scriptPubKey": "5120712447206d7a5238acc7ff53fbe94a3b64539ad291c7cdbc490b7577e4b17df5", + "bip350Address": "bc1pwyjywgrd0ffr3tx8laflh6228dj98xkjj8rum0zfpd6h0e930h6saqxrrm", + "scriptPathControlBlocks": [ + "c0ee4fe085983462a184015d1f782d6a5f8b9c2b60130aff050ce221ecf3786592f224a923cd0021ab202ab139cc56802ddb92dcfc172b9212261a539df79a112a", + "faee4fe085983462a184015d1f782d6a5f8b9c2b60130aff050ce221ecf37865928ad69ec7cf41c2a4001fd1f738bf1e505ce2277acdcaa63fe4765192497f47a7" + ] + } + }, + { + "given": { + "internalPubkey": "f9f400803e683727b14f463836e1e78e1c64417638aa066919291a225f0e8dd8", + "scriptTree": [ + { + "id": 0, + "script": "2044b178d64c32c4a05cc4f4d1407268f764c940d20ce97abfd44db5c3592b72fdac", + "leafVersion": 192 + }, + { + "id": 1, + "script": "07546170726f6f74", + "leafVersion": 192 + } + ] + }, + "intermediary": { + "leafHashes": [ + "64512fecdb5afa04f98839b50e6f0cb7b1e539bf6f205f67934083cdcc3c8d89", + "2cb2b90daa543b544161530c925f285b06196940d6085ca9474d41dc3822c5cb" + ], + "merkleRoot": "ab179431c28d3b68fb798957faf5497d69c883c6fb1e1cd9f81483d87bac90cc", + "tweak": "639f0281b7ac49e742cd25b7f188657626da1ad169209078e2761cefd91fd65e", + "tweakedPubkey": "77e30a5522dd9f894c3f8b8bd4c4b2cf82ca7da8a3ea6a239655c39c050ab220" + }, + "expected": { + "scriptPubKey": "512077e30a5522dd9f894c3f8b8bd4c4b2cf82ca7da8a3ea6a239655c39c050ab220", + "bip350Address": "bc1pwl3s54fzmk0cjnpl3w9af39je7pv5ldg504x5guk2hpecpg2kgsqaqstjq", + "scriptPathControlBlocks": [ + "c1f9f400803e683727b14f463836e1e78e1c64417638aa066919291a225f0e8dd82cb2b90daa543b544161530c925f285b06196940d6085ca9474d41dc3822c5cb", + "c1f9f400803e683727b14f463836e1e78e1c64417638aa066919291a225f0e8dd864512fecdb5afa04f98839b50e6f0cb7b1e539bf6f205f67934083cdcc3c8d89" + ] + } + }, + { + "given": { + "internalPubkey": "e0dfe2300b0dd746a3f8674dfd4525623639042569d829c7f0eed9602d263e6f", + "scriptTree": [ + { + "id": 0, + "script": "2072ea6adcf1d371dea8fba1035a09f3d24ed5a059799bae114084130ee5898e69ac", + "leafVersion": 192 + }, + [ + { + "id": 1, + "script": "202352d137f2f3ab38d1eaa976758873377fa5ebb817372c71e2c542313d4abda8ac", + "leafVersion": 192 + }, + { + "id": 2, + "script": "207337c0dd4253cb86f2c43a2351aadd82cccb12a172cd120452b9bb8324f2186aac", + "leafVersion": 192 + } + ] + ] + }, + "intermediary": { + "leafHashes": [ + "2645a02e0aac1fe69d69755733a9b7621b694bb5b5cde2bbfc94066ed62b9817", + "ba982a91d4fc552163cb1c0da03676102d5b7a014304c01f0c77b2b8e888de1c", + "9e31407bffa15fefbf5090b149d53959ecdf3f62b1246780238c24501d5ceaf6" + ], + "merkleRoot": "ccbd66c6f7e8fdab47b3a486f59d28262be857f30d4773f2d5ea47f7761ce0e2", + "tweak": "b57bfa183d28eeb6ad688ddaabb265b4a41fbf68e5fed2c72c74de70d5a786f4", + "tweakedPubkey": "91b64d5324723a985170e4dc5a0f84c041804f2cd12660fa5dec09fc21783605" + }, + "expected": { + "scriptPubKey": "512091b64d5324723a985170e4dc5a0f84c041804f2cd12660fa5dec09fc21783605", + "bip350Address": "bc1pjxmy65eywgafs5tsunw95ruycpqcqnev6ynxp7jaasylcgtcxczs6n332e", + "scriptPathControlBlocks": [ + "c0e0dfe2300b0dd746a3f8674dfd4525623639042569d829c7f0eed9602d263e6fffe578e9ea769027e4f5a3de40732f75a88a6353a09d767ddeb66accef85e553", + "c0e0dfe2300b0dd746a3f8674dfd4525623639042569d829c7f0eed9602d263e6f9e31407bffa15fefbf5090b149d53959ecdf3f62b1246780238c24501d5ceaf62645a02e0aac1fe69d69755733a9b7621b694bb5b5cde2bbfc94066ed62b9817", + "c0e0dfe2300b0dd746a3f8674dfd4525623639042569d829c7f0eed9602d263e6fba982a91d4fc552163cb1c0da03676102d5b7a014304c01f0c77b2b8e888de1c2645a02e0aac1fe69d69755733a9b7621b694bb5b5cde2bbfc94066ed62b9817" + ] + } + }, + { + "given": { + "internalPubkey": "55adf4e8967fbd2e29f20ac896e60c3b0f1d5b0efa9d34941b5958c7b0a0312d", + "scriptTree": [ + { + "id": 0, + "script": "2071981521ad9fc9036687364118fb6ccd2035b96a423c59c5430e98310a11abe2ac", + "leafVersion": 192 + }, + [ + { + "id": 1, + "script": "20d5094d2dbe9b76e2c245a2b89b6006888952e2faa6a149ae318d69e520617748ac", + "leafVersion": 192 + }, + { + "id": 2, + "script": "20c440b462ad48c7a77f94cd4532d8f2119dcebbd7c9764557e62726419b08ad4cac", + "leafVersion": 192 + } + ] + ] + }, + "intermediary": { + "leafHashes": [ + "f154e8e8e17c31d3462d7132589ed29353c6fafdb884c5a6e04ea938834f0d9d", + "737ed1fe30bc42b8022d717b44f0d93516617af64a64753b7a06bf16b26cd711", + "d7485025fceb78b9ed667db36ed8b8dc7b1f0b307ac167fa516fe4352b9f4ef7" + ], + "merkleRoot": "2f6b2c5397b6d68ca18e09a3f05161668ffe93a988582d55c6f07bd5b3329def", + "tweak": "6579138e7976dc13b6a92f7bfd5a2fc7684f5ea42419d43368301470f3b74ed9", + "tweakedPubkey": "75169f4001aa68f15bbed28b218df1d0a62cbbcf1188c6665110c293c907b831" + }, + "expected": { + "scriptPubKey": "512075169f4001aa68f15bbed28b218df1d0a62cbbcf1188c6665110c293c907b831", + "bip350Address": "bc1pw5tf7sqp4f50zka7629jrr036znzew70zxyvvej3zrpf8jg8hqcssyuewe", + "scriptPathControlBlocks": [ + "c155adf4e8967fbd2e29f20ac896e60c3b0f1d5b0efa9d34941b5958c7b0a0312d3cd369a528b326bc9d2133cbd2ac21451acb31681a410434672c8e34fe757e91", + "c155adf4e8967fbd2e29f20ac896e60c3b0f1d5b0efa9d34941b5958c7b0a0312dd7485025fceb78b9ed667db36ed8b8dc7b1f0b307ac167fa516fe4352b9f4ef7f154e8e8e17c31d3462d7132589ed29353c6fafdb884c5a6e04ea938834f0d9d", + "c155adf4e8967fbd2e29f20ac896e60c3b0f1d5b0efa9d34941b5958c7b0a0312d737ed1fe30bc42b8022d717b44f0d93516617af64a64753b7a06bf16b26cd711f154e8e8e17c31d3462d7132589ed29353c6fafdb884c5a6e04ea938834f0d9d" + ] + } + } + ], + "keyPathSpending": [ + { + "given": { + "rawUnsignedTx": "02000000097de20cbff686da83a54981d2b9bab3586f4ca7e48f57f5b55963115f3b334e9c010000000000000000d7b7cab57b1393ace2d064f4d4a2cb8af6def61273e127517d44759b6dafdd990000000000fffffffff8e1f583384333689228c5d28eac13366be082dc57441760d957275419a418420000000000fffffffff0689180aa63b30cb162a73c6d2a38b7eeda2a83ece74310fda0843ad604853b0100000000feffffffaa5202bdf6d8ccd2ee0f0202afbbb7461d9264a25e5bfd3c5a52ee1239e0ba6c0000000000feffffff956149bdc66faa968eb2be2d2faa29718acbfe3941215893a2a3446d32acd050000000000000000000e664b9773b88c09c32cb70a2a3e4da0ced63b7ba3b22f848531bbb1d5d5f4c94010000000000000000e9aa6b8e6c9de67619e6a3924ae25696bb7b694bb677a632a74ef7eadfd4eabf0000000000ffffffffa778eb6a263dc090464cd125c466b5a99667720b1c110468831d058aa1b82af10100000000ffffffff0200ca9a3b000000001976a91406afd46bcdfd22ef94ac122aa11f241244a37ecc88ac807840cb0000000020ac9a87f5594be208f8532db38cff670c450ed2fea8fcdefcc9a663f78bab962b0065cd1d", + "utxosSpent": [ + { + "scriptPubKey": "512053a1f6e454df1aa2776a2814a721372d6258050de330b3c6d10ee8f4e0dda343", + "amountSats": 420000000 + }, + { + "scriptPubKey": "5120147c9c57132f6e7ecddba9800bb0c4449251c92a1e60371ee77557b6620f3ea3", + "amountSats": 462000000 + }, + { + "scriptPubKey": "76a914751e76e8199196d454941c45d1b3a323f1433bd688ac", + "amountSats": 294000000 + }, + { + "scriptPubKey": "5120e4d810fd50586274face62b8a807eb9719cef49c04177cc6b76a9a4251d5450e", + "amountSats": 504000000 + }, + { + "scriptPubKey": "512091b64d5324723a985170e4dc5a0f84c041804f2cd12660fa5dec09fc21783605", + "amountSats": 630000000 + }, + { + "scriptPubKey": "00147dd65592d0ab2fe0d0257d571abf032cd9db93dc", + "amountSats": 378000000 + }, + { + "scriptPubKey": "512075169f4001aa68f15bbed28b218df1d0a62cbbcf1188c6665110c293c907b831", + "amountSats": 672000000 + }, + { + "scriptPubKey": "5120712447206d7a5238acc7ff53fbe94a3b64539ad291c7cdbc490b7577e4b17df5", + "amountSats": 546000000 + }, + { + "scriptPubKey": "512077e30a5522dd9f894c3f8b8bd4c4b2cf82ca7da8a3ea6a239655c39c050ab220", + "amountSats": 588000000 + } + ] + }, + "intermediary": { + "hashAmounts": "58a6964a4f5f8f0b642ded0a8a553be7622a719da71d1f5befcefcdee8e0fde6", + "hashOutputs": "a2e6dab7c1f0dcd297c8d61647fd17d821541ea69c3cc37dcbad7f90d4eb4bc5", + "hashPrevouts": "e3b33bb4ef3a52ad1fffb555c0d82828eb22737036eaeb02a235d82b909c4c3f", + "hashScriptPubkeys": "23ad0f61ad2bca5ba6a7693f50fce988e17c3780bf2b1e720cfbb38fbdd52e21", + "hashSequences": "18959c7221ab5ce9e26c3cd67b22c24f8baa54bac281d8e6b05e400e6c3a957e" + }, + "inputSpending": [ + { + "given": { + "txinIndex": 0, + "internalPrivkey": "6b973d88838f27366ed61c9ad6367663045cb456e28335c109e30717ae0c6baa", + "merkleRoot": null, + "hashType": 3 + }, + "intermediary": { + "internalPubkey": "d6889cb081036e0faefa3a35157ad71086b123b2b144b649798b494c300a961d", + "tweak": "b86e7be8f39bab32a6f2c0443abbc210f0edac0e2c53d501b36b64437d9c6c70", + "tweakedPrivkey": "2405b971772ad26915c8dcdf10f238753a9b837e5f8e6a86fd7c0cce5b7296d9", + "sigMsg": "0003020000000065cd1de3b33bb4ef3a52ad1fffb555c0d82828eb22737036eaeb02a235d82b909c4c3f58a6964a4f5f8f0b642ded0a8a553be7622a719da71d1f5befcefcdee8e0fde623ad0f61ad2bca5ba6a7693f50fce988e17c3780bf2b1e720cfbb38fbdd52e2118959c7221ab5ce9e26c3cd67b22c24f8baa54bac281d8e6b05e400e6c3a957e0000000000d0418f0e9a36245b9a50ec87f8bf5be5bcae434337b87139c3a5b1f56e33cba0", + "precomputedUsed": [ + "hashAmounts", + "hashPrevouts", + "hashScriptPubkeys", + "hashSequences" + ], + "sigHash": "2514a6272f85cfa0f45eb907fcb0d121b808ed37c6ea160a5a9046ed5526d555" + }, + "expected": { + "witness": [ + "ed7c1647cb97379e76892be0cacff57ec4a7102aa24296ca39af7541246d8ff14d38958d4cc1e2e478e4d4a764bbfd835b16d4e314b72937b29833060b87276c03" + ] + } + }, + { + "given": { + "txinIndex": 1, + "internalPrivkey": "1e4da49f6aaf4e5cd175fe08a32bb5cb4863d963921255f33d3bc31e1343907f", + "merkleRoot": "5b75adecf53548f3ec6ad7d78383bf84cc57b55a3127c72b9a2481752dd88b21", + "hashType": 131 + }, + "intermediary": { + "internalPubkey": "187791b6f712a8ea41c8ecdd0ee77fab3e85263b37e1ec18a3651926b3a6cf27", + "tweak": "cbd8679ba636c1110ea247542cfbd964131a6be84f873f7f3b62a777528ed001", + "tweakedPrivkey": "ea260c3b10e60f6de018455cd0278f2f5b7e454be1999572789e6a9565d26080", + "sigMsg": "0083020000000065cd1d00d7b7cab57b1393ace2d064f4d4a2cb8af6def61273e127517d44759b6dafdd9900000000808f891b00000000225120147c9c57132f6e7ecddba9800bb0c4449251c92a1e60371ee77557b6620f3ea3ffffffffffcef8fb4ca7efc5433f591ecfc57391811ce1e186a3793024def5c884cba51d", + "precomputedUsed": [], + "sigHash": "325a644af47e8a5a2591cda0ab0723978537318f10e6a63d4eed783b96a71a4d" + }, + "expected": { + "witness": [ + "052aedffc554b41f52b521071793a6b88d6dbca9dba94cf34c83696de0c1ec35ca9c5ed4ab28059bd606a4f3a657eec0bb96661d42921b5f50a95ad33675b54f83" + ] + } + }, + { + "given": { + "txinIndex": 3, + "internalPrivkey": "d3c7af07da2d54f7a7735d3d0fc4f0a73164db638b2f2f7c43f711f6d4aa7e64", + "merkleRoot": "c525714a7f49c28aedbbba78c005931a81c234b2f6c99a73e4d06082adc8bf2b", + "hashType": 1 + }, + "intermediary": { + "internalPubkey": "93478e9488f956df2396be2ce6c5cced75f900dfa18e7dabd2428aae78451820", + "tweak": "6af9e28dbf9d6aaf027696e2598a5b3d056f5fd2355a7fd5a37a0e5008132d30", + "tweakedPrivkey": "97323385e57015b75b0339a549c56a948eb961555973f0951f555ae6039ef00d", + "sigMsg": "0001020000000065cd1de3b33bb4ef3a52ad1fffb555c0d82828eb22737036eaeb02a235d82b909c4c3f58a6964a4f5f8f0b642ded0a8a553be7622a719da71d1f5befcefcdee8e0fde623ad0f61ad2bca5ba6a7693f50fce988e17c3780bf2b1e720cfbb38fbdd52e2118959c7221ab5ce9e26c3cd67b22c24f8baa54bac281d8e6b05e400e6c3a957ea2e6dab7c1f0dcd297c8d61647fd17d821541ea69c3cc37dcbad7f90d4eb4bc50003000000", + "precomputedUsed": [ + "hashAmounts", + "hashOutputs", + "hashPrevouts", + "hashScriptPubkeys", + "hashSequences" + ], + "sigHash": "bf013ea93474aa67815b1b6cc441d23b64fa310911d991e713cd34c7f5d46669" + }, + "expected": { + "witness": [ + "ff45f742a876139946a149ab4d9185574b98dc919d2eb6754f8abaa59d18b025637a3aa043b91817739554f4ed2026cf8022dbd83e351ce1fabc272841d2510a01" + ] + } + }, + { + "given": { + "txinIndex": 4, + "internalPrivkey": "f36bb07a11e469ce941d16b63b11b9b9120a84d9d87cff2c84a8d4affb438f4e", + "merkleRoot": "ccbd66c6f7e8fdab47b3a486f59d28262be857f30d4773f2d5ea47f7761ce0e2", + "hashType": 0 + }, + "intermediary": { + "internalPubkey": "e0dfe2300b0dd746a3f8674dfd4525623639042569d829c7f0eed9602d263e6f", + "tweak": "b57bfa183d28eeb6ad688ddaabb265b4a41fbf68e5fed2c72c74de70d5a786f4", + "tweakedPrivkey": "a8e7aa924f0d58854185a490e6c41f6efb7b675c0f3331b7f14b549400b4d501", + "sigMsg": "0000020000000065cd1de3b33bb4ef3a52ad1fffb555c0d82828eb22737036eaeb02a235d82b909c4c3f58a6964a4f5f8f0b642ded0a8a553be7622a719da71d1f5befcefcdee8e0fde623ad0f61ad2bca5ba6a7693f50fce988e17c3780bf2b1e720cfbb38fbdd52e2118959c7221ab5ce9e26c3cd67b22c24f8baa54bac281d8e6b05e400e6c3a957ea2e6dab7c1f0dcd297c8d61647fd17d821541ea69c3cc37dcbad7f90d4eb4bc50004000000", + "precomputedUsed": [ + "hashAmounts", + "hashOutputs", + "hashPrevouts", + "hashScriptPubkeys", + "hashSequences" + ], + "sigHash": "4f900a0bae3f1446fd48490c2958b5a023228f01661cda3496a11da502a7f7ef" + }, + "expected": { + "witness": [ + "b4010dd48a617db09926f729e79c33ae0b4e94b79f04a1ae93ede6315eb3669de185a17d2b0ac9ee09fd4c64b678a0b61a0a86fa888a273c8511be83bfd6810f" + ] + } + }, + { + "given": { + "txinIndex": 6, + "internalPrivkey": "415cfe9c15d9cea27d8104d5517c06e9de48e2f986b695e4f5ffebf230e725d8", + "merkleRoot": "2f6b2c5397b6d68ca18e09a3f05161668ffe93a988582d55c6f07bd5b3329def", + "hashType": 2 + }, + "intermediary": { + "internalPubkey": "55adf4e8967fbd2e29f20ac896e60c3b0f1d5b0efa9d34941b5958c7b0a0312d", + "tweak": "6579138e7976dc13b6a92f7bfd5a2fc7684f5ea42419d43368301470f3b74ed9", + "tweakedPrivkey": "241c14f2639d0d7139282aa6abde28dd8a067baa9d633e4e7230287ec2d02901", + "sigMsg": "0002020000000065cd1de3b33bb4ef3a52ad1fffb555c0d82828eb22737036eaeb02a235d82b909c4c3f58a6964a4f5f8f0b642ded0a8a553be7622a719da71d1f5befcefcdee8e0fde623ad0f61ad2bca5ba6a7693f50fce988e17c3780bf2b1e720cfbb38fbdd52e2118959c7221ab5ce9e26c3cd67b22c24f8baa54bac281d8e6b05e400e6c3a957e0006000000", + "precomputedUsed": [ + "hashAmounts", + "hashPrevouts", + "hashScriptPubkeys", + "hashSequences" + ], + "sigHash": "15f25c298eb5cdc7eb1d638dd2d45c97c4c59dcaec6679cfc16ad84f30876b85" + }, + "expected": { + "witness": [ + "a3785919a2ce3c4ce26f298c3d51619bc474ae24014bcdd31328cd8cfbab2eff3395fa0a16fe5f486d12f22a9cedded5ae74feb4bbe5351346508c5405bcfee002" + ] + } + }, + { + "given": { + "txinIndex": 7, + "internalPrivkey": "c7b0e81f0a9a0b0499e112279d718cca98e79a12e2f137c72ae5b213aad0d103", + "merkleRoot": "6c2dc106ab816b73f9d07e3cd1ef2c8c1256f519748e0813e4edd2405d277bef", + "hashType": 130 + }, + "intermediary": { + "internalPubkey": "ee4fe085983462a184015d1f782d6a5f8b9c2b60130aff050ce221ecf3786592", + "tweak": "9e0517edc8259bb3359255400b23ca9507f2a91cd1e4250ba068b4eafceba4a9", + "tweakedPrivkey": "65b6000cd2bfa6b7cf736767a8955760e62b6649058cbc970b7c0871d786346b", + "sigMsg": "0082020000000065cd1d00e9aa6b8e6c9de67619e6a3924ae25696bb7b694bb677a632a74ef7eadfd4eabf00000000804c8b2000000000225120712447206d7a5238acc7ff53fbe94a3b64539ad291c7cdbc490b7577e4b17df5ffffffff", + "precomputedUsed": [], + "sigHash": "cd292de50313804dabe4685e83f923d2969577191a3e1d2882220dca88cbeb10" + }, + "expected": { + "witness": [ + "ea0c6ba90763c2d3a296ad82ba45881abb4f426b3f87af162dd24d5109edc1cdd11915095ba47c3a9963dc1e6c432939872bc49212fe34c632cd3ab9fed429c482" + ] + } + }, + { + "given": { + "txinIndex": 8, + "internalPrivkey": "77863416be0d0665e517e1c375fd6f75839544eca553675ef7fdf4949518ebaa", + "merkleRoot": "ab179431c28d3b68fb798957faf5497d69c883c6fb1e1cd9f81483d87bac90cc", + "hashType": 129 + }, + "intermediary": { + "internalPubkey": "f9f400803e683727b14f463836e1e78e1c64417638aa066919291a225f0e8dd8", + "tweak": "639f0281b7ac49e742cd25b7f188657626da1ad169209078e2761cefd91fd65e", + "tweakedPrivkey": "ec18ce6af99f43815db543f47b8af5ff5df3b2cb7315c955aa4a86e8143d2bf5", + "sigMsg": "0081020000000065cd1da2e6dab7c1f0dcd297c8d61647fd17d821541ea69c3cc37dcbad7f90d4eb4bc500a778eb6a263dc090464cd125c466b5a99667720b1c110468831d058aa1b82af101000000002b0c230000000022512077e30a5522dd9f894c3f8b8bd4c4b2cf82ca7da8a3ea6a239655c39c050ab220ffffffff", + "precomputedUsed": [ + "hashOutputs" + ], + "sigHash": "cccb739eca6c13a8a89e6e5cd317ffe55669bbda23f2fd37b0f18755e008edd2" + }, + "expected": { + "witness": [ + "bbc9584a11074e83bc8c6759ec55401f0ae7b03ef290c3139814f545b58a9f8127258000874f44bc46db7646322107d4d86aec8e73b8719a61fff761d75b5dd981" + ] + } + } + ], + "auxiliary": { + "fullySignedTx": "020000000001097de20cbff686da83a54981d2b9bab3586f4ca7e48f57f5b55963115f3b334e9c010000000000000000d7b7cab57b1393ace2d064f4d4a2cb8af6def61273e127517d44759b6dafdd990000000000fffffffff8e1f583384333689228c5d28eac13366be082dc57441760d957275419a41842000000006b4830450221008f3b8f8f0537c420654d2283673a761b7ee2ea3c130753103e08ce79201cf32a022079e7ab904a1980ef1c5890b648c8783f4d10103dd62f740d13daa79e298d50c201210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798fffffffff0689180aa63b30cb162a73c6d2a38b7eeda2a83ece74310fda0843ad604853b0100000000feffffffaa5202bdf6d8ccd2ee0f0202afbbb7461d9264a25e5bfd3c5a52ee1239e0ba6c0000000000feffffff956149bdc66faa968eb2be2d2faa29718acbfe3941215893a2a3446d32acd050000000000000000000e664b9773b88c09c32cb70a2a3e4da0ced63b7ba3b22f848531bbb1d5d5f4c94010000000000000000e9aa6b8e6c9de67619e6a3924ae25696bb7b694bb677a632a74ef7eadfd4eabf0000000000ffffffffa778eb6a263dc090464cd125c466b5a99667720b1c110468831d058aa1b82af10100000000ffffffff0200ca9a3b000000001976a91406afd46bcdfd22ef94ac122aa11f241244a37ecc88ac807840cb0000000020ac9a87f5594be208f8532db38cff670c450ed2fea8fcdefcc9a663f78bab962b0141ed7c1647cb97379e76892be0cacff57ec4a7102aa24296ca39af7541246d8ff14d38958d4cc1e2e478e4d4a764bbfd835b16d4e314b72937b29833060b87276c030141052aedffc554b41f52b521071793a6b88d6dbca9dba94cf34c83696de0c1ec35ca9c5ed4ab28059bd606a4f3a657eec0bb96661d42921b5f50a95ad33675b54f83000141ff45f742a876139946a149ab4d9185574b98dc919d2eb6754f8abaa59d18b025637a3aa043b91817739554f4ed2026cf8022dbd83e351ce1fabc272841d2510a010140b4010dd48a617db09926f729e79c33ae0b4e94b79f04a1ae93ede6315eb3669de185a17d2b0ac9ee09fd4c64b678a0b61a0a86fa888a273c8511be83bfd6810f0247304402202b795e4de72646d76eab3f0ab27dfa30b810e856ff3a46c9a702df53bb0d8cc302203ccc4d822edab5f35caddb10af1be93583526ccfbade4b4ead350781e2f8adcd012102f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f90141a3785919a2ce3c4ce26f298c3d51619bc474ae24014bcdd31328cd8cfbab2eff3395fa0a16fe5f486d12f22a9cedded5ae74feb4bbe5351346508c5405bcfee0020141ea0c6ba90763c2d3a296ad82ba45881abb4f426b3f87af162dd24d5109edc1cdd11915095ba47c3a9963dc1e6c432939872bc49212fe34c632cd3ab9fed429c4820141bbc9584a11074e83bc8c6759ec55401f0ae7b03ef290c3139814f545b58a9f8127258000874f44bc46db7646322107d4d86aec8e73b8719a61fff761d75b5dd9810065cd1d" + } + } + ] +} diff --git a/crates/script_verify/tests/data/script_tests.json b/crates/script_verify/tests/data/script_tests.json new file mode 100644 index 0000000000..25581c4d64 --- /dev/null +++ b/crates/script_verify/tests/data/script_tests.json @@ -0,0 +1,2743 @@ +[ +["Format is: [[wit..., amount]?, scriptSig, scriptPubKey, flags, expected_scripterror, ... comments]"], +["It is evaluated as if there was a crediting coinbase transaction with two 0"], +["pushes as scriptSig, and one output of 0 satoshi and given scriptPubKey,"], +["followed by a spending transaction which spends this output as only input (and"], +["correct prevout hash), using the given scriptSig. All nLockTimes are 0, all"], +["nSequences are max."], + +["", "DEPTH 0 EQUAL", "P2SH,STRICTENC", "OK", "Test the test: we should have an empty stack after scriptSig evaluation"], +[" ", "DEPTH 0 EQUAL", "P2SH,STRICTENC", "OK", "and multiple spaces should not change that."], +[" ", "DEPTH 0 EQUAL", "P2SH,STRICTENC", "OK"], +[" ", "DEPTH 0 EQUAL", "P2SH,STRICTENC", "OK"], +["1 2", "2 EQUALVERIFY 1 EQUAL", "P2SH,STRICTENC", "OK", "Similarly whitespace around and between symbols"], +["1 2", "2 EQUALVERIFY 1 EQUAL", "P2SH,STRICTENC", "OK"], +[" 1 2", "2 EQUALVERIFY 1 EQUAL", "P2SH,STRICTENC", "OK"], +["1 2 ", "2 EQUALVERIFY 1 EQUAL", "P2SH,STRICTENC", "OK"], +[" 1 2 ", "2 EQUALVERIFY 1 EQUAL", "P2SH,STRICTENC", "OK"], + +["1", "", "P2SH,STRICTENC", "OK"], +["0x02 0x01 0x00", "", "P2SH,STRICTENC", "OK", "all bytes are significant, not only the last one"], +["0x09 0x00000000 0x00000000 0x10", "", "P2SH,STRICTENC", "OK", "equals zero when cast to Int64"], + +["0x01 0x0b", "11 EQUAL", "P2SH,STRICTENC", "OK", "push 1 byte"], +["0x02 0x417a", "'Az' EQUAL", "P2SH,STRICTENC", "OK"], +["0x4b 0x417a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a", + "'Azzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz' EQUAL", "P2SH,STRICTENC", "OK", "push 75 bytes"], + +["0x4c 0x01 0x07","7 EQUAL", "P2SH,STRICTENC", "OK", "0x4c is OP_PUSHDATA1"], +["0x4d 0x0100 0x08","8 EQUAL", "P2SH,STRICTENC", "OK", "0x4d is OP_PUSHDATA2"], +["0x4e 0x01000000 0x09","9 EQUAL", "P2SH,STRICTENC", "OK", "0x4e is OP_PUSHDATA4"], + +["0x4c 0x00","0 EQUAL", "P2SH,STRICTENC", "OK"], +["0x4d 0x0000","0 EQUAL", "P2SH,STRICTENC", "OK"], +["0x4e 0x00000000","0 EQUAL", "P2SH,STRICTENC", "OK"], +["0x4f 1000 ADD","999 EQUAL", "P2SH,STRICTENC", "OK"], +["0", "IF 0x50 ENDIF 1", "P2SH,STRICTENC", "OK", "0x50 is reserved (ok if not executed)"], +["0x51", "0x5f ADD 0x60 EQUAL", "P2SH,STRICTENC", "OK", "0x51 through 0x60 push 1 through 16 onto stack"], +["1","NOP", "P2SH,STRICTENC", "OK"], +["0", "IF VER ELSE 1 ENDIF", "P2SH,STRICTENC", "OK", "VER non-functional (ok if not executed)"], +["0", "IF RESERVED RESERVED1 RESERVED2 ELSE 1 ENDIF", "P2SH,STRICTENC", "OK", "RESERVED ok in un-executed IF"], + +["1", "DUP IF ENDIF", "P2SH,STRICTENC", "OK"], +["1", "IF 1 ENDIF", "P2SH,STRICTENC", "OK"], +["1", "DUP IF ELSE ENDIF", "P2SH,STRICTENC", "OK"], +["1", "IF 1 ELSE ENDIF", "P2SH,STRICTENC", "OK"], +["0", "IF ELSE 1 ENDIF", "P2SH,STRICTENC", "OK"], + +["1 1", "IF IF 1 ELSE 0 ENDIF ENDIF", "P2SH,STRICTENC", "OK"], +["1 0", "IF IF 1 ELSE 0 ENDIF ENDIF", "P2SH,STRICTENC", "OK"], +["1 1", "IF IF 1 ELSE 0 ENDIF ELSE IF 0 ELSE 1 ENDIF ENDIF", "P2SH,STRICTENC", "OK"], +["0 0", "IF IF 1 ELSE 0 ENDIF ELSE IF 0 ELSE 1 ENDIF ENDIF", "P2SH,STRICTENC", "OK"], + +["1 0", "NOTIF IF 1 ELSE 0 ENDIF ENDIF", "P2SH,STRICTENC", "OK"], +["1 1", "NOTIF IF 1 ELSE 0 ENDIF ENDIF", "P2SH,STRICTENC", "OK"], +["1 0", "NOTIF IF 1 ELSE 0 ENDIF ELSE IF 0 ELSE 1 ENDIF ENDIF", "P2SH,STRICTENC", "OK"], +["0 1", "NOTIF IF 1 ELSE 0 ENDIF ELSE IF 0 ELSE 1 ENDIF ENDIF", "P2SH,STRICTENC", "OK"], + +["0", "IF 0 ELSE 1 ELSE 0 ENDIF", "P2SH,STRICTENC", "OK", "Multiple ELSE's are valid and executed inverts on each ELSE encountered"], +["1", "IF 1 ELSE 0 ELSE ENDIF", "P2SH,STRICTENC", "OK"], +["1", "IF ELSE 0 ELSE 1 ENDIF", "P2SH,STRICTENC", "OK"], +["1", "IF 1 ELSE 0 ELSE 1 ENDIF ADD 2 EQUAL", "P2SH,STRICTENC", "OK"], +["'' 1", "IF SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ENDIF 0x14 0x68ca4fec736264c13b859bac43d5173df6871682 EQUAL", "P2SH,STRICTENC", "OK"], + +["1", "NOTIF 0 ELSE 1 ELSE 0 ENDIF", "P2SH,STRICTENC", "OK", "Multiple ELSE's are valid and execution inverts on each ELSE encountered"], +["0", "NOTIF 1 ELSE 0 ELSE ENDIF", "P2SH,STRICTENC", "OK"], +["0", "NOTIF ELSE 0 ELSE 1 ENDIF", "P2SH,STRICTENC", "OK"], +["0", "NOTIF 1 ELSE 0 ELSE 1 ENDIF ADD 2 EQUAL", "P2SH,STRICTENC", "OK"], +["'' 0", "NOTIF SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ENDIF 0x14 0x68ca4fec736264c13b859bac43d5173df6871682 EQUAL", "P2SH,STRICTENC", "OK"], + +["0", "IF 1 IF RETURN ELSE RETURN ELSE RETURN ENDIF ELSE 1 IF 1 ELSE RETURN ELSE 1 ENDIF ELSE RETURN ENDIF ADD 2 EQUAL", "P2SH,STRICTENC", "OK", "Nested ELSE ELSE"], +["1", "NOTIF 0 NOTIF RETURN ELSE RETURN ELSE RETURN ENDIF ELSE 0 NOTIF 1 ELSE RETURN ELSE 1 ENDIF ELSE RETURN ENDIF ADD 2 EQUAL", "P2SH,STRICTENC", "OK"], + +["0", "IF RETURN ENDIF 1", "P2SH,STRICTENC", "OK", "RETURN only works if executed"], + +["1 1", "VERIFY", "P2SH,STRICTENC", "OK"], +["1 0x05 0x01 0x00 0x00 0x00 0x00", "VERIFY", "P2SH,STRICTENC", "OK", "values >4 bytes can be cast to boolean"], +["1 0x01 0x80", "IF 0 ENDIF", "P2SH,STRICTENC", "OK", "negative 0 is false"], + +["10 0 11 TOALTSTACK DROP FROMALTSTACK", "ADD 21 EQUAL", "P2SH,STRICTENC", "OK"], +["'gavin_was_here' TOALTSTACK 11 FROMALTSTACK", "'gavin_was_here' EQUALVERIFY 11 EQUAL", "P2SH,STRICTENC", "OK"], + +["0 IFDUP", "DEPTH 1 EQUALVERIFY 0 EQUAL", "P2SH,STRICTENC", "OK"], +["1 IFDUP", "DEPTH 2 EQUALVERIFY 1 EQUALVERIFY 1 EQUAL", "P2SH,STRICTENC", "OK"], +["0x05 0x0100000000 IFDUP", "DEPTH 2 EQUALVERIFY 0x05 0x0100000000 EQUAL", "P2SH,STRICTENC", "OK", "IFDUP dups non ints"], +["0 DROP", "DEPTH 0 EQUAL", "P2SH,STRICTENC", "OK"], +["0", "DUP 1 ADD 1 EQUALVERIFY 0 EQUAL", "P2SH,STRICTENC", "OK"], +["0 1", "NIP", "P2SH,STRICTENC", "OK"], +["1 0", "OVER DEPTH 3 EQUALVERIFY", "P2SH,STRICTENC", "OK"], +["22 21 20", "0 PICK 20 EQUALVERIFY DEPTH 3 EQUAL", "P2SH,STRICTENC", "OK"], +["22 21 20", "1 PICK 21 EQUALVERIFY DEPTH 3 EQUAL", "P2SH,STRICTENC", "OK"], +["22 21 20", "2 PICK 22 EQUALVERIFY DEPTH 3 EQUAL", "P2SH,STRICTENC", "OK"], +["22 21 20", "0 ROLL 20 EQUALVERIFY DEPTH 2 EQUAL", "P2SH,STRICTENC", "OK"], +["22 21 20", "1 ROLL 21 EQUALVERIFY DEPTH 2 EQUAL", "P2SH,STRICTENC", "OK"], +["22 21 20", "2 ROLL 22 EQUALVERIFY DEPTH 2 EQUAL", "P2SH,STRICTENC", "OK"], +["22 21 20", "ROT 22 EQUAL", "P2SH,STRICTENC", "OK"], +["22 21 20", "ROT DROP 20 EQUAL", "P2SH,STRICTENC", "OK"], +["22 21 20", "ROT DROP DROP 21 EQUAL", "P2SH,STRICTENC", "OK"], +["22 21 20", "ROT ROT 21 EQUAL", "P2SH,STRICTENC", "OK"], +["22 21 20", "ROT ROT ROT 20 EQUAL", "P2SH,STRICTENC", "OK"], +["25 24 23 22 21 20", "2ROT 24 EQUAL", "P2SH,STRICTENC", "OK"], +["25 24 23 22 21 20", "2ROT DROP 25 EQUAL", "P2SH,STRICTENC", "OK"], +["25 24 23 22 21 20", "2ROT 2DROP 20 EQUAL", "P2SH,STRICTENC", "OK"], +["25 24 23 22 21 20", "2ROT 2DROP DROP 21 EQUAL", "P2SH,STRICTENC", "OK"], +["25 24 23 22 21 20", "2ROT 2DROP 2DROP 22 EQUAL", "P2SH,STRICTENC", "OK"], +["25 24 23 22 21 20", "2ROT 2DROP 2DROP DROP 23 EQUAL", "P2SH,STRICTENC", "OK"], +["25 24 23 22 21 20", "2ROT 2ROT 22 EQUAL", "P2SH,STRICTENC", "OK"], +["25 24 23 22 21 20", "2ROT 2ROT 2ROT 20 EQUAL", "P2SH,STRICTENC", "OK"], +["1 2 3 4 5 6", "2ROT DEPTH 6 EQUAL", "P2SH,STRICTENC", "OK"], +["1 0", "SWAP 1 EQUALVERIFY 0 EQUAL", "P2SH,STRICTENC", "OK"], +["0 1", "TUCK DEPTH 3 EQUALVERIFY SWAP 2DROP", "P2SH,STRICTENC", "OK"], +["13 14", "2DUP ROT EQUALVERIFY EQUAL", "P2SH,STRICTENC", "OK"], +["-1 0 1 2", "3DUP DEPTH 7 EQUALVERIFY ADD ADD 3 EQUALVERIFY 2DROP 0 EQUALVERIFY", "P2SH,STRICTENC", "OK"], +["1 2 3 5", "2OVER ADD ADD 8 EQUALVERIFY ADD ADD 6 EQUAL", "P2SH,STRICTENC", "OK"], +["1 3 5 7", "2SWAP ADD 4 EQUALVERIFY ADD 12 EQUAL", "P2SH,STRICTENC", "OK"], +["0", "SIZE 0 EQUAL", "P2SH,STRICTENC", "OK"], +["1", "SIZE 1 EQUAL", "P2SH,STRICTENC", "OK"], +["127", "SIZE 1 EQUAL", "P2SH,STRICTENC", "OK"], +["128", "SIZE 2 EQUAL", "P2SH,STRICTENC", "OK"], +["32767", "SIZE 2 EQUAL", "P2SH,STRICTENC", "OK"], +["32768", "SIZE 3 EQUAL", "P2SH,STRICTENC", "OK"], +["8388607", "SIZE 3 EQUAL", "P2SH,STRICTENC", "OK"], +["8388608", "SIZE 4 EQUAL", "P2SH,STRICTENC", "OK"], +["2147483647", "SIZE 4 EQUAL", "P2SH,STRICTENC", "OK"], +["2147483648", "SIZE 5 EQUAL", "P2SH,STRICTENC", "OK"], +["0x05ffffffff7f", "SIZE 5 EQUAL", "P2SH,STRICTENC", "OK"], +["0x06000000008000", "SIZE 6 EQUAL", "P2SH,STRICTENC", "OK"], +["0x08ffffffffffffff7f", "SIZE 8 EQUAL", "P2SH,STRICTENC", "OK"], +["-1", "SIZE 1 EQUAL", "P2SH,STRICTENC", "OK"], +["-127", "SIZE 1 EQUAL", "P2SH,STRICTENC", "OK"], +["-128", "SIZE 2 EQUAL", "P2SH,STRICTENC", "OK"], +["-32767", "SIZE 2 EQUAL", "P2SH,STRICTENC", "OK"], +["-32768", "SIZE 3 EQUAL", "P2SH,STRICTENC", "OK"], +["-8388607", "SIZE 3 EQUAL", "P2SH,STRICTENC", "OK"], +["-8388608", "SIZE 4 EQUAL", "P2SH,STRICTENC", "OK"], +["-2147483647", "SIZE 4 EQUAL", "P2SH,STRICTENC", "OK"], +["-2147483648", "SIZE 5 EQUAL", "P2SH,STRICTENC", "OK"], +["0x05ffffffffff", "SIZE 5 EQUAL", "P2SH,STRICTENC", "OK"], +["0x06000000008080", "SIZE 6 EQUAL", "P2SH,STRICTENC", "OK"], +["0x08ffffffffffffffff", "SIZE 8 EQUAL", "P2SH,STRICTENC", "OK"], +["'abcdefghijklmnopqrstuvwxyz'", "SIZE 26 EQUAL", "P2SH,STRICTENC", "OK"], + +["42", "SIZE 1 EQUALVERIFY 42 EQUAL", "P2SH,STRICTENC", "OK", "SIZE does not consume argument"], + +["2 -2 ADD", "0 EQUAL", "P2SH,STRICTENC", "OK"], +["2147483647 -2147483647 ADD", "0 EQUAL", "P2SH,STRICTENC", "OK"], +["-1 -1 ADD", "-2 EQUAL", "P2SH,STRICTENC", "OK"], + +["0 0","EQUAL", "P2SH,STRICTENC", "OK"], +["1 1 ADD", "2 EQUAL", "P2SH,STRICTENC", "OK"], +["1 1ADD", "2 EQUAL", "P2SH,STRICTENC", "OK"], +["111 1SUB", "110 EQUAL", "P2SH,STRICTENC", "OK"], +["111 1 ADD 12 SUB", "100 EQUAL", "P2SH,STRICTENC", "OK"], +["0 ABS", "0 EQUAL", "P2SH,STRICTENC", "OK"], +["16 ABS", "16 EQUAL", "P2SH,STRICTENC", "OK"], +["-16 ABS", "-16 NEGATE EQUAL", "P2SH,STRICTENC", "OK"], +["0 NOT", "NOP", "P2SH,STRICTENC", "OK"], +["1 NOT", "0 EQUAL", "P2SH,STRICTENC", "OK"], +["11 NOT", "0 EQUAL", "P2SH,STRICTENC", "OK"], +["0 0NOTEQUAL", "0 EQUAL", "P2SH,STRICTENC", "OK"], +["1 0NOTEQUAL", "1 EQUAL", "P2SH,STRICTENC", "OK"], +["111 0NOTEQUAL", "1 EQUAL", "P2SH,STRICTENC", "OK"], +["-111 0NOTEQUAL", "1 EQUAL", "P2SH,STRICTENC", "OK"], +["1 1 BOOLAND", "NOP", "P2SH,STRICTENC", "OK"], +["1 0 BOOLAND", "NOT", "P2SH,STRICTENC", "OK"], +["0 1 BOOLAND", "NOT", "P2SH,STRICTENC", "OK"], +["0 0 BOOLAND", "NOT", "P2SH,STRICTENC", "OK"], +["16 17 BOOLAND", "NOP", "P2SH,STRICTENC", "OK"], +["1 1 BOOLOR", "NOP", "P2SH,STRICTENC", "OK"], +["1 0 BOOLOR", "NOP", "P2SH,STRICTENC", "OK"], +["0 1 BOOLOR", "NOP", "P2SH,STRICTENC", "OK"], +["0 0 BOOLOR", "NOT", "P2SH,STRICTENC", "OK"], +["0x01 0x80", "DUP BOOLOR", "P2SH,STRICTENC", "EVAL_FALSE", "negative-0 negative-0 BOOLOR"], +["0x01 0x00", "DUP BOOLOR", "P2SH,STRICTENC", "EVAL_FALSE", " non-minimal-0 non-minimal-0 BOOLOR"], +["0x01 0x81", "DUP BOOLOR", "P2SH,STRICTENC", "OK", "-1 -1 BOOLOR"], +["0x01 0x80", "DUP BOOLAND", "P2SH,STRICTENC", "EVAL_FALSE", "negative-0 negative-0 BOOLAND"], +["0x01 0x00", "DUP BOOLAND", "P2SH,STRICTENC", "EVAL_FALSE", " non-minimal-0 non-minimal-0 BOOLAND"], +["0x01 0x81", "DUP BOOLAND", "P2SH,STRICTENC", "OK", "-1 -1 BOOLAND"], +["0x01 0x00", "NOT", "P2SH,STRICTENC", "OK", "non-minimal-0 NOT"], +["0x01 0x80", "NOT", "P2SH,STRICTENC", "OK", "negative-0 NOT"], +["0x01 0x81", "NOT", "P2SH,STRICTENC", "EVAL_FALSE", "negative 1 NOT"], +["0x01 0x80 0", "NUMEQUAL", "P2SH", "OK", "-0 0 NUMEQUAL"], +["0x01 0x00 0", "NUMEQUAL", "P2SH", "OK", "non-minimal-0 0 NUMEQUAL"], +["0x02 0x00 0x00 0", "NUMEQUAL", "P2SH", "OK", "non-minimal-0 0 NUMEQUAL"], +["16 17 BOOLOR", "NOP", "P2SH,STRICTENC", "OK"], +["11 10 1 ADD", "NUMEQUAL", "P2SH,STRICTENC", "OK"], +["11 10 1 ADD", "NUMEQUALVERIFY 1", "P2SH,STRICTENC", "OK"], +["11 10 1 ADD", "NUMNOTEQUAL NOT", "P2SH,STRICTENC", "OK"], +["111 10 1 ADD", "NUMNOTEQUAL", "P2SH,STRICTENC", "OK"], +["11 10", "LESSTHAN NOT", "P2SH,STRICTENC", "OK"], +["4 4", "LESSTHAN NOT", "P2SH,STRICTENC", "OK"], +["10 11", "LESSTHAN", "P2SH,STRICTENC", "OK"], +["-11 11", "LESSTHAN", "P2SH,STRICTENC", "OK"], +["-11 -10", "LESSTHAN", "P2SH,STRICTENC", "OK"], +["11 10", "GREATERTHAN", "P2SH,STRICTENC", "OK"], +["4 4", "GREATERTHAN NOT", "P2SH,STRICTENC", "OK"], +["10 11", "GREATERTHAN NOT", "P2SH,STRICTENC", "OK"], +["-11 11", "GREATERTHAN NOT", "P2SH,STRICTENC", "OK"], +["-11 -10", "GREATERTHAN NOT", "P2SH,STRICTENC", "OK"], +["11 10", "LESSTHANOREQUAL NOT", "P2SH,STRICTENC", "OK"], +["4 4", "LESSTHANOREQUAL", "P2SH,STRICTENC", "OK"], +["10 11", "LESSTHANOREQUAL", "P2SH,STRICTENC", "OK"], +["-11 11", "LESSTHANOREQUAL", "P2SH,STRICTENC", "OK"], +["-11 -10", "LESSTHANOREQUAL", "P2SH,STRICTENC", "OK"], +["11 10", "GREATERTHANOREQUAL", "P2SH,STRICTENC", "OK"], +["4 4", "GREATERTHANOREQUAL", "P2SH,STRICTENC", "OK"], +["10 11", "GREATERTHANOREQUAL NOT", "P2SH,STRICTENC", "OK"], +["-11 11", "GREATERTHANOREQUAL NOT", "P2SH,STRICTENC", "OK"], +["-11 -10", "GREATERTHANOREQUAL NOT", "P2SH,STRICTENC", "OK"], +["1 0 MIN", "0 NUMEQUAL", "P2SH,STRICTENC", "OK"], +["0 1 MIN", "0 NUMEQUAL", "P2SH,STRICTENC", "OK"], +["-1 0 MIN", "-1 NUMEQUAL", "P2SH,STRICTENC", "OK"], +["0 -2147483647 MIN", "-2147483647 NUMEQUAL", "P2SH,STRICTENC", "OK"], +["2147483647 0 MAX", "2147483647 NUMEQUAL", "P2SH,STRICTENC", "OK"], +["0 100 MAX", "100 NUMEQUAL", "P2SH,STRICTENC", "OK"], +["-100 0 MAX", "0 NUMEQUAL", "P2SH,STRICTENC", "OK"], +["0 -2147483647 MAX", "0 NUMEQUAL", "P2SH,STRICTENC", "OK"], +["0 0 1", "WITHIN", "P2SH,STRICTENC", "OK"], +["1 0 1", "WITHIN NOT", "P2SH,STRICTENC", "OK"], +["0 -2147483647 2147483647", "WITHIN", "P2SH,STRICTENC", "OK"], +["-1 -100 100", "WITHIN", "P2SH,STRICTENC", "OK"], +["11 -100 100", "WITHIN", "P2SH,STRICTENC", "OK"], +["-2147483647 -100 100", "WITHIN NOT", "P2SH,STRICTENC", "OK"], +["2147483647 -100 100", "WITHIN NOT", "P2SH,STRICTENC", "OK"], + +["2147483647 2147483647 SUB", "0 EQUAL", "P2SH,STRICTENC", "OK"], +["2147483647 DUP ADD", "4294967294 EQUAL", "P2SH,STRICTENC", "OK", ">32 bit EQUAL is valid"], +["2147483647 NEGATE DUP ADD", "-4294967294 EQUAL", "P2SH,STRICTENC", "OK"], + +["''", "RIPEMD160 0x14 0x9c1185a5c5e9fc54612808977ee8f548b2258d31 EQUAL", "P2SH,STRICTENC", "OK"], +["'a'", "RIPEMD160 0x14 0x0bdc9d2d256b3ee9daae347be6f4dc835a467ffe EQUAL", "P2SH,STRICTENC", "OK"], +["'abcdefghijklmnopqrstuvwxyz'", "RIPEMD160 0x14 0xf71c27109c692c1b56bbdceb5b9d2865b3708dbc EQUAL", "P2SH,STRICTENC", "OK"], +["''", "SHA1 0x14 0xda39a3ee5e6b4b0d3255bfef95601890afd80709 EQUAL", "P2SH,STRICTENC", "OK"], +["'a'", "SHA1 0x14 0x86f7e437faa5a7fce15d1ddcb9eaeaea377667b8 EQUAL", "P2SH,STRICTENC", "OK"], +["'abcdefghijklmnopqrstuvwxyz'", "SHA1 0x14 0x32d10c7b8cf96570ca04ce37f2a19d84240d3a89 EQUAL", "P2SH,STRICTENC", "OK"], +["''", "SHA256 0x20 0xe3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 EQUAL", "P2SH,STRICTENC", "OK"], +["'a'", "SHA256 0x20 0xca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb EQUAL", "P2SH,STRICTENC", "OK"], +["'abcdefghijklmnopqrstuvwxyz'", "SHA256 0x20 0x71c480df93d6ae2f1efad1447c66c9525e316218cf51fc8d9ed832f2daf18b73 EQUAL", "P2SH,STRICTENC", "OK"], +["''", "DUP HASH160 SWAP SHA256 RIPEMD160 EQUAL", "P2SH,STRICTENC", "OK"], +["''", "DUP HASH256 SWAP SHA256 SHA256 EQUAL", "P2SH,STRICTENC", "OK"], +["''", "NOP HASH160 0x14 0xb472a266d0bd89c13706a4132ccfb16f7c3b9fcb EQUAL", "P2SH,STRICTENC", "OK"], +["'a'", "HASH160 NOP 0x14 0x994355199e516ff76c4fa4aab39337b9d84cf12b EQUAL", "P2SH,STRICTENC", "OK"], +["'abcdefghijklmnopqrstuvwxyz'", "HASH160 0x4c 0x14 0xc286a1af0947f58d1ad787385b1c2c4a976f9e71 EQUAL", "P2SH,STRICTENC", "OK"], +["''", "HASH256 0x20 0x5df6e0e2761359d30a8275058e299fcc0381534545f55cf43e41983f5d4c9456 EQUAL", "P2SH,STRICTENC", "OK"], +["'a'", "HASH256 0x20 0xbf5d3affb73efd2ec6c36ad3112dd933efed63c4e1cbffcfa88e2759c144f2d8 EQUAL", "P2SH,STRICTENC", "OK"], +["'abcdefghijklmnopqrstuvwxyz'", "HASH256 0x4c 0x20 0xca139bc10c2f660da42666f72e89a225936fc60f193c161124a672050c434671 EQUAL", "P2SH,STRICTENC", "OK"], + + +["1","NOP1 CHECKLOCKTIMEVERIFY CHECKSEQUENCEVERIFY NOP4 NOP5 NOP6 NOP7 NOP8 NOP9 NOP10 1 EQUAL", "P2SH,STRICTENC", "OK"], +["'NOP_1_to_10' NOP1 CHECKLOCKTIMEVERIFY CHECKSEQUENCEVERIFY NOP4 NOP5 NOP6 NOP7 NOP8 NOP9 NOP10","'NOP_1_to_10' EQUAL", "P2SH,STRICTENC", "OK"], + +["1", "NOP", "P2SH,STRICTENC,DISCOURAGE_UPGRADABLE_NOPS", "OK", "Discourage NOPx flag allows OP_NOP"], + +["0", "IF NOP10 ENDIF 1", "P2SH,STRICTENC,DISCOURAGE_UPGRADABLE_NOPS", "OK", + "Discouraged NOPs are allowed if not executed"], + +["0", "IF 0xba ELSE 1 ENDIF", "P2SH,STRICTENC", "OK", "opcodes above MAX_OPCODE invalid if executed"], +["0", "IF 0xbb ELSE 1 ENDIF", "P2SH,STRICTENC", "OK"], +["0", "IF 0xbc ELSE 1 ENDIF", "P2SH,STRICTENC", "OK"], +["0", "IF 0xbd ELSE 1 ENDIF", "P2SH,STRICTENC", "OK"], +["0", "IF 0xbe ELSE 1 ENDIF", "P2SH,STRICTENC", "OK"], +["0", "IF 0xbf ELSE 1 ENDIF", "P2SH,STRICTENC", "OK"], +["0", "IF 0xc0 ELSE 1 ENDIF", "P2SH,STRICTENC", "OK"], +["0", "IF 0xc1 ELSE 1 ENDIF", "P2SH,STRICTENC", "OK"], +["0", "IF 0xc2 ELSE 1 ENDIF", "P2SH,STRICTENC", "OK"], +["0", "IF 0xc3 ELSE 1 ENDIF", "P2SH,STRICTENC", "OK"], +["0", "IF 0xc4 ELSE 1 ENDIF", "P2SH,STRICTENC", "OK"], +["0", "IF 0xc5 ELSE 1 ENDIF", "P2SH,STRICTENC", "OK"], +["0", "IF 0xc6 ELSE 1 ENDIF", "P2SH,STRICTENC", "OK"], +["0", "IF 0xc7 ELSE 1 ENDIF", "P2SH,STRICTENC", "OK"], +["0", "IF 0xc8 ELSE 1 ENDIF", "P2SH,STRICTENC", "OK"], +["0", "IF 0xc9 ELSE 1 ENDIF", "P2SH,STRICTENC", "OK"], +["0", "IF 0xca ELSE 1 ENDIF", "P2SH,STRICTENC", "OK"], +["0", "IF 0xcb ELSE 1 ENDIF", "P2SH,STRICTENC", "OK"], +["0", "IF 0xcc ELSE 1 ENDIF", "P2SH,STRICTENC", "OK"], +["0", "IF 0xcd ELSE 1 ENDIF", "P2SH,STRICTENC", "OK"], +["0", "IF 0xce ELSE 1 ENDIF", "P2SH,STRICTENC", "OK"], +["0", "IF 0xcf ELSE 1 ENDIF", "P2SH,STRICTENC", "OK"], +["0", "IF 0xd0 ELSE 1 ENDIF", "P2SH,STRICTENC", "OK"], +["0", "IF 0xd1 ELSE 1 ENDIF", "P2SH,STRICTENC", "OK"], +["0", "IF 0xd2 ELSE 1 ENDIF", "P2SH,STRICTENC", "OK"], +["0", "IF 0xd3 ELSE 1 ENDIF", "P2SH,STRICTENC", "OK"], +["0", "IF 0xd4 ELSE 1 ENDIF", "P2SH,STRICTENC", "OK"], +["0", "IF 0xd5 ELSE 1 ENDIF", "P2SH,STRICTENC", "OK"], +["0", "IF 0xd6 ELSE 1 ENDIF", "P2SH,STRICTENC", "OK"], +["0", "IF 0xd7 ELSE 1 ENDIF", "P2SH,STRICTENC", "OK"], +["0", "IF 0xd8 ELSE 1 ENDIF", "P2SH,STRICTENC", "OK"], +["0", "IF 0xd9 ELSE 1 ENDIF", "P2SH,STRICTENC", "OK"], +["0", "IF 0xda ELSE 1 ENDIF", "P2SH,STRICTENC", "OK"], +["0", "IF 0xdb ELSE 1 ENDIF", "P2SH,STRICTENC", "OK"], +["0", "IF 0xdc ELSE 1 ENDIF", "P2SH,STRICTENC", "OK"], +["0", "IF 0xdd ELSE 1 ENDIF", "P2SH,STRICTENC", "OK"], +["0", "IF 0xde ELSE 1 ENDIF", "P2SH,STRICTENC", "OK"], +["0", "IF 0xdf ELSE 1 ENDIF", "P2SH,STRICTENC", "OK"], +["0", "IF 0xe0 ELSE 1 ENDIF", "P2SH,STRICTENC", "OK"], +["0", "IF 0xe1 ELSE 1 ENDIF", "P2SH,STRICTENC", "OK"], +["0", "IF 0xe2 ELSE 1 ENDIF", "P2SH,STRICTENC", "OK"], +["0", "IF 0xe3 ELSE 1 ENDIF", "P2SH,STRICTENC", "OK"], +["0", "IF 0xe4 ELSE 1 ENDIF", "P2SH,STRICTENC", "OK"], +["0", "IF 0xe5 ELSE 1 ENDIF", "P2SH,STRICTENC", "OK"], +["0", "IF 0xe6 ELSE 1 ENDIF", "P2SH,STRICTENC", "OK"], +["0", "IF 0xe7 ELSE 1 ENDIF", "P2SH,STRICTENC", "OK"], +["0", "IF 0xe8 ELSE 1 ENDIF", "P2SH,STRICTENC", "OK"], +["0", "IF 0xe9 ELSE 1 ENDIF", "P2SH,STRICTENC", "OK"], +["0", "IF 0xea ELSE 1 ENDIF", "P2SH,STRICTENC", "OK"], +["0", "IF 0xeb ELSE 1 ENDIF", "P2SH,STRICTENC", "OK"], +["0", "IF 0xec ELSE 1 ENDIF", "P2SH,STRICTENC", "OK"], +["0", "IF 0xed ELSE 1 ENDIF", "P2SH,STRICTENC", "OK"], +["0", "IF 0xee ELSE 1 ENDIF", "P2SH,STRICTENC", "OK"], +["0", "IF 0xef ELSE 1 ENDIF", "P2SH,STRICTENC", "OK"], +["0", "IF 0xf0 ELSE 1 ENDIF", "P2SH,STRICTENC", "OK"], +["0", "IF 0xf1 ELSE 1 ENDIF", "P2SH,STRICTENC", "OK"], +["0", "IF 0xf2 ELSE 1 ENDIF", "P2SH,STRICTENC", "OK"], +["0", "IF 0xf3 ELSE 1 ENDIF", "P2SH,STRICTENC", "OK"], +["0", "IF 0xf4 ELSE 1 ENDIF", "P2SH,STRICTENC", "OK"], +["0", "IF 0xf5 ELSE 1 ENDIF", "P2SH,STRICTENC", "OK"], +["0", "IF 0xf6 ELSE 1 ENDIF", "P2SH,STRICTENC", "OK"], +["0", "IF 0xf7 ELSE 1 ENDIF", "P2SH,STRICTENC", "OK"], +["0", "IF 0xf8 ELSE 1 ENDIF", "P2SH,STRICTENC", "OK"], +["0", "IF 0xf9 ELSE 1 ENDIF", "P2SH,STRICTENC", "OK"], +["0", "IF 0xfa ELSE 1 ENDIF", "P2SH,STRICTENC", "OK"], +["0", "IF 0xfb ELSE 1 ENDIF", "P2SH,STRICTENC", "OK"], +["0", "IF 0xfc ELSE 1 ENDIF", "P2SH,STRICTENC", "OK"], +["0", "IF 0xfd ELSE 1 ENDIF", "P2SH,STRICTENC", "OK"], +["0", "IF 0xfe ELSE 1 ENDIF", "P2SH,STRICTENC", "OK"], +["0", "IF 0xff ELSE 1 ENDIF", "P2SH,STRICTENC", "OK"], + +["NOP", +"'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb'", +"P2SH,STRICTENC", "OK", +"520 byte push"], +["1", +"0x616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161", +"P2SH,STRICTENC", "OK", +"201 opcodes executed. 0x61 is NOP"], +["1 2 3 4 5 0x6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f", +"1 2 3 4 5 0x6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f", +"P2SH,STRICTENC", "OK", +"1,000 stack size (0x6f is 3DUP)"], +["1 TOALTSTACK 2 TOALTSTACK 3 4 5 0x6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f", +"1 2 3 4 5 6 7 0x6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f", +"P2SH,STRICTENC", "OK", +"1,000 stack size (altstack cleared between scriptSig/scriptPubKey)"], +["'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 0x6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f", +"'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 0x6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f 2DUP 0x616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161", +"P2SH,STRICTENC", "OK", +"Max-size (10,000-byte), max-push(520 bytes), max-opcodes(201), max stack size(1,000 items). 0x6f is 3DUP, 0x61 is NOP"], + +["0", +"IF 0x5050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050 ENDIF 1", +"P2SH,STRICTENC", "OK", +">201 opcodes, but RESERVED (0x50) doesn't count towards opcode limit."], + +["NOP","1", "P2SH,STRICTENC", "OK"], + +["1", "0x01 0x01 EQUAL", "P2SH,STRICTENC", "OK", "The following is useful for checking implementations of BN_bn2mpi"], +["127", "0x01 0x7F EQUAL", "P2SH,STRICTENC", "OK"], +["128", "0x02 0x8000 EQUAL", "P2SH,STRICTENC", "OK", "Leave room for the sign bit"], +["32767", "0x02 0xFF7F EQUAL", "P2SH,STRICTENC", "OK"], +["32768", "0x03 0x008000 EQUAL", "P2SH,STRICTENC", "OK"], +["8388607", "0x03 0xFFFF7F EQUAL", "P2SH,STRICTENC", "OK"], +["8388608", "0x04 0x00008000 EQUAL", "P2SH,STRICTENC", "OK"], +["2147483647", "0x04 0xFFFFFF7F EQUAL", "P2SH,STRICTENC", "OK"], +["2147483648", "0x05 0x0000008000 EQUAL", "P2SH,STRICTENC", "OK"], +["0x05ffffffff7f", "0x05 0xFFFFFFFF7F EQUAL", "P2SH,STRICTENC", "OK"], +["0x06000000008000", "0x06 0x000000008000 EQUAL", "P2SH,STRICTENC", "OK"], +["0x08ffffffffffffff7f", "0x08 0xFFFFFFFFFFFFFF7F EQUAL", "P2SH,STRICTENC", "OK"], +["-1", "0x01 0x81 EQUAL", "P2SH,STRICTENC", "OK", "Numbers are little-endian with the MSB being a sign bit"], +["-127", "0x01 0xFF EQUAL", "P2SH,STRICTENC", "OK"], +["-128", "0x02 0x8080 EQUAL", "P2SH,STRICTENC", "OK"], +["-32767", "0x02 0xFFFF EQUAL", "P2SH,STRICTENC", "OK"], +["-32768", "0x03 0x008080 EQUAL", "P2SH,STRICTENC", "OK"], +["-8388607", "0x03 0xFFFFFF EQUAL", "P2SH,STRICTENC", "OK"], +["-8388608", "0x04 0x00008080 EQUAL", "P2SH,STRICTENC", "OK"], +["-2147483647", "0x04 0xFFFFFFFF EQUAL", "P2SH,STRICTENC", "OK"], +["-2147483648", "0x05 0x0000008080 EQUAL", "P2SH,STRICTENC", "OK"], +["-4294967295", "0x05 0xFFFFFFFF80 EQUAL", "P2SH,STRICTENC", "OK"], +["0x05ffffffffff", "0x05 0xFFFFFFFFFF EQUAL", "P2SH,STRICTENC", "OK"], +["0x06000000008080", "0x06 0x000000008080 EQUAL", "P2SH,STRICTENC", "OK"], +["0x08ffffffffffffffff", "0x08 0xFFFFFFFFFFFFFFFF EQUAL", "P2SH,STRICTENC", "OK"], + +["2147483647", "1ADD 2147483648 EQUAL", "P2SH,STRICTENC", "OK", "We can do math on 4-byte integers, and compare 5-byte ones"], +["2147483647", "1ADD 1", "P2SH,STRICTENC", "OK"], +["-2147483647", "1ADD 1", "P2SH,STRICTENC", "OK"], + +["1", "0x02 0x0100 EQUAL NOT", "P2SH,STRICTENC", "OK", "Not the same byte array..."], +["1", "0x02 0x0100 NUMEQUAL", "P2SH,STRICTENC", "OK", "... but they are numerically equal"], +["11", "0x4c 0x03 0x0b0000 NUMEQUAL", "P2SH,STRICTENC", "OK"], +["0", "0x01 0x80 EQUAL NOT", "P2SH,STRICTENC", "OK"], +["0", "0x01 0x80 NUMEQUAL", "P2SH,STRICTENC", "OK", "Zero numerically equals negative zero"], +["0", "0x02 0x0080 NUMEQUAL", "P2SH,STRICTENC", "OK"], +["0x03 0x000080", "0x04 0x00000080 NUMEQUAL", "P2SH,STRICTENC", "OK"], +["0x03 0x100080", "0x04 0x10000080 NUMEQUAL", "P2SH,STRICTENC", "OK"], +["0x03 0x100000", "0x04 0x10000000 NUMEQUAL", "P2SH,STRICTENC", "OK"], + +["NOP", "NOP 1", "P2SH,STRICTENC", "OK", "The following tests check the if(stack.size() < N) tests in each opcode"], +["1", "IF 1 ENDIF", "P2SH,STRICTENC", "OK", "They are here to catch copy-and-paste errors"], +["0", "NOTIF 1 ENDIF", "P2SH,STRICTENC", "OK", "Most of them are duplicated elsewhere,"], +["1", "VERIFY 1", "P2SH,STRICTENC", "OK", "but, hey, more is always better, right?"], + +["0", "TOALTSTACK 1", "P2SH,STRICTENC", "OK"], +["1", "TOALTSTACK FROMALTSTACK", "P2SH,STRICTENC", "OK"], +["0 0", "2DROP 1", "P2SH,STRICTENC", "OK"], +["0 1", "2DUP", "P2SH,STRICTENC", "OK"], +["0 0 1", "3DUP", "P2SH,STRICTENC", "OK"], +["0 1 0 0", "2OVER", "P2SH,STRICTENC", "OK"], +["0 1 0 0 0 0", "2ROT", "P2SH,STRICTENC", "OK"], +["0 1 0 0", "2SWAP", "P2SH,STRICTENC", "OK"], +["1", "IFDUP", "P2SH,STRICTENC", "OK"], +["NOP", "DEPTH 1", "P2SH,STRICTENC", "OK"], +["0", "DROP 1", "P2SH,STRICTENC", "OK"], +["1", "DUP", "P2SH,STRICTENC", "OK"], +["0 1", "NIP", "P2SH,STRICTENC", "OK"], +["1 0", "OVER", "P2SH,STRICTENC", "OK"], +["1 0 0 0 3", "PICK", "P2SH,STRICTENC", "OK"], +["1 0", "PICK", "P2SH,STRICTENC", "OK"], +["1 0 0 0 3", "ROLL", "P2SH,STRICTENC", "OK"], +["1 0", "ROLL", "P2SH,STRICTENC", "OK"], +["1 0 0", "ROT", "P2SH,STRICTENC", "OK"], +["1 0", "SWAP", "P2SH,STRICTENC", "OK"], +["0 1", "TUCK", "P2SH,STRICTENC", "OK"], + +["1", "SIZE", "P2SH,STRICTENC", "OK"], + +["0 0", "EQUAL", "P2SH,STRICTENC", "OK"], +["0 0", "EQUALVERIFY 1", "P2SH,STRICTENC", "OK"], +["0 0 1", "EQUAL EQUAL", "P2SH,STRICTENC", "OK", "OP_0 and bools must have identical byte representations"], + +["0", "1ADD", "P2SH,STRICTENC", "OK"], +["2", "1SUB", "P2SH,STRICTENC", "OK"], +["-1", "NEGATE", "P2SH,STRICTENC", "OK"], +["-1", "ABS", "P2SH,STRICTENC", "OK"], +["0", "NOT", "P2SH,STRICTENC", "OK"], +["-1", "0NOTEQUAL", "P2SH,STRICTENC", "OK"], + +["1 0", "ADD", "P2SH,STRICTENC", "OK"], +["1 0", "SUB", "P2SH,STRICTENC", "OK"], +["-1 -1", "BOOLAND", "P2SH,STRICTENC", "OK"], +["-1 0", "BOOLOR", "P2SH,STRICTENC", "OK"], +["0 0", "NUMEQUAL", "P2SH,STRICTENC", "OK"], +["0 0", "NUMEQUALVERIFY 1", "P2SH,STRICTENC", "OK"], +["2 0", "NUMEQUALVERIFY", "P2SH,STRICTENC", "NUMEQUALVERIFY"], +["-1 0", "NUMNOTEQUAL", "P2SH,STRICTENC", "OK"], +["-1 0", "LESSTHAN", "P2SH,STRICTENC", "OK"], +["1 0", "GREATERTHAN", "P2SH,STRICTENC", "OK"], +["0 0", "LESSTHANOREQUAL", "P2SH,STRICTENC", "OK"], +["0 0", "GREATERTHANOREQUAL", "P2SH,STRICTENC", "OK"], +["-1 0", "MIN", "P2SH,STRICTENC", "OK"], +["1 0", "MAX", "P2SH,STRICTENC", "OK"], +["-1 -1 0", "WITHIN", "P2SH,STRICTENC", "OK"], + +["0", "RIPEMD160", "P2SH,STRICTENC", "OK"], +["0", "SHA1", "P2SH,STRICTENC", "OK"], +["0", "SHA256", "P2SH,STRICTENC", "OK"], +["0", "HASH160", "P2SH,STRICTENC", "OK"], +["0", "HASH256", "P2SH,STRICTENC", "OK"], +["NOP", "CODESEPARATOR 1", "P2SH,STRICTENC", "OK"], + +["NOP", "NOP1 1", "P2SH,STRICTENC", "OK"], +["NOP", "CHECKLOCKTIMEVERIFY 1", "P2SH,STRICTENC", "OK"], +["NOP", "CHECKSEQUENCEVERIFY 1", "P2SH,STRICTENC", "OK"], +["NOP", "NOP4 1", "P2SH,STRICTENC", "OK"], +["NOP", "NOP5 1", "P2SH,STRICTENC", "OK"], +["NOP", "NOP6 1", "P2SH,STRICTENC", "OK"], +["NOP", "NOP7 1", "P2SH,STRICTENC", "OK"], +["NOP", "NOP8 1", "P2SH,STRICTENC", "OK"], +["NOP", "NOP9 1", "P2SH,STRICTENC", "OK"], +["NOP", "NOP10 1", "P2SH,STRICTENC", "OK"], + +["", "0 0 0 CHECKMULTISIG VERIFY DEPTH 0 EQUAL", "P2SH,STRICTENC", "OK", "CHECKMULTISIG is allowed to have zero keys and/or sigs"], +["", "0 0 0 CHECKMULTISIGVERIFY DEPTH 0 EQUAL", "P2SH,STRICTENC", "OK"], +["", "0 0 0 1 CHECKMULTISIG VERIFY DEPTH 0 EQUAL", "P2SH,STRICTENC", "OK", "Zero sigs means no sigs are checked"], +["", "0 0 0 1 CHECKMULTISIGVERIFY DEPTH 0 EQUAL", "P2SH,STRICTENC", "OK"], + +["", "0 0 0 CHECKMULTISIG VERIFY DEPTH 0 EQUAL", "P2SH,STRICTENC", "OK", "CHECKMULTISIG is allowed to have zero keys and/or sigs"], +["", "0 0 0 CHECKMULTISIGVERIFY DEPTH 0 EQUAL", "P2SH,STRICTENC", "OK"], +["", "0 0 0 1 CHECKMULTISIG VERIFY DEPTH 0 EQUAL", "P2SH,STRICTENC", "OK", "Zero sigs means no sigs are checked"], +["", "0 0 0 1 CHECKMULTISIGVERIFY DEPTH 0 EQUAL", "P2SH,STRICTENC", "OK"], + +["", "0 0 'a' 'b' 2 CHECKMULTISIG VERIFY DEPTH 0 EQUAL", "P2SH,STRICTENC", "OK", "Test from up to 20 pubkeys, all not checked"], +["", "0 0 'a' 'b' 'c' 3 CHECKMULTISIG VERIFY DEPTH 0 EQUAL", "P2SH,STRICTENC", "OK"], +["", "0 0 'a' 'b' 'c' 'd' 4 CHECKMULTISIG VERIFY DEPTH 0 EQUAL", "P2SH,STRICTENC", "OK"], +["", "0 0 'a' 'b' 'c' 'd' 'e' 5 CHECKMULTISIG VERIFY DEPTH 0 EQUAL", "P2SH,STRICTENC", "OK"], +["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 6 CHECKMULTISIG VERIFY DEPTH 0 EQUAL", "P2SH,STRICTENC", "OK"], +["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 7 CHECKMULTISIG VERIFY DEPTH 0 EQUAL", "P2SH,STRICTENC", "OK"], +["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 8 CHECKMULTISIG VERIFY DEPTH 0 EQUAL", "P2SH,STRICTENC", "OK"], +["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 9 CHECKMULTISIG VERIFY DEPTH 0 EQUAL", "P2SH,STRICTENC", "OK"], +["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 10 CHECKMULTISIG VERIFY DEPTH 0 EQUAL", "P2SH,STRICTENC", "OK"], +["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 11 CHECKMULTISIG VERIFY DEPTH 0 EQUAL", "P2SH,STRICTENC", "OK"], +["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 12 CHECKMULTISIG VERIFY DEPTH 0 EQUAL", "P2SH,STRICTENC", "OK"], +["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 13 CHECKMULTISIG VERIFY DEPTH 0 EQUAL", "P2SH,STRICTENC", "OK"], +["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 14 CHECKMULTISIG VERIFY DEPTH 0 EQUAL", "P2SH,STRICTENC", "OK"], +["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 15 CHECKMULTISIG VERIFY DEPTH 0 EQUAL", "P2SH,STRICTENC", "OK"], +["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 16 CHECKMULTISIG VERIFY DEPTH 0 EQUAL", "P2SH,STRICTENC", "OK"], +["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 17 CHECKMULTISIG VERIFY DEPTH 0 EQUAL", "P2SH,STRICTENC", "OK"], +["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 18 CHECKMULTISIG VERIFY DEPTH 0 EQUAL", "P2SH,STRICTENC", "OK"], +["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 19 CHECKMULTISIG VERIFY DEPTH 0 EQUAL", "P2SH,STRICTENC", "OK"], +["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIG VERIFY DEPTH 0 EQUAL", "P2SH,STRICTENC", "OK"], +["", "0 0 'a' 1 CHECKMULTISIGVERIFY DEPTH 0 EQUAL", "P2SH,STRICTENC", "OK"], +["", "0 0 'a' 'b' 2 CHECKMULTISIGVERIFY DEPTH 0 EQUAL", "P2SH,STRICTENC", "OK"], +["", "0 0 'a' 'b' 'c' 3 CHECKMULTISIGVERIFY DEPTH 0 EQUAL", "P2SH,STRICTENC", "OK"], +["", "0 0 'a' 'b' 'c' 'd' 4 CHECKMULTISIGVERIFY DEPTH 0 EQUAL", "P2SH,STRICTENC", "OK"], +["", "0 0 'a' 'b' 'c' 'd' 'e' 5 CHECKMULTISIGVERIFY DEPTH 0 EQUAL", "P2SH,STRICTENC", "OK"], +["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 6 CHECKMULTISIGVERIFY DEPTH 0 EQUAL", "P2SH,STRICTENC", "OK"], +["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 7 CHECKMULTISIGVERIFY DEPTH 0 EQUAL", "P2SH,STRICTENC", "OK"], +["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 8 CHECKMULTISIGVERIFY DEPTH 0 EQUAL", "P2SH,STRICTENC", "OK"], +["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 9 CHECKMULTISIGVERIFY DEPTH 0 EQUAL", "P2SH,STRICTENC", "OK"], +["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 10 CHECKMULTISIGVERIFY DEPTH 0 EQUAL", "P2SH,STRICTENC", "OK"], +["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 11 CHECKMULTISIGVERIFY DEPTH 0 EQUAL", "P2SH,STRICTENC", "OK"], +["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 12 CHECKMULTISIGVERIFY DEPTH 0 EQUAL", "P2SH,STRICTENC", "OK"], +["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 13 CHECKMULTISIGVERIFY DEPTH 0 EQUAL", "P2SH,STRICTENC", "OK"], +["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 14 CHECKMULTISIGVERIFY DEPTH 0 EQUAL", "P2SH,STRICTENC", "OK"], +["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 15 CHECKMULTISIGVERIFY DEPTH 0 EQUAL", "P2SH,STRICTENC", "OK"], +["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 16 CHECKMULTISIGVERIFY DEPTH 0 EQUAL", "P2SH,STRICTENC", "OK"], +["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 17 CHECKMULTISIGVERIFY DEPTH 0 EQUAL", "P2SH,STRICTENC", "OK"], +["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 18 CHECKMULTISIGVERIFY DEPTH 0 EQUAL", "P2SH,STRICTENC", "OK"], +["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 19 CHECKMULTISIGVERIFY DEPTH 0 EQUAL", "P2SH,STRICTENC", "OK"], +["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIGVERIFY DEPTH 0 EQUAL", "P2SH,STRICTENC", "OK"], + +["", +"0 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG", +"P2SH,STRICTENC", "OK", +"nOpCount is incremented by the number of keys evaluated in addition to the usual one op per op. In this case we have zero keys, so we can execute 201 CHECKMULTISIGS"], + +["1", +"0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY", +"P2SH,STRICTENC", "OK"], + +["", +"NOP NOP NOP NOP NOP NOP NOP NOP NOP NOP NOP NOP 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIG 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIG 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIG 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIG 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIG 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIG 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIG 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIG 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIG", +"P2SH,STRICTENC", "OK", +"Even though there are no signatures being checked nOpCount is incremented by the number of keys."], + +["1", +"NOP NOP NOP NOP NOP NOP NOP NOP NOP NOP NOP NOP 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIGVERIFY 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIGVERIFY 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIGVERIFY 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIGVERIFY 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIGVERIFY 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIGVERIFY 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIGVERIFY 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIGVERIFY 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIGVERIFY", +"P2SH,STRICTENC", "OK"], + +["0 0x01 1", "HASH160 0x14 0xda1745e9b549bd0bfa1a569971c77eba30cd5a4b EQUAL", "P2SH,STRICTENC", "OK", "Very basic P2SH"], +["0x4c 0 0x01 1", "HASH160 0x14 0xda1745e9b549bd0bfa1a569971c77eba30cd5a4b EQUAL", "P2SH,STRICTENC", "OK"], + +["0x40 0x42424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242", +"0x4d 0x4000 0x42424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242 EQUAL", +"P2SH,STRICTENC", "OK", +"Basic PUSH signedness check"], + +["0x4c 0x40 0x42424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242", +"0x4d 0x4000 0x42424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242 EQUAL", +"P2SH,STRICTENC", "OK", +"Basic PUSHDATA1 signedness check"], + +["all PUSHDATA forms are equivalent"], + +["0x4c 0x4b 0x111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111", "0x4b 0x111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 EQUAL", "", "OK", "PUSHDATA1 of 75 bytes equals direct push of it"], +["0x4d 0xFF00 0x111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111", "0x4c 0xFF 0x111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 EQUAL", "", "OK", "PUSHDATA2 of 255 bytes equals PUSHDATA1 of it"], + +["0x00", "SIZE 0 EQUAL", "P2SH,STRICTENC", "OK", "Basic OP_0 execution"], + +["Numeric pushes"], + +["0x01 0x81", "0x4f EQUAL", "", "OK", "OP1_NEGATE pushes 0x81"], +["0x01 0x01", "0x51 EQUAL", "", "OK", "OP_1 pushes 0x01"], +["0x01 0x02", "0x52 EQUAL", "", "OK", "OP_2 pushes 0x02"], +["0x01 0x03", "0x53 EQUAL", "", "OK", "OP_3 pushes 0x03"], +["0x01 0x04", "0x54 EQUAL", "", "OK", "OP_4 pushes 0x04"], +["0x01 0x05", "0x55 EQUAL", "", "OK", "OP_5 pushes 0x05"], +["0x01 0x06", "0x56 EQUAL", "", "OK", "OP_6 pushes 0x06"], +["0x01 0x07", "0x57 EQUAL", "", "OK", "OP_7 pushes 0x07"], +["0x01 0x08", "0x58 EQUAL", "", "OK", "OP_8 pushes 0x08"], +["0x01 0x09", "0x59 EQUAL", "", "OK", "OP_9 pushes 0x09"], +["0x01 0x0a", "0x5a EQUAL", "", "OK", "OP_10 pushes 0x0a"], +["0x01 0x0b", "0x5b EQUAL", "", "OK", "OP_11 pushes 0x0b"], +["0x01 0x0c", "0x5c EQUAL", "", "OK", "OP_12 pushes 0x0c"], +["0x01 0x0d", "0x5d EQUAL", "", "OK", "OP_13 pushes 0x0d"], +["0x01 0x0e", "0x5e EQUAL", "", "OK", "OP_14 pushes 0x0e"], +["0x01 0x0f", "0x5f EQUAL", "", "OK", "OP_15 pushes 0x0f"], +["0x01 0x10", "0x60 EQUAL", "", "OK", "OP_16 pushes 0x10"], + +["Equivalency of different numeric encodings"], + +["0x02 0x8000", "128 NUMEQUAL", "", "OK", "0x8000 equals 128"], +["0x01 0x00", "0 NUMEQUAL", "", "OK", "0x00 numequals 0"], +["0x01 0x80", "0 NUMEQUAL", "", "OK", "0x80 (negative zero) numequals 0"], +["0x02 0x0080", "0 NUMEQUAL", "", "OK", "0x0080 numequals 0"], +["0x02 0x0500", "5 NUMEQUAL", "", "OK", "0x0500 numequals 5"], +["0x03 0xff7f80", "0x02 0xffff NUMEQUAL", "", "OK", ""], +["0x03 0xff7f00", "0x02 0xff7f NUMEQUAL", "", "OK", ""], +["0x04 0xffff7f80", "0x03 0xffffff NUMEQUAL", "", "OK", ""], +["0x04 0xffff7f00", "0x03 0xffff7f NUMEQUAL", "", "OK", ""], + +["Unevaluated non-minimal pushes are ignored"], + +["0 IF 0x4c 0x00 ENDIF 1", "", "MINIMALDATA", "OK", "non-minimal PUSHDATA1 ignored"], +["0 IF 0x4d 0x0000 ENDIF 1", "", "MINIMALDATA", "OK", "non-minimal PUSHDATA2 ignored"], +["0 IF 0x4c 0x00000000 ENDIF 1", "", "MINIMALDATA", "OK", "non-minimal PUSHDATA4 ignored"], +["0 IF 0x01 0x81 ENDIF 1", "", "MINIMALDATA", "OK", "1NEGATE equiv"], +["0 IF 0x01 0x01 ENDIF 1", "", "MINIMALDATA", "OK", "OP_1 equiv"], +["0 IF 0x01 0x02 ENDIF 1", "", "MINIMALDATA", "OK", "OP_2 equiv"], +["0 IF 0x01 0x03 ENDIF 1", "", "MINIMALDATA", "OK", "OP_3 equiv"], +["0 IF 0x01 0x04 ENDIF 1", "", "MINIMALDATA", "OK", "OP_4 equiv"], +["0 IF 0x01 0x05 ENDIF 1", "", "MINIMALDATA", "OK", "OP_5 equiv"], +["0 IF 0x01 0x06 ENDIF 1", "", "MINIMALDATA", "OK", "OP_6 equiv"], +["0 IF 0x01 0x07 ENDIF 1", "", "MINIMALDATA", "OK", "OP_7 equiv"], +["0 IF 0x01 0x08 ENDIF 1", "", "MINIMALDATA", "OK", "OP_8 equiv"], +["0 IF 0x01 0x09 ENDIF 1", "", "MINIMALDATA", "OK", "OP_9 equiv"], +["0 IF 0x01 0x0a ENDIF 1", "", "MINIMALDATA", "OK", "OP_10 equiv"], +["0 IF 0x01 0x0b ENDIF 1", "", "MINIMALDATA", "OK", "OP_11 equiv"], +["0 IF 0x01 0x0c ENDIF 1", "", "MINIMALDATA", "OK", "OP_12 equiv"], +["0 IF 0x01 0x0d ENDIF 1", "", "MINIMALDATA", "OK", "OP_13 equiv"], +["0 IF 0x01 0x0e ENDIF 1", "", "MINIMALDATA", "OK", "OP_14 equiv"], +["0 IF 0x01 0x0f ENDIF 1", "", "MINIMALDATA", "OK", "OP_15 equiv"], +["0 IF 0x01 0x10 ENDIF 1", "", "MINIMALDATA", "OK", "OP_16 equiv"], + +["Numeric minimaldata rules are only applied when a stack item is numerically evaluated; the push itself is allowed"], + +["0x01 0x00", "1", "MINIMALDATA", "OK"], +["0x01 0x80", "1", "MINIMALDATA", "OK"], +["0x02 0x0180", "1", "MINIMALDATA", "OK"], +["0x02 0x0100", "1", "MINIMALDATA", "OK"], +["0x02 0x0200", "1", "MINIMALDATA", "OK"], +["0x02 0x0300", "1", "MINIMALDATA", "OK"], +["0x02 0x0400", "1", "MINIMALDATA", "OK"], +["0x02 0x0500", "1", "MINIMALDATA", "OK"], +["0x02 0x0600", "1", "MINIMALDATA", "OK"], +["0x02 0x0700", "1", "MINIMALDATA", "OK"], +["0x02 0x0800", "1", "MINIMALDATA", "OK"], +["0x02 0x0900", "1", "MINIMALDATA", "OK"], +["0x02 0x0a00", "1", "MINIMALDATA", "OK"], +["0x02 0x0b00", "1", "MINIMALDATA", "OK"], +["0x02 0x0c00", "1", "MINIMALDATA", "OK"], +["0x02 0x0d00", "1", "MINIMALDATA", "OK"], +["0x02 0x0e00", "1", "MINIMALDATA", "OK"], +["0x02 0x0f00", "1", "MINIMALDATA", "OK"], +["0x02 0x1000", "1", "MINIMALDATA", "OK"], + +["Valid version of the 'Test every numeric-accepting opcode for correct handling of the numeric minimal encoding rule' script_invalid test"], + +["1 0x02 0x0000", "PICK DROP", "", "OK"], +["1 0x02 0x0000", "ROLL DROP 1", "", "OK"], +["0x02 0x0000", "1ADD DROP 1", "", "OK"], +["0x02 0x0000", "1SUB DROP 1", "", "OK"], +["0x02 0x0000", "NEGATE DROP 1", "", "OK"], +["0x02 0x0000", "ABS DROP 1", "", "OK"], +["0x02 0x0000", "NOT DROP 1", "", "OK"], +["0x02 0x0000", "0NOTEQUAL DROP 1", "", "OK"], + +["0 0x02 0x0000", "ADD DROP 1", "", "OK"], +["0x02 0x0000 0", "ADD DROP 1", "", "OK"], +["0 0x02 0x0000", "SUB DROP 1", "", "OK"], +["0x02 0x0000 0", "SUB DROP 1", "", "OK"], +["0 0x02 0x0000", "BOOLAND DROP 1", "", "OK"], +["0x02 0x0000 0", "BOOLAND DROP 1", "", "OK"], +["0 0x02 0x0000", "BOOLOR DROP 1", "", "OK"], +["0x02 0x0000 0", "BOOLOR DROP 1", "", "OK"], +["0 0x02 0x0000", "NUMEQUAL DROP 1", "", "OK"], +["0x02 0x0000 1", "NUMEQUAL DROP 1", "", "OK"], +["0 0x02 0x0000", "NUMEQUALVERIFY 1", "", "OK"], +["0x02 0x0000 0", "NUMEQUALVERIFY 1", "", "OK"], +["0 0x02 0x0000", "NUMNOTEQUAL DROP 1", "", "OK"], +["0x02 0x0000 0", "NUMNOTEQUAL DROP 1", "", "OK"], +["0 0x02 0x0000", "LESSTHAN DROP 1", "", "OK"], +["0x02 0x0000 0", "LESSTHAN DROP 1", "", "OK"], +["0 0x02 0x0000", "GREATERTHAN DROP 1", "", "OK"], +["0x02 0x0000 0", "GREATERTHAN DROP 1", "", "OK"], +["0 0x02 0x0000", "LESSTHANOREQUAL DROP 1", "", "OK"], +["0x02 0x0000 0", "LESSTHANOREQUAL DROP 1", "", "OK"], +["0 0x02 0x0000", "GREATERTHANOREQUAL DROP 1", "", "OK"], +["0x02 0x0000 0", "GREATERTHANOREQUAL DROP 1", "", "OK"], +["0 0x02 0x0000", "MIN DROP 1", "", "OK"], +["0x02 0x0000 0", "MIN DROP 1", "", "OK"], +["0 0x02 0x0000", "MAX DROP 1", "", "OK"], +["0x02 0x0000 0", "MAX DROP 1", "", "OK"], + +["0x02 0x0000 0 0", "WITHIN DROP 1", "", "OK"], +["0 0x02 0x0000 0", "WITHIN DROP 1", "", "OK"], +["0 0 0x02 0x0000", "WITHIN DROP 1", "", "OK"], + +["0 0 0x02 0x0000", "CHECKMULTISIG DROP 1", "", "OK"], +["0 0x02 0x0000 0", "CHECKMULTISIG DROP 1", "", "OK"], +["0 0x02 0x0000 0 1", "CHECKMULTISIG DROP 1", "", "OK"], +["0 0 0x02 0x0000", "CHECKMULTISIGVERIFY 1", "", "OK"], +["0 0x02 0x0000 0", "CHECKMULTISIGVERIFY 1", "", "OK"], + +["While not really correctly DER encoded, the empty signature is allowed by"], +["STRICTENC/DERSIG to provide a compact way to provide a deliberately invalid signature."], +["0", "0x21 0x02865c40293a680cb9c020e7b1e106d8c1916d3cef99aa431a56d253e69256dac0 CHECKSIG NOT", "STRICTENC", "OK"], +["0", "0x21 0x02865c40293a680cb9c020e7b1e106d8c1916d3cef99aa431a56d253e69256dac0 CHECKSIG NOT", "DERSIG", "OK"], +["0 0", "1 0x21 0x02865c40293a680cb9c020e7b1e106d8c1916d3cef99aa431a56d253e69256dac0 1 CHECKMULTISIG NOT", "STRICTENC", "OK"], +["0 0", "1 0x21 0x02865c40293a680cb9c020e7b1e106d8c1916d3cef99aa431a56d253e69256dac0 1 CHECKMULTISIG NOT", "DERSIG", "OK"], + +["CHECKMULTISIG evaluation order tests. CHECKMULTISIG evaluates signatures and"], +["pubkeys in a specific order, and will exit early if the number of signatures"], +["left to check is greater than the number of keys left. As STRICTENC/DERSIG fails the"], +["script when it reaches an invalidly encoded signature or pubkey, we can use it"], +["to test the exact order in which signatures and pubkeys are evaluated by"], +["distinguishing CHECKMULTISIG returning false on the stack and the script as a"], +["whole failing."], +[ + "0 0x47 0x3044022044dc17b0887c161bb67ba9635bf758735bdde503e4b0a0987f587f14a4e1143d022009a215772d49a85dae40d8ca03955af26ad3978a0ff965faa12915e9586249a501 0x47 0x3044022044dc17b0887c161bb67ba9635bf758735bdde503e4b0a0987f587f14a4e1143d022009a215772d49a85dae40d8ca03955af26ad3978a0ff965faa12915e9586249a501", + "2 0 0x21 0x02865c40293a680cb9c020e7b1e106d8c1916d3cef99aa431a56d253e69256dac0 2 CHECKMULTISIG NOT", + "STRICTENC", "OK", + "2-of-2 CHECKMULTISIG NOT with the second pubkey invalid, and both signatures validly encoded. Valid pubkey fails, and CHECKMULTISIG exits early, prior to evaluation of second invalid pubkey." +], +[ + "0 1 0x47 0x3044022044dc17b0887c161bb67ba9635bf758735bdde503e4b0a0987f587f14a4e1143d022009a215772d49a85dae40d8ca03955af26ad3978a0ff965faa12915e9586249a501", + "2 0x21 0x02865c40293a680cb9c020e7b1e106d8c1916d3cef99aa431a56d253e69256dac0 0x21 0x02865c40293a680cb9c020e7b1e106d8c1916d3cef99aa431a56d253e69256dac0 2 CHECKMULTISIG NOT", + "STRICTENC", "OK", + "2-of-2 CHECKMULTISIG NOT with both pubkeys valid, but second signature invalid. Valid pubkey fails, and CHECKMULTISIG exits early, prior to evaluation of second invalid signature (STRICTENC enabled)." +], +[ + "0 1 0x47 0x3044022044dc17b0887c161bb67ba9635bf758735bdde503e4b0a0987f587f14a4e1143d022009a215772d49a85dae40d8ca03955af26ad3978a0ff965faa12915e9586249a501", + "2 0x21 0x02865c40293a680cb9c020e7b1e106d8c1916d3cef99aa431a56d253e69256dac0 0x21 0x02865c40293a680cb9c020e7b1e106d8c1916d3cef99aa431a56d253e69256dac0 2 CHECKMULTISIG NOT", + "DERSIG", "OK", + "2-of-2 CHECKMULTISIG NOT with both pubkeys valid, but second signature invalid. Valid pubkey fails, and CHECKMULTISIG exits early, prior to evaluation of second invalid signature (DERSIG enabled)." +], + +["Increase test coverage for DERSIG"], +["0x4a 0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "0 CHECKSIG NOT", "", "OK", "Overly long signature is correctly encoded"], +["0x25 0x30220220000000000000000000000000000000000000000000000000000000000000000000", "0 CHECKSIG NOT", "", "OK", "Missing S is correctly encoded"], +["0x27 0x3024021077777777777777777777777777777777020a7777777777777777777777777777777701", "0 CHECKSIG NOT", "", "OK", "S with invalid S length is correctly encoded"], +["0x27 0x302403107777777777777777777777777777777702107777777777777777777777777777777701", "0 CHECKSIG NOT", "", "OK", "Non-integer R is correctly encoded"], +["0x27 0x302402107777777777777777777777777777777703107777777777777777777777777777777701", "0 CHECKSIG NOT", "", "OK", "Non-integer S is correctly encoded"], +["0x17 0x3014020002107777777777777777777777777777777701", "0 CHECKSIG NOT", "", "OK", "Zero-length R is correctly encoded"], +["0x17 0x3014021077777777777777777777777777777777020001", "0 CHECKSIG NOT", "", "OK", "Zero-length S is correctly encoded for DERSIG"], +["0x27 0x302402107777777777777777777777777777777702108777777777777777777777777777777701", "0 CHECKSIG NOT", "", "OK", "Negative S is correctly encoded"], + +["2147483648", "CHECKSEQUENCEVERIFY", "CHECKSEQUENCEVERIFY", "OK", "CSV passes if stack top bit 1 << 31 is set"], + +["", "DEPTH", "P2SH,STRICTENC", "EVAL_FALSE", "Test the test: we should have an empty stack after scriptSig evaluation"], +[" ", "DEPTH", "P2SH,STRICTENC", "EVAL_FALSE", "and multiple spaces should not change that."], +[" ", "DEPTH", "P2SH,STRICTENC", "EVAL_FALSE"], +[" ", "DEPTH", "P2SH,STRICTENC", "EVAL_FALSE"], + +["", "", "P2SH,STRICTENC","EVAL_FALSE"], +["", "NOP", "P2SH,STRICTENC","EVAL_FALSE"], +["", "NOP DEPTH", "P2SH,STRICTENC", "EVAL_FALSE"], +["NOP", "", "P2SH,STRICTENC", "EVAL_FALSE"], +["NOP", "DEPTH", "P2SH,STRICTENC", "EVAL_FALSE"], +["NOP","NOP", "P2SH,STRICTENC", "EVAL_FALSE"], +["NOP","NOP DEPTH", "P2SH,STRICTENC", "EVAL_FALSE"], + +["DEPTH", "", "P2SH,STRICTENC", "EVAL_FALSE"], + +["0x4c01","0x01 NOP", "P2SH,STRICTENC","BAD_OPCODE", "PUSHDATA1 with not enough bytes"], +["0x4d0200ff","0x01 NOP", "P2SH,STRICTENC","BAD_OPCODE", "PUSHDATA2 with not enough bytes"], +["0x4e03000000ffff","0x01 NOP", "P2SH,STRICTENC","BAD_OPCODE", "PUSHDATA4 with not enough bytes"], + +["1", "IF 0x50 ENDIF 1", "P2SH,STRICTENC","BAD_OPCODE", "0x50 is reserved"], +["0x52", "0x5f ADD 0x60 EQUAL", "P2SH,STRICTENC","EVAL_FALSE", "0x51 through 0x60 push 1 through 16 onto stack"], +["0","NOP", "P2SH,STRICTENC","EVAL_FALSE",""], +["1", "IF VER ELSE 1 ENDIF", "P2SH,STRICTENC", "BAD_OPCODE", "VER non-functional"], +["0", "IF VERIF ELSE 1 ENDIF", "P2SH,STRICTENC", "BAD_OPCODE", "VERIF illegal everywhere"], +["0", "IF ELSE 1 ELSE VERIF ENDIF", "P2SH,STRICTENC", "BAD_OPCODE", "VERIF illegal everywhere"], +["0", "IF VERNOTIF ELSE 1 ENDIF", "P2SH,STRICTENC", "BAD_OPCODE", "VERNOTIF illegal everywhere"], +["0", "IF ELSE 1 ELSE VERNOTIF ENDIF", "P2SH,STRICTENC", "BAD_OPCODE", "VERNOTIF illegal everywhere"], + +["1 IF", "1 ENDIF", "P2SH,STRICTENC", "UNBALANCED_CONDITIONAL", "IF/ENDIF can't span scriptSig/scriptPubKey"], +["1 IF 0 ENDIF", "1 ENDIF", "P2SH,STRICTENC", "UNBALANCED_CONDITIONAL"], +["1 ELSE 0 ENDIF", "1", "P2SH,STRICTENC", "UNBALANCED_CONDITIONAL"], +["0 NOTIF", "123", "P2SH,STRICTENC", "UNBALANCED_CONDITIONAL"], + +["0", "DUP IF ENDIF", "P2SH,STRICTENC", "EVAL_FALSE"], +["0", "IF 1 ENDIF", "P2SH,STRICTENC", "EVAL_FALSE"], +["0", "DUP IF ELSE ENDIF", "P2SH,STRICTENC", "EVAL_FALSE"], +["0", "IF 1 ELSE ENDIF", "P2SH,STRICTENC", "EVAL_FALSE"], +["0", "NOTIF ELSE 1 ENDIF", "P2SH,STRICTENC", "EVAL_FALSE"], + +["0 1", "IF IF 1 ELSE 0 ENDIF ENDIF", "P2SH,STRICTENC", "EVAL_FALSE"], +["0 0", "IF IF 1 ELSE 0 ENDIF ENDIF", "P2SH,STRICTENC", "EVAL_FALSE"], +["1 0", "IF IF 1 ELSE 0 ENDIF ELSE IF 0 ELSE 1 ENDIF ENDIF", "P2SH,STRICTENC", "EVAL_FALSE"], +["0 1", "IF IF 1 ELSE 0 ENDIF ELSE IF 0 ELSE 1 ENDIF ENDIF", "P2SH,STRICTENC", "EVAL_FALSE"], + +["0 0", "NOTIF IF 1 ELSE 0 ENDIF ENDIF", "P2SH,STRICTENC", "EVAL_FALSE"], +["0 1", "NOTIF IF 1 ELSE 0 ENDIF ENDIF", "P2SH,STRICTENC", "EVAL_FALSE"], +["1 1", "NOTIF IF 1 ELSE 0 ENDIF ELSE IF 0 ELSE 1 ENDIF ENDIF", "P2SH,STRICTENC", "EVAL_FALSE"], +["0 0", "NOTIF IF 1 ELSE 0 ENDIF ELSE IF 0 ELSE 1 ENDIF ENDIF", "P2SH,STRICTENC", "EVAL_FALSE"], + +["1", "IF RETURN ELSE ELSE 1 ENDIF", "P2SH,STRICTENC", "OP_RETURN", "Multiple ELSEs"], +["1", "IF 1 ELSE ELSE RETURN ENDIF", "P2SH,STRICTENC", "OP_RETURN"], + +["1", "ENDIF", "P2SH,STRICTENC", "UNBALANCED_CONDITIONAL", "Malformed IF/ELSE/ENDIF sequence"], +["1", "ELSE ENDIF", "P2SH,STRICTENC", "UNBALANCED_CONDITIONAL"], +["1", "ENDIF ELSE", "P2SH,STRICTENC", "UNBALANCED_CONDITIONAL"], +["1", "ENDIF ELSE IF", "P2SH,STRICTENC", "UNBALANCED_CONDITIONAL"], +["1", "IF ELSE ENDIF ELSE", "P2SH,STRICTENC", "UNBALANCED_CONDITIONAL"], +["1", "IF ELSE ENDIF ELSE ENDIF", "P2SH,STRICTENC", "UNBALANCED_CONDITIONAL"], +["1", "IF ENDIF ENDIF", "P2SH,STRICTENC", "UNBALANCED_CONDITIONAL"], +["1", "IF ELSE ELSE ENDIF ENDIF", "P2SH,STRICTENC", "UNBALANCED_CONDITIONAL"], + +["1", "RETURN", "P2SH,STRICTENC", "OP_RETURN"], +["1", "DUP IF RETURN ENDIF", "P2SH,STRICTENC", "OP_RETURN"], + +["1", "RETURN 'data'", "P2SH,STRICTENC", "OP_RETURN", "canonical prunable txout format"], +["0 IF", "RETURN ENDIF 1", "P2SH,STRICTENC", "UNBALANCED_CONDITIONAL", "still prunable because IF/ENDIF can't span scriptSig/scriptPubKey"], + +["0", "VERIFY 1", "P2SH,STRICTENC", "VERIFY"], +["1", "VERIFY", "P2SH,STRICTENC", "EVAL_FALSE"], +["1", "VERIFY 0", "P2SH,STRICTENC", "EVAL_FALSE"], + +["1 TOALTSTACK", "FROMALTSTACK 1", "P2SH,STRICTENC", "INVALID_ALTSTACK_OPERATION", "alt stack not shared between sig/pubkey"], + +["IFDUP", "DEPTH 0 EQUAL", "P2SH,STRICTENC", "INVALID_STACK_OPERATION"], +["DROP", "DEPTH 0 EQUAL", "P2SH,STRICTENC", "INVALID_STACK_OPERATION"], +["DUP", "DEPTH 0 EQUAL", "P2SH,STRICTENC", "INVALID_STACK_OPERATION"], +["1", "DUP 1 ADD 2 EQUALVERIFY 0 EQUAL", "P2SH,STRICTENC", "EVAL_FALSE"], +["NOP", "NIP", "P2SH,STRICTENC", "INVALID_STACK_OPERATION"], +["NOP", "1 NIP", "P2SH,STRICTENC", "INVALID_STACK_OPERATION"], +["NOP", "1 0 NIP", "P2SH,STRICTENC", "EVAL_FALSE"], +["NOP", "OVER 1", "P2SH,STRICTENC", "INVALID_STACK_OPERATION"], +["1", "OVER", "P2SH,STRICTENC", "INVALID_STACK_OPERATION"], +["0 1", "OVER DEPTH 3 EQUALVERIFY", "P2SH,STRICTENC", "EVAL_FALSE"], +["19 20 21", "PICK 19 EQUALVERIFY DEPTH 2 EQUAL", "P2SH,STRICTENC", "INVALID_STACK_OPERATION"], +["NOP", "0 PICK", "P2SH,STRICTENC", "INVALID_STACK_OPERATION"], +["1", "-1 PICK", "P2SH,STRICTENC", "INVALID_STACK_OPERATION"], +["19 20 21", "0 PICK 20 EQUALVERIFY DEPTH 3 EQUAL", "P2SH,STRICTENC", "EQUALVERIFY"], +["19 20 21", "1 PICK 21 EQUALVERIFY DEPTH 3 EQUAL", "P2SH,STRICTENC", "EQUALVERIFY"], +["19 20 21", "2 PICK 22 EQUALVERIFY DEPTH 3 EQUAL", "P2SH,STRICTENC", "EQUALVERIFY"], +["NOP", "0 ROLL", "P2SH,STRICTENC", "INVALID_STACK_OPERATION"], +["1", "-1 ROLL", "P2SH,STRICTENC", "INVALID_STACK_OPERATION"], +["19 20 21", "0 ROLL 20 EQUALVERIFY DEPTH 2 EQUAL", "P2SH,STRICTENC", "EQUALVERIFY"], +["19 20 21", "1 ROLL 21 EQUALVERIFY DEPTH 2 EQUAL", "P2SH,STRICTENC", "EQUALVERIFY"], +["19 20 21", "2 ROLL 22 EQUALVERIFY DEPTH 2 EQUAL", "P2SH,STRICTENC", "EQUALVERIFY"], +["NOP", "ROT 1", "P2SH,STRICTENC", "INVALID_STACK_OPERATION"], +["NOP", "1 ROT 1", "P2SH,STRICTENC", "INVALID_STACK_OPERATION"], +["NOP", "1 2 ROT 1", "P2SH,STRICTENC", "INVALID_STACK_OPERATION"], +["NOP", "0 1 2 ROT", "P2SH,STRICTENC", "EVAL_FALSE"], +["NOP", "SWAP 1", "P2SH,STRICTENC", "INVALID_STACK_OPERATION"], +["1", "SWAP 1", "P2SH,STRICTENC", "INVALID_STACK_OPERATION"], +["0 1", "SWAP 1 EQUALVERIFY", "P2SH,STRICTENC", "EQUALVERIFY"], +["NOP", "TUCK 1", "P2SH,STRICTENC", "INVALID_STACK_OPERATION"], +["1", "TUCK 1", "P2SH,STRICTENC", "INVALID_STACK_OPERATION"], +["1 0", "TUCK DEPTH 3 EQUALVERIFY SWAP 2DROP", "P2SH,STRICTENC", "EVAL_FALSE"], +["NOP", "2DUP 1", "P2SH,STRICTENC", "INVALID_STACK_OPERATION"], +["1", "2DUP 1", "P2SH,STRICTENC", "INVALID_STACK_OPERATION"], +["NOP", "3DUP 1", "P2SH,STRICTENC", "INVALID_STACK_OPERATION"], +["1", "3DUP 1", "P2SH,STRICTENC", "INVALID_STACK_OPERATION"], +["1 2", "3DUP 1", "P2SH,STRICTENC", "INVALID_STACK_OPERATION"], +["NOP", "2OVER 1", "P2SH,STRICTENC", "INVALID_STACK_OPERATION"], +["1", "2 3 2OVER 1", "P2SH,STRICTENC", "INVALID_STACK_OPERATION"], +["NOP", "2SWAP 1", "P2SH,STRICTENC", "INVALID_STACK_OPERATION"], +["1", "2 3 2SWAP 1", "P2SH,STRICTENC", "INVALID_STACK_OPERATION"], + + +["NOP", "SIZE 1", "P2SH,STRICTENC", "INVALID_STACK_OPERATION"], + +["TEST DISABLED OP CODES (CVE-2010-5137)"], +["'a' 'b'", "CAT", "P2SH,STRICTENC", "DISABLED_OPCODE", "CAT disabled"], +["'a' 'b' 0", "IF CAT ELSE 1 ENDIF", "P2SH,STRICTENC", "DISABLED_OPCODE", "CAT disabled"], +["'abc' 1 1", "SUBSTR", "P2SH,STRICTENC", "DISABLED_OPCODE", "SUBSTR disabled"], +["'abc' 1 1 0", "IF SUBSTR ELSE 1 ENDIF", "P2SH,STRICTENC", "DISABLED_OPCODE", "SUBSTR disabled"], +["'abc' 2 0", "IF LEFT ELSE 1 ENDIF", "P2SH,STRICTENC", "DISABLED_OPCODE", "LEFT disabled"], +["'abc' 2 0", "IF RIGHT ELSE 1 ENDIF", "P2SH,STRICTENC", "DISABLED_OPCODE", "RIGHT disabled"], +["'abc'", "IF INVERT ELSE 1 ENDIF", "P2SH,STRICTENC", "DISABLED_OPCODE", "INVERT disabled"], +["1 2 0 IF AND ELSE 1 ENDIF", "NOP", "P2SH,STRICTENC", "DISABLED_OPCODE", "AND disabled"], +["1 2 0 IF OR ELSE 1 ENDIF", "NOP", "P2SH,STRICTENC", "DISABLED_OPCODE", "OR disabled"], +["1 2 0 IF XOR ELSE 1 ENDIF", "NOP", "P2SH,STRICTENC", "DISABLED_OPCODE", "XOR disabled"], +["2 0 IF 2MUL ELSE 1 ENDIF", "NOP", "P2SH,STRICTENC", "DISABLED_OPCODE", "2MUL disabled"], +["2 0 IF 2DIV ELSE 1 ENDIF", "NOP", "P2SH,STRICTENC", "DISABLED_OPCODE", "2DIV disabled"], +["2 2 0 IF MUL ELSE 1 ENDIF", "NOP", "P2SH,STRICTENC", "DISABLED_OPCODE", "MUL disabled"], +["2 2 0 IF DIV ELSE 1 ENDIF", "NOP", "P2SH,STRICTENC", "DISABLED_OPCODE", "DIV disabled"], +["2 2 0 IF MOD ELSE 1 ENDIF", "NOP", "P2SH,STRICTENC", "DISABLED_OPCODE", "MOD disabled"], +["2 2 0 IF LSHIFT ELSE 1 ENDIF", "NOP", "P2SH,STRICTENC", "DISABLED_OPCODE", "LSHIFT disabled"], +["2 2 0 IF RSHIFT ELSE 1 ENDIF", "NOP", "P2SH,STRICTENC", "DISABLED_OPCODE", "RSHIFT disabled"], + +["", "EQUAL NOT", "P2SH,STRICTENC", "INVALID_STACK_OPERATION", "EQUAL must error when there are no stack items"], +["0", "EQUAL NOT", "P2SH,STRICTENC", "INVALID_STACK_OPERATION", "EQUAL must error when there are not 2 stack items"], +["0 1","EQUAL", "P2SH,STRICTENC", "EVAL_FALSE"], +["1 1 ADD", "0 EQUAL", "P2SH,STRICTENC", "EVAL_FALSE"], +["11 1 ADD 12 SUB", "11 EQUAL", "P2SH,STRICTENC", "EVAL_FALSE"], + +["2147483648 0 ADD", "NOP", "P2SH,STRICTENC", "SCRIPTNUM", "arithmetic operands must be in range [-2^31...2^31] "], +["-2147483648 0 ADD", "NOP", "P2SH,STRICTENC", "SCRIPTNUM", "arithmetic operands must be in range [-2^31...2^31] "], +["2147483647 DUP ADD", "4294967294 NUMEQUAL", "P2SH,STRICTENC", "SCRIPTNUM", "NUMEQUAL must be in numeric range"], +["'abcdef' NOT", "0 EQUAL", "P2SH,STRICTENC", "SCRIPTNUM", "NOT is an arithmetic operand"], + +["2 DUP MUL", "4 EQUAL", "P2SH,STRICTENC", "DISABLED_OPCODE", "disabled"], +["2 DUP DIV", "1 EQUAL", "P2SH,STRICTENC", "DISABLED_OPCODE", "disabled"], +["2 2MUL", "4 EQUAL", "P2SH,STRICTENC", "DISABLED_OPCODE", "disabled"], +["2 2DIV", "1 EQUAL", "P2SH,STRICTENC", "DISABLED_OPCODE", "disabled"], +["7 3 MOD", "1 EQUAL", "P2SH,STRICTENC", "DISABLED_OPCODE", "disabled"], +["2 2 LSHIFT", "8 EQUAL", "P2SH,STRICTENC", "DISABLED_OPCODE", "disabled"], +["2 1 RSHIFT", "1 EQUAL", "P2SH,STRICTENC", "DISABLED_OPCODE", "disabled"], + +["1", "NOP1 CHECKLOCKTIMEVERIFY CHECKSEQUENCEVERIFY NOP4 NOP5 NOP6 NOP7 NOP8 NOP9 NOP10 2 EQUAL", "P2SH,STRICTENC", "EVAL_FALSE"], +["'NOP_1_to_10' NOP1 CHECKLOCKTIMEVERIFY CHECKSEQUENCEVERIFY NOP4 NOP5 NOP6 NOP7 NOP8 NOP9 NOP10","'NOP_1_to_11' EQUAL", "P2SH,STRICTENC", "EVAL_FALSE"], + +["Ensure 100% coverage of discouraged NOPS"], +["1", "NOP1", "P2SH,DISCOURAGE_UPGRADABLE_NOPS", "DISCOURAGE_UPGRADABLE_NOPS"], +["1", "NOP4", "P2SH,DISCOURAGE_UPGRADABLE_NOPS", "DISCOURAGE_UPGRADABLE_NOPS"], +["1", "NOP5", "P2SH,DISCOURAGE_UPGRADABLE_NOPS", "DISCOURAGE_UPGRADABLE_NOPS"], +["1", "NOP6", "P2SH,DISCOURAGE_UPGRADABLE_NOPS", "DISCOURAGE_UPGRADABLE_NOPS"], +["1", "NOP7", "P2SH,DISCOURAGE_UPGRADABLE_NOPS", "DISCOURAGE_UPGRADABLE_NOPS"], +["1", "NOP8", "P2SH,DISCOURAGE_UPGRADABLE_NOPS", "DISCOURAGE_UPGRADABLE_NOPS"], +["1", "NOP9", "P2SH,DISCOURAGE_UPGRADABLE_NOPS", "DISCOURAGE_UPGRADABLE_NOPS"], +["1", "NOP10", "P2SH,DISCOURAGE_UPGRADABLE_NOPS", "DISCOURAGE_UPGRADABLE_NOPS"], + +["NOP10", "1", "P2SH,DISCOURAGE_UPGRADABLE_NOPS", "DISCOURAGE_UPGRADABLE_NOPS", "Discouraged NOP10 in scriptSig"], + +["1 0x01 0xb9", "HASH160 0x14 0x15727299b05b45fdaf9ac9ecf7565cfe27c3e567 EQUAL", + "P2SH,DISCOURAGE_UPGRADABLE_NOPS", "DISCOURAGE_UPGRADABLE_NOPS", "Discouraged NOP10 in redeemScript"], + +["0x50","1", "P2SH,STRICTENC", "BAD_OPCODE", "opcode 0x50 is reserved"], +["1", "IF 0xba ELSE 1 ENDIF", "P2SH,STRICTENC", "BAD_OPCODE", "opcodes above MAX_OPCODE invalid if executed"], +["1", "IF 0xbb ELSE 1 ENDIF", "P2SH,STRICTENC", "BAD_OPCODE"], +["1", "IF 0xbc ELSE 1 ENDIF", "P2SH,STRICTENC", "BAD_OPCODE"], +["1", "IF 0xbd ELSE 1 ENDIF", "P2SH,STRICTENC", "BAD_OPCODE"], +["1", "IF 0xbe ELSE 1 ENDIF", "P2SH,STRICTENC", "BAD_OPCODE"], +["1", "IF 0xbf ELSE 1 ENDIF", "P2SH,STRICTENC", "BAD_OPCODE"], +["1", "IF 0xc0 ELSE 1 ENDIF", "P2SH,STRICTENC", "BAD_OPCODE"], +["1", "IF 0xc1 ELSE 1 ENDIF", "P2SH,STRICTENC", "BAD_OPCODE"], +["1", "IF 0xc2 ELSE 1 ENDIF", "P2SH,STRICTENC", "BAD_OPCODE"], +["1", "IF 0xc3 ELSE 1 ENDIF", "P2SH,STRICTENC", "BAD_OPCODE"], +["1", "IF 0xc4 ELSE 1 ENDIF", "P2SH,STRICTENC", "BAD_OPCODE"], +["1", "IF 0xc5 ELSE 1 ENDIF", "P2SH,STRICTENC", "BAD_OPCODE"], +["1", "IF 0xc6 ELSE 1 ENDIF", "P2SH,STRICTENC", "BAD_OPCODE"], +["1", "IF 0xc7 ELSE 1 ENDIF", "P2SH,STRICTENC", "BAD_OPCODE"], +["1", "IF 0xc8 ELSE 1 ENDIF", "P2SH,STRICTENC", "BAD_OPCODE"], +["1", "IF 0xc9 ELSE 1 ENDIF", "P2SH,STRICTENC", "BAD_OPCODE"], +["1", "IF 0xca ELSE 1 ENDIF", "P2SH,STRICTENC", "BAD_OPCODE"], +["1", "IF 0xcb ELSE 1 ENDIF", "P2SH,STRICTENC", "BAD_OPCODE"], +["1", "IF 0xcc ELSE 1 ENDIF", "P2SH,STRICTENC", "BAD_OPCODE"], +["1", "IF 0xcd ELSE 1 ENDIF", "P2SH,STRICTENC", "BAD_OPCODE"], +["1", "IF 0xce ELSE 1 ENDIF", "P2SH,STRICTENC", "BAD_OPCODE"], +["1", "IF 0xcf ELSE 1 ENDIF", "P2SH,STRICTENC", "BAD_OPCODE"], +["1", "IF 0xd0 ELSE 1 ENDIF", "P2SH,STRICTENC", "BAD_OPCODE"], +["1", "IF 0xd1 ELSE 1 ENDIF", "P2SH,STRICTENC", "BAD_OPCODE"], +["1", "IF 0xd2 ELSE 1 ENDIF", "P2SH,STRICTENC", "BAD_OPCODE"], +["1", "IF 0xd3 ELSE 1 ENDIF", "P2SH,STRICTENC", "BAD_OPCODE"], +["1", "IF 0xd4 ELSE 1 ENDIF", "P2SH,STRICTENC", "BAD_OPCODE"], +["1", "IF 0xd5 ELSE 1 ENDIF", "P2SH,STRICTENC", "BAD_OPCODE"], +["1", "IF 0xd6 ELSE 1 ENDIF", "P2SH,STRICTENC", "BAD_OPCODE"], +["1", "IF 0xd7 ELSE 1 ENDIF", "P2SH,STRICTENC", "BAD_OPCODE"], +["1", "IF 0xd8 ELSE 1 ENDIF", "P2SH,STRICTENC", "BAD_OPCODE"], +["1", "IF 0xd9 ELSE 1 ENDIF", "P2SH,STRICTENC", "BAD_OPCODE"], +["1", "IF 0xda ELSE 1 ENDIF", "P2SH,STRICTENC", "BAD_OPCODE"], +["1", "IF 0xdb ELSE 1 ENDIF", "P2SH,STRICTENC", "BAD_OPCODE"], +["1", "IF 0xdc ELSE 1 ENDIF", "P2SH,STRICTENC", "BAD_OPCODE"], +["1", "IF 0xdd ELSE 1 ENDIF", "P2SH,STRICTENC", "BAD_OPCODE"], +["1", "IF 0xde ELSE 1 ENDIF", "P2SH,STRICTENC", "BAD_OPCODE"], +["1", "IF 0xdf ELSE 1 ENDIF", "P2SH,STRICTENC", "BAD_OPCODE"], +["1", "IF 0xe0 ELSE 1 ENDIF", "P2SH,STRICTENC", "BAD_OPCODE"], +["1", "IF 0xe1 ELSE 1 ENDIF", "P2SH,STRICTENC", "BAD_OPCODE"], +["1", "IF 0xe2 ELSE 1 ENDIF", "P2SH,STRICTENC", "BAD_OPCODE"], +["1", "IF 0xe3 ELSE 1 ENDIF", "P2SH,STRICTENC", "BAD_OPCODE"], +["1", "IF 0xe4 ELSE 1 ENDIF", "P2SH,STRICTENC", "BAD_OPCODE"], +["1", "IF 0xe5 ELSE 1 ENDIF", "P2SH,STRICTENC", "BAD_OPCODE"], +["1", "IF 0xe6 ELSE 1 ENDIF", "P2SH,STRICTENC", "BAD_OPCODE"], +["1", "IF 0xe7 ELSE 1 ENDIF", "P2SH,STRICTENC", "BAD_OPCODE"], +["1", "IF 0xe8 ELSE 1 ENDIF", "P2SH,STRICTENC", "BAD_OPCODE"], +["1", "IF 0xe9 ELSE 1 ENDIF", "P2SH,STRICTENC", "BAD_OPCODE"], +["1", "IF 0xea ELSE 1 ENDIF", "P2SH,STRICTENC", "BAD_OPCODE"], +["1", "IF 0xeb ELSE 1 ENDIF", "P2SH,STRICTENC", "BAD_OPCODE"], +["1", "IF 0xec ELSE 1 ENDIF", "P2SH,STRICTENC", "BAD_OPCODE"], +["1", "IF 0xed ELSE 1 ENDIF", "P2SH,STRICTENC", "BAD_OPCODE"], +["1", "IF 0xee ELSE 1 ENDIF", "P2SH,STRICTENC", "BAD_OPCODE"], +["1", "IF 0xef ELSE 1 ENDIF", "P2SH,STRICTENC", "BAD_OPCODE"], +["1", "IF 0xf0 ELSE 1 ENDIF", "P2SH,STRICTENC", "BAD_OPCODE"], +["1", "IF 0xf1 ELSE 1 ENDIF", "P2SH,STRICTENC", "BAD_OPCODE"], +["1", "IF 0xf2 ELSE 1 ENDIF", "P2SH,STRICTENC", "BAD_OPCODE"], +["1", "IF 0xf3 ELSE 1 ENDIF", "P2SH,STRICTENC", "BAD_OPCODE"], +["1", "IF 0xf4 ELSE 1 ENDIF", "P2SH,STRICTENC", "BAD_OPCODE"], +["1", "IF 0xf5 ELSE 1 ENDIF", "P2SH,STRICTENC", "BAD_OPCODE"], +["1", "IF 0xf6 ELSE 1 ENDIF", "P2SH,STRICTENC", "BAD_OPCODE"], +["1", "IF 0xf7 ELSE 1 ENDIF", "P2SH,STRICTENC", "BAD_OPCODE"], +["1", "IF 0xf8 ELSE 1 ENDIF", "P2SH,STRICTENC", "BAD_OPCODE"], +["1", "IF 0xf9 ELSE 1 ENDIF", "P2SH,STRICTENC", "BAD_OPCODE"], +["1", "IF 0xfa ELSE 1 ENDIF", "P2SH,STRICTENC", "BAD_OPCODE"], +["1", "IF 0xfb ELSE 1 ENDIF", "P2SH,STRICTENC", "BAD_OPCODE"], +["1", "IF 0xfc ELSE 1 ENDIF", "P2SH,STRICTENC", "BAD_OPCODE"], +["1", "IF 0xfd ELSE 1 ENDIF", "P2SH,STRICTENC", "BAD_OPCODE"], +["1", "IF 0xfe ELSE 1 ENDIF", "P2SH,STRICTENC", "BAD_OPCODE"], +["1", "IF 0xff ELSE 1 ENDIF", "P2SH,STRICTENC", "BAD_OPCODE"], + +["1 IF 1 ELSE", "0xff ENDIF", "P2SH,STRICTENC", "UNBALANCED_CONDITIONAL", "invalid because scriptSig and scriptPubKey are processed separately"], + +["NOP", "RIPEMD160", "P2SH,STRICTENC", "INVALID_STACK_OPERATION"], +["NOP", "SHA1", "P2SH,STRICTENC", "INVALID_STACK_OPERATION"], +["NOP", "SHA256", "P2SH,STRICTENC", "INVALID_STACK_OPERATION"], +["NOP", "HASH160", "P2SH,STRICTENC", "INVALID_STACK_OPERATION"], +["NOP", "HASH256", "P2SH,STRICTENC", "INVALID_STACK_OPERATION"], + +["NOP", +"'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb'", +"P2SH,STRICTENC", +"PUSH_SIZE", +">520 byte push"], +["0", +"IF 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' ENDIF 1", +"P2SH,STRICTENC", +"PUSH_SIZE", +">520 byte push in non-executed IF branch"], +["1", +"0x61616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161", +"P2SH,STRICTENC", +"OP_COUNT", +">201 opcodes executed. 0x61 is NOP"], +["0", +"IF 0x6161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161 ENDIF 1", +"P2SH,STRICTENC", +"OP_COUNT", +">201 opcodes including non-executed IF branch. 0x61 is NOP"], +["1 2 3 4 5 0x6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f", +"1 2 3 4 5 6 0x6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f", +"P2SH,STRICTENC", +"STACK_SIZE", +">1,000 stack size (0x6f is 3DUP)"], +["1 2 3 4 5 0x6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f", +"1 TOALTSTACK 2 TOALTSTACK 3 4 5 6 0x6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f", +"P2SH,STRICTENC", +"STACK_SIZE", +">1,000 stack+altstack size"], +["NOP", +"0 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 0x6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f 2DUP 0x616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161", +"P2SH,STRICTENC", +"SCRIPT_SIZE", +"10,001-byte scriptPubKey"], + +["NOP1","NOP10", "P2SH,STRICTENC", "EVAL_FALSE"], + +["1","VER", "P2SH,STRICTENC", "BAD_OPCODE", "OP_VER is reserved"], +["1","VERIF", "P2SH,STRICTENC", "BAD_OPCODE", "OP_VERIF is reserved"], +["1","VERNOTIF", "P2SH,STRICTENC", "BAD_OPCODE", "OP_VERNOTIF is reserved"], +["1","RESERVED", "P2SH,STRICTENC", "BAD_OPCODE", "OP_RESERVED is reserved"], +["1","RESERVED1", "P2SH,STRICTENC", "BAD_OPCODE", "OP_RESERVED1 is reserved"], +["1","RESERVED2", "P2SH,STRICTENC", "BAD_OPCODE", "OP_RESERVED2 is reserved"], +["1","0xba", "P2SH,STRICTENC", "BAD_OPCODE", "0xba == MAX_OPCODE + 1"], + +["2147483648", "1ADD 1", "P2SH,STRICTENC", "SCRIPTNUM", "We cannot do math on 5-byte integers"], +["2147483648", "NEGATE 1", "P2SH,STRICTENC", "SCRIPTNUM", "We cannot do math on 5-byte integers"], +["-2147483648", "1ADD 1", "P2SH,STRICTENC", "SCRIPTNUM", "Because we use a sign bit, -2147483648 is also 5 bytes"], +["2147483647", "1ADD 1SUB 1", "P2SH,STRICTENC", "SCRIPTNUM", "We cannot do math on 5-byte integers, even if the result is 4-bytes"], +["2147483648", "1SUB 1", "P2SH,STRICTENC", "SCRIPTNUM", "We cannot do math on 5-byte integers, even if the result is 4-bytes"], + +["2147483648 1", "BOOLOR 1", "P2SH,STRICTENC", "SCRIPTNUM", "We cannot do BOOLOR on 5-byte integers (but we can still do IF etc)"], +["2147483648 1", "BOOLAND 1", "P2SH,STRICTENC", "SCRIPTNUM", "We cannot do BOOLAND on 5-byte integers"], + +["1", "1 ENDIF", "P2SH,STRICTENC", "UNBALANCED_CONDITIONAL", "ENDIF without IF"], +["1", "IF 1", "P2SH,STRICTENC", "UNBALANCED_CONDITIONAL", "IF without ENDIF"], +["1 IF 1", "ENDIF", "P2SH,STRICTENC", "UNBALANCED_CONDITIONAL", "IFs don't carry over"], + +["NOP", "IF 1 ENDIF", "P2SH,STRICTENC", "INVALID_STACK_OPERATION", "The following tests check the if(stack.size() < N) tests in each opcode"], +["NOP", "NOTIF 1 ENDIF", "P2SH,STRICTENC", "INVALID_STACK_OPERATION", "They are here to catch copy-and-paste errors"], +["NOP", "VERIFY 1", "P2SH,STRICTENC", "INVALID_STACK_OPERATION", "Most of them are duplicated elsewhere,"], + +["NOP", "TOALTSTACK 1", "P2SH,STRICTENC", "INVALID_STACK_OPERATION", "but, hey, more is always better, right?"], +["1", "FROMALTSTACK", "P2SH,STRICTENC", "INVALID_ALTSTACK_OPERATION"], +["1", "2DROP 1", "P2SH,STRICTENC", "INVALID_STACK_OPERATION"], +["1", "2DUP", "P2SH,STRICTENC", "INVALID_STACK_OPERATION"], +["1 1", "3DUP", "P2SH,STRICTENC", "INVALID_STACK_OPERATION"], +["1 1 1", "2OVER", "P2SH,STRICTENC", "INVALID_STACK_OPERATION"], +["1 1 1 1 1", "2ROT", "P2SH,STRICTENC", "INVALID_STACK_OPERATION"], +["1 1 1", "2SWAP", "P2SH,STRICTENC", "INVALID_STACK_OPERATION"], +["NOP", "IFDUP 1", "P2SH,STRICTENC", "INVALID_STACK_OPERATION"], +["NOP", "DROP 1", "P2SH,STRICTENC", "INVALID_STACK_OPERATION"], +["NOP", "DUP 1", "P2SH,STRICTENC", "INVALID_STACK_OPERATION"], +["1", "NIP", "P2SH,STRICTENC", "INVALID_STACK_OPERATION"], +["1", "OVER", "P2SH,STRICTENC", "INVALID_STACK_OPERATION"], +["1 1 1 3", "PICK", "P2SH,STRICTENC", "INVALID_STACK_OPERATION"], +["0", "PICK 1", "P2SH,STRICTENC", "INVALID_STACK_OPERATION"], +["1 1 1 3", "ROLL", "P2SH,STRICTENC", "INVALID_STACK_OPERATION"], +["0", "ROLL 1", "P2SH,STRICTENC", "INVALID_STACK_OPERATION"], +["1 1", "ROT", "P2SH,STRICTENC", "INVALID_STACK_OPERATION"], +["1", "SWAP", "P2SH,STRICTENC", "INVALID_STACK_OPERATION"], +["1", "TUCK", "P2SH,STRICTENC", "INVALID_STACK_OPERATION"], + +["NOP", "SIZE 1", "P2SH,STRICTENC", "INVALID_STACK_OPERATION"], + +["1", "EQUAL 1", "P2SH,STRICTENC", "INVALID_STACK_OPERATION"], +["1", "EQUALVERIFY 1", "P2SH,STRICTENC", "INVALID_STACK_OPERATION"], + +["NOP", "1ADD 1", "P2SH,STRICTENC", "INVALID_STACK_OPERATION"], +["NOP", "1SUB 1", "P2SH,STRICTENC", "INVALID_STACK_OPERATION"], +["NOP", "NEGATE 1", "P2SH,STRICTENC", "INVALID_STACK_OPERATION"], +["NOP", "ABS 1", "P2SH,STRICTENC", "INVALID_STACK_OPERATION"], +["NOP", "NOT 1", "P2SH,STRICTENC", "INVALID_STACK_OPERATION"], +["NOP", "0NOTEQUAL 1", "P2SH,STRICTENC", "INVALID_STACK_OPERATION"], + +["1", "ADD", "P2SH,STRICTENC", "INVALID_STACK_OPERATION"], +["1", "SUB", "P2SH,STRICTENC", "INVALID_STACK_OPERATION"], +["1", "BOOLAND", "P2SH,STRICTENC", "INVALID_STACK_OPERATION"], +["1", "BOOLOR", "P2SH,STRICTENC", "INVALID_STACK_OPERATION"], +["1", "NUMEQUAL", "P2SH,STRICTENC", "INVALID_STACK_OPERATION"], +["1", "NUMEQUALVERIFY 1", "P2SH,STRICTENC", "INVALID_STACK_OPERATION"], +["1", "NUMNOTEQUAL", "P2SH,STRICTENC", "INVALID_STACK_OPERATION"], +["1", "LESSTHAN", "P2SH,STRICTENC", "INVALID_STACK_OPERATION"], +["1", "GREATERTHAN", "P2SH,STRICTENC", "INVALID_STACK_OPERATION"], +["1", "LESSTHANOREQUAL", "P2SH,STRICTENC", "INVALID_STACK_OPERATION"], +["1", "GREATERTHANOREQUAL", "P2SH,STRICTENC", "INVALID_STACK_OPERATION"], +["1", "MIN", "P2SH,STRICTENC", "INVALID_STACK_OPERATION"], +["1", "MAX", "P2SH,STRICTENC", "INVALID_STACK_OPERATION"], +["1 1", "WITHIN", "P2SH,STRICTENC", "INVALID_STACK_OPERATION"], + +["NOP", "RIPEMD160 1", "P2SH,STRICTENC", "INVALID_STACK_OPERATION"], +["NOP", "SHA1 1", "P2SH,STRICTENC", "INVALID_STACK_OPERATION"], +["NOP", "SHA256 1", "P2SH,STRICTENC", "INVALID_STACK_OPERATION"], +["NOP", "HASH160 1", "P2SH,STRICTENC", "INVALID_STACK_OPERATION"], +["NOP", "HASH256 1", "P2SH,STRICTENC", "INVALID_STACK_OPERATION"], + +["Increase CHECKSIG and CHECKMULTISIG negative test coverage"], +["", "CHECKSIG NOT", "STRICTENC", "INVALID_STACK_OPERATION", "CHECKSIG must error when there are no stack items"], +["0", "CHECKSIG NOT", "STRICTENC", "INVALID_STACK_OPERATION", "CHECKSIG must error when there are not 2 stack items"], +["", "CHECKMULTISIG NOT", "STRICTENC", "INVALID_STACK_OPERATION", "CHECKMULTISIG must error when there are no stack items"], +["", "-1 CHECKMULTISIG NOT", "STRICTENC", "PUBKEY_COUNT", "CHECKMULTISIG must error when the specified number of pubkeys is negative"], +["", "1 CHECKMULTISIG NOT", "STRICTENC", "INVALID_STACK_OPERATION", "CHECKMULTISIG must error when there are not enough pubkeys on the stack"], +["", "-1 0 CHECKMULTISIG NOT", "STRICTENC", "SIG_COUNT", "CHECKMULTISIG must error when the specified number of signatures is negative"], +["", "1 'pk1' 1 CHECKMULTISIG NOT", "STRICTENC", "INVALID_STACK_OPERATION", "CHECKMULTISIG must error when there are not enough signatures on the stack"], +["", "'dummy' 'sig1' 1 'pk1' 1 CHECKMULTISIG IF 1 ENDIF", "", "EVAL_FALSE", "CHECKMULTISIG must push false to stack when signature is invalid when NOT in strict enc mode"], + +["", +"0 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG", +"P2SH,STRICTENC", +"OP_COUNT", +"202 CHECKMULTISIGS, fails due to 201 op limit"], + +["1", +"0 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY", +"P2SH,STRICTENC", +"INVALID_STACK_OPERATION", +""], + +["", +"NOP NOP NOP NOP NOP NOP NOP NOP NOP NOP NOP NOP NOP 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIG 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIG 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIG 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIG 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIG 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIG 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIG 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIG 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIG", +"P2SH,STRICTENC", +"OP_COUNT", +"Fails due to 201 script operation limit"], + +["1", +"NOP NOP NOP NOP NOP NOP NOP NOP NOP NOP NOP NOP NOP 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIGVERIFY 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIGVERIFY 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIGVERIFY 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIGVERIFY 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIGVERIFY 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIGVERIFY 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIGVERIFY 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIGVERIFY 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIGVERIFY", +"P2SH,STRICTENC", +"OP_COUNT", +""], + + +["0 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21", "21 CHECKMULTISIG 1", "P2SH,STRICTENC", "PUBKEY_COUNT", "nPubKeys > 20"], +["0 'sig' 1 0", "CHECKMULTISIG 1", "P2SH,STRICTENC", "SIG_COUNT", "nSigs > nPubKeys"], + + +["NOP 0x01 1", "HASH160 0x14 0xda1745e9b549bd0bfa1a569971c77eba30cd5a4b EQUAL", "P2SH,STRICTENC", "SIG_PUSHONLY", "Tests for Script.IsPushOnly()"], +["NOP1 0x01 1", "HASH160 0x14 0xda1745e9b549bd0bfa1a569971c77eba30cd5a4b EQUAL", "P2SH,STRICTENC", "SIG_PUSHONLY"], + +["0 0x01 0x50", "HASH160 0x14 0xece424a6bb6ddf4db592c0faed60685047a361b1 EQUAL", "P2SH,STRICTENC", "BAD_OPCODE", "OP_RESERVED in P2SH should fail"], +["0 0x01 VER", "HASH160 0x14 0x0f4d7845db968f2a81b530b6f3c1d6246d4c7e01 EQUAL", "P2SH,STRICTENC", "BAD_OPCODE", "OP_VER in P2SH should fail"], + +["0x00", "'00' EQUAL", "P2SH,STRICTENC", "EVAL_FALSE", "Basic OP_0 execution"], + +["MINIMALDATA enforcement for PUSHDATAs"], + +["0x4c 0x00", "DROP 1", "MINIMALDATA", "MINIMALDATA", "Empty vector minimally represented by OP_0"], +["0x01 0x81", "DROP 1", "MINIMALDATA", "MINIMALDATA", "-1 minimally represented by OP_1NEGATE"], +["0x01 0x01", "DROP 1", "MINIMALDATA", "MINIMALDATA", "1 to 16 minimally represented by OP_1 to OP_16"], +["0x01 0x02", "DROP 1", "MINIMALDATA", "MINIMALDATA"], +["0x01 0x03", "DROP 1", "MINIMALDATA", "MINIMALDATA"], +["0x01 0x04", "DROP 1", "MINIMALDATA", "MINIMALDATA"], +["0x01 0x05", "DROP 1", "MINIMALDATA", "MINIMALDATA"], +["0x01 0x06", "DROP 1", "MINIMALDATA", "MINIMALDATA"], +["0x01 0x07", "DROP 1", "MINIMALDATA", "MINIMALDATA"], +["0x01 0x08", "DROP 1", "MINIMALDATA", "MINIMALDATA"], +["0x01 0x09", "DROP 1", "MINIMALDATA", "MINIMALDATA"], +["0x01 0x0a", "DROP 1", "MINIMALDATA", "MINIMALDATA"], +["0x01 0x0b", "DROP 1", "MINIMALDATA", "MINIMALDATA"], +["0x01 0x0c", "DROP 1", "MINIMALDATA", "MINIMALDATA"], +["0x01 0x0d", "DROP 1", "MINIMALDATA", "MINIMALDATA"], +["0x01 0x0e", "DROP 1", "MINIMALDATA", "MINIMALDATA"], +["0x01 0x0f", "DROP 1", "MINIMALDATA", "MINIMALDATA"], +["0x01 0x10", "DROP 1", "MINIMALDATA", "MINIMALDATA"], + +["0x4c 0x48 0x111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111", "DROP 1", "MINIMALDATA", + "MINIMALDATA", + "PUSHDATA1 of 72 bytes minimally represented by direct push"], + +["0x4d 0xFF00 0x111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111", "DROP 1", "MINIMALDATA", + "MINIMALDATA", + "PUSHDATA2 of 255 bytes minimally represented by PUSHDATA1"], + +["0x4e 0x00010000 0x11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111", "DROP 1", "MINIMALDATA", + "MINIMALDATA", + "PUSHDATA4 of 256 bytes minimally represented by PUSHDATA2"], + +["MINIMALDATA enforcement for numeric arguments"], + +["0x01 0x00", "NOT DROP 1", "MINIMALDATA", "SCRIPTNUM", "numequals 0"], +["0x02 0x0000", "NOT DROP 1", "MINIMALDATA", "SCRIPTNUM", "numequals 0"], +["0x01 0x80", "NOT DROP 1", "MINIMALDATA", "SCRIPTNUM", "0x80 (negative zero) numequals 0"], +["0x02 0x0080", "NOT DROP 1", "MINIMALDATA", "SCRIPTNUM", "numequals 0"], +["0x02 0x0500", "NOT DROP 1", "MINIMALDATA", "SCRIPTNUM", "numequals 5"], +["0x03 0x050000", "NOT DROP 1", "MINIMALDATA", "SCRIPTNUM", "numequals 5"], +["0x02 0x0580", "NOT DROP 1", "MINIMALDATA", "SCRIPTNUM", "numequals -5"], +["0x03 0x050080", "NOT DROP 1", "MINIMALDATA", "SCRIPTNUM", "numequals -5"], +["0x03 0xff7f80", "NOT DROP 1", "MINIMALDATA", "SCRIPTNUM", "Minimal encoding is 0xffff"], +["0x03 0xff7f00", "NOT DROP 1", "MINIMALDATA", "SCRIPTNUM", "Minimal encoding is 0xff7f"], +["0x04 0xffff7f80", "NOT DROP 1", "MINIMALDATA", "SCRIPTNUM", "Minimal encoding is 0xffffff"], +["0x04 0xffff7f00", "NOT DROP 1", "MINIMALDATA", "SCRIPTNUM", "Minimal encoding is 0xffff7f"], + +["Test every numeric-accepting opcode for correct handling of the numeric minimal encoding rule"], + +["1 0x02 0x0000", "PICK DROP", "MINIMALDATA", "SCRIPTNUM"], +["1 0x02 0x0000", "ROLL DROP 1", "MINIMALDATA", "SCRIPTNUM"], +["0x02 0x0000", "1ADD DROP 1", "MINIMALDATA", "SCRIPTNUM"], +["0x02 0x0000", "1SUB DROP 1", "MINIMALDATA", "SCRIPTNUM"], +["0x02 0x0000", "NEGATE DROP 1", "MINIMALDATA", "SCRIPTNUM"], +["0x02 0x0000", "ABS DROP 1", "MINIMALDATA", "SCRIPTNUM"], +["0x02 0x0000", "NOT DROP 1", "MINIMALDATA", "SCRIPTNUM"], +["0x02 0x0000", "0NOTEQUAL DROP 1", "MINIMALDATA", "SCRIPTNUM"], + +["0 0x02 0x0000", "ADD DROP 1", "MINIMALDATA", "SCRIPTNUM"], +["0x02 0x0000 0", "ADD DROP 1", "MINIMALDATA", "SCRIPTNUM"], +["0 0x02 0x0000", "SUB DROP 1", "MINIMALDATA", "SCRIPTNUM"], +["0x02 0x0000 0", "SUB DROP 1", "MINIMALDATA", "SCRIPTNUM"], +["0 0x02 0x0000", "BOOLAND DROP 1", "MINIMALDATA", "SCRIPTNUM"], +["0x02 0x0000 0", "BOOLAND DROP 1", "MINIMALDATA", "SCRIPTNUM"], +["0 0x02 0x0000", "BOOLOR DROP 1", "MINIMALDATA", "SCRIPTNUM"], +["0x02 0x0000 0", "BOOLOR DROP 1", "MINIMALDATA", "SCRIPTNUM"], +["0 0x02 0x0000", "NUMEQUAL DROP 1", "MINIMALDATA", "SCRIPTNUM"], +["0x02 0x0000 1", "NUMEQUAL DROP 1", "MINIMALDATA", "SCRIPTNUM"], +["0 0x02 0x0000", "NUMEQUALVERIFY 1", "MINIMALDATA", "SCRIPTNUM"], +["0x02 0x0000 0", "NUMEQUALVERIFY 1", "MINIMALDATA", "SCRIPTNUM"], +["0 0x02 0x0000", "NUMNOTEQUAL DROP 1", "MINIMALDATA", "SCRIPTNUM"], +["0x02 0x0000 0", "NUMNOTEQUAL DROP 1", "MINIMALDATA", "SCRIPTNUM"], +["0 0x02 0x0000", "LESSTHAN DROP 1", "MINIMALDATA", "SCRIPTNUM"], +["0x02 0x0000 0", "LESSTHAN DROP 1", "MINIMALDATA", "SCRIPTNUM"], +["0 0x02 0x0000", "GREATERTHAN DROP 1", "MINIMALDATA", "SCRIPTNUM"], +["0x02 0x0000 0", "GREATERTHAN DROP 1", "MINIMALDATA", "SCRIPTNUM"], +["0 0x02 0x0000", "LESSTHANOREQUAL DROP 1", "MINIMALDATA", "SCRIPTNUM"], +["0x02 0x0000 0", "LESSTHANOREQUAL DROP 1", "MINIMALDATA", "SCRIPTNUM"], +["0 0x02 0x0000", "GREATERTHANOREQUAL DROP 1", "MINIMALDATA", "SCRIPTNUM"], +["0x02 0x0000 0", "GREATERTHANOREQUAL DROP 1", "MINIMALDATA", "SCRIPTNUM"], +["0 0x02 0x0000", "MIN DROP 1", "MINIMALDATA", "SCRIPTNUM"], +["0x02 0x0000 0", "MIN DROP 1", "MINIMALDATA", "SCRIPTNUM"], +["0 0x02 0x0000", "MAX DROP 1", "MINIMALDATA", "SCRIPTNUM"], +["0x02 0x0000 0", "MAX DROP 1", "MINIMALDATA", "SCRIPTNUM"], + +["0x02 0x0000 0 0", "WITHIN DROP 1", "MINIMALDATA", "SCRIPTNUM"], +["0 0x02 0x0000 0", "WITHIN DROP 1", "MINIMALDATA", "SCRIPTNUM"], +["0 0 0x02 0x0000", "WITHIN DROP 1", "MINIMALDATA", "SCRIPTNUM"], + +["0 0 0x02 0x0000", "CHECKMULTISIG DROP 1", "MINIMALDATA", "SCRIPTNUM"], +["0 0x02 0x0000 0", "CHECKMULTISIG DROP 1", "MINIMALDATA", "SCRIPTNUM"], +["0 0x02 0x0000 0 1", "CHECKMULTISIG DROP 1", "MINIMALDATA", "SCRIPTNUM"], +["0 0 0x02 0x0000", "CHECKMULTISIGVERIFY 1", "MINIMALDATA", "SCRIPTNUM"], +["0 0x02 0x0000 0", "CHECKMULTISIGVERIFY 1", "MINIMALDATA", "SCRIPTNUM"], + + +["Order of CHECKMULTISIG evaluation tests, inverted by swapping the order of"], +["pubkeys/signatures so they fail due to the STRICTENC/DERSIG rules on validly encoded"], +["signatures and pubkeys."], +[ + "0 0x47 0x3044022044dc17b0887c161bb67ba9635bf758735bdde503e4b0a0987f587f14a4e1143d022009a215772d49a85dae40d8ca03955af26ad3978a0ff965faa12915e9586249a501 0x47 0x3044022044dc17b0887c161bb67ba9635bf758735bdde503e4b0a0987f587f14a4e1143d022009a215772d49a85dae40d8ca03955af26ad3978a0ff965faa12915e9586249a501", + "2 0x21 0x02865c40293a680cb9c020e7b1e106d8c1916d3cef99aa431a56d253e69256dac0 0 2 CHECKMULTISIG NOT", + "STRICTENC", + "PUBKEYTYPE", + "2-of-2 CHECKMULTISIG NOT with the first pubkey invalid, and both signatures validly encoded." +], +[ + "0 0x47 0x3044022044dc17b0887c161bb67ba9635bf758735bdde503e4b0a0987f587f14a4e1143d022009a215772d49a85dae40d8ca03955af26ad3978a0ff965faa12915e9586249a501 1", + "2 0x21 0x02865c40293a680cb9c020e7b1e106d8c1916d3cef99aa431a56d253e69256dac0 0x21 0x02865c40293a680cb9c020e7b1e106d8c1916d3cef99aa431a56d253e69256dac0 2 CHECKMULTISIG NOT", + "STRICTENC", + "SIG_DER", + "2-of-2 CHECKMULTISIG NOT with both pubkeys valid, but first signature invalid (STRICTENC enabled)." +], +[ + "0 0x47 0x3044022044dc17b0887c161bb67ba9635bf758735bdde503e4b0a0987f587f14a4e1143d022009a215772d49a85dae40d8ca03955af26ad3978a0ff965faa12915e9586249a501 1", + "2 0x21 0x02865c40293a680cb9c020e7b1e106d8c1916d3cef99aa431a56d253e69256dac0 0x21 0x02865c40293a680cb9c020e7b1e106d8c1916d3cef99aa431a56d253e69256dac0 2 CHECKMULTISIG NOT", + "DERSIG", + "SIG_DER", + "2-of-2 CHECKMULTISIG NOT with both pubkeys valid, but first signature invalid (DERSIG enabled)." +], +[ + "0 0x47 0x304402205451ce65ad844dbb978b8bdedf5082e33b43cae8279c30f2c74d9e9ee49a94f802203fe95a7ccf74da7a232ee523ef4a53cb4d14bdd16289680cdb97a63819b8f42f01 0x46 0x304402205451ce65ad844dbb978b8bdedf5082e33b43cae8279c30f2c74d9e9ee49a94f802203fe95a7ccf74da7a232ee523ef4a53cb4d14bdd16289680cdb97a63819b8f42f", + "2 0x21 0x02a673638cb9587cb68ea08dbef685c6f2d2a751a8b3c6f2a7e9a4999e6e4bfaf5 0x21 0x02a673638cb9587cb68ea08dbef685c6f2d2a751a8b3c6f2a7e9a4999e6e4bfaf5 0x21 0x02a673638cb9587cb68ea08dbef685c6f2d2a751a8b3c6f2a7e9a4999e6e4bfaf5 3 CHECKMULTISIG", + "P2SH,STRICTENC", + "SIG_DER", + "2-of-3 with one valid and one invalid signature due to parse error, nSigs > validSigs (STRICTENC enabled)." +], +[ + "0 0x47 0x304402205451ce65ad844dbb978b8bdedf5082e33b43cae8279c30f2c74d9e9ee49a94f802203fe95a7ccf74da7a232ee523ef4a53cb4d14bdd16289680cdb97a63819b8f42f01 0x46 0x304402205451ce65ad844dbb978b8bdedf5082e33b43cae8279c30f2c74d9e9ee49a94f802203fe95a7ccf74da7a232ee523ef4a53cb4d14bdd16289680cdb97a63819b8f42f", + "2 0x21 0x02a673638cb9587cb68ea08dbef685c6f2d2a751a8b3c6f2a7e9a4999e6e4bfaf5 0x21 0x02a673638cb9587cb68ea08dbef685c6f2d2a751a8b3c6f2a7e9a4999e6e4bfaf5 0x21 0x02a673638cb9587cb68ea08dbef685c6f2d2a751a8b3c6f2a7e9a4999e6e4bfaf5 3 CHECKMULTISIG", + "P2SH,DERSIG", + "SIG_DER", + "2-of-3 with one valid and one invalid signature due to parse error, nSigs > validSigs (DERSIG enabled)." +], + +["Increase DERSIG test coverage"], +["0x4a 0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "0 CHECKSIG NOT", "DERSIG", "SIG_DER", "Overly long signature is incorrectly encoded for DERSIG"], +["0x25 0x30220220000000000000000000000000000000000000000000000000000000000000000000", "0 CHECKSIG NOT", "DERSIG", "SIG_DER", "Missing S is incorrectly encoded for DERSIG"], +["0x27 0x3024021077777777777777777777777777777777020a7777777777777777777777777777777701", "0 CHECKSIG NOT", "DERSIG", "SIG_DER", "S with invalid S length is incorrectly encoded for DERSIG"], +["0x27 0x302403107777777777777777777777777777777702107777777777777777777777777777777701", "0 CHECKSIG NOT", "DERSIG", "SIG_DER", "Non-integer R is incorrectly encoded for DERSIG"], +["0x27 0x302402107777777777777777777777777777777703107777777777777777777777777777777701", "0 CHECKSIG NOT", "DERSIG", "SIG_DER", "Non-integer S is incorrectly encoded for DERSIG"], +["0x17 0x3014020002107777777777777777777777777777777701", "0 CHECKSIG NOT", "DERSIG", "SIG_DER", "Zero-length R is incorrectly encoded for DERSIG"], +["0x17 0x3014021077777777777777777777777777777777020001", "0 CHECKSIG NOT", "DERSIG", "SIG_DER", "Zero-length S is incorrectly encoded for DERSIG"], +["0x27 0x302402107777777777777777777777777777777702108777777777777777777777777777777701", "0 CHECKSIG NOT", "DERSIG", "SIG_DER", "Negative S is incorrectly encoded for DERSIG"], + +["Some basic segwit checks"], +[["00", 0.00000000 ], "", "0 0x206e340b9cffb37a989ca544e6bb780a2c78901d3fb33738768511a30617afa01d", "P2SH,WITNESS", "EVAL_FALSE", "Invalid witness script"], +[["51", 0.00000000 ], "", "0 0x206e340b9cffb37a989ca544e6bb780a2c78901d3fb33738768511a30617afa01d", "P2SH,WITNESS", "WITNESS_PROGRAM_MISMATCH", "Witness script hash mismatch"], +[["00", 0.00000000 ], "", "0 0x206e340b9cffb37a989ca544e6bb780a2c78901d3fb33738768511a30617afa01d", "", "OK", "Invalid witness script without WITNESS"], +[["51", 0.00000000 ], "", "0 0x206e340b9cffb37a989ca544e6bb780a2c78901d3fb33738768511a30617afa01d", "", "OK", "Witness script hash mismatch without WITNESS"], +[["51", 0.00000000 ], "", "-1 0x021234", "P2SH,WITNESS", "WITNESS_UNEXPECTED", "OP_1NEGATE does not introduce a witness program"], +[["51", 0.00000000 ], "00", "1 0x021234", "P2SH,WITNESS", "WITNESS_MALLEATED", "OP_1 does introduce a witness program"], +[["51", 0.00000000 ], "00", "16 0x021234", "P2SH,WITNESS", "WITNESS_MALLEATED", "OP_16 does introduce a witness program"], +[["51", 0.00000000 ], "", "NOP 0x021234", "P2SH,WITNESS", "WITNESS_UNEXPECTED", "NOP does not introduce a witness program"], + +["Automatically generated test cases"], +[ + "0x47 0x304402200a5c6163f07b8d3b013c4d1d6dba25e780b39658d79ba37af7057a3b7f15ffa102201fd9b4eaa9943f734928b99a83592c2e7bf342ea2680f6a2bb705167966b742001", + "0x41 0x0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 CHECKSIG", + "", + "OK", + "P2PK" +], +[ + "0x47 0x304402200a5c6163f07b8c3b013c4d1d6dba25e780b39658d79ba37af7057a3b7f15ffa102201fd9b4eaa9943f734928b99a83592c2e7bf342ea2680f6a2bb705167966b742001", + "0x41 0x0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 CHECKSIG", + "", + "EVAL_FALSE", + "P2PK, bad sig" +], +[ + "0x47 0x304402206e05a6fe23c59196ffe176c9ddc31e73a9885638f9d1328d47c0c703863b8876022076feb53811aa5b04e0e79f938eb19906cc5e67548bc555a8e8b8b0fc603d840c01 0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508", + "DUP HASH160 0x14 0x1018853670f9f3b0582c5b9ee8ce93764ac32b93 EQUALVERIFY CHECKSIG", + "", + "OK", + "P2PKH" +], +[ + "0x47 0x3044022034bb0494b50b8ef130e2185bb220265b9284ef5b4b8a8da4d8415df489c83b5102206259a26d9cc0a125ac26af6153b17c02956855ebe1467412f066e402f5f05d1201 0x21 0x03363d90d446b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640", + "DUP HASH160 0x14 0xc0834c0c158f53be706d234c38fd52de7eece656 EQUALVERIFY CHECKSIG", + "", + "EQUALVERIFY", + "P2PKH, bad pubkey" +], +[ + "0x47 0x304402204710a85181663b32d25c70ec2bbd14adff5ddfff6cb50d09e155ef5f541fc86c0220056b0cc949be9386ecc5f6c2ac0493269031dbb185781db90171b54ac127790281", + "0x41 0x048282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f5150811f8a8098557dfe45e8256e830b60ace62d613ac2f7b17bed31b6eaff6e26caf CHECKSIG", + "", + "OK", + "P2PK anyonecanpay" +], +[ + "0x47 0x304402204710a85181663b32d25c70ec2bbd14adff5ddfff6cb50d09e155ef5f541fc86c0220056b0cc949be9386ecc5f6c2ac0493269031dbb185781db90171b54ac127790201", + "0x41 0x048282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f5150811f8a8098557dfe45e8256e830b60ace62d613ac2f7b17bed31b6eaff6e26caf CHECKSIG", + "", + "EVAL_FALSE", + "P2PK anyonecanpay marked with normal hashtype" +], +[ + "0x47 0x3044022003fef42ed6c7be8917441218f525a60e2431be978e28b7aca4d7a532cc413ae8022067a1f82c74e8d69291b90d148778405c6257bbcfc2353cc38a3e1f22bf44254601 0x23 0x210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798ac", + "HASH160 0x14 0x23b0ad3477f2178bc0b3eed26e4e6316f4e83aa1 EQUAL", + "P2SH", + "OK", + "P2SH(P2PK)" +], +[ + "0x47 0x3044022003fef42ed6c7be8917441218f525a60e2431be978e28b7aca4d7a532cc413ae8022067a1f82c74e8d69291b90d148778405c6257bbcfc2353cc38a3e1f22bf44254601 0x23 0x210279be667ef9dcbbac54a06295ce870b07029bfcdb2dce28d959f2815b16f81798ac", + "HASH160 0x14 0x23b0ad3477f2178bc0b3eed26e4e6316f4e83aa1 EQUAL", + "P2SH", + "EVAL_FALSE", + "P2SH(P2PK), bad redeemscript" +], +[ + "0x47 0x30440220781ba4f59a7b207a10db87628bc2168df4d59b844b397d2dbc9a5835fb2f2b7602206ed8fbcc1072fe2dfc5bb25909269e5dc42ffcae7ec2bc81d59692210ff30c2b01 0x41 0x0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 0x19 0x76a91491b24bf9f5288532960ac687abb035127b1d28a588ac", + "HASH160 0x14 0x7f67f0521934a57d3039f77f9f32cf313f3ac74b EQUAL", + "P2SH", + "OK", + "P2SH(P2PKH)" +], +[ + "0x47 0x304402204e2eb034be7b089534ac9e798cf6a2c79f38bcb34d1b179efd6f2de0841735db022071461beb056b5a7be1819da6a3e3ce3662831ecc298419ca101eb6887b5dd6a401 0x19 0x76a9147cf9c846cd4882efec4bf07e44ebdad495c94f4b88ac", + "HASH160 0x14 0x2df519943d5acc0ef5222091f9dfe3543f489a82 EQUAL", + "", + "OK", + "P2SH(P2PKH), bad sig but no VERIFY_P2SH" +], +[ + "0x47 0x304402204e2eb034be7b089534ac9e798cf6a2c79f38bcb34d1b179efd6f2de0841735db022071461beb056b5a7be1819da6a3e3ce3662831ecc298419ca101eb6887b5dd6a401 0x19 0x76a9147cf9c846cd4882efec4bf07e44ebdad495c94f4b88ac", + "HASH160 0x14 0x2df519943d5acc0ef5222091f9dfe3543f489a82 EQUAL", + "P2SH", + "EQUALVERIFY", + "P2SH(P2PKH), bad sig" +], +[ + "0 0x47 0x3044022051254b9fb476a52d85530792b578f86fea70ec1ffb4393e661bcccb23d8d63d3022076505f94a403c86097841944e044c70c2045ce90e36de51f7e9d3828db98a07501 0x47 0x304402200a358f750934b3feb822f1966bfcd8bbec9eeaa3a8ca941e11ee5960e181fa01022050bf6b5a8e7750f70354ae041cb68a7bade67ec6c3ab19eb359638974410626e01 0x47 0x304402200955d031fff71d8653221e85e36c3c85533d2312fc3045314b19650b7ae2f81002202a6bb8505e36201909d0921f01abff390ae6b7ff97bbf959f98aedeb0a56730901", + "3 0x21 0x0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 3 CHECKMULTISIG", + "", + "OK", + "3-of-3" +], +[ + "0 0x47 0x3044022051254b9fb476a52d85530792b578f86fea70ec1ffb4393e661bcccb23d8d63d3022076505f94a403c86097841944e044c70c2045ce90e36de51f7e9d3828db98a07501 0x47 0x304402200a358f750934b3feb822f1966bfcd8bbec9eeaa3a8ca941e11ee5960e181fa01022050bf6b5a8e7750f70354ae041cb68a7bade67ec6c3ab19eb359638974410626e01 0", + "3 0x21 0x0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 3 CHECKMULTISIG", + "", + "EVAL_FALSE", + "3-of-3, 2 sigs" +], +[ + "0 0x47 0x304402205b7d2c2f177ae76cfbbf14d589c113b0b35db753d305d5562dd0b61cbf366cfb02202e56f93c4f08a27f986cd424ffc48a462c3202c4902104d4d0ff98ed28f4bf8001 0x47 0x30440220563e5b3b1fc11662a84bc5ea2a32cc3819703254060ba30d639a1aaf2d5068ad0220601c1f47ddc76d93284dd9ed68f7c9974c4a0ea7cbe8a247d6bc3878567a5fca01 0x4c69 0x52210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179821038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f515082103363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff464053ae", + "HASH160 0x14 0xc9e4a896d149702d0d1695434feddd52e24ad78d EQUAL", + "P2SH", + "OK", + "P2SH(2-of-3)" +], +[ + "0 0x47 0x304402205b7d2c2f177ae76cfbbf14d589c113b0b35db753d305d5562dd0b61cbf366cfb02202e56f93c4f08a27f986cd424ffc48a462c3202c4902104d4d0ff98ed28f4bf8001 0 0x4c69 0x52210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179821038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f515082103363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff464053ae", + "HASH160 0x14 0xc9e4a896d149702d0d1695434feddd52e24ad78d EQUAL", + "P2SH", + "EVAL_FALSE", + "P2SH(2-of-3), 1 sig" +], +[ + "0x47 0x304402200060558477337b9022e70534f1fea71a318caf836812465a2509931c5e7c4987022078ec32bd50ac9e03a349ba953dfd9fe1c8d2dd8bdb1d38ddca844d3d5c78c11801", + "0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 CHECKSIG", + "", + "OK", + "P2PK with too much R padding but no DERSIG" +], +[ + "0x47 0x304402200060558477337b9022e70534f1fea71a318caf836812465a2509931c5e7c4987022078ec32bd50ac9e03a349ba953dfd9fe1c8d2dd8bdb1d38ddca844d3d5c78c11801", + "0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 CHECKSIG", + "DERSIG", + "SIG_DER", + "P2PK with too much R padding" +], +[ + "0x48 0x304502202de8c03fc525285c9c535631019a5f2af7c6454fa9eb392a3756a4917c420edd02210046130bf2baf7cfc065067c8b9e33a066d9c15edcea9feb0ca2d233e3597925b401", + "0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 CHECKSIG", + "", + "OK", + "P2PK with too much S padding but no DERSIG" +], +[ + "0x48 0x304502202de8c03fc525285c9c535631019a5f2af7c6454fa9eb392a3756a4917c420edd02210046130bf2baf7cfc065067c8b9e33a066d9c15edcea9feb0ca2d233e3597925b401", + "0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 CHECKSIG", + "DERSIG", + "SIG_DER", + "P2PK with too much S padding" +], +[ + "0x47 0x30440220d7a0417c3f6d1a15094d1cf2a3378ca0503eb8a57630953a9e2987e21ddd0a6502207a6266d686c99090920249991d3d42065b6d43eb70187b219c0db82e4f94d1a201", + "0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 CHECKSIG", + "", + "OK", + "P2PK with too little R padding but no DERSIG" +], +[ + "0x47 0x30440220d7a0417c3f6d1a15094d1cf2a3378ca0503eb8a57630953a9e2987e21ddd0a6502207a6266d686c99090920249991d3d42065b6d43eb70187b219c0db82e4f94d1a201", + "0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 CHECKSIG", + "DERSIG", + "SIG_DER", + "P2PK with too little R padding" +], +[ + "0x47 0x30440220005ece1335e7f757a1a1f476a7fb5bd90964e8a022489f890614a04acfb734c002206c12b8294a6513c7710e8c82d3c23d75cdbfe83200eb7efb495701958501a5d601", + "0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 CHECKSIG NOT", + "", + "OK", + "P2PK NOT with bad sig with too much R padding but no DERSIG" +], +[ + "0x47 0x30440220005ece1335e7f757a1a1f476a7fb5bd90964e8a022489f890614a04acfb734c002206c12b8294a6513c7710e8c82d3c23d75cdbfe83200eb7efb495701958501a5d601", + "0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 CHECKSIG NOT", + "DERSIG", + "SIG_DER", + "P2PK NOT with bad sig with too much R padding" +], +[ + "0x47 0x30440220005ece1335e7f657a1a1f476a7fb5bd90964e8a022489f890614a04acfb734c002206c12b8294a6513c7710e8c82d3c23d75cdbfe83200eb7efb495701958501a5d601", + "0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 CHECKSIG NOT", + "", + "EVAL_FALSE", + "P2PK NOT with too much R padding but no DERSIG" +], +[ + "0x47 0x30440220005ece1335e7f657a1a1f476a7fb5bd90964e8a022489f890614a04acfb734c002206c12b8294a6513c7710e8c82d3c23d75cdbfe83200eb7efb495701958501a5d601", + "0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 CHECKSIG NOT", + "DERSIG", + "SIG_DER", + "P2PK NOT with too much R padding" +], +[ + "0x47 0x30440220d7a0417c3f6d1a15094d1cf2a3378ca0503eb8a57630953a9e2987e21ddd0a6502207a6266d686c99090920249991d3d42065b6d43eb70187b219c0db82e4f94d1a201", + "0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 CHECKSIG", + "", + "OK", + "BIP66 example 1, without DERSIG" +], +[ + "0x47 0x30440220d7a0417c3f6d1a15094d1cf2a3378ca0503eb8a57630953a9e2987e21ddd0a6502207a6266d686c99090920249991d3d42065b6d43eb70187b219c0db82e4f94d1a201", + "0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 CHECKSIG", + "DERSIG", + "SIG_DER", + "BIP66 example 1, with DERSIG" +], +[ + "0x47 0x304402208e43c0b91f7c1e5bc58e41c8185f8a6086e111b0090187968a86f2822462d3c902200a58f4076b1133b18ff1dc83ee51676e44c60cc608d9534e0df5ace0424fc0be01", + "0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 CHECKSIG NOT", + "", + "EVAL_FALSE", + "BIP66 example 2, without DERSIG" +], +[ + "0x47 0x304402208e43c0b91f7c1e5bc58e41c8185f8a6086e111b0090187968a86f2822462d3c902200a58f4076b1133b18ff1dc83ee51676e44c60cc608d9534e0df5ace0424fc0be01", + "0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 CHECKSIG NOT", + "DERSIG", + "SIG_DER", + "BIP66 example 2, with DERSIG" +], +[ + "0", + "0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 CHECKSIG", + "", + "EVAL_FALSE", + "BIP66 example 3, without DERSIG" +], +[ + "0", + "0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 CHECKSIG", + "DERSIG", + "EVAL_FALSE", + "BIP66 example 3, with DERSIG" +], +[ + "0", + "0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 CHECKSIG NOT", + "", + "OK", + "BIP66 example 4, without DERSIG" +], +[ + "0", + "0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 CHECKSIG NOT", + "DERSIG", + "OK", + "BIP66 example 4, with DERSIG" +], +[ + "0x09 0x300602010102010101", + "0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 CHECKSIG NOT", + "DERSIG", + "OK", + "BIP66 example 4, with DERSIG, non-null DER-compliant signature" +], +[ + "0", + "0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 CHECKSIG NOT", + "DERSIG,NULLFAIL", + "OK", + "BIP66 example 4, with DERSIG and NULLFAIL" +], +[ + "0x09 0x300602010102010101", + "0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 CHECKSIG NOT", + "DERSIG,NULLFAIL", + "NULLFAIL", + "BIP66 example 4, with DERSIG and NULLFAIL, non-null DER-compliant signature" +], +[ + "1", + "0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 CHECKSIG", + "", + "EVAL_FALSE", + "BIP66 example 5, without DERSIG" +], +[ + "1", + "0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 CHECKSIG", + "DERSIG", + "SIG_DER", + "BIP66 example 5, with DERSIG" +], +[ + "1", + "0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 CHECKSIG NOT", + "", + "OK", + "BIP66 example 6, without DERSIG" +], +[ + "1", + "0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 CHECKSIG NOT", + "DERSIG", + "SIG_DER", + "BIP66 example 6, with DERSIG" +], +[ + "0 0x47 0x30440220cae00b1444babfbf6071b0ba8707f6bd373da3df494d6e74119b0430c5db810502205d5231b8c5939c8ff0c82242656d6e06edb073d42af336c99fe8837c36ea39d501 0x47 0x3044022027c2714269ca5aeecc4d70edc88ba5ee0e3da4986e9216028f489ab4f1b8efce022022bd545b4951215267e4c5ceabd4c5350331b2e4a0b6494c56f361fa5a57a1a201", + "2 0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 2 CHECKMULTISIG", + "", + "OK", + "BIP66 example 7, without DERSIG" +], +[ + "0 0x47 0x30440220cae00b1444babfbf6071b0ba8707f6bd373da3df494d6e74119b0430c5db810502205d5231b8c5939c8ff0c82242656d6e06edb073d42af336c99fe8837c36ea39d501 0x47 0x3044022027c2714269ca5aeecc4d70edc88ba5ee0e3da4986e9216028f489ab4f1b8efce022022bd545b4951215267e4c5ceabd4c5350331b2e4a0b6494c56f361fa5a57a1a201", + "2 0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 2 CHECKMULTISIG", + "DERSIG", + "SIG_DER", + "BIP66 example 7, with DERSIG" +], +[ + "0 0x47 0x30440220b119d67d389315308d1745f734a51ff3ec72e06081e84e236fdf9dc2f5d2a64802204b04e3bc38674c4422ea317231d642b56dc09d214a1ecbbf16ecca01ed996e2201 0x47 0x3044022079ea80afd538d9ada421b5101febeb6bc874e01dde5bca108c1d0479aec339a4022004576db8f66130d1df686ccf00935703689d69cf539438da1edab208b0d63c4801", + "2 0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 2 CHECKMULTISIG NOT", + "", + "EVAL_FALSE", + "BIP66 example 8, without DERSIG" +], +[ + "0 0x47 0x30440220b119d67d389315308d1745f734a51ff3ec72e06081e84e236fdf9dc2f5d2a64802204b04e3bc38674c4422ea317231d642b56dc09d214a1ecbbf16ecca01ed996e2201 0x47 0x3044022079ea80afd538d9ada421b5101febeb6bc874e01dde5bca108c1d0479aec339a4022004576db8f66130d1df686ccf00935703689d69cf539438da1edab208b0d63c4801", + "2 0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 2 CHECKMULTISIG NOT", + "DERSIG", + "SIG_DER", + "BIP66 example 8, with DERSIG" +], +[ + "0 0 0x47 0x3044022081aa9d436f2154e8b6d600516db03d78de71df685b585a9807ead4210bd883490220534bb6bdf318a419ac0749660b60e78d17d515558ef369bf872eff405b676b2e01", + "2 0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 2 CHECKMULTISIG", + "", + "EVAL_FALSE", + "BIP66 example 9, without DERSIG" +], +[ + "0 0 0x47 0x3044022081aa9d436f2154e8b6d600516db03d78de71df685b585a9807ead4210bd883490220534bb6bdf318a419ac0749660b60e78d17d515558ef369bf872eff405b676b2e01", + "2 0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 2 CHECKMULTISIG", + "DERSIG", + "SIG_DER", + "BIP66 example 9, with DERSIG" +], +[ + "0 0 0x47 0x30440220da6f441dc3b4b2c84cfa8db0cd5b34ed92c9e01686de5a800d40498b70c0dcac02207c2cf91b0c32b860c4cd4994be36cfb84caf8bb7c3a8e4d96a31b2022c5299c501", + "2 0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 2 CHECKMULTISIG NOT", + "", + "OK", + "BIP66 example 10, without DERSIG" +], +[ + "0 0 0x47 0x30440220da6f441dc3b4b2c84cfa8db0cd5b34ed92c9e01686de5a800d40498b70c0dcac02207c2cf91b0c32b860c4cd4994be36cfb84caf8bb7c3a8e4d96a31b2022c5299c501", + "2 0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 2 CHECKMULTISIG NOT", + "DERSIG", + "SIG_DER", + "BIP66 example 10, with DERSIG" +], +[ + "0 0x47 0x30440220cae00b1444babfbf6071b0ba8707f6bd373da3df494d6e74119b0430c5db810502205d5231b8c5939c8ff0c82242656d6e06edb073d42af336c99fe8837c36ea39d501 0", + "2 0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 2 CHECKMULTISIG", + "", + "EVAL_FALSE", + "BIP66 example 11, without DERSIG" +], +[ + "0 0x47 0x30440220cae00b1444babfbf6071b0ba8707f6bd373da3df494d6e74119b0430c5db810502205d5231b8c5939c8ff0c82242656d6e06edb073d42af336c99fe8837c36ea39d501 0", + "2 0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 2 CHECKMULTISIG", + "DERSIG", + "EVAL_FALSE", + "BIP66 example 11, with DERSIG" +], +[ + "0 0x47 0x30440220b119d67d389315308d1745f734a51ff3ec72e06081e84e236fdf9dc2f5d2a64802204b04e3bc38674c4422ea317231d642b56dc09d214a1ecbbf16ecca01ed996e2201 0", + "2 0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 2 CHECKMULTISIG NOT", + "", + "OK", + "BIP66 example 12, without DERSIG" +], +[ + "0 0x47 0x30440220b119d67d389315308d1745f734a51ff3ec72e06081e84e236fdf9dc2f5d2a64802204b04e3bc38674c4422ea317231d642b56dc09d214a1ecbbf16ecca01ed996e2201 0", + "2 0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 2 CHECKMULTISIG NOT", + "DERSIG", + "OK", + "BIP66 example 12, with DERSIG" +], +[ + "0x48 0x304402203e4516da7253cf068effec6b95c41221c0cf3a8e6ccb8cbf1725b562e9afde2c022054e1c258c2981cdfba5df1f46661fb6541c44f77ca0092f3600331abfffb12510101", + "0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 CHECKSIG", + "", + "OK", + "P2PK with multi-byte hashtype, without DERSIG" +], +[ + "0x48 0x304402203e4516da7253cf068effec6b95c41221c0cf3a8e6ccb8cbf1725b562e9afde2c022054e1c258c2981cdfba5df1f46661fb6541c44f77ca0092f3600331abfffb12510101", + "0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 CHECKSIG", + "DERSIG", + "SIG_DER", + "P2PK with multi-byte hashtype, with DERSIG" +], +[ + "0x48 0x304502203e4516da7253cf068effec6b95c41221c0cf3a8e6ccb8cbf1725b562e9afde2c022100ab1e3da73d67e32045a20e0b999e049978ea8d6ee5480d485fcf2ce0d03b2ef001", + "0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 CHECKSIG", + "", + "OK", + "P2PK with high S but no LOW_S" +], +[ + "0x48 0x304502203e4516da7253cf068effec6b95c41221c0cf3a8e6ccb8cbf1725b562e9afde2c022100ab1e3da73d67e32045a20e0b999e049978ea8d6ee5480d485fcf2ce0d03b2ef001", + "0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 CHECKSIG", + "LOW_S", + "SIG_HIGH_S", + "P2PK with high S" +], +[ + "0x47 0x3044022057292e2d4dfe775becdd0a9e6547997c728cdf35390f6a017da56d654d374e4902206b643be2fc53763b4e284845bfea2c597d2dc7759941dce937636c9d341b71ed01", + "0x41 0x0679be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 CHECKSIG", + "", + "OK", + "P2PK with hybrid pubkey but no STRICTENC" +], +[ + "0x47 0x3044022057292e2d4dfe775becdd0a9e6547997c728cdf35390f6a017da56d654d374e4902206b643be2fc53763b4e284845bfea2c597d2dc7759941dce937636c9d341b71ed01", + "0x41 0x0679be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 CHECKSIG", + "STRICTENC", + "PUBKEYTYPE", + "P2PK with hybrid pubkey" +], +[ + "0x00", + "0x21 0x0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 CHECKSIG", + "STRICTENC", + "PUBKEYTYPE", + "P2PK with invalid length for uncompressed key" +], +[ + "0x00", + "0x41 0x0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 CHECKSIG", + "STRICTENC", + "PUBKEYTYPE", + "P2PK with invalid length for compressed key" +], +[ + "0x47 0x30440220035d554e3153c14950c9993f41c496607a8e24093db0595be7bf875cf64fcf1f02204731c8c4e5daf15e706cec19cdd8f2c5b1d05490e11dab8465ed426569b6e92101", + "0x41 0x0679be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 CHECKSIG NOT", + "", + "EVAL_FALSE", + "P2PK NOT with hybrid pubkey but no STRICTENC" +], +[ + "0x47 0x30440220035d554e3153c14950c9993f41c496607a8e24093db0595be7bf875cf64fcf1f02204731c8c4e5daf15e706cec19cdd8f2c5b1d05490e11dab8465ed426569b6e92101", + "0x41 0x0679be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 CHECKSIG NOT", + "STRICTENC", + "PUBKEYTYPE", + "P2PK NOT with hybrid pubkey" +], +[ + "0x47 0x30440220035d554e3153c04950c9993f41c496607a8e24093db0595be7bf875cf64fcf1f02204731c8c4e5daf15e706cec19cdd8f2c5b1d05490e11dab8465ed426569b6e92101", + "0x41 0x0679be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 CHECKSIG NOT", + "", + "OK", + "P2PK NOT with invalid hybrid pubkey but no STRICTENC" +], +[ + "0x47 0x30440220035d554e3153c04950c9993f41c496607a8e24093db0595be7bf875cf64fcf1f02204731c8c4e5daf15e706cec19cdd8f2c5b1d05490e11dab8465ed426569b6e92101", + "0x41 0x0679be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 CHECKSIG NOT", + "STRICTENC", + "PUBKEYTYPE", + "P2PK NOT with invalid hybrid pubkey" +], +[ + "0 0x47 0x304402202e79441ad1baf5a07fb86bae3753184f6717d9692680947ea8b6e8b777c69af1022079a262e13d868bb5a0964fefe3ba26942e1b0669af1afb55ef3344bc9d4fc4c401", + "1 0x41 0x0679be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 2 CHECKMULTISIG", + "", + "OK", + "1-of-2 with the second 1 hybrid pubkey and no STRICTENC" +], +[ + "0 0x47 0x304402202e79441ad1baf5a07fb86bae3753184f6717d9692680947ea8b6e8b777c69af1022079a262e13d868bb5a0964fefe3ba26942e1b0669af1afb55ef3344bc9d4fc4c401", + "1 0x41 0x0679be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 2 CHECKMULTISIG", + "STRICTENC", + "OK", + "1-of-2 with the second 1 hybrid pubkey" +], +[ + "0 0x47 0x3044022079c7824d6c868e0e1a273484e28c2654a27d043c8a27f49f52cb72efed0759090220452bbbf7089574fa082095a4fc1b3a16bafcf97a3a34d745fafc922cce66b27201", + "1 0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 0x41 0x0679be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 2 CHECKMULTISIG", + "STRICTENC", + "PUBKEYTYPE", + "1-of-2 with the first 1 hybrid pubkey" +], +[ + "0x47 0x304402206177d513ec2cda444c021a1f4f656fc4c72ba108ae063e157eb86dc3575784940220666fc66702815d0e5413bb9b1df22aed44f5f1efb8b99d41dd5dc9a5be6d205205", + "0x41 0x048282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f5150811f8a8098557dfe45e8256e830b60ace62d613ac2f7b17bed31b6eaff6e26caf CHECKSIG", + "", + "OK", + "P2PK with undefined hashtype but no STRICTENC" +], +[ + "0x47 0x304402206177d513ec2cda444c021a1f4f656fc4c72ba108ae063e157eb86dc3575784940220666fc66702815d0e5413bb9b1df22aed44f5f1efb8b99d41dd5dc9a5be6d205205", + "0x41 0x048282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f5150811f8a8098557dfe45e8256e830b60ace62d613ac2f7b17bed31b6eaff6e26caf CHECKSIG", + "STRICTENC", + "SIG_HASHTYPE", + "P2PK with undefined hashtype" +], +[ + "0x47 0x304402207409b5b320296e5e2136a7b281a7f803028ca4ca44e2b83eebd46932677725de02202d4eea1c8d3c98e6f42614f54764e6e5e6542e213eb4d079737e9a8b6e9812ec05", + "0x41 0x048282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f5150811f8a8098557dfe45e8256e830b60ace62d613ac2f7b17bed31b6eaff6e26caf CHECKSIG NOT", + "", + "OK", + "P2PK NOT with invalid sig and undefined hashtype but no STRICTENC" +], +[ + "0x47 0x304402207409b5b320296e5e2136a7b281a7f803028ca4ca44e2b83eebd46932677725de02202d4eea1c8d3c98e6f42614f54764e6e5e6542e213eb4d079737e9a8b6e9812ec05", + "0x41 0x048282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f5150811f8a8098557dfe45e8256e830b60ace62d613ac2f7b17bed31b6eaff6e26caf CHECKSIG NOT", + "STRICTENC", + "SIG_HASHTYPE", + "P2PK NOT with invalid sig and undefined hashtype" +], +[ + "1 0x47 0x3044022051254b9fb476a52d85530792b578f86fea70ec1ffb4393e661bcccb23d8d63d3022076505f94a403c86097841944e044c70c2045ce90e36de51f7e9d3828db98a07501 0x47 0x304402200a358f750934b3feb822f1966bfcd8bbec9eeaa3a8ca941e11ee5960e181fa01022050bf6b5a8e7750f70354ae041cb68a7bade67ec6c3ab19eb359638974410626e01 0x47 0x304402200955d031fff71d8653221e85e36c3c85533d2312fc3045314b19650b7ae2f81002202a6bb8505e36201909d0921f01abff390ae6b7ff97bbf959f98aedeb0a56730901", + "3 0x21 0x0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 3 CHECKMULTISIG", + "", + "OK", + "3-of-3 with nonzero dummy but no NULLDUMMY" +], +[ + "1 0x47 0x3044022051254b9fb476a52d85530792b578f86fea70ec1ffb4393e661bcccb23d8d63d3022076505f94a403c86097841944e044c70c2045ce90e36de51f7e9d3828db98a07501 0x47 0x304402200a358f750934b3feb822f1966bfcd8bbec9eeaa3a8ca941e11ee5960e181fa01022050bf6b5a8e7750f70354ae041cb68a7bade67ec6c3ab19eb359638974410626e01 0x47 0x304402200955d031fff71d8653221e85e36c3c85533d2312fc3045314b19650b7ae2f81002202a6bb8505e36201909d0921f01abff390ae6b7ff97bbf959f98aedeb0a56730901", + "3 0x21 0x0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 3 CHECKMULTISIG", + "NULLDUMMY", + "SIG_NULLDUMMY", + "3-of-3 with nonzero dummy" +], +[ + "1 0x47 0x304402201bb2edab700a5d020236df174fefed78087697143731f659bea59642c759c16d022061f42cdbae5bcd3e8790f20bf76687443436e94a634321c16a72aa54cbc7c2ea01 0x47 0x304402204bb4a64f2a6e5c7fb2f07fef85ee56fde5e6da234c6a984262307a20e99842d702206f8303aaba5e625d223897e2ffd3f88ef1bcffef55f38dc3768e5f2e94c923f901 0x47 0x3044022040c2809b71fffb155ec8b82fe7a27f666bd97f941207be4e14ade85a1249dd4d02204d56c85ec525dd18e29a0533d5ddf61b6b1bb32980c2f63edf951aebf7a27bfe01", + "3 0x21 0x0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 3 CHECKMULTISIG NOT", + "", + "OK", + "3-of-3 NOT with invalid sig and nonzero dummy but no NULLDUMMY" +], +[ + "1 0x47 0x304402201bb2edab700a5d020236df174fefed78087697143731f659bea59642c759c16d022061f42cdbae5bcd3e8790f20bf76687443436e94a634321c16a72aa54cbc7c2ea01 0x47 0x304402204bb4a64f2a6e5c7fb2f07fef85ee56fde5e6da234c6a984262307a20e99842d702206f8303aaba5e625d223897e2ffd3f88ef1bcffef55f38dc3768e5f2e94c923f901 0x47 0x3044022040c2809b71fffb155ec8b82fe7a27f666bd97f941207be4e14ade85a1249dd4d02204d56c85ec525dd18e29a0533d5ddf61b6b1bb32980c2f63edf951aebf7a27bfe01", + "3 0x21 0x0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 3 CHECKMULTISIG NOT", + "NULLDUMMY", + "SIG_NULLDUMMY", + "3-of-3 NOT with invalid sig with nonzero dummy" +], +[ + "0 0x47 0x304402200abeb4bd07f84222f474aed558cfbdfc0b4e96cde3c2935ba7098b1ff0bd74c302204a04c1ca67b2a20abee210cf9a21023edccbbf8024b988812634233115c6b73901 DUP", + "2 0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 2 CHECKMULTISIG", + "", + "OK", + "2-of-2 with two identical keys and sigs pushed using OP_DUP but no SIGPUSHONLY" +], +[ + "0 0x47 0x304402200abeb4bd07f84222f474aed558cfbdfc0b4e96cde3c2935ba7098b1ff0bd74c302204a04c1ca67b2a20abee210cf9a21023edccbbf8024b988812634233115c6b73901 DUP", + "2 0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 2 CHECKMULTISIG", + "SIGPUSHONLY", + "SIG_PUSHONLY", + "2-of-2 with two identical keys and sigs pushed using OP_DUP" +], +[ + "0x47 0x3044022018a2a81a93add5cb5f5da76305718e4ea66045ec4888b28d84cb22fae7f4645b02201e6daa5ed5d2e4b2b2027cf7ffd43d8d9844dd49f74ef86899ec8e669dfd39aa01 NOP8 0x23 0x2103363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640ac", + "HASH160 0x14 0x215640c2f72f0d16b4eced26762035a42ffed39a EQUAL", + "", + "OK", + "P2SH(P2PK) with non-push scriptSig but no P2SH or SIGPUSHONLY" +], +[ + "0x47 0x304402203e4516da7253cf068effec6b95c41221c0cf3a8e6ccb8cbf1725b562e9afde2c022054e1c258c2981cdfba5df1f46661fb6541c44f77ca0092f3600331abfffb125101 NOP8", + "0x21 0x03363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640 CHECKSIG", + "P2SH", + "OK", + "P2PK with non-push scriptSig but with P2SH validation" +], +[ + "0x47 0x3044022018a2a81a93add5cb5f5da76305718e4ea66045ec4888b28d84cb22fae7f4645b02201e6daa5ed5d2e4b2b2027cf7ffd43d8d9844dd49f74ef86899ec8e669dfd39aa01 NOP8 0x23 0x2103363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640ac", + "HASH160 0x14 0x215640c2f72f0d16b4eced26762035a42ffed39a EQUAL", + "P2SH", + "SIG_PUSHONLY", + "P2SH(P2PK) with non-push scriptSig but no SIGPUSHONLY" +], +[ + "0x47 0x3044022018a2a81a93add5cb5f5da76305718e4ea66045ec4888b28d84cb22fae7f4645b02201e6daa5ed5d2e4b2b2027cf7ffd43d8d9844dd49f74ef86899ec8e669dfd39aa01 NOP8 0x23 0x2103363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640ac", + "HASH160 0x14 0x215640c2f72f0d16b4eced26762035a42ffed39a EQUAL", + "SIGPUSHONLY", + "SIG_PUSHONLY", + "P2SH(P2PK) with non-push scriptSig but not P2SH" +], +[ + "0 0x47 0x304402200abeb4bd07f84222f474aed558cfbdfc0b4e96cde3c2935ba7098b1ff0bd74c302204a04c1ca67b2a20abee210cf9a21023edccbbf8024b988812634233115c6b73901 0x47 0x304402200abeb4bd07f84222f474aed558cfbdfc0b4e96cde3c2935ba7098b1ff0bd74c302204a04c1ca67b2a20abee210cf9a21023edccbbf8024b988812634233115c6b73901", + "2 0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 2 CHECKMULTISIG", + "SIGPUSHONLY", + "OK", + "2-of-2 with two identical keys and sigs pushed" +], +[ + "11 0x47 0x304402200a5c6163f07b8d3b013c4d1d6dba25e780b39658d79ba37af7057a3b7f15ffa102201fd9b4eaa9943f734928b99a83592c2e7bf342ea2680f6a2bb705167966b742001", + "0x41 0x0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 CHECKSIG", + "P2SH", + "OK", + "P2PK with unnecessary input but no CLEANSTACK" +], +[ + "11 0x47 0x304402200a5c6163f07b8d3b013c4d1d6dba25e780b39658d79ba37af7057a3b7f15ffa102201fd9b4eaa9943f734928b99a83592c2e7bf342ea2680f6a2bb705167966b742001", + "0x41 0x0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 CHECKSIG", + "CLEANSTACK,P2SH", + "CLEANSTACK", + "P2PK with unnecessary input" +], +[ + "11 0x47 0x304402202f7505132be14872581f35d74b759212d9da40482653f1ffa3116c3294a4a51702206adbf347a2240ca41c66522b1a22a41693610b76a8e7770645dc721d1635854f01 0x43 0x410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8ac", + "HASH160 0x14 0x31edc23bdafda4639e669f89ad6b2318dd79d032 EQUAL", + "P2SH", + "OK", + "P2SH with unnecessary input but no CLEANSTACK" +], +[ + "11 0x47 0x304402202f7505132be14872581f35d74b759212d9da40482653f1ffa3116c3294a4a51702206adbf347a2240ca41c66522b1a22a41693610b76a8e7770645dc721d1635854f01 0x43 0x410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8ac", + "HASH160 0x14 0x31edc23bdafda4639e669f89ad6b2318dd79d032 EQUAL", + "CLEANSTACK,P2SH", + "CLEANSTACK", + "P2SH with unnecessary input" +], +[ + "0x47 0x304402202f7505132be14872581f35d74b759212d9da40482653f1ffa3116c3294a4a51702206adbf347a2240ca41c66522b1a22a41693610b76a8e7770645dc721d1635854f01 0x43 0x410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8ac", + "HASH160 0x14 0x31edc23bdafda4639e669f89ad6b2318dd79d032 EQUAL", + "CLEANSTACK,P2SH", + "OK", + "P2SH with CLEANSTACK" +], + +["Testing with uncompressed keys in witness v0 without WITNESS_PUBKEYTYPE"], +[ + [ + "304402200d461c140cfdfcf36b94961db57ae8c18d1cb80e9d95a9e47ac22470c1bf125502201c8dc1cbfef6a3ef90acbbb992ca22fe9466ee6f9d4898eda277a7ac3ab4b25101", + "410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8ac", + 0.00000001 + ], + "", + "0 0x20 0xb95237b48faaa69eb078e1170be3b5cbb3fddf16d0a991e14ad274f7b33a4f64", + "P2SH,WITNESS", + "OK", + "Basic P2WSH" +], +[ + [ + "304402201e7216e5ccb3b61d46946ec6cc7e8c4e0117d13ac2fd4b152197e4805191c74202203e9903e33e84d9ee1dd13fb057afb7ccfb47006c23f6a067185efbc9dd780fc501", + "0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8", + 0.00000001 + ], + "", + "0 0x14 0x91b24bf9f5288532960ac687abb035127b1d28a5", + "P2SH,WITNESS", + "OK", + "Basic P2WPKH" +], +[ + [ + "3044022066e02c19a513049d49349cf5311a1b012b7c4fae023795a18ab1d91c23496c22022025e216342c8e07ce8ef51e8daee88f84306a9de66236cab230bb63067ded1ad301", + "410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8ac", + 0.00000001 + ], + "0x22 0x0020b95237b48faaa69eb078e1170be3b5cbb3fddf16d0a991e14ad274f7b33a4f64", + "HASH160 0x14 0xf386c2ba255cc56d20cfa6ea8b062f8b59945518 EQUAL", + "P2SH,WITNESS", + "OK", + "Basic P2SH(P2WSH)" +], +[ + [ + "304402200929d11561cd958460371200f82e9cae64c727a495715a31828e27a7ad57b36d0220361732ced04a6f97351ecca21a56d0b8cd4932c1da1f8f569a2b68e5e48aed7801", + "0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8", + 0.00000001 + ], + "0x16 0x001491b24bf9f5288532960ac687abb035127b1d28a5", + "HASH160 0x14 0x17743beb429c55c942d2ec703b98c4d57c2df5c6 EQUAL", + "P2SH,WITNESS", + "OK", + "Basic P2SH(P2WPKH)" +], +[ + [ + "304402202589f0512cb2408fb08ed9bd24f85eb3059744d9e4f2262d0b7f1338cff6e8b902206c0978f449693e0578c71bc543b11079fd0baae700ee5e9a6bee94db490af9fc01", + "41048282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f5150811f8a8098557dfe45e8256e830b60ace62d613ac2f7b17bed31b6eaff6e26cafac", + 0.00000000 + ], + "", + "0 0x20 0xac8ebd9e52c17619a381fa4f71aebb696087c6ef17c960fd0587addad99c0610", + "P2SH,WITNESS", + "EVAL_FALSE", + "Basic P2WSH with the wrong key" +], +[ + [ + "304402206ef7fdb2986325d37c6eb1a8bb24aeb46dede112ed8fc76c7d7500b9b83c0d3d02201edc2322c794fe2d6b0bd73ed319e714aa9b86d8891961530d5c9b7156b60d4e01", + "048282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f5150811f8a8098557dfe45e8256e830b60ace62d613ac2f7b17bed31b6eaff6e26caf", + 0.00000000 + ], + "", + "0 0x14 0x7cf9c846cd4882efec4bf07e44ebdad495c94f4b", + "P2SH,WITNESS", + "EVAL_FALSE", + "Basic P2WPKH with the wrong key" +], +[ + [ + "30440220069ea3581afaf8187f63feee1fd2bd1f9c0dc71ea7d6e8a8b07ee2ebcf824bf402201a4fdef4c532eae59223be1eda6a397fc835142d4ddc6c74f4aa85b766a5c16f01", + "41048282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f5150811f8a8098557dfe45e8256e830b60ace62d613ac2f7b17bed31b6eaff6e26cafac", + 0.00000000 + ], + "0x22 0x0020ac8ebd9e52c17619a381fa4f71aebb696087c6ef17c960fd0587addad99c0610", + "HASH160 0x14 0x61039a003883787c0d6ebc66d97fdabe8e31449d EQUAL", + "P2SH,WITNESS", + "EVAL_FALSE", + "Basic P2SH(P2WSH) with the wrong key" +], +[ + [ + "304402204209e49457c2358f80d0256bc24535b8754c14d08840fc4be762d6f5a0aed80b02202eaf7d8fc8d62f60c67adcd99295528d0e491ae93c195cec5a67e7a09532a88001", + "048282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f5150811f8a8098557dfe45e8256e830b60ace62d613ac2f7b17bed31b6eaff6e26caf", + 0.00000000 + ], + "0x16 0x00147cf9c846cd4882efec4bf07e44ebdad495c94f4b", + "HASH160 0x14 0x4e0c2aed91315303fc6a1dc4c7bc21c88f75402e EQUAL", + "P2SH,WITNESS", + "EVAL_FALSE", + "Basic P2SH(P2WPKH) with the wrong key" +], +[ + [ + "304402202589f0512cb2408fb08ed9bd24f85eb3059744d9e4f2262d0b7f1338cff6e8b902206c0978f449693e0578c71bc543b11079fd0baae700ee5e9a6bee94db490af9fc01", + "41048282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f5150811f8a8098557dfe45e8256e830b60ace62d613ac2f7b17bed31b6eaff6e26cafac", + 0.00000000 + ], + "", + "0 0x20 0xac8ebd9e52c17619a381fa4f71aebb696087c6ef17c960fd0587addad99c0610", + "P2SH", + "OK", + "Basic P2WSH with the wrong key but no WITNESS" +], +[ + [ + "304402206ef7fdb2986325d37c6eb1a8bb24aeb46dede112ed8fc76c7d7500b9b83c0d3d02201edc2322c794fe2d6b0bd73ed319e714aa9b86d8891961530d5c9b7156b60d4e01", + "048282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f5150811f8a8098557dfe45e8256e830b60ace62d613ac2f7b17bed31b6eaff6e26caf", + 0.00000000 + ], + "", + "0 0x14 0x7cf9c846cd4882efec4bf07e44ebdad495c94f4b", + "P2SH", + "OK", + "Basic P2WPKH with the wrong key but no WITNESS" +], +[ + [ + "30440220069ea3581afaf8187f63feee1fd2bd1f9c0dc71ea7d6e8a8b07ee2ebcf824bf402201a4fdef4c532eae59223be1eda6a397fc835142d4ddc6c74f4aa85b766a5c16f01", + "41048282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f5150811f8a8098557dfe45e8256e830b60ace62d613ac2f7b17bed31b6eaff6e26cafac", + 0.00000000 + ], + "0x22 0x0020ac8ebd9e52c17619a381fa4f71aebb696087c6ef17c960fd0587addad99c0610", + "HASH160 0x14 0x61039a003883787c0d6ebc66d97fdabe8e31449d EQUAL", + "P2SH", + "OK", + "Basic P2SH(P2WSH) with the wrong key but no WITNESS" +], +[ + [ + "304402204209e49457c2358f80d0256bc24535b8754c14d08840fc4be762d6f5a0aed80b02202eaf7d8fc8d62f60c67adcd99295528d0e491ae93c195cec5a67e7a09532a88001", + "048282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f5150811f8a8098557dfe45e8256e830b60ace62d613ac2f7b17bed31b6eaff6e26caf", + 0.00000000 + ], + "0x16 0x00147cf9c846cd4882efec4bf07e44ebdad495c94f4b", + "HASH160 0x14 0x4e0c2aed91315303fc6a1dc4c7bc21c88f75402e EQUAL", + "P2SH", + "OK", + "Basic P2SH(P2WPKH) with the wrong key but no WITNESS" +], +[ + [ + "3044022066faa86e74e8b30e82691b985b373de4f9e26dc144ec399c4f066aa59308e7c202204712b86f28c32503faa051dbeabff2c238ece861abc36c5e0b40b1139ca222f001", + "410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8ac", + 0.00000000 + ], + "", + "0 0x20 0xb95237b48faaa69eb078e1170be3b5cbb3fddf16d0a991e14ad274f7b33a4f64", + "P2SH,WITNESS", + "EVAL_FALSE", + "Basic P2WSH with wrong value" +], +[ + [ + "304402203b3389b87448d7dfdb5e82fb854fcf92d7925f9938ea5444e36abef02c3d6a9602202410bc3265049abb07fd2e252c65ab7034d95c9d5acccabe9fadbdc63a52712601", + "0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8", + 0.00000000 + ], + "", + "0 0x14 0x91b24bf9f5288532960ac687abb035127b1d28a5", + "P2SH,WITNESS", + "EVAL_FALSE", + "Basic P2WPKH with wrong value" +], +[ + [ + "3044022000a30c4cfc10e4387be528613575434826ad3c15587475e0df8ce3b1746aa210022008149265e4f8e9dafe1f3ea50d90cb425e9e40ea7ebdd383069a7cfa2b77004701", + "410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8ac", + 0.00000000 + ], + "0x22 0x0020b95237b48faaa69eb078e1170be3b5cbb3fddf16d0a991e14ad274f7b33a4f64", + "HASH160 0x14 0xf386c2ba255cc56d20cfa6ea8b062f8b59945518 EQUAL", + "P2SH,WITNESS", + "EVAL_FALSE", + "Basic P2SH(P2WSH) with wrong value" +], +[ + [ + "304402204fc3a2cd61a47913f2a5f9107d0ad4a504c7b31ee2d6b3b2f38c2b10ee031e940220055d58b7c3c281aaa381d8f486ac0f3e361939acfd568046cb6a311cdfa974cf01", + "0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8", + 0.00000000 + ], + "0x16 0x001491b24bf9f5288532960ac687abb035127b1d28a5", + "HASH160 0x14 0x17743beb429c55c942d2ec703b98c4d57c2df5c6 EQUAL", + "P2SH,WITNESS", + "EVAL_FALSE", + "Basic P2SH(P2WPKH) with wrong value" +], +[ + [ + "304402205ae57ae0534c05ca9981c8a6cdf353b505eaacb7375f96681a2d1a4ba6f02f84022056248e68643b7d8ce7c7d128c9f1f348bcab8be15d094ad5cadd24251a28df8001", + "0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8", + 0.00000000 + ], + "", + "1 0x14 0x91b24bf9f5288532960ac687abb035127b1d28a5", + "DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM,P2SH,WITNESS", + "DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM", + "P2WPKH with future witness version" +], +[ + [ + "3044022064100ca0e2a33332136775a86cd83d0230e58b9aebb889c5ac952abff79a46ef02205f1bf900e022039ad3091bdaf27ac2aef3eae9ed9f190d821d3e508405b9513101", + "0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8", + 0.00000000 + ], + "", + "0 0x1f 0xb34b78da162751647974d5cb7410aa428ad339dbf7d1e16e833f68a0cbf1c3", + "P2SH,WITNESS", + "WITNESS_PROGRAM_WRONG_LENGTH", + "P2WPKH with wrong witness program length" +], +[ + "", + "0 0x20 0xb95237b48faaa69eb078e1170be3b5cbb3fddf16d0a991e14ad274f7b33a4f64", + "P2SH,WITNESS", + "WITNESS_PROGRAM_WITNESS_EMPTY", + "P2WSH with empty witness" +], +[ + [ + "3044022039105b995a5f448639a997a5c90fda06f50b49df30c3bdb6663217bf79323db002206fecd54269dec569fcc517178880eb58bb40f381a282bb75766ff3637d5f4b4301", + "400479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8ac", + 0.00000000 + ], + "", + "0 0x20 0xb95237b48faaa69eb078e1170be3b5cbb3fddf16d0a991e14ad274f7b33a4f64", + "P2SH,WITNESS", + "WITNESS_PROGRAM_MISMATCH", + "P2WSH with witness program mismatch" +], +[ + [ + "304402201a96950593cb0af32d080b0f193517f4559241a8ebd1e95e414533ad64a3f423022047f4f6d3095c23235bdff3aeff480d0529c027a3f093cb265b7cbf148553b85101", + "0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8", + "", + 0.00000000 + ], + "", + "0 0x14 0x91b24bf9f5288532960ac687abb035127b1d28a5", + "P2SH,WITNESS", + "WITNESS_PROGRAM_MISMATCH", + "P2WPKH with witness program mismatch" +], +[ + [ + "304402201a96950593cb0af32d080b0f193517f4559241a8ebd1e95e414533ad64a3f423022047f4f6d3095c23235bdff3aeff480d0529c027a3f093cb265b7cbf148553b85101", + "0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8", + 0.00000000 + ], + "11", + "0 0x14 0x91b24bf9f5288532960ac687abb035127b1d28a5", + "P2SH,WITNESS", + "WITNESS_MALLEATED", + "P2WPKH with non-empty scriptSig" +], +[ + [ + "304402204209e49457c2358f80d0256bc24535b8754c14d08840fc4be762d6f5a0aed80b02202eaf7d8fc8d62f60c67adcd99295528d0e491ae93c195cec5a67e7a09532a88001", + "048282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f5150811f8a8098557dfe45e8256e830b60ace62d613ac2f7b17bed31b6eaff6e26caf", + 0.00000000 + ], + "11 0x16 0x00147cf9c846cd4882efec4bf07e44ebdad495c94f4b", + "HASH160 0x14 0x4e0c2aed91315303fc6a1dc4c7bc21c88f75402e EQUAL", + "P2SH,WITNESS", + "WITNESS_MALLEATED_P2SH", + "P2SH(P2WPKH) with superfluous push in scriptSig" +], +[ + [ + "", + 0.00000000 + ], + "0x47 0x304402200a5c6163f07b8d3b013c4d1d6dba25e780b39658d79ba37af7057a3b7f15ffa102201fd9b4eaa9943f734928b99a83592c2e7bf342ea2680f6a2bb705167966b742001", + "0x41 0x0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 CHECKSIG", + "P2SH,WITNESS", + "WITNESS_UNEXPECTED", + "P2PK with witness" +], + +["Testing with compressed keys in witness v0 with WITNESS_PUBKEYTYPE"], +[ + [ + "304402204256146fcf8e73b0fd817ffa2a4e408ff0418ff987dd08a4f485b62546f6c43c02203f3c8c3e2febc051e1222867f5f9d0eaf039d6792911c10940aa3cc74123378e01", + "210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798ac", + 0.00000001 + ], + "", + "0 0x20 0x1863143c14c5166804bd19203356da136c985678cd4d27a1b8c6329604903262", + "P2SH,WITNESS,WITNESS_PUBKEYTYPE", + "OK", + "Basic P2WSH with compressed key" +], +[ + [ + "304402204edf27486f11432466b744df533e1acac727e0c83e5f912eb289a3df5bf8035f022075809fdd876ede40ad21667eba8b7e96394938f9c9c50f11b6a1280cce2cea8601", + "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798", + 0.00000001 + ], + "", + "0 0x14 0x751e76e8199196d454941c45d1b3a323f1433bd6", + "P2SH,WITNESS,WITNESS_PUBKEYTYPE", + "OK", + "Basic P2WPKH with compressed key" +], +[ + [ + "304402203a549090cc46bce1e5e95c4922ea2c12747988e0207b04c42f81cdbe87bb1539022050f57a245b875fd5119c419aaf050bcdf41384f0765f04b809e5bced1fe7093d01", + "210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798ac", + 0.00000001 + ], + "0x22 0x00201863143c14c5166804bd19203356da136c985678cd4d27a1b8c6329604903262", + "HASH160 0x14 0xe4300531190587e3880d4c3004f5355d88ff928d EQUAL", + "P2SH,WITNESS,WITNESS_PUBKEYTYPE", + "OK", + "Basic P2SH(P2WSH) with compressed key" +], +[ + [ + "304402201bc0d53046827f4a35a3166e33e3b3366c4085540dc383b95d21ed2ab11e368a0220333e78c6231214f5f8e59621e15d7eeab0d4e4d0796437e00bfbd2680c5f9c1701", + "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798", + 0.00000001 + ], + "0x16 0x0014751e76e8199196d454941c45d1b3a323f1433bd6", + "HASH160 0x14 0xbcfeb728b584253d5f3f70bcb780e9ef218a68f4 EQUAL", + "P2SH,WITNESS,WITNESS_PUBKEYTYPE", + "OK", + "Basic P2SH(P2WPKH) with compressed key" +], + +["Testing with uncompressed keys in witness v0 with WITNESS_PUBKEYTYPE"], +[ + [ + "304402200d461c140cfdfcf36b94961db57ae8c18d1cb80e9d95a9e47ac22470c1bf125502201c8dc1cbfef6a3ef90acbbb992ca22fe9466ee6f9d4898eda277a7ac3ab4b25101", + "410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8ac", + 0.00000001 + ], + "", + "0 0x20 0xb95237b48faaa69eb078e1170be3b5cbb3fddf16d0a991e14ad274f7b33a4f64", + "P2SH,WITNESS,WITNESS_PUBKEYTYPE", + "WITNESS_PUBKEYTYPE", + "Basic P2WSH" +], +[ + [ + "304402201e7216e5ccb3b61d46946ec6cc7e8c4e0117d13ac2fd4b152197e4805191c74202203e9903e33e84d9ee1dd13fb057afb7ccfb47006c23f6a067185efbc9dd780fc501", + "0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8", + 0.00000001 + ], + "", + "0 0x14 0x91b24bf9f5288532960ac687abb035127b1d28a5", + "P2SH,WITNESS,WITNESS_PUBKEYTYPE", + "WITNESS_PUBKEYTYPE", + "Basic P2WPKH" +], +[ + [ + "3044022066e02c19a513049d49349cf5311a1b012b7c4fae023795a18ab1d91c23496c22022025e216342c8e07ce8ef51e8daee88f84306a9de66236cab230bb63067ded1ad301", + "410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8ac", + 0.00000001 + ], + "0x22 0x0020b95237b48faaa69eb078e1170be3b5cbb3fddf16d0a991e14ad274f7b33a4f64", + "HASH160 0x14 0xf386c2ba255cc56d20cfa6ea8b062f8b59945518 EQUAL", + "P2SH,WITNESS,WITNESS_PUBKEYTYPE", + "WITNESS_PUBKEYTYPE", + "Basic P2SH(P2WSH)" +], +[ + [ + "304402200929d11561cd958460371200f82e9cae64c727a495715a31828e27a7ad57b36d0220361732ced04a6f97351ecca21a56d0b8cd4932c1da1f8f569a2b68e5e48aed7801", + "0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8", + 0.00000001 + ], + "0x16 0x001491b24bf9f5288532960ac687abb035127b1d28a5", + "HASH160 0x14 0x17743beb429c55c942d2ec703b98c4d57c2df5c6 EQUAL", + "P2SH,WITNESS,WITNESS_PUBKEYTYPE", + "WITNESS_PUBKEYTYPE", + "Basic P2SH(P2WPKH)" +], +[ + [ + "", + "0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798", + 0.00000001 + ], + "", + "0 0x14 0xfe78b0052557b3d67248d04e4f7b99c361167432", + "P2SH,WITNESS,WITNESS_PUBKEYTYPE", + "WITNESS_PUBKEYTYPE", + "P2WPKH with invalid prefix for compressed key" +], + +["Testing P2WSH multisig with compressed keys"], +[ + [ + "", + "304402207eb8a59b5c65fc3f6aeef77066556ed5c541948a53a3ba7f7c375b8eed76ee7502201e036a7a9a98ff919ff94dc905d67a1ec006f79ef7cff0708485c8bb79dce38e01", + "5121038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179852ae", + 0.00000001 + ], + "", + "0 0x20 0x06c24420938f0fa3c1cb2707d867154220dca365cdbfa0dd2a83854730221460", + "P2SH,WITNESS,WITNESS_PUBKEYTYPE", + "OK", + "P2WSH CHECKMULTISIG with compressed keys" +], +[ + [ + "", + "3044022033706aed33b8155d5486df3b9bca8cdd3bd4bdb5436dce46d72cdaba51d22b4002203626e94fe53a178af46624f17315c6931f20a30b103f5e044e1eda0c3fe185c601", + "5121038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179852ae", + 0.00000001 + ], + "0x22 0x002006c24420938f0fa3c1cb2707d867154220dca365cdbfa0dd2a83854730221460", + "HASH160 0x14 0x26282aad7c29369d15fed062a778b6100d31a340 EQUAL", + "P2SH,WITNESS,WITNESS_PUBKEYTYPE", + "OK", + "P2SH(P2WSH) CHECKMULTISIG with compressed keys" +], +[ + [ + "", + "304402204048b7371ab1c544362efb89af0c80154747d665aa4fcfb2edfd2d161e57b42e02207e043748e96637080ffc3acbd4dcc6fee1e58d30f6d1269535f32188e5ddae7301", + "5121038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179852ae", + 0.00000001 + ], + "", + "0 0x20 0x06c24420938f0fa3c1cb2707d867154220dca365cdbfa0dd2a83854730221460", + "P2SH,WITNESS,WITNESS_PUBKEYTYPE", + "OK", + "P2WSH CHECKMULTISIG with compressed keys" +], +[ + [ + "", + "3044022073902ef0b8a554c36c44cc03c1b64df96ce2914ebcf946f5bb36078fd5245cdf02205b148f1ba127065fb8c83a5a9576f2dcd111739788ed4bb3ee08b2bd3860c91c01", + "5121038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179852ae", + 0.00000001 + ], + "0x22 0x002006c24420938f0fa3c1cb2707d867154220dca365cdbfa0dd2a83854730221460", + "HASH160 0x14 0x26282aad7c29369d15fed062a778b6100d31a340 EQUAL", + "P2SH,WITNESS,WITNESS_PUBKEYTYPE", + "OK", + "P2SH(P2WSH) CHECKMULTISIG with compressed keys" +], + +["Testing P2WSH multisig with compressed and uncompressed keys (first key being the key closer to the top of stack)"], +[ + [ + "", + "304402202d092ededd1f060609dbf8cb76950634ff42b3e62cf4adb69ab92397b07d742302204ff886f8d0817491a96d1daccdcc820f6feb122ee6230143303100db37dfa79f01", + "5121038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b852ae", + 0.00000001 + ], + "", + "0 0x20 0x08a6665ebfd43b02323423e764e185d98d1587f903b81507dbb69bfc41005efa", + "P2SH,WITNESS", + "OK", + "P2WSH CHECKMULTISIG with first key uncompressed and signing with the first key" +], +[ + [ + "", + "304402202dd7e91243f2235481ffb626c3b7baf2c859ae3a5a77fb750ef97b99a8125dc002204960de3d3c3ab9496e218ec57e5240e0e10a6f9546316fe240c216d45116d29301", + "5121038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b852ae", + 0.00000001 + ], + "0x22 0x002008a6665ebfd43b02323423e764e185d98d1587f903b81507dbb69bfc41005efa", + "HASH160 0x14 0x6f5ecd4b83b77f3c438f5214eff96454934fc5d1 EQUAL", + "P2SH,WITNESS", + "OK", + "P2SH(P2WSH) CHECKMULTISIG first key uncompressed and signing with the first key" +], +[ + [ + "", + "304402202d092ededd1f060609dbf8cb76950634ff42b3e62cf4adb69ab92397b07d742302204ff886f8d0817491a96d1daccdcc820f6feb122ee6230143303100db37dfa79f01", + "5121038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b852ae", + 0.00000001 + ], + "", + "0 0x20 0x08a6665ebfd43b02323423e764e185d98d1587f903b81507dbb69bfc41005efa", + "P2SH,WITNESS,WITNESS_PUBKEYTYPE", + "WITNESS_PUBKEYTYPE", + "P2WSH CHECKMULTISIG with first key uncompressed and signing with the first key" +], +[ + [ + "", + "304402202dd7e91243f2235481ffb626c3b7baf2c859ae3a5a77fb750ef97b99a8125dc002204960de3d3c3ab9496e218ec57e5240e0e10a6f9546316fe240c216d45116d29301", + "5121038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b852ae", + 0.00000001 + ], + "0x22 0x002008a6665ebfd43b02323423e764e185d98d1587f903b81507dbb69bfc41005efa", + "HASH160 0x14 0x6f5ecd4b83b77f3c438f5214eff96454934fc5d1 EQUAL", + "P2SH,WITNESS,WITNESS_PUBKEYTYPE", + "WITNESS_PUBKEYTYPE", + "P2SH(P2WSH) CHECKMULTISIG with first key uncompressed and signing with the first key" +], +[ + [ + "", + "304402201e9e6f7deef5b2f21d8223c5189b7d5e82d237c10e97165dd08f547c4e5ce6ed02206796372eb1cc6acb52e13ee2d7f45807780bf96b132cb6697f69434be74b1af901", + "5121038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b852ae", + 0.00000001 + ], + "", + "0 0x20 0x08a6665ebfd43b02323423e764e185d98d1587f903b81507dbb69bfc41005efa", + "P2SH,WITNESS", + "OK", + "P2WSH CHECKMULTISIG with first key uncompressed and signing with the second key" +], +[ + [ + "", + "3044022045e667f3f0f3147b95597a24babe9afecea1f649fd23637dfa7ed7e9f3ac18440220295748e81005231135289fe3a88338dabba55afa1bdb4478691337009d82b68d01", + "5121038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b852ae", + 0.00000001 + ], + "0x22 0x002008a6665ebfd43b02323423e764e185d98d1587f903b81507dbb69bfc41005efa", + "HASH160 0x14 0x6f5ecd4b83b77f3c438f5214eff96454934fc5d1 EQUAL", + "P2SH,WITNESS", + "OK", + "P2SH(P2WSH) CHECKMULTISIG with first key uncompressed and signing with the second key" +], +[ + [ + "", + "304402201e9e6f7deef5b2f21d8223c5189b7d5e82d237c10e97165dd08f547c4e5ce6ed02206796372eb1cc6acb52e13ee2d7f45807780bf96b132cb6697f69434be74b1af901", + "5121038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b852ae", + 0.00000001 + ], + "", + "0 0x20 0x08a6665ebfd43b02323423e764e185d98d1587f903b81507dbb69bfc41005efa", + "P2SH,WITNESS,WITNESS_PUBKEYTYPE", + "WITNESS_PUBKEYTYPE", + "P2WSH CHECKMULTISIG with first key uncompressed and signing with the second key" +], +[ + [ + "", + "3044022045e667f3f0f3147b95597a24babe9afecea1f649fd23637dfa7ed7e9f3ac18440220295748e81005231135289fe3a88338dabba55afa1bdb4478691337009d82b68d01", + "5121038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b852ae", + 0.00000001 + ], + "0x22 0x002008a6665ebfd43b02323423e764e185d98d1587f903b81507dbb69bfc41005efa", + "HASH160 0x14 0x6f5ecd4b83b77f3c438f5214eff96454934fc5d1 EQUAL", + "P2SH,WITNESS,WITNESS_PUBKEYTYPE", + "WITNESS_PUBKEYTYPE", + "P2SH(P2WSH) CHECKMULTISIG with first key uncompressed and signing with the second key" +], +[ + [ + "", + "3044022046f5367a261fd8f8d7de6eb390491344f8ec2501638fb9a1095a0599a21d3f4c02205c1b3b51d20091c5f1020841bbca87b44ebe25405c64e4acf758f2eae8665f8401", + "5141048282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f5150811f8a8098557dfe45e8256e830b60ace62d613ac2f7b17bed31b6eaff6e26caf210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179852ae", + 0.00000001 + ], + "", + "0 0x20 0x230828ed48871f0f362ce9432aa52f620f442cc8d9ce7a8b5e798365595a38bb", + "P2SH,WITNESS", + "OK", + "P2WSH CHECKMULTISIG with second key uncompressed and signing with the first key" +], +[ + [ + "", + "3044022053e210e4fb1881e6092fd75c3efc5163105599e246ded661c0ee2b5682cc2d6c02203a26b7ada8682a095b84c6d1b881637000b47d761fc837c4cee33555296d63f101", + "5141048282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f5150811f8a8098557dfe45e8256e830b60ace62d613ac2f7b17bed31b6eaff6e26caf210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179852ae", + 0.00000001 + ], + "0x22 0x0020230828ed48871f0f362ce9432aa52f620f442cc8d9ce7a8b5e798365595a38bb", + "HASH160 0x14 0x3478e7019ce61a68148f87549579b704cbe4c393 EQUAL", + "P2SH,WITNESS", + "OK", + "P2SH(P2WSH) CHECKMULTISIG second key uncompressed and signing with the first key" +], +[ + [ + "", + "3044022046f5367a261fd8f8d7de6eb390491344f8ec2501638fb9a1095a0599a21d3f4c02205c1b3b51d20091c5f1020841bbca87b44ebe25405c64e4acf758f2eae8665f8401", + "5141048282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f5150811f8a8098557dfe45e8256e830b60ace62d613ac2f7b17bed31b6eaff6e26caf210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179852ae", + 0.00000001 + ], + "", + "0 0x20 0x230828ed48871f0f362ce9432aa52f620f442cc8d9ce7a8b5e798365595a38bb", + "P2SH,WITNESS,WITNESS_PUBKEYTYPE", + "OK", + "P2WSH CHECKMULTISIG with second key uncompressed and signing with the first key should pass as the uncompressed key is not used" +], +[ + [ + "", + "3044022053e210e4fb1881e6092fd75c3efc5163105599e246ded661c0ee2b5682cc2d6c02203a26b7ada8682a095b84c6d1b881637000b47d761fc837c4cee33555296d63f101", + "5141048282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f5150811f8a8098557dfe45e8256e830b60ace62d613ac2f7b17bed31b6eaff6e26caf210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179852ae", + 0.00000001 + ], + "0x22 0x0020230828ed48871f0f362ce9432aa52f620f442cc8d9ce7a8b5e798365595a38bb", + "HASH160 0x14 0x3478e7019ce61a68148f87549579b704cbe4c393 EQUAL", + "P2SH,WITNESS,WITNESS_PUBKEYTYPE", + "OK", + "P2SH(P2WSH) CHECKMULTISIG with second key uncompressed and signing with the first key should pass as the uncompressed key is not used" +], +[ + [ + "", + "304402206c6d9f5daf85b54af2a93ec38b15ab27f205dbf5c735365ff12451e43613d1f40220736a44be63423ed5ebf53491618b7cc3d8a5093861908da853739c73717938b701", + "5141048282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f5150811f8a8098557dfe45e8256e830b60ace62d613ac2f7b17bed31b6eaff6e26caf210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179852ae", + 0.00000001 + ], + "", + "0 0x20 0x230828ed48871f0f362ce9432aa52f620f442cc8d9ce7a8b5e798365595a38bb", + "P2SH,WITNESS", + "OK", + "P2WSH CHECKMULTISIG with second key uncompressed and signing with the second key" +], +[ + [ + "", + "30440220687871bc6144012d75baf585bb26ce13997f7d8c626f4d8825b069c3b2d064470220108936fe1c57327764782253e99090b09c203ec400ed35ce9e026ce2ecf842a001", + "5141048282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f5150811f8a8098557dfe45e8256e830b60ace62d613ac2f7b17bed31b6eaff6e26caf210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179852ae", + 0.00000001 + ], + "0x22 0x0020230828ed48871f0f362ce9432aa52f620f442cc8d9ce7a8b5e798365595a38bb", + "HASH160 0x14 0x3478e7019ce61a68148f87549579b704cbe4c393 EQUAL", + "P2SH,WITNESS", + "OK", + "P2SH(P2WSH) CHECKMULTISIG with second key uncompressed and signing with the second key" +], +[ + [ + "", + "304402206c6d9f5daf85b54af2a93ec38b15ab27f205dbf5c735365ff12451e43613d1f40220736a44be63423ed5ebf53491618b7cc3d8a5093861908da853739c73717938b701", + "5141048282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f5150811f8a8098557dfe45e8256e830b60ace62d613ac2f7b17bed31b6eaff6e26caf210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179852ae", + 0.00000001 + ], + "", + "0 0x20 0x230828ed48871f0f362ce9432aa52f620f442cc8d9ce7a8b5e798365595a38bb", + "P2SH,WITNESS,WITNESS_PUBKEYTYPE", + "WITNESS_PUBKEYTYPE", + "P2WSH CHECKMULTISIG with second key uncompressed and signing with the second key" +], +[ + [ + "", + "30440220687871bc6144012d75baf585bb26ce13997f7d8c626f4d8825b069c3b2d064470220108936fe1c57327764782253e99090b09c203ec400ed35ce9e026ce2ecf842a001", + "5141048282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f5150811f8a8098557dfe45e8256e830b60ace62d613ac2f7b17bed31b6eaff6e26caf210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179852ae", + 0.00000001 + ], + "0x22 0x0020230828ed48871f0f362ce9432aa52f620f442cc8d9ce7a8b5e798365595a38bb", + "HASH160 0x14 0x3478e7019ce61a68148f87549579b704cbe4c393 EQUAL", + "P2SH,WITNESS,WITNESS_PUBKEYTYPE", + "WITNESS_PUBKEYTYPE", + "P2SH(P2WSH) CHECKMULTISIG with second key uncompressed and signing with the second key" +], + +["CHECKSEQUENCEVERIFY tests"], +["", "CHECKSEQUENCEVERIFY", "CHECKSEQUENCEVERIFY", "INVALID_STACK_OPERATION", "CSV automatically fails on an empty stack"], +["-1", "CHECKSEQUENCEVERIFY", "CHECKSEQUENCEVERIFY", "NEGATIVE_LOCKTIME", "CSV automatically fails if stack top is negative"], +["0x0100", "CHECKSEQUENCEVERIFY", "CHECKSEQUENCEVERIFY,MINIMALDATA", "SCRIPTNUM", "CSV fails if stack top is not minimally encoded"], +["0", "CHECKSEQUENCEVERIFY", "CHECKSEQUENCEVERIFY", "UNSATISFIED_LOCKTIME", "CSV fails if stack top bit 1 << 31 is set and the tx version < 2"], +["0x050000000001", "CHECKSEQUENCEVERIFY", "CHECKSEQUENCEVERIFY", "UNSATISFIED_LOCKTIME", + "CSV fails if stack top bit 1 << 31 is not set, and tx version < 2"], + +["MINIMALIF tests"], +["MINIMALIF is not applied to non-segwit scripts"], +["1", "IF 1 ENDIF", "P2SH,WITNESS,MINIMALIF", "OK"], +["2", "IF 1 ENDIF", "P2SH,WITNESS,MINIMALIF", "OK"], +["0x02 0x0100", "IF 1 ENDIF", "P2SH,WITNESS,MINIMALIF", "OK"], +["0", "IF 1 ENDIF", "P2SH,WITNESS,MINIMALIF", "EVAL_FALSE"], +["0x01 0x00", "IF 1 ENDIF", "P2SH,WITNESS,MINIMALIF", "EVAL_FALSE"], +["1", "NOTIF 1 ENDIF", "P2SH,WITNESS,MINIMALIF", "EVAL_FALSE"], +["2", "NOTIF 1 ENDIF", "P2SH,WITNESS,MINIMALIF", "EVAL_FALSE"], +["0x02 0x0100", "NOTIF 1 ENDIF", "P2SH,WITNESS,MINIMALIF", "EVAL_FALSE"], +["0", "NOTIF 1 ENDIF", "P2SH,WITNESS,MINIMALIF", "OK"], +["0x01 0x00", "NOTIF 1 ENDIF", "P2SH,WITNESS,MINIMALIF", "OK"], +["Normal P2SH IF 1 ENDIF"], +["1 0x03 0x635168", "HASH160 0x14 0xe7309652a8e3f600f06f5d8d52d6df03d2176cc3 EQUAL", "P2SH,WITNESS,MINIMALIF", "OK"], +["2 0x03 0x635168", "HASH160 0x14 0xe7309652a8e3f600f06f5d8d52d6df03d2176cc3 EQUAL", "P2SH,WITNESS,MINIMALIF", "OK"], +["0x02 0x0100 0x03 0x635168", "HASH160 0x14 0xe7309652a8e3f600f06f5d8d52d6df03d2176cc3 EQUAL", "P2SH,WITNESS,MINIMALIF", "OK"], +["0 0x03 0x635168", "HASH160 0x14 0xe7309652a8e3f600f06f5d8d52d6df03d2176cc3 EQUAL", "P2SH,WITNESS,MINIMALIF", "EVAL_FALSE"], +["0x01 0x00 0x03 0x635168", "HASH160 0x14 0xe7309652a8e3f600f06f5d8d52d6df03d2176cc3 EQUAL", "P2SH,WITNESS,MINIMALIF", "EVAL_FALSE"], +["0x03 0x635168", "HASH160 0x14 0xe7309652a8e3f600f06f5d8d52d6df03d2176cc3 EQUAL", "P2SH,WITNESS,MINIMALIF", "INVALID_STACK_OPERATION"], +["Normal P2SH NOTIF 1 ENDIF"], +["1 0x03 0x645168", "HASH160 0x14 0x0c3f8fe3d6ca266e76311ecda544c67d15fdd5b0 EQUAL", "P2SH,WITNESS,MINIMALIF", "EVAL_FALSE"], +["2 0x03 0x645168", "HASH160 0x14 0x0c3f8fe3d6ca266e76311ecda544c67d15fdd5b0 EQUAL", "P2SH,WITNESS,MINIMALIF", "EVAL_FALSE"], +["0x02 0x0100 0x03 0x645168", "HASH160 0x14 0x0c3f8fe3d6ca266e76311ecda544c67d15fdd5b0 EQUAL", "P2SH,WITNESS,MINIMALIF", "EVAL_FALSE"], +["0 0x03 0x645168", "HASH160 0x14 0x0c3f8fe3d6ca266e76311ecda544c67d15fdd5b0 EQUAL", "P2SH,WITNESS,MINIMALIF", "OK"], +["0x01 0x00 0x03 0x645168", "HASH160 0x14 0x0c3f8fe3d6ca266e76311ecda544c67d15fdd5b0 EQUAL", "P2SH,WITNESS,MINIMALIF", "OK"], +["0x03 0x645168", "HASH160 0x14 0x0c3f8fe3d6ca266e76311ecda544c67d15fdd5b0 EQUAL", "P2SH,WITNESS,MINIMALIF", "INVALID_STACK_OPERATION"], +["P2WSH IF 1 ENDIF"], +[["01", "635168", 0.00000001], "", "0 0x20 0xc7eaf06d5ae01a58e376e126eb1e6fab2036076922b96b2711ffbec1e590665d", "P2SH,WITNESS", "OK"], +[["02", "635168", 0.00000001], "", "0 0x20 0xc7eaf06d5ae01a58e376e126eb1e6fab2036076922b96b2711ffbec1e590665d", "P2SH,WITNESS", "OK"], +[["0100", "635168", 0.00000001], "", "0 0x20 0xc7eaf06d5ae01a58e376e126eb1e6fab2036076922b96b2711ffbec1e590665d", "P2SH,WITNESS", "OK"], +[["", "635168", 0.00000001], "", "0 0x20 0xc7eaf06d5ae01a58e376e126eb1e6fab2036076922b96b2711ffbec1e590665d", "P2SH,WITNESS", "CLEANSTACK"], +[["00", "635168", 0.00000001], "", "0 0x20 0xc7eaf06d5ae01a58e376e126eb1e6fab2036076922b96b2711ffbec1e590665d", "P2SH,WITNESS", "CLEANSTACK"], +[["01", "635168", 0.00000001], "", "0 0x20 0xc7eaf06d5ae01a58e376e126eb1e6fab2036076922b96b2711ffbec1e590665d", "P2SH,WITNESS,MINIMALIF", "OK"], +[["02", "635168", 0.00000001], "", "0 0x20 0xc7eaf06d5ae01a58e376e126eb1e6fab2036076922b96b2711ffbec1e590665d", "P2SH,WITNESS,MINIMALIF", "MINIMALIF"], +[["0100", "635168", 0.00000001], "", "0 0x20 0xc7eaf06d5ae01a58e376e126eb1e6fab2036076922b96b2711ffbec1e590665d", "P2SH,WITNESS,MINIMALIF", "MINIMALIF"], +[["", "635168", 0.00000001], "", "0 0x20 0xc7eaf06d5ae01a58e376e126eb1e6fab2036076922b96b2711ffbec1e590665d", "P2SH,WITNESS,MINIMALIF", "CLEANSTACK"], +[["00", "635168", 0.00000001], "", "0 0x20 0xc7eaf06d5ae01a58e376e126eb1e6fab2036076922b96b2711ffbec1e590665d", "P2SH,WITNESS,MINIMALIF", "MINIMALIF"], +[["635168", 0.00000001], "", "0 0x20 0xc7eaf06d5ae01a58e376e126eb1e6fab2036076922b96b2711ffbec1e590665d", "P2SH,WITNESS", "INVALID_STACK_OPERATION"], +[["635168", 0.00000001], "", "0 0x20 0xc7eaf06d5ae01a58e376e126eb1e6fab2036076922b96b2711ffbec1e590665d", "P2SH,WITNESS,MINIMALIF", "INVALID_STACK_OPERATION"], +["P2WSH NOTIF 1 ENDIF"], +[["01", "645168", 0.00000001], "", "0 0x20 0xf913eacf2e38a5d6fc3a8311d72ae704cb83866350a984dd3e5eb76d2a8c28e8", "P2SH,WITNESS", "CLEANSTACK"], +[["02", "645168", 0.00000001], "", "0 0x20 0xf913eacf2e38a5d6fc3a8311d72ae704cb83866350a984dd3e5eb76d2a8c28e8", "P2SH,WITNESS", "CLEANSTACK"], +[["0100", "645168", 0.00000001], "", "0 0x20 0xf913eacf2e38a5d6fc3a8311d72ae704cb83866350a984dd3e5eb76d2a8c28e8", "P2SH,WITNESS", "CLEANSTACK"], +[["", "645168", 0.00000001], "", "0 0x20 0xf913eacf2e38a5d6fc3a8311d72ae704cb83866350a984dd3e5eb76d2a8c28e8", "P2SH,WITNESS", "OK"], +[["00", "645168", 0.00000001], "", "0 0x20 0xf913eacf2e38a5d6fc3a8311d72ae704cb83866350a984dd3e5eb76d2a8c28e8", "P2SH,WITNESS", "OK"], +[["01", "645168", 0.00000001], "", "0 0x20 0xf913eacf2e38a5d6fc3a8311d72ae704cb83866350a984dd3e5eb76d2a8c28e8", "P2SH,WITNESS,MINIMALIF", "CLEANSTACK"], +[["02", "645168", 0.00000001], "", "0 0x20 0xf913eacf2e38a5d6fc3a8311d72ae704cb83866350a984dd3e5eb76d2a8c28e8", "P2SH,WITNESS,MINIMALIF", "MINIMALIF"], +[["0100", "645168", 0.00000001], "", "0 0x20 0xf913eacf2e38a5d6fc3a8311d72ae704cb83866350a984dd3e5eb76d2a8c28e8", "P2SH,WITNESS,MINIMALIF", "MINIMALIF"], +[["", "645168", 0.00000001], "", "0 0x20 0xf913eacf2e38a5d6fc3a8311d72ae704cb83866350a984dd3e5eb76d2a8c28e8", "P2SH,WITNESS,MINIMALIF", "OK"], +[["00", "645168", 0.00000001], "", "0 0x20 0xf913eacf2e38a5d6fc3a8311d72ae704cb83866350a984dd3e5eb76d2a8c28e8", "P2SH,WITNESS,MINIMALIF", "MINIMALIF"], +[["645168", 0.00000001], "", "0 0x20 0xf913eacf2e38a5d6fc3a8311d72ae704cb83866350a984dd3e5eb76d2a8c28e8", "P2SH,WITNESS", "INVALID_STACK_OPERATION"], +[["645168", 0.00000001], "", "0 0x20 0xf913eacf2e38a5d6fc3a8311d72ae704cb83866350a984dd3e5eb76d2a8c28e8", "P2SH,WITNESS,MINIMALIF", "INVALID_STACK_OPERATION"], + + + +["P2SH-P2WSH IF 1 ENDIF"], +[["01", "635168", 0.00000001], "0x22 0x0020c7eaf06d5ae01a58e376e126eb1e6fab2036076922b96b2711ffbec1e590665d", "HASH160 0x14 0x9b27ee6d9010c21bf837b334d043be5d150e7ba7 EQUAL", "P2SH,WITNESS", "OK"], +[["02", "635168", 0.00000001], "0x22 0x0020c7eaf06d5ae01a58e376e126eb1e6fab2036076922b96b2711ffbec1e590665d", "HASH160 0x14 0x9b27ee6d9010c21bf837b334d043be5d150e7ba7 EQUAL", "P2SH,WITNESS", "OK"], +[["0100", "635168", 0.00000001], "0x22 0x0020c7eaf06d5ae01a58e376e126eb1e6fab2036076922b96b2711ffbec1e590665d", "HASH160 0x14 0x9b27ee6d9010c21bf837b334d043be5d150e7ba7 EQUAL", "P2SH,WITNESS", "OK"], +[["", "635168", 0.00000001], "0x22 0x0020c7eaf06d5ae01a58e376e126eb1e6fab2036076922b96b2711ffbec1e590665d", "HASH160 0x14 0x9b27ee6d9010c21bf837b334d043be5d150e7ba7 EQUAL", "P2SH,WITNESS", "CLEANSTACK"], +[["00", "635168", 0.00000001], "0x22 0x0020c7eaf06d5ae01a58e376e126eb1e6fab2036076922b96b2711ffbec1e590665d", "HASH160 0x14 0x9b27ee6d9010c21bf837b334d043be5d150e7ba7 EQUAL", "P2SH,WITNESS", "CLEANSTACK"], +[["01", "635168", 0.00000001], "0x22 0x0020c7eaf06d5ae01a58e376e126eb1e6fab2036076922b96b2711ffbec1e590665d", "HASH160 0x14 0x9b27ee6d9010c21bf837b334d043be5d150e7ba7 EQUAL", "P2SH,WITNESS,MINIMALIF", "OK"], +[["02", "635168", 0.00000001], "0x22 0x0020c7eaf06d5ae01a58e376e126eb1e6fab2036076922b96b2711ffbec1e590665d", "HASH160 0x14 0x9b27ee6d9010c21bf837b334d043be5d150e7ba7 EQUAL", "P2SH,WITNESS,MINIMALIF", "MINIMALIF"], +[["0100", "635168", 0.00000001], "0x22 0x0020c7eaf06d5ae01a58e376e126eb1e6fab2036076922b96b2711ffbec1e590665d", "HASH160 0x14 0x9b27ee6d9010c21bf837b334d043be5d150e7ba7 EQUAL", "P2SH,WITNESS,MINIMALIF", "MINIMALIF"], +[["", "635168", 0.00000001], "0x22 0x0020c7eaf06d5ae01a58e376e126eb1e6fab2036076922b96b2711ffbec1e590665d", "HASH160 0x14 0x9b27ee6d9010c21bf837b334d043be5d150e7ba7 EQUAL", "P2SH,WITNESS,MINIMALIF", "CLEANSTACK"], +[["00", "635168", 0.00000001], "0x22 0x0020c7eaf06d5ae01a58e376e126eb1e6fab2036076922b96b2711ffbec1e590665d", "HASH160 0x14 0x9b27ee6d9010c21bf837b334d043be5d150e7ba7 EQUAL", "P2SH,WITNESS,MINIMALIF", "MINIMALIF"], +[["635168", 0.00000001], "0x22 0x0020c7eaf06d5ae01a58e376e126eb1e6fab2036076922b96b2711ffbec1e590665d", "HASH160 0x14 0x9b27ee6d9010c21bf837b334d043be5d150e7ba7 EQUAL", "P2SH,WITNESS", "INVALID_STACK_OPERATION"], +[["635168", 0.00000001], "0x22 0x0020c7eaf06d5ae01a58e376e126eb1e6fab2036076922b96b2711ffbec1e590665d", "HASH160 0x14 0x9b27ee6d9010c21bf837b334d043be5d150e7ba7 EQUAL", "P2SH,WITNESS,MINIMALIF", "INVALID_STACK_OPERATION"], +["P2SH-P2WSH NOTIF 1 ENDIF"], +[["01", "645168", 0.00000001], "0x22 0x0020f913eacf2e38a5d6fc3a8311d72ae704cb83866350a984dd3e5eb76d2a8c28e8", "HASH160 0x14 0xdbb7d1c0a56b7a9c423300c8cca6e6e065baf1dc EQUAL", "P2SH,WITNESS", "CLEANSTACK"], +[["02", "645168", 0.00000001], "0x22 0x0020f913eacf2e38a5d6fc3a8311d72ae704cb83866350a984dd3e5eb76d2a8c28e8", "HASH160 0x14 0xdbb7d1c0a56b7a9c423300c8cca6e6e065baf1dc EQUAL", "P2SH,WITNESS", "CLEANSTACK"], +[["0100", "645168", 0.00000001], "0x22 0x0020f913eacf2e38a5d6fc3a8311d72ae704cb83866350a984dd3e5eb76d2a8c28e8", "HASH160 0x14 0xdbb7d1c0a56b7a9c423300c8cca6e6e065baf1dc EQUAL", "P2SH,WITNESS", "CLEANSTACK"], +[["", "645168", 0.00000001], "0x22 0x0020f913eacf2e38a5d6fc3a8311d72ae704cb83866350a984dd3e5eb76d2a8c28e8", "HASH160 0x14 0xdbb7d1c0a56b7a9c423300c8cca6e6e065baf1dc EQUAL", "P2SH,WITNESS", "OK"], +[["00", "645168", 0.00000001], "0x22 0x0020f913eacf2e38a5d6fc3a8311d72ae704cb83866350a984dd3e5eb76d2a8c28e8", "HASH160 0x14 0xdbb7d1c0a56b7a9c423300c8cca6e6e065baf1dc EQUAL", "P2SH,WITNESS", "OK"], +[["01", "645168", 0.00000001], "0x22 0x0020f913eacf2e38a5d6fc3a8311d72ae704cb83866350a984dd3e5eb76d2a8c28e8", "HASH160 0x14 0xdbb7d1c0a56b7a9c423300c8cca6e6e065baf1dc EQUAL", "P2SH,WITNESS,MINIMALIF", "CLEANSTACK"], +[["02", "645168", 0.00000001], "0x22 0x0020f913eacf2e38a5d6fc3a8311d72ae704cb83866350a984dd3e5eb76d2a8c28e8", "HASH160 0x14 0xdbb7d1c0a56b7a9c423300c8cca6e6e065baf1dc EQUAL", "P2SH,WITNESS,MINIMALIF", "MINIMALIF"], +[["0100", "645168", 0.00000001], "0x22 0x0020f913eacf2e38a5d6fc3a8311d72ae704cb83866350a984dd3e5eb76d2a8c28e8", "HASH160 0x14 0xdbb7d1c0a56b7a9c423300c8cca6e6e065baf1dc EQUAL", "P2SH,WITNESS,MINIMALIF", "MINIMALIF"], +[["", "645168", 0.00000001], "0x22 0x0020f913eacf2e38a5d6fc3a8311d72ae704cb83866350a984dd3e5eb76d2a8c28e8", "HASH160 0x14 0xdbb7d1c0a56b7a9c423300c8cca6e6e065baf1dc EQUAL", "P2SH,WITNESS,MINIMALIF", "OK"], +[["00", "645168", 0.00000001], "0x22 0x0020f913eacf2e38a5d6fc3a8311d72ae704cb83866350a984dd3e5eb76d2a8c28e8", "HASH160 0x14 0xdbb7d1c0a56b7a9c423300c8cca6e6e065baf1dc EQUAL", "P2SH,WITNESS,MINIMALIF", "MINIMALIF"], +[["645168", 0.00000001], "0x22 0x0020f913eacf2e38a5d6fc3a8311d72ae704cb83866350a984dd3e5eb76d2a8c28e8", "HASH160 0x14 0xdbb7d1c0a56b7a9c423300c8cca6e6e065baf1dc EQUAL", "P2SH,WITNESS", "INVALID_STACK_OPERATION"], +[["645168", 0.00000001], "0x22 0x0020f913eacf2e38a5d6fc3a8311d72ae704cb83866350a984dd3e5eb76d2a8c28e8", "HASH160 0x14 0xdbb7d1c0a56b7a9c423300c8cca6e6e065baf1dc EQUAL", "P2SH,WITNESS,MINIMALIF", "INVALID_STACK_OPERATION"], + +["Tapscript tests"], +[ + [ + "1ffe1234567890", + "00", + "#SCRIPT# HASH256 DUP SHA1 DROP DUP DROP TOALTSTACK HASH256 DUP DROP TOALTSTACK FROMALTSTACK", + "#CONTROLBLOCK#", + 0.00000001 + ], + "", + "0x51 0x20 #TAPROOTOUTPUT#", + "P2SH,WITNESS,TAPROOT", + "OK", + "TAPSCRIPT Tests testing tapscript with many different op codes including ALTSTACK interactions" +], +[ + [ + "abcdef", + "#SCRIPT# 1 IF SHA256 ENDIF SIZE SWAP DROP 32 EQUAL", + "#CONTROLBLOCK#", + 0.00000001 + ], + "", + "0x51 0x20 #TAPROOTOUTPUT#", + "P2SH,WITNESS,TAPROOT", + "OK", + "TAPSCRIPT Test IF conditional when true" +], +[ + [ + "abcdef", + "#SCRIPT# 0 IF SHA256 ENDIF SIZE SWAP DROP 32 EQUAL", + "#CONTROLBLOCK#", + 0.00000001 + ], + "", + "0x51 0x20 #TAPROOTOUTPUT#", + "P2SH,WITNESS,TAPROOT", + "EVAL_FALSE", + "TAPSCRIPT Test IF conditional when false" +], +[ + [ + "aa", + "bb", + "cc", + "#SCRIPT# EQUAL IF DROP DROP ENDIF", + "#CONTROLBLOCK#", + 0.00000001 + ], + "", + "0x51 0x20 #TAPROOTOUTPUT#", + "P2SH,WITNESS,TAPROOT", + "OK", + "TAPSCRIPT Test that DROP operations do not execute inside of a false IF conditional" +], +[ + [ + "aa", + "#SCRIPT# 0 CHECKSIG", + "#CONTROLBLOCK#", + 0.00000001 + ], + "", + "0x51 0x20 #TAPROOTOUTPUT#", + "P2SH,WITNESS,TAPROOT", + "TAPSCRIPT_EMPTY_PUBKEY", + "TAPSCRIPT: OP_CHECKSIG with empty pubkey must fail" +], + +["NULLFAIL should cover all signatures and signatures only"], +["0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0", "0x01 0x14 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0x01 0x14 CHECKMULTISIG NOT", "DERSIG", "OK", "BIP66 and NULLFAIL-compliant"], +["0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0", "0x01 0x14 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0x01 0x14 CHECKMULTISIG NOT", "DERSIG,NULLFAIL", "OK", "BIP66 and NULLFAIL-compliant"], +["1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0", "0x01 0x14 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0x01 0x14 CHECKMULTISIG NOT", "DERSIG,NULLFAIL", "OK", "BIP66 and NULLFAIL-compliant, not NULLDUMMY-compliant"], +["1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0", "0x01 0x14 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0x01 0x14 CHECKMULTISIG NOT", "DERSIG,NULLFAIL,NULLDUMMY", "SIG_NULLDUMMY", "BIP66 and NULLFAIL-compliant, not NULLDUMMY-compliant"], +["0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0x09 0x300602010102010101", "0x01 0x14 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0x01 0x14 CHECKMULTISIG NOT", "DERSIG", "OK", "BIP66-compliant but not NULLFAIL-compliant"], +["0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0x09 0x300602010102010101", "0x01 0x14 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0x01 0x14 CHECKMULTISIG NOT", "DERSIG,NULLFAIL", "NULLFAIL", "BIP66-compliant but not NULLFAIL-compliant"], +["0 0x09 0x300602010102010101 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0", "0x01 0x14 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0x01 0x14 CHECKMULTISIG NOT", "DERSIG", "OK", "BIP66-compliant but not NULLFAIL-compliant"], +["0 0x09 0x300602010102010101 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0", "0x01 0x14 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0x01 0x14 CHECKMULTISIG NOT", "DERSIG,NULLFAIL", "NULLFAIL", "BIP66-compliant but not NULLFAIL-compliant"], + +["The End"] +] diff --git a/crates/script_verify/tests/data/tx_invalid.json b/crates/script_verify/tests/data/tx_invalid.json new file mode 100644 index 0000000000..486469ddef --- /dev/null +++ b/crates/script_verify/tests/data/tx_invalid.json @@ -0,0 +1,397 @@ +[ +["The following are deserialized transactions which are invalid."], +["They are in the form"], +["[[[prevout hash, prevout index, prevout scriptPubKey, amount?], [input 2], ...],"], +["serializedTransaction, verifyFlags]"], +["Use BADTX for verifyFlags if it is expected to fail CheckTransaction()"], +["Objects that are only a single string (like this one) are ignored"], + +["0e1b5688cf179cd9f7cbda1fac0090f6e684bbf8cd946660120197c3f3681809 but with extra junk appended to the end of the scriptPubKey"], +[[["6ca7ec7b1847f6bdbd737176050e6a08d66ccd55bb94ad24f4018024107a5827", 0, "0x41 0x043b640e983c9690a14c039a2037ecc3467b27a0dcd58f19d76c7bc118d09fec45adc5370a1c5bf8067ca9f5557a4cf885fdb0fe0dcc9c3a7137226106fbc779a5 CHECKSIG VERIFY 1"]], +"010000000127587a10248001f424ad94bb55cd6cd6086a0e05767173bdbdf647187beca76c000000004948304502201b822ad10d6adc1a341ae8835be3f70a25201bbff31f59cbb9c5353a5f0eca18022100ea7b2f7074e9aa9cf70aa8d0ffee13e6b45dddabf1ab961bda378bcdb778fa4701ffffffff0100f2052a010000001976a914fc50c5907d86fed474ba5ce8b12a66e0a4c139d888ac00000000", "NONE"], + +["This is the nearly-standard transaction with CHECKSIGVERIFY 1 instead of CHECKSIG from tx_valid.json"], +["but with the signature duplicated in the scriptPubKey with a non-standard pushdata prefix"], +["See FindAndDelete, which will only remove if it uses the same pushdata prefix as is standard"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "DUP HASH160 0x14 0x5b6462475454710f3c22f5fdf0b40704c92f25c3 EQUALVERIFY CHECKSIGVERIFY 1 0x4c 0x47 0x3044022067288ea50aa799543a536ff9306f8e1cba05b9c6b10951175b924f96732555ed022026d7b5265f38d21541519e4a1e55044d5b9e17e15cdbaf29ae3792e99e883e7a01"]], +"01000000010001000000000000000000000000000000000000000000000000000000000000000000006a473044022067288ea50aa799543a536ff9306f8e1cba05b9c6b10951175b924f96732555ed022026d7b5265f38d21541519e4a1e55044d5b9e17e15cdbaf29ae3792e99e883e7a012103ba8c8b86dea131c22ab967e6dd99bdae8eff7a1f75a2c35f1f944109e3fe5e22ffffffff010000000000000000015100000000", "NONE"], + +["Same as above, but with the sig in the scriptSig also pushed with the same non-standard OP_PUSHDATA"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "DUP HASH160 0x14 0x5b6462475454710f3c22f5fdf0b40704c92f25c3 EQUALVERIFY CHECKSIGVERIFY 1 0x4c 0x47 0x3044022067288ea50aa799543a536ff9306f8e1cba05b9c6b10951175b924f96732555ed022026d7b5265f38d21541519e4a1e55044d5b9e17e15cdbaf29ae3792e99e883e7a01"]], +"01000000010001000000000000000000000000000000000000000000000000000000000000000000006b4c473044022067288ea50aa799543a536ff9306f8e1cba05b9c6b10951175b924f96732555ed022026d7b5265f38d21541519e4a1e55044d5b9e17e15cdbaf29ae3792e99e883e7a012103ba8c8b86dea131c22ab967e6dd99bdae8eff7a1f75a2c35f1f944109e3fe5e22ffffffff010000000000000000015100000000", "NONE"], + +["This is the nearly-standard transaction with CHECKSIGVERIFY 1 instead of CHECKSIG from tx_valid.json"], +["but with the signature duplicated in the scriptPubKey with a different hashtype suffix"], +["See FindAndDelete, which will only remove if the signature, including the hash type, matches"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "DUP HASH160 0x14 0x5b6462475454710f3c22f5fdf0b40704c92f25c3 EQUALVERIFY CHECKSIGVERIFY 1 0x47 0x3044022067288ea50aa799543a536ff9306f8e1cba05b9c6b10951175b924f96732555ed022026d7b5265f38d21541519e4a1e55044d5b9e17e15cdbaf29ae3792e99e883e7a81"]], +"01000000010001000000000000000000000000000000000000000000000000000000000000000000006a473044022067288ea50aa799543a536ff9306f8e1cba05b9c6b10951175b924f96732555ed022026d7b5265f38d21541519e4a1e55044d5b9e17e15cdbaf29ae3792e99e883e7a012103ba8c8b86dea131c22ab967e6dd99bdae8eff7a1f75a2c35f1f944109e3fe5e22ffffffff010000000000000000015100000000", "NONE"], + +["An invalid P2SH Transaction"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0x7a052c840ba73af26755de42cf01cc9e0a49fef0 EQUAL"]], +"010000000100010000000000000000000000000000000000000000000000000000000000000000000009085768617420697320ffffffff010000000000000000015100000000", "P2SH"], + +["Tests for CheckTransaction()"], +["No outputs"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0x05ab9e14d983742513f0f451e105ffb4198d1dd4 EQUAL"]], +"01000000010001000000000000000000000000000000000000000000000000000000000000000000006d483045022100f16703104aab4e4088317c862daec83440242411b039d14280e03dd33b487ab802201318a7be236672c5c56083eb7a5a195bc57a40af7923ff8545016cd3b571e2a601232103c40e5d339df3f30bf753e7e04450ae4ef76c9e45587d1d993bdc4cd06f0651c7acffffffff0000000000", "BADTX"], + +["Negative output"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0xae609aca8061d77c5e111f6bb62501a6bbe2bfdb EQUAL"]], +"01000000010001000000000000000000000000000000000000000000000000000000000000000000006d4830450220063222cbb128731fc09de0d7323746539166544d6c1df84d867ccea84bcc8903022100bf568e8552844de664cd41648a031554327aa8844af34b4f27397c65b92c04de0123210243ec37dee0e2e053a9c976f43147e79bc7d9dc606ea51010af1ac80db6b069e1acffffffff01ffffffffffffffff015100000000", "BADTX"], + +["MAX_MONEY + 1 output"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0x32afac281462b822adbec5094b8d4d337dd5bd6a EQUAL"]], +"01000000010001000000000000000000000000000000000000000000000000000000000000000000006e493046022100e1eadba00d9296c743cb6ecc703fd9ddc9b3cd12906176a226ae4c18d6b00796022100a71aef7d2874deff681ba6080f1b278bac7bb99c61b08a85f4311970ffe7f63f012321030c0588dc44d92bdcbf8e72093466766fdc265ead8db64517b0c542275b70fffbacffffffff010140075af0750700015100000000", "BADTX"], + +["MAX_MONEY output + 1 output"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0xb558cbf4930954aa6a344363a15668d7477ae716 EQUAL"]], +"01000000010001000000000000000000000000000000000000000000000000000000000000000000006d483045022027deccc14aa6668e78a8c9da3484fbcd4f9dcc9bb7d1b85146314b21b9ae4d86022100d0b43dece8cfb07348de0ca8bc5b86276fa88f7f2138381128b7c36ab2e42264012321029bb13463ddd5d2cc05da6e84e37536cb9525703cfd8f43afdb414988987a92f6acffffffff020040075af075070001510001000000000000015100000000", "BADTX"], + +["Duplicate inputs"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0x236d0639db62b0773fd8ac34dc85ae19e9aba80a EQUAL"]], +"01000000020001000000000000000000000000000000000000000000000000000000000000000000006c47304402204bb1197053d0d7799bf1b30cd503c44b58d6240cccbdc85b6fe76d087980208f02204beeed78200178ffc6c74237bb74b3f276bbb4098b5605d814304fe128bf1431012321039e8815e15952a7c3fada1905f8cf55419837133bd7756c0ef14fc8dfe50c0deaacffffffff0001000000000000000000000000000000000000000000000000000000000000000000006c47304402202306489afef52a6f62e90bf750bbcdf40c06f5c6b138286e6b6b86176bb9341802200dba98486ea68380f47ebb19a7df173b99e6bc9c681d6ccf3bde31465d1f16b3012321039e8815e15952a7c3fada1905f8cf55419837133bd7756c0ef14fc8dfe50c0deaacffffffff010000000000000000015100000000", "BADTX"], + +["Coinbase of size 1"], +["Note the input is just required to make the tester happy"], +[[["0000000000000000000000000000000000000000000000000000000000000000", -1, "1"]], +"01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0151ffffffff010000000000000000015100000000", "BADTX"], + +["Coinbase of size 101"], +["Note the input is just required to make the tester happy"], +[[["0000000000000000000000000000000000000000000000000000000000000000", -1, "1"]], +"01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff655151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151ffffffff010000000000000000015100000000", "BADTX"], + +["Null txin, but without being a coinbase (because there are two inputs)"], +[[["0000000000000000000000000000000000000000000000000000000000000000", -1, "1"], + ["0000000000000000000000000000000000000000000000000000000000000100", 0, "1"]], +"01000000020000000000000000000000000000000000000000000000000000000000000000ffffffff00ffffffff00010000000000000000000000000000000000000000000000000000000000000000000000ffffffff010000000000000000015100000000", "BADTX"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "1"], + ["0000000000000000000000000000000000000000000000000000000000000000", -1, "1"]], +"010000000200010000000000000000000000000000000000000000000000000000000000000000000000ffffffff0000000000000000000000000000000000000000000000000000000000000000ffffffff00ffffffff010000000000000000015100000000", "BADTX"], + +["Same as the transactions in valid with one input SIGHASH_ALL and one SIGHASH_ANYONECANPAY, but we set the _ANYONECANPAY sequence number, invalidating the SIGHASH_ALL signature"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x21 0x035e7f0d4d0841bcd56c39337ed086b1a633ee770c1ffdd94ac552a95ac2ce0efc CHECKSIG"], + ["0000000000000000000000000000000000000000000000000000000000000200", 0, "0x21 0x035e7f0d4d0841bcd56c39337ed086b1a633ee770c1ffdd94ac552a95ac2ce0efc CHECKSIG"]], + "01000000020001000000000000000000000000000000000000000000000000000000000000000000004948304502203a0f5f0e1f2bdbcd04db3061d18f3af70e07f4f467cbc1b8116f267025f5360b022100c792b6e215afc5afc721a351ec413e714305cb749aae3d7fee76621313418df10101000000000200000000000000000000000000000000000000000000000000000000000000000000484730440220201dc2d030e380e8f9cfb41b442d930fa5a685bb2c8db5906671f865507d0670022018d9e7a8d4c8d86a73c2a724ee38ef983ec249827e0e464841735955c707ece98101000000010100000000000000015100000000", "NONE"], + +["CHECKMULTISIG with incorrect signature order"], +["Note the input is just required to make the tester happy"], +[[["b3da01dd4aae683c7aee4d5d8b52a540a508e1115f77cd7fa9a291243f501223", 0, "HASH160 0x14 0xb1ce99298d5f07364b57b1e5c9cc00be0b04a954 EQUAL"]], +"01000000012312503f2491a2a97fcd775f11e108a540a5528b5d4dee7a3c68ae4add01dab300000000fdfe000048304502207aacee820e08b0b174e248abd8d7a34ed63b5da3abedb99934df9fddd65c05c4022100dfe87896ab5ee3df476c2655f9fbe5bd089dccbef3e4ea05b5d121169fe7f5f401483045022100f6649b0eddfdfd4ad55426663385090d51ee86c3481bdc6b0c18ea6c0ece2c0b0220561c315b07cffa6f7dd9df96dbae9200c2dee09bf93cc35ca05e6cdf613340aa014c695221031d11db38972b712a9fe1fc023577c7ae3ddb4a3004187d41c45121eecfdbb5b7210207ec36911b6ad2382860d32989c7b8728e9489d7bbc94a6b5509ef0029be128821024ea9fac06f666a4adc3fc1357b7bec1fd0bdece2b9d08579226a8ebde53058e453aeffffffff0180380100000000001976a914c9b99cddf847d10685a4fabaa0baf505f7c3dfab88ac00000000", "P2SH"], + + +["The following is a tweaked form of 23b397edccd3740a74adb603c9756370fafcde9bcc4483eb271ecad09a94dd63"], +["It is an OP_CHECKMULTISIG with the dummy value missing"], +[[["60a20bd93aa49ab4b28d514ec10b06e1829ce6818ec06cd3aabd013ebcdc4bb1", 0, "1 0x41 0x04cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4 0x41 0x0461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af 2 OP_CHECKMULTISIG"]], +"0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa43ad90ba260000000004847304402203f16c6f40162ab686621ef3000b04e75418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb60996631bf6bf5bc0a0682c4db743ce7ca2b01ffffffff0140420f00000000001976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000", "NONE"], + + +["CHECKMULTISIG SCRIPT_VERIFY_NULLDUMMY tests:"], + +["The following is a tweaked form of 23b397edccd3740a74adb603c9756370fafcde9bcc4483eb271ecad09a94dd63"], +["It is an OP_CHECKMULTISIG with the dummy value set to something other than an empty string"], +[[["60a20bd93aa49ab4b28d514ec10b06e1829ce6818ec06cd3aabd013ebcdc4bb1", 0, "1 0x41 0x04cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4 0x41 0x0461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af 2 OP_CHECKMULTISIG"]], +"0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa43ad90ba260000000004a010047304402203f16c6f40162ab686621ef3000b04e75418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb60996631bf6bf5bc0a0682c4db743ce7ca2b01ffffffff0140420f00000000001976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000", "NULLDUMMY"], + +["As above, but using an OP_1"], +[[["60a20bd93aa49ab4b28d514ec10b06e1829ce6818ec06cd3aabd013ebcdc4bb1", 0, "1 0x41 0x04cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4 0x41 0x0461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af 2 OP_CHECKMULTISIG"]], +"0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa43ad90ba26000000000495147304402203f16c6f40162ab686621ef3000b04e75418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb60996631bf6bf5bc0a0682c4db743ce7ca2b01ffffffff0140420f00000000001976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000", "NULLDUMMY"], + +["As above, but using an OP_1NEGATE"], +[[["60a20bd93aa49ab4b28d514ec10b06e1829ce6818ec06cd3aabd013ebcdc4bb1", 0, "1 0x41 0x04cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4 0x41 0x0461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af 2 OP_CHECKMULTISIG"]], +"0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa43ad90ba26000000000494f47304402203f16c6f40162ab686621ef3000b04e75418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb60996631bf6bf5bc0a0682c4db743ce7ca2b01ffffffff0140420f00000000001976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000", "NULLDUMMY"], + +["As above, but with the dummy byte missing"], +[[["60a20bd93aa49ab4b28d514ec10b06e1829ce6818ec06cd3aabd013ebcdc4bb1", 0, "1 0x41 0x04cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4 0x41 0x0461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af 2 OP_CHECKMULTISIG"]], +"0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa43ad90ba260000000004847304402203f16c6f40162ab686621ef3000b04e75418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb60996631bf6bf5bc0a0682c4db743ce7ca2b01ffffffff0140420f00000000001976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000", "NONE"], + + +["Empty stack when we try to run CHECKSIG"], +[[["ad503f72c18df5801ee64d76090afe4c607fb2b822e9b7b63c5826c50e22fc3b", 0, "0x21 0x027c3a97665bf283a102a587a62a30a0c102d4d3b141015e2cae6f64e2543113e5 CHECKSIG NOT"]], +"01000000013bfc220ec526583cb6b7e922b8b27f604cfe0a09764de61e80f58dc1723f50ad0000000000ffffffff0101000000000000002321027c3a97665bf283a102a587a62a30a0c102d4d3b141015e2cae6f64e2543113e5ac00000000", "NONE"], + + +["Inverted versions of tx_valid CODESEPARATOR IF block tests"], + +["CODESEPARATOR in an unexecuted IF block does not change what is hashed"], +[[["a955032f4d6b0c9bfe8cad8f00a8933790b9c1dc28c82e0f48e75b35da0e4944", 0, "IF CODESEPARATOR ENDIF 0x21 0x0378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71 CHECKSIGVERIFY CODESEPARATOR 1"]], +"010000000144490eda355be7480f2ec828dcc1b9903793a8008fad8cfe9b0c6b4d2f0355a9000000004a48304502207a6974a77c591fa13dff60cabbb85a0de9e025c09c65a4b2285e47ce8e22f761022100f0efaac9ff8ac36b10721e0aae1fb975c90500b50c56e8a0cc52b0403f0425dd0151ffffffff010000000000000000016a00000000", "NONE"], + +["As above, with the IF block executed"], +[[["a955032f4d6b0c9bfe8cad8f00a8933790b9c1dc28c82e0f48e75b35da0e4944", 0, "IF CODESEPARATOR ENDIF 0x21 0x0378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71 CHECKSIGVERIFY CODESEPARATOR 1"]], +"010000000144490eda355be7480f2ec828dcc1b9903793a8008fad8cfe9b0c6b4d2f0355a9000000004a483045022100fa4a74ba9fd59c59f46c3960cf90cbe0d2b743c471d24a3d5d6db6002af5eebb02204d70ec490fd0f7055a7c45f86514336e3a7f03503dacecabb247fc23f15c83510100ffffffff010000000000000000016a00000000", "NONE"], + +["CHECKLOCKTIMEVERIFY tests"], + +["By-height locks, with argument just beyond tx nLockTime"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "1 CHECKLOCKTIMEVERIFY"]], +"010000000100010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000", "CHECKLOCKTIMEVERIFY"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "499999999 CHECKLOCKTIMEVERIFY"]], +"0100000001000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000fe64cd1d", "CHECKLOCKTIMEVERIFY"], + +["By-time locks, with argument just beyond tx nLockTime (but within numerical boundaries)"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "500000001 CHECKLOCKTIMEVERIFY"]], +"01000000010001000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000065cd1d", "CHECKLOCKTIMEVERIFY"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "4294967295 CHECKLOCKTIMEVERIFY"]], +"0100000001000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000feffffff", "CHECKLOCKTIMEVERIFY"], + +["Argument missing"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "CHECKLOCKTIMEVERIFY 1"]], +"010000000100010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000", "CHECKLOCKTIMEVERIFY"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "1"]], +"010000000100010000000000000000000000000000000000000000000000000000000000000000000001b1010000000100000000000000000000000000", "CHECKLOCKTIMEVERIFY"], + +["Argument negative with by-blockheight nLockTime=0"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "-1 CHECKLOCKTIMEVERIFY"]], +"010000000100010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000", "CHECKLOCKTIMEVERIFY"], + +["Argument negative with by-blocktime nLockTime=500,000,000"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "-1 CHECKLOCKTIMEVERIFY"]], +"01000000010001000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000065cd1d", "CHECKLOCKTIMEVERIFY"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "1"]], +"010000000100010000000000000000000000000000000000000000000000000000000000000000000004005194b1010000000100000000000000000002000000", "CHECKLOCKTIMEVERIFY"], + +["Input locked"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0 CHECKLOCKTIMEVERIFY 1"]], +"010000000100010000000000000000000000000000000000000000000000000000000000000000000000ffffffff0100000000000000000000000000", "CHECKLOCKTIMEVERIFY"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0"]], +"01000000010001000000000000000000000000000000000000000000000000000000000000000000000251b1ffffffff0100000000000000000002000000", "NONE"], + +["Another input being unlocked isn't sufficient; the CHECKLOCKTIMEVERIFY-using input must be unlocked"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0 CHECKLOCKTIMEVERIFY 1"] , + ["0000000000000000000000000000000000000000000000000000000000000200", 1, "1"]], +"010000000200010000000000000000000000000000000000000000000000000000000000000000000000ffffffff00020000000000000000000000000000000000000000000000000000000000000100000000000000000100000000000000000000000000", "CHECKLOCKTIMEVERIFY"], + +["Argument/tx height/time mismatch, both versions"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0 CHECKLOCKTIMEVERIFY 1"]], +"01000000010001000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000065cd1d", "CHECKLOCKTIMEVERIFY"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0"]], +"01000000010001000000000000000000000000000000000000000000000000000000000000000000000251b100000000010000000000000000000065cd1d", "NONE"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "499999999 CHECKLOCKTIMEVERIFY"]], +"01000000010001000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000065cd1d", "CHECKLOCKTIMEVERIFY"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "500000000 CHECKLOCKTIMEVERIFY"]], +"010000000100010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000", "CHECKLOCKTIMEVERIFY"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "500000000 CHECKLOCKTIMEVERIFY"]], +"0100000001000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000ff64cd1d", "CHECKLOCKTIMEVERIFY"], + +["Argument 2^32 with nLockTime=2^32-1"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x050000000001 CHECKLOCKTIMEVERIFY"]], +"0100000001000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000ffffffff", "CHECKLOCKTIMEVERIFY"], + +["Same, but with nLockTime=2^31-1"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "2147483648 CHECKLOCKTIMEVERIFY"]], +"0100000001000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000ffffff7f", "CHECKLOCKTIMEVERIFY"], + +["6 byte non-minimally-encoded arguments are invalid even if their contents are valid"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x06 0x000000000000 CHECKLOCKTIMEVERIFY 1"]], +"010000000100010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000", "CHECKLOCKTIMEVERIFY"], + +["Failure due to failing CHECKLOCKTIMEVERIFY in scriptSig"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "1"]], +"01000000010001000000000000000000000000000000000000000000000000000000000000000000000251b1000000000100000000000000000000000000", "CHECKLOCKTIMEVERIFY"], + +["Failure due to failing CHECKLOCKTIMEVERIFY in redeemScript"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0xc5b93064159b3b2d6ab506a41b1f50463771b988 EQUAL"]], +"0100000001000100000000000000000000000000000000000000000000000000000000000000000000030251b1000000000100000000000000000000000000", "P2SH,CHECKLOCKTIMEVERIFY"], + +["A transaction with a non-standard DER signature."], +[[["b1dbc81696c8a9c0fccd0693ab66d7c368dbc38c0def4e800685560ddd1b2132", 0, "DUP HASH160 0x14 0x4b3bd7eba3bc0284fd3007be7f3be275e94f5826 EQUALVERIFY CHECKSIG"]], +"010000000132211bdd0d568506804eef0d8cc3db68c3d766ab9306cdfcc0a9c89616c8dbb1000000006c493045022100c7bb0faea0522e74ff220c20c022d2cb6033f8d167fb89e75a50e237a35fd6d202203064713491b1f8ad5f79e623d0219ad32510bfaa1009ab30cbee77b59317d6e30001210237af13eb2d84e4545af287b919c2282019c9691cc509e78e196a9d8274ed1be0ffffffff0100000000000000001976a914f1b3ed2eda9a2ebe5a9374f692877cdf87c0f95b88ac00000000", "DERSIG"], + +["CHECKSEQUENCEVERIFY tests"], + +["By-height locks, with argument just beyond txin.nSequence"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "1 CHECKSEQUENCEVERIFY"]], +"020000000100010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000", "CHECKSEQUENCEVERIFY"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "4259839 CHECKSEQUENCEVERIFY"]], +"020000000100010000000000000000000000000000000000000000000000000000000000000000000000feff40000100000000000000000000000000", "CHECKSEQUENCEVERIFY"], + +["By-time locks, with argument just beyond txin.nSequence (but within numerical boundaries)"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "4194305 CHECKSEQUENCEVERIFY"]], +"020000000100010000000000000000000000000000000000000000000000000000000000000000000000000040000100000000000000000000000000", "CHECKSEQUENCEVERIFY"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "4259839 CHECKSEQUENCEVERIFY"]], +"020000000100010000000000000000000000000000000000000000000000000000000000000000000000feff40000100000000000000000000000000", "CHECKSEQUENCEVERIFY"], + +["Argument missing"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "CHECKSEQUENCEVERIFY 1"]], +"020000000100010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000", "CHECKSEQUENCEVERIFY"], + +["Argument negative with by-blockheight txin.nSequence=0"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "-1 CHECKSEQUENCEVERIFY"]], +"020000000100010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000", "CHECKSEQUENCEVERIFY"], + +["Argument negative with by-blocktime txin.nSequence=CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "-1 CHECKSEQUENCEVERIFY"]], +"020000000100010000000000000000000000000000000000000000000000000000000000000000000000000040000100000000000000000000000000", "CHECKSEQUENCEVERIFY"], + +["Argument/tx height/time mismatch, both versions"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0 CHECKSEQUENCEVERIFY 1"]], +"020000000100010000000000000000000000000000000000000000000000000000000000000000000000000040000100000000000000000000000000", "CHECKSEQUENCEVERIFY"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "65535 CHECKSEQUENCEVERIFY"]], +"020000000100010000000000000000000000000000000000000000000000000000000000000000000000000040000100000000000000000000000000", "CHECKSEQUENCEVERIFY"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "4194304 CHECKSEQUENCEVERIFY"]], +"020000000100010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000", "CHECKSEQUENCEVERIFY"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "4259839 CHECKSEQUENCEVERIFY"]], +"020000000100010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000", "CHECKSEQUENCEVERIFY"], + +["6 byte non-minimally-encoded arguments are invalid even if their contents are valid"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x06 0x000000000000 CHECKSEQUENCEVERIFY 1"]], +"020000000100010000000000000000000000000000000000000000000000000000000000000000000000ffff00000100000000000000000000000000", "CHECKSEQUENCEVERIFY"], + +["Failure due to failing CHECKSEQUENCEVERIFY in scriptSig"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "1"]], +"02000000010001000000000000000000000000000000000000000000000000000000000000000000000251b2000000000100000000000000000000000000", "CHECKSEQUENCEVERIFY"], + +["Failure due to failing CHECKSEQUENCEVERIFY in redeemScript"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0x7c17aff532f22beb54069942f9bf567a66133eaf EQUAL"]], +"0200000001000100000000000000000000000000000000000000000000000000000000000000000000030251b2000000000100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"], + +["Failure due to insufficient tx.version (<2)"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0 CHECKSEQUENCEVERIFY 1"]], +"010000000100010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000", "CHECKSEQUENCEVERIFY"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "4194304 CHECKSEQUENCEVERIFY"]], +"010000000100010000000000000000000000000000000000000000000000000000000000000000000000000040000100000000000000000000000000", "CHECKSEQUENCEVERIFY"], + +["Unknown witness program version (with DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM)"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x51", 1000], +["0000000000000000000000000000000000000000000000000000000000000100", 1, "0x60 0x14 0x4c9c3dfac4207d5d8cb89df5722cb3d712385e3f", 2000], +["0000000000000000000000000000000000000000000000000000000000000100", 2, "0x51", 3000]], +"0100000000010300010000000000000000000000000000000000000000000000000000000000000000000000ffffffff00010000000000000000000000000000000000000000000000000000000000000100000000ffffffff00010000000000000000000000000000000000000000000000000000000000000200000000ffffffff03e8030000000000000151d0070000000000000151b80b00000000000001510002483045022100a3cec69b52cba2d2de623ffffffffff1606184ea55476c0f8189fda231bc9cbb022003181ad597f7c380a7d1c740286b1d022b8b04ded028b833282e055e03b8efef812103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc710000000000", "P2SH,WITNESS,DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM"], + +["Unknown length for witness program v0"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x51", 1000], +["0000000000000000000000000000000000000000000000000000000000000100", 1, "0x00 0x15 0x4c9c3dfac4207d5d8cb89df5722cb3d712385e3fff", 2000], +["0000000000000000000000000000000000000000000000000000000000000100", 2, "0x51", 3000]], +"0100000000010300010000000000000000000000000000000000000000000000000000000000000000000000ffffffff00010000000000000000000000000000000000000000000000000000000000000100000000ffffffff00010000000000000000000000000000000000000000000000000000000000000200000000ffffffff04b60300000000000001519e070000000000000151860b0000000000000100960000000000000001510002473044022022fceb54f62f8feea77faac7083c3b56c4676a78f93745adc8a35800bc36adfa022026927df9abcf0a8777829bcfcce3ff0a385fa54c3f9df577405e3ef24ee56479022103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc710000000000", "P2SH,WITNESS"], + +["Witness with SigHash Single|AnyoneCanPay (same index output value changed)"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x51", 1000], +["0000000000000000000000000000000000000000000000000000000000000100", 1, "0x00 0x14 0x4c9c3dfac4207d5d8cb89df5722cb3d712385e3f", 2000], +["0000000000000000000000000000000000000000000000000000000000000100", 2, "0x51", 3000]], +"0100000000010300010000000000000000000000000000000000000000000000000000000000000000000000ffffffff00010000000000000000000000000000000000000000000000000000000000000100000000ffffffff00010000000000000000000000000000000000000000000000000000000000000200000000ffffffff03e80300000000000001516c070000000000000151b80b0000000000000151000248304502210092f4777a0f17bf5aeb8ae768dec5f2c14feabf9d1fe2c89c78dfed0f13fdb86902206da90a86042e252bcd1e80a168c719e4a1ddcc3cebea24b9812c5453c79107e9832103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc710000000000", "P2SH,WITNESS"], + +["Witness with SigHash None|AnyoneCanPay (input sequence changed)"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x51", 1000], +["0000000000000000000000000000000000000000000000000000000000000100", 1, "0x00 0x14 0x4c9c3dfac4207d5d8cb89df5722cb3d712385e3f", 2000], +["0000000000000000000000000000000000000000000000000000000000000100", 2, "0x51", 3000]], +"0100000000010300010000000000000000000000000000000000000000000000000000000000000000000000ffffffff000100000000000000000000000000000000000000000000000000000000000001000000000100000000010000000000000000000000000000000000000000000000000000000000000200000000ffffffff03e8030000000000000151d0070000000000000151b80b0000000000000151000248304502210091b32274295c2a3fa02f5bce92fb2789e3fc6ea947fbe1a76e52ea3f4ef2381a022079ad72aefa3837a2e0c033a8652a59731da05fa4a813f4fc48e87c075037256b822103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc710000000000", "P2SH,WITNESS"], + +["Witness with SigHash All|AnyoneCanPay (third output value changed)"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x51", 1000], +["0000000000000000000000000000000000000000000000000000000000000100", 1, "0x00 0x14 0x4c9c3dfac4207d5d8cb89df5722cb3d712385e3f", 2000], +["0000000000000000000000000000000000000000000000000000000000000100", 2, "0x51", 3000]], +"0100000000010300010000000000000000000000000000000000000000000000000000000000000000000000ffffffff00010000000000000000000000000000000000000000000000000000000000000100000000ffffffff00010000000000000000000000000000000000000000000000000000000000000200000000ffffffff03e8030000000000000151d0070000000000000151540b00000000000001510002483045022100a3cec69b52cba2d2de623eeef89e0ba1606184ea55476c0f8189fda231bc9cbb022003181ad597f7c380a7d1c740286b1d022b8b04ded028b833282e055e03b8efef812103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc710000000000", "P2SH,WITNESS"], + +["Witness with a push of 521 bytes"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x00 0x20 0x33198a9bfef674ebddb9ffaa52928017b8472791e54c609cb95f278ac6b1e349", 1000]], +"0100000000010100010000000000000000000000000000000000000000000000000000000000000000000000ffffffff010000000000000000015102fd0902000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002755100000000", "P2SH,WITNESS"], + +["Witness with unknown version which push false on the stack should be invalid (even without DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM)"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x60 0x02 0x0000", 2000]], +"0100000000010100010000000000000000000000000000000000000000000000000000000000000000000000ffffffff010000000000000000015101010100000000", "NONE"], + +["Witness program should leave clean stack"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x00 0x20 0x2f04a3aa051f1f60d695f6c44c0c3d383973dfd446ace8962664a76bb10e31a8", 2000]], +"0100000000010100010000000000000000000000000000000000000000000000000000000000000000000000ffffffff01000000000000000001510102515100000000", "P2SH,WITNESS"], + +["Witness v0 with a push of 2 bytes"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x00 0x02 0x0001", 2000]], +"0100000000010100010000000000000000000000000000000000000000000000000000000000000000000000ffffffff010000000000000000015101040002000100000000", "P2SH,WITNESS"], + +["Unknown witness version with non empty scriptSig"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x60 0x02 0x0001", 2000]], +"01000000010001000000000000000000000000000000000000000000000000000000000000000000000151ffffffff010000000000000000015100000000", "P2SH,WITNESS"], + +["Non witness Single|AnyoneCanPay hash input's position (permutation)"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x21 0x03596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc71 CHECKSIG", 1000], +["0000000000000000000000000000000000000000000000000000000000000100", 1, "0x21 0x03596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc71 CHECKSIG", 1001]], +"010000000200010000000000000000000000000000000000000000000000000000000000000100000049483045022100acb96cfdbda6dc94b489fd06f2d720983b5f350e31ba906cdbd800773e80b21c02200d74ea5bdf114212b4bbe9ed82c36d2e369e302dff57cb60d01c428f0bd3daab83ffffffff0001000000000000000000000000000000000000000000000000000000000000000000004847304402202a0b4b1294d70540235ae033d78e64b4897ec859c7b6f1b2b1d8a02e1d46006702201445e756d2254b0f1dfda9ab8e1e1bc26df9668077403204f32d16a49a36eb6983ffffffff02e9030000000000000151e803000000000000015100000000", "NONE"], + +["P2WSH with a redeem representing a witness scriptPubKey should fail due to too many stack items"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x00 0x20 0x34b6c399093e06cf9f0f7f660a1abcfe78fcf7b576f43993208edd9518a0ae9b", 1000]], +"0100000000010100010000000000000000000000000000000000000000000000000000000000000000000000ffffffff01e803000000000000015101045102010100000000", "P2SH,WITNESS"], + +["P2WSH with an empty redeem should fail due to empty stack"], +[[["3d4da21b04a67a54c8a58df1c53a0534b0a7f0864fb3d19abd43b8f6934e785f", 0, "0x00 0x20 0xe3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", 1337]], +"020000000001015f784e93f6b843bd9ad1b34f86f0a7b034053ac5f18da5c8547aa6041ba24d3d0000000000ffffffff013905000000000000220020e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855010000000000", "P2SH,WITNESS"], + +["33 bytes push should be considered a witness scriptPubKey"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x60 0x21 0xff25429251b5a84f452230a3c75fd886b7fc5a7865ce4a7bb7a9d7c5be6da3dbff", 1000]], +"010000000100010000000000000000000000000000000000000000000000000000000000000000000000ffffffff01e803000000000000015100000000", "P2SH,WITNESS,DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM"], + +["FindAndDelete tests"], +["This is a test of FindAndDelete. The first tx is a spend of normal scriptPubKey and the second tx is a spend of bare P2WSH."], +["The redeemScript/witnessScript is CHECKSIGVERIFY <0x30450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e01>."], +["The signature is <0x30450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e01> ,"], +["where the pubkey is obtained through key recovery with sig and the wrong sighash."], +["This is to show that FindAndDelete is applied only to non-segwit scripts"], +["To show that the tests are 'correctly wrong', they should pass by modifying OP_CHECKSIG under interpreter.cpp"], +["by replacing (sigversion == SigVersion::BASE) with (sigversion != SigVersion::BASE)"], +["Non-segwit: wrong sighash (without FindAndDelete) = 1ba1fe3bc90c5d1265460e684ce6774e324f0fabdf67619eda729e64e8b6bc08"], +[[["f18783ace138abac5d3a7a5cf08e88fe6912f267ef936452e0c27d090621c169", 7000, "HASH160 0x14 0x0c746489e2d83cdbb5b90b432773342ba809c134 EQUAL", 200000]], +"010000000169c12106097dc2e0526493ef67f21269fe888ef05c7a3a5dacab38e1ac8387f1581b0000b64830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e012103b12a1ec8428fc74166926318c15e17408fea82dbb157575e16a8c365f546248f4aad4830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e01ffffffff0101000000000000000000000000", "P2SH"], +["BIP143: wrong sighash (with FindAndDelete) = 71c9cd9b2869b9c70b01b1f0360c148f42dee72297db312638df136f43311f23"], +[[["f18783ace138abac5d3a7a5cf08e88fe6912f267ef936452e0c27d090621c169", 7500, "0x00 0x20 0x9e1be07558ea5cc8e02ed1d80c0911048afad949affa36d5c3951e3159dbea19", 200000]], +"0100000000010169c12106097dc2e0526493ef67f21269fe888ef05c7a3a5dacab38e1ac8387f14c1d000000ffffffff01010000000000000000034830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e012102a9d7ed6e161f0e255c10bbfcca0128a9e2035c2c8da58899c54d22d3a31afdef4aad4830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0100000000", "P2SH,WITNESS"], +["This is multisig version of the FindAndDelete tests"], +["Script is 2 CHECKMULTISIGVERIFY DROP"], +["52af4830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0148304502205286f726690b2e9b0207f0345711e63fa7012045b9eb0f19c2458ce1db90cf43022100e89f17f86abc5b149eba4115d4f128bcf45d77fb3ecdd34f594091340c0395960175"], +["Signature is 0 2 "], +["Should pass by replacing (sigversion == SigVersion::BASE) with (sigversion != SigVersion::BASE) under OP_CHECKMULTISIG"], +["Non-segwit: wrong sighash (without FindAndDelete) = 4bc6a53e8e16ef508c19e38bba08831daba85228b0211f323d4cb0999cf2a5e8"], +[[["9628667ad48219a169b41b020800162287d2c0f713c04157e95c484a8dcb7592", 7000, "HASH160 0x14 0x5748407f5ca5cdca53ba30b79040260770c9ee1b EQUAL", 200000]], +"01000000019275cb8d4a485ce95741c013f7c0d28722160008021bb469a11982d47a662896581b0000fd6f01004830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0148304502205286f726690b2e9b0207f0345711e63fa7012045b9eb0f19c2458ce1db90cf43022100e89f17f86abc5b149eba4115d4f128bcf45d77fb3ecdd34f594091340c039596015221023fd5dd42b44769c5653cbc5947ff30ab8871f240ad0c0e7432aefe84b5b4ff3421039d52178dbde360b83f19cf348deb04fa8360e1bf5634577be8e50fafc2b0e4ef4c9552af4830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0148304502205286f726690b2e9b0207f0345711e63fa7012045b9eb0f19c2458ce1db90cf43022100e89f17f86abc5b149eba4115d4f128bcf45d77fb3ecdd34f594091340c0395960175ffffffff0101000000000000000000000000", "P2SH"], +["BIP143: wrong sighash (with FindAndDelete) = 17c50ec2181ecdfdc85ca081174b248199ba81fff730794d4f69b8ec031f2dce"], +[[["9628667ad48219a169b41b020800162287d2c0f713c04157e95c484a8dcb7592", 7500, "0x00 0x20 0x9b66c15b4e0b4eb49fa877982cafded24859fe5b0e2dbfbe4f0df1de7743fd52", 200000]], +"010000000001019275cb8d4a485ce95741c013f7c0d28722160008021bb469a11982d47a6628964c1d000000ffffffff0101000000000000000007004830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0148304502205286f726690b2e9b0207f0345711e63fa7012045b9eb0f19c2458ce1db90cf43022100e89f17f86abc5b149eba4115d4f128bcf45d77fb3ecdd34f594091340c03959601010221023cb6055f4b57a1580c5a753e19610cafaedf7e0ff377731c77837fd666eae1712102c1b1db303ac232ffa8e5e7cc2cf5f96c6e40d3e6914061204c0541cb2043a0969552af4830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0148304502205286f726690b2e9b0207f0345711e63fa7012045b9eb0f19c2458ce1db90cf43022100e89f17f86abc5b149eba4115d4f128bcf45d77fb3ecdd34f594091340c039596017500000000", "P2SH,WITNESS"], + +["SCRIPT_VERIFY_CONST_SCRIPTCODE tests"], +["All transactions are copied from OP_CODESEPARATOR tests in tx_valid.json"], + +[[["bc7fd132fcf817918334822ee6d9bd95c889099c96e07ca2c1eb2cc70db63224", 0, "CODESEPARATOR 0x21 0x038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041 CHECKSIG"]], + "01000000012432b60dc72cebc1a27ce0969c0989c895bdd9e62e8234839117f8fc32d17fbc000000004a493046022100a576b52051962c25e642c0fd3d77ee6c92487048e5d90818bcf5b51abaccd7900221008204f8fb121be4ec3b24483b1f92d89b1b0548513a134e345c5442e86e8617a501ffffffff010000000000000000016a00000000", "CONST_SCRIPTCODE"], +[[["83e194f90b6ef21fa2e3a365b63794fb5daa844bdc9b25de30899fcfe7b01047", 0, "CODESEPARATOR CODESEPARATOR 0x21 0x038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041 CHECKSIG"]], + "01000000014710b0e7cf9f8930de259bdc4b84aa5dfb9437b665a3e3a21ff26e0bf994e183000000004a493046022100a166121a61b4eeb19d8f922b978ff6ab58ead8a5a5552bf9be73dc9c156873ea02210092ad9bc43ee647da4f6652c320800debcf08ec20a094a0aaf085f63ecb37a17201ffffffff010000000000000000016a00000000", "CONST_SCRIPTCODE"], + +[[["326882a7f22b5191f1a0cc9962ca4b878cd969cf3b3a70887aece4d801a0ba5e", 0, "0x21 0x038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041 CODESEPARATOR CHECKSIG"]], + "01000000015ebaa001d8e4ec7a88703a3bcf69d98c874bca6299cca0f191512bf2a7826832000000004948304502203bf754d1c6732fbf87c5dcd81258aefd30f2060d7bd8ac4a5696f7927091dad1022100f5bcb726c4cf5ed0ed34cc13dadeedf628ae1045b7cb34421bc60b89f4cecae701ffffffff010000000000000000016a00000000", "CONST_SCRIPTCODE"], + +[[["a955032f4d6b0c9bfe8cad8f00a8933790b9c1dc28c82e0f48e75b35da0e4944", 0, "0x21 0x038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041 CHECKSIGVERIFY CODESEPARATOR 0x21 0x038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041 CHECKSIGVERIFY CODESEPARATOR 1"]], + "010000000144490eda355be7480f2ec828dcc1b9903793a8008fad8cfe9b0c6b4d2f0355a900000000924830450221009c0a27f886a1d8cb87f6f595fbc3163d28f7a81ec3c4b252ee7f3ac77fd13ffa02203caa8dfa09713c8c4d7ef575c75ed97812072405d932bd11e6a1593a98b679370148304502201e3861ef39a526406bad1e20ecad06be7375ad40ddb582c9be42d26c3a0d7b240221009d0a3985e96522e59635d19cc4448547477396ce0ef17a58e7d74c3ef464292301ffffffff010000000000000000016a00000000", "CONST_SCRIPTCODE"], + +["CODESEPARATOR in an unexecuted IF block is still invalid"], +[[["a955032f4d6b0c9bfe8cad8f00a8933790b9c1dc28c82e0f48e75b35da0e4944", 0, "IF CODESEPARATOR ENDIF 0x21 0x0378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71 CHECKSIGVERIFY CODESEPARATOR 1"]], + "010000000144490eda355be7480f2ec828dcc1b9903793a8008fad8cfe9b0c6b4d2f0355a9000000004a48304502207a6974a77c591fa13dff60cabbb85a0de9e025c09c65a4b2285e47ce8e22f761022100f0efaac9ff8ac36b10721e0aae1fb975c90500b50c56e8a0cc52b0403f0425dd0100ffffffff010000000000000000016a00000000", "CONST_SCRIPTCODE"], + +["CODESEPARATOR in an executed IF block is invalid"], +[[["a955032f4d6b0c9bfe8cad8f00a8933790b9c1dc28c82e0f48e75b35da0e4944", 0, "IF CODESEPARATOR ENDIF 0x21 0x0378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71 CHECKSIGVERIFY CODESEPARATOR 1"]], + "010000000144490eda355be7480f2ec828dcc1b9903793a8008fad8cfe9b0c6b4d2f0355a9000000004a483045022100fa4a74ba9fd59c59f46c3960cf90cbe0d2b743c471d24a3d5d6db6002af5eebb02204d70ec490fd0f7055a7c45f86514336e3a7f03503dacecabb247fc23f15c83510151ffffffff010000000000000000016a00000000", "CONST_SCRIPTCODE"], + + +["Using CHECKSIG with signatures in scriptSigs will trigger FindAndDelete, which is invalid"], +[[["ccf7f4053a02e653c36ac75c891b7496d0dc5ce5214f6c913d9cf8f1329ebee0", 0, "DUP HASH160 0x14 0xee5a6aa40facefb2655ac23c0c28c57c65c41f9b EQUALVERIFY CHECKSIG"]], + "0100000001e0be9e32f1f89c3d916c4f21e55cdcd096741b895cc76ac353e6023a05f4f7cc00000000d86149304602210086e5f736a2c3622ebb62bd9d93d8e5d76508b98be922b97160edc3dcca6d8c47022100b23c312ac232a4473f19d2aeb95ab7bdf2b65518911a0d72d50e38b5dd31dc820121038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041ac4730440220508fa761865c8abd81244a168392876ee1d94e8ed83897066b5e2df2400dad24022043f5ee7538e87e9c6aef7ef55133d3e51da7cc522830a9c4d736977a76ef755c0121038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041ffffffff010000000000000000016a00000000", "CONST_SCRIPTCODE"], + +["OP_CODESEPARATOR in scriptSig is invalid"], +[[["10c9f0effe83e97f80f067de2b11c6a00c3088a4bce42c5ae761519af9306f3c", 1, "DUP HASH160 0x14 0xee5a6aa40facefb2655ac23c0c28c57c65c41f9b EQUALVERIFY CHECKSIG"]], + "01000000013c6f30f99a5161e75a2ce4bca488300ca0c6112bde67f0807fe983feeff0c91001000000e608646561646265656675ab61493046022100ce18d384221a731c993939015e3d1bcebafb16e8c0b5b5d14097ec8177ae6f28022100bcab227af90bab33c3fe0a9abfee03ba976ee25dc6ce542526e9b2e56e14b7f10121038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041ac493046022100c3b93edcc0fd6250eb32f2dd8a0bba1754b0f6c3be8ed4100ed582f3db73eba2022100bf75b5bd2eff4d6bf2bda2e34a40fcc07d4aa3cf862ceaa77b47b81eff829f9a01ab21038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041ffffffff010000000000000000016a00000000", "CONST_SCRIPTCODE"], + +["Again, FindAndDelete() in scriptSig"], +[[["6056ebd549003b10cbbd915cea0d82209fe40b8617104be917a26fa92cbe3d6f", 0, "DUP HASH160 0x14 0xee5a6aa40facefb2655ac23c0c28c57c65c41f9b EQUALVERIFY CHECKSIG"]], + "01000000016f3dbe2ca96fa217e94b1017860be49f20820dea5c91bdcb103b0049d5eb566000000000fd1d0147304402203989ac8f9ad36b5d0919d97fa0a7f70c5272abee3b14477dc646288a8b976df5022027d19da84a066af9053ad3d1d7459d171b7e3a80bc6c4ef7a330677a6be548140147304402203989ac8f9ad36b5d0919d97fa0a7f70c5272abee3b14477dc646288a8b976df5022027d19da84a066af9053ad3d1d7459d171b7e3a80bc6c4ef7a330677a6be548140121038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041ac47304402203757e937ba807e4a5da8534c17f9d121176056406a6465054bdd260457515c1a02200f02eccf1bec0f3a0d65df37889143c2e88ab7acec61a7b6f5aa264139141a2b0121038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041ffffffff010000000000000000016a00000000", "CONST_SCRIPTCODE"], + +[[["5a6b0021a6042a686b6b94abc36b387bef9109847774e8b1e51eb8cc55c53921", 1, "DUP HASH160 0x14 0xee5a6aa40facefb2655ac23c0c28c57c65c41f9b EQUALVERIFY CHECKSIG"]], + "01000000012139c555ccb81ee5b1e87477840991ef7b386bc3ab946b6b682a04a621006b5a01000000fdb40148304502201723e692e5f409a7151db386291b63524c5eb2030df652b1f53022fd8207349f022100b90d9bbf2f3366ce176e5e780a00433da67d9e5c79312c6388312a296a5800390148304502201723e692e5f409a7151db386291b63524c5eb2030df652b1f53022fd8207349f022100b90d9bbf2f3366ce176e5e780a00433da67d9e5c79312c6388312a296a5800390121038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f2204148304502201723e692e5f409a7151db386291b63524c5eb2030df652b1f53022fd8207349f022100b90d9bbf2f3366ce176e5e780a00433da67d9e5c79312c6388312a296a5800390175ac4830450220646b72c35beeec51f4d5bc1cbae01863825750d7f490864af354e6ea4f625e9c022100f04b98432df3a9641719dbced53393022e7249fb59db993af1118539830aab870148304502201723e692e5f409a7151db386291b63524c5eb2030df652b1f53022fd8207349f022100b90d9bbf2f3366ce176e5e780a00433da67d9e5c79312c6388312a296a580039017521038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041ffffffff010000000000000000016a00000000", "CONST_SCRIPTCODE"], + +["FindAndDelete() in redeemScript"], +[[["b5b598de91787439afd5938116654e0b16b7a0d0f82742ba37564219c5afcbf9", 0, "DUP HASH160 0x14 0xf6f365c40f0739b61de827a44751e5e99032ed8f EQUALVERIFY CHECKSIG"], + ["ab9805c6d57d7070d9a42c5176e47bb705023e6b67249fb6760880548298e742", 0, "HASH160 0x14 0xd8dacdadb7462ae15cd906f1878706d0da8660e6 EQUAL"]], + "0100000002f9cbafc519425637ba4227f8d0a0b7160b4e65168193d5af39747891de98b5b5000000006b4830450221008dd619c563e527c47d9bd53534a770b102e40faa87f61433580e04e271ef2f960220029886434e18122b53d5decd25f1f4acb2480659fea20aabd856987ba3c3907e0121022b78b756e2258af13779c1a1f37ea6800259716ca4b7f0b87610e0bf3ab52a01ffffffff42e7988254800876b69f24676b3e0205b77be476512ca4d970707dd5c60598ab00000000fd260100483045022015bd0139bcccf990a6af6ec5c1c52ed8222e03a0d51c334df139968525d2fcd20221009f9efe325476eb64c3958e4713e9eefe49bf1d820ed58d2112721b134e2a1a53034930460221008431bdfa72bc67f9d41fe72e94c88fb8f359ffa30b33c72c121c5a877d922e1002210089ef5fc22dd8bfc6bf9ffdb01a9862d27687d424d1fefbab9e9c7176844a187a014c9052483045022015bd0139bcccf990a6af6ec5c1c52ed8222e03a0d51c334df139968525d2fcd20221009f9efe325476eb64c3958e4713e9eefe49bf1d820ed58d2112721b134e2a1a5303210378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71210378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c7153aeffffffff01a08601000000000017a914d8dacdadb7462ae15cd906f1878706d0da8660e68700000000", "P2SH,CONST_SCRIPTCODE"], + +["FindAndDelete() in bare CHECKMULTISIG"], +[[["ceafe58e0f6e7d67c0409fbbf673c84c166e3c5d3c24af58f7175b18df3bb3db", 0, "DUP HASH160 0x14 0xf6f365c40f0739b61de827a44751e5e99032ed8f EQUALVERIFY CHECKSIG"], + ["ceafe58e0f6e7d67c0409fbbf673c84c166e3c5d3c24af58f7175b18df3bb3db", 1, "2 0x48 0x3045022015bd0139bcccf990a6af6ec5c1c52ed8222e03a0d51c334df139968525d2fcd20221009f9efe325476eb64c3958e4713e9eefe49bf1d820ed58d2112721b134e2a1a5303 0x21 0x0378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71 0x21 0x0378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71 3 CHECKMULTISIG"]], + "0100000002dbb33bdf185b17f758af243c5d3c6e164cc873f6bb9f40c0677d6e0f8ee5afce000000006b4830450221009627444320dc5ef8d7f68f35010b4c050a6ed0d96b67a84db99fda9c9de58b1e02203e4b4aaa019e012e65d69b487fdf8719df72f488fa91506a80c49a33929f1fd50121022b78b756e2258af13779c1a1f37ea6800259716ca4b7f0b87610e0bf3ab52a01ffffffffdbb33bdf185b17f758af243c5d3c6e164cc873f6bb9f40c0677d6e0f8ee5afce010000009300483045022015bd0139bcccf990a6af6ec5c1c52ed8222e03a0d51c334df139968525d2fcd20221009f9efe325476eb64c3958e4713e9eefe49bf1d820ed58d2112721b134e2a1a5303483045022015bd0139bcccf990a6af6ec5c1c52ed8222e03a0d51c334df139968525d2fcd20221009f9efe325476eb64c3958e4713e9eefe49bf1d820ed58d2112721b134e2a1a5303ffffffff01a0860100000000001976a9149bc0bbdd3024da4d0c38ed1aecf5c68dd1d3fa1288ac00000000", "CONST_SCRIPTCODE"], + +["Make diffs cleaner by leaving a comment here without comma at the end"] +] diff --git a/crates/script_verify/tests/data/tx_valid.json b/crates/script_verify/tests/data/tx_valid.json new file mode 100644 index 0000000000..ac25f8149b --- /dev/null +++ b/crates/script_verify/tests/data/tx_valid.json @@ -0,0 +1,528 @@ +[ +["The following are deserialized transactions which are valid."], +["They are in the form"], +["[[[prevout hash, prevout index, prevout scriptPubKey, amount?], [input 2], ...],"], +["serializedTransaction, excluded verifyFlags]"], +["Objects that are only a single string (like this one) are ignored"], + +["The following is 23b397edccd3740a74adb603c9756370fafcde9bcc4483eb271ecad09a94dd63"], +["It is of particular interest because it contains an invalidly-encoded signature which OpenSSL accepts"], +["See https://r6.ca/blog/20111119T211504Z.html"], +["It is also the first OP_CHECKMULTISIG transaction in standard form"], +[[["60a20bd93aa49ab4b28d514ec10b06e1829ce6818ec06cd3aabd013ebcdc4bb1", 0, "1 0x41 0x04cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4 0x41 0x0461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af 2 OP_CHECKMULTISIG"]], +"0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa43ad90ba26000000000490047304402203f16c6f40162ab686621ef3000b04e75418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb60996631bf6bf5bc0a0682c4db743ce7ca2b01ffffffff0140420f00000000001976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000", "DERSIG,LOW_S,STRICTENC"], + +["The following is a tweaked form of 23b397edccd3740a74adb603c9756370fafcde9bcc4483eb271ecad09a94dd63"], +["It is an OP_CHECKMULTISIG with an arbitrary extra byte stuffed into the signature at pos length - 2"], +["The dummy byte is fine however, so the NULLDUMMY flag should be happy"], +[[["60a20bd93aa49ab4b28d514ec10b06e1829ce6818ec06cd3aabd013ebcdc4bb1", 0, "1 0x41 0x04cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4 0x41 0x0461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af 2 OP_CHECKMULTISIG"]], +"0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa43ad90ba260000000004a0048304402203f16c6f40162ab686621ef3000b04e75418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb60996631bf6bf5bc0a0682c4db743ce7ca2bab01ffffffff0140420f00000000001976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000", "DERSIG,LOW_S,STRICTENC"], + +["The following is a tweaked form of 23b397edccd3740a74adb603c9756370fafcde9bcc4483eb271ecad09a94dd63"], +["It is an OP_CHECKMULTISIG with the dummy value set to something other than an empty string"], +[[["60a20bd93aa49ab4b28d514ec10b06e1829ce6818ec06cd3aabd013ebcdc4bb1", 0, "1 0x41 0x04cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4 0x41 0x0461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af 2 OP_CHECKMULTISIG"]], +"0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa43ad90ba260000000004a01ff47304402203f16c6f40162ab686621ef3000b04e75418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb60996631bf6bf5bc0a0682c4db743ce7ca2b01ffffffff0140420f00000000001976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000", "DERSIG,LOW_S,STRICTENC,NULLDUMMY"], + +["As above, but using an OP_1"], +[[["60a20bd93aa49ab4b28d514ec10b06e1829ce6818ec06cd3aabd013ebcdc4bb1", 0, "1 0x41 0x04cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4 0x41 0x0461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af 2 OP_CHECKMULTISIG"]], +"0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa43ad90ba26000000000495147304402203f16c6f40162ab686621ef3000b04e75418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb60996631bf6bf5bc0a0682c4db743ce7ca2b01ffffffff0140420f00000000001976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000", "DERSIG,LOW_S,STRICTENC,NULLDUMMY"], + +["As above, but using an OP_1NEGATE"], +[[["60a20bd93aa49ab4b28d514ec10b06e1829ce6818ec06cd3aabd013ebcdc4bb1", 0, "1 0x41 0x04cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4 0x41 0x0461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af 2 OP_CHECKMULTISIG"]], +"0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa43ad90ba26000000000494f47304402203f16c6f40162ab686621ef3000b04e75418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb60996631bf6bf5bc0a0682c4db743ce7ca2b01ffffffff0140420f00000000001976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000", "DERSIG,LOW_S,STRICTENC,NULLDUMMY"], + +["The following is c6c232a36395fa338da458b86ff1327395a9afc28c5d2daa4273e410089fd433 on testnet3"], +["It contains an OP_CHECKSIG with the shortest valid DER encoded signature (8 bytes w/o sighash flag), i.e. 3006020101020101 (r=1, s=1)"], [[["cf016927962ec028964c186043d48e465b3d4672f758953b00d3c4682f71cad6", 0, "HASH160 0x14 0x58a994e9d5ed9baa03ecfd1137592a90ad3cdfc5 EQUAL"]], +"0100000001d6ca712f68c4d3003b9558f772463d5b468ed44360184c9628c02e96276901cf000000002f21026d2204a9535443657a88a0724fbd49a0e78d305f50a82f2cc9dd9bea10a6c5cd0c093006020101020101017cacffffffff010000000000000000016a00000000", "CONST_SCRIPTCODE"], + +["The following is c99c49da4c38af669dea436d3e73780dfdb6c1ecf9958baa52960e8baee30e73"], +["It is of interest because it contains a 0-sequence as well as a signature of SIGHASH type 0 (which is not a real type)"], +[[["406b2b06bcd34d3c8733e6b79f7a394c8a431fbf4ff5ac705c93f4076bb77602", 0, "DUP HASH160 0x14 0xdc44b1164188067c3a32d4780f5996fa14a4f2d9 EQUALVERIFY CHECKSIG"]], +"01000000010276b76b07f4935c70acf54fbf1f438a4c397a9fb7e633873c4dd3bc062b6b40000000008c493046022100d23459d03ed7e9511a47d13292d3430a04627de6235b6e51a40f9cd386f2abe3022100e7d25b080f0bb8d8d5f878bba7d54ad2fda650ea8d158a33ee3cbd11768191fd004104b0e2c879e4daf7b9ab68350228c159766676a14f5815084ba166432aab46198d4cca98fa3e9981d0a90b2effc514b76279476550ba3663fdcaff94c38420e9d5000000000100093d00000000001976a9149a7b0f3b80c6baaeedce0a0842553800f832ba1f88ac00000000", "LOW_S,STRICTENC"], + +["A nearly-standard transaction with CHECKSIGVERIFY 1 instead of CHECKSIG"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "DUP HASH160 0x14 0x5b6462475454710f3c22f5fdf0b40704c92f25c3 EQUALVERIFY CHECKSIGVERIFY 1"]], +"01000000010001000000000000000000000000000000000000000000000000000000000000000000006a473044022067288ea50aa799543a536ff9306f8e1cba05b9c6b10951175b924f96732555ed022026d7b5265f38d21541519e4a1e55044d5b9e17e15cdbaf29ae3792e99e883e7a012103ba8c8b86dea131c22ab967e6dd99bdae8eff7a1f75a2c35f1f944109e3fe5e22ffffffff010000000000000000015100000000", "NONE"], + +["Same as above, but with the signature duplicated in the scriptPubKey with the proper pushdata prefix"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "DUP HASH160 0x14 0x5b6462475454710f3c22f5fdf0b40704c92f25c3 EQUALVERIFY CHECKSIGVERIFY 1 0x47 0x3044022067288ea50aa799543a536ff9306f8e1cba05b9c6b10951175b924f96732555ed022026d7b5265f38d21541519e4a1e55044d5b9e17e15cdbaf29ae3792e99e883e7a01"]], +"01000000010001000000000000000000000000000000000000000000000000000000000000000000006a473044022067288ea50aa799543a536ff9306f8e1cba05b9c6b10951175b924f96732555ed022026d7b5265f38d21541519e4a1e55044d5b9e17e15cdbaf29ae3792e99e883e7a012103ba8c8b86dea131c22ab967e6dd99bdae8eff7a1f75a2c35f1f944109e3fe5e22ffffffff010000000000000000015100000000", "CLEANSTACK,CONST_SCRIPTCODE"], + +["The following is f7fdd091fa6d8f5e7a8c2458f5c38faffff2d3f1406b6e4fe2c99dcc0d2d1cbb"], +["It caught a bug in the workaround for 23b397edccd3740a74adb603c9756370fafcde9bcc4483eb271ecad09a94dd63 in an overly simple implementation. In a signature, it contains an ASN1 integer which isn't strict-DER conformant due to being negative, which doesn't make sense in a signature. Before BIP66 activated, it was a valid signature. After it activated, it's not valid any more."], +[[["b464e85df2a238416f8bdae11d120add610380ea07f4ef19c5f9dfd472f96c3d", 0, "DUP HASH160 0x14 0xbef80ecf3a44500fda1bc92176e442891662aed2 EQUALVERIFY CHECKSIG"], +["b7978cc96e59a8b13e0865d3f95657561a7f725be952438637475920bac9eb21", 1, "DUP HASH160 0x14 0xbef80ecf3a44500fda1bc92176e442891662aed2 EQUALVERIFY CHECKSIG"]], +"01000000023d6cf972d4dff9c519eff407ea800361dd0a121de1da8b6f4138a2f25de864b4000000008a4730440220ffda47bfc776bcd269da4832626ac332adfca6dd835e8ecd83cd1ebe7d709b0e022049cffa1cdc102a0b56e0e04913606c70af702a1149dc3b305ab9439288fee090014104266abb36d66eb4218a6dd31f09bb92cf3cfa803c7ea72c1fc80a50f919273e613f895b855fb7465ccbc8919ad1bd4a306c783f22cd3227327694c4fa4c1c439affffffff21ebc9ba20594737864352e95b727f1a565756f9d365083eb1a8596ec98c97b7010000008a4730440220503ff10e9f1e0de731407a4a245531c9ff17676eda461f8ceeb8c06049fa2c810220c008ac34694510298fa60b3f000df01caa244f165b727d4896eb84f81e46bcc4014104266abb36d66eb4218a6dd31f09bb92cf3cfa803c7ea72c1fc80a50f919273e613f895b855fb7465ccbc8919ad1bd4a306c783f22cd3227327694c4fa4c1c439affffffff01f0da5200000000001976a914857ccd42dded6df32949d4646dfa10a92458cfaa88ac00000000", "DERSIG,LOW_S,STRICTENC"], + +["The following tests for the presence of a bug in the handling of SIGHASH_SINGLE"], +["It results in signing the constant 1, instead of something generated based on the transaction,"], +["when the input doing the signing has an index greater than the maximum output index"], +[[["0000000000000000000000000000000000000000000000000000000000000200", 0, "1"], ["0000000000000000000000000000000000000000000000000000000000000100", 0, "DUP HASH160 0x14 0xe52b482f2faa8ecbf0db344f93c84ac908557f33 EQUALVERIFY CHECKSIG"]], +"01000000020002000000000000000000000000000000000000000000000000000000000000000000000151ffffffff0001000000000000000000000000000000000000000000000000000000000000000000006b483045022100c9cdd08798a28af9d1baf44a6c77bcc7e279f47dc487c8c899911bc48feaffcc0220503c5c50ae3998a733263c5c0f7061b483e2b56c4c41b456e7d2f5a78a74c077032102d5c25adb51b61339d2b05315791e21bbe80ea470a49db0135720983c905aace0ffffffff010000000000000000015100000000", "CLEANSTACK"], + +["The following tests SIGHASH_SINGLE|SIGHASHANYONECANPAY inputs"], +[[["437a1002eb125dec0f93f635763e0ae45f28ff8e81d82945753d0107611cd390", 1, "DUP HASH160 0x14 0x383fb81cb0a3fc724b5e08cf8bbd404336d711f6 EQUALVERIFY CHECKSIG"], + ["2d48d32ccad087bcda0ac5b31555bd58d1d2568184cbc8e752dd2be2684af03f", 1, "DUP HASH160 0x14 0x275ec2a233e5b23d43fa19e7bf9beb0cb3996117 EQUALVERIFY CHECKSIG"], + ["c76168ef1a272a4f176e55e73157ecfce040cfad16a5272f6296eb7089dca846", 1, "DUP HASH160 0x14 0x34fea2c5a75414fd945273ae2d029ce1f28dafcf EQUALVERIFY CHECKSIG"]], +"010000000390d31c6107013d754529d8818eff285fe40a3e7635f6930fec5d12eb02107a43010000006b483045022100f40815ae3c81a0dd851cc8d376d6fd226c88416671346a9033468cca2cdcc6c202204f764623903e6c4bed1b734b75d82c40f1725e4471a55ad4f51218f86130ac038321033d710ab45bb54ac99618ad23b3c1da661631aa25f23bfe9d22b41876f1d46e4effffffff3ff04a68e22bdd52e7c8cb848156d2d158bd5515b3c50adabc87d0ca2cd3482d010000006a4730440220598d263c107004008e9e26baa1e770be30fd31ee55ded1898f7c00da05a75977022045536bead322ca246779698b9c3df3003377090f41afeca7fb2ce9e328ec4af2832102b738b531def73020bd637f32935924cc88549c8206976226d968edd3a42fc2d7ffffffff46a8dc8970eb96622f27a516adcf40e0fcec5731e7556e174f2a271aef6861c7010000006b483045022100c5b90a777a9fdc90c208dbef7290d1fc1be651f47151ee4ccff646872a454cf90220640cfbc4550446968fbbe9d12528f3adf7d87b31541569c59e790db8a220482583210391332546e22bbe8fe3af54addfad6f8b83d05fa4f5e047593d4c07ae938795beffffffff028036be26000000001976a914ddfb29efad43a667465ac59ff14dc6442a1adfca88ac3d5cba01000000001976a914b64dde7a505a13ca986c40e86e984a8dc81368b688ac00000000", "NONE"], + +["A valid P2SH Transaction using the standard transaction type put forth in BIP 16"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0x8febbed40483661de6958d957412f82deed8e2f7 EQUAL"]], +"01000000010001000000000000000000000000000000000000000000000000000000000000000000006e493046022100c66c9cdf4c43609586d15424c54707156e316d88b0a1534c9e6b0d4f311406310221009c0fe51dbc9c4ab7cc25d3fdbeccf6679fe6827f08edf2b4a9f16ee3eb0e438a0123210338e8034509af564c62644c07691942e0c056752008a173c89f60ab2a88ac2ebfacffffffff010000000000000000015100000000", "LOW_S"], + +["Tests for CheckTransaction()"], +["MAX_MONEY output"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0x32afac281462b822adbec5094b8d4d337dd5bd6a EQUAL"]], +"01000000010001000000000000000000000000000000000000000000000000000000000000000000006e493046022100e1eadba00d9296c743cb6ecc703fd9ddc9b3cd12906176a226ae4c18d6b00796022100a71aef7d2874deff681ba6080f1b278bac7bb99c61b08a85f4311970ffe7f63f012321030c0588dc44d92bdcbf8e72093466766fdc265ead8db64517b0c542275b70fffbacffffffff010040075af0750700015100000000", "LOW_S"], + +["MAX_MONEY output + 0 output"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0xb558cbf4930954aa6a344363a15668d7477ae716 EQUAL"]], +"01000000010001000000000000000000000000000000000000000000000000000000000000000000006d483045022027deccc14aa6668e78a8c9da3484fbcd4f9dcc9bb7d1b85146314b21b9ae4d86022100d0b43dece8cfb07348de0ca8bc5b86276fa88f7f2138381128b7c36ab2e42264012321029bb13463ddd5d2cc05da6e84e37536cb9525703cfd8f43afdb414988987a92f6acffffffff020040075af075070001510000000000000000015100000000", "LOW_S"], + +["Coinbase of size 2"], +["Note the input is just required to make the tester happy"], +[[["0000000000000000000000000000000000000000000000000000000000000000", -1, "1"]], +"01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff025151ffffffff010000000000000000015100000000", "CLEANSTACK"], + +["Coinbase of size 100"], +["Note the input is just required to make the tester happy"], +[[["0000000000000000000000000000000000000000000000000000000000000000", -1, "1"]], +"01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff6451515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151ffffffff010000000000000000015100000000", "CLEANSTACK"], + +["Simple transaction with first input is signed with SIGHASH_ALL, second with SIGHASH_ANYONECANPAY"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x21 0x035e7f0d4d0841bcd56c39337ed086b1a633ee770c1ffdd94ac552a95ac2ce0efc CHECKSIG"], + ["0000000000000000000000000000000000000000000000000000000000000200", 0, "0x21 0x035e7f0d4d0841bcd56c39337ed086b1a633ee770c1ffdd94ac552a95ac2ce0efc CHECKSIG"]], + "010000000200010000000000000000000000000000000000000000000000000000000000000000000049483045022100d180fd2eb9140aeb4210c9204d3f358766eb53842b2a9473db687fa24b12a3cc022079781799cd4f038b85135bbe49ec2b57f306b2bb17101b17f71f000fcab2b6fb01ffffffff0002000000000000000000000000000000000000000000000000000000000000000000004847304402205f7530653eea9b38699e476320ab135b74771e1c48b81a5d041e2ca84b9be7a802200ac8d1f40fb026674fe5a5edd3dea715c27baa9baca51ed45ea750ac9dc0a55e81ffffffff010100000000000000015100000000", "NONE"], + +["Same as above, but we change the sequence number of the first input to check that SIGHASH_ANYONECANPAY is being followed"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x21 0x035e7f0d4d0841bcd56c39337ed086b1a633ee770c1ffdd94ac552a95ac2ce0efc CHECKSIG"], + ["0000000000000000000000000000000000000000000000000000000000000200", 0, "0x21 0x035e7f0d4d0841bcd56c39337ed086b1a633ee770c1ffdd94ac552a95ac2ce0efc CHECKSIG"]], + "01000000020001000000000000000000000000000000000000000000000000000000000000000000004948304502203a0f5f0e1f2bdbcd04db3061d18f3af70e07f4f467cbc1b8116f267025f5360b022100c792b6e215afc5afc721a351ec413e714305cb749aae3d7fee76621313418df101010000000002000000000000000000000000000000000000000000000000000000000000000000004847304402205f7530653eea9b38699e476320ab135b74771e1c48b81a5d041e2ca84b9be7a802200ac8d1f40fb026674fe5a5edd3dea715c27baa9baca51ed45ea750ac9dc0a55e81ffffffff010100000000000000015100000000", "LOW_S"], + +["afd9c17f8913577ec3509520bd6e5d63e9c0fd2a5f70c787993b097ba6ca9fae which has several SIGHASH_SINGLE signatures"], +[[["63cfa5a09dc540bf63e53713b82d9ea3692ca97cd608c384f2aa88e51a0aac70", 0, "DUP HASH160 0x14 0xdcf72c4fd02f5a987cf9b02f2fabfcac3341a87d EQUALVERIFY CHECKSIG"], + ["04e8d0fcf3846c6734477b98f0f3d4badfb78f020ee097a0be5fe347645b817d", 1, "DUP HASH160 0x14 0xdcf72c4fd02f5a987cf9b02f2fabfcac3341a87d EQUALVERIFY CHECKSIG"], + ["ee1377aff5d0579909e11782e1d2f5f7b84d26537be7f5516dd4e43373091f3f", 1, "DUP HASH160 0x14 0xdcf72c4fd02f5a987cf9b02f2fabfcac3341a87d EQUALVERIFY CHECKSIG"]], + "010000000370ac0a1ae588aaf284c308d67ca92c69a39e2db81337e563bf40c59da0a5cf63000000006a4730440220360d20baff382059040ba9be98947fd678fb08aab2bb0c172efa996fd8ece9b702201b4fb0de67f015c90e7ac8a193aeab486a1f587e0f54d0fb9552ef7f5ce6caec032103579ca2e6d107522f012cd00b52b9a65fb46f0c57b9b8b6e377c48f526a44741affffffff7d815b6447e35fbea097e00e028fb7dfbad4f3f0987b4734676c84f3fcd0e804010000006b483045022100c714310be1e3a9ff1c5f7cacc65c2d8e781fc3a88ceb063c6153bf950650802102200b2d0979c76e12bb480da635f192cc8dc6f905380dd4ac1ff35a4f68f462fffd032103579ca2e6d107522f012cd00b52b9a65fb46f0c57b9b8b6e377c48f526a44741affffffff3f1f097333e4d46d51f5e77b53264db8f7f5d2e18217e1099957d0f5af7713ee010000006c493046022100b663499ef73273a3788dea342717c2640ac43c5a1cf862c9e09b206fcb3f6bb8022100b09972e75972d9148f2bdd462e5cb69b57c1214b88fc55ca638676c07cfc10d8032103579ca2e6d107522f012cd00b52b9a65fb46f0c57b9b8b6e377c48f526a44741affffffff0380841e00000000001976a914bfb282c70c4191f45b5a6665cad1682f2c9cfdfb88ac80841e00000000001976a9149857cc07bed33a5cf12b9c5e0500b675d500c81188ace0fd1c00000000001976a91443c52850606c872403c0601e69fa34b26f62db4a88ac00000000", "LOW_S"], + + ["ddc454a1c0c35c188c98976b17670f69e586d9c0f3593ea879928332f0a069e7, which spends an input that pushes using a PUSHDATA1 that is negative when read as signed"], + [[["c5510a5dd97a25f43175af1fe649b707b1df8e1a41489bac33a23087027a2f48", 0, "0x4c 0xae 0x606563686f2022553246736447566b58312b5a536e587574356542793066794778625456415675534a6c376a6a334878416945325364667657734f53474f36633338584d7439435c6e543249584967306a486956304f376e775236644546673d3d22203e20743b206f70656e73736c20656e63202d7061737320706173733a5b314a564d7751432d707269766b65792d6865785d202d64202d6165732d3235362d636263202d61202d696e207460 DROP DUP HASH160 0x14 0xbfd7436b6265aa9de506f8a994f881ff08cc2872 EQUALVERIFY CHECKSIG"]], + "0100000001482f7a028730a233ac9b48411a8edfb107b749e61faf7531f4257ad95d0a51c5000000008b483045022100bf0bbae9bde51ad2b222e87fbf67530fbafc25c903519a1e5dcc52a32ff5844e022028c4d9ad49b006dd59974372a54291d5764be541574bb0c4dc208ec51f80b7190141049dd4aad62741dc27d5f267f7b70682eee22e7e9c1923b9c0957bdae0b96374569b460eb8d5b40d972e8c7c0ad441de3d94c4a29864b212d56050acb980b72b2bffffffff0180969800000000001976a914e336d0017a9d28de99d16472f6ca6d5a3a8ebc9988ac00000000", "NONE"], + +["Correct signature order"], +["Note the input is just required to make the tester happy"], +[[["b3da01dd4aae683c7aee4d5d8b52a540a508e1115f77cd7fa9a291243f501223", 0, "HASH160 0x14 0xb1ce99298d5f07364b57b1e5c9cc00be0b04a954 EQUAL"]], +"01000000012312503f2491a2a97fcd775f11e108a540a5528b5d4dee7a3c68ae4add01dab300000000fdfe0000483045022100f6649b0eddfdfd4ad55426663385090d51ee86c3481bdc6b0c18ea6c0ece2c0b0220561c315b07cffa6f7dd9df96dbae9200c2dee09bf93cc35ca05e6cdf613340aa0148304502207aacee820e08b0b174e248abd8d7a34ed63b5da3abedb99934df9fddd65c05c4022100dfe87896ab5ee3df476c2655f9fbe5bd089dccbef3e4ea05b5d121169fe7f5f4014c695221031d11db38972b712a9fe1fc023577c7ae3ddb4a3004187d41c45121eecfdbb5b7210207ec36911b6ad2382860d32989c7b8728e9489d7bbc94a6b5509ef0029be128821024ea9fac06f666a4adc3fc1357b7bec1fd0bdece2b9d08579226a8ebde53058e453aeffffffff0180380100000000001976a914c9b99cddf847d10685a4fabaa0baf505f7c3dfab88ac00000000", "LOW_S"], + +["cc60b1f899ec0a69b7c3f25ddf32c4524096a9c5b01cbd84c6d0312a0c478984, which is a fairly strange transaction which relies on OP_CHECKSIG returning 0 when checking a completely invalid sig of length 0"], +[[["cbebc4da731e8995fe97f6fadcd731b36ad40e5ecb31e38e904f6e5982fa09f7", 0, "0x2102085c6600657566acc2d6382a47bc3f324008d2aa10940dd7705a48aa2a5a5e33ac7c2103f5d0fb955f95dd6be6115ce85661db412ec6a08abcbfce7da0ba8297c6cc0ec4ac7c5379a820d68df9e32a147cffa36193c6f7c43a1c8c69cda530e1c6db354bfabdcfefaf3c875379a820f531f3041d3136701ea09067c53e7159c8f9b2746a56c3d82966c54bbc553226879a5479827701200122a59a5379827701200122a59a6353798277537982778779679a68"]], +"0100000001f709fa82596e4f908ee331cb5e0ed46ab331d7dcfaf697fe95891e73dac4ebcb000000008c20ca42095840735e89283fec298e62ac2ddea9b5f34a8cbb7097ad965b87568100201b1b01dc829177da4a14551d2fc96a9db00c6501edfa12f22cd9cefd335c227f483045022100a9df60536df5733dd0de6bc921fab0b3eee6426501b43a228afa2c90072eb5ca02201c78b74266fac7d1db5deff080d8a403743203f109fbcabf6d5a760bf87386d20100ffffffff01c075790000000000232103611f9a45c18f28f06f19076ad571c344c82ce8fcfe34464cf8085217a2d294a6ac00000000", "CLEANSTACK"], + +["Empty pubkey"], +[[["229257c295e7f555421c1bfec8538dd30a4b5c37c1c8810bbe83cafa7811652c", 0, "0x00 CHECKSIG NOT"]], +"01000000012c651178faca83be0b81c8c1375c4b0ad38d53c8fe1b1c4255f5e795c25792220000000049483045022100d6044562284ac76c985018fc4a90127847708c9edb280996c507b28babdc4b2a02203d74eca3f1a4d1eea7ff77b528fde6d5dc324ec2dbfdb964ba885f643b9704cd01ffffffff010100000000000000232102c2410f8891ae918cab4ffc4bb4a3b0881be67c7a1e7faa8b5acf9ab8932ec30cac00000000", "STRICTENC,NULLFAIL"], + +["Empty signature"], +[[["9ca93cfd8e3806b9d9e2ba1cf64e3cc6946ee0119670b1796a09928d14ea25f7", 0, "0x21 0x028a1d66975dbdf97897e3a4aef450ebeb5b5293e4a0b4a6d3a2daaa0b2b110e02 CHECKSIG NOT"]], +"0100000001f725ea148d92096a79b1709611e06e94c63c4ef61cbae2d9b906388efd3ca99c000000000100ffffffff0101000000000000002321028a1d66975dbdf97897e3a4aef450ebeb5b5293e4a0b4a6d3a2daaa0b2b110e02ac00000000", "NONE"], + +[[["444e00ed7840d41f20ecd9c11d3f91982326c731a02f3c05748414a4fa9e59be", 0, "1 0x00 0x21 0x02136b04758b0b6e363e7a6fbe83aaf527a153db2b060d36cc29f7f8309ba6e458 2 CHECKMULTISIG"]], +"0100000001be599efaa4148474053c2fa031c7262398913f1dc1d9ec201fd44078ed004e44000000004900473044022022b29706cb2ed9ef0cb3c97b72677ca2dfd7b4160f7b4beb3ba806aa856c401502202d1e52582412eba2ed474f1f437a427640306fd3838725fab173ade7fe4eae4a01ffffffff010100000000000000232103ac4bba7e7ca3e873eea49e08132ad30c7f03640b6539e9b59903cf14fd016bbbac00000000", "NONE"], + +[[["e16abbe80bf30c080f63830c8dbf669deaef08957446e95940227d8c5e6db612", 0, "1 0x21 0x03905380c7013e36e6e19d305311c1b81fce6581f5ee1c86ef0627c68c9362fc9f 0x00 2 CHECKMULTISIG"]], +"010000000112b66d5e8c7d224059e946749508efea9d66bf8d0c83630f080cf30be8bb6ae100000000490047304402206ffe3f14caf38ad5c1544428e99da76ffa5455675ec8d9780fac215ca17953520220779502985e194d84baa36b9bd40a0dbd981163fa191eb884ae83fc5bd1c86b1101ffffffff010100000000000000232103905380c7013e36e6e19d305311c1b81fce6581f5ee1c86ef0627c68c9362fc9fac00000000", "STRICTENC"], + +[[["ebbcf4bfce13292bd791d6a65a2a858d59adbf737e387e40370d4e64cc70efb0", 0, "2 0x21 0x033bcaa0a602f0d44cc9d5637c6e515b0471db514c020883830b7cefd73af04194 0x21 0x03a88b326f8767f4f192ce252afe33c94d25ab1d24f27f159b3cb3aa691ffe1423 2 CHECKMULTISIG NOT"]], +"0100000001b0ef70cc644e0d37407e387e73bfad598d852a5aa6d691d72b2913cebff4bceb000000004a00473044022068cd4851fc7f9a892ab910df7a24e616f293bcb5c5fbdfbc304a194b26b60fba022078e6da13d8cb881a22939b952c24f88b97afd06b4c47a47d7f804c9a352a6d6d0100ffffffff0101000000000000002321033bcaa0a602f0d44cc9d5637c6e515b0471db514c020883830b7cefd73af04194ac00000000", "NULLFAIL"], + +[[["ba4cd7ae2ad4d4d13ebfc8ab1d93a63e4a6563f25089a18bf0fc68f282aa88c1", 0, "2 0x21 0x037c615d761e71d38903609bf4f46847266edc2fb37532047d747ba47eaae5ffe1 0x21 0x02edc823cd634f2c4033d94f5755207cb6b60c4b1f1f056ad7471c47de5f2e4d50 2 CHECKMULTISIG NOT"]], +"0100000001c188aa82f268fcf08ba18950f263654a3ea6931dabc8bf3ed1d4d42aaed74cba000000004b0000483045022100940378576e069aca261a6b26fb38344e4497ca6751bb10905c76bb689f4222b002204833806b014c26fd801727b792b1260003c55710f87c5adbd7a9cb57446dbc9801ffffffff0101000000000000002321037c615d761e71d38903609bf4f46847266edc2fb37532047d747ba47eaae5ffe1ac00000000", "NULLFAIL"], + + +["OP_CODESEPARATOR tests"], + +["Test that SignatureHash() removes OP_CODESEPARATOR with FindAndDelete()"], +[[["bc7fd132fcf817918334822ee6d9bd95c889099c96e07ca2c1eb2cc70db63224", 0, "CODESEPARATOR 0x21 0x038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041 CHECKSIG"]], +"01000000012432b60dc72cebc1a27ce0969c0989c895bdd9e62e8234839117f8fc32d17fbc000000004a493046022100a576b52051962c25e642c0fd3d77ee6c92487048e5d90818bcf5b51abaccd7900221008204f8fb121be4ec3b24483b1f92d89b1b0548513a134e345c5442e86e8617a501ffffffff010000000000000000016a00000000", "CONST_SCRIPTCODE,LOW_S"], +[[["83e194f90b6ef21fa2e3a365b63794fb5daa844bdc9b25de30899fcfe7b01047", 0, "CODESEPARATOR CODESEPARATOR 0x21 0x038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041 CHECKSIG"]], +"01000000014710b0e7cf9f8930de259bdc4b84aa5dfb9437b665a3e3a21ff26e0bf994e183000000004a493046022100a166121a61b4eeb19d8f922b978ff6ab58ead8a5a5552bf9be73dc9c156873ea02210092ad9bc43ee647da4f6652c320800debcf08ec20a094a0aaf085f63ecb37a17201ffffffff010000000000000000016a00000000", "CONST_SCRIPTCODE,LOW_S"], + +["Hashed data starts at the CODESEPARATOR"], +[[["326882a7f22b5191f1a0cc9962ca4b878cd969cf3b3a70887aece4d801a0ba5e", 0, "0x21 0x038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041 CODESEPARATOR CHECKSIG"]], +"01000000015ebaa001d8e4ec7a88703a3bcf69d98c874bca6299cca0f191512bf2a7826832000000004948304502203bf754d1c6732fbf87c5dcd81258aefd30f2060d7bd8ac4a5696f7927091dad1022100f5bcb726c4cf5ed0ed34cc13dadeedf628ae1045b7cb34421bc60b89f4cecae701ffffffff010000000000000000016a00000000", "CONST_SCRIPTCODE,LOW_S"], + +["But only if execution has reached it"], +[[["a955032f4d6b0c9bfe8cad8f00a8933790b9c1dc28c82e0f48e75b35da0e4944", 0, "0x21 0x038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041 CHECKSIGVERIFY CODESEPARATOR 0x21 0x038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041 CHECKSIGVERIFY CODESEPARATOR 1"]], +"010000000144490eda355be7480f2ec828dcc1b9903793a8008fad8cfe9b0c6b4d2f0355a900000000924830450221009c0a27f886a1d8cb87f6f595fbc3163d28f7a81ec3c4b252ee7f3ac77fd13ffa02203caa8dfa09713c8c4d7ef575c75ed97812072405d932bd11e6a1593a98b679370148304502201e3861ef39a526406bad1e20ecad06be7375ad40ddb582c9be42d26c3a0d7b240221009d0a3985e96522e59635d19cc4448547477396ce0ef17a58e7d74c3ef464292301ffffffff010000000000000000016a00000000", "CONST_SCRIPTCODE,LOW_S"], + +["CODESEPARATOR in an unexecuted IF block does not change what is hashed"], +[[["a955032f4d6b0c9bfe8cad8f00a8933790b9c1dc28c82e0f48e75b35da0e4944", 0, "IF CODESEPARATOR ENDIF 0x21 0x0378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71 CHECKSIGVERIFY CODESEPARATOR 1"]], +"010000000144490eda355be7480f2ec828dcc1b9903793a8008fad8cfe9b0c6b4d2f0355a9000000004a48304502207a6974a77c591fa13dff60cabbb85a0de9e025c09c65a4b2285e47ce8e22f761022100f0efaac9ff8ac36b10721e0aae1fb975c90500b50c56e8a0cc52b0403f0425dd0100ffffffff010000000000000000016a00000000", "CONST_SCRIPTCODE,LOW_S"], + +["As above, with the IF block executed"], +[[["a955032f4d6b0c9bfe8cad8f00a8933790b9c1dc28c82e0f48e75b35da0e4944", 0, "IF CODESEPARATOR ENDIF 0x21 0x0378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71 CHECKSIGVERIFY CODESEPARATOR 1"]], +"010000000144490eda355be7480f2ec828dcc1b9903793a8008fad8cfe9b0c6b4d2f0355a9000000004a483045022100fa4a74ba9fd59c59f46c3960cf90cbe0d2b743c471d24a3d5d6db6002af5eebb02204d70ec490fd0f7055a7c45f86514336e3a7f03503dacecabb247fc23f15c83510151ffffffff010000000000000000016a00000000", "CONST_SCRIPTCODE"], + + +["CHECKSIG is legal in scriptSigs"], +[[["ccf7f4053a02e653c36ac75c891b7496d0dc5ce5214f6c913d9cf8f1329ebee0", 0, "DUP HASH160 0x14 0xee5a6aa40facefb2655ac23c0c28c57c65c41f9b EQUALVERIFY CHECKSIG"]], +"0100000001e0be9e32f1f89c3d916c4f21e55cdcd096741b895cc76ac353e6023a05f4f7cc00000000d86149304602210086e5f736a2c3622ebb62bd9d93d8e5d76508b98be922b97160edc3dcca6d8c47022100b23c312ac232a4473f19d2aeb95ab7bdf2b65518911a0d72d50e38b5dd31dc820121038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041ac4730440220508fa761865c8abd81244a168392876ee1d94e8ed83897066b5e2df2400dad24022043f5ee7538e87e9c6aef7ef55133d3e51da7cc522830a9c4d736977a76ef755c0121038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041ffffffff010000000000000000016a00000000", "SIGPUSHONLY,CONST_SCRIPTCODE,LOW_S,CLEANSTACK"], + +["Same semantics for OP_CODESEPARATOR"], +[[["10c9f0effe83e97f80f067de2b11c6a00c3088a4bce42c5ae761519af9306f3c", 1, "DUP HASH160 0x14 0xee5a6aa40facefb2655ac23c0c28c57c65c41f9b EQUALVERIFY CHECKSIG"]], +"01000000013c6f30f99a5161e75a2ce4bca488300ca0c6112bde67f0807fe983feeff0c91001000000e608646561646265656675ab61493046022100ce18d384221a731c993939015e3d1bcebafb16e8c0b5b5d14097ec8177ae6f28022100bcab227af90bab33c3fe0a9abfee03ba976ee25dc6ce542526e9b2e56e14b7f10121038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041ac493046022100c3b93edcc0fd6250eb32f2dd8a0bba1754b0f6c3be8ed4100ed582f3db73eba2022100bf75b5bd2eff4d6bf2bda2e34a40fcc07d4aa3cf862ceaa77b47b81eff829f9a01ab21038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041ffffffff010000000000000000016a00000000", "SIGPUSHONLY,CONST_SCRIPTCODE,LOW_S,CLEANSTACK"], + +["Signatures are removed from the script they are in by FindAndDelete() in the CHECKSIG code; even multiple instances of one signature can be removed."], +[[["6056ebd549003b10cbbd915cea0d82209fe40b8617104be917a26fa92cbe3d6f", 0, "DUP HASH160 0x14 0xee5a6aa40facefb2655ac23c0c28c57c65c41f9b EQUALVERIFY CHECKSIG"]], +"01000000016f3dbe2ca96fa217e94b1017860be49f20820dea5c91bdcb103b0049d5eb566000000000fd1d0147304402203989ac8f9ad36b5d0919d97fa0a7f70c5272abee3b14477dc646288a8b976df5022027d19da84a066af9053ad3d1d7459d171b7e3a80bc6c4ef7a330677a6be548140147304402203989ac8f9ad36b5d0919d97fa0a7f70c5272abee3b14477dc646288a8b976df5022027d19da84a066af9053ad3d1d7459d171b7e3a80bc6c4ef7a330677a6be548140121038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041ac47304402203757e937ba807e4a5da8534c17f9d121176056406a6465054bdd260457515c1a02200f02eccf1bec0f3a0d65df37889143c2e88ab7acec61a7b6f5aa264139141a2b0121038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041ffffffff010000000000000000016a00000000", "SIGPUSHONLY,CONST_SCRIPTCODE,CLEANSTACK"], + +["That also includes ahead of the opcode being executed."], +[[["5a6b0021a6042a686b6b94abc36b387bef9109847774e8b1e51eb8cc55c53921", 1, "DUP HASH160 0x14 0xee5a6aa40facefb2655ac23c0c28c57c65c41f9b EQUALVERIFY CHECKSIG"]], +"01000000012139c555ccb81ee5b1e87477840991ef7b386bc3ab946b6b682a04a621006b5a01000000fdb40148304502201723e692e5f409a7151db386291b63524c5eb2030df652b1f53022fd8207349f022100b90d9bbf2f3366ce176e5e780a00433da67d9e5c79312c6388312a296a5800390148304502201723e692e5f409a7151db386291b63524c5eb2030df652b1f53022fd8207349f022100b90d9bbf2f3366ce176e5e780a00433da67d9e5c79312c6388312a296a5800390121038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f2204148304502201723e692e5f409a7151db386291b63524c5eb2030df652b1f53022fd8207349f022100b90d9bbf2f3366ce176e5e780a00433da67d9e5c79312c6388312a296a5800390175ac4830450220646b72c35beeec51f4d5bc1cbae01863825750d7f490864af354e6ea4f625e9c022100f04b98432df3a9641719dbced53393022e7249fb59db993af1118539830aab870148304502201723e692e5f409a7151db386291b63524c5eb2030df652b1f53022fd8207349f022100b90d9bbf2f3366ce176e5e780a00433da67d9e5c79312c6388312a296a580039017521038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041ffffffff010000000000000000016a00000000", "SIGPUSHONLY,CONST_SCRIPTCODE,LOW_S,CLEANSTACK"], + +["Finally CHECKMULTISIG removes all signatures prior to hashing the script containing those signatures. In conjunction with the SIGHASH_SINGLE bug this lets us test whether or not FindAndDelete() is actually present in scriptPubKey/redeemScript evaluation by including a signature of the digest 0x01 We can compute in advance for our pubkey, embed it in the scriptPubKey, and then also using a normal SIGHASH_ALL signature. If FindAndDelete() wasn't run, the 'bugged' signature would still be in the hashed script, and the normal signature would fail."], + +["Here's an example on mainnet within a P2SH redeemScript. Remarkably it's a standard transaction in <0.9"], +[[["b5b598de91787439afd5938116654e0b16b7a0d0f82742ba37564219c5afcbf9", 0, "DUP HASH160 0x14 0xf6f365c40f0739b61de827a44751e5e99032ed8f EQUALVERIFY CHECKSIG"], + ["ab9805c6d57d7070d9a42c5176e47bb705023e6b67249fb6760880548298e742", 0, "HASH160 0x14 0xd8dacdadb7462ae15cd906f1878706d0da8660e6 EQUAL"]], +"0100000002f9cbafc519425637ba4227f8d0a0b7160b4e65168193d5af39747891de98b5b5000000006b4830450221008dd619c563e527c47d9bd53534a770b102e40faa87f61433580e04e271ef2f960220029886434e18122b53d5decd25f1f4acb2480659fea20aabd856987ba3c3907e0121022b78b756e2258af13779c1a1f37ea6800259716ca4b7f0b87610e0bf3ab52a01ffffffff42e7988254800876b69f24676b3e0205b77be476512ca4d970707dd5c60598ab00000000fd260100483045022015bd0139bcccf990a6af6ec5c1c52ed8222e03a0d51c334df139968525d2fcd20221009f9efe325476eb64c3958e4713e9eefe49bf1d820ed58d2112721b134e2a1a53034930460221008431bdfa72bc67f9d41fe72e94c88fb8f359ffa30b33c72c121c5a877d922e1002210089ef5fc22dd8bfc6bf9ffdb01a9862d27687d424d1fefbab9e9c7176844a187a014c9052483045022015bd0139bcccf990a6af6ec5c1c52ed8222e03a0d51c334df139968525d2fcd20221009f9efe325476eb64c3958e4713e9eefe49bf1d820ed58d2112721b134e2a1a5303210378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71210378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c7153aeffffffff01a08601000000000017a914d8dacdadb7462ae15cd906f1878706d0da8660e68700000000", "CONST_SCRIPTCODE,LOW_S"], + +["Same idea, but with bare CHECKMULTISIG"], +[[["ceafe58e0f6e7d67c0409fbbf673c84c166e3c5d3c24af58f7175b18df3bb3db", 0, "DUP HASH160 0x14 0xf6f365c40f0739b61de827a44751e5e99032ed8f EQUALVERIFY CHECKSIG"], + ["ceafe58e0f6e7d67c0409fbbf673c84c166e3c5d3c24af58f7175b18df3bb3db", 1, "2 0x48 0x3045022015bd0139bcccf990a6af6ec5c1c52ed8222e03a0d51c334df139968525d2fcd20221009f9efe325476eb64c3958e4713e9eefe49bf1d820ed58d2112721b134e2a1a5303 0x21 0x0378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71 0x21 0x0378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71 3 CHECKMULTISIG"]], +"0100000002dbb33bdf185b17f758af243c5d3c6e164cc873f6bb9f40c0677d6e0f8ee5afce000000006b4830450221009627444320dc5ef8d7f68f35010b4c050a6ed0d96b67a84db99fda9c9de58b1e02203e4b4aaa019e012e65d69b487fdf8719df72f488fa91506a80c49a33929f1fd50121022b78b756e2258af13779c1a1f37ea6800259716ca4b7f0b87610e0bf3ab52a01ffffffffdbb33bdf185b17f758af243c5d3c6e164cc873f6bb9f40c0677d6e0f8ee5afce010000009300483045022015bd0139bcccf990a6af6ec5c1c52ed8222e03a0d51c334df139968525d2fcd20221009f9efe325476eb64c3958e4713e9eefe49bf1d820ed58d2112721b134e2a1a5303483045022015bd0139bcccf990a6af6ec5c1c52ed8222e03a0d51c334df139968525d2fcd20221009f9efe325476eb64c3958e4713e9eefe49bf1d820ed58d2112721b134e2a1a5303ffffffff01a0860100000000001976a9149bc0bbdd3024da4d0c38ed1aecf5c68dd1d3fa1288ac00000000", "CONST_SCRIPTCODE,LOW_S"], + + +["CHECKLOCKTIMEVERIFY tests"], + +["By-height locks, with argument == 0 and == tx nLockTime"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0 CHECKLOCKTIMEVERIFY 1"]], +"010000000100010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000", "CLEANSTACK"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "499999999 CHECKLOCKTIMEVERIFY"]], +"0100000001000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000ff64cd1d", "NONE"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0 CHECKLOCKTIMEVERIFY 1"]], +"0100000001000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000ff64cd1d", "CLEANSTACK"], + +["By-time locks, with argument just beyond tx nLockTime (but within numerical boundaries)"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "500000000 CHECKLOCKTIMEVERIFY"]], +"01000000010001000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000065cd1d", "NONE"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "4294967295 CHECKLOCKTIMEVERIFY"]], +"0100000001000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000ffffffff", "NONE"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "500000000 CHECKLOCKTIMEVERIFY"]], +"0100000001000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000ffffffff", "NONE"], + +["Any non-maxint nSequence is fine"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0 CHECKLOCKTIMEVERIFY 1"]], +"010000000100010000000000000000000000000000000000000000000000000000000000000000000000feffffff0100000000000000000000000000", "CLEANSTACK"], + +["The argument can be calculated rather than created directly by a PUSHDATA"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "499999999 1ADD CHECKLOCKTIMEVERIFY"]], +"01000000010001000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000065cd1d", "NONE"], + +["Perhaps even by an ADD producing a 5-byte result that is out of bounds for other opcodes"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "2147483647 2147483647 ADD CHECKLOCKTIMEVERIFY"]], +"0100000001000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000feffffff", "NONE"], + +["5 byte non-minimally-encoded arguments are valid"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x05 0x0000000000 CHECKLOCKTIMEVERIFY 1"]], +"010000000100010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000", "CLEANSTACK,MINIMALDATA"], + +["Valid CHECKLOCKTIMEVERIFY in scriptSig"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "1"]], +"01000000010001000000000000000000000000000000000000000000000000000000000000000000000251b1000000000100000000000000000001000000", "SIGPUSHONLY,CLEANSTACK"], + +["Valid CHECKLOCKTIMEVERIFY in redeemScript"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0xc5b93064159b3b2d6ab506a41b1f50463771b988 EQUAL"]], +"0100000001000100000000000000000000000000000000000000000000000000000000000000000000030251b1000000000100000000000000000001000000", "NONE"], + +["A transaction with a non-standard DER signature."], +[[["b1dbc81696c8a9c0fccd0693ab66d7c368dbc38c0def4e800685560ddd1b2132", 0, "DUP HASH160 0x14 0x4b3bd7eba3bc0284fd3007be7f3be275e94f5826 EQUALVERIFY CHECKSIG"]], +"010000000132211bdd0d568506804eef0d8cc3db68c3d766ab9306cdfcc0a9c89616c8dbb1000000006c493045022100c7bb0faea0522e74ff220c20c022d2cb6033f8d167fb89e75a50e237a35fd6d202203064713491b1f8ad5f79e623d0219ad32510bfaa1009ab30cbee77b59317d6e30001210237af13eb2d84e4545af287b919c2282019c9691cc509e78e196a9d8274ed1be0ffffffff0100000000000000001976a914f1b3ed2eda9a2ebe5a9374f692877cdf87c0f95b88ac00000000", "DERSIG,LOW_S,STRICTENC"], + +["CHECKSEQUENCEVERIFY tests"], + +["By-height locks, with argument == 0 and == txin.nSequence"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0 CHECKSEQUENCEVERIFY 1"]], +"020000000100010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000", "CLEANSTACK"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "65535 CHECKSEQUENCEVERIFY"]], +"020000000100010000000000000000000000000000000000000000000000000000000000000000000000ffff00000100000000000000000000000000", "NONE"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "65535 CHECKSEQUENCEVERIFY"]], +"020000000100010000000000000000000000000000000000000000000000000000000000000000000000ffffbf7f0100000000000000000000000000", "NONE"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0 CHECKSEQUENCEVERIFY 1"]], +"020000000100010000000000000000000000000000000000000000000000000000000000000000000000ffffbf7f0100000000000000000000000000", "CLEANSTACK"], + +["By-time locks, with argument == 0 and == txin.nSequence"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "4194304 CHECKSEQUENCEVERIFY"]], +"020000000100010000000000000000000000000000000000000000000000000000000000000000000000000040000100000000000000000000000000", "NONE"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "4259839 CHECKSEQUENCEVERIFY"]], +"020000000100010000000000000000000000000000000000000000000000000000000000000000000000ffff40000100000000000000000000000000", "NONE"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "4259839 CHECKSEQUENCEVERIFY"]], +"020000000100010000000000000000000000000000000000000000000000000000000000000000000000ffffff7f0100000000000000000000000000", "NONE"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "4194304 CHECKSEQUENCEVERIFY"]], +"020000000100010000000000000000000000000000000000000000000000000000000000000000000000ffffff7f0100000000000000000000000000", "NONE"], + +["Upper sequence with upper sequence is fine"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "2147483648 CHECKSEQUENCEVERIFY"]], +"020000000100010000000000000000000000000000000000000000000000000000000000000000000000000000800100000000000000000000000000", "NONE"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "4294967295 CHECKSEQUENCEVERIFY"]], +"020000000100010000000000000000000000000000000000000000000000000000000000000000000000000000800100000000000000000000000000", "NONE"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "2147483648 CHECKSEQUENCEVERIFY"]], +"020000000100010000000000000000000000000000000000000000000000000000000000000000000000feffffff0100000000000000000000000000", "NONE"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "4294967295 CHECKSEQUENCEVERIFY"]], +"020000000100010000000000000000000000000000000000000000000000000000000000000000000000feffffff0100000000000000000000000000", "NONE"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "2147483648 CHECKSEQUENCEVERIFY"]], +"020000000100010000000000000000000000000000000000000000000000000000000000000000000000ffffffff0100000000000000000000000000", "NONE"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "4294967295 CHECKSEQUENCEVERIFY"]], +"020000000100010000000000000000000000000000000000000000000000000000000000000000000000ffffffff0100000000000000000000000000", "NONE"], + +["Argument 2^31 with various nSequence"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "2147483648 CHECKSEQUENCEVERIFY"]], +"020000000100010000000000000000000000000000000000000000000000000000000000000000000000ffffbf7f0100000000000000000000000000", "NONE"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "2147483648 CHECKSEQUENCEVERIFY"]], +"020000000100010000000000000000000000000000000000000000000000000000000000000000000000ffffff7f0100000000000000000000000000", "NONE"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "2147483648 CHECKSEQUENCEVERIFY"]], +"020000000100010000000000000000000000000000000000000000000000000000000000000000000000ffffffff0100000000000000000000000000", "NONE"], + +["Argument 2^32-1 with various nSequence"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "4294967295 CHECKSEQUENCEVERIFY"]], +"020000000100010000000000000000000000000000000000000000000000000000000000000000000000ffffbf7f0100000000000000000000000000", "NONE"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "4294967295 CHECKSEQUENCEVERIFY"]], +"020000000100010000000000000000000000000000000000000000000000000000000000000000000000ffffff7f0100000000000000000000000000", "NONE"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "4294967295 CHECKSEQUENCEVERIFY"]], +"020000000100010000000000000000000000000000000000000000000000000000000000000000000000ffffffff0100000000000000000000000000", "NONE"], + +["Argument 3<<31 with various nSequence"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x050000008001 CHECKSEQUENCEVERIFY"]], +"020000000100010000000000000000000000000000000000000000000000000000000000000000000000ffffbf7f0100000000000000000000000000", "NONE"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x050000008001 CHECKSEQUENCEVERIFY"]], +"020000000100010000000000000000000000000000000000000000000000000000000000000000000000ffffff7f0100000000000000000000000000", "NONE"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x050000008001 CHECKSEQUENCEVERIFY"]], +"020000000100010000000000000000000000000000000000000000000000000000000000000000000000ffffffff0100000000000000000000000000", "NONE"], + +["5 byte non-minimally-encoded operandss are valid"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x05 0x0000000000 CHECKSEQUENCEVERIFY 1"]], +"020000000100010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000", "MINIMALDATA,CLEANSTACK"], + +["The argument can be calculated rather than created directly by a PUSHDATA"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "4194303 1ADD CHECKSEQUENCEVERIFY"]], +"020000000100010000000000000000000000000000000000000000000000000000000000000000000000000040000100000000000000000000000000", "NONE"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "4194304 1SUB CHECKSEQUENCEVERIFY"]], +"020000000100010000000000000000000000000000000000000000000000000000000000000000000000ffff00000100000000000000000000000000", "NONE"], + +["An ADD producing a 5-byte result that sets CTxIn::SEQUENCE_LOCKTIME_DISABLE_FLAG"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "2147483647 65536 ADD CHECKSEQUENCEVERIFY"]], +"020000000100010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000", "NONE"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "2147483647 4259840 ADD CHECKSEQUENCEVERIFY"]], +"020000000100010000000000000000000000000000000000000000000000000000000000000000000000000040000100000000000000000000000000", "NONE"], + +["Valid CHECKSEQUENCEVERIFY in scriptSig"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "1"]], +"02000000010001000000000000000000000000000000000000000000000000000000000000000000000251b2010000000100000000000000000000000000", "SIGPUSHONLY,CLEANSTACK"], + +["Valid CHECKSEQUENCEVERIFY in redeemScript"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0x7c17aff532f22beb54069942f9bf567a66133eaf EQUAL"]], +"0200000001000100000000000000000000000000000000000000000000000000000000000000000000030251b2010000000100000000000000000000000000", "NONE"], + +["Valid CHECKSEQUENCEVERIFY even with negative tx version number"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0x7c17aff532f22beb54069942f9bf567a66133eaf EQUAL"]], +"ffffffff01000100000000000000000000000000000000000000000000000000000000000000000000030251b2010000000100000000000000000000000000", "NONE"], + +["Valid P2WPKH (Private key of segwit tests is L5AQtV2HDm4xGsseLokK2VAT2EtYKcTm3c7HwqnJBFt9LdaQULsM)"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x00 0x14 0x4c9c3dfac4207d5d8cb89df5722cb3d712385e3f", 1000]], +"0100000000010100010000000000000000000000000000000000000000000000000000000000000000000000ffffffff01e8030000000000001976a9144c9c3dfac4207d5d8cb89df5722cb3d712385e3f88ac02483045022100cfb07164b36ba64c1b1e8c7720a56ad64d96f6ef332d3d37f9cb3c96477dc44502200a464cd7a9cf94cd70f66ce4f4f0625ef650052c7afcfe29d7d7e01830ff91ed012103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc7100000000", "NONE"], + +["Valid P2WSH"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x00 0x20 0xff25429251b5a84f452230a3c75fd886b7fc5a7865ce4a7bb7a9d7c5be6da3db", 1000]], +"0100000000010100010000000000000000000000000000000000000000000000000000000000000000000000ffffffff01e8030000000000001976a9144c9c3dfac4207d5d8cb89df5722cb3d712385e3f88ac02483045022100aa5d8aa40a90f23ce2c3d11bc845ca4a12acd99cbea37de6b9f6d86edebba8cb022022dedc2aa0a255f74d04c0b76ece2d7c691f9dd11a64a8ac49f62a99c3a05f9d01232103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc71ac00000000", "NONE"], + +["Valid P2SH(P2WPKH)"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0xfe9c7dacc9fcfbf7e3b7d5ad06aa2b28c5a7b7e3 EQUAL", 1000]], +"01000000000101000100000000000000000000000000000000000000000000000000000000000000000000171600144c9c3dfac4207d5d8cb89df5722cb3d712385e3fffffffff01e8030000000000001976a9144c9c3dfac4207d5d8cb89df5722cb3d712385e3f88ac02483045022100cfb07164b36ba64c1b1e8c7720a56ad64d96f6ef332d3d37f9cb3c96477dc44502200a464cd7a9cf94cd70f66ce4f4f0625ef650052c7afcfe29d7d7e01830ff91ed012103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc7100000000", "NONE"], + +["Valid P2SH(P2WSH)"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0x2135ab4f0981830311e35600eebc7376dce3a914 EQUAL", 1000]], +"0100000000010100010000000000000000000000000000000000000000000000000000000000000000000023220020ff25429251b5a84f452230a3c75fd886b7fc5a7865ce4a7bb7a9d7c5be6da3dbffffffff01e8030000000000001976a9144c9c3dfac4207d5d8cb89df5722cb3d712385e3f88ac02483045022100aa5d8aa40a90f23ce2c3d11bc845ca4a12acd99cbea37de6b9f6d86edebba8cb022022dedc2aa0a255f74d04c0b76ece2d7c691f9dd11a64a8ac49f62a99c3a05f9d01232103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc71ac00000000", "NONE"], + +["Witness with SigHash Single|AnyoneCanPay"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 2, "0x51", 3100], +["0000000000000000000000000000000000000000000000000000000000000100", 1, "0x00 0x14 0x4c9c3dfac4207d5d8cb89df5722cb3d712385e3f", 2000], +["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x51", 1100], +["0000000000000000000000000000000000000000000000000000000000000100", 3, "0x51", 4100]], +"0100000000010400010000000000000000000000000000000000000000000000000000000000000200000000ffffffff00010000000000000000000000000000000000000000000000000000000000000100000000ffffffff00010000000000000000000000000000000000000000000000000000000000000000000000ffffffff00010000000000000000000000000000000000000000000000000000000000000300000000ffffffff05540b0000000000000151d0070000000000000151840300000000000001513c0f00000000000001512c010000000000000151000248304502210092f4777a0f17bf5aeb8ae768dec5f2c14feabf9d1fe2c89c78dfed0f13fdb86902206da90a86042e252bcd1e80a168c719e4a1ddcc3cebea24b9812c5453c79107e9832103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc71000000000000", "NONE"], + +["Witness with SigHash Single|AnyoneCanPay (same signature as previous)"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x51", 1000], +["0000000000000000000000000000000000000000000000000000000000000100", 1, "0x00 0x14 0x4c9c3dfac4207d5d8cb89df5722cb3d712385e3f", 2000], +["0000000000000000000000000000000000000000000000000000000000000100", 2, "0x51", 3000]], +"0100000000010300010000000000000000000000000000000000000000000000000000000000000000000000ffffffff00010000000000000000000000000000000000000000000000000000000000000100000000ffffffff00010000000000000000000000000000000000000000000000000000000000000200000000ffffffff03e8030000000000000151d0070000000000000151b80b0000000000000151000248304502210092f4777a0f17bf5aeb8ae768dec5f2c14feabf9d1fe2c89c78dfed0f13fdb86902206da90a86042e252bcd1e80a168c719e4a1ddcc3cebea24b9812c5453c79107e9832103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc710000000000", "NONE"], + +["Witness with SigHash Single"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x51", 1000], +["0000000000000000000000000000000000000000000000000000000000000100", 1, "0x00 0x14 0x4c9c3dfac4207d5d8cb89df5722cb3d712385e3f", 2000], +["0000000000000000000000000000000000000000000000000000000000000100", 2, "0x51", 3000]], +"0100000000010300010000000000000000000000000000000000000000000000000000000000000000000000ffffffff00010000000000000000000000000000000000000000000000000000000000000100000000ffffffff00010000000000000000000000000000000000000000000000000000000000000200000000ffffffff0484030000000000000151d0070000000000000151540b0000000000000151c800000000000000015100024730440220699e6b0cfe015b64ca3283e6551440a34f901ba62dd4c72fe1cb815afb2e6761022021cc5e84db498b1479de14efda49093219441adc6c543e5534979605e273d80b032103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc710000000000", "NONE"], + +["Witness with SigHash Single (same signature as previous)"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x51", 1000], +["0000000000000000000000000000000000000000000000000000000000000100", 1, "0x00 0x14 0x4c9c3dfac4207d5d8cb89df5722cb3d712385e3f", 2000], +["0000000000000000000000000000000000000000000000000000000000000100", 2, "0x51", 3000]], +"0100000000010300010000000000000000000000000000000000000000000000000000000000000000000000ffffffff00010000000000000000000000000000000000000000000000000000000000000100000000ffffffff00010000000000000000000000000000000000000000000000000000000000000200000000ffffffff03e8030000000000000151d0070000000000000151b80b000000000000015100024730440220699e6b0cfe015b64ca3283e6551440a34f901ba62dd4c72fe1cb815afb2e6761022021cc5e84db498b1479de14efda49093219441adc6c543e5534979605e273d80b032103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc710000000000", "NONE"], + +["Witness with SigHash None|AnyoneCanPay"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 2, "0x51", 3100], +["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x51", 1100], +["0000000000000000000000000000000000000000000000000000000000000100", 1, "0x00 0x14 0x4c9c3dfac4207d5d8cb89df5722cb3d712385e3f", 2000], +["0000000000000000000000000000000000000000000000000000000000000100", 3, "0x51", 4100]], +"0100000000010400010000000000000000000000000000000000000000000000000000000000000200000000ffffffff00010000000000000000000000000000000000000000000000000000000000000000000000ffffffff00010000000000000000000000000000000000000000000000000000000000000100000000ffffffff00010000000000000000000000000000000000000000000000000000000000000300000000ffffffff04b60300000000000001519e070000000000000151860b00000000000001009600000000000000015100000248304502210091b32274295c2a3fa02f5bce92fb2789e3fc6ea947fbe1a76e52ea3f4ef2381a022079ad72aefa3837a2e0c033a8652a59731da05fa4a813f4fc48e87c075037256b822103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc710000000000", "NONE"], + +["Witness with SigHash None|AnyoneCanPay (same signature as previous)"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x51", 1000], +["0000000000000000000000000000000000000000000000000000000000000100", 1, "0x00 0x14 0x4c9c3dfac4207d5d8cb89df5722cb3d712385e3f", 2000], +["0000000000000000000000000000000000000000000000000000000000000100", 2, "0x51", 3000]], +"0100000000010300010000000000000000000000000000000000000000000000000000000000000000000000ffffffff00010000000000000000000000000000000000000000000000000000000000000100000000ffffffff00010000000000000000000000000000000000000000000000000000000000000200000000ffffffff03e8030000000000000151d0070000000000000151b80b0000000000000151000248304502210091b32274295c2a3fa02f5bce92fb2789e3fc6ea947fbe1a76e52ea3f4ef2381a022079ad72aefa3837a2e0c033a8652a59731da05fa4a813f4fc48e87c075037256b822103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc710000000000", "NONE"], + +["Witness with SigHash None"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x51", 1000], +["0000000000000000000000000000000000000000000000000000000000000100", 1, "0x00 0x14 0x4c9c3dfac4207d5d8cb89df5722cb3d712385e3f", 2000], +["0000000000000000000000000000000000000000000000000000000000000100", 2, "0x51", 3000]], +"0100000000010300010000000000000000000000000000000000000000000000000000000000000000000000ffffffff00010000000000000000000000000000000000000000000000000000000000000100000000ffffffff00010000000000000000000000000000000000000000000000000000000000000200000000ffffffff04b60300000000000001519e070000000000000151860b0000000000000100960000000000000001510002473044022022fceb54f62f8feea77faac7083c3b56c4676a78f93745adc8a35800bc36adfa022026927df9abcf0a8777829bcfcce3ff0a385fa54c3f9df577405e3ef24ee56479022103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc710000000000", "NONE"], + +["Witness with SigHash None (same signature as previous)"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x51", 1000], +["0000000000000000000000000000000000000000000000000000000000000100", 1, "0x00 0x14 0x4c9c3dfac4207d5d8cb89df5722cb3d712385e3f", 2000], +["0000000000000000000000000000000000000000000000000000000000000100", 2, "0x51", 3000]], +"0100000000010300010000000000000000000000000000000000000000000000000000000000000000000000ffffffff00010000000000000000000000000000000000000000000000000000000000000100000000ffffffff00010000000000000000000000000000000000000000000000000000000000000200000000ffffffff03e8030000000000000151d0070000000000000151b80b00000000000001510002473044022022fceb54f62f8feea77faac7083c3b56c4676a78f93745adc8a35800bc36adfa022026927df9abcf0a8777829bcfcce3ff0a385fa54c3f9df577405e3ef24ee56479022103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc710000000000", "NONE"], + +["Witness with SigHash None (same signature, only sequences changed)"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x51", 1000], +["0000000000000000000000000000000000000000000000000000000000000100", 1, "0x00 0x14 0x4c9c3dfac4207d5d8cb89df5722cb3d712385e3f", 2000], +["0000000000000000000000000000000000000000000000000000000000000100", 2, "0x51", 3000]], +"01000000000103000100000000000000000000000000000000000000000000000000000000000000000000000200000000010000000000000000000000000000000000000000000000000000000000000100000000ffffffff000100000000000000000000000000000000000000000000000000000000000002000000000200000003e8030000000000000151d0070000000000000151b80b00000000000001510002473044022022fceb54f62f8feea77faac7083c3b56c4676a78f93745adc8a35800bc36adfa022026927df9abcf0a8777829bcfcce3ff0a385fa54c3f9df577405e3ef24ee56479022103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc710000000000", "NONE"], + +["Witness with SigHash All|AnyoneCanPay"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 2, "0x51", 3100], +["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x51", 1100], +["0000000000000000000000000000000000000000000000000000000000000100", 1, "0x00 0x14 0x4c9c3dfac4207d5d8cb89df5722cb3d712385e3f", 2000], +["0000000000000000000000000000000000000000000000000000000000000100", 3, "0x51", 4100]], +"0100000000010400010000000000000000000000000000000000000000000000000000000000000200000000ffffffff00010000000000000000000000000000000000000000000000000000000000000000000000ffffffff00010000000000000000000000000000000000000000000000000000000000000100000000ffffffff00010000000000000000000000000000000000000000000000000000000000000300000000ffffffff03e8030000000000000151d0070000000000000151b80b0000000000000151000002483045022100a3cec69b52cba2d2de623eeef89e0ba1606184ea55476c0f8189fda231bc9cbb022003181ad597f7c380a7d1c740286b1d022b8b04ded028b833282e055e03b8efef812103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc710000000000", "NONE"], + +["Witness with SigHash All|AnyoneCanPay (same signature as previous)"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x51", 1000], +["0000000000000000000000000000000000000000000000000000000000000100", 1, "0x00 0x14 0x4c9c3dfac4207d5d8cb89df5722cb3d712385e3f", 2000], +["0000000000000000000000000000000000000000000000000000000000000100", 2, "0x51", 3000]], +"0100000000010300010000000000000000000000000000000000000000000000000000000000000000000000ffffffff00010000000000000000000000000000000000000000000000000000000000000100000000ffffffff00010000000000000000000000000000000000000000000000000000000000000200000000ffffffff03e8030000000000000151d0070000000000000151b80b00000000000001510002483045022100a3cec69b52cba2d2de623eeef89e0ba1606184ea55476c0f8189fda231bc9cbb022003181ad597f7c380a7d1c740286b1d022b8b04ded028b833282e055e03b8efef812103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc710000000000", "NONE"], + +["Unknown witness program version (without DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM)"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x51", 1000], +["0000000000000000000000000000000000000000000000000000000000000100", 1, "0x60 0x14 0x4c9c3dfac4207d5d8cb89df5722cb3d712385e3f", 2000], +["0000000000000000000000000000000000000000000000000000000000000100", 2, "0x51", 3000]], +"0100000000010300010000000000000000000000000000000000000000000000000000000000000000000000ffffffff00010000000000000000000000000000000000000000000000000000000000000100000000ffffffff00010000000000000000000000000000000000000000000000000000000000000200000000ffffffff03e8030000000000000151d0070000000000000151b80b00000000000001510002483045022100a3cec69b52cba2d2de623ffffffffff1606184ea55476c0f8189fda231bc9cbb022003181ad597f7c380a7d1c740286b1d022b8b04ded028b833282e055e03b8efef812103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc710000000000", "DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM"], + +["Witness with a push of 520 bytes"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x00 0x20 0x33198a9bfef674ebddb9ffaa52928017b8472791e54c609cb95f278ac6b1e349", 1000]], +"0100000000010100010000000000000000000000000000000000000000000000000000000000000000000000ffffffff010000000000000000015102fd08020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002755100000000", "NONE"], + +["Transaction mixing all SigHash, segwit and normal inputs"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x00 0x14 0x4c9c3dfac4207d5d8cb89df5722cb3d712385e3f", 1000], +["0000000000000000000000000000000000000000000000000000000000000100", 1, "0x00 0x14 0x4c9c3dfac4207d5d8cb89df5722cb3d712385e3f", 1001], +["0000000000000000000000000000000000000000000000000000000000000100", 2, "DUP HASH160 0x14 0x4c9c3dfac4207d5d8cb89df5722cb3d712385e3f EQUALVERIFY CHECKSIG", 1002], +["0000000000000000000000000000000000000000000000000000000000000100", 3, "DUP HASH160 0x14 0x4c9c3dfac4207d5d8cb89df5722cb3d712385e3f EQUALVERIFY CHECKSIG", 1003], +["0000000000000000000000000000000000000000000000000000000000000100", 4, "DUP HASH160 0x14 0x4c9c3dfac4207d5d8cb89df5722cb3d712385e3f EQUALVERIFY CHECKSIG", 1004], +["0000000000000000000000000000000000000000000000000000000000000100", 5, "DUP HASH160 0x14 0x4c9c3dfac4207d5d8cb89df5722cb3d712385e3f EQUALVERIFY CHECKSIG", 1005], +["0000000000000000000000000000000000000000000000000000000000000100", 6, "DUP HASH160 0x14 0x4c9c3dfac4207d5d8cb89df5722cb3d712385e3f EQUALVERIFY CHECKSIG", 1006], +["0000000000000000000000000000000000000000000000000000000000000100", 7, "0x00 0x14 0x4c9c3dfac4207d5d8cb89df5722cb3d712385e3f", 1007], +["0000000000000000000000000000000000000000000000000000000000000100", 8, "0x00 0x14 0x4c9c3dfac4207d5d8cb89df5722cb3d712385e3f", 1008], +["0000000000000000000000000000000000000000000000000000000000000100", 9, "0x00 0x14 0x4c9c3dfac4207d5d8cb89df5722cb3d712385e3f", 1009], +["0000000000000000000000000000000000000000000000000000000000000100", 10, "0x00 0x14 0x4c9c3dfac4207d5d8cb89df5722cb3d712385e3f", 1010], +["0000000000000000000000000000000000000000000000000000000000000100", 11, "DUP HASH160 0x14 0x4c9c3dfac4207d5d8cb89df5722cb3d712385e3f EQUALVERIFY CHECKSIG", 1011]], +"0100000000010c00010000000000000000000000000000000000000000000000000000000000000000000000ffffffff00010000000000000000000000000000000000000000000000000000000000000100000000ffffffff0001000000000000000000000000000000000000000000000000000000000000020000006a473044022026c2e65b33fcd03b2a3b0f25030f0244bd23cc45ae4dec0f48ae62255b1998a00220463aa3982b718d593a6b9e0044513fd67a5009c2fdccc59992cffc2b167889f4012103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc71ffffffff0001000000000000000000000000000000000000000000000000000000000000030000006a4730440220008bd8382911218dcb4c9f2e75bf5c5c3635f2f2df49b36994fde85b0be21a1a02205a539ef10fb4c778b522c1be852352ea06c67ab74200977c722b0bc68972575a012103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc71ffffffff0001000000000000000000000000000000000000000000000000000000000000040000006b483045022100d9436c32ff065127d71e1a20e319e4fe0a103ba0272743dbd8580be4659ab5d302203fd62571ee1fe790b182d078ecfd092a509eac112bea558d122974ef9cc012c7012103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc71ffffffff0001000000000000000000000000000000000000000000000000000000000000050000006a47304402200e2c149b114ec546015c13b2b464bbcb0cdc5872e6775787527af6cbc4830b6c02207e9396c6979fb15a9a2b96ca08a633866eaf20dc0ff3c03e512c1d5a1654f148012103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc71ffffffff0001000000000000000000000000000000000000000000000000000000000000060000006b483045022100b20e70d897dc15420bccb5e0d3e208d27bdd676af109abbd3f88dbdb7721e6d6022005836e663173fbdfe069f54cde3c2decd3d0ea84378092a5d9d85ec8642e8a41012103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc71ffffffff00010000000000000000000000000000000000000000000000000000000000000700000000ffffffff00010000000000000000000000000000000000000000000000000000000000000800000000ffffffff00010000000000000000000000000000000000000000000000000000000000000900000000ffffffff00010000000000000000000000000000000000000000000000000000000000000a00000000ffffffff00010000000000000000000000000000000000000000000000000000000000000b0000006a47304402206639c6e05e3b9d2675a7f3876286bdf7584fe2bbd15e0ce52dd4e02c0092cdc60220757d60b0a61fc95ada79d23746744c72bac1545a75ff6c2c7cdb6ae04e7e9592012103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc71ffffffff0ce8030000000000000151e9030000000000000151ea030000000000000151eb030000000000000151ec030000000000000151ed030000000000000151ee030000000000000151ef030000000000000151f0030000000000000151f1030000000000000151f2030000000000000151f30300000000000001510248304502210082219a54f61bf126bfc3fa068c6e33831222d1d7138c6faa9d33ca87fd4202d6022063f9902519624254d7c2c8ea7ba2d66ae975e4e229ae38043973ec707d5d4a83012103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc7102473044022017fb58502475848c1b09f162cb1688d0920ff7f142bed0ef904da2ccc88b168f02201798afa61850c65e77889cbcd648a5703b487895517c88f85cdd18b021ee246a012103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc7100000000000247304402202830b7926e488da75782c81a54cd281720890d1af064629ebf2e31bf9f5435f30220089afaa8b455bbeb7d9b9c3fe1ed37d07685ade8455c76472cda424d93e4074a012103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc7102473044022026326fcdae9207b596c2b05921dbac11d81040c4d40378513670f19d9f4af893022034ecd7a282c0163b89aaa62c22ec202cef4736c58cd251649bad0d8139bcbf55012103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc71024730440220214978daeb2f38cd426ee6e2f44131a33d6b191af1c216247f1dd7d74c16d84a02205fdc05529b0bc0c430b4d5987264d9d075351c4f4484c16e91662e90a72aab24012103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc710247304402204a6e9f199dc9672cf2ff8094aaa784363be1eb62b679f7ff2df361124f1dca3302205eeb11f70fab5355c9c8ad1a0700ea355d315e334822fa182227e9815308ee8f012103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc710000000000", "NONE"], + +["Unknown version witness program with empty witness"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x60 0x14 0x4c9c3dfac4207d5d8cb89df5722cb3d712385e3f", 1000]], +"010000000100010000000000000000000000000000000000000000000000000000000000000000000000ffffffff01e803000000000000015100000000", "DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM"], + +["Witness SIGHASH_SINGLE with output out of bound"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x51", 1000], +["0000000000000000000000000000000000000000000000000000000000000100", 1, "0x00 0x20 0x4d6c2a32c87821d68fc016fca70797abdb80df6cd84651d40a9300c6bad79e62", 1000]], +"0100000000010200010000000000000000000000000000000000000000000000000000000000000000000000ffffffff00010000000000000000000000000000000000000000000000000000000000000100000000ffffffff01d00700000000000001510003483045022100e078de4e96a0e05dcdc0a414124dd8475782b5f3f0ed3f607919e9a5eeeb22bf02201de309b3a3109adb3de8074b3610d4cf454c49b61247a2779a0bcbf31c889333032103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc711976a9144c9c3dfac4207d5d8cb89df5722cb3d712385e3f88ac00000000", "NONE"], + +["1 byte push should not be considered a witness scriptPubKey"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x60 0x01 0x01", 1000]], +"010000000100010000000000000000000000000000000000000000000000000000000000000000000000ffffffff01e803000000000000015100000000", "MINIMALDATA,CLEANSTACK"], + +["41 bytes push should not be considered a witness scriptPubKey"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x60 0x29 0xff25429251b5a84f452230a3c75fd886b7fc5a7865ce4a7bb7a9d7c5be6da3dbff0000000000000000", 1000]], +"010000000100010000000000000000000000000000000000000000000000000000000000000000000000ffffffff01e803000000000000015100000000", "CLEANSTACK"], + +["The witness version must use OP_1 to OP_16 only"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x01 0x10 0x02 0x0001", 1000]], +"010000000100010000000000000000000000000000000000000000000000000000000000000000000000ffffffff01e803000000000000015100000000", "MINIMALDATA,CLEANSTACK"], + +["The witness program push must be canonical"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x60 0x4c02 0x0001", 1000]], +"010000000100010000000000000000000000000000000000000000000000000000000000000000000000ffffffff01e803000000000000015100000000", "MINIMALDATA,CLEANSTACK"], + +["Witness Single|AnyoneCanPay does not hash input's position"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x00 0x14 0x4c9c3dfac4207d5d8cb89df5722cb3d712385e3f", 1000], +["0000000000000000000000000000000000000000000000000000000000000100", 1, "0x00 0x14 0x4c9c3dfac4207d5d8cb89df5722cb3d712385e3f", 1001]], +"0100000000010200010000000000000000000000000000000000000000000000000000000000000000000000ffffffff00010000000000000000000000000000000000000000000000000000000000000100000000ffffffff02e8030000000000000151e90300000000000001510247304402206d59682663faab5e4cb733c562e22cdae59294895929ec38d7c016621ff90da0022063ef0af5f970afe8a45ea836e3509b8847ed39463253106ac17d19c437d3d56b832103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc710248304502210085001a820bfcbc9f9de0298af714493f8a37b3b354bfd21a7097c3e009f2018c022050a8b4dbc8155d4d04da2f5cdd575dcf8dd0108de8bec759bd897ea01ecb3af7832103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc7100000000", "NONE"], + +["Witness Single|AnyoneCanPay does not hash input's position (permutation)"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 1, "0x00 0x14 0x4c9c3dfac4207d5d8cb89df5722cb3d712385e3f", 1001], +["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x00 0x14 0x4c9c3dfac4207d5d8cb89df5722cb3d712385e3f", 1000]], +"0100000000010200010000000000000000000000000000000000000000000000000000000000000100000000ffffffff00010000000000000000000000000000000000000000000000000000000000000000000000ffffffff02e9030000000000000151e80300000000000001510248304502210085001a820bfcbc9f9de0298af714493f8a37b3b354bfd21a7097c3e009f2018c022050a8b4dbc8155d4d04da2f5cdd575dcf8dd0108de8bec759bd897ea01ecb3af7832103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc710247304402206d59682663faab5e4cb733c562e22cdae59294895929ec38d7c016621ff90da0022063ef0af5f970afe8a45ea836e3509b8847ed39463253106ac17d19c437d3d56b832103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc7100000000", "NONE"], + +["Non witness Single|AnyoneCanPay hash input's position"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x21 0x03596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc71 CHECKSIG", 1000], +["0000000000000000000000000000000000000000000000000000000000000100", 1, "0x21 0x03596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc71 CHECKSIG", 1001]], +"01000000020001000000000000000000000000000000000000000000000000000000000000000000004847304402202a0b4b1294d70540235ae033d78e64b4897ec859c7b6f1b2b1d8a02e1d46006702201445e756d2254b0f1dfda9ab8e1e1bc26df9668077403204f32d16a49a36eb6983ffffffff00010000000000000000000000000000000000000000000000000000000000000100000049483045022100acb96cfdbda6dc94b489fd06f2d720983b5f350e31ba906cdbd800773e80b21c02200d74ea5bdf114212b4bbe9ed82c36d2e369e302dff57cb60d01c428f0bd3daab83ffffffff02e8030000000000000151e903000000000000015100000000", "NONE"], + +["BIP143 examples: details and private keys are available in BIP143"], +["BIP143 example: P2WSH with OP_CODESEPARATOR and out-of-range SIGHASH_SINGLE."], +[[["6eb316926b1c5d567cd6f5e6a84fec606fc53d7b474526d1fff3948020c93dfe", 0, "0x21 0x036d5c20fa14fb2f635474c1dc4ef5909d4568e5569b79fc94d3448486e14685f8 CHECKSIG", 156250000], +["f825690aee1b3dc247da796cacb12687a5e802429fd291cfd63e010f02cf1508", 0, "0x00 0x20 0x5d1b56b63d714eebe542309525f484b7e9d6f686b3781b6f61ef925d66d6f6a0", 4900000000]], +"01000000000102fe3dc9208094f3ffd12645477b3dc56f60ec4fa8e6f5d67c565d1c6b9216b36e000000004847304402200af4e47c9b9629dbecc21f73af989bdaa911f7e6f6c2e9394588a3aa68f81e9902204f3fcf6ade7e5abb1295b6774c8e0abd94ae62217367096bc02ee5e435b67da201ffffffff0815cf020f013ed6cf91d29f4202e8a58726b1ac6c79da47c23d1bee0a6925f80000000000ffffffff0100f2052a010000001976a914a30741f8145e5acadf23f751864167f32e0963f788ac000347304402200de66acf4527789bfda55fc5459e214fa6083f936b430a762c629656216805ac0220396f550692cd347171cbc1ef1f51e15282e837bb2b30860dc77c8f78bc8501e503473044022027dc95ad6b740fe5129e7e62a75dd00f291a2aeb1200b84b09d9e3789406b6c002201a9ecd315dd6a0e632ab20bbb98948bc0c6fb204f2c286963bb48517a7058e27034721026dccc749adc2a9d0d89497ac511f760f45c47dc5ed9cf352a58ac706453880aeadab210255a9626aebf5e29c0e6538428ba0d1dcf6ca98ffdf086aa8ced5e0d0215ea465ac00000000", "NONE"], + +["BIP143 example: P2WSH with unexecuted OP_CODESEPARATOR and SINGLE|ANYONECANPAY"], +[[["01c0cf7fba650638e55eb91261b183251fbb466f90dff17f10086817c542b5e9", 0, "0x00 0x20 0xba468eea561b26301e4cf69fa34bde4ad60c81e70f059f045ca9a79931004a4d", 16777215], +["1b2a9a426ba603ba357ce7773cb5805cb9c7c2b386d100d1fc9263513188e680", 0, "0x00 0x20 0xd9bbfbe56af7c4b7f960a70d7ea107156913d9e5a26b0a71429df5e097ca6537", 16777215]], +"01000000000102e9b542c5176808107ff1df906f46bb1f2583b16112b95ee5380665ba7fcfc0010000000000ffffffff80e68831516392fcd100d186b3c2c7b95c80b53c77e77c35ba03a66b429a2a1b0000000000ffffffff0280969800000000001976a914de4b231626ef508c9a74a8517e6783c0546d6b2888ac80969800000000001976a9146648a8cd4531e1ec47f35916de8e259237294d1e88ac02483045022100f6a10b8604e6dc910194b79ccfc93e1bc0ec7c03453caaa8987f7d6c3413566002206216229ede9b4d6ec2d325be245c5b508ff0339bf1794078e20bfe0babc7ffe683270063ab68210392972e2eb617b2388771abe27235fd5ac44af8e61693261550447a4c3e39da98ac024730440220032521802a76ad7bf74d0e2c218b72cf0cbc867066e2e53db905ba37f130397e02207709e2188ed7f08f4c952d9d13986da504502b8c3be59617e043552f506c46ff83275163ab68210392972e2eb617b2388771abe27235fd5ac44af8e61693261550447a4c3e39da98ac00000000", "NONE"], + +["BIP143 example: Same as the previous example with input-output pairs swapped"], +[[["1b2a9a426ba603ba357ce7773cb5805cb9c7c2b386d100d1fc9263513188e680", 0, "0x00 0x20 0xd9bbfbe56af7c4b7f960a70d7ea107156913d9e5a26b0a71429df5e097ca6537", 16777215], +["01c0cf7fba650638e55eb91261b183251fbb466f90dff17f10086817c542b5e9", 0, "0x00 0x20 0xba468eea561b26301e4cf69fa34bde4ad60c81e70f059f045ca9a79931004a4d", 16777215]], +"0100000000010280e68831516392fcd100d186b3c2c7b95c80b53c77e77c35ba03a66b429a2a1b0000000000ffffffffe9b542c5176808107ff1df906f46bb1f2583b16112b95ee5380665ba7fcfc0010000000000ffffffff0280969800000000001976a9146648a8cd4531e1ec47f35916de8e259237294d1e88ac80969800000000001976a914de4b231626ef508c9a74a8517e6783c0546d6b2888ac024730440220032521802a76ad7bf74d0e2c218b72cf0cbc867066e2e53db905ba37f130397e02207709e2188ed7f08f4c952d9d13986da504502b8c3be59617e043552f506c46ff83275163ab68210392972e2eb617b2388771abe27235fd5ac44af8e61693261550447a4c3e39da98ac02483045022100f6a10b8604e6dc910194b79ccfc93e1bc0ec7c03453caaa8987f7d6c3413566002206216229ede9b4d6ec2d325be245c5b508ff0339bf1794078e20bfe0babc7ffe683270063ab68210392972e2eb617b2388771abe27235fd5ac44af8e61693261550447a4c3e39da98ac00000000", "NONE"], + +["BIP143 example: P2SH-P2WSH 6-of-6 multisig signed with 6 different SIGHASH types"], +[[["6eb98797a21c6c10aa74edf29d618be109f48a8e94c694f3701e08ca69186436", 1, "HASH160 0x14 0x9993a429037b5d912407a71c252019287b8d27a5 EQUAL", 987654321]], +"0100000000010136641869ca081e70f394c6948e8af409e18b619df2ed74aa106c1ca29787b96e0100000023220020a16b5755f7f6f96dbd65f5f0d6ab9418b89af4b1f14a1bb8a09062c35f0dcb54ffffffff0200e9a435000000001976a914389ffce9cd9ae88dcc0631e88a821ffdbe9bfe2688acc0832f05000000001976a9147480a33f950689af511e6e84c138dbbd3c3ee41588ac080047304402206ac44d672dac41f9b00e28f4df20c52eeb087207e8d758d76d92c6fab3b73e2b0220367750dbbe19290069cba53d096f44530e4f98acaa594810388cf7409a1870ce01473044022068c7946a43232757cbdf9176f009a928e1cd9a1a8c212f15c1e11ac9f2925d9002205b75f937ff2f9f3c1246e547e54f62e027f64eefa2695578cc6432cdabce271502473044022059ebf56d98010a932cf8ecfec54c48e6139ed6adb0728c09cbe1e4fa0915302e022007cd986c8fa870ff5d2b3a89139c9fe7e499259875357e20fcbb15571c76795403483045022100fbefd94bd0a488d50b79102b5dad4ab6ced30c4069f1eaa69a4b5a763414067e02203156c6a5c9cf88f91265f5a942e96213afae16d83321c8b31bb342142a14d16381483045022100a5263ea0553ba89221984bd7f0b13613db16e7a70c549a86de0cc0444141a407022005c360ef0ae5a5d4f9f2f87a56c1546cc8268cab08c73501d6b3be2e1e1a8a08824730440220525406a1482936d5a21888260dc165497a90a15669636d8edca6b9fe490d309c022032af0c646a34a44d1f4576bf6a4a74b67940f8faa84c7df9abe12a01a11e2b4783cf56210307b8ae49ac90a048e9b53357a2354b3334e9c8bee813ecb98e99a7e07e8c3ba32103b28f0c28bfab54554ae8c658ac5c3e0ce6e79ad336331f78c428dd43eea8449b21034b8113d703413d57761b8b9781957b8c0ac1dfe69f492580ca4195f50376ba4a21033400f6afecb833092a9a21cfdf1ed1376e58c5d1f47de74683123987e967a8f42103a6d48b1131e94ba04d9737d61acdaa1322008af9602b3b14862c07a1789aac162102d8b661b0b3302ee2f162b09e07a55ad5dfbe673a9f01d9f0c19617681024306b56ae00000000", "NONE"], + +["FindAndDelete tests"], +["This is a test of FindAndDelete. The first tx is a spend of normal P2SH and the second tx is a spend of bare P2WSH."], +["The redeemScript/witnessScript is CHECKSIGVERIFY <0x30450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e01>."], +["The signature is <0x30450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e01> ,"], +["where the pubkey is obtained through key recovery with sig and correct sighash."], +["This is to show that FindAndDelete is applied only to non-segwit scripts"], +["Non-segwit: correct sighash (with FindAndDelete) = 1ba1fe3bc90c5d1265460e684ce6774e324f0fabdf67619eda729e64e8b6bc08"], +[[["f18783ace138abac5d3a7a5cf08e88fe6912f267ef936452e0c27d090621c169", 7000, "HASH160 0x14 0x0c746489e2d83cdbb5b90b432773342ba809c134 EQUAL", 200000]], +"010000000169c12106097dc2e0526493ef67f21269fe888ef05c7a3a5dacab38e1ac8387f1581b0000b64830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0121037a3fb04bcdb09eba90f69961ba1692a3528e45e67c85b200df820212d7594d334aad4830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e01ffffffff0101000000000000000000000000", "CONST_SCRIPTCODE,LOW_S"], +["BIP143: correct sighash (without FindAndDelete) = 71c9cd9b2869b9c70b01b1f0360c148f42dee72297db312638df136f43311f23"], +[[["f18783ace138abac5d3a7a5cf08e88fe6912f267ef936452e0c27d090621c169", 7500, "0x00 0x20 0x9e1be07558ea5cc8e02ed1d80c0911048afad949affa36d5c3951e3159dbea19", 200000]], +"0100000000010169c12106097dc2e0526493ef67f21269fe888ef05c7a3a5dacab38e1ac8387f14c1d000000ffffffff01010000000000000000034830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e012102a9781d66b61fb5a7ef00ac5ad5bc6ffc78be7b44a566e3c87870e1079368df4c4aad4830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0100000000", "LOW_S"], +["This is multisig version of the FindAndDelete tests"], +["Script is 2 CHECKMULTISIGVERIFY DROP"], +["52af4830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0148304502205286f726690b2e9b0207f0345711e63fa7012045b9eb0f19c2458ce1db90cf43022100e89f17f86abc5b149eba4115d4f128bcf45d77fb3ecdd34f594091340c0395960175"], +["Signature is 0 2 "], +["Non-segwit: correct sighash (with FindAndDelete) = 1d50f00ba4db2917b903b0ec5002e017343bb38876398c9510570f5dce099295"], +[[["9628667ad48219a169b41b020800162287d2c0f713c04157e95c484a8dcb7592", 7000, "HASH160 0x14 0x5748407f5ca5cdca53ba30b79040260770c9ee1b EQUAL", 200000]], +"01000000019275cb8d4a485ce95741c013f7c0d28722160008021bb469a11982d47a662896581b0000fd6f01004830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0148304502205286f726690b2e9b0207f0345711e63fa7012045b9eb0f19c2458ce1db90cf43022100e89f17f86abc5b149eba4115d4f128bcf45d77fb3ecdd34f594091340c03959601522102cd74a2809ffeeed0092bc124fd79836706e41f048db3f6ae9df8708cefb83a1c2102e615999372426e46fd107b76eaf007156a507584aa2cc21de9eee3bdbd26d36c4c9552af4830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0148304502205286f726690b2e9b0207f0345711e63fa7012045b9eb0f19c2458ce1db90cf43022100e89f17f86abc5b149eba4115d4f128bcf45d77fb3ecdd34f594091340c0395960175ffffffff0101000000000000000000000000", "CONST_SCRIPTCODE,LOW_S"], +["BIP143: correct sighash (without FindAndDelete) = c1628a1e7c67f14ca0c27c06e4fdeec2e6d1a73c7a91d7c046ff83e835aebb72"], +[[["9628667ad48219a169b41b020800162287d2c0f713c04157e95c484a8dcb7592", 7500, "0x00 0x20 0x9b66c15b4e0b4eb49fa877982cafded24859fe5b0e2dbfbe4f0df1de7743fd52", 200000]], +"010000000001019275cb8d4a485ce95741c013f7c0d28722160008021bb469a11982d47a6628964c1d000000ffffffff0101000000000000000007004830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0148304502205286f726690b2e9b0207f0345711e63fa7012045b9eb0f19c2458ce1db90cf43022100e89f17f86abc5b149eba4115d4f128bcf45d77fb3ecdd34f594091340c0395960101022102966f109c54e85d3aee8321301136cedeb9fc710fdef58a9de8a73942f8e567c021034ffc99dd9a79dd3cb31e2ab3e0b09e0e67db41ac068c625cd1f491576016c84e9552af4830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0148304502205286f726690b2e9b0207f0345711e63fa7012045b9eb0f19c2458ce1db90cf43022100e89f17f86abc5b149eba4115d4f128bcf45d77fb3ecdd34f594091340c039596017500000000", "LOW_S"], + +["Test long outputs, which are streamed using length-prefixed bitcoin strings. This might be surprising."], +[[["1111111111111111111111111111111111111111111111111111111111111111", 0, "0x00 0x14 0x751e76e8199196d454941c45d1b3a323f1433bd6", 5000000]], +"0100000000010111111111111111111111111111111111111111111111111111111111111111110000000000ffffffff0130244c0000000000fd02014cdc1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111175210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798ac02483045022100c1a4a6581996a7fdfea77d58d537955a5655c1d619b6f3ab6874f28bb2e19708022056402db6fede03caae045a3be616a1a2d0919a475ed4be828dc9ff21f24063aa01210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179800000000", "NONE"], + +["Make diffs cleaner by leaving a comment here without comma at the end"] +] diff --git a/crates/script_verify/tests/differential.rs b/crates/script_verify/tests/differential.rs new file mode 100644 index 0000000000..aa3cde4ed5 --- /dev/null +++ b/crates/script_verify/tests/differential.rs @@ -0,0 +1,2642 @@ +//! Differential tests: `bdk_script_verify` must reach Bitcoin Core's verdict on every input of +//! every generated transaction. Core is libbitcoinkernel, through the `bitcoinkernel` crate pinned +//! at 0.3.2. That builds Bitcoin Core master at `70d9ec7f` (2026-07-14) from source, since every +//! published `bitcoinkernel` bundles a master snapshot rather than a release. Its script +//! interpreter differs from v32.1 only by refactors. Building it needs CMake, a C++ compiler and +//! Boost's headers. +//! +//! Cases are generated as plain descriptions (a seed spend and a list of mutations) rather than as +//! bytes, so that proptest shrinks a disagreement to a readable spend. +//! +//! The case count is proptest's, `PROPTEST_CASES` (256 by default). A long local run: +//! `PROPTEST_CASES=100000 cargo test --release -p bdk_script_verify --test differential`. + +use std::sync::LazyLock; + +use bdk_script_verify::{Error, Flags}; +use bitcoin::absolute::LockTime; +use bitcoin::consensus::encode::{serialize, serialize_hex}; +use bitcoin::hashes::{hash160, ripemd160, sha256, sha256d, Hash}; +use bitcoin::key::{Keypair, TapTweak}; +use bitcoin::opcodes::all::*; +use bitcoin::opcodes::Opcode; +use bitcoin::secp256k1::{All, Message, Secp256k1, SecretKey, XOnlyPublicKey}; +use bitcoin::sighash::Annex; +use bitcoin::sighash::{EcdsaSighashType, Prevouts, SighashCache, TapSighashType}; +use bitcoin::taproot::{LeafVersion, TapLeafHash, TapNodeHash, TaprootBuilder}; +use bitcoin::transaction::Version; +use bitcoin::{ + relative, Amount, OutPoint, PublicKey, ScriptBuf, Sequence, Transaction, TxIn, TxOut, Txid, + Witness, +}; +use miniscript::policy::Concrete; +use miniscript::{Descriptor, Satisfier, Segwitv0}; +use proptest::collection::vec; +use proptest::prelude::*; +use proptest::test_runner::TestCaseError; + +static SECP: LazyLock> = LazyLock::new(Secp256k1::new); + +/// An unspendable internal key, so a taproot output with no key in its policy has no key path. +const NUMS: &str = "50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0"; + +const MAX_MONEY: u64 = 21_000_000 * 100_000_000; + +#[derive(Debug, Clone)] +struct Case { + tx: Transaction, + prevouts: Vec, + flags: Flags, +} + +fn core_flags(flags: Flags) -> bitcoinkernel::ScriptVerificationFlags { + use bitcoinkernel as k; + [ + (flags.p2sh, k::VERIFY_P2SH), + (flags.dersig, k::VERIFY_DERSIG), + (flags.checklocktimeverify, k::VERIFY_CHECKLOCKTIMEVERIFY), + (flags.checksequenceverify, k::VERIFY_CHECKSEQUENCEVERIFY), + (flags.witness, k::VERIFY_WITNESS), + (flags.nulldummy, k::VERIFY_NULLDUMMY), + (flags.taproot, k::VERIFY_TAPROOT), + ] + .into_iter() + .filter(|(on, _)| *on) + .fold(k::VERIFY_NONE, |acc, (_, bit)| acc | bit) +} + +/// Only a script failure is invalid. The kernel's other errors mean the case itself is malformed, +/// which must not be counted as agreement, so they panic. +fn core_verdict(case: &Case, tx_bytes: &[u8], vin: usize) -> bool { + use bitcoinkernel as k; + let malformed = |e: k::KernelError| -> ! { + panic!( + "malformed case, the kernel says {e:?}:\n{}", + report(case, vin) + ) + }; + let tx = k::Transaction::new(tx_bytes).unwrap_or_else(|e| malformed(e)); + let spent = case + .prevouts + .iter() + .map(|txout| { + let script = k::ScriptPubkey::new(txout.script_pubkey.as_bytes()) + .unwrap_or_else(|e| malformed(e)); + k::TxOut::new(&script, txout.value.to_sat() as i64) + }) + .collect::>(); + let tx_data = k::PrecomputedTransactionData::new(&tx, &spent).unwrap_or_else(|e| malformed(e)); + let prevout = &case.prevouts[vin]; + let script = + k::ScriptPubkey::new(prevout.script_pubkey.as_bytes()).unwrap_or_else(|e| malformed(e)); + match k::verify( + &script, + Some(prevout.value.to_sat() as i64), + &tx, + vin, + Some(core_flags(case.flags)), + &tx_data, + ) { + Ok(()) => true, + Err(k::KernelError::ScriptVerify(k::ScriptVerifyError::Invalid)) => false, + Err(e) => malformed(e), + } +} + +fn report(case: &Case, vin: usize) -> String { + let prevouts = case + .prevouts + .iter() + .map(|txout| { + format!( + " {} {}", + txout.value.to_sat(), + txout.script_pubkey.to_hex_string() + ) + }) + .collect::>() + .join("\n"); + format!( + "input {vin}, flags {:?}\ntx {}\nprevouts (sats, script):\n{prevouts}", + case.flags, + serialize_hex(&case.tx) + ) +} + +/// Each input's verdict, failing the test case where we and Core differ. +fn verdicts(case: &Case) -> Result, TestCaseError> { + let tx_bytes = serialize(&case.tx); + let prevouts = case.prevouts.iter().map(Some).collect::>(); + let shared = bdk_script_verify::verify_transaction(&case.tx, &prevouts, case.flags); + let mut out = Vec::new(); + for (vin, verification) in shared.into_iter().enumerate() { + prop_assert_eq!( + &verification, + &bdk_script_verify::verify_input( + &mut bdk_script_verify::VerificationCache::new(&case.tx, &prevouts), + vin, + case.flags + ), + "shared and independent verification differ: {}", + report(case, vin) + ); + let ours = verification.result; + if let Err(Error::MissingPrevouts(missing)) = &ours { + panic!("every prevout was given, yet {missing:?} are reported missing"); + } + let core = core_verdict(case, &tx_bytes, vin); + prop_assert_eq!( + ours.is_ok(), + core, + "we say {:?}, Core says {}\n{}", + ours, + if core { "valid" } else { "invalid" }, + report(case, vin) + ); + out.push(core); + } + Ok(out) +} + +fn assert_all_valid(case: &Case) -> Result<(), TestCaseError> { + let verdicts = verdicts(case)?; + prop_assert!( + verdicts.iter().all(|v| *v), + "generator built an invalid spend: {:?}\n{}", + verdicts, + report(case, 0) + ); + Ok(()) +} + +fn push(out: &mut Vec, data: &[u8]) { + let n = data.len(); + if n < usize::from(OP_PUSHDATA1.to_u8()) { + out.push(n as u8); + } else if n <= 0xff { + out.extend([OP_PUSHDATA1.to_u8(), n as u8]); + } else if n <= 0xffff { + out.push(OP_PUSHDATA2.to_u8()); + out.extend((n as u16).to_le_bytes()); + } else { + out.push(OP_PUSHDATA4.to_u8()); + out.extend((n as u32).to_le_bytes()); + } + out.extend_from_slice(data); +} + +fn scriptnum(n: i64) -> Vec { + let neg = n < 0; + let mut abs = n.unsigned_abs(); + let mut out = Vec::new(); + while abs > 0 { + out.push((abs & 0xff) as u8); + abs >>= 8; + } + if out.last().is_some_and(|b| b & 0x80 != 0) { + out.push(if neg { 0x80 } else { 0 }); + } else if neg { + *out.last_mut().unwrap() |= 0x80; + } + out +} + +#[derive(Debug, Clone)] +enum Op { + Code(Opcode), + Push(Vec), + Num(i64), + /// Bytes as they are, which may be a truncated or non-minimal push. + Raw(Vec), + /// A push through OP_PUSHDATA1, 2 or 4, whatever the length. + PushData(u8, Vec), +} + +fn encode(ops: &[Op]) -> Vec { + let mut out = Vec::new(); + for op in ops { + match op { + Op::Code(code) => out.push(code.to_u8()), + Op::Push(data) => push(&mut out, data), + Op::Num(0) => out.push(OP_PUSHBYTES_0.to_u8()), + Op::Num(-1) => out.push(OP_PUSHNUM_NEG1.to_u8()), + Op::Num(n @ 1..=16) => out.push(OP_PUSHNUM_1.to_u8() + *n as u8 - 1), + Op::Num(n) => push(&mut out, &scriptnum(*n)), + Op::Raw(bytes) => out.extend_from_slice(bytes), + Op::PushData(width, data) => { + match width { + 1 => out.extend([OP_PUSHDATA1.to_u8(), data.len() as u8]), + 2 => { + out.push(OP_PUSHDATA2.to_u8()); + out.extend((data.len() as u16).to_le_bytes()); + } + _ => { + out.push(OP_PUSHDATA4.to_u8()); + out.extend((data.len() as u32).to_le_bytes()); + } + } + out.extend_from_slice(data); + } + } + } + out +} + +fn secret(i: u64) -> SecretKey { + let mut bytes = [0u8; 32]; + bytes[24..].copy_from_slice(&(i + 1).to_be_bytes()); + SecretKey::from_slice(&bytes).unwrap() +} + +fn public(sk: &SecretKey) -> PublicKey { + PublicKey::new(sk.public_key(&SECP)) +} + +fn x_only(sk: &SecretKey) -> XOnlyPublicKey { + sk.x_only_public_key(&SECP).0 +} + +fn single_leaf_taproot(leaf: ScriptBuf, version: LeafVersion) -> (ScriptBuf, Vec) { + let info = TaprootBuilder::new() + .add_leaf_with_ver(0, leaf.clone(), version) + .unwrap() + .finalize(&SECP, x_only(&secret(0))) + .unwrap(); + let control_block = info.control_block(&(leaf, version)).unwrap(); + ( + ScriptBuf::new_p2tr_tweaked(info.output_key()), + control_block.serialize(), + ) +} + +fn prevout_txid(i: usize) -> Txid { + Txid::from_byte_array([i as u8 + 1; 32]) +} + +fn unsigned_tx(version: i32, lock_time: u32, sequences: &[u32], outputs: usize) -> Transaction { + Transaction { + version: Version(version), + lock_time: LockTime::from_consensus(lock_time), + input: sequences + .iter() + .enumerate() + .map(|(i, seq)| TxIn { + previous_output: OutPoint::new(prevout_txid(i), i as u32), + script_sig: ScriptBuf::new(), + sequence: Sequence(*seq), + witness: Witness::new(), + }) + .collect(), + output: (0..outputs) + .map(|i| TxOut { + value: Amount::from_sat(1000 + i as u64), + script_pubkey: ScriptBuf::new_p2wpkh( + &public(&secret(90 + i as u64)).wpubkey_hash().unwrap(), + ), + }) + .collect(), + } +} + +fn any_flags() -> impl Strategy { + any::<[bool; 7]>().prop_map(|b| Flags { + p2sh: b[0], + dersig: b[1], + checklocktimeverify: b[2], + checksequenceverify: b[3], + // Core requires these flags' prerequisites, as `Flags` documents. + witness: b[4] && b[0], + nulldummy: b[5], + taproot: b[6] && b[4] && b[0], + }) +} + +fn flags() -> impl Strategy { + prop_oneof![3 => Just(Flags::CONSENSUS), 1 => any_flags()] +} + +fn stack_item() -> impl Strategy> { + prop_oneof![ + Just(vec![]), + Just(vec![0x00]), + Just(vec![0x01]), + Just(vec![0x80]), + Just(vec![0x81]), + Just(vec![0x00, 0x80]), + vec(any::(), 1..5), + vec(any::(), 20..=20), + vec(any::(), 32..=33), + vec(any::(), 64..=65), + vec(any::(), 70..=73), + vec(any::(), 519..=521), + ] +} + +fn number() -> impl Strategy { + prop_oneof![ + 4 => -1..=16i64, + 1 => Just(0x7fff_ffff), + 1 => Just(-0x7fff_ffff), + 1 => Just(0x8000_0000), + 1 => Just(0xff_ffff_ffff), + 1 => prop_oneof![Just(127), Just(128), Just(-128), Just(255), Just(256)], + 2 => any::().prop_map(i64::from), + 2 => 0..1000i64, + ] +} + +fn opcode() -> impl Strategy { + prop_oneof![ + // Everything that is not a data push, including disabled opcodes and OP_SUCCESSx. + 4 => (OP_PUSHNUM_NEG1.to_u8()..=u8::MAX).prop_map(Opcode::from), + 2 => prop_oneof![ + Just(OP_IF), + Just(OP_NOTIF), + Just(OP_ELSE), + Just(OP_ENDIF), + Just(OP_VERIFY), + Just(OP_RETURN) + ], + 2 => prop_oneof![ + Just(OP_CODESEPARATOR), + Just(OP_CHECKSIG), + Just(OP_CHECKSIGVERIFY), + Just(OP_CHECKMULTISIG), + Just(OP_CHECKMULTISIGVERIFY), + Just(OP_CHECKSIGADD), + Just(OP_CLTV), + Just(OP_CSV) + ], + ] +} + +fn op() -> impl Strategy { + prop_oneof![ + 4 => opcode().prop_map(Op::Code), + 2 => stack_item().prop_map(Op::Push), + 2 => number().prop_map(Op::Num), + 1 => vec(any::(), 1..4).prop_map(Op::Raw), + 1 => (prop_oneof![Just(1u8), Just(2), Just(4)], vec(any::(), 0..4)) + .prop_map(|(width, data)| Op::PushData(width, data)), + ] +} + +fn sequence() -> impl Strategy { + prop_oneof![ + Just(u32::MAX), + Just(0xffff_fffe), + 0..1000u32, + (0..1000u32).prop_map(|n| n | (1 << 22)), + any::(), + ] +} + +fn lock_time() -> impl Strategy { + prop_oneof![ + Just(0), + 0..1000u32, + 500_000_000..500_001_000u32, + any::() + ] +} + +fn version() -> impl Strategy { + prop_oneof![Just(1), Just(2), any::()] +} + +fn amount() -> impl Strategy { + prop_oneof![Just(0), 1..100_000u64, 0..=MAX_MONEY] +} + +/// Indices are taken modulo what they index when applied, so shrinking keeps them meaningful. +#[derive(Debug, Clone)] +enum Mutation { + FlipWitnessBit { + input: usize, + elem: usize, + bit: usize, + }, + FlipScriptSigBit { + input: usize, + bit: usize, + }, + ReplaceWitnessElem { + input: usize, + elem: usize, + data: Vec, + }, + DropWitnessElem { + input: usize, + elem: usize, + }, + DupWitnessElem { + input: usize, + elem: usize, + }, + SwapWitnessElems { + input: usize, + a: usize, + b: usize, + }, + TruncateWitness { + input: usize, + len: usize, + }, + /// Replaces the last byte of an element: a signature's sighash type. + SetLastByte { + input: usize, + elem: usize, + byte: u8, + }, + /// Turns a 64-byte taproot signature into a 65-byte one with an explicit type. + AppendByte { + input: usize, + elem: usize, + byte: u8, + }, + TrimLastByte { + input: usize, + elem: usize, + }, + /// Gives the first 64-byte element, a default-type taproot signature, an explicit type. + TypeSchnorrSig { + input: usize, + byte: u8, + }, + /// Edits the DER structure of the first ECDSA signature found in the scriptSig or witness. + MangleDer { + input: usize, + edit: DerEdit, + }, + /// Toggles a small element between empty and `0x01`: an IF selector or a dummy. + ToggleSelector { + input: usize, + elem: usize, + }, + AddAnnex { + input: usize, + data: Vec, + }, + BumpAmount { + input: usize, + delta: i64, + }, + FlipPrevoutScriptBit { + input: usize, + bit: usize, + }, + SetWitnessVersion { + input: usize, + version: u8, + }, + TruncateWitnessProgram { + input: usize, + }, + SetLockTime(u32), + SetSequence { + input: usize, + sequence: u32, + }, + SetVersion(i32), + BumpOutput { + output: usize, + delta: i64, + }, + FlipOutputScriptBit { + output: usize, + bit: usize, + }, + PushOutput, + PopOutput, +} + +fn mutation() -> impl Strategy { + let i = || 0..8usize; + prop_oneof![ + 3 => (i(), i(), any::()).prop_map(|(input, elem, bit)| Mutation::FlipWitnessBit { input, elem, bit }), + 2 => (i(), any::()).prop_map(|(input, bit)| Mutation::FlipScriptSigBit { input, bit }), + 1 => (i(), i(), stack_item()).prop_map(|(input, elem, data)| Mutation::ReplaceWitnessElem { input, elem, data }), + 1 => (i(), i()).prop_map(|(input, elem)| Mutation::DropWitnessElem { input, elem }), + 1 => (i(), i()).prop_map(|(input, elem)| Mutation::DupWitnessElem { input, elem }), + 1 => (i(), i(), i()).prop_map(|(input, a, b)| Mutation::SwapWitnessElems { input, a, b }), + 1 => (i(), 0..4usize).prop_map(|(input, len)| Mutation::TruncateWitness { input, len }), + 2 => (i(), i(), sighash_byte()).prop_map(|(input, elem, byte)| Mutation::SetLastByte { input, elem, byte }), + 1 => (i(), i(), sighash_byte()).prop_map(|(input, elem, byte)| Mutation::AppendByte { input, elem, byte }), + 1 => (i(), i()).prop_map(|(input, elem)| Mutation::TrimLastByte { input, elem }), + 2 => (i(), prop_oneof![Just(0x00), sighash_byte()]).prop_map(|(input, byte)| Mutation::TypeSchnorrSig { input, byte }), + 3 => (i(), der_edit()).prop_map(|(input, edit)| Mutation::MangleDer { input, edit }), + 2 => (i(), i()).prop_map(|(input, elem)| Mutation::ToggleSelector { input, elem }), + 1 => (i(), vec(any::(), 0..4)).prop_map(|(input, data)| Mutation::AddAnnex { input, data }), + 2 => (i(), prop_oneof![Just(1i64), Just(-1), any::().prop_map(i64::from)]).prop_map(|(input, delta)| Mutation::BumpAmount { input, delta }), + 1 => (i(), any::()).prop_map(|(input, bit)| Mutation::FlipPrevoutScriptBit { input, bit }), + 1 => (i(), prop_oneof![Just(0u8), 0x51..=0x60u8]).prop_map(|(input, version)| Mutation::SetWitnessVersion { input, version }), + 1 => i().prop_map(|input| Mutation::TruncateWitnessProgram { input }), + 1 => lock_time().prop_map(Mutation::SetLockTime), + 1 => (i(), sequence()).prop_map(|(input, sequence)| Mutation::SetSequence { input, sequence }), + 1 => version().prop_map(Mutation::SetVersion), + 1 => (i(), prop_oneof![Just(1i64), Just(-1)]).prop_map(|(output, delta)| Mutation::BumpOutput { output, delta }), + 1 => (i(), any::()).prop_map(|(output, bit)| Mutation::FlipOutputScriptBit { output, bit }), + 1 => Just(Mutation::PushOutput), + 1 => Just(Mutation::PopOutput), + ] +} + +fn sighash_byte() -> impl Strategy { + prop_oneof![ + 3 => prop_oneof![Just(0x00), Just(0x01), Just(0x02), Just(0x03), Just(0x81), Just(0x82), Just(0x83)], + 1 => any::(), + ] +} + +#[derive(Debug, Clone, Copy)] +enum DerEdit { + /// Sets one header byte: 0 is the sequence tag, 1 its length, 2 R's tag, 3 R's length, and 4 + /// and 5 S's tag and length. + Header { field: u8, value: u8 }, + /// Pads R or S with a zero byte, which keeps its value: only strict DER rejects it. + Pad { s: bool }, + /// Sets the top bit of R or S, making it negative. + Negate { s: bool }, +} + +fn der_edit() -> impl Strategy { + prop_oneof![ + (0..6u8, any::()).prop_map(|(field, value)| DerEdit::Header { field, value }), + any::().prop_map(|s| DerEdit::Pad { s }), + any::().prop_map(|s| DerEdit::Negate { s }), + ] +} + +/// The R and S lengths of a well-formed DER signature with its type byte. An earlier edit can +/// leave a signature malformed, and later edits index by these lengths, so they only apply while +/// they hold. +fn der_layout(e: &[u8]) -> Option<(usize, usize)> { + let n = e.len(); + if n < 9 || e[0] != 0x30 || e[1] as usize != n - 3 || e[2] != 0x02 { + return None; + } + let len_r = e[3] as usize; + let s_tag = 4 + len_r; + if len_r == 0 || s_tag + 2 >= n || e[s_tag] != 0x02 { + return None; + } + let len_s = e[s_tag + 1] as usize; + (len_s > 0 && s_tag + 2 + len_s == n - 1).then_some((len_r, len_s)) +} + +fn is_der_like(e: &[u8]) -> bool { + der_layout(e).is_some() +} + +fn mangle_der(sig: &mut Vec, edit: DerEdit) { + let Some((len_r, _)) = der_layout(sig) else { + return; + }; + let s_tag = 4 + len_r; + match edit { + DerEdit::Header { field, value } => { + let at = [0, 1, 2, 3, s_tag, s_tag + 1][field as usize]; + sig[at] = value; + } + DerEdit::Pad { s } => { + let (len_at, start) = if s { (s_tag + 1, s_tag + 2) } else { (3, 4) }; + let (Some(len), Some(total)) = (sig[len_at].checked_add(1), sig[1].checked_add(1)) + else { + return; + }; + sig.insert(start, 0x00); + sig[len_at] = len; + sig[1] = total; + } + DerEdit::Negate { s } => { + let start = if s { s_tag + 2 } else { 4 }; + sig[start] |= 0x80; + } + } +} + +fn flip_bit(bytes: &mut [u8], bit: usize) { + if !bytes.is_empty() { + let bit = bit % (bytes.len() * 8); + bytes[bit / 8] ^= 1 << (bit % 8); + } +} + +fn edit_witness(tx: &mut Transaction, input: usize, edit: impl FnOnce(&mut Vec>)) { + let n = tx.input.len(); + let txin = &mut tx.input[input % n]; + let mut items = txin.witness.to_vec(); + edit(&mut items); + txin.witness = Witness::from_slice(&items); +} + +fn edit_elem(tx: &mut Transaction, input: usize, elem: usize, edit: impl FnOnce(&mut Vec)) { + edit_witness(tx, input, |items| { + if !items.is_empty() { + let n = items.len(); + edit(&mut items[elem % n]); + } + }); +} + +fn bump(value: u64, delta: i64) -> u64 { + value.saturating_add_signed(delta).min(MAX_MONEY) +} + +fn apply(mutation: &Mutation, case: &mut Case) { + let tx = &mut case.tx; + let n = tx.input.len(); + match mutation.clone() { + Mutation::FlipWitnessBit { input, elem, bit } => { + edit_elem(tx, input, elem, |e| flip_bit(e, bit)) + } + Mutation::FlipScriptSigBit { input, bit } => { + let mut bytes = tx.input[input % n].script_sig.to_bytes(); + flip_bit(&mut bytes, bit); + tx.input[input % n].script_sig = ScriptBuf::from_bytes(bytes); + } + Mutation::ReplaceWitnessElem { input, elem, data } => { + edit_elem(tx, input, elem, |e| *e = data) + } + Mutation::DropWitnessElem { input, elem } => edit_witness(tx, input, |items| { + if !items.is_empty() { + let n = items.len(); + items.remove(elem % n); + } + }), + Mutation::DupWitnessElem { input, elem } => edit_witness(tx, input, |items| { + if !items.is_empty() { + let at = elem % items.len(); + items.insert(at, items[at].clone()); + } + }), + Mutation::SwapWitnessElems { input, a, b } => edit_witness(tx, input, |items| { + if !items.is_empty() { + let n = items.len(); + items.swap(a % n, b % n); + } + }), + Mutation::TruncateWitness { input, len } => { + edit_witness(tx, input, |items| items.truncate(len)) + } + Mutation::SetLastByte { input, elem, byte } => edit_elem(tx, input, elem, |e| { + if let Some(last) = e.last_mut() { + *last = byte; + } + }), + Mutation::AppendByte { input, elem, byte } => edit_elem(tx, input, elem, |e| e.push(byte)), + Mutation::MangleDer { input, edit } => { + let txin = &mut tx.input[input % n]; + let mut items = txin.witness.to_vec(); + if let Some(sig) = items.iter_mut().find(|e| is_der_like(e)) { + mangle_der(sig, edit); + txin.witness = Witness::from_slice(&items); + } else { + let mut pushes = txin + .script_sig + .instructions() + .map(|i| match i { + Ok(bitcoin::script::Instruction::PushBytes(b)) => { + Some(b.as_bytes().to_vec()) + } + _ => None, + }) + .collect::>>() + .unwrap_or_default(); + if let Some(sig) = pushes.iter_mut().find(|e| is_der_like(e)) { + mangle_der(sig, edit); + let mut bytes = Vec::new(); + for p in &pushes { + if p.is_empty() { + bytes.push(OP_PUSHBYTES_0.to_u8()); + } else { + push(&mut bytes, p); + } + } + txin.script_sig = ScriptBuf::from_bytes(bytes); + } + } + } + Mutation::TypeSchnorrSig { input, byte } => edit_witness(tx, input, |items| { + if let Some(sig) = items.iter_mut().find(|e| e.len() == 64) { + sig.push(byte); + } + }), + Mutation::TrimLastByte { input, elem } => edit_elem(tx, input, elem, |e| { + e.pop(); + }), + Mutation::ToggleSelector { input, elem } => edit_witness(tx, input, |items| { + let small = (0..items.len()) + .filter(|i| items[*i].len() <= 1) + .collect::>(); + if !small.is_empty() { + let e = &mut items[small[elem % small.len()]]; + *e = if e.is_empty() { vec![1] } else { vec![] }; + } + }), + Mutation::AddAnnex { input, data } => edit_witness(tx, input, |items| { + let mut annex = vec![0x50]; + annex.extend(data); + items.push(annex); + }), + Mutation::BumpAmount { input, delta } => { + let txout = &mut case.prevouts[input % n]; + txout.value = Amount::from_sat(bump(txout.value.to_sat(), delta)); + } + Mutation::FlipPrevoutScriptBit { input, bit } => { + let mut bytes = case.prevouts[input % n].script_pubkey.to_bytes(); + flip_bit(&mut bytes, bit); + case.prevouts[input % n].script_pubkey = ScriptBuf::from_bytes(bytes); + } + Mutation::SetWitnessVersion { input, version } => { + let spk = &mut case.prevouts[input % n].script_pubkey; + if spk.is_witness_program() { + let mut bytes = spk.to_bytes(); + bytes[0] = version; + *spk = ScriptBuf::from_bytes(bytes); + } + } + Mutation::TruncateWitnessProgram { input } => { + let spk = &mut case.prevouts[input % n].script_pubkey; + if spk.is_witness_program() { + let mut bytes = spk.to_bytes(); + bytes.pop(); + bytes[1] -= 1; + *spk = ScriptBuf::from_bytes(bytes); + } + } + Mutation::SetLockTime(lock_time) => tx.lock_time = LockTime::from_consensus(lock_time), + Mutation::SetSequence { input, sequence } => { + tx.input[input % n].sequence = Sequence(sequence) + } + Mutation::SetVersion(version) => tx.version = Version(version), + Mutation::BumpOutput { output, delta } => { + let m = tx.output.len(); + let txout = &mut tx.output[output % m]; + txout.value = Amount::from_sat(bump(txout.value.to_sat(), delta)); + } + Mutation::FlipOutputScriptBit { output, bit } => { + let m = tx.output.len(); + let mut bytes = tx.output[output % m].script_pubkey.to_bytes(); + flip_bit(&mut bytes, bit); + tx.output[output % m].script_pubkey = ScriptBuf::from_bytes(bytes); + } + Mutation::PushOutput => tx.output.push(TxOut { + value: Amount::from_sat(1), + script_pubkey: ScriptBuf::new(), + }), + // An output is kept so the transaction stays unambiguous to deserialize. + Mutation::PopOutput => { + if tx.output.len() > 1 { + tx.output.pop(); + } + } + } +} + +#[derive(Debug, Clone)] +enum Placement { + Bare, + P2sh, + P2wsh, + P2shP2wsh, + Tapscript { + leaf_version: u8, + }, + /// No script: the output is a witness program of this version and length. + WitnessProgram { + version: u8, + len: usize, + }, +} + +fn placement() -> impl Strategy { + prop_oneof![ + 2 => Just(Placement::Bare), + 2 => Just(Placement::P2sh), + 2 => Just(Placement::P2wsh), + 2 => Just(Placement::P2shP2wsh), + 2 => Just(Placement::Tapscript { leaf_version: 0xc0 }), + // An unknown leaf version succeeds unconditionally; 0x50 would be an annex. + 1 => (0..0x80u8).prop_filter("not annex", |v| *v != 0x28).prop_map(|v| Placement::Tapscript { leaf_version: v * 2 }), + 1 => (prop_oneof![Just(0u8), 1..=16u8], prop_oneof![Just(20usize), Just(32), 0..=41usize]) + .prop_map(|(version, len)| Placement::WitnessProgram { version, len }), + ] +} + +#[derive(Debug, Clone)] +struct ScriptSpec { + placement: Placement, + script: Vec, + script_sig: Vec, + /// The stack the script starts with: pushed in the scriptSig of a legacy placement, and the + /// witness of a segwit one. + stack: Vec>, + /// Witness items below the stack, so a legacy placement gets an unexpected witness. + witness: Vec>, + /// Appended after the control block, so only a tapscript spend has one. + annex: Option>, + flags: Flags, + version: i32, + lock_time: u32, + sequence: u32, + amount: u64, +} + +/// The parts of a [`ScriptSpec`] that shape the script: script, scriptSig, stack and witness. +type Shape = (Vec, Vec, Vec>, Vec>); + +fn script_spec(shape: impl Strategy) -> impl Strategy { + ( + placement(), + shape, + proptest::option::weighted(0.1, vec(any::(), 0..4)), + flags(), + version(), + lock_time(), + sequence(), + amount(), + ) + .prop_map( + |( + placement, + (script, script_sig, stack, witness), + annex, + flags, + version, + lock_time, + sequence, + amount, + )| ScriptSpec { + placement, + script, + script_sig, + stack, + witness, + annex, + flags, + version, + lock_time, + sequence, + amount, + }, + ) +} + +fn random_shape() -> impl Strategy { + ( + vec(op(), 0..=12), + vec(op(), 0..=4), + vec(stack_item(), 0..=4), + prop_oneof![3 => Just(vec![]), 1 => vec(stack_item(), 1..=2)], + ) +} + +/// Simple ops for the bodies of shaped scripts, so that they often run to completion. +fn plain_op() -> impl Strategy { + prop_oneof![ + number().prop_map(Op::Num), + Just(Op::Code(OP_DROP)), + Just(Op::Code(OP_DUP)), + Just(Op::Code(OP_EQUAL)), + ] +} + +fn op_success() -> impl Strategy { + prop_oneof![ + // The ends of each range, where an off-by-one would be. + prop_oneof![ + Just(126u8), + Just(129), + Just(131), + Just(134), + Just(149), + Just(153), + Just(187), + Just(254) + ], + Just(80u8), + Just(98), + 126..=129u8, + 131..=134u8, + 137..=138u8, + 141..=142u8, + 149..=153u8, + 187..=254u8, + ] +} + +/// Scripts shaped to reach one rule each, leaving the transaction fields and the items they +/// consume random. +fn shaped() -> impl Strategy { + let conditional = ( + prop_oneof![Just(OP_IF), Just(OP_NOTIF)], + vec(plain_op(), 0..3), + proptest::option::of(vec(plain_op(), 0..3)), + stack_item(), + ) + .prop_map(|(code, then, otherwise, condition)| { + let mut ops = vec![Op::Code(code)]; + ops.extend(then); + if let Some(otherwise) = otherwise { + ops.push(Op::Code(OP_ELSE)); + ops.extend(otherwise); + } + ops.extend([Op::Code(OP_ENDIF), Op::Num(1)]); + (ops, vec![], vec![condition], vec![]) + }); + let timelock = ( + prop_oneof![number(), (0..1000i64).prop_map(|n| n | (1 << 22))], + prop_oneof![Just(OP_CLTV), Just(OP_CSV)], + ) + .prop_map(|(n, code)| { + let ops = vec![Op::Num(n), Op::Code(code), Op::Code(OP_DROP), Op::Num(1)]; + (ops, vec![], vec![], vec![]) + }); + let checksig = ( + prop_oneof![ + Just(vec![]), + vec(any::(), 1..5), + vec(any::(), 32..=32), + vec(any::(), 33..=33), + ], + stack_item(), + 0..4u8, + ) + .prop_map(|(pubkey, sig, form)| { + let ops = match form { + 0 => vec![Op::Push(pubkey), Op::Code(OP_CHECKSIG)], + 1 => vec![Op::Push(pubkey), Op::Code(OP_CHECKSIGVERIFY), Op::Num(1)], + // A check that fails softly passes here, so a hard failure is observable. + 2 => vec![Op::Push(pubkey), Op::Code(OP_CHECKSIG), Op::Code(OP_NOT)], + _ => vec![Op::Num(0), Op::Push(pubkey), Op::Code(OP_CHECKSIGADD)], + }; + (ops, vec![], vec![sig], vec![]) + }); + // A script that passes, so only the scriptSig's own rules decide. + let pushes = || vec(stack_item().prop_map(Op::Push), 0..=2); + let harmless = prop_oneof![ + Just(OP_NOP), + Just(OP_NOP1), + (OP_NOP4.to_u8()..=OP_NOP10.to_u8()).prop_map(Opcode::from) + ] + .prop_map(Op::Code); + let trivial = prop_oneof![ + vec(op(), 0..=4), + pushes(), + (pushes(), harmless, any::()).prop_map(|(mut ops, op, at)| { + let at = at % (ops.len() + 1); + ops.insert(at, op); + ops + }), + ] + .prop_map(|script_sig| (vec![Op::Num(1)], script_sig, vec![], vec![])); + let success = + (vec(op(), 0..=6), any::(), op_success()).prop_map(|(mut ops, at, code)| { + let at = at % (ops.len() + 1); + ops.insert(at, Op::Code(Opcode::from(code))); + (ops, vec![], vec![], vec![]) + }); + prop_oneof![conditional, timelock, checksig, trivial, success] +} + +fn build_script_case(spec: &ScriptSpec) -> Case { + let script = ScriptBuf::from_bytes(encode(&spec.script)); + let mut script_sig = encode(&spec.script_sig); + let mut witness = spec.witness.clone(); + let legacy = matches!(spec.placement, Placement::Bare | Placement::P2sh); + if legacy { + for item in &spec.stack { + push(&mut script_sig, item); + } + } else { + witness.extend(spec.stack.iter().cloned()); + } + let script_pubkey = match spec.placement { + Placement::Bare => script, + Placement::P2sh => { + push(&mut script_sig, script.as_bytes()); + ScriptBuf::new_p2sh(&script.script_hash()) + } + Placement::P2wsh => { + witness.push(script.to_bytes()); + ScriptBuf::new_p2wsh(&script.wscript_hash()) + } + Placement::P2shP2wsh => { + let program = ScriptBuf::new_p2wsh(&script.wscript_hash()); + push(&mut script_sig, program.as_bytes()); + witness.push(script.to_bytes()); + ScriptBuf::new_p2sh(&program.script_hash()) + } + Placement::Tapscript { leaf_version } => { + let version = LeafVersion::from_consensus(leaf_version).unwrap(); + let (output, control_block) = single_leaf_taproot(script.clone(), version); + witness.push(script.to_bytes()); + witness.push(control_block); + if let Some(data) = &spec.annex { + witness.push([&[0x50], &data[..]].concat()); + } + output + } + Placement::WitnessProgram { version, len } => { + let mut bytes = vec![if version == 0 { 0 } else { 0x50 + version }]; + push(&mut bytes, &vec![0x42; len]); + ScriptBuf::from_bytes(bytes) + } + }; + let mut tx = unsigned_tx(spec.version, spec.lock_time, &[spec.sequence], 1); + tx.input[0].script_sig = ScriptBuf::from_bytes(script_sig); + tx.input[0].witness = Witness::from_slice(&witness); + Case { + tx, + prevouts: vec![TxOut { + value: Amount::from_sat(spec.amount), + script_pubkey, + }], + flags: spec.flags, + } +} + +/// Scripts built to sit on either side of an interpreter limit. +#[derive(Debug, Clone)] +enum Limit { + /// `n` non-push opcodes, against the 201-opcode limit. + OpCount, + /// `n` stack items, dropped again, against the 1000-item limit. + StackSize, + /// `n` items moved to the altstack, which counts toward the same limit. + AltStack, + /// `n` initial witness stack items, against tapscript's 1000-item limit before execution. + WitnessItems, + /// A push of `n` bytes, against the 520-byte element limit. + PushSize, + /// A script of about `n` bytes, against the 10,000-byte limit. + ScriptSize, + /// A CHECKMULTISIG with `n` keys, against the 20-key limit and the opcode count it adds to. + MultisigKeys, + /// `n` opcodes before a 20-key CHECKMULTISIG, whose keys count toward the opcode limit. + MultisigOpCount, +} + +#[derive(Debug, Clone)] +struct LimitSpec { + limit: Limit, + delta: i64, + placement: Placement, + flags: Flags, +} + +const LIMITS: [Limit; 8] = [ + Limit::OpCount, + Limit::StackSize, + Limit::AltStack, + Limit::WitnessItems, + Limit::PushSize, + Limit::ScriptSize, + Limit::MultisigKeys, + Limit::MultisigOpCount, +]; + +const LIMIT_DELTAS: core::ops::RangeInclusive = -3..=3; + +const LIMIT_PLACEMENTS: [Placement; 4] = [ + Placement::Bare, + Placement::P2sh, + Placement::P2wsh, + Placement::Tapscript { leaf_version: 0xc0 }, +]; + +fn limit_spec() -> impl Strategy { + ( + proptest::sample::select(LIMITS.to_vec()), + LIMIT_DELTAS, + proptest::sample::select(LIMIT_PLACEMENTS.to_vec()), + flags(), + ) + .prop_map(|(limit, delta, placement, flags)| LimitSpec { + limit, + delta, + placement, + flags, + }) +} + +fn build_limit_case(spec: &LimitSpec) -> Case { + const ONE: Op = Op::Num(1); + const DROP: Op = Op::Code(OP_DROP); + let n = |base: i64| (base + spec.delta) as usize; + let mut witness = Vec::new(); + let script = match spec.limit { + Limit::OpCount => { + let mut ops = vec![Op::Code(OP_NOP); n(201)]; + ops.push(ONE); + ops + } + Limit::StackSize => { + let k = n(1000); + let mut ops = vec![ONE; k]; + ops.extend(vec![DROP; k - 1]); + ops + } + Limit::AltStack => { + let mut ops = Vec::new(); + for _ in 0..n(500) { + ops.extend([ONE, ONE, Op::Code(OP_TOALTSTACK)]); + } + ops.push(ONE); + ops + } + Limit::WitnessItems => { + let k = n(1000); + witness = vec![vec![]; k]; + let mut ops = vec![Op::Code(OP_2DROP); k / 2]; + if k % 2 == 1 { + ops.push(DROP); + } + ops.push(ONE); + ops + } + Limit::PushSize => vec![Op::Push(vec![0x42; n(520)]), DROP, ONE], + Limit::ScriptSize => { + let mut ops = Vec::new(); + for _ in 0..19 { + ops.extend([Op::Push(vec![0x42; 520]), DROP]); + } + ops.extend([Op::Push(vec![0x42; n(40)]), DROP, ONE]); + ops + } + Limit::MultisigKeys | Limit::MultisigOpCount => { + let (nops, keys) = match spec.limit { + Limit::MultisigKeys => (0, n(20)), + _ => (n(201 - 21), 20), + }; + let mut ops = vec![Op::Code(OP_NOP); nops]; + ops.extend([Op::Num(0), Op::Num(0)]); + ops.extend(vec![Op::Push(public(&secret(1)).to_bytes()); keys]); + ops.extend([Op::Num(keys as i64), Op::Code(OP_CHECKMULTISIG)]); + ops + } + }; + build_script_case(&ScriptSpec { + placement: spec.placement.clone(), + script, + script_sig: vec![], + stack: vec![], + witness, + annex: None, + flags: spec.flags, + version: 2, + lock_time: 0, + sequence: u32::MAX, + amount: 1000, + }) +} + +/// A tapscript leaf; every one is in each script-path tree, so the others are its siblings. +#[derive(Debug, Clone, Copy, PartialEq)] +enum Leaf { + Single, + Multi, + /// ` CODESEPARATOR CHECKSIG`, whose signature commits to the separator's position. + CodeSep, + /// `n - 1` repeats of `DUP CHECKSIGVERIFY`, then ` CHECKSIG`, all on one signature, + /// so that `n` crosses the sigop budget that the witness size buys. + Repeat(u8), +} + +#[derive(Debug, Clone, Copy, PartialEq)] +enum Kind { + P2pkh, + P2shMultisig, + P2wpkh, + P2shP2wpkh, + P2wshMultisig, + /// ` CHECKSIGVERIFY CODESEPARATOR CHECKSIG`, in P2SH or P2WSH, so the two + /// signatures commit to different script codes. + CodeSep { + witness: bool, + }, + /// P2SH ` DROP CHECKSIG` carrying its own signature, which FindAndDelete removes + /// from the script code it signs. + FindAndDelete, + P2trKey, + P2trScript(Leaf), +} + +impl Kind { + fn is_taproot(self) -> bool { + matches!(self, Kind::P2trKey | Kind::P2trScript(_)) + } +} + +#[derive(Debug, Clone, Copy)] +enum Sighash { + All, + None, + Single, + AcpAll, + AcpNone, + AcpSingle, + /// Taproot's implicit ALL; ALL elsewhere. + Default, + /// A non-standard type byte, which ECDSA signatures commit to as is; ALL for taproot. + Raw(u8), + /// A taproot signature over the default type that also spells it out as 0x00, which BIP341 + /// forbids; ALL elsewhere. + ExplicitDefault, +} + +impl Sighash { + fn ecdsa(self) -> EcdsaSighashType { + match self { + Sighash::None => EcdsaSighashType::None, + Sighash::Single => EcdsaSighashType::Single, + Sighash::AcpAll => EcdsaSighashType::AllPlusAnyoneCanPay, + Sighash::AcpNone => EcdsaSighashType::NonePlusAnyoneCanPay, + Sighash::AcpSingle => EcdsaSighashType::SinglePlusAnyoneCanPay, + Sighash::All | Sighash::Default | Sighash::Raw(_) | Sighash::ExplicitDefault => { + EcdsaSighashType::All + } + } + } + + fn raw(self) -> u8 { + match self { + Sighash::Raw(byte) => byte, + other => other.ecdsa() as u8, + } + } + + fn taproot(self) -> TapSighashType { + match self { + Sighash::Default | Sighash::ExplicitDefault => TapSighashType::Default, + Sighash::Raw(_) => TapSighashType::All, + other => TapSighashType::from_consensus_u8(other.ecdsa() as u8).unwrap(), + } + } + + fn is_single(self) -> bool { + matches!(self, Sighash::Single | Sighash::AcpSingle) + } +} + +fn sighash() -> impl Strategy { + prop_oneof![ + 3 => Just(Sighash::All), + 1 => Just(Sighash::None), + 1 => Just(Sighash::Single), + 1 => Just(Sighash::AcpAll), + 1 => Just(Sighash::AcpNone), + 1 => Just(Sighash::AcpSingle), + 2 => Just(Sighash::Default), + 2 => any::().prop_map(Sighash::Raw), + 1 => Just(Sighash::ExplicitDefault), + ] +} + +fn kind() -> impl Strategy { + prop_oneof![ + 1 => Just(Kind::P2pkh), + 1 => Just(Kind::P2shMultisig), + 1 => Just(Kind::P2wpkh), + 1 => Just(Kind::P2shP2wpkh), + 1 => Just(Kind::P2wshMultisig), + 1 => Just(Kind::CodeSep { witness: false }), + 1 => Just(Kind::CodeSep { witness: true }), + 1 => Just(Kind::FindAndDelete), + 1 => Just(Kind::P2trKey), + 1 => Just(Kind::P2trScript(Leaf::Single)), + 1 => Just(Kind::P2trScript(Leaf::Multi)), + 1 => Just(Kind::P2trScript(Leaf::CodeSep)), + // Around where the sigop budget runs out. + 3 => (8..=20u8).prop_map(|n| Kind::P2trScript(Leaf::Repeat(n))), + ] +} + +#[derive(Debug, Clone)] +struct InputSpec { + kind: Kind, + key: u8, + sighash: Sighash, + amount: u64, + sequence: u32, + /// A taproot input's annex, without its 0x50 tag, which the signature commits to. + annex: Option>, +} + +fn input_spec() -> impl Strategy { + ( + kind(), + any::(), + sighash(), + amount(), + sequence(), + proptest::option::weighted(0.2, vec(any::(), 0..4)), + ) + .prop_map(|(kind, key, sighash, amount, sequence, annex)| InputSpec { + kind, + key, + sighash, + amount, + sequence, + annex, + }) +} + +/// Replaces byte `pos` of a tapscript leaf before signing, with the tree rebuilt to commit to it, +/// so that the edited script is what executes. +#[derive(Debug, Clone)] +struct LeafEdit { + input: usize, + pos: usize, + byte: u8, +} + +#[derive(Debug, Clone)] +struct SignedSpec { + inputs: Vec, + /// One output only, so a SIGHASH_SINGLE input past it hits the legacy "one" hash. + one_output: bool, + version: i32, + lock_time: u32, + leaf_edit: Option, + mutations: Vec, + flags: Flags, +} + +fn signed_spec() -> impl Strategy { + ( + vec(input_spec(), 1..=3), + any::(), + version(), + lock_time(), + proptest::option::weighted( + 0.2, + (0..3usize, any::(), any::()).prop_map(|(input, pos, byte)| LeafEdit { + input, + pos, + byte, + }), + ), + vec(mutation(), 0..=3), + flags(), + ) + .prop_map( + |(inputs, one_output, version, lock_time, leaf_edit, mutations, flags)| SignedSpec { + inputs, + one_output, + version, + lock_time, + leaf_edit, + mutations, + flags, + }, + ) +} + +fn ecdsa_sig(sighash: [u8; 32], sk: &SecretKey, type_byte: u8) -> Vec { + let sig = SECP.sign_ecdsa(&Message::from_digest(sighash), sk); + let mut out = sig.serialize_der().to_vec(); + out.push(type_byte); + out +} + +fn schnorr_sig(sighash: [u8; 32], keypair: &Keypair, ty: TapSighashType) -> Vec { + let sig = SECP.sign_schnorr_no_aux_rand(&Message::from_digest(sighash), keypair); + let mut out = sig.serialize().to_vec(); + if ty != TapSighashType::Default { + out.push(ty as u8); + } + out +} + +/// BIP143 over the raw type byte, which rust-bitcoin's typed API cannot express. Written from the +/// BIP rather than shared with the verifier, so that Core is the only judge of both. +fn bip143_sighash( + tx: &Transaction, + vin: usize, + script_code: &[u8], + value: Amount, + raw: u32, +) -> [u8; 32] { + let dsha = |data: &[u8]| sha256d::Hash::hash(data).to_byte_array(); + let base = raw & 0x1f; + let anyone_can_pay = raw & 0x80 != 0; + let (none, single) = (base == 2, base == 3); + let prevouts = if anyone_can_pay { + [0; 32] + } else { + dsha( + &tx.input + .iter() + .flat_map(|i| serialize(&i.previous_output)) + .collect::>(), + ) + }; + let sequences = if anyone_can_pay || none || single { + [0; 32] + } else { + dsha( + &tx.input + .iter() + .flat_map(|i| i.sequence.0.to_le_bytes()) + .collect::>(), + ) + }; + let outputs = if !none && !single { + dsha(&tx.output.iter().flat_map(serialize).collect::>()) + } else if single && vin < tx.output.len() { + dsha(&serialize(&tx.output[vin])) + } else { + [0; 32] + }; + let mut data = Vec::new(); + data.extend(tx.version.0.to_le_bytes()); + data.extend(prevouts); + data.extend(sequences); + data.extend(serialize(&tx.input[vin].previous_output)); + data.extend(serialize(&ScriptBuf::from_bytes(script_code.to_vec()))); + data.extend(value.to_sat().to_le_bytes()); + data.extend(tx.input[vin].sequence.0.to_le_bytes()); + data.extend(outputs); + data.extend(tx.lock_time.to_consensus_u32().to_le_bytes()); + data.extend(raw.to_le_bytes()); + dsha(&data) +} + +fn multisig_script(keys: &[SecretKey]) -> ScriptBuf { + let mut bytes = vec![OP_PUSHNUM_2.to_u8()]; + for sk in keys { + push(&mut bytes, &public(sk).to_bytes()); + } + bytes.extend([OP_PUSHNUM_3.to_u8(), OP_CHECKMULTISIG.to_u8()]); + ScriptBuf::from_bytes(bytes) +} + +fn codesep_tail(keys: &[SecretKey]) -> Vec { + let mut tail = Vec::new(); + push(&mut tail, &public(&keys[1]).to_bytes()); + tail.push(OP_CHECKSIG.to_u8()); + tail +} + +fn codesep_script(keys: &[SecretKey], with_separator: bool) -> Vec { + let mut script = Vec::new(); + push(&mut script, &public(&keys[0]).to_bytes()); + script.push(OP_CHECKSIGVERIFY.to_u8()); + if with_separator { + script.push(OP_CODESEPARATOR.to_u8()); + } + script.extend(codesep_tail(keys)); + script +} + +fn leaf_script(leaf: Leaf, keys: &[SecretKey]) -> Vec { + let k1 = x_only(&keys[1]).serialize(); + let mut out = Vec::new(); + match leaf { + Leaf::Single => { + push(&mut out, &k1); + out.push(OP_CHECKSIG.to_u8()); + } + Leaf::Multi => { + push(&mut out, &k1); + out.push(OP_CHECKSIG.to_u8()); + push(&mut out, &x_only(&keys[2]).serialize()); + out.extend([OP_CHECKSIGADD, OP_PUSHNUM_2, OP_NUMEQUAL].map(Opcode::to_u8)); + } + Leaf::CodeSep => { + push(&mut out, &k1); + out.extend([OP_CODESEPARATOR, OP_CHECKSIG].map(Opcode::to_u8)); + } + Leaf::Repeat(n) => { + for _ in 1..n { + out.push(OP_DUP.to_u8()); + push(&mut out, &k1); + out.push(OP_CHECKSIGVERIFY.to_u8()); + } + push(&mut out, &k1); + out.push(OP_CHECKSIG.to_u8()); + } + } + out +} + +struct Prepared { + script_pubkey: ScriptBuf, + keys: Vec, + /// The redeem or witness script. + script: Option, + leaf: Option, + control_block: Option>, + merkle_root: Option, +} + +fn prepare(input: &InputSpec, index: usize, edit: Option<&LeafEdit>) -> Prepared { + let base = u64::from(input.key) * 4 + index as u64 * 1024; + let keys = (0..3).map(|i| secret(base + i)).collect::>(); + let mut prepared = Prepared { + script_pubkey: ScriptBuf::new(), + keys: keys.clone(), + script: None, + leaf: None, + control_block: None, + merkle_root: None, + }; + let wpkh = public(&keys[0]).wpubkey_hash().unwrap(); + match input.kind { + Kind::P2pkh => { + prepared.script_pubkey = ScriptBuf::new_p2pkh(&public(&keys[0]).pubkey_hash()) + } + Kind::P2wpkh => prepared.script_pubkey = ScriptBuf::new_p2wpkh(&wpkh), + Kind::P2shP2wpkh => { + let program = ScriptBuf::new_p2wpkh(&wpkh); + prepared.script_pubkey = ScriptBuf::new_p2sh(&program.script_hash()); + prepared.script = Some(program); + } + Kind::P2shMultisig | Kind::CodeSep { witness: false } => { + let redeem = match input.kind { + Kind::P2shMultisig => multisig_script(&keys), + _ => ScriptBuf::from_bytes(codesep_script(&keys, true)), + }; + prepared.script_pubkey = ScriptBuf::new_p2sh(&redeem.script_hash()); + prepared.script = Some(redeem); + } + Kind::P2wshMultisig | Kind::CodeSep { witness: true } => { + let ws = match input.kind { + Kind::P2wshMultisig => multisig_script(&keys), + _ => ScriptBuf::from_bytes(codesep_script(&keys, true)), + }; + prepared.script_pubkey = ScriptBuf::new_p2wsh(&ws.wscript_hash()); + prepared.script = Some(ws); + } + // Its output script contains its signature, so it is known only after signing. + Kind::FindAndDelete => {} + Kind::P2trKey => { + let info = TaprootBuilder::new() + .finalize(&SECP, x_only(&keys[0])) + .unwrap(); + prepared.script_pubkey = ScriptBuf::new_p2tr_tweaked(info.output_key()); + } + Kind::P2trScript(spent) => { + let sibling_repeat = match spent { + Leaf::Repeat(n) => n, + _ => 2, + }; + let mut leaves = [ + Leaf::Single, + Leaf::Multi, + Leaf::CodeSep, + Leaf::Repeat(sibling_repeat), + ] + .map(|leaf| (leaf, leaf_script(leaf, &keys))); + let spent_at = leaves.iter().position(|(leaf, _)| *leaf == spent).unwrap(); + if let Some(edit) = edit.filter(|e| e.input == index) { + let leaf = &mut leaves[spent_at].1; + let n = leaf.len(); + leaf[edit.pos % n] = edit.byte; + } + let leaves = leaves.map(|(_, script)| ScriptBuf::from_bytes(script)); + let info = leaves + .iter() + .fold(TaprootBuilder::new(), |builder, leaf| { + builder.add_leaf(2, leaf.clone()).unwrap() + }) + .finalize(&SECP, x_only(&keys[0])) + .unwrap(); + let leaf = leaves[spent_at].clone(); + prepared.control_block = Some( + info.control_block(&(leaf.clone(), LeafVersion::TapScript)) + .unwrap() + .serialize(), + ); + prepared.leaf = Some(leaf); + prepared.merkle_root = info.merkle_root(); + prepared.script_pubkey = ScriptBuf::new_p2tr_tweaked(info.output_key()); + } + } + prepared +} + +/// Signs a non-taproot input, whose signatures commit to no other input's spent output. +fn sign_ecdsa_input( + unsigned: &Transaction, + vin: usize, + input: &InputSpec, + p: &mut Prepared, + txin: &mut TxIn, +) { + let ty = input.sighash.raw(); + let amount = Amount::from_sat(input.amount); + let legacy = |code: &[u8], sk: &SecretKey| { + let hash = SighashCache::new(unsigned) + .legacy_signature_hash(vin, bitcoin::Script::from_bytes(code), ty.into()) + .unwrap(); + ecdsa_sig(hash.to_byte_array(), sk, ty) + }; + let segwit = |code: &[u8], sk: &SecretKey| { + ecdsa_sig( + bip143_sighash(unsigned, vin, code, amount, ty.into()), + sk, + ty, + ) + }; + let pk = public(&p.keys[0]).to_bytes(); + let mut script_sig = Vec::new(); + let mut witness = Vec::new(); + match input.kind { + Kind::P2pkh => { + push( + &mut script_sig, + &legacy(p.script_pubkey.as_bytes(), &p.keys[0]), + ); + push(&mut script_sig, &pk); + } + Kind::P2shMultisig => { + let redeem = p.script.as_ref().unwrap(); + script_sig.push(OP_PUSHBYTES_0.to_u8()); + for sk in &p.keys[..2] { + push(&mut script_sig, &legacy(redeem.as_bytes(), sk)); + } + push(&mut script_sig, redeem.as_bytes()); + } + Kind::P2wpkh | Kind::P2shP2wpkh => { + let code = ScriptBuf::new_p2pkh(&public(&p.keys[0]).pubkey_hash()); + witness = vec![segwit(code.as_bytes(), &p.keys[0]), pk]; + if let Some(program) = &p.script { + push(&mut script_sig, program.as_bytes()); + } + } + Kind::P2wshMultisig => { + let ws = p.script.as_ref().unwrap(); + witness.push(vec![]); + for sk in &p.keys[..2] { + witness.push(segwit(ws.as_bytes(), sk)); + } + witness.push(ws.to_bytes()); + } + Kind::CodeSep { witness: false } => { + // Legacy script codes drop every separator; the second also starts after it. + let first = legacy(&codesep_script(&p.keys, false), &p.keys[0]); + let second = legacy(&codesep_tail(&p.keys), &p.keys[1]); + push(&mut script_sig, &second); + push(&mut script_sig, &first); + push(&mut script_sig, p.script.as_ref().unwrap().as_bytes()); + } + Kind::CodeSep { witness: true } => { + // Segwit v0 keeps the separators, only starting the second code after the one it ran. + let ws = p.script.as_ref().unwrap(); + let first = segwit(ws.as_bytes(), &p.keys[0]); + let second = segwit(&codesep_tail(&p.keys), &p.keys[1]); + witness = vec![second, first, ws.to_bytes()]; + } + Kind::FindAndDelete => { + let mut code = vec![OP_DROP.to_u8()]; + push(&mut code, &pk); + code.push(OP_CHECKSIG.to_u8()); + let sig = legacy(&code, &p.keys[0]); + let mut redeem = Vec::new(); + push(&mut redeem, &sig); + redeem.extend(code); + let redeem = ScriptBuf::from_bytes(redeem); + p.script_pubkey = ScriptBuf::new_p2sh(&redeem.script_hash()); + push(&mut script_sig, &sig); + push(&mut script_sig, redeem.as_bytes()); + } + Kind::P2trKey | Kind::P2trScript(_) => unreachable!("taproot inputs are signed after"), + } + txin.script_sig = ScriptBuf::from_bytes(script_sig); + txin.witness = Witness::from_slice(&witness); +} + +/// The spend, correctly signed, before any mutation. +fn build_signed_case(spec: &SignedSpec) -> Case { + let edit = spec.leaf_edit.as_ref(); + let mut prepared = spec + .inputs + .iter() + .enumerate() + .map(|(i, input)| prepare(input, i, edit)) + .collect::>(); + let any_taproot = spec.inputs.iter().any(|i| i.kind.is_taproot()); + let outputs = if spec.one_output && !any_taproot { + 1 + } else { + spec.inputs.len() + }; + let sequences = spec.inputs.iter().map(|i| i.sequence).collect::>(); + let mut tx = unsigned_tx(spec.version, spec.lock_time, &sequences, outputs); + let unsigned = tx.clone(); + + for (vin, (input, p)) in spec.inputs.iter().zip(&mut prepared).enumerate() { + if !input.kind.is_taproot() { + sign_ecdsa_input(&unsigned, vin, input, p, &mut tx.input[vin]); + } + } + + let prevouts = spec + .inputs + .iter() + .zip(&prepared) + .map(|(input, p)| TxOut { + value: Amount::from_sat(input.amount), + script_pubkey: p.script_pubkey.clone(), + }) + .collect::>(); + let mut cache = SighashCache::new(&unsigned); + for (vin, (input, p)) in spec.inputs.iter().zip(&prepared).enumerate() { + if !input.kind.is_taproot() { + continue; + } + let mut ty = input.sighash.taproot(); + // A taproot SIGHASH_SINGLE with no matching output has no signature hash at all. + if input.sighash.is_single() && vin >= outputs { + ty = TapSighashType::All; + } + let all = Prevouts::All(&prevouts); + let annex = input + .annex + .as_ref() + .map(|data| [&[0x50], &data[..]].concat()); + let mut sighash = |leaf: Option<(TapLeafHash, u32)>| { + let annex = annex.as_deref().map(|a| Annex::new(a).unwrap()); + cache + .taproot_signature_hash(vin, &all, annex, leaf, ty) + .unwrap() + .to_byte_array() + }; + let mut items = match (&p.leaf, input.kind) { + (Some(leaf), Kind::P2trScript(spent)) => { + let leaf_hash = TapLeafHash::from_script(leaf, LeafVersion::TapScript); + // The separator is opcode 1 of its leaf; no other leaf has one. + let codesep_pos = if spent == Leaf::CodeSep { 1 } else { u32::MAX }; + let hash = sighash(Some((leaf_hash, codesep_pos))); + let sign = + |sk: &SecretKey| schnorr_sig(hash, &Keypair::from_secret_key(&SECP, sk), ty); + let mut items = Vec::new(); + // The 2-of-2 leaf checks key 1 first, which is the top of the stack. + if spent == Leaf::Multi { + items.push(sign(&p.keys[2])); + } + items.push(sign(&p.keys[1])); + items.push(leaf.to_bytes()); + items.push(p.control_block.clone().unwrap()); + items + } + _ => { + let keypair = Keypair::from_secret_key(&SECP, &p.keys[0]) + .tap_tweak(&SECP, p.merkle_root) + .to_keypair(); + vec![schnorr_sig(sighash(None), &keypair, ty)] + } + }; + if matches!(input.sighash, Sighash::ExplicitDefault) { + items[0].push(0x00); + } + items.extend(annex); + tx.input[vin].witness = Witness::from_slice(&items); + } + Case { + tx, + prevouts, + flags: spec.flags, + } +} + +#[derive(Debug, Clone)] +enum Policy { + Pk, + Sha256, + Hash256, + Ripemd160, + Hash160, + After(u32), + Older(u16), + And(Box, Box), + Or(u8, Box, u8, Box), + Thresh(usize, Vec), +} + +fn policy() -> impl Strategy { + let leaf = prop_oneof![ + 5 => Just(Policy::Pk), + 1 => Just(Policy::Sha256), + 1 => Just(Policy::Hash256), + 1 => Just(Policy::Ripemd160), + 1 => Just(Policy::Hash160), + 1 => (1..=500u32).prop_map(Policy::After), + 1 => (1..=100u16).prop_map(Policy::Older), + ]; + let inner = + leaf.prop_recursive(3, 12, 4, |inner| { + prop_oneof![ + (inner.clone(), inner.clone()) + .prop_map(|(a, b)| Policy::And(Box::new(a), Box::new(b))), + (1..=4u8, inner.clone(), 1..=4u8, inner.clone()) + .prop_map(|(wa, a, wb, b)| Policy::Or(wa, Box::new(a), wb, Box::new(b))), + vec(inner, 2..=4) + .prop_flat_map(|subs| (1..=subs.len(), Just(subs))) + .prop_map(|(k, subs)| Policy::Thresh(k, subs)), + ] + }); + // Wrapping in a key keeps most policies safe (every spend needs a signature), which the + // compiler requires. + prop_oneof![ + 2 => inner.clone().prop_map(|p| Policy::And(Box::new(Policy::Pk), Box::new(p))), + 1 => inner.clone().prop_map(|p| Policy::Or(1, Box::new(Policy::Pk), 1, Box::new(Policy::And(Box::new(Policy::Pk), Box::new(p))))), + 1 => inner, + ] +} + +/// The keys and preimages a rendered policy uses, in order of appearance. +#[derive(Default)] +struct Secrets { + keys: Vec, + preimages: Vec<[u8; 32]>, + max_after: u32, + max_older: u16, +} + +fn render(policy: &Policy, x_only_keys: bool, secrets: &mut Secrets) -> String { + let preimage = |secrets: &mut Secrets| { + let p = [0x10 + secrets.preimages.len() as u8; 32]; + secrets.preimages.push(p); + p + }; + match policy { + Policy::Pk => { + let sk = secret(500 + secrets.keys.len() as u64); + secrets.keys.push(sk); + if x_only_keys { + format!("pk({})", x_only(&sk)) + } else { + format!("pk({})", public(&sk)) + } + } + Policy::Sha256 => format!("sha256({})", sha256::Hash::hash(&preimage(secrets))), + Policy::Hash256 => format!( + "hash256({})", + miniscript::hash256::Hash::hash(&preimage(secrets)) + ), + Policy::Ripemd160 => format!("ripemd160({})", ripemd160::Hash::hash(&preimage(secrets))), + Policy::Hash160 => format!("hash160({})", hash160::Hash::hash(&preimage(secrets))), + Policy::After(n) => { + secrets.max_after = secrets.max_after.max(*n); + format!("after({n})") + } + Policy::Older(n) => { + secrets.max_older = secrets.max_older.max(*n); + format!("older({n})") + } + Policy::And(a, b) => format!( + "and({},{})", + render(a, x_only_keys, secrets), + render(b, x_only_keys, secrets) + ), + Policy::Or(wa, a, wb, b) => format!( + "or({wa}@{},{wb}@{})", + render(a, x_only_keys, secrets), + render(b, x_only_keys, secrets) + ), + Policy::Thresh(k, subs) => { + let subs = subs + .iter() + .map(|p| render(p, x_only_keys, secrets)) + .collect::>(); + format!("thresh({k},{})", subs.join(",")) + } + } +} + +/// Changes made before signing, so the signatures stay valid and only the policy can fail. +#[derive(Debug, Clone)] +enum PreSign { + /// The lock time one below what `after` needs, while the satisfier still claims it is met. + LockTimeBelow, + /// The sequence one below what `older` needs, likewise. + SequenceBelow, + /// The satisfier gives a wrong preimage for the `n`th hash. + WrongPreimage(usize), + /// A final sequence, which disables `after`. + FinalSequence, + /// Version 1, which disables `older`. + VersionOne, +} + +#[derive(Debug, Clone)] +struct MiniscriptSpec { + taproot: bool, + policy: Policy, + key_path: bool, + sighash: Sighash, + amount: u64, + pre_sign: Vec, + mutations: Vec, +} + +fn miniscript_spec(mutate: bool) -> impl Strategy { + let (pre_sign, mutations) = if mutate { + ( + vec( + prop_oneof![ + Just(PreSign::LockTimeBelow), + Just(PreSign::SequenceBelow), + (0..4usize).prop_map(PreSign::WrongPreimage), + Just(PreSign::FinalSequence), + Just(PreSign::VersionOne), + ], + 0..=2, + ) + .boxed(), + vec(mutation(), 0..=3).boxed(), + ) + } else { + (Just(vec![]).boxed(), Just(vec![]).boxed()) + }; + ( + any::(), + policy(), + proptest::bool::weighted(0.2), + prop_oneof![3 => Just(Sighash::All), 1 => Just(Sighash::Default), 1 => sighash()], + amount(), + pre_sign, + mutations, + ) + .prop_map( + |(taproot, policy, key_path, sighash, amount, pre_sign, mutations)| MiniscriptSpec { + taproot, + policy, + key_path, + sighash, + amount, + pre_sign, + mutations, + }, + ) +} + +struct Signer<'a> { + tx: &'a Transaction, + prevouts: &'a [TxOut], + secrets: &'a Secrets, + witness_script: Option, + merkle_root: Option, + key_path: bool, + sighash: Sighash, + wrong_preimage: Option, + lock_time: LockTime, + sequence: Sequence, +} + +impl Signer<'_> { + fn secret_for(&self, pk: &XOnlyPublicKey) -> Option<&SecretKey> { + self.secrets.keys.iter().find(|sk| x_only(sk) == *pk) + } + + fn preimage(&self, matches: impl Fn(&[u8; 32]) -> bool) -> Option<[u8; 32]> { + let i = self.secrets.preimages.iter().position(matches)?; + Some(if self.wrong_preimage == Some(i) { + [0xee; 32] + } else { + self.secrets.preimages[i] + }) + } + + fn older_met(&self, n: relative::LockTime) -> bool { + relative::LockTime::from_sequence(self.sequence).is_ok_and(|have| n.is_implied_by(have)) + } + + fn after_met(&self, n: LockTime) -> bool { + n.is_implied_by(self.lock_time) + } +} + +macro_rules! signer_lookups { + () => { + fn lookup_sha256(&self, h: &sha256::Hash) -> Option { + self.preimage(|p| sha256::Hash::hash(p) == *h) + } + + fn lookup_hash256(&self, h: &miniscript::hash256::Hash) -> Option { + self.preimage(|p| miniscript::hash256::Hash::hash(p) == *h) + } + + fn lookup_ripemd160(&self, h: &ripemd160::Hash) -> Option { + self.preimage(|p| ripemd160::Hash::hash(p) == *h) + } + + fn lookup_hash160(&self, h: &hash160::Hash) -> Option { + self.preimage(|p| hash160::Hash::hash(p) == *h) + } + + fn check_older(&self, n: relative::LockTime) -> bool { + self.older_met(n) + } + + fn check_after(&self, n: LockTime) -> bool { + self.after_met(n) + } + }; +} + +impl Satisfier for Signer<'_> { + fn lookup_ecdsa_sig(&self, pk: &PublicKey) -> Option { + let sk = self.secret_for(&pk.inner.x_only_public_key().0)?; + let ty = self.sighash.ecdsa(); + let hash = SighashCache::new(self.tx) + .p2wsh_signature_hash(0, self.witness_script.as_ref()?, self.prevouts[0].value, ty) + .unwrap(); + Some(bitcoin::ecdsa::Signature { + signature: SECP.sign_ecdsa(&Message::from_digest(hash.to_byte_array()), sk), + sighash_type: ty, + }) + } + + signer_lookups!(); +} + +impl Satisfier for Signer<'_> { + fn lookup_tap_key_spend_sig(&self, pk: &XOnlyPublicKey) -> Option { + if !self.key_path { + return None; + } + let sk = self.secret_for(pk)?; + let ty = self.sighash.taproot(); + let hash = SighashCache::new(self.tx) + .taproot_key_spend_signature_hash(0, &Prevouts::All(self.prevouts), ty) + .unwrap(); + let keypair = Keypair::from_secret_key(&SECP, sk) + .tap_tweak(&SECP, self.merkle_root) + .to_keypair(); + Some(bitcoin::taproot::Signature { + signature: SECP + .sign_schnorr_no_aux_rand(&Message::from_digest(hash.to_byte_array()), &keypair), + sighash_type: ty, + }) + } + + fn lookup_tap_leaf_script_sig( + &self, + pk: &XOnlyPublicKey, + leaf_hash: &TapLeafHash, + ) -> Option { + let sk = self.secret_for(pk)?; + let ty = self.sighash.taproot(); + let hash = SighashCache::new(self.tx) + .taproot_script_spend_signature_hash(0, &Prevouts::All(self.prevouts), *leaf_hash, ty) + .unwrap(); + Some(bitcoin::taproot::Signature { + signature: SECP.sign_schnorr_no_aux_rand( + &Message::from_digest(hash.to_byte_array()), + &Keypair::from_secret_key(&SECP, sk), + ), + sighash_type: ty, + }) + } + + signer_lookups!(); +} + +/// The satisfied spend, or `None` if the policy does not compile. +fn build_miniscript_case(spec: &MiniscriptSpec) -> Option { + let mut secrets = Secrets::default(); + let rendered = render(&spec.policy, spec.taproot, &mut secrets); + let lock_time = LockTime::from_consensus(secrets.max_after); + let sequence = if secrets.max_older > 0 { + Sequence::from_height(secrets.max_older) + } else { + Sequence::ENABLE_LOCKTIME_NO_RBF + }; + let has = |f: fn(&PreSign) -> bool| spec.pre_sign.iter().any(f); + let tx_lock_time = if has(|p| matches!(p, PreSign::LockTimeBelow)) && secrets.max_after > 0 { + secrets.max_after - 1 + } else { + secrets.max_after + }; + let tx_sequence = if has(|p| matches!(p, PreSign::FinalSequence)) { + u32::MAX + } else if has(|p| matches!(p, PreSign::SequenceBelow)) && secrets.max_older > 0 { + Sequence::from_height(secrets.max_older - 1).0 + } else { + sequence.0 + }; + let tx_version = if has(|p| matches!(p, PreSign::VersionOne)) { + 1 + } else { + 2 + }; + let wrong_preimage = spec.pre_sign.iter().find_map(|p| match p { + PreSign::WrongPreimage(i) => Some(*i), + _ => None, + }); + + let mut tx = unsigned_tx(tx_version, tx_lock_time, &[tx_sequence], 1); + let (script_pubkey, satisfied) = if spec.taproot { + let policy = rendered.parse::>().ok()?; + let descriptor = policy.compile_tr(Some(NUMS.parse().unwrap())).ok()?; + let Descriptor::Tr(tr) = &descriptor else { + unreachable!() + }; + let merkle_root = tr.spend_info().merkle_root(); + let prevouts = [TxOut { + value: Amount::from_sat(spec.amount), + script_pubkey: descriptor.script_pubkey(), + }]; + let signer = Signer { + tx: &tx, + prevouts: &prevouts, + secrets: &secrets, + witness_script: None, + merkle_root, + key_path: spec.key_path, + sighash: spec.sighash, + wrong_preimage, + lock_time, + sequence, + }; + let mut txin = tx.input[0].clone(); + descriptor.satisfy(&mut txin, &signer).ok()?; + (descriptor.script_pubkey(), txin) + } else { + let policy = rendered.parse::>().ok()?; + let ms = policy.compile::().ok()?; + let descriptor = Descriptor::new_wsh(ms).ok()?; + let prevouts = [TxOut { + value: Amount::from_sat(spec.amount), + script_pubkey: descriptor.script_pubkey(), + }]; + let signer = Signer { + tx: &tx, + prevouts: &prevouts, + secrets: &secrets, + witness_script: Some(descriptor.explicit_script().unwrap()), + merkle_root: None, + key_path: false, + sighash: spec.sighash, + wrong_preimage, + lock_time, + sequence, + }; + let mut txin = tx.input[0].clone(); + descriptor.satisfy(&mut txin, &signer).ok()?; + (descriptor.script_pubkey(), txin) + }; + tx.input[0] = satisfied; + Some(Case { + tx, + prevouts: vec![TxOut { + value: Amount::from_sat(spec.amount), + script_pubkey, + }], + flags: Flags::CONSENSUS, + }) +} + +fn config() -> ProptestConfig { + ProptestConfig { + max_global_rejects: 1 << 20, + ..ProptestConfig::default() + } +} + +#[test] +fn every_opcode() { + let placements = [ + Placement::Bare, + Placement::P2sh, + Placement::P2wsh, + Placement::Tapscript { leaf_version: 0xc0 }, + ]; + // A strictly encoded signature that verifies against nothing. + let der_sig = vec![0x30, 0x06, 0x02, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01]; + let stacks = [ + vec![], + vec![vec![]], + vec![vec![1]], + vec![vec![0xff]], + vec![vec![1], vec![2]], + vec![vec![0x81]; 3], + (1..=6).map(|i| vec![i]).collect(), + vec![der_sig.clone(), vec![0x02; 33]], + vec![der_sig.clone(), vec![0x05; 33]], + // An empty signature, which fails softly. + vec![vec![], vec![0x02; 32]], + // CHECKMULTISIG with an empty signature, and with more signatures than keys. + vec![vec![], vec![], vec![1], vec![0x02; 33], vec![1]], + vec![vec![], vec![5], vec![0x02; 33], vec![1]], + // CHECKMULTISIG of nothing, with a dummy that NULLDUMMY forbids. + vec![vec![1], vec![], vec![]], + ]; + for code in 0..=255u8 { + for script in [ + vec![Op::Raw(vec![code])], + vec![Op::Raw(vec![code]), Op::Num(1)], + ] { + for placement in &placements { + for stack in &stacks { + let case = build_script_case(&ScriptSpec { + placement: placement.clone(), + script: script.clone(), + script_sig: vec![], + stack: stack.clone(), + witness: vec![], + annex: None, + flags: Flags::CONSENSUS, + version: 2, + lock_time: 0, + sequence: 0, + amount: 1000, + }); + if let Err(e) = verdicts(&case) { + panic!("opcode {code:#04x}: {e}"); + } + } + } + } + } +} + +#[test] +fn timelock_edges() { + let args = [ + -1i64, + 0, + 1, + 0xffff, + 0x1_0000, + 1 << 22, + (1 << 22) | 1, + 499_999_999, + 500_000_000, + 500_000_001, + 1 << 31, + ]; + let lock_times = [0u32, 1, 499_999_999, 500_000_000, 500_000_001]; + let sequences = [ + 0u32, + 1, + 0xffff, + 1 << 22, + (1 << 22) | 1, + 1 << 31, + 0xffff_fffe, + u32::MAX, + ]; + for code in [OP_CLTV, OP_CSV] { + for arg in args { + for lock_time in lock_times { + for sequence in sequences { + for version in [1, 2, -1] { + for placement in [Placement::Bare, Placement::P2wsh] { + let case = build_script_case(&ScriptSpec { + placement, + script: vec![ + Op::Num(arg), + Op::Code(code), + Op::Code(OP_DROP), + Op::Num(1), + ], + script_sig: vec![], + stack: vec![], + witness: vec![], + annex: None, + flags: Flags::CONSENSUS, + version, + lock_time, + sequence, + amount: 1000, + }); + if let Err(e) = verdicts(&case) { + panic!("{e}"); + } + } + } + } + } + } + } +} + +#[test] +fn unexpected_witness() { + let no_witness = Flags { + witness: false, + taproot: false, + ..Flags::CONSENSUS + }; + for placement in [Placement::Bare, Placement::P2sh] { + for witness in [vec![], vec![vec![]], vec![vec![1]], vec![vec![], vec![1]]] { + for flags in [Flags::CONSENSUS, no_witness] { + let case = build_script_case(&ScriptSpec { + placement: placement.clone(), + script: vec![Op::Num(1)], + script_sig: vec![], + stack: vec![], + witness: witness.clone(), + annex: None, + flags, + version: 2, + lock_time: 0, + sequence: 0, + amount: 1000, + }); + if let Err(e) = verdicts(&case) { + panic!("{e}"); + } + } + } + } +} + +/// The generated limit family under consensus flags, enumerated, since an off-by-one limit fails +/// at a single delta, which 256 random cases can miss. +#[test] +fn every_limit_edge() { + for limit in LIMITS { + for delta in LIMIT_DELTAS { + for placement in LIMIT_PLACEMENTS { + let spec = LimitSpec { + limit: limit.clone(), + delta, + placement, + flags: Flags::CONSENSUS, + }; + if let Err(e) = verdicts(&build_limit_case(&spec)) { + panic!("{e}"); + } + } + } + } +} + +/// Mutations compose, so a DER edit must stay well-defined on a signature an earlier one broke. +#[test] +fn der_edits_compose() { + let mut edits = vec![ + DerEdit::Pad { s: false }, + DerEdit::Pad { s: true }, + DerEdit::Negate { s: false }, + DerEdit::Negate { s: true }, + ]; + for field in 0..6 { + for value in [0x00, 0x01, 0x02, 0x30, 0x7f, 0xff] { + edits.push(DerEdit::Header { field, value }); + } + } + for kind in [Kind::P2pkh, Kind::P2wpkh] { + let spec = SignedSpec { + inputs: vec![InputSpec { + kind, + key: 0, + sighash: Sighash::All, + amount: 1000, + sequence: u32::MAX, + annex: None, + }], + one_output: false, + version: 2, + lock_time: 0, + leaf_edit: None, + mutations: vec![], + flags: Flags::CONSENSUS, + }; + let signed = build_signed_case(&spec); + for first in &edits { + for second in &edits { + let mut case = signed.clone(); + for edit in [first, second] { + apply( + &Mutation::MangleDer { + input: 0, + edit: *edit, + }, + &mut case, + ); + } + if let Err(e) = verdicts(&case) { + panic!("{kind:?} {first:?} then {second:?}: {e}"); + } + } + } + } +} + +/// Legacy script codes are rebuilt around each signature check, walking every push encoding. +#[test] +fn script_code_walks() { + let der_sig = vec![0x30, 0x06, 0x02, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01]; + let mut long_sig = vec![0x30, 0x4d, 0x02, 0x01, 0x01, 0x02, 0x48]; + long_sig.extend(vec![0x01; 0x48]); + long_sig.push(0x01); + let checksig = Op::Code(OP_CHECKSIG); + let scripts = [ + vec![ + Op::PushData(1, vec![1]), + Op::Code(OP_DROP), + checksig.clone(), + ], + vec![ + Op::PushData(2, vec![1]), + Op::Code(OP_DROP), + checksig.clone(), + ], + vec![ + Op::PushData(4, vec![1]), + Op::Code(OP_DROP), + checksig.clone(), + ], + vec![checksig.clone(), Op::Raw(vec![OP_PUSHDATA2.to_u8(), 0x05])], + vec![ + checksig.clone(), + Op::Raw(vec![OP_PUSHDATA4.to_u8(), 0x05, 0x00]), + ], + vec![checksig.clone(), Op::Raw(vec![OP_PUSHDATA1.to_u8()])], + vec![checksig.clone(), Op::Code(OP_NOT)], + ]; + let no_dersig = Flags { + dersig: false, + ..Flags::CONSENSUS + }; + for script in &scripts { + for sig in [&der_sig, &long_sig] { + for flags in [Flags::CONSENSUS, no_dersig] { + for placement in [Placement::Bare, Placement::P2wsh] { + let case = build_script_case(&ScriptSpec { + placement, + script: script.clone(), + script_sig: vec![], + stack: vec![sig.clone(), public(&secret(1)).to_bytes()], + witness: vec![], + annex: None, + flags, + version: 2, + lock_time: 0, + sequence: 0, + amount: 1000, + }); + if let Err(e) = verdicts(&case) { + panic!("{e}"); + } + } + } + } + } +} + +/// Control blocks either side of the 128-hash depth limit, and a byte off a whole number of hashes. +/// Each output key commits to the leaf through every sibling, so only the size rule can fail it. +#[test] +fn control_block_sizes() { + let leaf = ScriptBuf::from_bytes(encode(&[Op::Num(1)])); + let internal = x_only(&secret(0)); + let mut decided = Vec::new(); + for depth in [0usize, 1, 127, 128, 129] { + let siblings = (0..depth) + .map(|i| TapNodeHash::from_byte_array([i as u8 + 1; 32])) + .collect::>(); + let mut node = TapNodeHash::from(TapLeafHash::from_script(&leaf, LeafVersion::TapScript)); + for sibling in &siblings { + node = TapNodeHash::from_node_hashes(node, *sibling); + } + let (output, parity) = internal.tap_tweak(&SECP, Some(node)); + let mut control_block = vec![LeafVersion::TapScript.to_consensus() | parity.to_u8()]; + control_block.extend(internal.serialize()); + for sibling in &siblings { + control_block.extend(sibling.to_byte_array()); + } + for extra in [-1i64, 0, 1] { + let mut control_block = control_block.clone(); + match extra { + -1 => { + control_block.pop(); + } + 1 => control_block.push(0), + _ => {} + } + let mut tx = unsigned_tx(2, 0, &[u32::MAX], 1); + tx.input[0].witness = Witness::from_slice(&[leaf.to_bytes(), control_block]); + let case = Case { + tx, + prevouts: vec![TxOut { + value: Amount::from_sat(1000), + script_pubkey: ScriptBuf::new_p2tr_tweaked(output), + }], + flags: Flags::CONSENSUS, + }; + match verdicts(&case) { + Ok(v) => decided.push(((depth, extra), v[0])), + Err(e) => panic!("depth {depth}, {extra:+} bytes: {e}"), + } + } + } + for ((depth, extra), valid) in decided { + assert_eq!( + valid, + extra == 0 && depth <= 128, + "depth {depth}, {extra:+} bytes" + ); + } +} + +/// The budget is 50 plus the witness size, and each signature costs 50, so a long enough repeat +/// leaf runs out. Every length is tried, so both sides of the boundary are. +#[test] +fn sigop_budget_crossover() { + let mut outcomes = Vec::new(); + for n in 1..=24 { + let case = build_signed_case(&SignedSpec { + inputs: vec![InputSpec { + kind: Kind::P2trScript(Leaf::Repeat(n)), + key: 0, + sighash: Sighash::Default, + amount: 1000, + sequence: u32::MAX, + annex: None, + }], + one_output: false, + version: 2, + lock_time: 0, + leaf_edit: None, + mutations: vec![], + flags: Flags::CONSENSUS, + }); + match verdicts(&case) { + Ok(v) => outcomes.push(v[0]), + Err(e) => panic!("{n} signatures: {e}"), + } + } + let first_invalid = outcomes + .iter() + .position(|v| !v) + .expect("the budget is exceeded"); + assert!(first_invalid > 1, "{outcomes:?}"); + assert!(outcomes[first_invalid..].iter().all(|v| !v), "{outcomes:?}"); +} + +proptest! { + #![proptest_config(config())] + + #[test] + fn random_scripts(spec in script_spec(random_shape())) { + verdicts(&build_script_case(&spec))?; + } + + #[test] + fn shaped_scripts(spec in script_spec(shaped())) { + verdicts(&build_script_case(&spec))?; + } + + #[test] + fn scripts_at_interpreter_limits(spec in limit_spec()) { + verdicts(&build_limit_case(&spec))?; + } + + #[test] + fn mutated_signed_spends(spec in signed_spec()) { + let mut clean = spec.clone(); + clean.leaf_edit = None; + clean.mutations.clear(); + clean.flags = Flags::CONSENSUS; + for input in &mut clean.inputs { + if matches!(input.sighash, Sighash::ExplicitDefault) { + input.sighash = Sighash::Default; + } + } + let clean = build_signed_case(&clean); + // A long enough repeat leaf exceeds the sigop budget, and is meant to. + if spec.inputs.iter().any(|i| matches!(i.kind, Kind::P2trScript(Leaf::Repeat(_)))) { + verdicts(&clean)?; + } else { + assert_all_valid(&clean)?; + } + + let mut case = build_signed_case(&spec); + for mutation in &spec.mutations { + apply(mutation, &mut case); + } + verdicts(&case)?; + } + + #[test] + fn miniscript_spends(spec in miniscript_spec(false)) { + let case = build_miniscript_case(&spec); + prop_assume!(case.is_some()); + assert_all_valid(&case.unwrap())?; + } + + #[test] + fn mutated_miniscript_spends(spec in miniscript_spec(true)) { + let mut clean = spec.clone(); + clean.pre_sign.clear(); + clean.mutations.clear(); + let clean = build_miniscript_case(&clean); + prop_assume!(clean.is_some()); + assert_all_valid(&clean.unwrap())?; + + let mut case = build_miniscript_case(&spec).unwrap(); + for mutation in &spec.mutations { + apply(mutation, &mut case); + } + verdicts(&case)?; + } +} diff --git a/docs/input-validation.md b/docs/input-validation.md new file mode 100644 index 0000000000..e739859630 --- /dev/null +++ b/docs/input-validation.md @@ -0,0 +1,96 @@ +# ADR: input script validation in `bdk_chain` + +## Problem + +A chain source can inject an invalid unconfirmed transaction that spends the wallet's on-chain +outputs. `bdk_chain` believes it: the coins look spent, the balance moves, and the wallet replaces +against (RBF) or bumps (CPFP) a transaction that can never confirm. + +## Decision + +- **Only a provably Invalid unconfirmed candidate is excluded, during canonicalization.** + `CanonicalTask::mark_canonical` runs `TxGraph::validate_tx` on a candidate that would be canonical + only by being seen (`last_seen`, or a block outside the best chain), and on its ancestors that are + not canonical yet. For each txid it selects a Valid whole copy if available, otherwise Unknown. + Only if every retained copy is Invalid is the candidate rejected through the existing undo path, + along with everything spending from it. The canonical view carries the exact selected copy. + Valid and Unknown are both eligible, so nothing has to be proven valid. +- **Anchored and assumed transactions are never checked.** An anchor in the best chain, or + `assume_canonical`, outranks validation for the transaction and the ancestry it pulls in. An + anchor's trust is a matter of proof of work. +- **One rule set:** today's consensus script rules (`bdk_script_verify::Flags::CONSENSUS`), the + ones every unconfirmed transaction must meet. There's no policy, sigop counting or block rules. +- **Floating txouts count as prevouts.** Each spent output comes from the graph's full transaction + if it has one, and otherwise from the floating txout. An input is Unknown only when the graph has + neither. Each verdict records `relies_on_floating`, and canonicalization ignores it. +- **No validation state persists between canonicalizations.** The graph keeps no verdicts and there + is no switch. Each task memoizes its selected copies (or all-Invalid results), including across + rejected walks, and judges the graph as it is. `validate_tx` answers for any transaction, in the + graph or not. +- **The verifier is a new pure-Rust, `no_std` crate, `bdk_script_verify`** + (`verify_input(&mut cache, vin, flags)` and `verify_transaction(tx, prevouts, flags)`), adapted + from rbitcoin's interpreter. Missing + prevouts are reported as missing, never as a script failure. Its tests run Core's script + vectors, plus differential tests against Bitcoin Core's libbitcoinkernel. They use the + `bitcoinkernel` 0.3.2 crate, which bundles Core master at `70d9ec7f` (2026-07-14). No published + crate bundles a tagged release, and that snapshot's script interpreter differs from v32.1 only + by refactors. +- **Every received copy of a txid is kept, by wtxid** (SQLite schema v4, `bdk_tx_copies`). The + ordinary graph queries expose one copy, chosen without reading scripts: the minimum of + `(empty witnesses, witness size, wtxid)`. Canonicalization uses that order within each validity + class, preferring Valid over Unknown and rejecting only all-Invalid sets. It stops checking + copies at the first Valid result. Witnesses from different copies are never spliced. + +## Consequences + +- An injected spend of the wallet's outputs can't carry a valid signature, so it comes out Invalid + and is excluded, against a full parent or a floating txout. The valid conflict wins. +- Canonicalization may check multiple witness copies of an unconfirmed transaction. Selection is + memoized for the task's lifetime; no cache is kept across canonicalizations. +- Each copy is checked through `bdk_script_verify::verify_transaction`, which shares signature-hash + caches and prepared Taproot prevouts across its inputs. Per-input dependency reports stay + independent. The caches borrow immutable transaction and prevout data and are discarded after + the call, so they cannot outlive a copy or graph update. +- `ChangeSet`'s type is unchanged. Its `txs` may hold several copies of one txid. +- Unsigned or placeholder-script drafts inserted with a `last_seen` drop out once their parents + are present, unless they are in `assume_canonical`. On master they stay canonical. + +## Known limitations + +- **A floating txout is the chain source's word.** A lying amount makes a genuine segwit or + taproot spend Invalid. That can drop a transaction the wallet built with a foreign input, such as + a payjoin, which master keeps. Inserting full parents would make those verdicts proof. +- Legacy signature hashing and input-specific script work can still scale quadratically. Segwit + and Taproot reuse transaction-wide hashes, but returning a list of missing prevouts for every + input can itself require quadratic output space. + +## Signature-hash cache validation + +Tests compare shared and independent caches, including repeated and reversed input order, +missing prevouts, failed signatures, and mixed legacy, segwit and Taproot inputs. Test-only +counters establish that shared BIP143 midstates and complete Taproot prevouts are prepared once +per transaction, rather than once per input. Core transaction vectors and differential tests +also compare the full results and dependency reports of both cache lifetimes. + +A local release-build comparison on Apple Silicon with Rust 1.96 measured `TxGraph::validate_tx` +before this change (`d35b3277`) and with the shared cache, using isolated source and target +directories. Both ran the same valid signed fixtures, excluding signing and graph construction. +Times below are microseconds per transaction, averaged over 100 calls for 1/16 inputs and 10 +calls for 128/1024 inputs. These are indicative timings, not test thresholds. + +| Inputs | Segwit before | Segwit shared | Taproot before | Taproot shared | +|-------:|--------------:|--------------:|---------------:|---------------:| +| 1 | 27.1 | 27.2 | 26.5 | 26.4 | +| 16 | 452.1 | 422.9 | 467.1 | 420.0 | +| 128 | 4,735.3 | 3,472.7 | 5,996.8 | 3,438.1 | +| 1024 | 104,270.8 | 28,529.0 | 393,902.2 | 27,908.1 | + +These timings include signature verification and graph prevout lookup. The improvement removes +repeated transaction-wide work; it does not make arbitrary scripts or legacy signature hashing +linear. The ignored `signature_hash_scaling` test provides an ongoing comparison of fresh +per-input caches against the graph's shared-cache path: + +```sh +cargo test --release -p bdk_chain --all-features --test test_input_validation \ + signature_hash_scaling -- --exact --ignored --nocapture +``` diff --git a/justfile b/justfile index e0b1bdbb32..fa32d5c393 100644 --- a/justfile +++ b/justfile @@ -35,6 +35,7 @@ test: @just _test-electrum @just _test-esplora @just _test-file_store + @just _test-script_verify @just _test-testenv _test-bitcoind_rpc: @@ -46,6 +47,9 @@ _test-chain: _test-core: cargo test -p bdk_core --all-features +_test-script_verify: + cargo test -p bdk_script_verify --all-features + _test-electrum: cargo test -p bdk_electrum --all-features