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(
cfg: RuntimeConfig, db: BeaconChainDB, blck: BlockRef): bool =
if blck.slot.epoch < cfg.ALTAIR_FORK_EPOCH:
db.containsBlockPhase0(blck.root)
elif blck.slot.epoch < cfg.MERGE_FORK_EPOCH:
db.containsBlockAltair(blck.root)
else:
db.containsBlockMerge(blck.root)
case cfg.stateForkAtEpoch(blck.slot.epoch)
of forkMerge: db.containsBlockMerge(blck.root)
of forkAltair: db.containsBlockAltair(blck.root)
of forkPhase0: db.containsBlockPhase0(blck.root)
func isStateCheckpoint(bs: BlockSlot): bool =
## State checkpoints are the points in time for which we store full state
@ -578,23 +576,24 @@ proc getState(
func restore() =
assign(v[], restoreAddr[].data)
if blck.slot.epoch < dag.cfg.ALTAIR_FORK_EPOCH:
if state.data.beaconStateFork != forkPhase0:
state.data = (ref ForkedHashedBeaconState)(beaconStateFork: forkPhase0)[]
case dag.cfg.stateForkAtEpoch(blck.slot.epoch)
of forkMerge:
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
elif blck.slot.epoch < dag.cfg.MERGE_FORK_EPOCH:
of forkAltair:
if state.data.beaconStateFork != forkAltair:
state.data = (ref ForkedHashedBeaconState)(beaconStateFork: forkAltair)[]
if not dag.db.getAltairState(stateRoot, state.data.hbsAltair.data, restore):
return false
else:
if state.data.beaconStateFork != forkMerge:
state.data = (ref ForkedHashedBeaconState)(beaconStateFork: forkMerge)[]
of forkPhase0:
if state.data.beaconStateFork != forkPhase0:
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
state.blck = blck
@ -613,12 +612,10 @@ template forkAtEpoch*(dag: ChainDAGRef, epoch: Epoch): Fork =
forkAtEpoch(dag.cfg, epoch)
proc forkDigestAtEpoch*(dag: ChainDAGRef, epoch: Epoch): ForkDigest =
if epoch < dag.cfg.ALTAIR_FORK_EPOCH:
dag.forkDigests.phase0
elif epoch < dag.cfg.MERGE_FORK_EPOCH:
dag.forkDigests.altair
else:
dag.forkDigests.merge
case dag.cfg.stateForkAtEpoch(epoch)
of forkMerge: dag.forkDigests.merge
of forkAltair: dag.forkDigests.altair
of forkPhase0: dag.forkDigests.phase0
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

View File

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

View File

@ -278,6 +278,17 @@ proc check_voluntary_exit*(
# 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
func get_current_epoch*(x: ForkedHashedBeaconState): Epoch =
## Return the current epoch.
@ -379,31 +390,22 @@ chronicles.formatIt ForkedSignedBeaconBlock: it.shortLog
chronicles.formatIt ForkedTrustedSignedBeaconBlock: it.shortLog
proc forkAtEpoch*(cfg: RuntimeConfig, epoch: Epoch): Fork =
doAssert cfg.ALTAIR_FORK_EPOCH <= cfg.MERGE_FORK_EPOCH
if epoch < cfg.ALTAIR_FORK_EPOCH:
genesisFork(cfg)
elif epoch < cfg.MERGE_FORK_EPOCH:
altairFork(cfg)
else:
mergeFork(cfg)
case cfg.stateForkAtEpoch(epoch)
of forkMerge: cfg.mergeFork
of forkAltair: cfg.altairFork
of forkPhase0: cfg.genesisFork
proc forkVersionAtEpoch*(cfg: RuntimeConfig, epoch: Epoch): Version =
doAssert cfg.ALTAIR_FORK_EPOCH <= cfg.MERGE_FORK_EPOCH
if epoch < cfg.ALTAIR_FORK_EPOCH:
cfg.GENESIS_FORK_VERSION
elif epoch < cfg.MERGE_FORK_EPOCH:
cfg.ALTAIR_FORK_VERSION
else:
cfg.MERGE_FORK_VERSION
case cfg.stateForkAtEpoch(epoch)
of forkMerge: cfg.MERGE_FORK_VERSION
of forkAltair: cfg.ALTAIR_FORK_VERSION
of forkPhase0: cfg.GENESIS_FORK_VERSION
proc nextForkEpochAtEpoch*(cfg: RuntimeConfig, epoch: Epoch): Epoch =
doAssert cfg.ALTAIR_FORK_EPOCH <= cfg.MERGE_FORK_EPOCH
if epoch < cfg.ALTAIR_FORK_EPOCH:
cfg.ALTAIR_FORK_EPOCH
elif epoch < cfg.MERGE_FORK_EPOCH:
cfg.MERGE_FORK_EPOCH
else:
FAR_FUTURE_EPOCH
case cfg.stateForkAtEpoch(epoch)
of forkMerge: FAR_FUTURE_EPOCH
of forkAltair: cfg.MERGE_FORK_EPOCH
of forkPhase0: cfg.ALTAIR_FORK_EPOCH
func getForkSchedule*(cfg: RuntimeConfig): array[2, Fork] =
## 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:
withTimer(timers[t]):
if slot.epoch < dag.cfg.ALTAIR_FORK_EPOCH:
proposePhase0Block(slot)
elif slot.epoch < dag.cfg.MERGE_FORK_EPOCH:
proposeAltairBlock(slot)
else:
proposeMergeBlock(slot)
case dag.cfg.stateForkAtEpoch(slot.epoch)
of forkMerge: proposeMergeBlock(slot)
of forkAltair: proposeAltairBlock(slot)
of forkPhase0: proposePhase0Block(slot)
if attesterRatio > 0.0:
withTimer(timers[tAttest]):
handleAttestations(slot)