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