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: Serializable> Serializable for OutputNoteCollection<N> {
138 fn write_into<W: ByteWriter>(&self, target: &mut W) {
139 assert!(self.notes.len() <= u16::MAX.into());
141 target.write_u16(self.notes.len() as u16);
142 target.write_many(&self.notes);
143 }
144}
145
146impl<N> Deserializable for OutputNoteCollection<N>
147where
148 N: Deserializable,
149 for<'a> &'a NoteHeader: From<&'a N>,
150 for<'a> NoteId: From<&'a N>,
151{
152 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
153 let num_notes = source.read_u16()?;
154 let notes = source.read_many_iter::<N>(num_notes.into())?.collect::<Result<_, _>>()?;
155 Self::new(notes).map_err(|err| DeserializationError::InvalidValue(err.to_string()))
156 }
157}
158
159pub type RawOutputNotes = OutputNoteCollection<RawOutputNote>;
167
168#[derive(Debug, Clone, PartialEq, Eq)]
179pub enum RawOutputNote {
180 Full(Note),
181 Partial(PartialNote),
182}
183
184impl RawOutputNote {
185 const FULL: u8 = 0;
186 const PARTIAL: u8 = 1;
187
188 pub fn assets(&self) -> &NoteAssets {
190 match self {
191 Self::Full(note) => note.assets(),
192 Self::Partial(note) => note.assets(),
193 }
194 }
195
196 pub fn id(&self) -> NoteId {
200 match self {
201 Self::Full(note) => note.id(),
202 Self::Partial(note) => note.id(),
203 }
204 }
205
206 pub fn recipient(&self) -> Option<&NoteRecipient> {
211 match self {
212 Self::Full(note) => Some(note.recipient()),
213 Self::Partial(_) => None,
214 }
215 }
216
217 pub fn recipient_digest(&self) -> Word {
221 match self {
222 RawOutputNote::Full(note) => note.recipient().digest(),
223 RawOutputNote::Partial(note) => note.recipient_digest(),
224 }
225 }
226
227 pub fn metadata(&self) -> &NoteMetadata {
229 match self {
230 Self::Full(note) => note.metadata(),
231 Self::Partial(note) => note.metadata(),
232 }
233 }
234
235 pub fn to_output_note(&self) -> Result<OutputNote, OutputNoteError> {
245 match self {
246 Self::Full(note) if note.metadata().is_private() => {
247 Ok(OutputNote::Private(PrivateNoteHeader::new(note.header().clone())?))
248 },
249 Self::Full(note) => Ok(OutputNote::Public(PublicOutputNote::new(note.clone())?)),
250 Self::Partial(note) => {
251 Ok(OutputNote::Private(PrivateNoteHeader::new(note.header().clone())?))
252 },
253 }
254 }
255
256 pub fn header(&self) -> &NoteHeader {
258 match self {
259 Self::Full(note) => note.header(),
260 Self::Partial(note) => note.header(),
261 }
262 }
263
264 pub fn commitment(&self) -> Word {
268 compute_note_commitment(self.id(), self.metadata())
269 }
270}
271
272impl From<&RawOutputNote> for NoteId {
273 fn from(note: &RawOutputNote) -> Self {
274 note.id()
275 }
276}
277
278impl<'note> From<&'note RawOutputNote> for &'note NoteHeader {
279 fn from(note: &'note RawOutputNote) -> Self {
280 note.header()
281 }
282}
283
284impl Serializable for RawOutputNote {
285 fn write_into<W: ByteWriter>(&self, target: &mut W) {
286 match self {
287 Self::Full(note) => {
288 target.write(Self::FULL);
289 target.write(note);
290 },
291 Self::Partial(note) => {
292 target.write(Self::PARTIAL);
293 target.write(note);
294 },
295 }
296 }
297
298 fn get_size_hint(&self) -> usize {
299 let tag_size = 0u8.get_size_hint();
301
302 match self {
303 Self::Full(note) => tag_size + note.get_size_hint(),
304 Self::Partial(note) => tag_size + note.get_size_hint(),
305 }
306 }
307}
308
309impl Deserializable for RawOutputNote {
310 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
311 match source.read_u8()? {
312 Self::FULL => Ok(Self::Full(Note::read_from(source)?)),
313 Self::PARTIAL => Ok(Self::Partial(PartialNote::read_from(source)?)),
314 v => Err(DeserializationError::InvalidValue(format!("invalid output note type: {v}"))),
315 }
316 }
317}
318
319pub type OutputNotes = OutputNoteCollection<OutputNote>;
327
328#[allow(clippy::large_enum_variant)]
336#[derive(Debug, Clone, PartialEq, Eq)]
337pub enum OutputNote {
338 Public(PublicOutputNote),
340 Private(PrivateNoteHeader),
342}
343
344impl OutputNote {
345 const PUBLIC: u8 = 0;
346 const PRIVATE: u8 = 1;
347
348 pub fn id(&self) -> NoteId {
352 match self {
353 Self::Public(note) => note.id(),
354 Self::Private(header) => header.id(),
355 }
356 }
357
358 pub fn metadata(&self) -> &NoteMetadata {
360 match self {
361 Self::Public(note) => note.metadata(),
362 Self::Private(header) => header.metadata(),
363 }
364 }
365
366 pub fn assets(&self) -> Option<&NoteAssets> {
370 match self {
371 Self::Public(note) => Some(note.assets()),
372 Self::Private(_) => None,
373 }
374 }
375
376 pub fn to_commitment(&self) -> Word {
380 compute_note_commitment(self.id(), self.metadata())
381 }
382
383 pub fn recipient(&self) -> Option<&NoteRecipient> {
385 match self {
386 Self::Public(note) => Some(note.recipient()),
387 Self::Private(_) => None,
388 }
389 }
390}
391
392impl<'note> From<&'note OutputNote> for &'note NoteHeader {
396 fn from(value: &'note OutputNote) -> Self {
397 match value {
398 OutputNote::Public(note) => note.header(),
399 OutputNote::Private(header) => &header.0,
400 }
401 }
402}
403
404impl From<&OutputNote> for NoteId {
405 fn from(value: &OutputNote) -> Self {
406 value.id()
407 }
408}
409
410impl Serializable for OutputNote {
414 fn write_into<W: ByteWriter>(&self, target: &mut W) {
415 match self {
416 Self::Public(note) => {
417 target.write(Self::PUBLIC);
418 target.write(note);
419 },
420 Self::Private(header) => {
421 target.write(Self::PRIVATE);
422 target.write(header);
423 },
424 }
425 }
426
427 fn get_size_hint(&self) -> usize {
428 let tag_size = 0u8.get_size_hint();
429 match self {
430 Self::Public(note) => tag_size + note.get_size_hint(),
431 Self::Private(header) => tag_size + header.get_size_hint(),
432 }
433 }
434}
435
436impl Deserializable for OutputNote {
437 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
438 match source.read_u8()? {
439 Self::PUBLIC => Ok(Self::Public(PublicOutputNote::read_from(source)?)),
440 Self::PRIVATE => Ok(Self::Private(PrivateNoteHeader::read_from(source)?)),
441 v => Err(DeserializationError::InvalidValue(format!(
442 "invalid proven output note type: {v}"
443 ))),
444 }
445 }
446}
447
448#[derive(Debug, Clone, PartialEq, Eq)]
460pub struct PublicOutputNote(Note);
461
462impl PublicOutputNote {
463 pub fn new(mut note: Note) -> Result<Self, OutputNoteError> {
470 if note.metadata().is_private() {
472 return Err(OutputNoteError::NoteIsPrivate(note.id()));
473 }
474
475 note.minify_script();
477
478 let note_size = note.get_size_hint();
480 if note_size > NOTE_MAX_SIZE as usize {
481 return Err(OutputNoteError::NoteSizeLimitExceeded { note_id: note.id(), note_size });
482 }
483
484 Ok(Self(note))
485 }
486
487 pub fn id(&self) -> NoteId {
489 self.0.id()
490 }
491
492 pub fn metadata(&self) -> &NoteMetadata {
494 self.0.metadata()
495 }
496
497 pub fn assets(&self) -> &NoteAssets {
499 self.0.assets()
500 }
501
502 pub fn recipient(&self) -> &NoteRecipient {
504 self.0.recipient()
505 }
506
507 pub fn header(&self) -> &NoteHeader {
509 self.0.header()
510 }
511
512 pub fn as_note(&self) -> &Note {
514 &self.0
515 }
516
517 pub fn into_note(self) -> Note {
519 self.0
520 }
521}
522
523impl Serializable for PublicOutputNote {
524 fn write_into<W: ByteWriter>(&self, target: &mut W) {
525 self.0.write_into(target);
526 }
527
528 fn get_size_hint(&self) -> usize {
529 self.0.get_size_hint()
530 }
531}
532
533impl Deserializable for PublicOutputNote {
534 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
535 let note = Note::read_from(source)?;
536 Self::new(note).map_err(|err| DeserializationError::InvalidValue(err.to_string()))
537 }
538}
539
540#[derive(Debug, Clone, PartialEq, Eq)]
545pub struct PrivateNoteHeader(NoteHeader);
546
547impl PrivateNoteHeader {
548 pub fn new(header: NoteHeader) -> Result<Self, OutputNoteError> {
554 if !header.metadata().is_private() {
555 return Err(OutputNoteError::NoteIsPublic(header.id()));
556 }
557
558 Ok(Self(header))
559 }
560
561 pub fn id(&self) -> NoteId {
565 self.0.id()
566 }
567
568 pub fn metadata(&self) -> &NoteMetadata {
570 self.0.metadata()
571 }
572
573 pub fn into_metadata(self) -> NoteMetadata {
575 self.0.into_metadata()
576 }
577
578 pub fn commitment(&self) -> Word {
585 self.0.commitment()
586 }
587
588 pub fn as_header(&self) -> &NoteHeader {
590 &self.0
591 }
592
593 pub fn into_header(self) -> NoteHeader {
595 self.0
596 }
597}
598
599impl Serializable for PrivateNoteHeader {
600 fn write_into<W: ByteWriter>(&self, target: &mut W) {
601 self.0.write_into(target);
602 }
603
604 fn get_size_hint(&self) -> usize {
605 self.0.get_size_hint()
606 }
607}
608
609impl Deserializable for PrivateNoteHeader {
610 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
611 let header = NoteHeader::read_from(source)?;
612 Self::new(header).map_err(|err| DeserializationError::InvalidValue(err.to_string()))
613 }
614}