alloy_rpc_types_engine/
identification.rs

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
//! Client identification: <https://github.com/ethereum/execution-apis/blob/main/src/engine/identification.md>

use alloc::string::String;

/// This enum defines a standard for specifying a client with just two letters. Clients teams which
/// have a code reserved in this list MUST use this code when identifying themselves.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize))]
#[derive(strum::IntoStaticStr)] // Into<&'static str>, AsRef<str>, fmt::Display and serde::Serialize
#[derive(strum::EnumString)] // FromStr, TryFrom<&str>
#[non_exhaustive]
pub enum ClientCode {
    /// Besu
    BU,
    /// EthereumJS
    EJ,
    /// Erigon
    EG,
    /// Geth, go-ethereum
    GE,
    /// Grandine
    GR,
    /// Lighthouse
    LH,
    /// Lodestar
    LS,
    /// Nethermind
    NM,
    /// Nimbus
    NB,
    /// Trin Execution
    TE,
    /// Teku
    TK,
    /// Prysm
    PM,
    /// Reth
    RH,
}

impl ClientCode {
    /// Returns the client identifier as str.
    pub fn as_str(&self) -> &'static str {
        (*self).into()
    }

    /// Returns the human readable client name for the given code.
    pub const fn client_name(&self) -> &'static str {
        match self {
            Self::BU => "Besu",
            Self::EJ => "EthereumJS",
            Self::EG => "Erigon",
            Self::GE => "Geth",
            Self::GR => "Grandine",
            Self::LH => "Lighthouse",
            Self::LS => "Lodestar",
            Self::NM => "Nethermind",
            Self::NB => "Nimbus",
            Self::TE => "Trin Execution",
            Self::TK => "Teku",
            Self::PM => "Prysm",
            Self::RH => "Reth",
        }
    }
}

#[cfg(feature = "serde")]
impl serde::Serialize for ClientCode {
    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
        serializer.serialize_str(self.as_str())
    }
}

impl core::fmt::Display for ClientCode {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        self.as_str().fmt(f)
    }
}

/// Contains information which identifies a client implementation.
#[derive(Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
pub struct ClientVersionV1 {
    /// Client code, e.g. GE for Geth
    pub code: ClientCode,
    /// Human-readable name of the client, e.g. Lighthouse or go-ethereum
    pub name: String,
    /// The version string of the current implementation e.g. v4.6.0 or 1.0.0-alpha.1 or
    /// 1.0.0+20130313144700
    pub version: String,
    /// first four bytes of the latest commit hash of this build e.g: `fa4ff922`
    pub commit: String,
}

#[cfg(test)]
mod tests {
    use super::*;
    use similar_asserts::assert_eq;

    #[test]
    #[cfg(feature = "serde")]
    fn client_id_serde() {
        let s = r#"{"code":"RH","name":"Reth","version":"v1.10.8","commit":"fa4ff922"}"#;
        let v: ClientVersionV1 = serde_json::from_str(s).unwrap();
        assert_eq!(v.code, ClientCode::RH);
        assert_eq!(v.name, "Reth");
        assert_eq!(serde_json::to_string(&v).unwrap(), s);
    }
}