casper_types/block/
block_identifier.rs1use alloc::vec::Vec;
2use core::num::ParseIntError;
3
4#[cfg(any(feature = "testing", test))]
5use rand::Rng;
6#[cfg(feature = "json-schema")]
7use schemars::JsonSchema;
8use serde::{Deserialize, Serialize};
9
10#[cfg(any(feature = "testing", test))]
11use crate::testing::TestRng;
12use crate::{
13 bytesrepr::{self, FromBytes, ToBytes, U8_SERIALIZED_LENGTH},
14 BlockHash, Digest, DigestError,
15};
16
17const HASH_TAG: u8 = 0;
18const HEIGHT_TAG: u8 = 1;
19
20#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq)]
22#[cfg_attr(feature = "json-schema", derive(JsonSchema))]
23#[serde(deny_unknown_fields)]
24pub enum BlockIdentifier {
25 Hash(BlockHash),
27 Height(u64),
29}
30
31impl BlockIdentifier {
32 #[cfg(any(feature = "testing", test))]
34 pub fn random(rng: &mut TestRng) -> Self {
35 match rng.gen_range(0..1) {
36 0 => Self::Hash(BlockHash::random(rng)),
37 1 => Self::Height(rng.gen()),
38 _ => panic!(),
39 }
40 }
41}
42
43impl FromBytes for BlockIdentifier {
44 fn from_bytes(bytes: &[u8]) -> Result<(Self, &[u8]), bytesrepr::Error> {
45 match bytes.split_first() {
46 Some((&HASH_TAG, rem)) => {
47 let (hash, rem) = FromBytes::from_bytes(rem)?;
48 Ok((BlockIdentifier::Hash(hash), rem))
49 }
50 Some((&HEIGHT_TAG, rem)) => {
51 let (height, rem) = FromBytes::from_bytes(rem)?;
52 Ok((BlockIdentifier::Height(height), rem))
53 }
54 Some(_) | None => Err(bytesrepr::Error::Formatting),
55 }
56 }
57}
58
59impl ToBytes for BlockIdentifier {
60 fn to_bytes(&self) -> Result<Vec<u8>, bytesrepr::Error> {
61 let mut buffer = bytesrepr::allocate_buffer(self)?;
62 self.write_bytes(&mut buffer)?;
63 Ok(buffer)
64 }
65
66 fn write_bytes(&self, writer: &mut Vec<u8>) -> Result<(), bytesrepr::Error> {
67 match self {
68 BlockIdentifier::Hash(hash) => {
69 writer.push(HASH_TAG);
70 hash.write_bytes(writer)?;
71 }
72 BlockIdentifier::Height(height) => {
73 writer.push(HEIGHT_TAG);
74 height.write_bytes(writer)?;
75 }
76 }
77 Ok(())
78 }
79
80 fn serialized_length(&self) -> usize {
81 U8_SERIALIZED_LENGTH
82 + match self {
83 BlockIdentifier::Hash(hash) => hash.serialized_length(),
84 BlockIdentifier::Height(height) => height.serialized_length(),
85 }
86 }
87}
88
89impl core::str::FromStr for BlockIdentifier {
90 type Err = ParseBlockIdentifierError;
91
92 fn from_str(maybe_block_identifier: &str) -> Result<Self, Self::Err> {
93 if maybe_block_identifier.is_empty() {
94 return Err(ParseBlockIdentifierError::EmptyString);
95 }
96
97 if maybe_block_identifier.len() == (Digest::LENGTH * 2) {
98 let hash = Digest::from_hex(maybe_block_identifier)
99 .map_err(ParseBlockIdentifierError::FromHexError)?;
100 Ok(BlockIdentifier::Hash(BlockHash::new(hash)))
101 } else {
102 let height = maybe_block_identifier
103 .parse()
104 .map_err(ParseBlockIdentifierError::ParseIntError)?;
105 Ok(BlockIdentifier::Height(height))
106 }
107 }
108}
109
110#[derive(Debug)]
112#[cfg_attr(feature = "std", derive(thiserror::Error))]
113pub enum ParseBlockIdentifierError {
114 #[cfg_attr(
116 feature = "std",
117 error("Empty string is not a valid block identifier.")
118 )]
119 EmptyString,
120 #[cfg_attr(feature = "std", error("Unable to parse height from string. {0}"))]
122 ParseIntError(ParseIntError),
123 #[cfg_attr(feature = "std", error("Unable to parse digest from string. {0}"))]
125 FromHexError(DigestError),
126}
127
128#[cfg(test)]
129mod tests {
130 use super::*;
131 use crate::testing::TestRng;
132
133 #[test]
134 fn bytesrepr_roundtrip() {
135 let rng = &mut TestRng::new();
136
137 let val = BlockIdentifier::random(rng);
138 bytesrepr::test_serialization_roundtrip(&val);
139 }
140}