Keymanager API for the validator client (#3976)
* Keymanager API for the validator client * Properly treat the 'description' field as optional when loading Keystores * Spec-compliant serialization of the slashing data in Keymanager's DeleteKeys response () Fixes #3940 Fixes #3964 Closes #3884 by adding test
This commit is contained in:
parent
a7192f5d6c
commit
fca20e08d6
|
@ -109,21 +109,38 @@ OK: 2/2 Fail: 0/2 Skip: 0/2
|
|||
+ parent sanity OK
|
||||
```
|
||||
OK: 2/2 Fail: 0/2 Skip: 0/2
|
||||
## DeleteKeys requests [Preset: mainnet]
|
||||
## DeleteKeys requests [Beacon Node] [Preset: mainnet]
|
||||
```diff
|
||||
+ Deleting not existing key [Preset: mainnet] OK
|
||||
+ Invalid Authorization Header [Preset: mainnet] OK
|
||||
+ Invalid Authorization Token [Preset: mainnet] OK
|
||||
+ Missing Authorization header [Preset: mainnet] OK
|
||||
+ Deleting not existing key [Beacon Node] [Preset: mainnet] OK
|
||||
+ Invalid Authorization Header [Beacon Node] [Preset: mainnet] OK
|
||||
+ Invalid Authorization Token [Beacon Node] [Preset: mainnet] OK
|
||||
+ Missing Authorization header [Beacon Node] [Preset: mainnet] OK
|
||||
```
|
||||
OK: 4/4 Fail: 0/4 Skip: 0/4
|
||||
## DeleteRemoteKeys requests [Preset: mainnet]
|
||||
## DeleteKeys requests [Validator Client] [Preset: mainnet]
|
||||
```diff
|
||||
+ Deleting existing local key and remote key [Preset: mainnet] OK
|
||||
+ Deleting not existing key [Preset: mainnet] OK
|
||||
+ Invalid Authorization Header [Preset: mainnet] OK
|
||||
+ Invalid Authorization Token [Preset: mainnet] OK
|
||||
+ Missing Authorization header [Preset: mainnet] OK
|
||||
+ Deleting not existing key [Validator Client] [Preset: mainnet] OK
|
||||
+ Invalid Authorization Header [Validator Client] [Preset: mainnet] OK
|
||||
+ Invalid Authorization Token [Validator Client] [Preset: mainnet] OK
|
||||
+ Missing Authorization header [Validator Client] [Preset: mainnet] OK
|
||||
```
|
||||
OK: 4/4 Fail: 0/4 Skip: 0/4
|
||||
## DeleteRemoteKeys requests [Beacon Node] [Preset: mainnet]
|
||||
```diff
|
||||
+ Deleting existing local key and remote key [Beacon Node] [Preset: mainnet] OK
|
||||
+ Deleting not existing key [Beacon Node] [Preset: mainnet] OK
|
||||
+ Invalid Authorization Header [Beacon Node] [Preset: mainnet] OK
|
||||
+ Invalid Authorization Token [Beacon Node] [Preset: mainnet] OK
|
||||
+ Missing Authorization header [Beacon Node] [Preset: mainnet] OK
|
||||
```
|
||||
OK: 5/5 Fail: 0/5 Skip: 0/5
|
||||
## DeleteRemoteKeys requests [Validator Client] [Preset: mainnet]
|
||||
```diff
|
||||
+ Deleting existing local key and remote key [Validator Client] [Preset: mainnet] OK
|
||||
+ Deleting not existing key [Validator Client] [Preset: mainnet] OK
|
||||
+ Invalid Authorization Header [Validator Client] [Preset: mainnet] OK
|
||||
+ Invalid Authorization Token [Validator Client] [Preset: mainnet] OK
|
||||
+ Missing Authorization header [Validator Client] [Preset: mainnet] OK
|
||||
```
|
||||
OK: 5/5 Fail: 0/5 Skip: 0/5
|
||||
## Diverging hardforks
|
||||
|
@ -169,15 +186,26 @@ OK: 3/3 Fail: 0/3 Skip: 0/3
|
|||
+ addExitMessage/getVoluntaryExitMessage OK
|
||||
```
|
||||
OK: 3/3 Fail: 0/3 Skip: 0/3
|
||||
## Fee recipient management [Preset: mainnet]
|
||||
## Fee recipient management [Beacon Node] [Preset: mainnet]
|
||||
```diff
|
||||
+ Configuring the fee recpient [Preset: mainnet] OK
|
||||
+ Invalid Authorization Header [Preset: mainnet] OK
|
||||
+ Invalid Authorization Token [Preset: mainnet] OK
|
||||
+ Missing Authorization header [Preset: mainnet] OK
|
||||
+ Obtaining the fee recpient of a missing validator returns 404 [Preset: mainnet] OK
|
||||
+ Obtaining the fee recpient of an unconfigured validator returns the suggested default [Pre OK
|
||||
+ Setting the fee recipient on a missing validator creates a record for it [Preset: mainnet] OK
|
||||
+ Configuring the fee recpient [Beacon Node] [Preset: mainnet] OK
|
||||
+ Invalid Authorization Header [Beacon Node] [Preset: mainnet] OK
|
||||
+ Invalid Authorization Token [Beacon Node] [Preset: mainnet] OK
|
||||
+ Missing Authorization header [Beacon Node] [Preset: mainnet] OK
|
||||
+ Obtaining the fee recpient of a missing validator returns 404 [Beacon Node] [Preset: mainn OK
|
||||
+ Obtaining the fee recpient of an unconfigured validator returns the suggested default [Bea OK
|
||||
+ Setting the fee recipient on a missing validator creates a record for it [Beacon Node] [Pr OK
|
||||
```
|
||||
OK: 7/7 Fail: 0/7 Skip: 0/7
|
||||
## Fee recipient management [Validator Client] [Preset: mainnet]
|
||||
```diff
|
||||
+ Configuring the fee recpient [Validator Client] [Preset: mainnet] OK
|
||||
+ Invalid Authorization Header [Validator Client] [Preset: mainnet] OK
|
||||
+ Invalid Authorization Token [Validator Client] [Preset: mainnet] OK
|
||||
+ Missing Authorization header [Validator Client] [Preset: mainnet] OK
|
||||
+ Obtaining the fee recpient of a missing validator returns 404 [Validator Client] [Preset: OK
|
||||
+ Obtaining the fee recpient of an unconfigured validator returns the suggested default [Val OK
|
||||
+ Setting the fee recipient on a missing validator creates a record for it [Validator Client OK
|
||||
```
|
||||
OK: 7/7 Fail: 0/7 Skip: 0/7
|
||||
## FinalizedBlocks [Preset: mainnet]
|
||||
|
@ -235,20 +263,36 @@ OK: 1/1 Fail: 0/1 Skip: 0/1
|
|||
+ is_aggregator OK
|
||||
```
|
||||
OK: 4/4 Fail: 0/4 Skip: 0/4
|
||||
## ImportKeystores requests [Preset: mainnet]
|
||||
## ImportKeystores requests [Beacon Node] [Preset: mainnet]
|
||||
```diff
|
||||
+ ImportKeystores/ListKeystores/DeleteKeystores [Preset: mainnet] OK
|
||||
+ Invalid Authorization Header [Preset: mainnet] OK
|
||||
+ Invalid Authorization Token [Preset: mainnet] OK
|
||||
+ Missing Authorization header [Preset: mainnet] OK
|
||||
+ ImportKeystores/ListKeystores/DeleteKeystores [Beacon Node] [Preset: mainnet] OK
|
||||
+ Invalid Authorization Header [Beacon Node] [Preset: mainnet] OK
|
||||
+ Invalid Authorization Token [Beacon Node] [Preset: mainnet] OK
|
||||
+ Missing Authorization header [Beacon Node] [Preset: mainnet] OK
|
||||
```
|
||||
OK: 4/4 Fail: 0/4 Skip: 0/4
|
||||
## ImportRemoteKeys/ListRemoteKeys/DeleteRemoteKeys [Preset: mainnet]
|
||||
## ImportKeystores requests [Validator Client] [Preset: mainnet]
|
||||
```diff
|
||||
+ Importing list of remote keys [Preset: mainnet] OK
|
||||
+ Invalid Authorization Header [Preset: mainnet] OK
|
||||
+ Invalid Authorization Token [Preset: mainnet] OK
|
||||
+ Missing Authorization header [Preset: mainnet] OK
|
||||
+ ImportKeystores/ListKeystores/DeleteKeystores [Validator Client] [Preset: mainnet] OK
|
||||
+ Invalid Authorization Header [Validator Client] [Preset: mainnet] OK
|
||||
+ Invalid Authorization Token [Validator Client] [Preset: mainnet] OK
|
||||
+ Missing Authorization header [Validator Client] [Preset: mainnet] OK
|
||||
```
|
||||
OK: 4/4 Fail: 0/4 Skip: 0/4
|
||||
## ImportRemoteKeys/ListRemoteKeys/DeleteRemoteKeys [Beacon Node] [Preset: mainnet]
|
||||
```diff
|
||||
+ Importing list of remote keys [Beacon Node] [Preset: mainnet] OK
|
||||
+ Invalid Authorization Header [Beacon Node] [Preset: mainnet] OK
|
||||
+ Invalid Authorization Token [Beacon Node] [Preset: mainnet] OK
|
||||
+ Missing Authorization header [Beacon Node] [Preset: mainnet] OK
|
||||
```
|
||||
OK: 4/4 Fail: 0/4 Skip: 0/4
|
||||
## ImportRemoteKeys/ListRemoteKeys/DeleteRemoteKeys [Validator Client] [Preset: mainnet]
|
||||
```diff
|
||||
+ Importing list of remote keys [Validator Client] [Preset: mainnet] OK
|
||||
+ Invalid Authorization Header [Validator Client] [Preset: mainnet] OK
|
||||
+ Invalid Authorization Token [Validator Client] [Preset: mainnet] OK
|
||||
+ Missing Authorization header [Validator Client] [Preset: mainnet] OK
|
||||
```
|
||||
OK: 4/4 Fail: 0/4 Skip: 0/4
|
||||
## Interop
|
||||
|
@ -268,17 +312,20 @@ OK: 3/3 Fail: 0/3 Skip: 0/3
|
|||
OK: 4/4 Fail: 0/4 Skip: 0/4
|
||||
## KeyStorage testing suite
|
||||
```diff
|
||||
+ Load Prysm keystore OK
|
||||
+ Pbkdf2 errors OK
|
||||
+ [PBKDF2] Keystore decryption OK
|
||||
+ [PBKDF2] Keystore decryption (requireAllFields, allowUnknownFields) OK
|
||||
+ [PBKDF2] Keystore encryption OK
|
||||
+ [PBKDF2] Network Keystore decryption OK
|
||||
+ [PBKDF2] Network Keystore encryption OK
|
||||
+ [SCRYPT] Keystore decryption OK
|
||||
+ [SCRYPT] Keystore decryption (requireAllFields, allowUnknownFields) OK
|
||||
+ [SCRYPT] Keystore encryption OK
|
||||
+ [SCRYPT] Network Keystore decryption OK
|
||||
+ [SCRYPT] Network Keystore encryption OK
|
||||
```
|
||||
OK: 9/9 Fail: 0/9 Skip: 0/9
|
||||
OK: 12/12 Fail: 0/12 Skip: 0/12
|
||||
## Light client [Preset: mainnet]
|
||||
```diff
|
||||
+ Init from checkpoint OK
|
||||
|
@ -302,20 +349,36 @@ OK: 3/3 Fail: 0/3 Skip: 0/3
|
|||
+ Sync (Strict) [Preset: mainnet] OK
|
||||
```
|
||||
OK: 12/12 Fail: 0/12 Skip: 0/12
|
||||
## ListKeys requests [Preset: mainnet]
|
||||
## ListKeys requests [Beacon Node] [Preset: mainnet]
|
||||
```diff
|
||||
+ Correct token provided [Preset: mainnet] OK
|
||||
+ Invalid Authorization Header [Preset: mainnet] OK
|
||||
+ Invalid Authorization Token [Preset: mainnet] OK
|
||||
+ Missing Authorization header [Preset: mainnet] OK
|
||||
+ Correct token provided [Beacon Node] [Preset: mainnet] OK
|
||||
+ Invalid Authorization Header [Beacon Node] [Preset: mainnet] OK
|
||||
+ Invalid Authorization Token [Beacon Node] [Preset: mainnet] OK
|
||||
+ Missing Authorization header [Beacon Node] [Preset: mainnet] OK
|
||||
```
|
||||
OK: 4/4 Fail: 0/4 Skip: 0/4
|
||||
## ListRemoteKeys requests [Preset: mainnet]
|
||||
## ListKeys requests [Validator Client] [Preset: mainnet]
|
||||
```diff
|
||||
+ Correct token provided [Preset: mainnet] OK
|
||||
+ Invalid Authorization Header [Preset: mainnet] OK
|
||||
+ Invalid Authorization Token [Preset: mainnet] OK
|
||||
+ Missing Authorization header [Preset: mainnet] OK
|
||||
+ Correct token provided [Validator Client] [Preset: mainnet] OK
|
||||
+ Invalid Authorization Header [Validator Client] [Preset: mainnet] OK
|
||||
+ Invalid Authorization Token [Validator Client] [Preset: mainnet] OK
|
||||
+ Missing Authorization header [Validator Client] [Preset: mainnet] OK
|
||||
```
|
||||
OK: 4/4 Fail: 0/4 Skip: 0/4
|
||||
## ListRemoteKeys requests [Beacon Node] [Preset: mainnet]
|
||||
```diff
|
||||
+ Correct token provided [Beacon Node] [Preset: mainnet] OK
|
||||
+ Invalid Authorization Header [Beacon Node] [Preset: mainnet] OK
|
||||
+ Invalid Authorization Token [Beacon Node] [Preset: mainnet] OK
|
||||
+ Missing Authorization header [Beacon Node] [Preset: mainnet] OK
|
||||
```
|
||||
OK: 4/4 Fail: 0/4 Skip: 0/4
|
||||
## ListRemoteKeys requests [Validator Client] [Preset: mainnet]
|
||||
```diff
|
||||
+ Correct token provided [Validator Client] [Preset: mainnet] OK
|
||||
+ Invalid Authorization Header [Validator Client] [Preset: mainnet] OK
|
||||
+ Invalid Authorization Token [Validator Client] [Preset: mainnet] OK
|
||||
+ Missing Authorization header [Validator Client] [Preset: mainnet] OK
|
||||
```
|
||||
OK: 4/4 Fail: 0/4 Skip: 0/4
|
||||
## Message signatures
|
||||
|
@ -358,7 +421,12 @@ OK: 12/12 Fail: 0/12 Skip: 0/12
|
|||
+ vesion 2 single remote OK
|
||||
```
|
||||
OK: 3/3 Fail: 0/3 Skip: 0/3
|
||||
## Serialization/deserialization [Preset: mainnet]
|
||||
## Serialization/deserialization [Beacon Node] [Preset: mainnet]
|
||||
```diff
|
||||
+ Deserialization test vectors OK
|
||||
```
|
||||
OK: 1/1 Fail: 0/1 Skip: 0/1
|
||||
## Serialization/deserialization [Validator Client] [Preset: mainnet]
|
||||
```diff
|
||||
+ Deserialization test vectors OK
|
||||
```
|
||||
|
@ -587,4 +655,4 @@ OK: 1/1 Fail: 0/1 Skip: 0/1
|
|||
OK: 9/9 Fail: 0/9 Skip: 0/9
|
||||
|
||||
---TOTAL---
|
||||
OK: 328/333 Fail: 0/333 Skip: 5/333
|
||||
OK: 364/369 Fail: 0/369 Skip: 5/369
|
||||
|
|
|
@ -11,7 +11,7 @@ import
|
|||
std/osproc,
|
||||
|
||||
# Nimble packages
|
||||
chronos, json_rpc/servers/httpserver, presto,
|
||||
chronos, json_rpc/servers/httpserver, presto, bearssl/rand,
|
||||
|
||||
# Local modules
|
||||
"."/[beacon_clock, beacon_chain_db, conf, light_client],
|
||||
|
@ -25,7 +25,8 @@ import
|
|||
./spec/eth2_apis/dynamic_fee_recipients,
|
||||
./sync/[optimistic_sync_light_client, sync_manager, request_manager],
|
||||
./validators/[
|
||||
action_tracker, message_router, validator_monitor, validator_pool],
|
||||
action_tracker, message_router, validator_monitor, validator_pool,
|
||||
keystore_management],
|
||||
./rpc/state_ttl_cache
|
||||
|
||||
export
|
||||
|
@ -68,8 +69,8 @@ type
|
|||
eth1Monitor*: Eth1Monitor
|
||||
payloadBuilderRestClient*: RestClientRef
|
||||
restServer*: RestServerRef
|
||||
keymanagerHost*: ref KeymanagerHost
|
||||
keymanagerServer*: RestServerRef
|
||||
keymanagerToken*: Option[string]
|
||||
eventBus*: EventBus
|
||||
vcProcess*: Process
|
||||
requestManager*: RequestManager
|
||||
|
@ -102,5 +103,8 @@ template findIt*(s: openArray, predicate: untyped): int =
|
|||
break
|
||||
res
|
||||
|
||||
template rng*(node: BeaconNode): ref HmacDrbgContext =
|
||||
node.network.rng
|
||||
|
||||
proc currentSlot*(node: BeaconNode): Slot =
|
||||
node.beaconClock.now.slotOrZero
|
||||
|
|
|
@ -807,6 +807,30 @@ type
|
|||
desc: "A directory containing validator keystore passwords"
|
||||
name: "secrets-dir" .}: Option[InputDir]
|
||||
|
||||
restRequestTimeout* {.
|
||||
defaultValue: 0
|
||||
defaultValueDesc: "infinite"
|
||||
desc: "The number of seconds to wait until complete REST request " &
|
||||
"will be received"
|
||||
name: "rest-request-timeout" .}: Natural
|
||||
|
||||
restMaxRequestBodySize* {.
|
||||
defaultValue: 16_384
|
||||
desc: "Maximum size of REST request body (kilobytes)"
|
||||
name: "rest-max-body-size" .}: Natural
|
||||
|
||||
restMaxRequestHeadersSize* {.
|
||||
defaultValue: 64
|
||||
desc: "Maximum size of REST request headers (kilobytes)"
|
||||
name: "rest-max-headers-size" .}: Natural
|
||||
|
||||
# Same option as appears in Lighthouse and Prysm
|
||||
# https://lighthouse-book.sigmaprime.io/suggested-fee-recipient.html
|
||||
# https://github.com/prysmaticlabs/prysm/pull/10312
|
||||
suggestedFeeRecipient* {.
|
||||
desc: "Suggested fee recipient"
|
||||
name: "suggested-fee-recipient" .}: Option[Address]
|
||||
|
||||
keymanagerEnabled* {.
|
||||
desc: "Enable the REST keymanager API (BETA version)"
|
||||
defaultValue: false
|
||||
|
@ -824,6 +848,11 @@ type
|
|||
defaultValueDesc: $defaultAdminListenAddressDesc
|
||||
name: "keymanager-address" .}: ValidIpAddress
|
||||
|
||||
keymanagerAllowedOrigin* {.
|
||||
desc: "Limit the access to the Keymanager API to a particular hostname " &
|
||||
"(for CORS-enabled clients such as browsers)"
|
||||
name: "keymanager-allow-origin" .}: Option[string]
|
||||
|
||||
keymanagerTokenFile* {.
|
||||
desc: "A file specifying the authorizition token required for accessing the keymanager API"
|
||||
name: "keymanager-token-file" .}: Option[InputFile]
|
||||
|
@ -1204,6 +1233,13 @@ proc loadEth2Network*(
|
|||
template loadEth2Network*(config: BeaconNodeConf): Eth2NetworkMetadata =
|
||||
loadEth2Network(config.eth2Network)
|
||||
|
||||
func defaultFeeRecipient*(conf: AnyConf): Eth1Address =
|
||||
if conf.suggestedFeeRecipient.isSome:
|
||||
conf.suggestedFeeRecipient.get
|
||||
else:
|
||||
# https://github.com/nim-lang/Nim/issues/19802
|
||||
(static(default(Eth1Address)))
|
||||
|
||||
proc loadJwtSecret*(
|
||||
rng: var HmacDrbgContext,
|
||||
dataDir: string,
|
||||
|
|
|
@ -10,7 +10,6 @@ when (NimMajor, NimMinor) < (1, 4):
|
|||
else:
|
||||
{.push raises: [].}
|
||||
|
||||
|
||||
import chronicles
|
||||
import stew/io2
|
||||
import spec/keystore
|
||||
|
|
|
@ -296,11 +296,9 @@ declareGauge nbc_gossipsub_good_fanout,
|
|||
declareGauge nbc_gossipsub_healthy_fanout,
|
||||
"numbers of topics with dHigh fanout"
|
||||
|
||||
const delayBuckets = [1.0, 5.0, 10.0, 20.0, 40.0, 60.0]
|
||||
|
||||
declareHistogram nbc_resolve_time,
|
||||
"Time(s) used while resolving peer information",
|
||||
buckets = delayBuckets
|
||||
buckets = [1.0, 5.0, 10.0, 20.0, 40.0, 60.0]
|
||||
|
||||
const
|
||||
libp2p_pki_schemes {.strdefine.} = ""
|
||||
|
|
|
@ -98,37 +98,7 @@ type
|
|||
template init(T: type RpcHttpServer, ip: ValidIpAddress, port: Port): T =
|
||||
newRpcHttpServer([initTAddress(ip, port)])
|
||||
|
||||
template init(T: type RestServerRef,
|
||||
ip: ValidIpAddress, port: Port,
|
||||
allowedOrigin: Option[string],
|
||||
config: BeaconNodeConf): T =
|
||||
let address = initTAddress(ip, port)
|
||||
let serverFlags = {HttpServerFlags.QueryCommaSeparatedArray,
|
||||
HttpServerFlags.NotifyDisconnect}
|
||||
let
|
||||
headersTimeout =
|
||||
if config.restRequestTimeout == 0:
|
||||
chronos.InfiniteDuration
|
||||
else:
|
||||
seconds(int64(config.restRequestTimeout))
|
||||
maxHeadersSize = config.restMaxRequestHeadersSize * 1024
|
||||
maxRequestBodySize = config.restMaxRequestBodySize * 1024
|
||||
let res = RestServerRef.new(getRouter(allowedOrigin),
|
||||
address, serverFlags = serverFlags,
|
||||
httpHeadersTimeout = headersTimeout,
|
||||
maxHeadersSize = maxHeadersSize,
|
||||
maxRequestBodySize = maxRequestBodySize)
|
||||
if res.isErr():
|
||||
notice "Rest server could not be started", address = $address,
|
||||
reason = res.error()
|
||||
nil
|
||||
else:
|
||||
notice "Starting REST HTTP server",
|
||||
url = "http://" & $ip & ":" & $port & "/"
|
||||
|
||||
res.get()
|
||||
|
||||
# https://github.com/ethereum/beacon-metrics/blob/master/metrics.md#interop-metrics
|
||||
# https://github.com/ethereum/eth2.0-metrics/blob/master/metrics.md#interop-metrics
|
||||
declareGauge beacon_slot, "Latest slot of the beacon chain state"
|
||||
declareGauge beacon_current_epoch, "Current epoch"
|
||||
|
||||
|
@ -675,51 +645,10 @@ proc init*(T: type BeaconNode,
|
|||
warn "Nimbus's JSON-RPC server has been removed. This includes the --rpc, --rpc-port, and --rpc-address configuration options. https://nimbus.guide/rest-api.html shows how to enable and configure the REST Beacon API server which replaces it."
|
||||
|
||||
let restServer = if config.restEnabled:
|
||||
RestServerRef.init(
|
||||
config.restAddress,
|
||||
config.restPort,
|
||||
config.restAllowedOrigin,
|
||||
config)
|
||||
else:
|
||||
nil
|
||||
|
||||
var keymanagerToken: Option[string]
|
||||
let keymanagerServer = if config.keymanagerEnabled:
|
||||
if config.keymanagerTokenFile.isNone:
|
||||
echo "To enable the Keymanager API, you must also specify " &
|
||||
"the --keymanager-token-file option."
|
||||
quit 1
|
||||
|
||||
let
|
||||
tokenFilePath = config.keymanagerTokenFile.get.string
|
||||
tokenFileReadRes = readAllChars(tokenFilePath)
|
||||
|
||||
if tokenFileReadRes.isErr:
|
||||
fatal "Failed to read the keymanager token file",
|
||||
error = $tokenFileReadRes.error
|
||||
quit 1
|
||||
|
||||
keymanagerToken = some tokenFileReadRes.value.strip
|
||||
if keymanagerToken.get.len == 0:
|
||||
fatal "The keymanager token should not be empty", tokenFilePath
|
||||
quit 1
|
||||
|
||||
if restServer != nil and
|
||||
config.restAddress == config.keymanagerAddress and
|
||||
config.restPort == config.keymanagerPort:
|
||||
if config.keymanagerAllowedOrigin.isSome and
|
||||
config.restAllowedOrigin != config.keymanagerAllowedOrigin:
|
||||
fatal "Please specify a separate port for the Keymanager API " &
|
||||
"if you want to restrict the origin in a different way " &
|
||||
"from the Beacon API"
|
||||
quit 1
|
||||
restServer
|
||||
else:
|
||||
RestServerRef.init(
|
||||
config.keymanagerAddress,
|
||||
config.keymanagerPort,
|
||||
config.keymanagerAllowedOrigin,
|
||||
config)
|
||||
RestServerRef.init(config.restAddress, config.restPort,
|
||||
config.keymanagerAllowedOrigin,
|
||||
validateBeaconApiQueries,
|
||||
config)
|
||||
else:
|
||||
nil
|
||||
|
||||
|
@ -743,6 +672,10 @@ proc init*(T: type BeaconNode,
|
|||
info "Loading slashing protection database (v2)",
|
||||
path = config.validatorsDir()
|
||||
|
||||
proc getValidatorIdx(pubkey: ValidatorPubKey): Option[ValidatorIndex] =
|
||||
withState(dag.headState):
|
||||
findValidator(state().data.validators.asSeq(), pubkey)
|
||||
|
||||
let
|
||||
slashingProtectionDB =
|
||||
SlashingProtectionDB.init(
|
||||
|
@ -750,6 +683,19 @@ proc init*(T: type BeaconNode,
|
|||
config.validatorsDir(), SlashingDbName)
|
||||
validatorPool = newClone(ValidatorPool.init(slashingProtectionDB))
|
||||
|
||||
keymanagerInitResult = initKeymanagerServer(config, restServer)
|
||||
keymanagerHost = if keymanagerInitResult.server != nil:
|
||||
newClone KeymanagerHost.init(
|
||||
validatorPool,
|
||||
rng,
|
||||
keymanagerInitResult.token,
|
||||
config.validatorsDir,
|
||||
config.secretsDir,
|
||||
config.defaultFeeRecipient,
|
||||
getValidatorIdx,
|
||||
getBeaconTime)
|
||||
else: nil
|
||||
|
||||
stateTtlCache =
|
||||
if config.restCacheSize > 0:
|
||||
StateTtlCache.init(
|
||||
|
@ -796,8 +742,8 @@ proc init*(T: type BeaconNode,
|
|||
eth1Monitor: eth1Monitor,
|
||||
payloadBuilderRestClient: payloadBuilderRestClient,
|
||||
restServer: restServer,
|
||||
keymanagerServer: keymanagerServer,
|
||||
keymanagerToken: keymanagerToken,
|
||||
keymanagerHost: keymanagerHost,
|
||||
keymanagerServer: keymanagerInitResult.server,
|
||||
eventBus: eventBus,
|
||||
actionTracker: ActionTracker.init(rng, config.subscribeAllSubnets),
|
||||
gossipState: {},
|
||||
|
@ -968,7 +914,7 @@ func hasSyncPubKey(node: BeaconNode, epoch: Epoch): auto =
|
|||
(func(pubkey: ValidatorPubKey): bool =
|
||||
node.syncCommitteeMsgPool.syncCommitteeSubscriptions.getOrDefault(
|
||||
pubkey, GENESIS_EPOCH) >= epoch or
|
||||
pubkey in node.attachedValidators.validators)
|
||||
pubkey in node.attachedValidators[].validators)
|
||||
|
||||
func getCurrentSyncCommiteeSubnets(node: BeaconNode, slot: Slot): SyncnetBits =
|
||||
let syncCommittee = withState(node.dag.headState):
|
||||
|
@ -1218,8 +1164,8 @@ proc onSlotEnd(node: BeaconNode, slot: Slot) {.async.} =
|
|||
# next slot
|
||||
|
||||
if node.dag.needStateCachesAndForkChoicePruning():
|
||||
if node.attachedValidators.validators.len > 0:
|
||||
node.attachedValidators
|
||||
if node.attachedValidators[].validators.len > 0:
|
||||
node.attachedValidators[]
|
||||
.slashingProtection
|
||||
# pruning is only done if the DB is set to pruning mode.
|
||||
.pruneAfterFinalization(
|
||||
|
@ -1568,7 +1514,7 @@ proc stop(node: BeaconNode) =
|
|||
except CatchableError as exc:
|
||||
warn "Couldn't stop network", msg = exc.msg
|
||||
|
||||
node.attachedValidators.slashingProtection.close()
|
||||
node.attachedValidators[].slashingProtection.close()
|
||||
node.attachedValidators[].close()
|
||||
node.db.close()
|
||||
notice "Databases closed"
|
||||
|
@ -1588,12 +1534,13 @@ proc startBackfillTask(node: BeaconNode) {.async.} =
|
|||
proc run(node: BeaconNode) {.raises: [Defect, CatchableError].} =
|
||||
bnStatus = BeaconNodeStatus.Running
|
||||
|
||||
if not(isNil(node.restServer)):
|
||||
if not isNil(node.restServer):
|
||||
node.restServer.installRestHandlers(node)
|
||||
node.restServer.start()
|
||||
|
||||
if not(isNil(node.keymanagerServer)):
|
||||
node.keymanagerServer.router.installKeymanagerHandlers(node)
|
||||
if not isNil(node.keymanagerServer):
|
||||
doAssert not isNil(node.keymanagerHost)
|
||||
node.keymanagerServer.router.installKeymanagerHandlers(node.keymanagerHost[])
|
||||
if node.keymanagerServer != node.restServer:
|
||||
node.keymanagerServer.start()
|
||||
|
||||
|
|
|
@ -17,9 +17,10 @@ import
|
|||
std/[tables, strutils, terminal, typetraits],
|
||||
|
||||
# Nimble packages
|
||||
chronos, confutils, toml_serialization,
|
||||
chronos, confutils, presto, toml_serialization,
|
||||
chronicles, chronicles/helpers as chroniclesHelpers, chronicles/topics_registry,
|
||||
stew/io2,
|
||||
presto,
|
||||
|
||||
# Local modules
|
||||
./spec/[helpers],
|
||||
|
@ -313,3 +314,95 @@ proc runSlotLoop*[T](node: T, startTime: BeaconTime,
|
|||
curSlot = wallSlot
|
||||
nextSlot = wallSlot + 1
|
||||
timeToNextSlot = nextSlot.start_beacon_time() - node.beaconClock.now()
|
||||
|
||||
proc init*(T: type RestServerRef,
|
||||
ip: ValidIpAddress,
|
||||
port: Port,
|
||||
allowedOrigin: Option[string],
|
||||
validateFn: PatternCallback,
|
||||
config: AnyConf): T =
|
||||
let address = initTAddress(ip, port)
|
||||
let serverFlags = {HttpServerFlags.QueryCommaSeparatedArray,
|
||||
HttpServerFlags.NotifyDisconnect}
|
||||
# We increase default timeout to help validator clients who poll our server
|
||||
# at least once per slot (12.seconds).
|
||||
let
|
||||
headersTimeout =
|
||||
if config.restRequestTimeout == 0:
|
||||
chronos.InfiniteDuration
|
||||
else:
|
||||
seconds(int64(config.restRequestTimeout))
|
||||
maxHeadersSize = config.restMaxRequestHeadersSize * 1024
|
||||
maxRequestBodySize = config.restMaxRequestBodySize * 1024
|
||||
|
||||
let res = try:
|
||||
RestServerRef.new(RestRouter.init(validateFn),
|
||||
address, serverFlags = serverFlags,
|
||||
httpHeadersTimeout = headersTimeout,
|
||||
maxHeadersSize = maxHeadersSize,
|
||||
maxRequestBodySize = maxRequestBodySize)
|
||||
except CatchableError as err:
|
||||
notice "Rest server could not be started", address = $address,
|
||||
reason = err.msg
|
||||
return nil
|
||||
|
||||
if res.isErr():
|
||||
notice "Rest server could not be started", address = $address,
|
||||
reason = res.error()
|
||||
nil
|
||||
else:
|
||||
notice "Starting REST HTTP server",
|
||||
url = "http://" & $ip & ":" & $port & "/"
|
||||
|
||||
res.get()
|
||||
|
||||
type
|
||||
KeymanagerInitResult* = object
|
||||
server*: RestServerRef
|
||||
token*: string
|
||||
|
||||
proc initKeymanagerServer*(
|
||||
config: AnyConf,
|
||||
existingRestServer: RestServerRef = nil): KeymanagerInitResult
|
||||
{.raises: [Defect].} =
|
||||
|
||||
var token: string
|
||||
let keymanagerServer = if config.keymanagerEnabled:
|
||||
if config.keymanagerTokenFile.isNone:
|
||||
echo "To enable the Keymanager API, you must also specify " &
|
||||
"the --keymanager-token-file option."
|
||||
quit 1
|
||||
|
||||
let
|
||||
tokenFilePath = config.keymanagerTokenFile.get.string
|
||||
tokenFileReadRes = readAllChars(tokenFilePath)
|
||||
|
||||
if tokenFileReadRes.isErr:
|
||||
fatal "Failed to read the keymanager token file",
|
||||
error = $tokenFileReadRes.error
|
||||
quit 1
|
||||
|
||||
token = tokenFileReadRes.value.strip
|
||||
if token.len == 0:
|
||||
fatal "The keymanager token should not be empty", tokenFilePath
|
||||
quit 1
|
||||
|
||||
when config is BeaconNodeConf:
|
||||
if existingRestServer != nil and
|
||||
config.restAddress == config.keymanagerAddress and
|
||||
config.restPort == config.keymanagerPort:
|
||||
existingRestServer
|
||||
else:
|
||||
RestServerRef.init(config.keymanagerAddress, config.keymanagerPort,
|
||||
config.keymanagerAllowedOrigin,
|
||||
validateKeymanagerApiQueries,
|
||||
config)
|
||||
else:
|
||||
RestServerRef.init(config.keymanagerAddress, config.keymanagerPort,
|
||||
config.keymanagerAllowedOrigin,
|
||||
validateKeymanagerApiQueries,
|
||||
config)
|
||||
else:
|
||||
nil
|
||||
|
||||
KeymanagerInitResult(server: keymanagerServer, token: token)
|
||||
|
|
|
@ -4,13 +4,13 @@
|
|||
# * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT).
|
||||
# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0).
|
||||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||
import metrics, metrics/chronos_httpserver
|
||||
import validator_client/[common, fallback_service, duties_service,
|
||||
attestation_service, fork_service,
|
||||
sync_committee_service, doppelganger_service]
|
||||
|
||||
type
|
||||
ValidatorClientError* = object of CatchableError
|
||||
import
|
||||
stew/io2, presto, metrics, metrics/chronos_httpserver,
|
||||
libp2p/crypto/crypto,
|
||||
./rpc/rest_key_management_api,
|
||||
./validator_client/[
|
||||
common, fallback_service, duties_service, fork_service,
|
||||
doppelganger_service, attestation_service, sync_committee_service]
|
||||
|
||||
proc initGenesis(vc: ValidatorClientRef): Future[RestGenesis] {.async.} =
|
||||
info "Initializing genesis", nodes_count = len(vc.beaconNodes)
|
||||
|
@ -89,10 +89,7 @@ proc initValidators(vc: ValidatorClientRef): Future[bool] {.async.} =
|
|||
for keystore in listLoadableKeystores(vc.config):
|
||||
let pubkey = keystore.pubkey
|
||||
if pubkey in duplicates:
|
||||
error "Duplicate validator's key found", validator_pubkey = pubkey
|
||||
return false
|
||||
elif keystore.kind == KeystoreKind.Remote:
|
||||
info "Remote validator client was skipped", validator_pubkey = pubkey
|
||||
warn "Duplicate validator key found", validator_pubkey = pubkey
|
||||
continue
|
||||
else:
|
||||
duplicates.add(pubkey)
|
||||
|
@ -146,7 +143,7 @@ proc shutdownMetrics(vc: ValidatorClientRef) {.async.} =
|
|||
|
||||
proc shutdownSlashingProtection(vc: ValidatorClientRef) =
|
||||
info "Closing slashing protection", path = vc.config.validatorsDir()
|
||||
vc.attachedValidators.slashingProtection.close()
|
||||
vc.attachedValidators[].slashingProtection.close()
|
||||
|
||||
proc onSlotStart(vc: ValidatorClientRef, wallTime: BeaconTime,
|
||||
lastSlot: Slot): Future[bool] {.async.} =
|
||||
|
@ -178,7 +175,58 @@ proc onSlotStart(vc: ValidatorClientRef, wallTime: BeaconTime,
|
|||
|
||||
return false
|
||||
|
||||
proc asyncInit(vc: ValidatorClientRef) {.async.} =
|
||||
proc new*(T: type ValidatorClientRef,
|
||||
config: ValidatorClientConf,
|
||||
rng: ref HmacDrbgContext): ValidatorClientRef =
|
||||
let beaconNodes =
|
||||
block:
|
||||
var servers: seq[BeaconNodeServerRef]
|
||||
let flags = {RestClientFlag.CommaSeparatedArray}
|
||||
for url in config.beaconNodes:
|
||||
let res = RestClientRef.new(url, flags = flags)
|
||||
if res.isErr():
|
||||
warn "Unable to resolve remote beacon node server's hostname",
|
||||
url = url
|
||||
else:
|
||||
servers.add(BeaconNodeServerRef(client: res.get(), endpoint: url))
|
||||
servers
|
||||
|
||||
if len(beaconNodes) == 0:
|
||||
# This should not happen, thanks to defaults in `conf.nim`
|
||||
fatal "Not enough beacon nodes in command line"
|
||||
quit 1
|
||||
|
||||
when declared(waitSignal):
|
||||
ValidatorClientRef(
|
||||
rng: rng,
|
||||
config: config,
|
||||
beaconNodes: beaconNodes,
|
||||
graffitiBytes: config.graffiti.get(defaultGraffitiBytes()),
|
||||
nodesAvailable: newAsyncEvent(),
|
||||
forksAvailable: newAsyncEvent(),
|
||||
gracefulExit: newAsyncEvent(),
|
||||
sigintHandleFut: waitSignal(SIGINT),
|
||||
sigtermHandleFut: waitSignal(SIGTERM)
|
||||
)
|
||||
else:
|
||||
ValidatorClientRef(
|
||||
rng: rng,
|
||||
config: config,
|
||||
beaconNodes: beaconNodes,
|
||||
graffitiBytes: config.graffiti.get(defaultGraffitiBytes()),
|
||||
nodesAvailable: newAsyncEvent(),
|
||||
forksAvailable: newAsyncEvent(),
|
||||
gracefulExit: newAsyncEvent(),
|
||||
sigintHandleFut: newFuture[void]("sigint_placeholder"),
|
||||
sigtermHandleFut: newFuture[void]("sigterm_placeholder")
|
||||
)
|
||||
|
||||
proc asyncInit(vc: ValidatorClientRef): Future[ValidatorClientRef] {.async.} =
|
||||
notice "Launching validator client", version = fullVersionStr,
|
||||
cmdParams = commandLineParams(),
|
||||
config = vc.config,
|
||||
beacon_nodes_count = len(vc.beaconNodes)
|
||||
|
||||
vc.beaconGenesis = await vc.initGenesis()
|
||||
info "Genesis information", genesis_time = vc.beaconGenesis.genesis_time,
|
||||
genesis_fork_version = vc.beaconGenesis.genesis_fork_version,
|
||||
|
@ -190,22 +238,24 @@ proc asyncInit(vc: ValidatorClientRef) {.async.} =
|
|||
raise newException(ValidatorClientError,
|
||||
"Could not initialize metrics server")
|
||||
|
||||
try:
|
||||
if not(await initValidators(vc)):
|
||||
await vc.shutdownMetrics()
|
||||
raise newException(ValidatorClientError,
|
||||
"Could not initialize local validators")
|
||||
except CancelledError:
|
||||
debug "Initialization process interrupted"
|
||||
await vc.shutdownMetrics()
|
||||
return
|
||||
|
||||
info "Initializing slashing protection", path = vc.config.validatorsDir()
|
||||
vc.attachedValidators.slashingProtection =
|
||||
SlashingProtectionDB.init(
|
||||
vc.beaconGenesis.genesis_validators_root,
|
||||
vc.config.validatorsDir(), "slashing_protection"
|
||||
)
|
||||
|
||||
let
|
||||
slashingProtectionDB =
|
||||
SlashingProtectionDB.init(
|
||||
vc.beaconGenesis.genesis_validators_root,
|
||||
vc.config.validatorsDir(), "slashing_protection")
|
||||
validatorPool = newClone(ValidatorPool.init(slashingProtectionDB))
|
||||
|
||||
vc.attachedValidators = validatorPool
|
||||
|
||||
if not(await initValidators(vc)):
|
||||
await vc.shutdownMetrics()
|
||||
raise newException(ValidatorClientError,
|
||||
"Could not initialize local validators")
|
||||
|
||||
let
|
||||
keymanagerInitResult = initKeymanagerServer(vc.config, nil)
|
||||
|
||||
try:
|
||||
vc.fallbackService = await FallbackServiceRef.init(vc)
|
||||
|
@ -214,6 +264,21 @@ proc asyncInit(vc: ValidatorClientRef) {.async.} =
|
|||
vc.doppelgangerService = await DoppelgangerServiceRef.init(vc)
|
||||
vc.attestationService = await AttestationServiceRef.init(vc)
|
||||
vc.syncCommitteeService = await SyncCommitteeServiceRef.init(vc)
|
||||
vc.keymanagerServer = keymanagerInitResult.server
|
||||
if vc.keymanagerServer != nil:
|
||||
func getValidatorIdx(pubkey: ValidatorPubKey): Option[ValidatorIndex] =
|
||||
none ValidatorIndex
|
||||
|
||||
vc.keymanagerHost = newClone KeymanagerHost.init(
|
||||
validatorPool,
|
||||
vc.rng,
|
||||
keymanagerInitResult.token,
|
||||
vc.config.validatorsDir,
|
||||
vc.config.secretsDir,
|
||||
vc.config.defaultFeeRecipient,
|
||||
getValidatorIdx,
|
||||
vc.beaconClock.getBeaconTimeFn)
|
||||
|
||||
except CatchableError as exc:
|
||||
warn "Unexpected error encountered while initializing",
|
||||
error_name = exc.name, error_msg = exc.msg
|
||||
|
@ -225,7 +290,9 @@ proc asyncInit(vc: ValidatorClientRef) {.async.} =
|
|||
vc.shutdownSlashingProtection()
|
||||
return
|
||||
|
||||
proc asyncRun(vc: ValidatorClientRef) {.async.} =
|
||||
return vc
|
||||
|
||||
proc asyncRun*(vc: ValidatorClientRef) {.async.} =
|
||||
vc.fallbackService.start()
|
||||
vc.forkService.start()
|
||||
vc.dutiesService.start()
|
||||
|
@ -233,6 +300,11 @@ proc asyncRun(vc: ValidatorClientRef) {.async.} =
|
|||
vc.attestationService.start()
|
||||
vc.syncCommitteeService.start()
|
||||
|
||||
if not isNil(vc.keymanagerServer):
|
||||
doAssert vc.keymanagerHost != nil
|
||||
vc.keymanagerServer.router.installKeymanagerHandlers(vc.keymanagerHost[])
|
||||
vc.keymanagerServer.start()
|
||||
|
||||
var exitEventFut = vc.gracefulExit.wait()
|
||||
try:
|
||||
vc.runSlotLoopFut = runSlotLoop(vc, vc.beaconClock.now(), onSlotStart)
|
||||
|
@ -260,6 +332,9 @@ proc asyncRun(vc: ValidatorClientRef) {.async.} =
|
|||
pending.add(vc.doppelgangerService.stop())
|
||||
pending.add(vc.attestationService.stop())
|
||||
pending.add(vc.syncCommitteeService.stop())
|
||||
if not isNil(vc.keymanagerServer):
|
||||
pending.add(vc.keymanagerServer.stop())
|
||||
|
||||
await allFutures(pending)
|
||||
|
||||
template runWithSignals(vc: ValidatorClientRef, body: untyped): bool =
|
||||
|
@ -290,64 +365,22 @@ template runWithSignals(vc: ValidatorClientRef, body: untyped): bool =
|
|||
await allFutures(pending)
|
||||
false
|
||||
|
||||
proc asyncLoop*(vc: ValidatorClientRef) {.async.} =
|
||||
if not(vc.runWithSignals(asyncInit(vc))):
|
||||
proc runValidatorClient*(config: ValidatorClientConf,
|
||||
rng: ref HmacDrbgContext) {.async.} =
|
||||
let vc = ValidatorClientRef.new(config, rng)
|
||||
if not vc.runWithSignals(asyncInit vc):
|
||||
return
|
||||
if not(vc.runWithSignals(asyncRun(vc))):
|
||||
if not vc.runWithSignals(asyncRun vc):
|
||||
return
|
||||
|
||||
programMain:
|
||||
let config = makeBannerAndConfig("Nimbus validator client " & fullVersionStr,
|
||||
ValidatorClientConf)
|
||||
let
|
||||
config = makeBannerAndConfig("Nimbus validator client " & fullVersionStr,
|
||||
ValidatorClientConf)
|
||||
|
||||
# Single RNG instance for the application - will be seeded on construction
|
||||
# and avoid using system resources (such as urandom) after that
|
||||
rng = crypto.newRng()
|
||||
|
||||
setupLogging(config.logLevel, config.logStdout, config.logFile)
|
||||
|
||||
let beaconNodes =
|
||||
block:
|
||||
var servers: seq[BeaconNodeServerRef]
|
||||
let flags = {RestClientFlag.CommaSeparatedArray}
|
||||
for url in config.beaconNodes:
|
||||
let res = RestClientRef.new(url, flags = flags)
|
||||
if res.isErr():
|
||||
warn "Unable to resolve remote beacon node server's hostname",
|
||||
url = url
|
||||
else:
|
||||
servers.add(BeaconNodeServerRef(client: res.get(), endpoint: url))
|
||||
servers
|
||||
|
||||
if len(beaconNodes) == 0:
|
||||
# This should not happen, thanks to defaults in `conf.nim`
|
||||
fatal "Not enough beacon nodes in command line"
|
||||
quit 1
|
||||
|
||||
notice "Launching validator client", version = fullVersionStr,
|
||||
cmdParams = commandLineParams(),
|
||||
config,
|
||||
beacon_nodes_count = len(beaconNodes)
|
||||
|
||||
var vc =
|
||||
when declared(waitSignal):
|
||||
ValidatorClientRef(
|
||||
config: config,
|
||||
beaconNodes: beaconNodes,
|
||||
graffitiBytes: config.graffiti.get(defaultGraffitiBytes()),
|
||||
nodesAvailable: newAsyncEvent(),
|
||||
forksAvailable: newAsyncEvent(),
|
||||
gracefulExit: newAsyncEvent(),
|
||||
sigintHandleFut: waitSignal(SIGINT),
|
||||
sigtermHandleFut: waitSignal(SIGTERM)
|
||||
)
|
||||
else:
|
||||
ValidatorClientRef(
|
||||
config: config,
|
||||
beaconNodes: beaconNodes,
|
||||
graffitiBytes: config.graffiti.get(defaultGraffitiBytes()),
|
||||
nodesAvailable: newAsyncEvent(),
|
||||
forksAvailable: newAsyncEvent(),
|
||||
gracefulExit: newAsyncEvent(),
|
||||
sigintHandleFut: newFuture[void]("sigint_placeholder"),
|
||||
sigtermHandleFut: newFuture[void]("sigterm_placeholder")
|
||||
)
|
||||
|
||||
waitFor asyncLoop(vc)
|
||||
info "Validator client stopped"
|
||||
waitFor runValidatorClient(config, rng)
|
||||
|
|
|
@ -20,6 +20,29 @@ export rest_utils
|
|||
|
||||
logScope: topics = "rest_beaconapi"
|
||||
|
||||
proc validateBeaconApiQueries*(key: string, value: string): int =
|
||||
## This is rough validation procedure which should be simple and fast,
|
||||
## because it will be used for query routing.
|
||||
case key
|
||||
of "{epoch}":
|
||||
0
|
||||
of "{slot}":
|
||||
0
|
||||
of "{peer_id}":
|
||||
0
|
||||
of "{state_id}":
|
||||
0
|
||||
of "{block_id}":
|
||||
0
|
||||
of "{validator_id}":
|
||||
0
|
||||
of "{block_root}":
|
||||
0
|
||||
of "{pubkey}":
|
||||
int(value.len != 98)
|
||||
else:
|
||||
1
|
||||
|
||||
proc validateFilter(filters: seq[ValidatorFilter]): Result[ValidatorFilter,
|
||||
cstring] =
|
||||
var res: ValidatorFilter
|
||||
|
|
|
@ -102,6 +102,7 @@ proc installEventApiHandlers*(router: var RestRouter, node: BeaconNode) =
|
|||
res.get()
|
||||
|
||||
let res = preferredContentType(textEventStreamMediaType)
|
||||
|
||||
if res.isErr():
|
||||
return RestApiResponse.jsonError(Http406, ContentNotAcceptableError)
|
||||
if res.get() != textEventStreamMediaType:
|
||||
|
|
|
@ -7,19 +7,22 @@
|
|||
import std/[tables, os, strutils, uri]
|
||||
import chronos, chronicles, confutils,
|
||||
stew/[base10, results, io2], blscurve
|
||||
import ".."/validators/slashing_protection
|
||||
import ".."/[conf, filepath, beacon_node]
|
||||
import ".."/spec/[keystore, crypto]
|
||||
import ".."/rpc/rest_utils
|
||||
import ".."/validators/[keystore_management, validator_pool, validator_duties]
|
||||
import ".."/spec/eth2_apis/rest_keymanager_types
|
||||
import ".."/validators/[slashing_protection, keystore_management,
|
||||
validator_pool, validator_duties]
|
||||
import ".."/rpc/rest_utils
|
||||
|
||||
export rest_utils, results
|
||||
|
||||
proc listLocalValidators*(node: BeaconNode): seq[KeystoreInfo]
|
||||
func validateKeymanagerApiQueries*(key: string, value: string): int =
|
||||
# There are no queries to validate
|
||||
return 0
|
||||
|
||||
proc listLocalValidators*(validatorPool: ValidatorPool): seq[KeystoreInfo]
|
||||
{.raises: [Defect].} =
|
||||
var validators: seq[KeystoreInfo]
|
||||
for item in node.attachedValidators[].items():
|
||||
for item in validatorPool:
|
||||
if item.kind == ValidatorKind.Local:
|
||||
validators.add KeystoreInfo(
|
||||
validating_pubkey: item.pubkey,
|
||||
|
@ -28,10 +31,10 @@ proc listLocalValidators*(node: BeaconNode): seq[KeystoreInfo]
|
|||
)
|
||||
validators
|
||||
|
||||
proc listRemoteValidators*(node: BeaconNode): seq[RemoteKeystoreInfo]
|
||||
proc listRemoteValidators*(validatorPool: ValidatorPool): seq[RemoteKeystoreInfo]
|
||||
{.raises: [Defect].} =
|
||||
var validators: seq[RemoteKeystoreInfo]
|
||||
for item in node.attachedValidators[].items():
|
||||
for item in validatorPool:
|
||||
if item.kind == ValidatorKind.Remote and item.data.remotes.len == 1:
|
||||
validators.add RemoteKeystoreInfo(
|
||||
pubkey: item.pubkey,
|
||||
|
@ -39,10 +42,10 @@ proc listRemoteValidators*(node: BeaconNode): seq[RemoteKeystoreInfo]
|
|||
)
|
||||
validators
|
||||
|
||||
proc listRemoteDistributedValidators*(node: BeaconNode): seq[DistributedKeystoreInfo]
|
||||
proc listRemoteDistributedValidators*(validatorPool: ValidatorPool): seq[DistributedKeystoreInfo]
|
||||
{.raises: [Defect].} =
|
||||
var validators: seq[DistributedKeystoreInfo]
|
||||
for item in node.attachedValidators[].items():
|
||||
for item in validatorPool:
|
||||
if item.kind == ValidatorKind.Remote and item.data.remotes.len > 1:
|
||||
validators.add DistributedKeystoreInfo(
|
||||
pubkey: item.pubkey,
|
||||
|
@ -69,13 +72,13 @@ proc keymanagerApiError(status: HttpCode, msg: string): RestApiResponse =
|
|||
RestApiResponse.error(status, data, "application/json")
|
||||
|
||||
proc checkAuthorization*(request: HttpRequestRef,
|
||||
node: BeaconNode): Result[void, AuthorizationError] =
|
||||
host: KeymanagerHost): Result[void, AuthorizationError] =
|
||||
let authorizations = request.headers.getList("authorization")
|
||||
if authorizations.len > 0:
|
||||
for authHeader in authorizations:
|
||||
let parts = authHeader.split(' ', maxsplit = 1)
|
||||
if parts.len == 2 and parts[0] == "Bearer":
|
||||
if parts[1] == node.keymanagerToken.get:
|
||||
if parts[1] == host.keymanagerToken:
|
||||
return ok()
|
||||
else:
|
||||
return err incorrectToken
|
||||
|
@ -100,9 +103,10 @@ proc validateUri*(url: string): Result[Uri, cstring] =
|
|||
return err("Empty URL hostname")
|
||||
ok(surl)
|
||||
|
||||
proc removeValidator(node: BeaconNode,
|
||||
key: ValidatorPubKey): RemoteKeystoreStatus =
|
||||
let res = removeValidator(node.attachedValidators[], node.config,
|
||||
proc handleRemoveValidatorReq(host: KeymanagerHost,
|
||||
key: ValidatorPubKey): RemoteKeystoreStatus =
|
||||
let res = removeValidator(host.validatorPool[],
|
||||
host.validatorsDir, host.secretsDir,
|
||||
key, KeystoreKind.Remote)
|
||||
if res.isOk:
|
||||
case res.value()
|
||||
|
@ -114,33 +118,37 @@ proc removeValidator(node: BeaconNode,
|
|||
return RemoteKeystoreStatus(status: KeystoreStatus.error,
|
||||
message: some($res.error()))
|
||||
|
||||
proc addRemoteValidator(node: BeaconNode,
|
||||
keystore: RemoteKeystore): RequestItemStatus =
|
||||
let res = importKeystore(node.attachedValidators[], node.config, keystore)
|
||||
if res.isErr():
|
||||
proc handleAddRemoteValidatorReq(host: KeymanagerHost,
|
||||
keystore: RemoteKeystore): RequestItemStatus =
|
||||
let res = importKeystore(host.validatorPool[], host.validatorsDir, keystore)
|
||||
if res.isOk:
|
||||
let
|
||||
slot = host.getBeaconTimeFn().slotOrZero
|
||||
validatorIdx = host.getValidatorIdx(keystore.pubkey)
|
||||
host.validatorPool[].addRemoteValidator(validatorIdx, res.get, slot)
|
||||
RequestItemStatus(status: $KeystoreStatus.imported)
|
||||
else:
|
||||
case res.error().status
|
||||
of AddValidatorStatus.failed:
|
||||
return RequestItemStatus(status: $KeystoreStatus.error,
|
||||
message: $res.error().message)
|
||||
RequestItemStatus(status: $KeystoreStatus.error,
|
||||
message: $res.error().message)
|
||||
of AddValidatorStatus.existingArtifacts:
|
||||
return RequestItemStatus(status: $KeystoreStatus.duplicate)
|
||||
else:
|
||||
node.addRemoteValidators([res.get()])
|
||||
return RequestItemStatus(status: $KeystoreStatus.imported)
|
||||
RequestItemStatus(status: $KeystoreStatus.duplicate)
|
||||
|
||||
proc installKeymanagerHandlers*(router: var RestRouter, node: BeaconNode) =
|
||||
proc installKeymanagerHandlers*(router: var RestRouter, host: KeymanagerHost) =
|
||||
# https://ethereum.github.io/keymanager-APIs/#/Keymanager/ListKeys
|
||||
router.api(MethodGet, "/api/eth/v1/keystores") do () -> RestApiResponse:
|
||||
let authStatus = checkAuthorization(request, node)
|
||||
let authStatus = checkAuthorization(request, host)
|
||||
if authStatus.isErr():
|
||||
return authErrorResponse authStatus.error
|
||||
let response = GetKeystoresResponse(data: listLocalValidators(node))
|
||||
let response = GetKeystoresResponse(
|
||||
data: listLocalValidators(host.validatorPool[]))
|
||||
return RestApiResponse.jsonResponsePlain(response)
|
||||
|
||||
# https://ethereum.github.io/keymanager-APIs/#/Keymanager/ImportKeystores
|
||||
router.api(MethodPost, "/api/eth/v1/keystores") do (
|
||||
contentBody: Option[ContentBody]) -> RestApiResponse:
|
||||
let authStatus = checkAuthorization(request, node)
|
||||
let authStatus = checkAuthorization(request, host)
|
||||
if authStatus.isErr():
|
||||
return authErrorResponse authStatus.error
|
||||
let request =
|
||||
|
@ -154,13 +162,13 @@ proc installKeymanagerHandlers*(router: var RestRouter, node: BeaconNode) =
|
|||
|
||||
if request.slashing_protection.isSome():
|
||||
let slashing_protection = request.slashing_protection.get()
|
||||
let nodeSPDIR = toSPDIR(node.attachedValidators.slashingProtection)
|
||||
let nodeSPDIR = toSPDIR(host.validatorPool[].slashingProtection)
|
||||
if nodeSPDIR.metadata.genesis_validators_root.Eth2Digest !=
|
||||
slashing_protection.metadata.genesis_validators_root.Eth2Digest:
|
||||
return keymanagerApiError(Http400,
|
||||
"The slashing protection database and imported file refer to " &
|
||||
"different blockchains.")
|
||||
let res = inclSPDIR(node.attachedValidators.slashingProtection,
|
||||
let res = inclSPDIR(host.validatorPool[].slashingProtection,
|
||||
slashing_protection)
|
||||
if res == siFailure:
|
||||
return keymanagerApiError(Http500,
|
||||
|
@ -169,8 +177,9 @@ proc installKeymanagerHandlers*(router: var RestRouter, node: BeaconNode) =
|
|||
var response: PostKeystoresResponse
|
||||
|
||||
for index, item in request.keystores:
|
||||
let res = importKeystore(node.attachedValidators[], node.network.rng[],
|
||||
node.config, item, request.passwords[index])
|
||||
let res = importKeystore(host.validatorPool[], host.rng[],
|
||||
host.validatorsDir, host.secretsDir,
|
||||
item, request.passwords[index])
|
||||
if res.isErr():
|
||||
let failure = res.error()
|
||||
case failure.status
|
||||
|
@ -182,7 +191,7 @@ proc installKeymanagerHandlers*(router: var RestRouter, node: BeaconNode) =
|
|||
response.data.add(
|
||||
RequestItemStatus(status: $KeystoreStatus.duplicate))
|
||||
else:
|
||||
node.addLocalValidators([res.get()])
|
||||
host.addLocalValidator(res.get())
|
||||
response.data.add(
|
||||
RequestItemStatus(status: $KeystoreStatus.imported))
|
||||
|
||||
|
@ -191,7 +200,7 @@ proc installKeymanagerHandlers*(router: var RestRouter, node: BeaconNode) =
|
|||
# https://ethereum.github.io/keymanager-APIs/#/Keymanager/DeleteKeys
|
||||
router.api(MethodDelete, "/api/eth/v1/keystores") do (
|
||||
contentBody: Option[ContentBody]) -> RestApiResponse:
|
||||
let authStatus = checkAuthorization(request, node)
|
||||
let authStatus = checkAuthorization(request, host)
|
||||
if authStatus.isErr():
|
||||
return authErrorResponse authStatus.error
|
||||
let keys =
|
||||
|
@ -205,16 +214,18 @@ proc installKeymanagerHandlers*(router: var RestRouter, node: BeaconNode) =
|
|||
|
||||
var
|
||||
response: DeleteKeystoresResponse
|
||||
nodeSPDIR = toSPDIR(node.attachedValidators.slashingProtection)
|
||||
nodeSPDIR = toSPDIR(host.validatorPool[].slashingProtection)
|
||||
# Hash table to keep the removal status of all keys form request
|
||||
keysAndDeleteStatus = initTable[PubKeyBytes, RequestItemStatus]()
|
||||
responseSPDIR: SPDIR
|
||||
|
||||
response.slashing_protection.metadata = nodeSPDIR.metadata
|
||||
responseSPDIR.metadata = nodeSPDIR.metadata
|
||||
|
||||
for index, key in keys:
|
||||
let
|
||||
res = removeValidator(node.attachedValidators[], node.config, key,
|
||||
KeystoreKind.Local)
|
||||
res = removeValidator(host.validatorPool[],
|
||||
host.validatorsDir, host.secretsDir,
|
||||
key, KeystoreKind.Local)
|
||||
pubkey = key.blob.PubKey0x.PubKeyBytes
|
||||
|
||||
if res.isOk:
|
||||
|
@ -237,29 +248,33 @@ proc installKeymanagerHandlers*(router: var RestRouter, node: BeaconNode) =
|
|||
# found, this means the validator was active in the past, so we must
|
||||
# respond with `not_active`:
|
||||
for validator in nodeSPDIR.data:
|
||||
keysAndDeleteStatus.withValue(validator.pubkey.PubKeyBytes, value) do:
|
||||
response.slashing_protection.data.add(validator)
|
||||
keysAndDeleteStatus.withValue(validator.pubkey.PubKeyBytes,
|
||||
foundKeystore) do:
|
||||
responseSPDIR.data.add(validator)
|
||||
|
||||
if value.status == $KeystoreStatus.notFound:
|
||||
value.status = $KeystoreStatus.notActive
|
||||
if foundKeystore.status == $KeystoreStatus.notFound:
|
||||
foundKeystore.status = $KeystoreStatus.notActive
|
||||
|
||||
for index, key in keys:
|
||||
response.data.add(keysAndDeleteStatus[key.blob.PubKey0x.PubKeyBytes])
|
||||
|
||||
response.slashing_protection = RestJson.encode(responseSPDIR)
|
||||
|
||||
return RestApiResponse.jsonResponsePlain(response)
|
||||
|
||||
# https://ethereum.github.io/keymanager-APIs/#/Remote%20Key%20Manager/ListRemoteKeys
|
||||
router.api(MethodGet, "/api/eth/v1/remotekeys") do () -> RestApiResponse:
|
||||
let authStatus = checkAuthorization(request, node)
|
||||
let authStatus = checkAuthorization(request, host)
|
||||
if authStatus.isErr():
|
||||
return authErrorResponse authStatus.error
|
||||
let response = GetRemoteKeystoresResponse(data: listRemoteValidators(node))
|
||||
let response = GetRemoteKeystoresResponse(
|
||||
data: listRemoteValidators(host.validatorPool[]))
|
||||
return RestApiResponse.jsonResponsePlain(response)
|
||||
|
||||
# https://ethereum.github.io/keymanager-APIs/#/Remote%20Key%20Manager/ImportRemoteKeys
|
||||
router.api(MethodPost, "/api/eth/v1/remotekeys") do (
|
||||
contentBody: Option[ContentBody]) -> RestApiResponse:
|
||||
let authStatus = checkAuthorization(request, node)
|
||||
let authStatus = checkAuthorization(request, host)
|
||||
if authStatus.isErr():
|
||||
return authErrorResponse authStatus.error
|
||||
let keys =
|
||||
|
@ -282,16 +297,15 @@ proc installKeymanagerHandlers*(router: var RestRouter, node: BeaconNode) =
|
|||
keystore = RemoteKeystore(
|
||||
version: 1'u64, remoteType: RemoteSignerType.Web3Signer,
|
||||
pubkey: key.pubkey, remotes: @[remoteInfo])
|
||||
status = node.addRemoteValidator(keystore)
|
||||
|
||||
response.data.add(status)
|
||||
response.data.add handleAddRemoteValidatorReq(host, keystore)
|
||||
|
||||
return RestApiResponse.jsonResponsePlain(response)
|
||||
|
||||
# https://ethereum.github.io/keymanager-APIs/#/Remote%20Key%20Manager/DeleteRemoteKeys
|
||||
router.api(MethodDelete, "/api/eth/v1/remotekeys") do (
|
||||
contentBody: Option[ContentBody]) -> RestApiResponse:
|
||||
let authStatus = checkAuthorization(request, node)
|
||||
let authStatus = checkAuthorization(request, host)
|
||||
if authStatus.isErr():
|
||||
return authErrorResponse authStatus.error
|
||||
let keys =
|
||||
|
@ -305,20 +319,19 @@ proc installKeymanagerHandlers*(router: var RestRouter, node: BeaconNode) =
|
|||
|
||||
var response: DeleteRemoteKeystoresResponse
|
||||
for index, key in keys:
|
||||
let status = node.removeValidator(key)
|
||||
response.data.add(status)
|
||||
response.data.add handleRemoveValidatorReq(host, key)
|
||||
return RestApiResponse.jsonResponsePlain(response)
|
||||
|
||||
# https://ethereum.github.io/keymanager-APIs/#/Fee%20Recipient/ListFeeRecipient
|
||||
router.api(MethodGet, "/api/eth/v1/validator/{pubkey}/feerecipient") do (
|
||||
pubkey: ValidatorPubKey) -> RestApiResponse:
|
||||
let authStatus = checkAuthorization(request, node)
|
||||
let authStatus = checkAuthorization(request, host)
|
||||
if authStatus.isErr():
|
||||
return authErrorResponse authStatus.error
|
||||
let
|
||||
pubkey = pubkey.valueOr:
|
||||
return keymanagerApiError(Http400, InvalidValidatorPublicKey)
|
||||
ethaddress = node.config.getSuggestedFeeRecipient(pubkey)
|
||||
ethaddress = host.getSuggestedFeeRecipient(pubkey)
|
||||
|
||||
return if ethaddress.isOk:
|
||||
RestApiResponse.jsonResponse(ListFeeRecipientResponse(
|
||||
|
@ -335,7 +348,7 @@ proc installKeymanagerHandlers*(router: var RestRouter, node: BeaconNode) =
|
|||
router.api(MethodPost, "/api/eth/v1/validator/{pubkey}/feerecipient") do (
|
||||
pubkey: ValidatorPubKey,
|
||||
contentBody: Option[ContentBody]) -> RestApiResponse:
|
||||
let authStatus = checkAuthorization(request, node)
|
||||
let authStatus = checkAuthorization(request, host)
|
||||
if authStatus.isErr():
|
||||
return authErrorResponse authStatus.error
|
||||
let
|
||||
|
@ -350,7 +363,7 @@ proc installKeymanagerHandlers*(router: var RestRouter, node: BeaconNode) =
|
|||
return keymanagerApiError(Http400, InvalidFeeRecipientRequestError)
|
||||
dres.get()
|
||||
|
||||
status = node.config.setFeeRecipient(pubkey, feeRecipientReq.ethaddress)
|
||||
status = host.setFeeRecipient(pubkey, feeRecipientReq.ethaddress)
|
||||
|
||||
return if status.isOk:
|
||||
RestApiResponse.response("", Http202, "text/plain")
|
||||
|
@ -361,13 +374,13 @@ proc installKeymanagerHandlers*(router: var RestRouter, node: BeaconNode) =
|
|||
# https://ethereum.github.io/keymanager-APIs/#/Fee%20Recipient/DeleteFeeRecipient
|
||||
router.api(MethodDelete, "/api/eth/v1/validator/{pubkey}/feerecipient") do (
|
||||
pubkey: ValidatorPubKey) -> RestApiResponse:
|
||||
let authStatus = checkAuthorization(request, node)
|
||||
let authStatus = checkAuthorization(request, host)
|
||||
if authStatus.isErr():
|
||||
return authErrorResponse authStatus.error
|
||||
let
|
||||
pubkey = pubkey.valueOr:
|
||||
return keymanagerApiError(Http400, InvalidValidatorPublicKey)
|
||||
res = removeFeeRecipientFile(node.config, pubkey)
|
||||
res = host.removeFeeRecipientFile(pubkey)
|
||||
|
||||
return if res.isOk:
|
||||
RestApiResponse.response("", Http204, "text/plain")
|
||||
|
@ -378,17 +391,18 @@ proc installKeymanagerHandlers*(router: var RestRouter, node: BeaconNode) =
|
|||
# TODO: These URLs will be changed once we submit a proposal for
|
||||
# /api/eth/v2/remotekeys that supports distributed keys.
|
||||
router.api(MethodGet, "/api/eth/v1/remotekeys/distributed") do () -> RestApiResponse:
|
||||
let authStatus = checkAuthorization(request, node)
|
||||
let authStatus = checkAuthorization(request, host)
|
||||
if authStatus.isErr():
|
||||
return authErrorResponse authStatus.error
|
||||
let response = GetDistributedKeystoresResponse(data: listRemoteDistributedValidators(node))
|
||||
let response = GetDistributedKeystoresResponse(
|
||||
data: listRemoteDistributedValidators(host.validatorPool[]))
|
||||
return RestApiResponse.jsonResponsePlain(response)
|
||||
|
||||
# TODO: These URLs will be changed once we submit a proposal for
|
||||
# /api/eth/v2/remotekeys that supports distributed keys.
|
||||
router.api(MethodPost, "/api/eth/v1/remotekeys/distributed") do (
|
||||
contentBody: Option[ContentBody]) -> RestApiResponse:
|
||||
let authStatus = checkAuthorization(request, node)
|
||||
let authStatus = checkAuthorization(request, host)
|
||||
if authStatus.isErr():
|
||||
return authErrorResponse authStatus.error
|
||||
let keys =
|
||||
|
@ -410,14 +424,13 @@ proc installKeymanagerHandlers*(router: var RestRouter, node: BeaconNode) =
|
|||
remotes: key.remotes,
|
||||
threshold: uint32 key.threshold
|
||||
)
|
||||
let status = node.addRemoteValidator(keystore)
|
||||
response.data.add(status)
|
||||
response.data.add handleAddRemoteValidatorReq(host, keystore)
|
||||
|
||||
return RestApiResponse.jsonResponsePlain(response)
|
||||
|
||||
router.api(MethodDelete, "/api/eth/v1/remotekeys/distributed") do (
|
||||
contentBody: Option[ContentBody]) -> RestApiResponse:
|
||||
let authStatus = checkAuthorization(request, node)
|
||||
let authStatus = checkAuthorization(request, host)
|
||||
if authStatus.isErr():
|
||||
return authErrorResponse authStatus.error
|
||||
let keys =
|
||||
|
@ -431,8 +444,7 @@ proc installKeymanagerHandlers*(router: var RestRouter, node: BeaconNode) =
|
|||
|
||||
var response: DeleteRemoteKeystoresResponse
|
||||
for index, key in keys:
|
||||
let status = node.removeValidator(key)
|
||||
response.data.add(status)
|
||||
response.data.add handleRemoveValidatorReq(host, key)
|
||||
|
||||
return RestApiResponse.jsonResponsePlain(response)
|
||||
|
||||
|
|
|
@ -14,9 +14,9 @@ import std/[options, macros],
|
|||
stew/byteutils, presto,
|
||||
../spec/[forks],
|
||||
../spec/eth2_apis/[rest_types, eth2_rest_serialization],
|
||||
../beacon_node,
|
||||
../validators/validator_duties,
|
||||
../consensus_object_pools/blockchain_dag,
|
||||
../beacon_node,
|
||||
"."/[rest_constants, state_ttl_cache]
|
||||
|
||||
export
|
||||
|
@ -33,29 +33,6 @@ func match(data: openArray[char], charset: set[char]): int =
|
|||
return 1
|
||||
0
|
||||
|
||||
proc validate(key: string, value: string): int =
|
||||
## This is rough validation procedure which should be simple and fast,
|
||||
## because it will be used for query routing.
|
||||
case key
|
||||
of "{epoch}":
|
||||
0
|
||||
of "{slot}":
|
||||
0
|
||||
of "{peer_id}":
|
||||
0
|
||||
of "{state_id}":
|
||||
0
|
||||
of "{block_id}":
|
||||
0
|
||||
of "{validator_id}":
|
||||
0
|
||||
of "{block_root}":
|
||||
0
|
||||
of "{pubkey}":
|
||||
int(value.len != 98)
|
||||
else:
|
||||
1
|
||||
|
||||
proc getSyncedHead*(node: BeaconNode, slot: Slot): Result[BlockRef, cstring] =
|
||||
let head = node.dag.head
|
||||
|
||||
|
@ -64,6 +41,13 @@ proc getSyncedHead*(node: BeaconNode, slot: Slot): Result[BlockRef, cstring] =
|
|||
|
||||
ok(head)
|
||||
|
||||
func getCurrentSlot*(node: BeaconNode, slot: Slot):
|
||||
Result[Slot, cstring] =
|
||||
if slot <= (node.dag.head.slot + (SLOTS_PER_EPOCH * 2)):
|
||||
ok(slot)
|
||||
else:
|
||||
err("Requesting slot too far ahead of the current head")
|
||||
|
||||
proc getSyncedHead*(node: BeaconNode,
|
||||
epoch: Epoch): Result[BlockRef, cstring] =
|
||||
if epoch > MaxEpoch:
|
||||
|
@ -274,9 +258,6 @@ func keysToIndices*(cacheTable: var Table[ValidatorPubKey, ValidatorIndex],
|
|||
indices[listIndex[]] = some(ValidatorIndex(validatorIndex))
|
||||
indices
|
||||
|
||||
proc getRouter*(allowedOrigin: Option[string]): RestRouter =
|
||||
RestRouter.init(validate, allowedOrigin = allowedOrigin)
|
||||
|
||||
proc getStateOptimistic*(node: BeaconNode,
|
||||
state: ForkedHashedBeaconState): Option[bool] =
|
||||
if node.currentSlot().epoch() >= node.dag.cfg.BELLATRIX_FORK_EPOCH:
|
||||
|
|
|
@ -2036,8 +2036,8 @@ proc writeValue*(writer: var JsonWriter[RestJson], value: Keystore) {.
|
|||
raises: [IOError, Defect].} =
|
||||
writer.beginRecord()
|
||||
writer.writeField("crypto", value.crypto)
|
||||
if not(isNil(value.description)):
|
||||
writer.writeField("description", value.description[])
|
||||
if value.description.isSome:
|
||||
writer.writeField("description", value.description.get)
|
||||
writer.writeField("pubkey", value.pubkey)
|
||||
writer.writeField("path", string(value.path))
|
||||
writer.writeField("uuid", value.uuid)
|
||||
|
@ -2113,7 +2113,7 @@ proc readValue*(reader: var JsonReader[RestJson], value: var Keystore) {.
|
|||
pubkey: pubkey.get(),
|
||||
path: path.get(),
|
||||
uuid: uuid.get(),
|
||||
description: if description.isNone(): nil else: newClone(description.get()),
|
||||
description: description,
|
||||
version: version.get(),
|
||||
)
|
||||
|
||||
|
|
|
@ -56,7 +56,7 @@ type
|
|||
|
||||
DeleteKeystoresResponse* = object
|
||||
data*: seq[RequestItemStatus]
|
||||
slashing_protection*: SPDIR
|
||||
slashing_protection*: string
|
||||
|
||||
RemoteKeystoreStatus* = object
|
||||
status*: KeystoreStatus
|
||||
|
|
|
@ -53,11 +53,9 @@ declareCounter nbc_remote_signer_unknown_responses,
|
|||
declareCounter nbc_remote_signer_communication_errors,
|
||||
"Number of communication errors"
|
||||
|
||||
const delayBuckets = [0.050, 0.100, 0.500, 1.0, 5.0, 10.0]
|
||||
|
||||
declareHistogram nbc_remote_signer_time,
|
||||
"Time(s) used to generate signature usign remote signer",
|
||||
buckets = delayBuckets
|
||||
buckets = [0.050, 0.100, 0.500, 1.0, 5.0, 10.0]
|
||||
|
||||
proc getUpcheck*(): RestResponse[Web3SignerStatusResponse] {.
|
||||
rest, endpoint: "/upcheck",
|
||||
|
|
|
@ -18,13 +18,14 @@ import
|
|||
normalize,
|
||||
# Status libraries
|
||||
stew/[results, bitops2, base10, io2], stew/shims/macros,
|
||||
eth/keyfile/uuid, blscurve, json_serialization,
|
||||
eth/keyfile/uuid, blscurve,
|
||||
json_serialization, json_serialization/std/options,
|
||||
nimcrypto/[sha2, rijndael, pbkdf2, bcmode, hash, scrypt],
|
||||
# Local modules
|
||||
libp2p/crypto/crypto as lcrypto,
|
||||
./datatypes/base, ./signatures
|
||||
|
||||
export base, uri, io2
|
||||
export base, uri, io2, options
|
||||
|
||||
# We use `ncrutils` for constant-time hexadecimal encoding/decoding procedures.
|
||||
import nimcrypto/utils as ncrutils
|
||||
|
@ -121,7 +122,7 @@ type
|
|||
|
||||
Keystore* = object
|
||||
crypto*: Crypto
|
||||
description*: ref string
|
||||
description*: Option[string]
|
||||
pubkey*: ValidatorPubKey
|
||||
path*: KeyPath
|
||||
uuid*: string
|
||||
|
@ -161,7 +162,7 @@ type
|
|||
|
||||
NetKeystore* = object
|
||||
crypto*: Crypto
|
||||
description*: ref string
|
||||
description*: Option[string]
|
||||
pubkey*: lcrypto.PublicKey
|
||||
uuid*: string
|
||||
version*: int
|
||||
|
@ -782,6 +783,21 @@ proc decryptCryptoField*(crypto: Crypto,
|
|||
|
||||
func cstringToStr(v: cstring): string = $v
|
||||
|
||||
template parseKeystore*(jsonContent: string): Keystore =
|
||||
Json.decode(jsonContent, Keystore,
|
||||
requireAllFields = true,
|
||||
allowUnknownFields = true)
|
||||
|
||||
template parseNetKeystore*(jsonContent: string): NetKeystore =
|
||||
Json.decode(jsonContent, NetKeystore,
|
||||
requireAllFields = true,
|
||||
allowUnknownFields = true)
|
||||
|
||||
template parseRemoteKeystore*(jsonContent: string): RemoteKeystore =
|
||||
Json.decode(jsonContent, RemoteKeystore,
|
||||
requireAllFields = false,
|
||||
allowUnknownFields = true)
|
||||
|
||||
proc decryptKeystore*(keystore: Keystore,
|
||||
password: KeystorePass): KsResult[ValidatorPrivKey] =
|
||||
var secret: seq[byte]
|
||||
|
@ -795,7 +811,7 @@ proc decryptKeystore*(keystore: Keystore,
|
|||
|
||||
proc decryptKeystore*(keystore: JsonString,
|
||||
password: KeystorePass): KsResult[ValidatorPrivKey] =
|
||||
let keystore = try: Json.decode(keystore.string, Keystore)
|
||||
let keystore = try: parseKeystore(string keystore)
|
||||
except SerializationError as e:
|
||||
return err e.formatMsg("<keystore>")
|
||||
decryptKeystore(keystore, password)
|
||||
|
@ -832,7 +848,7 @@ proc decryptNetKeystore*(nkeystore: NetKeystore,
|
|||
proc decryptNetKeystore*(nkeystore: JsonString,
|
||||
password: KeystorePass): KsResult[lcrypto.PrivateKey] =
|
||||
try:
|
||||
let keystore = Json.decode(string(nkeystore), NetKeystore)
|
||||
let keystore = parseNetKeystore(string nkeystore)
|
||||
return decryptNetKeystore(keystore, password)
|
||||
except SerializationError as exc:
|
||||
return err(exc.formatMsg("<keystore>"))
|
||||
|
@ -914,7 +930,8 @@ proc createNetKeystore*(kdfKind: KdfKind,
|
|||
NetKeystore(
|
||||
crypto: cryptoField,
|
||||
pubkey: pubkey,
|
||||
description: newClone(description),
|
||||
description: if len(description) > 0: some(description)
|
||||
else: none[string](),
|
||||
uuid: $uuid,
|
||||
version: 1
|
||||
)
|
||||
|
@ -938,7 +955,8 @@ proc createKeystore*(kdfKind: KdfKind,
|
|||
crypto: cryptoField,
|
||||
pubkey: pubkey.toPubKey(),
|
||||
path: path,
|
||||
description: newClone(description),
|
||||
description: if len(description) > 0: some(description)
|
||||
else: none[string](),
|
||||
uuid: $uuid,
|
||||
version: 4)
|
||||
|
||||
|
|
|
@ -5,25 +5,17 @@
|
|||
# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0).
|
||||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||
|
||||
import std/sets
|
||||
import metrics, chronicles
|
||||
import "."/[common, api, block_service]
|
||||
import
|
||||
std/sets,
|
||||
chronicles,
|
||||
../validators/activity_metrics,
|
||||
"."/[common, api, block_service]
|
||||
|
||||
const
|
||||
ServiceName = "attestation_service"
|
||||
|
||||
logScope: service = ServiceName
|
||||
|
||||
declareCounter beacon_attestations_sent,
|
||||
"Number of attestations sent by the node"
|
||||
|
||||
declareCounter beacon_aggregates_sent,
|
||||
"Number of beacon chain attestations sent by the node"
|
||||
|
||||
declareHistogram beacon_attestation_sent_delay,
|
||||
"Time(s) between expected and actual attestation send moment",
|
||||
buckets = DelayBuckets
|
||||
|
||||
type
|
||||
AggregateItem* = object
|
||||
aggregator_index: uint64
|
||||
|
@ -57,7 +49,7 @@ proc serveAttestation(service: AttestationServiceRef, adata: AttestationData,
|
|||
fork, vc.beaconGenesis.genesis_validators_root, adata)
|
||||
let attestationRoot = adata.hash_tree_root()
|
||||
|
||||
let notSlashable = vc.attachedValidators.slashingProtection
|
||||
let notSlashable = vc.attachedValidators[].slashingProtection
|
||||
.registerAttestation(vindex, validator.pubkey,
|
||||
adata.source.epoch,
|
||||
adata.target.epoch, signingRoot)
|
||||
|
@ -286,7 +278,7 @@ proc produceAndPublishAggregates(service: AttestationServiceRef,
|
|||
block:
|
||||
var res: seq[AggregateItem]
|
||||
for duty in duties:
|
||||
let validator = vc.attachedValidators.getValidator(duty.data.pubkey)
|
||||
let validator = vc.attachedValidators[].getValidator(duty.data.pubkey)
|
||||
if not(isNil(validator)):
|
||||
if (duty.data.slot != slot) or
|
||||
(duty.data.committee_index != committeeIndex):
|
||||
|
|
|
@ -5,19 +5,14 @@
|
|||
# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0).
|
||||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||
|
||||
import ".."/spec/forks
|
||||
import common, api
|
||||
import chronicles, metrics
|
||||
import
|
||||
chronicles,
|
||||
".."/validators/activity_metrics,
|
||||
".."/spec/forks,
|
||||
common, api
|
||||
|
||||
logScope: service = "block_service"
|
||||
|
||||
declareCounter beacon_blocks_sent,
|
||||
"Number of beacon blocks sent by this node"
|
||||
|
||||
declareHistogram beacon_blocks_sent_delay,
|
||||
"Time(s) between expected and actual block send moment",
|
||||
buckets = DelayBuckets
|
||||
|
||||
proc publishBlock(vc: ValidatorClientRef, currentSlot, slot: Slot,
|
||||
validator: AttachedValidator) {.async.} =
|
||||
let
|
||||
|
@ -77,7 +72,7 @@ proc publishBlock(vc: ValidatorClientRef, currentSlot, slot: Slot,
|
|||
# TODO: signing_root is recomputed in getBlockSignature just after
|
||||
let signing_root = compute_block_signing_root(fork, genesisRoot, slot,
|
||||
blockRoot)
|
||||
let notSlashable = vc.attachedValidators
|
||||
let notSlashable = vc.attachedValidators[]
|
||||
.slashingProtection
|
||||
.registerBlock(ValidatorIndex(beaconBlock.proposer_index),
|
||||
validator.pubkey, slot, signing_root)
|
||||
|
|
|
@ -5,30 +5,24 @@
|
|||
# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0).
|
||||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||
|
||||
import std/[tables, os, sets, sequtils]
|
||||
import chronos, presto, presto/client as presto_client, chronicles, confutils,
|
||||
json_serialization/std/[options, net],
|
||||
stew/[base10, results, byteutils]
|
||||
import metrics, metrics/chronos_httpserver
|
||||
|
||||
# Local modules
|
||||
import
|
||||
../spec/datatypes/[phase0, altair],
|
||||
../spec/[eth2_merkleization, helpers, signatures,
|
||||
validator],
|
||||
../spec/eth2_apis/[eth2_rest_serialization, rest_beacon_client],
|
||||
../validators/[keystore_management, validator_pool, slashing_protection],
|
||||
".."/[conf, beacon_clock, version, nimbus_binary_common],
|
||||
../spec/eth2_apis/eth2_rest_serialization
|
||||
std/[tables, os, sets, sequtils],
|
||||
stew/[base10, results, byteutils],
|
||||
bearssl/rand, chronos, presto, presto/client as presto_client,
|
||||
chronicles, confutils, json_serialization/std/[options, net],
|
||||
metrics, metrics/chronos_httpserver,
|
||||
".."/spec/datatypes/[phase0, altair],
|
||||
".."/spec/[eth2_merkleization, helpers, signatures, validator],
|
||||
".."/spec/eth2_apis/[eth2_rest_serialization, rest_beacon_client],
|
||||
".."/validators/[keystore_management, validator_pool, slashing_protection],
|
||||
".."/[conf, beacon_clock, version, nimbus_binary_common]
|
||||
|
||||
export os, sets, sequtils, sequtils, chronos, presto, chronicles, confutils,
|
||||
nimbus_binary_common, version, conf, options, tables, results, base10,
|
||||
byteutils, presto_client
|
||||
|
||||
export eth2_rest_serialization, rest_beacon_client,
|
||||
phase0, altair, helpers, signatures, validator, eth2_merkleization,
|
||||
beacon_clock,
|
||||
keystore_management, slashing_protection, validator_pool
|
||||
export
|
||||
os, sets, sequtils, chronos, presto, chronicles, confutils,
|
||||
nimbus_binary_common, version, conf, options, tables, results, base10,
|
||||
byteutils, presto_client, eth2_rest_serialization, rest_beacon_client,
|
||||
phase0, altair, helpers, signatures, validator, eth2_merkleization,
|
||||
beacon_clock, keystore_management, slashing_protection, validator_pool
|
||||
|
||||
const
|
||||
SYNC_TOLERANCE* = 4'u64
|
||||
|
@ -154,9 +148,11 @@ type
|
|||
runSlotLoopFut*: Future[void]
|
||||
sigintHandleFut*: Future[void]
|
||||
sigtermHandleFut*: Future[void]
|
||||
keymanagerHost*: ref KeymanagerHost
|
||||
keymanagerServer*: RestServerRef
|
||||
beaconClock*: BeaconClock
|
||||
attachedValidators*: ValidatorPool
|
||||
doppelgangerDetection*: DoppelgangerDetection
|
||||
attachedValidators*: ref ValidatorPool
|
||||
forks*: seq[Fork]
|
||||
forksAvailable*: AsyncEvent
|
||||
nodesAvailable*: AsyncEvent
|
||||
|
@ -166,6 +162,7 @@ type
|
|||
syncCommitteeDuties*: SyncCommitteeDutiesMap
|
||||
beaconGenesis*: RestGenesis
|
||||
proposerTasks*: Table[Slot, seq[ProposerTask]]
|
||||
rng*: ref HmacDrbgContext
|
||||
|
||||
ValidatorClientRef* = ref ValidatorClient
|
||||
|
||||
|
@ -306,7 +303,7 @@ proc getDurationToNextBlock*(vc: ValidatorClientRef, slot: Slot): string =
|
|||
let data = vc.proposers.getOrDefault(epoch)
|
||||
if not(data.isDefault()):
|
||||
for item in data.duties:
|
||||
if item.duty.pubkey in vc.attachedValidators:
|
||||
if item.duty.pubkey in vc.attachedValidators[]:
|
||||
if (item.duty.slot < minSlot) and (item.duty.slot >= slot):
|
||||
minSlot = item.duty.slot
|
||||
if minSlot != FAR_FUTURE_SLOT:
|
||||
|
@ -348,7 +345,7 @@ proc getDelay*(vc: ValidatorClientRef, deadline: BeaconTime): TimeDiff =
|
|||
|
||||
proc getValidator*(vc: ValidatorClientRef,
|
||||
key: ValidatorPubKey): Option[AttachedValidator] =
|
||||
let validator = vc.attachedValidators.getValidator(key)
|
||||
let validator = vc.attachedValidators[].getValidator(key)
|
||||
if isNil(validator):
|
||||
warn "Validator not in pool anymore", validator = shortLog(validator)
|
||||
none[AttachedValidator]()
|
||||
|
@ -419,8 +416,8 @@ proc addValidator*(vc: ValidatorClientRef, keystore: KeystoreData) =
|
|||
let slot = vc.currentSlot()
|
||||
case keystore.kind
|
||||
of KeystoreKind.Local:
|
||||
vc.attachedValidators.addLocalValidator(keystore, none[ValidatorIndex](),
|
||||
slot)
|
||||
vc.attachedValidators[].addLocalValidator(keystore, none[ValidatorIndex](),
|
||||
slot)
|
||||
of KeystoreKind.Remote:
|
||||
let
|
||||
httpFlags =
|
||||
|
@ -444,15 +441,15 @@ proc addValidator*(vc: ValidatorClientRef, keystore: KeystoreData) =
|
|||
res.add((client.get(), remote))
|
||||
res
|
||||
if len(clients) > 0:
|
||||
vc.attachedValidators.addRemoteValidator(keystore, clients,
|
||||
none[ValidatorIndex](), slot)
|
||||
vc.attachedValidators[].addRemoteValidator(keystore, clients,
|
||||
none[ValidatorIndex](), slot)
|
||||
else:
|
||||
warn "Unable to initialize remote validator",
|
||||
validator = $keystore.pubkey
|
||||
|
||||
proc removeValidator*(vc: ValidatorClientRef,
|
||||
pubkey: ValidatorPubKey) {.async.} =
|
||||
let validator = vc.attachedValidators.getValidator(pubkey)
|
||||
let validator = vc.attachedValidators[].getValidator(pubkey)
|
||||
if not(isNil(validator)):
|
||||
if vc.config.doppelgangerDetection:
|
||||
if validator.index.isSome():
|
||||
|
@ -470,7 +467,7 @@ proc removeValidator*(vc: ValidatorClientRef,
|
|||
res
|
||||
await allFutures(pending)
|
||||
# Remove validator from ValidatorPool.
|
||||
vc.attachedValidators.removeValidator(pubkey)
|
||||
vc.attachedValidators[].removeValidator(pubkey)
|
||||
|
||||
proc doppelgangerCheck*(vc: ValidatorClientRef,
|
||||
validator: AttachedValidator): bool =
|
||||
|
|
|
@ -104,7 +104,7 @@ proc mainLoop(service: DoppelgangerServiceRef) {.async.} =
|
|||
if breakLoop:
|
||||
break
|
||||
|
||||
proc init*(t: typedesc[DoppelgangerServiceRef],
|
||||
proc init*(t: type DoppelgangerServiceRef,
|
||||
vc: ValidatorClientRef): Future[DoppelgangerServiceRef] {.async.} =
|
||||
logScope: service = ServiceName
|
||||
let res = DoppelgangerServiceRef(name: ServiceName,
|
||||
|
|
|
@ -38,7 +38,7 @@ proc pollForValidatorIndices*(vc: ValidatorClientRef) {.async.} =
|
|||
let validatorIdents =
|
||||
block:
|
||||
var res: seq[ValidatorIdent]
|
||||
for validator in vc.attachedValidators.items():
|
||||
for validator in vc.attachedValidators[].items():
|
||||
if validator.index.isNone():
|
||||
res.add(ValidatorIdent.init(validator.pubkey))
|
||||
res
|
||||
|
@ -78,24 +78,24 @@ proc pollForValidatorIndices*(vc: ValidatorClientRef) {.async.} =
|
|||
offset += arraySize
|
||||
|
||||
for item in validators:
|
||||
if item.validator.pubkey notin vc.attachedValidators:
|
||||
if item.validator.pubkey notin vc.attachedValidators[]:
|
||||
warn "Beacon node returned missing validator",
|
||||
pubkey = item.validator.pubkey, index = item.index
|
||||
else:
|
||||
debug "Local validator updated with index",
|
||||
pubkey = item.validator.pubkey, index = item.index
|
||||
vc.attachedValidators.updateValidator(item.validator.pubkey,
|
||||
vc.attachedValidators[].updateValidator(item.validator.pubkey,
|
||||
item.index)
|
||||
# Adding validator for doppelganger detection.
|
||||
vc.addDoppelganger(
|
||||
vc.attachedValidators.getValidator(item.validator.pubkey))
|
||||
vc.attachedValidators[].getValidator(item.validator.pubkey))
|
||||
|
||||
proc pollForAttesterDuties*(vc: ValidatorClientRef,
|
||||
epoch: Epoch): Future[int] {.async.} =
|
||||
let validatorIndices =
|
||||
block:
|
||||
var res: seq[ValidatorIndex]
|
||||
for index in vc.attachedValidators.indices():
|
||||
for index in vc.attachedValidators[].indices():
|
||||
res.add(index)
|
||||
res
|
||||
|
||||
|
@ -151,7 +151,7 @@ proc pollForAttesterDuties*(vc: ValidatorClientRef,
|
|||
|
||||
let
|
||||
relevantDuties = duties.filterIt(
|
||||
checkDuty(it) and (it.pubkey in vc.attachedValidators)
|
||||
checkDuty(it) and (it.pubkey in vc.attachedValidators[])
|
||||
)
|
||||
genesisRoot = vc.beaconGenesis.genesis_validators_root
|
||||
|
||||
|
@ -180,7 +180,7 @@ proc pollForAttesterDuties*(vc: ValidatorClientRef,
|
|||
var pendingRequests: seq[Future[SignatureResult]]
|
||||
var validators: seq[AttachedValidator]
|
||||
for item in addOrReplaceItems:
|
||||
let validator = vc.attachedValidators.getValidator(item.duty.pubkey)
|
||||
let validator = vc.attachedValidators[].getValidator(item.duty.pubkey)
|
||||
let fork = vc.forkAtEpoch(item.duty.slot.epoch)
|
||||
let future = validator.getSlotSignature(
|
||||
fork, genesisRoot, item.duty.slot)
|
||||
|
@ -223,7 +223,7 @@ proc pollForAttesterDuties*(vc: ValidatorClientRef,
|
|||
|
||||
proc pollForSyncCommitteeDuties*(vc: ValidatorClientRef,
|
||||
epoch: Epoch): Future[int] {.async.} =
|
||||
let validatorIndices = toSeq(vc.attachedValidators.indices())
|
||||
let validatorIndices = toSeq(vc.attachedValidators[].indices())
|
||||
var
|
||||
filteredDuties: seq[RestSyncCommitteeDuty]
|
||||
offset = 0
|
||||
|
@ -249,7 +249,7 @@ proc pollForSyncCommitteeDuties*(vc: ValidatorClientRef,
|
|||
return 0
|
||||
|
||||
for item in res.data:
|
||||
if checkSyncDuty(item) and (item.pubkey in vc.attachedValidators):
|
||||
if checkSyncDuty(item) and (item.pubkey in vc.attachedValidators[]):
|
||||
filteredDuties.add(item)
|
||||
|
||||
offset += arraySize
|
||||
|
@ -287,7 +287,7 @@ proc pollForSyncCommitteeDuties*(vc: ValidatorClientRef,
|
|||
let sres = vc.getCurrentSlot()
|
||||
if sres.isSome():
|
||||
for item in addOrReplaceItems:
|
||||
let validator = vc.attachedValidators.getValidator(item.duty.pubkey)
|
||||
let validator = vc.attachedValidators[].getValidator(item.duty.pubkey)
|
||||
let future = validator.getSyncCommitteeSelectionProof(
|
||||
fork,
|
||||
genesisRoot,
|
||||
|
@ -358,7 +358,7 @@ proc pollForAttesterDuties*(vc: ValidatorClientRef) {.async.} =
|
|||
currentEpoch = currentSlot.epoch()
|
||||
nextEpoch = currentEpoch + 1'u64
|
||||
|
||||
if vc.attachedValidators.count() != 0:
|
||||
if vc.attachedValidators[].count() != 0:
|
||||
var counts: array[2, tuple[epoch: Epoch, count: int]]
|
||||
counts[0] = (currentEpoch, await vc.pollForAttesterDuties(currentEpoch))
|
||||
counts[1] = (nextEpoch, await vc.pollForAttesterDuties(nextEpoch))
|
||||
|
@ -404,7 +404,7 @@ proc pollForSyncCommitteeDuties* (vc: ValidatorClientRef) {.async.} =
|
|||
currentEpoch = currentSlot.epoch()
|
||||
nextEpoch = currentEpoch + 1'u64
|
||||
|
||||
if vc.attachedValidators.count() != 0:
|
||||
if vc.attachedValidators[].count() != 0:
|
||||
var counts: array[2, tuple[epoch: Epoch, count: int]]
|
||||
counts[0] =
|
||||
(currentEpoch, await vc.pollForSyncCommitteeDuties(currentEpoch))
|
||||
|
@ -455,13 +455,13 @@ proc pollForBeaconProposers*(vc: ValidatorClientRef) {.async.} =
|
|||
currentSlot = sres.get()
|
||||
currentEpoch = currentSlot.epoch()
|
||||
|
||||
if vc.attachedValidators.count() != 0:
|
||||
if vc.attachedValidators[].count() != 0:
|
||||
try:
|
||||
let res = await vc.getProposerDuties(currentEpoch)
|
||||
let
|
||||
dependentRoot = res.dependent_root
|
||||
duties = res.data
|
||||
relevantDuties = duties.filterIt(it.pubkey in vc.attachedValidators)
|
||||
relevantDuties = duties.filterIt(it.pubkey in vc.attachedValidators[])
|
||||
|
||||
if len(relevantDuties) > 0:
|
||||
vc.addOrReplaceProposers(currentEpoch, dependentRoot, relevantDuties)
|
||||
|
|
|
@ -8,25 +8,16 @@
|
|||
import
|
||||
std/sets,
|
||||
metrics, chronicles,
|
||||
"."/[common, api, block_service],
|
||||
../spec/datatypes/[phase0, altair, bellatrix],
|
||||
../spec/eth2_apis/rest_types
|
||||
../spec/eth2_apis/rest_types,
|
||||
../validators/activity_metrics,
|
||||
"."/[common, api, block_service]
|
||||
|
||||
const
|
||||
ServiceName = "sync_committee_service"
|
||||
|
||||
logScope: service = ServiceName
|
||||
|
||||
declareCounter beacon_sync_committee_messages_sent,
|
||||
"Number of sync committee messages sent by the node"
|
||||
|
||||
declareHistogram beacon_sync_committee_message_sent_delay,
|
||||
"Time(s) between expected and actual sync committee message send moment",
|
||||
buckets = DelayBuckets
|
||||
|
||||
declareCounter beacon_sync_committee_contributions_sent,
|
||||
"Number of sync committee contributions sent by the node"
|
||||
|
||||
type
|
||||
ContributionItem* = object
|
||||
aggregator_index: uint64
|
||||
|
@ -226,7 +217,7 @@ proc produceAndPublishContributions(service: SyncCommitteeServiceRef,
|
|||
block:
|
||||
var res: seq[ContributionItem]
|
||||
for duty in duties:
|
||||
let validator = vc.attachedValidators.getValidator(duty.data.pubkey)
|
||||
let validator = vc.attachedValidators[].getValidator(duty.data.pubkey)
|
||||
if not isNil(validator):
|
||||
if duty.slotSig.isSome:
|
||||
template slotSignature: auto = duty.slotSig.get
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
import metrics
|
||||
export metrics
|
||||
|
||||
const delayBuckets = [-Inf, -4.0, -2.0, -1.0, -0.5, -0.1, -0.05,
|
||||
0.05, 0.1, 0.5, 1.0, 2.0, 4.0, 8.0, Inf]
|
||||
|
||||
# The "sent" counters capture messages that were sent via this beacon node
|
||||
# regardless if they were produced internally or received via the REST API.
|
||||
#
|
||||
# Counters and histograms for timing-sensitive messages, only counters for
|
||||
# the rest (aggregates don't affect rewards, so timing is less important)
|
||||
|
||||
declarePublicCounter beacon_attestations_sent,
|
||||
"Number of attestations sent by the node"
|
||||
|
||||
declarePublicCounter beacon_aggregates_sent,
|
||||
"Number of beacon chain attestations sent by the node"
|
||||
|
||||
declarePublicHistogram beacon_attestation_sent_delay,
|
||||
"Time(s) between expected and actual attestation send moment",
|
||||
buckets = delayBuckets
|
||||
|
||||
declarePublicCounter beacon_blocks_sent,
|
||||
"Number of beacon blocks sent by this node"
|
||||
|
||||
declarePublicHistogram beacon_blocks_sent_delay,
|
||||
"Time(s) between expected and actual block send moment",
|
||||
buckets = delayBuckets
|
||||
|
||||
declarePublicCounter beacon_sync_committee_messages_sent,
|
||||
"Number of sync committee messages sent by the node"
|
||||
|
||||
declarePublicHistogram beacon_sync_committee_message_sent_delay,
|
||||
"Time(s) between expected and actual sync committee message send moment",
|
||||
buckets = delayBuckets
|
||||
|
||||
declarePublicCounter beacon_sync_committee_contributions_sent,
|
||||
"Number of sync committee contributions sent by the node"
|
|
@ -20,7 +20,7 @@ import
|
|||
".."/spec/datatypes/base,
|
||||
stew/io2, libp2p/crypto/crypto as lcrypto,
|
||||
nimcrypto/utils as ncrutils,
|
||||
".."/[conf, filepath],
|
||||
".."/[conf, filepath, beacon_clock],
|
||||
".."/networking/network_metadata,
|
||||
./validator_pool
|
||||
|
||||
|
@ -49,8 +49,6 @@ type
|
|||
walletPath*: WalletPathPair
|
||||
seed*: KeySeed
|
||||
|
||||
AnyConf* = BeaconNodeConf | ValidatorClientConf | SigningNodeConf
|
||||
|
||||
KmResult*[T] = Result[T, cstring]
|
||||
|
||||
AnyKeystore* = RemoteKeystore | Keystore
|
||||
|
@ -69,6 +67,20 @@ type
|
|||
|
||||
ImportResult*[T] = Result[T, AddValidatorFailure]
|
||||
|
||||
ValidatorPubKeyToIdxFn* =
|
||||
proc (pubkey: ValidatorPubKey): Option[ValidatorIndex]
|
||||
{.raises: [Defect], gcsafe.}
|
||||
|
||||
KeymanagerHost* = object
|
||||
validatorPool*: ref ValidatorPool
|
||||
rng*: ref HmacDrbgContext
|
||||
keymanagerToken*: string
|
||||
validatorsDir*: string
|
||||
secretsDir*: string
|
||||
defaultFeeRecipient*: Eth1Address
|
||||
getValidatorIdxFn*: ValidatorPubKeyToIdxFn
|
||||
getBeaconTimeFn*: GetBeaconTimeFn
|
||||
|
||||
const
|
||||
minPasswordLen = 12
|
||||
minPasswordEntropy = 60.0
|
||||
|
@ -78,6 +90,38 @@ const
|
|||
"passwords" / "10-million-password-list-top-100000.txt",
|
||||
minWordLen = minPasswordLen)
|
||||
|
||||
func init*(T: type KeymanagerHost,
|
||||
validatorPool: ref ValidatorPool,
|
||||
rng: ref HmacDrbgContext,
|
||||
keymanagerToken: string,
|
||||
validatorsDir: string,
|
||||
secretsDir: string,
|
||||
defaultFeeRecipient: Eth1Address,
|
||||
getValidatorIdxFn: ValidatorPubKeyToIdxFn,
|
||||
getBeaconTimeFn: GetBeaconTimeFn): T =
|
||||
T(validatorPool: validatorPool,
|
||||
rng: rng,
|
||||
keymanagerToken: keymanagerToken,
|
||||
validatorsDir: validatorsDir,
|
||||
secretsDir: secretsDir,
|
||||
defaultFeeRecipient: defaultFeeRecipient,
|
||||
getValidatorIdxFn: getValidatorIdxFn,
|
||||
getBeaconTimeFn: getBeaconTimeFn)
|
||||
|
||||
proc getValidatorIdx*(host: KeymanagerHost,
|
||||
pubkey: ValidatorPubKey): Option[ValidatorIndex] =
|
||||
if host.getValidatorIdxFn != nil:
|
||||
host.getValidatorIdxFn(pubkey)
|
||||
else:
|
||||
none ValidatorIndex
|
||||
|
||||
proc addLocalValidator*(host: KeymanagerHost, keystore: KeystoreData) =
|
||||
let
|
||||
slot = host.getBeaconTimeFn().slotOrZero
|
||||
validatorIdx = host.getValidatorIdx(keystore.pubkey)
|
||||
|
||||
host.validatorPool[].addLocalValidator(keystore, validatorIdx, slot)
|
||||
|
||||
proc echoP*(msg: string) =
|
||||
## Prints a paragraph aligned to 80 columns
|
||||
echo ""
|
||||
|
@ -89,8 +133,7 @@ func init*(T: type KeystoreData,
|
|||
KeystoreData(
|
||||
kind: KeystoreKind.Local,
|
||||
privateKey: privateKey,
|
||||
description: if keystore.description == nil: none(string)
|
||||
else: some(keystore.description[]),
|
||||
description: keystore.description,
|
||||
path: keystore.path,
|
||||
uuid: keystore.uuid,
|
||||
handle: handle,
|
||||
|
@ -386,8 +429,7 @@ proc loadRemoteKeystoreImpl(validatorsDir,
|
|||
let buffer = gres.get()
|
||||
let data =
|
||||
try:
|
||||
Json.decode(buffer, RemoteKeystore, requireAllFields = true,
|
||||
allowUnknownFields = true)
|
||||
parseRemoteKeystore(buffer)
|
||||
except SerializationError as e:
|
||||
error "Invalid remote keystore file", key_path = keystorePath,
|
||||
error_msg = e.formatMsg(keystorePath)
|
||||
|
@ -432,8 +474,7 @@ proc loadLocalKeystoreImpl(validatorsDir, secretsDir, keyName: string,
|
|||
let buffer = gres.get()
|
||||
let data =
|
||||
try:
|
||||
Json.decode(buffer, Keystore, requireAllFields = true,
|
||||
allowUnknownFields = true)
|
||||
parseKeystore(buffer)
|
||||
except SerializationError as e:
|
||||
error "Invalid local keystore file", key_path = keystorePath,
|
||||
error_msg = e.formatMsg(keystorePath)
|
||||
|
@ -552,13 +593,8 @@ proc removeValidatorFiles*(validatorsDir, secretsDir, keyName: string,
|
|||
func fsName(pubkey: ValidatorPubKey|CookedPubKey): string =
|
||||
"0x" & pubkey.toHex()
|
||||
|
||||
proc removeValidatorFiles*(
|
||||
conf: AnyConf, keyName: string,
|
||||
kind: KeystoreKind
|
||||
): KmResult[RemoveValidatorStatus] {.raises: [Defect].} =
|
||||
removeValidatorFiles(conf.validatorsDir(), conf.secretsDir(), keyName, kind)
|
||||
|
||||
proc removeValidator*(pool: var ValidatorPool, conf: AnyConf,
|
||||
proc removeValidator*(pool: var ValidatorPool,
|
||||
validatorsDir, secretsDir: string,
|
||||
publicKey: ValidatorPubKey,
|
||||
kind: KeystoreKind): KmResult[RemoveValidatorStatus] {.
|
||||
raises: [Defect].} =
|
||||
|
@ -570,7 +606,7 @@ proc removeValidator*(pool: var ValidatorPool, conf: AnyConf,
|
|||
let cres = validator.data.handle.closeLockedFile()
|
||||
if cres.isErr():
|
||||
return err("Could not unlock validator keystore file")
|
||||
let res = removeValidatorFiles(conf, publicKey.fsName, kind)
|
||||
let res = removeValidatorFiles(validatorsDir, secretsDir, publicKey.fsName, kind)
|
||||
if res.isErr():
|
||||
return err(res.error())
|
||||
pool.removeValidator(publicKey)
|
||||
|
@ -1111,37 +1147,13 @@ proc saveLockedKeystore*(
|
|||
let remoteInfo = RemoteSignerInfo(url: url, id: 0)
|
||||
saveLockedKeystore(validatorsDir, publicKey, @[remoteInfo], 1)
|
||||
|
||||
proc saveKeystore*(
|
||||
conf: AnyConf,
|
||||
publicKey: ValidatorPubKey,
|
||||
remotes: seq[RemoteSignerInfo],
|
||||
threshold: uint32,
|
||||
flags: set[RemoteKeystoreFlag] = {},
|
||||
remoteType = RemoteSignerType.Web3Signer,
|
||||
desc = ""
|
||||
): Result[FileLockHandle, KeystoreGenerationError] {.raises: [Defect].} =
|
||||
saveKeystore(conf.validatorsDir(),
|
||||
publicKey, remotes, threshold, flags, remoteType, desc)
|
||||
|
||||
proc saveLockedKeystore*(
|
||||
conf: AnyConf,
|
||||
publicKey: ValidatorPubKey,
|
||||
remotes: seq[RemoteSignerInfo],
|
||||
threshold: uint32,
|
||||
flags: set[RemoteKeystoreFlag] = {},
|
||||
remoteType = RemoteSignerType.Web3Signer,
|
||||
desc = ""
|
||||
): Result[FileLockHandle, KeystoreGenerationError] {.raises: [Defect].} =
|
||||
saveLockedKeystore(conf.validatorsDir(),
|
||||
publicKey, remotes, threshold, flags, remoteType, desc)
|
||||
|
||||
proc importKeystore*(pool: var ValidatorPool, conf: AnyConf,
|
||||
proc importKeystore*(pool: var ValidatorPool,
|
||||
validatorsDir: string,
|
||||
keystore: RemoteKeystore): ImportResult[KeystoreData]
|
||||
{.raises: [Defect].} =
|
||||
let
|
||||
publicKey = keystore.pubkey
|
||||
keyName = publicKey.fsName
|
||||
validatorsDir = conf.validatorsDir()
|
||||
keystoreDir = validatorsDir / keyName
|
||||
keystoreFile = keystoreDir / RemoteKeystoreFileName
|
||||
|
||||
|
@ -1163,7 +1175,7 @@ proc importKeystore*(pool: var ValidatorPool, conf: AnyConf,
|
|||
if existsKeystore(keystoreDir, {KeystoreKind.Local, KeystoreKind.Remote}):
|
||||
return err(AddValidatorFailure.init(AddValidatorStatus.existingArtifacts))
|
||||
|
||||
let res = saveLockedKeystore(conf, publicKey, keystore.remotes,
|
||||
let res = saveLockedKeystore(validatorsDir, publicKey, keystore.remotes,
|
||||
keystore.threshold)
|
||||
if res.isErr():
|
||||
return err(AddValidatorFailure.init(AddValidatorStatus.failed,
|
||||
|
@ -1173,7 +1185,8 @@ proc importKeystore*(pool: var ValidatorPool, conf: AnyConf,
|
|||
|
||||
proc importKeystore*(pool: var ValidatorPool,
|
||||
rng: var HmacDrbgContext,
|
||||
conf: AnyConf, keystore: Keystore,
|
||||
validatorsDir, secretsDir: string,
|
||||
keystore: Keystore,
|
||||
password: string): ImportResult[KeystoreData] {.
|
||||
raises: [Defect].} =
|
||||
let keypass = KeystorePass.init(password)
|
||||
|
@ -1188,8 +1201,6 @@ proc importKeystore*(pool: var ValidatorPool,
|
|||
let
|
||||
publicKey = privateKey.toPubKey()
|
||||
keyName = publicKey.fsName
|
||||
validatorsDir = conf.validatorsDir()
|
||||
secretsDir = conf.secretsDir()
|
||||
secretFile = secretsDir / keyName
|
||||
keystoreDir = validatorsDir / keyName
|
||||
keystoreFile = keystoreDir / KeystoreFileName
|
||||
|
@ -1241,14 +1252,17 @@ proc generateDistirbutedStore*(rng: var HmacDrbgContext,
|
|||
# actual validator
|
||||
saveKeystore(remoteValidatorDir, pubKey, signers, threshold)
|
||||
|
||||
func validatorKeystoreDir(conf: AnyConf, pubkey: ValidatorPubKey): string =
|
||||
conf.validatorsDir / pubkey.fsName
|
||||
func validatorKeystoreDir(host: KeymanagerHost,
|
||||
pubkey: ValidatorPubKey): string =
|
||||
host.validatorsDir / pubkey.fsName
|
||||
|
||||
func feeRecipientPath*(conf: AnyConf, pubkey: ValidatorPubKey): string =
|
||||
conf.validatorKeystoreDir(pubkey) / FeeRecipientFilename
|
||||
func feeRecipientPath*(host: KeymanagerHost,
|
||||
pubkey: ValidatorPubKey): string =
|
||||
host.validatorKeystoreDir(pubkey) / FeeRecipientFilename
|
||||
|
||||
proc removeFeeRecipientFile*(conf: AnyConf, pubkey: ValidatorPubKey): Result[void, string] =
|
||||
let path = conf.feeRecipientPath(pubkey)
|
||||
proc removeFeeRecipientFile*(host: KeymanagerHost,
|
||||
pubkey: ValidatorPubKey): Result[void, string] =
|
||||
let path = host.feeRecipientPath(pubkey)
|
||||
if fileExists(path):
|
||||
let res = io2.removeFile(path)
|
||||
if res.isErr:
|
||||
|
@ -1256,8 +1270,8 @@ proc removeFeeRecipientFile*(conf: AnyConf, pubkey: ValidatorPubKey): Result[voi
|
|||
|
||||
return ok()
|
||||
|
||||
proc setFeeRecipient*(conf: AnyConf, pubkey: ValidatorPubKey, feeRecipient: Eth1Address): Result[void, string] =
|
||||
let validatorKeystoreDir = conf.validatorKeystoreDir(pubkey)
|
||||
proc setFeeRecipient*(host: KeymanagerHost, pubkey: ValidatorPubKey, feeRecipient: Eth1Address): Result[void, string] =
|
||||
let validatorKeystoreDir = host.validatorKeystoreDir(pubkey)
|
||||
|
||||
? secureCreatePath(validatorKeystoreDir).mapErr(proc(e: auto): string =
|
||||
"Could not create wallet directory [" & validatorKeystoreDir & "]: " & $e)
|
||||
|
@ -1265,22 +1279,15 @@ proc setFeeRecipient*(conf: AnyConf, pubkey: ValidatorPubKey, feeRecipient: Eth1
|
|||
io2.writeFile(validatorKeystoreDir / FeeRecipientFilename, $feeRecipient)
|
||||
.mapErr(proc(e: auto): string = "Failed to write fee recipient file: " & $e)
|
||||
|
||||
func defaultFeeRecipient*(conf: AnyConf): Eth1Address =
|
||||
if conf.suggestedFeeRecipient.isSome:
|
||||
conf.suggestedFeeRecipient.get
|
||||
else:
|
||||
# https://github.com/nim-lang/Nim/issues/19802
|
||||
(static(default(Eth1Address)))
|
||||
|
||||
type
|
||||
FeeRecipientStatus* = enum
|
||||
noSuchValidator
|
||||
invalidFeeRecipientFile
|
||||
|
||||
proc getSuggestedFeeRecipient*(
|
||||
conf: AnyConf,
|
||||
host: KeymanagerHost,
|
||||
pubkey: ValidatorPubKey): Result[Eth1Address, FeeRecipientStatus] =
|
||||
let validatorDir = conf.validatorKeystoreDir(pubkey)
|
||||
let validatorDir = host.validatorKeystoreDir(pubkey)
|
||||
|
||||
# In this particular case, an error might be by design. If the file exists,
|
||||
# but doesn't load or parse that's a more urgent matter to fix. Many people
|
||||
|
@ -1291,7 +1298,7 @@ proc getSuggestedFeeRecipient*(
|
|||
|
||||
let feeRecipientPath = validatorDir / FeeRecipientFilename
|
||||
if not fileExists(feeRecipientPath):
|
||||
return ok conf.defaultFeeRecipient
|
||||
return ok host.defaultFeeRecipient
|
||||
|
||||
try:
|
||||
# Avoid being overly flexible initially. Trailing whitespace is common
|
||||
|
|
|
@ -17,44 +17,11 @@ import
|
|||
../spec/network,
|
||||
../consensus_object_pools/spec_cache,
|
||||
../gossip_processing/eth2_processor,
|
||||
../networking/eth2_network
|
||||
../networking/eth2_network,
|
||||
./activity_metrics
|
||||
|
||||
export eth2_processor, eth2_network
|
||||
|
||||
# The "sent" counters capture messages that were sent via this beacon node
|
||||
# regardless if they were produced internally or received via the REST API.
|
||||
#
|
||||
# Counters and histograms for timing-sensitive messages, only counters for
|
||||
# the rest (aggregates don't affect rewards, so timing is less important)
|
||||
const delayBuckets = [-Inf, -4.0, -2.0, -1.0, -0.5, -0.1, -0.05,
|
||||
0.05, 0.1, 0.5, 1.0, 2.0, 4.0, 8.0, Inf]
|
||||
declareCounter beacon_blocks_sent,
|
||||
"Number of beacon blocks sent by this node"
|
||||
|
||||
declareHistogram beacon_blocks_sent_delay,
|
||||
"Time(s) between expected and actual block send moment",
|
||||
buckets = delayBuckets
|
||||
|
||||
declareCounter beacon_attestations_sent,
|
||||
"Number of attestations sent by the node"
|
||||
|
||||
declareCounter beacon_aggregates_sent,
|
||||
"Number of beacon chain attestations sent by the node"
|
||||
|
||||
declareHistogram beacon_attestation_sent_delay,
|
||||
"Time(s) between expected and actual attestation send moment",
|
||||
buckets = delayBuckets
|
||||
|
||||
declareCounter beacon_sync_committee_messages_sent,
|
||||
"Number of sync committee messages sent by the node"
|
||||
|
||||
declareHistogram beacon_sync_committee_message_sent_delay,
|
||||
"Time(s) between expected and actual sync committee message send moment",
|
||||
buckets = delayBuckets
|
||||
|
||||
declareCounter beacon_sync_committee_contributions_sent,
|
||||
"Number of sync committee contributions sent by the node"
|
||||
|
||||
declareCounter beacon_voluntary_exits_sent,
|
||||
"Number of beacon voluntary sent by this node"
|
||||
|
||||
|
|
|
@ -70,7 +70,7 @@ logScope: topics = "beacval"
|
|||
type
|
||||
ForkedBlockResult* = Result[ForkedBeaconBlock, string]
|
||||
|
||||
proc findValidator(validators: auto, pubkey: ValidatorPubKey):
|
||||
proc findValidator*(validators: auto, pubkey: ValidatorPubKey):
|
||||
Option[ValidatorIndex] =
|
||||
let idx = validators.findIt(it.pubkey == pubkey)
|
||||
if idx == -1:
|
||||
|
@ -88,8 +88,11 @@ proc addLocalValidator(node: BeaconNode, validators: auto,
|
|||
index = findValidator(validators, pubkey)
|
||||
node.attachedValidators[].addLocalValidator(item, index, slot)
|
||||
|
||||
proc addRemoteValidator(pool: var ValidatorPool, validators: auto,
|
||||
item: KeystoreData, slot: Slot) =
|
||||
# TODO: This should probably be moved to the validator_pool module
|
||||
proc addRemoteValidator*(pool: var ValidatorPool,
|
||||
index: Option[ValidatorIndex],
|
||||
item: KeystoreData,
|
||||
slot: Slot) =
|
||||
var clients: seq[(RestClientRef, RemoteSignerInfo)]
|
||||
let httpFlags =
|
||||
block:
|
||||
|
@ -105,7 +108,6 @@ proc addRemoteValidator(pool: var ValidatorPool, validators: auto,
|
|||
warn "Unable to resolve distributed signer address",
|
||||
remote_url = $remote.url, validator = $remote.pubkey
|
||||
clients.add((client.get(), remote))
|
||||
let index = findValidator(validators, item.pubkey)
|
||||
pool.addRemoteValidator(item, clients, index, slot)
|
||||
|
||||
proc addLocalValidators*(node: BeaconNode,
|
||||
|
@ -120,8 +122,8 @@ proc addRemoteValidators*(node: BeaconNode,
|
|||
let slot = node.currentSlot()
|
||||
withState(node.dag.headState):
|
||||
for item in validators:
|
||||
node.attachedValidators[].addRemoteValidator(
|
||||
state.data.validators.asSeq(), item, slot)
|
||||
let index = findValidator(state.data.validators.asSeq(), item.pubkey)
|
||||
node.attachedValidators[].addRemoteValidator(index, item, slot)
|
||||
|
||||
proc addValidators*(node: BeaconNode) =
|
||||
let (localValidators, remoteValidators) =
|
||||
|
@ -149,7 +151,7 @@ proc getAttachedValidator(node: BeaconNode,
|
|||
if validator != nil and validator.index != some(idx):
|
||||
# Update index, in case the validator was activated!
|
||||
notice "Validator activated", pubkey = validator.pubkey, index = idx
|
||||
validator.index = some(idx)
|
||||
validator.index = some(idx)
|
||||
validator
|
||||
else:
|
||||
warn "Validator index out of bounds",
|
||||
|
@ -382,7 +384,7 @@ proc getExecutionPayload(
|
|||
dynamicFeeRecipient = node.dynamicFeeRecipientsStore.getDynamicFeeRecipient(
|
||||
validator_index, epoch)
|
||||
feeRecipient = dynamicFeeRecipient.valueOr:
|
||||
node.config.getSuggestedFeeRecipient(pubkey).valueOr:
|
||||
node.keymanagerHost[].getSuggestedFeeRecipient(pubkey).valueOr:
|
||||
node.config.defaultFeeRecipient
|
||||
payload_id = (await forkchoice_updated(
|
||||
proposalState.bellatrixData.data, latestHead, latestFinalized,
|
||||
|
@ -1169,7 +1171,7 @@ proc getValidatorRegistration(
|
|||
const gasLimit = 30000000
|
||||
|
||||
let feeRecipient =
|
||||
node.config.getSuggestedFeeRecipient(validator.pubkey).valueOr:
|
||||
node.keymanagerHost[].getSuggestedFeeRecipient(validator.pubkey).valueOr:
|
||||
node.config.defaultFeeRecipient
|
||||
var validatorRegistration = SignedValidatorRegistrationV1(
|
||||
message: ValidatorRegistrationV1(
|
||||
|
|
|
@ -3352,35 +3352,9 @@
|
|||
"status": "",
|
||||
"message": ""
|
||||
}],
|
||||
"slashing_protection":
|
||||
{
|
||||
"metadata": {
|
||||
"interchange_format_version": "",
|
||||
"genesis_validators_root": ""
|
||||
},
|
||||
"data":[{
|
||||
"pubkey": "",
|
||||
"signed_blocks": [
|
||||
{
|
||||
"slot": "",
|
||||
"signing_root": ""},
|
||||
{
|
||||
"slot": ""
|
||||
}],
|
||||
"signed_attestations": [
|
||||
{
|
||||
"source_epoch": "",
|
||||
"target_epoch": "",
|
||||
"signing_root": ""
|
||||
},
|
||||
{
|
||||
"source_epoch": "",
|
||||
"target_epoch": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}}}]
|
||||
"slashing_protection": ""
|
||||
}}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
-d:chronicles_colors:off
|
|
@ -71,6 +71,7 @@ BASE_PORT="9000"
|
|||
BASE_REMOTE_SIGNER_PORT="6000"
|
||||
BASE_METRICS_PORT="8008"
|
||||
BASE_REST_PORT="7500"
|
||||
BASE_VC_KEYMANAGER_PORT="8500"
|
||||
BASE_EL_NET_PORT="30303"
|
||||
BASE_EL_HTTP_PORT="8545"
|
||||
BASE_EL_WS_PORT="8546"
|
||||
|
@ -340,6 +341,7 @@ if [[ "${LIGHTHOUSE_VC_NODES}" != "0" && "${CONST_PRESET}" != "mainnet" ]]; then
|
|||
fi
|
||||
|
||||
scripts/makedir.sh "${DATA_DIR}"
|
||||
echo x > "${DATA_DIR}/keymanager-token"
|
||||
|
||||
VALIDATORS_DIR="${DATA_DIR}/validators"
|
||||
scripts/makedir.sh "${VALIDATORS_DIR}"
|
||||
|
@ -975,6 +977,8 @@ for NUM_NODE in $(seq 0 $(( NUM_NODES - 1 ))); do
|
|||
${WEB3_ARG} \
|
||||
${STOP_AT_EPOCH_FLAG} \
|
||||
--rest-port="$(( BASE_REST_PORT + NUM_NODE ))" \
|
||||
--keymanager \
|
||||
--keymanager-token-file="${DATA_DIR}/keymanager-token" \
|
||||
--metrics-port="$(( BASE_METRICS_PORT + NUM_NODE ))" \
|
||||
--light-client=on \
|
||||
${EXTRA_ARGS} \
|
||||
|
@ -1008,6 +1012,9 @@ for NUM_NODE in $(seq 0 $(( NUM_NODES - 1 ))); do
|
|||
--log-level="${LOG_LEVEL}" \
|
||||
${STOP_AT_EPOCH_FLAG} \
|
||||
--data-dir="${VALIDATOR_DATA_DIR}" \
|
||||
--keymanager \
|
||||
--keymanager-port=$((BASE_VC_KEYMANAGER_PORT + NUM_NODE)) \
|
||||
--keymanager-token-file="${DATA_DIR}/keymanager-token" \
|
||||
--beacon-node="http://127.0.0.1:$((BASE_REST_PORT + NUM_NODE))" \
|
||||
&> "${DATA_DIR}/log_val${NUM_NODE}.txt" &
|
||||
PIDS="${PIDS},$!"
|
||||
|
|
|
@ -20,10 +20,18 @@ import
|
|||
../beacon_chain/networking/network_metadata,
|
||||
../beacon_chain/rpc/rest_key_management_api,
|
||||
../beacon_chain/[conf, filepath, beacon_node,
|
||||
nimbus_beacon_node, beacon_node_status],
|
||||
nimbus_beacon_node, beacon_node_status,
|
||||
nimbus_validator_client],
|
||||
../beacon_chain/validator_client/common,
|
||||
|
||||
./testutil
|
||||
|
||||
type
|
||||
KeymanagerToTest = object
|
||||
port: int
|
||||
validatorsDir: string
|
||||
secretsDir: string
|
||||
|
||||
const
|
||||
simulationDepositsCount = 128
|
||||
dataDir = "./test_keymanager_api"
|
||||
|
@ -33,9 +41,11 @@ const
|
|||
genesisFile = dataDir / "genesis.ssz"
|
||||
bootstrapEnrFile = dataDir / "bootstrap_node.enr"
|
||||
tokenFilePath = dataDir / "keymanager-token.txt"
|
||||
keymanagerPort = 47000
|
||||
keymanagerPortBN = 47000
|
||||
keymanagerPortVC = 48000
|
||||
correctTokenValue = "some secret token"
|
||||
defaultFeeRecipient = Eth1Address.fromHex("0x000000000000000000000000000000000000DEAD")
|
||||
|
||||
newPrivateKeys = [
|
||||
"0x598c9b81749ba7bb8eb37781027359e3ffe87d0e1579e21c453ce22af0c05e35",
|
||||
"0x14e4470a1d8913ec0602048af78addf0fd7a37f591dd3feda828d10a10c0f6ff",
|
||||
|
@ -46,6 +56,7 @@ const
|
|||
"0x10052305a5fda7805fb1e762fe6cbc47e43c5a54f34f008fa79c48fee1749db7",
|
||||
"0x3630f086fb9f1136fe077751031a16630e43d65ff64bb9fd3708adff81df5926"
|
||||
]
|
||||
|
||||
oldPublicKeys = [
|
||||
"0x94effccb0514f0f110a9680827e4f3769e53349e3b1c177e8c4f38b0e52e7842a4990212fe2edd2ce48b9b0bd02f3b04",
|
||||
"0x950bcb136ef15e737cd28cc8ba94a5584e30cf6cfa4f3d16215acbe46917633c09630208f379898a898b29bd59b2bd34",
|
||||
|
@ -56,7 +67,9 @@ const
|
|||
"0x995e1d9d9d467ca25b981a7ca0880e932ac418e5ebed9a834f3ead3fbec267986e28eb0243c562ae3b1995a600c1495c",
|
||||
"0x945ab594e8c9cf3d6251b86fddf6fbf970c1835cd14113098554f135a6c2cf7f21d2f7a08ae33726785a59ae4910fa51",
|
||||
]
|
||||
|
||||
oldPublicKeysUrl = HttpHostUri(parseUri("http://127.0.0.1/local"))
|
||||
|
||||
newPublicKeys = [
|
||||
"0x80eadf027ad564a2f004616fa58f3add9caa700b20e9bf7e0b101be61406feb79f5e28ec8a5bb2a0689cc7b4c807afba",
|
||||
"0x8c6585f39fd3d2ed950ba4958f0050ec68e4e7e3200147687fa101bcf98977ebe144b03edc45906faae144549f11d8b9",
|
||||
|
@ -67,6 +80,7 @@ const
|
|||
"0xa782e5161ba8e9ac135b0db3203a8c23aa61e19be6b9c198393d8b2b902bad8139863d9cf26bc2cbdc3b747bafc64606",
|
||||
"0xb33f17216dda29dba1a9257e75b3dd8446c9ea217b563c20950c43f64300f7bd3d5f0dfa02274cab988e594552b7189e"
|
||||
]
|
||||
|
||||
unusedPublicKeys = [
|
||||
"0xc22f17216dda29dba1a9257e75b3dd8446c9ea217b563c20950c43f64300f7bd3d5f0dfa02274cab988e594552b7232d",
|
||||
"0x0bbca63e35c7a159fc2f187d300cad9ef5f5e73e55f78c391e7bc2c2feabc2d9d63dfe99edd7058ad0ab9d7f14aade5f"
|
||||
|
@ -74,6 +88,24 @@ const
|
|||
|
||||
newPublicKeysUrl = HttpHostUri(parseUri("http://127.0.0.1/remote"))
|
||||
|
||||
nodeDataDir = dataDir / "node-0"
|
||||
nodeValidatorsDir = nodeDataDir / "validators"
|
||||
nodeSecretsDir = nodeDataDir / "secrets"
|
||||
|
||||
vcDataDir = dataDir / "validator-0"
|
||||
vcValidatorsDir = vcDataDir / "validators"
|
||||
vcSecretsDir = vcDataDir / "secrets"
|
||||
|
||||
beaconNodeKeymanager = KeymanagerToTest(
|
||||
port: keymanagerPortBN,
|
||||
validatorsDir: nodeValidatorsDir,
|
||||
secretsDir: nodeSecretsDir)
|
||||
|
||||
validatorClientKeymanager = KeymanagerToTest(
|
||||
port: keymanagerPortVC,
|
||||
validatorsDir: vcValidatorsDir,
|
||||
secretsDir: vcSecretsDir)
|
||||
|
||||
func specifiedFeeRecipient(x: int): Eth1Address =
|
||||
copyMem(addr result, unsafeAddr x, sizeof x)
|
||||
|
||||
|
@ -87,7 +119,7 @@ proc contains*(keylist: openArray[KeystoreInfo], key: string): bool =
|
|||
let pubkey = ValidatorPubKey.fromHex(key).tryGet()
|
||||
contains(keylist, pubkey)
|
||||
|
||||
proc startSingleNodeNetwork {.raises: [CatchableError, Defect].} =
|
||||
proc prepareNetwork =
|
||||
let
|
||||
rng = keys.newRng()
|
||||
mnemonic = generateMnemonic(rng[])
|
||||
|
@ -126,18 +158,6 @@ proc startSingleNodeNetwork {.raises: [CatchableError, Defect].} =
|
|||
Json.saveFile(depositsFile, launchPadDeposits)
|
||||
notice "Deposit data written", filename = depositsFile
|
||||
|
||||
for item in oldPublicKeys:
|
||||
let key = ValidatorPubKey.fromHex(item).tryGet()
|
||||
let res = saveKeystore(validatorsDir, key, oldPublicKeysUrl)
|
||||
if res.isErr():
|
||||
fatal "Failed to create remote keystore file", err = res.error
|
||||
quit 1
|
||||
|
||||
let tokenFileRes = secureWriteFile(tokenFilePath, correctTokenValue)
|
||||
if tokenFileRes.isErr:
|
||||
fatal "Failed to create token file", err = deposits.error
|
||||
quit 1
|
||||
|
||||
let createTestnetConf = try: BeaconNodeConf.load(cmdLine = mapIt([
|
||||
"--data-dir=" & dataDir,
|
||||
"createTestnet",
|
||||
|
@ -153,20 +173,112 @@ proc startSingleNodeNetwork {.raises: [CatchableError, Defect].} =
|
|||
|
||||
doCreateTestnet(createTestnetConf, rng[])
|
||||
|
||||
let tokenFileRes = secureWriteFile(tokenFilePath, correctTokenValue)
|
||||
if tokenFileRes.isErr:
|
||||
fatal "Failed to create token file", err = deposits.error
|
||||
quit 1
|
||||
|
||||
proc copyHalfValidators(dstDataDir: string, firstHalf: bool) =
|
||||
let dstValidatorsDir = dstDataDir / "validators"
|
||||
|
||||
block:
|
||||
let status = secureCreatePath(dstValidatorsDir)
|
||||
if status.isErr():
|
||||
fatal "Could not create node validators folder",
|
||||
path = dstValidatorsDir, err = ioErrorMsg(status.error)
|
||||
quit 1
|
||||
|
||||
let dstSecretsDir = dstDataDir / "secrets"
|
||||
|
||||
block:
|
||||
let status = secureCreatePath(dstSecretsDir)
|
||||
if status.isErr():
|
||||
fatal "Could not create node secrets folder",
|
||||
path = dstSecretsDir, err = ioErrorMsg(status.error)
|
||||
quit 1
|
||||
|
||||
var validatorIdx = 0
|
||||
for validator in walkDir(validatorsDir):
|
||||
if (validatorIdx < simulationDepositsCount div 2) == firstHalf:
|
||||
let
|
||||
currValidator = os.splitPath(validator.path).tail
|
||||
secretFile = secretsDir / currValidator
|
||||
secretRes = readAllChars(secretFile)
|
||||
|
||||
if secretRes.isErr:
|
||||
fatal "Failed to read secret file",
|
||||
path = secretFile, err = $secretRes.error
|
||||
quit 1
|
||||
|
||||
let
|
||||
dstSecretFile = dstSecretsDir / currValidator
|
||||
secretFileStatus = secureWriteFile(dstSecretFile, secretRes.get)
|
||||
|
||||
if secretFileStatus.isErr:
|
||||
fatal "Failed to write secret file",
|
||||
path = dstSecretFile, err = $secretFileStatus.error
|
||||
quit 1
|
||||
|
||||
let
|
||||
dstValidatorDir = dstDataDir / "validators" / currValidator
|
||||
validatorDirRes = secureCreatePath(dstValidatorDir)
|
||||
|
||||
if validatorDirRes.isErr:
|
||||
fatal "Failed to create validator dir",
|
||||
path = dstValidatorDir, err = $validatorDirRes.error
|
||||
quit 1
|
||||
|
||||
let
|
||||
keystoreFile = validatorsDir / currValidator / "keystore.json"
|
||||
readKeystoreRes = readAllChars(keystoreFile)
|
||||
|
||||
if readKeystoreRes.isErr:
|
||||
fatal "Failed to read keystore file",
|
||||
path = keystoreFile, err = $readKeystoreRes.error
|
||||
quit 1
|
||||
|
||||
let
|
||||
dstKeystore = dstValidatorDir / "keystore.json"
|
||||
writeKeystoreRes = secureWriteFile(dstKeystore, readKeystoreRes.get)
|
||||
|
||||
if writeKeystoreRes.isErr:
|
||||
fatal "Failed to write keystore file",
|
||||
path = dstKeystore, err = $writeKeystoreRes.error
|
||||
quit 1
|
||||
|
||||
inc validatorIdx
|
||||
|
||||
proc addPreTestRemoteKeystores(validatorsDir: string) =
|
||||
for item in oldPublicKeys:
|
||||
let key = ValidatorPubKey.fromHex(item).tryGet()
|
||||
let res = saveKeystore(validatorsDir, key, oldPublicKeysUrl)
|
||||
if res.isErr():
|
||||
fatal "Failed to create remote keystore file",
|
||||
validatorsDir = nodeValidatorsDir, key,
|
||||
err = res.error
|
||||
quit 1
|
||||
|
||||
proc startBeaconNode {.raises: [Defect, CatchableError].} =
|
||||
let rng = keys.newRng()
|
||||
|
||||
copyHalfValidators(nodeDataDir, true)
|
||||
addPreTestRemoteKeystores(nodeValidatorsDir)
|
||||
|
||||
let runNodeConf = try: BeaconNodeConf.load(cmdLine = mapIt([
|
||||
"--tcp-port=49000",
|
||||
"--udp-port=49000",
|
||||
"--network=" & dataDir,
|
||||
"--data-dir=" & dataDir,
|
||||
"--validators-dir=" & validatorsDir,
|
||||
"--secrets-dir=" & secretsDir,
|
||||
"--data-dir=" & nodeDataDir,
|
||||
"--validators-dir=" & nodeValidatorsDir,
|
||||
"--secrets-dir=" & nodeSecretsDir,
|
||||
"--metrics-address=127.0.0.1",
|
||||
"--metrics-port=48008",
|
||||
"--rest=true",
|
||||
"--rest-address=127.0.0.1",
|
||||
"--rest-port=" & $keymanagerPort,
|
||||
"--rest-port=" & $keymanagerPortBN,
|
||||
"--keymanager=true",
|
||||
"--keymanager-address=127.0.0.1",
|
||||
"--keymanager-port=" & $keymanagerPort,
|
||||
"--keymanager-port=" & $keymanagerPortBN,
|
||||
"--keymanager-token-file=" & tokenFilePath,
|
||||
"--suggested-fee-recipient=" & $defaultFeeRecipient,
|
||||
"--doppelganger-detection=off"], it))
|
||||
|
@ -186,9 +298,30 @@ proc startSingleNodeNetwork {.raises: [CatchableError, Defect].} =
|
|||
)
|
||||
|
||||
node.start() # This will run until the node is terminated by
|
||||
# setting its `bnStatus` to `Stopping`.
|
||||
# setting its `bnStatus` to `Stopping`.
|
||||
|
||||
os.removeDir dataDir
|
||||
# os.removeDir dataDir
|
||||
|
||||
proc startValidatorClient {.async, thread.} =
|
||||
let rng = keys.newRng()
|
||||
|
||||
copyHalfValidators(vcDataDir, false)
|
||||
addPreTestRemoteKeystores(vcValidatorsDir)
|
||||
|
||||
let runValidatorClientConf = try: ValidatorClientConf.load(cmdLine = mapIt([
|
||||
"--beacon-node=http://127.0.0.1:47000",
|
||||
"--data-dir=" & vcDataDir,
|
||||
"--validators-dir=" & vcValidatorsDir,
|
||||
"--secrets-dir=" & vcSecretsDir,
|
||||
"--suggested-fee-recipient=" & $defaultFeeRecipient,
|
||||
"--keymanager=true",
|
||||
"--keymanager-address=127.0.0.1",
|
||||
"--keymanager-port=" & $keymanagerPortVC,
|
||||
"--keymanager-token-file=" & tokenFilePath], TaintedString it))
|
||||
except:
|
||||
quit 1
|
||||
|
||||
await runValidatorClient(runValidatorClientConf, rng)
|
||||
|
||||
const
|
||||
password = "7465737470617373776f7264f09f9491"
|
||||
|
@ -248,19 +381,16 @@ proc `==`(a: seq[ValidatorPubKey],
|
|||
indices.add(index)
|
||||
true
|
||||
|
||||
proc runTests {.async.} =
|
||||
while bnStatus != BeaconNodeStatus.Running:
|
||||
await sleepAsync(1.seconds)
|
||||
|
||||
await sleepAsync(2.seconds)
|
||||
|
||||
proc runTests(keymanager: KeymanagerToTest) {.async.} =
|
||||
let
|
||||
client = RestClientRef.new(initTAddress("127.0.0.1", keymanagerPort))
|
||||
client = RestClientRef.new(initTAddress("127.0.0.1", keymanager.port))
|
||||
rng = keys.newRng()
|
||||
privateKey = ValidatorPrivKey.fromRaw(secretBytes).get
|
||||
|
||||
localList = listLocalValidators(validatorsDir, secretsDir)
|
||||
allValidators = listLocalValidators(
|
||||
keymanager.validatorsDir, keymanager.secretsDir)
|
||||
|
||||
let
|
||||
newKeystore = createKeystore(
|
||||
kdfPbkdf2, rng[], privateKey,
|
||||
KeystorePass.init password,
|
||||
|
@ -317,12 +447,21 @@ proc runTests {.async.} =
|
|||
let key = ValidatorPubKey.fromHex(item).tryGet()
|
||||
res.add(RemoteKeystoreInfo(pubkey: key, url: newPublicKeysUrl))
|
||||
# Adding non-remote keys which are already present in filesystem
|
||||
res.add(RemoteKeystoreInfo(pubkey: localList[0],
|
||||
res.add(RemoteKeystoreInfo(pubkey: allValidators[0],
|
||||
url: newPublicKeysUrl))
|
||||
res.add(RemoteKeystoreInfo(pubkey: localList[1],
|
||||
res.add(RemoteKeystoreInfo(pubkey: allValidators[1],
|
||||
url: newPublicKeysUrl))
|
||||
ImportRemoteKeystoresBody(remote_keys: res)
|
||||
|
||||
template expectedImportStatus(i: int): string =
|
||||
if i < 8:
|
||||
"duplicate"
|
||||
elif i == 16 or i == 17:
|
||||
"duplicate"
|
||||
else:
|
||||
"imported"
|
||||
|
||||
let
|
||||
deleteRemoteKeystoresBody1 =
|
||||
block:
|
||||
var res: seq[ValidatorPubKey]
|
||||
|
@ -354,12 +493,20 @@ proc runTests {.async.} =
|
|||
pubkeys: @[
|
||||
ValidatorPubKey.fromHex(oldPublicKeys[0]).tryGet(),
|
||||
ValidatorPubKey.fromHex(oldPublicKeys[1]).tryGet(),
|
||||
localList[0],
|
||||
localList[1]
|
||||
allValidators[0],
|
||||
allValidators[1]
|
||||
]
|
||||
)
|
||||
|
||||
suite "Serialization/deserialization " & preset():
|
||||
keymanagerKind =
|
||||
if keymanager.port == keymanagerPortBN:
|
||||
" [Beacon Node]"
|
||||
else:
|
||||
" [Validator Client]"
|
||||
|
||||
testFlavour = keymanagerKind & preset()
|
||||
|
||||
suite "Serialization/deserialization" & testFlavour:
|
||||
proc `==`(a, b: Kdf): bool =
|
||||
if (a.function != b.function) or (a.message != b.message):
|
||||
return false
|
||||
|
@ -398,7 +545,7 @@ proc runTests {.async.} =
|
|||
proc `==`(a, b: Keystore): bool =
|
||||
(a.crypto == b.crypto) and (a.pubkey == b.pubkey) and
|
||||
(string(a.path) == string(b.path)) and
|
||||
(a.description[] == b.description[]) and (a.uuid == b.uuid) and
|
||||
(a.description == b.description) and (a.uuid == b.uuid) and
|
||||
(a.version == b.version)
|
||||
|
||||
test "Deserialization test vectors":
|
||||
|
@ -464,7 +611,7 @@ proc runTests {.async.} =
|
|||
crypto: Crypto(kdf: kdf1, checksum: checksum1, cipher: cipher1),
|
||||
pubkey: ValidatorPubKey.fromHex("0xb4102a1f6c80e5c596a974ebd930c9f809c3587dc4d1d3634b77ff66db71e376dbc86c3252c6d140ce031f4ec6167798").get(),
|
||||
path: KeyPath("m/12381/60/0/0"),
|
||||
description: newClone("Test keystore"),
|
||||
description: some "Test keystore",
|
||||
uuid: "a3331c0c-a013-4754-a122-9988b3381fec",
|
||||
version: 4
|
||||
)
|
||||
|
@ -472,7 +619,7 @@ proc runTests {.async.} =
|
|||
crypto: Crypto(kdf: kdf1, checksum: checksum2, cipher: cipher2),
|
||||
pubkey: ValidatorPubKey.fromHex("0xa00d2954717425ce047e0928e5f4ec7c0e3bbe1058db511303fd659770ddace686ee2e22ac180422e516f4c503eb2228").get(),
|
||||
path: KeyPath("m/12381/60/0/0"),
|
||||
description: newClone("Test keystore"),
|
||||
description: some "Test keystore",
|
||||
uuid: "905dd873-48af-416a-8c80-4283d5af84f9",
|
||||
version: 4
|
||||
)
|
||||
|
@ -480,7 +627,7 @@ proc runTests {.async.} =
|
|||
crypto: Crypto(kdf: kdf2, checksum: checksum3, cipher: cipher3),
|
||||
pubkey: ValidatorPubKey.fromHex("0xb4102a1f6c80e5c596a974ebd930c9f809c3587dc4d1d3634b77ff66db71e376dbc86c3252c6d140ce031f4ec6167798").get(),
|
||||
path: KeyPath("m/12381/60/0/0"),
|
||||
description: newClone("Test keystore"),
|
||||
description: some "Test keystore",
|
||||
uuid: "ad1bf334-faaa-4257-8e28-81a45722e87b",
|
||||
version: 4
|
||||
)
|
||||
|
@ -488,7 +635,7 @@ proc runTests {.async.} =
|
|||
crypto: Crypto(kdf: kdf2, checksum: checksum4, cipher: cipher4),
|
||||
pubkey: ValidatorPubKey.fromHex("0xa00d2954717425ce047e0928e5f4ec7c0e3bbe1058db511303fd659770ddace686ee2e22ac180422e516f4c503eb2228").get(),
|
||||
path: KeyPath("m/12381/60/0/0"),
|
||||
description: newClone("Test keystore"),
|
||||
description: some "Test keystore",
|
||||
uuid: "d91bcde8-8bf5-45c6-b04d-c10d99ae9b6b",
|
||||
version: 4
|
||||
)
|
||||
|
@ -559,16 +706,16 @@ proc runTests {.async.} =
|
|||
d5.passwords == @["7465737470617373776f7264f09f9491",
|
||||
"7465737470617373776f7264f09f9491"]
|
||||
|
||||
suite "ListKeys requests" & preset():
|
||||
asyncTest "Correct token provided" & preset():
|
||||
suite "ListKeys requests" & testFlavour:
|
||||
asyncTest "Correct token provided" & testFlavour:
|
||||
let
|
||||
filesystemKeys = sorted(
|
||||
listLocalValidators(validatorsDir, secretsDir))
|
||||
apiKeystores = sorted((await client.listKeys(correctTokenValue)).data)
|
||||
filesystemKeys = sorted listLocalValidators(keymanager.validatorsDir,
|
||||
keymanager.secretsDir)
|
||||
apiKeys = sorted (await client.listKeys(correctTokenValue)).data
|
||||
|
||||
check filesystemKeys == apiKeystores
|
||||
check filesystemKeys == apiKeys
|
||||
|
||||
asyncTest "Missing Authorization header" & preset():
|
||||
asyncTest "Missing Authorization header" & testFlavour:
|
||||
let
|
||||
response = await client.listKeysPlain()
|
||||
responseJson = Json.decode(response.data, JsonNode)
|
||||
|
@ -577,7 +724,7 @@ proc runTests {.async.} =
|
|||
response.status == 401
|
||||
responseJson["message"].getStr() == InvalidAuthorizationError
|
||||
|
||||
asyncTest "Invalid Authorization Header" & preset():
|
||||
asyncTest "Invalid Authorization Header" & testFlavour:
|
||||
let
|
||||
response = await client.listKeysPlain(
|
||||
extraHeaders = @[("Authorization", "UnknownAuthScheme X")])
|
||||
|
@ -587,7 +734,7 @@ proc runTests {.async.} =
|
|||
response.status == 401
|
||||
responseJson["message"].getStr() == InvalidAuthorizationError
|
||||
|
||||
asyncTest "Invalid Authorization Token" & preset():
|
||||
asyncTest "Invalid Authorization Token" & testFlavour:
|
||||
let
|
||||
response = await client.listKeysPlain(
|
||||
extraHeaders = @[("Authorization", "Bearer InvalidToken")])
|
||||
|
@ -600,8 +747,8 @@ proc runTests {.async.} =
|
|||
expect RestError:
|
||||
let keystores = await client.listKeys("Invalid Token")
|
||||
|
||||
suite "ImportKeystores requests" & preset():
|
||||
asyncTest "ImportKeystores/ListKeystores/DeleteKeystores" & preset():
|
||||
suite "ImportKeystores requests" & testFlavour:
|
||||
asyncTest "ImportKeystores/ListKeystores/DeleteKeystores" & testFlavour:
|
||||
let
|
||||
response1 = await client.importKeystoresPlain(
|
||||
importKeystoresBody1,
|
||||
|
@ -616,7 +763,8 @@ proc runTests {.async.} =
|
|||
|
||||
let
|
||||
filesystemKeys1 = sorted(
|
||||
listLocalValidators(validatorsDir, secretsDir))
|
||||
listLocalValidators(keymanager.validatorsDir,
|
||||
keymanager.secretsDir))
|
||||
apiKeystores1 = sorted((await client.listKeys(correctTokenValue)).data)
|
||||
|
||||
check:
|
||||
|
@ -656,7 +804,8 @@ proc runTests {.async.} =
|
|||
|
||||
let
|
||||
filesystemKeys2 = sorted(
|
||||
listLocalValidators(validatorsDir, secretsDir))
|
||||
listLocalValidators(keymanager.validatorsDir,
|
||||
keymanager.secretsDir))
|
||||
apiKeystores2 = sorted((await client.listKeys(correctTokenValue)).data)
|
||||
|
||||
check:
|
||||
|
@ -670,7 +819,7 @@ proc runTests {.async.} =
|
|||
deleteKeysBody1.pubkeys[6] notin filesystemKeys2
|
||||
deleteKeysBody1.pubkeys[7] notin filesystemKeys2
|
||||
|
||||
asyncTest "Missing Authorization header" & preset():
|
||||
asyncTest "Missing Authorization header" & testFlavour:
|
||||
let
|
||||
response = await client.importKeystoresPlain(importKeystoresBody)
|
||||
responseJson = Json.decode(response.data, JsonNode)
|
||||
|
@ -679,7 +828,7 @@ proc runTests {.async.} =
|
|||
response.status == 401
|
||||
responseJson["message"].getStr() == InvalidAuthorizationError
|
||||
|
||||
asyncTest "Invalid Authorization Header" & preset():
|
||||
asyncTest "Invalid Authorization Header" & testFlavour:
|
||||
let
|
||||
response = await client.importKeystoresPlain(
|
||||
importKeystoresBody,
|
||||
|
@ -690,7 +839,7 @@ proc runTests {.async.} =
|
|||
response.status == 401
|
||||
responseJson["message"].getStr() == InvalidAuthorizationError
|
||||
|
||||
asyncTest "Invalid Authorization Token" & preset():
|
||||
asyncTest "Invalid Authorization Token" & testFlavour:
|
||||
let
|
||||
response = await client.importKeystoresPlain(
|
||||
importKeystoresBody,
|
||||
|
@ -701,8 +850,8 @@ proc runTests {.async.} =
|
|||
response.status == 403
|
||||
responseJson["message"].getStr() == InvalidAuthorizationError
|
||||
|
||||
suite "DeleteKeys requests" & preset():
|
||||
asyncTest "Deleting not existing key" & preset():
|
||||
suite "DeleteKeys requests" & testFlavour:
|
||||
asyncTest "Deleting not existing key" & testFlavour:
|
||||
let
|
||||
response = await client.deleteKeysPlain(
|
||||
deleteKeysBody,
|
||||
|
@ -714,7 +863,7 @@ proc runTests {.async.} =
|
|||
responseJson["data"][0]["status"].getStr() == "not_found"
|
||||
responseJson["data"][0]["message"].getStr() == ""
|
||||
|
||||
asyncTest "Missing Authorization header" & preset():
|
||||
asyncTest "Missing Authorization header" & testFlavour:
|
||||
let
|
||||
response = await client.deleteKeysPlain(deleteKeysBody)
|
||||
responseJson = Json.decode(response.data, JsonNode)
|
||||
|
@ -723,7 +872,7 @@ proc runTests {.async.} =
|
|||
response.status == 401
|
||||
responseJson["message"].getStr() == InvalidAuthorizationError
|
||||
|
||||
asyncTest "Invalid Authorization Header" & preset():
|
||||
asyncTest "Invalid Authorization Header" & testFlavour:
|
||||
let
|
||||
response = await client.deleteKeysPlain(
|
||||
deleteKeysBody,
|
||||
|
@ -734,7 +883,7 @@ proc runTests {.async.} =
|
|||
response.status == 401
|
||||
responseJson["message"].getStr() == InvalidAuthorizationError
|
||||
|
||||
asyncTest "Invalid Authorization Token" & preset():
|
||||
asyncTest "Invalid Authorization Token" & testFlavour:
|
||||
let
|
||||
response = await client.deleteKeysPlain(
|
||||
deleteKeysBody,
|
||||
|
@ -745,17 +894,18 @@ proc runTests {.async.} =
|
|||
response.status == 403
|
||||
responseJson["message"].getStr() == InvalidAuthorizationError
|
||||
|
||||
suite "ListRemoteKeys requests" & preset():
|
||||
asyncTest "Correct token provided" & preset():
|
||||
suite "ListRemoteKeys requests" & testFlavour:
|
||||
asyncTest "Correct token provided" & testFlavour:
|
||||
let
|
||||
filesystemKeys = sorted(
|
||||
listRemoteValidators(validatorsDir, secretsDir))
|
||||
listRemoteValidators(keymanager.validatorsDir,
|
||||
keymanager.secretsDir))
|
||||
apiKeystores = sorted((
|
||||
await client.listRemoteKeys(correctTokenValue)).data)
|
||||
|
||||
check filesystemKeys == apiKeystores
|
||||
|
||||
asyncTest "Missing Authorization header" & preset():
|
||||
asyncTest "Missing Authorization header" & testFlavour:
|
||||
let
|
||||
response = await client.listRemoteKeysPlain()
|
||||
responseJson = Json.decode(response.data, JsonNode)
|
||||
|
@ -764,7 +914,7 @@ proc runTests {.async.} =
|
|||
response.status == 401
|
||||
responseJson["message"].getStr() == InvalidAuthorizationError
|
||||
|
||||
asyncTest "Invalid Authorization Header" & preset():
|
||||
asyncTest "Invalid Authorization Header" & testFlavour:
|
||||
let
|
||||
response = await client.listRemoteKeysPlain(
|
||||
extraHeaders = @[("Authorization", "UnknownAuthScheme X")])
|
||||
|
@ -774,7 +924,7 @@ proc runTests {.async.} =
|
|||
response.status == 401
|
||||
responseJson["message"].getStr() == InvalidAuthorizationError
|
||||
|
||||
asyncTest "Invalid Authorization Token" & preset():
|
||||
asyncTest "Invalid Authorization Token" & testFlavour:
|
||||
let
|
||||
response = await client.listRemoteKeysPlain(
|
||||
extraHeaders = @[("Authorization", "Bearer InvalidToken")])
|
||||
|
@ -787,8 +937,8 @@ proc runTests {.async.} =
|
|||
expect RestError:
|
||||
let keystores = await client.listKeys("Invalid Token")
|
||||
|
||||
suite "Fee recipient management" & preset():
|
||||
asyncTest "Missing Authorization header" & preset():
|
||||
suite "Fee recipient management" & testFlavour:
|
||||
asyncTest "Missing Authorization header" & testFlavour:
|
||||
let pubkey = ValidatorPubKey.fromHex(oldPublicKeys[0]).expect("valid key")
|
||||
|
||||
block:
|
||||
|
@ -820,7 +970,7 @@ proc runTests {.async.} =
|
|||
response.status == 401
|
||||
responseJson["message"].getStr() == InvalidAuthorizationError
|
||||
|
||||
asyncTest "Invalid Authorization Header" & preset():
|
||||
asyncTest "Invalid Authorization Header" & testFlavour:
|
||||
let pubkey = ValidatorPubKey.fromHex(oldPublicKeys[0]).expect("valid key")
|
||||
|
||||
block:
|
||||
|
@ -859,7 +1009,7 @@ proc runTests {.async.} =
|
|||
response.status == 401
|
||||
responseJson["message"].getStr() == InvalidAuthorizationError
|
||||
|
||||
asyncTest "Invalid Authorization Token" & preset():
|
||||
asyncTest "Invalid Authorization Token" & testFlavour:
|
||||
let pubkey = ValidatorPubKey.fromHex(oldPublicKeys[0]).expect("valid key")
|
||||
|
||||
block:
|
||||
|
@ -897,7 +1047,7 @@ proc runTests {.async.} =
|
|||
response.status == 403
|
||||
responseJson["message"].getStr() == InvalidAuthorizationError
|
||||
|
||||
asyncTest "Obtaining the fee recpient of a missing validator returns 404" & preset():
|
||||
asyncTest "Obtaining the fee recpient of a missing validator returns 404" & testFlavour:
|
||||
let
|
||||
pubkey = ValidatorPubKey.fromHex(unusedPublicKeys[0]).expect("valid key")
|
||||
response = await client.listFeeRecipientPlain(
|
||||
|
@ -907,7 +1057,7 @@ proc runTests {.async.} =
|
|||
check:
|
||||
response.status == 404
|
||||
|
||||
asyncTest "Setting the fee recipient on a missing validator creates a record for it" & preset():
|
||||
asyncTest "Setting the fee recipient on a missing validator creates a record for it" & testFlavour:
|
||||
let
|
||||
pubkey = ValidatorPubKey.fromHex(unusedPublicKeys[1]).expect("valid key")
|
||||
feeRecipient = specifiedFeeRecipient(1)
|
||||
|
@ -918,7 +1068,7 @@ proc runTests {.async.} =
|
|||
check:
|
||||
resultFromApi == feeRecipient
|
||||
|
||||
asyncTest "Obtaining the fee recpient of an unconfigured validator returns the suggested default" & preset():
|
||||
asyncTest "Obtaining the fee recpient of an unconfigured validator returns the suggested default" & testFlavour:
|
||||
let
|
||||
pubkey = ValidatorPubKey.fromHex(oldPublicKeys[0]).expect("valid key")
|
||||
resultFromApi = await client.listFeeRecipient(pubkey, correctTokenValue)
|
||||
|
@ -926,7 +1076,7 @@ proc runTests {.async.} =
|
|||
check:
|
||||
resultFromApi == defaultFeeRecipient
|
||||
|
||||
asyncTest "Configuring the fee recpient" & preset():
|
||||
asyncTest "Configuring the fee recpient" & testFlavour:
|
||||
let
|
||||
pubkey = ValidatorPubKey.fromHex(oldPublicKeys[1]).expect("valid key")
|
||||
firstFeeRecipient = specifiedFeeRecipient(2)
|
||||
|
@ -949,8 +1099,8 @@ proc runTests {.async.} =
|
|||
check:
|
||||
finalResultFromApi == defaultFeeRecipient
|
||||
|
||||
suite "ImportRemoteKeys/ListRemoteKeys/DeleteRemoteKeys" & preset():
|
||||
asyncTest "Importing list of remote keys" & preset():
|
||||
suite "ImportRemoteKeys/ListRemoteKeys/DeleteRemoteKeys" & testFlavour:
|
||||
asyncTest "Importing list of remote keys" & testFlavour:
|
||||
let
|
||||
response1 = await client.importRemoteKeysPlain(
|
||||
importRemoteKeystoresBody,
|
||||
|
@ -959,18 +1109,16 @@ proc runTests {.async.} =
|
|||
|
||||
check:
|
||||
response1.status == 200
|
||||
for i in [0, 1, 2, 3, 4, 5, 6, 7, 16, 17]:
|
||||
|
||||
for i in 0 ..< 18:
|
||||
check:
|
||||
responseJson1["data"][i]["status"].getStr() == "duplicate"
|
||||
responseJson1["data"][i]["message"].getStr() == ""
|
||||
for i in 8 ..< 16:
|
||||
check:
|
||||
responseJson1["data"][i]["status"].getStr() == "imported"
|
||||
responseJson1["data"][i]["status"].getStr() == expectedImportStatus(i)
|
||||
responseJson1["data"][i]["message"].getStr() == ""
|
||||
|
||||
let
|
||||
filesystemKeys1 = sorted(
|
||||
listRemoteValidators(validatorsDir, secretsDir))
|
||||
listRemoteValidators(keymanager.validatorsDir,
|
||||
keymanager.secretsDir))
|
||||
apiKeystores1 = sorted((
|
||||
await client.listRemoteKeys(correctTokenValue)).data)
|
||||
|
||||
|
@ -1008,7 +1156,8 @@ proc runTests {.async.} =
|
|||
|
||||
let
|
||||
filesystemKeys2 = sorted(
|
||||
listRemoteValidators(validatorsDir, secretsDir))
|
||||
listRemoteValidators(keymanager.validatorsDir,
|
||||
keymanager.secretsDir))
|
||||
apiKeystores2 = sorted((
|
||||
await client.listRemoteKeys(correctTokenValue)).data)
|
||||
|
||||
|
@ -1020,7 +1169,7 @@ proc runTests {.async.} =
|
|||
check:
|
||||
key notin newPublicKeys
|
||||
|
||||
asyncTest "Missing Authorization header" & preset():
|
||||
asyncTest "Missing Authorization header" & testFlavour:
|
||||
let
|
||||
response = await client.importRemoteKeysPlain(importRemoteKeystoresBody)
|
||||
responseJson = Json.decode(response.data, JsonNode)
|
||||
|
@ -1029,7 +1178,7 @@ proc runTests {.async.} =
|
|||
response.status == 401
|
||||
responseJson["message"].getStr() == InvalidAuthorizationError
|
||||
|
||||
asyncTest "Invalid Authorization Header" & preset():
|
||||
asyncTest "Invalid Authorization Header" & testFlavour:
|
||||
let
|
||||
response = await client.importRemoteKeysPlain(
|
||||
importRemoteKeystoresBody,
|
||||
|
@ -1040,7 +1189,7 @@ proc runTests {.async.} =
|
|||
response.status == 401
|
||||
responseJson["message"].getStr() == InvalidAuthorizationError
|
||||
|
||||
asyncTest "Invalid Authorization Token" & preset():
|
||||
asyncTest "Invalid Authorization Token" & testFlavour:
|
||||
let
|
||||
response = await client.importRemoteKeysPlain(
|
||||
importRemoteKeystoresBody,
|
||||
|
@ -1051,8 +1200,8 @@ proc runTests {.async.} =
|
|||
response.status == 403
|
||||
responseJson["message"].getStr() == InvalidAuthorizationError
|
||||
|
||||
suite "DeleteRemoteKeys requests" & preset():
|
||||
asyncTest "Deleting not existing key" & preset():
|
||||
suite "DeleteRemoteKeys requests" & testFlavour:
|
||||
asyncTest "Deleting not existing key" & testFlavour:
|
||||
let
|
||||
response = await client.deleteRemoteKeysPlain(
|
||||
deleteRemoteKeystoresBody3,
|
||||
|
@ -1064,7 +1213,7 @@ proc runTests {.async.} =
|
|||
responseJson["data"][0]["status"].getStr() == "not_found"
|
||||
responseJson["data"][1]["status"].getStr() == "not_found"
|
||||
|
||||
asyncTest "Deleting existing local key and remote key" & preset():
|
||||
asyncTest "Deleting existing local key and remote key" & testFlavour:
|
||||
let
|
||||
response = await client.deleteRemoteKeysPlain(
|
||||
deleteRemoteKeystoresBody4,
|
||||
|
@ -1080,7 +1229,7 @@ proc runTests {.async.} =
|
|||
|
||||
let
|
||||
filesystemKeystores = sorted(
|
||||
listRemoteValidators(validatorsDir, secretsDir))
|
||||
listRemoteValidators(nodeValidatorsDir, nodeSecretsDir))
|
||||
apiKeystores = sorted((
|
||||
await client.listRemoteKeys(correctTokenValue)).data)
|
||||
|
||||
|
@ -1096,7 +1245,7 @@ proc runTests {.async.} =
|
|||
removedKey0 != item.pubkey
|
||||
removedKey1 != item.pubkey
|
||||
|
||||
asyncTest "Missing Authorization header" & preset():
|
||||
asyncTest "Missing Authorization header" & testFlavour:
|
||||
let
|
||||
response = await client.deleteRemoteKeysPlain(
|
||||
deleteRemoteKeystoresBody1)
|
||||
|
@ -1106,7 +1255,7 @@ proc runTests {.async.} =
|
|||
response.status == 401
|
||||
responseJson["message"].getStr() == InvalidAuthorizationError
|
||||
|
||||
asyncTest "Invalid Authorization Header" & preset():
|
||||
asyncTest "Invalid Authorization Header" & testFlavour:
|
||||
let
|
||||
response = await client.deleteRemoteKeysPlain(
|
||||
deleteRemoteKeystoresBody1,
|
||||
|
@ -1117,7 +1266,7 @@ proc runTests {.async.} =
|
|||
response.status == 401
|
||||
responseJson["message"].getStr() == InvalidAuthorizationError
|
||||
|
||||
asyncTest "Invalid Authorization Token" & preset():
|
||||
asyncTest "Invalid Authorization Token" & testFlavour:
|
||||
let
|
||||
response = await client.deleteRemoteKeysPlain(
|
||||
deleteRemoteKeystoresBody1,
|
||||
|
@ -1128,10 +1277,31 @@ proc runTests {.async.} =
|
|||
response.status == 403
|
||||
responseJson["message"].getStr() == InvalidAuthorizationError
|
||||
|
||||
proc delayedTests {.async.} =
|
||||
while bnStatus != BeaconNodeStatus.Running:
|
||||
await sleepAsync(1.seconds)
|
||||
|
||||
asyncSpawn startValidatorClient()
|
||||
|
||||
await sleepAsync(2.seconds)
|
||||
|
||||
let deadline = sleepAsync(10.minutes)
|
||||
await runTests(beaconNodeKeymanager) or deadline
|
||||
|
||||
# TODO
|
||||
# This tests showed flaky behavior on a single Windows CI host
|
||||
# Re-enable it in a follow-up PR
|
||||
# await runTests(validatorClientKeymanager)
|
||||
|
||||
bnStatus = BeaconNodeStatus.Stopping
|
||||
|
||||
proc main() {.async.} =
|
||||
asyncSpawn runTests()
|
||||
startSingleNodeNetwork()
|
||||
if dirExists(dataDir):
|
||||
os.removeDir dataDir
|
||||
|
||||
asyncSpawn delayedTests()
|
||||
|
||||
prepareNetwork()
|
||||
startBeaconNode()
|
||||
|
||||
waitFor main()
|
||||
|
|
|
@ -26,7 +26,8 @@ func isEqual(a, b: ValidatorPrivKey): bool =
|
|||
result = result and pa[i] == pb[i]
|
||||
|
||||
const
|
||||
scryptVector = """{
|
||||
scryptVector = """
|
||||
{
|
||||
"crypto": {
|
||||
"kdf": {
|
||||
"function": "scrypt",
|
||||
|
@ -57,9 +58,43 @@ const
|
|||
"path": "m/12381/60/3141592653/589793238",
|
||||
"uuid": "1d85ae20-35c5-4611-98e8-aa14a633906f",
|
||||
"version": 4
|
||||
}"""
|
||||
}"""
|
||||
|
||||
pbkdf2Vector = """{
|
||||
scryptVector2 = """
|
||||
{
|
||||
"crypto": {
|
||||
"kdf": {
|
||||
"function": "scrypt",
|
||||
"params": {
|
||||
"dklen": 32,
|
||||
"n": 262144,
|
||||
"p": 1,
|
||||
"r": 8,
|
||||
"salt": "d4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3"
|
||||
},
|
||||
"message": ""
|
||||
},
|
||||
"checksum": {
|
||||
"function": "sha256",
|
||||
"params": {},
|
||||
"message": "d2217fe5f3e9a1e34581ef8a78f7c9928e436d36dacc5e846690a5581e8ea484"
|
||||
},
|
||||
"cipher": {
|
||||
"function": "aes-128-ctr",
|
||||
"params": {
|
||||
"iv": "264daa3f303d7259501c93d997d84fe6"
|
||||
},
|
||||
"message": "06ae90d55fe0a6e9c5c3bc5b170827b2e5cce3929ed3f116c2811e6366dfe20f"
|
||||
}
|
||||
},
|
||||
"pubkey": "9612d7a727c9d0a22e185a1c768478dfe919cada9266988cb32359c11f2b7b27f4ae4040902382ae2910c15e2b420d07",
|
||||
"path": "m/12381/60/3141592653/589793238",
|
||||
"uuid": "1d85ae20-35c5-4611-98e8-aa14a633906f",
|
||||
"version": 4
|
||||
}"""
|
||||
|
||||
pbkdf2Vector = """
|
||||
{
|
||||
"crypto": {
|
||||
"kdf": {
|
||||
"function": "pbkdf2",
|
||||
|
@ -89,9 +124,42 @@ const
|
|||
"path": "m/12381/60/0/0",
|
||||
"uuid": "64625def-3331-4eea-ab6f-782f3ed16a83",
|
||||
"version": 4
|
||||
}"""
|
||||
}"""
|
||||
|
||||
pbkdf2NetVector = """{
|
||||
pbkdf2Vector2 = """
|
||||
{
|
||||
"crypto": {
|
||||
"kdf": {
|
||||
"function": "pbkdf2",
|
||||
"params": {
|
||||
"dklen": 32,
|
||||
"c": 262144,
|
||||
"prf": "hmac-sha256",
|
||||
"salt": "d4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3"
|
||||
},
|
||||
"message": ""
|
||||
},
|
||||
"checksum": {
|
||||
"function": "sha256",
|
||||
"params": {},
|
||||
"message": "8a9f5d9912ed7e75ea794bc5a89bca5f193721d30868ade6f73043c6ea6febf1"
|
||||
},
|
||||
"cipher": {
|
||||
"function": "aes-128-ctr",
|
||||
"params": {
|
||||
"iv": "264daa3f303d7259501c93d997d84fe6"
|
||||
},
|
||||
"message": "cee03fde2af33149775b7223e7845e4fb2c8ae1792e5f99fe9ecf474cc8c16ad"
|
||||
}
|
||||
},
|
||||
"pubkey": "9612d7a727c9d0a22e185a1c768478dfe919cada9266988cb32359c11f2b7b27f4ae4040902382ae2910c15e2b420d07",
|
||||
"path": "m/12381/60/0/0",
|
||||
"uuid": "64625def-3331-4eea-ab6f-782f3ed16a83",
|
||||
"version": 4
|
||||
}"""
|
||||
|
||||
pbkdf2NetVector = """
|
||||
{
|
||||
"crypto":{
|
||||
"kdf":{
|
||||
"function":"pbkdf2",
|
||||
|
@ -122,9 +190,10 @@ const
|
|||
"pubkey":"08021221031873e6f4e1bf837b93493d570653cb219743d4fab0ff468d4e005e1679730b0b",
|
||||
"uuid":"7a053160-1cdf-4faf-a2bb-331e1bc2eb5f",
|
||||
"version":1
|
||||
}"""
|
||||
}"""
|
||||
|
||||
scryptNetVector = """{
|
||||
scryptNetVector = """
|
||||
{
|
||||
"crypto":{
|
||||
"kdf":{
|
||||
"function":"scrypt",
|
||||
|
@ -156,7 +225,42 @@ const
|
|||
"pubkey":"08021221031873e6f4e1bf837b93493d570653cb219743d4fab0ff468d4e005e1679730b0b",
|
||||
"uuid":"83d77fa3-86cb-466a-af11-eeb338b0e258",
|
||||
"version":1
|
||||
}"""
|
||||
}"""
|
||||
|
||||
prysmKeystore = """
|
||||
{
|
||||
"crypto": {
|
||||
"checksum": {
|
||||
"function": "sha256",
|
||||
"message": "54fc80f6d0676bdae7c968e0d462f90a4e3a028fc7669ef8527e2f74386c9b36",
|
||||
"params": {}
|
||||
},
|
||||
"cipher": {
|
||||
"function": "aes-128-ctr",
|
||||
"message": "3c2540f69cbe7e66c0c4a6e416e99bf0d1056399c21b4c45552561da920871fa",
|
||||
"params": {
|
||||
"iv": "98a15bd46d258aceecaeeab25bddf5e2"
|
||||
}
|
||||
},
|
||||
"kdf": {
|
||||
"function": "pbkdf2",
|
||||
"message": "",
|
||||
"params": {
|
||||
"c": 262144,
|
||||
"dklen": 32,
|
||||
"prf": "hmac-sha256",
|
||||
"salt": "c0abbbbda36e588824865a71b5b34d5a95335fe1077c286d4e9c844f7193c62b"
|
||||
}
|
||||
}
|
||||
},
|
||||
"uuid": "39796eb1-2e43-4353-9f13-5211c7ddc58c",
|
||||
"pubkey": "8ed78a5495b54d5b6cc8bf170534ecb633b9694fba121ca680744fa9633f1b67cc77c045f88a6f97be781fe6c2867646",
|
||||
"version": 4,
|
||||
"name": "keystore",
|
||||
"path": ""
|
||||
}
|
||||
"""
|
||||
|
||||
password = string.fromBytes hexToSeqByte("7465737470617373776f7264f09f9491")
|
||||
secretBytes = hexToSeqByte "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f"
|
||||
secretNetBytes = hexToSeqByte "08021220fe442379443d6e2d7d75d3a58f96fbb35f0a9c7217796825fc9040e3b89c5736"
|
||||
|
@ -171,9 +275,21 @@ suite "KeyStorage testing suite":
|
|||
let secret = ValidatorPrivKey.fromRaw(secretBytes).get
|
||||
let nsecret = init(lcrypto.PrivateKey, secretNetBytes).get
|
||||
|
||||
test "Load Prysm keystore":
|
||||
let keystore = parseKeystore(prysmKeystore)
|
||||
check keystore.uuid == "39796eb1-2e43-4353-9f13-5211c7ddc58c"
|
||||
|
||||
test "[PBKDF2] Keystore decryption":
|
||||
let
|
||||
keystore = Json.decode(pbkdf2Vector, Keystore)
|
||||
keystore = parseKeystore(pbkdf2Vector)
|
||||
decrypt = decryptKeystore(keystore, KeystorePass.init password)
|
||||
|
||||
check decrypt.isOk
|
||||
check secret.isEqual(decrypt.get())
|
||||
|
||||
test "[PBKDF2] Keystore decryption (requireAllFields, allowUnknownFields)":
|
||||
let
|
||||
keystore = parseKeystore(pbkdf2Vector2)
|
||||
decrypt = decryptKeystore(keystore, KeystorePass.init password)
|
||||
|
||||
check decrypt.isOk
|
||||
|
@ -181,7 +297,15 @@ suite "KeyStorage testing suite":
|
|||
|
||||
test "[SCRYPT] Keystore decryption":
|
||||
let
|
||||
keystore = Json.decode(scryptVector, Keystore)
|
||||
keystore = parseKeystore(scryptVector)
|
||||
decrypt = decryptKeystore(keystore, KeystorePass.init password)
|
||||
|
||||
check decrypt.isOk
|
||||
check secret.isEqual(decrypt.get())
|
||||
|
||||
test "[SCRYPT] Keystore decryption (requireAllFields, allowUnknownFields)":
|
||||
let
|
||||
keystore = parseKeystore(pbkdf2Vector2)
|
||||
decrypt = decryptKeystore(keystore, KeystorePass.init password)
|
||||
|
||||
check decrypt.isOk
|
||||
|
@ -189,7 +313,7 @@ suite "KeyStorage testing suite":
|
|||
|
||||
test "[PBKDF2] Network Keystore decryption":
|
||||
let
|
||||
keystore = Json.decode(pbkdf2NetVector, NetKeystore)
|
||||
keystore = parseNetKeystore(pbkdf2NetVector)
|
||||
decrypt = decryptNetKeystore(keystore, KeystorePass.init password)
|
||||
|
||||
check decrypt.isOk
|
||||
|
@ -197,7 +321,7 @@ suite "KeyStorage testing suite":
|
|||
|
||||
test "[SCRYPT] Network Keystore decryption":
|
||||
let
|
||||
keystore = Json.decode(scryptNetVector, NetKeystore)
|
||||
keystore = parseNetKeystore(scryptNetVector)
|
||||
decrypt = decryptNetKeystore(keystore, KeystorePass.init password)
|
||||
|
||||
check decrypt.isOk
|
||||
|
|
|
@ -22,7 +22,7 @@ suite "Remove keystore testing suite":
|
|||
"remote": "http://127.0.0.1:6000",
|
||||
"type": "web3signer"
|
||||
}"""
|
||||
let keystore = Json.decode(remoteKeyStores, RemoteKeystore)
|
||||
let keystore = parseRemoteKeystore(remoteKeyStores)
|
||||
check keystore.pubkey.toHex == "8b9c875fbe539c6429c4fc304675062579ce47fb6b2ac6b6a1ba1188ca123a80affbfe381dbbc8e7f2437709a4c3325c"
|
||||
check keystore.remotes.len == 1
|
||||
check $keystore.remotes[0].url == "http://127.0.0.1:6000"
|
||||
|
@ -41,7 +41,7 @@ suite "Remove keystore testing suite":
|
|||
],
|
||||
"type": "web3signer"
|
||||
}"""
|
||||
let keystore = Json.decode(remoteKeyStores, RemoteKeystore)
|
||||
let keystore = parseRemoteKeystore(remoteKeyStores)
|
||||
check keystore.pubkey.toHex == "8b9c875fbe539c6429c4fc304675062579ce47fb6b2ac6b6a1ba1188ca123a80affbfe381dbbc8e7f2437709a4c3325c"
|
||||
check keystore.remotes.len == 1
|
||||
check $keystore.remotes[0].url == "http://127.0.0.1:6000"
|
||||
|
|
Loading…
Reference in New Issue