1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220
//! Runtime state representation for the VM
use alloc::vec::Vec;
use fuel_tx::Receipt;
use fuel_types::{
Bytes32,
Word,
};
mod debug;
mod debugger;
pub use debug::{
Breakpoint,
DebugEval,
};
pub use debugger::Debugger;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
/// Resulting state of an instruction set execution.
pub enum ExecuteState {
/// The VM should proceed normally with the execution.
Proceed,
/// The current context returned a [`Word`].
Return(Word),
/// The current context returned some data represented as its digest.
ReturnData(Bytes32),
/// The set execution resulted in a `RVRT` instruction.
Revert(Word),
/// A debug event was reached.
DebugEvent(DebugEval),
}
impl ExecuteState {
/// Return true if the VM execution should continue.
pub const fn should_continue(&self) -> bool {
matches!(self, Self::Proceed | Self::DebugEvent(DebugEval::Continue))
}
}
impl Default for ExecuteState {
fn default() -> Self {
Self::Proceed
}
}
impl From<DebugEval> for ExecuteState {
fn from(d: DebugEval) -> Self {
Self::DebugEvent(d)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
/// Resulting state of a transaction/program execution.
pub enum ProgramState {
/// The transaction returned a [`Word`].
Return(Word),
/// The transaction returned some data represented as its digest.
ReturnData(Bytes32),
/// The transaction execution resulted in a `RVRT` instruction.
Revert(Word),
/// A debug event was reached for the transaction. The VM is suspended.
RunProgram(DebugEval),
/// A debug event was reached for a predicate verification. The VM is
/// suspended.
VerifyPredicate(DebugEval),
}
impl PartialEq<Breakpoint> for ProgramState {
fn eq(&self, other: &Breakpoint) -> bool {
match self.debug_ref() {
Some(&DebugEval::Breakpoint(b)) => &b == other,
_ => false,
}
}
}
impl ProgramState {
/// Debug event representation.
///
/// Will return `None` if no debug event was reached.
pub const fn debug_ref(&self) -> Option<&DebugEval> {
match self {
Self::RunProgram(d) | Self::VerifyPredicate(d) => Some(d),
_ => None,
}
}
/// Return `true` if a debug event was reached.
pub const fn is_debug(&self) -> bool {
self.debug_ref().is_some()
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
/// Representation of the result of a transaction execution.
pub struct StateTransition<Tx> {
state: ProgramState,
tx: Tx,
receipts: Vec<Receipt>,
}
impl<Tx> StateTransition<Tx> {
/// Create a new state transition representation.
pub const fn new(state: ProgramState, tx: Tx, receipts: Vec<Receipt>) -> Self {
Self {
state,
tx,
receipts,
}
}
/// Program state representation.
pub const fn state(&self) -> &ProgramState {
&self.state
}
/// Resulting mutated transaction after VM execution.
pub const fn tx(&self) -> &Tx {
&self.tx
}
/// Flag whether the client should revert after execution.
pub fn should_revert(&self) -> bool {
self.receipts
.iter()
.any(|r| matches!(r, Receipt::Revert { .. } | Receipt::Panic { .. }))
}
/// Transaction receipts representing the state transition.
pub fn receipts(&self) -> &[Receipt] {
self.receipts.as_slice()
}
/// Convert this instance into its internal attributes.
pub fn into_inner(self) -> (ProgramState, Tx, Vec<Receipt>) {
(self.state, self.tx, self.receipts)
}
}
impl<Tx> From<StateTransition<Tx>> for ProgramState {
fn from(t: StateTransition<Tx>) -> ProgramState {
t.state
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
/// Zero-copy Representation of the result of a transaction execution bound to
/// the lifetime of the VM.
pub struct StateTransitionRef<'a, Tx> {
state: ProgramState,
tx: &'a Tx,
receipts: &'a [Receipt],
}
impl<'a, Tx> StateTransitionRef<'a, Tx> {
/// Create a new by reference state transition representation.
pub const fn new(state: ProgramState, tx: &'a Tx, receipts: &'a [Receipt]) -> Self {
Self {
state,
tx,
receipts,
}
}
/// Program state representation.
pub const fn state(&self) -> &ProgramState {
&self.state
}
/// Resulting mutated transaction after VM execution.
pub const fn tx(&self) -> &Tx {
self.tx
}
/// Transaction receipts representing the state transition.
pub const fn receipts(&self) -> &[Receipt] {
self.receipts
}
/// Flag whether the client should revert after execution.
pub fn should_revert(&self) -> bool {
self.receipts
.iter()
.any(|r| matches!(r, Receipt::Revert { .. } | Receipt::Panic { .. }))
}
}
impl<'a, Tx> From<&'a StateTransition<Tx>> for StateTransitionRef<'a, Tx> {
fn from(t: &'a StateTransition<Tx>) -> StateTransitionRef<'a, Tx> {
Self {
state: *t.state(),
tx: t.tx(),
receipts: t.receipts(),
}
}
}
impl<'a, Tx: Clone> From<StateTransitionRef<'a, Tx>> for StateTransition<Tx> {
fn from(t: StateTransitionRef<Tx>) -> StateTransition<Tx> {
StateTransition {
state: *t.state(),
tx: t.tx().clone(),
receipts: t.receipts().to_vec(),
}
}
}
impl<'a, Tx: Clone> From<StateTransitionRef<'a, Tx>> for ProgramState {
fn from(t: StateTransitionRef<'a, Tx>) -> ProgramState {
t.state
}
}