Dynamic validators set. (#5366)
* Initial commit. * Fix argument to be optional. * Adopt options.md.
This commit is contained in:
parent
1fbf371826
commit
757328372a
|
@ -161,6 +161,10 @@ type
|
|||
desc: "A directory containing validator keystores"
|
||||
name: "validators-dir" .}: Option[InputDir]
|
||||
|
||||
validatorsSource* {.
|
||||
desc: "Remote Web3Signer URL that will be used as a source of validators"
|
||||
name: "validators-source"}: Option[string]
|
||||
|
||||
secretsDirFlag* {.
|
||||
desc: "A directory containing validator keystore passwords"
|
||||
name: "secrets-dir" .}: Option[InputDir]
|
||||
|
@ -875,6 +879,10 @@ type
|
|||
desc: "A directory containing validator keystores"
|
||||
name: "validators-dir" .}: Option[InputDir]
|
||||
|
||||
validatorsSource* {.
|
||||
desc: "Remote Web3Signer URL that will be used as a source of validators"
|
||||
name: "validators-source"}: Option[string]
|
||||
|
||||
secretsDirFlag* {.
|
||||
desc: "A directory containing validator keystore passwords"
|
||||
name: "secrets-dir" .}: Option[InputDir]
|
||||
|
|
|
@ -90,6 +90,9 @@ proc initValidators(vc: ValidatorClientRef): Future[bool] {.async.} =
|
|||
var duplicates: seq[ValidatorPubKey]
|
||||
for keystore in listLoadableKeystores(vc.config, vc.keystoreCache):
|
||||
vc.addValidator(keystore)
|
||||
let dynamicKeystores = await queryValidatorsSource(vc.config)
|
||||
for keystore in dynamicKeystores:
|
||||
vc.addValidator(keystore)
|
||||
return true
|
||||
|
||||
proc initClock(vc: ValidatorClientRef): Future[BeaconClock] {.async.} =
|
||||
|
|
|
@ -78,6 +78,11 @@ proc getKeys*(): RestResponse[Web3SignerKeysResponse] {.
|
|||
meth: MethodGet, accept: "application/json" .}
|
||||
## https://consensys.github.io/web3signer/web3signer-eth2.html#tag/Public-Key
|
||||
|
||||
proc getKeysPlain*(): RestPlainResponse {.
|
||||
rest, endpoint: "/api/v1/eth2/publicKeys",
|
||||
meth: MethodGet, accept: "application/json" .}
|
||||
## https://consensys.github.io/web3signer/web3signer-eth2.html#tag/Public-Key
|
||||
|
||||
proc signDataPlain*(identifier: ValidatorPubKey,
|
||||
body: Web3SignerRequest): RestPlainResponse {.
|
||||
rest, endpoint: "/api/v1/eth2/sign/{identifier}",
|
||||
|
|
|
@ -130,7 +130,7 @@ type
|
|||
Local, Remote
|
||||
|
||||
RemoteKeystoreFlag* {.pure.} = enum
|
||||
IgnoreSSLVerification
|
||||
IgnoreSSLVerification, DynamicKeystore
|
||||
|
||||
HttpHostUri* = distinct Uri
|
||||
|
||||
|
|
|
@ -117,8 +117,8 @@ proc getValidator*(validators: auto,
|
|||
proc addValidators*(node: BeaconNode) =
|
||||
info "Loading validators", validatorsDir = node.config.validatorsDir(),
|
||||
keystore_cache_available = not(isNil(node.keystoreCache))
|
||||
let
|
||||
epoch = node.currentSlot().epoch
|
||||
let epoch = node.currentSlot().epoch
|
||||
|
||||
for keystore in listLoadableKeystores(node.config, node.keystoreCache):
|
||||
let
|
||||
data = withState(node.dag.headState):
|
||||
|
@ -132,7 +132,33 @@ proc addValidators*(node: BeaconNode) =
|
|||
keystore.pubkey, index, epoch)
|
||||
gasLimit = node.consensusManager[].getGasLimit(keystore.pubkey)
|
||||
|
||||
v = node.attachedValidators[].addValidator(keystore, feeRecipient, gasLimit)
|
||||
v = node.attachedValidators[].addValidator(keystore, feeRecipient,
|
||||
gasLimit)
|
||||
v.updateValidator(data)
|
||||
|
||||
let dynamicStores =
|
||||
try:
|
||||
waitFor(queryValidatorsSource(node.config))
|
||||
except CatchableError as exc:
|
||||
warn "Unexpected error happens while polling validator's source",
|
||||
error = $exc.name, reason = $exc.msg
|
||||
default(seq[KeystoreData])
|
||||
|
||||
for keystore in dynamicStores:
|
||||
let
|
||||
data =
|
||||
withState(node.dag.headState):
|
||||
getValidator(forkyState.data.validators.asSeq(), keystore.pubkey)
|
||||
index =
|
||||
if data.isSome():
|
||||
Opt.some(data.get().index)
|
||||
else:
|
||||
Opt.none(ValidatorIndex)
|
||||
feeRecipient =
|
||||
node.consensusManager[].getFeeRecipient(keystore.pubkey, index, epoch)
|
||||
gasLimit = node.consensusManager[].getGasLimit(keystore.pubkey)
|
||||
v = node.attachedValidators[].addValidator(keystore, feeRecipient,
|
||||
gasLimit)
|
||||
v.updateValidator(data)
|
||||
|
||||
proc getValidator*(node: BeaconNode, idx: ValidatorIndex): Opt[AttachedValidator] =
|
||||
|
|
|
@ -627,6 +627,70 @@ proc existsKeystore(keystoreDir: string,
|
|||
return true
|
||||
false
|
||||
|
||||
proc queryValidatorsSource*(config: AnyConf): Future[seq[KeystoreData]] {.
|
||||
async.} =
|
||||
var keystores: seq[KeystoreData]
|
||||
if config.validatorsSource.isNone() or
|
||||
len(config.validatorsSource.get()) == 0:
|
||||
return keystores
|
||||
|
||||
let vsource = config.validatorsSource.get()
|
||||
logScope:
|
||||
validators_source = vsource
|
||||
|
||||
let
|
||||
httpFlags: HttpClientFlags = {}
|
||||
prestoFlags = {RestClientFlag.CommaSeparatedArray}
|
||||
socketFlags = {SocketFlags.TcpNoDelay}
|
||||
client =
|
||||
block:
|
||||
let res = RestClientRef.new(vsource, prestoFlags,
|
||||
httpFlags, socketFlags = socketFlags)
|
||||
if res.isErr():
|
||||
# TODO keep trying in case of temporary network failure
|
||||
warn "Unable to resolve validator's source distributed signer address"
|
||||
return keystores
|
||||
res.get()
|
||||
keys =
|
||||
try:
|
||||
let response = await getKeysPlain(client)
|
||||
if response.status != 200:
|
||||
warn "Remote validator's source responded with error",
|
||||
error = response.status
|
||||
return keystores
|
||||
|
||||
let res = decodeBytes(Web3SignerKeysResponse, response.data,
|
||||
response.contentType)
|
||||
if res.isErr():
|
||||
warn "Unable to obtain validator's source response",
|
||||
reason = res.error
|
||||
return keystores
|
||||
|
||||
res.get()
|
||||
except RestError as exc:
|
||||
warn "Unable to poll validator's source", reason = $exc.msg
|
||||
return keystores
|
||||
except CancelledError as exc:
|
||||
debug "The polling of validator's source was interrupted"
|
||||
raise exc
|
||||
except CatchableError as exc:
|
||||
warn "Unexpected error occured while polling validator's source",
|
||||
error = $exc.name, reason = $exc.msg
|
||||
return keystores
|
||||
|
||||
for pubkey in keys:
|
||||
keystores.add(KeystoreData(
|
||||
kind: KeystoreKind.Remote,
|
||||
handle: FileLockHandle(opened: false),
|
||||
pubkey: pubkey,
|
||||
remotes: @[RemoteSignerInfo(
|
||||
url: HttpHostUri(parseUri(vsource)),
|
||||
pubkey: pubkey)],
|
||||
flags: {RemoteKeystoreFlag.DynamicKeystore},
|
||||
remoteType: RemoteSignerType.Web3Signer))
|
||||
|
||||
keystores
|
||||
|
||||
iterator listLoadableKeys*(validatorsDir, secretsDir: string,
|
||||
keysMask: set[KeystoreKind]): CookedPubKey =
|
||||
try:
|
||||
|
@ -1244,8 +1308,8 @@ proc saveKeystore*(
|
|||
|
||||
proc importKeystore*(pool: var ValidatorPool,
|
||||
validatorsDir: string,
|
||||
keystore: RemoteKeystore): ImportResult[KeystoreData]
|
||||
{.raises: [].} =
|
||||
keystore: RemoteKeystore): ImportResult[KeystoreData] {.
|
||||
raises: [].} =
|
||||
let
|
||||
publicKey = keystore.pubkey
|
||||
keyName = publicKey.fsName
|
||||
|
@ -1253,9 +1317,9 @@ proc importKeystore*(pool: var ValidatorPool,
|
|||
|
||||
# We check `publicKey`.
|
||||
let cookedKey = publicKey.load().valueOr:
|
||||
return err(
|
||||
AddValidatorFailure.init(AddValidatorStatus.failed,
|
||||
"Invalid validator's public key"))
|
||||
return err(
|
||||
AddValidatorFailure.init(AddValidatorStatus.failed,
|
||||
"Invalid validator's public key"))
|
||||
|
||||
# We check `publicKey` in memory storage first.
|
||||
if publicKey in pool:
|
||||
|
|
|
@ -146,12 +146,16 @@ proc addRemoteValidator(pool: var ValidatorPool, keystore: KeystoreData,
|
|||
activationEpoch: FAR_FUTURE_EPOCH,
|
||||
)
|
||||
pool.validators[v.pubkey] = v
|
||||
notice "Remote validator attached",
|
||||
pubkey = v.pubkey,
|
||||
validator = shortLog(v),
|
||||
remote_signer = $keystore.remotes,
|
||||
initial_fee_recipient = feeRecipient.toHex(),
|
||||
initial_gas_limit = gasLimit
|
||||
if RemoteKeystoreFlag.DynamicKeystore in keystore.flags:
|
||||
notice "Dynamic remote validator attached", pubkey = v.pubkey,
|
||||
validator = shortLog(v), remote_signer = $keystore.remotes,
|
||||
initial_fee_recipient = feeRecipient.toHex(),
|
||||
initial_gas_limit = gasLimit
|
||||
else:
|
||||
notice "Remote validator attached", pubkey = v.pubkey,
|
||||
validator = shortLog(v), remote_signer = $keystore.remotes,
|
||||
initial_fee_recipient = feeRecipient.toHex(),
|
||||
initial_gas_limit = gasLimit
|
||||
|
||||
validators.set(pool.count().int64)
|
||||
|
||||
|
|
|
@ -33,6 +33,7 @@ The following options are available:
|
|||
--network The Eth2 network to join [=mainnet].
|
||||
-d, --data-dir The directory where nimbus will store all blockchain data.
|
||||
--validators-dir A directory containing validator keystores.
|
||||
--validators-source Remote Web3Signer URL that will be used as a source of validators.
|
||||
--secrets-dir A directory containing validator keystore passwords.
|
||||
--wallets-dir A directory containing wallet files.
|
||||
--web3-url One or more execution layer Engine API URLs.
|
||||
|
|
Loading…
Reference in New Issue