1use crate::{
9 block_data::{BlockData, BlockDataUnchecked, BlockType},
10 common::{Author, Payload, Round},
11 quorum_cert::QuorumCert,
12};
13use anyhow::{bail, ensure, format_err};
14use diem_crypto::{hash::CryptoHash, HashValue};
15use diem_types::{
16 account_address::AccountAddress,
17 block_info::{BlockInfo, PivotBlockDecision},
18 block_metadata::BlockMetadata,
19 epoch_state::EpochState,
20 ledger_info::LedgerInfo,
21 transaction::Version,
22 validator_config::{ConsensusSignature, ConsensusVRFProof},
23 validator_signer::ValidatorSigner,
24 validator_verifier::ValidatorVerifier,
25};
26use mirai_annotations::debug_checked_verify_eq;
27use serde::{Deserialize, Deserializer, Serialize};
28use std::{
29 fmt::{self, Display, Formatter},
30 time::{SystemTime, UNIX_EPOCH},
31};
32
33#[path = "block_test_utils.rs"]
34#[cfg(any(test, feature = "fuzzing"))]
35pub mod block_test_utils;
36
37#[cfg(test)]
38#[path = "block_test.rs"]
39pub mod block_test;
40
41#[derive(Serialize, Clone, PartialEq, Eq)]
42pub struct Block {
46 #[serde(skip)]
48 id: HashValue,
49 block_data: BlockData,
51 signature: Option<ConsensusSignature>,
54 vrf_nonce_and_proof: Option<(u64, ConsensusVRFProof)>,
57}
58
59#[derive(Deserialize)]
60pub struct BlockUnchecked {
61 block_data: BlockDataUnchecked,
62 signature: Option<ConsensusSignature>,
63 vrf_nonce_and_proof: Option<(u64, ConsensusVRFProof)>,
64}
65
66impl From<BlockUnchecked> for Block {
67 fn from(b: BlockUnchecked) -> Self {
68 let block_data: BlockData = b.block_data.into();
69 Self {
70 id: block_data.hash(),
71 block_data,
72 signature: b.signature,
73 vrf_nonce_and_proof: b.vrf_nonce_and_proof,
74 }
75 }
76}
77
78impl fmt::Debug for Block {
79 fn fmt(&self, f: &mut Formatter) -> fmt::Result { write!(f, "{}", self) }
80}
81
82impl Display for Block {
83 fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
84 let nil_marker = if self.is_nil_block() { " (NIL)" } else { "" };
85 write!(
86 f,
87 "[id: {}{}, epoch: {}, round: {:02}, parent_id: {}]",
88 self.id,
89 nil_marker,
90 self.epoch(),
91 self.round(),
92 self.quorum_cert().certified_block().id(),
93 )
94 }
95}
96
97impl Block {
98 pub fn author(&self) -> Option<Author> { self.block_data.author() }
99
100 pub fn epoch(&self) -> u64 { self.block_data.epoch() }
101
102 pub fn id(&self) -> HashValue { self.id }
103
104 #[cfg(test)]
106 pub fn is_parent_of(&self, block: &Self) -> bool {
107 block.parent_id() == self.id
108 }
109
110 pub fn parent_id(&self) -> HashValue {
111 self.block_data.quorum_cert().certified_block().id()
112 }
113
114 pub fn payload(&self) -> Option<&Payload> { self.block_data.payload() }
115
116 pub fn quorum_cert(&self) -> &QuorumCert { self.block_data.quorum_cert() }
117
118 pub fn round(&self) -> Round { self.block_data.round() }
119
120 pub fn signature(&self) -> Option<&ConsensusSignature> {
121 self.signature.as_ref()
122 }
123
124 pub fn vrf_proof(&self) -> Option<&ConsensusVRFProof> {
125 self.vrf_nonce_and_proof
126 .as_ref()
127 .map(|(_nonce, proof)| proof)
128 }
129
130 pub fn vrf_nonce(&self) -> Option<u64> {
131 self.vrf_nonce_and_proof
132 .as_ref()
133 .map(|(nonce, _proof)| *nonce)
134 }
135
136 pub fn set_vrf_nonce_and_proof(
137 &mut self, vrf_nonce_and_proof: (u64, ConsensusVRFProof),
138 ) {
139 debug_assert!(self.vrf_nonce_and_proof.is_none());
140 self.vrf_nonce_and_proof = Some(vrf_nonce_and_proof);
141 }
142
143 pub fn timestamp_usecs(&self) -> u64 { self.block_data.timestamp_usecs() }
144
145 pub fn gen_block_info(
146 &self, executed_state_id: HashValue, version: Version,
147 next_epoch_state: Option<EpochState>,
148 pivot: Option<PivotBlockDecision>,
149 ) -> BlockInfo {
150 BlockInfo::new(
151 self.epoch(),
152 self.round(),
153 self.id(),
154 executed_state_id,
155 version,
156 self.timestamp_usecs(),
157 next_epoch_state,
158 pivot,
159 )
160 }
161
162 pub fn block_data(&self) -> &BlockData { &self.block_data }
163
164 pub fn is_genesis_block(&self) -> bool {
165 self.block_data.is_genesis_block()
166 }
167
168 pub fn is_nil_block(&self) -> bool { self.block_data.is_nil_block() }
169
170 #[cfg(any(test, feature = "fuzzing"))]
171 pub fn make_genesis_block() -> Self {
172 Self::make_genesis_block_from_ledger_info(&LedgerInfo::mock_genesis(
173 None,
174 ))
175 }
176
177 pub fn make_genesis_block_from_ledger_info(
181 ledger_info: &LedgerInfo,
182 ) -> Self {
183 let block_data = BlockData::new_genesis_from_ledger_info(ledger_info);
184 Block {
185 id: block_data.hash(),
186 block_data,
187 signature: None,
188 vrf_nonce_and_proof: None,
189 }
190 }
191
192 #[cfg(any(test, feature = "fuzzing"))]
193 pub fn new_for_testing(
196 id: HashValue, block_data: BlockData,
197 signature: Option<ConsensusSignature>,
198 vrf_proof: Option<(u64, ConsensusVRFProof)>,
199 ) -> Self {
200 Block {
201 id,
202 block_data,
203 signature,
204 vrf_nonce_and_proof: vrf_proof,
205 }
206 }
207
208 pub fn new_nil(round: Round, quorum_cert: QuorumCert) -> Self {
212 let block_data = BlockData::new_nil(round, quorum_cert);
213
214 Block {
215 id: block_data.hash(),
216 block_data,
217 signature: None,
218 vrf_nonce_and_proof: None,
219 }
220 }
221
222 pub fn new_proposal(
224 payload: Payload, round: Round, timestamp_usecs: u64,
225 quorum_cert: QuorumCert, validator_signer: &ValidatorSigner,
226 ) -> Self {
227 let block_data = BlockData::new_proposal(
228 payload,
229 validator_signer.author(),
230 round,
231 timestamp_usecs,
232 quorum_cert,
233 );
234
235 Self::new_proposal_from_block_data(block_data, validator_signer)
236 }
237
238 pub fn new_proposal_from_block_data(
240 block_data: BlockData, validator_signer: &ValidatorSigner,
241 ) -> Self {
242 let signature = validator_signer.sign(&block_data);
243 Self::new_proposal_from_block_data_and_signature(
244 block_data, signature, None,
245 )
246 }
247
248 pub fn new_proposal_from_block_data_and_signature(
249 block_data: BlockData, signature: ConsensusSignature,
250 vrf_nonce_and_proof: Option<(u64, ConsensusVRFProof)>,
251 ) -> Self {
252 Block {
253 id: block_data.hash(),
254 block_data,
255 signature: Some(signature),
256 vrf_nonce_and_proof,
257 }
258 }
259
260 pub fn validate_signature(
263 &self, validator: &ValidatorVerifier,
264 ) -> anyhow::Result<()> {
265 match self.block_data.block_type() {
266 BlockType::Genesis => {
267 bail!("We should not accept genesis from others")
268 }
269 BlockType::NilBlock => self.quorum_cert().verify(validator),
270 BlockType::Proposal { author, .. } => {
271 let signature = self.signature.as_ref().ok_or_else(|| {
272 format_err!("Missing signature in Proposal")
273 })?;
274 validator.verify(*author, &self.block_data, signature)?;
275 self.quorum_cert().verify(validator)
276 }
277 }
278 }
279
280 pub fn verify_well_formed(&self) -> anyhow::Result<()> {
283 ensure!(
284 !self.is_genesis_block(),
285 "We must not accept genesis from others"
286 );
287 let parent = self.quorum_cert().certified_block();
288 ensure!(
289 parent.round() < self.round(),
290 "Block must have a greater round than parent's block"
291 );
292 ensure!(
293 parent.epoch() == self.epoch(),
294 "block's parent should be in the same epoch"
295 );
296 if parent.has_reconfiguration() {
297 ensure!(
298 self.payload().map_or(true, |p| p.is_empty()),
299 "Reconfiguration suffix should not carry payload"
300 );
301 }
302 if self.is_nil_block() || parent.has_reconfiguration() {
303 ensure!(
304 self.timestamp_usecs() == parent.timestamp_usecs(),
305 "Nil/reconfig suffix block must have same timestamp as parent"
306 );
307 } else {
308 ensure!(
309 self.timestamp_usecs() > parent.timestamp_usecs(),
310 "Blocks must have strictly increasing timestamps"
311 );
312
313 let current_ts = SystemTime::now()
314 .duration_since(UNIX_EPOCH)
315 .expect("System time is before UNIX_EPOCH");
316
317 const TIMEBOUND: u64 = 300_000_000;
319 ensure!(
320 self.timestamp_usecs()
321 <= (current_ts.as_micros() as u64)
322 .saturating_add(TIMEBOUND),
323 "Blocks must not be too far in the future"
324 );
325 }
326 ensure!(
327 !self.quorum_cert().ends_epoch(),
328 "Block cannot be proposed in an epoch that has ended"
329 );
330 debug_checked_verify_eq!(
331 self.id(),
332 self.block_data.hash(),
333 "Block id mismatch the hash"
334 );
335 Ok(())
336 }
337}
338
339impl<'de> Deserialize<'de> for Block {
340 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
341 where D: Deserializer<'de> {
342 #[derive(Deserialize)]
343 #[serde(rename = "Block")]
344 struct BlockWithoutId {
345 block_data: BlockData,
346 signature: Option<ConsensusSignature>,
347 vrf_nonce_and_proof: Option<(u64, ConsensusVRFProof)>,
348 }
349
350 let BlockWithoutId {
351 block_data,
352 signature,
353 vrf_nonce_and_proof,
354 } = BlockWithoutId::deserialize(deserializer)?;
355
356 Ok(Block {
357 id: block_data.hash(),
358 block_data,
359 signature,
360 vrf_nonce_and_proof,
361 })
362 }
363}
364
365impl From<&Block> for BlockMetadata {
366 fn from(block: &Block) -> Self {
367 Self::new(
368 block.id(),
369 block.round(),
370 block.timestamp_usecs(),
371 block
373 .quorum_cert()
374 .ledger_info()
375 .signatures()
376 .keys()
377 .cloned()
378 .collect(),
379 block.author().unwrap_or(AccountAddress::ZERO),
382 )
383 }
384}