snarkvm_ledger_block/transaction/execution/
mod.rs

1// Copyright (c) 2019-2025 Provable Inc.
2// This file is part of the snarkVM library.
3
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at:
7
8// http://www.apache.org/licenses/LICENSE-2.0
9
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15
16mod bytes;
17mod serialize;
18mod string;
19
20use crate::{Transaction, Transition};
21use console::{account::Field, network::prelude::*, program::ProgramID};
22use synthesizer_snark::Proof;
23
24use indexmap::IndexMap;
25
26#[derive(Clone, Default, PartialEq, Eq)]
27pub struct Execution<N: Network> {
28    /// The transitions.
29    transitions: IndexMap<N::TransitionID, Transition<N>>,
30    /// The global state root.
31    global_state_root: N::StateRoot,
32    /// The proof.
33    proof: Option<Proof<N>>,
34}
35
36impl<N: Network> Execution<N> {
37    /// Initialize a new `Execution` instance.
38    pub fn new() -> Self {
39        Self { transitions: Default::default(), global_state_root: Default::default(), proof: None }
40    }
41
42    /// Initializes a new `Execution` instance with the given transitions.
43    pub fn from(
44        transitions: impl Iterator<Item = Transition<N>>,
45        global_state_root: N::StateRoot,
46        proof: Option<Proof<N>>,
47    ) -> Result<Self> {
48        // Construct the execution.
49        let execution = Self { transitions: transitions.map(|t| (*t.id(), t)).collect(), global_state_root, proof };
50        // Ensure the transitions are not empty.
51        ensure!(!execution.transitions.is_empty(), "Execution cannot initialize from empty list of transitions");
52        // Return the new `Execution` instance.
53        Ok(execution)
54    }
55
56    /// Returns the size in bytes.
57    pub fn size_in_bytes(&self) -> Result<u64> {
58        Ok(u64::try_from(self.to_bytes_le()?.len())?)
59    }
60
61    /// Returns the global state root.
62    pub const fn global_state_root(&self) -> N::StateRoot {
63        self.global_state_root
64    }
65
66    /// Returns the proof.
67    pub const fn proof(&self) -> Option<&Proof<N>> {
68        self.proof.as_ref()
69    }
70
71    /// Returns the execution ID.
72    pub fn to_execution_id(&self) -> Result<Field<N>> {
73        Ok(*Transaction::execution_tree(self)?.root())
74    }
75}
76
77impl<N: Network> Execution<N> {
78    /// Returns `true` if the execution contains the transition for the given transition ID.
79    pub fn contains_transition(&self, transition_id: &N::TransitionID) -> bool {
80        self.transitions.contains_key(transition_id)
81    }
82
83    /// Returns the `Transition` corresponding to the given transition ID. This method is `O(1)`.
84    pub fn get_transition(&self, transition_id: &N::TransitionID) -> Option<&Transition<N>> {
85        self.transitions.get(transition_id)
86    }
87
88    /// Returns the program ID corresponding to the given transition ID. This method is `O(1)`.
89    pub fn get_program_id(&self, transition_id: &N::TransitionID) -> Option<&ProgramID<N>> {
90        self.transitions.get(transition_id).map(|t| t.program_id())
91    }
92
93    /// Returns the `Transition` at the given index.
94    pub fn get(&self, index: usize) -> Result<&Transition<N>> {
95        match self.transitions.get_index(index) {
96            Some((_, transition)) => Ok(transition),
97            None => bail!("Transition index {index} out of bounds in the execution object"),
98        }
99    }
100
101    /// Returns the next `Transition` in the execution.
102    pub fn peek(&self) -> Result<&Transition<N>> {
103        self.get(self.len() - 1)
104    }
105
106    /// Appends the given `Transition` to the execution.
107    pub fn push(&mut self, transition: Transition<N>) {
108        self.transitions.insert(*transition.id(), transition);
109    }
110
111    /// Pops the last `Transition` from the execution.
112    pub fn pop(&mut self) -> Result<Transition<N>> {
113        match self.transitions.pop() {
114            Some((_, transition)) => Ok(transition),
115            None => bail!("Cannot pop a transition from an empty execution object"),
116        }
117    }
118
119    /// Returns the number of transitions in the execution.
120    pub fn len(&self) -> usize {
121        self.transitions.len()
122    }
123
124    /// Returns `true` if the execution is empty.
125    pub fn is_empty(&self) -> bool {
126        self.transitions.is_empty()
127    }
128}
129
130impl<N: Network> Execution<N> {
131    /// Returns a consuming iterator over the underlying transitions.
132    pub fn into_transitions(self) -> impl ExactSizeIterator + DoubleEndedIterator<Item = Transition<N>> {
133        self.transitions.into_values()
134    }
135
136    /// Returns an iterator over the underlying transitions.
137    pub fn transitions(&self) -> impl '_ + ExactSizeIterator + DoubleEndedIterator<Item = &Transition<N>> {
138        self.transitions.values()
139    }
140
141    /// Returns an iterator over the commitments.
142    pub fn commitments(&self) -> impl '_ + Iterator<Item = &Field<N>> {
143        self.transitions.values().flat_map(Transition::commitments)
144    }
145}
146
147#[cfg(test)]
148pub mod test_helpers {
149    use super::*;
150
151    type CurrentNetwork = console::network::MainnetV0;
152
153    /// Samples a random execution.
154    pub(crate) fn sample_execution(rng: &mut TestRng) -> Execution<CurrentNetwork> {
155        // Sample the genesis block.
156        let block = crate::test_helpers::sample_genesis_block(rng);
157        // Retrieve a transaction.
158        let transaction = block.transactions().iter().next().unwrap().deref().clone();
159        // Retrieve the execution.
160        if let Transaction::Execute(_, _, execution, _) = transaction { *execution } else { unreachable!() }
161    }
162}