Skip to content

MinerGetBaseInfo returns too few beacon entries after a null round #7412

Description

@LesnyRumcajs

Note

This was AI found and seems correct, but do your own checks. Regardless of that, a test will be useful.

Location: src/beacon/drand.rs#L115-L118, in beacon_entries_for_block (#L76-L131). Reached only via miner_get_base_info (src/state_manager/mining.rs#L83-L90) → the Filecoin.MinerGetBaseInfo RPC.

Current behavior

The post-quicknet (unchained) branch unconditionally returns a single entry:

// We only ever need one entry after drand quicknet upgrade (FIP-0063)
if curr_beacon.network().is_unchained() {
    let entry = curr_beacon.entry(max_round).await?;
    Ok(vec![entry])
}

The comment holds only when the parent is exactly one epoch back. A block covers every epoch since its parent, contributing one beacon entry per covered epoch. When one or more null rounds sit between parent and current epoch, the block must carry one entry per covered epoch.

Correct behavior

Lotus BeaconEntriesForBlock (chain/beacon/beacon.go#L115) loops currEpoch from epoch down to parentEpoch+1, emitting MaxBeaconRoundForEpoch(currEpoch) for each — epoch − parentEpoch entries. The loop runs for chained and unchained alike; Forest special-cased unchained to one entry and lost the null-round case.

On-chain confirmation

Mainnet epoch 6216199 is null; the block at 6216200 (parent 6216198) carries two entries — rounds 30662992 (for null epoch 6216199) and 30663002 (for 6216200). Forest's beacon_entries_for_block(epoch=6216200, parent=6216198) returns only [30663002].

Impact

An external miner (lotus-miner / Venus / curio) using Forest as its full node builds a block missing a beacon entry whenever the previous epoch was null. The rest of the network rejects it (Lotus ValidateBlockValues requires the per-epoch entries), so the miner orphans its own block and loses the reward. Intermittent (null rounds are low-single-digit % of epochs) but not rare. Not a chain-safety issue — Forest serves wrong mining base info, it does not produce the block.

Fix

Replace the single-entry shortcut with the same per-covered-epoch loop the chained branch (#L119-L130) already uses — iterate parent_epoch+1..=epoch, one entry(max_beacon_round_for_epoch(e)) per epoch.

Test

MinerGetBaseInfo at an epoch whose parent tipset is 2+ epochs back must return epoch − parent_epoch entries at the per-epoch rounds; the 6216200 case above is a ready fixture.

Metadata

Metadata

Labels

Type: BugSomething isn't working

Type

No type

Projects

Status
Done

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions