1use std::collections::{BTreeMap, BTreeSet, HashSet};
7use std::ops::RangeInclusive;
8use std::path::Path;
9use std::sync::Arc;
10
11use miden_node_proto::domain::account::{
12 AccountDetailRequest,
13 AccountDetails,
14 AccountInfo,
15 AccountProofRequest,
16 AccountProofResponse,
17 AccountStorageDetails,
18 AccountStorageMapDetails,
19 AccountVaultDetails,
20 NetworkAccountPrefix,
21 StorageMapRequest,
22};
23use miden_node_proto::domain::batch::BatchInputs;
24use miden_node_utils::ErrorReport;
25use miden_node_utils::formatting::format_array;
26use miden_objects::account::{AccountHeader, AccountId, StorageSlot};
27use miden_objects::block::account_tree::{AccountTree, account_id_to_smt_key};
28use miden_objects::block::{
29 AccountWitness,
30 BlockHeader,
31 BlockInputs,
32 BlockNumber,
33 Blockchain,
34 NullifierTree,
35 NullifierWitness,
36 ProvenBlock,
37};
38use miden_objects::crypto::merkle::{
39 Forest,
40 LargeSmt,
41 MemoryStorage,
42 Mmr,
43 MmrDelta,
44 MmrPeaks,
45 MmrProof,
46 PartialMmr,
47 SmtProof,
48 SmtStorage,
49};
50use miden_objects::note::{NoteDetails, NoteId, NoteScript, Nullifier};
51use miden_objects::transaction::{OutputNote, PartialBlockchain};
52use miden_objects::utils::Serializable;
53use miden_objects::{AccountError, Word};
54use tokio::sync::{Mutex, RwLock, oneshot};
55use tracing::{Instrument, info, info_span, instrument};
56
57use crate::blocks::BlockStore;
58use crate::db::models::Page;
59use crate::db::models::queries::StorageMapValuesPage;
60use crate::db::{
61 AccountVaultValue,
62 Db,
63 NoteRecord,
64 NoteSyncUpdate,
65 NullifierInfo,
66 StateSyncUpdate,
67};
68use crate::errors::{
69 ApplyBlockError,
70 DatabaseError,
71 GetBatchInputsError,
72 GetBlockHeaderError,
73 GetBlockInputsError,
74 GetCurrentBlockchainDataError,
75 InvalidBlockError,
76 NoteSyncError,
77 StateInitializationError,
78 StateSyncError,
79};
80use crate::{AccountTreeWithHistory, COMPONENT, DataDirectory, InMemoryAccountTree};
81
82#[derive(Debug, Default)]
86pub struct TransactionInputs {
87 pub account_commitment: Word,
88 pub nullifiers: Vec<NullifierInfo>,
89 pub found_unauthenticated_notes: HashSet<Word>,
90 pub new_account_id_prefix_is_unique: Option<bool>,
91}
92
93struct InnerState<S = MemoryStorage>
95where
96 S: SmtStorage,
97{
98 nullifier_tree: NullifierTree,
99 blockchain: Blockchain,
100 account_tree: AccountTreeWithHistory<AccountTree<LargeSmt<S>>>,
101}
102
103impl<S> InnerState<S>
104where
105 S: SmtStorage,
106{
107 fn latest_block_num(&self) -> BlockNumber {
109 self.blockchain
110 .chain_tip()
111 .expect("chain should always have at least the genesis block")
112 }
113}
114
115pub struct State {
117 db: Arc<Db>,
120
121 block_store: Arc<BlockStore>,
123
124 inner: RwLock<InnerState>,
128
129 writer: Mutex<()>,
132}
133
134impl State {
135 #[instrument(target = COMPONENT, skip_all)]
137 pub async fn load(data_path: &Path) -> Result<Self, StateInitializationError> {
138 let data_directory = DataDirectory::load(data_path.to_path_buf())
139 .map_err(StateInitializationError::DataDirectoryLoadError)?;
140
141 let block_store = Arc::new(
142 BlockStore::load(data_directory.block_store_dir())
143 .map_err(StateInitializationError::BlockStoreLoadError)?,
144 );
145
146 let database_filepath = data_directory.database_path();
147 let mut db = Db::load(database_filepath.clone())
148 .await
149 .map_err(StateInitializationError::DatabaseLoadError)?;
150
151 let chain_mmr = load_mmr(&mut db).await?;
152 let block_headers = db.select_all_block_headers().await?;
153 let latest_block_num = block_headers
159 .last()
160 .map_or(BlockNumber::GENESIS, miden_objects::block::BlockHeader::block_num);
161 let account_tree = load_account_tree(&mut db, latest_block_num).await?;
162 let nullifier_tree = load_nullifier_tree(&mut db).await?;
163
164 let inner = RwLock::new(InnerState {
165 nullifier_tree,
166 blockchain: Blockchain::from_mmr_unchecked(chain_mmr),
169 account_tree,
170 });
171
172 let writer = Mutex::new(());
173 let db = Arc::new(db);
174
175 Ok(Self { db, block_store, inner, writer })
176 }
177
178 #[allow(clippy::too_many_lines)]
203 #[instrument(target = COMPONENT, skip_all, err)]
204 pub async fn apply_block(&self, block: ProvenBlock) -> Result<(), ApplyBlockError> {
205 let _lock = self.writer.try_lock().map_err(|_| ApplyBlockError::ConcurrentWrite)?;
206
207 let header = block.header();
208
209 let tx_commitment = block.transactions().commitment();
210
211 if header.tx_commitment() != tx_commitment {
212 return Err(InvalidBlockError::InvalidBlockTxCommitment {
213 expected: tx_commitment,
214 actual: header.tx_commitment(),
215 }
216 .into());
217 }
218
219 let block_num = header.block_num();
220 let block_commitment = block.commitment();
221
222 let prev_block = self
224 .db
225 .select_block_header_by_block_num(None)
226 .await?
227 .ok_or(ApplyBlockError::DbBlockHeaderEmpty)?;
228
229 let expected_block_num = prev_block.block_num().child();
230 if block_num != expected_block_num {
231 return Err(InvalidBlockError::NewBlockInvalidBlockNum {
232 expected: expected_block_num,
233 submitted: block_num,
234 }
235 .into());
236 }
237 if header.prev_block_commitment() != prev_block.commitment() {
238 return Err(InvalidBlockError::NewBlockInvalidPrevCommitment.into());
239 }
240
241 let block_data = block.to_bytes();
242
243 let store = Arc::clone(&self.block_store);
249 let block_save_task = tokio::spawn(
250 async move { store.save_block(block_num, &block_data).await }.in_current_span(),
251 );
252
253 let (
256 nullifier_tree_old_root,
257 nullifier_tree_update,
258 account_tree_old_root,
259 account_tree_update,
260 ) = {
261 let inner = self.inner.read().await;
262
263 let _span = info_span!(target: COMPONENT, "update_in_memory_structs").entered();
264
265 let duplicate_nullifiers: Vec<_> = block
267 .created_nullifiers()
268 .iter()
269 .filter(|&n| inner.nullifier_tree.get_block_num(n).is_some())
270 .copied()
271 .collect();
272 if !duplicate_nullifiers.is_empty() {
273 return Err(InvalidBlockError::DuplicatedNullifiers(duplicate_nullifiers).into());
274 }
275
276 let peaks = inner.blockchain.peaks();
280 if peaks.hash_peaks() != header.chain_commitment() {
281 return Err(InvalidBlockError::NewBlockInvalidChainCommitment.into());
282 }
283
284 let nullifier_tree_update = inner
286 .nullifier_tree
287 .compute_mutations(
288 block.created_nullifiers().iter().map(|nullifier| (*nullifier, block_num)),
289 )
290 .map_err(InvalidBlockError::NewBlockNullifierAlreadySpent)?;
291
292 if nullifier_tree_update.as_mutation_set().root() != header.nullifier_root() {
293 return Err(InvalidBlockError::NewBlockInvalidNullifierRoot.into());
294 }
295
296 let account_tree_update = inner
298 .account_tree
299 .compute_mutations(
300 block
301 .updated_accounts()
302 .iter()
303 .map(|update| (update.account_id(), update.final_state_commitment())),
304 )
305 .map_err(|e| match e {
306 crate::HistoricalError::AccountTreeError(err) => {
307 InvalidBlockError::NewBlockDuplicateAccountIdPrefix(err)
308 },
309 crate::HistoricalError::MerkleError(_) => {
310 panic!("Unexpected MerkleError during account tree mutation computation")
311 },
312 })?;
313
314 if account_tree_update.as_mutation_set().root() != header.account_root() {
315 return Err(InvalidBlockError::NewBlockInvalidAccountRoot.into());
316 }
317
318 (
319 inner.nullifier_tree.root(),
320 nullifier_tree_update,
321 inner.account_tree.root_latest(),
322 account_tree_update,
323 )
324 };
325
326 let note_tree = block.build_output_note_tree();
328 if note_tree.root() != header.note_root() {
329 return Err(InvalidBlockError::NewBlockInvalidNoteRoot.into());
330 }
331
332 let notes = block
333 .output_notes()
334 .map(|(note_index, note)| {
335 let (details, nullifier) = match note {
336 OutputNote::Full(note) => {
337 (Some(NoteDetails::from(note)), Some(note.nullifier()))
338 },
339 OutputNote::Header(_) => (None, None),
340 note @ OutputNote::Partial(_) => {
341 return Err(InvalidBlockError::InvalidOutputNoteType(Box::new(
342 note.clone(),
343 )));
344 },
345 };
346
347 let inclusion_path = note_tree.open(note_index);
348
349 let note_record = NoteRecord {
350 block_num,
351 note_index,
352 note_id: note.id().into(),
353 note_commitment: note.commitment(),
354 metadata: *note.metadata(),
355 details,
356 inclusion_path,
357 };
358
359 Ok((note_record, nullifier))
360 })
361 .collect::<Result<Vec<_>, InvalidBlockError>>()?;
362
363 let (allow_acquire, acquired_allowed) = oneshot::channel::<()>();
365 let (inform_acquire_done, acquire_done) = oneshot::channel::<()>();
367
368 let db = Arc::clone(&self.db);
373 let db_update_task = tokio::spawn(
374 async move { db.apply_block(allow_acquire, acquire_done, block, notes).await }
375 .in_current_span(),
376 );
377
378 acquired_allowed.await.map_err(ApplyBlockError::ClosedChannel)?;
380
381 block_save_task.await??;
383
384 async move {
386 let mut inner = self.inner.write().await;
390
391 if inner.nullifier_tree.root() != nullifier_tree_old_root
396 || inner.account_tree.root_latest() != account_tree_old_root
397 {
398 return Err(ApplyBlockError::ConcurrentWrite);
399 }
400
401 inform_acquire_done
404 .send(())
405 .map_err(|_| ApplyBlockError::DbUpdateTaskFailed("Receiver was dropped".into()))?;
406
407 db_update_task
412 .await?
413 .map_err(|err| ApplyBlockError::DbUpdateTaskFailed(err.as_report()))?;
414
415 inner
417 .nullifier_tree
418 .apply_mutations(nullifier_tree_update)
419 .expect("Unreachable: old nullifier tree root must be checked before this step");
420 inner
421 .account_tree
422 .apply_mutations(account_tree_update)
423 .expect("Unreachable: old account tree root must be checked before this step");
424 inner.blockchain.push(block_commitment);
425
426 Ok(())
427 }
428 .instrument(info_span!("update trees"))
429 .await
430 }
431
432 #[instrument(level = "debug", target = COMPONENT, skip_all, ret(level = "debug"), err)]
437 pub async fn get_block_header(
438 &self,
439 block_num: Option<BlockNumber>,
440 include_mmr_proof: bool,
441 ) -> Result<(Option<BlockHeader>, Option<MmrProof>), GetBlockHeaderError> {
442 let block_header = self.db.select_block_header_by_block_num(block_num).await?;
443 if let Some(header) = block_header {
444 let mmr_proof = if include_mmr_proof {
445 let inner = self.inner.read().await;
446 let mmr_proof = inner.blockchain.open(header.block_num())?;
447 Some(mmr_proof)
448 } else {
449 None
450 };
451 Ok((Some(header), mmr_proof))
452 } else {
453 Ok((None, None))
454 }
455 }
456
457 pub async fn sync_nullifiers(
458 &self,
459 prefix_len: u32,
460 nullifier_prefixes: Vec<u32>,
461 block_range: RangeInclusive<BlockNumber>,
462 ) -> Result<(Vec<NullifierInfo>, BlockNumber), DatabaseError> {
463 self.db
464 .select_nullifiers_by_prefix(prefix_len, nullifier_prefixes, block_range)
465 .await
466 }
467
468 #[instrument(level = "debug", target = COMPONENT, skip_all, ret)]
473 pub async fn check_nullifiers(&self, nullifiers: &[Nullifier]) -> Vec<SmtProof> {
474 let inner = self.inner.read().await;
475 nullifiers
476 .iter()
477 .map(|n| inner.nullifier_tree.open(n))
478 .map(NullifierWitness::into_proof)
479 .collect()
480 }
481
482 pub async fn get_notes_by_id(
487 &self,
488 note_ids: Vec<NoteId>,
489 ) -> Result<Vec<NoteRecord>, DatabaseError> {
490 self.db.select_notes_by_id(note_ids).await
491 }
492
493 pub async fn get_current_blockchain_data(
496 &self,
497 block_num: Option<BlockNumber>,
498 ) -> Result<Option<(BlockHeader, MmrPeaks)>, GetCurrentBlockchainDataError> {
499 let blockchain = &self.inner.read().await.blockchain;
500 if let Some(number) = block_num
501 && number == self.latest_block_num().await
502 {
503 return Ok(None);
504 }
505
506 let block_header: BlockHeader = self
509 .db
510 .select_block_header_by_block_num(None)
511 .await
512 .map_err(GetCurrentBlockchainDataError::ErrorRetrievingBlockHeader)?
513 .unwrap();
514 let peaks = blockchain
515 .peaks_at(block_header.block_num())
516 .map_err(GetCurrentBlockchainDataError::InvalidPeaks)?;
517
518 Ok(Some((block_header, peaks)))
519 }
520
521 pub async fn get_batch_inputs(
540 &self,
541 tx_reference_blocks: BTreeSet<BlockNumber>,
542 unauthenticated_note_commitments: BTreeSet<Word>,
543 ) -> Result<BatchInputs, GetBatchInputsError> {
544 if tx_reference_blocks.is_empty() {
545 return Err(GetBatchInputsError::TransactionBlockReferencesEmpty);
546 }
547
548 let note_proofs = self
552 .db
553 .select_note_inclusion_proofs(unauthenticated_note_commitments)
554 .await
555 .map_err(GetBatchInputsError::SelectNoteInclusionProofError)?;
556
557 let note_blocks = note_proofs.values().map(|proof| proof.location().block_num());
559
560 let mut blocks: BTreeSet<BlockNumber> = tx_reference_blocks;
564 blocks.extend(note_blocks);
565
566 let (batch_reference_block, partial_mmr) = {
569 let inner_state = self.inner.read().await;
570
571 let latest_block_num = inner_state.latest_block_num();
572
573 let highest_block_num =
574 *blocks.last().expect("we should have checked for empty block references");
575 if highest_block_num > latest_block_num {
576 return Err(GetBatchInputsError::UnknownTransactionBlockReference {
577 highest_block_num,
578 latest_block_num,
579 });
580 }
581
582 blocks.remove(&latest_block_num);
586
587 let partial_mmr = inner_state
595 .blockchain
596 .partial_mmr_from_blocks(&blocks, latest_block_num)
597 .expect("latest block num should exist and all blocks in set should be < than latest block");
598
599 (latest_block_num, partial_mmr)
600 };
601
602 let mut headers = self
605 .db
606 .select_block_headers(blocks.into_iter().chain(std::iter::once(batch_reference_block)))
607 .await
608 .map_err(GetBatchInputsError::SelectBlockHeaderError)?;
609
610 let header_index = headers
612 .iter()
613 .enumerate()
614 .find_map(|(index, header)| {
615 (header.block_num() == batch_reference_block).then_some(index)
616 })
617 .expect("DB should have returned the header of the batch reference block");
618
619 let batch_reference_block_header = headers.swap_remove(header_index);
621
622 let partial_block_chain = PartialBlockchain::new_unchecked(partial_mmr, headers)
631 .expect("partial mmr and block headers should be consistent");
632
633 Ok(BatchInputs {
634 batch_reference_block_header,
635 note_proofs,
636 partial_block_chain,
637 })
638 }
639
640 #[instrument(level = "debug", target = COMPONENT, skip_all, ret(level = "debug"), err)]
654 pub async fn sync_state(
655 &self,
656 block_num: BlockNumber,
657 account_ids: Vec<AccountId>,
658 note_tags: Vec<u32>,
659 ) -> Result<(StateSyncUpdate, MmrDelta), StateSyncError> {
660 let inner = self.inner.read().await;
661
662 let state_sync = self.db.get_state_sync(block_num, account_ids, note_tags).await?;
663
664 let delta = if block_num == state_sync.block_header.block_num() {
665 MmrDelta {
667 forest: Forest::new(block_num.as_usize()),
668 data: vec![],
669 }
670 } else {
671 let from_forest = (block_num + 1).as_usize();
681 let to_forest = state_sync.block_header.block_num().as_usize();
682 inner
683 .blockchain
684 .as_mmr()
685 .get_delta(Forest::new(from_forest), Forest::new(to_forest))
686 .map_err(StateSyncError::FailedToBuildMmrDelta)?
687 };
688
689 Ok((state_sync, delta))
690 }
691
692 #[instrument(level = "debug", target = COMPONENT, skip_all, ret(level = "debug"), err)]
704 pub async fn sync_notes(
705 &self,
706 note_tags: Vec<u32>,
707 block_range: RangeInclusive<BlockNumber>,
708 ) -> Result<(NoteSyncUpdate, MmrProof, BlockNumber), NoteSyncError> {
709 let inner = self.inner.read().await;
710
711 let (note_sync, last_included_block) =
712 self.db.get_note_sync(block_range, note_tags).await?;
713
714 let mmr_proof = inner.blockchain.open(note_sync.block_header.block_num())?;
715
716 Ok((note_sync, mmr_proof, last_included_block))
717 }
718
719 pub async fn get_block_inputs(
721 &self,
722 account_ids: Vec<AccountId>,
723 nullifiers: Vec<Nullifier>,
724 unauthenticated_note_commitments: BTreeSet<Word>,
725 reference_blocks: BTreeSet<BlockNumber>,
726 ) -> Result<BlockInputs, GetBlockInputsError> {
727 let unauthenticated_note_proofs = self
731 .db
732 .select_note_inclusion_proofs(unauthenticated_note_commitments)
733 .await
734 .map_err(GetBlockInputsError::SelectNoteInclusionProofError)?;
735
736 let note_proof_reference_blocks =
738 unauthenticated_note_proofs.values().map(|proof| proof.location().block_num());
739
740 let mut blocks = reference_blocks;
742 blocks.extend(note_proof_reference_blocks);
743
744 let (latest_block_number, account_witnesses, nullifier_witnesses, partial_mmr) =
745 self.get_block_inputs_witnesses(&mut blocks, account_ids, nullifiers).await?;
746
747 let mut headers = self
750 .db
751 .select_block_headers(blocks.into_iter().chain(std::iter::once(latest_block_number)))
752 .await
753 .map_err(GetBlockInputsError::SelectBlockHeaderError)?;
754
755 let latest_block_header_index = headers
758 .iter()
759 .enumerate()
760 .find_map(|(index, header)| {
761 (header.block_num() == latest_block_number).then_some(index)
762 })
763 .expect("DB should have returned the header of the latest block header");
764
765 let latest_block_header = headers.swap_remove(latest_block_header_index);
767
768 let partial_block_chain = PartialBlockchain::new_unchecked(partial_mmr, headers)
777 .expect("partial mmr and block headers should be consistent");
778
779 Ok(BlockInputs::new(
780 latest_block_header,
781 partial_block_chain,
782 account_witnesses,
783 nullifier_witnesses,
784 unauthenticated_note_proofs,
785 ))
786 }
787
788 async fn get_block_inputs_witnesses(
795 &self,
796 blocks: &mut BTreeSet<BlockNumber>,
797 account_ids: Vec<AccountId>,
798 nullifiers: Vec<Nullifier>,
799 ) -> Result<
800 (
801 BlockNumber,
802 BTreeMap<AccountId, AccountWitness>,
803 BTreeMap<Nullifier, NullifierWitness>,
804 PartialMmr,
805 ),
806 GetBlockInputsError,
807 > {
808 let inner = self.inner.read().await;
809
810 let latest_block_number = inner.latest_block_num();
811
812 let highest_block_number = blocks.last().copied().unwrap_or(latest_block_number);
814 if highest_block_number > latest_block_number {
815 return Err(GetBlockInputsError::UnknownBatchBlockReference {
816 highest_block_number,
817 latest_block_number,
818 });
819 }
820
821 blocks.remove(&latest_block_number);
824
825 let partial_mmr =
836 inner.blockchain.partial_mmr_from_blocks(blocks, latest_block_number).expect(
837 "latest block num should exist and all blocks in set should be < than latest block",
838 );
839
840 let account_witnesses = account_ids
842 .iter()
843 .copied()
844 .map(|account_id| (account_id, inner.account_tree.open_latest(account_id)))
845 .collect::<BTreeMap<AccountId, AccountWitness>>();
846
847 let nullifier_witnesses: BTreeMap<Nullifier, NullifierWitness> = nullifiers
850 .iter()
851 .copied()
852 .map(|nullifier| (nullifier, inner.nullifier_tree.open(&nullifier)))
853 .collect();
854
855 Ok((latest_block_number, account_witnesses, nullifier_witnesses, partial_mmr))
856 }
857
858 #[instrument(target = COMPONENT, skip_all, ret)]
860 pub async fn get_transaction_inputs(
861 &self,
862 account_id: AccountId,
863 nullifiers: &[Nullifier],
864 unauthenticated_note_commitments: Vec<Word>,
865 ) -> Result<TransactionInputs, DatabaseError> {
866 info!(target: COMPONENT, account_id = %account_id.to_string(), nullifiers = %format_array(nullifiers));
867
868 let inner = self.inner.read().await;
869
870 let account_commitment = inner.account_tree.get_latest_commitment(account_id);
871
872 let new_account_id_prefix_is_unique = if account_commitment.is_empty() {
873 Some(!inner.account_tree.contains_account_id_prefix_in_latest(account_id.prefix()))
874 } else {
875 None
876 };
877
878 if let Some(false) = new_account_id_prefix_is_unique {
880 return Ok(TransactionInputs {
881 new_account_id_prefix_is_unique,
882 ..Default::default()
883 });
884 }
885
886 let nullifiers = nullifiers
887 .iter()
888 .map(|nullifier| NullifierInfo {
889 nullifier: *nullifier,
890 block_num: inner.nullifier_tree.get_block_num(nullifier).unwrap_or_default(),
891 })
892 .collect();
893
894 let found_unauthenticated_notes = self
895 .db
896 .select_notes_by_commitment(unauthenticated_note_commitments)
897 .await?
898 .into_iter()
899 .map(|note| note.note_commitment)
900 .collect();
901
902 Ok(TransactionInputs {
903 account_commitment,
904 nullifiers,
905 found_unauthenticated_notes,
906 new_account_id_prefix_is_unique,
907 })
908 }
909
910 pub async fn get_account_details(&self, id: AccountId) -> Result<AccountInfo, DatabaseError> {
912 self.db.select_account(id).await
913 }
914
915 pub async fn get_network_account_details_by_prefix(
917 &self,
918 id_prefix: u32,
919 ) -> Result<Option<AccountInfo>, DatabaseError> {
920 self.db.select_network_account_by_prefix(id_prefix).await
921 }
922
923 #[allow(clippy::too_many_lines)]
929 pub async fn get_account_proof(
930 &self,
931 account_request: AccountProofRequest,
932 ) -> Result<AccountProofResponse, DatabaseError> {
933 let AccountProofRequest { block_num, account_id, details } = account_request;
934 let _ = block_num.ok_or_else(|| {
935 DatabaseError::NotImplemented(
936 "Handling of historical/past block numbers is not implemented yet".to_owned(),
937 )
938 });
939
940 let inner_state = self.inner.read().await;
944
945 let block_num = inner_state.account_tree.block_number_latest();
946 let witness = inner_state.account_tree.open_latest(account_id);
947
948 let account_details = if let Some(AccountDetailRequest {
949 code_commitment,
950 asset_vault_commitment,
951 storage_requests,
952 }) = details
953 {
954 let account_info = self.db.select_account(account_id).await?;
955
956 let Some(account) = account_info.details else {
958 return Err(DatabaseError::AccountNotPublic(account_id));
959 };
960
961 let storage_header = account.storage().to_header();
962
963 let mut storage_map_details =
964 Vec::<AccountStorageMapDetails>::with_capacity(storage_requests.len());
965
966 for StorageMapRequest { slot_index, slot_data } in storage_requests {
967 let Some(StorageSlot::Map(storage_map)) =
968 account.storage().slots().get(slot_index as usize)
969 else {
970 return Err(AccountError::StorageSlotNotMap(slot_index).into());
971 };
972 let details = AccountStorageMapDetails::new(slot_index, slot_data, storage_map);
973 storage_map_details.push(details);
974 }
975
976 let account_code = code_commitment
979 .is_some_and(|code_commitment| code_commitment != account.code().commitment())
980 .then(|| account.code().to_bytes());
981
982 let storage_details = AccountStorageDetails {
984 header: storage_header,
985 map_details: storage_map_details,
986 };
987
988 let vault_details = match asset_vault_commitment {
995 Some(commitment) if commitment == account.vault().root() => {
996 AccountVaultDetails::empty()
998 },
999 Some(_) => {
1000 AccountVaultDetails::new(account.vault())
1002 },
1003 None => {
1004 AccountVaultDetails::empty()
1006 },
1007 };
1008
1009 Some(AccountDetails {
1010 account_header: AccountHeader::from(account),
1011 account_code,
1012 vault_details,
1013 storage_details,
1014 })
1015 } else {
1016 None
1017 };
1018
1019 let response = AccountProofResponse {
1020 block_num,
1021 witness,
1022 details: account_details,
1023 };
1024
1025 Ok(response)
1026 }
1027
1028 pub(crate) async fn get_storage_map_sync_values(
1030 &self,
1031 account_id: AccountId,
1032 block_range: RangeInclusive<BlockNumber>,
1033 ) -> Result<StorageMapValuesPage, DatabaseError> {
1034 self.db.select_storage_map_sync_values(account_id, block_range).await
1035 }
1036
1037 pub async fn load_block(
1039 &self,
1040 block_num: BlockNumber,
1041 ) -> Result<Option<Vec<u8>>, DatabaseError> {
1042 if block_num > self.latest_block_num().await {
1043 return Ok(None);
1044 }
1045 self.block_store.load_block(block_num).await.map_err(Into::into)
1046 }
1047
1048 pub async fn latest_block_num(&self) -> BlockNumber {
1050 self.inner.read().await.latest_block_num()
1051 }
1052
1053 pub async fn optimize_db(&self) -> Result<(), DatabaseError> {
1055 self.db.optimize().await
1056 }
1057
1058 pub async fn analyze_table_sizes(&self) -> Result<(), DatabaseError> {
1060 self.db.analyze_table_sizes().await
1061 }
1062
1063 pub async fn sync_account_vault(
1065 &self,
1066 account_id: AccountId,
1067 block_range: RangeInclusive<BlockNumber>,
1068 ) -> Result<(BlockNumber, Vec<AccountVaultValue>), DatabaseError> {
1069 self.db.get_account_vault_sync(account_id, block_range).await
1070 }
1071
1072 pub async fn get_unconsumed_network_notes(
1074 &self,
1075 page: Page,
1076 ) -> Result<(Vec<NoteRecord>, Page), DatabaseError> {
1077 self.db.select_unconsumed_network_notes(page).await
1078 }
1079
1080 pub async fn get_unconsumed_network_notes_for_account(
1083 &self,
1084 network_account_id_prefix: NetworkAccountPrefix,
1085 block_num: BlockNumber,
1086 page: Page,
1087 ) -> Result<(Vec<NoteRecord>, Page), DatabaseError> {
1088 self.db
1089 .select_unconsumed_network_notes_for_account(network_account_id_prefix, block_num, page)
1090 .await
1091 }
1092
1093 pub async fn get_note_script_by_root(
1095 &self,
1096 root: Word,
1097 ) -> Result<Option<NoteScript>, DatabaseError> {
1098 self.db.select_note_script_by_root(root).await
1099 }
1100
1101 pub async fn sync_transactions(
1104 &self,
1105 account_ids: Vec<AccountId>,
1106 block_range: RangeInclusive<BlockNumber>,
1107 ) -> Result<(BlockNumber, Vec<crate::db::TransactionRecord>), DatabaseError> {
1108 self.db.select_transactions_records(account_ids, block_range).await
1109 }
1110}
1111
1112#[instrument(level = "info", target = COMPONENT, skip_all)]
1116async fn load_nullifier_tree(db: &mut Db) -> Result<NullifierTree, StateInitializationError> {
1117 let nullifiers = db.select_all_nullifiers().await?;
1118
1119 NullifierTree::with_entries(nullifiers.into_iter().map(|info| (info.nullifier, info.block_num)))
1120 .map_err(StateInitializationError::FailedToCreateNullifierTree)
1121}
1122
1123#[instrument(level = "info", target = COMPONENT, skip_all)]
1124async fn load_mmr(db: &mut Db) -> Result<Mmr, StateInitializationError> {
1125 let block_commitments: Vec<Word> = db
1126 .select_all_block_headers()
1127 .await?
1128 .iter()
1129 .map(BlockHeader::commitment)
1130 .collect();
1131
1132 Ok(block_commitments.into())
1133}
1134
1135#[instrument(level = "info", target = COMPONENT, skip_all)]
1136async fn load_account_tree(
1137 db: &mut Db,
1138 block_number: BlockNumber,
1139) -> Result<AccountTreeWithHistory<InMemoryAccountTree>, StateInitializationError> {
1140 let account_data = db.select_all_account_commitments().await?.into_iter().collect::<Vec<_>>();
1141
1142 let smt_entries = account_data
1144 .into_iter()
1145 .map(|(id, commitment)| (account_id_to_smt_key(id), commitment));
1146
1147 let smt = LargeSmt::with_entries(MemoryStorage::default(), smt_entries)
1148 .expect("Failed to create LargeSmt from database account data");
1149
1150 let account_tree = AccountTree::new(smt).expect("Failed to create AccountTree");
1151 Ok(AccountTreeWithHistory::new(account_tree, block_number))
1152}