blvm-consensus 0.1.12

Bitcoin Commons BLVM: Direct mathematical implementation of Bitcoin consensus rules from the Orange Paper
Documentation
//! Script verification flag combination testing
//!
//! Tests all script verification flag combinations to ensure consensus correctness.
//! consensus uses 32 different flag combinations, and all must be tested.
//!
//! Flags tested:
//! - SCRIPT_VERIFY_P2SH (0x01)
//! - SCRIPT_VERIFY_STRICTENC (0x02)
//! - SCRIPT_VERIFY_DERSIG (0x04)
//! - SCRIPT_VERIFY_LOW_S (0x08)
//! - SCRIPT_VERIFY_NULLDUMMY (0x10)
//! - SCRIPT_VERIFY_SIGPUSHONLY (0x20)
//! - SCRIPT_VERIFY_MINIMALDATA (0x40)
//! - SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS (0x80)
//! - SCRIPT_VERIFY_CLEANSTACK (0x100)
//! - SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY (0x200)
//! - SCRIPT_VERIFY_CHECKSEQUENCEVERIFY (0x400)
//! - SCRIPT_VERIFY_WITNESS (0x800)
//! - SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM (0x1000)
//! - SCRIPT_VERIFY_MINIMALIF (0x2000)
//! - SCRIPT_VERIFY_TAPROOT (0x4000)

use blvm_consensus::script::{eval_script, verify_script, SigVersion};

/// All script verification flags
pub const ALL_FLAGS: &[u32] = &[
    0x01,   // SCRIPT_VERIFY_P2SH
    0x02,   // SCRIPT_VERIFY_STRICTENC
    0x04,   // SCRIPT_VERIFY_DERSIG
    0x08,   // SCRIPT_VERIFY_LOW_S
    0x10,   // SCRIPT_VERIFY_NULLDUMMY
    0x20,   // SCRIPT_VERIFY_SIGPUSHONLY
    0x40,   // SCRIPT_VERIFY_MINIMALDATA
    0x80,   // SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS
    0x100,  // SCRIPT_VERIFY_CLEANSTACK
    0x200,  // SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY
    0x400,  // SCRIPT_VERIFY_CHECKSEQUENCEVERIFY
    0x800,  // SCRIPT_VERIFY_WITNESS
    0x1000, // SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM
    0x2000, // SCRIPT_VERIFY_MINIMALIF
    0x4000, // SCRIPT_VERIFY_TAPROOT
];

/// Generate all 32 flag combinations (2^5 = 32)
///
/// Tests combinations of the 5 most common flags:
/// - P2SH, STRICTENC, DERSIG, WITNESS, TAPROOT
pub fn generate_all_flag_combinations() -> Vec<u32> {
    let mut combinations = Vec::new();

    // Generate all 32 combinations (2^5)
    for i in 0..32 {
        let mut flags = 0u32;
        if i & 0x01 != 0 {
            flags |= 0x01;
        } // P2SH
        if i & 0x02 != 0 {
            flags |= 0x02;
        } // STRICTENC
        if i & 0x04 != 0 {
            flags |= 0x04;
        } // DERSIG
        if i & 0x08 != 0 {
            flags |= 0x800;
        } // WITNESS
        if i & 0x10 != 0 {
            flags |= 0x4000;
        } // TAPROOT
        combinations.push(flags);
    }

    combinations
}

/// Test all flag combinations with a simple script
#[test]
fn test_all_flag_combinations_simple() {
    let flag_combinations = generate_all_flag_combinations();
    let script = vec![0x51]; // OP_1

    for flags in flag_combinations {
        let mut stack = Vec::new();
        let result = eval_script(&script, &mut stack, flags, SigVersion::Base);

        // Should not panic with any flag combination
        assert!(
            result.is_ok() || result.is_err(),
            "Script failed with flags 0x{flags:x}"
        );
    }
}

/// Test flag combinations with P2SH scripts
#[test]
fn test_flag_combinations_p2sh() {
    let flag_combinations = generate_all_flag_combinations();

    // P2SH script: OP_HASH160 <hash> OP_EQUAL
    let script_pubkey = vec![
        0xa9, // OP_HASH160
        0x14, // Push 20 bytes
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x87, // OP_EQUAL
    ];

    let script_sig = vec![0x51]; // OP_1

    for flags in flag_combinations {
        let result = verify_script(&script_sig, &script_pubkey, None, flags);

        // Should not panic
        assert!(
            result.is_ok() || result.is_err(),
            "P2SH script failed with flags 0x{flags:x}"
        );
    }
}

