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
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
use std::io;

use deadpool_sqlite::PoolError;
use miden_objects::{
    crypto::{
        hash::rpo::RpoDigest,
        merkle::{MerkleError, MmrError},
        utils::DeserializationError,
    },
    notes::Nullifier,
    transaction::OutputNote,
    AccountError, BlockError, BlockHeader, NoteError,
};
use rusqlite::types::FromSqlError;
use thiserror::Error;
use tokio::sync::oneshot::error::RecvError;

use crate::types::{AccountId, BlockNumber};

// INTERNAL ERRORS
// =================================================================================================

#[derive(Debug, Error)]
pub enum NullifierTreeError {
    #[error("Merkle error: {0}")]
    MerkleError(#[from] MerkleError),
    #[error("Nullifier {nullifier} for block #{block_num} already exists in the nullifier tree")]
    NullifierAlreadyExists {
        nullifier: Nullifier,
        block_num: BlockNumber,
    },
}

// DATABASE ERRORS
// =================================================================================================

#[derive(Debug, Error)]
pub enum DatabaseError {
    #[error("Missing database connection: {0}")]
    MissingDbConnection(#[from] PoolError),
    #[error("SQLite error: {0}")]
    SqliteError(#[from] rusqlite::Error),
    #[error("SQLite error: {0}")]
    FromSqlError(#[from] FromSqlError),
    #[error("Hex parsing error: {0}")]
    FromHexError(#[from] hex::FromHexError),
    #[error("I/O error: {0}")]
    IoError(#[from] io::Error),
    #[error("Account error: {0}")]
    AccountError(#[from] AccountError),
    #[error("Note error: {0}")]
    NoteError(#[from] NoteError),
    #[error("Migration error: {0}")]
    MigrationError(#[from] rusqlite_migration::Error),
    #[error("SQLite pool interaction task failed: {0}")]
    InteractError(String),
    #[error("Deserialization of BLOB data from database failed: {0}")]
    DeserializationError(DeserializationError),
    #[error("Corrupted data: {0}")]
    CorruptedData(String),
    #[error("Invalid Felt: {0}")]
    InvalidFelt(String),
    #[error("Block applying was broken because of closed channel on state side: {0}")]
    ApplyBlockFailedClosedChannel(RecvError),
    #[error("Account {0} not found in the database")]
    AccountNotFoundInDb(AccountId),
    #[error("Account {0} is not on the chain")]
    AccountNotOnChain(AccountId),
    #[error("Failed to apply block because of on-chain account final hashes mismatch (expected {expected}, \
        but calculated is {calculated}")]
    ApplyBlockFailedAccountHashesMismatch {
        expected: RpoDigest,
        calculated: RpoDigest,
    },
    #[error("Unsupported database version. There is no migration chain from/to this version. Remove database files \
        and try again.")]
    UnsupportedDatabaseVersion,
}

impl From<DeserializationError> for DatabaseError {
    fn from(value: DeserializationError) -> Self {
        Self::DeserializationError(value)
    }
}

// INITIALIZATION ERRORS
// =================================================================================================

#[derive(Error, Debug)]
pub enum StateInitializationError {
    #[error("Database error: {0}")]
    DatabaseError(#[from] DatabaseError),
    #[error("Failed to create nullifier tree: {0}")]
    FailedToCreateNullifierTree(NullifierTreeError),
    #[error("Failed to create accounts tree: {0}")]
    FailedToCreateAccountsTree(MerkleError),
}

#[derive(Debug, Error)]
pub enum DatabaseSetupError {
    #[error("I/O error: {0}")]
    IoError(#[from] io::Error),
    #[error("Database error: {0}")]
    DatabaseError(#[from] DatabaseError),
    #[error("Genesis block error: {0}")]
    GenesisBlockError(#[from] GenesisError),
    #[error("Pool build error: {0}")]
    PoolBuildError(#[from] deadpool_sqlite::BuildError),
    #[error("SQLite migration error: {0}")]
    SqliteMigrationError(#[from] rusqlite_migration::Error),
}

#[derive(Debug, Error)]
pub enum GenesisError {
    #[error("Database error: {0}")]
    DatabaseError(#[from] DatabaseError),
    #[error("Block error: {0}")]
    BlockError(#[from] BlockError),
    #[error("Merkle error: {0}")]
    MerkleError(#[from] MerkleError),
    #[error("Apply block failed: {0}")]
    ApplyBlockFailed(String),
    #[error("Failed to read genesis file \"{genesis_filepath}\": {error}")]
    FailedToReadGenesisFile {
        genesis_filepath: String,
        error: io::Error,
    },
    #[error("Failed to deserialize genesis file: {0}")]
    GenesisFileDeserializationError(DeserializationError),
    #[error("Block header in store doesn't match block header in genesis file. Expected {expected_genesis_header:?}, but store contained {block_header_in_store:?}")]
    GenesisBlockHeaderMismatch {
        expected_genesis_header: Box<BlockHeader>,
        block_header_in_store: Box<BlockHeader>,
    },
    #[error("Retrieving genesis block header failed: {0}")]
    SelectBlockHeaderByBlockNumError(Box<DatabaseError>),
}

// ENDPOINT ERRORS
// =================================================================================================

#[derive(Error, Debug)]
pub enum ApplyBlockError {
    #[error("Database error: {0}")]
    DatabaseError(#[from] DatabaseError),
    #[error("I/O error: {0}")]
    IoError(#[from] io::Error),
    #[error("Task join error: {0}")]
    TokioJoinError(#[from] tokio::task::JoinError),
    #[error("Concurrent write detected")]
    ConcurrentWrite,
    #[error("New block number must be 1 greater than the current block number")]
    NewBlockInvalidBlockNum,
    #[error("New block `prev_hash` must match the chain's tip")]
    NewBlockInvalidPrevHash,
    #[error("New block chain root is not consistent with chain MMR")]
    NewBlockInvalidChainRoot,
    #[error("Received invalid account tree root")]
    NewBlockInvalidAccountRoot,
    #[error("Received invalid note root")]
    NewBlockInvalidNoteRoot,
    #[error("Received invalid nullifier root")]
    NewBlockInvalidNullifierRoot,
    #[error("Duplicated nullifiers {0:?}")]
    DuplicatedNullifiers(Vec<Nullifier>),
    #[error("Unable to create proof for note: {0}")]
    UnableToCreateProofForNote(MerkleError),
    #[error("Block applying was broken because of closed channel on database side: {0}")]
    BlockApplyingBrokenBecauseOfClosedChannel(RecvError),
    #[error("Failed to create notes tree: {0}")]
    FailedToCreateNoteTree(MerkleError),
    #[error("Database doesn't have any block header data")]
    DbBlockHeaderEmpty,
    #[error("Failed to get MMR peaks for forest ({forest}): {error}")]
    FailedToGetMmrPeaksForForest { forest: usize, error: MmrError },
    #[error("Failed to update nullifier tree: {0}")]
    FailedToUpdateNullifierTree(NullifierTreeError),
    #[error("Invalid output note type: {0:?}")]
    InvalidOutputNoteType(OutputNote),
    #[error("Invalid tx hash: expected {expected}, but got {actual}")]
    InvalidTxHash { expected: RpoDigest, actual: RpoDigest },
}

#[derive(Error, Debug)]
pub enum GetBlockHeaderError {
    #[error("Database error: {0}")]
    DatabaseError(#[from] DatabaseError),
    #[error("Error retrieving the merkle proof for the block: {0}")]
    MmrError(#[from] MmrError),
}

#[derive(Error, Debug)]
pub enum GetBlockInputsError {
    #[error("Database error: {0}")]
    DatabaseError(#[from] DatabaseError),
    #[error("Account error: {0}")]
    AccountError(#[from] AccountError),
    #[error("Database doesn't have any block header data")]
    DbBlockHeaderEmpty,
    #[error("Failed to get MMR peaks for forest ({forest}): {error}")]
    FailedToGetMmrPeaksForForest { forest: usize, error: MmrError },
    #[error("Chain MMR forest expected to be 1 less than latest header's block num. Chain MMR forest: {forest}, block num: {block_num}")]
    IncorrectChainMmrForestNumber { forest: usize, block_num: u32 },
}

#[derive(Error, Debug)]
pub enum StateSyncError {
    #[error("Database error: {0}")]
    DatabaseError(#[from] DatabaseError),
    #[error("Block headers table is empty")]
    EmptyBlockHeadersTable,
    #[error("Failed to build MMR delta: {0}")]
    FailedToBuildMmrDelta(MmrError),
}