executor/
db_bootstrapper.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
8#![forbid(unsafe_code)]
9
10use crate::Executor;
11use anyhow::{format_err, Result};
12use cached_pos_ledger_db::CachedPosLedgerDB;
13use consensus_types::db::FakeLedgerBlockDB;
14use diem_crypto::{hash::PRE_GENESIS_BLOCK_ID, HashValue};
15use diem_logger::prelude::*;
16use diem_state_view::{StateView, StateViewId};
17use diem_types::{
18    account_address::AccountAddress,
19    block_info::{
20        BlockInfo, PivotBlockDecision, GENESIS_EPOCH, GENESIS_ROUND,
21        GENESIS_TIMESTAMP_USECS,
22    },
23    contract_event::ContractEvent,
24    ledger_info::{LedgerInfo, LedgerInfoWithSignatures},
25    on_chain_config::{new_epoch_event_key, ValidatorSet},
26    term_state::NodeID,
27    transaction::Transaction,
28    validator_config::ValidatorConfig,
29    validator_info::ValidatorInfo,
30};
31use executor_types::BlockExecutor;
32use pow_types::FakePowHandler;
33use std::{collections::btree_map::BTreeMap, sync::Arc};
34use storage_interface::{DbReaderWriter, TreeState};
35
36/// Build the genesis `Transaction` from the initial validator set. The
37/// transaction carries a single epoch-change event with the serialised
38/// `ValidatorSet`.
39fn build_genesis_transaction(initial_nodes: &[(NodeID, u64)]) -> Transaction {
40    let validators: Vec<ValidatorInfo> = initial_nodes
41        .iter()
42        .map(|(node_id, voting_power)| {
43            let config = ValidatorConfig::new(
44                node_id.public_key.clone(),
45                Some(node_id.vrf_public_key.clone()),
46                vec![],
47                vec![],
48            );
49            ValidatorInfo::new(node_id.addr, *voting_power, config)
50        })
51        .collect();
52
53    let validator_set = ValidatorSet::new(validators);
54    let event = ContractEvent::new(
55        new_epoch_event_key(),
56        bcs::to_bytes(&validator_set)
57            .expect("ValidatorSet serialization cannot fail"),
58    );
59
60    Transaction::GenesisTransaction(vec![event])
61}
62
63/// If the database has not been bootstrapped yet, commit the genesis
64/// transaction. Returns Ok(true) if committed, Ok(false) if already
65/// bootstrapped.
66pub fn maybe_bootstrap(
67    db: &DbReaderWriter, genesis_pivot_decision: Option<PivotBlockDecision>,
68    initial_seed: Vec<u8>, initial_nodes: Vec<(NodeID, u64)>,
69    initial_committee: Vec<(AccountAddress, u64)>,
70) -> Result<bool> {
71    let tree_state = db.reader.get_latest_tree_state()?;
72    // If the DB already has transactions, it's already bootstrapped.
73    if tree_state.num_transactions != 0 {
74        diem_info!("DB already bootstrapped, skipping genesis.");
75        return Ok(false);
76    }
77
78    let genesis_txn = build_genesis_transaction(&initial_nodes);
79    diem_debug!(
80        "genesis_txn={:?}, initial_nodes={:?} ",
81        genesis_txn,
82        initial_nodes,
83    );
84
85    let committer = calculate_genesis(
86        db,
87        tree_state,
88        &genesis_txn,
89        genesis_pivot_decision,
90        initial_seed,
91        initial_nodes,
92        initial_committee,
93    )?;
94    committer.commit()?;
95    Ok(true)
96}
97
98struct GenesisCommitter {
99    executor: Executor,
100    ledger_info_with_sigs: LedgerInfoWithSignatures,
101}
102
103impl GenesisCommitter {
104    fn new(
105        executor: Executor, ledger_info_with_sigs: LedgerInfoWithSignatures,
106    ) -> Result<Self> {
107        Ok(Self {
108            executor,
109            ledger_info_with_sigs,
110        })
111    }
112
113    fn commit(self) -> Result<()> {
114        self.executor.commit_blocks(
115            vec![genesis_block_id()],
116            self.ledger_info_with_sigs,
117        )?;
118        diem_info!("Genesis commited.");
119        Ok(())
120    }
121}
122
123fn calculate_genesis(
124    db: &DbReaderWriter, tree_state: TreeState, genesis_txn: &Transaction,
125    genesis_pivot_decision: Option<PivotBlockDecision>, initial_seed: Vec<u8>,
126    initial_nodes: Vec<(NodeID, u64)>,
127    initial_committee: Vec<(AccountAddress, u64)>,
128) -> Result<GenesisCommitter> {
129    let genesis_version = tree_state.num_transactions;
130    let db_with_cache = Arc::new(CachedPosLedgerDB::new_on_unbootstrapped_db(
131        db.clone(),
132        tree_state,
133        initial_seed,
134        initial_nodes,
135        initial_committee,
136        genesis_pivot_decision.clone(),
137    ));
138    let executor = Executor::new(
139        db_with_cache,
140        // This will not be used in genesis execution.
141        Arc::new(FakePowHandler {}),
142        Arc::new(FakeLedgerBlockDB {}),
143    );
144
145    let block_id = HashValue::zero();
146    assert_eq!(
147        genesis_version, 0,
148        "Conflux PoS only supports genesis at version 0"
149    );
150    let epoch = GENESIS_EPOCH;
151
152    let result = executor.execute_block(
153        (block_id, vec![genesis_txn.clone()]),
154        *PRE_GENESIS_BLOCK_ID,
155        false,
156    )?;
157
158    let root_hash = result.root_hash();
159    let next_epoch_state = result.epoch_state().as_ref().ok_or_else(|| {
160        format_err!("Genesis transaction must emit a epoch change.")
161    })?;
162    let executed_trees = executor.get_executed_trees(block_id)?;
163    let state_view = executor
164        .get_executed_state_view(StateViewId::Miscellaneous, &executed_trees);
165    diem_debug!(
166        "after genesis: epoch_state={:?}, pos_state={:?}",
167        next_epoch_state,
168        state_view.pos_state().epoch_state()
169    );
170    let timestamp_usecs = GENESIS_TIMESTAMP_USECS;
171
172    let ledger_info_with_sigs = LedgerInfoWithSignatures::new(
173        LedgerInfo::new(
174            BlockInfo::new(
175                epoch,
176                GENESIS_ROUND,
177                block_id,
178                root_hash,
179                genesis_version,
180                timestamp_usecs,
181                Some(next_epoch_state.clone()),
182                genesis_pivot_decision,
183            ),
184            HashValue::zero(), /* consensus_data_hash */
185        ),
186        BTreeMap::default(), /* signatures */
187    );
188
189    let committer = GenesisCommitter::new(executor, ledger_info_with_sigs)?;
190    diem_info!(
191        "Genesis calculated: ledger_info_with_sigs {:?}",
192        committer.ledger_info_with_sigs,
193    );
194    Ok(committer)
195}
196
197fn genesis_block_id() -> HashValue { HashValue::zero() }