Skip to main content

bsv_script/interpreter/
error.rs

1//! Interpreter error types matching the Go SDK's errs package.
2
3use std::fmt;
4
5/// Error codes for the script interpreter.
6#[derive(Debug, Clone, Copy, PartialEq, Eq)]
7pub enum InterpreterErrorCode {
8    /// An internal interpreter error occurred.
9    Internal,
10    /// No error; used as a sentinel for early successful return.
11    Ok,
12    /// The combination of script flags is invalid.
13    InvalidFlags,
14    /// An index is out of range for the operation.
15    InvalidIndex,
16    /// The address type is not supported.
17    UnsupportedAddress,
18    /// The script is not a valid multisig script.
19    NotMultisigScript,
20    /// The number of required signatures exceeds the allowed maximum.
21    TooManyRequiredSigs,
22    /// The OP_RETURN data payload exceeds the allowed size.
23    TooMuchNullData,
24    /// Invalid parameters were supplied to the interpreter.
25    InvalidParams,
26    /// Script execution returned early (post-genesis OP_RETURN).
27    EarlyReturn,
28    /// The stack is empty when an operand was expected.
29    EmptyStack,
30    /// The top stack value is false at the end of script execution.
31    EvalFalse,
32    /// Script execution ended before all opcodes were processed.
33    ScriptUnfinished,
34    /// The program counter points to an invalid script position.
35    InvalidProgramCounter,
36    /// The script exceeds the maximum allowed size.
37    ScriptTooBig,
38    /// A data element exceeds the maximum allowed element size.
39    ElementTooBig,
40    /// The number of non-push opcodes exceeds the allowed maximum.
41    TooManyOperations,
42    /// The combined data and alt stack size exceeds the allowed maximum.
43    StackOverflow,
44    /// The public key count in a multisig is out of range.
45    InvalidPubKeyCount,
46    /// The signature count in a multisig is out of range.
47    InvalidSignatureCount,
48    /// A numeric operand exceeds the maximum allowed byte length.
49    NumberTooBig,
50    /// A numeric operand is below the minimum allowed value.
51    NumberTooSmall,
52    /// Division or modulo by zero was attempted.
53    DivideByZero,
54    /// OP_VERIFY failed because the top stack value is false.
55    Verify,
56    /// OP_EQUALVERIFY failed because the top two values are not equal.
57    EqualVerify,
58    /// OP_NUMEQUALVERIFY failed because the top two numeric values differ.
59    NumEqualVerify,
60    /// OP_CHECKSIGVERIFY failed because signature verification failed.
61    CheckSigVerify,
62    /// OP_CHECKMULTISIGVERIFY failed because multisig verification failed.
63    CheckMultiSigVerify,
64    /// A disabled opcode was encountered during execution.
65    DisabledOpcode,
66    /// A reserved opcode was encountered during execution.
67    ReservedOpcode,
68    /// A push opcode has a malformed or truncated data payload.
69    MalformedPush,
70    /// A stack operation references an invalid stack index.
71    InvalidStackOperation,
72    /// An IF/ELSE/ENDIF block is not properly balanced.
73    UnbalancedConditional,
74    /// An input length is invalid for the operation.
75    InvalidInputLength,
76    /// A data push does not use the minimal encoding required by policy.
77    MinimalData,
78    /// An OP_IF/OP_NOTIF argument is not minimally encoded (must be empty or 0x01).
79    MinimalIf,
80    /// The sighash type byte in a signature is invalid.
81    InvalidSigHashType,
82    /// The DER-encoded signature is shorter than the minimum valid length.
83    SigTooShort,
84    /// The DER-encoded signature is longer than the maximum valid length.
85    SigTooLong,
86    /// The DER sequence identifier byte is missing or invalid.
87    SigInvalidSeqID,
88    /// The DER data length field does not match the actual signature length.
89    SigInvalidDataLen,
90    /// The DER S-type identifier byte (0x02) is missing.
91    SigMissingSTypeID,
92    /// The DER S-length field is missing.
93    SigMissingSLen,
94    /// The DER S-length value is invalid.
95    SigInvalidSLen,
96    /// The DER R integer type identifier byte (0x02) is invalid.
97    SigInvalidRIntID,
98    /// The DER R value has zero length.
99    SigZeroRLen,
100    /// The DER R value is negative (leading byte has high bit set without padding).
101    SigNegativeR,
102    /// The DER R value has excessive zero-byte padding.
103    SigTooMuchRPadding,
104    /// The DER S integer type identifier byte (0x02) is invalid.
105    SigInvalidSIntID,
106    /// The DER S value has zero length.
107    SigZeroSLen,
108    /// The DER S value is negative (leading byte has high bit set without padding).
109    SigNegativeS,
110    /// The DER S value has excessive zero-byte padding.
111    SigTooMuchSPadding,
112    /// The S value in the signature is not in the low-S canonical form.
113    SigHighS,
114    /// The unlocking script contains non-push opcodes when push-only is required.
115    NotPushOnly,
116    /// The dummy element for OP_CHECKMULTISIG is not empty (null dummy rule).
117    SigNullDummy,
118    /// A public key does not conform to the required encoding format.
119    PubKeyType,
120    /// The stack contains extra items after execution when clean stack is enforced.
121    CleanStack,
122    /// A failed signature check did not have an empty signature (NULLFAIL rule).
123    NullFail,
124    /// An upgradable NOP opcode was encountered and the discourage flag is set.
125    DiscourageUpgradableNOPs,
126    /// The lock time value is negative.
127    NegativeLockTime,
128    /// The transaction lock time does not satisfy OP_CHECKLOCKTIMEVERIFY.
129    UnsatisfiedLockTime,
130    /// The SIGHASH_FORKID flag is missing or incorrectly set.
131    IllegalForkID,
132}
133
134impl fmt::Display for InterpreterErrorCode {
135    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
136        write!(f, "{:?}", self)
137    }
138}
139
140/// A script interpreter error with an error code and description.
141#[derive(Debug, Clone)]
142pub struct InterpreterError {
143    /// The error code identifying the class of error.
144    pub code: InterpreterErrorCode,
145    /// A human-readable description of the error.
146    pub description: String,
147}
148
149impl InterpreterError {
150    /// Create a new interpreter error from an error code and description string.
151    pub fn new(code: InterpreterErrorCode, description: String) -> Self {
152        InterpreterError { code, description }
153    }
154}
155
156impl fmt::Display for InterpreterError {
157    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
158        write!(f, "{}", self.description)
159    }
160}
161
162impl std::error::Error for InterpreterError {}
163
164/// Check if an error has a specific error code.
165pub fn is_error_code(err: &InterpreterError, code: InterpreterErrorCode) -> bool {
166    err.code == code
167}