1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
//! Definition of all the possible outcomes of the operation on an `EngineState` instance.
use datasize::DataSize;
use thiserror::Error;

use casper_hashing::Digest;
use casper_types::{bytesrepr, system::mint, ApiError, ProtocolVersion};

use crate::{
    core::{
        engine_state::{genesis::GenesisError, upgrade::ProtocolUpgradeError},
        execution,
        runtime::stack,
    },
    shared::wasm_prep,
    storage::{self, global_state::CommitError},
};

/// Engine state errors.
#[derive(Clone, Error, Debug)]
#[non_exhaustive]
pub enum Error {
    /// Specified state root hash is not found.
    #[error("Root not found: {0}")]
    RootNotFound(Digest),
    /// Protocol version used in the deploy is invalid.
    #[error("Invalid protocol version: {0}")]
    InvalidProtocolVersion(ProtocolVersion),
    /// Genesis error.
    #[error("{0:?}")]
    Genesis(Box<GenesisError>),
    /// WASM preprocessing error.
    #[error("Wasm preprocessing error: {0}")]
    WasmPreprocessing(#[from] wasm_prep::PreprocessingError),
    /// WASM serialization error.
    #[error("Wasm serialization error: {0:?}")]
    WasmSerialization(#[from] casper_wasm::SerializationError),
    /// Contract execution error.
    #[error(transparent)]
    Exec(execution::Error),
    /// Storage error.
    #[error("Storage error: {0}")]
    Storage(#[from] storage::error::Error),
    /// Authorization error.
    #[error("Authorization failure: not authorized.")]
    Authorization,
    /// Payment code provided insufficient funds for execution.
    #[error("Insufficient payment")]
    InsufficientPayment,
    /// Motes to gas conversion resulted in an overflow.
    #[error("Gas conversion overflow")]
    GasConversionOverflow,
    /// General deploy error.
    #[error("Deploy error")]
    Deploy,
    /// Executing a payment finalization code resulted in an error.
    #[error("Payment finalization error")]
    Finalization,
    /// Serialization/deserialization error.
    #[error("Bytesrepr error: {0}")]
    Bytesrepr(String),
    /// Mint error.
    #[error("Mint error: {0}")]
    Mint(String),
    /// Invalid key variant.
    #[error("Unsupported key type")]
    InvalidKeyVariant,
    /// Protocol upgrade error.
    #[error("Protocol upgrade error: {0}")]
    ProtocolUpgrade(#[from] ProtocolUpgradeError),
    /// Invalid deploy item variant.
    #[error("Unsupported deploy item variant: {0}")]
    InvalidDeployItemVariant(String),
    /// Commit error.
    #[error(transparent)]
    CommitError(#[from] CommitError),
    /// Missing system contract registry.
    #[error("Missing system contract registry")]
    MissingSystemContractRegistry,
    /// Missing system contract hash.
    #[error("Missing system contract hash: {0}")]
    MissingSystemContractHash(String),
    /// Missing checksum registry.
    #[error("Missing checksum registry")]
    MissingChecksumRegistry,
    /// An attempt to push to the runtime stack while already at the maximum height.
    #[error("Runtime stack overflow")]
    RuntimeStackOverflow,
    /// Failed to get the set of Key::Withdraw from global state.
    #[error("Failed to get withdraw keys")]
    FailedToGetWithdrawKeys,
    /// Failed to get the purses stored under Key::Withdraw
    #[error("Failed to get stored values under withdraws")]
    FailedToGetStoredWithdraws,
    /// Failed to convert the StoredValue into WithdrawPurse.
    #[error("Failed to convert the stored value to a withdraw purse")]
    FailedToGetWithdrawPurses,
    /// Failed to retrieve the unbonding delay from the auction state.
    #[error("Failed to retrieve the unbonding delay from the auction state")]
    FailedToRetrieveUnbondingDelay,
    /// Failed to retrieve the current EraId from the auction state.
    #[error("Failed to retrieve the era_id from the auction state")]
    FailedToRetrieveEraId,
    /// Failed to put a trie node into global state because some of its children were missing.
    #[error("Failed to put a trie into global state because some of its children were missing")]
    MissingTrieNodeChildren(Vec<Digest>),
}

impl Error {
    /// Creates an [`enum@Error`] instance of an [`Error::Exec`] variant with an API
    /// error-compatible object.
    ///
    /// This method should be used only by native code that has to mimic logic of a WASM executed
    /// code.
    pub fn reverter(api_error: impl Into<ApiError>) -> Error {
        Error::Exec(execution::Error::Revert(api_error.into()))
    }
}

impl From<execution::Error> for Error {
    fn from(error: execution::Error) -> Self {
        match error {
            execution::Error::WasmPreprocessing(preprocessing_error) => {
                Error::WasmPreprocessing(preprocessing_error)
            }
            _ => Error::Exec(error),
        }
    }
}

impl From<bytesrepr::Error> for Error {
    fn from(error: bytesrepr::Error) -> Self {
        Error::Bytesrepr(format!("{}", error))
    }
}

impl From<lmdb::Error> for Error {
    fn from(error: lmdb::Error) -> Self {
        Error::Storage(storage::error::Error::Lmdb(error))
    }
}

impl From<mint::Error> for Error {
    fn from(error: mint::Error) -> Self {
        Error::Mint(format!("{}", error))
    }
}

impl From<Box<GenesisError>> for Error {
    fn from(genesis_error: Box<GenesisError>) -> Self {
        Self::Genesis(genesis_error)
    }
}

impl From<stack::RuntimeStackOverflow> for Error {
    fn from(_: stack::RuntimeStackOverflow) -> Self {
        Self::RuntimeStackOverflow
    }
}

impl DataSize for Error {
    const IS_DYNAMIC: bool = true;

    const STATIC_HEAP_SIZE: usize = 0;

    // TODO
    #[inline]
    fn estimate_heap_size(&self) -> usize {
        12 // TODO: replace with some actual estimation depending on the variant
    }
}