Skip to main content

dpp/block/extended_block_info/
mod.rs

1use crate::block::block_info::BlockInfo;
2use crate::block::extended_block_info::v0::{
3    ExtendedBlockInfoV0, ExtendedBlockInfoV0Getters, ExtendedBlockInfoV0Setters,
4};
5use crate::protocol_error::ProtocolError;
6#[cfg(feature = "json-conversion")]
7use crate::serialization::JsonConvertible;
8#[cfg(feature = "value-conversion")]
9use crate::serialization::ValueConvertible;
10use crate::version::FeatureVersion;
11use bincode::{Decode, DecodeUntrusted, Encode};
12use derive_more::From;
13use platform_serialization_derive::{
14    PlatformDeserializeTrusted, PlatformDeserializeUntrusted, PlatformSerialize,
15};
16use serde::{Deserialize, Serialize};
17
18pub mod v0;
19
20/// Extended Block information
21#[cfg_attr(feature = "json-conversion", derive(JsonConvertible))]
22#[cfg_attr(feature = "value-conversion", derive(ValueConvertible))]
23#[derive(
24    Clone,
25    Debug,
26    PartialEq,
27    Eq,
28    Serialize,
29    Deserialize,
30    Encode,
31    Decode,
32    PlatformSerialize,
33    PlatformDeserializeTrusted,
34    PlatformDeserializeUntrusted,
35    From,
36    DecodeUntrusted,
37)]
38#[platform_serialize(unversioned)] //versioned directly, no need to use platform_version
39#[serde(tag = "$formatVersion")]
40pub enum ExtendedBlockInfo {
41    #[serde(rename = "0")]
42    V0(ExtendedBlockInfoV0),
43}
44
45impl ExtendedBlockInfo {
46    /// Returns the version of this ExtendedBlockInfo.
47    /// Currently, the only available version is 0.
48    pub fn version(&self) -> FeatureVersion {
49        match self {
50            ExtendedBlockInfo::V0(_) => 0,
51        }
52    }
53}
54
55impl ExtendedBlockInfoV0Getters for ExtendedBlockInfo {
56    fn basic_info(&self) -> &BlockInfo {
57        match self {
58            ExtendedBlockInfo::V0(v0) => &v0.basic_info,
59        }
60    }
61
62    fn basic_info_mut(&mut self) -> &mut BlockInfo {
63        match self {
64            ExtendedBlockInfo::V0(v0) => &mut v0.basic_info,
65        }
66    }
67
68    fn basic_info_owned(self) -> BlockInfo {
69        match self {
70            ExtendedBlockInfo::V0(v0) => v0.basic_info,
71        }
72    }
73
74    fn app_hash(&self) -> &[u8; 32] {
75        match self {
76            ExtendedBlockInfo::V0(v0) => &v0.app_hash,
77        }
78    }
79
80    fn quorum_hash(&self) -> &[u8; 32] {
81        match self {
82            ExtendedBlockInfo::V0(v0) => &v0.quorum_hash,
83        }
84    }
85
86    fn proposer_pro_tx_hash(&self) -> &[u8; 32] {
87        match self {
88            ExtendedBlockInfo::V0(v0) => &v0.proposer_pro_tx_hash,
89        }
90    }
91
92    fn block_id_hash(&self) -> &[u8; 32] {
93        match self {
94            ExtendedBlockInfo::V0(v0) => &v0.block_id_hash,
95        }
96    }
97
98    fn signature(&self) -> &[u8; 96] {
99        match self {
100            ExtendedBlockInfo::V0(v0) => &v0.signature,
101        }
102    }
103
104    fn round(&self) -> u32 {
105        match self {
106            ExtendedBlockInfo::V0(v0) => v0.round,
107        }
108    }
109}
110
111impl ExtendedBlockInfoV0Setters for ExtendedBlockInfo {
112    fn set_basic_info(&mut self, info: BlockInfo) {
113        match self {
114            ExtendedBlockInfo::V0(v0) => {
115                v0.set_basic_info(info);
116            }
117        }
118    }
119
120    fn set_app_hash(&mut self, hash: [u8; 32]) {
121        match self {
122            ExtendedBlockInfo::V0(v0) => {
123                v0.set_app_hash(hash);
124            }
125        }
126    }
127
128    fn set_quorum_hash(&mut self, hash: [u8; 32]) {
129        match self {
130            ExtendedBlockInfo::V0(v0) => {
131                v0.set_quorum_hash(hash);
132            }
133        }
134    }
135
136    fn set_signature(&mut self, signature: [u8; 96]) {
137        match self {
138            ExtendedBlockInfo::V0(v0) => {
139                v0.set_signature(signature);
140            }
141        }
142    }
143
144    fn set_round(&mut self, round: u32) {
145        match self {
146            ExtendedBlockInfo::V0(v0) => {
147                v0.set_round(round);
148            }
149        }
150    }
151}
152
153#[cfg(test)]
154mod tests {
155    use super::*;
156    use crate::block::block_info::BlockInfo;
157    use crate::serialization::{PlatformDeserializableUntrusted, PlatformSerializable};
158
159    #[test]
160    fn test_extended_block_info_bincode() {
161        let block_info: ExtendedBlockInfo = ExtendedBlockInfoV0 {
162            basic_info: BlockInfo::default(),
163            app_hash: [1; 32],
164            quorum_hash: [2; 32],
165            block_id_hash: [3; 32],
166            proposer_pro_tx_hash: [4; 32],
167            signature: [3; 96],
168            round: 1,
169        }
170        .into();
171
172        // Serialize into a vector
173        let encoded =
174            PlatformSerializable::serialize_to_bytes(&block_info).expect("expected to serialize");
175
176        // Deserialize from the vector
177        let decoded: ExtendedBlockInfo =
178            PlatformDeserializableUntrusted::deserialize_from_bytes_untrusted(&encoded)
179                .expect("expected to deserialize");
180
181        assert_eq!(block_info, decoded);
182    }
183}
184
185// (TODO replaced) extendedblockinfo — needs explicit fixture (no Default).
186
187#[cfg(all(
188    test,
189    feature = "json-conversion",
190    feature = "value-conversion",
191    feature = "serde-conversion"
192))]
193mod json_convertible_tests_extendedblockinfo {
194    use super::*;
195    use crate::block::block_info::BlockInfo;
196    use crate::block::extended_block_info::v0::ExtendedBlockInfoV0;
197    use platform_value::platform_value;
198    use serde_json::json;
199
200    fn fixture() -> ExtendedBlockInfo {
201        ExtendedBlockInfo::V0(ExtendedBlockInfoV0 {
202            basic_info: BlockInfo::default(),
203            app_hash: [0x11; 32],
204            quorum_hash: [0x22; 32],
205            block_id_hash: [0x33; 32],
206            proposer_pro_tx_hash: [0x44; 32],
207            signature: [0x55; 96],
208            round: 3,
209        })
210    }
211
212    #[test]
213    fn json_round_trip_with_full_wire_shape() {
214        use crate::serialization::JsonConvertible;
215        let original = fixture();
216        let json = original.to_json().expect("to_json");
217        // `json_safe_fields` proc-macro converts u64 -> string only when above
218        // JS_MAX_SAFE_INTEGER. Default `BlockInfo` (zeros) stays numeric.
219        // 32-byte arrays are emitted as base64 strings (`appHash`, etc.); the
220        // 96-byte signature is also base64 (no Bytes32 path). JSON erases
221        // size for `round` (u32) — value-path locks `3u32` below.
222        assert_eq!(
223            json,
224            json!({
225                "$formatVersion": "0",
226                "basicInfo": {
227                    "timeMs": 0,
228                    "height": 0,
229                    "coreHeight": 0,
230                    "epoch": {"index": 0},
231                },
232                "appHash": "ERERERERERERERERERERERERERERERERERERERERERE=",
233                "quorumHash": "IiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI=",
234                "blockIdHash": "MzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzM=",
235                "proposerProTxHash": "REREREREREREREREREREREREREREREREREREREREREQ=",
236                "signature": "VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV",
237                "round": 3,
238            })
239        );
240        let recovered = ExtendedBlockInfo::from_json(json).expect("from_json");
241        assert_eq!(original, recovered);
242    }
243
244    #[test]
245    fn value_round_trip_with_full_wire_shape() {
246        use crate::serialization::ValueConvertible;
247        use platform_value::Value;
248        let original = fixture();
249        let value = original.to_object().expect("to_object");
250        // `[u8; 32]` -> `Value::Bytes32`, `[u8; 96]` -> `Value::Bytes`,
251        // `round` is `u32` -> `Value::U32`. `BlockInfo` fields use their
252        // native typed variants (U64 / U32 / U16).
253        assert_eq!(
254            value,
255            platform_value!({
256                "$formatVersion": "0",
257                "basicInfo": {
258                    "timeMs": 0u64,
259                    "height": 0u64,
260                    "coreHeight": 0u32,
261                    "epoch": {"index": 0u16},
262                },
263                "appHash": Value::Bytes32([0x11; 32]),
264                "quorumHash": Value::Bytes32([0x22; 32]),
265                "blockIdHash": Value::Bytes32([0x33; 32]),
266                "proposerProTxHash": Value::Bytes32([0x44; 32]),
267                "signature": Value::Bytes(vec![0x55; 96]),
268                "round": 3u32,
269            })
270        );
271        let recovered = ExtendedBlockInfo::from_object(value).expect("from_object");
272        assert_eq!(original, recovered);
273    }
274}