1use std::{collections::BTreeMap, pin::Pin, sync::Arc, time::Duration};
5
6use async_trait::async_trait;
7use bytes::Bytes;
8use consensus_config::AuthorityIndex;
9use consensus_types::block::{BlockRef, Round};
10use futures::{Stream, StreamExt, ready, stream, task};
11use mysten_metrics::spawn_monitored_task;
12use parking_lot::RwLock;
13use sui_macros::fail_point_async;
14use tap::TapFallible;
15use tokio::sync::broadcast;
16use tokio_util::sync::ReusableBoxFuture;
17use tracing::{debug, info, warn};
18
19use crate::{
20 block::{BlockAPI as _, ExtendedBlock, GENESIS_ROUND, SignedBlock, VerifiedBlock},
21 block_sync_service::BlockSyncService,
22 block_verifier::BlockVerifier,
23 commit::{CommitRange, TrustedCommit},
24 commit_vote_monitor::{CommitVoteMonitor, is_commit_lagging},
25 context::Context,
26 core_thread::CoreThreadDispatcher,
27 dag_state::DagState,
28 error::{ConsensusError, ConsensusResult},
29 network::{
30 BlockStream, ExtendedSerializedBlock, PeerId, SerializedBlockForm, ValidatorNetworkService,
31 },
32 round_tracker::RoundTracker,
33 synchronizer::SynchronizerHandle,
34 task::spawn_blocking,
35 transaction_vote_tracker::TransactionVoteTracker,
36};
37
38pub(crate) struct AuthorityService<C: CoreThreadDispatcher> {
40 context: Arc<Context>,
41 commit_vote_monitor: Arc<CommitVoteMonitor>,
42 block_verifier: Arc<dyn BlockVerifier>,
43 synchronizer: Arc<SynchronizerHandle>,
44 core_dispatcher: Arc<C>,
45 rx_block_broadcast: broadcast::Receiver<ExtendedBlock>,
46 subscription_counter: Arc<SubscriptionCounter>,
47 transaction_vote_tracker: TransactionVoteTracker,
48 dag_state: Arc<RwLock<DagState>>,
49 round_tracker: Arc<RwLock<RoundTracker>>,
50 block_sync_service: Arc<BlockSyncService>,
51}
52
53impl<C: CoreThreadDispatcher> AuthorityService<C> {
54 pub(crate) fn new(
55 context: Arc<Context>,
56 block_verifier: Arc<dyn BlockVerifier>,
57 commit_vote_monitor: Arc<CommitVoteMonitor>,
58 round_tracker: Arc<RwLock<RoundTracker>>,
59 synchronizer: Arc<SynchronizerHandle>,
60 core_dispatcher: Arc<C>,
61 rx_block_broadcast: broadcast::Receiver<ExtendedBlock>,
62 transaction_vote_tracker: TransactionVoteTracker,
63 dag_state: Arc<RwLock<DagState>>,
64 block_sync_service: Arc<BlockSyncService>,
65 ) -> Self {
66 let subscription_counter = Arc::new(SubscriptionCounter::new(context.clone()));
67 Self {
68 context,
69 block_verifier,
70 commit_vote_monitor,
71 synchronizer,
72 core_dispatcher,
73 rx_block_broadcast,
74 subscription_counter,
75 transaction_vote_tracker,
76 dag_state,
77 round_tracker,
78 block_sync_service,
79 }
80 }
81
82 fn parse_excluded_ancestors(
84 &self,
85 peer: AuthorityIndex,
86 block: &VerifiedBlock,
87 mut excluded_ancestors: Vec<Vec<u8>>,
88 ) -> ConsensusResult<Vec<BlockRef>> {
89 let peer_hostname = &self.context.committee.authority(peer).hostname;
90
91 let excluded_ancestors_limit = self.context.committee.size() * 2;
92 if excluded_ancestors.len() > excluded_ancestors_limit {
93 debug!(
94 "Dropping {} excluded ancestor(s) from {} {} due to size limit",
95 excluded_ancestors.len() - excluded_ancestors_limit,
96 peer,
97 peer_hostname,
98 );
99 excluded_ancestors.truncate(excluded_ancestors_limit);
100 }
101
102 let excluded_ancestors = excluded_ancestors
103 .into_iter()
104 .map(|serialized| {
105 let block_ref: BlockRef =
106 bcs::from_bytes(&serialized).map_err(ConsensusError::MalformedBlock)?;
107 if !self.context.committee.is_valid_index(block_ref.author) {
108 return Err(ConsensusError::InvalidAuthorityIndex {
109 loc: format!("excluded ancestor {}", block_ref),
110 index: block_ref.author,
111 max: self.context.committee.size() - 1,
112 });
113 }
114 if block_ref.round >= block.round() {
115 return Err(ConsensusError::InvalidAncestorRound {
116 ancestor: block_ref.round,
117 block: block.round(),
118 });
119 }
120 Ok(block_ref)
121 })
122 .collect::<ConsensusResult<Vec<BlockRef>>>()?;
123
124 for excluded_ancestor in &excluded_ancestors {
125 let excluded_ancestor_hostname = &self
126 .context
127 .committee
128 .authority(excluded_ancestor.author)
129 .hostname;
130 self.context
131 .metrics
132 .node_metrics
133 .network_excluded_ancestors_count_by_authority
134 .with_label_values(&[excluded_ancestor_hostname])
135 .inc();
136 }
137 self.context
138 .metrics
139 .node_metrics
140 .network_received_excluded_ancestors_from_authority
141 .with_label_values(&[peer_hostname])
142 .inc_by(excluded_ancestors.len() as u64);
143
144 Ok(excluded_ancestors)
145 }
146}
147
148#[async_trait]
149impl<C: CoreThreadDispatcher> ValidatorNetworkService for AuthorityService<C> {
150 async fn handle_send_block(
151 &self,
152 peer: AuthorityIndex,
153 serialized_block: ExtendedSerializedBlock,
154 ) -> ConsensusResult<()> {
155 fail_point_async!("consensus-rpc-response");
156
157 let peer_hostname = &self.context.committee.authority(peer).hostname;
158
159 let SerializedBlockForm::Full(serialized_bytes) = serialized_block.block else {
163 return Err(ConsensusError::UnexpectedBlockForm);
164 };
165 let signed_block: SignedBlock =
166 bcs::from_bytes(&serialized_bytes).map_err(ConsensusError::MalformedBlock)?;
167
168 if peer != signed_block.author() {
170 self.context
171 .metrics
172 .node_metrics
173 .invalid_blocks
174 .with_label_values(&[
175 peer_hostname.as_str(),
176 "handle_send_block",
177 "UnexpectedAuthority",
178 ])
179 .inc();
180 let e = ConsensusError::UnexpectedAuthority(signed_block.author(), peer);
181 info!("Block with wrong authority from {}: {}", peer, e);
182 return Err(e);
183 }
184
185 let block_verifier = self.block_verifier.clone();
187 let serialized = serialized_bytes.clone();
188 let (verified_block, reject_txn_votes) =
189 spawn_blocking(move || block_verifier.verify_and_vote(signed_block, serialized))
190 .await?
191 .tap_err(|e| {
192 self.context
193 .metrics
194 .node_metrics
195 .invalid_blocks
196 .with_label_values(&[peer_hostname.as_str(), "handle_send_block", e.name()])
197 .inc();
198 info!("Invalid block from {}: {}", peer, e);
199 })?;
200 let excluded_ancestors = self
201 .parse_excluded_ancestors(peer, &verified_block, serialized_block.excluded_ancestors)
202 .tap_err(|e| {
203 debug!("Failed to parse excluded ancestors from {peer} {peer_hostname}: {e}");
204 self.context
205 .metrics
206 .node_metrics
207 .invalid_blocks
208 .with_label_values(&[peer_hostname.as_str(), "handle_send_block", e.name()])
209 .inc();
210 })?;
211
212 let block_ref = verified_block.reference();
213 debug!("Received block {} via send block.", block_ref);
214
215 self.context
216 .metrics
217 .node_metrics
218 .verified_blocks
219 .with_label_values(&[peer_hostname])
220 .inc();
221
222 let now = self.context.clock.timestamp_utc_ms();
223 let forward_time_drift =
224 Duration::from_millis(verified_block.timestamp_ms().saturating_sub(now));
225
226 self.context
227 .metrics
228 .node_metrics
229 .block_timestamp_drift_ms
230 .with_label_values(&[peer_hostname.as_str(), "handle_send_block"])
231 .inc_by(forward_time_drift.as_millis() as u64);
232
233 self.commit_vote_monitor.observe_block(&verified_block);
236
237 self.round_tracker
239 .write()
240 .update_from_verified_block(&ExtendedBlock {
241 block: verified_block.clone(),
242 excluded_ancestors: excluded_ancestors.clone(),
243 });
244
245 let last_commit_index = self.dag_state.read().last_commit_index();
254 let quorum_commit_index = self.commit_vote_monitor.quorum_commit_index();
255 if is_commit_lagging(
256 self.context.as_ref(),
257 last_commit_index,
258 quorum_commit_index,
259 ) {
260 self.context
261 .metrics
262 .node_metrics
263 .rejected_blocks
264 .with_label_values(&["commit_lagging"])
265 .inc();
266 debug!(
267 "Block {:?} is rejected because last commit index is lagging quorum commit index too much ({} < {})",
268 block_ref, last_commit_index, quorum_commit_index,
269 );
270 return Err(ConsensusError::BlockRejected {
271 block_ref,
272 reason: format!(
273 "Last commit index is lagging quorum commit index too much ({} < {})",
274 last_commit_index, quorum_commit_index,
275 ),
276 });
277 }
278
279 if self.context.protocol_config.transaction_voting_enabled() {
282 self.transaction_vote_tracker
283 .add_voted_blocks(vec![(verified_block.clone(), reject_txn_votes)]);
284 }
285
286 let missing_ancestors = self
288 .core_dispatcher
289 .add_blocks(vec![verified_block.clone()])
290 .await
291 .map_err(|_| ConsensusError::Shutdown)?;
292
293 if !missing_ancestors.is_empty() {
295 self.context
296 .metrics
297 .node_metrics
298 .handler_received_block_missing_ancestors
299 .with_label_values(&[peer_hostname])
300 .inc_by(missing_ancestors.len() as u64);
301 let synchronizer = self.synchronizer.clone();
302 spawn_monitored_task!(async move {
303 if let Err(err) = synchronizer
308 .fetch_blocks(missing_ancestors, PeerId::Validator(peer))
309 .await
310 {
311 debug!("Failed to fetch missing ancestors via synchronizer: {err}");
312 }
313 });
314 }
315
316 let missing_excluded_ancestors = self
318 .core_dispatcher
319 .check_block_refs(excluded_ancestors)
320 .await
321 .map_err(|_| ConsensusError::Shutdown)?;
322 if !missing_excluded_ancestors.is_empty() {
323 self.context
324 .metrics
325 .node_metrics
326 .network_excluded_ancestors_sent_to_fetch
327 .with_label_values(&[peer_hostname])
328 .inc_by(missing_excluded_ancestors.len() as u64);
329
330 let synchronizer = self.synchronizer.clone();
331 spawn_monitored_task!(async move {
332 if let Err(err) = synchronizer
333 .fetch_blocks(missing_excluded_ancestors, PeerId::Validator(peer))
334 .await
335 {
336 debug!("Failed to fetch excluded ancestors via synchronizer: {err}");
337 }
338 });
339 }
340
341 Ok(())
342 }
343
344 async fn handle_subscribe_blocks(
345 &self,
346 peer: AuthorityIndex,
347 last_received: Round,
348 ) -> ConsensusResult<BlockStream> {
349 fail_point_async!("consensus-rpc-response");
350
351 let broadcast_rx = self.rx_block_broadcast.resubscribe();
356
357 let past_proposed_blocks = {
366 let dag_state = self.dag_state.read();
367
368 let mut proposed_blocks = dag_state
371 .get_cached_blocks(self.context.own_index, last_received.saturating_add(1));
372 if proposed_blocks.is_empty() {
373 let last_proposed_block = dag_state
374 .get_last_proposed_block()
375 .expect("Last proposed block should be returned on validators");
376 proposed_blocks = if last_proposed_block.round() > GENESIS_ROUND {
377 vec![last_proposed_block]
378 } else {
379 vec![]
380 };
381 }
382 stream::iter(
383 proposed_blocks
384 .into_iter()
385 .map(|block| ExtendedSerializedBlock {
386 block: SerializedBlockForm::Full(block.serialized().clone()),
387 excluded_ancestors: vec![],
388 }),
389 )
390 };
391
392 const MAX_BLOCKS_PER_POLL: usize = 1;
394 let broadcasted_blocks = BroadcastedBlockStream::new(
395 PeerId::Validator(peer),
396 broadcast_rx,
397 MAX_BLOCKS_PER_POLL,
398 self.subscription_counter.clone(),
399 );
400
401 Ok(Box::pin(past_proposed_blocks.chain(
403 broadcasted_blocks.flat_map(|items| {
404 debug_assert!(
405 items.len() <= MAX_BLOCKS_PER_POLL,
406 "Too many blocks received from broadcast"
407 );
408 stream::iter(items.into_iter().map(ExtendedSerializedBlock::from))
409 }),
410 )))
411 }
412
413 async fn handle_fetch_blocks(
429 &self,
430 _peer: AuthorityIndex,
431 block_refs: Vec<BlockRef>,
432 fetch_after_rounds: Vec<Round>,
433 fetch_missing_ancestors: bool,
434 ) -> ConsensusResult<Vec<Bytes>> {
435 fail_point_async!("consensus-rpc-response");
436
437 self.block_sync_service
439 .fetch_blocks(block_refs, fetch_after_rounds, fetch_missing_ancestors)
440 .await
441 }
442
443 async fn handle_fetch_commits(
444 &self,
445 _peer: AuthorityIndex,
446 commit_range: CommitRange,
447 ) -> ConsensusResult<(Vec<TrustedCommit>, Vec<VerifiedBlock>)> {
448 fail_point_async!("consensus-rpc-response");
449
450 self.block_sync_service.fetch_commits(commit_range).await
452 }
453
454 async fn handle_fetch_latest_blocks(
455 &self,
456 peer: AuthorityIndex,
457 authorities: Vec<AuthorityIndex>,
458 ) -> ConsensusResult<Vec<Bytes>> {
459 fail_point_async!("consensus-rpc-response");
460
461 self.block_sync_service
463 .fetch_latest_blocks(peer, authorities)
464 .await
465 }
466
467 async fn handle_get_latest_rounds(
468 &self,
469 _peer: AuthorityIndex,
470 ) -> ConsensusResult<(Vec<Round>, Vec<Round>)> {
471 fail_point_async!("consensus-rpc-response");
472
473 let highest_received_rounds = self.round_tracker.read().local_highest_received_rounds();
474
475 let blocks = self
476 .dag_state
477 .read()
478 .get_last_cached_block_per_authority(Round::MAX);
479 let highest_accepted_rounds = blocks
480 .into_iter()
481 .map(|(block, _)| block.round())
482 .collect::<Vec<_>>();
483
484 Ok((highest_received_rounds, highest_accepted_rounds))
485 }
486}
487
488struct Counter {
489 count: usize,
490 subscriptions_by_peer: BTreeMap<PeerId, usize>,
491}
492
493pub(crate) struct SubscriptionCounter {
495 context: Arc<Context>,
496 counter: parking_lot::Mutex<Counter>,
497}
498
499impl SubscriptionCounter {
500 pub(crate) fn new(context: Arc<Context>) -> Self {
501 for (_, authority) in context.committee.authorities() {
503 context
504 .metrics
505 .node_metrics
506 .subscribed_by
507 .with_label_values(&[authority.hostname.as_str()])
508 .set(0);
509 }
510
511 Self {
512 counter: parking_lot::Mutex::new(Counter {
513 count: 0,
514 subscriptions_by_peer: BTreeMap::new(),
515 }),
516 context,
517 }
518 }
519
520 fn increment(&self, peer: &PeerId) {
521 let mut counter = self.counter.lock();
522 counter.count += 1;
523 let peer_count = {
524 let count = counter
525 .subscriptions_by_peer
526 .entry(peer.clone())
527 .or_default();
528 *count += 1;
529 *count
530 };
531
532 match peer {
533 PeerId::Validator(authority) => {
534 let peer_hostname = &self.context.committee.authority(*authority).hostname;
535 self.context
536 .metrics
537 .node_metrics
538 .subscribed_by
539 .with_label_values(&[peer_hostname])
540 .set(1);
541 }
542 PeerId::Observer(_) => {
543 if peer_count == 1 {
545 self.context
546 .metrics
547 .node_metrics
548 .subscribed_by
549 .with_label_values(&["observer"])
550 .inc();
551 }
552 }
553 }
554 }
555
556 fn decrement(&self, peer: &PeerId) {
557 let mut counter = self.counter.lock();
558 counter.count = counter.count.saturating_sub(1);
559 let peer_count = counter
560 .subscriptions_by_peer
561 .entry(peer.clone())
562 .or_default();
563 *peer_count = peer_count.saturating_sub(1);
564
565 if *peer_count == 0 {
566 match peer {
567 PeerId::Validator(authority) => {
568 let peer_hostname = &self.context.committee.authority(*authority).hostname;
569 self.context
570 .metrics
571 .node_metrics
572 .subscribed_by
573 .with_label_values(&[peer_hostname])
574 .set(0);
575 }
576 PeerId::Observer(_) => {
577 self.context
578 .metrics
579 .node_metrics
580 .subscribed_by
581 .with_label_values(&["observer"])
582 .dec();
583 }
584 }
585 }
586 }
587}
588
589type BroadcastedBlockStream = BroadcastStream<ExtendedBlock>;
592
593pub(crate) struct BroadcastStream<T> {
596 peer: Option<PeerId>,
597 inner: ReusableBoxFuture<
599 'static,
600 (
601 Result<T, broadcast::error::RecvError>,
602 broadcast::Receiver<T>,
603 ),
604 >,
605 max_items_per_poll: usize,
607 subscription_counter: Option<Arc<SubscriptionCounter>>,
609}
610
611impl<T: 'static + Clone + Send> BroadcastStream<T> {
612 pub fn new(
613 peer: PeerId,
614 rx: broadcast::Receiver<T>,
615 max_items_per_poll: usize,
616 subscription_counter: Arc<SubscriptionCounter>,
617 ) -> Self {
618 assert!(max_items_per_poll > 0, "max_items_per_poll must be > 0");
619 subscription_counter.increment(&peer);
620 Self {
621 peer: Some(peer),
622 inner: ReusableBoxFuture::new(make_recv_future(rx)),
623 max_items_per_poll,
624 subscription_counter: Some(subscription_counter),
625 }
626 }
627
628 pub fn new_untracked(rx: broadcast::Receiver<T>, max_items_per_poll: usize) -> Self {
630 assert!(max_items_per_poll > 0, "max_items_per_poll must be > 0");
631 Self {
632 peer: None,
633 inner: ReusableBoxFuture::new(make_recv_future(rx)),
634 max_items_per_poll,
635 subscription_counter: None,
636 }
637 }
638}
639
640impl<T: 'static + Clone + Send> Stream for BroadcastStream<T> {
641 type Item = Vec<T>;
642
643 fn poll_next(
644 mut self: Pin<&mut Self>,
645 cx: &mut task::Context<'_>,
646 ) -> task::Poll<Option<Self::Item>> {
647 loop {
648 let (result, mut rx) = ready!(self.inner.poll(cx));
649
650 match result {
651 Ok(item) => {
652 let mut items = Vec::new();
653 items.push(item);
654
655 while items.len() < self.max_items_per_poll {
657 match rx.try_recv() {
658 Ok(item) => items.push(item),
659 Err(broadcast::error::TryRecvError::Empty) => break,
660 Err(broadcast::error::TryRecvError::Closed) => break,
661 Err(broadcast::error::TryRecvError::Lagged(n)) => {
662 warn!("BroadcastStream {:?} lagged by {} messages", self.peer, n);
663 break;
664 }
665 }
666 }
667
668 self.inner.set(make_recv_future(rx));
669 return task::Poll::Ready(Some(items));
670 }
671 Err(broadcast::error::RecvError::Closed) => {
672 info!("BroadcastStream {:?} closed", self.peer);
673 return task::Poll::Ready(None);
674 }
675 Err(broadcast::error::RecvError::Lagged(n)) => {
676 warn!("BroadcastStream {:?} lagged by {} messages", self.peer, n);
677 self.inner.set(make_recv_future(rx));
679 continue;
680 }
681 }
682 }
683 }
684}
685
686impl<T> Drop for BroadcastStream<T> {
687 fn drop(&mut self) {
688 if let (Some(counter), Some(peer)) = (&self.subscription_counter, &self.peer) {
689 counter.decrement(peer);
690 }
691 }
692}
693
694async fn make_recv_future<T: Clone>(
695 mut rx: broadcast::Receiver<T>,
696) -> (
697 Result<T, broadcast::error::RecvError>,
698 broadcast::Receiver<T>,
699) {
700 let result = rx.recv().await;
701 (result, rx)
702}
703
704#[cfg(test)]
705mod tests {
706 use std::{
707 collections::{BTreeMap, BTreeSet},
708 sync::Arc,
709 time::Duration,
710 };
711
712 use async_trait::async_trait;
713 use bytes::Bytes;
714 use consensus_config::AuthorityIndex;
715 use consensus_types::block::{BlockDigest, BlockRef, Round};
716 use parking_lot::{Mutex, RwLock};
717 use tokio::{sync::broadcast, time::sleep};
718
719 use futures::StreamExt as _;
720
721 fn expect_full(form: &SerializedBlockForm) -> &[u8] {
723 match form {
724 SerializedBlockForm::Full(bytes) => bytes,
725 SerializedBlockForm::Slim(_) => panic!("expected a full block"),
726 }
727 }
728
729 use crate::{
730 authority_service::AuthorityService,
731 block::{BlockAPI, SignedBlock, TestBlock, VerifiedBlock},
732 block_sync_service::BlockSyncService,
733 commit::{CertifiedCommits, CommitRange},
734 commit_vote_monitor::CommitVoteMonitor,
735 context::Context,
736 core_thread::{CoreError, CoreThreadDispatcher},
737 dag_state::DagState,
738 error::ConsensusResult,
739 network::{
740 BlockStream, ExtendedSerializedBlock, ObserverNetworkClient, SerializedBlockForm,
741 SynchronizerClient, ValidatorNetworkClient, ValidatorNetworkService,
742 },
743 peers_pool::PeersPool,
744 round_tracker::RoundTracker,
745 storage::mem_store::MemStore,
746 synchronizer::Synchronizer,
747 test_dag_builder::DagBuilder,
748 transaction_vote_tracker::TransactionVoteTracker,
749 };
750 struct FakeCoreThreadDispatcher {
751 blocks: Mutex<Vec<VerifiedBlock>>,
752 }
753
754 impl FakeCoreThreadDispatcher {
755 fn new() -> Self {
756 Self {
757 blocks: Mutex::new(vec![]),
758 }
759 }
760
761 fn get_blocks(&self) -> Vec<VerifiedBlock> {
762 self.blocks.lock().clone()
763 }
764 }
765
766 #[async_trait]
767 impl CoreThreadDispatcher for FakeCoreThreadDispatcher {
768 async fn add_blocks(
769 &self,
770 blocks: Vec<VerifiedBlock>,
771 ) -> Result<BTreeSet<BlockRef>, CoreError> {
772 let block_refs = blocks.iter().map(|b| b.reference()).collect();
773 self.blocks.lock().extend(blocks);
774 Ok(block_refs)
775 }
776
777 async fn check_block_refs(
778 &self,
779 _block_refs: Vec<BlockRef>,
780 ) -> Result<BTreeSet<BlockRef>, CoreError> {
781 Ok(BTreeSet::new())
782 }
783
784 async fn add_certified_commits(
785 &self,
786 _commits: CertifiedCommits,
787 ) -> Result<BTreeSet<BlockRef>, CoreError> {
788 todo!()
789 }
790
791 async fn new_block(&self, _round: Round, _force: bool) -> Result<(), CoreError> {
792 Ok(())
793 }
794
795 async fn get_missing_blocks(&self) -> Result<BTreeSet<BlockRef>, CoreError> {
796 Ok(Default::default())
797 }
798
799 fn set_propagation_delay(&self, _propagation_delay: Round) -> Result<(), CoreError> {
800 todo!()
801 }
802
803 fn set_last_known_proposed_round(&self, _round: Round) -> Result<(), CoreError> {
804 todo!()
805 }
806 }
807
808 #[derive(Default)]
809 struct FakeNetworkClient {}
810
811 #[async_trait]
812 impl ValidatorNetworkClient for FakeNetworkClient {
813 #[cfg(not(msim))]
814 async fn send_block(
815 &self,
816 _peer: AuthorityIndex,
817 _block: &VerifiedBlock,
818 _timeout: Duration,
819 ) -> ConsensusResult<()> {
820 unimplemented!("Unimplemented")
821 }
822
823 async fn subscribe_blocks(
824 &self,
825 _peer: AuthorityIndex,
826 _last_received: Round,
827 _timeout: Duration,
828 ) -> ConsensusResult<BlockStream> {
829 unimplemented!("Unimplemented")
830 }
831
832 async fn fetch_blocks(
833 &self,
834 _peer: AuthorityIndex,
835 _block_refs: Vec<BlockRef>,
836 _fetch_after_rounds: Vec<Round>,
837 _fetch_missing_ancestors: bool,
838 _timeout: Duration,
839 ) -> ConsensusResult<Vec<Bytes>> {
840 unimplemented!("Unimplemented")
841 }
842
843 async fn fetch_commits(
844 &self,
845 _peer: AuthorityIndex,
846 _commit_range: CommitRange,
847 _timeout: Duration,
848 ) -> ConsensusResult<(Vec<Bytes>, Vec<Bytes>)> {
849 unimplemented!("Unimplemented")
850 }
851
852 async fn fetch_latest_blocks(
853 &self,
854 _peer: AuthorityIndex,
855 _authorities: Vec<AuthorityIndex>,
856 _timeout: Duration,
857 ) -> ConsensusResult<Vec<Bytes>> {
858 unimplemented!("Unimplemented")
859 }
860
861 async fn get_latest_rounds(
862 &self,
863 _peer: AuthorityIndex,
864 _timeout: Duration,
865 ) -> ConsensusResult<(Vec<Round>, Vec<Round>)> {
866 unimplemented!("Unimplemented")
867 }
868 }
869
870 #[async_trait]
871 impl ObserverNetworkClient for FakeNetworkClient {
872 async fn stream_blocks(
873 &self,
874 _peer: crate::network::PeerId,
875 _highest_round_per_authority: Vec<Round>,
876 _filter: crate::network::BlockStreamFilter,
877 _timeout: Duration,
878 ) -> ConsensusResult<crate::network::ObserverBlockStream> {
879 unimplemented!("Unimplemented")
880 }
881
882 async fn fetch_blocks(
883 &self,
884 _peer: crate::network::PeerId,
885 _block_refs: Vec<BlockRef>,
886 _fetch_after_rounds: Vec<Round>,
887 _fetch_missing_ancestors: bool,
888 _timeout: Duration,
889 ) -> ConsensusResult<Vec<Bytes>> {
890 unimplemented!("Unimplemented")
891 }
892
893 async fn fetch_commits(
894 &self,
895 _peer: crate::network::PeerId,
896 _commit_range: CommitRange,
897 _timeout: Duration,
898 ) -> ConsensusResult<(Vec<Bytes>, Vec<Bytes>)> {
899 unimplemented!("Unimplemented")
900 }
901 }
902
903 #[tokio::test(flavor = "current_thread", start_paused = true)]
904 async fn test_handle_send_block() {
905 let (context, _keys) = Context::new_for_test(4);
906 let context = Arc::new(context);
907 let block_verifier = Arc::new(crate::block_verifier::NoopBlockVerifier {});
908 let commit_vote_monitor = Arc::new(CommitVoteMonitor::new(context.clone()));
909 let core_dispatcher = Arc::new(FakeCoreThreadDispatcher::new());
910 let (_tx_block_broadcast, rx_block_broadcast) = broadcast::channel(100);
911 let fake_client = Arc::new(FakeNetworkClient::default());
912 let network_client = Arc::new(SynchronizerClient::new(
913 context.clone(),
914 Some(fake_client.clone()),
915 Some(fake_client.clone()),
916 ));
917 let store = Arc::new(MemStore::new());
918 let dag_state = Arc::new(RwLock::new(DagState::new(context.clone(), store.clone())));
919 let transaction_vote_tracker =
920 TransactionVoteTracker::new(context.clone(), block_verifier.clone(), dag_state.clone());
921 let round_tracker = Arc::new(RwLock::new(RoundTracker::new(context.clone(), vec![])));
922 let peers_pool = Arc::new(PeersPool::new(context.clone()));
923 let synchronizer = Synchronizer::start(
924 network_client,
925 context.clone(),
926 core_dispatcher.clone(),
927 commit_vote_monitor.clone(),
928 block_verifier.clone(),
929 transaction_vote_tracker.clone(),
930 round_tracker.clone(),
931 dag_state.clone(),
932 peers_pool.clone(),
933 false,
934 );
935 let block_sync_service = Arc::new(BlockSyncService::new(
936 context.clone(),
937 dag_state.clone(),
938 store.clone(),
939 ));
940 let authority_service = Arc::new(AuthorityService::new(
941 context.clone(),
942 block_verifier,
943 commit_vote_monitor,
944 round_tracker,
945 synchronizer,
946 core_dispatcher.clone(),
947 rx_block_broadcast,
948 transaction_vote_tracker,
949 dag_state,
950 block_sync_service,
951 ));
952
953 let now = context.clock.timestamp_utc_ms();
955 let max_drift = context.parameters.max_forward_time_drift;
956 let input_block = VerifiedBlock::new_for_test(
957 TestBlock::new(9, 0)
958 .set_timestamp_ms(now + max_drift.as_millis() as u64)
959 .build(),
960 );
961
962 let service = authority_service.clone();
963 let serialized = ExtendedSerializedBlock {
964 block: SerializedBlockForm::Full(input_block.serialized().clone()),
965 excluded_ancestors: vec![],
966 };
967
968 tokio::spawn({
969 let service = service.clone();
970 let context = context.clone();
971 async move {
972 service
973 .handle_send_block(context.committee.to_authority_index(0).unwrap(), serialized)
974 .await
975 .unwrap();
976 }
977 });
978
979 sleep(max_drift / 2).await;
980
981 let blocks = core_dispatcher.get_blocks();
982 assert_eq!(blocks.len(), 1);
983 assert_eq!(blocks[0], input_block);
984
985 let invalid_block =
987 VerifiedBlock::new_for_test(TestBlock::new(10, 1000).set_timestamp_ms(10).build());
988 let extended_block = ExtendedSerializedBlock {
989 block: SerializedBlockForm::Full(invalid_block.serialized().clone()),
990 excluded_ancestors: vec![],
991 };
992 service
993 .handle_send_block(
994 context.committee.to_authority_index(0).unwrap(),
995 extended_block,
996 )
997 .await
998 .unwrap_err();
999
1000 let slim_block = ExtendedSerializedBlock {
1003 block: SerializedBlockForm::Slim(Bytes::from_static(b"slim")),
1004 excluded_ancestors: vec![],
1005 };
1006 let result = service
1007 .handle_send_block(context.committee.to_authority_index(0).unwrap(), slim_block)
1008 .await;
1009 assert!(matches!(
1010 result,
1011 Err(crate::error::ConsensusError::UnexpectedBlockForm)
1012 ));
1013
1014 let invalid_excluded_ancestors = vec![
1016 bcs::to_bytes(&BlockRef::new(
1017 10,
1018 AuthorityIndex::new_for_test(1000),
1019 BlockDigest::MIN,
1020 ))
1021 .unwrap(),
1022 vec![3u8; 40],
1023 bcs::to_bytes(&invalid_block.reference()).unwrap(),
1024 ];
1025 let extended_block = ExtendedSerializedBlock {
1026 block: SerializedBlockForm::Full(input_block.serialized().clone()),
1027 excluded_ancestors: invalid_excluded_ancestors,
1028 };
1029 service
1030 .handle_send_block(
1031 context.committee.to_authority_index(0).unwrap(),
1032 extended_block,
1033 )
1034 .await
1035 .unwrap_err();
1036 }
1037
1038 #[tokio::test(flavor = "current_thread", start_paused = true)]
1039 async fn test_handle_fetch_blocks() {
1040 const NUM_AUTHORITIES: usize = 40;
1043 const NUM_ROUNDS: usize = 40;
1044 let (mut context, _keys) = Context::new_for_test(NUM_AUTHORITIES);
1045 context.parameters.max_blocks_per_fetch = 50;
1046 let context = Arc::new(context);
1047 let block_verifier = Arc::new(crate::block_verifier::NoopBlockVerifier {});
1048 let commit_vote_monitor = Arc::new(CommitVoteMonitor::new(context.clone()));
1049 let core_dispatcher = Arc::new(FakeCoreThreadDispatcher::new());
1050 let (_tx_block_broadcast, rx_block_broadcast) = broadcast::channel(100);
1051 let fake_client = Arc::new(FakeNetworkClient::default());
1052 let network_client = Arc::new(SynchronizerClient::new(
1053 context.clone(),
1054 Some(fake_client.clone()),
1055 Some(fake_client.clone()),
1056 ));
1057 let store = Arc::new(MemStore::new());
1058 let dag_state = Arc::new(RwLock::new(DagState::new(context.clone(), store.clone())));
1059 let transaction_vote_tracker =
1060 TransactionVoteTracker::new(context.clone(), block_verifier.clone(), dag_state.clone());
1061 let round_tracker = Arc::new(RwLock::new(RoundTracker::new(context.clone(), vec![])));
1062 let peers_pool = Arc::new(PeersPool::new(context.clone()));
1063 let synchronizer = Synchronizer::start(
1064 network_client,
1065 context.clone(),
1066 core_dispatcher.clone(),
1067 commit_vote_monitor.clone(),
1068 block_verifier.clone(),
1069 transaction_vote_tracker.clone(),
1070 round_tracker.clone(),
1071 dag_state.clone(),
1072 peers_pool.clone(),
1073 false,
1074 );
1075 let block_sync_service = Arc::new(BlockSyncService::new(
1076 context.clone(),
1077 dag_state.clone(),
1078 store.clone(),
1079 ));
1080 let authority_service = Arc::new(AuthorityService::new(
1081 context.clone(),
1082 block_verifier,
1083 commit_vote_monitor,
1084 round_tracker,
1085 synchronizer,
1086 core_dispatcher.clone(),
1087 rx_block_broadcast,
1088 transaction_vote_tracker,
1089 dag_state.clone(),
1090 block_sync_service,
1091 ));
1092
1093 let mut dag_builder = DagBuilder::new(context.clone());
1095 dag_builder
1096 .layers(1..=(NUM_ROUNDS as u32))
1097 .build()
1098 .persist_layers(dag_state.clone());
1099 dag_state.write().flush();
1100 let all_blocks = dag_builder.all_blocks();
1101
1102 let missing_block_refs: Vec<BlockRef> = all_blocks
1104 .iter()
1105 .rev()
1106 .take(2)
1107 .map(|b| b.reference())
1108 .collect();
1109 let highest_accepted_rounds: Vec<Round> = vec![1; NUM_AUTHORITIES];
1110 let results = authority_service
1111 .handle_fetch_blocks(
1112 AuthorityIndex::new_for_test(0),
1113 missing_block_refs.clone(),
1114 highest_accepted_rounds,
1115 true,
1116 )
1117 .await
1118 .unwrap();
1119
1120 let blocks: BTreeMap<BlockRef, VerifiedBlock> = results
1122 .iter()
1123 .map(|b| {
1124 let signed = bcs::from_bytes(b).unwrap();
1125 let block = VerifiedBlock::new_verified(signed, b.clone());
1126 (block.reference(), block)
1127 })
1128 .collect();
1129 assert_eq!(blocks.len(), context.parameters.max_blocks_per_sync);
1130 for b in &missing_block_refs {
1132 assert!(blocks.contains_key(b));
1133 }
1134 let num_missing_ancestors = blocks
1135 .keys()
1136 .filter(|b| b.round == NUM_ROUNDS as Round - 1)
1137 .count();
1138 assert_eq!(
1139 num_missing_ancestors,
1140 context.parameters.max_blocks_per_sync - missing_block_refs.len()
1141 );
1142
1143 let missing_round = NUM_ROUNDS as Round - 3;
1145 let missing_block_refs: Vec<BlockRef> = all_blocks
1146 .iter()
1147 .filter(|b| b.reference().round == missing_round)
1148 .map(|b| b.reference())
1149 .take(2)
1150 .collect();
1151 let mut highest_accepted_rounds: Vec<Round> = vec![1; NUM_AUTHORITIES];
1152 highest_accepted_rounds[missing_block_refs[0].author] = missing_round - 5;
1154 let results = authority_service
1155 .handle_fetch_blocks(
1156 AuthorityIndex::new_for_test(0),
1157 missing_block_refs.clone(),
1158 highest_accepted_rounds,
1159 false,
1160 )
1161 .await
1162 .unwrap();
1163
1164 let blocks: BTreeMap<BlockRef, VerifiedBlock> = results
1166 .iter()
1167 .map(|b| {
1168 let signed = bcs::from_bytes(b).unwrap();
1169 let block = VerifiedBlock::new_verified(signed, b.clone());
1170 (block.reference(), block)
1171 })
1172 .collect();
1173 assert_eq!(blocks.len(), context.parameters.max_blocks_per_sync);
1174 for b in &missing_block_refs {
1176 assert!(blocks.contains_key(b));
1177 }
1178 let expected_authors = [missing_block_refs[0].author, missing_block_refs[1].author];
1180 for b in blocks.keys() {
1181 assert!(b.round <= missing_round);
1182 assert!(expected_authors.contains(&b.author));
1183 }
1184
1185 let mut highest_accepted_rounds: Vec<Round> = vec![1; NUM_AUTHORITIES];
1187 highest_accepted_rounds[0] = (NUM_ROUNDS as Round) - 5;
1189 highest_accepted_rounds[1] = (NUM_ROUNDS as Round) - 3;
1190 let results = authority_service
1191 .handle_fetch_blocks(
1192 AuthorityIndex::new_for_test(0),
1193 vec![],
1194 highest_accepted_rounds.clone(),
1195 false,
1196 )
1197 .await
1198 .unwrap();
1199
1200 let blocks: BTreeMap<BlockRef, VerifiedBlock> = results
1202 .iter()
1203 .map(|b| {
1204 let signed = bcs::from_bytes(b).unwrap();
1205 let block = VerifiedBlock::new_verified(signed, b.clone());
1206 (block.reference(), block)
1207 })
1208 .collect();
1209 assert_eq!(blocks.len(), context.parameters.max_blocks_per_fetch);
1210 for block_ref in blocks.keys() {
1212 let accepted = highest_accepted_rounds[block_ref.author];
1213 assert!(block_ref.round > accepted);
1214 }
1215 let max_round_in_result = blocks.keys().map(|b| b.round).max().unwrap();
1218 assert!(
1221 max_round_in_result <= 4,
1222 "Expected low rounds from fair round-order fetching, got max round {}",
1223 max_round_in_result
1224 );
1225
1226 let missing_block_refs: Vec<BlockRef> = all_blocks
1228 .iter()
1229 .filter(|b| b.reference().round == NUM_ROUNDS as Round - 10)
1230 .map(|b| b.reference())
1231 .take(5)
1232 .collect();
1233 let results = authority_service
1234 .handle_fetch_blocks(
1235 AuthorityIndex::new_for_test(0),
1236 missing_block_refs.clone(),
1237 vec![],
1238 false,
1239 )
1240 .await
1241 .unwrap();
1242
1243 let blocks: BTreeMap<BlockRef, VerifiedBlock> = results
1245 .iter()
1246 .map(|b| {
1247 let signed = bcs::from_bytes(b).unwrap();
1248 let block = VerifiedBlock::new_verified(signed, b.clone());
1249 (block.reference(), block)
1250 })
1251 .collect();
1252 assert_eq!(blocks.len(), 5);
1253 for b in &missing_block_refs {
1254 assert!(blocks.contains_key(b));
1255 }
1256 }
1257
1258 #[tokio::test(flavor = "current_thread", start_paused = true)]
1259 async fn test_handle_fetch_latest_blocks() {
1260 let (context, _keys) = Context::new_for_test(4);
1262 let context = Arc::new(context);
1263 let block_verifier = Arc::new(crate::block_verifier::NoopBlockVerifier {});
1264 let commit_vote_monitor = Arc::new(CommitVoteMonitor::new(context.clone()));
1265 let core_dispatcher = Arc::new(FakeCoreThreadDispatcher::new());
1266 let (_tx_block_broadcast, rx_block_broadcast) = broadcast::channel(100);
1267 let fake_client = Arc::new(FakeNetworkClient::default());
1268 let network_client = Arc::new(SynchronizerClient::new(
1269 context.clone(),
1270 Some(fake_client.clone()),
1271 Some(fake_client.clone()),
1272 ));
1273 let store = Arc::new(MemStore::new());
1274 let dag_state = Arc::new(RwLock::new(DagState::new(context.clone(), store.clone())));
1275 let transaction_vote_tracker =
1276 TransactionVoteTracker::new(context.clone(), block_verifier.clone(), dag_state.clone());
1277 let round_tracker = Arc::new(RwLock::new(RoundTracker::new(context.clone(), vec![])));
1278 let peers_pool = Arc::new(PeersPool::new(context.clone()));
1279 let synchronizer = Synchronizer::start(
1280 network_client,
1281 context.clone(),
1282 core_dispatcher.clone(),
1283 commit_vote_monitor.clone(),
1284 block_verifier.clone(),
1285 transaction_vote_tracker.clone(),
1286 round_tracker.clone(),
1287 dag_state.clone(),
1288 peers_pool.clone(),
1289 true,
1290 );
1291 let block_sync_service = Arc::new(BlockSyncService::new(
1292 context.clone(),
1293 dag_state.clone(),
1294 store.clone(),
1295 ));
1296 let authority_service = Arc::new(AuthorityService::new(
1297 context.clone(),
1298 block_verifier,
1299 commit_vote_monitor,
1300 round_tracker,
1301 synchronizer,
1302 core_dispatcher.clone(),
1303 rx_block_broadcast,
1304 transaction_vote_tracker,
1305 dag_state.clone(),
1306 block_sync_service,
1307 ));
1308
1309 let mut dag_builder = DagBuilder::new(context.clone());
1311 dag_builder
1312 .layers(1..=10)
1313 .authorities(vec![AuthorityIndex::new_for_test(2)])
1314 .equivocate(1)
1315 .build()
1316 .persist_layers(dag_state);
1317
1318 let authorities_to_request = vec![
1320 AuthorityIndex::new_for_test(1),
1321 AuthorityIndex::new_for_test(2),
1322 ];
1323 let results = authority_service
1324 .handle_fetch_latest_blocks(AuthorityIndex::new_for_test(1), authorities_to_request)
1325 .await;
1326
1327 let serialised_blocks = results.unwrap();
1329 for serialised_block in serialised_blocks {
1330 let signed_block: SignedBlock =
1331 bcs::from_bytes(&serialised_block).expect("Error while deserialising block");
1332 let verified_block = VerifiedBlock::new_verified(signed_block, serialised_block);
1333
1334 assert_eq!(verified_block.round(), 10);
1335 }
1336 }
1337
1338 #[tokio::test(flavor = "current_thread", start_paused = true)]
1339 async fn test_handle_subscribe_blocks() {
1340 let (context, _keys) = Context::new_for_test(4);
1341 let context = Arc::new(context);
1342 let block_verifier = Arc::new(crate::block_verifier::NoopBlockVerifier {});
1343 let commit_vote_monitor = Arc::new(CommitVoteMonitor::new(context.clone()));
1344 let core_dispatcher = Arc::new(FakeCoreThreadDispatcher::new());
1345 let (_tx_block_broadcast, rx_block_broadcast) = broadcast::channel(100);
1346 let fake_client = Arc::new(FakeNetworkClient::default());
1347 let network_client = Arc::new(SynchronizerClient::new(
1348 context.clone(),
1349 Some(fake_client.clone()),
1350 Some(fake_client.clone()),
1351 ));
1352 let store = Arc::new(MemStore::new());
1353 let dag_state = Arc::new(RwLock::new(DagState::new(context.clone(), store.clone())));
1354 let transaction_vote_tracker =
1355 TransactionVoteTracker::new(context.clone(), block_verifier.clone(), dag_state.clone());
1356 let round_tracker = Arc::new(RwLock::new(RoundTracker::new(context.clone(), vec![])));
1357 let peers_pool = Arc::new(PeersPool::new(context.clone()));
1358 let synchronizer = Synchronizer::start(
1359 network_client,
1360 context.clone(),
1361 core_dispatcher.clone(),
1362 commit_vote_monitor.clone(),
1363 block_verifier.clone(),
1364 transaction_vote_tracker.clone(),
1365 round_tracker.clone(),
1366 dag_state.clone(),
1367 peers_pool.clone(),
1368 false,
1369 );
1370 let block_sync_service = Arc::new(BlockSyncService::new(
1371 context.clone(),
1372 dag_state.clone(),
1373 store.clone(),
1374 ));
1375
1376 dag_state
1378 .write()
1379 .accept_block(VerifiedBlock::new_for_test(TestBlock::new(5, 0).build()));
1380 dag_state
1381 .write()
1382 .accept_block(VerifiedBlock::new_for_test(TestBlock::new(10, 0).build()));
1383 dag_state
1384 .write()
1385 .accept_block(VerifiedBlock::new_for_test(TestBlock::new(15, 0).build()));
1386
1387 let authority_service = Arc::new(AuthorityService::new(
1388 context.clone(),
1389 block_verifier,
1390 commit_vote_monitor,
1391 round_tracker,
1392 synchronizer,
1393 core_dispatcher.clone(),
1394 rx_block_broadcast,
1395 transaction_vote_tracker,
1396 dag_state.clone(),
1397 block_sync_service,
1398 ));
1399
1400 let peer = context.committee.to_authority_index(1).unwrap();
1401
1402 {
1405 let mut stream = authority_service
1406 .handle_subscribe_blocks(peer, 100)
1407 .await
1408 .unwrap();
1409 let block: SignedBlock =
1410 bcs::from_bytes(expect_full(&stream.next().await.unwrap().block)).unwrap();
1411 assert_eq!(
1412 block.round(),
1413 15,
1414 "Should return last proposed block as fallback"
1415 );
1416 assert_eq!(block.author().value(), 0);
1417 }
1418
1419 {
1422 let mut stream = authority_service
1423 .handle_subscribe_blocks(peer, 7)
1424 .await
1425 .unwrap();
1426
1427 let block1: SignedBlock =
1428 bcs::from_bytes(expect_full(&stream.next().await.unwrap().block)).unwrap();
1429 assert_eq!(block1.round(), 10, "Should return block at round 10");
1430
1431 let block2: SignedBlock =
1432 bcs::from_bytes(expect_full(&stream.next().await.unwrap().block)).unwrap();
1433 assert_eq!(block2.round(), 15, "Should return block at round 15");
1434 }
1435 }
1436
1437 #[tokio::test(flavor = "current_thread", start_paused = true)]
1438 async fn test_handle_subscribe_blocks_not_proposed() {
1439 let (context, _keys) = Context::new_for_test(4);
1440 let context = Arc::new(context);
1441 let block_verifier = Arc::new(crate::block_verifier::NoopBlockVerifier {});
1442 let commit_vote_monitor = Arc::new(CommitVoteMonitor::new(context.clone()));
1443 let core_dispatcher = Arc::new(FakeCoreThreadDispatcher::new());
1444 let (_tx_block_broadcast, rx_block_broadcast) = broadcast::channel(100);
1445 let fake_client = Arc::new(FakeNetworkClient::default());
1446 let network_client = Arc::new(SynchronizerClient::new(
1447 context.clone(),
1448 Some(fake_client.clone()),
1449 Some(fake_client.clone()),
1450 ));
1451 let store = Arc::new(MemStore::new());
1452 let dag_state = Arc::new(RwLock::new(DagState::new(context.clone(), store.clone())));
1453 let transaction_vote_tracker =
1454 TransactionVoteTracker::new(context.clone(), block_verifier.clone(), dag_state.clone());
1455 let round_tracker = Arc::new(RwLock::new(RoundTracker::new(context.clone(), vec![])));
1456 let peers_pool = Arc::new(PeersPool::new(context.clone()));
1457 let synchronizer = Synchronizer::start(
1458 network_client,
1459 context.clone(),
1460 core_dispatcher.clone(),
1461 commit_vote_monitor.clone(),
1462 block_verifier.clone(),
1463 transaction_vote_tracker.clone(),
1464 round_tracker.clone(),
1465 dag_state.clone(),
1466 peers_pool.clone(),
1467 false,
1468 );
1469 let block_sync_service = Arc::new(BlockSyncService::new(
1470 context.clone(),
1471 dag_state.clone(),
1472 store.clone(),
1473 ));
1474
1475 let authority_service = Arc::new(AuthorityService::new(
1478 context.clone(),
1479 block_verifier,
1480 commit_vote_monitor,
1481 round_tracker,
1482 synchronizer,
1483 core_dispatcher.clone(),
1484 rx_block_broadcast,
1485 transaction_vote_tracker,
1486 dag_state.clone(),
1487 block_sync_service,
1488 ));
1489
1490 let peer = context.committee.to_authority_index(1).unwrap();
1491
1492 let mut stream = authority_service
1494 .handle_subscribe_blocks(peer, 0)
1495 .await
1496 .unwrap();
1497
1498 use futures::poll;
1500 use std::task::Poll;
1501 let poll_result = poll!(stream.next());
1502 assert!(
1503 matches!(poll_result, Poll::Pending),
1504 "Should not receive genesis block on subscription stream"
1505 );
1506 }
1507}