1#![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
36fn 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
63pub 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 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 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(), ),
186 BTreeMap::default(), );
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() }