diem_types/
block_info.rs

1// Copyright (c) The Diem Core Contributors
2// SPDX-License-Identifier: Apache-2.0
3
4// Copyright 2021 Conflux Foundation. All rights reserved.
5// Conflux is free software and distributed under GNU General Public License.
6// See http://www.gnu.org/licenses/
7
8use crate::{
9    account_config, contract_event::ContractEvent, epoch_state::EpochState,
10    event::EventKey, on_chain_config::ValidatorSet, transaction::Version,
11};
12use anyhow::Result;
13use cfx_types::H256;
14use diem_crypto::hash::HashValue;
15#[cfg(any(test, feature = "fuzzing"))]
16use diem_crypto::hash::ACCUMULATOR_PLACEHOLDER_HASH;
17use diem_crypto_derive::{BCSCryptoHash, CryptoHasher};
18#[cfg(any(test, feature = "fuzzing"))]
19use proptest_derive::Arbitrary;
20use serde::{Deserialize, Serialize};
21use std::fmt::{Display, Formatter};
22
23/// The round of a block is a consensus-internal counter, which starts with 0
24/// and increases monotonically.
25pub type Round = u64;
26pub type View = u64;
27
28// Constants for the initial genesis block.
29pub const GENESIS_EPOCH: u64 = 0;
30pub const GENESIS_ROUND: Round = 0;
31pub const GENESIS_VERSION: Version = 0;
32pub const GENESIS_TIMESTAMP_USECS: u64 = 0;
33
34/// This structure contains all the information needed for tracking a block
35/// without having access to the block or its execution output state. It
36/// assumes that the block is the last block executed within the ledger.
37#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
38#[cfg_attr(any(test, feature = "fuzzing"), derive(Arbitrary))]
39pub struct BlockInfo {
40    /// Epoch number corresponds to the set of validators that are active for
41    /// this block.
42    epoch: u64,
43    /// The consensus protocol is executed in rounds, which monotonically
44    /// increase per epoch.
45    round: Round,
46    /// The identifier (hash) of the block.
47    id: HashValue,
48    /// The accumulator root hash after executing this block.
49    executed_state_id: HashValue,
50    /// The version of the latest transaction after executing this block.
51    version: Version,
52    /// The timestamp this block was proposed by a proposer.
53    timestamp_usecs: u64,
54    /// An optional field containing the next epoch info
55    next_epoch_state: Option<EpochState>,
56    /// TODO(lpl): Remove Option?
57    /// The last pivot block selection after executing this block.
58    /// None means choosing TreeGraph genesis as the first pivot block.
59    pivot: Option<PivotBlockDecision>,
60}
61
62impl BlockInfo {
63    pub fn new(
64        epoch: u64, round: Round, id: HashValue, _executed_state_id: HashValue,
65        version: Version, timestamp_usecs: u64,
66        next_epoch_state: Option<EpochState>,
67        pivot: Option<PivotBlockDecision>,
68    ) -> Self {
69        Self {
70            epoch,
71            round,
72            id,
73            // TODO(lpl): Cleanup.
74            executed_state_id: Default::default(),
75            version,
76            timestamp_usecs,
77            next_epoch_state,
78            pivot,
79        }
80    }
81
82    pub fn empty() -> Self {
83        Self {
84            epoch: 0,
85            round: 0,
86            id: HashValue::zero(),
87            executed_state_id: HashValue::zero(),
88            version: 0,
89            timestamp_usecs: 0,
90            next_epoch_state: None,
91            pivot: None,
92        }
93    }
94
95    #[cfg(any(test, feature = "fuzzing"))]
96    pub fn random(round: Round) -> Self {
97        Self {
98            epoch: 1,
99            round,
100            id: HashValue::zero(),
101            executed_state_id: HashValue::zero(),
102            version: 0,
103            timestamp_usecs: 0,
104            next_epoch_state: None,
105            pivot: None,
106        }
107    }
108
109    /// Create a new genesis block. The genesis block is effectively the
110    /// blockchain state after executing the initial genesis transaction.
111    ///
112    /// * `genesis_state_root_hash` - the state tree root hash after executing
113    ///   the
114    /// initial genesis transaction.
115    ///
116    /// * `validator_set` - the initial validator set, configured when
117    ///   generating
118    /// the genesis transaction itself and emitted after executing the genesis
119    /// transaction. Using this genesis block means transitioning to a new epoch
120    /// (GENESIS_EPOCH + 1) with this `validator_set`.
121    pub fn genesis(
122        genesis_state_root_hash: HashValue, validator_set: ValidatorSet,
123    ) -> Self {
124        Self {
125            epoch: GENESIS_EPOCH,
126            round: GENESIS_ROUND,
127            id: HashValue::zero(),
128            executed_state_id: genesis_state_root_hash,
129            version: GENESIS_VERSION,
130            timestamp_usecs: GENESIS_TIMESTAMP_USECS,
131            next_epoch_state: Some(EpochState::new(
132                1,
133                (&validator_set).into(),
134                // Only used in unit test.
135                vec![],
136            )),
137            pivot: None,
138        }
139    }
140
141    /// Create a mock genesis `BlockInfo` with an empty state tree and empty
142    /// validator set.
143    #[cfg(any(test, feature = "fuzzing"))]
144    pub fn mock_genesis(validator_set: Option<ValidatorSet>) -> Self {
145        let validator_set = validator_set.unwrap_or_else(ValidatorSet::empty);
146        Self::genesis(*ACCUMULATOR_PLACEHOLDER_HASH, validator_set)
147    }
148
149    /// The epoch after this block committed
150    pub fn next_block_epoch(&self) -> u64 {
151        self.next_epoch_state().map_or(self.epoch(), |e| e.epoch)
152    }
153
154    pub fn epoch(&self) -> u64 { self.epoch }
155
156    pub fn executed_state_id(&self) -> HashValue { self.executed_state_id }
157
158    pub fn has_reconfiguration(&self) -> bool {
159        self.next_epoch_state.is_some()
160    }
161
162    pub fn id(&self) -> HashValue { self.id }
163
164    pub fn next_epoch_state(&self) -> Option<&EpochState> {
165        self.next_epoch_state.as_ref()
166    }
167
168    pub fn round(&self) -> Round { self.round }
169
170    pub fn timestamp_usecs(&self) -> u64 { self.timestamp_usecs }
171
172    pub fn version(&self) -> Version { self.version }
173
174    pub fn pivot_decision(&self) -> Option<&PivotBlockDecision> {
175        self.pivot.as_ref()
176    }
177}
178
179impl Display for BlockInfo {
180    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
181        write!(
182            f,
183            "BlockInfo: [epoch: {}, round: {}, id: {}, executed_state_id: {}, version: {}, timestamp (us): {}, next_epoch_state: {}, pivot: {:?}]",
184            self.epoch(),
185            self.round(),
186            self.id(),
187            self.executed_state_id(),
188            self.version(),
189            self.timestamp_usecs(),
190            self.next_epoch_state.as_ref().map_or("None".to_string(), |epoch_state| format!("{}", epoch_state)),
191            self.pivot,
192        )
193    }
194}
195
196#[derive(
197    Clone,
198    Debug,
199    Default,
200    Hash,
201    Eq,
202    PartialEq,
203    Serialize,
204    Deserialize,
205    BCSCryptoHash,
206    CryptoHasher,
207)]
208#[serde(rename_all = "camelCase")]
209pub struct PivotBlockDecision {
210    pub height: u64,
211    pub block_hash: H256,
212}
213
214impl PivotBlockDecision {
215    pub fn pivot_select_event_key() -> EventKey {
216        EventKey::new_from_address(
217            &account_config::pivot_chain_select_address(),
218            2,
219        )
220    }
221
222    pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
223        bcs::from_bytes(bytes).map_err(Into::into)
224    }
225
226    pub fn to_event(&self) -> ContractEvent {
227        ContractEvent::new(
228            Self::pivot_select_event_key(),
229            bcs::to_bytes(&self).unwrap(),
230        )
231    }
232}
233
234#[cfg(any(test, feature = "fuzzing"))]
235use proptest::prelude::*;
236
237#[cfg(any(test, feature = "fuzzing"))]
238impl proptest::arbitrary::Arbitrary for PivotBlockDecision {
239    type Parameters = ();
240    type Strategy = BoxedStrategy<Self>;
241
242    fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
243        any::<u8>()
244            .prop_map(|_seed| PivotBlockDecision {
245                height: 0,
246                block_hash: H256::zero(),
247            })
248            .boxed()
249    }
250}