Skip to main content

alloy_rpc_types_beacon/
state.rs

1//! Types for the beacon state endpoints.
2//!
3//! See <https://ethereum.github.io/beacon-APIs/#/Beacon>
4
5use crate::block::Checkpoint;
6use alloy_primitives::B256;
7use serde::{Deserialize, Serialize};
8use serde_with::{serde_as, DisplayFromStr};
9
10/// Response from the [`/eth/v1/beacon/states/{state_id}/committees`](https://ethereum.github.io/beacon-APIs/#/Beacon/getEpochCommittees) endpoint.
11#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
12pub struct CommitteesResponse {
13    /// Whether the response references an unverified execution payload.
14    #[serde(default)]
15    pub execution_optimistic: bool,
16    /// Whether the response references finalized history.
17    #[serde(default)]
18    pub finalized: bool,
19    /// The list of committees.
20    pub data: Vec<Committee>,
21}
22
23/// A single committee entry.
24#[serde_as]
25#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
26pub struct Committee {
27    /// The committee index at a slot.
28    #[serde_as(as = "DisplayFromStr")]
29    pub index: u64,
30    /// The slot at which the committee was assigned.
31    #[serde_as(as = "DisplayFromStr")]
32    pub slot: u64,
33    /// List of validator indices assigned to this committee.
34    #[serde_as(as = "Vec<DisplayFromStr>")]
35    pub validators: Vec<u64>,
36}
37
38/// Response from the [`/eth/v1/beacon/states/{state_id}/sync_committees`](https://ethereum.github.io/beacon-APIs/#/Beacon/getEpochSyncCommittees) endpoint.
39#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
40pub struct SyncCommitteesResponse {
41    /// Whether the response references an unverified execution payload.
42    #[serde(default)]
43    pub execution_optimistic: bool,
44    /// Whether the response references finalized history.
45    #[serde(default)]
46    pub finalized: bool,
47    /// The sync committee data.
48    pub data: SyncCommittee,
49}
50
51/// Sync committee validator indices.
52#[serde_as]
53#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
54pub struct SyncCommittee {
55    /// All validators in the current sync committee.
56    #[serde_as(as = "Vec<DisplayFromStr>")]
57    pub validators: Vec<u64>,
58    /// Subcommittee slices of the sync committee.
59    #[serde_as(as = "Vec<Vec<DisplayFromStr>>")]
60    pub validator_aggregates: Vec<Vec<u64>>,
61}
62
63/// Response from the [`/eth/v1/beacon/states/{state_id}/finality_checkpoints`](https://ethereum.github.io/beacon-APIs/#/Beacon/getStateFinalityCheckpoints) endpoint.
64#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
65pub struct FinalityCheckpointsResponse {
66    /// Whether the response references an unverified execution payload.
67    #[serde(default)]
68    pub execution_optimistic: bool,
69    /// Whether the response references finalized history.
70    #[serde(default)]
71    pub finalized: bool,
72    /// The finality checkpoint data.
73    pub data: FinalityCheckpoints,
74}
75
76/// Finality checkpoints for the beacon state.
77#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
78pub struct FinalityCheckpoints {
79    /// The previous justified checkpoint.
80    pub previous_justified: Checkpoint,
81    /// The current justified checkpoint.
82    pub current_justified: Checkpoint,
83    /// The finalized checkpoint.
84    pub finalized: Checkpoint,
85}
86
87/// Response from the [`/eth/v1/beacon/states/{state_id}/validator_balances`](https://ethereum.github.io/beacon-APIs/#/Beacon/getStateValidatorBalances) endpoint.
88#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
89pub struct ValidatorBalancesResponse {
90    /// Whether the response references an unverified execution payload.
91    #[serde(default)]
92    pub execution_optimistic: bool,
93    /// Whether the response references finalized history.
94    #[serde(default)]
95    pub finalized: bool,
96    /// The list of validator balances.
97    pub data: Vec<ValidatorBalance>,
98}
99
100/// A single validator balance entry.
101#[serde_as]
102#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
103pub struct ValidatorBalance {
104    /// The index of the validator in the validator registry.
105    #[serde_as(as = "DisplayFromStr")]
106    pub index: u64,
107    /// The balance of the validator in Gwei.
108    #[serde_as(as = "DisplayFromStr")]
109    pub balance: u64,
110}
111
112/// Response from the [`/eth/v1/beacon/states/{state_id}/randao`](https://ethereum.github.io/beacon-APIs/#/Beacon/getStateRandao) endpoint.
113#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
114pub struct RandaoResponse {
115    /// Whether the response references an unverified execution payload.
116    #[serde(default)]
117    pub execution_optimistic: bool,
118    /// Whether the response references finalized history.
119    #[serde(default)]
120    pub finalized: bool,
121    /// The RANDAO mix data.
122    pub data: RandaoData,
123}
124
125/// The RANDAO mix for the requested state.
126#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
127pub struct RandaoData {
128    /// The RANDAO mix value.
129    pub randao: B256,
130}
131
132/// Response from the [`/eth/v1/beacon/states/{state_id}/root`](https://ethereum.github.io/beacon-APIs/#/Beacon/getStateRoot) endpoint.
133#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
134pub struct StateRootResponse {
135    /// Whether the response references an unverified execution payload.
136    #[serde(default)]
137    pub execution_optimistic: bool,
138    /// Whether the response references finalized history.
139    #[serde(default)]
140    pub finalized: bool,
141    /// The state root data.
142    pub data: StateRootData,
143}
144
145/// The state root for the requested state.
146#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
147pub struct StateRootData {
148    /// The state root hash.
149    pub root: B256,
150}
151
152#[cfg(test)]
153mod tests {
154    use super::*;
155
156    #[test]
157    fn serde_committees_response() {
158        let s = r#"{
159            "execution_optimistic": false,
160            "finalized": true,
161            "data": [
162                {
163                    "index": "1",
164                    "slot": "2",
165                    "validators": ["0", "1", "2"]
166                }
167            ]
168        }"#;
169        let resp: CommitteesResponse = serde_json::from_str(s).unwrap();
170        assert_eq!(resp.data.len(), 1);
171        assert_eq!(resp.data[0].index, 1);
172        assert_eq!(resp.data[0].slot, 2);
173        assert_eq!(resp.data[0].validators, vec![0, 1, 2]);
174        assert!(resp.finalized);
175        assert!(!resp.execution_optimistic);
176
177        let roundtrip: CommitteesResponse =
178            serde_json::from_str(&serde_json::to_string(&resp).unwrap()).unwrap();
179        assert_eq!(resp, roundtrip);
180    }
181
182    #[test]
183    fn serde_sync_committees_response() {
184        let s = r#"{
185            "execution_optimistic": false,
186            "finalized": true,
187            "data": {
188                "validators": ["0", "1", "2", "3"],
189                "validator_aggregates": [
190                    ["0", "1"],
191                    ["2", "3"]
192                ]
193            }
194        }"#;
195        let resp: SyncCommitteesResponse = serde_json::from_str(s).unwrap();
196        assert_eq!(resp.data.validators, vec![0, 1, 2, 3]);
197        assert_eq!(resp.data.validator_aggregates, vec![vec![0, 1], vec![2, 3]]);
198
199        let roundtrip: SyncCommitteesResponse =
200            serde_json::from_str(&serde_json::to_string(&resp).unwrap()).unwrap();
201        assert_eq!(resp, roundtrip);
202    }
203
204    #[test]
205    fn serde_finality_checkpoints_response() {
206        let s = r#"{
207            "execution_optimistic": false,
208            "finalized": true,
209            "data": {
210                "previous_justified": {
211                    "epoch": "10",
212                    "root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"
213                },
214                "current_justified": {
215                    "epoch": "11",
216                    "root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"
217                },
218                "finalized": {
219                    "epoch": "9",
220                    "root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"
221                }
222            }
223        }"#;
224        let resp: FinalityCheckpointsResponse = serde_json::from_str(s).unwrap();
225        assert_eq!(resp.data.previous_justified.epoch, 10);
226        assert_eq!(resp.data.current_justified.epoch, 11);
227        assert_eq!(resp.data.finalized.epoch, 9);
228
229        let roundtrip: FinalityCheckpointsResponse =
230            serde_json::from_str(&serde_json::to_string(&resp).unwrap()).unwrap();
231        assert_eq!(resp, roundtrip);
232    }
233
234    #[test]
235    fn serde_validator_balances_response() {
236        let s = r#"{
237            "execution_optimistic": false,
238            "finalized": true,
239            "data": [
240                {
241                    "index": "1",
242                    "balance": "32000000000"
243                }
244            ]
245        }"#;
246        let resp: ValidatorBalancesResponse = serde_json::from_str(s).unwrap();
247        assert_eq!(resp.data.len(), 1);
248        assert_eq!(resp.data[0].index, 1);
249        assert_eq!(resp.data[0].balance, 32000000000);
250
251        let roundtrip: ValidatorBalancesResponse =
252            serde_json::from_str(&serde_json::to_string(&resp).unwrap()).unwrap();
253        assert_eq!(resp, roundtrip);
254    }
255
256    #[test]
257    fn serde_randao_response() {
258        let s = r#"{
259            "execution_optimistic": false,
260            "finalized": true,
261            "data": {
262                "randao": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"
263            }
264        }"#;
265        let resp: RandaoResponse = serde_json::from_str(s).unwrap();
266        assert_eq!(
267            resp.data.randao,
268            "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"
269                .parse::<B256>()
270                .unwrap()
271        );
272
273        let roundtrip: RandaoResponse =
274            serde_json::from_str(&serde_json::to_string(&resp).unwrap()).unwrap();
275        assert_eq!(resp, roundtrip);
276    }
277
278    #[test]
279    fn serde_state_root_response() {
280        let s = r#"{
281            "execution_optimistic": false,
282            "finalized": true,
283            "data": {
284                "root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"
285            }
286        }"#;
287        let resp: StateRootResponse = serde_json::from_str(s).unwrap();
288        assert_eq!(
289            resp.data.root,
290            "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"
291                .parse::<B256>()
292                .unwrap()
293        );
294
295        let roundtrip: StateRootResponse =
296            serde_json::from_str(&serde_json::to_string(&resp).unwrap()).unwrap();
297        assert_eq!(resp, roundtrip);
298    }
299
300    #[test]
301    fn serde_committees_defaults() {
302        let s = r#"{
303            "data": [
304                {
305                    "index": "0",
306                    "slot": "0",
307                    "validators": []
308                }
309            ]
310        }"#;
311        let resp: CommitteesResponse = serde_json::from_str(s).unwrap();
312        assert!(!resp.execution_optimistic);
313        assert!(!resp.finalized);
314    }
315}