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(¬e_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}