Skip to main content

hiero_sdk/
error.rs

1// SPDX-License-Identifier: Apache-2.0
2
3use std::error::Error as StdError;
4use std::result::Result as StdResult;
5
6use crate::entity_id::Checksum;
7use crate::{
8    AccountId,
9    Hbar,
10    Status,
11    TransactionId,
12};
13
14/// `Result<T, Error>`
15pub type Result<T> = StdResult<T, Error>;
16
17pub(crate) type BoxStdError = Box<dyn StdError + Send + Sync + 'static>;
18
19/// Represents any possible error from a fallible function in the Hiero SDK.
20#[derive(Debug, thiserror::Error)]
21#[non_exhaustive]
22pub enum Error {
23    /// Request timed out.
24    #[error("failed to complete request within the maximum time allowed; most recent attempt failed with: {0}")]
25    TimedOut(#[source] Box<Error>),
26
27    /// GRPC status code was an error.
28    #[error("grpc: {0:?}")]
29    GrpcStatus(#[from] tonic::Status),
30
31    /// Failed to parse an SDK type from a protobuf response.
32    #[error("failed to create a SDK type from a protobuf response: {0}")]
33    FromProtobuf(#[source] BoxStdError),
34
35    // todo: bikeshed this.
36    /// Freeze failed due to there being no explicitly set node account IDs and no client being provided to generate them.
37    #[error("freeze failed due to node account IDs being unset")]
38    FreezeUnsetNodeAccountIds,
39
40    /// A transaction failed pre-check.
41    ///
42    /// The transaction had the ID `transaction_id`.
43    ///
44    /// Caused by `status` being an error.
45    #[error("transaction `{transaction_id}` failed pre-check with status `{status:?}`")]
46    TransactionPreCheckStatus {
47        /// The status that caused the [`Transaction`](crate::Transaction) to fail pre-check.
48        status: Status,
49
50        /// The `TransactionId` of the failed [`Transaction`](crate::Transaction) .
51        transaction_id: Box<TransactionId>,
52
53        /// The estimated transaction fee, if the status was [`InsufficientTxFee`].
54        cost: Option<Hbar>,
55    },
56
57    /// A [`Query`](crate::Query) for `transaction_id` failed pre-check.
58    ///
59    /// Caused by `status` being an error.
60    #[error("query for transaction `{transaction_id}` failed pre-check with status `{status:?}`")]
61    QueryPreCheckStatus {
62        /// The `Status` that caused the [`Query`](crate::Query) to fail pre-check.
63        status: Status,
64        /// The associated transaction's ID.
65        transaction_id: Box<TransactionId>,
66    },
67
68    /// A [`Query`](crate::Query) failed pre-check.
69    ///
70    /// The query had an associated `PaymentTransaction` with ID `transaction_id`.
71    ///
72    /// Caused by `status` being an error.
73    #[error(
74    "query with payment transaction `{transaction_id}` failed pre-check with status `{status:?}`"
75    )]
76    QueryPaymentPreCheckStatus {
77        /// The `Status` that caused the [`Query`](crate::Query) to fail pre-check.
78        status: Status,
79        /// The associated `PaymentTransaction`'s `TransactionId`.
80        transaction_id: Box<TransactionId>,
81    },
82
83    /// A [`Query`](crate::Query) failed pre-check.
84    ///
85    /// The query had no `PaymentTransaction`.
86    ///
87    /// Caused by `status` being an error.
88    #[error("query with no payment transaction failed pre-check with status `{status:?}`")]
89    QueryNoPaymentPreCheckStatus {
90        /// The `Status` that caused the [`Query`](crate::Query) to fail pre-check.
91        status: Status,
92    },
93
94    /// Failed to parse a basic type from string
95    /// (ex. [`AccountId`](crate::AccountId), [`ContractId`](crate::ContractId), [`TransactionId`](crate::TransactionId), etc.).
96    #[error("failed to parse: {0}")]
97    BasicParse(#[source] BoxStdError),
98
99    /// An entity ID had an invalid checksum
100    #[error("entity ID {shard}.{realm}.{num}-{present_checksum} was incorrect")]
101    BadEntityId {
102        /// The shard number
103        shard: u64,
104        /// The realm number
105        realm: u64,
106        /// The entity number
107        num: u64,
108        /// The (invalid) checksum that was present on the entity ID
109        present_checksum: Checksum,
110        /// The checksum that SHOULD HAVE BEEN on the entity ID
111        expected_checksum: Checksum,
112    },
113
114    /// An entity ID cannot be converted to a string with a checksum, because it is in an alternate form,
115    /// such as an `alias` or `evm_address`
116    #[error("an entity ID with an `alias` or `evm_address` cannot have a checksum")]
117    CannotCreateChecksum,
118
119    /// Failed to parse a [`PublicKey`](crate::PublicKey) or [`PrivateKey`](crate::PrivateKey).
120    #[error("failed to parse a key: {0}")]
121    KeyParse(#[source] BoxStdError),
122
123    /// Failed to derive a [`PrivateKey`](crate::PrivateKey) from another `PrivateKey`.
124    ///
125    /// Examples of when this can happen (non-exhaustive):
126    /// - [`PrivateKey::derive`](fn@crate::PrivateKey::derive) when the `PrivateKey` doesn't have a chain code.
127    /// - [`PrivateKey::derive`](fn@crate::PrivateKey::derive)
128    ///   or [`PrivateKey::legacy_derive`](fn@crate::PrivateKey::legacy_derive) on an `Ecsda` key.
129    #[error("Failed to derive a key: {0}")]
130    KeyDerive(#[source] BoxStdError),
131
132    /// Failed to parse a [`Mnemonic`](crate::Mnemonic) due to the given `reason`.
133    ///
134    /// the `Mnemonic` is provided because invalid `Mnemonics`
135    /// can technically still provide valid [`PrivateKeys`](crate::PrivateKey).
136    #[cfg(feature = "mnemonic")]
137    #[error("failed to parse a mnemonic: {reason}")]
138    MnemonicParse {
139        /// This error's source.
140        #[source]
141        reason: MnemonicParseError,
142        /// The `Mnemonic` in question.
143        mnemonic: crate::Mnemonic,
144    },
145
146    /// An error occurred while attempting to convert a [`Mnemonic`](crate::Mnemonic) to a [`PrivateKey`](crate::PrivateKey)
147    #[cfg(feature = "mnemonic")]
148    #[error("failed to convert a mnemonic to entropy: {0}")]
149    MnemonicEntropy(#[from] MnemonicEntropyError),
150
151    /// The [`Client`](crate::Client) had no payer account (operator)
152    /// and the attempted request had no explicit [`TransactionId`].
153    #[error("client must be configured with a payer account or requests must be given an explicit transaction id")]
154    NoPayerAccountOrTransactionId,
155
156    /// Cost of a [`Query`](crate::Query) is more expensive than `max_query_payment`.
157    ///
158    /// The actual cost of the `Query` is `query_cost`.
159    #[error("cost of {query_cost} without explicit payment is greater than the maximum allowed payment of {max_query_payment}")]
160    MaxQueryPaymentExceeded {
161        /// the configured maximum query payment.
162        max_query_payment: Hbar,
163
164        /// How much the query actually cost.
165        query_cost: Hbar,
166    },
167
168    /// The associated node account was not found in the network.
169    #[error("node account `{0}` was not found in the configured network")]
170    NodeAccountUnknown(Box<AccountId>),
171
172    /// Received an unrecognized status code from the Hiero Network.
173    ///
174    /// This can happen when the SDK is outdated, try updating your SDK.
175    #[error("received unrecognized status code: {0}, try updating your SDK")]
176    ResponseStatusUnrecognized(i32),
177
178    // fixme(sr): Citation needed (unsure if this is accurate).
179    /// Getting the receipt for `transaction_id` failed with `status`.
180    #[error("receipt for transaction `{transaction_id:?}` failed with status `{status:?}`")]
181    ReceiptStatus {
182        /// The Error's status code.
183        status: Status,
184        /// The [`Transaction`](crate::Transaction)'s ID.
185        transaction_id: Option<Box<TransactionId>>,
186    },
187
188    /// Failed to verify a signature.
189    #[error("failed to verify a signature: {0}")]
190    SignatureVerify(#[source] BoxStdError),
191}
192
193impl Error {
194    pub(crate) fn from_protobuf<E: Into<BoxStdError>>(error: E) -> Self {
195        Self::FromProtobuf(error.into())
196    }
197
198    pub(crate) fn key_parse<E: Into<BoxStdError>>(error: E) -> Self {
199        Self::KeyParse(error.into())
200    }
201
202    pub(crate) fn key_derive<E: Into<BoxStdError>>(error: E) -> Self {
203        Self::KeyDerive(error.into())
204    }
205
206    pub(crate) fn basic_parse<E: Into<BoxStdError>>(error: E) -> Self {
207        Self::BasicParse(error.into())
208    }
209
210    pub(crate) fn signature_verify(error: impl Into<BoxStdError>) -> Self {
211        Self::SignatureVerify(error.into())
212    }
213}
214
215/// Failed to parse a mnemonic.
216#[cfg(feature = "mnemonic")]
217#[derive(Debug, thiserror::Error)]
218#[non_exhaustive]
219pub enum MnemonicParseError {
220    /// The [`Mnemonic`](crate::Mnemonic) contains an unexpected length.
221    #[error("bad length: expected `12` or `24` words, found `{0}`")]
222    BadLength(usize),
223
224    /// The [`Mnemonic`](crate::Mnemonic) contains words that aren't in the wordlist.
225    #[error("unknown words at indecies: `{0:?}`")]
226    UnknownWords(Vec<usize>),
227
228    /// The [`Mnemonic`](crate::Mnemonic) has an invalid checksum.
229    #[error("checksum mismatch: expected `{expected:02x}`, found `{actual:02x}`")]
230    ChecksumMismatch {
231        /// The checksum that was expected.
232        expected: u8,
233        /// The checksum that was actually found.
234        actual: u8,
235    },
236}
237
238/// Failed to convert a [`Mnemonic`](crate::Mnemonic) to a [`PrivateKey`](crate::PrivateKey)
239// todo: find a better name before release.
240#[cfg(feature = "mnemonic")]
241#[derive(Debug, thiserror::Error)]
242#[non_exhaustive]
243pub enum MnemonicEntropyError {
244    /// Encountered a [`Mnemonic`](crate::Mnemonic) of unexpected length.
245    #[error("bad length: expected `{expected}` words, found {actual} words")]
246    BadLength {
247        /// The number of words that were expected (12, 22, or 24)
248        expected: usize,
249        /// The number of words that were actually found.
250        actual: usize,
251    },
252
253    /// The [`Mnemonic`](crate::Mnemonic) has an invalid checksum.
254    #[error("checksum mismatch: expected `{expected:02x}`, found `{actual:02x}`")]
255    ChecksumMismatch {
256        /// The checksum that was expected.
257        expected: u8,
258        /// The checksum that was actually found.
259        actual: u8,
260    },
261
262    /// Used a passphrase with a legacy [`Mnemonic`](crate::Mnemonic).
263    #[error("used a passphrase with a legacy mnemonic")]
264    LegacyWithPassphrase,
265}