1use alloc::{
2 string::{String, ToString},
3 vec::Vec,
4};
5
6use crate::{
7 bytesrepr,
8 bytesrepr::{FromBytes, ToBytes},
9 checksummed_hex,
10 key::FromStrError,
11 Key,
12};
13
14use core::{
15 convert::TryFrom,
16 fmt::{Debug, Display, Formatter},
17};
18#[cfg(feature = "datasize")]
19use datasize::DataSize;
20#[cfg(any(feature = "testing", test))]
21use rand::distributions::{Distribution, Standard};
22#[cfg(any(feature = "testing", test))]
23use rand::Rng;
24#[cfg(feature = "json-schema")]
25use schemars::JsonSchema;
26use serde::{Deserialize, Serialize};
27
28const BLOCK_TIME_TAG: u8 = 0;
29const MESSAGE_COUNT_TAG: u8 = 1;
30const PROTOCOL_VERSION_TAG: u8 = 2;
31const ADDRESSABLE_ENTITY_TAG: u8 = 3;
32
33#[derive(
35 Debug, Default, PartialOrd, Ord, PartialEq, Eq, Hash, Clone, Copy, Serialize, Deserialize,
36)]
37#[repr(u8)]
38#[cfg_attr(feature = "datasize", derive(DataSize))]
39#[cfg_attr(feature = "json-schema", derive(JsonSchema))]
40pub enum BlockGlobalAddrTag {
41 #[default]
42 BlockTime = BLOCK_TIME_TAG,
44 MessageCount = MESSAGE_COUNT_TAG,
46 ProtocolVersion = PROTOCOL_VERSION_TAG,
48 AddressableEntity = ADDRESSABLE_ENTITY_TAG,
50}
51
52impl BlockGlobalAddrTag {
53 pub const BLOCK_GLOBAL_ADDR_TAG_LENGTH: usize = 1;
55
56 pub fn try_from_u8(value: u8) -> Option<Self> {
58 if value == BLOCK_TIME_TAG {
60 return Some(BlockGlobalAddrTag::BlockTime);
61 }
62 if value == MESSAGE_COUNT_TAG {
63 return Some(BlockGlobalAddrTag::MessageCount);
64 }
65 if value == PROTOCOL_VERSION_TAG {
66 return Some(BlockGlobalAddrTag::ProtocolVersion);
67 }
68 if value == ADDRESSABLE_ENTITY_TAG {
69 return Some(BlockGlobalAddrTag::AddressableEntity);
70 }
71 None
72 }
73}
74
75impl Display for BlockGlobalAddrTag {
76 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
77 let tag = match self {
78 BlockGlobalAddrTag::BlockTime => BLOCK_TIME_TAG,
79 BlockGlobalAddrTag::MessageCount => MESSAGE_COUNT_TAG,
80 BlockGlobalAddrTag::ProtocolVersion => PROTOCOL_VERSION_TAG,
81 BlockGlobalAddrTag::AddressableEntity => ADDRESSABLE_ENTITY_TAG,
82 };
83 write!(f, "{}", base16::encode_lower(&[tag]))
84 }
85}
86
87impl ToBytes for BlockGlobalAddrTag {
88 fn to_bytes(&self) -> Result<Vec<u8>, bytesrepr::Error> {
89 let mut buffer = bytesrepr::allocate_buffer(self)?;
90 self.write_bytes(&mut buffer)?;
91 Ok(buffer)
92 }
93
94 fn serialized_length(&self) -> usize {
95 Self::BLOCK_GLOBAL_ADDR_TAG_LENGTH
96 }
97
98 fn write_bytes(&self, writer: &mut Vec<u8>) -> Result<(), bytesrepr::Error> {
99 writer.push(*self as u8);
100 Ok(())
101 }
102}
103
104impl FromBytes for BlockGlobalAddrTag {
105 fn from_bytes(bytes: &[u8]) -> Result<(Self, &[u8]), bytesrepr::Error> {
106 if let Some((byte, rem)) = bytes.split_first() {
107 let tag = BlockGlobalAddrTag::try_from_u8(*byte).ok_or(bytesrepr::Error::Formatting)?;
108 Ok((tag, rem))
109 } else {
110 Err(bytesrepr::Error::Formatting)
111 }
112 }
113}
114
115#[derive(PartialOrd, Ord, PartialEq, Eq, Hash, Clone, Copy, Serialize, Deserialize, Default)]
119#[cfg_attr(feature = "datasize", derive(DataSize))]
120#[cfg_attr(feature = "json-schema", derive(JsonSchema))]
121pub enum BlockGlobalAddr {
122 #[default]
124 BlockTime,
125 MessageCount,
127 ProtocolVersion,
129 AddressableEntity,
131}
132
133impl BlockGlobalAddr {
134 pub const BLOCK_GLOBAL_ADDR_LENGTH: usize = BlockGlobalAddrTag::BLOCK_GLOBAL_ADDR_TAG_LENGTH;
136
137 pub fn serialized_length(&self) -> usize {
139 Self::BLOCK_GLOBAL_ADDR_LENGTH
140 }
141
142 pub fn tag(&self) -> BlockGlobalAddrTag {
144 match self {
145 BlockGlobalAddr::MessageCount => BlockGlobalAddrTag::MessageCount,
146 BlockGlobalAddr::BlockTime => BlockGlobalAddrTag::BlockTime,
147 BlockGlobalAddr::ProtocolVersion => BlockGlobalAddrTag::ProtocolVersion,
148 BlockGlobalAddr::AddressableEntity => BlockGlobalAddrTag::AddressableEntity,
149 }
150 }
151
152 pub fn to_formatted_string(self) -> String {
154 match self {
155 BlockGlobalAddr::BlockTime => base16::encode_lower(&BLOCK_TIME_TAG.to_le_bytes()),
156 BlockGlobalAddr::MessageCount => base16::encode_lower(&MESSAGE_COUNT_TAG.to_le_bytes()),
157 BlockGlobalAddr::ProtocolVersion => {
158 base16::encode_lower(&PROTOCOL_VERSION_TAG.to_le_bytes())
159 }
160 BlockGlobalAddr::AddressableEntity => {
161 base16::encode_lower(&ADDRESSABLE_ENTITY_TAG.to_le_bytes())
162 }
163 }
164 }
165
166 pub fn from_formatted_string(hex: &str) -> Result<Self, FromStrError> {
168 let bytes = checksummed_hex::decode(hex)
169 .map_err(|error| FromStrError::BlockGlobal(error.to_string()))?;
170 if bytes.is_empty() {
171 return Err(FromStrError::BlockGlobal(
172 "bytes should not be 0 len".to_string(),
173 ));
174 }
175 let tag_bytes = <[u8; BlockGlobalAddrTag::BLOCK_GLOBAL_ADDR_TAG_LENGTH]>::try_from(
176 bytes[0..BlockGlobalAddrTag::BLOCK_GLOBAL_ADDR_TAG_LENGTH].as_ref(),
177 )
178 .map_err(|err| FromStrError::BlockGlobal(err.to_string()))?;
179 let tag = <u8>::from_le_bytes(tag_bytes);
180 let tag = BlockGlobalAddrTag::try_from_u8(tag).ok_or_else(|| {
181 FromStrError::BlockGlobal("failed to parse block global addr tag".to_string())
182 })?;
183
184 match tag {
186 BlockGlobalAddrTag::BlockTime => Ok(BlockGlobalAddr::BlockTime),
187 BlockGlobalAddrTag::MessageCount => Ok(BlockGlobalAddr::MessageCount),
188 BlockGlobalAddrTag::ProtocolVersion => Ok(BlockGlobalAddr::ProtocolVersion),
189 BlockGlobalAddrTag::AddressableEntity => Ok(BlockGlobalAddr::AddressableEntity),
190 }
191 }
192}
193
194impl ToBytes for BlockGlobalAddr {
195 fn to_bytes(&self) -> Result<Vec<u8>, bytesrepr::Error> {
196 let mut buffer = bytesrepr::allocate_buffer(self)?;
197 buffer.push(self.tag() as u8);
198 Ok(buffer)
199 }
200
201 fn serialized_length(&self) -> usize {
202 self.serialized_length()
203 }
204}
205
206impl FromBytes for BlockGlobalAddr {
207 fn from_bytes(bytes: &[u8]) -> Result<(Self, &[u8]), bytesrepr::Error> {
208 let (tag, remainder): (u8, &[u8]) = FromBytes::from_bytes(bytes)?;
209 match tag {
210 tag if tag == BlockGlobalAddrTag::BlockTime as u8 => {
211 Ok((BlockGlobalAddr::BlockTime, remainder))
212 }
213 tag if tag == BlockGlobalAddrTag::MessageCount as u8 => {
214 Ok((BlockGlobalAddr::MessageCount, remainder))
215 }
216 tag if tag == BlockGlobalAddrTag::ProtocolVersion as u8 => {
217 Ok((BlockGlobalAddr::ProtocolVersion, remainder))
218 }
219 tag if tag == BlockGlobalAddrTag::AddressableEntity as u8 => {
220 Ok((BlockGlobalAddr::AddressableEntity, remainder))
221 }
222 _ => Err(bytesrepr::Error::Formatting),
223 }
224 }
225}
226
227impl From<BlockGlobalAddr> for Key {
228 fn from(block_global_addr: BlockGlobalAddr) -> Self {
229 Key::BlockGlobal(block_global_addr)
230 }
231}
232
233#[cfg(any(feature = "std", test))]
234impl TryFrom<Key> for BlockGlobalAddr {
235 type Error = ();
236
237 fn try_from(value: Key) -> Result<Self, Self::Error> {
238 if let Key::BlockGlobal(block_global_addr) = value {
239 Ok(block_global_addr)
240 } else {
241 Err(())
242 }
243 }
244}
245
246impl Display for BlockGlobalAddr {
247 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
248 let tag = self.tag();
249 write!(f, "{}", tag,)
250 }
251}
252
253impl Debug for BlockGlobalAddr {
254 fn fmt(&self, f: &mut Formatter) -> core::fmt::Result {
255 match self {
256 BlockGlobalAddr::BlockTime => write!(f, "BlockTime",),
257 BlockGlobalAddr::MessageCount => write!(f, "MessageCount",),
258 BlockGlobalAddr::ProtocolVersion => write!(f, "ProtocolVersion"),
259 BlockGlobalAddr::AddressableEntity => write!(f, "AddressableEntity"),
260 }
261 }
262}
263
264#[cfg(any(feature = "testing", test))]
265impl Distribution<BlockGlobalAddr> for Standard {
266 fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> BlockGlobalAddr {
267 match rng.gen_range(BLOCK_TIME_TAG..=ADDRESSABLE_ENTITY_TAG) {
268 BLOCK_TIME_TAG => BlockGlobalAddr::BlockTime,
269 MESSAGE_COUNT_TAG => BlockGlobalAddr::MessageCount,
270 PROTOCOL_VERSION_TAG => BlockGlobalAddr::ProtocolVersion,
271 ADDRESSABLE_ENTITY_TAG => BlockGlobalAddr::AddressableEntity,
272 _ => unreachable!(),
273 }
274 }
275}
276
277#[cfg(test)]
278mod tests {
279 use crate::{block::block_global::BlockGlobalAddr, bytesrepr};
280
281 #[test]
282 fn serialization_roundtrip() {
283 let addr = BlockGlobalAddr::BlockTime;
284 bytesrepr::test_serialization_roundtrip(&addr);
285 let addr = BlockGlobalAddr::MessageCount;
286 bytesrepr::test_serialization_roundtrip(&addr);
287 let addr = BlockGlobalAddr::ProtocolVersion;
288 bytesrepr::test_serialization_roundtrip(&addr);
289 let addr = BlockGlobalAddr::AddressableEntity;
290 bytesrepr::test_serialization_roundtrip(&addr);
291 }
292}
293
294#[cfg(test)]
295mod prop_test_gas {
296 use proptest::prelude::*;
297
298 use crate::{bytesrepr, gens};
299
300 proptest! {
301 #[test]
302 fn test_variant_gas(addr in gens::balance_hold_addr_arb()) {
303 bytesrepr::test_serialization_roundtrip(&addr);
304 }
305 }
306}