piecrust_uplink/
error.rs

1// This Source Code Form is subject to the terms of the Mozilla Public
2// License, v. 2.0. If a copy of the MPL was not distributed with this
3// file, You can obtain one at http://mozilla.org/MPL/2.0/.
4//
5// Copyright (c) DUSK NETWORK. All rights reserved.
6
7extern crate alloc;
8
9use bytecheck::CheckBytes;
10use rkyv::{Archive, Deserialize, Serialize};
11
12use alloc::string::String;
13
14use core::fmt::{Display, Formatter};
15use core::str;
16
17/// The error possibly returned on an inter-contract-call.
18//
19// We do **not use rkyv** to pass it to the contract from the VM. Instead, we
20// use use the calling convention being able to pass negative numbers to signal
21// a failure.
22//
23// The contract writer, however, is free to pass it around and react to it if it
24// wishes.
25#[derive(Debug, Clone, PartialEq, Eq, Archive, Serialize, Deserialize)]
26#[archive_attr(derive(CheckBytes))]
27pub enum ContractError {
28    Panic(String),
29    OutOfGas,
30    DoesNotExist,
31    Unknown,
32}
33
34impl ContractError {
35    /// Returns a contract error from a return `code` and the data in the
36    /// `slice`.
37    #[cfg(feature = "abi")]
38    pub(crate) fn from_parts(code: i32, slice: &[u8]) -> Self {
39        fn get_msg(slice: &[u8]) -> String {
40            let msg_len = {
41                let mut msg_len_bytes = [0u8; 4];
42                msg_len_bytes.copy_from_slice(&slice[..4]);
43                u32::from_le_bytes(msg_len_bytes)
44            } as usize;
45
46            // SAFETY: the host guarantees that the message is valid UTF-8,
47            // so this is safe.
48            let msg = unsafe {
49                use alloc::string::ToString;
50                let msg_bytes = &slice[4..4 + msg_len];
51                let msg_str = str::from_utf8_unchecked(msg_bytes);
52                msg_str.to_string()
53            };
54
55            msg
56        }
57
58        match code {
59            -1 => Self::Panic(get_msg(slice)),
60            -2 => Self::OutOfGas,
61            -3 => Self::DoesNotExist,
62            i32::MIN => Self::Unknown,
63            _ => unreachable!("The host must guarantee that the code is valid"),
64        }
65    }
66
67    /// Write the appropriate data the `arg_buf` and return the error code.
68    pub fn to_parts(&self, slice: &mut [u8]) -> i32 {
69        fn put_msg(msg: &str, slice: &mut [u8]) {
70            let msg_bytes = msg.as_bytes();
71            let msg_len = msg_bytes.len();
72
73            let mut msg_len_bytes = [0u8; 4];
74            msg_len_bytes.copy_from_slice(&(msg_len as u32).to_le_bytes());
75
76            slice[..4].copy_from_slice(&msg_len_bytes);
77            slice[4..4 + msg_len].copy_from_slice(msg_bytes);
78        }
79
80        match self {
81            Self::Panic(msg) => {
82                put_msg(msg, slice);
83                -1
84            }
85            Self::OutOfGas => -2,
86            Self::DoesNotExist => -3,
87            Self::Unknown => i32::MIN,
88        }
89    }
90}
91
92impl From<ContractError> for i32 {
93    fn from(err: ContractError) -> Self {
94        match err {
95            ContractError::Panic(_) => -1,
96            ContractError::OutOfGas => -2,
97            ContractError::DoesNotExist => -3,
98            ContractError::Unknown => i32::MIN,
99        }
100    }
101}
102
103impl Display for ContractError {
104    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
105        match self {
106            ContractError::Panic(msg) => write!(f, "Panic: {msg}"),
107            ContractError::OutOfGas => write!(f, "OutOfGas"),
108            ContractError::DoesNotExist => {
109                write!(f, "Contract does not exist")
110            }
111            ContractError::Unknown => write!(f, "Unknown"),
112        }
113    }
114}