miden_protocol/transaction/
tx_args.rs1use alloc::collections::BTreeMap;
2use alloc::string::String;
3use alloc::sync::Arc;
4use alloc::vec::Vec;
5use core::fmt::Display;
6
7use miden_core::mast::MastNodeExt;
8use miden_crypto::merkle::InnerNodeInfo;
9use miden_crypto_derive::WordWrapper;
10use miden_mast_package::Package;
11
12use super::{Felt, Hasher, Word};
13use crate::account::auth::{PublicKeyCommitment, Signature};
14use crate::errors::TransactionScriptError;
15use crate::note::{NoteId, NoteRecipient};
16use crate::utils::serde::{
17 ByteReader,
18 ByteWriter,
19 Deserializable,
20 DeserializationError,
21 Serializable,
22};
23use crate::vm::{AdviceInputs, AdviceMap, Program};
24use crate::{EMPTY_WORD, MastForest, MastNodeId};
25
26#[derive(Clone, Debug, PartialEq, Eq)]
48pub struct TransactionArgs {
49 tx_script: Option<TransactionScript>,
50 tx_script_args: Word,
51 note_args: BTreeMap<NoteId, Word>,
52 advice_inputs: AdviceInputs,
53 auth_args: Word,
54}
55
56impl TransactionArgs {
57 pub fn new(advice_map: AdviceMap) -> Self {
63 let advice_inputs = AdviceInputs { map: advice_map, ..Default::default() };
64
65 Self {
66 tx_script: None,
67 tx_script_args: EMPTY_WORD,
68 note_args: Default::default(),
69 advice_inputs,
70 auth_args: EMPTY_WORD,
71 }
72 }
73
74 #[must_use]
79 pub fn with_tx_script(mut self, tx_script: TransactionScript) -> Self {
80 self.tx_script = Some(tx_script);
81 self
82 }
83
84 #[must_use]
90 pub fn with_tx_script_and_args(
91 mut self,
92 tx_script: TransactionScript,
93 tx_script_args: Word,
94 ) -> Self {
95 self.tx_script = Some(tx_script);
96 self.tx_script_args = tx_script_args;
97 self
98 }
99
100 #[must_use]
105 pub fn with_note_args(mut self, note_args: BTreeMap<NoteId, Word>) -> Self {
106 self.note_args = note_args;
107 self
108 }
109
110 #[must_use]
112 pub fn with_auth_args(mut self, auth_args: Word) -> Self {
113 self.auth_args = auth_args;
114 self
115 }
116
117 pub fn tx_script(&self) -> Option<&TransactionScript> {
122 self.tx_script.as_ref()
123 }
124
125 pub fn tx_script_args(&self) -> Word {
133 self.tx_script_args
134 }
135
136 pub fn get_note_args(&self, note_id: NoteId) -> Option<&Word> {
138 self.note_args.get(¬e_id)
139 }
140
141 pub fn advice_inputs(&self) -> &AdviceInputs {
143 &self.advice_inputs
144 }
145
146 pub fn auth_args(&self) -> Word {
154 self.auth_args
155 }
156
157 pub fn add_output_note_recipient<T: AsRef<NoteRecipient>>(&mut self, note_recipient: T) {
169 let note_recipient = note_recipient.as_ref();
170 let storage = note_recipient.storage();
171 let script = note_recipient.script();
172 let script_encoded: Vec<Felt> = script.into();
173
174 let script_root: Word = script.root().into();
176 let sn_hash = Hasher::merge(&[note_recipient.serial_num(), Word::empty()]);
177 let sn_script_hash = Hasher::merge(&[sn_hash, script_root]);
178
179 let new_elements = vec![
180 (sn_hash, concat_words(note_recipient.serial_num(), Word::empty())),
181 (sn_script_hash, concat_words(sn_hash, script_root)),
182 (note_recipient.digest(), concat_words(sn_script_hash, storage.commitment())),
183 (storage.commitment(), storage.to_elements()),
184 (script_root, script_encoded),
185 ];
186
187 self.advice_inputs.extend(AdviceInputs::default().with_map(new_elements));
188 }
189
190 pub fn add_signature(
196 &mut self,
197 pub_key: PublicKeyCommitment,
198 message: Word,
199 signature: Signature,
200 ) {
201 let pk_word: Word = pub_key.into();
202 self.advice_inputs
203 .map
204 .insert(Hasher::merge(&[pk_word, message]), signature.to_prepared_signature(message));
205 }
206
207 pub fn extend_output_note_recipients<T, L>(&mut self, notes: L)
215 where
216 L: IntoIterator<Item = T>,
217 T: AsRef<NoteRecipient>,
218 {
219 for note in notes {
220 self.add_output_note_recipient(note);
221 }
222 }
223
224 pub fn extend_advice_map<T: IntoIterator<Item = (Word, Vec<Felt>)>>(&mut self, iter: T) {
226 self.advice_inputs.map.extend(iter);
227 }
228
229 pub fn extend_merkle_store<I: Iterator<Item = InnerNodeInfo>>(&mut self, iter: I) {
231 self.advice_inputs.store.extend(iter);
232 }
233
234 pub fn extend_advice_inputs(&mut self, advice_inputs: AdviceInputs) {
236 self.advice_inputs.extend(advice_inputs);
237 }
238}
239
240fn concat_words(first: Word, second: Word) -> Vec<Felt> {
242 let mut result = Vec::with_capacity(8);
243 result.extend(first);
244 result.extend(second);
245 result
246}
247
248impl Default for TransactionArgs {
249 fn default() -> Self {
250 Self::new(AdviceMap::default())
251 }
252}
253
254impl Serializable for TransactionArgs {
255 fn write_into<W: ByteWriter>(&self, target: &mut W) {
256 self.tx_script.write_into(target);
257 self.tx_script_args.write_into(target);
258 self.note_args.write_into(target);
259 self.advice_inputs.write_into(target);
260 self.auth_args.write_into(target);
261 }
262}
263
264impl Deserializable for TransactionArgs {
265 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
266 let tx_script = Option::<TransactionScript>::read_from(source)?;
267 let tx_script_args = Word::read_from(source)?;
268 let note_args = BTreeMap::<NoteId, Word>::read_from(source)?;
269 let advice_inputs = AdviceInputs::read_from(source)?;
270 let auth_args = Word::read_from(source)?;
271
272 Ok(Self {
273 tx_script,
274 tx_script_args,
275 note_args,
276 advice_inputs,
277 auth_args,
278 })
279 }
280}
281
282#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, WordWrapper)]
287pub struct TransactionScriptRoot(Word);
288
289impl From<TransactionScriptRoot> for Word {
290 fn from(root: TransactionScriptRoot) -> Self {
291 root.0
292 }
293}
294
295impl Display for TransactionScriptRoot {
296 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
297 Display::fmt(&self.0, f)
298 }
299}
300
301impl Serializable for TransactionScriptRoot {
302 fn write_into<W: ByteWriter>(&self, target: &mut W) {
303 target.write(self.0);
304 }
305
306 fn get_size_hint(&self) -> usize {
307 self.0.get_size_hint()
308 }
309}
310
311impl Deserializable for TransactionScriptRoot {
312 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
313 let word: Word = source.read()?;
314 Ok(Self::from_raw(word))
315 }
316}
317
318#[derive(Clone, Debug, PartialEq, Eq)]
329pub struct TransactionScript {
330 mast: Arc<MastForest>,
331 entrypoint: MastNodeId,
332}
333
334impl TransactionScript {
335 pub fn new(code: Program) -> Self {
340 Self::from_parts(code.mast_forest().clone(), code.entrypoint())
341 }
342
343 pub fn from_parts(mast: Arc<MastForest>, entrypoint: MastNodeId) -> Self {
348 assert!(mast.get_node_by_id(entrypoint).is_some());
349
350 Self { mast, entrypoint }
351 }
352
353 pub fn from_package(package: &Package) -> Result<Self, TransactionScriptError> {
361 let program =
362 package.try_into_program().map_err(TransactionScriptError::PackageNotProgram)?;
363
364 Ok(TransactionScript::new(program))
365 }
366
367 pub fn mast(&self) -> Arc<MastForest> {
372 self.mast.clone()
373 }
374
375 pub fn root(&self) -> TransactionScriptRoot {
377 TransactionScriptRoot::from_raw(self.mast[self.entrypoint].digest())
378 }
379
380 pub fn with_advice_map(self, advice_map: AdviceMap) -> Self {
386 if advice_map.is_empty() {
387 return self;
388 }
389
390 let mut mast = (*self.mast).clone();
391 mast.advice_map_mut().extend(advice_map);
392 Self {
393 mast: Arc::new(mast),
394 entrypoint: self.entrypoint,
395 }
396 }
397}
398
399impl Serializable for TransactionScript {
403 fn write_into<W: ByteWriter>(&self, target: &mut W) {
404 self.mast.write_into(target);
405 target.write_u32(u32::from(self.entrypoint));
406 }
407}
408
409impl Deserializable for TransactionScript {
410 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
411 let mast = MastForest::read_from(source)?;
412 let entrypoint = MastNodeId::from_u32_safe(source.read_u32()?, &mast)?;
413
414 Ok(Self::from_parts(Arc::new(mast), entrypoint))
415 }
416}
417
418#[cfg(test)]
419mod tests {
420 use miden_core::advice::AdviceMap;
421
422 use crate::transaction::TransactionArgs;
423 use crate::utils::serde::{Deserializable, Serializable};
424
425 #[test]
426 fn test_tx_args_serialization() {
427 let tx_args = TransactionArgs::new(AdviceMap::default());
428 let bytes: std::vec::Vec<u8> = tx_args.to_bytes();
429 let decoded = TransactionArgs::read_from_bytes(&bytes).unwrap();
430
431 assert_eq!(tx_args, decoded);
432 }
433
434 #[test]
435 fn test_transaction_script_with_advice_map() {
436 use miden_core::{Felt, Word};
437
438 use super::TransactionScript;
439 use crate::assembly::Assembler;
440
441 let assembler = Assembler::default();
442 let program = assembler.assemble_program("begin nop end").unwrap();
443 let script = TransactionScript::new(program);
444
445 assert!(script.mast().advice_map().is_empty());
446
447 let original_root = script.root();
449 let script = script.with_advice_map(AdviceMap::default());
450 assert_eq!(original_root, script.root());
451
452 let key = Word::from([1u32, 2, 3, 4]);
454 let value = vec![Felt::new_unchecked(42), Felt::new_unchecked(43)];
455 let mut advice_map = AdviceMap::default();
456 advice_map.insert(key, value.clone());
457
458 let script = script.with_advice_map(advice_map);
459
460 let mast = script.mast();
461 let stored = mast.advice_map().get(&key).expect("entry should be present");
462 assert_eq!(stored.as_ref(), value.as_slice());
463 }
464}