1#![allow(clippy::boxed_local)] mod event_queue_metrics;
33pub(crate) mod main_reactor;
34mod queue_kind;
35
36use std::{
37 any,
38 collections::HashMap,
39 env,
40 fmt::{Debug, Display},
41 io::Write,
42 num::NonZeroU64,
43 str::FromStr,
44 sync::{atomic::Ordering, Arc},
45};
46
47use datasize::DataSize;
48use erased_serde::Serialize as ErasedSerialize;
49#[cfg(test)]
50use fake_instant::FakeClock;
51#[cfg(test)]
52use futures::future::BoxFuture;
53use futures::FutureExt;
54use once_cell::sync::Lazy;
55use prometheus::{self, Histogram, HistogramOpts, IntCounter, IntGauge, Registry};
56use quanta::{Clock, IntoNanoseconds};
57use serde::Serialize;
58use signal_hook::consts::signal::{SIGINT, SIGQUIT, SIGTERM};
59use stats_alloc::{Stats, INSTRUMENTED_SYSTEM};
60use tokio::time::{Duration, Instant};
61use tracing::{debug_span, error, info, instrument, trace, warn, Instrument, Span};
62
63#[cfg(test)]
64use crate::components::ComponentState;
65#[cfg(test)]
66use casper_types::testing::TestRng;
67use casper_types::{
68 Block, BlockHeader, Chainspec, ChainspecRawBytes, FinalitySignature, Transaction,
69};
70
71#[cfg(target_os = "linux")]
72use utils::rlimit::{Limit, OpenFiles, ResourceLimit};
73
74#[cfg(test)]
75use crate::testing::{network::NetworkedReactor, ConditionCheckReactor};
76use crate::{
77 components::{
78 block_accumulator,
79 fetcher::{self, FetchItem},
80 network::{blocklist::BlocklistJustification, Identity as NetworkIdentity},
81 transaction_acceptor,
82 },
83 effect::{
84 announcements::{ControlAnnouncement, PeerBehaviorAnnouncement, QueueDumpFormat},
85 incoming::NetResponse,
86 Effect, EffectBuilder, EffectExt, Effects,
87 },
88 failpoints::FailpointActivation,
89 types::{BlockExecutionResultsOrChunk, ExitCode, LegacyDeploy, NodeId, SyncLeap, TrieOrChunk},
90 unregister_metric,
91 utils::{self, SharedFlag, WeightedRoundRobin},
92 NodeRng, TERMINATION_REQUESTED,
93};
94use casper_storage::block_store::types::ApprovalsHashes;
95pub(crate) use queue_kind::QueueKind;
96
97const DEFAULT_DISPATCH_EVENT_THRESHOLD: Duration = Duration::from_secs(1);
100const DISPATCH_EVENT_THRESHOLD_ENV_VAR: &str = "CL_EVENT_MAX_MICROSECS";
101#[cfg(test)]
102const POLL_INTERVAL: Duration = Duration::from_millis(10);
103
104static DISPATCH_EVENT_THRESHOLD: Lazy<Duration> = Lazy::new(|| {
105 env::var(DISPATCH_EVENT_THRESHOLD_ENV_VAR)
106 .map(|threshold_str| {
107 let threshold_microsecs = u64::from_str(&threshold_str).unwrap_or_else(|error| {
108 panic!(
109 "can't parse env var {}={} as a u64: {}",
110 DISPATCH_EVENT_THRESHOLD_ENV_VAR, threshold_str, error
111 )
112 });
113 Duration::from_micros(threshold_microsecs)
114 })
115 .unwrap_or_else(|_| DEFAULT_DISPATCH_EVENT_THRESHOLD)
116});
117
118#[cfg(target_os = "linux")]
119const TARGET_OPEN_FILES_LIMIT: Limit = 64_000;
121
122#[cfg(target_os = "linux")]
123fn adjust_open_files_limit() {
125 match ResourceLimit::<OpenFiles>::get() {
127 Err(err) => {
128 warn!(%err, "could not retrieve open files limit");
129 }
130
131 Ok(current_limit) => {
132 if current_limit.current() < TARGET_OPEN_FILES_LIMIT {
133 let best_possible = if current_limit.max() < TARGET_OPEN_FILES_LIMIT {
134 warn!(
135 wanted = TARGET_OPEN_FILES_LIMIT,
136 hard_limit = current_limit.max(),
137 "settling for lower open files limit due to hard limit"
138 );
139 current_limit.max()
140 } else {
141 TARGET_OPEN_FILES_LIMIT
142 };
143
144 let new_limit = ResourceLimit::<OpenFiles>::fixed(best_possible);
145 if let Err(err) = new_limit.set() {
146 warn!(%err, current=current_limit.current(), target=best_possible, "did not succeed in raising open files limit")
147 } else {
148 tracing::debug!(?new_limit, "successfully increased open files limit");
149 }
150 } else {
151 tracing::debug!(
152 ?current_limit,
153 "not changing open files limit, already sufficient"
154 );
155 }
156 }
157 }
158}
159
160#[cfg(not(target_os = "linux"))]
161fn adjust_open_files_limit() {
163 info!("not on linux, not adjusting open files limit");
164}
165
166pub(crate) type Scheduler<Ev> = WeightedRoundRobin<(Option<NonZeroU64>, Ev), QueueKind>;
176
177#[derive(DataSize, Debug)]
183pub(crate) struct EventQueueHandle<REv>
184where
185 REv: 'static,
186{
187 scheduler: &'static Scheduler<REv>,
189 is_shutting_down: SharedFlag,
191}
192
193impl<REv> Clone for EventQueueHandle<REv> {
195 fn clone(&self) -> Self {
196 *self
197 }
198}
199impl<REv> Copy for EventQueueHandle<REv> {}
200
201impl<REv> EventQueueHandle<REv> {
202 pub(crate) fn new(scheduler: &'static Scheduler<REv>, is_shutting_down: SharedFlag) -> Self {
204 EventQueueHandle {
205 scheduler,
206 is_shutting_down,
207 }
208 }
209
210 #[cfg(test)]
214 pub(crate) fn without_shutdown(scheduler: &'static Scheduler<REv>) -> Self {
215 EventQueueHandle::new(scheduler, SharedFlag::global_shared())
216 }
217
218 pub(crate) async fn schedule<Ev>(self, event: Ev, queue_kind: QueueKind)
222 where
223 REv: From<Ev>,
224 {
225 self.schedule_with_ancestor(None, event, queue_kind).await;
226 }
227
228 pub(crate) async fn schedule_with_ancestor<Ev>(
230 self,
231 ancestor: Option<NonZeroU64>,
232 event: Ev,
233 queue_kind: QueueKind,
234 ) where
235 REv: From<Ev>,
236 {
237 self.scheduler
238 .push((ancestor, event.into()), queue_kind)
239 .await;
240 }
241
242 pub(crate) fn event_queues_counts(&self) -> HashMap<QueueKind, usize> {
244 self.scheduler.event_queues_counts()
245 }
246
247 pub(crate) fn shutdown_flag(&self) -> SharedFlag {
249 self.is_shutting_down
250 }
251}
252
253pub(crate) trait Reactor: Sized {
257 type Event: ReactorEvent + Display;
264
265 type Config;
267
268 type Error: Send + 'static;
270
271 fn dispatch_event(
277 &mut self,
278 effect_builder: EffectBuilder<Self::Event>,
279 rng: &mut NodeRng,
280 event: Self::Event,
281 ) -> Effects<Self::Event>;
282
283 fn new(
290 cfg: Self::Config,
291 chainspec: Arc<Chainspec>,
292 chainspec_raw_bytes: Arc<ChainspecRawBytes>,
293 network_identity: NetworkIdentity,
294 registry: &Registry,
295 event_queue: EventQueueHandle<Self::Event>,
296 rng: &mut NodeRng,
297 ) -> Result<(Self, Effects<Self::Event>), Self::Error>;
298
299 fn update_metrics(&mut self, _event_queue_handle: EventQueueHandle<Self::Event>) {}
301
302 fn activate_failpoint(&mut self, _activation: &FailpointActivation) {
304 }
307
308 #[allow(dead_code)]
313 #[cfg(test)]
314 fn get_component_state(&self, _name: &str) -> Option<&ComponentState> {
315 None
316 }
317}
318
319pub(crate) trait ReactorEvent: Send + Debug + From<ControlAnnouncement> + 'static {
321 fn is_control(&self) -> bool;
323
324 fn try_into_control(self) -> Option<ControlAnnouncement>;
328
329 fn description(&self) -> &'static str {
331 "anonymous event"
332 }
333}
334
335#[cfg(test)]
339pub(crate) trait Finalize: Sized {
340 fn finalize(self) -> BoxFuture<'static, ()> {
345 async move {}.boxed()
346 }
347}
348
349struct AllocatedMem {
351 allocated: u64,
353 consumed: u64,
355 total: u64,
357}
358
359#[derive(Debug)]
364pub(crate) struct Runner<R>
365where
366 R: Reactor,
367{
368 scheduler: &'static Scheduler<R::Event>,
370
371 reactor: R,
373
374 current_event_id: u64,
376
377 last_metrics: Instant,
379
380 metrics: RunnerMetrics,
382
383 event_metrics_threshold: u64,
385
386 event_metrics_min_delay: Duration,
388
389 clock: Clock,
391
392 is_shutting_down: SharedFlag,
394}
395
396#[derive(Debug)]
398struct RunnerMetrics {
399 events: IntCounter,
401 event_dispatch_duration: Histogram,
403 allocated_ram_bytes: IntGauge,
405 consumed_ram_bytes: IntGauge,
407 total_ram_bytes: IntGauge,
409 registry: Registry,
411}
412
413impl RunnerMetrics {
414 fn new(registry: &Registry) -> Result<Self, prometheus::Error> {
416 let events = IntCounter::new(
417 "runner_events",
418 "running total count of events handled by this reactor",
419 )?;
420
421 let event_dispatch_duration = Histogram::with_opts(
423 HistogramOpts::new(
424 "event_dispatch_duration",
425 "time in nanoseconds to dispatch an event",
426 )
427 .buckets(vec![
428 100.0,
429 500.0,
430 1_000.0,
431 5_000.0,
432 10_000.0,
433 20_000.0,
434 50_000.0,
435 100_000.0,
436 200_000.0,
437 300_000.0,
438 400_000.0,
439 500_000.0,
440 600_000.0,
441 700_000.0,
442 800_000.0,
443 900_000.0,
444 1_000_000.0,
445 2_000_000.0,
446 5_000_000.0,
447 ]),
448 )?;
449
450 let allocated_ram_bytes =
451 IntGauge::new("allocated_ram_bytes", "total allocated ram in bytes")?;
452 let consumed_ram_bytes =
453 IntGauge::new("consumed_ram_bytes", "total consumed ram in bytes")?;
454 let total_ram_bytes = IntGauge::new("total_ram_bytes", "total system ram in bytes")?;
455
456 registry.register(Box::new(events.clone()))?;
457 registry.register(Box::new(event_dispatch_duration.clone()))?;
458 registry.register(Box::new(allocated_ram_bytes.clone()))?;
459 registry.register(Box::new(consumed_ram_bytes.clone()))?;
460 registry.register(Box::new(total_ram_bytes.clone()))?;
461
462 Ok(RunnerMetrics {
463 events,
464 event_dispatch_duration,
465 registry: registry.clone(),
466 allocated_ram_bytes,
467 consumed_ram_bytes,
468 total_ram_bytes,
469 })
470 }
471}
472
473impl Drop for RunnerMetrics {
474 fn drop(&mut self) {
475 unregister_metric!(self.registry, self.events);
476 unregister_metric!(self.registry, self.event_dispatch_duration);
477 unregister_metric!(self.registry, self.allocated_ram_bytes);
478 unregister_metric!(self.registry, self.consumed_ram_bytes);
479 unregister_metric!(self.registry, self.total_ram_bytes);
480 }
481}
482
483impl<R> Runner<R>
484where
485 R: Reactor,
486 R::Event: Serialize,
487 R::Error: From<prometheus::Error>,
488{
489 #[instrument(
491 "init",
492 level = "debug",
493 skip_all,
494 fields(node_id = %NodeId::from(&network_identity))
495 )]
496 pub(crate) async fn with_metrics(
497 cfg: R::Config,
498 chainspec: Arc<Chainspec>,
499 chainspec_raw_bytes: Arc<ChainspecRawBytes>,
500 network_identity: NetworkIdentity,
501 rng: &mut NodeRng,
502 registry: &Registry,
503 ) -> Result<Self, R::Error> {
504 adjust_open_files_limit();
505
506 let event_size = size_of::<R::Event>();
507
508 if event_size > 16 * size_of::<usize>() {
512 warn!(
513 %event_size, type_name = ?any::type_name::<R::Event>(),
514 "large event size, consider reducing it or boxing"
515 );
516 }
517
518 let event_queue_dump_threshold =
519 env::var("CL_EVENT_QUEUE_DUMP_THRESHOLD").map_or(None, |s| s.parse::<usize>().ok());
520
521 let scheduler = utils::leak(Scheduler::new(
522 QueueKind::weights(),
523 event_queue_dump_threshold,
524 ));
525 let is_shutting_down = SharedFlag::new();
526 let event_queue = EventQueueHandle::new(scheduler, is_shutting_down);
527 let (reactor, initial_effects) = R::new(
528 cfg,
529 chainspec,
530 chainspec_raw_bytes,
531 network_identity,
532 registry,
533 event_queue,
534 rng,
535 )?;
536
537 info!(
538 "Reactor: with_metrics has: {} initial_effects",
539 initial_effects.len()
540 );
541 process_effects(None, scheduler, initial_effects, QueueKind::Regular)
543 .instrument(debug_span!("process initial effects"))
544 .await;
545
546 info!("reactor main loop is ready");
547
548 Ok(Runner {
549 scheduler,
550 reactor,
551 current_event_id: 1,
552 metrics: RunnerMetrics::new(registry)?,
553 last_metrics: Instant::now(),
554 event_metrics_min_delay: Duration::from_secs(30),
555 event_metrics_threshold: 1000,
556 clock: Clock::new(),
557 is_shutting_down,
558 })
559 }
560
561 #[instrument("dispatch", level = "debug", fields(a, ev = self.current_event_id), skip(self, rng))]
565 pub(crate) async fn crank(&mut self, rng: &mut NodeRng) -> Option<ExitCode> {
566 self.metrics.events.inc();
567
568 let event_queue = EventQueueHandle::new(self.scheduler, self.is_shutting_down);
569 let effect_builder = EffectBuilder::new(event_queue);
570
571 if self.current_event_id % self.event_metrics_threshold == 0 {
573 if self.last_metrics.elapsed() >= self.event_metrics_min_delay {
575 self.reactor.update_metrics(event_queue);
576
577 self.last_metrics = Instant::now();
581 }
582
583 if let Some(AllocatedMem {
584 allocated,
585 consumed,
586 total,
587 }) = Self::get_allocated_memory()
588 {
589 trace!(%allocated, %total, "memory allocated");
590 self.metrics.allocated_ram_bytes.set(allocated as i64);
591 self.metrics.consumed_ram_bytes.set(consumed as i64);
592 self.metrics.total_ram_bytes.set(total as i64);
593 }
594 }
595
596 let ((ancestor, event), queue_kind) = self.scheduler.pop().await;
597 trace!(%event, %queue_kind, "current");
598 let event_desc = event.description();
599
600 Span::current().record("ev", self.current_event_id);
602
603 if let Some(ancestor) = ancestor {
605 Span::current().record("a", ancestor.get());
606 }
607
608 let start = self.clock.start();
610
611 let (effects, maybe_exit_code, queue_kind) = if event.is_control() {
612 match event.try_into_control() {
614 None => {
615 error!(
618 "event::as_control succeeded, but try_into_control failed. this is a bug"
619 );
620
621 (Effects::new(), None, QueueKind::Control)
623 }
624 Some(ControlAnnouncement::ShutdownDueToUserRequest) => (
625 Effects::new(),
626 Some(ExitCode::CleanExitDontRestart),
627 QueueKind::Control,
628 ),
629 Some(ControlAnnouncement::ShutdownForUpgrade) => {
630 (Effects::new(), Some(ExitCode::Success), QueueKind::Control)
631 }
632 Some(ControlAnnouncement::ShutdownAfterCatchingUp) => (
633 Effects::new(),
634 Some(ExitCode::CleanExitDontRestart),
635 QueueKind::Control,
636 ),
637 Some(ControlAnnouncement::FatalError { file, line, msg }) => {
638 error!(%file, %line, %msg, "fatal error via control announcement");
639 (Effects::new(), Some(ExitCode::Abort), QueueKind::Control)
640 }
641 Some(ControlAnnouncement::QueueDumpRequest {
642 dump_format,
643 finished,
644 }) => {
645 match dump_format {
646 QueueDumpFormat::Serde(mut ser) => {
647 self.scheduler
648 .dump(move |queue_dump| {
649 if let Err(err) =
650 queue_dump.erased_serialize(&mut ser.as_serializer())
651 {
652 warn!(%err, "queue dump failed to serialize");
653 }
654 })
655 .await;
656 }
657 QueueDumpFormat::Debug(ref file) => {
658 match file.try_clone() {
659 Ok(mut local_file) => {
660 self.scheduler
661 .dump(move |queue_dump| {
662 write!(&mut local_file, "{:?}", queue_dump)
663 .and_then(|_| local_file.flush())
664 .map_err(|err| {
665 warn!(
666 ?err,
667 "failed to write/flush queue dump using debug format"
668 );
669 })
670 .ok();
671 })
672 .await;
673 }
674 Err(err) => warn!(
675 %err,
676 "could not create clone of temporary file for queue debug dump"
677 ),
678 };
679 }
680 }
681
682 finished.respond(()).await;
684
685 (Default::default(), None, QueueKind::Control)
687 }
688 Some(ControlAnnouncement::ActivateFailpoint { activation }) => {
689 self.reactor.activate_failpoint(&activation);
690
691 (Effects::new(), None, QueueKind::Control)
693 }
694 }
695 } else {
696 (
697 self.reactor.dispatch_event(effect_builder, rng, event),
698 None,
699 queue_kind,
700 )
701 };
702
703 let end = self.clock.end();
704
705 let delta = self.clock.delta(start, end);
707 if delta > *DISPATCH_EVENT_THRESHOLD {
708 warn!(%event_desc, ns = delta.into_nanos(), "event took very long to dispatch");
709 }
710 self.metrics
711 .event_dispatch_duration
712 .observe(delta.into_nanos() as f64);
713
714 process_effects(
716 NonZeroU64::new(self.current_event_id),
717 self.scheduler,
718 effects,
719 queue_kind,
720 )
721 .in_current_span()
722 .await;
723
724 self.current_event_id += 1;
725
726 maybe_exit_code
727 }
728
729 fn get_allocated_memory() -> Option<AllocatedMem> {
731 let mem_info = match sys_info::mem_info() {
732 Ok(mem_info) => mem_info,
733 Err(error) => {
734 warn!(%error, "unable to get mem_info using sys-info");
735 return None;
736 }
737 };
738
739 let total = mem_info.total * 1024;
741 let consumed = total - (mem_info.avail * 1024);
742
743 let Stats {
744 allocations: _,
745 deallocations: _,
746 reallocations: _,
747 bytes_allocated,
748 bytes_deallocated,
749 bytes_reallocated: _,
750 } = INSTRUMENTED_SYSTEM.stats();
751
752 Some(AllocatedMem {
753 allocated: bytes_allocated.saturating_sub(bytes_deallocated) as u64,
754 consumed,
755 total,
756 })
757 }
758
759 pub(crate) async fn run(&mut self, rng: &mut NodeRng) -> ExitCode {
762 loop {
763 match TERMINATION_REQUESTED.load(Ordering::SeqCst) as i32 {
764 0 => {
765 if let Some(exit_code) = self.crank(rng).await {
766 self.is_shutting_down.set();
767 break exit_code;
768 }
769 }
770 SIGINT => {
771 self.is_shutting_down.set();
772 break ExitCode::SigInt;
773 }
774 SIGQUIT => {
775 self.is_shutting_down.set();
776 break ExitCode::SigQuit;
777 }
778 SIGTERM => {
779 self.is_shutting_down.set();
780 break ExitCode::SigTerm;
781 }
782 _ => error!("should be unreachable - bug in signal handler"),
783 }
784 }
785 }
786}
787
788#[cfg(test)]
789#[derive(Eq, PartialEq, Debug)]
790pub(crate) enum TryCrankOutcome {
791 NoEventsToProcess,
792 ProcessedAnEvent,
793 ShouldExit(ExitCode),
794 Exited,
795}
796
797#[cfg(test)]
798impl<R> Runner<R>
799where
800 R: Reactor,
801 R::Event: Serialize,
802 R::Error: From<prometheus::Error>,
803{
804 pub(crate) async fn new(
808 cfg: R::Config,
809 chainspec: Arc<Chainspec>,
810 chainspec_raw_bytes: Arc<ChainspecRawBytes>,
811 rng: &mut NodeRng,
812 ) -> Result<Self, R::Error> {
813 let registry = Registry::new();
815 let network_identity = NetworkIdentity::with_generated_certs().unwrap();
816 Self::with_metrics(
817 cfg,
818 chainspec,
819 chainspec_raw_bytes,
820 network_identity,
821 rng,
822 ®istry,
823 )
824 .await
825 }
826
827 #[cfg(test)]
829 pub(crate) fn effect_builder(&self) -> EffectBuilder<R::Event> {
830 let event_queue = EventQueueHandle::new(self.scheduler, self.is_shutting_down);
831 EffectBuilder::new(event_queue)
832 }
833
834 #[cfg(test)]
837 pub(crate) async fn process_injected_effects<F>(&mut self, create_effects: F)
838 where
839 F: FnOnce(EffectBuilder<R::Event>) -> Effects<R::Event>,
840 {
841 use tracing::{debug_span, Instrument};
842
843 let event_queue = EventQueueHandle::new(self.scheduler, self.is_shutting_down);
844 let effect_builder = EffectBuilder::new(event_queue);
845
846 let effects = create_effects(effect_builder);
847
848 process_effects(None, self.scheduler, effects, QueueKind::Regular)
849 .instrument(debug_span!(
850 "process injected effects",
851 ev = self.current_event_id
852 ))
853 .await
854 }
855
856 pub(crate) async fn try_crank(&mut self, rng: &mut NodeRng) -> TryCrankOutcome {
858 if self.is_shutting_down.is_set() {
859 TryCrankOutcome::Exited
860 } else if self.scheduler.item_count() == 0 {
861 TryCrankOutcome::NoEventsToProcess
862 } else {
863 match self.crank(rng).await {
864 Some(exit_code) => {
865 self.is_shutting_down.set();
866 TryCrankOutcome::ShouldExit(exit_code)
867 }
868 None => TryCrankOutcome::ProcessedAnEvent,
869 }
870 }
871 }
872
873 pub(crate) fn reactor(&self) -> &R {
875 &self.reactor
876 }
877
878 pub(crate) fn reactor_mut(&mut self) -> &mut R {
880 &mut self.reactor
881 }
882
883 pub(crate) async fn drain_into_inner(self) -> R {
885 self.is_shutting_down.set();
886 self.scheduler.seal();
887 for (ancestor, event) in self.scheduler.drain_queues().await {
888 tracing::debug!(?ancestor, %event, "drained event");
889 }
890 self.reactor
891 }
892}
893
894#[cfg(test)]
895impl<R> Runner<ConditionCheckReactor<R>>
896where
897 R: Reactor + NetworkedReactor,
898 R::Event: Serialize,
899 R::Error: From<prometheus::Error>,
900{
901 pub(crate) async fn crank_until<F>(&mut self, rng: &mut TestRng, condition: F, within: Duration)
907 where
908 F: Fn(&R::Event) -> bool + Send + 'static,
909 {
910 self.reactor.set_condition_checker(Box::new(condition));
911
912 tokio::time::timeout(within, self.crank_and_check_indefinitely(rng))
913 .await
914 .unwrap_or_else(|_| {
915 panic!(
916 "Runner::crank_until() timed out after {}s on node {}",
917 within.as_secs_f64(),
918 self.reactor.inner().node_id()
919 )
920 })
921 }
922
923 async fn crank_and_check_indefinitely(&mut self, rng: &mut TestRng) {
924 loop {
925 match self.try_crank(rng).await {
926 TryCrankOutcome::NoEventsToProcess => {
927 FakeClock::advance_time(POLL_INTERVAL.as_millis() as u64);
928 tokio::time::sleep(POLL_INTERVAL).await;
929 continue;
930 }
931 TryCrankOutcome::ProcessedAnEvent => {}
932 TryCrankOutcome::ShouldExit(exit_code) => {
933 panic!("should not exit: {:?}", exit_code)
934 }
935 TryCrankOutcome::Exited => unreachable!(),
936 }
937
938 if self.reactor.condition_result() {
939 info!("{} met condition", self.reactor.inner().node_id());
940 return;
941 }
942 }
943 }
944}
945
946async fn process_effects<Ev>(
950 ancestor: Option<NonZeroU64>,
951 scheduler: &'static Scheduler<Ev>,
952 effects: Effects<Ev>,
953 queue_kind: QueueKind,
954) where
955 Ev: Send + 'static,
956{
957 for effect in effects {
958 tokio::spawn(async move {
959 for event in effect.await {
960 scheduler.push((ancestor, event), queue_kind).await;
961 }
962 });
963 }
964}
965
966fn wrap_effect<Ev, REv, F>(wrap: F, effect: Effect<Ev>) -> Effect<REv>
968where
969 F: Fn(Ev) -> REv + Send + 'static,
970 Ev: Send + 'static,
971 REv: Send + 'static,
972{
973 (async move {
975 let events = effect.await;
976 events.into_iter().map(wrap).collect()
977 })
978 .boxed()
979}
980
981pub(crate) fn wrap_effects<Ev, REv, F>(wrap: F, effects: Effects<Ev>) -> Effects<REv>
983where
984 F: Fn(Ev) -> REv + Send + 'static + Clone,
985 Ev: Send + 'static,
986 REv: Send + 'static,
987{
988 effects
989 .into_iter()
990 .map(move |effect| wrap_effect(wrap.clone(), effect))
991 .collect()
992}
993
994fn handle_fetch_response<R, I>(
995 reactor: &mut R,
996 effect_builder: EffectBuilder<<R as Reactor>::Event>,
997 rng: &mut NodeRng,
998 sender: NodeId,
999 serialized_item: &[u8],
1000) -> Effects<<R as Reactor>::Event>
1001where
1002 I: FetchItem,
1003 R: Reactor,
1004 <R as Reactor>::Event: From<fetcher::Event<I>> + From<PeerBehaviorAnnouncement>,
1005{
1006 match fetcher::Event::<I>::from_get_response_serialized_item(sender, serialized_item) {
1007 Some(fetcher_event) => {
1008 Reactor::dispatch_event(reactor, effect_builder, rng, fetcher_event.into())
1009 }
1010 None => effect_builder
1011 .announce_block_peer_with_justification(
1012 sender,
1013 BlocklistJustification::SentBadItem { tag: I::TAG },
1014 )
1015 .ignore(),
1016 }
1017}
1018
1019fn handle_get_response<R>(
1020 reactor: &mut R,
1021 effect_builder: EffectBuilder<<R as Reactor>::Event>,
1022 rng: &mut NodeRng,
1023 sender: NodeId,
1024 message: Box<NetResponse>,
1025) -> Effects<<R as Reactor>::Event>
1026where
1027 R: Reactor,
1028 <R as Reactor>::Event: From<transaction_acceptor::Event>
1029 + From<fetcher::Event<FinalitySignature>>
1030 + From<fetcher::Event<Block>>
1031 + From<fetcher::Event<BlockHeader>>
1032 + From<fetcher::Event<BlockExecutionResultsOrChunk>>
1033 + From<fetcher::Event<LegacyDeploy>>
1034 + From<fetcher::Event<Transaction>>
1035 + From<fetcher::Event<SyncLeap>>
1036 + From<fetcher::Event<TrieOrChunk>>
1037 + From<fetcher::Event<ApprovalsHashes>>
1038 + From<block_accumulator::Event>
1039 + From<PeerBehaviorAnnouncement>,
1040{
1041 match *message {
1042 NetResponse::Transaction(ref serialized_item) => handle_fetch_response::<R, Transaction>(
1043 reactor,
1044 effect_builder,
1045 rng,
1046 sender,
1047 serialized_item,
1048 ),
1049 NetResponse::LegacyDeploy(ref serialized_item) => handle_fetch_response::<R, LegacyDeploy>(
1050 reactor,
1051 effect_builder,
1052 rng,
1053 sender,
1054 serialized_item,
1055 ),
1056 NetResponse::Block(ref serialized_item) => {
1057 handle_fetch_response::<R, Block>(reactor, effect_builder, rng, sender, serialized_item)
1058 }
1059 NetResponse::BlockHeader(ref serialized_item) => handle_fetch_response::<R, BlockHeader>(
1060 reactor,
1061 effect_builder,
1062 rng,
1063 sender,
1064 serialized_item,
1065 ),
1066 NetResponse::FinalitySignature(ref serialized_item) => {
1067 handle_fetch_response::<R, FinalitySignature>(
1068 reactor,
1069 effect_builder,
1070 rng,
1071 sender,
1072 serialized_item,
1073 )
1074 }
1075 NetResponse::SyncLeap(ref serialized_item) => handle_fetch_response::<R, SyncLeap>(
1076 reactor,
1077 effect_builder,
1078 rng,
1079 sender,
1080 serialized_item,
1081 ),
1082 NetResponse::ApprovalsHashes(ref serialized_item) => {
1083 handle_fetch_response::<R, ApprovalsHashes>(
1084 reactor,
1085 effect_builder,
1086 rng,
1087 sender,
1088 serialized_item,
1089 )
1090 }
1091 NetResponse::BlockExecutionResults(ref serialized_item) => {
1092 handle_fetch_response::<R, BlockExecutionResultsOrChunk>(
1093 reactor,
1094 effect_builder,
1095 rng,
1096 sender,
1097 serialized_item,
1098 )
1099 }
1100 }
1101}