1use std::collections::BTreeMap;
2
3use consensus_types::{block::Block, vote::Vote};
4use diem_logger::{error as diem_error, prelude::*};
5use diem_state_view::StateView;
6use diem_types::{
7 account_address::from_consensus_public_key,
8 block_info::PivotBlockDecision,
9 contract_event::ContractEvent,
10 epoch_state::EpochState,
11 on_chain_config::{self, new_epoch_event_key, OnChainConfig, ValidatorSet},
12 term_state::pos_state_config::{PosStateConfigTrait, POS_STATE_CONFIG},
13 transaction::{
14 authenticator::TransactionAuthenticator, ConflictSignature,
15 DisputePayload, ElectionPayload, RegisterPayload, RetirePayload,
16 SignatureCheckedTransaction, SignedTransaction, Transaction,
17 TransactionOutput, TransactionPayload, TransactionStatus,
18 UpdateVotingPowerPayload, WriteSetPayload,
19 },
20 validator_verifier::{ValidatorConsensusInfo, ValidatorVerifier},
21 vm_status::{KeptVMStatus, StatusCode, VMStatus},
22 write_set::{WriteOp, WriteSetMut},
23};
24
25pub trait VMExecutor: Send {
27 fn execute_block(
35 transactions: Vec<Transaction>, state_view: &dyn StateView,
36 catch_up_mode: bool,
37 ) -> Result<Vec<TransactionOutput>, VMStatus>;
38}
39
40pub struct PosVM;
42
43impl VMExecutor for PosVM {
44 fn execute_block(
45 transactions: Vec<Transaction>, state_view: &dyn StateView,
46 catch_up_mode: bool,
47 ) -> Result<Vec<TransactionOutput>, VMStatus> {
48 let mut vm_outputs = Vec::new();
49 for transaction in transactions {
50 let output = match transaction {
51 Transaction::BlockMetadata(_) => {
52 Self::process_block_metadata(state_view)?
53 }
54 Transaction::UserTransaction(trans) => {
55 let tx = Self::check_signature_for_user_tx(trans)?;
56 let spec = Spec { catch_up_mode };
57 Self::process_user_transaction(state_view, &tx, &spec)?
58 }
59 Transaction::GenesisTransaction(change_set) => {
60 Self::process_genesis_transction(&change_set)?
61 }
62 };
63 vm_outputs.push(output);
64 }
65
66 Ok(vm_outputs)
67 }
68}
69
70impl PosVM {
71 fn process_block_metadata(
72 state_view: &dyn StateView,
73 ) -> Result<TransactionOutput, VMStatus> {
74 let mut events = state_view.pos_state().get_unlock_events();
75 diem_debug!("get_unlock_events: {}", events.len());
76
77 let next_view = state_view.pos_state().current_view() + 1;
78 let (term, view_in_term) = POS_STATE_CONFIG.get_term_view(next_view);
79
80 if view_in_term == 0 {
82 let (validator_verifier, vrf_seed) =
83 state_view.pos_state().get_committee_at(term).map_err(|e| {
84 diem_warn!("get_new_committee error: {:?}", e);
85 VMStatus::Error(StatusCode::CFX_INVALID_TX)
86 })?;
87 let epoch = term + 1;
88 let validator_bytes = bcs::to_bytes(&EpochState::new(
89 epoch,
90 validator_verifier,
91 vrf_seed,
92 ))
93 .unwrap();
94 let contract_event =
95 ContractEvent::new(new_epoch_event_key(), validator_bytes);
96 events.push(contract_event);
97 }
98 Ok(Self::gen_output(events, false))
99 }
100
101 fn check_signature_for_user_tx(
102 trans: SignedTransaction,
103 ) -> Result<SignatureCheckedTransaction, VMStatus> {
104 trans.check_signature().map_err(|e| {
106 diem_trace!("invalid transactions signature: e={:?}", e);
107 VMStatus::Error(StatusCode::INVALID_SIGNATURE)
108 })
109 }
110
111 fn process_user_transaction(
112 state_view: &dyn StateView, tx: &SignatureCheckedTransaction,
113 spec: &Spec,
114 ) -> Result<TransactionOutput, VMStatus> {
115 let events = match tx.payload() {
116 TransactionPayload::WriteSet(WriteSetPayload::Direct(
117 change_set,
118 )) => change_set.events().to_vec(),
119 TransactionPayload::Election(election_payload) => {
120 election_payload.execute(state_view, tx, spec)?
121 }
122 TransactionPayload::Retire(retire_payload) => {
123 retire_payload.execute(state_view, tx, spec)?
124 }
125 TransactionPayload::PivotDecision(pivot_decision) => {
126 pivot_decision.execute(state_view, tx, spec)?
127 }
128 TransactionPayload::Register(register) => {
129 register.execute(state_view, tx, spec)?
130 }
131 TransactionPayload::UpdateVotingPower(update) => {
132 update.execute(state_view, tx, spec)?
133 }
134 TransactionPayload::Dispute(dispute) => {
135 dispute.execute(state_view, tx, spec)?
136 }
137 _ => return Err(VMStatus::Error(StatusCode::CFX_UNEXPECTED_TX)),
138 };
139
140 Ok(Self::gen_output(events, false))
141 }
142
143 fn process_genesis_transction(
144 change_set: &WriteSetPayload,
145 ) -> Result<TransactionOutput, VMStatus> {
146 let events = match change_set {
147 WriteSetPayload::Direct(change_set) => change_set.events().to_vec(),
148 _ => return Err(VMStatus::Error(StatusCode::CFX_UNEXPECTED_TX)),
149 };
150
151 Ok(Self::gen_output(events, true))
152 }
153
154 fn gen_output(
155 events: Vec<ContractEvent>, record_events_on_state: bool,
156 ) -> TransactionOutput {
157 let new_epoch_event_key = on_chain_config::new_epoch_event_key();
158 let status = TransactionStatus::Keep(KeptVMStatus::Executed);
159 let mut write_set = WriteSetMut::default();
160
161 if record_events_on_state {
163 for event in &events {
164 if *event.key() == new_epoch_event_key {
165 write_set.push((
166 ValidatorSet::CONFIG_ID.access_path(),
167 WriteOp::Value(event.event_data().to_vec()),
168 ));
169 }
170 }
171 }
172
173 TransactionOutput::new(write_set.freeze().unwrap(), events, 0, status)
174 }
175}
176
177pub struct Spec {
178 pub catch_up_mode: bool,
179}
180
181pub trait ExecutableBuiltinTx {
182 fn execute(
183 &self, state_view: &dyn StateView, tx: &SignatureCheckedTransaction,
184 spec: &Spec,
185 ) -> Result<Vec<ContractEvent>, VMStatus>;
186}
187
188impl ExecutableBuiltinTx for ElectionPayload {
189 fn execute(
190 &self, state_view: &dyn StateView, _tx: &SignatureCheckedTransaction,
191 spec: &Spec,
192 ) -> Result<Vec<ContractEvent>, VMStatus> {
193 if !spec.catch_up_mode {
194 state_view
195 .pos_state()
196 .validate_election(self)
197 .map_err(|e| {
198 diem_error!("election tx error: {:?}", e);
199 VMStatus::Error(StatusCode::CFX_INVALID_TX)
200 })?;
201 }
202 Ok(vec![self.to_event()])
203 }
204}
205
206impl ExecutableBuiltinTx for PivotBlockDecision {
207 fn execute(
208 &self, state_view: &dyn StateView, tx: &SignatureCheckedTransaction,
209 spec: &Spec,
210 ) -> Result<Vec<ContractEvent>, VMStatus> {
211 if !spec.catch_up_mode {
212 let authenticator = tx.authenticator();
213 let signature = match authenticator {
214 TransactionAuthenticator::MultiBLS { signature } => {
215 Ok(signature)
216 }
217 _ => Err(VMStatus::Error(StatusCode::CFX_INVALID_TX)),
218 }?;
219 state_view
220 .pos_state()
221 .validate_pivot_decision(self, signature)
222 .map_err(|e| {
223 diem_error!("pivot decision tx error: {:?}", e);
224 VMStatus::Error(StatusCode::CFX_INVALID_TX)
225 })?;
226 }
227 Ok(vec![self.to_event()])
228 }
229}
230
231impl ExecutableBuiltinTx for DisputePayload {
232 fn execute(
233 &self, state_view: &dyn StateView, _tx: &SignatureCheckedTransaction,
234 _spec: &Spec,
235 ) -> Result<Vec<ContractEvent>, VMStatus> {
236 state_view.pos_state().validate_dispute(self).map_err(|e| {
237 diem_error!("dispute tx error: {:?}", e);
238 VMStatus::Error(StatusCode::CFX_INVALID_TX)
239 })?;
240 if !verify_dispute(self) {
241 return Err(VMStatus::Error(StatusCode::CFX_INVALID_TX));
242 }
243 Ok(vec![self.to_event()])
244 }
245}
246
247macro_rules! impl_builtin_tx_by_gen_events {
248 ( $($name:ident),* ) => {
249 $(impl ExecutableBuiltinTx for $name {
250 fn execute(&self, _state_view: &dyn StateView,_tx: &SignatureCheckedTransaction, _spec: &Spec) -> Result<Vec<ContractEvent>, VMStatus> {
251 Ok(vec![self.to_event()])
252 }
253 })*
254 }
255}
256
257impl_builtin_tx_by_gen_events!(
259 RegisterPayload,
260 RetirePayload,
261 UpdateVotingPowerPayload
262);
263
264pub fn verify_dispute(dispute: &DisputePayload) -> bool {
268 let computed_address =
269 from_consensus_public_key(&dispute.bls_pub_key, &dispute.vrf_pub_key);
270 if dispute.address != computed_address {
271 diem_trace!("Incorrect address and public keys");
272 return false;
273 }
274 match &dispute.conflicting_votes {
275 ConflictSignature::Proposal((proposal_byte1, proposal_byte2)) => {
276 let proposal1: Block =
277 match bcs::from_bytes(proposal_byte1.as_slice()) {
278 Ok(proposal) => proposal,
279 Err(e) => {
280 diem_trace!("1st proposal encoding error: {:?}", e);
281 return false;
282 }
283 };
284 let proposal2: Block =
285 match bcs::from_bytes(proposal_byte2.as_slice()) {
286 Ok(proposal) => proposal,
287 Err(e) => {
288 diem_trace!("2nd proposal encoding error: {:?}", e);
289 return false;
290 }
291 };
292 if (proposal1.block_data().epoch()
293 != proposal2.block_data().epoch())
294 || (proposal1.block_data().round()
295 != proposal2.block_data().round())
296 {
297 diem_trace!("Two proposals are from different rounds");
298 return false;
299 }
300 let mut temp_map = BTreeMap::new();
301 temp_map.insert(
302 dispute.address,
303 ValidatorConsensusInfo::new(
304 dispute.bls_pub_key.clone(),
305 Some(dispute.vrf_pub_key.clone()),
306 1,
307 ),
308 );
309 let temp_verifier = ValidatorVerifier::new(temp_map);
310 if proposal1.validate_signature(&temp_verifier).is_err()
311 || proposal2.validate_signature(&temp_verifier).is_err()
312 {
313 return false;
314 }
315 }
316 ConflictSignature::Vote((vote_byte1, vote_byte2)) => {
317 let vote1: Vote = match bcs::from_bytes(vote_byte1.as_slice()) {
318 Ok(vote) => vote,
319 Err(e) => {
320 diem_trace!("1st vote encoding error: {:?}", e);
321 return false;
322 }
323 };
324 let vote2: Vote = match bcs::from_bytes(vote_byte2.as_slice()) {
325 Ok(vote) => vote,
326 Err(e) => {
327 diem_trace!("2nd vote encoding error: {:?}", e);
328 return false;
329 }
330 };
331 if (vote1.vote_data().proposed().epoch()
332 != vote2.vote_data().proposed().epoch())
333 || (vote1.vote_data().proposed().round()
334 != vote2.vote_data().proposed().round())
335 {
336 diem_trace!("Two votes are from different rounds");
337 return false;
338 }
339 let mut temp_map = BTreeMap::new();
340 temp_map.insert(
341 dispute.address,
342 ValidatorConsensusInfo::new(
343 dispute.bls_pub_key.clone(),
344 Some(dispute.vrf_pub_key.clone()),
345 1,
346 ),
347 );
348 let temp_verifier = ValidatorVerifier::new(temp_map);
349 if vote1.verify(&temp_verifier).is_err()
350 || vote2.verify(&temp_verifier).is_err()
351 {
352 diem_trace!("dispute vote verification error: vote1_r={:?} vote2_r={:?}", vote1.verify(&temp_verifier), vote2.verify(&temp_verifier));
353 return false;
354 }
355 }
356 }
357 true
358}