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    NoteAttachmentKind,
8    NoteDetails,
9    NoteHeader,
10    NoteId,
11    NoteInclusionProof,
12    NoteMetadata,
13    NoteMetadataHeader,
14    NoteScript,
15    NoteTag,
16    NoteType,
17};
18use miden_protocol::utils::serde::Serializable;
19use miden_protocol::{MastForest, MastNodeId, Word};
20use miden_standards::note::AccountTargetNetworkNote;
21
22use crate::decode::{ConversionResultExt, DecodeBytesExt, GrpcDecodeExt};
23use crate::errors::ConversionError;
24use crate::{decode, generated as proto};
25
26// NOTE TYPE
27// ================================================================================================
28
29impl From<NoteType> for proto::note::NoteType {
30    fn from(note_type: NoteType) -> Self {
31        match note_type {
32            NoteType::Public => proto::note::NoteType::Public,
33            NoteType::Private => proto::note::NoteType::Private,
34        }
35    }
36}
37
38impl TryFrom<proto::note::NoteType> for NoteType {
39    type Error = ConversionError;
40
41    fn try_from(note_type: proto::note::NoteType) -> Result<Self, Self::Error> {
42        match note_type {
43            proto::note::NoteType::Public => Ok(NoteType::Public),
44            proto::note::NoteType::Private => Ok(NoteType::Private),
45            proto::note::NoteType::Unspecified => {
46                Err(ConversionError::message("enum variant discriminant out of range"))
47            },
48        }
49    }
50}
51
52// NOTE ATTACHMENT KIND
53// ================================================================================================
54
55impl From<NoteAttachmentKind> for proto::note::NoteAttachmentKind {
56    fn from(kind: NoteAttachmentKind) -> Self {
57        match kind {
58            NoteAttachmentKind::None => proto::note::NoteAttachmentKind::None,
59            NoteAttachmentKind::Word => proto::note::NoteAttachmentKind::Word,
60            NoteAttachmentKind::Array => proto::note::NoteAttachmentKind::Array,
61        }
62    }
63}
64
65impl TryFrom<proto::note::NoteAttachmentKind> for NoteAttachmentKind {
66    type Error = ConversionError;
67
68    fn try_from(kind: proto::note::NoteAttachmentKind) -> Result<Self, Self::Error> {
69        match kind {
70            proto::note::NoteAttachmentKind::None => Ok(NoteAttachmentKind::None),
71            proto::note::NoteAttachmentKind::Word => Ok(NoteAttachmentKind::Word),
72            proto::note::NoteAttachmentKind::Array => Ok(NoteAttachmentKind::Array),
73            proto::note::NoteAttachmentKind::Unspecified => {
74                Err(ConversionError::message("enum variant discriminant out of range"))
75            },
76        }
77    }
78}
79
80// NOTE METADATA HEADER
81// ================================================================================================
82
83impl From<NoteMetadataHeader> for proto::note::NoteMetadataHeader {
84    fn from(header: NoteMetadataHeader) -> Self {
85        Self {
86            sender: Some(header.sender().into()),
87            note_type: proto::note::NoteType::from(header.note_type()) as i32,
88            tag: header.tag().as_u32(),
89            attachment_kind: proto::note::NoteAttachmentKind::from(header.attachment_kind()) as i32,
90            attachment_scheme: header.attachment_scheme().as_u32(),
91        }
92    }
93}
94
95// NOTE METADATA
96// ================================================================================================
97
98impl TryFrom<proto::note::NoteMetadata> for NoteMetadata {
99    type Error = ConversionError;
100
101    fn try_from(value: proto::note::NoteMetadata) -> Result<Self, Self::Error> {
102        let decoder = value.decoder();
103        let sender = decode!(decoder, value.sender)?;
104        let note_type = proto::note::NoteType::try_from(value.note_type)
105            .map_err(|_| ConversionError::message("enum variant discriminant out of range"))?
106            .try_into()
107            .context("note_type")?;
108        let tag = NoteTag::new(value.tag);
109
110        // Deserialize attachment if present
111        let attachment = if value.attachment.is_empty() {
112            NoteAttachment::default()
113        } else {
114            NoteAttachment::decode_bytes(&value.attachment, "NoteAttachment")?
115        };
116
117        Ok(NoteMetadata::new(sender, note_type).with_tag(tag).with_attachment(attachment))
118    }
119}
120
121impl From<Note> for proto::note::NetworkNote {
122    fn from(note: Note) -> Self {
123        Self {
124            metadata: Some(proto::note::NoteMetadata::from(note.metadata().clone())),
125            details: NoteDetails::from(note).to_bytes(),
126        }
127    }
128}
129
130impl From<Note> for proto::note::Note {
131    fn from(note: Note) -> Self {
132        Self {
133            metadata: Some(proto::note::NoteMetadata::from(note.metadata().clone())),
134            details: Some(NoteDetails::from(note).to_bytes()),
135        }
136    }
137}
138
139impl From<AccountTargetNetworkNote> for proto::note::NetworkNote {
140    fn from(note: AccountTargetNetworkNote) -> Self {
141        let note = note.into_note();
142        Self {
143            metadata: Some(proto::note::NoteMetadata::from(note.metadata().clone())),
144            details: NoteDetails::from(note).to_bytes(),
145        }
146    }
147}
148
149impl TryFrom<proto::note::NetworkNote> for AccountTargetNetworkNote {
150    type Error = ConversionError;
151
152    fn try_from(value: proto::note::NetworkNote) -> Result<Self, Self::Error> {
153        let decoder = value.decoder();
154        let details = NoteDetails::decode_bytes(&value.details, "NoteDetails")?;
155        let (assets, recipient) = details.into_parts();
156        let metadata: NoteMetadata = decode!(decoder, value.metadata)?;
157        let note = Note::new(assets, metadata, recipient);
158        AccountTargetNetworkNote::new(note).map_err(ConversionError::from)
159    }
160}
161
162impl From<NoteMetadata> for proto::note::NoteMetadata {
163    fn from(val: NoteMetadata) -> Self {
164        let sender = Some(val.sender().into());
165        let note_type = proto::note::NoteType::from(val.note_type()) as i32;
166        let tag = val.tag().as_u32();
167        let attachment = val.attachment().to_bytes();
168
169        proto::note::NoteMetadata { sender, note_type, tag, attachment }
170    }
171}
172
173impl From<Word> for proto::note::NoteId {
174    fn from(digest: Word) -> Self {
175        Self { id: Some(digest.into()) }
176    }
177}
178
179impl TryFrom<proto::note::NoteId> for Word {
180    type Error = ConversionError;
181
182    fn try_from(note_id: proto::note::NoteId) -> Result<Self, Self::Error> {
183        note_id
184            .id
185            .as_ref()
186            .ok_or(ConversionError::missing_field::<proto::note::NoteId>("id"))?
187            .try_into()
188    }
189}
190
191impl From<&NoteId> for proto::note::NoteId {
192    fn from(note_id: &NoteId) -> Self {
193        Self { id: Some(note_id.into()) }
194    }
195}
196
197impl From<(&NoteId, &NoteInclusionProof)> for proto::note::NoteInclusionInBlockProof {
198    fn from((note_id, proof): (&NoteId, &NoteInclusionProof)) -> Self {
199        Self {
200            note_id: Some(note_id.into()),
201            block_num: proof.location().block_num().as_u32(),
202            note_index_in_block: proof.location().block_note_tree_index().into(),
203            inclusion_path: Some(proof.note_path().clone().into()),
204        }
205    }
206}
207
208impl TryFrom<&proto::note::NoteInclusionInBlockProof> for (NoteId, NoteInclusionProof) {
209    type Error = ConversionError;
210
211    fn try_from(
212        proof: &proto::note::NoteInclusionInBlockProof,
213    ) -> Result<(NoteId, NoteInclusionProof), Self::Error> {
214        let inclusion_path = SparseMerklePath::try_from(
215            proof
216                .inclusion_path
217                .as_ref()
218                .ok_or(ConversionError::missing_field::<proto::note::NoteInclusionInBlockProof>(
219                    "inclusion_path",
220                ))?
221                .clone(),
222        )
223        .context("inclusion_path")?;
224
225        let note_id = Word::try_from(
226            proof
227                .note_id
228                .as_ref()
229                .ok_or(ConversionError::missing_field::<proto::note::NoteInclusionInBlockProof>(
230                    "note_id",
231                ))?
232                .id
233                .as_ref()
234                .ok_or(ConversionError::missing_field::<proto::note::NoteId>("id"))?,
235        )
236        .context("note_id")?;
237
238        Ok((
239            NoteId::from_raw(note_id),
240            NoteInclusionProof::new(
241                proof.block_num.into(),
242                proof.note_index_in_block.try_into().context("note_index_in_block")?,
243                inclusion_path,
244            )?,
245        ))
246    }
247}
248
249impl TryFrom<proto::note::Note> for Note {
250    type Error = ConversionError;
251
252    fn try_from(proto_note: proto::note::Note) -> Result<Self, Self::Error> {
253        let decoder = proto_note.decoder();
254        let metadata: NoteMetadata = decode!(decoder, proto_note.metadata)?;
255
256        let details = proto_note
257            .details
258            .ok_or(ConversionError::missing_field::<proto::note::Note>("details"))?;
259
260        let note_details = NoteDetails::decode_bytes(&details, "NoteDetails")?;
261
262        let (assets, recipient) = note_details.into_parts();
263        Ok(Note::new(assets, metadata, recipient))
264    }
265}
266
267// NOTE HEADER
268// ================================================================================================
269
270impl From<NoteHeader> for proto::note::NoteHeader {
271    fn from(header: NoteHeader) -> Self {
272        Self {
273            note_id: Some((&header.id()).into()),
274            metadata: Some(header.into_metadata().into()),
275        }
276    }
277}
278
279impl TryFrom<proto::note::NoteHeader> for NoteHeader {
280    type Error = ConversionError;
281
282    fn try_from(value: proto::note::NoteHeader) -> Result<Self, Self::Error> {
283        let decoder = value.decoder();
284        let note_id_word: Word = decode!(decoder, value.note_id)?;
285        let metadata: NoteMetadata = decode!(decoder, value.metadata)?;
286
287        Ok(NoteHeader::new(NoteId::from_raw(note_id_word), metadata))
288    }
289}
290
291// NOTE SCRIPT
292// ================================================================================================
293
294impl From<NoteScript> for proto::note::NoteScript {
295    fn from(script: NoteScript) -> Self {
296        Self {
297            entrypoint: script.entrypoint().into(),
298            mast: script.mast().to_bytes(),
299        }
300    }
301}
302
303impl TryFrom<proto::note::NoteScript> for NoteScript {
304    type Error = ConversionError;
305
306    fn try_from(value: proto::note::NoteScript) -> Result<Self, Self::Error> {
307        let proto::note::NoteScript { entrypoint, mast } = value;
308
309        let mast = MastForest::decode_bytes(&mast, "note_script.mast")?;
310        let entrypoint = MastNodeId::from_u32_safe(entrypoint, &mast)
311            .map_err(|err| ConversionError::deserialization("note_script.entrypoint", err))?;
312
313        Ok(Self::from_parts(Arc::new(mast), entrypoint))
314    }
315}