miden_objects/conversion/
note.rs1use alloc::format;
2use alloc::sync::Arc;
3use alloc::vec::Vec;
4
5use miden_protocol::asset::Asset;
6use miden_protocol::note::{
7 Note,
8 NoteAssets,
9 NoteAttachment,
10 NoteAttachmentHeader,
11 NoteAttachmentScheme,
12 NoteAttachments,
13 NoteDetails,
14 NoteDetailsCommitment,
15 NoteHeader,
16 NoteId,
17 NoteInclusionProof,
18 NoteMetadata,
19 NoteRecipient,
20 NoteScript,
21 NoteStorage,
22 NoteTag,
23 NoteType,
24 PartialNoteMetadata,
25};
26use miden_protocol::{Felt, MastNodeId, Word};
27
28use super::{MessageDecodeExt, MessageDecoder, required};
29use crate::{ConversionError, ConversionResultExt, proto};
30
31impl From<NoteType> for proto::note::NoteType {
35 fn from(note_type: NoteType) -> Self {
36 match note_type {
37 NoteType::Private => proto::note::NoteType::Private,
38 NoteType::Public => proto::note::NoteType::Public,
39 }
40 }
41}
42
43impl TryFrom<proto::note::NoteType> for NoteType {
44 type Error = ConversionError;
45
46 fn try_from(note_type: proto::note::NoteType) -> Result<Self, Self::Error> {
47 match note_type {
48 proto::note::NoteType::Private => Ok(NoteType::Private),
49 proto::note::NoteType::Public => Ok(NoteType::Public),
50 proto::note::NoteType::Unspecified => {
51 Err(ConversionError::message("enum variant discriminant out of range"))
52 },
53 }
54 }
55}
56
57impl From<NoteMetadata> for proto::note::NoteMetadata {
61 fn from(metadata: NoteMetadata) -> Self {
62 Self {
63 version: proto::note::NoteVersion::V1 as i32,
64 sender: Some(metadata.sender().into()),
65 note_type: proto::note::NoteType::from(metadata.note_type()) as i32,
66 tag: metadata.tag().as_u32(),
67 attachment_schemes: metadata
68 .attachment_headers()
69 .iter()
70 .map(|header| u32::from(header.scheme().map_or(0, |scheme| scheme.as_u16())))
71 .collect(),
72 attachments_commitment: Some(metadata.attachments_commitment().into()),
73 }
74 }
75}
76
77impl TryFrom<proto::note::NoteMetadata> for NoteMetadata {
78 type Error = ConversionError;
79
80 fn try_from(metadata: proto::note::NoteMetadata) -> Result<Self, Self::Error> {
81 decode_note_version(metadata.version).context("version")?;
82 decode_note_metadata(metadata)
83 }
84}
85
86impl From<&NoteAttachment> for proto::note::NoteAttachment {
90 fn from(attachment: &NoteAttachment) -> Self {
91 Self {
92 scheme: u32::from(attachment.attachment_scheme().as_u16()),
93 words: attachment.content().as_words().iter().map(Into::into).collect(),
94 }
95 }
96}
97
98impl TryFrom<proto::note::NoteAttachment> for NoteAttachment {
99 type Error = ConversionError;
100
101 fn try_from(attachment: proto::note::NoteAttachment) -> Result<Self, Self::Error> {
102 let scheme = u16::try_from(attachment.scheme).context("scheme")?;
103 let scheme = NoteAttachmentScheme::new(scheme)
104 .map_err(ConversionError::from)
105 .context("scheme")?;
106 let words = attachment
107 .words
108 .into_iter()
109 .map(Word::try_from)
110 .collect::<Result<Vec<_>, _>>()
111 .context("words")?;
112
113 NoteAttachment::with_words(scheme, words)
114 .map_err(ConversionError::from)
115 .context("words")
116 }
117}
118
119impl From<NoteAttachments> for proto::note::NoteAttachments {
120 fn from(attachments: NoteAttachments) -> Self {
121 Self::from(&attachments)
122 }
123}
124
125impl From<&NoteAttachments> for proto::note::NoteAttachments {
126 fn from(attachments: &NoteAttachments) -> Self {
127 Self {
128 attachments: attachments.iter().map(Into::into).collect(),
129 }
130 }
131}
132
133impl TryFrom<proto::note::NoteAttachments> for NoteAttachments {
134 type Error = ConversionError;
135
136 fn try_from(attachments: proto::note::NoteAttachments) -> Result<Self, Self::Error> {
137 let attachments = attachments
138 .attachments
139 .into_iter()
140 .map(NoteAttachment::try_from)
141 .collect::<Result<Vec<_>, _>>()
142 .context("attachments")?;
143
144 NoteAttachments::new(attachments)
145 .map_err(ConversionError::from)
146 .context("attachments")
147 }
148}
149
150impl From<NoteStorage> for proto::note::NoteStorage {
154 fn from(storage: NoteStorage) -> Self {
155 Self::from(&storage)
156 }
157}
158
159impl From<&NoteStorage> for proto::note::NoteStorage {
160 fn from(storage: &NoteStorage) -> Self {
161 Self {
162 items: storage.items().iter().map(Into::into).collect(),
163 }
164 }
165}
166
167impl TryFrom<proto::note::NoteStorage> for NoteStorage {
168 type Error = ConversionError;
169
170 fn try_from(storage: proto::note::NoteStorage) -> Result<Self, Self::Error> {
171 let items = storage
172 .items
173 .into_iter()
174 .map(Felt::try_from)
175 .collect::<Result<Vec<_>, _>>()
176 .context("items")?;
177
178 NoteStorage::new(items).map_err(ConversionError::from).context("items")
179 }
180}
181
182impl From<NoteRecipient> for proto::note::NoteRecipient {
183 fn from(recipient: NoteRecipient) -> Self {
184 Self::from(&recipient)
185 }
186}
187
188impl From<&NoteRecipient> for proto::note::NoteRecipient {
189 fn from(recipient: &NoteRecipient) -> Self {
190 Self {
191 serial_num: Some(recipient.serial_num().into()),
192 script: Some(recipient.script().into()),
193 storage: Some(recipient.storage().into()),
194 }
195 }
196}
197
198impl TryFrom<proto::note::NoteRecipient> for NoteRecipient {
199 type Error = ConversionError;
200
201 fn try_from(recipient: proto::note::NoteRecipient) -> Result<Self, Self::Error> {
202 let decoder = recipient.decoder();
203 let serial_num = required!(decoder, recipient.serial_num)?;
204 let script = required!(decoder, recipient.script)?;
205 let storage = required!(decoder, recipient.storage)?;
206
207 Ok(NoteRecipient::new(serial_num, script, storage))
208 }
209}
210
211impl From<NoteDetails> for proto::note::NoteDetails {
212 fn from(details: NoteDetails) -> Self {
213 Self::from(&details)
214 }
215}
216
217impl From<&NoteDetails> for proto::note::NoteDetails {
218 fn from(details: &NoteDetails) -> Self {
219 Self {
220 assets: details.assets().iter().copied().map(Into::into).collect(),
221 recipient: Some(details.recipient().into()),
222 }
223 }
224}
225
226impl TryFrom<proto::note::NoteDetails> for NoteDetails {
227 type Error = ConversionError;
228
229 fn try_from(details: proto::note::NoteDetails) -> Result<Self, Self::Error> {
230 let decoder = details.decoder();
231 let assets = details
232 .assets
233 .into_iter()
234 .map(Asset::try_from)
235 .collect::<Result<Vec<_>, _>>()
236 .context("assets")?;
237 let assets = NoteAssets::new(assets).map_err(ConversionError::from).context("assets")?;
238 let recipient = required!(decoder, details.recipient)?;
239
240 Ok(NoteDetails::new(assets, recipient))
241 }
242}
243
244impl From<Note> for proto::note::Note {
248 fn from(note: Note) -> Self {
249 let (assets, metadata, recipient, attachments) = note.into_parts();
250 Self {
251 metadata: Some(metadata.into()),
252 note_details: Some(NoteDetails::new(assets, recipient).into()),
253 note_attachments: Some(attachments.into()),
254 }
255 }
256}
257
258impl TryFrom<proto::note::Note> for Note {
259 type Error = ConversionError;
260
261 fn try_from(proto_note: proto::note::Note) -> Result<Self, Self::Error> {
262 let decoder = proto_note.decoder();
263 let proto::note::Note { metadata, note_details, note_attachments } = proto_note;
264
265 let metadata = required!(decoder, metadata)?;
266 let partial_metadata = partial_note_metadata_from_proto(metadata)?;
267
268 let note_details: NoteDetails = required!(decoder, note_details)?;
269 let (assets, recipient) = note_details.into_parts();
270 let attachments = decode_note_attachments::<proto::note::Note>(note_attachments)?;
271
272 Ok(Note::with_attachments(assets, partial_metadata, recipient, attachments))
273 }
274}
275
276impl From<Word> for proto::note::NoteId {
280 fn from(digest: Word) -> Self {
281 Self { id: Some(digest.into()) }
282 }
283}
284
285impl TryFrom<proto::note::NoteId> for Word {
286 type Error = ConversionError;
287
288 fn try_from(note_id: proto::note::NoteId) -> Result<Self, Self::Error> {
289 let decoder = note_id.decoder();
290 required!(decoder, note_id.id)
291 }
292}
293
294impl From<&NoteId> for proto::note::NoteId {
295 fn from(note_id: &NoteId) -> Self {
296 Self { id: Some(note_id.as_word().into()) }
297 }
298}
299
300impl From<(&NoteId, &NoteInclusionProof)> for proto::note::NoteInclusionProof {
301 fn from((note_id, proof): (&NoteId, &NoteInclusionProof)) -> Self {
302 Self {
303 note_id: Some(note_id.into()),
304 block_num: Some(proof.location().block_num().into()),
305 note_index_in_block: proof.location().block_note_tree_index().into(),
306 inclusion_path: Some(proof.note_path().clone().into()),
307 }
308 }
309}
310
311impl TryFrom<&proto::note::NoteInclusionProof> for (NoteId, NoteInclusionProof) {
312 type Error = ConversionError;
313
314 fn try_from(
315 proof: &proto::note::NoteInclusionProof,
316 ) -> Result<(NoteId, NoteInclusionProof), Self::Error> {
317 let proof = proof.clone();
318 let decoder = proof.decoder();
319 let inclusion_path = required!(decoder, proof.inclusion_path)?;
320 let note_id = required!(decoder, proof.note_id)?;
321 let block_num = required!(decoder, proof.block_num).context("block_num")?;
322
323 Ok((
324 NoteId::from_raw(note_id),
325 NoteInclusionProof::new(
326 block_num,
327 proof.note_index_in_block.try_into().context("note_index_in_block")?,
328 inclusion_path,
329 )?,
330 ))
331 }
332}
333
334impl From<NoteHeader> for proto::note::NoteHeader {
338 fn from(header: NoteHeader) -> Self {
339 Self {
340 details_commitment: Some(header.details_commitment().as_word().into()),
341 metadata: Some(header.into_metadata().into()),
342 }
343 }
344}
345
346impl TryFrom<proto::note::NoteHeader> for NoteHeader {
347 type Error = ConversionError;
348
349 fn try_from(value: proto::note::NoteHeader) -> Result<Self, Self::Error> {
350 let decoder = value.decoder();
351 let details_commitment_word = required!(decoder, value.details_commitment)?;
352 let metadata: NoteMetadata = required!(decoder, value.metadata)?;
353
354 Ok(NoteHeader::new(
355 NoteDetailsCommitment::from_raw(details_commitment_word),
356 metadata,
357 ))
358 }
359}
360
361impl From<NoteScript> for proto::note::NoteScript {
365 fn from(script: NoteScript) -> Self {
366 Self::from(&script)
367 }
368}
369
370impl From<&NoteScript> for proto::note::NoteScript {
371 fn from(script: &NoteScript) -> Self {
372 Self {
373 entrypoint: script.entrypoint().into(),
374 mast: Some(script.mast().as_ref().into()),
375 }
376 }
377}
378
379impl TryFrom<proto::note::NoteScript> for NoteScript {
380 type Error = ConversionError;
381
382 fn try_from(value: proto::note::NoteScript) -> Result<Self, Self::Error> {
383 let decoder = value.decoder();
384 let mast = required!(decoder, value.mast)?;
385 let entrypoint = value.entrypoint;
386 let entrypoint = MastNodeId::from_u32_safe(entrypoint, &mast)
387 .map_err(|err| ConversionError::deserialization("note_script.entrypoint", err))?;
388
389 Self::from_parts(Arc::new(mast), entrypoint).map_err(ConversionError::new)
390 }
391}
392
393fn partial_note_metadata_from_proto(
401 value: proto::note::NoteMetadata,
402) -> Result<PartialNoteMetadata, ConversionError> {
403 decode_note_version(value.version).context("version")?;
404 decode_partial_note_metadata(value.sender, value.note_type, value.tag)
405}
406
407fn decode_note_version(version: i32) -> Result<(), ConversionError> {
408 match proto::note::NoteVersion::try_from(version) {
409 Ok(proto::note::NoteVersion::V1) => Ok(()),
410 Ok(proto::note::NoteVersion::Unspecified) => {
411 Err(ConversionError::message("note metadata version is unspecified"))
412 },
413 Err(error) => Err(ConversionError::with_source(
414 format!("unknown note metadata version {version}"),
415 error,
416 )),
417 }
418}
419
420fn decode_note_metadata(
421 metadata: proto::note::NoteMetadata,
422) -> Result<NoteMetadata, ConversionError> {
423 let proto::note::NoteMetadata {
424 sender,
425 note_type,
426 tag,
427 attachment_schemes,
428 attachments_commitment,
429 ..
430 } = metadata;
431
432 let partial = decode_partial_note_metadata(sender, note_type, tag)?;
433 let decoder = MessageDecoder::<proto::note::NoteMetadata>::default();
434 let attachments_commitment = required!(decoder, attachments_commitment)?;
435
436 if attachment_schemes.len() > NoteAttachments::MAX_COUNT {
437 return Err(ConversionError::message("too many attachment schemes"));
438 }
439 let mut attachment_headers = [NoteAttachmentHeader::absent(); NoteAttachments::MAX_COUNT];
440 for (slot, raw) in attachment_headers.iter_mut().zip(attachment_schemes) {
441 let raw = u16::try_from(raw)
442 .map_err(|_| ConversionError::message("attachment scheme out of u16 range"))?;
443 *slot = if raw == 0 {
444 NoteAttachmentHeader::absent()
445 } else {
446 NoteAttachmentHeader::new(NoteAttachmentScheme::new(raw)?)
447 };
448 }
449
450 Ok(NoteMetadata::from_parts(partial, attachment_headers, attachments_commitment))
451}
452
453fn decode_partial_note_metadata(
454 sender: Option<proto::account::AccountId>,
455 note_type: i32,
456 tag: u32,
457) -> Result<PartialNoteMetadata, ConversionError> {
458 let decoder = MessageDecoder::<proto::note::NoteMetadata>::default();
459 let sender = required!(decoder, sender)?;
460 let note_type = proto::note::NoteType::try_from(note_type)
461 .map_err(|_| ConversionError::message("enum variant discriminant out of range"))?
462 .try_into()
463 .context("note_type")?;
464 let tag = NoteTag::new(tag);
465 Ok(PartialNoteMetadata::new(sender, note_type).with_tag(tag))
466}
467
468fn decode_note_attachments<M: prost::Message>(
470 note_attachments: Option<proto::note::NoteAttachments>,
471) -> Result<NoteAttachments, ConversionError> {
472 let decoder = MessageDecoder::<M>::default();
473 required!(decoder, note_attachments)
474}