miden_objects/transaction/
executed_tx.rs

1use alloc::vec::Vec;
2
3use super::{
4    Account,
5    AccountDelta,
6    AccountHeader,
7    AccountId,
8    AdviceInputs,
9    BlockHeader,
10    InputNote,
11    InputNotes,
12    NoteId,
13    OutputNotes,
14    TransactionArgs,
15    TransactionId,
16    TransactionInputs,
17    TransactionOutputs,
18    TransactionWitness,
19};
20use crate::asset::FungibleAsset;
21use crate::block::BlockNumber;
22use crate::utils::serde::{
23    ByteReader,
24    ByteWriter,
25    Deserializable,
26    DeserializationError,
27    Serializable,
28};
29
30// EXECUTED TRANSACTION
31// ================================================================================================
32
33/// Describes the result of executing a transaction program for the Miden protocol.
34///
35/// Executed transaction serves two primary purposes:
36/// - It contains a complete description of the effects of the transaction. Specifically, it
37///   contains all output notes created as the result of the transaction and describes all the
38///   changes made to the involved account (i.e., the account delta).
39/// - It contains all the information required to re-execute and prove the transaction in a
40///   stateless manner. This includes all public transaction inputs, but also all nondeterministic
41///   inputs that the host provided to Miden VM while executing the transaction (i.e., advice
42///   witness).
43#[derive(Debug, Clone, PartialEq)]
44pub struct ExecutedTransaction {
45    id: TransactionId,
46    tx_inputs: TransactionInputs,
47    tx_outputs: TransactionOutputs,
48    account_delta: AccountDelta,
49    tx_args: TransactionArgs,
50    advice_witness: AdviceInputs,
51    tx_measurements: TransactionMeasurements,
52}
53
54impl ExecutedTransaction {
55    // CONSTRUCTOR
56    // --------------------------------------------------------------------------------------------
57
58    /// Returns a new [ExecutedTransaction] instantiated from the provided data.
59    ///
60    /// # Panics
61    /// Panics if input and output account IDs are not the same.
62    pub fn new(
63        tx_inputs: TransactionInputs,
64        tx_outputs: TransactionOutputs,
65        account_delta: AccountDelta,
66        tx_args: TransactionArgs,
67        advice_witness: AdviceInputs,
68        tx_measurements: TransactionMeasurements,
69    ) -> Self {
70        // make sure account IDs are consistent across transaction inputs and outputs
71        assert_eq!(tx_inputs.account().id(), tx_outputs.account.id());
72
73        // we create the id from the content, so we cannot construct the
74        // `id` value after construction `Self {..}` without moving
75        let id = TransactionId::new(
76            tx_inputs.account().init_commitment(),
77            tx_outputs.account.commitment(),
78            tx_inputs.input_notes().commitment(),
79            tx_outputs.output_notes.commitment(),
80        );
81
82        Self {
83            id,
84            tx_inputs,
85            tx_outputs,
86            account_delta,
87            tx_args,
88            advice_witness,
89            tx_measurements,
90        }
91    }
92
93    // PUBLIC ACCESSORS
94    // --------------------------------------------------------------------------------------------
95
96    /// Returns a unique identifier of this transaction.
97    pub fn id(&self) -> TransactionId {
98        self.id
99    }
100
101    /// Returns the ID of the account against which this transaction was executed.
102    pub fn account_id(&self) -> AccountId {
103        self.initial_account().id()
104    }
105
106    /// Returns the description of the account before the transaction was executed.
107    pub fn initial_account(&self) -> &Account {
108        self.tx_inputs.account()
109    }
110
111    /// Returns description of the account after the transaction was executed.
112    pub fn final_account(&self) -> &AccountHeader {
113        &self.tx_outputs.account
114    }
115
116    /// Returns the notes consumed in this transaction.
117    pub fn input_notes(&self) -> &InputNotes<InputNote> {
118        self.tx_inputs.input_notes()
119    }
120
121    /// Returns the notes created in this transaction.
122    pub fn output_notes(&self) -> &OutputNotes {
123        &self.tx_outputs.output_notes
124    }
125
126    /// Returns the fee of the transaction.
127    pub fn fee(&self) -> FungibleAsset {
128        self.tx_outputs.fee
129    }
130
131    /// Returns the block number at which the transaction will expire.
132    pub fn expiration_block_num(&self) -> BlockNumber {
133        self.tx_outputs.expiration_block_num
134    }
135
136    /// Returns a reference to the transaction arguments.
137    pub fn tx_args(&self) -> &TransactionArgs {
138        &self.tx_args
139    }
140
141    /// Returns the block header for the block against which the transaction was executed.
142    pub fn block_header(&self) -> &BlockHeader {
143        self.tx_inputs.block_header()
144    }
145
146    /// Returns a description of changes between the initial and final account states.
147    pub fn account_delta(&self) -> &AccountDelta {
148        &self.account_delta
149    }
150
151    /// Returns a reference to the inputs for this transaction.
152    pub fn tx_inputs(&self) -> &TransactionInputs {
153        &self.tx_inputs
154    }
155
156    /// Returns all the data requested by the VM from the advice provider while executing the
157    /// transaction program.
158    pub fn advice_witness(&self) -> &AdviceInputs {
159        &self.advice_witness
160    }
161
162    /// Returns a reference to the transaction measurements which are the cycle counts for
163    /// each stage.
164    pub fn measurements(&self) -> &TransactionMeasurements {
165        &self.tx_measurements
166    }
167
168    // CONVERSIONS
169    // --------------------------------------------------------------------------------------------
170
171    /// Returns individual components of this transaction.
172    pub fn into_parts(
173        self,
174    ) -> (AccountDelta, TransactionOutputs, TransactionWitness, TransactionMeasurements) {
175        let tx_witness = TransactionWitness {
176            tx_inputs: self.tx_inputs,
177            tx_args: self.tx_args,
178            advice_witness: self.advice_witness,
179        };
180        (self.account_delta, self.tx_outputs, tx_witness, self.tx_measurements)
181    }
182}
183
184impl From<ExecutedTransaction> for TransactionWitness {
185    fn from(tx: ExecutedTransaction) -> Self {
186        let (_, _, tx_witness, _) = tx.into_parts();
187        tx_witness
188    }
189}
190
191impl From<ExecutedTransaction> for TransactionMeasurements {
192    fn from(tx: ExecutedTransaction) -> Self {
193        let (_, _, _, tx_progress) = tx.into_parts();
194        tx_progress
195    }
196}
197
198impl Serializable for ExecutedTransaction {
199    fn write_into<W: ByteWriter>(&self, target: &mut W) {
200        self.tx_inputs.write_into(target);
201        self.tx_outputs.write_into(target);
202        self.account_delta.write_into(target);
203        self.tx_args.write_into(target);
204        self.advice_witness.write_into(target);
205        self.tx_measurements.write_into(target);
206    }
207}
208
209impl Deserializable for ExecutedTransaction {
210    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
211        let tx_inputs = TransactionInputs::read_from(source)?;
212        let tx_outputs = TransactionOutputs::read_from(source)?;
213        let account_delta = AccountDelta::read_from(source)?;
214        let tx_args = TransactionArgs::read_from(source)?;
215        let advice_witness = AdviceInputs::read_from(source)?;
216        let tx_measurements = TransactionMeasurements::read_from(source)?;
217
218        Ok(Self::new(
219            tx_inputs,
220            tx_outputs,
221            account_delta,
222            tx_args,
223            advice_witness,
224            tx_measurements,
225        ))
226    }
227}
228
229// TRANSACTION MEASUREMENTS
230// ================================================================================================
231
232/// Stores the resulting number of cycles for each transaction execution stage obtained from the
233/// `TransactionProgress` struct.
234#[derive(Debug, Clone, PartialEq)]
235pub struct TransactionMeasurements {
236    pub prologue: usize,
237    pub notes_processing: usize,
238    pub note_execution: Vec<(NoteId, usize)>,
239    pub tx_script_processing: usize,
240    pub epilogue: usize,
241    /// The number of cycles the epilogue took to execute after compute_fee determined the cycle
242    /// count.
243    ///
244    /// This is used to get the total number of cycles the transaction takes for use in
245    /// compute_fee itself.
246    pub after_tx_cycles_obtained: usize,
247}
248
249impl TransactionMeasurements {
250    /// Returns the total number of cycles spent executing the transaction.
251    pub fn total_cycles(&self) -> usize {
252        self.prologue + self.notes_processing + self.tx_script_processing + self.epilogue
253    }
254
255    /// Returns the trace length of the transaction which is the next power of 2 of the total cycles
256    /// spent executing the transaction.
257    pub fn trace_length(&self) -> usize {
258        let total_cycles = self.total_cycles();
259        total_cycles.next_power_of_two()
260    }
261}
262
263impl Serializable for TransactionMeasurements {
264    fn write_into<W: ByteWriter>(&self, target: &mut W) {
265        self.prologue.write_into(target);
266        self.notes_processing.write_into(target);
267        self.note_execution.write_into(target);
268        self.tx_script_processing.write_into(target);
269        self.epilogue.write_into(target);
270        self.after_tx_cycles_obtained.write_into(target);
271    }
272}
273
274impl Deserializable for TransactionMeasurements {
275    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
276        let prologue = usize::read_from(source)?;
277        let notes_processing = usize::read_from(source)?;
278        let note_execution = Vec::<(NoteId, usize)>::read_from(source)?;
279        let tx_script_processing = usize::read_from(source)?;
280        let epilogue = usize::read_from(source)?;
281        let after_tx_cycles_obtained = usize::read_from(source)?;
282
283        Ok(Self {
284            prologue,
285            notes_processing,
286            note_execution,
287            tx_script_processing,
288            epilogue,
289            after_tx_cycles_obtained,
290        })
291    }
292}
293
294// TESTS
295// ================================================================================================
296
297#[cfg(test)]
298mod tests {
299    use core::marker::PhantomData;
300
301    use crate::transaction::ExecutedTransaction;
302
303    fn ensure_send<T: Send>(_: PhantomData<T>) {}
304
305    /// Add assurance `ExecutedTransaction` remains `Send`
306    #[allow(dead_code)]
307    fn compiletime_ensure_send_for_types() {
308        ensure_send::<ExecutedTransaction>(PhantomData);
309    }
310}