massa_models/
block_id.rs

1use crate::error::ModelsError;
2use crate::prehash::PreHashed;
3use crate::secure_share::Id;
4use massa_hash::{Hash, HashDeserializer};
5use massa_serialization::{
6    DeserializeError, Deserializer, SerializeError, Serializer, U64VarIntDeserializer,
7    U64VarIntSerializer,
8};
9use nom::{
10    error::{context, ContextError, ErrorKind, ParseError},
11    IResult,
12};
13use serde_with::{DeserializeFromStr, SerializeDisplay};
14use std::collections::Bound::Included;
15use std::str::FromStr;
16use transition::Versioned;
17
18/// block id
19#[allow(missing_docs)]
20#[transition::versioned(versions("0"))]
21#[derive(
22    Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash, SerializeDisplay, DeserializeFromStr,
23)]
24pub struct BlockId(pub Hash);
25
26impl PreHashed for BlockId {}
27
28impl Id for BlockId {
29    fn new(hash: Hash) -> Self {
30        BlockId::BlockIdV0(BlockIdV0(hash))
31    }
32
33    fn get_hash(&self) -> &Hash {
34        match self {
35            BlockId::BlockIdV0(block_id) => block_id.get_hash(),
36        }
37    }
38}
39
40impl BlockId {
41    /// first bit of the hashed block id
42    pub fn get_first_bit(&self) -> bool {
43        match self {
44            BlockId::BlockIdV0(block_id) => block_id.get_first_bit(),
45        }
46    }
47
48    /// version of the block id
49    pub fn get_version(&self) -> u64 {
50        match self {
51            BlockId::BlockIdV0(block_id) => block_id.get_version(),
52        }
53    }
54
55    /// Generate a version 0 block id from an hash used only for tests
56    #[cfg(any(test, feature = "test-exports"))]
57    pub fn generate_from_hash(hash: Hash) -> BlockId {
58        BlockId::BlockIdV0(BlockIdV0(hash))
59    }
60}
61
62#[transition::impl_version(versions("0"))]
63impl BlockId {
64    fn get_hash(&self) -> &Hash {
65        &self.0
66    }
67
68    /// first bit of the hashed block id
69    pub fn get_first_bit(&self) -> bool {
70        self.0.to_bytes()[0] >> 7 == 1
71    }
72
73    /// version of the block id
74    pub fn get_version(&self) -> u64 {
75        Self::VERSION
76    }
77}
78
79const BLOCKID_PREFIX: char = 'B';
80
81impl std::fmt::Display for BlockId {
82    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
83        match self {
84            BlockId::BlockIdV0(block_id) => write!(f, "{}", block_id),
85        }
86    }
87}
88
89#[transition::impl_version(versions("0"))]
90impl std::fmt::Display for BlockId {
91    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
92        let u64_serializer = U64VarIntSerializer::new();
93        // might want to allocate the vector with capacity in order to avoid re-allocation
94        let mut bytes: Vec<u8> = Vec::new();
95        u64_serializer
96            .serialize(&Self::VERSION, &mut bytes)
97            .map_err(|_| std::fmt::Error)?;
98        bytes.extend(self.0.to_bytes());
99        write!(
100            f,
101            "{}{}",
102            BLOCKID_PREFIX,
103            bs58::encode(bytes).with_check().into_string()
104        )
105    }
106}
107
108impl std::fmt::Debug for BlockId {
109    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
110        write!(f, "{}", self)
111    }
112}
113
114impl FromStr for BlockId {
115    type Err = ModelsError;
116    /// ## Example
117    /// ```rust
118    /// # use massa_hash::Hash;
119    /// # use std::str::FromStr;
120    /// # use massa_models::block_id::BlockId;
121    /// # use crate::massa_models::secure_share::Id;
122    /// # let hash = Hash::compute_from(b"test");
123    /// # let block_id = BlockId::new(hash);
124    /// let ser = block_id.to_string();
125    /// let res_block_id = BlockId::from_str(&ser).unwrap();
126    /// assert_eq!(block_id, res_block_id);
127    /// ```
128    fn from_str(s: &str) -> Result<Self, Self::Err> {
129        let mut chars = s.chars();
130        match chars.next() {
131            Some(prefix) if prefix == BLOCKID_PREFIX => {
132                let data = chars.collect::<String>();
133                let decoded_bs58_check = bs58::decode(data)
134                    .with_check(None)
135                    .into_vec()
136                    .map_err(|_| ModelsError::BlockIdParseError)?;
137                let block_id_deserializer = BlockIdDeserializer::new();
138                let (rest, block_id) = block_id_deserializer
139                    .deserialize::<DeserializeError>(&decoded_bs58_check[..])
140                    .map_err(|_| ModelsError::OperationIdParseError)?;
141                if rest.is_empty() {
142                    Ok(block_id)
143                } else {
144                    Err(ModelsError::OperationIdParseError)
145                }
146            }
147            _ => Err(ModelsError::BlockIdParseError),
148        }
149    }
150}
151
152#[transition::impl_version(versions("0"))]
153impl FromStr for BlockId {
154    type Err = ModelsError;
155    fn from_str(s: &str) -> Result<Self, Self::Err> {
156        let mut chars = s.chars();
157        match chars.next() {
158            Some(prefix) if prefix == BLOCKID_PREFIX => {
159                let data = chars.collect::<String>();
160                let decoded_bs58_check = bs58::decode(data)
161                    .with_check(None)
162                    .into_vec()
163                    .map_err(|_| ModelsError::BlockIdParseError)?;
164                let block_id_deserializer = BlockIdDeserializer::new();
165                let (rest, block_id) = block_id_deserializer
166                    .deserialize::<DeserializeError>(&decoded_bs58_check[..])
167                    .map_err(|_| ModelsError::OperationIdParseError)?;
168                if rest.is_empty() {
169                    Ok(block_id)
170                } else {
171                    Err(ModelsError::OperationIdParseError)
172                }
173            }
174            _ => Err(ModelsError::BlockIdParseError),
175        }
176    }
177}
178
179/// Serializer for `BlockId`
180#[derive(Default, Clone)]
181pub struct BlockIdSerializer {
182    version_serializer: U64VarIntSerializer,
183}
184
185impl BlockIdSerializer {
186    /// Creates a new serializer for `BlockId`
187    pub fn new() -> Self {
188        Self {
189            version_serializer: U64VarIntSerializer::new(),
190        }
191    }
192}
193
194impl Serializer<BlockId> for BlockIdSerializer {
195    fn serialize(&self, value: &BlockId, buffer: &mut Vec<u8>) -> Result<(), SerializeError> {
196        self.version_serializer
197            .serialize(&value.get_version(), buffer)?;
198        match value {
199            BlockId::BlockIdV0(block_id) => self.serialize(block_id, buffer),
200        }
201    }
202}
203
204#[transition::impl_version(versions("0"), structures("BlockId"))]
205impl Serializer<BlockId> for BlockIdSerializer {
206    fn serialize(&self, value: &BlockId, buffer: &mut Vec<u8>) -> Result<(), SerializeError> {
207        buffer.extend(value.0.to_bytes());
208        Ok(())
209    }
210}
211
212/// Deserializer for `BlockId`
213#[derive(Clone)]
214pub struct BlockIdDeserializer {
215    hash_deserializer: HashDeserializer,
216    version_deserializer: U64VarIntDeserializer,
217}
218
219impl Default for BlockIdDeserializer {
220    fn default() -> Self {
221        Self::new()
222    }
223}
224
225impl BlockIdDeserializer {
226    /// Creates a new deserializer for `BlockId`
227    pub fn new() -> Self {
228        Self {
229            hash_deserializer: HashDeserializer::new(),
230            version_deserializer: U64VarIntDeserializer::new(Included(0), Included(u64::MAX)),
231        }
232    }
233}
234
235impl Deserializer<BlockId> for BlockIdDeserializer {
236    fn deserialize<'a, E: ParseError<&'a [u8]> + ContextError<&'a [u8]>>(
237        &self,
238        buffer: &'a [u8],
239    ) -> IResult<&'a [u8], BlockId, E> {
240        // Verify that we at least have a version and something else
241        if buffer.len() < 2 {
242            return Err(nom::Err::Error(E::from_error_kind(buffer, ErrorKind::Eof)));
243        }
244        let (rest, op_id_version) =
245            self.version_deserializer
246                .deserialize(buffer)
247                .map_err(|_: nom::Err<E>| {
248                    nom::Err::Error(E::from_error_kind(buffer, ErrorKind::Eof))
249                })?;
250        match op_id_version {
251            <BlockId!["0"]>::VERSION => {
252                let (rest, op_id) = self.deserialize(rest)?;
253                Ok((rest, BlockIdVariant!["0"](op_id)))
254            }
255            _ => Err(nom::Err::Error(E::from_error_kind(buffer, ErrorKind::Eof))),
256        }
257    }
258}
259
260#[transition::impl_version(versions("0"), structures("BlockId"))]
261impl Deserializer<BlockId> for BlockIdDeserializer {
262    fn deserialize<'a, E: ParseError<&'a [u8]> + ContextError<&'a [u8]>>(
263        &self,
264        buffer: &'a [u8],
265    ) -> IResult<&'a [u8], BlockId, E> {
266        context("Failed BlockId deserialization", |input| {
267            let (rest, hash) = self.hash_deserializer.deserialize(input)?;
268            Ok((rest, BlockId(hash)))
269        })(buffer)
270    }
271}
272
273#[cfg(test)]
274mod test {
275    use super::*;
276
277    #[test]
278    fn test_block_id() {
279        let expected_block_id =
280            BlockId::from_str("B12iL52kye2CraMXdkdKDxjZJXF9it6E4hz8KEj656QnhvwocvBX").unwrap();
281        let actual_block_id = BlockId::generate_from_hash(Hash::compute_from("blk".as_bytes()));
282
283        assert_eq!(actual_block_id, expected_block_id);
284    }
285
286    #[test]
287    fn test_block_id_errors() {
288        let actual_error = BlockId::from_str("SomeInvalidBlockId")
289            .unwrap_err()
290            .to_string();
291        let expected_error = "block id parsing error".to_string();
292
293        assert_eq!(actual_error, expected_error);
294    }
295
296    #[test]
297    fn test_block_id_serde() {
298        let expected_block_id =
299            BlockId::from_str("B12DvrcQkzF1Wi8BVoNfc4n93CD3E2qhCNe7nVhnEQGWHZ24fEmg").unwrap();
300
301        let serialized = serde_json::to_string(&expected_block_id).unwrap();
302        let actual_block_id: BlockId = serde_json::from_str(&serialized).unwrap();
303
304        assert_eq!(actual_block_id, expected_block_id);
305    }
306}