casper_types/block/
block_global.rs

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/// Serialization tag for BlockGlobalAddr variants.
34#[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    /// Tag for block time variant.
43    BlockTime = BLOCK_TIME_TAG,
44    /// Tag for processing variant.
45    MessageCount = MESSAGE_COUNT_TAG,
46    /// Tag for protocol version variant.
47    ProtocolVersion = PROTOCOL_VERSION_TAG,
48    /// Tag for addressable entity variant.
49    AddressableEntity = ADDRESSABLE_ENTITY_TAG,
50}
51
52impl BlockGlobalAddrTag {
53    /// The length in bytes of a [`BlockGlobalAddrTag`].
54    pub const BLOCK_GLOBAL_ADDR_TAG_LENGTH: usize = 1;
55
56    /// Attempts to map `BalanceHoldAddrTag` from a u8.
57    pub fn try_from_u8(value: u8) -> Option<Self> {
58        // TryFrom requires std, so doing this instead.
59        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/// Address for singleton values associated to specific block. These are values which are
116/// calculated or set during the execution of a block such as the block timestamp, or the
117/// total count of messages emitted during the execution of the block, and so on.
118#[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    /// Block time variant
123    #[default]
124    BlockTime,
125    /// Message count variant.
126    MessageCount,
127    /// Protocol version.
128    ProtocolVersion,
129    /// Addressable entity.
130    AddressableEntity,
131}
132
133impl BlockGlobalAddr {
134    /// The length in bytes of a [`BlockGlobalAddr`].
135    pub const BLOCK_GLOBAL_ADDR_LENGTH: usize = BlockGlobalAddrTag::BLOCK_GLOBAL_ADDR_TAG_LENGTH;
136
137    /// How long is be the serialized value for this instance.
138    pub fn serialized_length(&self) -> usize {
139        Self::BLOCK_GLOBAL_ADDR_LENGTH
140    }
141
142    /// Returns the tag of this instance.
143    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    /// To formatted string.
153    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    /// From formatted string.
167    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        // if more tags are added, extend the below logic to handle every case.
185        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}