risc0_binfmt/exit_code.rs
1// Copyright 2024 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)]
31pub enum ExitCode {
32 /// This indicates normal termination of a program with an interior exit
33 /// code returned from the guest program. A halted program cannot be
34 /// resumed.
35 Halted(u32),
36
37 /// This indicates the execution ended in a paused state with an interior
38 /// exit code set by the guest program. A paused program can be resumed such
39 /// that execution picks up where it left of, with the same memory state.
40 Paused(u32),
41
42 /// This indicates the execution ended on a host-initiated system split.
43 ///
44 /// System split is mechanism by which the host can temporarily stop
45 /// execution of the guest. Execution ended in a system split has no output
46 /// and no conclusions can be drawn about whether the program will
47 /// eventually halt. System split is used in [continuations] to split
48 /// execution into individually provable [segments].
49 ///
50 /// [continuations]: https://dev.risczero.com/terminology#continuations
51 /// [segments]: https://dev.risczero.com/terminology#segment
52 SystemSplit,
53
54 /// This indicates that the guest exited upon reaching the session limit set by the host.
55 ///
56 /// NOTE: The current version of the RISC Zero zkVM will never exit with an exit code of SessionLimit.
57 /// This is because the system cannot currently prove that the session limit as been reached.
58 SessionLimit,
59}
60
61impl ExitCode {
62 /// Convert this [ExitCode] into a pair representation, where the first number is the "system"
63 /// part, and the second is the "user" part. E.g. Halted(255) -> (0, 255)
64 pub fn into_pair(self) -> (u32, u32) {
65 match self {
66 ExitCode::Halted(user_exit) => (0, user_exit),
67 ExitCode::Paused(user_exit) => (1, user_exit),
68 ExitCode::SystemSplit => (2, 0),
69 ExitCode::SessionLimit => (2, 2),
70 }
71 }
72
73 /// Convert this [ExitCode] from its pair representation, where the first number is the "system"
74 /// part, and the second is the "user" part. E.g. (0, 255) -> Halted(255)
75 pub fn from_pair(sys_exit: u32, user_exit: u32) -> Result<ExitCode, InvalidExitCodeError> {
76 match sys_exit {
77 0 => Ok(ExitCode::Halted(user_exit)),
78 1 => Ok(ExitCode::Paused(user_exit)),
79 2 => Ok(ExitCode::SystemSplit),
80 _ => Err(InvalidExitCodeError(sys_exit, user_exit)),
81 }
82 }
83
84 /// Whether the verifier should expect a non-empty output field. Exit codes Halted and Paused
85 /// produce can produce a non-empty outputs, whereas system initiated exits like SystemSplit do
86 /// not.
87 pub fn expects_output(&self) -> bool {
88 match self {
89 ExitCode::Halted(_) | ExitCode::Paused(_) => true,
90 ExitCode::SystemSplit | ExitCode::SessionLimit => false,
91 }
92 }
93
94 /// True if the exit code is Halted(0), indicating the program guest exited with an ok status.
95 pub fn is_ok(&self) -> bool {
96 matches!(self, ExitCode::Halted(0))
97 }
98}
99
100impl Eq for ExitCode {}
101
102/// Error returned when a `(system, user)` exit code pair is an invalid
103/// representation.
104#[derive(Debug, Copy, Clone)]
105pub struct InvalidExitCodeError(pub u32, pub u32);
106
107impl fmt::Display for InvalidExitCodeError {
108 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
109 write!(f, "invalid exit code pair ({}, {})", self.0, self.1)
110 }
111}
112
113#[cfg(feature = "std")]
114impl std::error::Error for InvalidExitCodeError {}