Skip to main content

miden_protocol/transaction/
tx_args.rs

1use alloc::collections::BTreeMap;
2use alloc::vec::Vec;
3
4use miden_crypto::merkle::InnerNodeInfo;
5
6use super::script::TransactionScript;
7use super::{Felt, Hasher, Word};
8use crate::EMPTY_WORD;
9use crate::account::AccountCodeUpgrade;
10use crate::account::auth::{PublicKeyCommitment, Signature};
11use crate::note::{NoteId, NoteRecipient};
12use crate::utils::serde::{
13    ByteReader,
14    ByteWriter,
15    Deserializable,
16    DeserializationError,
17    Serializable,
18};
19use crate::vm::{AdviceInputs, AdviceMap};
20
21// TRANSACTION ARGUMENTS
22// ================================================================================================
23
24/// Optional transaction arguments.
25///
26/// - Transaction script: a program that is executed in a transaction after all input notes scripts
27///   have been executed.
28/// - Transaction script arguments: a [`Word`], which will be pushed to the operand stack before the
29///   transaction script execution. If these arguments are not specified, the [`EMPTY_WORD`] would
30///   be used as a default value. If the [AdviceInputs] are propagated with some user defined map
31///   entries, this script arguments word could be used as a key to access the corresponding value.
32/// - Note arguments: data put onto the stack right before a note script is executed. These are
33///   different from note storage, as the user executing the transaction can specify arbitrary note
34///   args.
35/// - Advice inputs: provides data needed by the runtime, like the details of public output notes.
36/// - Auth arguments: data put onto the stack right before authentication procedure execution. If
37///   this argument is not specified, the [`EMPTY_WORD`] would be used as a default value. If the
38///   [AdviceInputs] are propagated with some user defined map entries, this argument could be used
39///   as a key to access the corresponding value.
40/// - Account code upgrade: the [`AccountCodeUpgrade`] of the native account if the transaction
41///   upgrades its code.
42#[derive(Clone, Debug, PartialEq, Eq)]
43pub struct TransactionArgs {
44    tx_script: Option<TransactionScript>,
45    tx_script_args: Word,
46    note_args: BTreeMap<NoteId, Word>,
47    advice_inputs: AdviceInputs,
48    auth_args: Word,
49    account_code_upgrade: Option<AccountCodeUpgrade>,
50}
51
52impl TransactionArgs {
53    // CONSTRUCTORS
54    // --------------------------------------------------------------------------------------------
55
56    /// Returns new [`TransactionArgs`] instantiated with the provided advice map.
57    pub fn new(advice_map: AdviceMap) -> Self {
58        Self::from_parts(
59            None,
60            EMPTY_WORD,
61            BTreeMap::new(),
62            AdviceInputs::from(advice_map),
63            EMPTY_WORD,
64            None,
65        )
66    }
67
68    /// Creates [`TransactionArgs`] from all of its components.
69    pub fn from_parts(
70        tx_script: Option<TransactionScript>,
71        tx_script_args: Word,
72        note_args: BTreeMap<NoteId, Word>,
73        advice_inputs: AdviceInputs,
74        auth_args: Word,
75        account_code_upgrade: Option<AccountCodeUpgrade>,
76    ) -> Self {
77        Self {
78            tx_script,
79            tx_script_args,
80            note_args,
81            advice_inputs,
82            auth_args,
83            account_code_upgrade,
84        }
85    }
86
87    /// Returns new [`TransactionArgs`] instantiated with the provided transaction script.
88    ///
89    /// If the transaction script is already set, it will be overwritten with the newly provided
90    /// one.
91    #[must_use]
92    pub fn with_tx_script(mut self, tx_script: TransactionScript) -> Self {
93        self.tx_script = Some(tx_script);
94        self
95    }
96
97    /// Returns new [`TransactionArgs`] instantiated with the provided transaction script and its
98    /// arguments.
99    ///
100    /// If the transaction script and arguments are already set, they will be overwritten with the
101    /// newly provided ones.
102    #[must_use]
103    pub fn with_tx_script_and_args(
104        mut self,
105        tx_script: TransactionScript,
106        tx_script_args: Word,
107    ) -> Self {
108        self.tx_script = Some(tx_script);
109        self.tx_script_args = tx_script_args;
110        self
111    }
112
113    /// Returns new [`TransactionArgs`] instantiated with the provided note arguments.
114    ///
115    /// If the note arguments were already set, they will be overwritten with the newly provided
116    /// ones.
117    #[must_use]
118    pub fn with_note_args(mut self, note_args: BTreeMap<NoteId, Word>) -> Self {
119        self.note_args = note_args;
120        self
121    }
122
123    /// Returns new [`TransactionArgs`] instantiated with the provided auth arguments.
124    #[must_use]
125    pub fn with_auth_args(mut self, auth_args: Word) -> Self {
126        self.auth_args = auth_args;
127        self
128    }
129
130    /// Returns new [`TransactionArgs`] instantiated with the provided code upgrade of the native
131    /// account.
132    #[must_use]
133    pub fn with_account_code_upgrade(mut self, account_code_upgrade: AccountCodeUpgrade) -> Self {
134        self.account_code_upgrade = Some(account_code_upgrade);
135        self
136    }
137
138    // PUBLIC ACCESSORS
139    // --------------------------------------------------------------------------------------------
140
141    /// Returns a reference to the transaction script.
142    pub fn tx_script(&self) -> Option<&TransactionScript> {
143        self.tx_script.as_ref()
144    }
145
146    /// Returns the transaction script arguments, or [`EMPTY_WORD`] if the arguments were not
147    /// specified.
148    ///
149    /// These arguments could be potentially used as a key to access the advice map during the
150    /// transaction script execution. Notice that the corresponding map entry should be provided
151    /// separately during the creation with the [`TransactionArgs::new`] or using the
152    /// [`TransactionArgs::extend_advice_map`] method.
153    pub fn tx_script_args(&self) -> Word {
154        self.tx_script_args
155    }
156
157    /// Returns a reference to a specific note argument.
158    pub fn get_note_args(&self, note_id: NoteId) -> Option<&Word> {
159        self.note_args.get(&note_id)
160    }
161
162    /// Returns the note arguments keyed by note ID.
163    pub fn note_args(&self) -> &BTreeMap<NoteId, Word> {
164        &self.note_args
165    }
166
167    /// Returns a reference to the internal [AdviceInputs].
168    pub fn advice_inputs(&self) -> &AdviceInputs {
169        &self.advice_inputs
170    }
171
172    /// Returns a reference to the authentication procedure argument, or [`EMPTY_WORD`] if the
173    /// argument was not specified.
174    ///
175    /// This argument could be potentially used as a key to access the advice map during the
176    /// transaction script execution. Notice that the corresponding map entry should be provided
177    /// separately during the creation with the [`TransactionArgs::new`] or using the
178    /// [`TransactionArgs::extend_advice_map`] method.
179    pub fn auth_args(&self) -> Word {
180        self.auth_args
181    }
182
183    /// Returns the code upgrade of the native account, if the transaction upgrades its code.
184    pub fn account_code_upgrade(&self) -> Option<&AccountCodeUpgrade> {
185        self.account_code_upgrade.as_ref()
186    }
187
188    // STATE MUTATORS
189    // --------------------------------------------------------------------------------------------
190
191    /// Populates the advice inputs with the expected recipient data for creating output notes.
192    ///
193    /// The advice inputs' map is extended with the following entries:
194    /// - RECIPIENT: [SERIAL_SCRIPT_HASH, STORAGE_COMMITMENT]
195    /// - SERIAL_SCRIPT_HASH: [SERIAL_HASH, SCRIPT_ROOT]
196    /// - SERIAL_HASH: [SERIAL_NUM, EMPTY_WORD]
197    /// - storage_commitment |-> storage_items.
198    /// - script_root |-> script.
199    pub fn add_output_note_recipient<T: AsRef<NoteRecipient>>(&mut self, note_recipient: T) {
200        self.advice_inputs.extend(
201            AdviceInputs::default().with_map(note_recipient.as_ref().to_advice_map_entries()),
202        );
203    }
204
205    /// Adds the `signature` corresponding to `pub_key` on `message` to the advice inputs' map.
206    ///
207    /// The advice inputs' map is extended with the following key:
208    ///
209    /// - hash(pub_key, message) |-> signature (encoded for VM execution).
210    pub fn add_signature(
211        &mut self,
212        pub_key: PublicKeyCommitment,
213        message: Word,
214        signature: Signature,
215    ) {
216        let pk_word: Word = pub_key.into();
217        self.advice_inputs.extend(AdviceInputs::default().with_map([(
218            Hasher::merge(&[pk_word, message]),
219            signature.to_encoded_signature(message),
220        )]));
221    }
222
223    /// Populates the advice inputs with the specified note recipient details.
224    ///
225    /// The advice inputs' map is extended with the following keys:
226    ///
227    /// - recipient |-> recipient details (inputs_hash, script_root, serial_num).
228    /// - storage_commitment |-> storage_items.
229    /// - script_root |-> script.
230    pub fn extend_output_note_recipients<T, L>(&mut self, notes: L)
231    where
232        L: IntoIterator<Item = T>,
233        T: AsRef<NoteRecipient>,
234    {
235        for note in notes {
236            self.add_output_note_recipient(note);
237        }
238    }
239
240    /// Extends the internal advice inputs' map with the provided key-value pairs.
241    pub fn extend_advice_map<T: IntoIterator<Item = (Word, Vec<Felt>)>>(&mut self, iter: T) {
242        self.advice_inputs.extend(AdviceInputs::default().with_map(iter));
243    }
244
245    /// Extends the internal advice inputs' merkle store with the provided nodes.
246    pub fn extend_merkle_store<I: Iterator<Item = InnerNodeInfo>>(&mut self, iter: I) {
247        self.advice_inputs
248            .extend(AdviceInputs::default().with_merkle_store(iter.collect()));
249    }
250
251    /// Extends the advice inputs in self with the provided ones.
252    pub fn extend_advice_inputs(&mut self, advice_inputs: AdviceInputs) {
253        self.advice_inputs.extend(advice_inputs);
254    }
255}
256
257/// Concatenates two [`Word`]s into a [`Vec<Felt>`] containing 8 elements.
258impl Default for TransactionArgs {
259    fn default() -> Self {
260        Self::new(AdviceMap::default())
261    }
262}
263
264impl Serializable for TransactionArgs {
265    fn write_into<W: ByteWriter>(&self, target: &mut W) {
266        self.tx_script.write_into(target);
267        self.tx_script_args.write_into(target);
268        self.note_args.write_into(target);
269        self.advice_inputs.write_into(target);
270        self.auth_args.write_into(target);
271        self.account_code_upgrade.write_into(target);
272    }
273}
274
275impl Deserializable for TransactionArgs {
276    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
277        let tx_script = Option::<TransactionScript>::read_from(source)?;
278        let tx_script_args = Word::read_from(source)?;
279        let note_args = BTreeMap::<NoteId, Word>::read_from(source)?;
280        let advice_inputs = AdviceInputs::read_from(source)?;
281        let auth_args = Word::read_from(source)?;
282        let account_code_upgrade = Option::<AccountCodeUpgrade>::read_from(source)?;
283
284        Ok(Self::from_parts(
285            tx_script,
286            tx_script_args,
287            note_args,
288            advice_inputs,
289            auth_args,
290            account_code_upgrade,
291        ))
292    }
293}
294
295// TESTS
296// ================================================================================================
297
298#[cfg(test)]
299mod tests {
300    use std::collections::BTreeMap;
301
302    use miden_core::advice::AdviceMap;
303
304    use crate::note::Note;
305    use crate::transaction::TransactionArgs;
306    use crate::utils::serde::{Deserializable, Serializable};
307    use crate::vm::AdviceInputs;
308    use crate::{Felt, Word};
309
310    #[test]
311    fn test_tx_args_serialization() {
312        let tx_args = TransactionArgs::new(AdviceMap::default());
313        let bytes: std::vec::Vec<u8> = tx_args.to_bytes();
314        let decoded = TransactionArgs::read_from_bytes(&bytes).unwrap();
315
316        assert_eq!(tx_args, decoded);
317    }
318
319    #[test]
320    fn from_parts_preserves_note_args_and_advice_inputs() {
321        let note_id = Note::mock_noop(Word::empty()).id();
322        let note_args = BTreeMap::from([(note_id, Word::new([Felt::from(1_u32); 4]))]);
323        let advice_inputs = AdviceInputs::default()
324            .with_map([(Word::new([Felt::from(2_u32); 4]), vec![Felt::from(3_u32)])]);
325
326        let tx_args = TransactionArgs::from_parts(
327            None,
328            Word::new([Felt::from(4_u32); 4]),
329            note_args.clone(),
330            advice_inputs.clone(),
331            Word::new([Felt::from(5_u32); 4]),
332            None,
333        );
334
335        assert_eq!(tx_args.note_args(), &note_args);
336        assert_eq!(tx_args.advice_inputs(), &advice_inputs);
337        assert_eq!(tx_args.tx_script_args(), Word::new([Felt::from(4_u32); 4]));
338        assert_eq!(tx_args.auth_args(), Word::new([Felt::from(5_u32); 4]));
339    }
340}