Initial commit.
This commit is contained in:
parent
8ff0f623b4
commit
19e3c8b4e4
|
@ -161,6 +161,10 @@ type
|
||||||
desc: "A directory containing validator keystores"
|
desc: "A directory containing validator keystores"
|
||||||
name: "validators-dir" .}: Option[InputDir]
|
name: "validators-dir" .}: Option[InputDir]
|
||||||
|
|
||||||
|
validatorsSource* {.
|
||||||
|
desc: "Remote Web3Signer URL that will be used as a source of validators"
|
||||||
|
name: "validators-source"}: string
|
||||||
|
|
||||||
secretsDirFlag* {.
|
secretsDirFlag* {.
|
||||||
desc: "A directory containing validator keystore passwords"
|
desc: "A directory containing validator keystore passwords"
|
||||||
name: "secrets-dir" .}: Option[InputDir]
|
name: "secrets-dir" .}: Option[InputDir]
|
||||||
|
@ -875,6 +879,10 @@ type
|
||||||
desc: "A directory containing validator keystores"
|
desc: "A directory containing validator keystores"
|
||||||
name: "validators-dir" .}: Option[InputDir]
|
name: "validators-dir" .}: Option[InputDir]
|
||||||
|
|
||||||
|
validatorsSource* {.
|
||||||
|
desc: "Remote Web3Signer URL that will be used as a source of validators"
|
||||||
|
name: "validators-source"}: string
|
||||||
|
|
||||||
secretsDirFlag* {.
|
secretsDirFlag* {.
|
||||||
desc: "A directory containing validator keystore passwords"
|
desc: "A directory containing validator keystore passwords"
|
||||||
name: "secrets-dir" .}: Option[InputDir]
|
name: "secrets-dir" .}: Option[InputDir]
|
||||||
|
|
|
@ -90,6 +90,9 @@ proc initValidators(vc: ValidatorClientRef): Future[bool] {.async.} =
|
||||||
var duplicates: seq[ValidatorPubKey]
|
var duplicates: seq[ValidatorPubKey]
|
||||||
for keystore in listLoadableKeystores(vc.config, vc.keystoreCache):
|
for keystore in listLoadableKeystores(vc.config, vc.keystoreCache):
|
||||||
vc.addValidator(keystore)
|
vc.addValidator(keystore)
|
||||||
|
let dynamicKeystores = await queryValidatorsSource(vc.config)
|
||||||
|
for keystore in dynamicKeystores:
|
||||||
|
vc.addValidator(keystore)
|
||||||
return true
|
return true
|
||||||
|
|
||||||
proc initClock(vc: ValidatorClientRef): Future[BeaconClock] {.async.} =
|
proc initClock(vc: ValidatorClientRef): Future[BeaconClock] {.async.} =
|
||||||
|
|
|
@ -78,6 +78,11 @@ proc getKeys*(): RestResponse[Web3SignerKeysResponse] {.
|
||||||
meth: MethodGet, accept: "application/json" .}
|
meth: MethodGet, accept: "application/json" .}
|
||||||
## https://consensys.github.io/web3signer/web3signer-eth2.html#tag/Public-Key
|
## 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,
|
proc signDataPlain*(identifier: ValidatorPubKey,
|
||||||
body: Web3SignerRequest): RestPlainResponse {.
|
body: Web3SignerRequest): RestPlainResponse {.
|
||||||
rest, endpoint: "/api/v1/eth2/sign/{identifier}",
|
rest, endpoint: "/api/v1/eth2/sign/{identifier}",
|
||||||
|
|
|
@ -130,7 +130,7 @@ type
|
||||||
Local, Remote
|
Local, Remote
|
||||||
|
|
||||||
RemoteKeystoreFlag* {.pure.} = enum
|
RemoteKeystoreFlag* {.pure.} = enum
|
||||||
IgnoreSSLVerification
|
IgnoreSSLVerification, DynamicKeystore
|
||||||
|
|
||||||
HttpHostUri* = distinct Uri
|
HttpHostUri* = distinct Uri
|
||||||
|
|
||||||
|
|
|
@ -117,8 +117,8 @@ proc getValidator*(validators: auto,
|
||||||
proc addValidators*(node: BeaconNode) =
|
proc addValidators*(node: BeaconNode) =
|
||||||
info "Loading validators", validatorsDir = node.config.validatorsDir(),
|
info "Loading validators", validatorsDir = node.config.validatorsDir(),
|
||||||
keystore_cache_available = not(isNil(node.keystoreCache))
|
keystore_cache_available = not(isNil(node.keystoreCache))
|
||||||
let
|
let epoch = node.currentSlot().epoch
|
||||||
epoch = node.currentSlot().epoch
|
|
||||||
for keystore in listLoadableKeystores(node.config, node.keystoreCache):
|
for keystore in listLoadableKeystores(node.config, node.keystoreCache):
|
||||||
let
|
let
|
||||||
data = withState(node.dag.headState):
|
data = withState(node.dag.headState):
|
||||||
|
@ -132,7 +132,33 @@ proc addValidators*(node: BeaconNode) =
|
||||||
keystore.pubkey, index, epoch)
|
keystore.pubkey, index, epoch)
|
||||||
gasLimit = node.consensusManager[].getGasLimit(keystore.pubkey)
|
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)
|
v.updateValidator(data)
|
||||||
|
|
||||||
proc getValidator*(node: BeaconNode, idx: ValidatorIndex): Opt[AttachedValidator] =
|
proc getValidator*(node: BeaconNode, idx: ValidatorIndex): Opt[AttachedValidator] =
|
||||||
|
|
|
@ -627,6 +627,67 @@ proc existsKeystore(keystoreDir: string,
|
||||||
return true
|
return true
|
||||||
false
|
false
|
||||||
|
|
||||||
|
proc queryValidatorsSource*(config: AnyConf): Future[seq[KeystoreData]] {.
|
||||||
|
async.} =
|
||||||
|
var keystores: seq[KeystoreData]
|
||||||
|
if len(config.validatorsSource) == 0:
|
||||||
|
return keystores
|
||||||
|
|
||||||
|
let
|
||||||
|
httpFlags: HttpClientFlags = {}
|
||||||
|
prestoFlags = {RestClientFlag.CommaSeparatedArray}
|
||||||
|
socketFlags = {SocketFlags.TcpNoDelay}
|
||||||
|
client =
|
||||||
|
block:
|
||||||
|
let res = RestClientRef.new(config.validatorsSource, 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", remote_url = config.validatorsSource
|
||||||
|
return keystores
|
||||||
|
else:
|
||||||
|
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(config.validatorsSource)),
|
||||||
|
pubkey: pubkey)],
|
||||||
|
flags: {RemoteKeystoreFlag.DynamicKeystore},
|
||||||
|
remoteType: RemoteSignerType.Web3Signer))
|
||||||
|
|
||||||
|
keystores
|
||||||
|
|
||||||
iterator listLoadableKeys*(validatorsDir, secretsDir: string,
|
iterator listLoadableKeys*(validatorsDir, secretsDir: string,
|
||||||
keysMask: set[KeystoreKind]): CookedPubKey =
|
keysMask: set[KeystoreKind]): CookedPubKey =
|
||||||
try:
|
try:
|
||||||
|
@ -1244,8 +1305,8 @@ proc saveKeystore*(
|
||||||
|
|
||||||
proc importKeystore*(pool: var ValidatorPool,
|
proc importKeystore*(pool: var ValidatorPool,
|
||||||
validatorsDir: string,
|
validatorsDir: string,
|
||||||
keystore: RemoteKeystore): ImportResult[KeystoreData]
|
keystore: RemoteKeystore): ImportResult[KeystoreData] {.
|
||||||
{.raises: [].} =
|
raises: [].} =
|
||||||
let
|
let
|
||||||
publicKey = keystore.pubkey
|
publicKey = keystore.pubkey
|
||||||
keyName = publicKey.fsName
|
keyName = publicKey.fsName
|
||||||
|
|
|
@ -146,10 +146,14 @@ proc addRemoteValidator(pool: var ValidatorPool, keystore: KeystoreData,
|
||||||
activationEpoch: FAR_FUTURE_EPOCH,
|
activationEpoch: FAR_FUTURE_EPOCH,
|
||||||
)
|
)
|
||||||
pool.validators[v.pubkey] = v
|
pool.validators[v.pubkey] = v
|
||||||
notice "Remote validator attached",
|
if RemoteKeystoreFlag.DynamicKeystore in keystore.flags:
|
||||||
pubkey = v.pubkey,
|
notice "Dynamic remote validator attached", pubkey = v.pubkey,
|
||||||
validator = shortLog(v),
|
validator = shortLog(v), remote_signer = $keystore.remotes,
|
||||||
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_fee_recipient = feeRecipient.toHex(),
|
||||||
initial_gas_limit = gasLimit
|
initial_gas_limit = gasLimit
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue