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