1use alloc::string::ToString;
2use alloc::vec::Vec;
3
4use crate::crypto::SequentialCommit;
5use crate::errors::NoteError;
6use crate::utils::serde::{
7 ByteReader,
8 ByteWriter,
9 Deserializable,
10 DeserializationError,
11 Serializable,
12};
13use crate::{Felt, Hasher, Word};
14
15#[derive(Debug, Clone, Default, PartialEq, Eq)]
44pub struct NoteAttachment {
45 attachment_scheme: NoteAttachmentScheme,
46 content: NoteAttachmentContent,
47}
48
49impl NoteAttachment {
50 pub fn new(
61 attachment_scheme: NoteAttachmentScheme,
62 content: NoteAttachmentContent,
63 ) -> Result<Self, NoteError> {
64 if content.attachment_kind().is_none() && !attachment_scheme.is_none() {
65 return Err(NoteError::AttachmentKindNoneMustHaveAttachmentSchemeNone);
66 }
67
68 Ok(Self { attachment_scheme, content })
69 }
70
71 pub fn new_word(attachment_scheme: NoteAttachmentScheme, word: Word) -> Self {
74 Self {
75 attachment_scheme,
76 content: NoteAttachmentContent::new_word(word),
77 }
78 }
79
80 pub fn new_array(
88 attachment_scheme: NoteAttachmentScheme,
89 elements: Vec<Felt>,
90 ) -> Result<Self, NoteError> {
91 NoteAttachmentContent::new_array(elements)
92 .map(|content| Self { attachment_scheme, content })
93 }
94
95 pub fn attachment_scheme(&self) -> NoteAttachmentScheme {
100 self.attachment_scheme
101 }
102
103 pub fn attachment_kind(&self) -> NoteAttachmentKind {
105 self.content.attachment_kind()
106 }
107
108 pub fn content(&self) -> &NoteAttachmentContent {
110 &self.content
111 }
112}
113
114impl Serializable for NoteAttachment {
115 fn write_into<W: ByteWriter>(&self, target: &mut W) {
116 self.attachment_scheme().write_into(target);
117 self.content().write_into(target);
118 }
119
120 fn get_size_hint(&self) -> usize {
121 self.attachment_scheme().get_size_hint() + self.content().get_size_hint()
122 }
123}
124
125impl Deserializable for NoteAttachment {
126 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
127 let attachment_scheme = NoteAttachmentScheme::read_from(source)?;
128 let content = NoteAttachmentContent::read_from(source)?;
129
130 Self::new(attachment_scheme, content)
131 .map_err(|err| DeserializationError::InvalidValue(err.to_string()))
132 }
133}
134
135#[derive(Debug, Clone, Default, PartialEq, Eq)]
147pub enum NoteAttachmentContent {
148 #[default]
150 None,
151
152 Word(Word),
154
155 Array(NoteAttachmentArray),
157}
158
159impl NoteAttachmentContent {
160 pub fn empty_word() -> Self {
165 Self::Word(Word::empty())
166 }
167
168 pub fn new_word(word: Word) -> Self {
170 Self::Word(word)
171 }
172
173 pub fn new_array(elements: Vec<Felt>) -> Result<Self, NoteError> {
180 NoteAttachmentArray::new(elements).map(Self::from)
181 }
182
183 pub fn attachment_kind(&self) -> NoteAttachmentKind {
188 match self {
189 NoteAttachmentContent::None => NoteAttachmentKind::None,
190 NoteAttachmentContent::Word(_) => NoteAttachmentKind::Word,
191 NoteAttachmentContent::Array(_) => NoteAttachmentKind::Array,
192 }
193 }
194
195 pub fn to_word(&self) -> Word {
199 match self {
200 NoteAttachmentContent::None => Word::empty(),
201 NoteAttachmentContent::Word(word) => *word,
202 NoteAttachmentContent::Array(attachment_commitment) => {
203 attachment_commitment.commitment()
204 },
205 }
206 }
207}
208
209impl Serializable for NoteAttachmentContent {
210 fn write_into<W: ByteWriter>(&self, target: &mut W) {
211 self.attachment_kind().write_into(target);
212
213 match self {
214 NoteAttachmentContent::None => (),
215 NoteAttachmentContent::Word(word) => {
216 word.write_into(target);
217 },
218 NoteAttachmentContent::Array(attachment_commitment) => {
219 attachment_commitment.num_elements().write_into(target);
220 target.write_many(&attachment_commitment.elements);
221 },
222 }
223 }
224
225 fn get_size_hint(&self) -> usize {
226 let kind_size = self.attachment_kind().get_size_hint();
227 match self {
228 NoteAttachmentContent::None => kind_size,
229 NoteAttachmentContent::Word(word) => kind_size + word.get_size_hint(),
230 NoteAttachmentContent::Array(attachment_commitment) => {
231 kind_size
232 + attachment_commitment.num_elements().get_size_hint()
233 + attachment_commitment.elements.len() * crate::ZERO.get_size_hint()
234 },
235 }
236 }
237}
238
239impl Deserializable for NoteAttachmentContent {
240 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
241 let attachment_kind = NoteAttachmentKind::read_from(source)?;
242
243 match attachment_kind {
244 NoteAttachmentKind::None => Ok(NoteAttachmentContent::None),
245 NoteAttachmentKind::Word => {
246 let word = Word::read_from(source)?;
247 Ok(NoteAttachmentContent::Word(word))
248 },
249 NoteAttachmentKind::Array => {
250 let num_elements = u16::read_from(source)?;
251 let elements =
252 source.read_many_iter(num_elements as usize)?.collect::<Result<_, _>>()?;
253 Self::new_array(elements)
254 .map_err(|err| DeserializationError::InvalidValue(err.to_string()))
255 },
256 }
257 }
258}
259
260#[derive(Debug, Clone, PartialEq, Eq)]
266pub struct NoteAttachmentArray {
267 elements: Vec<Felt>,
268 commitment: Word,
269}
270
271impl NoteAttachmentArray {
272 pub const MAX_NUM_ELEMENTS: u16 = 2048;
280
281 pub fn new(elements: Vec<Felt>) -> Result<Self, NoteError> {
291 if elements.len() > Self::MAX_NUM_ELEMENTS as usize {
292 return Err(NoteError::NoteAttachmentArraySizeExceeded(elements.len()));
293 }
294
295 let commitment = Hasher::hash_elements(&elements);
296 Ok(Self { elements, commitment })
297 }
298
299 pub fn as_slice(&self) -> &[Felt] {
304 &self.elements
305 }
306
307 pub fn num_elements(&self) -> u16 {
309 u16::try_from(self.elements.len()).expect("type should enforce that size fits in u16")
310 }
311
312 pub fn commitment(&self) -> Word {
314 self.commitment
315 }
316}
317
318impl SequentialCommit for NoteAttachmentArray {
319 type Commitment = Word;
320
321 fn to_elements(&self) -> Vec<Felt> {
322 self.elements.clone()
323 }
324
325 fn to_commitment(&self) -> Self::Commitment {
326 self.commitment
327 }
328}
329
330impl From<NoteAttachmentArray> for NoteAttachmentContent {
331 fn from(array: NoteAttachmentArray) -> Self {
332 NoteAttachmentContent::Array(array)
333 }
334}
335
336#[derive(Debug, Clone, Copy, PartialEq, Eq)]
347pub struct NoteAttachmentScheme(u32);
348
349impl NoteAttachmentScheme {
350 const NONE: u32 = 0;
355
356 pub const fn new(attachment_scheme: u32) -> Self {
361 Self(attachment_scheme)
362 }
363
364 pub const fn none() -> Self {
366 Self(Self::NONE)
367 }
368
369 pub const fn is_none(&self) -> bool {
372 self.0 == Self::NONE
373 }
374
375 pub const fn as_u32(&self) -> u32 {
380 self.0
381 }
382}
383
384impl Default for NoteAttachmentScheme {
385 fn default() -> Self {
387 Self::none()
388 }
389}
390
391impl core::fmt::Display for NoteAttachmentScheme {
392 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
393 f.write_fmt(format_args!("{}", self.0))
394 }
395}
396
397impl Serializable for NoteAttachmentScheme {
398 fn write_into<W: ByteWriter>(&self, target: &mut W) {
399 self.as_u32().write_into(target);
400 }
401
402 fn get_size_hint(&self) -> usize {
403 core::mem::size_of::<u32>()
404 }
405}
406
407impl Deserializable for NoteAttachmentScheme {
408 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
409 let attachment_scheme = u32::read_from(source)?;
410 Ok(Self::new(attachment_scheme))
411 }
412}
413
414#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
421#[repr(u8)]
422pub enum NoteAttachmentKind {
423 #[default]
425 None = Self::NONE,
426
427 Word = Self::WORD,
429
430 Array = Self::ARRAY,
432}
433
434impl NoteAttachmentKind {
435 const NONE: u8 = 0;
439 const WORD: u8 = 1;
440 const ARRAY: u8 = 2;
441
442 pub const fn as_u8(&self) -> u8 {
447 *self as u8
448 }
449
450 pub const fn is_none(&self) -> bool {
452 matches!(self, Self::None)
453 }
454
455 pub const fn is_word(&self) -> bool {
457 matches!(self, Self::Word)
458 }
459
460 pub const fn is_array(&self) -> bool {
462 matches!(self, Self::Array)
463 }
464}
465
466impl TryFrom<u8> for NoteAttachmentKind {
467 type Error = NoteError;
468
469 fn try_from(value: u8) -> Result<Self, Self::Error> {
470 match value {
471 Self::NONE => Ok(Self::None),
472 Self::WORD => Ok(Self::Word),
473 Self::ARRAY => Ok(Self::Array),
474 _ => Err(NoteError::UnknownNoteAttachmentKind(value)),
475 }
476 }
477}
478
479impl core::fmt::Display for NoteAttachmentKind {
480 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
481 let output = match self {
482 NoteAttachmentKind::None => "None",
483 NoteAttachmentKind::Word => "Word",
484 NoteAttachmentKind::Array => "Array",
485 };
486
487 f.write_str(output)
488 }
489}
490
491impl Serializable for NoteAttachmentKind {
492 fn write_into<W: ByteWriter>(&self, target: &mut W) {
493 self.as_u8().write_into(target);
494 }
495
496 fn get_size_hint(&self) -> usize {
497 core::mem::size_of::<u8>()
498 }
499}
500
501impl Deserializable for NoteAttachmentKind {
502 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
503 let attachment_kind = u8::read_from(source)?;
504 Self::try_from(attachment_kind)
505 .map_err(|err| DeserializationError::InvalidValue(err.to_string()))
506 }
507}
508
509#[cfg(test)]
513mod tests {
514 use assert_matches::assert_matches;
515
516 use super::*;
517
518 #[rstest::rstest]
519 #[case::attachment_none(NoteAttachment::default())]
520 #[case::attachment_word(NoteAttachment::new_word(NoteAttachmentScheme::new(1), Word::from([3, 4, 5, 6u32])))]
521 #[case::attachment_array(NoteAttachment::new_array(
522 NoteAttachmentScheme::new(u32::MAX),
523 vec![Felt::new(5), Felt::new(6), Felt::new(7)],
524 )?)]
525 #[test]
526 fn note_attachment_serde(#[case] attachment: NoteAttachment) -> anyhow::Result<()> {
527 assert_eq!(attachment, NoteAttachment::read_from_bytes(&attachment.to_bytes())?);
528 Ok(())
529 }
530
531 #[test]
532 fn note_attachment_commitment_fails_on_too_many_elements() -> anyhow::Result<()> {
533 let too_many_elements = (NoteAttachmentArray::MAX_NUM_ELEMENTS as usize) + 1;
534 let elements = vec![Felt::from(1u32); too_many_elements];
535 let err = NoteAttachmentArray::new(elements).unwrap_err();
536
537 assert_matches!(err, NoteError::NoteAttachmentArraySizeExceeded(len) => {
538 len == too_many_elements
539 });
540
541 Ok(())
542 }
543
544 #[test]
545 fn note_attachment_kind_fails_on_unknown_variant() -> anyhow::Result<()> {
546 let err = NoteAttachmentKind::try_from(3u8).unwrap_err();
547 assert_matches!(err, NoteError::UnknownNoteAttachmentKind(3u8));
548 Ok(())
549 }
550}