1mod block_proposal_process;
2mod block_transactions_process;
3mod block_transactions_verifier;
4mod block_uncles_verifier;
5mod compact_block_process;
6mod compact_block_verifier;
7mod get_block_proposal_process;
8mod get_block_transactions_process;
9mod get_transactions_process;
10#[cfg(test)]
11pub(crate) mod tests;
12mod transaction_hashes_process;
13mod transactions_process;
14
15use self::block_proposal_process::BlockProposalProcess;
16use self::block_transactions_process::BlockTransactionsProcess;
17pub(crate) use self::compact_block_process::CompactBlockProcess;
18use self::get_block_proposal_process::GetBlockProposalProcess;
19use self::get_block_transactions_process::GetBlockTransactionsProcess;
20use self::get_transactions_process::GetTransactionsProcess;
21use self::transaction_hashes_process::TransactionHashesProcess;
22use self::transactions_process::TransactionsProcess;
23use crate::types::{ActiveChain, SyncShared, post_sync_process};
24use crate::utils::{
25 MetricDirection, async_quick_send_message_to, async_send_message_to, metric_ckb_message_bytes,
26 send_block_proposals,
27};
28use crate::{Status, StatusCode};
29use ckb_chain::VerifyResult;
30use ckb_chain::{ChainController, RemoteBlock};
31use ckb_constant::sync::BAD_MESSAGE_BAN_TIME;
32use ckb_error::is_internal_db_error;
33use ckb_logger::{
34 debug, debug_target, error, error_target, info_target, trace_target, warn_target,
35};
36use ckb_network::{
37 CKBProtocolContext, CKBProtocolHandler, PeerIndex, SupportProtocols, TargetSession,
38 async_trait, bytes::Bytes,
39};
40use ckb_shared::Shared;
41use ckb_shared::block_status::BlockStatus;
42use ckb_systemtime::unix_time_as_millis;
43use ckb_tx_pool::service::TxVerificationResult;
44use ckb_types::BlockNumberAndHash;
45use ckb_types::{
46 core::{self, BlockView},
47 packed::{self, Byte32, ProposalShortId},
48 prelude::*,
49};
50use itertools::Itertools;
51use std::collections::{HashMap, HashSet};
52use std::sync::Arc;
53use std::time::{Duration, Instant};
54
55pub const TX_PROPOSAL_TOKEN: u64 = 0;
56pub const ASK_FOR_TXS_TOKEN: u64 = 1;
57pub const TX_HASHES_TOKEN: u64 = 2;
58
59pub const MAX_RELAY_PEERS: usize = 128;
60pub const MAX_RELAY_TXS_NUM_PER_BATCH: usize = 32767;
61pub const MAX_RELAY_TXS_BYTES_PER_BATCH: usize = 1024 * 1024;
62
63type RateLimiter<T> = governor::RateLimiter<
64 T,
65 governor::state::keyed::HashMapStateStore<T>,
66 governor::clock::DefaultClock,
67>;
68
69#[derive(Debug, Eq, PartialEq)]
70pub enum ReconstructionResult {
71 Block(BlockView),
72 Missing(Vec<usize>, Vec<usize>),
73 Collided,
74 Error(Status),
75}
76
77pub struct Relayer {
79 chain: ChainController,
80 pub(crate) shared: Arc<SyncShared>,
81 rate_limiter: RateLimiter<(PeerIndex, u32)>,
82}
83
84impl Relayer {
85 pub fn new(chain: ChainController, shared: Arc<SyncShared>) -> Self {
89 let quota = governor::Quota::per_second(std::num::NonZeroU32::new(30).unwrap());
92 let rate_limiter = RateLimiter::hashmap(quota);
93
94 Relayer {
95 chain,
96 shared,
97 rate_limiter,
98 }
99 }
100
101 pub fn shared(&self) -> &Arc<SyncShared> {
103 &self.shared
104 }
105
106 async fn try_process(
107 &mut self,
108 nc: Arc<dyn CKBProtocolContext + Sync>,
109 peer: PeerIndex,
110 message: packed::RelayMessageUnionReader<'_>,
111 ) -> Status {
112 let should_check_rate =
114 !matches!(message, packed::RelayMessageUnionReader::CompactBlock(_));
115
116 if should_check_rate
117 && self
118 .rate_limiter
119 .check_key(&(peer, message.item_id()))
120 .is_err()
121 {
122 return StatusCode::TooManyRequests.with_context(message.item_name());
123 }
124
125 match message {
126 packed::RelayMessageUnionReader::CompactBlock(reader) => {
127 if reader.check_data() {
128 CompactBlockProcess::new(reader, self, nc, peer)
129 .execute()
130 .await
131 } else {
132 StatusCode::ProtocolMessageIsMalformed.with_context("CompactBlock is invalid")
133 }
134 }
135 packed::RelayMessageUnionReader::RelayTransactions(reader) => {
136 if reader.check_data() {
137 TransactionsProcess::new(reader, self, nc, peer).execute()
138 } else {
139 StatusCode::ProtocolMessageIsMalformed
140 .with_context("RelayTransactions is invalid")
141 }
142 }
143 packed::RelayMessageUnionReader::RelayTransactionHashes(reader) => {
144 TransactionHashesProcess::new(reader, self, peer).execute()
145 }
146 packed::RelayMessageUnionReader::GetRelayTransactions(reader) => {
147 GetTransactionsProcess::new(reader, self, nc, peer)
148 .execute()
149 .await
150 }
151 packed::RelayMessageUnionReader::GetBlockTransactions(reader) => {
152 GetBlockTransactionsProcess::new(reader, self, nc, peer)
153 .execute()
154 .await
155 }
156 packed::RelayMessageUnionReader::BlockTransactions(reader) => {
157 if reader.check_data() {
158 BlockTransactionsProcess::new(reader, self, nc, peer)
159 .execute()
160 .await
161 } else {
162 StatusCode::ProtocolMessageIsMalformed
163 .with_context("BlockTransactions is invalid")
164 }
165 }
166 packed::RelayMessageUnionReader::GetBlockProposal(reader) => {
167 GetBlockProposalProcess::new(reader, self, nc, peer)
168 .execute()
169 .await
170 }
171 packed::RelayMessageUnionReader::BlockProposal(reader) => {
172 BlockProposalProcess::new(reader, self).execute().await
173 }
174 }
175 }
176
177 async fn process(
178 &mut self,
179 nc: Arc<dyn CKBProtocolContext + Sync>,
180 peer: PeerIndex,
181 message: packed::RelayMessageUnionReader<'_>,
182 ) {
183 let item_name = message.item_name();
184 let item_bytes = message.as_slice().len() as u64;
185 let status = self.try_process(Arc::clone(&nc), peer, message).await;
186
187 metric_ckb_message_bytes(
188 MetricDirection::In,
189 &SupportProtocols::RelayV3.name(),
190 message.item_name(),
191 Some(status.code()),
192 item_bytes,
193 );
194
195 if let Some(ban_time) = status.should_ban() {
196 error_target!(
197 crate::LOG_TARGET_RELAY,
198 "receive {} from {}, ban {:?} for {}",
199 item_name,
200 peer,
201 ban_time,
202 status
203 );
204 nc.ban_peer(peer, ban_time, status.to_string());
205 } else if status.should_warn() {
206 warn_target!(
207 crate::LOG_TARGET_RELAY,
208 "receive {} from {}, {}",
209 item_name,
210 peer,
211 status
212 );
213 } else if !status.is_ok() {
214 debug_target!(
215 crate::LOG_TARGET_RELAY,
216 "receive {} from {}, {}",
217 item_name,
218 peer,
219 status
220 );
221 }
222 }
223
224 pub fn request_proposal_txs(
226 &self,
227 nc: &Arc<dyn CKBProtocolContext + Sync>,
228 peer: PeerIndex,
229 block_hash_and_number: BlockNumberAndHash,
230 proposals: Vec<packed::ProposalShortId>,
231 ) {
232 let tx_pool = self.shared.shared().tx_pool_controller().clone();
233 let shared = Arc::clone(&self.shared);
234 let nc = Arc::clone(nc);
235 self.shared().shared().async_handle().spawn(async move {
236 let fresh_proposals: Vec<ProposalShortId> =
237 match tx_pool.fresh_proposals_filter(proposals).await {
238 Err(err) => {
239 debug_target!(
240 crate::LOG_TARGET_RELAY,
241 "tx_pool fresh_proposals_filter error: {:?}",
242 err,
243 );
244 return;
245 }
246 Ok(fresh_proposals) => fresh_proposals.into_iter().unique().collect(),
247 };
248
249 let to_ask_proposals: Vec<ProposalShortId> = shared
250 .state()
251 .insert_inflight_proposals(fresh_proposals.clone(), block_hash_and_number.number)
252 .into_iter()
253 .zip(fresh_proposals)
254 .filter_map(|(firstly_in, id)| if firstly_in { Some(id) } else { None })
255 .collect();
256 if !to_ask_proposals.is_empty() {
257 let content = packed::GetBlockProposal::new_builder()
258 .block_hash(block_hash_and_number.hash)
259 .proposals(to_ask_proposals.clone())
260 .build();
261 let message = packed::RelayMessage::new_builder().set(content).build();
262 if !async_quick_send_message_to(&nc, peer, &message)
263 .await
264 .is_ok()
265 {
266 shared.state().remove_inflight_proposals(&to_ask_proposals);
267 }
268 }
269 });
270 }
271
272 #[allow(clippy::needless_collect)]
274 pub fn accept_block(
275 &self,
276 nc: Arc<dyn CKBProtocolContext + Sync>,
277 peer_id: PeerIndex,
278 block: core::BlockView,
279 msg_name: &str,
280 ) {
281 if self
282 .shared()
283 .active_chain()
284 .contains_block_status(&block.hash(), BlockStatus::BLOCK_STORED)
285 {
286 return;
287 }
288
289 let block = Arc::new(block);
290
291 let verify_callback = {
292 let nc: Arc<dyn CKBProtocolContext + Sync> = Arc::clone(&nc);
293 let block = Arc::clone(&block);
294 let shared = Arc::clone(self.shared());
295 let msg_name = msg_name.to_owned();
296 Box::new(move |result: VerifyResult| match result {
297 Ok(verified) => {
298 if !verified {
299 debug!(
300 "block {}-{} has verified already, won't build compact block and broadcast it",
301 block.number(),
302 block.hash()
303 );
304 return;
305 }
306
307 build_and_broadcast_compact_block(nc.as_ref(), shared.shared(), peer_id, block);
308 }
309 Err(err) => {
310 error!(
311 "verify block {}-{} failed: {:?}, won't build compact block and broadcast it",
312 block.number(),
313 block.hash(),
314 err
315 );
316
317 let is_internal_db_error = is_internal_db_error(&err);
318 if is_internal_db_error {
319 return;
320 }
321
322 post_sync_process(
324 nc.as_ref(),
325 peer_id,
326 &msg_name,
327 StatusCode::BlockIsInvalid.with_context(format!(
328 "block {} is invalid, reason: {}",
329 block.hash(),
330 err
331 )),
332 );
333 }
334 })
335 };
336
337 let remote_block = RemoteBlock {
338 block,
339 verify_callback,
340 };
341
342 self.shared.accept_remote_block(&self.chain, remote_block);
343 }
344
345 pub async fn reconstruct_block(
355 &self,
356 active_chain: &ActiveChain,
357 compact_block: &packed::CompactBlock,
358 received_transactions: Vec<core::TransactionView>,
359 uncles_index: &[u32],
360 received_uncles: &[core::UncleBlockView],
361 ) -> ReconstructionResult {
362 let block_txs_len = received_transactions.len();
363 let compact_block_hash = compact_block.calc_header_hash();
364 debug_target!(
365 crate::LOG_TARGET_RELAY,
366 "start block reconstruction, block hash: {}, received transactions len: {}",
367 compact_block_hash,
368 block_txs_len,
369 );
370
371 let mut short_ids_set: HashSet<ProposalShortId> =
372 compact_block.short_ids().into_iter().collect();
373
374 let mut txs_map: HashMap<ProposalShortId, core::TransactionView> = received_transactions
375 .into_iter()
376 .filter_map(|tx| {
377 let short_id = tx.proposal_short_id();
378 if short_ids_set.remove(&short_id) {
379 Some((short_id, tx))
380 } else {
381 None
382 }
383 })
384 .collect();
385
386 if !short_ids_set.is_empty() {
387 let tx_pool = self.shared.shared().tx_pool_controller();
388 let fetch_txs = tx_pool.fetch_txs(short_ids_set).await;
389 if let Err(e) = fetch_txs {
390 return ReconstructionResult::Error(StatusCode::TxPool.with_context(e));
391 }
392 txs_map.extend(fetch_txs.unwrap());
393 }
394
395 let txs_len = compact_block.txs_len();
396 let mut block_transactions: Vec<Option<core::TransactionView>> =
397 Vec::with_capacity(txs_len);
398
399 let short_ids_iter = &mut compact_block.short_ids().into_iter();
400 compact_block
402 .prefilled_transactions()
403 .into_iter()
404 .for_each(|pt| {
405 let index: usize = pt.index().into();
406 let gap = index - block_transactions.len();
407 if gap > 0 {
408 short_ids_iter
409 .take(gap)
410 .for_each(|short_id| block_transactions.push(txs_map.remove(&short_id)));
411 }
412 block_transactions.push(Some(pt.transaction().into_view()));
413 });
414
415 short_ids_iter.for_each(|short_id| block_transactions.push(txs_map.remove(&short_id)));
417
418 let missing = block_transactions.iter().any(Option::is_none);
419
420 let mut missing_uncles = Vec::with_capacity(compact_block.uncles().len());
421 let mut uncles = Vec::with_capacity(compact_block.uncles().len());
422
423 let mut position = 0;
424 for (i, uncle_hash) in compact_block.uncles().into_iter().enumerate() {
425 if uncles_index.contains(&(i as u32)) {
426 let Some(uncle) = received_uncles.get(position) else {
427 return ReconstructionResult::Error(
428 StatusCode::BlockUnclesLengthIsUnmatchedWithPendingCompactBlock
429 .with_context(format!("Missing received uncle at position {position}")),
430 );
431 };
432 uncles.push(uncle.clone().data());
433 position += 1;
434 continue;
435 };
436 let status = active_chain.get_block_status(&uncle_hash);
437 match status {
438 BlockStatus::UNKNOWN | BlockStatus::HEADER_VALID => missing_uncles.push(i),
439 BlockStatus::BLOCK_STORED | BlockStatus::BLOCK_VALID => {
440 if let Some(uncle) = active_chain.get_block(&uncle_hash) {
441 uncles.push(uncle.as_uncle().data());
442 } else {
443 debug_target!(
444 crate::LOG_TARGET_RELAY,
445 "reconstruct_block could not find {:#?} uncle block: {:#?}",
446 status,
447 uncle_hash,
448 );
449 missing_uncles.push(i);
450 }
451 }
452 BlockStatus::BLOCK_RECEIVED => {
453 if let Some(uncle) = self
454 .chain
455 .get_orphan_block(self.shared().store(), &uncle_hash)
456 {
457 uncles.push(uncle.as_uncle().data());
458 } else {
459 debug_target!(
460 crate::LOG_TARGET_RELAY,
461 "reconstruct_block could not find {:#?} uncle block: {:#?}",
462 status,
463 uncle_hash,
464 );
465 missing_uncles.push(i);
466 }
467 }
468 BlockStatus::BLOCK_INVALID => {
469 return ReconstructionResult::Error(
470 StatusCode::CompactBlockHasInvalidUncle.with_context(uncle_hash),
471 );
472 }
473 _ => missing_uncles.push(i),
474 }
475 }
476
477 if !missing && missing_uncles.is_empty() {
478 let txs = block_transactions
479 .into_iter()
480 .collect::<Option<Vec<_>>>()
481 .expect("missing checked, should not fail");
482 let block = if let Some(extension) = compact_block.extension() {
483 packed::BlockV1::new_builder()
484 .header(compact_block.header())
485 .uncles(uncles)
486 .transactions(txs.into_iter().map(|tx| tx.data()).collect::<Vec<_>>())
487 .proposals(compact_block.proposals())
488 .extension(extension)
489 .build()
490 .as_v0()
491 } else {
492 packed::Block::new_builder()
493 .header(compact_block.header())
494 .uncles(uncles)
495 .transactions(txs.into_iter().map(|tx| tx.data()).collect::<Vec<_>>())
496 .proposals(compact_block.proposals())
497 .build()
498 }
499 .into_view();
500
501 debug_target!(
502 crate::LOG_TARGET_RELAY,
503 "finish block reconstruction, block hash: {}",
504 compact_block.calc_header_hash(),
505 );
506
507 let compact_block_tx_root = compact_block.header().raw().transactions_root();
508 let reconstruct_block_tx_root = block.transactions_root();
509 if compact_block_tx_root != reconstruct_block_tx_root {
510 if compact_block.short_ids().is_empty()
511 || compact_block.short_ids().len() == block_txs_len
512 {
513 return ReconstructionResult::Error(
514 StatusCode::CompactBlockHasUnmatchedTransactionRootWithReconstructedBlock
515 .with_context(format!(
516 "Compact_block_tx_root({}) != reconstruct_block_tx_root({})",
517 compact_block.header().raw().transactions_root(),
518 block.transactions_root(),
519 )),
520 );
521 } else {
522 if let Some(metrics) = ckb_metrics::handle() {
523 metrics.ckb_relay_transaction_short_id_collide.inc();
524 }
525 return ReconstructionResult::Collided;
526 }
527 }
528
529 ReconstructionResult::Block(block)
530 } else {
531 let missing_indexes: Vec<usize> = block_transactions
532 .iter()
533 .enumerate()
534 .filter_map(|(i, t)| if t.is_none() { Some(i) } else { None })
535 .collect();
536
537 debug_target!(
538 crate::LOG_TARGET_RELAY,
539 "block reconstruction failed, block hash: {}, missing: {}, total: {}",
540 compact_block.calc_header_hash(),
541 missing_indexes.len(),
542 compact_block.short_ids().len(),
543 );
544
545 ReconstructionResult::Missing(missing_indexes, missing_uncles)
546 }
547 }
548
549 async fn prune_tx_proposal_request(&self, nc: &Arc<dyn CKBProtocolContext + Sync>) {
550 let get_block_proposals = self.shared().state().drain_get_block_proposals();
551 let tx_pool = self.shared.shared().tx_pool_controller();
552
553 let fetch_txs = tx_pool
554 .fetch_txs(
555 get_block_proposals
556 .iter()
557 .map(|kv_pair| kv_pair.key().clone())
558 .collect(),
559 )
560 .await;
561 if let Err(err) = fetch_txs {
562 debug_target!(
563 crate::LOG_TARGET_RELAY,
564 "relayer prune_tx_proposal_request internal error: {:?}",
565 err,
566 );
567 return;
568 }
569
570 let txs = fetch_txs.unwrap();
571
572 let mut peer_txs = HashMap::new();
573 for (id, peer_indices) in get_block_proposals.into_iter() {
574 if let Some(tx) = txs.get(&id) {
575 for peer_index in peer_indices {
576 let tx_set = peer_txs.entry(peer_index).or_insert_with(Vec::new);
577 tx_set.push(tx.clone());
578 }
579 }
580 }
581
582 let mut relay_bytes = 0;
583 let mut relay_proposals = Vec::new();
584 for (peer_index, txs) in peer_txs {
585 for tx in txs {
586 let data = tx.data();
587 let tx_size = data.total_size();
588 if relay_bytes + tx_size > MAX_RELAY_TXS_BYTES_PER_BATCH {
589 send_block_proposals(nc, peer_index, std::mem::take(&mut relay_proposals))
590 .await;
591 relay_bytes = tx_size;
592 } else {
593 relay_bytes += tx_size;
594 }
595 relay_proposals.push(data);
596 }
597 if !relay_proposals.is_empty() {
598 send_block_proposals(nc, peer_index, std::mem::take(&mut relay_proposals)).await;
599 relay_bytes = 0;
600 }
601 }
602 }
603
604 pub async fn ask_for_txs(&self, nc: &Arc<dyn CKBProtocolContext + Sync>) {
606 for (peer, mut tx_hashes) in self.shared().state().pop_ask_for_txs() {
607 if !tx_hashes.is_empty() {
608 debug_target!(
609 crate::LOG_TARGET_RELAY,
610 "Send get transaction ({} hashes) to {}",
611 tx_hashes.len(),
612 peer,
613 );
614 tx_hashes.truncate(MAX_RELAY_TXS_NUM_PER_BATCH);
615 let content = packed::GetRelayTransactions::new_builder()
616 .tx_hashes(tx_hashes)
617 .build();
618 let message = packed::RelayMessage::new_builder().set(content).build();
619 let status = async_send_message_to(nc, peer, &message).await;
620 if !status.is_ok() {
621 ckb_logger::error!(
622 "interrupted request for transactions, status: {:?}",
623 status
624 );
625 }
626 }
627 }
628 }
629
630 pub async fn send_bulk_of_tx_hashes(&self, nc: &Arc<dyn CKBProtocolContext + Sync>) {
632 const BUFFER_SIZE: usize = 42;
633
634 let connected_peers = nc.full_relay_connected_peers();
635 if connected_peers.is_empty() {
636 return;
637 }
638
639 let tx_verify_results = self
640 .shared
641 .state()
642 .take_relay_tx_verify_results(MAX_RELAY_TXS_NUM_PER_BATCH);
643 let mut selected: HashMap<PeerIndex, Vec<Byte32>> = HashMap::default();
644 {
645 for tx_verify_result in tx_verify_results {
646 match tx_verify_result {
647 TxVerificationResult::Ok {
648 original_peer,
649 tx_hash,
650 } => {
651 for target in &connected_peers {
652 match original_peer {
653 Some(peer) => {
654 if peer != *target {
656 let hashes = selected
657 .entry(*target)
658 .or_insert_with(|| Vec::with_capacity(BUFFER_SIZE));
659 hashes.push(tx_hash.clone());
660 }
661 }
662 None => {
663 let hashes = selected
665 .entry(*target)
666 .or_insert_with(|| Vec::with_capacity(BUFFER_SIZE));
667 hashes.push(tx_hash.clone());
668 self.shared.state().mark_as_known_tx(tx_hash.clone());
669 }
670 }
671 }
672 }
673 TxVerificationResult::Reject { tx_hash } => {
674 self.shared.state().remove_from_known_txs(&tx_hash);
675 }
676 TxVerificationResult::UnknownParents { peer, parents } => {
677 let tx_hashes: Vec<_> = {
678 let mut tx_filter = self.shared.state().tx_filter();
679 tx_filter.remove_expired();
680 parents
681 .into_iter()
682 .filter(|tx_hash| !tx_filter.contains(tx_hash))
683 .collect()
684 };
685 self.shared.state().add_ask_for_txs(peer, tx_hashes);
686 }
687 }
688 }
689 }
690 for (peer, hashes) in selected {
691 let content = packed::RelayTransactionHashes::new_builder()
692 .tx_hashes(hashes)
693 .build();
694 let message = packed::RelayMessage::new_builder().set(content).build();
695
696 if let Err(err) = nc
697 .async_filter_broadcast(TargetSession::Single(peer), message.as_bytes())
698 .await
699 {
700 debug_target!(
701 crate::LOG_TARGET_RELAY,
702 "relayer send TransactionHashes error: {:?}",
703 err,
704 );
705 }
706 }
707 }
708}
709
710fn build_and_broadcast_compact_block(
711 nc: &dyn CKBProtocolContext,
712 shared: &Shared,
713 peer: PeerIndex,
714 block: Arc<BlockView>,
715) {
716 debug_target!(
717 crate::LOG_TARGET_RELAY,
718 "[block_relay] relayer accept_block {} {}",
719 block.header().hash(),
720 unix_time_as_millis()
721 );
722 let block_hash = block.hash();
723 shared.remove_header_view(&block_hash);
724 let cb = packed::CompactBlock::build_from_block(&block, &HashSet::new());
725 let message = packed::RelayMessage::new_builder().set(cb).build();
726
727 let selected_peers: Vec<PeerIndex> = nc
728 .connected_peers()
729 .into_iter()
730 .filter(|target_peer| peer != *target_peer)
731 .take(MAX_RELAY_PEERS)
732 .collect();
733 let handle = shared.async_handle();
734 if let Err(err) = handle.block_on(nc.async_quick_filter_broadcast(
735 TargetSession::Multi(Box::new(selected_peers.into_iter())),
736 message.as_bytes(),
737 )) {
738 debug_target!(
739 crate::LOG_TARGET_RELAY,
740 "relayer send block when accept block error: {:?}",
741 err,
742 );
743 }
744
745 let snapshot = shared.snapshot();
746 let parent_chain_root = {
747 let mmr = snapshot.chain_root_mmr(block.header().number() - 1);
748 match mmr.get_root() {
749 Ok(root) => root,
750 Err(err) => {
751 error_target!(
752 crate::LOG_TARGET_RELAY,
753 "Generate last state to light client failed: {:?}",
754 err
755 );
756 return;
757 }
758 }
759 };
760
761 let tip_header = packed::VerifiableHeader::new_builder()
762 .header(block.header().data())
763 .uncles_hash(block.calc_uncles_hash())
764 .extension(Pack::pack(&block.extension()))
765 .parent_chain_root(parent_chain_root)
766 .build();
767 let light_client_message = {
768 let content = packed::SendLastState::new_builder()
769 .last_header(tip_header)
770 .build();
771 packed::LightClientMessage::new_builder()
772 .set(content)
773 .build()
774 };
775 let light_client_peers: HashSet<PeerIndex> = nc
776 .connected_peers()
777 .into_iter()
778 .filter_map(|index| nc.get_peer(index).map(|peer| (index, peer)))
779 .filter(|(_id, peer)| peer.if_lightclient_subscribed)
780 .map(|(id, _)| id)
781 .collect();
782 if let Err(err) = handle.block_on(nc.async_filter_broadcast_with_proto(
783 SupportProtocols::LightClient.protocol_id(),
784 TargetSession::Filter(Box::new(move |id| light_client_peers.contains(id))),
785 light_client_message.as_bytes(),
786 )) {
787 debug_target!(
788 crate::LOG_TARGET_RELAY,
789 "relayer send last state to light client when accept block, error: {:?}",
790 err,
791 );
792 }
793}
794
795#[async_trait]
796impl CKBProtocolHandler for Relayer {
797 async fn init(&mut self, nc: Arc<dyn CKBProtocolContext + Sync>) {
798 nc.set_notify(Duration::from_millis(100), TX_PROPOSAL_TOKEN)
799 .await
800 .expect("set_notify at init is ok");
801 nc.set_notify(Duration::from_millis(100), ASK_FOR_TXS_TOKEN)
802 .await
803 .expect("set_notify at init is ok");
804 nc.set_notify(Duration::from_millis(300), TX_HASHES_TOKEN)
805 .await
806 .expect("set_notify at init is ok");
807 }
808
809 async fn received(
810 &mut self,
811 nc: Arc<dyn CKBProtocolContext + Sync>,
812 peer_index: PeerIndex,
813 data: Bytes,
814 ) {
815 if self.shared.active_chain().is_initial_block_download() {
817 return;
818 }
819
820 let msg = match packed::RelayMessageReader::from_compatible_slice(&data) {
821 Ok(msg) => {
822 let item = msg.to_enum();
823 if let packed::RelayMessageUnionReader::CompactBlock(ref reader) = item {
824 if reader.count_extra_fields() > 1 {
825 info_target!(
826 crate::LOG_TARGET_RELAY,
827 "Peer {} sends us a malformed message: \
828 too many fields in CompactBlock",
829 peer_index
830 );
831 nc.ban_peer(
832 peer_index,
833 BAD_MESSAGE_BAN_TIME,
834 String::from(
835 "send us a malformed message: \
836 too many fields in CompactBlock",
837 ),
838 );
839 return;
840 } else {
841 item
842 }
843 } else {
844 match packed::RelayMessageReader::from_slice(&data) {
845 Ok(msg) => msg.to_enum(),
846 _ => {
847 info_target!(
848 crate::LOG_TARGET_RELAY,
849 "Peer {} sends us a malformed message: \
850 too many fields",
851 peer_index
852 );
853 nc.ban_peer(
854 peer_index,
855 BAD_MESSAGE_BAN_TIME,
856 String::from(
857 "send us a malformed message \
858 too many fields",
859 ),
860 );
861 return;
862 }
863 }
864 }
865 }
866 _ => {
867 info_target!(
868 crate::LOG_TARGET_RELAY,
869 "Peer {} sends us a malformed message",
870 peer_index
871 );
872 nc.ban_peer(
873 peer_index,
874 BAD_MESSAGE_BAN_TIME,
875 String::from("send us a malformed message"),
876 );
877 return;
878 }
879 };
880
881 debug_target!(
882 crate::LOG_TARGET_RELAY,
883 "received msg {} from {}",
884 msg.item_name(),
885 peer_index
886 );
887 #[cfg(feature = "with_sentry")]
888 {
889 let sentry_hub = sentry::Hub::current();
890 let _scope_guard = sentry_hub.push_scope();
891 sentry_hub.configure_scope(|scope| {
892 scope.set_tag("p2p.protocol", "relayer");
893 scope.set_tag("p2p.message", msg.item_name());
894 });
895 }
896
897 let start_time = Instant::now();
898 self.process(nc, peer_index, msg).await;
899 debug_target!(
900 crate::LOG_TARGET_RELAY,
901 "process message={}, peer={}, cost={:?}",
902 msg.item_name(),
903 peer_index,
904 Instant::now().saturating_duration_since(start_time),
905 );
906 }
907
908 async fn connected(
909 &mut self,
910 _nc: Arc<dyn CKBProtocolContext + Sync>,
911 peer_index: PeerIndex,
912 version: &str,
913 ) {
914 self.shared().state().peers().relay_connected(peer_index);
915 info_target!(
916 crate::LOG_TARGET_RELAY,
917 "RelayProtocol({}).connected peer={}",
918 version,
919 peer_index
920 );
921 }
922
923 async fn disconnected(
924 &mut self,
925 _nc: Arc<dyn CKBProtocolContext + Sync>,
926 peer_index: PeerIndex,
927 ) {
928 info_target!(
929 crate::LOG_TARGET_RELAY,
930 "RelayProtocol.disconnected peer={}",
931 peer_index
932 );
933 self.rate_limiter.retain_recent();
935 }
936
937 async fn notify(&mut self, nc: Arc<dyn CKBProtocolContext + Sync>, token: u64) {
938 if self.shared.active_chain().is_initial_block_download() {
940 return;
941 }
942
943 let start_time = Instant::now();
944 trace_target!(
945 crate::LOG_TARGET_RELAY,
946 "start notifas_ref()y token={}",
947 token
948 );
949 match token {
950 TX_PROPOSAL_TOKEN => self.prune_tx_proposal_request(&nc).await,
951 ASK_FOR_TXS_TOKEN => self.ask_for_txs(&nc).await,
952 TX_HASHES_TOKEN => self.send_bulk_of_tx_hashes(&nc).await,
953 _ => unreachable!(),
954 }
955 trace_target!(
956 crate::LOG_TARGET_RELAY,
957 "finished notify token={} cost={:?}",
958 token,
959 Instant::now().saturating_duration_since(start_time)
960 );
961 }
962}