Skip to main content

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