casper_types/block/
block_v1.rs

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/// A block after execution, with the resulting global state root hash. This is the core component
31/// of the Casper linear blockchain. Version 1.
32#[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    /// The block hash identifying this block.
38    pub(super) hash: BlockHash,
39    /// The header portion of the block.
40    pub(super) header: BlockHeaderV1,
41    /// The body portion of the block.
42    pub(super) body: BlockBodyV1,
43}
44
45impl BlockV1 {
46    // This method is not intended to be used by third party crates.
47    #[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    // This method is not intended to be used by third party crates.
84    #[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    /// Returns the `BlockHash` identifying this block.
91    pub fn hash(&self) -> &BlockHash {
92        &self.hash
93    }
94
95    /// Returns the block's header.
96    pub fn header(&self) -> &BlockHeaderV1 {
97        &self.header
98    }
99
100    /// Returns the block's header, consuming `self`.
101    pub fn take_header(self) -> BlockHeaderV1 {
102        self.header
103    }
104
105    /// Returns the block's body.
106    pub fn body(&self) -> &BlockBodyV1 {
107        &self.body
108    }
109
110    /// Returns the block's body, consuming `self`.
111    pub fn take_body(self) -> BlockBodyV1 {
112        self.body
113    }
114
115    /// Returns the parent block's hash.
116    pub fn parent_hash(&self) -> &BlockHash {
117        self.header.parent_hash()
118    }
119
120    /// Returns the root hash of global state after the deploys in this block have been executed.
121    pub fn state_root_hash(&self) -> &Digest {
122        self.header.state_root_hash()
123    }
124
125    /// Returns the hash of the block's body.
126    pub fn body_hash(&self) -> &Digest {
127        self.header.body_hash()
128    }
129
130    /// Returns a random bit needed for initializing a future era.
131    pub fn random_bit(&self) -> bool {
132        self.header.random_bit()
133    }
134
135    /// Returns a seed needed for initializing a future era.
136    pub fn accumulated_seed(&self) -> &Digest {
137        self.header.accumulated_seed()
138    }
139
140    /// Returns the `EraEnd` of a block if it is a switch block.
141    pub fn era_end(&self) -> Option<&EraEndV1> {
142        self.header.era_end()
143    }
144
145    /// Returns the timestamp from when the block was proposed.
146    pub fn timestamp(&self) -> Timestamp {
147        self.header.timestamp()
148    }
149
150    /// Returns the era ID in which this block was created.
151    pub fn era_id(&self) -> EraId {
152        self.header.era_id()
153    }
154
155    /// Returns the height of this block, i.e. the number of ancestors.
156    pub fn height(&self) -> u64 {
157        self.header.height()
158    }
159
160    /// Returns the protocol version of the network from when this block was created.
161    pub fn protocol_version(&self) -> ProtocolVersion {
162        self.header.protocol_version()
163    }
164
165    /// Returns `true` if this block is the last one in the current era.
166    pub fn is_switch_block(&self) -> bool {
167        self.header.is_switch_block()
168    }
169
170    /// Returns `true` if this block is the Genesis block, i.e. has height 0 and era 0.
171    pub fn is_genesis(&self) -> bool {
172        self.header.is_genesis()
173    }
174
175    /// Returns the public key of the validator which proposed the block.
176    pub fn proposer(&self) -> &PublicKey {
177        self.body.proposer()
178    }
179
180    /// Returns the deploy hashes within the block.
181    pub fn deploy_hashes(&self) -> &[DeployHash] {
182        self.body.deploy_hashes()
183    }
184
185    /// Returns the transfer hashes within the block.
186    pub fn transfer_hashes(&self) -> &[DeployHash] {
187        self.body.transfer_hashes()
188    }
189
190    /// Returns the deploy and transfer hashes in the order in which they were executed.
191    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    /// Returns `Ok` if and only if the block's provided block hash and body hash are identical to
198    /// those generated by hashing the appropriate input data.
199    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    /// Returns a random block, but using the provided values.
220    ///
221    /// If `deploy_hashes_iter` is empty, a few random deploy hashes will be added to the
222    /// `deploy_hashes` and `transfer_hashes` fields of the body.  Otherwise, the provided deploy
223    /// hashes will populate the `deploy_hashes` field and `transfer_hashes` will be empty.
224    #[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}