miden_client/store/
mod.rs

1//! Defines the storage interfaces used by the Miden client.
2//!
3//! It provides mechanisms for persisting and retrieving data, such as account states, transaction
4//! history, block headers, notes, and MMR nodes.
5//!
6//! ## Overview
7//!
8//! The storage module is central to the Miden client’s persistence layer. It defines the
9//! [`Store`] trait which abstracts over any concrete storage implementation. The trait exposes
10//! methods to (among others):
11//!
12//! - Retrieve and update transactions, notes, and accounts.
13//! - Store and query block headers along with MMR peaks and authentication nodes.
14//! - Manage note tags for synchronizing with the node.
15//!
16//! These are all used by the Miden client to provide transaction execution in the correct contexts.
17//!
18//! In addition to the main [`Store`] trait, the module provides types for filtering queries, such
19//! as [`TransactionFilter`] and [`NoteFilter`], to narrow down the set of returned transactions or
20//! notes. For more advanced usage, see the documentation of individual methods in the [`Store`]
21//! trait.
22
23use alloc::boxed::Box;
24use alloc::collections::{BTreeMap, BTreeSet};
25use alloc::string::{String, ToString};
26use alloc::vec::Vec;
27use core::fmt::Debug;
28
29use miden_objects::account::{
30    Account,
31    AccountCode,
32    AccountHeader,
33    AccountId,
34    AccountIdPrefix,
35    AccountStorage,
36    StorageMapWitness,
37    StorageSlot,
38};
39use miden_objects::address::Address;
40use miden_objects::asset::{Asset, AssetVault, AssetWitness};
41use miden_objects::block::{BlockHeader, BlockNumber};
42use miden_objects::crypto::merkle::{InOrderIndex, MmrPeaks, PartialMmr};
43use miden_objects::note::{NoteId, NoteScript, NoteTag, Nullifier};
44use miden_objects::transaction::TransactionId;
45use miden_objects::{AccountError, Word};
46
47use crate::note_transport::{
48    NOTE_TRANSPORT_CURSOR_STORE_SETTING,
49    NoteTransportCursor,
50    NoteTransportUpdate,
51};
52use crate::sync::{NoteTagRecord, StateSyncUpdate};
53use crate::transaction::{TransactionRecord, TransactionStatusVariant, TransactionStoreUpdate};
54
55/// Contains [`ClientDataStore`] to automatically implement [`DataStore`] for anything that
56/// implements [`Store`]. This isn't public because it's an implementation detail to instantiate the
57/// executor.
58///
59/// The user is tasked with creating a [`Store`] which the client will wrap into a
60/// [`ClientDataStore`] at creation time.
61pub(crate) mod data_store;
62
63mod errors;
64pub use errors::*;
65
66mod account;
67pub use account::{AccountRecord, AccountStatus, AccountUpdates};
68mod note_record;
69pub use note_record::{
70    InputNoteRecord,
71    InputNoteState,
72    NoteExportType,
73    NoteRecordError,
74    OutputNoteRecord,
75    OutputNoteState,
76    input_note_states,
77};
78
79// STORE TRAIT
80// ================================================================================================
81
82/// The [`Store`] trait exposes all methods that the client store needs in order to track the
83/// current state.
84///
85/// All update functions are implied to be atomic. That is, if multiple entities are meant to be
86/// updated as part of any single function and an error is returned during its execution, any
87/// changes that might have happened up to that point need to be rolled back and discarded.
88///
89/// Because the [`Store`]'s ownership is shared between the executor and the client, interior
90/// mutability is expected to be implemented, which is why all methods receive `&self` and
91/// not `&mut self`.
92#[cfg_attr(not(target_arch = "wasm32"), async_trait::async_trait)]
93#[cfg_attr(target_arch = "wasm32", async_trait::async_trait(?Send))]
94pub trait Store: Send + Sync {
95    /// Returns the current timestamp tracked by the store, measured in non-leap seconds since
96    /// Unix epoch. If the store implementation is incapable of tracking time, it should return
97    /// `None`.
98    ///
99    /// This method is used to add time metadata to notes' states. This information doesn't have a
100    /// functional impact on the client's operation, it's shown to the user for informational
101    /// purposes.
102    fn get_current_timestamp(&self) -> Option<u64>;
103
104    // TRANSACTIONS
105    // --------------------------------------------------------------------------------------------
106
107    /// Retrieves stored transactions, filtered by [`TransactionFilter`].
108    async fn get_transactions(
109        &self,
110        filter: TransactionFilter,
111    ) -> Result<Vec<TransactionRecord>, StoreError>;
112
113    /// Applies a transaction, atomically updating the current state based on the
114    /// [`TransactionStoreUpdate`].
115    ///
116    /// An update involves:
117    /// - Updating the stored account which is being modified by the transaction.
118    /// - Storing new input/output notes and payback note details as a result of the transaction
119    ///   execution.
120    /// - Updating the input notes that are being processed by the transaction.
121    /// - Inserting the new tracked tags into the store.
122    /// - Inserting the transaction into the store to track.
123    async fn apply_transaction(&self, tx_update: TransactionStoreUpdate) -> Result<(), StoreError>;
124
125    // NOTES
126    // --------------------------------------------------------------------------------------------
127
128    /// Retrieves the input notes from the store.
129    async fn get_input_notes(&self, filter: NoteFilter)
130    -> Result<Vec<InputNoteRecord>, StoreError>;
131
132    /// Retrieves the output notes from the store.
133    async fn get_output_notes(
134        &self,
135        filter: NoteFilter,
136    ) -> Result<Vec<OutputNoteRecord>, StoreError>;
137
138    /// Returns the nullifiers of all unspent input notes.
139    ///
140    /// The default implementation of this method uses [`Store::get_input_notes`].
141    async fn get_unspent_input_note_nullifiers(&self) -> Result<Vec<Nullifier>, StoreError> {
142        self.get_input_notes(NoteFilter::Unspent)
143            .await?
144            .iter()
145            .map(|input_note| Ok(input_note.nullifier()))
146            .collect::<Result<Vec<_>, _>>()
147    }
148
149    /// Inserts the provided input notes into the database. If a note with the same ID already
150    /// exists, it will be replaced.
151    async fn upsert_input_notes(&self, notes: &[InputNoteRecord]) -> Result<(), StoreError>;
152
153    /// Returns the note script associated with the given root.
154    async fn get_note_script(&self, script_root: Word) -> Result<NoteScript, StoreError>;
155
156    /// Inserts the provided note scripts into the database. If a script with the same root already
157    /// exists, it will be replaced.
158    async fn upsert_note_scripts(&self, note_scripts: &[NoteScript]) -> Result<(), StoreError>;
159
160    // CHAIN DATA
161    // --------------------------------------------------------------------------------------------
162
163    /// Retrieves a vector of [`BlockHeader`]s filtered by the provided block numbers.
164    ///
165    /// The returned vector may not contain some or all of the requested block headers. It's up to
166    /// the callee to check whether all requested block headers were found.
167    ///
168    /// For each block header an additional boolean value is returned representing whether the block
169    /// contains notes relevant to the client.
170    async fn get_block_headers(
171        &self,
172        block_numbers: &BTreeSet<BlockNumber>,
173    ) -> Result<Vec<(BlockHeader, BlockRelevance)>, StoreError>;
174
175    /// Retrieves a [`BlockHeader`] corresponding to the provided block number and a boolean value
176    /// that represents whether the block contains notes relevant to the client. Returns `None` if
177    /// the block is not found.
178    ///
179    /// The default implementation of this method uses [`Store::get_block_headers`].
180    async fn get_block_header_by_num(
181        &self,
182        block_number: BlockNumber,
183    ) -> Result<Option<(BlockHeader, BlockRelevance)>, StoreError> {
184        self.get_block_headers(&[block_number].into_iter().collect())
185            .await
186            .map(|mut block_headers_list| block_headers_list.pop())
187    }
188
189    /// Retrieves a list of [`BlockHeader`] that include relevant notes to the client.
190    async fn get_tracked_block_headers(&self) -> Result<Vec<BlockHeader>, StoreError>;
191
192    /// Retrieves all MMR authentication nodes based on [`PartialBlockchainFilter`].
193    async fn get_partial_blockchain_nodes(
194        &self,
195        filter: PartialBlockchainFilter,
196    ) -> Result<BTreeMap<InOrderIndex, Word>, StoreError>;
197
198    /// Inserts blockchain MMR authentication nodes.
199    ///
200    /// In the case where the [`InOrderIndex`] already exists on the table, the insertion is
201    /// ignored.
202    async fn insert_partial_blockchain_nodes(
203        &self,
204        nodes: &[(InOrderIndex, Word)],
205    ) -> Result<(), StoreError>;
206
207    /// Returns peaks information from the blockchain by a specific block number.
208    ///
209    /// If there is no partial blockchain info stored for the provided block returns an empty
210    /// [`MmrPeaks`].
211    async fn get_partial_blockchain_peaks_by_block_num(
212        &self,
213        block_num: BlockNumber,
214    ) -> Result<MmrPeaks, StoreError>;
215
216    /// Inserts a block header into the store, alongside peaks information at the block's height.
217    ///
218    /// `has_client_notes` describes whether the block has relevant notes to the client; this means
219    /// the client might want to authenticate merkle paths based on this value.
220    /// If the block header exists and `has_client_notes` is `true` then the `has_client_notes`
221    /// column is updated to `true` to signify that the block now contains a relevant note.
222    async fn insert_block_header(
223        &self,
224        block_header: &BlockHeader,
225        partial_blockchain_peaks: MmrPeaks,
226        has_client_notes: bool,
227    ) -> Result<(), StoreError>;
228
229    /// Removes block headers that do not contain any client notes and aren't the genesis or last
230    /// block.
231    async fn prune_irrelevant_blocks(&self) -> Result<(), StoreError>;
232
233    // ACCOUNT
234    // --------------------------------------------------------------------------------------------
235
236    /// Returns the account IDs of all accounts stored in the database.
237    async fn get_account_ids(&self) -> Result<Vec<AccountId>, StoreError>;
238
239    /// Returns a list of [`AccountHeader`] of all accounts stored in the database along with their
240    /// statuses.
241    ///
242    /// Said accounts' state is the state after the last performed sync.
243    async fn get_account_headers(&self) -> Result<Vec<(AccountHeader, AccountStatus)>, StoreError>;
244
245    /// Retrieves an [`AccountHeader`] object for the specified [`AccountId`] along with its status.
246    /// Returns `None` if the account is not found.
247    ///
248    /// Said account's state is the state according to the last sync performed.
249    async fn get_account_header(
250        &self,
251        account_id: AccountId,
252    ) -> Result<Option<(AccountHeader, AccountStatus)>, StoreError>;
253
254    /// Returns an [`AccountHeader`] corresponding to the stored account state that matches the
255    /// given commitment. If no account state matches the provided commitment, `None` is returned.
256    async fn get_account_header_by_commitment(
257        &self,
258        account_commitment: Word,
259    ) -> Result<Option<AccountHeader>, StoreError>;
260
261    /// Retrieves a full [`AccountRecord`] object, this contains the account's latest state along
262    /// with its status. Returns `None` if the account is not found.
263    async fn get_account(&self, account_id: AccountId)
264    -> Result<Option<AccountRecord>, StoreError>;
265
266    /// Inserts an [`Account`] to the store.
267    /// Receives an [`Address`] as the initial address to associate with the account. This address
268    /// will be tracked for incoming notes and its derived note tag will be monitored.
269    ///
270    /// # Errors
271    ///
272    /// - If the account is new and does not contain a seed
273    async fn insert_account(
274        &self,
275        account: &Account,
276        initial_address: Address,
277    ) -> Result<(), StoreError>;
278
279    /// Upserts the account code for a foreign account. This value will be used as a cache of known
280    /// script roots and added to the `GetForeignAccountCode` request.
281    async fn upsert_foreign_account_code(
282        &self,
283        account_id: AccountId,
284        code: AccountCode,
285    ) -> Result<(), StoreError>;
286
287    /// Retrieves the cached account code for various foreign accounts.
288    async fn get_foreign_account_code(
289        &self,
290        account_ids: Vec<AccountId>,
291    ) -> Result<BTreeMap<AccountId, AccountCode>, StoreError>;
292
293    /// Retrieves all [`Address`] objects that correspond to the provided account ID.
294    async fn get_addresses_by_account_id(
295        &self,
296        account_id: AccountId,
297    ) -> Result<Vec<Address>, StoreError>;
298
299    /// Updates an existing [`Account`] with a new state.
300    ///
301    /// # Errors
302    ///
303    /// Returns a `StoreError::AccountDataNotFound` if there is no account for the provided ID.
304    async fn update_account(&self, new_account_state: &Account) -> Result<(), StoreError>;
305
306    /// Adds an [`Address`] to an [`Account`], alongside its derived note tag.
307    async fn insert_address(
308        &self,
309        address: Address,
310        account_id: AccountId,
311    ) -> Result<(), StoreError>;
312
313    /// Removes an [`Address`] from an [`Account`], alongside its derived note tag.
314    async fn remove_address(
315        &self,
316        address: Address,
317        account_id: AccountId,
318    ) -> Result<(), StoreError>;
319
320    // SETTINGS
321    // --------------------------------------------------------------------------------------------
322
323    /// Adds a value to the `settings` table.
324    async fn set_setting(&self, key: String, value: Vec<u8>) -> Result<(), StoreError>;
325
326    /// Retrieves a value from the `settings` table.
327    async fn get_setting(&self, key: String) -> Result<Option<Vec<u8>>, StoreError>;
328
329    /// Deletes a value from the `settings` table.
330    async fn remove_setting(&self, key: String) -> Result<(), StoreError>;
331
332    /// Returns all the keys from the `settings` table.
333    async fn list_setting_keys(&self) -> Result<Vec<String>, StoreError>;
334
335    // SYNC
336    // --------------------------------------------------------------------------------------------
337
338    /// Returns the note tag records that the client is interested in.
339    async fn get_note_tags(&self) -> Result<Vec<NoteTagRecord>, StoreError>;
340
341    /// Returns the unique note tags (without source) that the client is interested in.
342    async fn get_unique_note_tags(&self) -> Result<BTreeSet<NoteTag>, StoreError> {
343        Ok(self.get_note_tags().await?.into_iter().map(|r| r.tag).collect())
344    }
345
346    /// Adds a note tag to the list of tags that the client is interested in.
347    ///
348    /// If the tag was already being tracked, returns false since no new tags were actually added.
349    /// Otherwise true.
350    async fn add_note_tag(&self, tag: NoteTagRecord) -> Result<bool, StoreError>;
351
352    /// Removes a note tag from the list of tags that the client is interested in.
353    ///
354    /// If the tag wasn't present in the store returns false since no tag was actually removed.
355    /// Otherwise returns true.
356    async fn remove_note_tag(&self, tag: NoteTagRecord) -> Result<usize, StoreError>;
357
358    /// Returns the block number of the last state sync block.
359    async fn get_sync_height(&self) -> Result<BlockNumber, StoreError>;
360
361    /// Applies the state sync update to the store. An update involves:
362    ///
363    /// - Inserting the new block header to the store alongside new MMR peaks information.
364    /// - Updating the corresponding tracked input/output notes.
365    /// - Removing note tags that are no longer relevant.
366    /// - Updating transactions in the store, marking as `committed` or `discarded`.
367    ///   - In turn, validating private account's state transitions. If a private account's
368    ///     commitment locally does not match the `StateSyncUpdate` information, the account may be
369    ///     locked.
370    /// - Storing new MMR authentication nodes.
371    /// - Updating the tracked public accounts.
372    async fn apply_state_sync(&self, state_sync_update: StateSyncUpdate) -> Result<(), StoreError>;
373
374    // TRANSPORT
375    // --------------------------------------------------------------------------------------------
376
377    /// Gets the note transport cursor.
378    ///
379    /// This is used to reduce the number of fetched notes from the note transport network.
380    async fn get_note_transport_cursor(&self) -> Result<NoteTransportCursor, StoreError> {
381        let cursor_bytes = self
382            .get_setting(NOTE_TRANSPORT_CURSOR_STORE_SETTING.into())
383            .await?
384            .ok_or(StoreError::NoteTransportCursorNotFound)?;
385        let array: [u8; 8] = cursor_bytes
386            .as_slice()
387            .try_into()
388            .map_err(|e: core::array::TryFromSliceError| StoreError::ParsingError(e.to_string()))?;
389        let cursor = u64::from_be_bytes(array);
390        Ok(cursor.into())
391    }
392
393    /// Updates the note transport cursor.
394    ///
395    /// This is used to track the last cursor position when fetching notes from the note transport
396    /// network.
397    async fn update_note_transport_cursor(
398        &self,
399        cursor: NoteTransportCursor,
400    ) -> Result<(), StoreError> {
401        let cursor_bytes = cursor.value().to_be_bytes().to_vec();
402        self.set_setting(NOTE_TRANSPORT_CURSOR_STORE_SETTING.into(), cursor_bytes)
403            .await?;
404        Ok(())
405    }
406
407    /// Applies a note transport update
408    ///
409    /// An update involves:
410    /// - Insert fetched notes;
411    /// - Update pagination cursor used in note fetching.
412    async fn apply_note_transport_update(
413        &self,
414        note_transport_update: NoteTransportUpdate,
415    ) -> Result<(), StoreError> {
416        self.update_note_transport_cursor(note_transport_update.cursor).await?;
417        self.upsert_input_notes(&note_transport_update.note_updates).await?;
418        Ok(())
419    }
420
421    // PARTIAL MMR
422    // --------------------------------------------------------------------------------------------
423
424    /// Builds the current view of the chain's [`PartialMmr`]. Because we want to add all new
425    /// authentication nodes that could come from applying the MMR updates, we need to track all
426    /// known leaves thus far.
427    ///
428    /// The default implementation is based on [`Store::get_partial_blockchain_nodes`],
429    /// [`Store::get_partial_blockchain_peaks_by_block_num`] and [`Store::get_block_header_by_num`]
430    async fn get_current_partial_mmr(&self) -> Result<PartialMmr, StoreError> {
431        let current_block_num = self.get_sync_height().await?;
432
433        let tracked_nodes = self.get_partial_blockchain_nodes(PartialBlockchainFilter::All).await?;
434        let current_peaks =
435            self.get_partial_blockchain_peaks_by_block_num(current_block_num).await?;
436
437        // FIXME: Because each block stores the peaks for the MMR for the leaf of pos `block_num-1`,
438        // we can get an MMR based on those peaks, add the current block number and align it with
439        // the set of all nodes in the store.
440        // Otherwise, by doing `PartialMmr::from_parts` we would effectively have more nodes than
441        // we need for the passed peaks. The alternative here is to truncate the set of all nodes
442        // before calling `from_parts`
443        //
444        // This is a bit hacky but it works. One alternative would be to _just_ get nodes required
445        // for tracked blocks in the MMR. This would however block us from the convenience of
446        // just getting all nodes from the store.
447
448        let (current_block, has_client_notes) = self
449            .get_block_header_by_num(current_block_num)
450            .await?
451            .expect("Current block should be in the store");
452
453        let mut current_partial_mmr = PartialMmr::from_peaks(current_peaks);
454        let has_client_notes = has_client_notes.into();
455        current_partial_mmr.add(current_block.commitment(), has_client_notes);
456
457        let current_partial_mmr =
458            PartialMmr::from_parts(current_partial_mmr.peaks(), tracked_nodes, has_client_notes);
459
460        Ok(current_partial_mmr)
461    }
462
463    // ACCOUNT VAULT AND STORE
464    // --------------------------------------------------------------------------------------------
465
466    /// Retrieves the asset vault for a specific account.
467    async fn get_account_vault(&self, account_id: AccountId) -> Result<AssetVault, StoreError>;
468
469    /// Retrieves a specific asset from the account's vault along with its Merkle witness.
470    ///
471    /// The default implementation of this method uses [`Store::get_account_vault`].
472    async fn get_account_asset(
473        &self,
474        account_id: AccountId,
475        faucet_id_prefix: AccountIdPrefix,
476    ) -> Result<Option<(Asset, AssetWitness)>, StoreError> {
477        let vault = self.get_account_vault(account_id).await?;
478        let Some(asset) = vault.assets().find(|a| a.faucet_id_prefix() == faucet_id_prefix) else {
479            return Ok(None);
480        };
481
482        let witness = AssetWitness::new(vault.open(asset.vault_key()).into())?;
483
484        Ok(Some((asset, witness)))
485    }
486
487    /// Retrieves the storage for a specific account.
488    async fn get_account_storage(
489        &self,
490        account_id: AccountId,
491    ) -> Result<AccountStorage, StoreError>;
492
493    /// Retrieves a specific item from the account's storage map along with its Merkle proof.
494    ///
495    /// The default implementation of this method uses [`Store::get_account_storage`].
496    async fn get_account_map_item(
497        &self,
498        account_id: AccountId,
499        index: u8,
500        key: Word,
501    ) -> Result<(Word, StorageMapWitness), StoreError> {
502        let storage = self.get_account_storage(account_id).await?;
503        let Some(StorageSlot::Map(map)) = storage.slots().get(index as usize) else {
504            return Err(StoreError::AccountError(AccountError::StorageSlotNotMap(index)));
505        };
506
507        let value = map.get(&key);
508        let witness = map.open(&key);
509
510        Ok((value, witness))
511    }
512}
513
514// PARTIAL BLOCKCHAIN NODE FILTER
515// ================================================================================================
516
517/// Filters for searching specific MMR nodes.
518// TODO: Should there be filters for specific blocks instead of nodes?
519pub enum PartialBlockchainFilter {
520    /// Return all nodes.
521    All,
522    /// Filter by the specified in-order indices.
523    List(Vec<InOrderIndex>),
524}
525
526// TRANSACTION FILTERS
527// ================================================================================================
528
529/// Filters for narrowing the set of transactions returned by the client's store.
530#[derive(Debug, Clone)]
531pub enum TransactionFilter {
532    /// Return all transactions.
533    All,
534    /// Filter by transactions that haven't yet been committed to the blockchain as per the last
535    /// sync.
536    Uncommitted,
537    /// Return a list of the transaction that matches the provided [`TransactionId`]s.
538    Ids(Vec<TransactionId>),
539    /// Return a list of the expired transactions that were executed before the provided
540    /// [`BlockNumber`]. Transactions created after the provided block number are not
541    /// considered.
542    ///
543    /// A transaction is considered expired if is uncommitted and the transaction's block number
544    /// is less than the provided block number.
545    ExpiredBefore(BlockNumber),
546}
547
548// TRANSACTIONS FILTER HELPERS
549// ================================================================================================
550
551impl TransactionFilter {
552    /// Returns a [String] containing the query for this Filter.
553    pub fn to_query(&self) -> String {
554        const QUERY: &str = "SELECT tx.id, script.script, tx.details, tx.status \
555            FROM transactions AS tx LEFT JOIN transaction_scripts AS script ON tx.script_root = script.script_root";
556        match self {
557            TransactionFilter::All => QUERY.to_string(),
558            TransactionFilter::Uncommitted => format!(
559                "{QUERY} WHERE tx.status_variant IN ({}, {})",
560                TransactionStatusVariant::Pending as u8,
561                TransactionStatusVariant::Discarded as u8
562            ),
563            TransactionFilter::Ids(_) => {
564                // Use SQLite's array parameter binding
565                format!("{QUERY} WHERE tx.id IN rarray(?)")
566            },
567            TransactionFilter::ExpiredBefore(block_num) => {
568                format!(
569                    "{QUERY} WHERE tx.block_num < {} AND tx.status_variant != {} AND tx.status_variant != {}",
570                    block_num.as_u32(),
571                    TransactionStatusVariant::Discarded as u8,
572                    TransactionStatusVariant::Committed as u8
573                )
574            },
575        }
576    }
577}
578
579// NOTE FILTER
580// ================================================================================================
581
582/// Filters for narrowing the set of notes returned by the client's store.
583#[derive(Debug, Clone)]
584pub enum NoteFilter {
585    /// Return a list of all notes ([`InputNoteRecord`] or [`OutputNoteRecord`]).
586    All,
587    /// Return a list of committed notes ([`InputNoteRecord`] or [`OutputNoteRecord`]). These
588    /// represent notes that the blockchain has included in a block.
589    Committed,
590    /// Filter by consumed notes ([`InputNoteRecord`] or [`OutputNoteRecord`]). notes that have
591    /// been used as inputs in transactions.
592    Consumed,
593    /// Return a list of expected notes ([`InputNoteRecord`] or [`OutputNoteRecord`]). These
594    /// represent notes for which the store doesn't have anchor data.
595    Expected,
596    /// Return a list containing any notes that match with the provided [`NoteId`] vector.
597    List(Vec<NoteId>),
598    /// Return a list containing any notes that match the provided [`Nullifier`] vector.
599    Nullifiers(Vec<Nullifier>),
600    /// Return a list of notes that are currently being processed. This filter doesn't apply to
601    /// output notes.
602    Processing,
603    /// Return a list containing the note that matches with the provided [`NoteId`]. The query will
604    /// return an error if the note isn't found.
605    Unique(NoteId),
606    /// Return a list containing notes that haven't been nullified yet, this includes expected,
607    /// committed, processing and unverified notes.
608    Unspent,
609    /// Return a list containing notes with unverified inclusion proofs. This filter doesn't apply
610    /// to output notes.
611    Unverified,
612}
613
614// BLOCK RELEVANCE
615// ================================================================================================
616
617/// Expresses metadata about the block header.
618#[derive(Debug, Clone)]
619pub enum BlockRelevance {
620    /// The block header includes notes that the client may consume.
621    HasNotes,
622    /// The block header does not contain notes relevant to the client.
623    Irrelevant,
624}
625
626impl From<BlockRelevance> for bool {
627    fn from(val: BlockRelevance) -> Self {
628        match val {
629            BlockRelevance::HasNotes => true,
630            BlockRelevance::Irrelevant => false,
631        }
632    }
633}
634
635impl From<bool> for BlockRelevance {
636    fn from(has_notes: bool) -> Self {
637        if has_notes {
638            BlockRelevance::HasNotes
639        } else {
640            BlockRelevance::Irrelevant
641        }
642    }
643}