pulse_candidates.rs
Overview
This file defines the PulseCandidateBlocks struct and its associated methods, which are responsible for managing timing and broadcasting logic related to "pulse" events in the context of block finalization within a networked node. The main functionality revolves around detecting when block finalization has stalled or not progressed and triggering broadcast events to re-announce candidate blocks to the network in order to maintain synchronization and liveness.
Key responsibilities include tracking the last finalized block, detecting timing conditions that require rebroadcasting, and interfacing with network message senders to propagate these candidate blocks.
Struct: PulseCandidateBlocks
PulseCandidateBlocks maintains state and timing information relevant to the pulse mechanism for candidate blocks. It is designed to be instantiated with specific node and thread identifiers, network senders, and timeout configurations.
Fields
node_id: NodeIdentifier
Unique identifier of the node owning this pulse candidate instance.thread_identifier: ThreadIdentifier
Identifier to distinguish the thread (or shard) for which this pulse logic applies.broadcast_tx: NetBroadcastSender<NetworkMessage>
Sender used for broadcasting messages to the network.direct_send_tx: NetDirectSender<NodeIdentifier, NetworkMessage>
Sender used for direct (unicast) sending of network messages.last_finalized_block: Option<BlockSeqNo>
Optionally stores the sequence number of the last finalized block processed by this node.last_pulse: Instant
Timestamp of the last pulse event, used to detect inactivity or finalization stalls.last_broadcast_timestamp: Instant
Timestamp of the last broadcast attempt to throttle repeated sends.resend_timeout: Duration
The minimum duration to wait before resending broadcast messages.resend_extra_timeout_per_candidate: Duration
Additional timeout added per candidate block broadcasted to prevent flooding.trigger_by_finalization_stopped_timer: Duration
Timeout duration to trigger pulse when finalization has stopped.trigger_by_no_finalized_since_start_timer: Duration
Timeout duration to trigger pulse if no blocks have been finalized since start.
Construction
PulseCandidateBlocks is constructed using the TypedBuilder pattern, which allows setting most fields explicitly, while some fields like last_finalized_block, last_pulse, and last_broadcast_timestamp have default initializations.
Methods
pulse(&mut self, last_finalized_block: BlockSeqNo) -> anyhow::Result<()>
Updates the internal state when a new block is finalized, resetting relevant timers and updating the last finalized block sequence number.
Parameters
last_finalized_block: BlockSeqNo— The sequence number of the most recently finalized block.
Behavior
Checks if the new finalized block sequence number is greater than the previous one to ensure monotonic progression.
Updates
last_finalized_block,last_pulse, andlast_broadcast_timestampto the current instant.Returns
Ok(())on successful update; returns an error if the new finalized block is not strictly greater than the previous.
Usage Example
let mut pulse_candidates = PulseCandidateBlocks::builder()
.node_id(node_id)
.thread_identifier(thread_id)
.broadcast_tx(broadcast_sender)
.direct_send_tx(direct_sender)
.resend_timeout(Duration::from_secs(10))
.resend_extra_timeout_per_candidate(Duration::from_secs(1))
.trigger_by_finalization_stopped_timer(Duration::from_secs(30))
.trigger_by_no_finalized_since_start_timer(Duration::from_secs(300))
.build();
pulse_candidates.pulse(new_finalized_block_seq_no)?;
evaluate(&mut self, candidates: &UnfinalizedBlocksSnapshot, blocks_repository: &RepositoryImpl) -> anyhow::Result<()>
Evaluates whether a rebroadcast should be triggered based on timing conditions and the current state of candidate blocks. If conditions are met, it initiates broadcasting of candidate blocks.
Parameters
candidates: &UnfinalizedBlocksSnapshot— Snapshot of unfinalized blocks available for potential rebroadcast.blocks_repository: &RepositoryImpl— Interface to query finalized and candidate blocks from persistent storage.
Behavior
Determines if a rebroadcast should be triggered based on two main conditions:
Finalization has stopped for longer than
trigger_by_finalization_stopped_timer.No blocks have been finalized since start for longer than
trigger_by_no_finalized_since_start_timer.
If triggering conditions are met, checks if enough time has passed since the last broadcast, respecting
resend_timeout.Initiates rebroadcast of all candidate blocks by sending
NetworkMessages viabroadcast_tx. (Note: current implementation is naive and broadcasts all candidates.)Adjusts
last_broadcast_timestampby adding an extra timeout per candidate rebroadcasted to prevent rapid re-sending.Returns
Ok(())after evaluation and possible broadcasting.
Usage Example
pulse_candidates.evaluate(&unfinalized_blocks_snapshot, &repository_impl)?;
Implementation Notes
The rebroadcasting logic is currently simplistic and marked with TODO comments indicating future improvements to selectively send only necessary candidate blocks.
Commented-out code hints at interaction with the repository to retrieve finalized blocks and to generate network messages for rebroadcast.
The
sent_cntvariable is hardcoded to1for now, but intended to represent the count of candidate blocks rebroadcasted.
Implementation Details and Algorithms
The pulse mechanism uses timestamp comparisons via
Instantto detect inactivity or stalled finalization.The logic distinguishes two main triggering conditions using elapsed time checks.
Throttling of broadcasts is implemented using
resend_timeoutand an additional scaled timeout based on the number of candidates rebroadcasted.The use of
anyhow::Resultfor error handling allows flexible propagation of errors from underlying operations.
Interaction with Other Parts
Uses
NodeIdentifierandThreadIdentifierfrom the node's associated types to contextualize actions per node and thread.Communicates over the network using
NetBroadcastSenderandNetDirectSenderfor message broadcasting and direct sending respectively.Reads block state and candidates from
UnfinalizedBlocksSnapshotandRepositoryImplto determine broadcast content.Constructs
NetworkMessageinstances (commented in this file) to represent rebroadcasted candidate blocks.This file's functionality ties closely with the block finalization and synchronization logic present elsewhere in the node’s implementation.
Diagram: PulseCandidateBlocks Structure and Workflow
classDiagram
class PulseCandidateBlocks {
-node_id: NodeIdentifier
-thread_identifier: ThreadIdentifier
-broadcast_tx: NetBroadcastSender
-direct_send_tx: NetDirectSender
-last_finalized_block: Option<BlockSeqNo>
-last_pulse: Instant
-last_broadcast_timestamp: Instant
-resend_timeout: Duration
-resend_extra_timeout_per_candidate: Duration
-trigger_by_finalization_stopped_timer: Duration
-trigger_by_no_finalized_since_start_timer: Duration
+pulse()
+evaluate()
}
PulseCandidateBlocks ..> NodeIdentifier : uses
PulseCandidateBlocks ..> ThreadIdentifier : uses
PulseCandidateBlocks ..> NetBroadcastSender : sends messages
PulseCandidateBlocks ..> NetDirectSender : sends messages
PulseCandidateBlocks ..> UnfinalizedBlocksSnapshot : reads candidate blocks
PulseCandidateBlocks ..> RepositoryImpl : queries block repository
This diagram illustrates PulseCandidateBlocks' internal structure, key methods, and its relationships with external entities such as identifiers, network senders, and data sources.