miden_objects/transaction/
executed_tx.rs

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