1use std::io;
2
3use miden_node_proto::domain::block::InvalidBlockRange;
4use miden_node_proto::errors::ConversionError;
5use miden_node_utils::limiter::QueryLimitError;
6use miden_protocol::Word;
7use miden_protocol::account::AccountId;
8use miden_protocol::block::BlockNumber;
9use miden_protocol::crypto::merkle::MerkleError;
10use miden_protocol::crypto::merkle::mmr::MmrError;
11use miden_protocol::crypto::merkle::smt::{LargeSmtForestError, LineageId};
12use miden_protocol::crypto::utils::DeserializationError;
13use miden_protocol::errors::{
14 AccountDeltaError,
15 AccountError,
16 AccountTreeError,
17 AssetError,
18 AssetVaultError,
19 NoteError,
20 NullifierTreeError,
21 StorageMapError,
22};
23use miden_protocol::note::Nullifier;
24use miden_protocol::transaction::OutputNote;
25use thiserror::Error;
26use tokio::sync::oneshot::error::RecvError;
27
28#[derive(Debug, Error)]
33pub enum AccountStateForestUpdateError {
34 #[error("account {account_id} vault lineage already exists")]
36 VaultLineageAlreadyExists { account_id: AccountId },
37 #[error("account {account_id} storage map lineage for slot {slot_name} already exists")]
39 StorageLineageAlreadyExists {
40 account_id: AccountId,
41 slot_name: miden_protocol::account::StorageSlotName,
42 },
43 #[error("computed forest mutations are missing lineage {lineage}")]
45 MissingComputedRoot { lineage: LineageId },
46 #[error(transparent)]
48 Forest(#[from] LargeSmtForestError),
49}
50
51#[derive(Debug, Error)]
55pub enum DatabaseError {
56 #[error("invalid persisted protobuf value")]
57 Persistence(#[from] miden_node_persistence::PersistenceError),
58 #[error("account error")]
61 AccountError(#[from] AccountError),
62 #[error("asset vault error")]
63 AssetVaultError(#[from] AssetVaultError),
64 #[error("asset error")]
65 AssetError(#[from] AssetError),
66 #[error("closed channel")]
67 ClosedChannel(#[from] RecvError),
68 #[error("database error")]
69 DatabaseError(#[from] miden_node_db::DatabaseError),
70 #[error("deserialization failed")]
71 DeserializationError(#[from] DeserializationError),
72 #[error("I/O error")]
73 IoError(#[from] io::Error),
74 #[error("merkle error")]
75 MerkleError(#[from] MerkleError),
76 #[error("note error")]
77 NoteError(#[from] NoteError),
78 #[error("storage map error")]
79 StorageMapError(#[from] StorageMapError),
80 #[error(transparent)]
81 QueryParamLimit(#[from] QueryLimitError),
82 #[error(transparent)]
83 RangeBeyondTip(#[from] RangeBeyondTip),
84
85 #[error("account commitment mismatch (expected {expected}, but calculated is {calculated})")]
88 AccountCommitmentsMismatch { expected: Word, calculated: Word },
89 #[error(
90 "protocol config commitment mismatch (expected {expected}, but calculated is {calculated})"
91 )]
92 ProtocolConfigCommitmentMismatch { expected: Word, calculated: Word },
93 #[error("protocol config {0} is missing")]
94 ProtocolConfigNotFound(Word),
95 #[error("account {0} not found")]
96 AccountNotFoundInDb(AccountId),
97 #[error("account {0} already exists")]
98 AccountAlreadyExistsInDb(AccountId),
99 #[error("accounts {0:?} not found")]
100 AccountsNotFoundInDb(Vec<AccountId>),
101 #[error("account {0} is not on the chain")]
102 AccountNotPublic(AccountId),
103 #[error("invalid block parameters: block_from ({from}) > block_to ({to})")]
104 InvalidBlockRange { from: BlockNumber, to: BlockNumber },
105 #[error(
106 "transactions for block {block_num} would exceed maximum response size, \
107 use a stricter filter to reduce the number of transactions returned"
108 )]
109 TransactionPageExceedsPayloadLimit { block_num: BlockNumber },
110 #[error("data corrupted: {0}")]
111 DataCorrupted(String),
112 #[error("storage root not found for account {account_id}, slot {slot_name}, block {block_num}")]
113 StorageRootNotFound {
114 account_id: AccountId,
115 slot_name: String,
116 block_num: BlockNumber,
117 },
118}
119
120#[derive(Error, Debug)]
124pub enum StateInitializationError {
125 #[error("account tree IO error: {0}")]
126 AccountTreeIoError(String),
127 #[error("nullifier tree IO error: {0}")]
128 NullifierTreeIoError(String),
129 #[error("account state forest IO error: {0}")]
130 AccountStateForestIoError(String),
131 #[error("failed to rebuild account state forest")]
132 AccountStateForestRebuild(#[source] AccountStateForestUpdateError),
133 #[error("database error")]
134 DatabaseError(#[from] DatabaseError),
135 #[error("failed to create nullifier tree")]
136 FailedToCreateNullifierTree(#[from] NullifierTreeError),
137 #[error("failed to create accounts tree")]
138 FailedToCreateAccountsTree(#[source] AccountTreeError),
139 #[error("failed to load data directory")]
140 DataDirectoryLoadError(#[source] std::io::Error),
141 #[error("failed to load block store")]
142 BlockStoreLoadError(#[source] std::io::Error),
143 #[error("failed to load proven tip")]
144 ProvenTipLoadError(#[source] std::io::Error),
145 #[error("failed to load database")]
146 DatabaseLoadError(#[source] DatabaseError),
147 #[error(
148 "{tree_name} SMT root ({tree_root:?}) does not match expected root from block {block_num} \
149 ({block_root:?}). Delete the tree storage directories and restart the node to rebuild \
150 from the database."
151 )]
152 TreeStorageDiverged {
153 tree_name: &'static str,
154 block_num: BlockNumber,
155 tree_root: Word,
156 block_root: Word,
157 },
158 #[error("loaded chain MMR cannot produce peaks for block {block_num}")]
159 ChainMmrLoadError {
160 block_num: BlockNumber,
161 #[source]
162 source: MmrError,
163 },
164 #[error(
165 "chain MMR commitment ({chain_mmr_commitment:?}) does not match expected chain commitment \
166 from block {block_num} ({block_header_commitment:?})"
167 )]
168 ChainMmrStorageDiverged {
169 block_num: BlockNumber,
170 chain_mmr_commitment: Word,
171 block_header_commitment: Word,
172 },
173 #[error(
174 "account state forest root ({forest_root}) does not match SQLite root \
175 ({database_root}) for account {account_id}, slot {slot_name:?}. Delete the account \
176 state forest storage directory and restart the node to rebuild from the database."
177 )]
178 AccountStateForestStorageDiverged {
179 account_id: AccountId,
180 slot_name: Option<String>,
181 forest_root: Word,
182 database_root: Word,
183 },
184 #[error("public account {0} is missing details in database")]
185 PublicAccountMissingDetails(AccountId),
186 #[error("failed to convert account to delta: {0}")]
187 AccountToDeltaConversionFailed(String),
188 #[error("genesis block missing. The database should be bootstrapped first.")]
189 GenesisBlockMissing,
190 #[error(
191 "genesis protocol config {commitment} is missing. Rebootstrap the database from genesis."
192 )]
193 GenesisProtocolConfigMissing { commitment: Word },
194}
195
196#[derive(Error, Debug)]
199pub enum InvalidBlockError {
200 #[error("duplicated nullifiers {0:?}")]
201 DuplicatedNullifiers(Vec<Nullifier>),
202 #[error("invalid output note type: {0:?}")]
203 InvalidOutputNoteType(Box<OutputNote>),
204 #[error("invalid block tx commitment: expected {expected}, but got {actual}")]
205 InvalidBlockTxCommitment { expected: Word, actual: Word },
206 #[error("received invalid account tree root")]
207 NewBlockInvalidAccountRoot,
208 #[error("new block number must be 1 greater than the current block number")]
209 NewBlockInvalidBlockNum {
210 expected: BlockNumber,
211 submitted: BlockNumber,
212 },
213 #[error("new block chain commitment is not consistent with chain MMR")]
214 NewBlockInvalidChainCommitment,
215 #[error("received invalid note root")]
216 NewBlockInvalidNoteRoot,
217 #[error("received invalid nullifier root")]
218 NewBlockInvalidNullifierRoot,
219 #[error("new block `prev_block_commitment` must match the chain's tip")]
220 NewBlockInvalidPrevCommitment,
221 #[error("nullifier in new block is already spent")]
222 NewBlockNullifierAlreadySpent(#[source] NullifierTreeError),
223 #[error("duplicate account ID prefix in new block")]
224 NewBlockDuplicateAccountIdPrefix(#[source] AccountTreeError),
225 #[error("failed to build note tree: {0}")]
226 FailedToBuildNoteTree(String),
227}
228
229#[derive(Error, Debug)]
230pub enum ApplyBlockError {
231 #[error("database error")]
234 DatabaseError(#[from] DatabaseError),
235 #[error("I/O error")]
236 IoError(#[from] io::Error),
237 #[error("task join error")]
238 TokioJoinError(#[from] tokio::task::JoinError),
239 #[error("invalid block error")]
240 InvalidBlockError(#[from] InvalidBlockError),
241
242 #[error("block applying was cancelled because the writer task dropped the result channel")]
245 ClosedChannel(#[from] RecvError),
246 #[error("account state forest update preparation failed")]
247 AccountStateForestPreparation(#[source] AccountStateForestUpdateError),
248 #[error("database doesn't have any block header data")]
249 DbBlockHeaderEmpty,
250 #[error("database update failed: {0}")]
251 DbUpdateTaskFailed(String),
252 #[error("failed to send block to the writer task: {0}")]
253 WriterTaskSendFailed(String),
254}
255
256#[derive(Error, Debug)]
257pub enum ApplyBlockWithProvingInputsError {
258 #[error("failed to save block proving inputs")]
259 SaveProvingInputs(#[source] io::Error),
260 #[error("failed to apply block")]
261 ApplyBlock(#[source] ApplyBlockError),
262}
263
264#[derive(Error, Debug)]
266#[error("block_to ({block_to}) is greater than chain tip ({chain_tip})")]
267pub struct RangeBeyondTip {
268 pub chain_tip: BlockNumber,
269 pub block_to: BlockNumber,
270}
271
272#[derive(Error, Debug)]
273pub enum GetBlockHeaderError {
274 #[error("database error")]
275 DatabaseError(#[from] DatabaseError),
276 #[error("error retrieving the merkle proof for the block")]
277 MmrError(#[from] MmrError),
278}
279
280#[derive(Error, Debug)]
281pub enum StateSyncError {
282 #[error("database error")]
283 DatabaseError(#[from] DatabaseError),
284 #[error("block headers table is empty")]
285 EmptyBlockHeadersTable,
286 #[error("failed to build MMR delta")]
287 FailedToBuildMmrDelta(#[from] MmrError),
288 #[error(transparent)]
289 RangeBeyondTip(#[from] RangeBeyondTip),
290}
291
292#[derive(Error, Debug)]
293pub enum NoteSyncError {
294 #[error("database error")]
295 DatabaseError(#[from] DatabaseError),
296 #[error("database error")]
297 UnderlyingDatabaseError(#[from] miden_node_db::DatabaseError),
298 #[error("block headers table is empty")]
299 EmptyBlockHeadersTable,
300 #[error("error retrieving the merkle proof for the block")]
301 MmrError(#[from] MmrError),
302 #[error("invalid block range")]
303 InvalidBlockRange(#[from] InvalidBlockRange),
304 #[error(transparent)]
305 RangeBeyondTip(#[from] RangeBeyondTip),
306 #[error("malformed note tags")]
307 DeserializationFailed(#[from] ConversionError),
308}
309
310#[derive(Error, Debug)]
311pub enum GetNoteInclusionProofsError {
312 #[error("failed to select note inclusion proofs")]
313 SelectNoteInclusionProofError(#[source] DatabaseError),
314 #[error("reference block {reference_block} is newer than the latest block {latest_block_num}")]
315 ReferenceBlockAfterTip {
316 reference_block: BlockNumber,
317 latest_block_num: BlockNumber,
318 },
319}
320
321#[derive(Error, Debug)]
322pub enum GetBlockInclusionProofsError {
323 #[error("failed to select block headers")]
324 SelectBlockHeaderError(#[source] DatabaseError),
325 #[error("reference block {reference_block} is newer than the latest block {latest_block_num}")]
326 ReferenceBlockAfterTip {
327 reference_block: BlockNumber,
328 latest_block_num: BlockNumber,
329 },
330 #[error("block {block_num} is newer than the reference block {reference_block}")]
331 BlockAfterReferenceBlock {
332 block_num: BlockNumber,
333 reference_block: BlockNumber,
334 },
335}
336
337#[derive(Debug, Error)]
341pub enum GetAccountError {
342 #[error("database error")]
343 DatabaseError(#[from] DatabaseError),
344 #[error("malformed request")]
345 DeserializationFailed(#[from] ConversionError),
346 #[error("account {0} not found at block {1}")]
347 AccountNotFound(AccountId, BlockNumber),
348 #[error("account {0} is not public")]
349 AccountNotPublic(AccountId),
350 #[error("block {0} is unknown")]
351 UnknownBlock(BlockNumber),
352 #[error("block {0} has been pruned")]
353 BlockPruned(BlockNumber),
354}
355
356mod compile_tests {
360 use std::marker::PhantomData;
361
362 use super::{
363 AccountDeltaError,
364 AccountError,
365 DatabaseError,
366 DeserializationError,
367 NoteError,
368 RecvError,
369 StateInitializationError,
370 };
371
372 #[expect(dead_code)]
375 fn assumed_trait_bounds_upheld() {
376 fn ensure_is_error<E>(_phony: PhantomData<E>)
377 where
378 E: std::error::Error + Send + Sync + 'static,
379 {
380 }
381
382 ensure_is_error::<AccountError>(PhantomData);
383 ensure_is_error::<AccountDeltaError>(PhantomData);
384 ensure_is_error::<RecvError>(PhantomData);
385 ensure_is_error::<DeserializationError>(PhantomData);
386 ensure_is_error::<NoteError>(PhantomData);
387 ensure_is_error::<hex::FromHexError>(PhantomData);
388
389 ensure_is_error::<DatabaseError>(PhantomData);
390 ensure_is_error::<StateInitializationError>(PhantomData);
391 }
392}