dpp/block/finalized_epoch_info/
mod.rs1mod getters;
2pub mod v0;
3
4use crate::block::finalized_epoch_info::v0::FinalizedEpochInfoV0;
5use crate::protocol_error::ProtocolError;
6#[cfg(feature = "json-conversion")]
7use crate::serialization::JsonConvertible;
8#[cfg(feature = "value-conversion")]
9use crate::serialization::ValueConvertible;
10use bincode::{Decode, DecodeUntrusted, Encode};
11use derive_more::From;
12use platform_serialization_derive::{
13 PlatformDeserializeTrusted, PlatformDeserializeUntrusted, PlatformSerialize,
14};
15use serde::{Deserialize, Serialize};
16
17#[cfg_attr(feature = "json-conversion", derive(JsonConvertible))]
19#[cfg_attr(feature = "value-conversion", derive(ValueConvertible))]
20#[derive(
21 Clone,
22 Debug,
23 PartialEq,
24 Serialize,
25 Deserialize,
26 Encode,
27 Decode,
28 PlatformSerialize,
29 PlatformDeserializeTrusted,
30 PlatformDeserializeUntrusted,
31 From,
32 DecodeUntrusted,
33)]
34#[platform_serialize(unversioned)] #[serde(tag = "$formatVersion")]
36pub enum FinalizedEpochInfo {
37 #[serde(rename = "0")]
38 V0(FinalizedEpochInfoV0),
39}
40
41#[cfg(all(test, feature = "json-conversion"))]
42mod tests {
43 use super::*;
44 use crate::serialization::JsonConvertible;
45 use platform_value::Identifier;
46 use std::collections::BTreeMap;
47
48 #[test]
49 fn finalized_epoch_info_json_round_trip() {
50 let proposer_id = Identifier::from([1u8; 32]);
51 let mut block_proposers = BTreeMap::new();
52 block_proposers.insert(proposer_id, 42u64);
53
54 let info = FinalizedEpochInfo::V0(FinalizedEpochInfoV0 {
55 first_block_time: 1_700_000_000_000u64,
56 first_block_height: 100_000u64,
57 total_blocks_in_epoch: 2_000u64,
58 first_core_block_height: 500_000u32,
59 next_epoch_start_core_block_height: 500_200u32,
60 total_processing_fees: 1_000_000u64,
61 total_distributed_storage_fees: 500_000u64,
62 total_created_storage_fees: 600_000u64,
63 core_block_rewards: 10_000_000u64,
64 block_proposers,
65 fee_multiplier_permille: 1_000u64,
66 protocol_version: 3,
67 });
68
69 let json = info.to_json().expect("to_json should succeed");
70 assert!(json["firstBlockTime"].is_number());
71 assert!(json["firstBlockHeight"].is_number());
72 assert!(json["totalBlocksInEpoch"].is_number());
73 assert!(json["totalProcessingFees"].is_number());
74 assert!(json["feeMultiplierPermille"].is_number());
75 assert!(json["firstCoreBlockHeight"].is_number());
76 assert!(json["nextEpochStartCoreBlockHeight"].is_number());
77
78 let proposers = json["blockProposers"]
79 .as_object()
80 .expect("blockProposers should be an object");
81 assert_eq!(proposers.len(), 1);
82
83 let expected_base58 =
84 proposer_id.to_string(platform_value::string_encoding::Encoding::Base58);
85 assert!(
86 proposers.contains_key(&expected_base58),
87 "Expected key {} in blockProposers, got keys: {:?}",
88 expected_base58,
89 proposers.keys().collect::<Vec<_>>()
90 );
91
92 let value = &proposers[&expected_base58];
93 assert!(value.is_number());
94 assert_eq!(value.as_u64().unwrap(), 42);
95
96 let restored = FinalizedEpochInfo::from_json(json).expect("from_json should succeed");
97 assert_eq!(info, restored);
98 }
99
100 #[test]
101 fn finalized_epoch_info_value_round_trip_empty_proposers() {
102 let info = FinalizedEpochInfo::V0(FinalizedEpochInfoV0 {
103 first_block_time: 1_000_000u64,
104 first_block_height: 10_000u64,
105 total_blocks_in_epoch: 500u64,
106 first_core_block_height: 50_000u32,
107 next_epoch_start_core_block_height: 50_200u32,
108 total_processing_fees: 100u64,
109 total_distributed_storage_fees: 50u64,
110 total_created_storage_fees: 60u64,
111 core_block_rewards: 1_000u64,
112 block_proposers: BTreeMap::new(),
113 fee_multiplier_permille: 1_000u64,
114 protocol_version: 2,
115 });
116
117 let obj = info.to_object().expect("to_object should succeed");
118 let restored = FinalizedEpochInfo::from_object(obj).expect("from_object should succeed");
119 assert_eq!(info, restored);
120 }
121}
122
123#[cfg(all(
124 test,
125 feature = "json-conversion",
126 feature = "value-conversion",
127 feature = "serde-conversion"
128))]
129mod json_convertible_tests_finalizedepochinfo {
130 use super::*;
131 use crate::block::finalized_epoch_info::v0::FinalizedEpochInfoV0;
132 use platform_value::Identifier;
133 use std::collections::BTreeMap;
134
135 fn fixture() -> FinalizedEpochInfo {
136 let mut block_proposers = BTreeMap::new();
137 block_proposers.insert(Identifier::new([0xab; 32]), 5u64);
138 FinalizedEpochInfo::V0(FinalizedEpochInfoV0 {
139 first_block_time: 1_700_000_000_000,
140 first_block_height: 100,
141 total_blocks_in_epoch: 250,
142 first_core_block_height: 50,
143 next_epoch_start_core_block_height: 75,
144 total_processing_fees: 1_000_000,
145 total_distributed_storage_fees: 200_000,
146 total_created_storage_fees: 250_000,
147 core_block_rewards: 500_000,
148 block_proposers,
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 use serde_json::json;
158 let original = fixture();
159 let json = original.to_json().expect("to_json");
160 assert_eq!(
167 json,
168 json!({
169 "$formatVersion": "0",
170 "firstBlockTime": 1_700_000_000_000_u64,
171 "firstBlockHeight": 100,
172 "totalBlocksInEpoch": 250,
173 "firstCoreBlockHeight": 50,
174 "nextEpochStartCoreBlockHeight": 75,
175 "totalProcessingFees": 1_000_000,
176 "totalDistributedStorageFees": 200_000,
177 "totalCreatedStorageFees": 250_000,
178 "coreBlockRewards": 500_000,
179 "blockProposers": {
180 "CZ8YUVdk7znjrUmnb5n7kgySk9yRAsQDYmyCxzfSky9t": 5,
181 },
182 "feeMultiplierPermille": 1500,
183 "protocolVersion": 9,
184 })
185 );
186 let recovered = FinalizedEpochInfo::from_json(json).expect("from_json");
187 assert_eq!(original, recovered);
188 }
189
190 #[test]
191 fn value_round_trip_with_full_wire_shape() {
192 use crate::serialization::ValueConvertible;
193 use platform_value::platform_value;
194 let original = fixture();
195 let value = original.to_object().expect("to_object");
196 assert_eq!(
200 value,
201 platform_value!({
202 "$formatVersion": "0",
203 "firstBlockTime": 1_700_000_000_000_u64,
204 "firstBlockHeight": 100u64,
205 "totalBlocksInEpoch": 250u64,
206 "firstCoreBlockHeight": 50u32,
207 "nextEpochStartCoreBlockHeight": 75u32,
208 "totalProcessingFees": 1_000_000u64,
209 "totalDistributedStorageFees": 200_000u64,
210 "totalCreatedStorageFees": 250_000u64,
211 "coreBlockRewards": 500_000u64,
212 "blockProposers": {
213 Identifier::new([0xab; 32]): 5u64,
214 },
215 "feeMultiplierPermille": 1500u64,
216 "protocolVersion": 9u32,
217 })
218 );
219 let recovered = FinalizedEpochInfo::from_object(value).expect("from_object");
220 assert_eq!(original, recovered);
221 }
222}