cfxcore/pos/mempool/shared_mempool/
tasks.rs1use crate::pos::mempool::{
12 core_mempool::{CoreMempool, TimelineState, TxnPointer},
13 logging::{LogEntry, LogEvent, LogSchema},
14 network::MempoolSyncMsg,
15 shared_mempool::types::{
16 notify_subscribers, ScheduledBroadcast, SharedMempool,
17 SharedMempoolNotification, SubmissionStatusBundle,
18 },
19 CommitNotification, CommitResponse, CommittedTransaction, ConsensusRequest,
20 ConsensusResponse, SubmissionStatus,
21};
22use anyhow::Result;
23use cached_pos_ledger_db::CachedPosLedgerDB;
24use diem_logger::prelude::*;
25use diem_types::{
26 mempool_status::{MempoolStatus, MempoolStatusCode},
27 transaction::SignedTransaction,
28};
29use futures::{channel::oneshot, stream::FuturesUnordered};
30use network::node_table::NodeId;
31use parking_lot::Mutex;
32use rayon::prelude::*;
33use std::{
34 cmp,
35 collections::HashSet,
36 sync::Arc,
37 time::{Duration, Instant},
38};
39use tokio::runtime::Handle;
40
41pub(crate) fn execute_broadcast(
47 peer: NodeId, backoff: bool, smp: &mut SharedMempool,
48 scheduled_broadcasts: &mut FuturesUnordered<ScheduledBroadcast>,
49 broadcasting_peers: &mut HashSet<NodeId>, executor: Handle,
50) {
51 diem_trace!("execute_broadcast starts: peer={}", peer);
52 let peer_manager = &smp.peer_manager.clone();
53 peer_manager.execute_broadcast(peer.clone(), backoff, smp);
54 let schedule_backoff = peer_manager.is_backoff_mode(&peer);
55
56 let interval_ms = if schedule_backoff {
57 smp.config.shared_mempool_backoff_interval_ms
58 } else {
59 smp.config.shared_mempool_tick_interval_ms
60 };
61
62 if peer_manager.contains_peer(&peer) {
63 broadcasting_peers.insert(peer);
65 scheduled_broadcasts.push(ScheduledBroadcast::new(
66 Instant::now() + Duration::from_millis(interval_ms),
67 peer,
68 schedule_backoff,
69 executor,
70 ));
71 } else {
72 broadcasting_peers.remove(&peer);
75 }
76 diem_trace!("execute_broadcast end: peer={}", peer);
77}
78
79pub(crate) async fn process_client_transaction_submission(
85 smp: SharedMempool, transaction: SignedTransaction,
86 callback: oneshot::Sender<Result<SubmissionStatus>>,
87) {
88 let statuses = process_incoming_transactions(
89 &smp,
90 vec![transaction],
91 TimelineState::NotReady,
92 )
93 .await;
94 log_txn_process_results(&statuses, None);
95
96 if let Some(status) = statuses.get(0) {
97 if callback.send(Ok(status.1.clone())).is_err() {
98 diem_error!(LogSchema::event_log(
99 LogEntry::JsonRpc,
100 LogEvent::CallbackFail
101 ));
102 }
103 }
104}
105
106pub(crate) async fn process_transaction_broadcast(
108 smp: SharedMempool, transactions: Vec<SignedTransaction>,
109 request_id: Vec<u8>, timeline_state: TimelineState, peer: NodeId,
110) {
111 diem_trace!("process_transaction_broadcast starts: peer={}", peer);
112 let results = process_incoming_transactions(
113 &smp,
114 transactions.clone(),
115 timeline_state,
116 )
117 .await;
118 log_txn_process_results(&results, Some(peer.clone()));
119
120 let ack_response = gen_ack_response(request_id, results, &peer);
121 if let Err(e) = smp
122 .network_sender
123 .send_message_with_peer_id(&peer, &ack_response)
124 {
125 diem_error!(LogSchema::event_log(
126 LogEntry::BroadcastACK,
127 LogEvent::NetworkSendFail
128 )
129 .error(&e.into()));
130 return;
131 }
132 notify_subscribers(SharedMempoolNotification::ACK, &smp.subscribers);
133 diem_trace!("process_transaction_broadcast ends: peer={}", peer);
134}
135
136fn gen_ack_response(
137 request_id: Vec<u8>, results: Vec<SubmissionStatusBundle>, peer: &NodeId,
138) -> MempoolSyncMsg {
139 let retry = results.iter().any(|(_, (status, _))| {
142 matches!(status.code, MempoolStatusCode::TooManyTransactions)
143 });
144
145 diem_trace!(
146 "request[{:?}] from peer[{:?}] retry[{:?}]",
147 request_id,
148 peer,
149 retry
150 );
151
152 MempoolSyncMsg::BroadcastTransactionsResponse {
153 request_id,
154 retry,
155 backoff: false,
156 }
157}
158
159pub(crate) async fn process_incoming_transactions(
162 smp: &SharedMempool, transactions: Vec<SignedTransaction>,
163 timeline_state: TimelineState,
164) -> Vec<SubmissionStatusBundle> {
165 let mut statuses = vec![];
166
167 let transactions: Vec<SignedTransaction> = {
170 let mempool = smp.mempool.lock();
171 transactions
172 .into_iter()
173 .filter(|tx| mempool.transactions.get(&tx.hash()).is_none())
174 .collect()
175 };
176 let pos_state = smp.db_with_cache.db.reader.get_latest_pos_state();
177 let validation_results = transactions
178 .par_iter()
179 .map(|t| smp.validator.read().validate_transaction(&t, &pos_state))
180 .collect::<Vec<_>>();
181
182 {
183 let mut mempool = smp.mempool.lock();
184 for (idx, transaction) in transactions.into_iter().enumerate() {
185 match validation_results[idx] {
186 None => {
187 let mempool_status =
188 mempool.add_txn(transaction.clone(), timeline_state);
189 statuses.push((transaction, (mempool_status, None)));
190 }
191 Some(validation_status) => {
192 statuses.push((
193 transaction,
194 (
195 MempoolStatus::new(MempoolStatusCode::VmError),
196 Some(validation_status),
197 ),
198 ));
199 }
200 }
201 }
202 }
203 notify_subscribers(
204 SharedMempoolNotification::NewTransactions,
205 &smp.subscribers,
206 );
207 statuses
208}
209
210fn log_txn_process_results(
211 results: &[SubmissionStatusBundle], sender: Option<NodeId>,
212) {
213 let sender = match sender {
214 Some(peer) => peer,
215 None => {
216 return;
217 }
218 };
219 for (txn, (_mempool_status, maybe_vm_status)) in results.iter() {
220 if let Some(vm_status) = maybe_vm_status {
221 diem_trace!(
222 SecurityEvent::InvalidTransactionMempool,
223 failed_transaction = txn,
224 vm_status = vm_status,
225 sender = sender,
226 );
227 }
228 }
229}
230
231pub(crate) async fn process_committed_transactions(
236 mempool: Arc<Mutex<CoreMempool>>, req: CommitNotification,
237) {
238 diem_debug!(LogSchema::event_log(
239 LogEntry::StateSyncCommit,
240 LogEvent::Received
241 )
242 .state_sync_msg(&req));
243 commit_txns(&mempool, req.transactions).await;
244 if req.callback.send(Ok(CommitResponse::success())).is_err() {
245 diem_error!(LogSchema::event_log(
246 LogEntry::StateSyncCommit,
247 LogEvent::CallbackFail
248 ));
249 }
250}
251
252pub(crate) async fn process_consensus_request(
253 db: Arc<CachedPosLedgerDB>, mempool: &Mutex<CoreMempool>,
254 req: ConsensusRequest,
255) {
256 diem_debug!(
257 LogSchema::event_log(LogEntry::Consensus, LogEvent::Received)
258 .consensus_msg(&req)
259 );
260
261 let ConsensusRequest {
262 max_block_size,
263 exclude_txns,
264 parent_block_id,
265 validators,
266 callback,
267 } = req;
268 let exclude_transactions: HashSet<TxnPointer> = exclude_txns
269 .iter()
270 .map(|txn| (txn.sender, txn.hash))
271 .collect();
272 let mut txns;
273 {
274 let mut mempool = mempool.lock();
275 let block_size = cmp::max(max_block_size, 1);
276 let pos_state = db
277 .get_pos_state(&parent_block_id)
278 .expect("pos_state should exist");
279 txns = mempool.get_block(
280 block_size,
281 exclude_transactions,
282 &pos_state,
283 validators,
284 );
285 }
286 let pulled_block = txns.drain(..).map(SignedTransaction::into).collect();
287 let resp = ConsensusResponse { txns: pulled_block };
288 if callback.send(Ok(resp)).is_err() {
289 diem_error!(LogSchema::event_log(
290 LogEntry::Consensus,
291 LogEvent::CallbackFail
292 ));
293 }
294}
295
296async fn commit_txns(
297 mempool: &Mutex<CoreMempool>, transactions: Vec<CommittedTransaction>,
298) {
299 let mut pool = mempool.lock();
300
301 for transaction in transactions {
302 pool.remove_transaction(transaction.hash);
303 }
304}