darkfi/
error.rs

1/* This file is part of DarkFi (https://dark.fi)
2 *
3 * Copyright (C) 2020-2025 Dyne.org foundation
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Affero General Public License as
7 * published by the Free Software Foundation, either version 3 of the
8 * License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 * GNU Affero General Public License for more details.
14 *
15 * You should have received a copy of the GNU Affero General Public License
16 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
17 */
18
19// Hello developer. Please add your error to the according subsection
20// that is commented, or make a new subsection. Keep it clean.
21
22/// Main result type used throughout the codebase.
23pub type Result<T> = std::result::Result<T, Error>;
24
25/// Result type used in the Client module
26pub type ClientResult<T> = std::result::Result<T, ClientFailed>;
27
28/// General library errors used throughout the codebase.
29#[derive(Debug, Clone, thiserror::Error)]
30pub enum Error {
31    // ==============
32    // Parsing errors
33    // ==============
34    #[error("Parse failed: {0}")]
35    ParseFailed(&'static str),
36
37    #[error(transparent)]
38    ParseIntError(#[from] std::num::ParseIntError),
39
40    #[error(transparent)]
41    ParseFloatError(#[from] std::num::ParseFloatError),
42
43    #[cfg(feature = "url")]
44    #[error(transparent)]
45    UrlParseError(#[from] url::ParseError),
46
47    #[error("URL parse error: {0}")]
48    UrlParse(String),
49
50    #[error(transparent)]
51    AddrParseError(#[from] std::net::AddrParseError),
52
53    #[error("Could not parse token parameter")]
54    TokenParseError,
55
56    #[error(transparent)]
57    TryFromSliceError(#[from] std::array::TryFromSliceError),
58
59    #[cfg(feature = "semver")]
60    #[error("semver parse error: {0}")]
61    SemverError(String),
62
63    // ===============
64    // Encoding errors
65    // ===============
66    #[error("decode failed: {0}")]
67    DecodeError(&'static str),
68
69    #[error("encode failed: {0}")]
70    EncodeError(&'static str),
71
72    #[error("VarInt was encoded in a non-minimal way")]
73    NonMinimalVarInt,
74
75    #[error(transparent)]
76    Utf8Error(#[from] std::string::FromUtf8Error),
77
78    #[error(transparent)]
79    StrUtf8Error(#[from] std::str::Utf8Error),
80
81    #[cfg(feature = "tinyjson")]
82    #[error("JSON parse error: {0}")]
83    JsonParseError(String),
84
85    #[cfg(feature = "tinyjson")]
86    #[error("JSON generate error: {0}")]
87    JsonGenerateError(String),
88
89    #[cfg(feature = "toml")]
90    #[error(transparent)]
91    TomlDeserializeError(#[from] toml::de::Error),
92
93    #[cfg(feature = "bs58")]
94    #[error(transparent)]
95    Bs58DecodeError(#[from] bs58::decode::Error),
96
97    #[error("Bad operation type byte")]
98    BadOperationType,
99
100    // ======================
101    // Network-related errors
102    // ======================
103    #[error("Invalid Dialer scheme")]
104    InvalidDialerScheme,
105
106    #[error("Invalid Listener scheme")]
107    InvalidListenerScheme,
108
109    #[error("Unsupported network transport: {0}")]
110    UnsupportedTransport(String),
111
112    #[error("Unsupported network transport upgrade: {0}")]
113    UnsupportedTransportUpgrade(String),
114
115    #[error("Transport request exceeds number of accepted transports")]
116    InvalidTransportRequest,
117
118    #[error("Connection failed")]
119    ConnectFailed,
120
121    #[cfg(feature = "system")]
122    #[error(transparent)]
123    TimeoutError(#[from] crate::system::timeout::TimeoutError),
124
125    #[error("Connection timed out")]
126    ConnectTimeout,
127
128    #[error("Channel stopped")]
129    ChannelStopped,
130
131    #[error("Channel timed out")]
132    ChannelTimeout,
133
134    #[error("Failed to reach any seeds")]
135    SeedFailed,
136
137    #[error("Network service stopped")]
138    NetworkServiceStopped,
139
140    #[error("Create listener bound to {0} failed")]
141    BindFailed(String),
142
143    #[error("Accept a new incoming connection from the listener {0} failed")]
144    AcceptConnectionFailed(String),
145
146    #[error("Accept a new tls connection from the listener {0} failed")]
147    AcceptTlsConnectionFailed(String),
148
149    #[error("Connector stopped")]
150    ConnectorStopped,
151
152    #[error("Network operation failed")]
153    NetworkOperationFailed,
154
155    #[error("Missing P2P message dispatcher")]
156    MissingDispatcher,
157
158    #[error("P2P message is invalid")]
159    MessageInvalid,
160
161    #[error("P2P message subsystem over metering limit")]
162    MeteringLimitExceeded,
163
164    #[cfg(feature = "arti-client")]
165    #[error(transparent)]
166    ArtiError(#[from] arti_client::Error),
167
168    #[error("Malformed packet")]
169    MalformedPacket,
170
171    #[error("Error decoding packet: {0}")]
172    DecodePacket(String),
173
174    #[error("Socks proxy error: {0}")]
175    SocksError(String),
176
177    #[error("No Socks5 URL found")]
178    NoSocks5UrlFound,
179
180    #[error("No URL found")]
181    NoUrlFound,
182
183    #[error("Tor error: {0}")]
184    TorError(String),
185
186    #[error("Node is not connected to other nodes.")]
187    NetworkNotConnected,
188
189    #[error("P2P network stopped")]
190    P2PNetworkStopped,
191
192    #[error("No such host color exists")]
193    InvalidHostColor,
194
195    #[error("No matching hostlist entry")]
196    HostDoesNotExist,
197
198    #[error("Invalid state transition: current_state={0}, end_state={1}")]
199    HostStateBlocked(String, String),
200
201    // =============
202    // Crypto errors
203    // =============
204    #[cfg(feature = "halo2_proofs")]
205    #[error("halo2 plonk error: {0}")]
206    PlonkError(String),
207
208    #[error("Wrong witness type at index: {0}")]
209    WrongWitnessType(usize),
210
211    #[error("Wrong witnesses count")]
212    WrongWitnessesCount,
213
214    #[error("Wrong public inputs count")]
215    WrongPublicInputsCount,
216
217    #[error("Unable to decrypt mint note: {0}")]
218    NoteDecryptionFailed(String),
219
220    #[error("No keypair file detected")]
221    KeypairPathNotFound,
222
223    #[error("Failed converting bytes to PublicKey")]
224    PublicKeyFromBytes,
225
226    #[error("Failed converting bytes to Coin")]
227    CoinFromBytes,
228
229    #[error("Failed converting bytes to SecretKey")]
230    SecretKeyFromBytes,
231
232    #[error("Failed converting b58 string to PublicKey")]
233    PublicKeyFromStr,
234
235    #[error("Failed converting bs58 string to SecretKey")]
236    SecretKeyFromStr,
237
238    #[error("Invalid DarkFi address")]
239    InvalidAddress,
240
241    #[error("unable to decrypt rcpt")]
242    TxRcptDecryptionError,
243
244    #[cfg(feature = "blake3")]
245    #[error(transparent)]
246    Blake3FromHexError(#[from] blake3::HexError),
247
248    // =======================
249    // Protocol-related errors
250    // =======================
251    #[error("Unsupported chain")]
252    UnsupportedChain,
253
254    #[error("JSON-RPC error: {0:?}")]
255    JsonRpcError((i32, String)),
256
257    #[cfg(feature = "rpc")]
258    #[error(transparent)]
259    RpcServerError(RpcError),
260
261    #[cfg(feature = "rpc")]
262    #[error("JSON-RPC connections exhausted")]
263    RpcConnectionsExhausted,
264
265    #[cfg(feature = "rpc")]
266    #[error("JSON-RPC server stopped")]
267    RpcServerStopped,
268
269    #[cfg(feature = "rpc")]
270    #[error("JSON-RPC client stopped")]
271    RpcClientStopped,
272
273    #[error("Unexpected JSON-RPC data received: {0}")]
274    UnexpectedJsonRpc(String),
275
276    #[error("Received proposal from unknown node")]
277    UnknownNodeError,
278
279    #[error("Public inputs are invalid")]
280    InvalidPublicInputsError,
281
282    #[error("Signature could not be verified")]
283    InvalidSignature,
284
285    #[error("State transition failed")]
286    StateTransitionError,
287
288    #[error("No forks exist")]
289    ForksNotFound,
290
291    #[error("Check if proposal extends any existing fork chains failed")]
292    ExtendedChainIndexNotFound,
293
294    #[error("Proposal contains missmatched hashes")]
295    ProposalHashesMissmatchError,
296
297    #[error("Proposal contains missmatched headers")]
298    ProposalHeadersMissmatchError,
299
300    #[error("Unable to verify transfer transaction")]
301    TransferTxVerification,
302
303    #[error("Erroneous transactions detected")]
304    ErroneousTxsDetected,
305
306    #[error("Proposal task stopped")]
307    ProposalTaskStopped,
308
309    #[error("Proposal already exists")]
310    ProposalAlreadyExists,
311
312    #[error("Consensus task stopped")]
313    ConsensusTaskStopped,
314
315    #[error("Miner task stopped")]
316    MinerTaskStopped,
317
318    #[error("Garbage collection task stopped")]
319    GarbageCollectionTaskStopped,
320
321    #[error("Calculated total work is zero")]
322    PoWTotalWorkIsZero,
323
324    #[error("Erroneous cutoff calculation")]
325    PoWCuttofCalculationError,
326
327    #[error("Provided timestamp is invalid")]
328    PoWInvalidTimestamp,
329
330    #[error("Provided output hash is greater than current target")]
331    PoWInvalidOutHash,
332
333    #[error("Contracts states monotree root missing")]
334    ContractsStatesRootNotFoundError,
335
336    #[error("Contracts states monotree root missmatch: {0} - {1}")]
337    ContractsStatesRootError(String, String),
338
339    #[error("Hashing of Monero data failed: {0}")]
340    MoneroHashingError(String),
341
342    // ===============
343    // Database errors
344    // ===============
345    #[error("Database error: {0}")]
346    DatabaseError(String),
347
348    #[cfg(feature = "sled-overlay")]
349    #[error(transparent)]
350    SledError(#[from] sled_overlay::sled::Error),
351
352    #[cfg(feature = "sled-overlay")]
353    #[error(transparent)]
354    SledTransactionError(#[from] sled_overlay::sled::transaction::TransactionError),
355
356    #[error("Transaction {0} not found in database")]
357    TransactionNotFound(String),
358
359    #[error("Transaction already seen")]
360    TransactionAlreadySeen,
361
362    #[error("Input vectors have different length")]
363    InvalidInputLengths,
364
365    #[error("Header {0} not found in database")]
366    HeaderNotFound(String),
367
368    #[error("Block {0} is invalid")]
369    BlockIsInvalid(String),
370
371    #[error("Block version {0} is invalid")]
372    BlockVersionIsInvalid(u8),
373
374    #[error("Block {0} already in database")]
375    BlockAlreadyExists(String),
376
377    #[error("Block {0} not found in database")]
378    BlockNotFound(String),
379
380    #[error("Block with height number {0} not found in database")]
381    BlockHeightNotFound(u32),
382
383    #[error("Block difficulty for height number {0} not found in database")]
384    BlockDifficultyNotFound(u32),
385
386    #[error("Block state inverse diff for height number {0} not found in database")]
387    BlockStateInverseDiffNotFound(u32),
388
389    #[error("Block {0} contains 0 transactions")]
390    BlockContainsNoTransactions(String),
391
392    #[error("Contract {0} not found in database")]
393    ContractNotFound(String),
394
395    #[error("Contract state tree not found")]
396    ContractStateNotFound,
397
398    #[error("Contract already initialized")]
399    ContractAlreadyInitialized,
400
401    #[error("zkas bincode not found in sled database")]
402    ZkasBincodeNotFound,
403
404    // ===================
405    // wasm runtime errors
406    // ===================
407    #[cfg(feature = "wasm-runtime")]
408    #[error("Wasmer compile error: {0}")]
409    WasmerCompileError(String),
410
411    #[cfg(feature = "wasm-runtime")]
412    #[error("Wasmer export error: {0}")]
413    WasmerExportError(String),
414
415    #[cfg(feature = "wasm-runtime")]
416    #[error("Wasmer runtime error: {0}")]
417    WasmerRuntimeError(String),
418
419    #[cfg(feature = "wasm-runtime")]
420    #[error("Wasmer instantiation error: {0}")]
421    WasmerInstantiationError(String),
422
423    #[cfg(feature = "wasm-runtime")]
424    #[error("wasm memory error")]
425    WasmerMemoryError(String),
426
427    #[cfg(feature = "wasm-runtime")]
428    #[error("wasm runtime out of memory")]
429    WasmerOomError(String),
430
431    #[cfg(feature = "darkfi-sdk")]
432    #[error("Contract execution failed: {0}")]
433    ContractError(darkfi_sdk::error::ContractError),
434
435    #[cfg(feature = "darkfi-sdk")]
436    #[error("Invalid DarkTree: {0}")]
437    DarkTreeError(darkfi_sdk::error::DarkTreeError),
438
439    #[cfg(feature = "blockchain")]
440    #[error("contract wasm bincode not found")]
441    WasmBincodeNotFound,
442
443    #[cfg(feature = "wasm-runtime")]
444    #[error("contract initialize error")]
445    ContractInitError(u64),
446
447    #[cfg(feature = "wasm-runtime")]
448    #[error("contract execution error")]
449    ContractExecError(u64),
450
451    #[cfg(feature = "wasm-runtime")]
452    #[error("wasm function ACL denied")]
453    WasmFunctionAclDenied,
454
455    // ====================
456    // Event Graph errors
457    // ====================
458    #[error("Event is not found in tree: {0}")]
459    EventNotFound(String),
460
461    #[error("Event is invalid")]
462    EventIsInvalid,
463
464    // ====================
465    // Miscellaneous errors
466    // ====================
467    #[error("IO error: {0}")]
468    Io(std::io::ErrorKind),
469
470    #[error("Infallible error: {0}")]
471    InfallibleError(String),
472
473    #[cfg(feature = "smol")]
474    #[error("async_channel sender error: {0}")]
475    AsyncChannelSendError(String),
476
477    #[cfg(feature = "smol")]
478    #[error("async_channel receiver error: {0}")]
479    AsyncChannelRecvError(String),
480
481    #[error("SetLogger (log crate) failed: {0}")]
482    SetLoggerError(String),
483
484    #[error("ValueIsNotObject")]
485    ValueIsNotObject,
486
487    #[error("No config file detected")]
488    ConfigNotFound,
489
490    #[error("Invalid config file detected")]
491    ConfigInvalid,
492
493    #[error("Configuration error: {0}")]
494    ConfigError(String),
495
496    #[error("Failed decoding bincode: {0}")]
497    ZkasDecoderError(String),
498
499    #[cfg(feature = "util")]
500    #[error("System clock is not correct!")]
501    InvalidClock,
502
503    #[error("Unsupported OS")]
504    UnsupportedOS,
505
506    #[error("System clock went backwards")]
507    BackwardsTime(std::time::SystemTimeError),
508
509    #[error("Detached task stopped")]
510    DetachedTaskStopped,
511
512    #[error("Addition overflow")]
513    AdditionOverflow,
514
515    #[error("Subtraction underflow")]
516    SubtractionUnderflow,
517
518    // ==============================================
519    // Wrappers for other error types in this library
520    // ==============================================
521    #[error(transparent)]
522    ClientFailed(#[from] ClientFailed),
523
524    #[cfg(feature = "tx")]
525    #[error(transparent)]
526    TxVerifyFailed(#[from] TxVerifyFailed),
527
528    //=============
529    // clock
530    //=============
531    #[error("clock out of sync with peers: {0}")]
532    ClockOutOfSync(String),
533
534    // ================
535    // DHT/Geode errors
536    // ================
537    #[error("Geode needs garbage collection")]
538    GeodeNeedsGc,
539
540    #[error("Geode file not found")]
541    GeodeFileNotFound,
542
543    #[error("Geode chunk not found")]
544    GeodeChunkNotFound,
545
546    #[error("Geode file route not found")]
547    GeodeFileRouteNotFound,
548
549    #[error("Geode chunk route not found")]
550    GeodeChunkRouteNotFound,
551
552    // ==================
553    // Event Graph errors
554    // ==================
555    #[error("DAG sync failed")]
556    DagSyncFailed,
557
558    #[error("Malicious flood detected")]
559    MaliciousFlood,
560
561    // =========
562    // Catch-all
563    // =========
564    #[error("{0}")]
565    Custom(String),
566}
567
568#[cfg(feature = "tx")]
569impl Error {
570    /// Auxiliary function to retrieve the vector of erroneous
571    /// transactions from a TxVerifyFailed error.
572    /// In any other case, we return the error itself.
573    pub fn retrieve_erroneous_txs(&self) -> Result<Vec<crate::tx::Transaction>> {
574        if let Self::TxVerifyFailed(TxVerifyFailed::ErroneousTxs(erroneous_txs)) = self {
575            return Ok(erroneous_txs.clone())
576        };
577
578        Err(self.clone())
579    }
580}
581
582#[cfg(feature = "tx")]
583/// Transaction verification errors
584#[derive(Debug, Clone, thiserror::Error)]
585pub enum TxVerifyFailed {
586    #[error("Transaction {0} already exists")]
587    AlreadySeenTx(String),
588
589    #[error("Invalid transaction signature")]
590    InvalidSignature,
591
592    #[error("Missing signatures in transaction")]
593    MissingSignatures,
594
595    #[error("Missing contract calls in transaction")]
596    MissingCalls,
597
598    #[error("Invalid ZK proof in transaction")]
599    InvalidZkProof,
600
601    #[error("Missing Money::Fee call in transaction")]
602    MissingFee,
603
604    #[error("Invalid Money::Fee call in transaction")]
605    InvalidFee,
606
607    #[error("Insufficient fee paid")]
608    InsufficientFee,
609
610    #[error("Erroneous transactions found")]
611    ErroneousTxs(Vec<crate::tx::Transaction>),
612}
613
614/// Client module errors
615#[derive(Debug, Clone, thiserror::Error)]
616pub enum ClientFailed {
617    #[error("IO error: {0}")]
618    Io(std::io::ErrorKind),
619
620    #[error("Not enough value: {0}")]
621    NotEnoughValue(u64),
622
623    #[error("Invalid address: {0}")]
624    InvalidAddress(String),
625
626    #[error("Invalid amount: {0}")]
627    InvalidAmount(u64),
628
629    #[error("Invalid token ID: {0}")]
630    InvalidTokenId(String),
631
632    #[error("Internal error: {0}")]
633    InternalError(String),
634
635    #[error("Verify error: {0}")]
636    VerifyError(String),
637}
638
639#[cfg(feature = "rpc")]
640#[derive(Clone, Debug, thiserror::Error)]
641pub enum RpcError {
642    #[error("Connection closed: {0}")]
643    ConnectionClosed(String),
644
645    #[error("Invalid JSON: {0}")]
646    InvalidJson(String),
647
648    #[error("IO Error: {0}")]
649    IoError(std::io::ErrorKind),
650
651    #[error("Method not found: {0}")]
652    MethodNotFound(String),
653
654    #[error("Server-side Error: {0}")]
655    ServerError(#[from] std::sync::Arc<dyn std::error::Error + Send + Sync + 'static>),
656}
657
658#[cfg(feature = "rpc")]
659impl From<std::io::Error> for RpcError {
660    fn from(err: std::io::Error) -> Self {
661        Self::IoError(err.kind())
662    }
663}
664
665#[cfg(feature = "rpc")]
666impl From<RpcError> for Error {
667    fn from(err: RpcError) -> Self {
668        Self::RpcServerError(err)
669    }
670}
671
672impl From<Error> for ClientFailed {
673    fn from(err: Error) -> Self {
674        Self::InternalError(err.to_string())
675    }
676}
677
678impl From<std::io::Error> for ClientFailed {
679    fn from(err: std::io::Error) -> Self {
680        Self::Io(err.kind())
681    }
682}
683
684impl From<std::io::Error> for Error {
685    fn from(err: std::io::Error) -> Self {
686        Self::Io(err.kind())
687    }
688}
689
690impl From<std::time::SystemTimeError> for Error {
691    fn from(err: std::time::SystemTimeError) -> Self {
692        Self::BackwardsTime(err)
693    }
694}
695
696impl From<std::convert::Infallible> for Error {
697    fn from(err: std::convert::Infallible) -> Self {
698        Self::InfallibleError(err.to_string())
699    }
700}
701
702impl From<()> for Error {
703    fn from(_err: ()) -> Self {
704        Self::InfallibleError("Infallible".into())
705    }
706}
707
708#[cfg(feature = "net")]
709impl From<std::collections::TryReserveError> for Error {
710    fn from(err: std::collections::TryReserveError) -> Self {
711        Self::DecodePacket(err.to_string())
712    }
713}
714#[cfg(feature = "smol")]
715impl<T> From<smol::channel::SendError<T>> for Error {
716    fn from(err: smol::channel::SendError<T>) -> Self {
717        Self::AsyncChannelSendError(err.to_string())
718    }
719}
720
721#[cfg(feature = "smol")]
722impl From<smol::channel::RecvError> for Error {
723    fn from(err: smol::channel::RecvError) -> Self {
724        Self::AsyncChannelRecvError(err.to_string())
725    }
726}
727
728impl From<log::SetLoggerError> for Error {
729    fn from(err: log::SetLoggerError) -> Self {
730        Self::SetLoggerError(err.to_string())
731    }
732}
733
734#[cfg(feature = "halo2_proofs")]
735impl From<halo2_proofs::plonk::Error> for Error {
736    fn from(err: halo2_proofs::plonk::Error) -> Self {
737        Self::PlonkError(err.to_string())
738    }
739}
740
741#[cfg(feature = "semver")]
742impl From<semver::Error> for Error {
743    fn from(err: semver::Error) -> Self {
744        Self::SemverError(err.to_string())
745    }
746}
747
748#[cfg(feature = "tinyjson")]
749impl From<tinyjson::JsonParseError> for Error {
750    fn from(err: tinyjson::JsonParseError) -> Self {
751        Self::JsonParseError(err.to_string())
752    }
753}
754
755#[cfg(feature = "tinyjson")]
756impl From<tinyjson::JsonGenerateError> for Error {
757    fn from(err: tinyjson::JsonGenerateError) -> Self {
758        Self::JsonGenerateError(err.to_string())
759    }
760}
761
762#[cfg(feature = "wasm-runtime")]
763impl From<wasmer::CompileError> for Error {
764    fn from(err: wasmer::CompileError) -> Self {
765        Self::WasmerCompileError(err.to_string())
766    }
767}
768
769#[cfg(feature = "wasm-runtime")]
770impl From<wasmer::ExportError> for Error {
771    fn from(err: wasmer::ExportError) -> Self {
772        Self::WasmerExportError(err.to_string())
773    }
774}
775
776#[cfg(feature = "wasm-runtime")]
777impl From<wasmer::RuntimeError> for Error {
778    fn from(err: wasmer::RuntimeError) -> Self {
779        Self::WasmerRuntimeError(err.to_string())
780    }
781}
782
783#[cfg(feature = "wasm-runtime")]
784impl From<wasmer::InstantiationError> for Error {
785    fn from(err: wasmer::InstantiationError) -> Self {
786        Self::WasmerInstantiationError(err.to_string())
787    }
788}
789
790#[cfg(feature = "wasm-runtime")]
791impl From<wasmer::MemoryAccessError> for Error {
792    fn from(err: wasmer::MemoryAccessError) -> Self {
793        Self::WasmerMemoryError(err.to_string())
794    }
795}
796
797#[cfg(feature = "wasm-runtime")]
798impl From<wasmer::MemoryError> for Error {
799    fn from(err: wasmer::MemoryError) -> Self {
800        Self::WasmerOomError(err.to_string())
801    }
802}
803
804#[cfg(feature = "darkfi-sdk")]
805impl From<darkfi_sdk::error::ContractError> for Error {
806    fn from(err: darkfi_sdk::error::ContractError) -> Self {
807        Self::ContractError(err)
808    }
809}
810
811#[cfg(feature = "darkfi-sdk")]
812impl From<darkfi_sdk::error::DarkTreeError> for Error {
813    fn from(err: darkfi_sdk::error::DarkTreeError) -> Self {
814        Self::DarkTreeError(err)
815    }
816}