Skip to main content

miden_protocol/transaction/
executed_tx.rs

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