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
use cairo_lang_casm::ap_change::ApChange;
use cairo_lang_sierra::program::StatementIdx;
use frame_state::{FrameState, FrameStateError};
use thiserror::Error;

use self::frame_state::validate_final_frame_state;
use self::gas_wallet::GasWallet;

pub mod ap_tracking;
pub mod frame_state;
pub mod gas_wallet;

#[derive(Error, Debug, Eq, PartialEq)]
pub enum EnvironmentError {
    #[error("Inconsistent ap tracking.")]
    InconsistentApTracking,
    #[error("Inconsistent ap tracking base.")]
    InconsistentApTrackingBase,
    #[error("Inconsistent frame state.")]
    InconsistentFrameState,
    #[error("Inconsistent gas wallet state.")]
    InconsistentGasWallet,
    #[error("{0}")]
    InvalidFinalFrameState(FrameStateError),
}

/// Part of the program annotations that libfuncs may access as part of their run.
#[derive(Clone, Debug)]
pub struct Environment {
    /// The ap change starting from `ap_tracking_base`.
    /// Once it changes to ApChange::Unknown it remains in that state, unless it is reenabled.
    pub ap_tracking: ApChange,
    pub ap_tracking_base: Option<StatementIdx>,
    /// The size of the continuous known stack.
    pub stack_size: usize,
    pub frame_state: FrameState,
    pub gas_wallet: GasWallet,
}
impl Environment {
    pub fn new(gas_wallet: GasWallet, ap_tracking_base: StatementIdx) -> Self {
        let ap_tracking = ApChange::Known(0);
        Self {
            ap_tracking,
            ap_tracking_base: Some(ap_tracking_base),
            stack_size: 0,
            frame_state: FrameState::Allocating { allocated: 0, last_ap_tracking: ap_tracking },
            gas_wallet,
        }
    }
}

// Validates that the environments match and returns appropriate error if not.
pub fn validate_environment_equality(
    a: &Environment,
    b: &Environment,
) -> Result<(), EnvironmentError> {
    if a.ap_tracking_base != b.ap_tracking_base {
        Err(EnvironmentError::InconsistentApTrackingBase)
    } else if a.ap_tracking != b.ap_tracking {
        Err(EnvironmentError::InconsistentApTracking)
    } else if a.frame_state != b.frame_state {
        Err(EnvironmentError::InconsistentFrameState)
    } else if a.gas_wallet != b.gas_wallet {
        Err(EnvironmentError::InconsistentGasWallet)
    } else {
        Ok(())
    }
}

// Validates that the state at the end of a function is valid.
pub fn validate_final_environment(env: &Environment) -> Result<(), EnvironmentError> {
    validate_final_frame_state(&env.frame_state).map_err(EnvironmentError::InvalidFinalFrameState)
}