mirror of
https://github.com/status-im/nimbus-eth2.git
synced 2025-01-14 16:47:21 +00:00
VC+BN: Validator voluntary exits through the Keymanager API (#5020)
* Initial commit. * Address review comments.
This commit is contained in:
parent
c0e5c26da1
commit
927180f36f
@ -666,6 +666,12 @@ proc init*(T: type BeaconNode,
|
|||||||
withState(dag.headState):
|
withState(dag.headState):
|
||||||
getValidator(forkyState().data.validators.asSeq(), pubkey)
|
getValidator(forkyState().data.validators.asSeq(), pubkey)
|
||||||
|
|
||||||
|
proc getForkForEpoch(epoch: Epoch): Opt[Fork] =
|
||||||
|
Opt.some(dag.forkAtEpoch(epoch))
|
||||||
|
|
||||||
|
proc getGenesisRoot(): Eth2Digest =
|
||||||
|
getStateField(dag.headState, genesis_validators_root)
|
||||||
|
|
||||||
let
|
let
|
||||||
slashingProtectionDB =
|
slashingProtectionDB =
|
||||||
SlashingProtectionDB.init(
|
SlashingProtectionDB.init(
|
||||||
@ -685,7 +691,9 @@ proc init*(T: type BeaconNode,
|
|||||||
config.defaultFeeRecipient,
|
config.defaultFeeRecipient,
|
||||||
config.suggestedGasLimit,
|
config.suggestedGasLimit,
|
||||||
getValidatorAndIdx,
|
getValidatorAndIdx,
|
||||||
getBeaconTime)
|
getBeaconTime,
|
||||||
|
getForkForEpoch,
|
||||||
|
getGenesisRoot)
|
||||||
else: nil
|
else: nil
|
||||||
|
|
||||||
stateTtlCache =
|
stateTtlCache =
|
||||||
|
@ -310,6 +310,15 @@ proc asyncInit(vc: ValidatorClientRef): Future[ValidatorClientRef] {.async.} =
|
|||||||
let
|
let
|
||||||
keymanagerInitResult = initKeymanagerServer(vc.config, nil)
|
keymanagerInitResult = initKeymanagerServer(vc.config, nil)
|
||||||
|
|
||||||
|
proc getForkForEpoch(epoch: Epoch): Opt[Fork] =
|
||||||
|
if len(vc.forks) > 0:
|
||||||
|
Opt.some(vc.forkAtEpoch(epoch))
|
||||||
|
else:
|
||||||
|
Opt.none(Fork)
|
||||||
|
|
||||||
|
proc getGenesisRoot(): Eth2Digest =
|
||||||
|
vc.beaconGenesis.genesis_validators_root
|
||||||
|
|
||||||
try:
|
try:
|
||||||
vc.fallbackService = await FallbackServiceRef.init(vc)
|
vc.fallbackService = await FallbackServiceRef.init(vc)
|
||||||
vc.forkService = await ForkServiceRef.init(vc)
|
vc.forkService = await ForkServiceRef.init(vc)
|
||||||
@ -329,7 +338,10 @@ proc asyncInit(vc: ValidatorClientRef): Future[ValidatorClientRef] {.async.} =
|
|||||||
vc.config.defaultFeeRecipient,
|
vc.config.defaultFeeRecipient,
|
||||||
vc.config.suggestedGasLimit,
|
vc.config.suggestedGasLimit,
|
||||||
nil,
|
nil,
|
||||||
vc.beaconClock.getBeaconTimeFn)
|
vc.beaconClock.getBeaconTimeFn,
|
||||||
|
getForkForEpoch,
|
||||||
|
getGenesisRoot
|
||||||
|
)
|
||||||
|
|
||||||
except CatchableError as exc:
|
except CatchableError as exc:
|
||||||
warn "Unexpected error encountered while initializing",
|
warn "Unexpected error encountered while initializing",
|
||||||
|
@ -115,6 +115,8 @@ const
|
|||||||
"Invalid subscription request object(s)"
|
"Invalid subscription request object(s)"
|
||||||
ValidatorNotFoundError* =
|
ValidatorNotFoundError* =
|
||||||
"Could not find validator"
|
"Could not find validator"
|
||||||
|
ValidatorIndexMissingError* =
|
||||||
|
"Validator missing index value"
|
||||||
ValidatorStatusNotFoundError* =
|
ValidatorStatusNotFoundError* =
|
||||||
"Could not obtain validator's status"
|
"Could not obtain validator's status"
|
||||||
TooHighValidatorIndexValueError* =
|
TooHighValidatorIndexValueError* =
|
||||||
@ -234,3 +236,5 @@ const
|
|||||||
"The given merkle proof is invalid"
|
"The given merkle proof is invalid"
|
||||||
InvalidMerkleProofIndexError* =
|
InvalidMerkleProofIndexError* =
|
||||||
"The given merkle proof index is invalid"
|
"The given merkle proof index is invalid"
|
||||||
|
FailedToObtainForkError* =
|
||||||
|
"Failed to obtain fork information"
|
||||||
|
@ -23,8 +23,8 @@ func validateKeymanagerApiQueries*(key: string, value: string): int =
|
|||||||
# There are no queries to validate
|
# There are no queries to validate
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
proc listLocalValidators*(validatorPool: ValidatorPool): seq[KeystoreInfo]
|
proc listLocalValidators*(validatorPool: ValidatorPool): seq[KeystoreInfo] {.
|
||||||
{.raises: [Defect].} =
|
raises: [Defect].} =
|
||||||
var validators: seq[KeystoreInfo]
|
var validators: seq[KeystoreInfo]
|
||||||
for item in validatorPool:
|
for item in validatorPool:
|
||||||
if item.kind == ValidatorKind.Local:
|
if item.kind == ValidatorKind.Local:
|
||||||
@ -35,8 +35,9 @@ proc listLocalValidators*(validatorPool: ValidatorPool): seq[KeystoreInfo]
|
|||||||
)
|
)
|
||||||
validators
|
validators
|
||||||
|
|
||||||
proc listRemoteValidators*(validatorPool: ValidatorPool): seq[RemoteKeystoreInfo]
|
proc listRemoteValidators*(
|
||||||
{.raises: [Defect].} =
|
validatorPool: ValidatorPool): seq[RemoteKeystoreInfo] {.
|
||||||
|
raises: [Defect].} =
|
||||||
var validators: seq[RemoteKeystoreInfo]
|
var validators: seq[RemoteKeystoreInfo]
|
||||||
for item in validatorPool:
|
for item in validatorPool:
|
||||||
if item.kind == ValidatorKind.Remote and item.data.remotes.len == 1:
|
if item.kind == ValidatorKind.Remote and item.data.remotes.len == 1:
|
||||||
@ -46,8 +47,9 @@ proc listRemoteValidators*(validatorPool: ValidatorPool): seq[RemoteKeystoreInfo
|
|||||||
)
|
)
|
||||||
validators
|
validators
|
||||||
|
|
||||||
proc listRemoteDistributedValidators*(validatorPool: ValidatorPool): seq[DistributedKeystoreInfo]
|
proc listRemoteDistributedValidators*(
|
||||||
{.raises: [Defect].} =
|
validatorPool: ValidatorPool): seq[DistributedKeystoreInfo] {.
|
||||||
|
raises: [Defect].} =
|
||||||
var validators: seq[DistributedKeystoreInfo]
|
var validators: seq[DistributedKeystoreInfo]
|
||||||
for item in validatorPool:
|
for item in validatorPool:
|
||||||
if item.kind == ValidatorKind.Remote and item.data.remotes.len > 1:
|
if item.kind == ValidatorKind.Remote and item.data.remotes.len > 1:
|
||||||
@ -75,8 +77,9 @@ proc keymanagerApiError(status: HttpCode, msg: string): RestApiResponse =
|
|||||||
default
|
default
|
||||||
RestApiResponse.error(status, data, "application/json")
|
RestApiResponse.error(status, data, "application/json")
|
||||||
|
|
||||||
proc checkAuthorization*(request: HttpRequestRef,
|
proc checkAuthorization*(
|
||||||
host: KeymanagerHost): Result[void, AuthorizationError] =
|
request: HttpRequestRef,
|
||||||
|
host: KeymanagerHost): Result[void, AuthorizationError] =
|
||||||
let authorizations = request.headers.getList("authorization")
|
let authorizations = request.headers.getList("authorization")
|
||||||
if authorizations.len > 0:
|
if authorizations.len > 0:
|
||||||
for authHeader in authorizations:
|
for authHeader in authorizations:
|
||||||
@ -522,3 +525,54 @@ proc installKeymanagerHandlers*(router: var RestRouter, host: KeymanagerHost) =
|
|||||||
response.data.add handleRemoveValidatorReq(host, key)
|
response.data.add handleRemoveValidatorReq(host, key)
|
||||||
|
|
||||||
return RestApiResponse.jsonResponsePlain(response)
|
return RestApiResponse.jsonResponsePlain(response)
|
||||||
|
|
||||||
|
# https://ethereum.github.io/keymanager-APIs/?urls.primaryName=dev#/Voluntary%20Exit/signVoluntaryExit
|
||||||
|
router.api(MethodPost, "/eth/v1/validator/{pubkey}/voluntary_exit") do (
|
||||||
|
pubkey: ValidatorPubKey, epoch: Option[Epoch],
|
||||||
|
contentBody: Option[ContentBody]) -> RestApiResponse:
|
||||||
|
|
||||||
|
let authStatus = checkAuthorization(request, host)
|
||||||
|
if authStatus.isErr():
|
||||||
|
return authErrorResponse(authStatus.error)
|
||||||
|
|
||||||
|
let
|
||||||
|
qpubkey = pubkey.valueOr:
|
||||||
|
return keymanagerApiError(Http400, InvalidValidatorPublicKey)
|
||||||
|
qepoch =
|
||||||
|
if epoch.isSome():
|
||||||
|
let res = epoch.get()
|
||||||
|
if res.isErr():
|
||||||
|
return keymanagerApiError(Http400, InvalidEpochValueError)
|
||||||
|
res.get()
|
||||||
|
else:
|
||||||
|
host.getBeaconTimeFn().slotOrZero().epoch()
|
||||||
|
validator =
|
||||||
|
block:
|
||||||
|
let res = host.validatorPool[].getValidator(qpubkey).valueOr:
|
||||||
|
return keymanagerApiError(Http404, ValidatorNotFoundError)
|
||||||
|
if res.index.isNone():
|
||||||
|
return keymanagerApiError(Http404, ValidatorIndexMissingError)
|
||||||
|
res
|
||||||
|
voluntaryExit =
|
||||||
|
VoluntaryExit(epoch: qepoch,
|
||||||
|
validator_index: uint64(validator.index.get()))
|
||||||
|
fork = host.getForkFn(qepoch).valueOr:
|
||||||
|
return keymanagerApiError(Http500, FailedToObtainForkError)
|
||||||
|
signature =
|
||||||
|
try:
|
||||||
|
let res = await validator.getValidatorExitSignature(
|
||||||
|
fork, host.getGenesisFn(), voluntaryExit)
|
||||||
|
if res.isErr():
|
||||||
|
return keymanagerApiError(Http500, res.error())
|
||||||
|
res.get()
|
||||||
|
except CancelledError as exc:
|
||||||
|
raise exc
|
||||||
|
except CatchableError as exc:
|
||||||
|
error "An unexpected error occurred while signing validator exit",
|
||||||
|
err_name = exc.name, err_msg = exc.msg
|
||||||
|
return keymanagerApiError(Http500, $exc.msg)
|
||||||
|
response = SignedVoluntaryExit(
|
||||||
|
message: voluntaryExit,
|
||||||
|
signature: signature
|
||||||
|
)
|
||||||
|
return RestApiResponse.jsonResponse(response)
|
||||||
|
@ -69,6 +69,11 @@ type
|
|||||||
proc (pubkey: ValidatorPubKey): Opt[ValidatorAndIndex]
|
proc (pubkey: ValidatorPubKey): Opt[ValidatorAndIndex]
|
||||||
{.raises: [Defect], gcsafe.}
|
{.raises: [Defect], gcsafe.}
|
||||||
|
|
||||||
|
GetForkFn* =
|
||||||
|
proc (epoch: Epoch): Opt[Fork] {.raises: [Defect], gcsafe.}
|
||||||
|
GetGenesisFn* =
|
||||||
|
proc (): Eth2Digest {.raises: [Defect], gcsafe.}
|
||||||
|
|
||||||
KeymanagerHost* = object
|
KeymanagerHost* = object
|
||||||
validatorPool*: ref ValidatorPool
|
validatorPool*: ref ValidatorPool
|
||||||
rng*: ref HmacDrbgContext
|
rng*: ref HmacDrbgContext
|
||||||
@ -79,6 +84,8 @@ type
|
|||||||
defaultGasLimit*: uint64
|
defaultGasLimit*: uint64
|
||||||
getValidatorAndIdxFn*: ValidatorPubKeyToDataFn
|
getValidatorAndIdxFn*: ValidatorPubKeyToDataFn
|
||||||
getBeaconTimeFn*: GetBeaconTimeFn
|
getBeaconTimeFn*: GetBeaconTimeFn
|
||||||
|
getForkFn*: GetForkFn
|
||||||
|
getGenesisFn*: GetGenesisFn
|
||||||
|
|
||||||
MultipleKeystoresDecryptor* = object
|
MultipleKeystoresDecryptor* = object
|
||||||
previouslyUsedPassword*: string
|
previouslyUsedPassword*: string
|
||||||
@ -104,7 +111,9 @@ func init*(T: type KeymanagerHost,
|
|||||||
defaultFeeRecipient: Opt[Eth1Address],
|
defaultFeeRecipient: Opt[Eth1Address],
|
||||||
defaultGasLimit: uint64,
|
defaultGasLimit: uint64,
|
||||||
getValidatorAndIdxFn: ValidatorPubKeyToDataFn,
|
getValidatorAndIdxFn: ValidatorPubKeyToDataFn,
|
||||||
getBeaconTimeFn: GetBeaconTimeFn): T =
|
getBeaconTimeFn: GetBeaconTimeFn,
|
||||||
|
getForkFn: GetForkFn,
|
||||||
|
getGenesisFn: GetGenesisFn): T =
|
||||||
T(validatorPool: validatorPool,
|
T(validatorPool: validatorPool,
|
||||||
rng: rng,
|
rng: rng,
|
||||||
keymanagerToken: keymanagerToken,
|
keymanagerToken: keymanagerToken,
|
||||||
@ -113,7 +122,9 @@ func init*(T: type KeymanagerHost,
|
|||||||
defaultFeeRecipient: defaultFeeRecipient,
|
defaultFeeRecipient: defaultFeeRecipient,
|
||||||
defaultGasLimit: defaultGasLimit,
|
defaultGasLimit: defaultGasLimit,
|
||||||
getValidatorAndIdxFn: getValidatorAndIdxFn,
|
getValidatorAndIdxFn: getValidatorAndIdxFn,
|
||||||
getBeaconTimeFn: getBeaconTimeFn)
|
getBeaconTimeFn: getBeaconTimeFn,
|
||||||
|
getForkFn: getForkFn,
|
||||||
|
getGenesisFn: getGenesisFn)
|
||||||
|
|
||||||
proc echoP*(msg: string) =
|
proc echoP*(msg: string) =
|
||||||
## Prints a paragraph aligned to 80 columns
|
## Prints a paragraph aligned to 80 columns
|
||||||
|
Loading…
x
Reference in New Issue
Block a user