diff --git a/beacon_chain/spec/eth2_apis/eth2_rest_serialization.nim b/beacon_chain/spec/eth2_apis/eth2_rest_serialization.nim index fd70b514d..075233ef6 100644 --- a/beacon_chain/spec/eth2_apis/eth2_rest_serialization.nim +++ b/beacon_chain/spec/eth2_apis/eth2_rest_serialization.nim @@ -2996,6 +2996,16 @@ proc readValue*(reader: var JsonReader[RestJson], stacktraces: stacktraces ) +## VCRuntimeConfig +proc readValue*(reader: var JsonReader[RestJson], + value: var VCRuntimeConfig) {. + raises: [SerializationError, IOError, Defect].} = + for fieldName in readObjectFields(reader): + let fieldValue = reader.readValue(string) + if value.hasKeyOrPut(toUpperAscii(fieldName), fieldValue): + let msg = "Multiple `" & fieldName & "` fields found" + reader.raiseUnexpectedField(msg, "VCRuntimeConfig") + proc parseRoot(value: string): Result[Eth2Digest, cstring] = try: ok(Eth2Digest(data: hexToByteArray[32](value))) diff --git a/beacon_chain/spec/eth2_apis/rest_types.nim b/beacon_chain/spec/eth2_apis/rest_types.nim index 355cc4539..58e7e473f 100644 --- a/beacon_chain/spec/eth2_apis/rest_types.nim +++ b/beacon_chain/spec/eth2_apis/rest_types.nim @@ -14,7 +14,7 @@ {.push raises: [].} import - std/json, + std/[json, tables], stew/base10, web3/ethtypes, ".."/forks, ".."/datatypes/[phase0, altair, bellatrix, deneb], @@ -22,7 +22,8 @@ import from ".."/datatypes/capella import BeaconBlockBody -export forks, phase0, altair, bellatrix, capella, bellatrix_mev, capella_mev +export forks, phase0, altair, bellatrix, capella, bellatrix_mev, capella_mev, + tables const # https://github.com/ethereum/eth2.0-APIs/blob/master/apis/beacon/states/validator_balances.yaml#L17 @@ -478,38 +479,7 @@ type TARGET_AGGREGATORS_PER_SYNC_SUBCOMMITTEE*: uint64 SYNC_COMMITTEE_SUBNET_COUNT*: uint64 - # The `RestSpec` is a dynamic dictionary that includes version-specific spec - # constants. New versions may introduce new constants, and remove old ones. - # The Nimbus validator client fetches the remote spec to determine whether it - # is connected to a compatible beacon node. For this purpose, it only needs to - # verify a small set of relevant spec constants. To avoid rejecting a remote - # spec that includes all of those relevant spec constants, but that does not - # include all of the locally known spec constants, a separate type is defined - # that includes just the spec constants relevant for the validator client. - # Extra spec constants are silently ignored. - RestSpecVC* = object - # /!\ Keep in sync with `validator_client/api.nim` > `checkCompatible`. - MAX_VALIDATORS_PER_COMMITTEE*: uint64 - SLOTS_PER_EPOCH*: uint64 - SECONDS_PER_SLOT*: uint64 - EPOCHS_PER_ETH1_VOTING_PERIOD*: uint64 - SLOTS_PER_HISTORICAL_ROOT*: uint64 - EPOCHS_PER_HISTORICAL_VECTOR*: uint64 - EPOCHS_PER_SLASHINGS_VECTOR*: uint64 - HISTORICAL_ROOTS_LIMIT*: uint64 - VALIDATOR_REGISTRY_LIMIT*: uint64 - MAX_PROPOSER_SLASHINGS*: uint64 - MAX_ATTESTER_SLASHINGS*: uint64 - MAX_ATTESTATIONS*: uint64 - MAX_DEPOSITS*: uint64 - MAX_VOLUNTARY_EXITS*: uint64 - DOMAIN_BEACON_PROPOSER*: DomainType - DOMAIN_BEACON_ATTESTER*: DomainType - DOMAIN_RANDAO*: DomainType - DOMAIN_DEPOSIT*: DomainType - DOMAIN_VOLUNTARY_EXIT*: DomainType - DOMAIN_SELECTION_PROOF*: DomainType - DOMAIN_AGGREGATE_AND_PROOF*: DomainType + VCRuntimeConfig* = Table[string, string] RestDepositContract* = object chain_id*: string @@ -692,7 +662,7 @@ type GetPoolVoluntaryExitsResponse* = DataEnclosedObject[seq[SignedVoluntaryExit]] GetProposerDutiesResponse* = DataRootEnclosedObject[seq[RestProposerDuty]] GetSpecResponse* = DataEnclosedObject[RestSpec] - GetSpecVCResponse* = DataEnclosedObject[RestSpecVC] + GetSpecVCResponse* = DataEnclosedObject[VCRuntimeConfig] GetStateFinalityCheckpointsResponse* = DataEnclosedObject[RestBeaconStatesFinalityCheckpoints] GetStateForkResponse* = DataEnclosedObject[Fork] GetStateRootResponse* = DataOptimisticObject[RestRoot] diff --git a/beacon_chain/validator_client/common.nim b/beacon_chain/validator_client/common.nim index 832a4a9a6..b2ac5314b 100644 --- a/beacon_chain/validator_client/common.nim +++ b/beacon_chain/validator_client/common.nim @@ -119,7 +119,7 @@ type BeaconNodeServer* = object client*: RestClientRef endpoint*: string - config*: Opt[RestSpecVC] + config*: VCRuntimeConfig ident*: Opt[string] genesis*: Opt[RestGenesis] syncInfo*: Opt[RestSyncInfo] @@ -451,29 +451,62 @@ chronicles.expandIt(SyncCommitteeDuty): validator_index = it.validator_index validator_sync_committee_index = it.validator_sync_committee_index -proc checkConfig*(info: RestSpecVC): bool = - # /!\ Keep in sync with `spec/eth2_apis/rest_types.nim` > `RestSpecVC`. - info.MAX_VALIDATORS_PER_COMMITTEE == MAX_VALIDATORS_PER_COMMITTEE and - info.SLOTS_PER_EPOCH == SLOTS_PER_EPOCH and - info.SECONDS_PER_SLOT == SECONDS_PER_SLOT and - info.EPOCHS_PER_ETH1_VOTING_PERIOD == EPOCHS_PER_ETH1_VOTING_PERIOD and - info.SLOTS_PER_HISTORICAL_ROOT == SLOTS_PER_HISTORICAL_ROOT and - info.EPOCHS_PER_HISTORICAL_VECTOR == EPOCHS_PER_HISTORICAL_VECTOR and - info.EPOCHS_PER_SLASHINGS_VECTOR == EPOCHS_PER_SLASHINGS_VECTOR and - info.HISTORICAL_ROOTS_LIMIT == HISTORICAL_ROOTS_LIMIT and - info.VALIDATOR_REGISTRY_LIMIT == VALIDATOR_REGISTRY_LIMIT and - info.MAX_PROPOSER_SLASHINGS == MAX_PROPOSER_SLASHINGS and - info.MAX_ATTESTER_SLASHINGS == MAX_ATTESTER_SLASHINGS and - info.MAX_ATTESTATIONS == MAX_ATTESTATIONS and - info.MAX_DEPOSITS == MAX_DEPOSITS and - info.MAX_VOLUNTARY_EXITS == MAX_VOLUNTARY_EXITS and - info.DOMAIN_BEACON_PROPOSER == DOMAIN_BEACON_PROPOSER and - info.DOMAIN_BEACON_ATTESTER == DOMAIN_BEACON_ATTESTER and - info.DOMAIN_RANDAO == DOMAIN_RANDAO and - info.DOMAIN_DEPOSIT == DOMAIN_DEPOSIT and - info.DOMAIN_VOLUNTARY_EXIT == DOMAIN_VOLUNTARY_EXIT and - info.DOMAIN_SELECTION_PROOF == DOMAIN_SELECTION_PROOF and - info.DOMAIN_AGGREGATE_AND_PROOF == DOMAIN_AGGREGATE_AND_PROOF +proc equals*(info: VCRuntimeConfig, name: string, check: uint64): bool = + let numstr = info.getOrDefault(name, "missing") + if numstr == "missing": return false + let value = Base10.decode(uint64, numstr).valueOr: + return false + value == check + +proc equals*(info: VCRuntimeConfig, name: string, check: DomainType): bool = + let domstr = info.getOrDefault(name, "missing") + if domstr == "missing": return false + let value = + try: + var dres: DomainType + hexToByteArray(domstr, distinctBase(dres)) + dres + except ValueError: + return false + value == check + +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 + c.equals("SECONDS_PER_SLOT", SECONDS_PER_SLOT) and + c.equals("EPOCHS_PER_ETH1_VOTING_PERIOD", EPOCHS_PER_ETH1_VOTING_PERIOD) and + c.equals("SLOTS_PER_HISTORICAL_ROOT", SLOTS_PER_HISTORICAL_ROOT) and + c.equals("EPOCHS_PER_HISTORICAL_VECTOR", EPOCHS_PER_HISTORICAL_VECTOR) and + c.equals("EPOCHS_PER_SLASHINGS_VECTOR", EPOCHS_PER_SLASHINGS_VECTOR) and + c.equals("HISTORICAL_ROOTS_LIMIT", HISTORICAL_ROOTS_LIMIT) and + c.equals("VALIDATOR_REGISTRY_LIMIT", VALIDATOR_REGISTRY_LIMIT) and + c.equals("MAX_PROPOSER_SLASHINGS", MAX_PROPOSER_SLASHINGS) and + c.equals("MAX_ATTESTER_SLASHINGS", MAX_ATTESTER_SLASHINGS) and + c.equals("MAX_ATTESTATIONS", MAX_ATTESTATIONS) and + c.equals("MAX_DEPOSITS", MAX_DEPOSITS) and + c.equals("MAX_VOLUNTARY_EXITS", MAX_VOLUNTARY_EXITS) and + c.equals("DOMAIN_BEACON_PROPOSER", DOMAIN_BEACON_PROPOSER) and + c.equals("DOMAIN_BEACON_ATTESTER", DOMAIN_BEACON_ATTESTER) and + c.equals("DOMAIN_RANDAO", DOMAIN_RANDAO) and + c.equals("DOMAIN_DEPOSIT", DOMAIN_DEPOSIT) and + c.equals("DOMAIN_VOLUNTARY_EXIT", DOMAIN_VOLUNTARY_EXIT) and + c.equals("DOMAIN_SELECTION_PROOF", DOMAIN_SELECTION_PROOF) and + c.equals("DOMAIN_AGGREGATE_AND_PROOF", DOMAIN_AGGREGATE_AND_PROOF) and + c.hasKey("ALTAIR_FORK_VERSION") and c.hasKey("ALTAIR_FORK_EPOCH") and + not(c.equals("ALTAIR_FORK_EPOCH", FAR_FUTURE_EPOCH)) proc updateStatus*(node: BeaconNodeServerRef, status: RestBeaconNodeStatus, diff --git a/beacon_chain/validator_client/fallback_service.nim b/beacon_chain/validator_client/fallback_service.nim index 9829bd233..7e1e6082c 100644 --- a/beacon_chain/validator_client/fallback_service.nim +++ b/beacon_chain/validator_client/fallback_service.nim @@ -139,7 +139,7 @@ proc checkCompatible( genesisFlag = (genesis != vc.beaconGenesis) configFlag = not(checkConfig(info)) - node.config = Opt.some(info) + node.config = info node.genesis = Opt.some(genesis) let res = if configFlag or genesisFlag: