risc0_binfmt/
exit_code.rs

1// Copyright 2025 RISC Zero, Inc.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use core::fmt;
16
17use borsh::{BorshDeserialize, BorshSerialize};
18use serde::{Deserialize, Serialize};
19
20/// Exit condition indicated by the zkVM at the end of the guest execution.
21///
22/// Exit codes have a "system" part and a "user" part. Semantically, the system
23/// part is set to indicate the type of exit (e.g. halt, pause, or system split)
24/// and is directly controlled by the zkVM. The user part is an exit code,
25/// similar to exit codes used in Linux, chosen by the guest program to indicate
26/// additional information (e.g. 0 to indicate success or 1 to indicate an
27/// error).
28#[derive(
29    Clone, Copy, Debug, Serialize, Deserialize, PartialEq, BorshSerialize, BorshDeserialize,
30)]
31#[non_exhaustive]
32pub enum ExitCode {
33    /// This indicates normal termination of a program with an interior exit
34    /// code returned from the guest program. A halted program cannot be
35    /// resumed.
36    Halted(u32),
37
38    /// This indicates the execution ended in a paused state with an interior
39    /// exit code set by the guest program. A paused program can be resumed such
40    /// that execution picks up where it left of, with the same memory state.
41    Paused(u32),
42
43    /// This indicates the execution ended on a host-initiated system split.
44    ///
45    /// System split is mechanism by which the host can temporarily stop
46    /// execution of the guest. Execution ended in a system split has no output
47    /// and no conclusions can be drawn about whether the program will
48    /// eventually halt. System split is used in [continuations] to split
49    /// execution into individually provable [segments].
50    ///
51    /// [continuations]: https://dev.risczero.com/terminology#continuations
52    /// [segments]: https://dev.risczero.com/terminology#segment
53    SystemSplit,
54
55    /// This indicates that the guest exited upon reaching the session limit set by the host.
56    ///
57    /// NOTE: The current version of the RISC Zero zkVM will never exit with an exit code of SessionLimit.
58    /// This is because the system cannot currently prove that the session limit as been reached.
59    SessionLimit,
60}
61
62impl ExitCode {
63    /// Convert this [ExitCode] into a pair representation, where the first number is the "system"
64    /// part, and the second is the "user" part. E.g. Halted(255) -> (0, 255)
65    pub fn into_pair(self) -> (u32, u32) {
66        match self {
67            ExitCode::Halted(user_exit) => (0, user_exit),
68            ExitCode::Paused(user_exit) => (1, user_exit),
69            ExitCode::SystemSplit => (2, 0),
70            ExitCode::SessionLimit => (2, 2),
71        }
72    }
73
74    /// Convert this [ExitCode] from its pair representation, where the first number is the "system"
75    /// part, and the second is the "user" part. E.g. (0, 255) -> Halted(255)
76    pub fn from_pair(sys_exit: u32, user_exit: u32) -> Result<ExitCode, InvalidExitCodeError> {
77        match sys_exit {
78            0 => Ok(ExitCode::Halted(user_exit)),
79            1 => Ok(ExitCode::Paused(user_exit)),
80            2 => Ok(ExitCode::SystemSplit),
81            _ => Err(InvalidExitCodeError(sys_exit, user_exit)),
82        }
83    }
84
85    /// Whether the verifier should expect a non-empty output field. Exit codes Halted and Paused
86    /// produce can produce a non-empty outputs, whereas system initiated exits like SystemSplit do
87    /// not.
88    pub fn expects_output(&self) -> bool {
89        match self {
90            ExitCode::Halted(_) | ExitCode::Paused(_) => true,
91            ExitCode::SystemSplit | ExitCode::SessionLimit => false,
92        }
93    }
94
95    /// True if the exit code is Halted(0), indicating the program guest exited with an ok status.
96    pub fn is_ok(&self) -> bool {
97        matches!(self, ExitCode::Halted(0))
98    }
99}
100
101impl Eq for ExitCode {}
102
103/// Error returned when a `(system, user)` exit code pair is an invalid
104/// representation.
105#[derive(Debug, Copy, Clone)]
106pub struct InvalidExitCodeError(pub u32, pub u32);
107
108impl fmt::Display for InvalidExitCodeError {
109    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
110        write!(f, "invalid exit code pair ({}, {})", self.0, self.1)
111    }
112}
113
114#[cfg(feature = "std")]
115impl std::error::Error for InvalidExitCodeError {}