abort compile when fork epoch is forgotten (#2939)

There are a few locations in the code that compare the current epoch to
the various FORK_EPOCH constants and branch off into fork-specific code.
When a new fork is introduced, it is sometimes forgotten to update all
of those branch locations. This patch introduces a compile-time check
that ensures that all branches need to be covered exhaustively. This is
done by replacing if-elif structures with case expressions.
This commit is contained in:
Etan Kissling 2021-10-04 10:31:21 +02:00 committed by GitHub
parent 4a7a62c072
commit 2bbffbde10
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 49 additions and 54 deletions

View File

@ -311,12 +311,10 @@ func contains*(dag: ChainDAGRef, root: Eth2Digest): bool =
proc containsBlock( proc containsBlock(
cfg: RuntimeConfig, db: BeaconChainDB, blck: BlockRef): bool = cfg: RuntimeConfig, db: BeaconChainDB, blck: BlockRef): bool =
if blck.slot.epoch < cfg.ALTAIR_FORK_EPOCH: case cfg.stateForkAtEpoch(blck.slot.epoch)
db.containsBlockPhase0(blck.root) of forkMerge: db.containsBlockMerge(blck.root)
elif blck.slot.epoch < cfg.MERGE_FORK_EPOCH: of forkAltair: db.containsBlockAltair(blck.root)
db.containsBlockAltair(blck.root) of forkPhase0: db.containsBlockPhase0(blck.root)
else:
db.containsBlockMerge(blck.root)
func isStateCheckpoint(bs: BlockSlot): bool = func isStateCheckpoint(bs: BlockSlot): bool =
## State checkpoints are the points in time for which we store full state ## State checkpoints are the points in time for which we store full state
@ -578,23 +576,24 @@ proc getState(
func restore() = func restore() =
assign(v[], restoreAddr[].data) assign(v[], restoreAddr[].data)
if blck.slot.epoch < dag.cfg.ALTAIR_FORK_EPOCH: case dag.cfg.stateForkAtEpoch(blck.slot.epoch)
if state.data.beaconStateFork != forkPhase0: of forkMerge:
state.data = (ref ForkedHashedBeaconState)(beaconStateFork: forkPhase0)[] if state.data.beaconStateFork != forkMerge:
state.data = (ref ForkedHashedBeaconState)(beaconStateFork: forkMerge)[]
if not dag.db.getState(stateRoot, state.data.hbsPhase0.data, restore): if not dag.db.getMergeState(stateRoot, state.data.hbsMerge.data, restore):
return false return false
elif blck.slot.epoch < dag.cfg.MERGE_FORK_EPOCH: of forkAltair:
if state.data.beaconStateFork != forkAltair: if state.data.beaconStateFork != forkAltair:
state.data = (ref ForkedHashedBeaconState)(beaconStateFork: forkAltair)[] state.data = (ref ForkedHashedBeaconState)(beaconStateFork: forkAltair)[]
if not dag.db.getAltairState(stateRoot, state.data.hbsAltair.data, restore): if not dag.db.getAltairState(stateRoot, state.data.hbsAltair.data, restore):
return false return false
else: of forkPhase0:
if state.data.beaconStateFork != forkMerge: if state.data.beaconStateFork != forkPhase0:
state.data = (ref ForkedHashedBeaconState)(beaconStateFork: forkMerge)[] state.data = (ref ForkedHashedBeaconState)(beaconStateFork: forkPhase0)[]
if not dag.db.getMergeState(stateRoot, state.data.hbsMerge.data, restore): if not dag.db.getState(stateRoot, state.data.hbsPhase0.data, restore):
return false return false
state.blck = blck state.blck = blck
@ -613,12 +612,10 @@ template forkAtEpoch*(dag: ChainDAGRef, epoch: Epoch): Fork =
forkAtEpoch(dag.cfg, epoch) forkAtEpoch(dag.cfg, epoch)
proc forkDigestAtEpoch*(dag: ChainDAGRef, epoch: Epoch): ForkDigest = proc forkDigestAtEpoch*(dag: ChainDAGRef, epoch: Epoch): ForkDigest =
if epoch < dag.cfg.ALTAIR_FORK_EPOCH: case dag.cfg.stateForkAtEpoch(epoch)
dag.forkDigests.phase0 of forkMerge: dag.forkDigests.merge
elif epoch < dag.cfg.MERGE_FORK_EPOCH: of forkAltair: dag.forkDigests.altair
dag.forkDigests.altair of forkPhase0: dag.forkDigests.phase0
else:
dag.forkDigests.merge
proc getState(dag: ChainDAGRef, state: var StateData, bs: BlockSlot): bool = proc getState(dag: ChainDAGRef, state: var StateData, bs: BlockSlot): bool =
## Load a state from the database given a block and a slot - this will first ## Load a state from the database given a block and a slot - this will first

View File

@ -2140,12 +2140,10 @@ func getRandomSubnetId*(node: Eth2Node): SubnetId =
node.rng[].rand(ATTESTATION_SUBNET_COUNT - 1).SubnetId node.rng[].rand(ATTESTATION_SUBNET_COUNT - 1).SubnetId
func forkDigestAtEpoch(node: Eth2Node, epoch: Epoch): ForkDigest = func forkDigestAtEpoch(node: Eth2Node, epoch: Epoch): ForkDigest =
if epoch < node.cfg.ALTAIR_FORK_EPOCH: case node.cfg.stateForkAtEpoch(epoch)
node.forkDigests.phase0 of forkMerge: node.forkDigests.merge
elif epoch < node.cfg.MERGE_FORK_EPOCH: of forkAltair: node.forkDigests.altair
node.forkDigests.altair of forkPhase0: node.forkDigests.phase0
else:
node.forkDigests.merge
proc getWallEpoch(node: Eth2Node): Epoch = proc getWallEpoch(node: Eth2Node): Epoch =
node.getBeaconTime().slotOrZero.epoch node.getBeaconTime().slotOrZero.epoch

View File

@ -278,6 +278,17 @@ proc check_voluntary_exit*(
# Derived utilities # Derived utilities
func stateForkAtEpoch*(cfg: RuntimeConfig, epoch: Epoch): BeaconStateFork =
## Return the current fork for the given epoch.
static:
doAssert forkMerge > forkAltair
doAssert forkAltair > forkPhase0
doAssert GENESIS_EPOCH == 0
if epoch >= cfg.MERGE_FORK_EPOCH: forkMerge
elif epoch >= cfg.ALTAIR_FORK_EPOCH: forkAltair
else: forkPhase0
# https://github.com/ethereum/eth2.0-specs/blob/v1.0.1/specs/phase0/beacon-chain.md#get_current_epoch # https://github.com/ethereum/eth2.0-specs/blob/v1.0.1/specs/phase0/beacon-chain.md#get_current_epoch
func get_current_epoch*(x: ForkedHashedBeaconState): Epoch = func get_current_epoch*(x: ForkedHashedBeaconState): Epoch =
## Return the current epoch. ## Return the current epoch.
@ -379,31 +390,22 @@ chronicles.formatIt ForkedSignedBeaconBlock: it.shortLog
chronicles.formatIt ForkedTrustedSignedBeaconBlock: it.shortLog chronicles.formatIt ForkedTrustedSignedBeaconBlock: it.shortLog
proc forkAtEpoch*(cfg: RuntimeConfig, epoch: Epoch): Fork = proc forkAtEpoch*(cfg: RuntimeConfig, epoch: Epoch): Fork =
doAssert cfg.ALTAIR_FORK_EPOCH <= cfg.MERGE_FORK_EPOCH case cfg.stateForkAtEpoch(epoch)
if epoch < cfg.ALTAIR_FORK_EPOCH: of forkMerge: cfg.mergeFork
genesisFork(cfg) of forkAltair: cfg.altairFork
elif epoch < cfg.MERGE_FORK_EPOCH: of forkPhase0: cfg.genesisFork
altairFork(cfg)
else:
mergeFork(cfg)
proc forkVersionAtEpoch*(cfg: RuntimeConfig, epoch: Epoch): Version = proc forkVersionAtEpoch*(cfg: RuntimeConfig, epoch: Epoch): Version =
doAssert cfg.ALTAIR_FORK_EPOCH <= cfg.MERGE_FORK_EPOCH case cfg.stateForkAtEpoch(epoch)
if epoch < cfg.ALTAIR_FORK_EPOCH: of forkMerge: cfg.MERGE_FORK_VERSION
cfg.GENESIS_FORK_VERSION of forkAltair: cfg.ALTAIR_FORK_VERSION
elif epoch < cfg.MERGE_FORK_EPOCH: of forkPhase0: cfg.GENESIS_FORK_VERSION
cfg.ALTAIR_FORK_VERSION
else:
cfg.MERGE_FORK_VERSION
proc nextForkEpochAtEpoch*(cfg: RuntimeConfig, epoch: Epoch): Epoch = proc nextForkEpochAtEpoch*(cfg: RuntimeConfig, epoch: Epoch): Epoch =
doAssert cfg.ALTAIR_FORK_EPOCH <= cfg.MERGE_FORK_EPOCH case cfg.stateForkAtEpoch(epoch)
if epoch < cfg.ALTAIR_FORK_EPOCH: of forkMerge: FAR_FUTURE_EPOCH
cfg.ALTAIR_FORK_EPOCH of forkAltair: cfg.MERGE_FORK_EPOCH
elif epoch < cfg.MERGE_FORK_EPOCH: of forkPhase0: cfg.ALTAIR_FORK_EPOCH
cfg.MERGE_FORK_EPOCH
else:
FAR_FUTURE_EPOCH
func getForkSchedule*(cfg: RuntimeConfig): array[2, Fork] = func getForkSchedule*(cfg: RuntimeConfig): array[2, Fork] =
## This procedure returns list of known and/or scheduled forks. ## This procedure returns list of known and/or scheduled forks.

View File

@ -391,12 +391,10 @@ cli do(slots = SLOTS_PER_EPOCH * 6,
if blockRatio > 0.0: if blockRatio > 0.0:
withTimer(timers[t]): withTimer(timers[t]):
if slot.epoch < dag.cfg.ALTAIR_FORK_EPOCH: case dag.cfg.stateForkAtEpoch(slot.epoch)
proposePhase0Block(slot) of forkMerge: proposeMergeBlock(slot)
elif slot.epoch < dag.cfg.MERGE_FORK_EPOCH: of forkAltair: proposeAltairBlock(slot)
proposeAltairBlock(slot) of forkPhase0: proposePhase0Block(slot)
else:
proposeMergeBlock(slot)
if attesterRatio > 0.0: if attesterRatio > 0.0:
withTimer(timers[tAttest]): withTimer(timers[tAttest]):
handleAttestations(slot) handleAttestations(slot)