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