Skip to main content

miden_node_proto/domain/
sync.rs

1use std::collections::HashMap;
2
3use miden_protobuf::{ConversionError, ConversionResultExt, Verify};
4use miden_protocol::block::BlockNumber;
5use miden_protocol::note::Nullifier;
6
7use crate::generated as proto;
8
9#[cfg(test)]
10mod tests;
11
12#[derive(Debug, PartialEq, Eq)]
13pub struct PaginationInfo {
14    pub chain_tip: BlockNumber,
15    pub block_num: BlockNumber,
16}
17
18impl Verify for proto::miden::node::v1::DecodedPaginationInfo {
19    type Verified = PaginationInfo;
20    type Error = ConversionError;
21
22    fn verify(self) -> Result<Self::Verified, Self::Error> {
23        if self.block_num > self.chain_tip {
24            return Err(ConversionError::message("last checked block exceeds chain tip")
25                .context("block_num"));
26        }
27
28        Ok(PaginationInfo {
29            chain_tip: self.chain_tip.into(),
30            block_num: self.block_num.into(),
31        })
32    }
33}
34
35#[derive(Debug, PartialEq, Eq)]
36pub struct SyncNullifiersResponse {
37    pub pagination_info: PaginationInfo,
38    /// Maps each nullifier to the block that consumed the note.
39    pub nullifiers: HashMap<Nullifier, BlockNumber>,
40}
41
42impl Verify for proto::miden::node::v1::DecodedSyncNullifiersResponse {
43    type Verified = SyncNullifiersResponse;
44    type Error = ConversionError;
45
46    /// Check response consistency. The caller must check the requested block range and prefixes.
47    /// The response does not contain proofs of nullifier inclusion or completeness.
48    fn verify(self) -> Result<Self::Verified, Self::Error> {
49        let pagination_info = self.pagination_info.verify().context("pagination_info")?;
50        let mut nullifiers = HashMap::with_capacity(self.nullifiers.as_slice().len());
51        for (index, update) in self.nullifiers.into_inner().into_iter().enumerate() {
52            let block_num = BlockNumber::from(update.block_num);
53            if block_num > pagination_info.block_num {
54                return Err(ConversionError::message("nullifier block exceeds last checked block")
55                    .context(format!("nullifiers[{index}].block_num")));
56            }
57            let nullifier = Nullifier::from_raw(update.nullifier);
58            if nullifiers.insert(nullifier, block_num).is_some() {
59                return Err(ConversionError::message(format!("duplicate nullifier {nullifier}"))
60                    .context(format!("nullifiers[{index}].nullifier")));
61            }
62        }
63
64        Ok(SyncNullifiersResponse { pagination_info, nullifiers })
65    }
66}