nour 1.0.0

High-performance Bitcoin SV toolkit for transactions, scripts, P2P, and wallets
//! Script opcodes and interpreter for Bitcoin SV.
///
/// Supports building and evaluating transaction scripts with checkers for validation.
/// Aligned with BSV consensus (pre-Genesis rules via flags).
///
/// # Examples
///
/// Evaluate a script that divides two numbers:
/// ```
/// use nour::script::{op_codes::*, Script, TransactionlessChecker, NO_FLAGS};
///
/// let mut script = Script::new();
/// script.append(OP_10);
/// script.append(OP_5);
/// script.append(OP_DIV);
///
/// script.eval(&mut TransactionlessChecker::default(), NO_FLAGS).unwrap();
/// ```
use crate::script::op_codes::*;
use crate::util::{Error, Result};
use hex;
use std::fmt;
pub mod checker;
pub mod interpreter;
#[allow(dead_code)]
/// Script opcodes and utilities.
pub mod op_codes;
pub mod stack;
pub use self::checker::{Checker, TransactionChecker, TransactionlessChecker};
pub(crate) use self::interpreter::next_op;
pub use self::interpreter::{NO_FLAGS, PREGENESIS_RULES};

/// Transaction script as a sequence of opcodes and data.
#[derive(Default, Clone, PartialEq, Eq, Hash)]
pub struct Script(pub Vec<u8>);

impl Script {
    /// Creates a new empty script.
    #[must_use]
    pub fn new() -> Self {
        Self::default()
    }

    /// Appends a single opcode or data byte.
    pub fn append(&mut self, byte: u8) {
        self.0.push(byte);
    }

    /// Appends a slice of data.
    pub fn append_slice(&mut self, slice: &[u8]) {
        self.0.extend_from_slice(slice);
    }

    /// Appends the opcodes and data to push it onto the stack.
    ///
    /// # Errors
    /// Returns `Error::BadArgument` if data exceeds consensus limits (4.2GB for BSV).
    pub fn append_data(&mut self, data: &[u8]) -> Result<()> {
        let len = data.len();
        if len > u32::MAX as usize {
            return Err(Error::BadArgument("Data too large for push".to_string()));
        }
        match len {
            0 => self.0.push(OP_0),
            1..=75 => {
                self.0.push(len as u8);
                self.0.extend_from_slice(data);
            }
            76..=255 => {
                self.0.push(OP_PUSHDATA1);
                self.0.push(len as u8);
                self.0.extend_from_slice(data);
            }
            256..=65535 => {
                self.0.push(OP_PUSHDATA2);
                self.0.push((len & 0xFF) as u8);
                self.0.push(((len >> 8) & 0xFF) as u8);
                self.0.extend_from_slice(data);
            }
            _ => {
                self.0.push(OP_PUSHDATA4);
                self.0.push((len & 0xFF) as u8);
                self.0.push(((len >> 8) & 0xFF) as u8);
                self.0.push(((len >> 16) & 0xFF) as u8);
                self.0.push(((len >> 24) & 0xFF) as u8);
                self.0.extend_from_slice(data);
            }
        }
        Ok(())
    }

    /// Appends the opcodes to push a number to the stack.
    ///
    /// The number must be in the range [-2^31 + 1, 2^31 - 1].
    ///
    /// # Errors
    /// Returns `Error::BadData` if out of range.
    pub fn append_num(&mut self, n: i32) -> Result<()> {
        let _ = self.append_data(&stack::encode_num(n as i64)?);
        Ok(())
    }

    /// Evaluates the script using the provided checker and flags.
    ///
    /// # Errors
    /// Propagates from checker or interpreter (e.g., invalid opcode, sig fail).
    #[must_use]
    pub fn eval<T: Checker>(&self, checker: &mut T, flags: u32) -> Result<()> {
        interpreter::eval(&self.0, checker, flags)
    }
}

