Skip to main content

ckb_sync/relayer/
mod.rs

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
77/// Relayer protocol handle
78pub struct Relayer {
79    chain: ChainController,
80    pub(crate) shared: Arc<SyncShared>,
81    rate_limiter: RateLimiter<(PeerIndex, u32)>,
82}
83
84impl Relayer {
85    /// Init relay protocol handle
86    ///
87    /// This is a runtime relay protocol shared state, and any relay messages will be processed and forwarded by it
88    pub fn new(chain: ChainController, shared: Arc<SyncShared>) -> Self {
89        // setup a rate limiter keyed by peer and message type that lets through 30 requests per second
90        // current max rps is 10 (ASK_FOR_TXS_TOKEN / TX_PROPOSAL_TOKEN), 30 is a flexible hard cap with buffer
91        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    /// Get shared state
102    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        // CompactBlock will be verified by POW, it's OK to skip rate limit checking.
113        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    /// Request the transaction corresponding to the proposal id from the specified node
225    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    /// Accept a new block from network
273    #[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                    // punish the malicious peer
323                    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    /// Reorganize the full block according to the compact block/txs/uncles
346    // nodes should attempt to reconstruct the full block by taking the prefilledtxn transactions
347    // from the original CompactBlock message and placing them in the marked positions,
348    // then for each short transaction ID from the original compact_block message, in order,
349    // find the corresponding transaction either from the BlockTransactions message or
350    // from other sources and place it in the first available position in the block
351    // then once the block has been reconstructed, it shall be processed as normal,
352    // keeping in mind that short_ids are expected to occasionally collide,
353    // and that nodes must not be penalized for such collisions, wherever they appear.
354    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        // fill transactions gap
401        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        // append remain transactions
416        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    /// Ask for relay transaction by hash from all peers
605    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    /// Send bulk of tx hashes to selected peers
631    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                                    // broadcast tx hash to all connected peers except original peer
655                                    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                                    // since this tx is submitted through local rpc, it is assumed to be a new tx for all connected peers
664                                    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 is in the IBD state, don't process any relayer message.
816        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        // Retains all keys in the rate limiter that were used recently enough.
934        self.rate_limiter.retain_recent();
935    }
936
937    async fn notify(&mut self, nc: Arc<dyn CKBProtocolContext + Sync>, token: u64) {
938        // If self is in the IBD state, don't trigger any relayer notify.
939        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}