1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
// The `(*.0).0` syntax of thiserror falsely triggers this clippy warning
#![allow(clippy::explicit_auto_deref)]

use crate::stdlib::prelude::*;
use crate::types::builtin_name::BuiltinName;

use thiserror_no_std::Error;

use crate::Felt252;
use crate::{
    types::{
        errors::math_errors::MathError,
        relocatable::{MaybeRelocatable, Relocatable},
    },
    vm::errors::{
        exec_scope_errors::ExecScopeError, hint_errors::HintError, memory_errors::MemoryError,
        runner_errors::RunnerError, trace_errors::TraceError,
    },
};

pub const HINT_ERROR_STR: &str = "Got an exception while executing a hint: ";

#[derive(Debug, Error)]
pub enum VirtualMachineError {
    #[error(transparent)]
    RunnerError(#[from] RunnerError),
    #[error(transparent)]
    Memory(#[from] MemoryError),
    #[error(transparent)]
    Math(#[from] MathError),
    #[error(transparent)]
    TracerError(#[from] TraceError),
    #[error(transparent)]
    MainScopeError(#[from] ExecScopeError),
    #[error(transparent)]
    Other(anyhow::Error),
    #[error("Instruction MSB should be 0")]
    InstructionNonZeroHighBit,
    #[error("Instruction should be an int")]
    InvalidInstructionEncoding,
    #[error("Invalid op1_register value: {0}")]
    InvalidOp1Reg(u64),
    #[error("In immediate mode, off2 should be 1")]
    ImmShouldBe1,
    #[error("op0 must be known in double dereference")]
    UnknownOp0,
    #[error("Invalid ap_update value: {0}")]
    InvalidApUpdate(u64),
    #[error("Invalid pc_update value: {0}")]
    InvalidPcUpdate(u64),
    #[error("Res.UNCONSTRAINED cannot be used with ApUpdate.ADD")]
    UnconstrainedResAdd,
    #[error("Res.UNCONSTRAINED cannot be used with PcUpdate.JUMP")]
    UnconstrainedResJump,
    #[error("Res.UNCONSTRAINED cannot be used with PcUpdate.JUMP_REL")]
    UnconstrainedResJumpRel,
    #[error("Res.UNCONSTRAINED cannot be used with Opcode.ASSERT_EQ")]
    UnconstrainedResAssertEq,
    #[error("A relocatable value as Res cannot be used with PcUpdate.JUMP_REL")]
    JumpRelNotInt,
    #[error(
        "Failed to compute Res.MUL: Could not complete computation of non pure values {} * {}", (*.0).0, (*.0).1
    )]
    ComputeResRelocatableMul(Box<(MaybeRelocatable, MaybeRelocatable)>),
    #[error("Couldn't compute operand {}. Unknown value for memory cell {}", (*.0).0, (*.0).1)]
    FailedToComputeOperands(Box<(String, Relocatable)>),
    #[error("An ASSERT_EQ instruction failed: {} != {}.", (*.0).0, (*.0).1)]
    DiffAssertValues(Box<(MaybeRelocatable, MaybeRelocatable)>),
    #[error("Call failed to write return-pc (inconsistent op0): {} != {}. Did you forget to increment ap?", (*.0).0, (*.0).1)]
    CantWriteReturnPc(Box<(MaybeRelocatable, MaybeRelocatable)>),
    #[error("Call failed to write return-fp (inconsistent dst): {} != {}. Did you forget to increment ap?", (*.0).0, (*.0).1)]
    CantWriteReturnFp(Box<(MaybeRelocatable, MaybeRelocatable)>),
    #[error("Couldn't get or load dst")]
    NoDst,
    #[error("Invalid res value: {0}")]
    InvalidRes(u64),
    #[error("Invalid opcode value: {0}")]
    InvalidOpcode(u64),
    #[error("This is not implemented")]
    NotImplemented,
    #[error("Inconsistent auto-deduction for {}, expected {}, got {:?}", (*.0).0, (*.0).1, (*.0).2)]
    InconsistentAutoDeduction(Box<(BuiltinName, MaybeRelocatable, Option<MaybeRelocatable>)>),
    #[error("Invalid hint encoding at pc: {0}")]
    InvalidHintEncoding(Box<MaybeRelocatable>),
    #[error("Expected output builtin to be present")]
    NoOutputBuiltin,
    #[error("Expected range_check builtin to be present")]
    NoRangeCheckBuiltin,
    #[error("Expected ecdsa builtin to be present")]
    NoSignatureBuiltin,
    #[error("Expected {0} to be present")]
    NoModBuiltin(BuiltinName),
    #[error("Div out of range: 0 < {} <= {}", (*.0).0, (*.0).1)]
    OutOfValidRange(Box<(Felt252, Felt252)>),
    #[error("Failed to compare {} and {}, cant compare a relocatable to an integer value", (*.0).0, (*.0).1)]
    DiffTypeComparison(Box<(MaybeRelocatable, MaybeRelocatable)>),
    #[error("Failed to compare {} and  {}, cant compare two relocatable values of different segment indexes", (*.0).0, (*.0).1)]
    DiffIndexComp(Box<(Relocatable, Relocatable)>),
    #[error("Couldn't convert usize to u32")]
    NoneInMemoryRange,
    #[error("Expected integer, found: {0:?}")]
    ExpectedIntAtRange(Box<Option<MaybeRelocatable>>),
    #[error("Could not convert slice to array")]
    SliceToArrayError,
    #[error("Failed to compile hint: {0}")]
    CompileHintFail(Box<str>),
    #[error("op1_addr is Op1Addr.IMM, but no immediate was given")]
    NoImm,
    #[error("Execution reached the end of the program. Requested remaining steps: {0}.")]
    EndOfProgram(usize),
    #[error("Could not reach the end of the program. Executed steps: {0}.")]
    StepsLimit(u64),
    #[error("Could not reach the end of the program. RunResources has no remaining steps.")]
    UnfinishedExecution,
    #[error("Current run is not finished")]
    RunNotFinished,
    #[error("Invalid argument count, expected {} but got {}", (*.0).0, (*.0).1)]
    InvalidArgCount(Box<(usize, usize)>),
    #[error("Couldn't parse prime: {0}")]
    CouldntParsePrime(Box<str>),
    #[error("{HINT_ERROR_STR}{}", (*.0).1)]
    Hint(Box<(usize, HintError)>),
    #[error("Unexpected Failure")]
    Unexpected,
    #[error("Out of bounds access to builtin segment")]
    OutOfBoundsBuiltinSegmentAccess,
    #[error("Out of bounds access to program segment")]
    OutOfBoundsProgramSegmentAccess,
    #[error("Security Error: Invalid Memory Value: temporary address not relocated: {0}")]
    InvalidMemoryValueTemporaryAddress(Box<Relocatable>),
    #[error("accessed_addresses is None.")]
    MissingAccessedAddresses,
    #[error("Failed to write the output builtin content")]
    FailedToWriteOutput,
    #[error("Failed to find index {0} in the vm's relocation table")]
    RelocationNotFound(usize),
    #[error("{} batch size is not {}", (*.0).0, (*.0).1)]
    ModBuiltinBatchSize(Box<(BuiltinName, usize)>),
}

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

    #[test]
    // Test to catch possible enum size regressions
    fn test_vm_error_size() {
        let size = crate::stdlib::mem::size_of::<VirtualMachineError>();
        assert!(size <= 32, "{size}")
    }
}