VC: Runtime config (#5130)

* Make VC able to understand any type of `/eth/v1/config/spec` response without any changes in source code.
Update compatibility checking.
Now VC is able to obtain any constant from `spec` call.

* Remove RestSpecVC declaration.
This commit is contained in:
Eugene Kabanov 2023-06-28 16:33:38 +03:00 committed by GitHub
parent c534a285b9
commit 5e1a0eac85
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 73 additions and 60 deletions

View File

@ -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)))

View File

@ -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]

View File

@ -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,

View File

@ -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: