You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The reason will be displayed to describe this comment to others. Learn more.
🟡 Changes recommended
Inflight key classification, scheduler rekeying, rooted-parent handling, and repair-statistics behavior have unresolved correctness issues.
Get a fresh assessment by requesting another Copilot review.
Review details
Suppressed comments (4)
Previously missed (1) — in code that hasn't changed since the last review.
src/discof/rotor/fd_repair_stats.c:149
This logs every completion passed in, but records are shared by all versions of a slot and contain no completed/reported state. Since the new chainer deliberately allows multiple versions to complete and deliver, the same slot can emit multiple “complete” lines with different durations, contrary to the documented “first to complete stops it” behavior. Persist the first completion (or a reported flag) in the slot record and ignore later completions.
src/discof/rotor/fd_chainer.c:460
Finalizing the turbine block changes its scheduler identity from {slot, ZERO} to {slot, block_id}, but this function does not expose that rename and after_fec_complete only schedules newly created blocks. The existing zero-key task therefore becomes stale and is dropped; a completed turbine block with a missing parent will never be walked under its finalized ID to continue orphan repair. Report the rekey to the tile (or update the scheduler directly) so the old task is removed and the finalized key is queued.
if( FD_UNLIKELY( turbine->complete_idx!=UINT_MAX &&
turbine->buffered_fec_idx==turbine->complete_idx &&
fd_hash_check_zero( &turbine->block_id ) ) ) {
if( FD_UNLIKELY( !finalize_block_id( chainer, turbine ) ) ) FD_LOG_WARNING(( "failed to finalize block_id for slot %lu, parent_slot %lu parent_bid is zero %d", slot, turbine->parent_slot, fd_hash_check_zero( &turbine->parent_block_id ) ));
src/discof/rotor/fd_repair_stats.c:110
A delayed older slot unconditionally overwrites a newer slot that occupies the same modulo bucket, even though the API promises replacement only when a newer slot lands. Since the rotor can retain more than slot_max slots and shreds arrive out of order, this can erase the active newer slot's timing and counters. Reject stale slot<r->slot updates (and make callers handle the ignored record) instead of replacing it.
When the verified parent is absent but already at or below the current root, creating it leaves a block below the root (or a noncanonical root sibling). This contradicts fd_chainer_verify's invariant and restores state for a dead fork; the previous implementation explicitly returned without allocating in this case. Return NULL when parent_slot<=chainer->root before acquiring the parent.
Get a fresh assessment by requesting another Copilot review.
Review details
Suppressed comments (3)
src/discof/repair/fd_inflight.c:123
kind can now be AG_REPAIR_KIND_SHRED_FOR_BLOCK_ID, but fd_inflight_key_is_shred still returns true only for FD_REPAIR_KIND_SHRED. These requests therefore take the metadata nonce-only equality/hash path, so slot, shred index, FEC root, and even request kind are ignored; a nonce collision can remove and credit the wrong inflight request. Update the key classification/equality so both shred kinds use the full shred key (including a consistent treatment of kind).
Finalizing a turbine block changes its scheduler identity from {slot, ZERO} to {slot, block_id}, but this mutation is not reported to the tile. For the normal case where the block was created by earlier shreds, the tile only has the old queued key; that stale check is dropped after finalization and no check is queued for the new key. If the completed block's parent is still absent, ancestry retries stop and the block can remain permanently undelivered. Rekey the scheduler entry when finalization succeeds or expose the rename so the tile can do so.
if( FD_UNLIKELY( turbine->complete_idx!=UINT_MAX &&
turbine->buffered_fec_idx==turbine->complete_idx &&
fd_hash_check_zero( &turbine->block_id ) ) ) {
if( FD_UNLIKELY( !finalize_block_id( chainer, turbine ) ) ) FD_LOG_WARNING(( "failed to finalize block_id for slot %lu, parent_slot %lu parent_bid is zero %d", slot, turbine->parent_slot, fd_hash_check_zero( &turbine->parent_block_id ) ));
src/discof/rotor/fd_chainer.c:530
When the named parent is no longer retained because parent_slot<=root, creating it reintroduces a pruned block (and for parent_slot<root, immediately violates fd_chainer_verify's no-block-below-root invariant). At the root, each dead-fork parent ID can also consume another version until the per-slot limit aborts. Treat an absent parent at or below the root as already settled instead of allocating it, as the previous implementation did.
Get a fresh assessment by requesting another Copilot review.
Review details
Suppressed comments (4)
Previously missed (1) — in code that hasn't changed since the last review.
src/discof/rotor/fd_repair_stats.c:129
This counts every shred notification, including duplicates: the shred tile explicitly forwards FD_FEC_RESOLVER_SHRED_DUPLICATE (fd_shred_tile.c:1096-1100). The legacy forest statistics this sidecar replaces increment source counts only for newly seen shred indices (fd_forest.c:1367-1370). Repeated packets will therefore inflate turbine/repair/recovered and coding counts; pass duplicate/newness information or deduplicate by shred index before incrementing.
src/discof/rotor/fd_chainer.c:625
Creating the verified sibling without abandoning or otherwise deduplicating the incomplete turbine sibling allows both delivery streams to complete with the same {slot, block_id}. Replay still explicitly relies on the old one-stream guarantee (fd_replay_tile.c:3871-3885): turbine finalization removes any existing entry for that key and inserts its own, even when the existing verified bank is live. Restore single-stream behavior or update replay to merge/reject the duplicate live bank before enabling both versions.
Finalization changes the turbine block's identity from {slot, 0} to {slot, block_id}, but the tile's queued schedulor task remains keyed by {slot, 0}. The next check is consequently treated as stale and dropped, with no task inserted for the finalized key; if this completed block still lacks its parent, ancestry retries stop and it may never connect or deliver. Re-key the schedulor task when finalization succeeds.
if( FD_UNLIKELY( turbine->complete_idx!=UINT_MAX &&
turbine->buffered_fec_idx==turbine->complete_idx &&
fd_hash_check_zero( &turbine->block_id ) ) ) {
if( FD_UNLIKELY( !finalize_block_id( chainer, turbine ) ) ) FD_LOG_WARNING(( "failed to finalize block_id for slot %lu, parent_slot %lu parent_bid is zero %d", slot, turbine->parent_slot, fd_hash_check_zero( &turbine->parent_block_id ) ));
src/discof/rotor/fd_repair_stats.c:110
A different slot always overwrites this bucket, even when it is older than the resident slot. Shreds can arrive out of order, so a delayed slot that is still above root can erase a newer colliding record and its subsequent packets can corrupt the newer slot's statistics, contrary to the documented newer-slot-only eviction policy. Preserve the resident record when the incoming slot is older (and have callers ignore that update).
The reason will be displayed to describe this comment to others. Learn more.
🔵 Needs a closer look
Inflight key classification, duplicate replay streams, and schedulor key transitions can misroute responses or strand blocks.
Review details
Suppressed comments (9)
Previously missed (4) — in code that hasn't changed since the last review.
src/discof/rotor/fd_chainer.h:393
This summary contradicts the documented NULL/unknown-ID behavior on lines 383-384 and the implementation, which keeps all versions of the new root when no canonical block is found. Preserve that exception here so callers do not infer that all siblings are always removed. src/discof/rotor/fd_chainer.h:187
The magic constant name is incorrectly lowercased.
This issue also appears on line 198 of the same file. src/discof/rotor/fd_chainer.c:6
This reusable Firedancer component introduces buffered stdio streaming through printf/fflush. That bypasses the repository's fd_io path and can add libc syscall behavior incompatible with strict tile seccomp profiles. Convert fd_chainer_print to the existing fd_io output helpers and remove this include. src/discof/rotor/fd_chainer.c:72
fd_chainer_join(NULL) dereferences chainer while reading magic, unlike the other join APIs in this PR. This turns invalid input into a null-pointer crash instead of returning NULL.
src/discof/repair/fd_inflight.h:52
AG_REPAIR_KIND_SHRED_FOR_BLOCK_ID is still classified as metadata by fd_inflight_key_is_shred (line 86), which only recognizes FD_REPAIR_KIND_SHRED. Consequently these new records compare and hash by nonce alone, so a same-nonce metadata request can be consumed by a shred response (and vice versa), while slot/index/FEC-root keying is ignored. Classify both shred kinds as shred keys.
uint kind; /* FD_REPAIR_KIND_SHRED, AG_REPAIR_KIND_SHRED_FOR_BLOCK_ID, AG_REPAIR_KIND_PARENT_FEC_COUNT or AG_REPAIR_KIND_FEC_ROOT */
src/discof/rotor/fd_chainer.c:625
Keeping an unfinished turbine block live when the verified version is inserted violates replay's one-delivery-stream contract. If the votor ID is the ID the turbine block later computes (the normal non-equivocation case), replay already has {slot, block_id} from the verified stream; the turbine slot-complete FEC is then skipped or rekeys {slot,0} over that live entry (fd_replay_tile.c:3865-3885). Restore abandonment/deduplication so only one stream can complete for a block ID.
Finalizing a turbine block mutates its schedulor identity from {slot,0} to {slot,block_id}, but the tile is not notified to move the queued check. For a complete block whose known parent is absent, the old-key check later finds nothing and is dropped, so no further Orphan request is made and the block can remain disconnected indefinitely. Return/emit the key transition and atomically remove the zero-key check and queue the finalized key.
if( FD_UNLIKELY( turbine->complete_idx!=UINT_MAX &&
turbine->buffered_fec_idx==turbine->complete_idx &&
fd_hash_check_zero( &turbine->block_id ) ) ) {
if( FD_UNLIKELY( !finalize_block_id( chainer, turbine ) ) ) FD_LOG_WARNING(( "failed to finalize block_id for slot %lu, parent_slot %lu parent_bid is zero %d", slot, turbine->parent_slot, fd_hash_check_zero( &turbine->parent_block_id ) ));
src/discof/rotor/fd_requestor.c:278
some_parent_exists treats any version at the parent slot as the required parent. If only an equivocating sibling exists, this disables the orphan fill bound and can emit every missing shred before the Orphan request, contradicting the exact {parent_slot,parent_block_id} ancestry check and the API's documented bound. Base parent_seen only on parent_orphaned. src/discof/rotor/fd_chainer.h:200
The constant name is incorrectly lowercased in this public API documentation.
The reason will be displayed to describe this comment to others. Learn more.
🟡 Changes recommended
Inflight key classification, duplicate block streams, and scheduler rekeying introduce correctness and reliability failures.
Get a fresh assessment by requesting another Copilot review.
Review details
Suppressed comments (4)
src/discof/repair/fd_inflight.c:123
AG_REPAIR_KIND_SHRED_FOR_BLOCK_ID is still classified as metadata by fd_inflight_key_is_shred, which only recognizes FD_REPAIR_KIND_SHRED. Consequently these records hash and compare by nonce alone: a response with the echoed nonce matches even when its slot, index, or FEC root differs, and a nonce collision can consume an unrelated metadata request. Treat both shred kinds as shred keys and include kind in shred-key equality so it remains consistent with hashing the full key.
Keeping both versions live is unsafe when the votor ID equals the eventual turbine ID (the normal no-equivocation race). Both streams can reach replay as {slot, block_id}; replay still assumes the turbine stream was abandoned and removes any existing entry under that key as “stale” (fd_replay_tile.c:3871-3885), which can detach a live verified bank. Either retain the turbine-abandon/coalescing behavior or update replay to safely merge this case before allowing both versions to deliver.
This increment is unreachable because the turbine branch immediately above always returns. The block_id_only suppression case is already handled before the loop, so leaving this line suggests a control path that cannot occur. src/discof/rotor/fd_chainer.c:460
Finalizing a turbine block changes its scheduler identity from {slot, 0} to {slot, block_id}, but this API does not report that rekey and the rotor only schedules newly created blocks. The queued zero-ID check therefore becomes stale; if the completed block's parent is absent, it stops issuing Orphan retries and cannot connect or deliver. Report the rekey to the tile and migrate the queued check (remove the zero key and insert the finalized key).
if( FD_UNLIKELY( turbine->complete_idx!=UINT_MAX &&
turbine->buffered_fec_idx==turbine->complete_idx &&
fd_hash_check_zero( &turbine->block_id ) ) ) {
if( FD_UNLIKELY( !finalize_block_id( chainer, turbine ) ) ) FD_LOG_WARNING(( "failed to finalize block_id for slot %lu, parent_slot %lu parent_bid is zero %d", slot, turbine->parent_slot, fd_hash_check_zero( &turbine->parent_block_id ) ));
This description now implies that every unmatched request is included, but the rotor does not insert HighestShred or Orphan requests into ctx->rtt (fd_rotor_tile.c:312-320). The gauge still excludes those request types, so dashboards will undercount relative to the documented meaning.
Align repair response counter with counted request types
src/disco/metrics/metrics.xml:1439
The implementation increments this counter only when fd_rnonce_ss_normal_repair(nonce) is true (fd_rotor_tile.c:717-720), so data returned by HighestShred or Orphan requests is excluded even though it is also a repair response. Narrow the summary to the requests actually counted.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.