1use std::collections::BTreeMap;
4use std::rc::Rc;
5use std::string::{String, ToString};
6use std::sync::{Arc, RwLock};
7use std::vec::Vec;
8
9use miden_client::account::{
10 Account,
11 AccountCode,
12 AccountDelta,
13 AccountHeader,
14 AccountId,
15 AccountIdPrefix,
16 AccountStorage,
17 Address,
18 PartialAccount,
19 PartialStorage,
20 PartialStorageMap,
21 StorageMap,
22 StorageSlotName,
23 StorageSlotType,
24};
25use miden_client::asset::{Asset, AssetVault, AssetWitness, FungibleAsset};
26use miden_client::store::{
27 AccountRecord,
28 AccountRecordData,
29 AccountStatus,
30 AccountStorageFilter,
31 StoreError,
32};
33use miden_client::sync::NoteTagRecord;
34use miden_client::utils::Serializable;
35use miden_client::{AccountError, Word};
36use miden_protocol::account::{AccountStorageHeader, StorageMapWitness, StorageSlotHeader};
37use miden_protocol::asset::{AssetVaultKey, PartialVault};
38use miden_protocol::crypto::merkle::MerkleError;
39use rusqlite::types::Value;
40use rusqlite::{Connection, Transaction, named_params, params};
41
42use crate::account::helpers::{
43 SerializedHeaderData,
44 parse_accounts,
45 query_account_addresses,
46 query_account_code,
47 query_account_headers,
48 query_storage_maps,
49 query_storage_slots,
50 query_storage_values,
51 query_vault_assets,
52};
53use crate::smt_forest::AccountSmtForest;
54use crate::sql_error::SqlResultExt;
55use crate::sync::{add_note_tag_tx, remove_note_tag_tx};
56use crate::{SqliteStore, column_value_as_u64, insert_sql, subst, u64_to_value};
57
58impl SqliteStore {
59 pub(crate) fn get_account_ids(conn: &mut Connection) -> Result<Vec<AccountId>, StoreError> {
63 const QUERY: &str = "SELECT id FROM tracked_accounts";
64
65 conn.prepare_cached(QUERY)
66 .into_store_error()?
67 .query_map([], |row| row.get(0))
68 .expect("no binding parameters used in query")
69 .map(|result| {
70 let id: String = result.map_err(|e| StoreError::ParsingError(e.to_string()))?;
71 Ok(AccountId::from_hex(&id).expect("account id is valid"))
72 })
73 .collect::<Result<Vec<AccountId>, StoreError>>()
74 }
75
76 pub(crate) fn get_account_headers(
77 conn: &mut Connection,
78 ) -> Result<Vec<(AccountHeader, AccountStatus)>, StoreError> {
79 const QUERY: &str = "
80 SELECT
81 a.id,
82 a.nonce,
83 a.vault_root,
84 a.storage_commitment,
85 a.code_commitment,
86 a.account_seed,
87 a.locked
88 FROM accounts AS a
89 JOIN (
90 SELECT id, MAX(nonce) AS nonce
91 FROM accounts
92 GROUP BY id
93 ) AS latest
94 ON a.id = latest.id
95 AND a.nonce = latest.nonce
96 ORDER BY a.id;
97 ";
98
99 conn.prepare_cached(QUERY)
100 .into_store_error()?
101 .query_map(params![], |row| {
102 let id: String = row.get(0)?;
103 let nonce: u64 = column_value_as_u64(row, 1)?;
104 let vault_root: String = row.get(2)?;
105 let storage_commitment: String = row.get(3)?;
106 let code_commitment: String = row.get(4)?;
107 let account_seed: Option<Vec<u8>> = row.get(5)?;
108 let locked: bool = row.get(6)?;
109
110 Ok(SerializedHeaderData {
111 id,
112 nonce,
113 vault_root,
114 storage_commitment,
115 code_commitment,
116 account_seed,
117 locked,
118 })
119 })
120 .into_store_error()?
121 .map(|result| parse_accounts(result.into_store_error()?))
122 .collect::<Result<Vec<(AccountHeader, AccountStatus)>, StoreError>>()
123 }
124
125 pub(crate) fn get_account_header(
126 conn: &mut Connection,
127 account_id: AccountId,
128 ) -> Result<Option<(AccountHeader, AccountStatus)>, StoreError> {
129 Ok(query_account_headers(
130 conn,
131 "id = ? ORDER BY nonce DESC LIMIT 1",
132 params![account_id.to_hex()],
133 )?
134 .pop())
135 }
136
137 pub(crate) fn get_account_header_by_commitment(
138 conn: &mut Connection,
139 account_commitment: Word,
140 ) -> Result<Option<AccountHeader>, StoreError> {
141 let account_commitment_str: String = account_commitment.to_string();
142 Ok(
143 query_account_headers(conn, "account_commitment = ?", params![account_commitment_str])?
144 .pop()
145 .map(|(header, _)| header),
146 )
147 }
148
149 pub(crate) fn get_account(
151 conn: &mut Connection,
152 account_id: AccountId,
153 ) -> Result<Option<AccountRecord>, StoreError> {
154 let Some((header, status)) = Self::get_account_header(conn, account_id)? else {
155 return Ok(None);
156 };
157
158 let assets = query_vault_assets(conn, "root = ?", params![header.vault_root().to_hex()])?;
159 let vault = AssetVault::new(&assets)?;
160
161 let slots = query_storage_slots(
162 conn,
163 "commitment = ?",
164 params![header.storage_commitment().to_hex()],
165 )?
166 .into_values()
167 .collect();
168
169 let storage = AccountStorage::new(slots)?;
170
171 let Some(account_code) = query_account_code(conn, header.code_commitment())? else {
172 return Ok(None);
173 };
174
175 let account = Account::new_unchecked(
176 header.id(),
177 vault,
178 storage,
179 account_code,
180 header.nonce(),
181 status.seed().copied(),
182 );
183
184 let addresses = query_account_addresses(conn, header.id())?;
185 let account_data = AccountRecordData::Full(account);
186 Ok(Some(AccountRecord::new(account_data, status, addresses)))
187 }
188
189 pub(crate) fn get_minimal_partial_account(
191 conn: &mut Connection,
192 smt_forest: &Arc<RwLock<AccountSmtForest>>,
193 account_id: AccountId,
194 ) -> Result<Option<AccountRecord>, StoreError> {
195 let Some((header, status)) = Self::get_account_header(conn, account_id)? else {
196 return Ok(None);
197 };
198
199 let partial_vault = PartialVault::new(header.vault_root());
201
202 let mut storage_header = Vec::new();
204 let mut maps = vec![];
205
206 let storage_values = query_storage_values(
207 conn,
208 "commitment = ?",
209 params![header.storage_commitment().to_hex()],
210 )?;
211
212 let map_roots: Vec<Value> = storage_values
214 .iter()
215 .filter(|(_, (slot_type, _))| *slot_type == StorageSlotType::Map)
216 .map(|(_, (_, value))| Value::from(value.to_hex()))
217 .collect();
218
219 let mut all_storage_maps = if map_roots.is_empty() {
221 BTreeMap::new()
222 } else {
223 query_storage_maps(conn, "root IN rarray(?)", [Rc::new(map_roots)])?
224 };
225
226 for (slot_name, (slot_type, value)) in storage_values {
227 storage_header.push(StorageSlotHeader::new(slot_name.clone(), slot_type, value));
228 if slot_type == StorageSlotType::Map {
229 let mut partial_storage_map = PartialStorageMap::new(value);
230
231 if let Some(map) = all_storage_maps.remove(&value) {
232 let smt_forest = smt_forest.read().expect("smt_forest read lock not poisoned");
233 for (k, _v) in map.entries() {
234 let witness = smt_forest.get_storage_map_item_witness(value, *k)?;
235 partial_storage_map.add(witness).map_err(StoreError::MerkleStoreError)?;
236 }
237 }
238
239 maps.push(partial_storage_map);
240 }
241 }
242 storage_header.sort_by_key(StorageSlotHeader::id);
243 let storage_header =
244 AccountStorageHeader::new(storage_header).map_err(StoreError::AccountError)?;
245 let partial_storage =
246 PartialStorage::new(storage_header, maps).map_err(StoreError::AccountError)?;
247
248 let Some(account_code) = query_account_code(conn, header.code_commitment())? else {
249 return Ok(None);
250 };
251
252 let partial_account = PartialAccount::new(
253 header.id(),
254 header.nonce(),
255 account_code,
256 partial_storage,
257 partial_vault,
258 status.seed().copied(),
259 )?;
260 let account_record_data = AccountRecordData::Partial(partial_account);
261 let addresses = query_account_addresses(conn, header.id())?;
262 Ok(Some(AccountRecord::new(account_record_data, status, addresses)))
263 }
264
265 pub fn get_foreign_account_code(
266 conn: &mut Connection,
267 account_ids: Vec<AccountId>,
268 ) -> Result<BTreeMap<AccountId, AccountCode>, StoreError> {
269 let params: Vec<Value> =
270 account_ids.into_iter().map(|id| Value::from(id.to_hex())).collect();
271 const QUERY: &str = "
272 SELECT account_id, code
273 FROM foreign_account_code JOIN account_code ON foreign_account_code.code_commitment = account_code.commitment
274 WHERE account_id IN rarray(?)";
275
276 conn.prepare_cached(QUERY)
277 .into_store_error()?
278 .query_map([Rc::new(params)], |row| Ok((row.get(0)?, row.get(1)?)))
279 .expect("no binding parameters used in query")
280 .map(|result| {
281 result.map_err(|err| StoreError::ParsingError(err.to_string())).and_then(
282 |(id, code): (String, Vec<u8>)| {
283 Ok((
284 AccountId::from_hex(&id).map_err(|err| {
285 StoreError::AccountError(
286 AccountError::FinalAccountHeaderIdParsingFailed(err),
287 )
288 })?,
289 AccountCode::from_bytes(&code).map_err(StoreError::AccountError)?,
290 ))
291 },
292 )
293 })
294 .collect::<Result<BTreeMap<AccountId, AccountCode>, _>>()
295 }
296
297 pub fn get_account_vault(
299 conn: &Connection,
300 account_id: AccountId,
301 ) -> Result<AssetVault, StoreError> {
302 let assets = query_vault_assets(
303 conn,
304 "root = (SELECT vault_root FROM accounts WHERE id = ? ORDER BY nonce DESC LIMIT 1)",
305 params![account_id.to_hex()],
306 )?;
307
308 Ok(AssetVault::new(&assets)?)
309 }
310
311 pub fn get_account_storage(
313 conn: &Connection,
314 account_id: AccountId,
315 filter: &AccountStorageFilter,
316 ) -> Result<AccountStorage, StoreError> {
317 let (where_clause, params) = match filter {
318 AccountStorageFilter::All => (
319 "commitment = (SELECT storage_commitment FROM accounts WHERE id = ? ORDER BY nonce DESC LIMIT 1)",
320 params![account_id.to_hex()],
321 ),
322 AccountStorageFilter::Root(root) => (
323 "commitment = (SELECT storage_commitment FROM accounts WHERE id = ? ORDER BY nonce DESC LIMIT 1) AND slot_value = ?",
324 params![account_id.to_hex(), root.to_hex()],
325 ),
326 AccountStorageFilter::SlotName(slot_name) => (
327 "commitment = (SELECT storage_commitment FROM accounts WHERE id = ? ORDER BY nonce DESC LIMIT 1) AND slot_name = ?",
328 params![account_id.to_hex(), slot_name.to_string()],
329 ),
330 };
331
332 let slots = query_storage_slots(conn, where_clause, params)?.into_values().collect();
333
334 Ok(AccountStorage::new(slots)?)
335 }
336
337 pub(crate) fn get_account_asset(
340 conn: &mut Connection,
341 smt_forest: &Arc<RwLock<AccountSmtForest>>,
342 account_id: AccountId,
343 vault_key: AssetVaultKey,
344 ) -> Result<Option<(Asset, AssetWitness)>, StoreError> {
345 let header = Self::get_account_header(conn, account_id)?
346 .ok_or(StoreError::AccountDataNotFound(account_id))?
347 .0;
348
349 let smt_forest = smt_forest.read().expect("smt_forest read lock not poisoned");
350 match smt_forest.get_asset_and_witness(header.vault_root(), vault_key) {
351 Ok((asset, witness)) => Ok(Some((asset, witness))),
352 Err(StoreError::MerkleStoreError(MerkleError::UntrackedKey(_))) => Ok(None),
353 Err(err) => Err(err),
354 }
355 }
356
357 pub(crate) fn get_account_map_item(
360 conn: &mut Connection,
361 smt_forest: &Arc<RwLock<AccountSmtForest>>,
362 account_id: AccountId,
363 slot_name: StorageSlotName,
364 key: Word,
365 ) -> Result<(Word, StorageMapWitness), StoreError> {
366 let header = Self::get_account_header(conn, account_id)?
367 .ok_or(StoreError::AccountDataNotFound(account_id))?
368 .0;
369
370 let mut storage_values = query_storage_values(
371 conn,
372 "commitment = ? AND slot_name = ?",
373 params![header.storage_commitment().to_hex(), slot_name.to_string()],
374 )?;
375 let (slot_type, map_root) = storage_values
376 .remove(&slot_name)
377 .ok_or(StoreError::AccountStorageRootNotFound(header.storage_commitment()))?;
378 if slot_type != StorageSlotType::Map {
379 return Err(StoreError::AccountError(AccountError::StorageSlotNotMap(slot_name)));
380 }
381
382 let smt_forest = smt_forest.read().expect("smt_forest read lock not poisoned");
383 let witness = smt_forest.get_storage_map_item_witness(map_root, key)?;
384 let item = witness.get(&key).unwrap_or(miden_client::EMPTY_WORD);
385
386 Ok((item, witness))
387 }
388
389 pub(crate) fn get_account_addresses(
390 conn: &mut Connection,
391 account_id: AccountId,
392 ) -> Result<Vec<Address>, StoreError> {
393 query_account_addresses(conn, account_id)
394 }
395
396 pub(crate) fn insert_account(
400 conn: &mut Connection,
401 smt_forest: &Arc<RwLock<AccountSmtForest>>,
402 account: &Account,
403 initial_address: &Address,
404 ) -> Result<(), StoreError> {
405 let tx = conn.transaction().into_store_error()?;
406
407 Self::insert_account_code(&tx, account.code())?;
408
409 Self::insert_storage_slots(
410 &tx,
411 account.storage().to_commitment(),
412 account.storage().slots().iter(),
413 )?;
414
415 Self::insert_assets(&tx, account.vault().root(), account.vault().assets())?;
416 Self::insert_account_header(&tx, &account.into(), account.seed())?;
417
418 Self::insert_address(&tx, initial_address, account.id())?;
419
420 tx.commit().into_store_error()?;
421
422 let mut smt_forest = smt_forest.write().expect("smt_forest write lock not poisoned");
423 smt_forest.insert_account_state(account.vault(), account.storage())?;
424
425 Ok(())
426 }
427
428 pub(crate) fn update_account(
429 conn: &mut Connection,
430 smt_forest: &Arc<RwLock<AccountSmtForest>>,
431 new_account_state: &Account,
432 ) -> Result<(), StoreError> {
433 const QUERY: &str = "SELECT id FROM accounts WHERE id = ?";
434 if conn
435 .prepare(QUERY)
436 .into_store_error()?
437 .query_map(params![new_account_state.id().to_hex()], |row| row.get(0))
438 .into_store_error()?
439 .map(|result| {
440 result.map_err(|err| StoreError::ParsingError(err.to_string())).and_then(
441 |id: String| {
442 AccountId::from_hex(&id).map_err(|err| {
443 StoreError::AccountError(
444 AccountError::FinalAccountHeaderIdParsingFailed(err),
445 )
446 })
447 },
448 )
449 })
450 .next()
451 .is_none()
452 {
453 return Err(StoreError::AccountDataNotFound(new_account_state.id()));
454 }
455
456 let mut smt_forest = smt_forest.write().expect("smt_forest write lock not poisoned");
457 let tx = conn.transaction().into_store_error()?;
458 Self::update_account_state(&tx, &mut smt_forest, new_account_state)?;
459 tx.commit().into_store_error()
460 }
461
462 pub fn upsert_foreign_account_code(
463 conn: &mut Connection,
464 account_id: AccountId,
465 code: &AccountCode,
466 ) -> Result<(), StoreError> {
467 let tx = conn.transaction().into_store_error()?;
468
469 Self::insert_account_code(&tx, code)?;
470
471 const QUERY: &str =
472 insert_sql!(foreign_account_code { account_id, code_commitment } | REPLACE);
473
474 tx.execute(QUERY, params![account_id.to_hex(), code.commitment().to_string()])
475 .into_store_error()?;
476
477 Self::insert_account_code(&tx, code)?;
478 tx.commit().into_store_error()
479 }
480
481 pub(crate) fn insert_address(
482 tx: &Transaction<'_>,
483 address: &Address,
484 account_id: AccountId,
485 ) -> Result<(), StoreError> {
486 let derived_note_tag = address.to_note_tag();
487 let note_tag_record = NoteTagRecord::with_account_source(derived_note_tag, account_id);
488
489 add_note_tag_tx(tx, ¬e_tag_record)?;
490 Self::insert_address_internal(tx, address, account_id)?;
491
492 Ok(())
493 }
494
495 pub(crate) fn remove_address(
496 conn: &mut Connection,
497 address: &Address,
498 account_id: AccountId,
499 ) -> Result<(), StoreError> {
500 let derived_note_tag = address.to_note_tag();
501 let note_tag_record = NoteTagRecord::with_account_source(derived_note_tag, account_id);
502
503 let tx = conn.transaction().into_store_error()?;
504 remove_note_tag_tx(&tx, note_tag_record)?;
505 Self::remove_address_internal(&tx, address)?;
506
507 tx.commit().into_store_error()
508 }
509
510 pub(crate) fn insert_account_code(
512 tx: &Transaction<'_>,
513 account_code: &AccountCode,
514 ) -> Result<(), StoreError> {
515 const QUERY: &str = insert_sql!(account_code { commitment, code } | IGNORE);
516 tx.execute(QUERY, params![account_code.commitment().to_hex(), account_code.to_bytes()])
517 .into_store_error()?;
518 Ok(())
519 }
520
521 pub(crate) fn apply_account_delta(
529 tx: &Transaction<'_>,
530 smt_forest: &mut AccountSmtForest,
531 init_account_state: &AccountHeader,
532 final_account_state: &AccountHeader,
533 updated_fungible_assets: BTreeMap<AccountIdPrefix, FungibleAsset>,
534 updated_storage_maps: BTreeMap<StorageSlotName, StorageMap>,
535 delta: &AccountDelta,
536 ) -> Result<(), StoreError> {
537 Self::copy_account_state(tx, init_account_state, final_account_state)?;
540
541 Self::apply_account_vault_delta(
542 tx,
543 smt_forest,
544 init_account_state,
545 final_account_state,
546 updated_fungible_assets,
547 delta,
548 )?;
549
550 let updated_storage_slots =
551 Self::apply_account_storage_delta(smt_forest, updated_storage_maps, delta)?;
552
553 Self::insert_storage_slots(
554 tx,
555 final_account_state.storage_commitment(),
556 updated_storage_slots.values(),
557 )?;
558
559 Ok(())
560 }
561
562 pub(crate) fn undo_account_state(
571 tx: &Transaction<'_>,
572 smt_forest: &mut AccountSmtForest,
573 account_commitments: &[Word],
574 ) -> Result<(), StoreError> {
575 if account_commitments.is_empty() {
576 return Ok(());
577 }
578
579 let account_hash_params = Rc::new(
580 account_commitments.iter().map(|h| Value::from(h.to_hex())).collect::<Vec<_>>(),
581 );
582
583 let smt_roots = Self::get_smt_roots_by_account_commitment(tx, &account_hash_params)?;
585
586 const DELETE_QUERY: &str = "DELETE FROM accounts WHERE account_commitment IN rarray(?)";
587 tx.execute(DELETE_QUERY, params![account_hash_params]).into_store_error()?;
588
589 smt_forest.pop_roots(smt_roots);
591
592 Ok(())
593 }
594
595 pub(crate) fn update_account_state(
615 tx: &Transaction<'_>,
616 smt_forest: &mut AccountSmtForest,
617 new_account_state: &Account,
618 ) -> Result<(), StoreError> {
619 let old_roots = Self::get_smt_roots_by_account_id(tx, new_account_state.id())?;
621
622 smt_forest.insert_account_state(new_account_state.vault(), new_account_state.storage())?;
623 Self::insert_storage_slots(
624 tx,
625 new_account_state.storage().to_commitment(),
626 new_account_state.storage().slots().iter(),
627 )?;
628 Self::insert_assets(
629 tx,
630 new_account_state.vault().root(),
631 new_account_state.vault().assets(),
632 )?;
633 Self::insert_account_header(tx, &new_account_state.into(), None)?;
634
635 smt_forest.pop_roots(old_roots);
637
638 Ok(())
639 }
640
641 pub(crate) fn lock_account_on_unexpected_commitment(
644 tx: &Transaction<'_>,
645 account_id: &AccountId,
646 mismatched_digest: &Word,
647 ) -> Result<(), StoreError> {
648 const QUERY: &str = "UPDATE accounts SET locked = true WHERE id = :account_id AND NOT EXISTS (SELECT 1 FROM accounts WHERE id = :account_id AND account_commitment = :digest)";
652 tx.execute(
653 QUERY,
654 named_params! {
655 ":account_id": account_id.to_hex(),
656 ":digest": mismatched_digest.to_string()
657 },
658 )
659 .into_store_error()?;
660 Ok(())
661 }
662
663 fn copy_account_state(
668 tx: &Transaction<'_>,
669 init_account_header: &AccountHeader,
670 final_account_header: &AccountHeader,
671 ) -> Result<(), StoreError> {
672 Self::insert_account_header(tx, final_account_header, None)?;
673
674 if init_account_header.vault_root() != final_account_header.vault_root() {
675 const VAULT_QUERY: &str = "
676 INSERT OR IGNORE INTO account_assets (
677 root,
678 vault_key,
679 faucet_id_prefix,
680 asset
681 )
682 SELECT
683 ?, --new root
684 vault_key,
685 faucet_id_prefix,
686 asset
687 FROM account_assets
688 WHERE root = (SELECT vault_root FROM accounts WHERE account_commitment = ?)
689 ";
690 tx.execute(
691 VAULT_QUERY,
692 params![
693 final_account_header.vault_root().to_hex(),
694 init_account_header.commitment().to_hex()
695 ],
696 )
697 .into_store_error()?;
698 }
699
700 if init_account_header.storage_commitment() != final_account_header.storage_commitment() {
701 const STORAGE_QUERY: &str = "
702 INSERT OR IGNORE INTO account_storage (
703 commitment,
704 slot_name,
705 slot_value,
706 slot_type
707 )
708 SELECT
709 ?, -- new commitment
710 slot_name,
711 slot_value,
712 slot_type
713 FROM account_storage
714 WHERE commitment = (SELECT storage_commitment FROM accounts WHERE account_commitment = ?)
715 ";
716
717 tx.execute(
718 STORAGE_QUERY,
719 params![
720 final_account_header.storage_commitment().to_hex(),
721 init_account_header.commitment().to_hex()
722 ],
723 )
724 .into_store_error()?;
725 }
726
727 Ok(())
728 }
729
730 fn get_smt_roots_by_account_id(
736 tx: &Transaction<'_>,
737 account_id: AccountId,
738 ) -> Result<Vec<Word>, StoreError> {
739 const LATEST_ACCOUNT_QUERY: &str = r"
740 SELECT vault_root, storage_commitment
741 FROM accounts
742 WHERE id = ?1
743 ORDER BY nonce DESC
744 LIMIT 1
745 ";
746
747 const STORAGE_MAP_ROOTS_QUERY: &str = r"
748 SELECT slot_value
749 FROM account_storage
750 WHERE commitment = ?1
751 AND slot_type = ?2
752 AND slot_value IS NOT NULL
753 ";
754
755 let map_slot_type = StorageSlotType::Map as u8;
756
757 let (vault_root, storage_commitment): (String, String) = tx
759 .query_row(LATEST_ACCOUNT_QUERY, params![account_id.to_hex()], |row| {
760 Ok((row.get(0)?, row.get(1)?))
761 })
762 .into_store_error()?;
763
764 let mut roots = Vec::new();
765
766 if let Ok(root) = Word::try_from(vault_root.as_str()) {
768 roots.push(root);
769 }
770
771 let mut stmt = tx.prepare(STORAGE_MAP_ROOTS_QUERY).into_store_error()?;
773 let iter = stmt
774 .query_map(params![storage_commitment, map_slot_type], |row| row.get::<_, String>(0))
775 .into_store_error()?;
776
777 roots.extend(iter.filter_map(Result::ok).filter_map(|r| Word::try_from(r.as_str()).ok()));
778
779 Ok(roots)
780 }
781
782 fn get_smt_roots_by_account_commitment(
784 tx: &Transaction<'_>,
785 account_hash_params: &Rc<Vec<Value>>,
786 ) -> Result<Vec<Word>, StoreError> {
787 const ROOTS_QUERY: &str = "
788 SELECT vault_root FROM accounts WHERE account_commitment IN rarray(?1)
789 UNION ALL
790 SELECT slot_value FROM account_storage
791 WHERE commitment IN (
792 SELECT storage_commitment FROM accounts WHERE account_commitment IN rarray(?1)
793 ) AND slot_type = ?2";
794
795 let map_slot_type = StorageSlotType::Map as u8;
796 let mut stmt = tx.prepare(ROOTS_QUERY).into_store_error()?;
797 let roots = stmt
798 .query_map(params![account_hash_params, map_slot_type], |row| row.get::<_, String>(0))
799 .into_store_error()?
800 .filter_map(Result::ok)
801 .filter_map(|r| Word::try_from(r.as_str()).ok())
802 .collect();
803
804 Ok(roots)
805 }
806
807 fn insert_account_header(
809 tx: &Transaction<'_>,
810 account: &AccountHeader,
811 account_seed: Option<Word>,
812 ) -> Result<(), StoreError> {
813 let id: String = account.id().to_hex();
814 let code_commitment = account.code_commitment().to_string();
815 let storage_commitment = account.storage_commitment().to_string();
816 let vault_root = account.vault_root().to_string();
817 let nonce = u64_to_value(account.nonce().as_int());
818 let commitment = account.commitment().to_string();
819
820 let account_seed = account_seed.map(|seed| seed.to_bytes());
821
822 const QUERY: &str = insert_sql!(
823 accounts {
824 id,
825 code_commitment,
826 storage_commitment,
827 vault_root,
828 nonce,
829 account_seed,
830 account_commitment,
831 locked
832 } | REPLACE
833 );
834
835 tx.execute(
836 QUERY,
837 params![
838 id,
839 code_commitment,
840 storage_commitment,
841 vault_root,
842 nonce,
843 account_seed,
844 commitment,
845 false,
846 ],
847 )
848 .into_store_error()?;
849
850 Self::insert_tracked_account_id_tx(tx, account.id())?;
851 Ok(())
852 }
853
854 fn insert_tracked_account_id_tx(
855 tx: &Transaction<'_>,
856 account_id: AccountId,
857 ) -> Result<(), StoreError> {
858 const QUERY: &str = insert_sql!(tracked_accounts { id } | IGNORE);
859 tx.execute(QUERY, params![account_id.to_hex()]).into_store_error()?;
860 Ok(())
861 }
862
863 fn insert_address_internal(
864 tx: &Transaction<'_>,
865 address: &Address,
866 account_id: AccountId,
867 ) -> Result<(), StoreError> {
868 const QUERY: &str = insert_sql!(addresses { address, account_id } | REPLACE);
869 let serialized_address = address.to_bytes();
870 tx.execute(QUERY, params![serialized_address, account_id.to_hex(),])
871 .into_store_error()?;
872
873 Ok(())
874 }
875
876 fn remove_address_internal(tx: &Transaction<'_>, address: &Address) -> Result<(), StoreError> {
877 let serialized_address = address.to_bytes();
878
879 const DELETE_QUERY: &str = "DELETE FROM addresses WHERE address = ?";
880 tx.execute(DELETE_QUERY, params![serialized_address]).into_store_error()?;
881
882 Ok(())
883 }
884}