1use crate::*;
4
5#[derive(Clone, Copy, Debug, Error, PartialEq)]
6pub enum BlockstampFromBytesError {
8 #[error("Given bytes have invalid length")]
10 InvalidLen,
11}
12
13#[derive(Clone, Copy, Debug, Eq, Error, PartialEq)]
17pub enum BlockstampParseError {
18 #[error("Given bytes have invalid length")]
20 InvalidLen,
21 #[error("Given string have invalid format")]
23 InvalidFormat,
24 #[error("BlockNumber part is not a valid number.")]
26 InvalidBlockNumber,
27 #[error("BlockHash part is not a valid hex number.")]
29 InvalidBlockHash(BaseConversionError),
30}
31
32#[derive(Copy, Clone, Default, Deserialize, PartialEq, Eq, Hash, Serialize)]
44pub struct Blockstamp {
45 pub number: BlockNumber,
47 pub hash: BlockHash,
49}
50
51pub type PreviousBlockstamp = Blockstamp;
53
54impl Blockstamp {
55 pub const SIZE_IN_BYTES: usize = 36;
57}
58
59impl From<Blockstamp> for [u8; Blockstamp::SIZE_IN_BYTES] {
60 fn from(blockstamp: Blockstamp) -> Self {
61 let mut bytes = [0u8; Blockstamp::SIZE_IN_BYTES];
62
63 bytes[..4].copy_from_slice(&blockstamp.number.0.to_be_bytes());
64
65 unsafe {
66 std::ptr::copy_nonoverlapping(
67 (blockstamp.hash.0).0.as_ptr(),
68 bytes[4..].as_mut_ptr(),
69 Hash::SIZE_IN_BYTES,
70 );
71 }
72
73 bytes
74 }
75}
76
77impl Display for Blockstamp {
78 fn fmt(&self, f: &mut Formatter) -> Result<(), FmtError> {
79 write!(f, "{}-{}", self.number, self.hash)
80 }
81}
82
83impl Debug for Blockstamp {
84 fn fmt(&self, f: &mut Formatter) -> Result<(), FmtError> {
85 write!(f, "Blockstamp({})", self)
86 }
87}
88
89impl PartialOrd for Blockstamp {
90 fn partial_cmp(&self, other: &Blockstamp) -> Option<Ordering> {
91 Some(self.cmp(other))
92 }
93}
94
95impl Ord for Blockstamp {
96 fn cmp(&self, other: &Blockstamp) -> Ordering {
97 if self.number == other.number {
98 self.hash.cmp(&other.hash)
99 } else {
100 self.number.cmp(&other.number)
101 }
102 }
103}
104
105impl crate::bytes_traits::FromBytes for Blockstamp {
106 type Err = BlockstampFromBytesError;
107
108 fn from_bytes(src: &[u8]) -> Result<Blockstamp, BlockstampFromBytesError> {
110 if src.len() != Blockstamp::SIZE_IN_BYTES {
111 Err(BlockstampFromBytesError::InvalidLen)
112 } else {
113 let mut id_bytes = [0u8; 4];
114 id_bytes.copy_from_slice(&src[..4]);
115 let mut hash_bytes = [0u8; 32];
116 unsafe {
117 std::ptr::copy_nonoverlapping(
118 src[4..].as_ptr(),
119 hash_bytes.as_mut_ptr(),
120 Hash::SIZE_IN_BYTES,
121 );
122 }
123 Ok(Blockstamp {
124 number: BlockNumber(u32::from_be_bytes(id_bytes)),
125 hash: BlockHash(Hash(hash_bytes)),
126 })
127 }
128 }
129}
130
131impl FromStr for Blockstamp {
132 type Err = BlockstampParseError;
133
134 fn from_str(src: &str) -> Result<Blockstamp, BlockstampParseError> {
135 let mut split = src.split('-');
136
137 match (split.next(), split.next(), split.next()) {
138 (Some(id), Some(hash), None) => {
139 let hash = Hash::from_hex(hash).map_err(BlockstampParseError::InvalidBlockHash)?;
140
141 if let Ok(id) = id.parse::<u32>() {
142 Ok(Blockstamp {
143 number: BlockNumber(id),
144 hash: BlockHash(hash),
145 })
146 } else {
147 Err(BlockstampParseError::InvalidBlockNumber)
148 }
149 }
150 _ => Err(BlockstampParseError::InvalidFormat),
151 }
152 }
153}
154
155#[cfg(test)]
156mod tests {
157
158 use super::*;
159 use crate::bytes_traits::FromBytes;
160 use unwrap::unwrap;
161
162 #[test]
163 fn blockstamp_default() {
164 assert_eq!(
165 Blockstamp::default(),
166 Blockstamp {
167 number: BlockNumber(0),
168 hash: BlockHash(Hash([0u8; 32])),
169 }
170 )
171 }
172
173 #[test]
174 fn blockstamp_ord() {
175 let b1 = unwrap!(Blockstamp::from_str(
176 "123-000003176306959F8674C25757BCE1CD27768E29A9B2F6DD2A4AACEAFF8C9413"
177 ));
178 let b2 = unwrap!(Blockstamp::from_str(
179 "124-000003176306959F8674C25757BCE1CD27768E29A9B2F6DD2A4AACEAFF8C9413"
180 ));
181 let b3 = unwrap!(Blockstamp::from_str(
182 "124-000003176306959F8674C25757BCE1CD27768E29A9B2F6DD2A4AACEAFF8C9415"
183 ));
184
185 assert!(b1 < b2);
186 assert!(b2 < b3);
187 }
188
189 #[test]
190 fn blockstamp_from_str_errors() {
191 assert_eq!(
192 Err(BlockstampParseError::InvalidFormat),
193 Blockstamp::from_str("invalid_format")
194 );
195 assert_eq!(
196 Err(BlockstampParseError::InvalidBlockNumber),
197 Blockstamp::from_str(
198 "not_a_number-000003176306959F8674C25757BCE1CD27768E29A9B2F6DD2A4AACEAFF8C9413"
199 )
200 );
201 assert_eq!(
202 Err(BlockstampParseError::InvalidBlockHash(
203 BaseConversionError::InvalidLength {
204 expected: 64,
205 found: 3,
206 }
207 )),
208 Blockstamp::from_str("123-ZZZ")
209 );
210 }
211
212 #[test]
213 fn blockstamp_from_bytes() -> Result<(), BlockstampFromBytesError> {
214 assert_eq!(
215 Blockstamp::from_bytes(&[]),
216 Err(BlockstampFromBytesError::InvalidLen)
217 );
218
219 assert_eq!(
220 Blockstamp::default(),
221 Blockstamp::from_bytes(&[
222 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
223 0, 0, 0, 0, 0, 0, 0, 0
224 ])?
225 );
226
227 assert_eq!(
228 Blockstamp {
229 number: BlockNumber(3),
230 hash: BlockHash(Hash([2u8; 32])),
231 },
232 Blockstamp::from_bytes(&[
233 0, 0, 0, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
234 2, 2, 2, 2, 2, 2, 2, 2,
235 ])?
236 );
237
238 Ok(())
239 }
240
241 #[test]
242 fn blockstamp_into_bytes() {
243 let bytes: [u8; Blockstamp::SIZE_IN_BYTES] = Blockstamp::default().into();
244 assert_eq!(&bytes[..4], &[0, 0, 0, 0,]);
245 assert_eq!(
246 &bytes[4..],
247 &[
248 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
249 0, 0, 0, 0,
250 ]
251 );
252
253 let bytes: [u8; Blockstamp::SIZE_IN_BYTES] = Blockstamp {
254 number: BlockNumber(3),
255 hash: BlockHash(Hash([2u8; 32])),
256 }
257 .into();
258 assert_eq!(&bytes[..4], &[0, 0, 0, 3,]);
259 assert_eq!(
260 &bytes[4..],
261 &[
262 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
263 2, 2, 2, 2,
264 ]
265 );
266 }
267}