1#[cfg(any(all(feature = "std", feature = "testing"), test))]
2use alloc::collections::BTreeMap;
3use alloc::{boxed::Box, vec::Vec};
4use core::fmt::{self, Display, Formatter};
5#[cfg(any(all(feature = "std", feature = "testing"), test))]
6use core::iter;
7
8#[cfg(feature = "datasize")]
9use datasize::DataSize;
10#[cfg(feature = "json-schema")]
11use schemars::JsonSchema;
12#[cfg(any(feature = "std", test))]
13use serde::{Deserialize, Serialize};
14
15#[cfg(any(feature = "once_cell", test))]
16use once_cell::sync::OnceCell;
17#[cfg(any(all(feature = "std", feature = "testing"), test))]
18use rand::Rng;
19
20#[cfg(any(all(feature = "std", feature = "testing"), test))]
21use crate::U512;
22use crate::{
23 bytesrepr::{self, FromBytes, ToBytes},
24 Block, BlockBodyV1, BlockHash, BlockHeaderV1, BlockValidationError, DeployHash, Digest,
25 EraEndV1, EraId, ProtocolVersion, PublicKey, Timestamp,
26};
27#[cfg(any(all(feature = "std", feature = "testing"), test))]
28use crate::{testing::TestRng, EraReport};
29
30#[cfg_attr(any(feature = "std", test), derive(Serialize, Deserialize))]
33#[cfg_attr(feature = "datasize", derive(DataSize))]
34#[derive(Clone, Eq, PartialEq, Debug)]
35#[cfg_attr(feature = "json-schema", derive(JsonSchema))]
36pub struct BlockV1 {
37 pub(super) hash: BlockHash,
39 pub(super) header: BlockHeaderV1,
41 pub(super) body: BlockBodyV1,
43}
44
45impl BlockV1 {
46 #[doc(hidden)]
48 #[allow(clippy::too_many_arguments)]
49 pub fn new(
50 parent_hash: BlockHash,
51 parent_seed: Digest,
52 state_root_hash: Digest,
53 random_bit: bool,
54 era_end: Option<EraEndV1>,
55 timestamp: Timestamp,
56 era_id: EraId,
57 height: u64,
58 protocol_version: ProtocolVersion,
59 proposer: PublicKey,
60 deploy_hashes: Vec<DeployHash>,
61 transfer_hashes: Vec<DeployHash>,
62 ) -> Self {
63 let body = BlockBodyV1::new(proposer, deploy_hashes, transfer_hashes);
64 let body_hash = body.hash();
65 let accumulated_seed = Digest::hash_pair(parent_seed, [random_bit as u8]);
66 let header = BlockHeaderV1::new(
67 parent_hash,
68 state_root_hash,
69 body_hash,
70 random_bit,
71 accumulated_seed,
72 era_end,
73 timestamp,
74 era_id,
75 height,
76 protocol_version,
77 #[cfg(any(feature = "once_cell", test))]
78 OnceCell::new(),
79 );
80 Self::new_from_header_and_body(header, body)
81 }
82
83 #[doc(hidden)]
85 pub fn new_from_header_and_body(header: BlockHeaderV1, body: BlockBodyV1) -> Self {
86 let hash = header.block_hash();
87 BlockV1 { hash, header, body }
88 }
89
90 pub fn hash(&self) -> &BlockHash {
92 &self.hash
93 }
94
95 pub fn header(&self) -> &BlockHeaderV1 {
97 &self.header
98 }
99
100 pub fn take_header(self) -> BlockHeaderV1 {
102 self.header
103 }
104
105 pub fn body(&self) -> &BlockBodyV1 {
107 &self.body
108 }
109
110 pub fn take_body(self) -> BlockBodyV1 {
112 self.body
113 }
114
115 pub fn parent_hash(&self) -> &BlockHash {
117 self.header.parent_hash()
118 }
119
120 pub fn state_root_hash(&self) -> &Digest {
122 self.header.state_root_hash()
123 }
124
125 pub fn body_hash(&self) -> &Digest {
127 self.header.body_hash()
128 }
129
130 pub fn random_bit(&self) -> bool {
132 self.header.random_bit()
133 }
134
135 pub fn accumulated_seed(&self) -> &Digest {
137 self.header.accumulated_seed()
138 }
139
140 pub fn era_end(&self) -> Option<&EraEndV1> {
142 self.header.era_end()
143 }
144
145 pub fn timestamp(&self) -> Timestamp {
147 self.header.timestamp()
148 }
149
150 pub fn era_id(&self) -> EraId {
152 self.header.era_id()
153 }
154
155 pub fn height(&self) -> u64 {
157 self.header.height()
158 }
159
160 pub fn protocol_version(&self) -> ProtocolVersion {
162 self.header.protocol_version()
163 }
164
165 pub fn is_switch_block(&self) -> bool {
167 self.header.is_switch_block()
168 }
169
170 pub fn is_genesis(&self) -> bool {
172 self.header.is_genesis()
173 }
174
175 pub fn proposer(&self) -> &PublicKey {
177 self.body.proposer()
178 }
179
180 pub fn deploy_hashes(&self) -> &[DeployHash] {
182 self.body.deploy_hashes()
183 }
184
185 pub fn transfer_hashes(&self) -> &[DeployHash] {
187 self.body.transfer_hashes()
188 }
189
190 pub fn deploy_and_transfer_hashes(&self) -> impl Iterator<Item = &DeployHash> {
192 self.deploy_hashes()
193 .iter()
194 .chain(self.transfer_hashes().iter())
195 }
196
197 pub fn verify(&self) -> Result<(), BlockValidationError> {
200 let actual_block_header_hash = self.header().block_hash();
201 if *self.hash() != actual_block_header_hash {
202 return Err(BlockValidationError::UnexpectedBlockHash {
203 block: Box::new(Block::V1(self.clone())),
204 actual_block_hash: actual_block_header_hash,
205 });
206 }
207
208 let actual_block_body_hash = self.body.hash();
209 if *self.header.body_hash() != actual_block_body_hash {
210 return Err(BlockValidationError::UnexpectedBodyHash {
211 block: Box::new(Block::V1(self.clone())),
212 actual_block_body_hash,
213 });
214 }
215
216 Ok(())
217 }
218
219 #[cfg(any(all(feature = "std", feature = "testing"), test))]
225 pub fn random_with_specifics<I: IntoIterator<Item = DeployHash>>(
226 rng: &mut TestRng,
227 era_id: EraId,
228 height: u64,
229 protocol_version: ProtocolVersion,
230 is_switch: bool,
231 deploy_hashes_iter: I,
232 ) -> Self {
233 let parent_hash = BlockHash::random(rng);
234 let parent_seed = Digest::random(rng);
235 let state_root_hash = Digest::random(rng);
236 let random_bit = rng.gen();
237 let era_end = is_switch.then(|| {
238 let mut next_era_validator_weights = BTreeMap::new();
239 for i in 1_u64..6 {
240 let _ = next_era_validator_weights.insert(PublicKey::random(rng), U512::from(i));
241 }
242 EraEndV1::new(EraReport::random(rng), next_era_validator_weights)
243 });
244 let timestamp = Timestamp::now();
245 let proposer = PublicKey::random(rng);
246 let mut deploy_hashes: Vec<DeployHash> = deploy_hashes_iter.into_iter().collect();
247 let mut transfer_hashes: Vec<DeployHash> = vec![];
248 if deploy_hashes.is_empty() {
249 let count = rng.gen_range(0..6);
250 deploy_hashes = iter::repeat_with(|| DeployHash::random(rng))
251 .take(count)
252 .collect();
253 let count = rng.gen_range(0..6);
254 transfer_hashes = iter::repeat_with(|| DeployHash::random(rng))
255 .take(count)
256 .collect();
257 }
258
259 BlockV1::new(
260 parent_hash,
261 parent_seed,
262 state_root_hash,
263 random_bit,
264 era_end,
265 timestamp,
266 era_id,
267 height,
268 protocol_version,
269 proposer,
270 deploy_hashes,
271 transfer_hashes,
272 )
273 }
274}
275
276impl Display for BlockV1 {
277 fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
278 write!(
279 formatter,
280 "executed block #{}, {}, timestamp {}, {}, parent {}, post-state hash {}, body hash \
281 {}, random bit {}, protocol version: {}",
282 self.height(),
283 self.hash(),
284 self.timestamp(),
285 self.era_id(),
286 self.parent_hash().inner(),
287 self.state_root_hash(),
288 self.body_hash(),
289 self.random_bit(),
290 self.protocol_version()
291 )?;
292 if let Some(era_end) = self.era_end() {
293 write!(formatter, ", era_end: {}", era_end)?;
294 }
295 Ok(())
296 }
297}
298
299impl ToBytes for BlockV1 {
300 fn write_bytes(&self, writer: &mut Vec<u8>) -> Result<(), bytesrepr::Error> {
301 self.hash.write_bytes(writer)?;
302 self.header.write_bytes(writer)?;
303 self.body.write_bytes(writer)
304 }
305
306 fn to_bytes(&self) -> Result<Vec<u8>, bytesrepr::Error> {
307 let mut buffer = bytesrepr::allocate_buffer(self)?;
308 self.write_bytes(&mut buffer)?;
309 Ok(buffer)
310 }
311
312 fn serialized_length(&self) -> usize {
313 self.hash.serialized_length()
314 + self.header.serialized_length()
315 + self.body.serialized_length()
316 }
317}
318
319impl FromBytes for BlockV1 {
320 fn from_bytes(bytes: &[u8]) -> Result<(Self, &[u8]), bytesrepr::Error> {
321 let (hash, remainder) = BlockHash::from_bytes(bytes)?;
322 let (header, remainder) = BlockHeaderV1::from_bytes(remainder)?;
323 let (body, remainder) = BlockBodyV1::from_bytes(remainder)?;
324 let block = BlockV1 { hash, header, body };
325 Ok((block, remainder))
326 }
327}
328
329#[cfg(test)]
330mod tests {
331 use crate::{Block, TestBlockV1Builder};
332
333 use super::*;
334
335 #[test]
336 fn bytesrepr_roundtrip() {
337 let rng = &mut TestRng::new();
338 let block = TestBlockV1Builder::new().build(rng);
339 bytesrepr::test_serialization_roundtrip(&block);
340 }
341
342 #[test]
343 fn block_check_bad_body_hash_sad_path() {
344 let rng = &mut TestRng::new();
345
346 let mut block = TestBlockV1Builder::new().build(rng);
347 let bogus_block_body_hash = Digest::hash([0xde, 0xad, 0xbe, 0xef]);
348 block.header.set_body_hash(bogus_block_body_hash);
349 block.hash = block.header.block_hash();
350
351 let expected_error = BlockValidationError::UnexpectedBodyHash {
352 block: Box::new(Block::V1(block.clone())),
353 actual_block_body_hash: block.body.hash(),
354 };
355 assert_eq!(block.verify(), Err(expected_error));
356 }
357
358 #[test]
359 fn block_check_bad_block_hash_sad_path() {
360 let rng = &mut TestRng::new();
361
362 let mut block = TestBlockV1Builder::new().build(rng);
363 let bogus_block_hash = BlockHash::from(Digest::hash([0xde, 0xad, 0xbe, 0xef]));
364 block.hash = bogus_block_hash;
365
366 let expected_error = BlockValidationError::UnexpectedBlockHash {
367 block: Box::new(Block::V1(block.clone())),
368 actual_block_hash: block.header.block_hash(),
369 };
370 assert_eq!(block.verify(), Err(expected_error));
371 }
372}