impl fmt::Debug for Script {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let script = &self.0;
        let mut ret = String::with_capacity(script.len() * 3); // Rough estimate
        ret.push('[');
        let mut i = 0;
        while i < script.len() {
            if i != 0 {
                ret.push(' ');
            }
            match script[i] {
                OP_0 => ret.push_str("OP_0"),
                OP_1NEGATE => ret.push_str("OP_1NEGATE"),
                OP_1 => ret.push_str("OP_1"),
                OP_2 => ret.push_str("OP_2"),
                OP_3 => ret.push_str("OP_3"),
                OP_4 => ret.push_str("OP_4"),
                OP_5 => ret.push_str("OP_5"),
                OP_6 => ret.push_str("OP_6"),
                OP_7 => ret.push_str("OP_7"),
                OP_8 => ret.push_str("OP_8"),
                OP_9 => ret.push_str("OP_9"),
                OP_10 => ret.push_str("OP_10"),
                OP_11 => ret.push_str("OP_11"),
                OP_12 => ret.push_str("OP_12"),
                OP_13 => ret.push_str("OP_13"),
                OP_14 => ret.push_str("OP_14"),
                OP_15 => ret.push_str("OP_15"),
                OP_16 => ret.push_str("OP_16"),
                len @ 1..=75 => {
                    ret.push_str(&format!("OP_PUSH{} ", len));
                    let end = i + 1 + len as usize;
                    if end <= script.len() {
                        ret.push_str(&hex::encode(&script[i + 1..end]));
                    } else {
                        break;
                    }
                }
                OP_PUSHDATA1 => {
                    ret.push_str("OP_PUSHDATA1 ");
                    if i + 2 > script.len() {
                        break;
                    }
                    let len = script[i + 1] as usize;
                    ret.push_str(&format!("{} ", len));
                    let end = i + 2 + len;
                    if end <= script.len() {
                        ret.push_str(&hex::encode(&script[i + 2..end]));
                    } else {
                        break;
                    }
                }
                OP_PUSHDATA2 => {
                    ret.push_str("OP_PUSHDATA2 ");
                    if i + 3 > script.len() {
                        break;
                    }
                    let len = u16::from_le_bytes([script[i + 1], script[i + 2]]) as usize;
                    ret.push_str(&format!("{} ", len));
                    let end = i + 3 + len;
                    if end <= script.len() {
                        ret.push_str(&hex::encode(&script[i + 3..end]));
                    } else {
                        break;
                    }
                }
                OP_PUSHDATA4 => {
                    ret.push_str("OP_PUSHDATA4 ");
                    if i + 5 > script.len() {
                        break;
                    }
                    let len = u32::from_le_bytes([
                        script[i + 1],
                        script[i + 2],
                        script[i + 3],
                        script[i + 4],
                    ]) as usize;
                    ret.push_str(&format!("{} ", len));
                    let end = i + 5 + len;
                    if end <= script.len() {
                        ret.push_str(&hex::encode(&script[i + 5..end]));
                    } else {
                        break;
                    }
                }
                OP_NOP => ret.push_str("OP_NOP"),
                OP_IF => ret.push_str("OP_IF"),
                OP_NOTIF => ret.push_str("OP_NOTIF"),
                OP_ELSE => ret.push_str("OP_ELSE"),
                OP_ENDIF => ret.push_str("OP_ENDIF"),
                OP_VERIFY => ret.push_str("OP_VERIFY"),
                OP_RETURN => ret.push_str("OP_RETURN"),
                OP_TOALTSTACK => ret.push_str("OP_TOALTSTACK"),
                OP_FROMALTSTACK => ret.push_str("OP_FROMALTSTACK"),
                OP_IFDUP => ret.push_str("OP_IFDUP"),
                OP_DEPTH => ret.push_str("OP_DEPTH"),
                OP_DROP => ret.push_str("OP_DROP"),
                OP_DUP => ret.push_str("OP_DUP"),
                OP_NIP => ret.push_str("OP_NIP"),
                OP_OVER => ret.push_str("OP_OVER"),
                OP_PICK => ret.push_str("OP_PICK"),
                OP_ROLL => ret.push_str("OP_ROLL"),
                OP_ROT => ret.push_str("OP_ROT"),
                OP_SWAP => ret.push_str("OP_SWAP"),
                OP_TUCK => ret.push_str("OP_TUCK"),
                OP_2DROP => ret.push_str("OP_2DROP"),
                OP_2DUP => ret.push_str("OP_2DUP"),
                OP_3DUP => ret.push_str("OP_3DUP"),
                OP_2OVER => ret.push_str("OP_2OVER"),
                OP_2ROT => ret.push_str("OP_2ROT"),
                OP_2SWAP => ret.push_str("OP_2SWAP"),
                OP_CAT => ret.push_str("OP_CAT"),
                OP_SPLIT => ret.push_str("OP_SPLIT"),
                OP_SIZE => ret.push_str("OP_SIZE"),
                OP_AND => ret.push_str("OP_AND"),
                OP_OR => ret.push_str("OP_OR"),
                OP_XOR => ret.push_str("OP_XOR"),
                OP_EQUAL => ret.push_str("OP_EQUAL"),
                OP_EQUALVERIFY => ret.push_str("OP_EQUALVERIFY"),
                OP_1ADD => ret.push_str("OP_1ADD"),
                OP_1SUB => ret.push_str("OP_1SUB"),
                OP_NEGATE => ret.push_str("OP_NEGATE"),
                OP_ABS => ret.push_str("OP_ABS"),
                OP_NOT => ret.push_str("OP_NOT"),
                OP_0NOTEQUAL => ret.push_str("OP_0NOTEQUAL"),
                OP_ADD => ret.push_str("OP_ADD"),
                OP_SUB => ret.push_str("OP_SUB"),
                OP_DIV => ret.push_str("OP_DIV"),
                OP_MOD => ret.push_str("OP_MOD"),
                OP_BOOLAND => ret.push_str("OP_BOOLAND"),
                OP_BOOLOR => ret.push_str("OP_BOOLOR"),
                OP_NUMEQUAL => ret.push_str("OP_NUMEQUAL"),
                OP_NUMEQUALVERIFY => ret.push_str("OP_NUMEQUALVERIFY"),
                OP_NUMNOTEQUAL => ret.push_str("OP_NUMNOTEQUAL"),
                OP_LESSTHAN => ret.push_str("OP_LESSTHAN"),
                OP_GREATERTHAN => ret.push_str("OP_GREATERTHAN"),
                OP_LESSTHANOREQUAL => ret.push_str("OP_LESSTHANOREQUAL"),
                OP_GREATERTHANOREQUAL => ret.push_str("OP_GREATERTHANOREQUAL"),
                OP_MIN => ret.push_str("OP_MIN"),
                OP_MAX => ret.push_str("OP_MAX"),
                OP_WITHIN => ret.push_str("OP_WITHIN"),
                OP_NUM2BIN => ret.push_str("OP_NUM2BIN"),
                OP_BIN2NUM => ret.push_str("OP_BIN2NUM"),
                OP_RIPEMD160 => ret.push_str("OP_RIPEMD160"),
                OP_SHA1 => ret.push_str("OP_SHA1"),
                OP_SHA256 => ret.push_str("OP_SHA256"),
                OP_HASH160 => ret.push_str("OP_HASH160"),
                OP_HASH256 => ret.push_str("OP_HASH256"),
                OP_CODESEPARATOR => ret.push_str("OP_CODESEPARATOR"),
                OP_CHECKSIG => ret.push_str("OP_CHECKSIG"),
                OP_CHECKSIGVERIFY => ret.push_str("OP_CHECKSIGVERIFY"),
                OP_CHECKMULTISIG => ret.push_str("OP_CHECKMULTISIG"),
                OP_CHECKMULTISIGVERIFY => ret.push_str("OP_CHECKMULTISIGVERIFY"),
                OP_CHECKLOCKTIMEVERIFY => ret.push_str("OP_CHECKLOCKTIMEVERIFY"),
                OP_CHECKSEQUENCEVERIFY => ret.push_str("OP_CHECKSEQUENCEVERIFY"),
                _ => ret.push_str(&format!("{}", script[i])),
            }
            i = next_op(i, script);
        }
        if i < script.len() {
            for j in i..script.len() {
                ret.push_str(&format!(" {}", script[j]));
            }
        }
        ret.push(']');
        f.write_str(&ret)
    }
}

