Skip to main content

miden_client/rpc/domain/
sync.rs

1use alloc::vec::Vec;
2
3use miden_protocol::Word;
4use miden_protocol::account::AccountId;
5use miden_protocol::block::{BlockHeader, BlockNumber};
6use miden_protocol::crypto::merkle::mmr::MmrDelta;
7use miden_protocol::note::NoteId;
8use miden_protocol::transaction::TransactionId;
9
10use super::note::CommittedNote;
11use super::transaction::TransactionInclusion;
12use crate::rpc::domain::MissingFieldHelper;
13use crate::rpc::{RpcError, generated as proto};
14
15// STATE SYNC INFO
16// ================================================================================================
17
18/// Represents a `proto::rpc::SyncStateResponse` with fields converted into domain types.
19pub struct StateSyncInfo {
20    /// The block number of the chain tip at the moment of the response.
21    pub chain_tip: BlockNumber,
22    /// The returned block header.
23    pub block_header: BlockHeader,
24    /// MMR delta that contains data for (`current_block.num`, `incoming_block_header.num-1`).
25    pub mmr_delta: MmrDelta,
26    /// Tuples of `AccountId` alongside their new account commitments.
27    pub account_commitment_updates: Vec<(AccountId, Word)>,
28    /// List of tuples of Note ID, Note Index and Merkle Path for all new notes.
29    pub note_inclusions: Vec<CommittedNote>,
30    /// List of transaction IDs of transaction that were included in (`request.block_num`,
31    /// `response.block_num-1`) along with the account the tx was executed against and the block
32    /// number the transaction was included in.
33    pub transactions: Vec<TransactionInclusion>,
34}
35
36// STATE SYNC INFO CONVERSION
37// ================================================================================================
38
39impl TryFrom<proto::rpc::SyncStateResponse> for StateSyncInfo {
40    type Error = RpcError;
41
42    fn try_from(value: proto::rpc::SyncStateResponse) -> Result<Self, Self::Error> {
43        let chain_tip = value.chain_tip;
44
45        // Validate and convert block header
46        let block_header: BlockHeader = value
47            .block_header
48            .ok_or(proto::rpc::SyncStateResponse::missing_field(stringify!(block_header)))?
49            .try_into()?;
50
51        // Validate and convert MMR Delta
52        let mmr_delta = value
53            .mmr_delta
54            .ok_or(proto::rpc::SyncStateResponse::missing_field(stringify!(mmr_delta)))?
55            .try_into()?;
56
57        // Validate and convert account commitment updates into an (AccountId, Word) tuple
58        let mut account_commitment_updates = vec![];
59        for update in value.accounts {
60            let account_id = update
61                .account_id
62                .ok_or(proto::rpc::SyncStateResponse::missing_field(stringify!(
63                    accounts.account_id
64                )))?
65                .try_into()?;
66            let account_commitment = update
67                .account_commitment
68                .ok_or(proto::rpc::SyncStateResponse::missing_field(stringify!(
69                    accounts.account_commitment
70                )))?
71                .try_into()?;
72            account_commitment_updates.push((account_id, account_commitment));
73        }
74
75        // Validate and convert account note inclusions into an (AccountId, Word) tuple
76        let mut note_inclusions = vec![];
77        for note in value.notes {
78            let note_id: NoteId = note
79                .note_id
80                .ok_or(proto::rpc::SyncStateResponse::missing_field(stringify!(notes.note_id)))?
81                .try_into()?;
82
83            let inclusion_path = note
84                .inclusion_path
85                .ok_or(proto::rpc::SyncStateResponse::missing_field(stringify!(
86                    notes.inclusion_path
87                )))?
88                .try_into()?;
89
90            let metadata = note
91                .metadata
92                .ok_or(proto::rpc::SyncStateResponse::missing_field(stringify!(notes.metadata)))?
93                .try_into()?;
94
95            let committed_note = super::note::CommittedNote::new(
96                note_id,
97                u16::try_from(note.note_index_in_block).expect("note index out of range"),
98                inclusion_path,
99                metadata,
100            );
101
102            note_inclusions.push(committed_note);
103        }
104
105        let transactions = value
106            .transactions
107            .iter()
108            .map(|transaction_summary| {
109                let transaction_id = transaction_summary.transaction_id.ok_or(
110                    proto::rpc::SyncStateResponse::missing_field(stringify!(
111                        transactions.transaction_id
112                    )),
113                )?;
114                let transaction_id = TransactionId::try_from(transaction_id)?;
115
116                let transaction_block_num = transaction_summary.block_num;
117
118                let transaction_account_id = transaction_summary.account_id.clone().ok_or(
119                    proto::rpc::SyncStateResponse::missing_field(stringify!(
120                        transactions.account_id
121                    )),
122                )?;
123                let transaction_account_id = AccountId::try_from(transaction_account_id)?;
124
125                Ok(TransactionInclusion {
126                    transaction_id,
127                    block_num: transaction_block_num.into(),
128                    account_id: transaction_account_id,
129                })
130            })
131            .collect::<Result<Vec<TransactionInclusion>, RpcError>>()?;
132
133        Ok(Self {
134            chain_tip: chain_tip.into(),
135            block_header,
136            mmr_delta,
137            account_commitment_updates,
138            note_inclusions,
139            transactions,
140        })
141    }
142}