miden_protocol/transaction/
executed_tx.rs

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