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