Skip to main content

miden_protocol/note/
recipient.rs

1use alloc::vec::Vec;
2use core::fmt::Debug;
3
4use super::{
5    ByteReader,
6    ByteWriter,
7    Deserializable,
8    DeserializationError,
9    Hasher,
10    NoteScript,
11    NoteStorage,
12    Serializable,
13    Word,
14};
15use crate::Felt;
16
17/// Value that describes under which condition a note can be consumed.
18///
19/// The recipient is not an account address, instead it is a value that describes when a note
20/// can be consumed. Because not all notes have predetermined consumer addresses, e.g. swap
21/// notes can be consumed by anyone, the recipient is defined as the code and its storage, that
22/// when successfully executed results in the note's consumption.
23///
24/// Recipient is computed as:
25///
26/// > hash(hash(hash(serial_num, [0; 4]), script_root), storage_commitment)
27#[derive(Clone, Debug, PartialEq, Eq)]
28pub struct NoteRecipient {
29    serial_num: Word,
30    script: NoteScript,
31    storage: NoteStorage,
32    digest: Word,
33}
34
35impl NoteRecipient {
36    pub fn new(serial_num: Word, script: NoteScript, storage: NoteStorage) -> Self {
37        let (_, _, digest) = compute_recipient_chain(serial_num, &script, &storage);
38        Self { serial_num, script, storage, digest }
39    }
40
41    // PUBLIC ACCESSORS
42    // --------------------------------------------------------------------------------------------
43
44    /// The recipient's serial_num, the secret required to consume the note.
45    pub fn serial_num(&self) -> Word {
46        self.serial_num
47    }
48
49    /// The recipients's script which locks the assets of this note.
50    pub fn script(&self) -> &NoteScript {
51        &self.script
52    }
53
54    /// The recipient's storage which customizes the script's behavior.
55    pub fn storage(&self) -> &NoteStorage {
56        &self.storage
57    }
58
59    /// The recipient's digest, which commits to its details.
60    ///
61    /// This is the public data required to create a note.
62    pub fn digest(&self) -> Word {
63        self.digest
64    }
65
66    /// Returns the advice map entries opening every link of the recipient's hash chain.
67    ///
68    /// They allow the VM to recover the note's script root and storage commitment from the
69    /// recipient alone, which is all a note is committed to on chain.
70    pub fn to_advice_map_entries(&self) -> [(Word, Vec<Felt>); 5] {
71        let (serial_commitment, serial_script_commitment, digest) =
72            compute_recipient_chain(self.serial_num, &self.script, &self.storage);
73        let script_root = Word::from(self.script.root());
74        let script_encoded = <Vec<Felt>>::from(&self.script);
75
76        [
77            (serial_commitment, concat_words(self.serial_num, Word::empty())),
78            (serial_script_commitment, concat_words(serial_commitment, script_root)),
79            (digest, concat_words(serial_script_commitment, self.storage.commitment())),
80            (self.storage().commitment(), self.storage().to_elements()),
81            (script_root, script_encoded),
82        ]
83    }
84
85    // MUTATORS
86    // --------------------------------------------------------------------------------------------
87
88    /// Removes debug info associated with the script, if any.
89    pub fn clear_debug_info(&mut self) {
90        self.script.clear_debug_info();
91    }
92
93    /// Consumes self and returns the underlying parts of the [`NoteRecipient`].
94    pub fn into_parts(self) -> (Word, NoteScript, NoteStorage) {
95        (self.serial_num, self.script, self.storage)
96    }
97}
98
99/// Returns the links of the recipient's hash chain: the serial commitment, the serial-script
100/// commitment and the recipient digest itself.
101fn compute_recipient_chain(
102    serial_num: Word,
103    script: &NoteScript,
104    storage: &NoteStorage,
105) -> (Word, Word, Word) {
106    let serial_commitment = Hasher::merge(&[serial_num, Word::empty()]);
107    let serial_script_commitment = Hasher::merge(&[serial_commitment, script.root().into()]);
108    let recipient_digest = Hasher::merge(&[serial_script_commitment, storage.commitment()]);
109
110    (serial_commitment, serial_script_commitment, recipient_digest)
111}
112
113fn concat_words(first: Word, second: Word) -> Vec<Felt> {
114    let mut elements = Vec::with_capacity(2 * Word::NUM_ELEMENTS);
115    elements.extend(first);
116    elements.extend(second);
117    elements
118}
119
120// SERIALIZATION
121// ================================================================================================
122
123impl Serializable for NoteRecipient {
124    fn write_into<W: ByteWriter>(&self, target: &mut W) {
125        let Self {
126            script,
127            storage,
128            serial_num,
129
130            // These attributes don't have to be serialized, they can be re-computed from the rest
131            // of the data
132            digest: _,
133        } = self;
134
135        script.write_into(target);
136        storage.write_into(target);
137        serial_num.write_into(target);
138    }
139
140    fn get_size_hint(&self) -> usize {
141        self.script.get_size_hint() + self.storage.get_size_hint() + Word::SERIALIZED_SIZE
142    }
143}
144
145impl Deserializable for NoteRecipient {
146    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
147        let script = NoteScript::read_from(source)?;
148        let storage = NoteStorage::read_from(source)?;
149        let serial_num = Word::read_from(source)?;
150
151        Ok(Self::new(serial_num, script, storage))
152    }
153}