Skip to main content

miden_node_proto/domain/
note.rs

1use std::sync::Arc;
2
3use miden_protocol::crypto::merkle::SparseMerklePath;
4use miden_protocol::note::{
5    Note,
6    NoteAttachment,
7    NoteDetails,
8    NoteId,
9    NoteInclusionProof,
10    NoteMetadata,
11    NoteScript,
12    NoteTag,
13    NoteType,
14};
15use miden_protocol::utils::{Deserializable, Serializable};
16use miden_protocol::{MastForest, MastNodeId, Word};
17use miden_standards::note::AccountTargetNetworkNote;
18
19use crate::errors::{ConversionError, MissingFieldHelper};
20use crate::generated as proto;
21
22// NOTE TYPE
23// ================================================================================================
24
25impl From<NoteType> for proto::note::NoteType {
26    fn from(note_type: NoteType) -> Self {
27        match note_type {
28            NoteType::Public => proto::note::NoteType::Public,
29            NoteType::Private => proto::note::NoteType::Private,
30        }
31    }
32}
33
34impl TryFrom<proto::note::NoteType> for NoteType {
35    type Error = ConversionError;
36
37    fn try_from(note_type: proto::note::NoteType) -> Result<Self, Self::Error> {
38        match note_type {
39            proto::note::NoteType::Public => Ok(NoteType::Public),
40            proto::note::NoteType::Private => Ok(NoteType::Private),
41            proto::note::NoteType::Unspecified => Err(ConversionError::EnumDiscriminantOutOfRange),
42        }
43    }
44}
45
46// NOTE METADATA
47// ================================================================================================
48
49impl TryFrom<proto::note::NoteMetadata> for NoteMetadata {
50    type Error = ConversionError;
51
52    fn try_from(value: proto::note::NoteMetadata) -> Result<Self, Self::Error> {
53        let sender = value
54            .sender
55            .ok_or_else(|| proto::note::NoteMetadata::missing_field(stringify!(sender)))?
56            .try_into()?;
57        let note_type = proto::note::NoteType::try_from(value.note_type)
58            .map_err(|_| ConversionError::EnumDiscriminantOutOfRange)?
59            .try_into()?;
60        let tag = NoteTag::new(value.tag);
61
62        // Deserialize attachment if present
63        let attachment = if value.attachment.is_empty() {
64            NoteAttachment::default()
65        } else {
66            NoteAttachment::read_from_bytes(&value.attachment)
67                .map_err(|err| ConversionError::deserialization_error("NoteAttachment", err))?
68        };
69
70        Ok(NoteMetadata::new(sender, note_type).with_tag(tag).with_attachment(attachment))
71    }
72}
73
74impl From<Note> for proto::note::NetworkNote {
75    fn from(note: Note) -> Self {
76        Self {
77            metadata: Some(proto::note::NoteMetadata::from(note.metadata().clone())),
78            details: NoteDetails::from(note).to_bytes(),
79        }
80    }
81}
82
83impl From<Note> for proto::note::Note {
84    fn from(note: Note) -> Self {
85        Self {
86            metadata: Some(proto::note::NoteMetadata::from(note.metadata().clone())),
87            details: Some(NoteDetails::from(note).to_bytes()),
88        }
89    }
90}
91
92impl From<AccountTargetNetworkNote> for proto::note::NetworkNote {
93    fn from(note: AccountTargetNetworkNote) -> Self {
94        let note = note.into_note();
95        Self {
96            metadata: Some(proto::note::NoteMetadata::from(note.metadata().clone())),
97            details: NoteDetails::from(note).to_bytes(),
98        }
99    }
100}
101
102impl TryFrom<proto::note::NetworkNote> for AccountTargetNetworkNote {
103    type Error = ConversionError;
104
105    fn try_from(value: proto::note::NetworkNote) -> Result<Self, Self::Error> {
106        let details = NoteDetails::read_from_bytes(&value.details)
107            .map_err(|err| ConversionError::deserialization_error("NoteDetails", err))?;
108        let (assets, recipient) = details.into_parts();
109        let metadata: NoteMetadata = value
110            .metadata
111            .ok_or_else(|| proto::note::NetworkNote::missing_field(stringify!(metadata)))?
112            .try_into()?;
113        let note = Note::new(assets, metadata, recipient);
114        AccountTargetNetworkNote::new(note).map_err(ConversionError::NetworkNoteError)
115    }
116}
117
118impl From<NoteMetadata> for proto::note::NoteMetadata {
119    fn from(val: NoteMetadata) -> Self {
120        let sender = Some(val.sender().into());
121        let note_type = proto::note::NoteType::from(val.note_type()) as i32;
122        let tag = val.tag().as_u32();
123        let attachment = val.attachment().to_bytes();
124
125        proto::note::NoteMetadata { sender, note_type, tag, attachment }
126    }
127}
128
129impl From<Word> for proto::note::NoteId {
130    fn from(digest: Word) -> Self {
131        Self { id: Some(digest.into()) }
132    }
133}
134
135impl TryFrom<proto::note::NoteId> for Word {
136    type Error = ConversionError;
137
138    fn try_from(note_id: proto::note::NoteId) -> Result<Self, Self::Error> {
139        note_id
140            .id
141            .as_ref()
142            .ok_or(proto::note::NoteId::missing_field(stringify!(id)))?
143            .try_into()
144    }
145}
146
147impl From<&NoteId> for proto::note::NoteId {
148    fn from(note_id: &NoteId) -> Self {
149        Self { id: Some(note_id.into()) }
150    }
151}
152
153impl From<(&NoteId, &NoteInclusionProof)> for proto::note::NoteInclusionInBlockProof {
154    fn from((note_id, proof): (&NoteId, &NoteInclusionProof)) -> Self {
155        Self {
156            note_id: Some(note_id.into()),
157            block_num: proof.location().block_num().as_u32(),
158            note_index_in_block: proof.location().node_index_in_block().into(),
159            inclusion_path: Some(proof.note_path().clone().into()),
160        }
161    }
162}
163
164impl TryFrom<&proto::note::NoteInclusionInBlockProof> for (NoteId, NoteInclusionProof) {
165    type Error = ConversionError;
166
167    fn try_from(
168        proof: &proto::note::NoteInclusionInBlockProof,
169    ) -> Result<(NoteId, NoteInclusionProof), Self::Error> {
170        let inclusion_path = SparseMerklePath::try_from(
171            proof
172                .inclusion_path
173                .as_ref()
174                .ok_or(proto::note::NoteInclusionInBlockProof::missing_field(stringify!(
175                    inclusion_path
176                )))?
177                .clone(),
178        )?;
179
180        let note_id = Word::try_from(
181            proof
182                .note_id
183                .as_ref()
184                .ok_or(proto::note::NoteInclusionInBlockProof::missing_field(stringify!(note_id)))?
185                .id
186                .as_ref()
187                .ok_or(proto::note::NoteId::missing_field(stringify!(id)))?,
188        )?;
189
190        Ok((
191            NoteId::from_raw(note_id),
192            NoteInclusionProof::new(
193                proof.block_num.into(),
194                proof.note_index_in_block.try_into()?,
195                inclusion_path,
196            )?,
197        ))
198    }
199}
200
201impl TryFrom<proto::note::Note> for Note {
202    type Error = ConversionError;
203
204    fn try_from(proto_note: proto::note::Note) -> Result<Self, Self::Error> {
205        let metadata: NoteMetadata = proto_note
206            .metadata
207            .ok_or(proto::note::Note::missing_field(stringify!(metadata)))?
208            .try_into()?;
209
210        let details = proto_note
211            .details
212            .ok_or(proto::note::Note::missing_field(stringify!(details)))?;
213
214        let note_details = NoteDetails::read_from_bytes(&details)
215            .map_err(|err| ConversionError::deserialization_error("NoteDetails", err))?;
216
217        let (assets, recipient) = note_details.into_parts();
218        Ok(Note::new(assets, metadata, recipient))
219    }
220}
221
222// NOTE SCRIPT
223// ================================================================================================
224
225impl From<NoteScript> for proto::note::NoteScript {
226    fn from(script: NoteScript) -> Self {
227        Self {
228            entrypoint: script.entrypoint().into(),
229            mast: script.mast().to_bytes(),
230        }
231    }
232}
233
234impl TryFrom<proto::note::NoteScript> for NoteScript {
235    type Error = ConversionError;
236
237    fn try_from(value: proto::note::NoteScript) -> Result<Self, Self::Error> {
238        let proto::note::NoteScript { entrypoint, mast } = value;
239
240        let mast = MastForest::read_from_bytes(&mast)
241            .map_err(|err| Self::Error::deserialization_error("note_script.mast", err))?;
242        let entrypoint = MastNodeId::from_u32_safe(entrypoint, &mast)
243            .map_err(|err| Self::Error::deserialization_error("note_script.entrypoint", err))?;
244
245        Ok(Self::from_parts(Arc::new(mast), entrypoint))
246    }
247}