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 }