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
use core::convert::TryFrom;
use num_enum::TryFromPrimitive;
use revm_precompiles::SpecId as PrecompileId;

#[repr(u8)]
#[derive(Debug, Copy, Clone, TryFromPrimitive, Eq, PartialEq, Hash, Ord, PartialOrd)]
#[cfg_attr(feature = "with-serde", derive(serde::Serialize, serde::Deserialize))]
#[allow(non_camel_case_types)]
pub enum SpecId {
    FRONTIER = 1,
    HOMESTEAD = 2,
    TANGERINE = 3,
    SPURIOUS_DRAGON = 4,
    BYZANTIUM = 5,
    CONSTANTINOPLE = 6,
    PETERSBURG = 7,
    ISTANBUL = 8,
    MUIRGLACIER = 9,
    BERLIN = 10,
    LONDON = 11,
    LATEST = 12,
}

impl SpecId {
    pub const fn to_precompile_id(self) -> u8 {
        match self {
            FRONTIER | HOMESTEAD | TANGERINE | SPURIOUS_DRAGON => PrecompileId::HOMESTEAD as u8,
            BYZANTIUM | CONSTANTINOPLE | PETERSBURG => PrecompileId::BYZANTIUM as u8,
            ISTANBUL | MUIRGLACIER => PrecompileId::ISTANBUL as u8,
            BERLIN | LONDON | LATEST => PrecompileId::BERLIN as u8,
        }
    }

    pub fn try_from_u8(spec_id: u8) -> Option<Self> {
        Self::try_from(spec_id).ok()
    }
}

pub use SpecId::*;

impl From<&str> for SpecId {
    fn from(name: &str) -> Self {
        match name {
            "Frontier" => SpecId::FRONTIER,
            "Homestead" => SpecId::HOMESTEAD,
            "Tangerine" => SpecId::TANGERINE,
            "Spurious" => SpecId::SPURIOUS_DRAGON,
            "Byzantium" => SpecId::BYZANTIUM,
            "Constantinople" => SpecId::CONSTANTINOPLE,
            "Petersburg" => SpecId::PETERSBURG,
            "Istanbul" => SpecId::ISTANBUL,
            "MuirGlacier" => SpecId::MUIRGLACIER,
            "Berlin" => SpecId::BERLIN,
            "London" => SpecId::LONDON,
            _ => SpecId::LATEST,
        }
    }
}

impl SpecId {
    #[inline]
    pub const fn enabled(our: SpecId, other: SpecId) -> bool {
        our as u8 >= other as u8
    }
}

pub(crate) trait NotStaticSpec {}

pub trait Spec: Sized {
    /// little bit of magic. We can have child version of Spec that contains static flag enabled
    type STATIC: Spec;

    #[inline(always)]
    fn enabled(spec_id: SpecId) -> bool {
        Self::SPEC_ID as u8 >= spec_id as u8
    }
    const SPEC_ID: SpecId;
    /// static flag used in STATIC type;
    const IS_STATIC_CALL: bool;

    const ASSUME_PRECOMPILE_HAS_BALANCE: bool;
}

mod spec_impl {
    use super::{NotStaticSpec, Spec};

    macro_rules! spec {
        ($spec_id:tt) => {
            #[allow(non_snake_case)]
            pub mod $spec_id {
                use super::{NotStaticSpec, Spec};
                use crate::SpecId;

                pub struct SpecInner<
                    const STATIC_CALL: bool,
                    const ASSUME_PRECOMPILE_HAS_BALANCE: bool,
                >;

                pub type SpecImpl = SpecInner<false, true>;
                pub type SpecStaticImpl = SpecInner<true, true>;

                impl NotStaticSpec for SpecImpl {}

                impl<const IS_STATIC_CALL: bool, const ASSUME_PRECOMPILE_HAS_BALANCE: bool> Spec
                    for SpecInner<IS_STATIC_CALL, ASSUME_PRECOMPILE_HAS_BALANCE>
                {
                    type STATIC = SpecInner<true, ASSUME_PRECOMPILE_HAS_BALANCE>;

                    //specification id
                    const SPEC_ID: SpecId = SpecId::$spec_id;

                    const IS_STATIC_CALL: bool = IS_STATIC_CALL;

                    const ASSUME_PRECOMPILE_HAS_BALANCE: bool = ASSUME_PRECOMPILE_HAS_BALANCE;
                }
            }
        };
    }

    spec!(LATEST);
    spec!(LONDON);
    spec!(BERLIN);
    spec!(ISTANBUL);
    spec!(BYZANTIUM);
    spec!(FRONTIER);
}

pub use spec_impl::{
    BERLIN::SpecImpl as BerlinSpec, BYZANTIUM::SpecImpl as ByzantiumSpec,
    FRONTIER::SpecImpl as FrontierSpec, ISTANBUL::SpecImpl as IstanbulSpec,
    LATEST::SpecImpl as LatestSpec, LONDON::SpecImpl as LondonSpec,
};