From 4e9bc7f57011214b789609b57a8bbf2f9c8fb5ca Mon Sep 17 00:00:00 2001 From: Etan Kissling Date: Mon, 26 Feb 2024 09:48:07 +0100 Subject: [PATCH] add EIP-7044 support to keymanager API (#5959) * add EIP-7044 support to keymanager API When trying to sign `VoluntaryExit` via keymanager API, the logic is not yet aware of EIP-7044 (part of Deneb). This patch adds missing EIP-7044 support to the keymanager API as well. As part of this, the VC needs to become aware about: - `CAPELLA_FORK_VERSION`: To correctly form the EIP-7044 signing domain. The fork schedule does not indicate which of the results, if any, corresponds to Capella. - `CAPELLA_FORK_EPOCH`: To detect whether Capella was scheduled. If a BN does not have it in its config while other BNs have it, this leads to a log if Capella has not activated yet, or marks the BN as incompatible if Capella already activated. - `DENEB_FORK_EPOCH`: To check whether EIP-7044 logic should be used. Related PRs: - #5120 added support for processing EIP-7044 `VoluntaryExit` messages as part of the state transition functions (tested by EF spec tests). - #5953 synced the support from #5120 to gossip validation. - #5954 added support to the `nimbus_beacon_node deposits exit` command. - #5956 contains an alternative generic version of `VCForkConfig`. * address reviewer feedback: letter case, module location, double lookup --------- Co-authored-by: cheatfate * Update beacon_chain/rpc/rest_constants.nim * move `VCRuntimeConfig` back to `rest_types` --------- Co-authored-by: cheatfate * fix `getForkVersion` helper --------- Co-authored-by: cheatfate --- beacon_chain/deposits.nim | 34 ++---- beacon_chain/nimbus_beacon_node.nim | 8 ++ beacon_chain/nimbus_validator_client.nim | 17 +++ beacon_chain/rpc/rest_constants.nim | 4 + beacon_chain/rpc/rest_key_management_api.nim | 14 ++- .../spec/eth2_apis/rest_beacon_client.nim | 6 +- .../spec/eth2_apis/rest_fork_config.nim | 97 ++++++++++++++++ beacon_chain/spec/signatures.nim | 27 ++++- beacon_chain/validator_client/common.nim | 107 ++++++++++++------ .../validator_client/duties_service.nim | 12 +- .../validators/keystore_management.nim | 10 ++ beacon_chain/validators/validator_pool.nim | 1 + 12 files changed, 270 insertions(+), 67 deletions(-) create mode 100644 beacon_chain/spec/eth2_apis/rest_fork_config.nim diff --git a/beacon_chain/deposits.nim b/beacon_chain/deposits.nim index 33718c99c5..2af065cbb7 100644 --- a/beacon_chain/deposits.nim +++ b/beacon_chain/deposits.nim @@ -220,33 +220,19 @@ proc restValidatorExit(config: BeaconNodeConf) {.async.} = reason = exc.msg quit 1 + # https://github.com/ethereum/consensus-specs/blob/v1.4.0-beta.6/specs/phase0/beacon-chain.md#voluntary-exits + # https://github.com/ethereum/consensus-specs/blob/v1.4.0-beta.0/specs/deneb/beacon-chain.md#modified-process_voluntary_exit let signingFork = try: let response = await client.getSpecVC() if response.status == 200: - let - spec = response.data.data - denebForkEpoch = - block: - let s = spec.getOrDefault("DENEB_FORK_EPOCH", $FAR_FUTURE_EPOCH) - Epoch(Base10.decode(uint64, s).get(uint64(FAR_FUTURE_EPOCH))) - # https://github.com/ethereum/consensus-specs/blob/v1.4.0-beta.6/specs/phase0/beacon-chain.md#voluntary-exits - # https://github.com/ethereum/consensus-specs/blob/v1.4.0-beta.0/specs/deneb/beacon-chain.md#modified-process_voluntary_exit - if currentEpoch >= denebForkEpoch: - let capellaForkVersion = - block: - var res: Version - # CAPELLA_FOR_VERSION has specific format - "0x01000000", so - # default empty string is invalid, so `hexToByteArrayStrict` - # will raise exception on empty string. - let s = spec.getOrDefault("CAPELLA_FORK_VERSION", "") - hexToByteArrayStrict(s, distinctBase(res)) - res - Fork( - current_version: capellaForkVersion, - previous_version: capellaForkVersion, - epoch: GENESIS_EPOCH) # irrelevant when current/previous identical - else: - fork + let forkConfig = response.data.data.getConsensusForkConfig() + if forkConfig.isErr: + raise newException(RestError, "Invalid config: " & forkConfig.error) + let capellaForkVersion = forkConfig.get.capellaVersion.valueOr: + raise newException(RestError, + ConsensusFork.Capella.forkVersionConfigKey() & " missing") + voluntary_exit_signature_fork( + fork, capellaForkVersion, currentEpoch, forkConfig.get.denebEpoch) else: raise newException(RestError, "Error response (" & $response.status & ")") except CatchableError as exc: diff --git a/beacon_chain/nimbus_beacon_node.nim b/beacon_chain/nimbus_beacon_node.nim index 364ade0e78..bdfbaffda5 100644 --- a/beacon_chain/nimbus_beacon_node.nim +++ b/beacon_chain/nimbus_beacon_node.nim @@ -755,6 +755,12 @@ proc init*(T: type BeaconNode, withState(dag.headState): getValidator(forkyState().data.validators.asSeq(), pubkey) + func getCapellaForkVersion(): Opt[Version] = + Opt.some(cfg.CAPELLA_FORK_VERSION) + + func getDenebForkEpoch(): Opt[Epoch] = + Opt.some(cfg.DENEB_FORK_EPOCH) + proc getForkForEpoch(epoch: Epoch): Opt[Fork] = Opt.some(dag.forkAtEpoch(epoch)) @@ -784,6 +790,8 @@ proc init*(T: type BeaconNode, config.getPayloadBuilderAddress, getValidatorAndIdx, getBeaconTime, + getCapellaForkVersion, + getDenebForkEpoch, getForkForEpoch, getGenesisRoot) else: nil diff --git a/beacon_chain/nimbus_validator_client.nim b/beacon_chain/nimbus_validator_client.nim index 5899ef6fb6..e4bdfd0a78 100644 --- a/beacon_chain/nimbus_validator_client.nim +++ b/beacon_chain/nimbus_validator_client.nim @@ -4,6 +4,9 @@ # * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT). # * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0). # at your option. This file may not be copied, modified, or distributed except according to those terms. + +{.push raises: [].} + import stew/io2, presto, metrics, metrics/chronos_httpserver, ./rpc/rest_key_management_api, @@ -348,6 +351,18 @@ proc asyncInit(vc: ValidatorClientRef): Future[ValidatorClientRef] {.async.} = let keymanagerInitResult = initKeymanagerServer(vc.config, nil) + func getCapellaForkVersion(): Opt[Version] = + if vc.runtimeConfig.forkConfig.isSome(): + vc.runtimeConfig.forkConfig.get().capellaVersion + else: + Opt.none(Version) + + func getDenebForkEpoch(): Opt[Epoch] = + if vc.runtimeConfig.forkConfig.isSome(): + Opt.some(vc.runtimeConfig.forkConfig.get().denebEpoch) + else: + Opt.none(Epoch) + proc getForkForEpoch(epoch: Epoch): Opt[Fork] = if len(vc.forks) > 0: Opt.some(vc.forkAtEpoch(epoch)) @@ -379,6 +394,8 @@ proc asyncInit(vc: ValidatorClientRef): Future[ValidatorClientRef] {.async.} = Opt.none(string), nil, vc.beaconClock.getBeaconTimeFn, + getCapellaForkVersion, + getDenebForkEpoch, getForkForEpoch, getGenesisRoot ) diff --git a/beacon_chain/rpc/rest_constants.nim b/beacon_chain/rpc/rest_constants.nim index 58e327ed36..33372581f5 100644 --- a/beacon_chain/rpc/rest_constants.nim +++ b/beacon_chain/rpc/rest_constants.nim @@ -241,6 +241,10 @@ const "The given Merkle proof is invalid" InvalidMerkleProofIndexError* = "The given Merkle proof index is invalid" + FailedToObtainForkVersionError* = + "Failed to obtain fork version" + FailedToObtainConsensusForkError* = + "Failed to obtain consensus fork information" FailedToObtainForkError* = "Failed to obtain fork information" InvalidTimestampValue* = diff --git a/beacon_chain/rpc/rest_key_management_api.nim b/beacon_chain/rpc/rest_key_management_api.nim index 6d697168cd..84a8f14bf5 100644 --- a/beacon_chain/rpc/rest_key_management_api.nim +++ b/beacon_chain/rpc/rest_key_management_api.nim @@ -1,9 +1,12 @@ +# beacon_chain # Copyright (c) 2021-2024 Status Research & Development GmbH # Licensed and distributed under either of # * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT). # * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0). # at your option. This file may not be copied, modified, or distributed except according to those terms. +{.push raises: [].} + # NOTE: This module has been used in both `beacon_node` and `validator_client`, # please keep imports clear of `rest_utils` or any other module which imports # beacon node's specific networking code. @@ -561,6 +564,7 @@ proc installKeymanagerHandlers*(router: var RestRouter, host: KeymanagerHost) = let qpubkey = pubkey.valueOr: return keymanagerApiError(Http400, InvalidValidatorPublicKey) + currentEpoch = host.getBeaconTimeFn().slotOrZero().epoch() qepoch = if epoch.isSome(): let res = epoch.get() @@ -568,7 +572,7 @@ proc installKeymanagerHandlers*(router: var RestRouter, host: KeymanagerHost) = return keymanagerApiError(Http400, InvalidEpochValueError) res.get() else: - host.getBeaconTimeFn().slotOrZero().epoch() + currentEpoch validator = block: let res = host.validatorPool[].getValidator(qpubkey).valueOr: @@ -581,10 +585,16 @@ proc installKeymanagerHandlers*(router: var RestRouter, host: KeymanagerHost) = validator_index: uint64(validator.index.get())) fork = host.getForkFn(qepoch).valueOr: return keymanagerApiError(Http500, FailedToObtainForkError) + capellaForkVersion = host.getCapellaForkVersionFn().valueOr: + return keymanagerApiError(Http500, FailedToObtainForkVersionError) + denebForkEpoch = host.getDenebForkEpochFn().valueOr: + return keymanagerApiError(Http500, FailedToObtainConsensusForkError) + signingFork = voluntary_exit_signature_fork( + fork, capellaForkVersion, currentEpoch, denebForkEpoch) signature = try: let res = await validator.getValidatorExitSignature( - fork, host.getGenesisFn(), voluntaryExit) + signingFork, host.getGenesisFn(), voluntaryExit) if res.isErr(): return keymanagerApiError(Http500, res.error()) res.get() diff --git a/beacon_chain/spec/eth2_apis/rest_beacon_client.nim b/beacon_chain/spec/eth2_apis/rest_beacon_client.nim index f6b3bc24b4..66c0ac3417 100644 --- a/beacon_chain/spec/eth2_apis/rest_beacon_client.nim +++ b/beacon_chain/spec/eth2_apis/rest_beacon_client.nim @@ -13,7 +13,8 @@ import rest_beacon_calls, rest_builder_calls, rest_config_calls, rest_debug_calls, rest_keymanager_calls, rest_light_client_calls, rest_node_calls, rest_validator_calls, - rest_nimbus_calls, rest_event_calls, rest_common + rest_nimbus_calls, rest_event_calls, rest_common, + rest_fork_config ] export @@ -21,4 +22,5 @@ export rest_beacon_calls, rest_builder_calls, rest_config_calls, rest_debug_calls, rest_keymanager_calls, rest_light_client_calls, rest_node_calls, rest_validator_calls, - rest_nimbus_calls, rest_event_calls, rest_common + rest_nimbus_calls, rest_event_calls, rest_common, + rest_fork_config diff --git a/beacon_chain/spec/eth2_apis/rest_fork_config.nim b/beacon_chain/spec/eth2_apis/rest_fork_config.nim new file mode 100644 index 0000000000..f263cf2597 --- /dev/null +++ b/beacon_chain/spec/eth2_apis/rest_fork_config.nim @@ -0,0 +1,97 @@ +# beacon_chain +# Copyright (c) 2018-2024 Status Research & Development GmbH +# Licensed and distributed under either of +# * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT). +# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0). +# at your option. This file may not be copied, modified, or distributed except according to those terms. + +{.push raises: [].} + +import + std/strutils, + stew/[base10, byteutils], + ../forks + +from ./rest_types import VCRuntimeConfig + +export forks, rest_types + +type VCForkConfig* = object + altairEpoch*: Epoch + capellaVersion*: Opt[Version] + capellaEpoch*: Epoch + denebEpoch*: Epoch + +func forkVersionConfigKey*(consensusFork: ConsensusFork): string = + if consensusFork > ConsensusFork.Phase0: + ($consensusFork).toUpperAscii() & "_FORK_VERSION" + else: + "GENESIS_FORK_VERSION" + +func forkEpochConfigKey*(consensusFork: ConsensusFork): string = + doAssert consensusFork > ConsensusFork.Phase0 + ($consensusFork).toUpperAscii() & "_FORK_EPOCH" + +proc getOrDefault*(info: VCRuntimeConfig, name: string, + default: uint64): uint64 = + let numstr = info.getOrDefault(name, "missing") + if numstr == "missing": return default + Base10.decode(uint64, numstr).valueOr: + return default + +proc getOrDefault*(info: VCRuntimeConfig, name: string, default: Epoch): Epoch = + Epoch(info.getOrDefault(name, uint64(default))) + +func getForkVersion( + info: VCRuntimeConfig, + consensusFork: Consensusfork): Result[Opt[Version], string] = + let key = consensusFork.forkVersionConfigKey() + let stringValue = info.getOrDefault(key, "missing") + if stringValue == "missing": return ok Opt.none(Version) + var value: Version + try: + hexToByteArrayStrict(stringValue, distinctBase(value)) + except ValueError as exc: + return err(key & " is invalid: " & exc.msg) + ok Opt.some value + +func getForkEpoch(info: VCRuntimeConfig, consensusFork: ConsensusFork): Epoch = + if consensusFork > ConsensusFork.Phase0: + let key = consensusFork.forkEpochConfigKey() + info.getOrDefault(key, FAR_FUTURE_EPOCH) + else: + GENESIS_EPOCH + +func getConsensusForkConfig*( + info: VCRuntimeConfig): Result[VCForkConfig, string] = + ## This extracts all `_FORK_VERSION` and `_FORK_EPOCH` constants + ## that are relevant for Validator Client operation. + ## + ## Note that the fork schedule (`/eth/v1/config/fork_schedule`) cannot be used + ## because it does not indicate whether the forks refer to `ConsensusFork` or + ## to a different fork sequence from an incompatible network (e.g., devnet) + let + res = VCForkConfig( + altairEpoch: info.getForkEpoch(ConsensusFork.Altair), + capellaVersion: ? info.getForkVersion(ConsensusFork.Capella), + capellaEpoch: info.getForkEpoch(ConsensusFork.Capella), + denebEpoch: info.getForkEpoch(ConsensusFork.Deneb)) + + if res.capellaEpoch < res.altairEpoch: + return err( + "Fork epochs are inconsistent, " & $ConsensusFork.Capella & + " is scheduled at epoch " & $res.capellaEpoch & + " which is before prior fork epoch " & $res.altairEpoch) + if res.denebEpoch < res.capellaEpoch: + return err( + "Fork epochs are inconsistent, " & $ConsensusFork.Deneb & + " is scheduled at epoch " & $res.denebEpoch & + " which is before prior fork epoch " & $res.capellaEpoch) + + if res.capellaEpoch != FAR_FUTURE_EPOCH and res.capellaVersion.isNone: + return err( + "Beacon node has scheduled " & + ConsensusFork.Capella.forkEpochConfigKey() & + " but does not report " & + ConsensusFork.Capella.forkVersionConfigKey()) + ok res diff --git a/beacon_chain/spec/signatures.nim b/beacon_chain/spec/signatures.nim index 36ec7bc52e..946966fab3 100644 --- a/beacon_chain/spec/signatures.nim +++ b/beacon_chain/spec/signatures.nim @@ -216,11 +216,11 @@ func compute_voluntary_exit_signing_root*( fork, DOMAIN_VOLUNTARY_EXIT, epoch, genesis_validators_root) compute_signing_root(voluntary_exit, domain) -func voluntary_exit_signature_fork*( - consensusFork: static ConsensusFork, +func voluntary_exit_signature_fork( + is_post_deneb: static bool, state_fork: Fork, capella_fork_version: Version): Fork = - when consensusFork >= ConsensusFork.Deneb: + when is_post_deneb: # Always use Capella fork version, disregarding `VoluntaryExit` epoch # [Modified in Deneb:EIP7044] Fork( @@ -230,6 +230,27 @@ func voluntary_exit_signature_fork*( else: state_fork +func voluntary_exit_signature_fork*( + consensusFork: static ConsensusFork, + state_fork: Fork, + capella_fork_version: Version): Fork = + const is_post_deneb = (consensusFork >= ConsensusFork.Deneb) + voluntary_exit_signature_fork(is_post_deneb, state_fork, capella_fork_version) + +func voluntary_exit_signature_fork*( + state_fork: Fork, + capella_fork_version: Version, + current_epoch: Epoch, + deneb_fork_epoch: Epoch): Fork = + if current_epoch >= deneb_fork_epoch: + const is_post_deneb = true + voluntary_exit_signature_fork( + is_post_deneb, state_fork, capella_fork_version) + else: + const is_post_deneb = false + voluntary_exit_signature_fork( + is_post_deneb, state_fork, capella_fork_version) + func get_voluntary_exit_signature*( fork: Fork, genesis_validators_root: Eth2Digest, voluntary_exit: VoluntaryExit, diff --git a/beacon_chain/validator_client/common.nim b/beacon_chain/validator_client/common.nim index aa610ae582..db0cb1631f 100644 --- a/beacon_chain/validator_client/common.nim +++ b/beacon_chain/validator_client/common.nim @@ -192,7 +192,7 @@ type waiters*: seq[BlockWaiter] ValidatorRuntimeConfig* = object - altairEpoch*: Opt[Epoch] + forkConfig*: Opt[VCForkConfig] ValidatorClient* = object config*: ValidatorClientConf @@ -518,16 +518,6 @@ proc equals*(info: VCRuntimeConfig, name: string, check: DomainType): bool = proc equals*(info: VCRuntimeConfig, name: string, check: Epoch): bool = info.equals(name, uint64(check)) -proc getOrDefault*(info: VCRuntimeConfig, name: string, - default: uint64): uint64 = - let numstr = info.getOrDefault(name, "missing") - if numstr == "missing": return default - Base10.decode(uint64, numstr).valueOr: - return default - -proc getOrDefault*(info: VCRuntimeConfig, name: string, default: Epoch): Epoch = - Epoch(info.getOrDefault(name, uint64(default))) - proc checkConfig*(c: VCRuntimeConfig): bool = c.equals("MAX_VALIDATORS_PER_COMMITTEE", MAX_VALIDATORS_PER_COMMITTEE) and c.equals("SLOTS_PER_EPOCH", SLOTS_PER_EPOCH) and @@ -1436,33 +1426,84 @@ func `==`*(a, b: SyncCommitteeDuty): bool = proc updateRuntimeConfig*(vc: ValidatorClientRef, node: BeaconNodeServerRef, info: VCRuntimeConfig): Result[void, string] = - if not(info.hasKey("ALTAIR_FORK_EPOCH")): - debug "Beacon node's configuration missing ALTAIR_FORK_EPOCH value", - node = node + var forkConfig = ? info.getConsensusForkConfig() - let - res = info.getOrDefault("ALTAIR_FORK_EPOCH", FAR_FUTURE_EPOCH) - wallEpoch = vc.beaconClock.now().slotOrZero().epoch() - - return - if vc.runtimeConfig.altairEpoch.get(FAR_FUTURE_EPOCH) == FAR_FUTURE_EPOCH: - vc.runtimeConfig.altairEpoch = Opt.some(res) - ok() - else: - if res == vc.runtimeConfig.altairEpoch.get(): - ok() + if vc.runtimeConfig.forkConfig.isNone(): + vc.runtimeConfig.forkConfig = Opt.some(forkConfig) + else: + template localForkConfig: untyped = vc.runtimeConfig.forkConfig.get() + let wallEpoch = vc.beaconClock.now().slotOrZero().epoch() + + proc validateForkVersionCompatibility( + consensusFork: ConsensusFork, + localForkVersion: Opt[Version], + localForkEpoch: Epoch, + forkVersion: Opt[Version]): Result[void, string] = + if localForkVersion.isNone(): + discard # Potentially discovered new fork, save it at end of function else: - if res == FAR_FUTURE_EPOCH: - if wallEpoch < vc.runtimeConfig.altairEpoch.get(): - debug "Beacon node must be updated before Altair activates", + if forkVersion.isSome(): + if forkVersion.get() == localForkVersion.get(): + discard # Already known + else: + return err("Beacon node has conflicting " & + consensusFork.forkVersionConfigKey() & " value") + else: + if wallEpoch < localForkEpoch: + debug "Beacon node must be updated before fork activates", node = node, - altairForkEpoch = vc.runtimeConfig.altairEpoch.get() - ok() + consensusFork, + forkEpoch = localForkEpoch else: - err("Beacon node must be updated and report correct " & - "ALTAIR_FORK_EPOCH value") + return err("Beacon node must be updated and report correct " & + $consensusFork & " config value") + + ? ConsensusFork.Capella.validateForkVersionCompatibility( + localForkConfig.capellaVersion, + localForkConfig.capellaEpoch, + forkConfig.capellaVersion) + + proc validateForkEpochCompatibility( + consensusFork: ConsensusFork, + localForkEpoch: Epoch, + forkEpoch: Epoch): Result[void, string] = + if localForkEpoch == FAR_FUTURE_EPOCH: + discard # Potentially discovered new fork, save it at end of function + else: + if forkEpoch != FAR_FUTURE_EPOCH: + if forkEpoch == localForkEpoch: + discard # Already known + else: + return err("Beacon node has conflicting " & + consensusFork.forkEpochConfigKey() & " value") else: - err("Beacon node has conflicting ALTAIR_FORK_EPOCH value") + if wallEpoch < localForkEpoch: + debug "Beacon node must be updated before fork activates", + node = node, + consensusFork, + forkEpoch = localForkEpoch + else: + return err("Beacon node must be updated and report correct " & + $consensusFork & " config value") + + ? ConsensusFork.Altair.validateForkEpochCompatibility( + localForkConfig.altairEpoch, forkConfig.altairEpoch) + ? ConsensusFork.Capella.validateForkEpochCompatibility( + localForkConfig.capellaEpoch, forkConfig.capellaEpoch) + ? ConsensusFork.Deneb.validateForkEpochCompatibility( + localForkConfig.denebEpoch, forkConfig.denebEpoch) + + # Save newly discovered forks. + if localForkConfig.altairEpoch == FAR_FUTURE_EPOCH: + localForkConfig.altairEpoch = forkConfig.altairEpoch + if localForkConfig.capellaVersion.isNone(): + localForkConfig.capellaVersion = forkConfig.capellaVersion + if localForkConfig.capellaEpoch == FAR_FUTURE_EPOCH: + localForkConfig.capellaEpoch = forkConfig.capellaEpoch + if localForkConfig.denebEpoch == FAR_FUTURE_EPOCH: + localForkConfig.denebEpoch = forkConfig.denebEpoch + + ok() proc `+`*(slot: Slot, epochs: Epoch): Slot = slot + uint64(epochs) * SLOTS_PER_EPOCH diff --git a/beacon_chain/validator_client/duties_service.nim b/beacon_chain/validator_client/duties_service.nim index 340ca49d7e..fa6b84b369 100644 --- a/beacon_chain/validator_client/duties_service.nim +++ b/beacon_chain/validator_client/duties_service.nim @@ -5,6 +5,8 @@ # * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0). # at your option. This file may not be copied, modified, or distributed except according to those terms. +{.push raises: [].} + import std/[sets, sequtils] import chronicles, metrics import "."/[common, api, block_service, selection_proofs] @@ -210,7 +212,8 @@ proc pollForSyncCommitteeDuties*( let vc = service.client indices = toSeq(vc.attachedValidators[].indices()) - epoch = max(period.start_epoch(), vc.runtimeConfig.altairEpoch.get()) + altairEpoch = vc.runtimeConfig.forkConfig.get().altairEpoch + epoch = max(period.start_epoch(), altairEpoch) relevantDuties = block: var duties: seq[RestSyncCommitteeDuty] @@ -369,8 +372,11 @@ proc pollForSyncCommitteeDuties*(service: DutiesServiceRef) {.async.} = let currentSlot = vc.getCurrentSlot().get(Slot(0)) currentEpoch = currentSlot.epoch() - altairEpoch = vc.runtimeConfig.altairEpoch.valueOr: - return + altairEpoch = + if vc.runtimeConfig.forkConfig.isSome(): + vc.runtimeConfig.forkConfig.get().altairEpoch + else: + return if currentEpoch < altairEpoch: # We are not going to poll for sync committee duties until `altairEpoch`. diff --git a/beacon_chain/validators/keystore_management.nim b/beacon_chain/validators/keystore_management.nim index c090419e8a..20d85060c9 100644 --- a/beacon_chain/validators/keystore_management.nim +++ b/beacon_chain/validators/keystore_management.nim @@ -73,6 +73,10 @@ type proc (pubkey: ValidatorPubKey): Opt[ValidatorAndIndex] {.raises: [], gcsafe.} + GetCapellaForkVersionFn* = + proc (): Opt[Version] {.raises: [], gcsafe.} + GetDenebForkEpochFn* = + proc (): Opt[Epoch] {.raises: [], gcsafe.} GetForkFn* = proc (epoch: Epoch): Opt[Fork] {.raises: [], gcsafe.} GetGenesisFn* = @@ -90,6 +94,8 @@ type defaultBuilderAddress*: Opt[string] getValidatorAndIdxFn*: ValidatorPubKeyToDataFn getBeaconTimeFn*: GetBeaconTimeFn + getCapellaForkVersionFn*: GetCapellaForkVersionFn + getDenebForkEpochFn*: GetDenebForkEpochFn getForkFn*: GetForkFn getGenesisFn*: GetGenesisFn @@ -122,6 +128,8 @@ func init*(T: type KeymanagerHost, defaultBuilderAddress: Opt[string], getValidatorAndIdxFn: ValidatorPubKeyToDataFn, getBeaconTimeFn: GetBeaconTimeFn, + getCapellaForkVersionFn: GetCapellaForkVersionFn, + getDenebForkEpochFn: GetDenebForkEpochFn, getForkFn: GetForkFn, getGenesisFn: GetGenesisFn): T = T(validatorPool: validatorPool, @@ -135,6 +143,8 @@ func init*(T: type KeymanagerHost, defaultBuilderAddress: defaultBuilderAddress, getValidatorAndIdxFn: getValidatorAndIdxFn, getBeaconTimeFn: getBeaconTimeFn, + getCapellaForkVersionFn: getCapellaForkVersionFn, + getDenebForkEpochFn: getDenebForkEpochFn, getForkFn: getForkFn, getGenesisFn: getGenesisFn) diff --git a/beacon_chain/validators/validator_pool.nim b/beacon_chain/validators/validator_pool.nim index 19ddbc5b1e..12877a8ca9 100644 --- a/beacon_chain/validators/validator_pool.nim +++ b/beacon_chain/validators/validator_pool.nim @@ -22,6 +22,7 @@ import export streams, keystore, phase0, altair, tables, uri, crypto, + signatures.voluntary_exit_signature_fork, rest_types, eth2_rest_serialization, rest_remote_signer_calls, slashing_protection