miden_node_proto/domain/
note.rs

1use miden_objects::crypto::merkle::SparseMerklePath;
2use miden_objects::note::{
3    Note,
4    NoteDetails,
5    NoteExecutionHint,
6    NoteId,
7    NoteInclusionProof,
8    NoteMetadata,
9    NoteScript,
10    NoteTag,
11    NoteType,
12    Nullifier,
13};
14use miden_objects::utils::{Deserializable, Serializable};
15use miden_objects::{Felt, Word};
16use thiserror::Error;
17
18use super::account::NetworkAccountPrefix;
19use crate::errors::{ConversionError, MissingFieldHelper};
20use crate::generated as proto;
21
22impl TryFrom<proto::note::NoteMetadata> for NoteMetadata {
23    type Error = ConversionError;
24
25    fn try_from(value: proto::note::NoteMetadata) -> Result<Self, Self::Error> {
26        let sender = value
27            .sender
28            .ok_or_else(|| proto::note::NoteMetadata::missing_field(stringify!(sender)))?
29            .try_into()?;
30        let note_type = NoteType::try_from(u64::from(value.note_type))?;
31        let tag = NoteTag::from(value.tag);
32
33        let execution_hint = NoteExecutionHint::try_from(value.execution_hint)?;
34
35        let aux = Felt::try_from(value.aux).map_err(|_| ConversionError::NotAValidFelt)?;
36
37        Ok(NoteMetadata::new(sender, note_type, tag, execution_hint, aux)?)
38    }
39}
40
41impl From<Note> for proto::note::NetworkNote {
42    fn from(note: Note) -> Self {
43        Self {
44            metadata: Some(proto::note::NoteMetadata::from(*note.metadata())),
45            details: NoteDetails::from(note).to_bytes(),
46        }
47    }
48}
49
50impl From<Note> for proto::note::Note {
51    fn from(note: Note) -> Self {
52        Self {
53            metadata: Some(proto::note::NoteMetadata::from(*note.metadata())),
54            details: Some(NoteDetails::from(note).to_bytes()),
55        }
56    }
57}
58
59impl From<NetworkNote> for proto::note::NetworkNote {
60    fn from(note: NetworkNote) -> Self {
61        let note = Note::from(note);
62        Self {
63            metadata: Some(proto::note::NoteMetadata::from(*note.metadata())),
64            details: NoteDetails::from(note).to_bytes(),
65        }
66    }
67}
68
69impl From<NoteMetadata> for proto::note::NoteMetadata {
70    fn from(val: NoteMetadata) -> Self {
71        let sender = Some(val.sender().into());
72        let note_type = val.note_type() as u32;
73        let tag = val.tag().into();
74        let execution_hint: u64 = val.execution_hint().into();
75        let aux = val.aux().into();
76
77        proto::note::NoteMetadata {
78            sender,
79            note_type,
80            tag,
81            execution_hint,
82            aux,
83        }
84    }
85}
86
87impl From<Word> for proto::note::NoteId {
88    fn from(digest: Word) -> Self {
89        Self { id: Some(digest.into()) }
90    }
91}
92
93impl TryFrom<proto::note::NoteId> for Word {
94    type Error = ConversionError;
95
96    fn try_from(note_id: proto::note::NoteId) -> Result<Self, Self::Error> {
97        note_id
98            .id
99            .as_ref()
100            .ok_or(proto::note::NoteId::missing_field(stringify!(id)))?
101            .try_into()
102    }
103}
104
105impl From<&NoteId> for proto::note::NoteId {
106    fn from(note_id: &NoteId) -> Self {
107        Self { id: Some(note_id.into()) }
108    }
109}
110
111impl From<(&NoteId, &NoteInclusionProof)> for proto::note::NoteInclusionInBlockProof {
112    fn from((note_id, proof): (&NoteId, &NoteInclusionProof)) -> Self {
113        Self {
114            note_id: Some(note_id.into()),
115            block_num: proof.location().block_num().as_u32(),
116            note_index_in_block: proof.location().node_index_in_block().into(),
117            inclusion_path: Some(proof.note_path().clone().into()),
118        }
119    }
120}
121
122impl TryFrom<&proto::note::NoteInclusionInBlockProof> for (NoteId, NoteInclusionProof) {
123    type Error = ConversionError;
124
125    fn try_from(
126        proof: &proto::note::NoteInclusionInBlockProof,
127    ) -> Result<(NoteId, NoteInclusionProof), Self::Error> {
128        let inclusion_path = SparseMerklePath::try_from(
129            proof
130                .inclusion_path
131                .as_ref()
132                .ok_or(proto::note::NoteInclusionInBlockProof::missing_field(stringify!(
133                    inclusion_path
134                )))?
135                .clone(),
136        )?;
137
138        Ok((
139            Word::try_from(
140                proof
141                    .note_id
142                    .as_ref()
143                    .ok_or(proto::note::NoteInclusionInBlockProof::missing_field(stringify!(
144                        note_id
145                    )))?
146                    .id
147                    .as_ref()
148                    .ok_or(proto::note::NoteId::missing_field(stringify!(id)))?,
149            )?
150            .into(),
151            NoteInclusionProof::new(
152                proof.block_num.into(),
153                proof.note_index_in_block.try_into()?,
154                inclusion_path,
155            )?,
156        ))
157    }
158}
159
160impl TryFrom<proto::note::Note> for Note {
161    type Error = ConversionError;
162
163    fn try_from(proto_note: proto::note::Note) -> Result<Self, Self::Error> {
164        let metadata: NoteMetadata = proto_note
165            .metadata
166            .ok_or(proto::note::Note::missing_field(stringify!(metadata)))?
167            .try_into()?;
168
169        let details = proto_note
170            .details
171            .ok_or(proto::note::Note::missing_field(stringify!(details)))?;
172
173        let note_details = NoteDetails::read_from_bytes(&details)
174            .map_err(|err| ConversionError::deserialization_error("NoteDetails", err))?;
175
176        let (assets, recipient) = note_details.into_parts();
177        Ok(Note::new(assets, metadata, recipient))
178    }
179}
180
181// NETWORK NOTE
182// ================================================================================================
183
184/// An enum that wraps around notes used in a network mode.
185#[derive(Clone, Debug, PartialEq, Eq)]
186pub enum NetworkNote {
187    SingleTarget(SingleTargetNetworkNote),
188    MultiTarget(MultiTargetNetworkNote),
189}
190
191impl NetworkNote {
192    pub fn inner(&self) -> &Note {
193        match self {
194            NetworkNote::SingleTarget(note) => &note.0,
195            NetworkNote::MultiTarget(note) => &note.0,
196        }
197    }
198
199    pub fn metadata(&self) -> &NoteMetadata {
200        self.inner().metadata()
201    }
202
203    pub fn nullifier(&self) -> Nullifier {
204        self.inner().nullifier()
205    }
206
207    pub fn id(&self) -> NoteId {
208        self.inner().id()
209    }
210}
211
212impl From<NetworkNote> for Note {
213    fn from(value: NetworkNote) -> Self {
214        match value {
215            NetworkNote::SingleTarget(note) => note.0,
216            NetworkNote::MultiTarget(note) => note.0,
217        }
218    }
219}
220
221impl TryFrom<Note> for NetworkNote {
222    type Error = NetworkNoteError;
223
224    fn try_from(note: Note) -> Result<Self, Self::Error> {
225        if note.is_network_note() {
226            if note.metadata().tag().is_single_target() {
227                Ok(NetworkNote::SingleTarget(SingleTargetNetworkNote(note)))
228            } else {
229                Ok(NetworkNote::MultiTarget(MultiTargetNetworkNote(note)))
230            }
231        } else {
232            Err(NetworkNoteError::InvalidExecutionMode(note.metadata().tag()))
233        }
234    }
235}
236
237impl TryFrom<proto::note::NetworkNote> for NetworkNote {
238    type Error = ConversionError;
239
240    fn try_from(proto_note: proto::note::NetworkNote) -> Result<Self, Self::Error> {
241        from_proto(proto_note)
242    }
243}
244
245// MULTI TARGET NETWORK NOTE
246// ================================================================================================
247
248/// A newtype that wraps around notes having multiple targets to be used in a network mode.
249#[derive(Clone, Debug, PartialEq, Eq)]
250pub struct MultiTargetNetworkNote(Note);
251
252impl TryFrom<Note> for MultiTargetNetworkNote {
253    type Error = NetworkNoteError;
254
255    fn try_from(note: Note) -> Result<Self, Self::Error> {
256        if note.is_network_note() && !note.metadata().tag().is_single_target() {
257            Ok(Self(note))
258        } else {
259            Err(NetworkNoteError::InvalidExecutionMode(note.metadata().tag()))
260        }
261    }
262}
263
264impl TryFrom<proto::note::NetworkNote> for MultiTargetNetworkNote {
265    type Error = ConversionError;
266
267    fn try_from(proto_note: proto::note::NetworkNote) -> Result<Self, Self::Error> {
268        from_proto(proto_note)
269    }
270}
271
272// SINGLE TARGET NETWORK NOTE
273// ================================================================================================
274
275/// A newtype that wraps around notes targeting a single account to be used in a network mode.
276#[derive(Clone, Debug, PartialEq, Eq)]
277pub struct SingleTargetNetworkNote(Note);
278
279impl SingleTargetNetworkNote {
280    pub fn inner(&self) -> &Note {
281        &self.0
282    }
283
284    pub fn metadata(&self) -> &NoteMetadata {
285        self.inner().metadata()
286    }
287
288    pub fn nullifier(&self) -> Nullifier {
289        self.inner().nullifier()
290    }
291
292    pub fn id(&self) -> NoteId {
293        self.inner().id()
294    }
295
296    /// The account prefix that this note targets.
297    pub fn account_prefix(&self) -> NetworkAccountPrefix {
298        self.metadata()
299            .tag()
300            .try_into()
301            .expect("Single target network note's tag should contain an account prefix")
302    }
303}
304
305impl From<SingleTargetNetworkNote> for Note {
306    fn from(value: SingleTargetNetworkNote) -> Self {
307        value.0
308    }
309}
310
311impl TryFrom<Note> for SingleTargetNetworkNote {
312    type Error = NetworkNoteError;
313
314    fn try_from(note: Note) -> Result<Self, Self::Error> {
315        if note.is_network_note() && note.metadata().tag().is_single_target() {
316            Ok(Self(note))
317        } else {
318            Err(NetworkNoteError::InvalidExecutionMode(note.metadata().tag()))
319        }
320    }
321}
322
323impl TryFrom<proto::note::NetworkNote> for SingleTargetNetworkNote {
324    type Error = ConversionError;
325
326    fn try_from(proto_note: proto::note::NetworkNote) -> Result<Self, Self::Error> {
327        from_proto(proto_note)
328    }
329}
330
331/// Helper function to deduplicate implementations `TryFrom<proto::note::NetworkNote>`.
332fn from_proto<T>(proto_note: proto::note::NetworkNote) -> Result<T, ConversionError>
333where
334    T: TryFrom<Note>,
335    T::Error: Into<ConversionError>,
336{
337    let details = NoteDetails::read_from_bytes(&proto_note.details)
338        .map_err(|err| ConversionError::deserialization_error("NoteDetails", err))?;
339    let (assets, recipient) = details.into_parts();
340    let metadata: NoteMetadata = proto_note
341        .metadata
342        .ok_or_else(|| proto::note::NetworkNote::missing_field(stringify!(metadata)))?
343        .try_into()?;
344    let note = Note::new(assets, metadata, recipient);
345    T::try_from(note).map_err(Into::into)
346}
347
348#[derive(Debug, Error)]
349pub enum NetworkNoteError {
350    #[error("note tag {0} is not a valid network note tag")]
351    InvalidExecutionMode(NoteTag),
352}
353
354// NOTE SCRIPT
355// ================================================================================================
356
357impl From<NoteScript> for proto::note::NoteScript {
358    fn from(script: NoteScript) -> Self {
359        Self {
360            entrypoint: script.entrypoint().into(),
361            mast: script.mast().to_bytes(),
362        }
363    }
364}