1use vm_core::utils::{ByteReader, ByteWriter, Deserializable, Serializable};
2use vm_processor::DeserializationError;
3
4use super::{Note, NoteDetails, NoteId, NoteInclusionProof, NoteTag};
5use crate::block::BlockNumber;
6
7pub enum NoteFile {
12 NoteId(NoteId),
14 NoteDetails {
23 details: NoteDetails,
24 after_block_num: BlockNumber,
25 tag: Option<NoteTag>,
26 },
27 NoteWithProof(Note, NoteInclusionProof),
29}
30
31impl From<NoteDetails> for NoteFile {
32 fn from(details: NoteDetails) -> Self {
33 NoteFile::NoteDetails {
34 details,
35 after_block_num: 0.into(),
36 tag: None,
37 }
38 }
39}
40
41impl From<NoteId> for NoteFile {
42 fn from(note_id: NoteId) -> Self {
43 NoteFile::NoteId(note_id)
44 }
45}
46
47impl Serializable for NoteFile {
51 fn write_into<W: ByteWriter>(&self, target: &mut W) {
52 target.write_bytes("note".as_bytes());
53 match self {
54 NoteFile::NoteId(note_id) => {
55 target.write_u8(0);
56 note_id.write_into(target);
57 },
58 NoteFile::NoteDetails { details, after_block_num, tag } => {
59 target.write_u8(1);
60 details.write_into(target);
61 after_block_num.write_into(target);
62 tag.write_into(target);
63 },
64 NoteFile::NoteWithProof(note, proof) => {
65 target.write_u8(2);
66 note.write_into(target);
67 proof.write_into(target);
68 },
69 }
70 }
71}
72
73impl Deserializable for NoteFile {
74 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
75 let magic_value = source.read_string(4)?;
76 if magic_value != "note" {
77 return Err(DeserializationError::InvalidValue(format!(
78 "Invalid note file marker: {magic_value}"
79 )));
80 }
81 match source.read_u8()? {
82 0 => Ok(NoteFile::NoteId(NoteId::read_from(source)?)),
83 1 => {
84 let details = NoteDetails::read_from(source)?;
85 let after_block_num = BlockNumber::read_from(source)?;
86 let tag = Option::<NoteTag>::read_from(source)?;
87 Ok(NoteFile::NoteDetails { details, after_block_num, tag })
88 },
89 2 => {
90 let note = Note::read_from(source)?;
91 let proof = NoteInclusionProof::read_from(source)?;
92 Ok(NoteFile::NoteWithProof(note, proof))
93 },
94 v => {
95 Err(DeserializationError::InvalidValue(format!("unknown variant {v} for NoteFile")))
96 },
97 }
98 }
99}
100
101#[cfg(test)]
105mod tests {
106 use alloc::vec::Vec;
107
108 use vm_core::{
109 utils::{Deserializable, Serializable},
110 Felt,
111 };
112
113 use crate::{
114 account::AccountId,
115 asset::{Asset, FungibleAsset},
116 block::BlockNumber,
117 note::{
118 Note, NoteAssets, NoteFile, NoteInclusionProof, NoteInputs, NoteMetadata,
119 NoteRecipient, NoteScript, NoteTag, NoteType,
120 },
121 testing::account_id::{
122 ACCOUNT_ID_FUNGIBLE_FAUCET_ON_CHAIN,
123 ACCOUNT_ID_REGULAR_ACCOUNT_UPDATABLE_CODE_OFF_CHAIN,
124 },
125 };
126
127 fn create_example_note() -> Note {
128 let faucet = AccountId::try_from(ACCOUNT_ID_FUNGIBLE_FAUCET_ON_CHAIN).unwrap();
129 let target =
130 AccountId::try_from(ACCOUNT_ID_REGULAR_ACCOUNT_UPDATABLE_CODE_OFF_CHAIN).unwrap();
131
132 let serial_num = [Felt::new(0), Felt::new(1), Felt::new(2), Felt::new(3)];
133 let script = NoteScript::mock();
134 let note_inputs = NoteInputs::new(vec![target.prefix().into()]).unwrap();
135 let recipient = NoteRecipient::new(serial_num, script, note_inputs);
136
137 let asset = Asset::Fungible(FungibleAsset::new(faucet, 100).unwrap());
138 let metadata = NoteMetadata::new(
139 faucet,
140 NoteType::Public,
141 NoteTag::from(123),
142 crate::note::NoteExecutionHint::None,
143 Felt::new(0),
144 )
145 .unwrap();
146
147 Note::new(NoteAssets::new(vec![asset]).unwrap(), metadata, recipient)
148 }
149
150 #[test]
151 fn serialized_note_magic() {
152 let note = create_example_note();
153 let file = NoteFile::NoteId(note.id());
154 let mut buffer = Vec::new();
155 file.write_into(&mut buffer);
156
157 let magic_value = &buffer[..4];
158 assert_eq!(magic_value, b"note");
159 }
160
161 #[test]
162 fn serialize_id() {
163 let note = create_example_note();
164 let file = NoteFile::NoteId(note.id());
165 let mut buffer = Vec::new();
166 file.write_into(&mut buffer);
167
168 let file_copy = NoteFile::read_from_bytes(&buffer).unwrap();
169
170 match file_copy {
171 NoteFile::NoteId(note_id) => {
172 assert_eq!(note.id(), note_id);
173 },
174 _ => panic!("Invalid note file variant"),
175 }
176 }
177
178 #[test]
179 fn serialize_details() {
180 let note = create_example_note();
181 let file = NoteFile::NoteDetails {
182 details: note.details.clone(),
183 after_block_num: 456.into(),
184 tag: Some(NoteTag::from(123)),
185 };
186 let mut buffer = Vec::new();
187 file.write_into(&mut buffer);
188
189 let file_copy = NoteFile::read_from_bytes(&buffer).unwrap();
190
191 match file_copy {
192 NoteFile::NoteDetails { details, after_block_num, tag } => {
193 assert_eq!(details, note.details);
194 assert_eq!(after_block_num, 456.into());
195 assert_eq!(tag, Some(NoteTag::from(123)));
196 },
197 _ => panic!("Invalid note file variant"),
198 }
199 }
200
201 #[test]
202 fn serialize_with_proof() {
203 let note = create_example_note();
204 let mock_inclusion_proof =
205 NoteInclusionProof::new(BlockNumber::from(0), 0, Default::default()).unwrap();
206 let file = NoteFile::NoteWithProof(note.clone(), mock_inclusion_proof.clone());
207 let mut buffer = Vec::new();
208 file.write_into(&mut buffer);
209
210 let file_copy = NoteFile::read_from_bytes(&buffer).unwrap();
211
212 match file_copy {
213 NoteFile::NoteWithProof(note_copy, inclusion_proof_copy) => {
214 assert_eq!(note, note_copy);
215 assert_eq!(inclusion_proof_copy, mock_inclusion_proof);
216 },
217 _ => panic!("Invalid note file variant"),
218 }
219 }
220}