#[cfg(test)]
mod tests {
    use super::op_codes::*;
    use super::*;
    use pretty_assertions::assert_eq;

    #[test]
    fn append_data() {
        let mut s = Script::new();
        s.append_data(&[]).unwrap();
        assert_eq!(s.0.len(), 1);
        let mut s = Script::new();
        s.append_data(&vec![0; 1]).unwrap();
        assert_eq!(s.0[0], OP_PUSH + 1);
        assert_eq!(s.0.len(), 2);
        let mut s = Script::new();
        s.append_data(&vec![0; 75]).unwrap();
        assert_eq!(s.0[0], OP_PUSH + 75);
        assert_eq!(s.0.len(), 76);
        let mut s = Script::new();
        s.append_data(&vec![0; 76]).unwrap();
        assert_eq!(s.0[0], OP_PUSHDATA1);
        assert_eq!(s.0[1], 76);
        assert_eq!(s.0.len(), 78);
        let mut s = Script::new();
        s.append_data(&vec![0; 255]).unwrap();
        assert_eq!(s.0[0], OP_PUSHDATA1);
        assert_eq!(s.0[1], 255);
        assert_eq!(s.0.len(), 257);
        let mut s = Script::new();
        s.append_data(&vec![0; 256]).unwrap();
        assert_eq!(s.0[0], OP_PUSHDATA2);
        assert_eq!(s.0[1], 0);
        assert_eq!(s.0[2], 1);
        assert_eq!(s.0.len(), 259);
        let mut s = Script::new();
        s.append_data(&vec![0; 65535]).unwrap();
        assert_eq!(s.0[0], OP_PUSHDATA2);
        assert_eq!(s.0[1], 255);
        assert_eq!(s.0[2], 255);
        assert_eq!(s.0.len(), 65538);
        let mut s = Script::new();
        s.append_data(&vec![0; 65536]).unwrap();
        assert_eq!(s.0[0], OP_PUSHDATA4);
        assert_eq!(s.0[1], 0);
        assert_eq!(s.0[2], 0);
        assert_eq!(s.0[3], 1);
        assert_eq!(s.0[4], 0);
        assert_eq!(s.0.len(), 65541);
    }

    #[test]
    fn append_data_too_large() {
        let mut s = Script::new();
        let result = s.append_data(&vec![0u8; (u32::MAX as usize) + 1]);
        assert!(result.is_err());
        assert_eq!(
            result.unwrap_err().to_string(),
            "Bad argument: Data too large for push"
        );
    }
}