VC: Fix forks management behavior. (#6698)

* Fix issue "ignore _VERSION configuration value if _EPOCH == FAR_FUTURE_EPOCH".
Add `OptionalForks` constant which should provide default values for _VERSION/_EPOCH.
Fix Fork schedule should update Fork's configuration with _EPOCH values.

* Fix compilation error.

* Add comment why and how `OptionalForks` should be maintained.
This commit is contained in:
Eugene Kabanov 2024-11-02 11:59:07 +02:00 committed by GitHub
parent 6cf388065d
commit 7726f39004
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 60 additions and 16 deletions

View File

@ -48,7 +48,8 @@ func getOrDefault*(info: VCRuntimeConfig, name: string, default: Epoch): Epoch =
func getForkVersion(
info: VCRuntimeConfig,
consensusFork: ConsensusFork
consensusFork: ConsensusFork,
optionalForks: set[ConsensusFork] = {}
): Result[Version, string] =
let
key = consensusFork.forkVersionConfigKey()
@ -56,7 +57,10 @@ func getForkVersion(
try:
info[key]
except KeyError:
return err("Forks configuration missing value " & $consensusFork)
if consensusFork in optionalForks:
"0xFFFFFFFF"
else:
return err("Forks configuration missing value " & $consensusFork)
var value: Version
try:
hexToByteArrayStrict(stringValue, distinctBase(value))
@ -64,8 +68,11 @@ func getForkVersion(
return err(key & " is invalid, reason " & exc.msg)
ok(value)
func getForkEpoch(info: VCRuntimeConfig,
consensusFork: ConsensusFork): Result[Epoch, string] =
func getForkEpoch(
info: VCRuntimeConfig,
consensusFork: ConsensusFork,
optionalForks: set[ConsensusFork] = {}
): Result[Epoch, string] =
if consensusFork > ConsensusFork.Phase0:
let
key = consensusFork.forkEpochConfigKey()
@ -73,7 +80,10 @@ func getForkEpoch(info: VCRuntimeConfig,
try:
info[key]
except KeyError:
return err("Forks configuration missing value " & $consensusFork)
if consensusFork in optionalForks:
"18446744073709551615"
else:
return err("Forks configuration missing value " & $consensusFork)
numValue = Base10.decode(uint64, stringValue).valueOr:
return err(key & " is invalid, reason " & $error)
ok(Epoch(numValue))
@ -84,7 +94,8 @@ template toString(epoch: Epoch): string =
Base10.toString(uint64(epoch))
func getConsensusForkConfig*(
info: VCRuntimeConfig
info: VCRuntimeConfig,
optionalForks: set[ConsensusFork] = {}
): Result[VCForkConfig, string] =
## This extracts all `_FORK_VERSION` and `_FORK_EPOCH` constants
var
@ -92,8 +103,8 @@ func getConsensusForkConfig*(
presence: set[ConsensusFork]
for fork in ConsensusFork:
let
forkVersion = ? info.getForkVersion(fork)
forkEpoch = ? info.getForkEpoch(fork)
forkVersion = ? info.getForkVersion(fork, optionalForks)
forkEpoch = ? info.getForkEpoch(fork, optionalForks)
config[fork] = ForkConfigItem(version: forkVersion, epoch: forkEpoch)
presence.incl(fork)

View File

@ -46,6 +46,16 @@ const
ZeroTimeDiff* = TimeDiff(nanoseconds: 0'i64)
static: doAssert(high(ConsensusFork) == ConsensusFork.Electra,
"Update OptionalForks constant!")
const
OptionalForks* = {ConsensusFork.Electra}
## When a new ConsensusFork is added and before this fork is activated on
## `mainnet`, it should be part of `OptionalForks`.
## In this case, the client will ignore missing <FORKNAME>_VERSION
## and <FORKNAME>_EPOCH constants from the data reported by BN via
## `/eth/v1/config/spec` API call.
type
ServiceState* {.pure.} = enum
Initialized, Running, Error, Closing, Closed
@ -1487,24 +1497,25 @@ proc validateForkCompatibility(
forkConfig: VCForkConfig
): Result[void, string] =
let
item =
storedConfig =
try:
vc.forkConfig.get()[consensusFork]
except KeyError:
raiseAssert "Fork should be present in configuration"
if forkVersion != item.version:
return err("Beacon node has conflicting " &
consensusFork.forkVersionConfigKey() & " value")
if forkEpoch != item.epoch:
if forkEpoch != storedConfig.epoch:
if forkEpoch == FAR_FUTURE_EPOCH:
return err("Beacon node do not know about " &
$consensusFork & " starting epoch")
else:
if item.epoch != FAR_FUTURE_EPOCH:
if storedConfig.epoch != FAR_FUTURE_EPOCH:
return err("Beacon node has conflicting " &
consensusFork.forkEpochConfigKey() & " value")
else:
if forkEpoch != FAR_FUTURE_EPOCH:
if forkVersion != storedConfig.version:
return err("Beacon node has conflicting " &
consensusFork.forkVersionConfigKey() & " value")
ok()
proc updateRuntimeConfig*(
@ -1512,7 +1523,7 @@ proc updateRuntimeConfig*(
node: BeaconNodeServerRef,
info: VCRuntimeConfig
): Result[void, string] =
let forkConfig = ? info.getConsensusForkConfig()
let forkConfig = ? info.getConsensusForkConfig(OptionalForks)
if vc.forkConfig.isNone():
vc.forkConfig = Opt.some(forkConfig)
@ -1526,11 +1537,32 @@ proc updateRuntimeConfig*(
# Save newly discovered forks.
if localForkConfig[fork].epoch == FAR_FUTURE_EPOCH:
localForkConfig[fork].epoch = item.epoch
localForkConfig[fork].version = item.version
except KeyError:
raiseAssert "All the forks should be present inside forks configuration"
vc.forkConfig = Opt.some(localForkConfig)
ok()
proc updateForkConfig*(vc: ValidatorClientRef) =
if vc.forkConfig.isNone():
return
var config = vc.forkConfig.get()
for fork in ConsensusFork:
let configItem =
try:
config[fork]
except KeyError:
raiseAssert "All the forks should be present inside forks configuration"
for scheduleItem in vc.forks:
if scheduleItem.current_version == configItem.version:
if configItem.epoch == FAR_FUTURE_EPOCH:
# Fork schedule knows about Fork's epoch.
config[fork] = ForkConfigItem(version: scheduleItem.current_version,
epoch: scheduleItem.epoch)
break
vc.forkConfig = Opt.some(config)
proc `+`*(slot: Slot, epochs: Epoch): Slot =
slot + uint64(epochs) * SLOTS_PER_EPOCH

View File

@ -69,6 +69,7 @@ proc pollForFork(vc: ValidatorClientRef) {.async: (raises: [CancelledError]).} =
if (len(vc.forks) == 0) or (vc.forks != sortedForks):
vc.forks = sortedForks
vc.updateForkConfig()
notice "Fork schedule updated", fork_schedule = sortedForks
vc.forksAvailable.fire()