miden_client/rpc/domain/
note.rs

1use alloc::vec::Vec;
2
3use miden_objects::{
4    block::BlockHeader,
5    crypto::merkle::MerklePath,
6    note::{Note, NoteExecutionHint, NoteId, NoteInclusionProof, NoteMetadata, NoteTag, NoteType},
7    Digest, Felt,
8};
9
10use super::{MissingFieldHelper, RpcConversionError};
11use crate::rpc::{
12    generated::{note::NoteMetadata as ProtoNoteMetadata, responses::SyncNoteResponse},
13    RpcError,
14};
15
16impl TryFrom<ProtoNoteMetadata> for NoteMetadata {
17    type Error = RpcConversionError;
18
19    fn try_from(value: ProtoNoteMetadata) -> Result<Self, Self::Error> {
20        let sender = value
21            .sender
22            .ok_or_else(|| ProtoNoteMetadata::missing_field("Sender"))?
23            .try_into()?;
24        let note_type = NoteType::try_from(value.note_type as u64)?;
25        let tag = NoteTag::from(value.tag);
26        let execution_hint_tag = (value.execution_hint & 0xff) as u8;
27        let execution_hint_payload = ((value.execution_hint >> 8) & 0xffffff) as u32;
28        let execution_hint =
29            NoteExecutionHint::from_parts(execution_hint_tag, execution_hint_payload)?;
30
31        let aux = Felt::try_from(value.aux).map_err(|_| RpcConversionError::NotAValidFelt)?;
32
33        Ok(NoteMetadata::new(sender, note_type, tag, execution_hint, aux)?)
34    }
35}
36
37impl From<NoteMetadata> for ProtoNoteMetadata {
38    fn from(value: NoteMetadata) -> Self {
39        ProtoNoteMetadata {
40            sender: Some(value.sender().into()),
41            note_type: value.note_type() as u32,
42            tag: value.tag().into(),
43            execution_hint: value.execution_hint().into(),
44            aux: value.aux().into(),
45        }
46    }
47}
48
49// SYNC NOTE
50// ================================================================================================
51
52/// Represents a `SyncNoteResponse` with fields converted into domain types.
53#[derive(Debug)]
54pub struct NoteSyncInfo {
55    /// Number of the latest block in the chain.
56    pub chain_tip: u32,
57    /// Block header of the block with the first note matching the specified criteria.
58    pub block_header: BlockHeader,
59    /// Proof for block header's MMR with respect to the chain tip.
60    ///
61    /// More specifically, the full proof consists of `forest`, `position` and `path` components.
62    /// This value constitutes the `path`. The other two components can be obtained as follows:
63    ///    - `position` is simply `resopnse.block_header.block_num`.
64    ///    - `forest` is the same as `response.chain_tip + 1`.
65    pub mmr_path: MerklePath,
66    /// List of all notes together with the Merkle paths from `response.block_header.note_root`.
67    pub notes: Vec<CommittedNote>,
68}
69
70impl TryFrom<SyncNoteResponse> for NoteSyncInfo {
71    type Error = RpcError;
72
73    fn try_from(value: SyncNoteResponse) -> Result<Self, Self::Error> {
74        let chain_tip = value.chain_tip;
75
76        // Validate and convert block header
77        let block_header = value
78            .block_header
79            .ok_or(RpcError::ExpectedDataMissing("BlockHeader".into()))?
80            .try_into()?;
81
82        let mmr_path = value
83            .mmr_path
84            .ok_or(RpcError::ExpectedDataMissing("MmrPath".into()))?
85            .try_into()?;
86
87        // Validate and convert account note inclusions into an (AccountId, Digest) tuple
88        let mut notes = vec![];
89        for note in value.notes {
90            let note_id: Digest = note
91                .note_id
92                .ok_or(RpcError::ExpectedDataMissing("Notes.Id".into()))?
93                .try_into()?;
94
95            let note_id: NoteId = note_id.into();
96
97            let merkle_path = note
98                .merkle_path
99                .ok_or(RpcError::ExpectedDataMissing("Notes.MerklePath".into()))?
100                .try_into()?;
101
102            let metadata = note
103                .metadata
104                .ok_or(RpcError::ExpectedDataMissing("Metadata".into()))?
105                .try_into()?;
106
107            let committed_note =
108                CommittedNote::new(note_id, note.note_index as u16, merkle_path, metadata);
109
110            notes.push(committed_note);
111        }
112
113        Ok(NoteSyncInfo { chain_tip, block_header, mmr_path, notes })
114    }
115}
116
117// COMMITTED NOTE
118// ================================================================================================
119
120/// Represents a committed note, returned as part of a `SyncStateResponse`.
121#[derive(Debug, Clone)]
122pub struct CommittedNote {
123    /// Note ID of the committed note.
124    note_id: NoteId,
125    /// Note index for the note merkle tree.
126    note_index: u16,
127    /// Merkle path for the note merkle tree up to the block's note root.
128    merkle_path: MerklePath,
129    /// Note metadata.
130    metadata: NoteMetadata,
131}
132
133impl CommittedNote {
134    pub fn new(
135        note_id: NoteId,
136        note_index: u16,
137        merkle_path: MerklePath,
138        metadata: NoteMetadata,
139    ) -> Self {
140        Self {
141            note_id,
142            note_index,
143            merkle_path,
144            metadata,
145        }
146    }
147
148    pub fn note_id(&self) -> &NoteId {
149        &self.note_id
150    }
151
152    pub fn note_index(&self) -> u16 {
153        self.note_index
154    }
155
156    pub fn merkle_path(&self) -> &MerklePath {
157        &self.merkle_path
158    }
159
160    pub fn metadata(&self) -> NoteMetadata {
161        self.metadata
162    }
163}
164
165// NETWORK NOTE
166// ================================================================================================
167
168/// Describes the possible responses from  the `GetNotesById` endpoint for a single note.
169#[allow(clippy::large_enum_variant)]
170pub enum NetworkNote {
171    /// Details for a private note only include its [NoteMetadata] and [NoteInclusionProof].
172    /// Other details needed to consume the note are expected to be stored locally, off-chain.
173    Private(NoteId, NoteMetadata, NoteInclusionProof),
174    /// Contains the full [Note] object alongside its [NoteInclusionProof].
175    Public(Note, NoteInclusionProof),
176}
177
178impl NetworkNote {
179    /// Returns the note's inclusion details.
180    pub fn inclusion_proof(&self) -> &NoteInclusionProof {
181        match self {
182            NetworkNote::Private(_, _, inclusion_proof) => inclusion_proof,
183            NetworkNote::Public(_, inclusion_proof) => inclusion_proof,
184        }
185    }
186
187    /// Returns the note's metadata.
188    pub fn metadata(&self) -> &NoteMetadata {
189        match self {
190            NetworkNote::Private(_, metadata, _) => metadata,
191            NetworkNote::Public(note, _) => note.metadata(),
192        }
193    }
194
195    /// Returns the note's ID.
196    pub fn id(&self) -> NoteId {
197        match self {
198            NetworkNote::Private(id, ..) => *id,
199            NetworkNote::Public(note, _) => note.id(),
200        }
201    }
202}