Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

VC: Electra fixes. #6631

Merged
merged 22 commits into from
Oct 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
93bade2
Initial commit.
cheatfate Oct 3, 2024
abbe2ea
Add aggregated attestation processing.
cheatfate Oct 9, 2024
61751eb
Add missing presets file.
cheatfate Oct 9, 2024
0c7e34d
Fix compilation error.
cheatfate Oct 9, 2024
f562140
Fix post-rebase compilation error.
cheatfate Oct 9, 2024
13372f3
Satisfy push raises requirement.
cheatfate Oct 9, 2024
e37ad5c
Fix sync committee duties retrieval process.
cheatfate Oct 10, 2024
95e7353
Fix forks configuration management.
cheatfate Oct 11, 2024
f9e3908
Fix deposits to use new fork configuration scheme.
cheatfate Oct 11, 2024
2b5dff2
Fix /eth/v2/validator/aggregate_attestation implementation.
cheatfate Oct 12, 2024
00bb8e5
Fix RANDAO preparation loop to handle blocks at epoch boundary properly.
cheatfate Oct 13, 2024
a20c406
Simplification of RANDAO fix.
cheatfate Oct 13, 2024
3a0688a
Fix typo.
cheatfate Oct 13, 2024
e96c203
Address review comments and fix tests.
cheatfate Oct 13, 2024
8767dd4
Fix incorrect status codes in REST test.
cheatfate Oct 14, 2024
a27b8e6
Rework attestation and aggregated attestations processing code.
cheatfate Oct 14, 2024
cf8c795
Address review comments.
cheatfate Oct 14, 2024
4cd4718
Fill committee_index in RegisteredAttestation construction code.
cheatfate Oct 14, 2024
7e8b248
Address review comments part 2.
cheatfate Oct 15, 2024
c881784
Address review comments part 3.
cheatfate Oct 15, 2024
6a55fda
use Deneb fork epoch
tersec Oct 16, 2024
aaec8ef
Add transition from Deneb to Electra into CI finalization test.
cheatfate Oct 16, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,7 @@ local-testnet-minimal:
--remote-validators-count 512 \
--signer-type $(SIGNER_TYPE) \
--deneb-fork-epoch 0 \
--electra-fork-epoch 5 \
--stop-at-epoch 6 \
--disable-htop \
--enable-payload-builder \
Expand Down Expand Up @@ -265,6 +266,7 @@ local-testnet-mainnet:
--data-dir $@ \
--nodes 2 \
--deneb-fork-epoch 0 \
--electra-fork-epoch 5 \
--stop-at-epoch 6 \
--disable-htop \
--enable-logtrace \
Expand Down
18 changes: 14 additions & 4 deletions beacon_chain/deposits.nim
Original file line number Diff line number Diff line change
Expand Up @@ -236,11 +236,21 @@ proc restValidatorExit(config: BeaconNodeConf) {.async.} =
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")
let
capellaForkVersion =
try:
forkConfig.get()[ConsensusFork.Capella].version
except KeyError:
raise newException(RestError,
ConsensusFork.Capella.forkVersionConfigKey() & " missing")
denebForkEpoch =
try:
forkConfig.get()[ConsensusFork.Deneb].epoch
except KeyError:
raise newException(RestError,
ConsensusFork.Deneb.forkEpochConfigKey() & " missing")
voluntary_exit_signature_fork(
fork, capellaForkVersion, currentEpoch, forkConfig.get.denebEpoch)
fork, capellaForkVersion, currentEpoch, denebForkEpoch)
else:
raise newException(RestError, "Error response (" & $response.status & ")")
except CatchableError as exc:
Expand Down
5 changes: 5 additions & 0 deletions beacon_chain/nimbus_binary_common.nim
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,11 @@ proc sleepAsync*(t: TimeDiff): Future[void] =
sleepAsync(nanoseconds(
if t.nanoseconds < 0: 0'i64 else: t.nanoseconds))

proc sleepAsync2*(t: TimeDiff): Future[void] {.
async: (raises: [CancelledError], raw: true).} =
sleepAsync(nanoseconds(
if t.nanoseconds < 0: 0'i64 else: t.nanoseconds))

proc runSlotLoop*[T](node: T, startTime: BeaconTime,
slotProc: SlotStartProc[T]) {.async.} =
var
Expand Down
115 changes: 66 additions & 49 deletions beacon_chain/nimbus_validator_client.nim
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ declareGauge validator_client_node_counts,
"Number of connected beacon nodes and their status",
labels = ["status"]

proc initGenesis(vc: ValidatorClientRef): Future[RestGenesis] {.async.} =
proc initGenesis(vc: ValidatorClientRef): Future[RestGenesis] {.
async: (raises: [CancelledError]).} =
info "Initializing genesis", nodes_count = len(vc.beaconNodes)
var nodes = vc.beaconNodes
while true:
Expand Down Expand Up @@ -61,7 +62,7 @@ proc initGenesis(vc: ValidatorClientRef): Future[RestGenesis] {.async.} =
for i in 0 ..< len(pendingRequests):
let fut = pendingRequests[i]
if fut.completed():
let resp = fut.read()
let resp = fut.value
if resp.status == 200:
debug "Received genesis information", endpoint = nodes[i],
genesis_time = resp.data.data.genesis_time,
Expand All @@ -73,10 +74,10 @@ proc initGenesis(vc: ValidatorClientRef): Future[RestGenesis] {.async.} =
response_code = resp.status
bres.add(nodes[i])
elif fut.failed():
let error = fut.readError()
let error = fut.error
debug "Could not obtain genesis information from beacon node",
endpoint = nodes[i], error_name = error.name,
error_msg = error.msg
reason = error.msg
bres.add(nodes[i])
else:
debug "Interrupted while requesting information from beacon node",
Expand Down Expand Up @@ -105,14 +106,19 @@ proc initGenesis(vc: ValidatorClientRef): Future[RestGenesis] {.async.} =
dec(counter)
return melem

proc addValidatorsFromWeb3Signer(vc: ValidatorClientRef, web3signerUrl: Web3SignerUrl) {.async.} =
proc addValidatorsFromWeb3Signer(
vc: ValidatorClientRef,
web3signerUrl: Web3SignerUrl
) {.async: (raises: [CancelledError]).} =
let res = await queryValidatorsSource(web3signerUrl)
if res.isOk():
let dynamicKeystores = res.get()
for keystore in dynamicKeystores:
vc.addValidator(keystore)

proc initValidators(vc: ValidatorClientRef): Future[bool] {.async.} =
proc initValidators(
vc: ValidatorClientRef
): Future[bool] {.async: (raises: [CancelledError]).} =
info "Loading validators", validatorsDir = vc.config.validatorsDir()
for keystore in listLoadableKeystores(vc.config, vc.keystoreCache):
vc.addValidator(keystore)
Expand All @@ -129,7 +135,10 @@ proc initValidators(vc: ValidatorClientRef): Future[bool] {.async.} =

true

proc initClock(vc: ValidatorClientRef): Future[BeaconClock] {.async.} =
proc initClock(
vc: ValidatorClientRef
): Future[BeaconClock] {.
async: (raises: [CancelledError, ValidatorClientError]).} =
# This procedure performs initialization of BeaconClock using current genesis
# information. It also performs waiting for genesis.
let
Expand All @@ -150,9 +159,11 @@ proc initClock(vc: ValidatorClientRef): Future[BeaconClock] {.async.} =
info "Initializing beacon clock",
genesis_time = vc.beaconGenesis.genesis_time,
current_slot = currentSlot, current_epoch = currentEpoch
return res
res

proc initMetrics(vc: ValidatorClientRef): Future[bool] {.async.} =
proc initMetrics(
vc: ValidatorClientRef
): Future[bool] {.async: (raises: [CancelledError]).} =
if vc.config.metricsEnabled:
let
metricsAddress = vc.config.metricsAddress
Expand All @@ -174,9 +185,9 @@ proc initMetrics(vc: ValidatorClientRef): Future[bool] {.async.} =
error "Could not start metrics HTTP server", url = url,
error_msg = exc.msg, error_name = exc.name
return false
return true
true

proc shutdownMetrics(vc: ValidatorClientRef) {.async.} =
proc shutdownMetrics(vc: ValidatorClientRef) {.async: (raises: []).} =
if vc.config.metricsEnabled:
if vc.metricsServer.isSome():
info "Shutting down metrics HTTP server"
Expand All @@ -186,7 +197,8 @@ proc shutdownSlashingProtection(vc: ValidatorClientRef) =
info "Closing slashing protection", path = vc.config.validatorsDir()
vc.attachedValidators[].slashingProtection.close()

proc runVCSlotLoop(vc: ValidatorClientRef) {.async.} =
proc runVCSlotLoop(
vc: ValidatorClientRef) {.async: (raises: [CancelledError]).} =
var
startTime = vc.beaconClock.now()
curSlot = startTime.slotOrZero()
Expand Down Expand Up @@ -255,9 +267,11 @@ proc runVCSlotLoop(vc: ValidatorClientRef) {.async.} =
node_status = $vc.beaconNodes[0].status,
delay = shortLog(delay)

proc new*(T: type ValidatorClientRef,
config: ValidatorClientConf,
rng: ref HmacDrbgContext): ValidatorClientRef =
proc new*(
T: type ValidatorClientRef,
config: ValidatorClientConf,
rng: ref HmacDrbgContext
): ValidatorClientRef =
let beaconNodes =
block:
var servers: seq[BeaconNodeServerRef]
Expand Down Expand Up @@ -317,7 +331,8 @@ proc new*(T: type ValidatorClientRef,
keystoreCache: KeystoreCacheRef.init()
)

proc asyncInit(vc: ValidatorClientRef): Future[ValidatorClientRef] {.async.} =
proc asyncInit(vc: ValidatorClientRef): Future[ValidatorClientRef] {.
async: (raises: [CancelledError, ValidatorClientError]).} =
notice "Launching validator client", version = fullVersionStr,
cmdParams = commandLineParams(),
config = vc.config,
Expand Down Expand Up @@ -361,15 +376,21 @@ proc asyncInit(vc: ValidatorClientRef): Future[ValidatorClientRef] {.async.} =
keymanagerInitResult = initKeymanagerServer(vc.config, nil)

func getCapellaForkVersion(): Opt[Version] =
if vc.runtimeConfig.forkConfig.isSome():
vc.runtimeConfig.forkConfig.get().capellaVersion
else:
if vc.forkConfig.isNone():
return Opt.none(Version)

try:
Opt.some(vc.forkConfig.get()[ConsensusFork.Capella].version)
except KeyError:
Opt.none(Version)

func getDenebForkEpoch(): Opt[Epoch] =
if vc.runtimeConfig.forkConfig.isSome():
Opt.some(vc.runtimeConfig.forkConfig.get().denebEpoch)
else:
if vc.forkConfig.isNone():
return Opt.none(Epoch)

try:
Opt.some(vc.forkConfig.get()[ConsensusFork.Deneb].epoch)
except KeyError:
Opt.none(Epoch)

proc getForkForEpoch(epoch: Epoch): Opt[Fork] =
Expand Down Expand Up @@ -409,12 +430,6 @@ proc asyncInit(vc: ValidatorClientRef): Future[ValidatorClientRef] {.async.} =
getForkForEpoch,
getGenesisRoot
)

except CatchableError as exc:
warn "Unexpected error encountered while initializing",
error_name = exc.name, error_msg = exc.msg
await vc.shutdownMetrics()
vc.shutdownSlashingProtection()
except CancelledError:
debug "Initialization process interrupted"
await vc.shutdownMetrics()
Expand All @@ -423,7 +438,9 @@ proc asyncInit(vc: ValidatorClientRef): Future[ValidatorClientRef] {.async.} =

return vc

proc runPreGenesisWaitingLoop(vc: ValidatorClientRef) {.async.} =
proc runPreGenesisWaitingLoop(
vc: ValidatorClientRef
) {.async: (raises: [CancelledError]).} =
var breakLoop = false
while not(breakLoop):
let
Expand All @@ -444,15 +461,13 @@ proc runPreGenesisWaitingLoop(vc: ValidatorClientRef) {.async.} =
except CancelledError as exc:
debug "Pre-genesis waiting loop was interrupted"
raise exc
except CatchableError as exc:
error "Pre-genesis waiting loop failed with unexpected error",
err_name = $exc.name, err_msg = $exc.msg
true

if not(breakLoop):
vc.preGenesisEvent.fire()

proc runGenesisWaitingLoop(vc: ValidatorClientRef) {.async.} =
proc runGenesisWaitingLoop(
vc: ValidatorClientRef
) {.async: (raises: [CancelledError]).} =
var breakLoop = false
while not(breakLoop):
let genesisTime = vc.beaconClock.fromNow(Slot(0))
Expand All @@ -471,15 +486,13 @@ proc runGenesisWaitingLoop(vc: ValidatorClientRef) {.async.} =
except CancelledError as exc:
debug "Genesis waiting loop was interrupted"
raise exc
except CatchableError as exc:
error "Genesis waiting loop failed with unexpected error",
err_name = $exc.name, err_msg = $exc.msg
true

if not(breakLoop):
vc.genesisEvent.fire()

proc asyncRun*(vc: ValidatorClientRef) {.async.} =
proc asyncRun*(
vc: ValidatorClientRef
) {.async: (raises: [ValidatorClientError]).} =
vc.fallbackService.start()
vc.forkService.start()
vc.dutiesService.start()
Expand Down Expand Up @@ -508,9 +521,6 @@ proc asyncRun*(vc: ValidatorClientRef) {.async.} =
notice "Received shutdown event, exiting"
except CancelledError:
debug "Main loop interrupted"
except CatchableError as exc:
debug "Main loop failed with an error", err_name = $exc.name,
err_msg = $exc.msg

await vc.shutdownMetrics()
vc.shutdownSlashingProtection()
Expand Down Expand Up @@ -539,22 +549,27 @@ proc asyncRun*(vc: ValidatorClientRef) {.async.} =
pending.add(vc.syncCommitteeService.stop())
if not isNil(vc.keymanagerServer):
pending.add(vc.keymanagerServer.stop())
await allFutures(pending)
await noCancel allFutures(pending)

template runWithSignals(vc: ValidatorClientRef, body: untyped): bool =
let future = body
discard await race(future, vc.sigintHandleFut, vc.sigtermHandleFut)

try:
discard await race(future, vc.sigintHandleFut, vc.sigtermHandleFut)
except CancelledError:
discard

if future.finished():
if future.failed() or future.cancelled():
let exc = future.readError()
let exc = future.error
error "Validator client initialization failed", err_name = $exc.name,
err_msg = $exc.msg
var pending: seq[Future[void]]
if not(vc.sigintHandleFut.finished()):
pending.add(cancelAndWait(vc.sigintHandleFut))
if not(vc.sigtermHandleFut.finished()):
pending.add(cancelAndWait(vc.sigtermHandleFut))
await allFutures(pending)
await noCancel allFutures(pending)
false
else:
true
Expand All @@ -566,11 +581,13 @@ template runWithSignals(vc: ValidatorClientRef, body: untyped): bool =
pending.add(cancelAndWait(vc.sigintHandleFut))
if not(vc.sigtermHandleFut.finished()):
pending.add(cancelAndWait(vc.sigtermHandleFut))
await allFutures(pending)
await noCancel allFutures(pending)
false

proc runValidatorClient*(config: ValidatorClientConf,
rng: ref HmacDrbgContext) {.async.} =
proc runValidatorClient*(
config: ValidatorClientConf,
rng: ref HmacDrbgContext
) {.async: (raises: []).} =
let vc = ValidatorClientRef.new(config, rng)
if not vc.runWithSignals(asyncInit vc):
return
Expand Down
34 changes: 20 additions & 14 deletions beacon_chain/rpc/rest_validator_api.nim
Original file line number Diff line number Diff line change
Expand Up @@ -840,21 +840,27 @@ proc installValidatorApiHandlers*(router: var RestRouter, node: BeaconNode) =
return RestApiResponse.jsonError(Http400,
InvalidAttestationDataRootValueError, $res.error())
res.get()
let phase0_attestations =
node.attestationPool[].getPhase0AggregatedAttestation(qslot, root)

if phase0_attestations.isSome():
return RestApiResponse.jsonResponse(phase0_attestations.get())

let electra_attestations =
node.attestationPool[].getElectraAggregatedAttestation(qslot,
root,
committee_index)

if electra_attestations.isSome():
return RestApiResponse.jsonResponse(electra_attestations.get())

RestApiResponse.jsonError(Http400, UnableToGetAggregatedAttestationError)
let
qfork = node.dag.cfg.consensusForkAtEpoch(qslot.epoch)
forked =
if qfork >= ConsensusFork.Electra:
let electra_attestation =
node.attestationPool[].getElectraAggregatedAttestation(
qslot, root, committee_index).valueOr:
return RestApiResponse.jsonError(Http404,
UnableToGetAggregatedAttestationError)
ForkedAttestation.init(electra_attestation, qfork)
else:
let phase0_attestation =
node.attestationPool[].getPhase0AggregatedAttestation(
qslot, root).valueOr:
return RestApiResponse.jsonError(Http404,
UnableToGetAggregatedAttestationError)
ForkedAttestation.init(phase0_attestation, qfork)

let headers = HttpTable.init([("eth-consensus-version", qfork.toString())])
RestApiResponse.jsonResponsePlain(forked, headers)

# https://ethereum.github.io/beacon-APIs/#/Validator/publishAggregateAndProofs
router.api2(MethodPost, "/eth/v1/validator/aggregate_and_proofs") do (
Expand Down
Loading
Loading