use std::ops::RangeInclusive;
use std::sync::Arc;
use miden_node_utils::limiter::MAX_RESPONSE_PAYLOAD_BYTES;
use miden_protocol::account::AccountId;
use miden_protocol::block::{BlockHeader, BlockNumber};
use miden_protocol::crypto::merkle::mmr::{Forest, MmrDelta, MmrProof};
use tracing::instrument;
use super::State;
use crate::COMPONENT;
use crate::db::models::queries::StorageMapValuesPage;
use crate::db::{AccountVaultValue, NoteSyncUpdate, NullifierInfo};
use crate::errors::{DatabaseError, NoteSyncError, StateSyncError};
const BLOCK_OVERHEAD_BYTES: usize = 1600;
const NOTE_RECORD_BYTES: usize = 700;
impl State {
pub async fn sync_transactions(
&self,
account_ids: Vec<AccountId>,
block_range: RangeInclusive<BlockNumber>,
) -> Result<(BlockNumber, Vec<crate::db::TransactionRecord>), DatabaseError> {
self.db.select_transactions_records(account_ids, block_range).await
}
#[instrument(level = "debug", target = COMPONENT, skip_all, ret(level = "debug"), err)]
pub async fn sync_chain_mmr(
&self,
block_range: RangeInclusive<BlockNumber>,
) -> Result<(MmrDelta, BlockHeader), StateSyncError> {
let block_from = *block_range.start();
let block_to = *block_range.end();
let block_header = self
.db
.select_block_header_by_block_num(Some(block_to))
.await?
.expect("block_to should exist in the database");
if block_from == block_to {
return Ok((
MmrDelta {
forest: Forest::new(block_from.as_usize()),
data: vec![],
},
block_header,
));
}
let from_forest = (block_from + 1).as_usize();
let to_forest = block_to.as_usize();
let mmr_delta = self
.inner
.read()
.await
.blockchain
.as_mmr()
.get_delta(Forest::new(from_forest), Forest::new(to_forest))
.map_err(StateSyncError::FailedToBuildMmrDelta)?;
Ok((mmr_delta, block_header))
}
#[instrument(level = "debug", target = COMPONENT, skip_all, ret(level = "debug"), err)]
pub async fn sync_notes(
&self,
note_tags: Vec<u32>,
block_range: RangeInclusive<BlockNumber>,
) -> Result<(Vec<(NoteSyncUpdate, MmrProof)>, BlockNumber), NoteSyncError> {
let block_end = *block_range.end();
let note_tags: Arc<[u32]> = note_tags.into();
let mut results = Vec::new();
let mut accumulated_size: usize = 0;
let mut current_from = *block_range.start();
loop {
let range = current_from..=block_end;
let Some(note_sync) = self.db.get_note_sync(range, Arc::clone(¬e_tags)).await?
else {
break;
};
accumulated_size += BLOCK_OVERHEAD_BYTES + note_sync.notes.len() * NOTE_RECORD_BYTES;
if !results.is_empty() && accumulated_size > MAX_RESPONSE_PAYLOAD_BYTES {
break;
}
let block_num = note_sync.block_header.block_num();
let mmr_checkpoint = block_end + 1;
let mmr_proof =
self.inner.read().await.blockchain.open_at(block_num, mmr_checkpoint)?;
results.push((note_sync, mmr_proof));
current_from = block_num + 1;
}
let last_block_checked =
results.last().map_or(block_end, |(update, _)| update.block_header.block_num());
Ok((results, last_block_checked))
}
pub async fn sync_nullifiers(
&self,
prefix_len: u32,
nullifier_prefixes: Vec<u32>,
block_range: RangeInclusive<BlockNumber>,
) -> Result<(Vec<NullifierInfo>, BlockNumber), DatabaseError> {
self.db
.select_nullifiers_by_prefix(prefix_len, nullifier_prefixes, block_range)
.await
}
pub async fn sync_account_vault(
&self,
account_id: AccountId,
block_range: RangeInclusive<BlockNumber>,
) -> Result<(BlockNumber, Vec<AccountVaultValue>), DatabaseError> {
self.db.get_account_vault_sync(account_id, block_range).await
}
pub async fn sync_account_storage_maps(
&self,
account_id: AccountId,
block_range: RangeInclusive<BlockNumber>,
) -> Result<StorageMapValuesPage, DatabaseError> {
self.db.select_storage_map_sync_values(account_id, block_range, None).await
}
}