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
use cfx_internal_common::debug::ComputeEpochDebugRecord;
use cfx_statedb::{
    for_all_global_param_keys,
    global_params::{self, GlobalParamKey, TOTAL_GLOBAL_PARAMS},
    Result as DbResult, StateDbExt, StateDbGeneric as StateDb,
};
use cfx_types::U256;

/// Manages specially-treated global variables during execution.
///
/// Underlying this structure is a fixed-length array, where each global
/// variable corresponds to an instance of the `GlobalParamKey` trait. This
/// trait defines the variable's index in the array, its initialization process,
/// and the type conversion between database layer format and application layer
/// format.
//
// This approach, compared to implementing each global variable as a separate
// field, significantly reduces repetitive code, leverages a few generic
// functions instead of individual logic for each field. As variable indices are
// compile-time constants, the resulting code is free from array boundary
// checks, achieving performance comparable to a field-based implementation.
//
// TODO: Incorporating these variables into existing cache/checkpoint logic
// would make the code clean, but it would be difficult to achieve back forward
// compatibility.
#[derive(Copy, Clone, Debug)]
pub(super) struct GlobalStat([U256; TOTAL_GLOBAL_PARAMS]);

impl GlobalStat {
    /// Make new global statistical variables with their initialization value.
    pub fn new() -> Self {
        let mut ans = <[U256; TOTAL_GLOBAL_PARAMS]>::default();
        fn init_value<T: GlobalParamKey>(
            ans: &mut [U256; TOTAL_GLOBAL_PARAMS],
        ) {
            ans[T::ID] = T::init_vm_value();
        }
        use global_params::*;
        for_all_global_param_keys! {
            init_value::<Key>(&mut ans);
        }
        GlobalStat(ans)
    }

    /// Get loaded global statistic variables from the database.
    pub fn loaded(db: &StateDb) -> DbResult<Self> {
        let mut ans = Default::default();
        fn load_value<T: GlobalParamKey>(
            ans: &mut [U256; TOTAL_GLOBAL_PARAMS], db: &StateDb,
        ) -> DbResult<()> {
            let loaded = db.get_global_param::<T>()?;
            ans[T::ID] = T::into_vm_value(loaded);
            Ok(())
        }
        use global_params::*;
        for_all_global_param_keys! {
            load_value::<Key>(&mut ans, db)?;
        }
        Ok(GlobalStat(ans))
    }

    /// Assert the global statistic variables have never been inited in the
    /// database.
    pub fn assert_non_inited(db: &StateDb) -> DbResult<()> {
        // If db is not initialized, all the loaded value should be zero.
        fn assert_zero_global_params<T: GlobalParamKey>(
            db: &StateDb,
        ) -> DbResult<()> {
            assert!(
                db.get_global_param::<T>()?.is_zero(),
                "{:x?} is non-zero when db is un-init",
                T::STORAGE_KEY
            );
            Ok(())
        }
        use global_params::*;
        for_all_global_param_keys! {
            assert_zero_global_params::<Key>(&db)?;
        }
        Ok(())
    }

    /// Commit the in-memory global statistic variables to the database.
    pub fn commit(
        &self, db: &mut StateDb,
        mut debug_record: Option<&mut ComputeEpochDebugRecord>,
    ) -> DbResult<()> {
        fn commit_param<T: GlobalParamKey>(
            ans: &[U256; TOTAL_GLOBAL_PARAMS], db: &mut StateDb,
            debug_record: Option<&mut ComputeEpochDebugRecord>,
        ) -> DbResult<()> {
            let value = T::from_vm_value(ans[T::ID]);
            db.set_global_param::<T>(&value, debug_record)?;
            Ok(())
        }
        use global_params::*;
        for_all_global_param_keys! {
            commit_param::<Key>(&self.0, db, debug_record.as_deref_mut())?;
        }
        Ok(())
    }

    /// Get the owned value of a variable
    pub fn get<T: GlobalParamKey>(&self) -> U256 { self.0[T::ID] }

    /// Get the immutable reference of a variable
    pub fn refr<T: GlobalParamKey>(&self) -> &U256 { &self.0[T::ID] }

    /// Get the mutable reference of a variable
    pub fn val<T: GlobalParamKey>(&mut self) -> &mut U256 { &mut self.0[T::ID] }
}