cfxcore/pos/consensus/
state_computer.rs1use super::state_replication::StateComputer;
9use crate::pos::mempool::{CommitNotification, CommittedTransaction};
10use anyhow::Result;
11use consensus_types::block::Block;
12use diem_crypto::HashValue;
13use diem_logger::prelude::*;
14use diem_types::{
15 ledger_info::LedgerInfoWithSignatures, transaction::Transaction,
16};
17use executor_types::{
18 BlockExecutor, Error as ExecutionError, StateComputeResult,
19};
20use fail::fail_point;
21use futures::channel::{mpsc, oneshot};
22use parking_lot::Mutex;
23use std::boxed::Box;
24
25pub struct ExecutionProxy {
28 executor: Mutex<Box<dyn BlockExecutor>>,
29 mempool_commit_sender: mpsc::Sender<CommitNotification>,
30 mempool_commit_timeout_ms: u64,
31}
32
33impl ExecutionProxy {
34 pub fn new(
35 executor: Box<dyn BlockExecutor>,
36 mempool_commit_sender: mpsc::Sender<CommitNotification>,
37 mempool_commit_timeout_ms: u64,
38 ) -> Self {
39 Self {
40 executor: Mutex::new(executor),
41 mempool_commit_sender,
42 mempool_commit_timeout_ms,
43 }
44 }
45
46 async fn notify_mempool(&self, committed_txns: Vec<Transaction>) {
48 let user_txns: Vec<CommittedTransaction> = committed_txns
49 .iter()
50 .filter_map(|txn| match txn {
51 Transaction::UserTransaction(signed_txn) => {
52 Some(CommittedTransaction {
53 sender: signed_txn.sender(),
54 hash: signed_txn.hash(),
55 })
56 }
57 _ => None,
58 })
59 .collect();
60
61 if user_txns.is_empty() {
62 return;
63 }
64
65 let (callback, cb_receiver) = oneshot::channel();
66 let notification = CommitNotification {
67 transactions: user_txns,
68 callback,
69 };
70
71 if let Err(e) =
72 self.mempool_commit_sender.clone().try_send(notification)
73 {
74 diem_error!(
75 error = ?e,
76 "Failed to send commit notification to mempool"
77 );
78 return;
79 }
80
81 match tokio::time::timeout(
82 std::time::Duration::from_millis(self.mempool_commit_timeout_ms),
83 cb_receiver,
84 )
85 .await
86 {
87 Ok(Ok(Ok(response))) => {
88 if !response.success {
89 diem_error!(
90 "Mempool commit failed: {:?}",
91 response.error_message
92 );
93 }
94 }
95 Ok(Ok(Err(e))) => {
96 diem_error!(
97 error = ?e,
98 "Mempool commit returned error"
99 );
100 }
101 Ok(Err(_)) => {
102 diem_error!("Mempool commit callback dropped");
103 }
104 Err(_) => {
105 diem_error!(
106 "Mempool commit notification timed out after {} ms",
107 self.mempool_commit_timeout_ms
108 );
109 }
110 }
111 }
112}
113
114#[async_trait::async_trait]
115impl StateComputer for ExecutionProxy {
116 fn compute(
117 &self,
118 block: &Block,
120 parent_block_id: HashValue,
122 catch_up_mode: bool,
123 ) -> Result<StateComputeResult, ExecutionError> {
124 fail_point!("consensus::compute", |_| {
125 Err(ExecutionError::InternalError {
126 error: "Injected error in compute".into(),
127 })
128 });
129 diem_debug!(
130 block_id = block.id(),
131 parent_id = block.parent_id(),
132 "Executing block",
133 );
134
135 self.executor.lock().execute_block(
136 id_and_transactions_from_block(block),
137 parent_block_id,
138 catch_up_mode,
139 )
140 }
141
142 async fn commit(
143 &self, block_ids: Vec<HashValue>,
144 finality_proof: LedgerInfoWithSignatures,
145 ) -> Result<(), ExecutionError> {
146 let committed_txns = self
147 .executor
148 .lock()
149 .commit_blocks(block_ids, finality_proof)?;
150 self.notify_mempool(committed_txns).await;
151 Ok(())
152 }
153}
154
155fn id_and_transactions_from_block(
156 block: &Block,
157) -> (HashValue, Vec<Transaction>) {
158 let id = block.id();
159 let mut transactions = vec![Transaction::BlockMetadata(block.into())];
160 transactions.extend(
161 block
162 .payload()
163 .unwrap_or(&vec![])
164 .iter()
165 .map(|txn| Transaction::UserTransaction(txn.clone())),
166 );
167 (id, transactions)
168}