mod.rs
  1  // Copyright (c) 2019-2025 Alpha-Delta Network Inc.
  2  // This file is part of the alphavm 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  
 16  mod bytes;
 17  mod serialize;
 18  mod string;
 19  
 20  use crate::{Transaction, Transition};
 21  use alphavm_synthesizer_snark::Proof;
 22  use console::{account::Field, network::prelude::*, program::ProgramID};
 23  
 24  use indexmap::IndexMap;
 25  
 26  #[derive(Clone, Default, PartialEq, Eq)]
 27  pub 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  
 36  impl<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  
 77  impl<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  
130  impl<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>> + Clone {
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)]
148  pub 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, index: usize) -> 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().nth(index).unwrap().deref().clone();
159          // Retrieve the execution.
160          if let Transaction::Execute(_, _, execution, _) = transaction {
161              *execution
162          } else {
163              panic!("Index {index} exceeded the number of executions in the genesis block")
164          }
165      }
166  }