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 NoteHeader,
12 NoteId,
13 NoteMetadata,
14 NoteRecipient,
15 PartialNote,
16 compute_note_commitment,
17};
18use crate::utils::serde::{
19 ByteReader,
20 ByteWriter,
21 Deserializable,
22 DeserializationError,
23 Serializable,
24};
25use crate::{Felt, Hasher, MAX_OUTPUT_NOTES_PER_TX, Word};
26
27#[derive(Debug, Clone, PartialEq, Eq)]
37pub struct OutputNoteCollection<N> {
38 notes: Vec<N>,
39 commitment: Word,
40}
41
42impl<N> OutputNoteCollection<N>
43where
44 for<'a> &'a NoteHeader: From<&'a N>,
45 for<'a> NoteId: From<&'a N>,
46{
47 pub fn new(notes: Vec<N>) -> Result<Self, TransactionOutputError> {
57 if notes.len() > MAX_OUTPUT_NOTES_PER_TX {
58 return Err(TransactionOutputError::TooManyOutputNotes(notes.len()));
59 }
60
61 let mut seen_notes = BTreeSet::new();
62 for note in notes.iter() {
63 let note_id = NoteId::from(note);
64 if !seen_notes.insert(note_id) {
65 return Err(TransactionOutputError::DuplicateOutputNote(note_id));
66 }
67 }
68
69 let commitment = Self::compute_commitment(notes.iter().map(<&NoteHeader>::from));
70
71 Ok(Self { notes, commitment })
72 }
73
74 pub fn commitment(&self) -> Word {
82 self.commitment
83 }
84
85 pub fn num_notes(&self) -> usize {
87 self.notes.len()
88 }
89
90 pub fn is_empty(&self) -> bool {
92 self.notes.is_empty()
93 }
94
95 pub fn get_note(&self, idx: usize) -> &N {
97 &self.notes[idx]
98 }
99
100 pub fn iter(&self) -> impl Iterator<Item = &N> {
105 self.notes.iter()
106 }
107
108 pub(crate) fn compute_commitment<'header>(
118 notes: impl ExactSizeIterator<Item = &'header NoteHeader>,
119 ) -> Word {
120 if notes.len() == 0 {
121 return Word::empty();
122 }
123
124 let mut elements: Vec<Felt> = Vec::with_capacity(notes.len() * 8);
125 for note_header in notes {
126 elements.extend_from_slice(note_header.id().as_elements());
127 elements.extend_from_slice(note_header.metadata().to_commitment().as_elements());
128 }
129
130 Hasher::hash_elements(&elements)
131 }
132}
133
134impl<N> IntoIterator for OutputNoteCollection<N> {
135 type Item = N;
136
137 type IntoIter = alloc::vec::IntoIter<N>;
138
139 fn into_iter(self) -> Self::IntoIter {
140 self.notes.into_iter()
141 }
142}
143
144impl<N: Serializable> Serializable for OutputNoteCollection<N> {
148 fn write_into<W: ByteWriter>(&self, target: &mut W) {
149 assert!(self.notes.len() <= u16::MAX.into());
151 target.write_u16(self.notes.len() as u16);
152 target.write_many(&self.notes);
153 }
154}
155
156impl<N> Deserializable for OutputNoteCollection<N>
157where
158 N: Deserializable,
159 for<'a> &'a NoteHeader: From<&'a N>,
160 for<'a> NoteId: From<&'a N>,
161{
162 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
163 let num_notes = source.read_u16()?;
164 let notes = source.read_many_iter::<N>(num_notes.into())?.collect::<Result<_, _>>()?;
165 Self::new(notes).map_err(|err| DeserializationError::InvalidValue(err.to_string()))
166 }
167}
168
169pub type RawOutputNotes = OutputNoteCollection<RawOutputNote>;
177
178#[derive(Debug, Clone, PartialEq, Eq)]
189pub enum RawOutputNote {
190 Full(Note),
191 Partial(PartialNote),
192}
193
194impl RawOutputNote {
195 const FULL: u8 = 0;
196 const PARTIAL: u8 = 1;
197
198 pub fn assets(&self) -> &NoteAssets {
200 match self {
201 Self::Full(note) => note.assets(),
202 Self::Partial(note) => note.assets(),
203 }
204 }
205
206 pub fn id(&self) -> NoteId {
210 match self {
211 Self::Full(note) => note.id(),
212 Self::Partial(note) => note.id(),
213 }
214 }
215
216 pub fn recipient(&self) -> Option<&NoteRecipient> {
221 match self {
222 Self::Full(note) => Some(note.recipient()),
223 Self::Partial(_) => None,
224 }
225 }
226
227 pub fn recipient_digest(&self) -> Word {
231 match self {
232 RawOutputNote::Full(note) => note.recipient().digest(),
233 RawOutputNote::Partial(note) => note.recipient_digest(),
234 }
235 }
236
237 pub fn metadata(&self) -> &NoteMetadata {
239 match self {
240 Self::Full(note) => note.metadata(),
241 Self::Partial(note) => note.metadata(),
242 }
243 }
244
245 pub fn into_output_note(self) -> Result<OutputNote, OutputNoteError> {
255 match self {
256 Self::Full(note) if note.metadata().is_private() => {
257 let note_id = note.id();
258 let (_, metadata, _) = note.into_parts();
259 let note_header = NoteHeader::new(note_id, metadata);
260 Ok(OutputNote::Private(PrivateNoteHeader::new(note_header)?))
261 },
262 Self::Full(note) => Ok(OutputNote::Public(PublicOutputNote::new(note)?)),
263 Self::Partial(note) => {
264 let (_, header) = note.into_parts();
265 Ok(OutputNote::Private(PrivateNoteHeader::new(header)?))
266 },
267 }
268 }
269
270 pub fn header(&self) -> &NoteHeader {
272 match self {
273 Self::Full(note) => note.header(),
274 Self::Partial(note) => note.header(),
275 }
276 }
277
278 pub fn commitment(&self) -> Word {
282 compute_note_commitment(self.id(), self.metadata())
283 }
284}
285
286impl From<&RawOutputNote> for NoteId {
287 fn from(note: &RawOutputNote) -> Self {
288 note.id()
289 }
290}
291
292impl<'note> From<&'note RawOutputNote> for &'note NoteHeader {
293 fn from(note: &'note RawOutputNote) -> Self {
294 note.header()
295 }
296}
297
298impl Serializable for RawOutputNote {
299 fn write_into<W: ByteWriter>(&self, target: &mut W) {
300 match self {
301 Self::Full(note) => {
302 target.write(Self::FULL);
303 target.write(note);
304 },
305 Self::Partial(note) => {
306 target.write(Self::PARTIAL);
307 target.write(note);
308 },
309 }
310 }
311
312 fn get_size_hint(&self) -> usize {
313 let tag_size = 0u8.get_size_hint();
315
316 match self {
317 Self::Full(note) => tag_size + note.get_size_hint(),
318 Self::Partial(note) => tag_size + note.get_size_hint(),
319 }
320 }
321}
322
323impl Deserializable for RawOutputNote {
324 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
325 match source.read_u8()? {
326 Self::FULL => Ok(Self::Full(Note::read_from(source)?)),
327 Self::PARTIAL => Ok(Self::Partial(PartialNote::read_from(source)?)),
328 v => Err(DeserializationError::InvalidValue(format!("invalid output note type: {v}"))),
329 }
330 }
331}
332
333pub type OutputNotes = OutputNoteCollection<OutputNote>;
341
342#[allow(clippy::large_enum_variant)]
350#[derive(Debug, Clone, PartialEq, Eq)]
351pub enum OutputNote {
352 Public(PublicOutputNote),
354 Private(PrivateNoteHeader),
356}
357
358impl OutputNote {
359 const PUBLIC: u8 = 0;
360 const PRIVATE: u8 = 1;
361
362 pub fn id(&self) -> NoteId {
366 match self {
367 Self::Public(note) => note.id(),
368 Self::Private(header) => header.id(),
369 }
370 }
371
372 pub fn metadata(&self) -> &NoteMetadata {
374 match self {
375 Self::Public(note) => note.metadata(),
376 Self::Private(header) => header.metadata(),
377 }
378 }
379
380 pub fn assets(&self) -> Option<&NoteAssets> {
384 match self {
385 Self::Public(note) => Some(note.assets()),
386 Self::Private(_) => None,
387 }
388 }
389
390 pub fn to_commitment(&self) -> Word {
394 compute_note_commitment(self.id(), self.metadata())
395 }
396
397 pub fn recipient(&self) -> Option<&NoteRecipient> {
399 match self {
400 Self::Public(note) => Some(note.recipient()),
401 Self::Private(_) => None,
402 }
403 }
404}
405
406impl<'note> From<&'note OutputNote> for &'note NoteHeader {
410 fn from(value: &'note OutputNote) -> Self {
411 match value {
412 OutputNote::Public(note) => note.header(),
413 OutputNote::Private(header) => &header.0,
414 }
415 }
416}
417
418impl From<&OutputNote> for NoteId {
419 fn from(value: &OutputNote) -> Self {
420 value.id()
421 }
422}
423
424impl Serializable for OutputNote {
428 fn write_into<W: ByteWriter>(&self, target: &mut W) {
429 match self {
430 Self::Public(note) => {
431 target.write(Self::PUBLIC);
432 target.write(note);
433 },
434 Self::Private(header) => {
435 target.write(Self::PRIVATE);
436 target.write(header);
437 },
438 }
439 }
440
441 fn get_size_hint(&self) -> usize {
442 let tag_size = 0u8.get_size_hint();
443 match self {
444 Self::Public(note) => tag_size + note.get_size_hint(),
445 Self::Private(header) => tag_size + header.get_size_hint(),
446 }
447 }
448}
449
450impl Deserializable for OutputNote {
451 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
452 match source.read_u8()? {
453 Self::PUBLIC => Ok(Self::Public(PublicOutputNote::read_from(source)?)),
454 Self::PRIVATE => Ok(Self::Private(PrivateNoteHeader::read_from(source)?)),
455 v => Err(DeserializationError::InvalidValue(format!(
456 "invalid proven output note type: {v}"
457 ))),
458 }
459 }
460}
461
462#[derive(Debug, Clone, PartialEq, Eq)]
474pub struct PublicOutputNote(Note);
475
476impl PublicOutputNote {
477 pub fn new(mut note: Note) -> Result<Self, OutputNoteError> {
484 if note.metadata().is_private() {
486 return Err(OutputNoteError::NoteIsPrivate(note.id()));
487 }
488
489 note.minify_script();
491
492 let note_size = note.get_size_hint();
494 if note_size > NOTE_MAX_SIZE as usize {
495 return Err(OutputNoteError::NoteSizeLimitExceeded { note_id: note.id(), note_size });
496 }
497
498 Ok(Self(note))
499 }
500
501 pub fn id(&self) -> NoteId {
503 self.0.id()
504 }
505
506 pub fn metadata(&self) -> &NoteMetadata {
508 self.0.metadata()
509 }
510
511 pub fn assets(&self) -> &NoteAssets {
513 self.0.assets()
514 }
515
516 pub fn recipient(&self) -> &NoteRecipient {
518 self.0.recipient()
519 }
520
521 pub fn header(&self) -> &NoteHeader {
523 self.0.header()
524 }
525
526 pub fn as_note(&self) -> &Note {
528 &self.0
529 }
530
531 pub fn into_note(self) -> Note {
533 self.0
534 }
535}
536
537impl Serializable for PublicOutputNote {
538 fn write_into<W: ByteWriter>(&self, target: &mut W) {
539 self.0.write_into(target);
540 }
541
542 fn get_size_hint(&self) -> usize {
543 self.0.get_size_hint()
544 }
545}
546
547impl Deserializable for PublicOutputNote {
548 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
549 let note = Note::read_from(source)?;
550 Self::new(note).map_err(|err| DeserializationError::InvalidValue(err.to_string()))
551 }
552}
553
554#[derive(Debug, Clone, PartialEq, Eq)]
559pub struct PrivateNoteHeader(NoteHeader);
560
561impl PrivateNoteHeader {
562 pub fn new(header: NoteHeader) -> Result<Self, OutputNoteError> {
568 if !header.metadata().is_private() {
569 return Err(OutputNoteError::NoteIsPublic(header.id()));
570 }
571
572 Ok(Self(header))
573 }
574
575 pub fn id(&self) -> NoteId {
579 self.0.id()
580 }
581
582 pub fn metadata(&self) -> &NoteMetadata {
584 self.0.metadata()
585 }
586
587 pub fn into_metadata(self) -> NoteMetadata {
589 self.0.into_metadata()
590 }
591
592 pub fn commitment(&self) -> Word {
599 self.0.to_commitment()
600 }
601
602 pub fn as_header(&self) -> &NoteHeader {
604 &self.0
605 }
606
607 pub fn into_header(self) -> NoteHeader {
609 self.0
610 }
611}
612
613impl Serializable for PrivateNoteHeader {
614 fn write_into<W: ByteWriter>(&self, target: &mut W) {
615 self.0.write_into(target);
616 }
617
618 fn get_size_hint(&self) -> usize {
619 self.0.get_size_hint()
620 }
621}
622
623impl Deserializable for PrivateNoteHeader {
624 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
625 let header = NoteHeader::read_from(source)?;
626 Self::new(header).map_err(|err| DeserializationError::InvalidValue(err.to_string()))
627 }
628}