1#![forbid(unsafe_code)]
9
10use std::{
11 collections::{BTreeMap, HashSet},
12 sync::Arc,
13};
14
15use anyhow::{anyhow, bail, ensure, format_err, Result};
16use fail::fail_point;
17
18use cached_pos_ledger_db::CachedPosLedgerDB;
19use cfx_types::H256;
20use consensus_types::db::LedgerBlockRW;
21use diem_crypto::{
22 hash::{CryptoHash, EventAccumulatorHasher, PRE_GENESIS_BLOCK_ID},
23 HashValue,
24};
25use diem_logger::prelude::*;
26use diem_state_view::StateViewId;
27use diem_types::{
28 block_info::PivotBlockDecision,
29 committed_block::CommittedBlock,
30 epoch_state::EpochState,
31 ledger_info::LedgerInfoWithSignatures,
32 on_chain_config::{self, ValidatorSet},
33 proof::accumulator::InMemoryAccumulator,
34 reward_distribution_event::{RewardDistributionEventV2, VoteCount},
35 term_state::{
36 ElectionEvent, RegisterEvent, RetireEvent, UpdateVotingPowerEvent,
37 },
38 transaction::{
39 Transaction, TransactionInfo, TransactionOutput, TransactionStatus,
40 TransactionToCommit, Version,
41 },
42};
43use executor_types::{
44 BlockExecutor, Error, ExecutedTrees, ProcessedVMOutput, StateComputeResult,
45 TransactionData,
46};
47use pow_types::PowInterface;
48use storage_interface::state_view::VerifiedStateView;
49
50use crate::{
51 logging::{LogEntry, LogSchema},
52 vm::PosVM,
53};
54use diem_types::term_state::{
55 decode_dispute_event,
56 pos_state_config::{PosStateConfigTrait, POS_STATE_CONFIG},
57};
58
59pub mod db_bootstrapper;
60mod logging;
61pub mod vm;
62
63pub struct Executor {
66 db_with_cache: Arc<CachedPosLedgerDB>,
67 consensus_db: Arc<dyn LedgerBlockRW>,
68 pow_handler: Arc<dyn PowInterface>,
69}
70
71impl Executor {
72 pub fn committed_block_id(&self) -> HashValue {
73 self.db_with_cache.committed_block_id()
74 }
75
76 pub fn new(
78 db_with_cache: Arc<CachedPosLedgerDB>,
79 pow_handler: Arc<dyn PowInterface>,
80 consensus_db: Arc<dyn LedgerBlockRW>,
81 ) -> Self {
82 Self {
83 db_with_cache,
84 consensus_db,
85 pow_handler,
86 }
87 }
88
89 fn process_vm_outputs(
92 &self, transactions: &[Transaction],
93 vm_outputs: Vec<TransactionOutput>, parent_trees: &ExecutedTrees,
94 parent_block_id: &HashValue, catch_up_mode: bool,
95 ) -> Result<ProcessedVMOutput> {
96 let mut txn_data = vec![];
101 let mut txn_info_hashes = vec![];
105
106 let pivot_select_event_key =
107 PivotBlockDecision::pivot_select_event_key();
108 let election_event_key = ElectionEvent::event_key();
109 let retire_event_key = RetireEvent::event_key();
110 let register_event_key = RegisterEvent::event_key();
111 let update_voting_power_event_key = UpdateVotingPowerEvent::event_key();
112
113 let mut pivot_decision = None;
115 let mut new_pos_state = parent_trees.pos_state().clone();
116 let parent_pivot_decision = new_pos_state.pivot_decision().clone();
117 for vm_output in vm_outputs.clone().into_iter() {
118 for event in vm_output.events() {
119 if *event.key() == pivot_select_event_key {
121 if pivot_decision.is_some() {
122 bail!("Multiple pivot decisions in one block!");
123 }
124 pivot_decision = Some(PivotBlockDecision::from_bytes(
125 event.event_data(),
126 )?);
127 } else if *event.key() == election_event_key {
128 let election_event =
129 ElectionEvent::from_bytes(event.event_data())?;
130 new_pos_state.new_node_elected(&election_event)?;
131 } else if let Some(dispute) = decode_dispute_event(event) {
132 let (node_id, offense_epoch) = dispute?;
133 new_pos_state.forfeit_node(&node_id, offense_epoch)?;
134 }
135 }
136 }
137
138 if *parent_block_id != *PRE_GENESIS_BLOCK_ID {
139 if let Some(pivot_decision) = &pivot_decision {
140 diem_debug!(
141 "process_vm_outputs: parent={:?} parent_pivot={:?}",
142 parent_block_id,
143 parent_pivot_decision
144 );
145
146 if !catch_up_mode {
150 if !self.pow_handler.validate_proposal_pivot_decision(
151 parent_pivot_decision.block_hash,
152 pivot_decision.block_hash,
153 ) {
154 bail!("Invalid pivot decision for block");
155 }
156
157 diem_debug!(
160 "check staking events: parent={:?} me={:?}",
161 parent_pivot_decision,
162 pivot_decision
163 );
164 let staking_events = self.pow_handler.get_staking_events(
165 parent_pivot_decision.height,
166 pivot_decision.height,
167 parent_pivot_decision.block_hash,
168 pivot_decision.block_hash,
169 )?;
170 let mut staking_events_iter = staking_events.iter();
171 for vm_output in vm_outputs.clone().into_iter() {
172 for event in vm_output.events() {
173 if *event.key() == register_event_key {
175 let register_event = RegisterEvent::from_bytes(
176 event.event_data(),
177 )?;
178 match register_event.matches_staking_event(staking_events_iter.next().ok_or(anyhow!("More staking transactions packed than actual pow events"))?) {
179 Ok(true) => {}
180 Ok(false) => bail!("Packed staking transactions unmatch PoW events)"),
181 Err(e) => diem_error!("error decoding pow events: err={:?}", e),
182 }
183 new_pos_state
184 .register_node(register_event.node_id)?;
185 } else if *event.key()
186 == update_voting_power_event_key
187 {
188 let update_voting_power_event =
189 UpdateVotingPowerEvent::from_bytes(
190 event.event_data(),
191 )?;
192 match update_voting_power_event.matches_staking_event(staking_events_iter.next().ok_or(anyhow!("More staking transactions packed than actual pow events"))?) {
193 Ok(true) => {}
194 Ok(false) => bail!("Packed staking transactions unmatch PoW events)"),
195 Err(e) => diem_error!("error decoding pow events: err={:?}", e),
196 }
197 new_pos_state.update_voting_power(
198 &update_voting_power_event.node_address,
199 update_voting_power_event.voting_power,
200 )?;
201 } else if *event.key() == retire_event_key {
202 let retire_event = RetireEvent::from_bytes(
203 event.event_data(),
204 )?;
205 match retire_event.matches_staking_event(staking_events_iter.next().ok_or(anyhow!("More staking transactions packed than actual pow events"))?) {
206 Ok(true) => {}
207 Ok(false) => bail!("Packed staking transactions unmatch PoW events)"),
208 Err(e) => diem_error!("error decoding pow events: err={:?}", e),
209 }
210 new_pos_state.retire_node(
211 &retire_event.node_id,
212 retire_event.votes,
213 )?;
214 }
215 }
216 }
217 ensure!(
218 staking_events_iter.next().is_none(),
219 "Not all PoW staking events are packed"
220 );
221 } else {
222 for vm_output in vm_outputs.clone().into_iter() {
223 for event in vm_output.events() {
224 if *event.key() == register_event_key {
226 let register_event = RegisterEvent::from_bytes(
227 event.event_data(),
228 )?;
229 new_pos_state
230 .register_node(register_event.node_id)?;
231 } else if *event.key()
232 == update_voting_power_event_key
233 {
234 let update_voting_power_event =
235 UpdateVotingPowerEvent::from_bytes(
236 event.event_data(),
237 )?;
238 new_pos_state.update_voting_power(
239 &update_voting_power_event.node_address,
240 update_voting_power_event.voting_power,
241 )?;
242 } else if *event.key() == retire_event_key {
243 let retire_event = RetireEvent::from_bytes(
244 event.event_data(),
245 )?;
246 new_pos_state.retire_node(
247 &retire_event.node_id,
248 retire_event.votes,
249 )?;
250 }
251 }
252 }
253 }
254 } else {
255 if vm_outputs.iter().any(|output| {
258 output.events().iter().any(|event| {
259 *event.key() == retire_event_key
260 || *event.key() == update_voting_power_event_key
261 })
262 }) {
263 bail!("Should not pack staking related transactions");
264 }
265 pivot_decision = Some(parent_pivot_decision);
266 }
267 }
268 if let Some(pivot_decision) = &pivot_decision {
270 new_pos_state.set_pivot_decision(pivot_decision.clone());
271 }
272 let mut next_epoch_state = new_pos_state.next_view()?;
273
274 let is_genesis =
275 next_epoch_state.as_ref().map_or(false, |es| es.epoch == 1);
276
277 for (vm_output, txn) in
278 itertools::zip_eq(vm_outputs.into_iter(), transactions.iter())
279 {
280 let event_tree = {
281 let event_hashes: Vec<_> =
282 vm_output.events().iter().map(CryptoHash::hash).collect();
283 InMemoryAccumulator::<EventAccumulatorHasher>::from_leaves(
284 &event_hashes,
285 )
286 };
287
288 let mut txn_info_hash = None;
289 match vm_output.status() {
290 TransactionStatus::Keep(status) => {
291 let txn_info = TransactionInfo::new(
299 txn.hash(),
300 Default::default(),
301 event_tree.root_hash(),
302 vm_output.gas_used(),
303 status.clone(),
304 );
305
306 let real_txn_info_hash = txn_info.hash();
307 txn_info_hashes.push(real_txn_info_hash);
308 txn_info_hash = Some(real_txn_info_hash);
309 }
310 TransactionStatus::Discard(status) => {
311 if !vm_output.events().is_empty() {
312 diem_error!(
313 "Discarded transaction has non-empty write set or events. \
314 Transaction: {:?}. Status: {:?}.",
315 txn, status,
316 );
317 }
318 }
319 TransactionStatus::Retry => (),
320 }
321
322 txn_data.push(TransactionData::new(
323 vm_output.events().to_vec(),
324 vm_output.status().clone(),
325 Arc::new(event_tree),
326 vm_output.gas_used(),
327 txn_info_hash,
328 ));
329 }
330
331 if is_genesis {
335 let new_epoch_event_key = on_chain_config::new_epoch_event_key();
336 let validator_set = txn_data
337 .iter()
338 .flat_map(|td| td.events())
339 .find(|event| *event.key() == new_epoch_event_key)
340 .ok_or_else(|| format_err!("Genesis epoch event not found"))
341 .and_then(|event| {
342 bcs::from_bytes::<ValidatorSet>(event.event_data()).map_err(
343 |e| {
344 format_err!(
345 "Failed to deserialize ValidatorSet: {}",
346 e
347 )
348 },
349 )
350 })?;
351 next_epoch_state = Some(EpochState::new(
352 1,
353 (&validator_set).into(),
354 pivot_decision
355 .as_ref()
356 .map(|p| p.block_hash.as_bytes().to_vec())
357 .unwrap_or(vec![]),
358 ))
359 }
360
361 let current_transaction_accumulator =
362 parent_trees.txn_accumulator().append(&txn_info_hashes);
363
364 Ok(ProcessedVMOutput::new(
365 txn_data,
366 ExecutedTrees::new_copy(
367 Arc::new(current_transaction_accumulator),
368 new_pos_state,
369 ),
370 next_epoch_state,
371 pivot_decision,
373 ))
374 }
375
376 fn get_executed_trees(
377 &self, block_id: HashValue,
378 ) -> Result<ExecutedTrees, Error> {
379 let executed_trees = if block_id
380 == self.db_with_cache.cache.lock().committed_block_id()
381 {
382 self.db_with_cache.cache.lock().committed_trees().clone()
383 } else {
384 self.db_with_cache
385 .get_block(&block_id)?
386 .lock()
387 .output()
388 .executed_trees()
389 .clone()
390 };
391
392 Ok(executed_trees)
393 }
394
395 fn get_executed_state_view(
396 &self, id: StateViewId, executed_trees: &ExecutedTrees,
397 ) -> VerifiedStateView {
398 VerifiedStateView::new(id, executed_trees.pos_state().clone())
399 }
400}
401
402impl BlockExecutor for Executor {
403 fn committed_block_id(&self) -> Result<HashValue, Error> {
404 Ok(self.committed_block_id())
405 }
406
407 fn execute_block(
408 &self, block: (HashValue, Vec<Transaction>),
409 parent_block_id: HashValue, catch_up_mode: bool,
410 ) -> Result<StateComputeResult, Error> {
411 let (block_id, mut transactions) = block;
412
413 let (output, state_compute_result) = if parent_block_id
417 != self.committed_block_id()
418 && self
419 .db_with_cache
420 .get_block(&parent_block_id)?
421 .lock()
422 .output()
423 .has_reconfiguration()
424 {
425 let parent = self.db_with_cache.get_block(&parent_block_id)?;
426 let parent_block = parent.lock();
427 let parent_output = parent_block.output();
428
429 diem_info!(
430 LogSchema::new(LogEntry::BlockExecutor).block_id(block_id),
431 "reconfig_descendant_block_received"
432 );
433
434 let mut output = ProcessedVMOutput::new(
435 vec![],
436 parent_output.executed_trees().clone(),
437 parent_output.epoch_state().clone(),
438 parent_output.pivot_block().clone(),
441 );
442 output.set_pos_state_skipped();
443
444 let parent_accu = parent_output.executed_trees().txn_accumulator();
445 let state_compute_result = output.compute_result(
446 parent_accu.frozen_subtree_roots().clone(),
447 parent_accu.num_leaves(),
448 );
449
450 transactions = vec![];
452
453 (output, state_compute_result)
454 } else {
455 diem_info!(
456 LogSchema::new(LogEntry::BlockExecutor).block_id(block_id),
457 "execute_block"
458 );
459
460 let parent_block_executed_trees =
461 self.get_executed_trees(parent_block_id)?;
462
463 let state_view = self.get_executed_state_view(
464 StateViewId::BlockExecution { block_id },
465 &parent_block_executed_trees,
466 );
467
468 let vm_outputs = {
473 fail_point!("executor::vm_execute_block", |_| {
476 Err(Error::from(anyhow::anyhow!(
477 "Injected error in vm_execute_block"
478 )))
479 });
480 PosVM::execute_block(
481 transactions.clone(),
482 &state_view,
483 catch_up_mode,
484 )
485 .map_err(anyhow::Error::from)?
486 };
487
488 let status: Vec<_> = vm_outputs
491 .iter()
492 .map(TransactionOutput::status)
493 .cloned()
494 .collect();
495 if !status.is_empty() {
496 diem_trace!("Execution status: {:?}", status);
497 }
498
499 let output = self
500 .process_vm_outputs(
501 &transactions,
502 vm_outputs,
503 &parent_block_executed_trees,
504 &parent_block_id,
505 catch_up_mode,
506 )
507 .map_err(|err| {
508 format_err!("Failed to execute block: {}", err)
509 })?;
510
511 let parent_accu = parent_block_executed_trees.txn_accumulator();
512
513 diem_debug!("parent leaves: {}", parent_accu.num_leaves());
514 let state_compute_result = output.compute_result(
515 parent_accu.frozen_subtree_roots().clone(),
516 parent_accu.num_leaves(),
517 );
518 (output, state_compute_result)
519 };
520
521 self.db_with_cache
523 .add_block(parent_block_id, (block_id, transactions, output))?;
524
525 Ok(state_compute_result)
526 }
527
528 fn commit_blocks(
529 &self, block_ids: Vec<HashValue>,
530 ledger_info_with_sigs: LedgerInfoWithSignatures,
531 ) -> Result<Vec<Transaction>, Error> {
532 let mut pos_state_to_commit = self
533 .get_executed_trees(
534 ledger_info_with_sigs.ledger_info().consensus_block_id(),
535 )?
536 .pos_state()
537 .clone();
538
539 if ledger_info_with_sigs.ledger_info().ends_epoch()
542 && ledger_info_with_sigs.ledger_info().epoch() != 0
543 {
544 let ending_block =
545 ledger_info_with_sigs.ledger_info().consensus_block_id();
546 let mut elected = BTreeMap::new();
547 let mut voted_block_id = ending_block;
548 let verifier = self
551 .db_with_cache
552 .cache
553 .lock()
554 .committed_trees()
555 .pos_state()
556 .epoch_state()
557 .verifier()
558 .clone();
560 for committee_member in verifier.address_to_validator_info().keys()
561 {
562 elected.insert(*committee_member, VoteCount::default());
563 }
564 loop {
565 let block = self
566 .consensus_db
567 .get_ledger_block(&voted_block_id)?
568 .unwrap();
569 diem_trace!("count vote for block {:?}", block);
570 if block.quorum_cert().ledger_info().signatures().len() == 0 {
571 if let Some(author) = block.author() {
574 let leader_status =
575 elected.get_mut(&author).expect("in epoch state");
576 leader_status.leader_count += 1;
577 }
578 break;
579 }
580 if let Some(author) = block.author() {
581 let leader_status =
582 elected.get_mut(&author).expect("in epoch state");
583 leader_status.leader_count += 1;
584 leader_status.included_vote_count += verifier
585 .extra_vote_count(
586 block
587 .quorum_cert()
588 .ledger_info()
589 .signatures()
590 .keys(),
591 )
592 .unwrap();
593 }
594 for voter in
595 block.quorum_cert().ledger_info().signatures().keys()
596 {
597 elected
598 .get_mut(&voter)
599 .expect("in epoch state")
600 .vote_count +=
601 verifier.get_voting_power(voter).unwrap();
602 }
603 voted_block_id = block.parent_id();
604 }
605 let mut force_retired = HashSet::new();
606
607 for (node, vote_count) in elected.iter_mut() {
609 if vote_count.vote_count == 0 {
610 force_retired.insert(node);
611 } else {
612 vote_count.total_votes =
613 verifier.get_voting_power(node).unwrap_or(0);
614 if vote_count.total_votes == 0 {
615 diem_warn!("Node {:?} has voted for epoch {} without voting power.",
616 node,
617 pos_state_to_commit.epoch_state().epoch);
618 }
619 }
620 }
621
622 if !force_retired.is_empty() {
623 let end_epoch = ledger_info_with_sigs.ledger_info().epoch();
625 let start_epoch = end_epoch.saturating_sub(
626 POS_STATE_CONFIG.force_retire_check_epoch_count(
627 pos_state_to_commit.current_view(),
628 ),
629 ) + 1;
630 for end_ledger_info in self
633 .db_with_cache
634 .db
635 .reader
636 .get_epoch_ending_ledger_infos(start_epoch, end_epoch)?
637 .get_all_ledger_infos()
638 {
639 let mut voted_block_id =
640 end_ledger_info.ledger_info().consensus_block_id();
641 loop {
642 let block = self
643 .consensus_db
644 .get_ledger_block(&voted_block_id)?
645 .unwrap();
646 if block.quorum_cert().ledger_info().signatures().len()
647 == 0
648 {
649 break;
650 }
651 for voter in block
652 .quorum_cert()
653 .ledger_info()
654 .signatures()
655 .keys()
656 {
657 force_retired.remove(voter);
660 }
661 voted_block_id = block.parent_id();
662 }
663 }
664 for node in force_retired {
665 pos_state_to_commit.force_retire_node(&node)?;
666 }
667 }
668
669 let reward_event = RewardDistributionEventV2 {
670 candidates: pos_state_to_commit.next_evicted_term(),
671 elected: elected
672 .into_iter()
673 .map(|(k, v)| (H256::from_slice(k.as_ref()), v))
674 .collect(),
675 view: pos_state_to_commit.current_view(),
676 };
677 self.db_with_cache.db.writer.save_reward_event(
678 ledger_info_with_sigs.ledger_info().epoch(),
679 &reward_event,
680 )?;
681 self.db_with_cache
682 .get_block(&ending_block)
683 .expect("latest committed block not pruned")
684 .lock()
685 .replace_pos_state(pos_state_to_commit.clone());
686 }
687
688 diem_info!(
689 LogSchema::new(LogEntry::BlockExecutor).block_id(
690 ledger_info_with_sigs.ledger_info().consensus_block_id()
691 ),
692 "commit_block"
693 );
694
695 let version = ledger_info_with_sigs.ledger_info().version();
696
697 let num_txns_in_li = version
698 .checked_add(1)
699 .ok_or_else(|| format_err!("version + 1 overflows"))?;
700 let num_persistent_txns = self
701 .db_with_cache
702 .cache
703 .lock()
704 .synced_trees()
705 .txn_accumulator()
706 .num_leaves();
707
708 if num_txns_in_li < num_persistent_txns {
709 return Err(Error::InternalError {
710 error: format!(
711 "Try to commit stale transactions with the last version as {}",
712 version
713 ),
714 });
715 }
716
717 let mut txns_to_keep = vec![];
722 let arc_blocks = block_ids
723 .iter()
724 .map(|id| self.db_with_cache.get_block(id))
725 .collect::<Result<Vec<_>, Error>>()?;
726 let blocks = arc_blocks.iter().map(|b| b.lock()).collect::<Vec<_>>();
727 let mut committed_blocks = Vec::new();
728 let mut signatures_vec = Vec::new();
729 if ledger_info_with_sigs.ledger_info().epoch() != 0 {
730 for (i, b) in blocks.iter().enumerate() {
731 let ledger_block = self
732 .consensus_db
733 .get_ledger_block(&b.id())
734 .unwrap()
735 .unwrap();
736 let view =
737 b.output().executed_trees().pos_state().current_view();
738 committed_blocks.push(CommittedBlock {
739 hash: b.id(),
740 epoch: ledger_block.epoch(),
741 miner: ledger_block.author(),
742 parent_hash: ledger_block.parent_id(),
743 round: ledger_block.round(),
744 pivot_decision: b.output().pivot_block().clone().unwrap(),
745 version: b.output().version().unwrap(),
746 timestamp: ledger_block.timestamp_usecs(),
747 view,
748 is_skipped: b
749 .output()
750 .executed_trees()
751 .pos_state()
752 .skipped(),
753 });
754 if i != 0 {
756 signatures_vec.push((
757 ledger_block.quorum_cert().certified_block().id(),
758 ledger_block.quorum_cert().ledger_info().clone(),
759 ));
760 }
761 }
762 let last_block = blocks.last().expect("not empty").id();
763 if let Some(qc) = self.consensus_db.get_qc_for_block(&last_block)? {
764 signatures_vec.push((last_block, qc.ledger_info().clone()));
765 } else {
766 assert!(ledger_info_with_sigs.ledger_info().ends_epoch());
771 }
772 } else {
773 committed_blocks.push(CommittedBlock {
774 hash: ledger_info_with_sigs.ledger_info().consensus_block_id(),
775 epoch: 0,
776 round: 0,
777 miner: None,
778 parent_hash: HashValue::default(),
779 pivot_decision: ledger_info_with_sigs
780 .ledger_info()
781 .pivot_decision()
782 .unwrap()
783 .clone(),
784 version: ledger_info_with_sigs.ledger_info().version(),
785 timestamp: ledger_info_with_sigs
786 .ledger_info()
787 .timestamp_usecs(),
788 view: 1,
789 is_skipped: false,
790 });
791 }
792 for (txn, txn_data) in blocks.iter().flat_map(|block| {
793 itertools::zip_eq(
794 block.transactions(),
795 block.output().transaction_data(),
796 )
797 }) {
798 if let TransactionStatus::Keep(recorded_status) = txn_data.status()
799 {
800 txns_to_keep.push(TransactionToCommit::new(
801 txn.clone(),
802 txn_data.events().to_vec(),
803 txn_data.gas_used(),
804 recorded_status.clone(),
805 ));
806 }
807 }
808
809 let last_block = blocks
810 .last()
811 .ok_or_else(|| format_err!("CommittableBlockBatch is empty"))?;
812
813 let num_txns_in_speculative_accumulator = last_block
816 .output()
817 .executed_trees()
818 .txn_accumulator()
819 .num_leaves();
820 assert_eq!(
821 num_txns_in_li, num_txns_in_speculative_accumulator as Version,
822 "Number of transactions in ledger info ({}) does not match number of transactions \
823 in accumulator ({}).",
824 num_txns_in_li, num_txns_in_speculative_accumulator,
825 );
826
827 let num_txns_to_keep = txns_to_keep.len() as u64;
828
829 let first_version_to_keep = num_txns_in_li - num_txns_to_keep;
832 assert!(
833 first_version_to_keep <= num_persistent_txns,
834 "first_version {} in the blocks to commit cannot exceed # of committed txns: {}.",
835 first_version_to_keep,
836 num_persistent_txns
837 );
838
839 let num_txns_to_skip = num_persistent_txns - first_version_to_keep;
840 let first_version_to_commit = first_version_to_keep + num_txns_to_skip;
841
842 if num_txns_to_skip != 0 {
843 diem_debug!(
844 LogSchema::new(LogEntry::BlockExecutor)
845 .latest_synced_version(num_persistent_txns - 1)
846 .first_version_to_keep(first_version_to_keep)
847 .num_txns_to_keep(num_txns_to_keep)
848 .first_version_to_commit(first_version_to_commit),
849 "skip_transactions_when_committing"
850 );
851 }
852
853 let txns_to_commit = &txns_to_keep[num_txns_to_skip as usize..];
855
856 let num_txns_to_commit = txns_to_commit.len() as u64;
857 {
858 assert_eq!(
859 first_version_to_commit,
860 num_txns_in_li - num_txns_to_commit
861 );
862 fail_point!("executor::commit_blocks", |_| {
863 Err(Error::from(anyhow::anyhow!(
864 "Injected error in commit_blocks"
865 )))
866 });
867 self.db_with_cache.db.writer.save_transactions(
868 txns_to_commit,
869 first_version_to_commit,
870 Some(&ledger_info_with_sigs),
871 Some(pos_state_to_commit),
872 committed_blocks,
873 signatures_vec,
874 )?;
875 }
876
877 let committed_txns: Vec<Transaction> = txns_to_commit
878 .iter()
879 .map(|txn| txn.transaction().clone())
880 .collect();
881
882 drop(blocks);
884 drop(arc_blocks);
885
886 let old_committed_block = self.db_with_cache.prune(
887 ledger_info_with_sigs.ledger_info(),
888 committed_txns.clone(),
889 )?;
890 self.db_with_cache
891 .db
892 .writer
893 .delete_pos_state_by_block(&old_committed_block)?;
894
895 Ok(committed_txns)
896 }
897}