Skip to main content

dpp/block/extended_epoch_info/
mod.rs

1pub mod v0;
2
3use crate::block::epoch::EpochIndex;
4use crate::block::extended_epoch_info::v0::{ExtendedEpochInfoV0, ExtendedEpochInfoV0Getters};
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::util::deserializer::ProtocolVersion;
11use bincode::{Decode, DecodeUntrusted, Encode};
12use derive_more::From;
13use platform_serialization_derive::{
14    PlatformDeserializeTrusted, PlatformDeserializeUntrusted, PlatformSerialize,
15};
16use serde::{Deserialize, Serialize};
17
18/// Extended Epoch information
19#[cfg_attr(feature = "json-conversion", derive(JsonConvertible))]
20#[cfg_attr(feature = "value-conversion", derive(ValueConvertible))]
21#[derive(
22    Clone,
23    Debug,
24    PartialEq,
25    Serialize,
26    Deserialize,
27    Encode,
28    Decode,
29    PlatformSerialize,
30    PlatformDeserializeTrusted,
31    PlatformDeserializeUntrusted,
32    From,
33    DecodeUntrusted,
34)]
35#[platform_serialize(unversioned)] //versioned directly, no need to use platform_version
36#[serde(tag = "$formatVersion")]
37pub enum ExtendedEpochInfo {
38    #[serde(rename = "0")]
39    V0(ExtendedEpochInfoV0),
40}
41
42impl ExtendedEpochInfoV0Getters for ExtendedEpochInfo {
43    fn index(&self) -> EpochIndex {
44        match self {
45            ExtendedEpochInfo::V0(v0) => v0.index,
46        }
47    }
48
49    fn first_block_time(&self) -> u64 {
50        match self {
51            ExtendedEpochInfo::V0(v0) => v0.first_block_time,
52        }
53    }
54
55    fn first_block_height(&self) -> u64 {
56        match self {
57            ExtendedEpochInfo::V0(v0) => v0.first_block_height,
58        }
59    }
60
61    fn first_core_block_height(&self) -> u32 {
62        match self {
63            ExtendedEpochInfo::V0(v0) => v0.first_core_block_height,
64        }
65    }
66
67    fn fee_multiplier_permille(&self) -> u64 {
68        match self {
69            ExtendedEpochInfo::V0(v0) => v0.fee_multiplier_permille,
70        }
71    }
72
73    fn protocol_version(&self) -> ProtocolVersion {
74        match self {
75            ExtendedEpochInfo::V0(v0) => v0.protocol_version,
76        }
77    }
78}
79
80#[cfg(all(test, feature = "json-conversion"))]
81mod tests {
82    use super::*;
83    use crate::serialization::JsonConvertible;
84
85    #[test]
86    fn extended_epoch_info_json_round_trip() {
87        let info = ExtendedEpochInfo::V0(ExtendedEpochInfoV0 {
88            index: 5,
89            first_block_time: 1_700_000_000_000u64,
90            first_block_height: 500_000u64,
91            first_core_block_height: 800_000u32,
92            fee_multiplier_permille: 1_500u64,
93            protocol_version: 4,
94        });
95
96        let json = info.to_json().expect("to_json should succeed");
97        assert!(json["firstBlockTime"].is_number());
98        assert_eq!(json["firstBlockTime"].as_u64().unwrap(), 1700000000000);
99        assert!(json["firstBlockHeight"].is_number());
100        assert_eq!(json["firstBlockHeight"].as_u64().unwrap(), 500000);
101        assert!(json["feeMultiplierPermille"].is_number());
102        assert_eq!(json["feeMultiplierPermille"].as_u64().unwrap(), 1500);
103        assert!(json["firstCoreBlockHeight"].is_number());
104        assert_eq!(json["firstCoreBlockHeight"].as_u64().unwrap(), 800_000);
105        assert!(json["protocolVersion"].is_number());
106        assert_eq!(json["protocolVersion"].as_u64().unwrap(), 4);
107
108        let restored = ExtendedEpochInfo::from_json(json).expect("from_json should succeed");
109        assert_eq!(info, restored);
110    }
111
112    #[test]
113    fn extended_epoch_info_value_round_trip() {
114        let info = ExtendedEpochInfo::V0(ExtendedEpochInfoV0 {
115            index: 10,
116            first_block_time: u64::MAX,
117            first_block_height: 0,
118            first_core_block_height: u32::MAX,
119            fee_multiplier_permille: 1_000,
120            protocol_version: 1,
121        });
122
123        let obj = info.to_object().expect("to_object should succeed");
124        let restored = ExtendedEpochInfo::from_object(obj).expect("from_object should succeed");
125        assert_eq!(info, restored);
126    }
127}
128
129// (TODO replaced) extendedepochinfo — needs explicit fixture (no Default).
130
131#[cfg(all(
132    test,
133    feature = "json-conversion",
134    feature = "value-conversion",
135    feature = "serde-conversion"
136))]
137mod json_convertible_tests_extendedepochinfo {
138    use super::*;
139    use crate::block::extended_epoch_info::v0::ExtendedEpochInfoV0;
140    use platform_value::platform_value;
141    use serde_json::json;
142
143    fn fixture() -> ExtendedEpochInfo {
144        ExtendedEpochInfo::V0(ExtendedEpochInfoV0 {
145            index: 7,
146            first_block_time: 1_700_000_000_000,
147            first_block_height: 100,
148            first_core_block_height: 50,
149            fee_multiplier_permille: 1500,
150            protocol_version: 9,
151        })
152    }
153
154    #[test]
155    fn json_round_trip_with_full_wire_shape() {
156        use crate::serialization::JsonConvertible;
157        let original = fixture();
158        let json = original.to_json().expect("to_json");
159        // `json_safe_fields` wraps u64 values above JS_MAX_SAFE_INTEGER as
160        // strings. 1_700_000_000_000 is below the threshold (~9.0e15), so it
161        // stays numeric. JSON erases u16/u32/u64 size — the value-path
162        // assertion below uses explicit suffixes.
163        assert_eq!(
164            json,
165            json!({
166                "$formatVersion": "0",
167                "index": 7,
168                "firstBlockTime": 1_700_000_000_000_u64,
169                "firstBlockHeight": 100,
170                "firstCoreBlockHeight": 50,
171                "feeMultiplierPermille": 1500,
172                "protocolVersion": 9,
173            })
174        );
175        let recovered = ExtendedEpochInfo::from_json(json).expect("from_json");
176        assert_eq!(original, recovered);
177    }
178
179    #[test]
180    fn value_round_trip_with_full_wire_shape() {
181        use crate::serialization::ValueConvertible;
182        let original = fixture();
183        let value = original.to_object().expect("to_object");
184        // Source field types: index u16, first_block_time u64, first_block_height u64,
185        // first_core_block_height u32, fee_multiplier_permille u64, protocol_version u32.
186        assert_eq!(
187            value,
188            platform_value!({
189                "$formatVersion": "0",
190                "index": 7u16,
191                "firstBlockTime": 1_700_000_000_000_u64,
192                "firstBlockHeight": 100u64,
193                "firstCoreBlockHeight": 50u32,
194                "feeMultiplierPermille": 1500u64,
195                "protocolVersion": 9u32,
196            })
197        );
198        let recovered = ExtendedEpochInfo::from_object(value).expect("from_object");
199        assert_eq!(original, recovered);
200    }
201}