/// Test flag interactions
///
/// Verifies that flags interact correctly when combined.
#[test]
fn test_flag_interactions() {
    // Test P2SH + WITNESS combination
    let flags = 0x01 | 0x800; // P2SH + WITNESS
    let script = vec![0x51]; // OP_1
    let mut stack = Vec::new();
    let result = eval_script(&script, &mut stack, flags, SigVersion::Base);
    assert!(result.is_ok() || result.is_err());

    // Test STRICTENC + DERSIG combination
    let flags = 0x02 | 0x04; // STRICTENC + DERSIG
    let script = vec![0x51]; // OP_1
    let mut stack = Vec::new();
    let result = eval_script(&script, &mut stack, flags, SigVersion::Base);
    assert!(result.is_ok() || result.is_err());

    // Test TAPROOT + WITNESS combination
    let flags = 0x4000 | 0x800; // TAPROOT + WITNESS (legacy mapping in this test module)
    let script = vec![0x51]; // OP_1
    let mut stack = Vec::new();
    let result = eval_script(&script, &mut stack, flags, SigVersion::Base);
    assert!(result.is_ok() || result.is_err());
}

/// Test historical flag changes
///
/// Verifies behavior with flags that were added at different times:
/// - Pre-SegWit: No WITNESS flag
/// - Post-SegWit: WITNESS flag enabled
/// - Post-Taproot: TAPROOT flag enabled
#[test]
fn test_historical_flag_changes() {
    // Pre-SegWit flags (no WITNESS)
    let pre_segwit_flags = 0x01 | 0x02 | 0x04; // P2SH + STRICTENC + DERSIG
    let script = vec![0x51]; // OP_1
    let mut stack = Vec::new();
    let result = eval_script(&script, &mut stack, pre_segwit_flags, SigVersion::Base);
    assert!(result.is_ok() || result.is_err());

    // Post-SegWit flags (WITNESS enabled)
    let post_segwit_flags = 0x01 | 0x02 | 0x04 | 0x800; // + WITNESS
    let script = vec![0x51]; // OP_1
    let mut stack = Vec::new();
    let result = eval_script(&script, &mut stack, post_segwit_flags, SigVersion::Base);
    assert!(result.is_ok() || result.is_err());

    // Post-Taproot flags (TAPROOT enabled)
    let post_taproot_flags = 0x01 | 0x02 | 0x04 | 0x800 | 0x4000; // + TAPROOT (legacy mapping in this test module)
    let script = vec![0x51]; // OP_1
    let mut stack = Vec::new();
    let result = eval_script(&script, &mut stack, post_taproot_flags, SigVersion::Base);
    assert!(result.is_ok() || result.is_err());
}

/// Test flag inheritance in transaction chains
///
/// Verifies that flags are correctly inherited when validating
/// transaction chains (e.g., P2SH transactions).
#[test]
fn test_flag_inheritance() {
    // Test that flags are properly applied in nested script execution
    // (e.g., P2SH redeem script execution)

    let flags = 0x01; // P2SH
    let script = vec![0x51]; // OP_1
    let mut stack = Vec::new();
    let result = eval_script(&script, &mut stack, flags, SigVersion::Base);

    // Flags should be inherited correctly
    assert!(result.is_ok() || result.is_err());
}

/// Test all individual flags
///
/// Verifies that each flag works correctly when used alone.
#[test]
fn test_individual_flags() {
    for &flag in ALL_FLAGS {
        let script = vec![0x51]; // OP_1
        let mut stack = Vec::new();
        let result = eval_script(&script, &mut stack, flag, SigVersion::Base);

        // Each flag should work correctly
        assert!(
            result.is_ok() || result.is_err(),
            "Flag 0x{flag:x} caused error"
        );
    }
}

/// Test flag combinations with edge case scripts
///
/// Tests flag combinations with scripts that trigger edge cases.
#[test]
fn test_flag_combinations_edge_cases() {
    let flag_combinations = generate_all_flag_combinations();

    // Edge case scripts
    let edge_case_scripts = vec![
        vec![],         // Empty script
        vec![0x51; 10], // Repeated opcodes
        vec![0x00],     // OP_0
        vec![0xff],     // Invalid opcode
    ];

    for script in edge_case_scripts {
        for flags in &flag_combinations {
            let mut stack = Vec::new();
            let result = eval_script(&script, &mut stack, *flags, SigVersion::Base);

            // Should handle edge cases with any flag combination
            assert!(result.is_ok() || result.is_err());
        }
    }
}

/// Comprehensive flag combination test
///
/// Tests all flag combinations with multiple script types.
#[test]
fn test_comprehensive_flag_combinations() {
    let flag_combinations = generate_all_flag_combinations();

    // Test scripts
    let test_scripts = vec![
        (vec![0x51], vec![0x51]),       // Simple: OP_1, OP_1
        (vec![0x51, 0x76], vec![0x51]), // OP_1 OP_DUP, OP_1
        (vec![], vec![0x51]),           // Empty scriptSig, OP_1 scriptPubKey
    ];

    for (script_sig, script_pubkey) in test_scripts {
        for flags in &flag_combinations {
            let result = verify_script(&script_sig, &script_pubkey, None, *flags);

            // Should handle all combinations
            assert!(
                result.is_ok() || result.is_err(),
                "Script failed with flags 0x{flags:x}"
            );
        }
    }
}