miden_objects/note/
file.rs

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
7// NOTE FILE
8// ================================================================================================
9
10/// A serialized representation of a note.
11pub enum NoteFile {
12    /// The note's details aren't known.
13    NoteId(NoteId),
14    /// The note may or may not have already been recorded on chain.
15    ///
16    /// The `after_block_num` specifies the block after which the note is expected to appear on
17    /// chain. Though this should be treated as a hint (i.e., there is no guarantee that the note
18    /// will appear on chain or that it will in fact appear after the specified block).
19    ///
20    /// An optional tag specifies the tag associated with the note, though this also should be
21    /// treated as a hint.
22    NoteDetails {
23        details: NoteDetails,
24        after_block_num: BlockNumber,
25        tag: Option<NoteTag>,
26    },
27    /// The note has been recorded on chain.
28    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
47// SERIALIZATION
48// ================================================================================================
49
50impl 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// TESTS
102// ================================================================================================
103
104#[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}