miden_protocol/transaction/outputs/
notes.rs1use alloc::collections::BTreeSet;
2use alloc::string::ToString;
3use alloc::vec::Vec;
4use core::fmt::Debug;
5
6use crate::constants::NOTE_MAX_SIZE;
7use crate::errors::{OutputNoteError, TransactionOutputError};
8use crate::note::{
9 Note,
10 NoteAssets,
11 NoteAttachments,
12 NoteDetailsCommitment,
13 NoteHeader,
14 NoteId,
15 NoteMetadata,
16 NoteRecipient,
17 PartialNote,
18};
19use crate::utils::serde::{
20 ByteReader,
21 ByteWriter,
22 Deserializable,
23 DeserializationError,
24 Serializable,
25};
26use crate::{Felt, Hasher, MAX_OUTPUT_NOTES_PER_TX, Word};
27
28#[derive(Debug, Clone, PartialEq, Eq)]
38pub struct OutputNoteCollection<N> {
39 notes: Vec<N>,
40 commitment: Word,
41}
42
43impl<N> OutputNoteCollection<N>
44where
45 for<'a> &'a NoteHeader: From<&'a N>,
46 for<'a> NoteId: From<&'a N>,
47{
48 pub fn new(notes: Vec<N>) -> Result<Self, TransactionOutputError> {
58 if notes.len() > MAX_OUTPUT_NOTES_PER_TX {
59 return Err(TransactionOutputError::TooManyOutputNotes(notes.len()));
60 }
61
62 let mut seen_notes = BTreeSet::new();
63 for note in notes.iter() {
64 let note_id = NoteId::from(note);
65 if !seen_notes.insert(note_id) {
66 return Err(TransactionOutputError::DuplicateOutputNote(note_id));
67 }
68 }
69
70 let commitment = Self::compute_commitment(notes.iter().map(<&NoteHeader>::from));
71
72 Ok(Self { notes, commitment })
73 }
74
75 pub fn commitment(&self) -> Word {
83 self.commitment
84 }
85
86 pub fn num_notes(&self) -> usize {
88 self.notes.len()
89 }
90
91 pub fn is_empty(&self) -> bool {
93 self.notes.is_empty()
94 }
95
96 pub fn get_note(&self, idx: usize) -> &N {
98 &self.notes[idx]
99 }
100
101 pub fn iter(&self) -> impl Iterator<Item = &N> {
106 self.notes.iter()
107 }
108
109 pub(crate) fn compute_commitment<'header>(
119 notes: impl ExactSizeIterator<Item = &'header NoteHeader>,
120 ) -> Word {
121 if notes.len() == 0 {
122 return Word::empty();
123 }
124
125 let mut elements: Vec<Felt> = Vec::with_capacity(notes.len() * 8);
126 for note_header in notes {
127 elements.extend_from_slice(note_header.details_commitment().as_elements());
128 elements.extend_from_slice(note_header.metadata().to_commitment().as_elements());
129 }
130
131 Hasher::hash_elements(&elements)
132 }
133}
134
135impl<N> IntoIterator for OutputNoteCollection<N> {
136 type Item = N;
137
138 type IntoIter = alloc::vec::IntoIter<N>;
139
140 fn into_iter(self) -> Self::IntoIter {
141 self.notes.into_iter()
142 }
143}
144
145impl<N: Serializable> Serializable for OutputNoteCollection<N> {
149 fn write_into<W: ByteWriter>(&self, target: &mut W) {
150 assert!(self.notes.len() <= u16::MAX.into());
152 target.write_u16(self.notes.len() as u16);
153 target.write_many(&self.notes);
154 }
155}
156
157impl<N> Deserializable for OutputNoteCollection<N>
158where
159 N: Deserializable,
160 for<'a> &'a NoteHeader: From<&'a N>,
161 for<'a> NoteId: From<&'a N>,
162{
163 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
164 let num_notes = source.read_u16()?;
165 let notes = source.read_many_iter::<N>(num_notes.into())?.collect::<Result<_, _>>()?;
166 Self::new(notes).map_err(|err| DeserializationError::InvalidValue(err.to_string()))
167 }
168}
169
170pub type RawOutputNotes = OutputNoteCollection<RawOutputNote>;
178
179#[derive(Debug, Clone, PartialEq, Eq)]
190pub enum RawOutputNote {
191 Full(Note),
192 Partial(PartialNote),
193}
194
195impl RawOutputNote {
196 const FULL: u8 = 0;
197 const PARTIAL: u8 = 1;
198
199 pub fn assets(&self) -> &NoteAssets {
201 match self {
202 Self::Full(note) => note.assets(),
203 Self::Partial(note) => note.assets(),
204 }
205 }
206
207 pub fn id(&self) -> NoteId {
211 match self {
212 Self::Full(note) => note.id(),
213 Self::Partial(note) => note.id(),
214 }
215 }
216
217 pub fn details_commitment(&self) -> NoteDetailsCommitment {
219 match self {
220 Self::Full(note) => note.details_commitment(),
221 Self::Partial(note) => note.details_commitment(),
222 }
223 }
224
225 pub fn recipient(&self) -> Option<&NoteRecipient> {
230 match self {
231 Self::Full(note) => Some(note.recipient()),
232 Self::Partial(_) => None,
233 }
234 }
235
236 pub fn recipient_digest(&self) -> Word {
240 match self {
241 RawOutputNote::Full(note) => note.recipient().digest(),
242 RawOutputNote::Partial(note) => note.recipient_digest(),
243 }
244 }
245
246 pub fn metadata(&self) -> &NoteMetadata {
248 match self {
249 Self::Full(note) => note.metadata(),
250 Self::Partial(note) => note.metadata(),
251 }
252 }
253
254 pub fn into_output_note(self) -> Result<OutputNote, OutputNoteError> {
264 match self {
265 Self::Full(note) if note.metadata().is_private() => {
266 let details_commitment = note.details_commitment();
267 let (_, metadata, _, attachments) = note.into_parts();
268 let note_header = NoteHeader::new(details_commitment, metadata);
269 Ok(OutputNote::Private(PrivateOutputNote::new(note_header, attachments)?))
270 },
271 Self::Full(note) => Ok(OutputNote::Public(PublicOutputNote::new(note)?)),
272 Self::Partial(note) => {
273 let (_, header, attachments) = note.into_parts();
274 Ok(OutputNote::Private(PrivateOutputNote::new(header, attachments)?))
275 },
276 }
277 }
278
279 pub fn header(&self) -> &NoteHeader {
281 match self {
282 Self::Full(note) => note.header(),
283 Self::Partial(note) => note.header(),
284 }
285 }
286
287 pub fn attachments(&self) -> &NoteAttachments {
289 match self {
290 Self::Full(note) => note.attachments(),
291 Self::Partial(note) => note.attachments(),
292 }
293 }
294}
295
296impl From<&RawOutputNote> for NoteId {
297 fn from(note: &RawOutputNote) -> Self {
298 note.id()
299 }
300}
301
302impl<'note> From<&'note RawOutputNote> for &'note NoteHeader {
303 fn from(note: &'note RawOutputNote) -> Self {
304 note.header()
305 }
306}
307
308impl Serializable for RawOutputNote {
309 fn write_into<W: ByteWriter>(&self, target: &mut W) {
310 match self {
311 Self::Full(note) => {
312 target.write(Self::FULL);
313 target.write(note);
314 },
315 Self::Partial(note) => {
316 target.write(Self::PARTIAL);
317 target.write(note);
318 },
319 }
320 }
321
322 fn get_size_hint(&self) -> usize {
323 let tag_size = 0u8.get_size_hint();
325
326 match self {
327 Self::Full(note) => tag_size + note.get_size_hint(),
328 Self::Partial(note) => tag_size + note.get_size_hint(),
329 }
330 }
331}
332
333impl Deserializable for RawOutputNote {
334 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
335 match source.read_u8()? {
336 Self::FULL => Ok(Self::Full(Note::read_from(source)?)),
337 Self::PARTIAL => Ok(Self::Partial(PartialNote::read_from(source)?)),
338 v => Err(DeserializationError::InvalidValue(format!("invalid output note type: {v}"))),
339 }
340 }
341}
342
343pub type OutputNotes = OutputNoteCollection<OutputNote>;
351
352#[allow(clippy::large_enum_variant)]
360#[derive(Debug, Clone, PartialEq, Eq)]
361pub enum OutputNote {
362 Public(PublicOutputNote),
364 Private(PrivateOutputNote),
366}
367
368impl OutputNote {
369 const PUBLIC: u8 = 0;
370 const PRIVATE: u8 = 1;
371
372 pub fn id(&self) -> NoteId {
376 match self {
377 Self::Public(note) => note.id(),
378 Self::Private(header) => header.id(),
379 }
380 }
381
382 pub fn details_commitment(&self) -> NoteDetailsCommitment {
384 match self {
385 Self::Public(note) => note.details_commitment(),
386 Self::Private(header) => header.details_commitment(),
387 }
388 }
389
390 pub fn assets(&self) -> Option<&NoteAssets> {
394 match self {
395 Self::Public(note) => Some(note.assets()),
396 Self::Private(_) => None,
397 }
398 }
399
400 pub fn metadata(&self) -> &NoteMetadata {
402 <&NoteHeader>::from(self).metadata()
403 }
404
405 pub fn recipient(&self) -> Option<&NoteRecipient> {
407 match self {
408 Self::Public(note) => Some(note.recipient()),
409 Self::Private(_) => None,
410 }
411 }
412}
413
414impl<'note> From<&'note OutputNote> for &'note NoteHeader {
418 fn from(note: &'note OutputNote) -> Self {
419 match note {
420 OutputNote::Public(public_note) => public_note.header(),
421 OutputNote::Private(private_note) => private_note.header(),
422 }
423 }
424}
425
426impl From<&OutputNote> for NoteId {
427 fn from(note: &OutputNote) -> Self {
428 note.id()
429 }
430}
431
432impl Serializable for OutputNote {
436 fn write_into<W: ByteWriter>(&self, target: &mut W) {
437 match self {
438 Self::Public(note) => {
439 target.write(Self::PUBLIC);
440 target.write(note);
441 },
442 Self::Private(header) => {
443 target.write(Self::PRIVATE);
444 target.write(header);
445 },
446 }
447 }
448
449 fn get_size_hint(&self) -> usize {
450 let tag_size = 0u8.get_size_hint();
451 match self {
452 Self::Public(note) => tag_size + note.get_size_hint(),
453 Self::Private(header) => tag_size + header.get_size_hint(),
454 }
455 }
456}
457
458impl Deserializable for OutputNote {
459 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
460 match source.read_u8()? {
461 Self::PUBLIC => Ok(Self::Public(PublicOutputNote::read_from(source)?)),
462 Self::PRIVATE => Ok(Self::Private(PrivateOutputNote::read_from(source)?)),
463 v => Err(DeserializationError::InvalidValue(format!(
464 "invalid proven output note type: {v}"
465 ))),
466 }
467 }
468}
469
470#[derive(Debug, Clone, PartialEq, Eq)]
482pub struct PublicOutputNote(Note);
483
484impl PublicOutputNote {
485 pub fn new(mut note: Note) -> Result<Self, OutputNoteError> {
492 if note.metadata().is_private() {
494 return Err(OutputNoteError::NoteIsPrivate(note.id()));
495 }
496
497 note.minify_script();
499
500 let note_size = note.get_size_hint();
502 if note_size > NOTE_MAX_SIZE as usize {
503 return Err(OutputNoteError::NoteSizeLimitExceeded { note_id: note.id(), note_size });
504 }
505
506 Ok(Self(note))
507 }
508
509 pub fn id(&self) -> NoteId {
511 self.0.id()
512 }
513
514 pub fn details_commitment(&self) -> NoteDetailsCommitment {
516 self.0.details_commitment()
517 }
518
519 pub fn metadata(&self) -> &NoteMetadata {
521 self.0.metadata()
522 }
523
524 pub fn assets(&self) -> &NoteAssets {
526 self.0.assets()
527 }
528
529 pub fn recipient(&self) -> &NoteRecipient {
531 self.0.recipient()
532 }
533
534 pub fn header(&self) -> &NoteHeader {
536 self.0.header()
537 }
538
539 pub fn as_note(&self) -> &Note {
541 &self.0
542 }
543
544 pub fn into_note(self) -> Note {
546 self.0
547 }
548}
549
550impl Serializable for PublicOutputNote {
551 fn write_into<W: ByteWriter>(&self, target: &mut W) {
552 self.0.write_into(target);
553 }
554
555 fn get_size_hint(&self) -> usize {
556 self.0.get_size_hint()
557 }
558}
559
560impl Deserializable for PublicOutputNote {
561 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
562 let note = Note::read_from(source)?;
563 Self::new(note).map_err(|err| DeserializationError::InvalidValue(err.to_string()))
564 }
565}
566
567#[derive(Debug, Clone, PartialEq, Eq)]
572pub struct PrivateOutputNote {
573 header: NoteHeader,
574 attachments: NoteAttachments,
575}
576
577impl PrivateOutputNote {
578 pub fn new(header: NoteHeader, attachments: NoteAttachments) -> Result<Self, OutputNoteError> {
584 if header.metadata().is_public() {
585 return Err(OutputNoteError::NoteIsPublic(header.id()));
586 }
587
588 Ok(Self { header, attachments })
589 }
590
591 pub fn id(&self) -> NoteId {
595 self.header.id()
596 }
597
598 pub fn metadata(&self) -> &NoteMetadata {
600 self.header.metadata()
601 }
602
603 pub fn attachments(&self) -> &NoteAttachments {
605 &self.attachments
606 }
607
608 pub fn details_commitment(&self) -> NoteDetailsCommitment {
610 self.header.details_commitment()
611 }
612
613 pub fn header(&self) -> &NoteHeader {
615 &self.header
616 }
617
618 pub fn into_header(self) -> NoteHeader {
620 self.header
621 }
622}
623
624impl Serializable for PrivateOutputNote {
625 fn write_into<W: ByteWriter>(&self, target: &mut W) {
626 self.header.write_into(target);
627 self.attachments.write_into(target);
628 }
629
630 fn get_size_hint(&self) -> usize {
631 self.header.get_size_hint() + self.attachments.get_size_hint()
632 }
633}
634
635impl Deserializable for PrivateOutputNote {
636 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
637 let header = NoteHeader::read_from(source)?;
638 let attachments = NoteAttachments::read_from(source)?;
639 Self::new(header, attachments)
640 .map_err(|err| DeserializationError::InvalidValue(err.to_string()))
641 }
642}