1#![forbid(unsafe_code)]
9
10use crate::{vm::VMExecutor, Executor};
11use anyhow::{ensure, 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 access_path::AccessPath,
19 account_address::AccountAddress,
20 account_config::diem_root_address,
21 block_info::{
22 BlockInfo, PivotBlockDecision, GENESIS_EPOCH, GENESIS_ROUND,
23 GENESIS_TIMESTAMP_USECS,
24 },
25 diem_timestamp::DiemTimestampResource,
26 ledger_info::{LedgerInfo, LedgerInfoWithSignatures},
27 on_chain_config::{config_address, ConfigurationResource},
28 term_state::NodeID,
29 transaction::Transaction,
30 waypoint::Waypoint,
31};
32use executor_types::BlockExecutor;
33use move_core_types::move_resource::MoveResource;
34use pow_types::FakePowHandler;
35use std::{collections::btree_map::BTreeMap, sync::Arc};
36use storage_interface::{
37 state_view::VerifiedStateView, DbReaderWriter, TreeState,
38};
39
40pub fn generate_waypoint<V: VMExecutor>(
41 db: &DbReaderWriter, genesis_txn: &Transaction,
42) -> Result<Waypoint> {
43 let tree_state = db.reader.get_latest_tree_state()?;
44
45 let committer = calculate_genesis::<V>(
48 db,
49 tree_state,
50 genesis_txn,
51 None,
52 Vec::new(),
53 Vec::new(),
54 Vec::new(),
55 )?;
56 Ok(committer.waypoint)
57}
58
59pub fn maybe_bootstrap<V: VMExecutor>(
63 db: &DbReaderWriter, genesis_txn: &Transaction, waypoint: Waypoint,
64 genesis_pivot_decision: Option<PivotBlockDecision>, initial_seed: Vec<u8>,
65 initial_nodes: Vec<(NodeID, u64)>,
66 initial_committee: Vec<(AccountAddress, u64)>,
67) -> Result<bool> {
68 let tree_state = db.reader.get_latest_tree_state()?;
69 if tree_state.num_transactions != waypoint.version() {
72 diem_info!(waypoint = %waypoint, "Skip genesis txn.");
73 return Ok(false);
74 }
75 diem_debug!(
76 "genesis_txn={:?}, initial_nodes={:?} ",
77 genesis_txn,
78 initial_nodes,
79 );
80
81 let committer = calculate_genesis::<V>(
82 db,
83 tree_state,
84 genesis_txn,
85 genesis_pivot_decision,
86 initial_seed,
87 initial_nodes,
88 initial_committee,
89 )?;
90 ensure!(
91 waypoint == committer.waypoint(),
92 "Waypoint verification failed. Expected {:?}, got {:?}.",
93 waypoint,
94 committer.waypoint(),
95 );
96 committer.commit()?;
97 Ok(true)
98}
99
100pub struct GenesisCommitter<V: VMExecutor> {
101 executor: Executor<V>,
102 ledger_info_with_sigs: LedgerInfoWithSignatures,
103 waypoint: Waypoint,
104}
105
106impl<V: VMExecutor> GenesisCommitter<V> {
107 pub fn new(
108 executor: Executor<V>, ledger_info_with_sigs: LedgerInfoWithSignatures,
109 ) -> Result<Self> {
110 let waypoint =
111 Waypoint::new_epoch_boundary(ledger_info_with_sigs.ledger_info())?;
112
113 Ok(Self {
114 executor,
115 ledger_info_with_sigs,
116 waypoint,
117 })
118 }
119
120 pub fn waypoint(&self) -> Waypoint { self.waypoint }
121
122 pub fn commit(self) -> Result<()> {
123 self.executor.commit_blocks(
124 vec![genesis_block_id()],
125 self.ledger_info_with_sigs,
126 )?;
127 diem_info!("Genesis commited.");
128 Ok(())
131 }
132}
133
134pub fn calculate_genesis<V: VMExecutor>(
135 db: &DbReaderWriter, tree_state: TreeState, genesis_txn: &Transaction,
136 genesis_pivot_decision: Option<PivotBlockDecision>, initial_seed: Vec<u8>,
137 initial_nodes: Vec<(NodeID, u64)>,
138 initial_committee: Vec<(AccountAddress, u64)>,
139) -> Result<GenesisCommitter<V>> {
140 let genesis_version = tree_state.num_transactions;
144 let db_with_cache = Arc::new(CachedPosLedgerDB::new_on_unbootstrapped_db(
145 db.clone(),
146 tree_state,
147 initial_seed,
148 initial_nodes,
149 initial_committee,
150 genesis_pivot_decision.clone(),
151 ));
152 let executor = Executor::<V>::new(
153 db_with_cache,
154 Arc::new(FakePowHandler {}),
156 Arc::new(FakeLedgerBlockDB {}),
157 );
158
159 let block_id = HashValue::zero();
160 let epoch = if genesis_version == 0 {
161 GENESIS_EPOCH
162 } else {
163 let executor_trees =
164 executor.get_executed_trees(*PRE_GENESIS_BLOCK_ID)?;
165 let state_view = executor.get_executed_state_view(
166 StateViewId::Miscellaneous,
167 &executor_trees,
168 );
169 get_state_epoch(&state_view)?
170 };
171
172 let result = executor.execute_block(
175 (block_id, vec![genesis_txn.clone()]),
176 *PRE_GENESIS_BLOCK_ID,
177 false,
179 )?;
180
181 let root_hash = result.root_hash();
182 let next_epoch_state = result.epoch_state().as_ref().ok_or_else(|| {
183 format_err!("Genesis transaction must emit a epoch change.")
184 })?;
185 let executed_trees = executor.get_executed_trees(block_id)?;
186 let state_view = executor
187 .get_executed_state_view(StateViewId::Miscellaneous, &executed_trees);
188 diem_debug!(
189 "after genesis: epoch_state={:?}, pos_state={:?}",
190 next_epoch_state,
191 state_view.pos_state().epoch_state()
192 );
193 let timestamp_usecs = if genesis_version == 0 {
194 GENESIS_TIMESTAMP_USECS
197 } else {
198 let next_epoch = epoch
199 .checked_add(1)
200 .ok_or_else(|| format_err!("integer overflow occurred"))?;
201
202 ensure!(
203 next_epoch == get_state_epoch(&state_view)?,
204 "Genesis txn didn't bump epoch."
205 );
206 get_state_timestamp(&state_view)?
207 };
208
209 let ledger_info_with_sigs = LedgerInfoWithSignatures::new(
210 LedgerInfo::new(
211 BlockInfo::new(
212 epoch,
213 GENESIS_ROUND,
214 block_id,
215 root_hash,
216 genesis_version,
217 timestamp_usecs,
218 Some(next_epoch_state.clone()),
219 genesis_pivot_decision,
220 ),
221 HashValue::zero(), ),
223 BTreeMap::default(), );
225
226 let committer = GenesisCommitter::new(executor, ledger_info_with_sigs)?;
227 diem_info!(
228 "Genesis calculated: ledger_info_with_sigs {:?}, waypoint {:?}",
229 committer.ledger_info_with_sigs,
230 committer.waypoint,
231 );
232 Ok(committer)
233}
234
235fn get_state_timestamp(state_view: &VerifiedStateView) -> Result<u64> {
236 let rsrc_bytes = &state_view
237 .get(&AccessPath::new(
238 diem_root_address(),
239 DiemTimestampResource::resource_path(),
240 ))?
241 .ok_or_else(|| format_err!("DiemTimestampResource missing."))?;
242 let rsrc = bcs::from_bytes::<DiemTimestampResource>(&rsrc_bytes)?;
243 Ok(rsrc.diem_timestamp.microseconds)
244}
245
246fn get_state_epoch(state_view: &VerifiedStateView) -> Result<u64> {
247 let rsrc_bytes = &state_view
248 .get(&AccessPath::new(
249 config_address(),
250 ConfigurationResource::resource_path(),
251 ))?
252 .ok_or_else(|| format_err!("ConfigurationResource missing."))?;
253 let rsrc = bcs::from_bytes::<ConfigurationResource>(&rsrc_bytes)?;
254 Ok(rsrc.epoch())
255}
256
257fn genesis_block_id() -> HashValue { HashValue::zero() }