1pub mod error;
2pub(crate) mod metrics;
3pub mod timer;
4
5use std::{
6 collections::{BTreeMap, HashMap},
7 sync::Arc,
8 time::{Duration, Instant},
9};
10
11use bon::{Builder, bon};
12use committable::Commitment;
13use hotshot_types::{
14 consensus::{ConsensusMetricsValue, ParticipationTracker},
15 data::{EpochNumber, Leaf2, VidCommitment, VidCommitment2, ViewChangeEvidence2, ViewNumber},
16 epoch_membership::EpochMembershipCoordinator,
17 initializer::HotShotInitializer,
18 message::{Proposal as SignedProposal, UpgradeLock},
19 simple_certificate::{
20 OneHonestThreshold, SuccessThreshold, TimeoutCertificate2, TimeoutCertificate3,
21 TimeoutEvidence, UpgradeCertificate2,
22 },
23 simple_vote::{HasEpoch, QuorumVote2, TimeoutVote2},
24 traits::{
25 BlockPayload,
26 block_contents::BlockHeader,
27 metrics::Metrics,
28 node_implementation::NodeType,
29 signature_key::{SignatureKey, StateSignatureKey},
30 },
31 upgrade_config::UpgradeConfig,
32 utils::{is_epoch_root, is_last_block},
33 vote::{HasViewNumber, Vote},
34};
35use time::OffsetDateTime;
36use tokio::{
37 select,
38 sync::{mpsc, oneshot},
39};
40use tracing::{debug, error, info, warn};
41use versions::Version;
42
43use crate::{
44 block::{BlockAndHeaderRequest, BlockBuilder, BlockBuilderConfig},
45 cert_verifier::CertVerifiers,
46 client::{ClientApi, ClientRequest, CoordinatorClient, QueryError},
47 consensus::{Consensus, ConsensusInput, ConsensusOutput, GC_MARGIN_VIEWS},
48 coordinator::{
49 error::{CoordinatorError, ErrorSource, Severity},
50 timer::Timer,
51 },
52 epoch::{EpochManager, EpochRootResult},
53 fetch::{Fetcher, Retry},
54 helpers::{ViewEpoch, proposal_commitment, validated_state_cert},
55 leader_trace::LeaderTracerHandle,
56 logging::KeyPrefix,
57 message::{
58 self, BlockMessage, CatchupEvidence, Certificate1, Certificate2, ConsensusMessage, Message,
59 MessageType, OpaqueMessage, Proposal, ProposalFetchMessage, ProposalMessage, TimeoutVote,
60 TransactionMessage, Unchecked, Validated, Vote2, payload::PayloadFetchMessage,
61 },
62 network::Cliquenet,
63 outbox::Outbox,
64 proposal::{ProposalValidator, VidShareValidator},
65 serve::Server,
66 state::{HeaderRequest, StateEntry, StateManager, StateManagerOutput},
67 storage::{NewProtocolStorage, Storage},
68 upgrade::UpgradeProtocol,
69 vid::{
70 BuiltDispersal, ObtainedPayload, PinnedBy, VidDisperseRequest, VidDisperser,
71 VidFragmentAccumulator, VidReconstructor, expected_vid_param,
72 },
73 vote::{EpochRootTally, SimpleTally, TimeoutTally, UpgradeTally, VoteCollector},
74};
75
76pub(crate) const VID_RECONSTRUCT_GC_MARGIN: u64 = 5;
90
91const STORAGE_GC_MARGIN: u64 = 5;
94
95pub(crate) const EPOCH_CHANGE_LOOKAHEAD: u64 = 3;
102
103const EPOCH_CHANGE_LOOKBEHIND: u64 = 1;
105
106pub(crate) const MAX_VIEWS_AHEAD: ViewNumber = ViewNumber::new(30);
107
108#[derive(Builder)]
109pub struct Coordinator<T: NodeType, S> {
110 membership_coordinator: EpochMembershipCoordinator<T>,
111 consensus: Consensus<T>,
112 network: Cliquenet<T>,
113 state_manager: StateManager<T>,
114 #[builder(default)]
115 client: CoordinatorClient<T>,
116 vid_disperser: VidDisperser<T>,
117 vid_reconstructor: VidReconstructor<T>,
118 #[builder(default)]
119 vid_fragment_accumulator: VidFragmentAccumulator<T>,
120 vote1_collector: VoteCollector<T, SimpleTally<T, QuorumVote2<T>, SuccessThreshold>>,
121 vote2_collector: VoteCollector<T, SimpleTally<T, Vote2<T>, SuccessThreshold>>,
122 timeout_collector: VoteCollector<T, SimpleTally<T, TimeoutVote2<T>, SuccessThreshold>>,
123 timeout_one_honest_collector:
124 VoteCollector<T, SimpleTally<T, TimeoutVote2<T>, OneHonestThreshold>>,
125 timeout3_collector: VoteCollector<T, TimeoutTally<T, SuccessThreshold>>,
126 timeout_one_honest3_collector: VoteCollector<T, TimeoutTally<T, OneHonestThreshold>>,
127 epoch_root_collector: VoteCollector<T, EpochRootTally<T>>,
128 upgrade_vote_collector: VoteCollector<T, UpgradeTally<T>>,
129 upgrade_protocol: UpgradeProtocol<T>,
130 cert_verifiers: CertVerifiers<T>,
131 epoch_manager: EpochManager<T>,
132 block_builder: BlockBuilder<T>,
133 proposal_validator: ProposalValidator<T>,
134 share_validator: VidShareValidator<T>,
135 storage: Storage<T, S>,
136 #[builder(default)]
137 outbox: Outbox<ConsensusOutput<T>>,
138 external_tx: mpsc::UnboundedSender<OpaqueMessage<T::SignatureKey>>,
140 public_key: T::SignatureKey,
141 #[builder(default = KeyPrefix::from(&public_key))]
142 node_id: KeyPrefix,
143 timer: Timer,
144 #[builder(skip)]
145 pending_proposal_fetches: PendingProposalFetches<T>,
146 #[builder(skip)]
147 requested_missing_proposals: HashMap<ProposalFetchKey<T>, ViewNumber>,
149 #[builder(default = Fetcher::new(public_key.clone()))]
150 fetcher: Fetcher<T>,
151 #[builder(default = Server::new(public_key.clone()))]
152 server: Server<T>,
153 #[builder(skip)]
154 da_payloads: BTreeMap<(ViewNumber, VidCommitment2), PendingDa<T>>,
155 #[builder(skip)]
157 built_dispersals: BTreeMap<(ViewNumber, EpochNumber, VidCommitment2), BuiltDispersal<T>>,
158 metrics: Option<metrics::Metrics>,
159 #[builder(default)]
160 participation: ParticipationTracker<T>,
161 #[builder(skip)]
162 voted_view: Option<ViewNumber>,
163 #[builder(skip)]
167 last_timeout_view: Option<ViewNumber>,
168 #[builder(skip)]
169 view_started: Option<(ViewNumber, EpochNumber, Instant)>,
170 #[builder(skip)]
171 proposal_received_at: Option<(ViewNumber, Instant)>,
172 #[builder(skip)]
173 invalid_certs_at_decide: u64,
174 #[builder(skip)]
175 payload_txn_bytes: BTreeMap<ViewNumber, usize>,
176}
177
178#[bon]
179impl<T, S> Coordinator<T, S>
180where
181 T: NodeType,
182 S: NewProtocolStorage<T>,
183{
184 #[builder(builder_type = CoordinatorMaker, finish_fn = make)]
185 #[allow(clippy::too_many_arguments)]
186 pub fn maker(
187 membership_coordinator: EpochMembershipCoordinator<T>,
188 network: Cliquenet<T>,
189 initializer: &HotShotInitializer<T>,
190 upgrade_lock: UpgradeLock<T>,
191 public_key: T::SignatureKey,
192 private_key: <T::SignatureKey as SignatureKey>::PrivateKey,
193 state_private_key: <T::StateSignatureKey as StateSignatureKey>::StatePrivateKey,
194 stake_table_capacity: usize,
195 timeout_duration: Duration,
196 empty_block_delay: Duration,
197 block_sizes: BTreeMap<Version, u64>,
198 storage: S,
199 metrics: &dyn Metrics,
200 consensus_metrics: ConsensusMetricsValue,
201 locked_qc: Option<Certificate1<T>>,
203 boundary_qc: Option<Certificate1<T>>,
206 anchor_cert2: Option<Certificate2<T>>,
208 upgrade_config: UpgradeConfig,
209 leader_tracer: Option<LeaderTracerHandle>,
210 external_tx: mpsc::UnboundedSender<OpaqueMessage<T::SignatureKey>>,
211 ) -> Self {
212 let mut consensus = Consensus::new(
213 membership_coordinator.clone(),
214 public_key.clone(),
215 private_key.clone(),
216 state_private_key,
217 stake_table_capacity,
218 upgrade_lock.clone(),
219 initializer.anchor_leaf().clone(),
220 initializer.epoch_height(),
221 );
222 consensus.set_tracer(leader_tracer.clone());
223
224 let anchor_leaf = initializer.anchor_leaf();
225 let anchor_view = anchor_leaf.view_number();
226 let anchor_epoch = anchor_leaf
227 .epoch(initializer.epoch_height())
228 .unwrap_or(EpochNumber::genesis());
229 let cert1 = initializer.high_qc().clone();
230 let parent_proposal = message::Proposal {
231 block_header: anchor_leaf.block_header().clone(),
232 view_number: anchor_view,
233 epoch: anchor_epoch,
234 justify_qc: anchor_leaf.justify_qc().clone(),
235 next_epoch_justify_qc: None,
236 upgrade_certificate: anchor_leaf
237 .upgrade_certificate()
238 .cloned()
239 .map(|cert| UpgradeCertificate2::restore_epoch(cert, anchor_epoch)),
240 view_change_evidence: anchor_leaf
241 .view_change_evidence
242 .clone()
243 .and_then(ViewChangeEvidence2::timeout_evidence),
244 next_drb_result: anchor_leaf.next_drb_result,
245 state_cert: None,
246 };
247
248 let coordinator_metrics = metrics
249 .is_recording()
250 .then(|| metrics::Metrics::new(consensus_metrics));
251
252 let mut state_manager = StateManager::new(
253 Arc::new(initializer.instance_state().clone()),
254 upgrade_lock.clone(),
255 )
256 .with_metrics(
257 coordinator_metrics.as_ref().map(|m| {
258 m.consensus
259 .validate_and_apply_header_duration
260 .clone()
261 .into()
262 }),
263 coordinator_metrics
264 .as_ref()
265 .map(|m| m.consensus.update_leaf_duration.clone().into()),
266 );
267 for p in initializer.saved_proposals().values() {
270 state_manager.seed_from_header(message::Proposal::from(p.data.clone()));
271 }
272 state_manager.seed_state(
273 anchor_view,
274 initializer.anchor_state().clone(),
275 anchor_leaf.clone(),
276 );
277 let reconstructed_blocks =
281 std::iter::once((anchor_view, anchor_leaf.block_header().clone()))
282 .chain(
283 initializer
284 .saved_proposals()
285 .iter()
286 .map(|(view, p)| (*view, p.data.block_header().clone())),
287 )
288 .filter_map(|(view, header)| match header.payload_commitment() {
289 VidCommitment::V2(commitment) => Some((view, commitment)),
290 _ => None,
291 });
292 let saved_proposals = initializer
296 .saved_proposals()
297 .values()
298 .map(|p| SignedProposal {
299 data: message::Proposal::from(p.data.clone()),
300 signature: p.signature.clone(),
301 _pd: std::marker::PhantomData,
302 });
303 consensus.seed_signed_proposals(saved_proposals);
304 consensus.seed_parent(cert1, parent_proposal, reconstructed_blocks);
309 if let Some(locked_qc) = locked_qc {
312 consensus.seed_locked_cert(locked_qc);
313 }
314 if let Some(boundary_qc) = boundary_qc {
315 consensus.seed_boundary_cert(boundary_qc);
316 }
317 if let Some(anchor_cert2) = anchor_cert2 {
318 consensus.seed_anchor_cert2(anchor_cert2);
319 }
320 if upgrade_lock.decided_upgrade_cert().is_none()
325 && let Some(cert) = anchor_leaf.upgrade_certificate()
326 && anchor_view <= cert.data.decide_by
327 && cert.data.new_version == upgrade_lock.upgrade().target
328 {
329 info!(
330 view = %anchor_view,
331 new_version = %cert.data.new_version,
332 "restoring decided upgrade certificate from the anchor leaf"
333 );
334 upgrade_lock.set_decided_upgrade_cert(cert.clone());
335 }
336
337 consensus.resume_from_restart(
338 anchor_view,
339 initializer.start_view(),
340 initializer.last_actioned_view(),
341 );
342 if let Some(state_cert) = initializer.state_cert().cloned() {
343 consensus.seed_state_cert(state_cert);
344 }
345
346 let participation = ParticipationTracker::new(&membership_coordinator, anchor_epoch);
347
348 let mut vid_disperser = VidDisperser::new(
349 membership_coordinator.clone(),
350 network.sender().clone(),
351 public_key.clone(),
352 private_key.clone(),
353 )
354 .with_metrics(
355 coordinator_metrics
356 .as_ref()
357 .map(|m| m.consensus.vid_disperse_duration.clone().into()),
358 );
359 vid_disperser.set_tracer(leader_tracer.clone());
360 let mut vid_reconstructor = VidReconstructor::new();
361 vid_reconstructor.set_tracer(leader_tracer.clone());
362
363 let lock = upgrade_lock.clone();
364 let genesis_qc = consensus.cert1_at(ViewNumber::genesis()).cloned();
365
366 Self::builder()
367 .consensus(consensus)
368 .network(network)
369 .state_manager(state_manager)
370 .vid_disperser(vid_disperser)
371 .vid_reconstructor(vid_reconstructor)
372 .vote1_collector(VoteCollector::new(
373 membership_coordinator.clone(),
374 lock.clone(),
375 ))
376 .vote2_collector(VoteCollector::new(
377 membership_coordinator.clone(),
378 lock.clone(),
379 ))
380 .timeout_collector(VoteCollector::new(
381 membership_coordinator.clone(),
382 lock.clone(),
383 ))
384 .timeout_one_honest_collector(VoteCollector::new(
385 membership_coordinator.clone(),
386 lock.clone(),
387 ))
388 .timeout3_collector(VoteCollector::new(
389 membership_coordinator.clone(),
390 lock.clone(),
391 ))
392 .timeout_one_honest3_collector(VoteCollector::new(
393 membership_coordinator.clone(),
394 lock.clone(),
395 ))
396 .epoch_root_collector(VoteCollector::new(
397 membership_coordinator.clone(),
398 lock.clone(),
399 ))
400 .upgrade_vote_collector(VoteCollector::new(
401 membership_coordinator.clone(),
402 lock.clone(),
403 ))
404 .upgrade_protocol(UpgradeProtocol::new(
405 upgrade_config,
406 lock.clone(),
407 public_key.clone(),
408 private_key.clone(),
409 ))
410 .cert_verifiers(CertVerifiers::new(
411 membership_coordinator.clone(),
412 lock.clone(),
413 ))
414 .epoch_manager(EpochManager::new(
415 initializer.epoch_height(),
416 membership_coordinator.clone(),
417 ))
418 .block_builder(
419 BlockBuilder::new(
420 Arc::new(initializer.instance_state().clone()),
421 membership_coordinator.clone(),
422 BlockBuilderConfig {
423 empty_block_delay,
424 block_sizes,
425 ..BlockBuilderConfig::default()
426 },
427 upgrade_lock.clone(),
428 )
429 .with_metrics(
430 coordinator_metrics
431 .as_ref()
432 .map(|m| m.consensus.block_commitments_duration.clone().into()),
433 )
434 .with_tracer(leader_tracer),
435 )
436 .proposal_validator(ProposalValidator::new(
437 membership_coordinator.clone(),
438 initializer.epoch_height(),
439 upgrade_lock.clone(),
440 genesis_qc.as_ref(),
441 ))
442 .share_validator(VidShareValidator::new(
443 membership_coordinator.clone(),
444 initializer.epoch_height(),
445 upgrade_lock,
446 ))
447 .storage(Storage::new(storage, private_key).with_metrics(metrics))
448 .membership_coordinator(membership_coordinator)
449 .timer(Timer::new(timeout_duration, anchor_view))
450 .public_key(public_key)
451 .maybe_metrics(coordinator_metrics)
452 .participation(participation)
453 .external_tx(external_tx)
454 .build()
455 }
456
457 pub fn start(&mut self) {
460 let cur_view = self.consensus.current_view();
461 let next_view = cur_view + 1;
462 self.block_builder.start_at(next_view);
465 let epoch = self.consensus.current_epoch();
466
467 if self.consensus.last_decided_leaf().view_number() == ViewNumber::genesis() {
468 let genesis_leaf = self.consensus.last_decided_leaf().clone();
470 let (payload, metadata) = T::BlockPayload::empty();
471 self.storage.append_da(
472 ViewNumber::genesis(),
473 EpochNumber::genesis(),
474 payload,
475 metadata,
476 genesis_leaf.payload_commitment(),
477 );
478
479 self.outbox.push_back(ConsensusOutput::LeafDecided {
481 leaves: vec![genesis_leaf],
482 cert1: self
483 .consensus
484 .cert1_at(ViewNumber::genesis())
485 .cloned()
486 .expect("genesis cert1 must be seeded"),
487 cert2: None,
488 vid_shares: vec![None],
489 });
490 }
491
492 self.outbox
493 .push_back(ConsensusOutput::ViewChanged(next_view, epoch));
494
495 if let Some(leader) = self.leader(next_view, epoch)
496 && leader == self.public_key
497 {
498 if let Some(parent_proposal) = self.consensus.proposal_at(cur_view).cloned() {
502 self.consensus.push_block_request(
503 BlockAndHeaderRequest {
504 view: next_view,
505 epoch,
506 parent_proposal,
507 },
508 &mut self.outbox,
509 );
510 }
511 }
512 }
513
514 pub async fn stop(mut self) {
515 futures::join!(self.network.shutdown(), self.storage.flush());
516 }
517
518 pub async fn next_consensus_input(&mut self) -> Result<ConsensusInput<T>, CoordinatorError> {
519 loop {
520 select! {
521 message = self.network.receive() => match message {
522 Ok(m) => {
523 if let Some(input) = self.on_network_message(m) {
524 return Ok(input)
525 }
526 }
527 Err(e) => {
528 return Err(CoordinatorError::from(e).context("network receive"))
529 }
530 },
531 () = &mut self.timer => {
532 let view = self.timer.view();
533 let epoch = self.consensus.current_epoch();
534 self.timer.reset();
539 let stats = self.vote1_collector.stats(view);
540 if stats.is_empty() {
541 warn!(%view, %epoch, "timeout: no vote1 received for this view");
542 }
543 for (voted_epoch, stats) in stats {
544 match stats.stake {
545 Some((stake, threshold)) => warn!(
546 %view, %epoch, %voted_epoch,
547 signers = %stats.signers, %stake, %threshold,
548 "timeout: vote1 stake observed (deduped by signer)"
549 ),
550 None => warn!(
551 %view, %epoch, %voted_epoch,
552 signers = %stats.signers,
553 "timeout: vote1 received, but the epoch's stake table is gone"
554 ),
555 }
556 }
557 let input = ConsensusInput::Timeout(view);
558 if self.last_timeout_view != Some(view) {
559 self.last_timeout_view = Some(view);
560 let leader = self.leader(view, epoch);
561 if let Some(leader) = leader.clone() {
562 self.participation.leader_missed(leader, epoch);
563 }
564 if let Some(m) = &self.metrics {
565 m.consensus.number_of_timeouts.add(1);
566 if leader.as_ref() == Some(&self.public_key) {
567 m.consensus.number_of_timeouts_as_leader.add(1);
568 }
569 }
570 }
571 return Ok(input)
572 }
573 Some(output) = self.state_manager.next() => {
574 if let Some(input) = self.on_state_manager_output(output) {
575 return Ok(input)
576 }
577 }
578 Some(request) = self.client.next_request() => {
579 if let Err(err) = self.on_client_request(request) {
580 error!(%err, "error while handling client request");
581 }
582 }
583 Some(tcert) = self.timeout_collector.next() => {
584 self.cert_verifiers
585 .timeout
586 .mark_completed(tcert.view_number(), tcert.epoch());
587 return Ok(ConsensusInput::TimeoutCertificate(tcert.map(TimeoutEvidence::V2)))
588 }
589 Some(tcert) = self.timeout3_collector.next() => {
590 self.cert_verifiers
591 .timeout3
592 .mark_completed(tcert.view_number(), tcert.epoch());
593 return Ok(ConsensusInput::TimeoutCertificate(tcert.map(TimeoutEvidence::V3)))
594 }
595 Some(out) = self.timeout_one_honest_collector.next() => {
600 return Ok(ConsensusInput::TimeoutOneHonest(out.view_number()))
601 }
602 Some(out) = self.timeout_one_honest3_collector.next() => {
603 return Ok(ConsensusInput::TimeoutOneHonest(out.view_number()))
604 }
605 Some(cert1) = self.vote1_collector.next() => {
606 self.cert_verifiers
607 .cert1
608 .mark_completed(ViewEpoch(cert1.view_number(), cert1.epoch()));
609 return Ok(ConsensusInput::Certificate1(cert1))
610 }
611 Some(cert2) = self.vote2_collector.next() => {
612 self.cert_verifiers
613 .cert2
614 .mark_completed(ViewEpoch(cert2.view_number(), cert2.epoch()));
615 return Ok(ConsensusInput::Certificate2(cert2))
616 }
617 Some(cert1) = self.cert_verifiers.cert1.next() => {
618 self.vote1_collector.mark_completed(cert1.view_number(), cert1.epoch());
622 return Ok(ConsensusInput::Certificate1(cert1))
623 }
624 Some(cert2) = self.cert_verifiers.cert2.next() => {
625 self.vote2_collector.mark_completed(cert2.view_number(), cert2.epoch());
626 return Ok(ConsensusInput::Certificate2(cert2))
627 }
628 Some(tc) = self.cert_verifiers.timeout.next() => {
629 self.timeout_collector.mark_completed(tc.view_number(), tc.epoch());
630 return Ok(ConsensusInput::TimeoutCertificate(tc.map(TimeoutEvidence::V2)))
631 }
632 Some(tc) = self.cert_verifiers.timeout3.next() => {
633 self.timeout3_collector.mark_completed(tc.view_number(), tc.epoch());
634 return Ok(ConsensusInput::TimeoutCertificate(tc.map(TimeoutEvidence::V3)))
635 }
636 Some(cert1) = self.cert_verifiers.advance.next() => {
637 return Ok(ConsensusInput::AdvanceView(cert1))
638 }
639 Some(revote) = self.cert_verifiers.revote.next() => {
640 return Ok(ConsensusInput::ReVote(revote.into_cert()))
641 }
642 Some(epoch_change) = self.cert_verifiers.epoch_change.next() => {
643 let epoch_change = epoch_change.into_cert();
644 self.state_manager
649 .seed_from_header(epoch_change.proposal.clone());
650 return Ok(ConsensusInput::EpochChange(epoch_change))
651 }
652 Some(cert) = self.upgrade_vote_collector.next() => {
653 return Ok(ConsensusInput::UpgradeCertificateFormed(cert))
654 }
655 Some((cert1, state_cert)) = self.epoch_root_collector.next() => {
656 self.cert_verifiers
657 .cert1
658 .mark_completed(ViewEpoch(cert1.view_number(), cert1.epoch()));
659 self.storage
660 .append_state_cert(state_cert.view_number(), state_cert.clone());
661 return Ok(ConsensusInput::EpochRootCertificates { cert1, state_cert })
662 }
663 Some(item) = self.share_validator.next() => match item {
664 Ok(vid_share) => {
665 return Ok(ConsensusInput::VidShare(vid_share))
666 },
667 Err(e) => {
668 return Err(CoordinatorError::regular(e).context("vid share validation"))
669 }
670 },
671 Some(item) = self.proposal_validator.next() => match item {
672 Ok(validated) if validated.fetched => {
673 return Ok(ConsensusInput::FetchedProposal(validated.message))
674 }
675 Ok(validated) => {
676 let epoch = validated.message.proposal.data.epoch;
678 self.bump_network_epoch(epoch);
679 return Ok(ConsensusInput::Proposal(validated.sender, validated.message))
680 }
681 Err(e) => {
682 return Err(CoordinatorError::regular(e).context("proposal validation"))
683 }
684 },
685 Some(item) = self.block_builder.next() => match item {
686 Ok(mut block) => {
687 self.state_manager.request_header(HeaderRequest::from(&block));
688 let epoch = block.epoch;
689 let manifest = block.manifest.clone();
690 if let VidCommitment::V2(commit) = block.payload_commitment {
693 self.da_payloads.insert(
694 (block.view, commit),
695 PendingDa {
696 epoch: block.epoch,
697 payload: block.payload.payload.clone(),
698 metadata: block.payload.metadata.clone(),
699 hashes: manifest.hashes.clone(),
700 },
701 );
702 } else {
703 warn!(view = %block.view, "block payload commitment is not V2");
704 }
705 self.vid_reconstructor.retire_view(block.view);
707 let holders = self.upcoming_leaders(
711 block.view + 1,
712 self.block_builder.fanout().get(),
713 epoch,
714 );
715 for (_, leader) in holders {
716 self.unicast_in(
717 self.slot_for(block.view),
718 &leader,
719 MessageType::Block(BlockMessage::DedupManifest(manifest.clone())),
720 "leader unicast",
721 )?;
722 }
723 if let VidCommitment::V2(commit) = block.payload_commitment
726 && let Some(dispersal) = block.dispersal.take()
727 {
728 self.built_dispersals
729 .insert((block.view, epoch, commit), dispersal);
730 }
731 return Ok(block.into())
732 }
733 Err(err) => {
734 return Err(CoordinatorError::regular(err).context("block building"))
735 }
736 },
737 Some(item) = self.vid_disperser.next() => match item {
738 Ok(out) => {
739 return Ok(ConsensusInput::VidDisperseCreated(out.view, out.payload_commitment))
740 }
741 Err(err) => {
742 return Err(CoordinatorError::from(err).context("vid disperse"))
743 }
744 },
745 Some(item) = self.vid_reconstructor.next() => match item {
746 Ok(out) => {
747 return Ok(self.on_payload_obtained(out))
748 }
749 Err(err) => {
750 return Err(CoordinatorError::regular(err).context("vid reconstruction"))
751 }
752 },
753 Some(out) = self.fetcher.next() => {
754 if self.consensus.is_reconstructed(out.view, out.payload_commitment) {
755 continue
756 }
757 self.vid_reconstructor.retire_view(out.view);
758 return Ok(self.on_payload_obtained(out))
759 },
760 Some(stored) = self.storage.next() => {
761 return Ok(ConsensusInput::Stored(stored))
762 },
763 Some(result) = self.epoch_manager.next() => match result {
764 Ok(EpochRootResult::DrbResult(epoch, drb_result)) => {
765 self.vote1_collector.retry_pending_votes();
766 self.vote2_collector.retry_pending_votes();
767 self.timeout_collector.retry_pending_votes();
768 self.timeout_one_honest_collector.retry_pending_votes();
769 self.timeout3_collector.retry_pending_votes();
770 self.timeout_one_honest3_collector.retry_pending_votes();
771 self.epoch_root_collector.retry_pending_votes();
772 self.upgrade_vote_collector.retry_pending_votes();
773 self.cert_verifiers.retry_pending(|e| self.epoch_manager.request_drb_result(e));
774 return Ok(ConsensusInput::DrbResult(epoch, drb_result))
775 }
776 Err(failure) => {
777 warn!(err = %failure.error, epoch = %failure.epoch, "DRB request failed");
782 continue;
783 }
784 },
785 else => {
786 return Err(CoordinatorError::critical(ErrorSource::NoInput))
787 }
788 }
789 }
790 }
791
792 fn on_payload_obtained(&mut self, out: ObtainedPayload<T>) -> ConsensusInput<T> {
793 self.server
794 .retain(out.view, out.payload_commitment, &out.payload);
795 self.payload_txn_bytes
796 .insert(out.view, out.payload.txn_bytes());
797 self.block_builder.on_block_reconstructed(
798 out.view,
799 out.payload_commitment,
800 out.tx_commitments,
801 );
802 self.storage.append_da(
803 out.view,
804 out.epoch,
805 out.payload.clone(),
806 out.metadata.clone(),
807 VidCommitment::V2(out.payload_commitment),
808 );
809 if let Some(proposal) = self
811 .consensus
812 .proposal_with_payload(out.view, out.payload_commitment)
813 {
814 self.outbox
815 .push_back(ConsensusOutput::BlockPayloadReconstructed {
816 view: out.view,
817 header: proposal.block_header.clone(),
818 payload: Arc::new(out.payload.clone()),
819 });
820 } else if self.consensus.proposal_at(out.view).is_some() {
821 warn!(
822 view = %out.view,
823 obtained = %out.payload_commitment,
824 "payload commitment does not match a proposal header"
825 );
826 }
827 ConsensusInput::BlockReconstructed {
828 view: out.view,
829 payload_commitment: out.payload_commitment,
830 payload: out.payload,
831 }
832 }
833
834 pub fn apply_consensus(&mut self, input: ConsensusInput<T>) {
835 self.consensus.apply(input, &mut self.outbox)
836 }
837
838 pub fn process_consensus_output(
839 &mut self,
840 output: ConsensusOutput<T>,
841 ) -> Result<(), CoordinatorError> {
842 let node = self.node_id;
843 match output {
844 ConsensusOutput::RequestState(state_request) => {
845 debug!(
846 %node,
847 view = %state_request.view,
848 epoch = %state_request.epoch,
849 block = %state_request.block,
850 "request state validation"
851 );
852 self.state_manager.request_state(state_request);
853 },
854 ConsensusOutput::RequestVidDisperse {
855 view,
856 epoch,
857 payload,
858 metadata,
859 payload_commitment,
860 } => {
861 debug!(%node, %view, %epoch, "request vid disperse");
862 let built = self
863 .built_dispersals
864 .remove(&(view, epoch, payload_commitment));
865 self.vid_disperser.request_vid_disperse(
866 VidDisperseRequest {
867 view,
868 epoch,
869 block: payload,
870 metadata,
871 payload_commitment,
872 },
873 built,
874 );
875 },
876 ConsensusOutput::RequestDrbResult(epoch) => {
877 debug!(%node, %epoch, "request drb result");
878 self.epoch_manager.request_drb_result(epoch);
879 },
880 ConsensusOutput::LeafDecided { leaves, cert1, .. } => {
881 info!(
882 %node,
883 view = %cert1.view_number(),
884 epoch = ?cert1.epoch().map(|e| *e),
885 leaves = leaves.len(),
886 "leaves decided"
887 );
888 self.on_decide_metrics(&leaves);
889 self.block_builder
890 .on_blocks_decided(leaves.iter().filter_map(|leaf| {
891 match leaf.block_header().payload_commitment() {
893 VidCommitment::V2(commit) => Some((leaf.view_number(), commit)),
894 VidCommitment::V0(_) | VidCommitment::V1(_) => None,
895 }
896 }));
897 if let Some(newest) = leaves.first() {
900 let gc_view = newest.view_number();
901 let gc_epoch = newest.justify_qc().epoch().unwrap_or_default();
902 self.gc(gc_epoch, GcScope::Decided(gc_view))?;
903 if gc_view == self.consensus.last_decided_view() {
905 self.state_manager.seed_decided(newest.clone());
906 }
907 }
908 for leaf in leaves.into_iter().rev() {
909 self.participation
910 .on_leaf_decided(&leaf, &self.membership_coordinator);
911 self.epoch_manager.handle_leaf_decided(leaf);
912 }
913 },
914 ConsensusOutput::LockUpdated(view) => {
915 debug!(%node, %view, "lock updated");
916 self.server.lock_moved(view);
917 },
918 ConsensusOutput::RequestMissingProposal { view, leaf_commit } => {
919 debug!(%node, %view, "request missing proposal");
920 if let Err(err) = self.request_missing_proposal(view, leaf_commit) {
921 warn!(%node, %view, %err, "failed to request missing proposal");
922 }
923 },
924 ConsensusOutput::RequestMissingPayload {
925 view,
926 payload_commitment,
927 } => {
928 debug!(%node, %view, "request missing payload");
929 if let Some((peer, message)) = self.fetcher.request(
930 view,
931 payload_commitment,
932 Retry::NewRound,
933 &self.consensus,
934 &self.membership_coordinator,
935 ) {
936 self.unicast_in(
937 self.slot_for(view),
938 &peer,
939 message.message_type,
940 "payload request",
941 )?;
942 }
943 },
944 ConsensusOutput::RequestBlockAndHeader(request) => {
945 debug!(
946 %node,
947 view = %request.view,
948 epoch = %request.epoch,
949 "request block and header"
950 );
951 self.block_builder.request_block(request);
952 },
953 ConsensusOutput::RecordAction(view, kind) => {
954 debug!(%node, %view, ?kind, "record action");
955 self.storage.record_action(view, kind);
956 },
957 ConsensusOutput::PersistProposal(proposal) => {
958 let view = proposal.data.view_number;
959 debug!(%node, %view, "persist proposal");
960 self.storage.append_proposal(proposal.data.clone());
961 if let VidCommitment::V2(commit) = proposal.data.block_header.payload_commitment() {
964 if let Some(da) = self.da_payloads.remove(&(view, commit)) {
965 self.payload_txn_bytes.insert(view, da.payload.txn_bytes());
966 if let Some(m) = &self.metrics
967 && da.payload.transactions(&da.metadata).next().is_none()
968 {
969 m.consensus.number_of_empty_blocks_proposed.add(1);
970 }
971 self.block_builder
975 .on_block_reconstructed(view, commit, da.hashes);
976 self.storage.append_da(
977 view,
978 da.epoch,
979 da.payload,
980 da.metadata,
981 VidCommitment::V2(commit),
982 );
983 } else {
984 warn!(%node, %view, "no payload for proposed block");
985 }
986 }
987 },
988 ConsensusOutput::ProposalPaired {
989 proposal,
990 vid_share,
991 } => {
992 let view = proposal.data.view_number;
993 debug!(%node, %view, "proposal paired with vid share");
994 self.storage.append_vid(vid_share.clone());
995 self.storage.append_proposal(proposal.data.clone());
996 if let Some(state_cert) =
1003 validated_state_cert(&proposal.data, *self.consensus.epoch_height)
1004 {
1005 self.storage
1006 .append_state_cert(state_cert.view_number(), state_cert.clone());
1007 }
1008 self.notify_vid_reconstructor(&proposal.data, PinnedBy::Leader);
1009 self.vid_reconstructor
1010 .handle_vid_share(self.public_key.clone(), vid_share);
1011 },
1012 ConsensusOutput::SendProposal(proposal) => {
1013 let view = proposal.data.view_number;
1014 let epoch = proposal.data.epoch;
1015 let block = proposal.data.block_header.block_number();
1016 info!(%node, %view, %epoch, %block, "send proposal");
1017 if let Some(m) = &self.metrics
1018 && proposal.data.view_change_evidence.is_none()
1019 && let Some((prev_view, received_at)) = self.proposal_received_at
1020 && (*prev_view).checked_add(1) == Some(*view)
1021 {
1022 m.consensus
1023 .previous_proposal_to_proposal_time
1024 .add_point(received_at.elapsed().as_millis() as f64);
1025 }
1026 crate::trace_leader_event!(
1027 self.consensus.tracer,
1028 view,
1029 crate::leader_trace::LeaderEvent::ProposalBroadcastStart
1030 );
1031 if let Err(err) = self.broadcast_in(
1032 self.slot_for(view),
1033 ConsensusMessage::Proposal(ProposalMessage::validated(proposal.clone())),
1034 "proposal broadcast",
1035 ) {
1036 if err.severity == Severity::Critical {
1037 return Err(err);
1038 } else {
1039 warn!(%node, %err, "network error while broadcasting proposal")
1040 }
1041 }
1042 crate::trace_leader_event!(
1043 self.consensus.tracer,
1044 view,
1045 crate::leader_trace::LeaderEvent::ProposalBroadcastEnd
1046 );
1047 },
1048 ConsensusOutput::SendTimeoutVote(vote, evidence) => {
1049 let view = vote.view_number();
1050 debug!(
1051 %node, %view,
1052 has_evidence = evidence.is_some(),
1053 "send timeout vote"
1054 );
1055 let message = match vote {
1056 TimeoutVote::V2(vote) => {
1057 ConsensusMessage::TimeoutVote(message::TimeoutVoteMessage {
1058 vote,
1059 evidence,
1060 })
1061 },
1062 TimeoutVote::V3(ballot) => ConsensusMessage::TimeoutVote3(
1063 message::TimeoutVoteMessage3::new(ballot, evidence),
1064 ),
1065 };
1066 self.broadcast_in(self.slot_for(view), message, "broadcast timeout vote")?
1067 },
1068 ConsensusOutput::SendReVote(revote) => {
1069 info!(
1070 %node,
1071 view = %revote.revote.view,
1072 epoch = %revote.revote.epoch,
1073 block_view = %revote.revote.cert1.view_number(),
1074 "send re-vote request"
1075 );
1076 self.broadcast_in(
1077 self.slot_for(revote.revote.view),
1078 ConsensusMessage::ReVote(revote),
1079 "broadcast re-vote request",
1080 )?
1081 },
1082 ConsensusOutput::SendTimeoutCertificate(tc, view, epoch) => {
1083 debug!(
1084 %node, %view, %epoch,
1085 cert_view = %tc.view_number(),
1086 "send timeout certificate"
1087 );
1088 let consensus_message = match tc {
1089 TimeoutEvidence::V2(cert) => ConsensusMessage::TimeoutCertificate(cert),
1090 TimeoutEvidence::V3(cert) => ConsensusMessage::TimeoutCertificate3(cert),
1091 };
1092 self.broadcast_in(
1096 self.slot_for(view),
1097 consensus_message,
1098 "broadcast timeout certificate",
1099 )?
1100 },
1101 ConsensusOutput::SendVote1(vote1) => {
1102 let view = vote1.vote.view_number();
1103 debug!(
1104 %node, %view,
1105 epoch_root = vote1.state_vote.is_some(),
1106 "send vote1"
1107 );
1108 self.record_voted_view(view);
1109 if let Some(epoch) = vote1.vote.data.epoch
1110 && let Some(leader) = self.leader(view, epoch)
1111 {
1112 self.participation.leader_proposed(leader, epoch);
1113 }
1114 crate::trace_leader_event!(
1115 self.consensus.tracer,
1116 view,
1117 crate::leader_trace::LeaderEvent::Vote1VMinus1BroadcastStart
1118 );
1119 let r = self.broadcast_in(
1120 self.slot_for(view),
1121 ConsensusMessage::Vote1(vote1),
1122 "broadcast vote1",
1123 );
1124 crate::trace_leader_event!(
1125 self.consensus.tracer,
1126 view,
1127 crate::leader_trace::LeaderEvent::Vote1VMinus1BroadcastEnd
1128 );
1129 r?
1130 },
1131 ConsensusOutput::BroadcastVidShare(share) => {
1132 let view = share.view_number();
1133 debug!(%node, %view, "send vid share");
1134 self.broadcast_in(
1135 self.slot_for(view),
1136 ConsensusMessage::VidShareBroadcast(share),
1137 "broadcast vid share",
1138 )?
1139 },
1140 ConsensusOutput::SendVote2(vote2) => {
1141 let view = vote2.view_number();
1142 debug!(%node, %view, "send vote2");
1143 self.record_voted_view(view);
1144 crate::trace_leader_event!(
1145 self.consensus.tracer,
1146 view,
1147 crate::leader_trace::LeaderEvent::Vote2VMinus1BroadcastStart
1148 );
1149 let r = self.broadcast_in(
1150 self.slot_for(view),
1151 ConsensusMessage::Vote2(vote2),
1152 "broadcast vote2",
1153 );
1154 crate::trace_leader_event!(
1155 self.consensus.tracer,
1156 view,
1157 crate::leader_trace::LeaderEvent::Vote2VMinus1BroadcastEnd
1158 );
1159 r?
1160 },
1161 ConsensusOutput::PersistBoundaryQc(qc) => {
1162 debug!(%node, view = %qc.view_number(), "persist boundary qc");
1163 self.storage.append_boundary_qc2(qc);
1164 },
1165 ConsensusOutput::PersistHighQc(high_qc) => {
1166 debug!(%node, view = %high_qc.view_number(), "persist high qc");
1167 self.storage.append_high_qc2(high_qc);
1168 },
1169 ConsensusOutput::SendEpochChange(epoch_change) => {
1170 let view = epoch_change.cert1.view_number();
1171 info!(
1172 %node,
1173 %view,
1174 epoch = ?epoch_change.cert1.epoch().map(|e| *e),
1175 "send epoch change"
1176 );
1177 self.state_manager
1181 .seed_from_header(epoch_change.proposal.clone());
1182 self.broadcast_in(
1183 self.slot_for(view),
1184 ConsensusMessage::EpochChange(epoch_change),
1185 "broadcast epoch change",
1186 )?
1187 },
1188 ConsensusOutput::SendCertificate1(cert1) => {
1189 let view = cert1.view_number();
1190 debug!(
1191 %node,
1192 %view,
1193 epoch = ?cert1.epoch().map(|e| *e),
1194 "send certificate1"
1195 );
1196 crate::trace_leader_event!(
1197 self.consensus.tracer,
1198 view,
1199 crate::leader_trace::LeaderEvent::Cert1VMinus1BroadcastStart
1200 );
1201 let r = self.broadcast_in(
1202 self.slot_for(view),
1203 ConsensusMessage::Certificate1(cert1, self.public_key.clone()),
1204 "broadcast certificate1",
1205 );
1206 crate::trace_leader_event!(
1207 self.consensus.tracer,
1208 view,
1209 crate::leader_trace::LeaderEvent::Cert1VMinus1BroadcastEnd
1210 );
1211 r?
1212 },
1213 ConsensusOutput::SendCertificate2(cert2) => {
1214 let view = cert2.view_number();
1215 debug!(
1216 %node,
1217 %view,
1218 epoch = ?cert2.epoch().map(|e| *e),
1219 "send certificate2"
1220 );
1221 self.broadcast_in(
1222 self.slot_for(view),
1223 ConsensusMessage::Certificate2(cert2, self.public_key.clone()),
1224 "broadcast certificate2",
1225 )?
1226 },
1227 ConsensusOutput::ProposalValidated { proposal, sender } => {
1228 debug!(
1229 %node,
1230 view = %proposal.data.view_number,
1231 sender = %KeyPrefix::from(&sender),
1232 "proposal validated"
1233 );
1234 },
1235 ConsensusOutput::ViewChanged(view, epoch) => {
1236 let current_view = self.consensus.current_view();
1237 if view < current_view {
1238 warn!(
1239 %node, %view, %epoch, %current_view,
1240 "ignoring view change to stale view"
1241 );
1242 return Ok(());
1243 }
1244 info!(%node, %view, %epoch, "view changed");
1245 self.timer.reset_with(view);
1246 self.gc(epoch, GcScope::Local(view))?;
1247 let resend = self.block_builder.on_view_changed(view);
1248 self.participation.on_view_changed(epoch);
1249 self.on_view_changed_metrics(view, epoch);
1250 if let Some(resend) = resend {
1251 self.send_transactions(resend, epoch)?;
1252 }
1253
1254 let next_epoch = epoch + 1;
1257 if next_epoch > EpochNumber::genesis() + 1 {
1258 self.epoch_manager.request_drb_result(next_epoch);
1259 }
1260
1261 if self.leader(view, epoch).as_ref() == Some(&self.public_key)
1262 && let Some(upgrade_proposal) = self.upgrade_protocol.maybe_propose(view, epoch)
1263 {
1264 self.broadcast_in(
1265 self.slot_for(view),
1266 ConsensusMessage::UpgradeProposal(upgrade_proposal),
1267 "broadcast upgrade proposal",
1268 )?;
1269 }
1270 },
1271 ConsensusOutput::ViewTimedOut(view) => {
1272 debug!(%node, %view, "view timed out");
1273 self.server.view_timed_out(view);
1274 let epoch = self.consensus.current_epoch();
1275 self.gc(epoch, GcScope::Timeout(view))?;
1276 },
1277 ConsensusOutput::ProposalCertified(proposal) => {
1278 debug!(
1282 %node, view = %proposal.view_number,
1283 "pinning reconstructor from certified proposal"
1284 );
1285 self.notify_vid_reconstructor(&proposal, PinnedBy::Certificate);
1286 },
1287 ConsensusOutput::BlockPayloadReconstructed { .. } => {},
1288 ConsensusOutput::UpgradeDecided(cert) => {
1289 info!(
1290 %node,
1291 view = %cert.view_number(),
1292 new_version = %cert.data.new_version,
1293 first_view = %cert.data.new_version_first_view,
1294 "persisting decided upgrade certificate"
1295 );
1296 self.storage.update_decided_upgrade_certificate(cert);
1297 },
1298 }
1299 Ok(())
1300 }
1301
1302 pub fn node_id(&self) -> &KeyPrefix {
1303 &self.node_id
1304 }
1305
1306 pub fn outbox(&self) -> &Outbox<ConsensusOutput<T>> {
1307 &self.outbox
1308 }
1309
1310 pub fn outbox_mut(&mut self) -> &mut Outbox<ConsensusOutput<T>> {
1311 &mut self.outbox
1312 }
1313
1314 pub fn current_view(&self) -> ViewNumber {
1315 self.consensus.current_view()
1316 }
1317
1318 pub fn last_decided_leaf(&self) -> &Leaf2<T> {
1319 self.consensus.last_decided_leaf()
1320 }
1321
1322 pub fn state(&self, v: ViewNumber) -> Option<&StateEntry<T>> {
1323 self.state_manager.get_state(v)
1324 }
1325
1326 pub fn client_api(&self) -> &ClientApi<T> {
1327 self.client.handle()
1328 }
1329
1330 #[cfg(test)]
1331 pub(crate) fn consensus(&self) -> &Consensus<T> {
1332 &self.consensus
1333 }
1334
1335 #[cfg(test)]
1336 pub(crate) fn consensus_mut(&mut self) -> &mut Consensus<T> {
1337 &mut self.consensus
1338 }
1339
1340 #[cfg(test)]
1341 pub(crate) fn holds_built_dispersal(
1342 &self,
1343 view: ViewNumber,
1344 epoch: EpochNumber,
1345 commit: VidCommitment2,
1346 ) -> bool {
1347 self.built_dispersals.contains_key(&(view, epoch, commit))
1348 }
1349
1350 #[cfg(test)]
1351 pub(crate) fn block_builder_mut(&mut self) -> &mut BlockBuilder<T> {
1352 &mut self.block_builder
1353 }
1354
1355 #[cfg(test)]
1356 pub(crate) fn network(&self) -> &Cliquenet<T> {
1357 &self.network
1358 }
1359
1360 #[cfg(test)]
1361 pub(crate) fn network_mut(&mut self) -> &mut Cliquenet<T> {
1362 &mut self.network
1363 }
1364
1365 pub fn bump_network_epoch(&mut self, epoch: EpochNumber) {
1371 if let Err(err) = self
1372 .network
1373 .apply_epoch(epoch, &self.membership_coordinator)
1374 {
1375 error!(%epoch, %err, "network apply_epoch failed");
1376 }
1377 }
1378
1379 pub(crate) fn on_network_message(
1380 &mut self,
1381 message: Message<T, Unchecked>,
1382 ) -> Option<ConsensusInput<T>> {
1383 let sender = KeyPrefix::from(&message.sender);
1384 let node = self.node_id;
1385 match message.message_type {
1386 MessageType::Consensus(msg) => match msg {
1387 ConsensusMessage::Proposal(p) => {
1388 let view = p.view_number();
1389 let epoch = p.proposal.data.epoch;
1390 let block = p.proposal.data.block_header.block_number();
1391 debug!(%node, %sender, %view, %epoch, %block, "recv proposal");
1392 if self.is_epoch_too_far_ahead(Some(epoch)) {
1393 warn!(%node, %sender, %view, %epoch, "proposal epoch is too far ahead");
1394 return None;
1395 }
1396 if !self.is_view_too_far_ahead(view)
1397 && self.proposal_received_at.is_none_or(|(v, _)| v < view)
1398 {
1399 self.proposal_received_at = Some((view, Instant::now()));
1400 }
1401 if self.consensus.wants_proposal_for_view(&view, epoch) {
1402 self.proposal_validator.validate(p);
1403 }
1404 None
1405 },
1406 ConsensusMessage::VidShareFragment(fragment) => {
1407 let view = fragment.data.view_number();
1408 debug!(%node, %sender, %view, "received vid share fragment");
1409 if self.is_view_too_far_ahead(view) {
1410 warn!(%node, %sender, %view, "vid share fragment is too far ahead");
1411 return None;
1412 }
1413 if fragment.data.recipient_key != self.public_key {
1414 warn!(
1415 %node,
1416 %sender,
1417 %view,
1418 "ignoring vid share fragment not addressed to this node"
1419 );
1420 return None;
1421 }
1422 let Some(epoch) = fragment.data.epoch else {
1423 warn!(%node, %sender, %view, "ignoring vid share fragment without an epoch");
1424 return None;
1425 };
1426 if !self.is_fragment_epoch_admissible(epoch) {
1427 warn!(
1428 %node,
1429 %sender,
1430 %view,
1431 %epoch,
1432 "vid share fragment epoch is outside the admissible window"
1433 );
1434 return None;
1435 }
1436 if self.leader(view, epoch).as_ref() != Some(&message.sender) {
1437 warn!(
1438 %node,
1439 %sender,
1440 %view,
1441 %epoch,
1442 "ignoring vid share fragment not from the view leader"
1443 );
1444 return None;
1445 }
1446 if self.consensus.wants_proposal_for_view(&view, epoch) {
1447 let signature = fragment.signature.clone();
1448 match self
1449 .vid_fragment_accumulator
1450 .accept(&message.sender, fragment.data)
1451 {
1452 Ok(Some(share)) => self
1453 .share_validator
1454 .validate(SignedProposal::new(share, signature)),
1455 Ok(None) => {}, Err(err) => {
1457 warn!(
1458 %node,
1459 %sender,
1460 %view,
1461 %err, "rejecting malformed vid share fragment"
1462 );
1463 },
1464 }
1465 }
1466 None
1467 },
1468 ConsensusMessage::Vote1(vote1) => {
1469 let view = vote1.vote.view_number();
1470 if self.is_view_too_far_ahead(view) {
1471 warn!(%node, %sender, %view, "vote1 is too far ahead");
1472 return None;
1473 }
1474 if !self.is_vote_epoch_admissible(vote1.vote.epoch()) {
1475 warn!(
1476 %node,
1477 %sender,
1478 %view,
1479 epoch = ?vote1.vote.epoch(),
1480 "vote1 epoch is out of range"
1481 );
1482 return None;
1483 }
1484 if vote1.vote.signing_key() != message.sender {
1485 warn!(%node, %sender, %view, "vote1 signing key != sender");
1486 return None;
1487 }
1488 if !vote1.vote.data.is_well_formed(*self.consensus.epoch_height) {
1489 warn!(%node, %sender, %view, "vote1 is not well formed");
1490 return None;
1491 }
1492 crate::trace_leader_event!(
1493 self.consensus.tracer,
1494 view,
1495 crate::leader_trace::LeaderEvent::Vote1VMinus1Arrived
1496 );
1497 let bn = vote1.vote.data.block_number.unwrap_or(0);
1498 let epoch_height = *self.consensus.epoch_height;
1499 let is_epoch_root_vote = is_epoch_root(bn, epoch_height);
1500 debug!(
1501 %node, %sender, %view,
1502 epoch_root = is_epoch_root_vote,
1503 has_state_vote = vote1.state_vote.is_some(),
1504 "recv vote1"
1505 );
1506 if is_epoch_root_vote {
1507 vote1.state_vote.as_ref()?;
1510 self.epoch_root_collector.accumulate_vote(vote1);
1511 } else {
1512 self.vote1_collector.accumulate_vote(vote1.vote);
1513 }
1514 None
1515 },
1516 ConsensusMessage::VidShareBroadcast(share) => {
1517 let view = share.view_number();
1518 if self.is_view_too_far_ahead(view) {
1519 warn!(%node, %sender, %view, "vid share broadcast is too far ahead");
1520 return None;
1521 }
1522 debug!(%node, %sender, %view, "recv vid share broadcast");
1523 self.vid_reconstructor
1527 .handle_vid_share(message.sender.clone(), share);
1528 None
1529 },
1530 ConsensusMessage::Vote2(vote2) => {
1531 let view = vote2.view_number();
1532 if self.is_view_too_far_ahead(view) {
1533 warn!(%node, %sender, %view, "vote2 is too far ahead");
1534 return None;
1535 }
1536 if !self.is_vote_epoch_admissible(vote2.epoch()) {
1537 warn!(
1538 %node,
1539 %sender,
1540 %view,
1541 epoch = ?vote2.epoch(),
1542 "vote2 epoch is out of range"
1543 );
1544 return None;
1545 }
1546 if vote2.signing_key() != message.sender {
1547 warn!(%node, %sender, %view, "vote2 signing key != sender");
1548 return None;
1549 }
1550 if !vote2.data.is_well_formed(*self.consensus.epoch_height) {
1551 warn!(%node, %sender, %view, "vote2 is not well formed");
1552 return None;
1553 }
1554 debug!(%node, %sender, %view, "recv vote2");
1555 self.vote2_collector.accumulate_vote(vote2);
1556 None
1557 },
1558 ConsensusMessage::Certificate1(certificate1, _key) => {
1559 let view = certificate1.view_number();
1560 debug!(
1561 %node, %sender, %view,
1562 epoch = ?certificate1.epoch().map(|e| *e),
1563 "recv certificate1"
1564 );
1565 if self.is_view_too_far_ahead(view) {
1566 warn!(%node, %sender, %view, "certificate1 is too far ahead");
1567 return None;
1568 }
1569 if self.is_epoch_too_far_ahead(certificate1.epoch()) {
1570 warn!(%node, %sender, %view, "certificate1 epoch is too far ahead");
1571 return None;
1572 }
1573 if view == ViewNumber::genesis() {
1574 warn!(%node, %sender, "certificate1 at the genesis view");
1575 return None;
1576 }
1577 if let Some(epoch) = self
1578 .cert_verifiers
1579 .cert1
1580 .verify(message.sender, certificate1)
1581 {
1582 self.epoch_manager.request_drb_result(epoch);
1583 }
1584 None
1585 },
1586 ConsensusMessage::Certificate2(certificate2, _key) => {
1587 let view = certificate2.view_number();
1588 debug!(
1589 %node, %sender, %view,
1590 epoch = ?certificate2.epoch().map(|e| *e),
1591 "recv certificate2"
1592 );
1593 if self.is_view_too_far_ahead(view) {
1594 warn!(%node, %sender, %view, "certificate2 is too far ahead");
1595 return None;
1596 }
1597 if self.is_epoch_too_far_ahead(certificate2.epoch()) {
1598 warn!(%node, %sender, %view, "certificate2 epoch is too far ahead");
1599 return None;
1600 }
1601 if let Some(epoch) = self
1602 .cert_verifiers
1603 .cert2
1604 .verify(message.sender, certificate2)
1605 {
1606 self.epoch_manager.request_drb_result(epoch);
1607 }
1608 None
1609 },
1610 ConsensusMessage::TimeoutVote(timeout_msg) => {
1611 self.on_timeout_vote(
1612 &message.sender,
1613 TimeoutVote::V2(timeout_msg.vote),
1614 timeout_msg.evidence,
1615 );
1616 None
1617 },
1618 ConsensusMessage::TimeoutVote3(timeout_msg) => {
1619 match timeout_msg.ballot() {
1620 Ok(ballot) => self.on_timeout_vote(
1621 &message.sender,
1622 TimeoutVote::V3(ballot),
1623 timeout_msg.evidence(),
1624 ),
1625 Err(reason) => warn!(
1626 %node, %sender, view = %timeout_msg.view, %reason,
1627 "malformed timeout vote"
1628 ),
1629 }
1630 None
1631 },
1632 ConsensusMessage::ReVote(revote) => {
1633 let view = revote.view_number();
1634 let epoch = revote.revote.epoch;
1635 debug!(%node, %sender, %view, %epoch, "recv re-vote request");
1636 if self.is_view_too_far_ahead(view) {
1637 warn!(%node, %sender, %view, "re-vote is too far ahead");
1638 return None;
1639 }
1640 if !self.is_vote_epoch_admissible(Some(epoch)) {
1641 warn!(%node, %sender, %view, %epoch, "re-vote epoch is out of range");
1642 return None;
1643 }
1644 if let Err(err) = revote.revote.well_formed(*self.consensus.epoch_height) {
1646 warn!(%node, %sender, %view, %err, "malformed re-vote request");
1647 return None;
1648 }
1649 if let Some(epoch) = self.cert_verifiers.revote.verify(message.sender, revote) {
1650 self.epoch_manager.request_drb_result(epoch);
1651 }
1652 None
1653 },
1654 ConsensusMessage::TimeoutCertificate(tc) => {
1655 debug!(
1656 %node, %sender,
1657 view = %tc.view_number(),
1658 epoch = ?tc.data.epoch.map(|e| *e),
1659 "recv timeout certificate"
1660 );
1661 if let Some(epoch) = self.verify_timeout_cert(&message.sender, tc) {
1662 self.epoch_manager.request_drb_result(epoch);
1663 }
1664 None
1665 },
1666 ConsensusMessage::TimeoutCertificate3(tc) => {
1667 debug!(
1668 %node, %sender,
1669 view = %tc.view_number(),
1670 epoch = %tc.epoch,
1671 "recv timeout certificate"
1672 );
1673 if let Some(epoch) = self.verify_timeout_cert3(&message.sender, tc) {
1674 self.epoch_manager.request_drb_result(epoch);
1675 }
1676 None
1677 },
1678 ConsensusMessage::HighQc(qc) => {
1679 debug!(
1680 %node, %sender,
1681 view = %qc.view_number(),
1682 epoch = ?qc.epoch().map(|e| *e),
1683 "recv high qc"
1684 );
1685 if let Some(epoch) = self.verify_advance(&message.sender, qc) {
1686 self.epoch_manager.request_drb_result(epoch);
1687 }
1688 None
1689 },
1690 ConsensusMessage::EpochChange(epoch_change) => {
1691 let view = epoch_change.cert1.view_number();
1692 let epoch = epoch_change.cert1.epoch();
1693 debug!(%node, %sender, %view, epoch = ?epoch.map(|e| *e), "recv epoch change");
1694 if !epoch
1695 .is_some_and(|e| is_epoch_admissible(e, self.consensus.current_epoch()))
1696 {
1697 warn!(%node, %sender, %view, ?epoch, "epoch change is out of range");
1698 return None;
1699 }
1700 if let Some(epoch) = self
1701 .cert_verifiers
1702 .epoch_change
1703 .verify(message.sender, epoch_change)
1704 {
1705 self.epoch_manager.request_drb_result(epoch);
1706 }
1707 None
1708 },
1709 ConsensusMessage::UpgradeProposal(upgrade_proposal) => {
1710 let view = upgrade_proposal.data.view_number();
1711 debug!(%node, %sender, %view, "recv upgrade proposal");
1712 if self.is_view_too_far_ahead(view) {
1713 warn!(%node, %sender, %view, "upgrade proposal is too far ahead");
1714 return None;
1715 }
1716 let current_view = self.consensus.current_view();
1717 let current_epoch = self.consensus.current_epoch();
1718 let leader = self.leader(view, current_epoch);
1719 if let Some(vote) = self.upgrade_protocol.maybe_vote(
1720 &upgrade_proposal,
1721 &message.sender,
1722 leader.as_ref(),
1723 current_view,
1724 current_epoch,
1725 ) && let Err(err) = self.broadcast_in(
1726 self.slot_for(view),
1727 ConsensusMessage::UpgradeVote(vote),
1728 "upgrade vote",
1729 ) {
1730 warn!(%node, %err, "network error while broadcasting upgrade vote");
1731 }
1732 None
1733 },
1734 ConsensusMessage::UpgradeVote(vote) => {
1735 let view = vote.view_number();
1736 if self.is_view_too_far_ahead(view) {
1737 warn!(%node, %sender, %view, "upgrade vote is too far ahead");
1738 return None;
1739 }
1740 if !self.is_vote_epoch_admissible(vote.epoch()) {
1741 warn!(
1742 %node,
1743 %sender,
1744 %view,
1745 epoch = ?vote.epoch(),
1746 "upgrade vote epoch is out of range"
1747 );
1748 return None;
1749 }
1750 if vote.signing_key() != message.sender {
1751 warn!(%node, %sender, %view, "upgrade vote signing key != sender");
1752 return None;
1753 }
1754 debug!(%node, %sender, %view, "recv upgrade vote");
1755 self.upgrade_vote_collector.accumulate_vote(vote);
1756 None
1757 },
1758 },
1759 MessageType::Block(msg) => {
1760 match msg {
1761 BlockMessage::Transactions(msg) => {
1762 debug!(
1763 %node, %sender,
1764 view = %msg.view,
1765 count = msg.transactions.len(),
1766 "recv transactions"
1767 );
1768 if !self.is_view_too_far_ahead(msg.view) {
1769 self.block_builder.on_transactions(msg)
1770 }
1771 },
1772 BlockMessage::DedupManifest(manifest) => {
1773 debug!(
1774 %node, %sender,
1775 view = %manifest.view,
1776 epoch = %manifest.epoch,
1777 hashes = manifest.hashes.len(),
1778 "recv dedup manifest"
1779 );
1780 if !self.is_view_too_far_ahead(manifest.view)
1781 && let Some(view_leader) = self.leader(manifest.view, manifest.epoch)
1782 && view_leader == message.sender
1783 {
1784 self.block_builder.on_dedup_manifest(manifest)
1785 }
1786 },
1787 }
1788 None
1789 },
1790 MessageType::ProposalFetch(ProposalFetchMessage::Request(request)) => {
1791 let view = request.view_number();
1792 debug!(%node, %sender, %view, "recv proposal fetch request");
1793 if !request.validate_sender(&message.sender) {
1794 warn!(
1795 %node,
1796 sender = %message.sender,
1797 %view,
1798 "ignoring invalid proposal fetch request signature"
1799 );
1800 return None;
1801 }
1802 let proposals: Vec<_> = self
1803 .consensus
1804 .signed_proposals_for_fetch(&view)
1805 .into_iter()
1806 .cloned()
1807 .collect();
1808 for proposal in proposals {
1809 if let Err(err) = self.unicast_in(
1810 self.slot_for(view),
1811 &message.sender,
1812 MessageType::ProposalFetch(ProposalFetchMessage::Response(Box::new(
1813 proposal,
1814 ))),
1815 "proposal response",
1816 ) {
1817 warn!(%node, %err, "network error while sending proposal response");
1818 }
1819 }
1820 None
1821 },
1822 MessageType::ProposalFetch(ProposalFetchMessage::Response(proposal)) => {
1823 debug!(
1824 %node, %sender,
1825 view = %proposal.data.view_number,
1826 "recv proposal fetch response"
1827 );
1828 self.pending_proposal_fetches.resolve(&proposal);
1829 self.maybe_validate_fetched_proposal(*proposal);
1830 None
1831 },
1832 MessageType::PayloadFetch(PayloadFetchMessage::Req(request)) => {
1833 let view = request.view_number();
1834 debug!(%node, %sender, %view, "received payload fetch request");
1835 if let Err(err) = self.server.handle_request(
1836 &request,
1837 &message.sender,
1838 self.consensus.current_view(),
1839 self.network.sender(),
1840 ) {
1841 warn!(%node, %sender, %view, %err, "failed to send payload response");
1842 }
1843 None
1844 },
1845 MessageType::PayloadFetch(PayloadFetchMessage::Res(response)) => {
1846 let view = response.view_number();
1847 debug!(%node, %sender, %view, "received payload fetch response");
1848 let refused = self.fetcher.response(
1849 response,
1850 &message.sender,
1851 &self.consensus,
1852 &self.membership_coordinator,
1853 );
1854 for commit in refused {
1855 if !self.consensus.is_reconstructed(view, commit)
1856 && let Some((peer, message)) = self.fetcher.request(
1857 view,
1858 commit,
1859 Retry::SameRound,
1860 &self.consensus,
1861 &self.membership_coordinator,
1862 )
1863 && let Err(err) = self.unicast_in(
1864 self.slot_for(view),
1865 &peer,
1866 message.message_type,
1867 "payload request",
1868 )
1869 {
1870 warn!(%node, %sender, %view, %err, "failed to send payload request");
1871 }
1872 }
1873 None
1874 },
1875 MessageType::External(data) => {
1876 debug!(%node, %sender, bytes = data.len(), "recv external message");
1877 let _ = self.external_tx.send(OpaqueMessage {
1878 sender: message.sender,
1879 data,
1880 });
1881 None
1882 },
1883 }
1884 }
1885
1886 fn on_state_manager_output(
1887 &mut self,
1888 output: StateManagerOutput<T>,
1889 ) -> Option<ConsensusInput<T>> {
1890 match output {
1891 StateManagerOutput::State {
1892 response,
1893 validated: true,
1894 } => Some(ConsensusInput::StateValidated(response)),
1895 StateManagerOutput::State {
1896 response,
1897 validated: false,
1898 } => Some(ConsensusInput::StateValidationFailed(response)),
1899 StateManagerOutput::Header {
1900 response,
1901 header: Some(hdr),
1902 } => Some(ConsensusInput::HeaderCreated(
1903 response.view,
1904 proposal_commitment(&response.parent_proposal),
1905 hdr,
1906 )),
1907 StateManagerOutput::Header {
1908 response,
1909 header: None,
1910 } => {
1911 warn!(view = %response.view, "header creation failed");
1912 None
1913 },
1914 }
1915 }
1916
1917 fn slot_for(&self, view: ViewNumber) -> ViewNumber {
1925 view.max(self.consensus.current_view())
1926 }
1927
1928 fn broadcast_in(
1929 &self,
1930 slot: ViewNumber,
1931 message_type: impl Into<MessageType<T, Validated>>,
1932 ctx: &'static str,
1933 ) -> Result<(), CoordinatorError> {
1934 let message = Message {
1935 sender: self.public_key.clone(),
1936 message_type: message_type.into(),
1937 };
1938 self.network
1939 .sender()
1940 .broadcast(slot, &message)
1941 .map_err(|e| CoordinatorError::from(e).context(ctx))
1942 }
1943
1944 fn unicast_in(
1945 &self,
1946 slot: ViewNumber,
1947 recipient: &T::SignatureKey,
1948 message_type: impl Into<MessageType<T, Validated>>,
1949 ctx: &'static str,
1950 ) -> Result<(), CoordinatorError> {
1951 let message = Message {
1952 sender: self.public_key.clone(),
1953 message_type: message_type.into(),
1954 };
1955 self.network
1956 .sender()
1957 .unicast(slot, recipient, &message)
1958 .map_err(|e| CoordinatorError::from(e).context(ctx))
1959 }
1960
1961 fn send_transactions(
1964 &mut self,
1965 forward: TransactionMessage<T>,
1966 epoch: EpochNumber,
1967 ) -> Result<(), CoordinatorError> {
1968 let leaders = self.upcoming_leaders(forward.view, self.block_builder.fanout().get(), epoch);
1969 for (view, leader) in leaders {
1970 let message = TransactionMessage {
1971 view,
1972 transactions: forward.transactions.clone(),
1973 };
1974 self.unicast_in(
1975 self.slot_for(view),
1976 &leader,
1977 MessageType::Block(BlockMessage::Transactions(message)),
1978 "unicast transactions",
1979 )?;
1980 }
1981 Ok(())
1982 }
1983
1984 fn upcoming_leaders(
1986 &mut self,
1987 first: ViewNumber,
1988 count: u64,
1989 epoch: EpochNumber,
1990 ) -> Vec<(ViewNumber, T::SignatureKey)> {
1991 let mut leaders = Vec::<(ViewNumber, T::SignatureKey)>::new();
1992 for view in (0..count).map(|ahead| first + ahead) {
1993 let Some(leader) = self.upcoming_leader(view, epoch) else {
1994 warn!(%view, %epoch, "failed to resolve an upcoming leader");
1995 continue;
1996 };
1997 if leaders.iter().all(|(_, known)| *known != leader) {
1998 leaders.push((view, leader));
1999 }
2000 }
2001 leaders
2002 }
2003
2004 fn upcoming_leader(&mut self, view: ViewNumber, epoch: EpochNumber) -> Option<T::SignatureKey> {
2009 let expected = self.consensus.last_proposal_before(view).map(|p| {
2010 if is_last_block(p.block_header.block_number(), *self.consensus.epoch_height) {
2011 p.epoch + 1
2012 } else {
2013 p.epoch
2014 }
2015 });
2016 expected
2017 .and_then(|e| self.leader(view, e))
2018 .or_else(|| self.leader(view, epoch))
2019 }
2020
2021 fn leader(&mut self, view: ViewNumber, epoch: EpochNumber) -> Option<T::SignatureKey> {
2022 let membership = self
2023 .membership_coordinator
2024 .membership_for_epoch(Some(epoch))
2025 .ok()?;
2026 membership.leader(view).ok()
2027 }
2028
2029 fn on_client_request(&mut self, request: ClientRequest<T>) -> Result<(), CoordinatorError> {
2030 match request {
2031 ClientRequest::CurrentView(tx) => {
2032 let _ = tx.send(self.consensus.current_view());
2033 },
2034 ClientRequest::CurrentEpoch(tx) => {
2035 let _ = tx.send(self.consensus.current_epoch());
2036 },
2037 ClientRequest::DecidedLeaf(tx) => {
2038 let _ = tx.send(self.consensus.last_decided_leaf().clone());
2039 },
2040 ClientRequest::DecidedState(tx) => {
2041 let view = self.consensus.last_decided_leaf().view_number();
2042 let _ = tx.send(self.state(view).map(|s| s.state.clone()));
2043 },
2044 ClientRequest::UndecidedLeaves(tx) => {
2045 let _ = tx.send(self.consensus.undecided_leaves().cloned().collect());
2046 },
2047 ClientRequest::GetState { view, respond } => {
2048 let _ = respond.send(self.state(view).map(|s| s.state.clone()));
2049 },
2050 ClientRequest::GetStateAndDelta { view, respond } => {
2051 let _ = respond.send(match self.state(view) {
2052 Some(s) => (Some(s.state.clone()), s.delta.clone()),
2053 None => (None, None),
2054 });
2055 },
2056 ClientRequest::ProposalParticipation { epoch, respond } => {
2057 let _ = respond.send(match epoch {
2058 Some(epoch) => self.participation.proposal_participation(epoch),
2059 None => self.participation.current_proposal_participation(),
2060 });
2061 },
2062 ClientRequest::VoteParticipation { epoch, respond } => {
2063 let _ = respond.send(match epoch {
2064 Some(epoch) => self.participation.vote_participation(epoch),
2065 None => self.participation.current_vote_participation(),
2066 });
2067 },
2068 ClientRequest::SubmitTransaction { tx, respond } => {
2069 let forward = match self.block_builder.on_submit_transaction(tx) {
2070 Ok(forward) => forward,
2071 Err(err) => {
2072 let _ = respond.send(Err(err));
2073 return Ok(());
2074 },
2075 };
2076 let _ = respond.send(Ok(()));
2079 if let Some(forward) = forward {
2080 self.send_transactions(forward, self.consensus.current_epoch())?;
2081 }
2082 },
2083 ClientRequest::UpdateLeaf { update, respond } => {
2084 self.state_manager.update_state(update);
2085 let _ = respond.send(());
2086 },
2087 ClientRequest::RequestProposal {
2088 view,
2089 leaf_commitment,
2090 respond,
2091 } => {
2092 if let Some(proposal) = self.consensus.signed_proposal(view, leaf_commitment) {
2093 let _ = respond.send(Ok(proposal.clone()));
2094 return Ok(());
2095 }
2096 if !self
2097 .pending_proposal_fetches
2098 .contains_request(view, leaf_commitment)
2099 {
2100 self.broadcast_proposal_fetch(view)?;
2101 }
2102 self.pending_proposal_fetches
2103 .push(view, leaf_commitment, respond);
2104 },
2105 ClientRequest::SendExternalMessage {
2106 payload,
2107 recipient,
2108 respond,
2109 } => {
2110 let message = Message {
2111 sender: self.public_key.clone(),
2112 message_type: MessageType::External(payload),
2113 };
2114 let result = self
2115 .network
2116 .sender()
2117 .unicast(self.consensus.current_view(), &recipient, &message)
2118 .map_err(|err| {
2119 CoordinatorError::from(err)
2120 .context("send external message")
2121 .into()
2122 });
2123 let _ = respond.send(result);
2124 },
2125 }
2126
2127 Ok(())
2128 }
2129
2130 fn record_voted_view(&mut self, view: ViewNumber) {
2131 if self.voted_view.is_some_and(|v| v >= view) {
2132 return;
2133 }
2134 self.voted_view = Some(view);
2135 if let Some(m) = &self.metrics {
2136 m.consensus.last_voted_view.set(*view as usize);
2137 }
2138 }
2139
2140 fn on_view_changed_metrics(&mut self, view: ViewNumber, epoch: EpochNumber) {
2141 if let Some((started_view, started_epoch, started_at)) = self.view_started
2142 && started_view == view
2143 {
2144 if epoch > started_epoch {
2145 self.view_started = Some((view, epoch, started_at));
2146 }
2147 return;
2148 }
2149 let prev = self.view_started.replace((view, epoch, Instant::now()));
2150 let duration_as_leader = prev.and_then(|(prev_view, prev_epoch, entered)| {
2151 (self.leader(prev_view, prev_epoch).as_ref() == Some(&self.public_key))
2152 .then(|| entered.elapsed())
2153 });
2154 let Some(m) = &self.metrics else { return };
2155 let consensus = &m.consensus;
2156 consensus.current_view.set(*view as usize);
2157 let invalid_certs = self
2158 .cert_verifiers
2159 .num_invalid_certs()
2160 .saturating_sub(self.invalid_certs_at_decide);
2161 consensus.invalid_qc.set(invalid_certs as usize);
2162 let last_decided_view = self.consensus.last_decided_view();
2163 if view > last_decided_view {
2164 consensus
2165 .number_of_views_since_last_decide
2166 .set((*view - *last_decided_view) as usize);
2167 }
2168 if let Some(duration) = duration_as_leader {
2169 consensus
2170 .view_duration_as_leader
2171 .add_point(duration.as_secs_f64());
2172 }
2173 let (outstanding_txns, outstanding_bytes) = self.block_builder.outstanding_transactions();
2174 consensus.outstanding_transactions.set(outstanding_txns);
2175 consensus
2176 .outstanding_transactions_memory_size
2177 .set(outstanding_bytes);
2178 }
2179
2180 fn on_decide_metrics(&mut self, leaves: &[Leaf2<T>]) {
2181 let Some(newest) = leaves.first() else { return };
2182 let advanced = newest.view_number() == self.consensus.last_decided_view();
2186 if advanced {
2187 self.invalid_certs_at_decide = self.cert_verifiers.num_invalid_certs();
2188 }
2189 let Some(m) = &self.metrics else { return };
2190 let consensus = &m.consensus;
2191 let now = OffsetDateTime::now_utc().unix_timestamp();
2192 for leaf in leaves {
2193 let txn_bytes = self
2194 .payload_txn_bytes
2195 .get(&leaf.view_number())
2196 .copied()
2197 .or_else(|| leaf.block_payload_ref().map(|p| p.txn_bytes()));
2198 if let Some(txn_bytes) = txn_bytes {
2199 consensus.finalized_bytes.add_point(txn_bytes as f64);
2200 }
2201 if advanced {
2202 match (now as u64).checked_sub(leaf.block_header().timestamp()) {
2203 Some(age) => consensus.proposal_to_decide_time.add_point(age as f64),
2204 None => error!(
2205 timestamp = leaf.block_header().timestamp(),
2206 "failed to calculate proposal to decide time: timestamp in the future"
2207 ),
2208 }
2209 }
2210 }
2211 if !advanced {
2212 return;
2213 }
2214 consensus.last_decided_time.set(now as usize);
2215 consensus.invalid_qc.set(0);
2216 consensus
2217 .last_decided_view
2218 .set(*newest.view_number() as usize);
2219 consensus
2220 .last_synced_block_height
2221 .set(newest.block_header().block_number() as usize);
2222 if let Some(views_in_flight) =
2223 (*self.consensus.current_view()).checked_sub(*newest.view_number())
2224 {
2225 consensus
2226 .number_of_views_per_decide_event
2227 .add_point(views_in_flight as f64);
2228 }
2229 }
2230
2231 fn broadcast_proposal_fetch(&mut self, view: ViewNumber) -> Result<(), CoordinatorError> {
2233 let request = self
2234 .consensus
2235 .signed_proposal_fetch_request(view)
2236 .map_err(|err| {
2237 let err = format!("failed to sign proposal request: {err}");
2238 CoordinatorError::regular(err).context("sign proposal request")
2239 })?;
2240 self.broadcast_in(
2241 self.slot_for(view),
2242 MessageType::ProposalFetch(ProposalFetchMessage::Request(request)),
2243 "broadcast proposal request",
2244 )
2245 }
2246
2247 fn request_missing_proposal(
2248 &mut self,
2249 view: ViewNumber,
2250 leaf_commit: Commitment<Leaf2<T>>,
2251 ) -> Result<(), CoordinatorError> {
2252 let current = self.consensus.current_view();
2256 let key = ProposalFetchKey::new(view, leaf_commit);
2257 if self
2258 .requested_missing_proposals
2259 .get(&key)
2260 .is_some_and(|asked_in| *asked_in >= current)
2261 {
2262 return Ok(());
2263 }
2264 self.requested_missing_proposals.insert(key, current);
2265 self.broadcast_proposal_fetch(view)
2266 }
2267
2268 fn maybe_validate_fetched_proposal(&mut self, proposal: SignedProposal<T, Proposal<T>>) {
2269 let view = proposal.data.view_number;
2270 let leaf_commitment = proposal_commitment(&proposal.data);
2273 let Some(key) = self
2274 .requested_missing_proposals
2275 .keys()
2276 .find(|key| key.leaf_commitment == leaf_commitment && key.view >= view)
2277 .copied()
2278 else {
2279 return;
2280 };
2281 self.requested_missing_proposals.remove(&key);
2282 if self.consensus.proposals().contains(view, leaf_commitment) {
2283 return;
2284 }
2285 self.proposal_validator
2286 .validate_fetched(ProposalMessage::unchecked(proposal));
2287 }
2288
2289 fn notify_vid_reconstructor(&mut self, proposal: &Proposal<T>, pinned_by: PinnedBy) {
2292 let view = proposal.view_number;
2293 let VidCommitment::V2(payload_commitment) = proposal.block_header.payload_commitment()
2294 else {
2295 warn!(node = %self.node_id, %view, "proposal payload commitment is not V2");
2296 return;
2297 };
2298 let expected_param = expected_vid_param(&self.membership_coordinator, proposal.epoch);
2299 self.vid_reconstructor.handle_proposal(
2300 view,
2301 payload_commitment,
2302 proposal.block_header.metadata().clone(),
2303 proposal.epoch,
2304 expected_param,
2305 pinned_by,
2306 );
2307 }
2308
2309 fn gc(&mut self, epoch: EpochNumber, scope: GcScope) -> Result<(), CoordinatorError> {
2310 self.consensus.gc(scope);
2311 match scope {
2312 GcScope::Local(view) => {
2313 let view = view.saturating_sub(GC_MARGIN_VIEWS.get() - 1).into();
2318 self.block_builder.gc(view);
2319 self.vid_disperser.gc(view);
2320 self.vid_fragment_accumulator.gc(view);
2321 self.network.gc(view)?;
2322 self.timeout_collector.gc(view);
2323 self.timeout_one_honest_collector.gc(view);
2324 self.timeout3_collector.gc(view);
2325 self.timeout_one_honest3_collector.gc(view);
2326 self.vote1_collector.gc(view);
2327 self.vote2_collector.gc(view);
2328 self.upgrade_vote_collector.gc(view);
2329 self.upgrade_protocol.gc(view);
2330 },
2331 GcScope::Decided(view) => {
2332 let decide_floor = self.consensus.decide_floor();
2333 self.epoch_manager.gc(epoch);
2334 self.epoch_root_collector.gc(view);
2335 self.cert_verifiers.gc(decide_floor, epoch);
2336 self.pending_proposal_fetches.gc(view);
2337 self.requested_missing_proposals
2338 .retain(|key, _| key.view > view);
2339 self.fetcher.gc(view);
2340 self.state_manager.gc(view);
2341 self.storage
2342 .gc(view.saturating_sub(STORAGE_GC_MARGIN).into());
2343 self.vid_reconstructor
2344 .gc(view.saturating_sub(VID_RECONSTRUCT_GC_MARGIN).into());
2345 let vc = VidCommitment2::default();
2346 self.da_payloads = self.da_payloads.split_off(&(view, vc));
2347 self.built_dispersals =
2348 self.built_dispersals
2349 .split_off(&(view, EpochNumber::new(0), vc));
2350 self.payload_txn_bytes = self.payload_txn_bytes.split_off(&(decide_floor + 1));
2351 },
2352 GcScope::Timeout(view) => {
2353 let before = ViewNumber::from(view.saturating_sub(1));
2357 if !self.consensus.keeps_payload_on_timeout(before) {
2358 self.vid_reconstructor.retire_view(before);
2359 }
2360 if self.consensus.keeps_payload_on_timeout(view) {
2362 return Ok(());
2363 }
2364 let vc = VidCommitment2::default();
2365 self.da_payloads
2366 .extract_if((view, vc)..(view + 1, vc), |_, _| true)
2367 .for_each(drop);
2368 let e = EpochNumber::new(0);
2369 self.built_dispersals
2370 .extract_if((view, e, vc)..(view + 1, e, vc), |_, _| true)
2371 .for_each(drop);
2372 },
2373 }
2374 Ok(())
2375 }
2376
2377 fn is_view_too_far_ahead(&self, v: ViewNumber) -> bool {
2379 v > self.consensus.current_view() + *MAX_VIEWS_AHEAD
2380 }
2381
2382 fn is_vote_epoch_admissible(&self, epoch: Option<EpochNumber>) -> bool {
2384 let current = self.consensus.current_epoch();
2385 epoch.is_none_or(|e| is_epoch_admissible(e, current))
2386 }
2387
2388 fn is_epoch_too_far_ahead(&self, epoch: Option<EpochNumber>) -> bool {
2390 let current = self.consensus.current_epoch();
2391 epoch.is_some_and(|e| e > current + EPOCH_CHANGE_LOOKAHEAD)
2392 }
2393
2394 fn is_epoch_too_far_behind(&self, epoch: EpochNumber) -> bool {
2396 let current = self.consensus.current_epoch();
2397 epoch.u64() < current.saturating_sub(EPOCH_CHANGE_LOOKBEHIND)
2398 }
2399
2400 fn is_fragment_epoch_admissible(&self, epoch: EpochNumber) -> bool {
2402 let current = self.consensus.current_epoch();
2403 is_epoch_admissible(epoch, current)
2404 }
2405
2406 pub(crate) fn catchup_evidence(&self) -> Option<ConsensusMessage<T, Validated>> {
2407 Some(match self.consensus.catchup_evidence()? {
2408 CatchupEvidence::Qc(qc) => ConsensusMessage::HighQc(qc),
2409 CatchupEvidence::Tc(tc) => ConsensusMessage::TimeoutCertificate(tc),
2410 CatchupEvidence::Tc3(tc) => ConsensusMessage::TimeoutCertificate3(tc),
2411 })
2412 }
2413
2414 fn on_timeout_vote(
2415 &mut self,
2416 sender: &T::SignatureKey,
2417 mut vote: TimeoutVote<T>,
2418 evidence: Option<CatchupEvidence<T>>,
2419 ) {
2420 let node = self.node_id;
2421 let view = vote.view_number();
2422 if vote.signing_key() != *sender {
2423 warn!(%node, %sender, %view, "timeout vote signing key != sender");
2424 return;
2425 }
2426
2427 let current_view = self.consensus.current_view();
2428 let has_evidence = evidence.is_some();
2429
2430 if let Some(e) = evidence.filter(|e| e.view_number() >= current_view) {
2431 let epoch = match e {
2432 CatchupEvidence::Qc(qc) => self.verify_advance(sender, qc),
2433 CatchupEvidence::Tc(tc) => self.verify_timeout_cert(sender, tc),
2434 CatchupEvidence::Tc3(tc) => self.verify_timeout_cert3(sender, tc),
2435 };
2436 if let Some(epoch) = epoch {
2437 self.epoch_manager.request_drb_result(epoch);
2438 }
2439 }
2440
2441 if self.is_view_too_far_ahead(view) {
2442 warn!(%node, %sender, %view, "timeout vote is too far ahead");
2443 return;
2444 }
2445
2446 if !vote.is_well_formed() {
2447 warn!(%node, %sender, %view, "timeout vote not well formed");
2448 return;
2449 }
2450
2451 if vote.binds_epoch()
2454 && let Some(epoch) = vote.epoch()
2455 && epoch < self.consensus.current_epoch()
2456 {
2457 self.send_epoch_change(sender, epoch);
2458 }
2459
2460 if view < current_view {
2461 debug!(
2462 %node, %sender, %view, %current_view,
2463 "timeout vote for stale view; replying with catchup evidence"
2464 );
2465 self.send_catchup_evidence(sender, view);
2466 return;
2467 }
2468
2469 debug!(%node, %sender, %view, has_evidence, "recv timeout vote");
2470
2471 if let TimeoutVote::V2(vote) = &mut vote {
2484 vote.data.epoch = Some(self.consensus.current_epoch());
2485 }
2486
2487 if !self.is_vote_epoch_admissible(vote.epoch()) {
2488 warn!(
2489 %node,
2490 %sender,
2491 %view,
2492 epoch = ?vote.epoch(),
2493 "timeout vote epoch is out of range"
2494 );
2495 return;
2496 }
2497
2498 if vote.binds_epoch() != self.consensus.upgrade_lock().timeout_epoch_bound(view) {
2499 warn!(%node, %sender, %view, "timeout vote has the wrong form for its version");
2500 return;
2501 }
2502
2503 match vote {
2504 TimeoutVote::V2(vote) => {
2505 self.timeout_collector.accumulate_vote(vote.clone());
2506 self.timeout_one_honest_collector.accumulate_vote(vote);
2507 },
2508 TimeoutVote::V3(ballot) => {
2509 if let Some(epoch) = self.timeout3_collector.accumulate_vote(ballot.clone()) {
2512 debug!(%node, %sender, %view, %epoch, "timeout vote waits for a committee");
2513 self.epoch_manager.request_drb_result(epoch);
2514 }
2515 self.timeout_one_honest3_collector.accumulate_vote(ballot);
2516 },
2517 }
2518 }
2519
2520 fn verify_advance(
2528 &mut self,
2529 sender: &T::SignatureKey,
2530 qc: Certificate1<T>,
2531 ) -> Option<EpochNumber> {
2532 let genesis = ViewNumber::genesis();
2533 if qc.view_number() == genesis
2534 && self
2535 .consensus
2536 .cert1_at(genesis)
2537 .is_none_or(|seeded| seeded.data != qc.data)
2538 {
2539 warn!(node = %self.node_id, %sender, "cert1 at the genesis view is not the genesis QC");
2540 return None;
2541 }
2542 self.cert_verifiers.advance.verify(sender.clone(), qc)
2543 }
2544
2545 fn verify_timeout_cert(
2546 &mut self,
2547 sender: &T::SignatureKey,
2548 tc: TimeoutCertificate2<T>,
2549 ) -> Option<EpochNumber> {
2550 let view = tc.view_number();
2551 if self.consensus.upgrade_lock().timeout_epoch_bound(view) {
2552 warn!(node = %self.node_id, %sender, %view, "inadmissible timeout certificate");
2553 return None;
2554 }
2555 self.cert_verifiers.timeout.verify(sender.clone(), tc)
2556 }
2557
2558 fn verify_timeout_cert3(
2559 &mut self,
2560 sender: &T::SignatureKey,
2561 tc: TimeoutCertificate3<T>,
2562 ) -> Option<EpochNumber> {
2563 let view = tc.view_number();
2564 let epoch = tc.epoch;
2565 if self.is_epoch_too_far_behind(epoch) {
2566 warn!(node = %self.node_id, %sender, %view, %epoch, "timeout certificate too far behind");
2567 return None;
2568 }
2569 if !self.consensus.upgrade_lock().timeout_epoch_bound(view) {
2570 warn!(node = %self.node_id, %sender, %view, "inadmissible timeout certificate");
2571 return None;
2572 }
2573 if view < self.consensus.decide_floor() {
2574 debug!(node = %self.node_id, %sender, %view, "stale timeout certificate");
2575 return None;
2576 }
2577 if let Err(err) = tc.check_lock() {
2578 warn!(node = %self.node_id, %sender, %view, %err, "malformed timeout certificate");
2579 return None;
2580 }
2581 self.cert_verifiers.timeout3.verify(sender.clone(), tc)
2582 }
2583
2584 fn send_epoch_change(&mut self, peer: &T::SignatureKey, epoch: EpochNumber) {
2585 let Some(epoch_change) = self.consensus.epoch_change_ending(epoch) else {
2586 return;
2587 };
2588 let message = Message {
2589 sender: self.public_key.clone(),
2590 message_type: MessageType::Consensus(ConsensusMessage::EpochChange(epoch_change)),
2591 };
2592 if let Err(err) =
2593 self.network
2594 .sender()
2595 .unicast(self.consensus.current_view(), peer, &message)
2596 {
2597 warn!(%epoch, %err, "failed to send epoch change");
2598 }
2599 }
2600
2601 fn send_catchup_evidence(&mut self, peer: &T::SignatureKey, stale_view: ViewNumber) {
2602 let Some(evidence) = self.catchup_evidence() else {
2603 return;
2604 };
2605 let view = evidence.view_number();
2606 if view < stale_view {
2607 return;
2608 }
2609 if let Err(err) = self.unicast_in(self.slot_for(view), peer, evidence, "catchup evidence") {
2610 warn!(%stale_view, %err, "failed to send catchup evidence");
2611 }
2612 }
2613}
2614
2615#[derive(Debug, Clone, Copy)]
2617pub enum GcScope {
2618 Local(ViewNumber),
2620 Decided(ViewNumber),
2622 Timeout(ViewNumber),
2624}
2625
2626pub(crate) fn is_epoch_admissible(epoch: EpochNumber, current: EpochNumber) -> bool {
2628 current.saturating_sub(EPOCH_CHANGE_LOOKBEHIND) <= *epoch
2629 && *epoch <= current.saturating_add(EPOCH_CHANGE_LOOKAHEAD)
2630}
2631
2632struct PendingDa<T: NodeType> {
2634 epoch: EpochNumber,
2635 payload: T::BlockPayload,
2636 metadata: <T::BlockPayload as BlockPayload<T>>::Metadata,
2637 hashes: Vec<Commitment<T::Transaction>>,
2638}
2639
2640type ProposalFetchResponseSender<T> =
2641 oneshot::Sender<Result<SignedProposal<T, Proposal<T>>, QueryError>>;
2642
2643#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
2644struct ProposalFetchKey<T: NodeType> {
2645 view: ViewNumber,
2646 leaf_commitment: Commitment<Leaf2<T>>,
2647}
2648
2649impl<T: NodeType> ProposalFetchKey<T> {
2650 fn new(view: ViewNumber, leaf_commitment: Commitment<Leaf2<T>>) -> Self {
2651 Self {
2652 view,
2653 leaf_commitment,
2654 }
2655 }
2656}
2657
2658#[derive(Default)]
2659struct PendingProposalFetches<T: NodeType> {
2660 pending: HashMap<ProposalFetchKey<T>, Vec<ProposalFetchResponseSender<T>>>,
2661}
2662
2663impl<T: NodeType> PendingProposalFetches<T> {
2664 fn prune_closed(&mut self) {
2665 self.pending.retain(|_, responders| {
2666 responders.retain(|respond| !respond.is_closed());
2667 !responders.is_empty()
2668 });
2669 }
2670
2671 fn contains_request(
2672 &mut self,
2673 view: ViewNumber,
2674 leaf_commitment: Commitment<Leaf2<T>>,
2675 ) -> bool {
2676 self.prune_closed();
2677 self.pending
2678 .contains_key(&ProposalFetchKey::new(view, leaf_commitment))
2679 }
2680
2681 fn push(
2682 &mut self,
2683 view: ViewNumber,
2684 leaf_commitment: Commitment<Leaf2<T>>,
2685 respond: ProposalFetchResponseSender<T>,
2686 ) {
2687 self.pending
2688 .entry(ProposalFetchKey::new(view, leaf_commitment))
2689 .or_default()
2690 .push(respond);
2691 }
2692
2693 fn gc(&mut self, view: ViewNumber) {
2694 self.pending.retain(|key, responders| {
2695 responders.retain(|respond| !respond.is_closed());
2696 key.view >= view && !responders.is_empty()
2697 });
2698 }
2699
2700 fn resolve(&mut self, proposal: &SignedProposal<T, Proposal<T>>) {
2701 self.prune_closed();
2702 let view = proposal.data.view_number;
2703 let leaf_commitment = proposal_commitment(&proposal.data);
2704 let key = ProposalFetchKey::new(view, leaf_commitment);
2705
2706 if let Some(responders) = self.pending.remove(&key) {
2707 for respond in responders {
2708 let _ = respond.send(Ok(proposal.clone()));
2709 }
2710 }
2711 }
2712}