Skip to main content

alloy_rpc_types_beacon/
header.rs

1//! Beacon block header types.
2//!
3//! See also <https://ethereum.github.io/beacon-APIs/#/Beacon/getBlockHeaders>
4
5use alloy_primitives::{Bytes, B256};
6use serde::{Deserialize, Serialize};
7use serde_with::{serde_as, DisplayFromStr};
8
9/// Response from the [`/eth/v1/beacon/headers`](https://ethereum.github.io/beacon-APIs/#/Beacon/getBlockHeaders) endpoint.
10#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
11pub struct HeadersResponse {
12    /// True if the response references an unverified execution payload. Optimistic information may
13    /// be invalidated at a later time. If the field is not present, assume the False value.
14    pub execution_optimistic: bool,
15    /// True if the response references the finalized history of the chain, as determined by fork
16    /// choice. If the field is not present, additional calls are necessary to compare the epoch of
17    /// the requested information with the finalized checkpoint.
18    pub finalized: bool,
19    /// Container for the header data.
20    pub data: Vec<HeaderData>,
21}
22
23/// Response from the [`/eth/v1/beacon/headers/{block_id}`](https://ethereum.github.io/beacon-APIs/#/Beacon/getBlockHeader) endpoint.
24#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
25pub struct HeaderResponse {
26    /// True if the response references an unverified execution payload. Optimistic information may
27    /// be invalidated at a later time. If the field is not present, assume the False value.
28    pub execution_optimistic: bool,
29    /// True if the response references the finalized history of the chain, as determined by fork
30    /// choice. If the field is not present, additional calls are necessary to compare the epoch of
31    /// the requested information with the finalized checkpoint.
32    pub finalized: bool,
33    /// Container for the header data.
34    pub data: HeaderData,
35}
36
37/// Container type for a beacon block header.
38#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
39pub struct HeaderData {
40    /// root hash of the block
41    pub root: B256,
42    /// Whether the block is part of the canonical chain
43    pub canonical: bool,
44    /// The `SignedBeaconBlockHeader` object envelope from the CL spec.
45    pub header: Header,
46}
47
48/// [BeaconBlockHeader] with a signature.
49#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
50pub struct Header {
51    /// The [`BeaconBlockHeader`] object from the CL spec.
52    pub message: BeaconBlockHeader,
53    /// The signature associated with the [`BeaconBlockHeader`].
54    pub signature: Bytes,
55}
56
57/// The header of a beacon block.
58///
59/// See [`BeaconBlockHeader`](https://github.com/ethereum/consensus-specs/blob/v1.5.0/specs/phase0/beacon-chain.md#beaconblockheader) in the CL spec.
60#[serde_as]
61#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
62#[cfg_attr(feature = "ssz", derive(ssz_derive::Encode, ssz_derive::Decode))]
63pub struct BeaconBlockHeader {
64    /// The slot to which this block corresponds.
65    #[serde_as(as = "DisplayFromStr")]
66    pub slot: u64,
67    /// Index of validator in validator registry.
68    #[serde_as(as = "DisplayFromStr")]
69    pub proposer_index: u64,
70    /// The signing Merkle root of the parent BeaconBlock.
71    pub parent_root: B256,
72    /// The tree hash Merkle root of the BeaconState for the BeaconBlock.
73    pub state_root: B256,
74    /// The tree hash Merkle root of the BeaconBlockBody for the BeaconBlock
75    pub body_root: B256,
76}
77
78#[cfg(test)]
79mod tests {
80    use super::*;
81
82    #[test]
83    fn serde_headers_response() {
84        let s = r#"{
85            "execution_optimistic": false,
86            "finalized": false,
87            "data": [
88                {
89                    "root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
90                    "canonical": true,
91                    "header": {
92                        "message": {
93                            "slot": "1",
94                            "proposer_index": "1",
95                            "parent_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
96                            "state_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
97                            "body_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"
98                        },
99                        "signature": "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505"
100                    }
101                }
102            ]
103        }"#;
104        let _header_response: HeadersResponse = serde_json::from_str(s).unwrap();
105    }
106
107    #[test]
108    fn serde_header_response() {
109        let s = r#"{
110            "execution_optimistic": false,
111            "finalized": false,
112            "data": {
113                "root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
114                "canonical": true,
115                "header": {
116                    "message": {
117                        "slot": "1",
118                        "proposer_index": "1",
119                        "parent_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
120                        "state_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
121                        "body_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"
122                    },
123                    "signature": "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505"
124                }
125            }
126        }"#;
127        let _header_response: HeaderResponse = serde_json::from_str(s).unwrap();
128    }
129
130    #[cfg(feature = "ssz")]
131    mod ssz_tests {
132        use super::*;
133        use ssz::{Decode, Encode};
134
135        #[test]
136        fn ssz_roundtrip_beacon_block_header() {
137            let header = BeaconBlockHeader {
138                slot: 12345,
139                proposer_index: 678,
140                parent_root: B256::repeat_byte(0x11),
141                state_root: B256::repeat_byte(0x22),
142                body_root: B256::repeat_byte(0x33),
143            };
144            let encoded = header.as_ssz_bytes();
145            let decoded = BeaconBlockHeader::from_ssz_bytes(&encoded).unwrap();
146            assert_eq!(header, decoded);
147        }
148    }
149}