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 {}