mirror of
https://github.com/status-im/nimbus-eth2.git
synced 2025-02-02 09:46:26 +00:00
f70ff38b53
Some upstream repos still need fixes, but this gets us close enough that style hints can be enabled by default. In general, "canonical" spellings are preferred even if they violate nep-1 - this applies in particular to spec-related stuff like `genesis_validators_root` which appears throughout the codebase.
785 lines
30 KiB
Nim
785 lines
30 KiB
Nim
# beacon_chain
|
|
# Copyright (c) 2021-2022 Status Research & Development GmbH
|
|
# Licensed and distributed under either of
|
|
# * 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.
|
|
|
|
{.used.}
|
|
|
|
import
|
|
std/[typetraits, os, options, json, sequtils, uri, algorithm],
|
|
testutils/unittests, chronicles, stint, json_serialization, confutils,
|
|
chronos, eth/keys, blscurve, libp2p/crypto/crypto as lcrypto,
|
|
stew/[byteutils, io2], stew/shims/net, nimcrypto/utils,
|
|
|
|
../beacon_chain/spec/[crypto, keystore, eth2_merkleization],
|
|
../beacon_chain/spec/datatypes/base,
|
|
../beacon_chain/spec/eth2_apis/[rest_keymanager_calls, rest_keymanager_types],
|
|
../beacon_chain/validators/[keystore_management, slashing_protection_common],
|
|
../beacon_chain/networking/network_metadata,
|
|
../beacon_chain/rpc/rest_key_management_api,
|
|
../beacon_chain/[conf, filepath, beacon_node,
|
|
nimbus_beacon_node, beacon_node_status],
|
|
|
|
./testutil
|
|
|
|
const
|
|
simulationDepositsCount = 128
|
|
dataDir = "./test_keymanager_api"
|
|
validatorsDir = dataDir / "validators"
|
|
secretsDir = dataDir / "secrets"
|
|
depositsFile = dataDir / "deposits.json"
|
|
genesisFile = dataDir / "genesis.ssz"
|
|
bootstrapEnrFile = dataDir / "bootstrap_node.enr"
|
|
tokenFilePath = dataDir / "keymanager-token.txt"
|
|
keymanagerPort = 47000
|
|
correctTokenValue = "some secret token"
|
|
newPrivateKeys = [
|
|
"0x598c9b81749ba7bb8eb37781027359e3ffe87d0e1579e21c453ce22af0c05e35",
|
|
"0x14e4470a1d8913ec0602048af78addf0fd7a37f591dd3feda828d10a10c0f6ff",
|
|
"0x3b4498c4e26f83702ceeed5e32600ecb3e71f08fc4561215d0f0ced13bf5dbdf",
|
|
"0x3cae8cf27c7e12549486f5613974661285d40596907a7fc39bac7c55a56660ab",
|
|
"0x71fd9bb8eadcf64df9cc8e716652709492c16518f73f87c770a54fe8c80ac5ae",
|
|
"0x4be74b7b0b0058dea2d4744e0069486500770f68296ac9b9bbd26df6749ed0ca",
|
|
"0x10052305a5fda7805fb1e762fe6cbc47e43c5a54f34f008fa79c48fee1749db7",
|
|
"0x3630f086fb9f1136fe077751031a16630e43d65ff64bb9fd3708adff81df5926"
|
|
]
|
|
oldPublicKeys = [
|
|
"0x94effccb0514f0f110a9680827e4f3769e53349e3b1c177e8c4f38b0e52e7842a4990212fe2edd2ce48b9b0bd02f3b04",
|
|
"0x950bcb136ef15e737cd28cc8ba94a5584e30cf6cfa4f3d16215acbe46917633c09630208f379898a898b29bd59b2bd34",
|
|
"0xaa96fddc809e0678b192cebd3a64873a339c7352eafaa88ab13bac84244e19b9afe2de8282320f5e0e7c155573f80ac3",
|
|
"0xa0f1da63e35c7a159fc2f187d300cad9ef5f5e73e55f78c391e7bc2c2feabc2d9d63dfe99edd7058ad0ab9d7f14a1e1a",
|
|
"0x9315ea03755881989b0d34e9594520d2ebca4d2f0fd955dafe42948a91840a2e812d1d61f26684c603a60c99e3537151",
|
|
"0x88c9737238fa23ed8e485e17349c523fe3fe848eab173959d34e7f7f2c731fb896ab7c0b0877a40782a5cd529dc7b080",
|
|
"0x995e1d9d9d467ca25b981a7ca0880e932ac418e5ebed9a834f3ead3fbec267986e28eb0243c562ae3b1995a600c1495c",
|
|
"0x945ab594e8c9cf3d6251b86fddf6fbf970c1835cd14113098554f135a6c2cf7f21d2f7a08ae33726785a59ae4910fa51",
|
|
]
|
|
oldPublicKeysUrl = HttpHostUri(parseUri("http://127.0.0.1/local"))
|
|
newPublicKeys = [
|
|
"0x80eadf027ad564a2f004616fa58f3add9caa700b20e9bf7e0b101be61406feb79f5e28ec8a5bb2a0689cc7b4c807afba",
|
|
"0x8c6585f39fd3d2ed950ba4958f0050ec68e4e7e3200147687fa101bcf98977ebe144b03edc45906faae144549f11d8b9",
|
|
"0xb3939c9ecfb3679de8aa7f81e8dfb9eaa51e958d165e8b963aa88767217ce03316e4bad74e7a475ed6009365d297e0cd",
|
|
"0xb093029010dd400f49350db77b13e70c3d75f5286c2cc5d7f1d0865e251cc547764de85371583eba2b1810cf36a4feb1",
|
|
"0x8893a6f03de181cc93537ebb89ed242f65f3722fe22cd7aaab71a4149a792b231e23e1575c12efb0d2934e6d7b755431",
|
|
"0x88c475e022971f0698b50aa2c9dd91df8b1c9f1079cbe7b2243bb5dee3a5cb5c46e170f90165efecdc794e14ae5b8fd9",
|
|
"0xa782e5161ba8e9ac135b0db3203a8c23aa61e19be6b9c198393d8b2b902bad8139863d9cf26bc2cbdc3b747bafc64606",
|
|
"0xb33f17216dda29dba1a9257e75b3dd8446c9ea217b563c20950c43f64300f7bd3d5f0dfa02274cab988e594552b7189e"
|
|
]
|
|
newPublicKeysUrl = HttpHostUri(parseUri("http://127.0.0.1/remote"))
|
|
|
|
proc contains*(keylist: openArray[KeystoreInfo], key: ValidatorPubKey): bool =
|
|
for item in keylist:
|
|
if item.validating_pubkey == key:
|
|
return true
|
|
false
|
|
|
|
proc contains*(keylist: openArray[KeystoreInfo], key: string): bool =
|
|
let pubkey = ValidatorPubKey.fromHex(key).tryGet()
|
|
contains(keylist, pubkey)
|
|
|
|
proc startSingleNodeNetwork {.raises: [CatchableError, Defect].} =
|
|
let
|
|
rng = keys.newRng()
|
|
mnemonic = generateMnemonic(rng[])
|
|
seed = getSeed(mnemonic, KeystorePass.init "")
|
|
cfg = defaultRuntimeConfig
|
|
|
|
let vres = secureCreatePath(validatorsDir)
|
|
if vres.isErr():
|
|
warn "Could not create validators folder",
|
|
path = validatorsDir, err = ioErrorMsg(vres.error)
|
|
|
|
let sres = secureCreatePath(secretsDir)
|
|
if sres.isErr():
|
|
warn "Could not create secrets folder",
|
|
path = secretsDir, err = ioErrorMsg(sres.error)
|
|
|
|
let deposits = generateDeposits(
|
|
cfg,
|
|
rng[],
|
|
seed,
|
|
0, simulationDepositsCount,
|
|
validatorsDir,
|
|
secretsDir,
|
|
KeystoreMode.Fast)
|
|
|
|
if deposits.isErr:
|
|
fatal "Failed to generate deposits", err = deposits.error
|
|
quit 1
|
|
|
|
let launchPadDeposits =
|
|
mapIt(deposits.value, LaunchPadDeposit.init(cfg, it))
|
|
|
|
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",
|
|
"--total-validators=" & $simulationDepositsCount,
|
|
"--deposits-file=" & depositsFile,
|
|
"--output-genesis=" & genesisFile,
|
|
"--output-bootstrap-file=" & bootstrapEnrFile,
|
|
"--netkey-file=network_key.json",
|
|
"--insecure-netkey-password=true",
|
|
"--genesis-offset=0"], it))
|
|
except Exception as exc: # TODO Fix confutils exceptions
|
|
raiseAssert exc.msg
|
|
|
|
doCreateTestnet(createTestnetConf, rng[])
|
|
|
|
let runNodeConf = try: BeaconNodeConf.load(cmdLine = mapIt([
|
|
"--tcp-port=49000",
|
|
"--udp-port=49000",
|
|
"--network=" & dataDir,
|
|
"--data-dir=" & dataDir,
|
|
"--validators-dir=" & validatorsDir,
|
|
"--secrets-dir=" & secretsDir,
|
|
"--metrics-address=127.0.0.1",
|
|
"--metrics-port=48008",
|
|
"--rest-address=127.0.0.1",
|
|
"--rest-port=" & $keymanagerPort,
|
|
"--keymanager=true",
|
|
"--keymanager-address=127.0.0.1",
|
|
"--keymanager-port=" & $keymanagerPort,
|
|
"--keymanager-token-file=" & tokenFilePath,
|
|
"--serve-light-client-data=off",
|
|
"--import-light-client-data=none",
|
|
"--doppelganger-detection=off"], it))
|
|
except Exception as exc: # TODO fix confutils exceptions
|
|
raiseAssert exc.msg
|
|
|
|
let metadata = loadEth2NetworkMetadata(dataDir)
|
|
|
|
let node = BeaconNode.init(
|
|
metadata.cfg,
|
|
rng,
|
|
runNodeConf,
|
|
metadata.depositContractDeployedAt,
|
|
metadata.eth1Network,
|
|
metadata.genesisData,
|
|
metadata.genesisDepositsSnapshot
|
|
)
|
|
|
|
node.start() # This will run until the node is terminated by
|
|
# setting its `bnStatus` to `Stopping`.
|
|
|
|
os.removeDir dataDir
|
|
|
|
const
|
|
password = "7465737470617373776f7264f09f9491"
|
|
# This is taken from the offical test vectors in test_keystores.nim
|
|
secretBytes = hexToSeqByte "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f"
|
|
salt = hexToSeqByte "d4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3"
|
|
iv = hexToSeqByte "264daa3f303d7259501c93d997d84fe6"
|
|
secretNetBytes = hexToSeqByte "08021220fe442379443d6e2d7d75d3a58f96fbb35f0a9c7217796825fc9040e3b89c5736"
|
|
|
|
proc listLocalValidators(validatorsDir,
|
|
secretsDir: string): seq[KeystoreInfo] {.
|
|
raises: [Defect].} =
|
|
var validators: seq[KeystoreInfo]
|
|
|
|
try:
|
|
for el in listLoadableKeystores(validatorsDir, secretsDir, true,
|
|
{KeystoreKind.Local}):
|
|
validators.add KeystoreInfo(validating_pubkey: el.pubkey,
|
|
derivation_path: el.path.string,
|
|
readonly: false)
|
|
except OSError as err:
|
|
error "Failure to list the validator directories",
|
|
validatorsDir, secretsDir, err = err.msg
|
|
|
|
validators
|
|
|
|
proc listRemoteValidators(validatorsDir,
|
|
secretsDir: string): seq[RemoteKeystoreInfo] {.
|
|
raises: [Defect].} =
|
|
var validators: seq[RemoteKeystoreInfo]
|
|
|
|
try:
|
|
for el in listLoadableKeystores(validatorsDir, secretsDir, true,
|
|
{KeystoreKind.Remote}):
|
|
validators.add RemoteKeystoreInfo(pubkey: el.pubkey,
|
|
url: el.remoteUrl)
|
|
|
|
except OSError as err:
|
|
error "Failure to list the validator directories",
|
|
validatorsDir, secretsDir, err = err.msg
|
|
|
|
validators
|
|
|
|
proc runTests {.async.} =
|
|
while bnStatus != BeaconNodeStatus.Running:
|
|
await sleepAsync(1.seconds)
|
|
|
|
await sleepAsync(2.seconds)
|
|
|
|
let
|
|
client = RestClientRef.new(initTAddress("127.0.0.1", keymanagerPort))
|
|
rng = keys.newRng()
|
|
privateKey = ValidatorPrivKey.fromRaw(secretBytes).get
|
|
|
|
localList = listLocalValidators(validatorsDir, secretsDir)
|
|
|
|
newKeystore = createKeystore(
|
|
kdfPbkdf2, rng[], privateKey,
|
|
KeystorePass.init password,
|
|
salt = salt, iv = iv,
|
|
description = "This is a test keystore that uses PBKDF2 to secure the secret",
|
|
path = validateKeyPath("m/12381/60/0/0").expect("Valid Keypath"))
|
|
|
|
importKeystoresBody1 =
|
|
block:
|
|
var
|
|
res1: seq[Keystore]
|
|
res2: seq[string]
|
|
for key in newPrivateKeys:
|
|
let privateKey = ValidatorPrivKey.fromHex(key).tryGet()
|
|
let store = createKeystore(kdfPbkdf2, rng[], privateKey,
|
|
KeystorePass.init password, salt = salt, iv = iv,
|
|
description = "Test keystore",
|
|
path = validateKeyPath("m/12381/60/0/0").expect("Valid Keypath"))
|
|
res1.add(store)
|
|
res2.add(password)
|
|
KeystoresAndSlashingProtection(
|
|
keystores: res1,
|
|
passwords: res2,
|
|
)
|
|
|
|
deleteKeysBody1 =
|
|
block:
|
|
var res: seq[ValidatorPubKey]
|
|
for item in newPrivateKeys:
|
|
let privateKey = ValidatorPrivKey.fromHex(item).tryGet()
|
|
let publicKey = privateKey.toPubKey().toPubKey()
|
|
res.add(publicKey)
|
|
DeleteKeystoresBody(
|
|
pubkeys: res
|
|
)
|
|
|
|
importKeystoresBody = KeystoresAndSlashingProtection(
|
|
keystores: @[newKeystore],
|
|
passwords: @[password],
|
|
)
|
|
|
|
deleteKeysBody = DeleteKeystoresBody(
|
|
pubkeys: @[privateKey.toPubKey.toPubKey])
|
|
|
|
importRemoteKeystoresBody =
|
|
block:
|
|
var res: seq[RemoteKeystoreInfo]
|
|
# Adding keys which are already present in filesystem
|
|
for item in oldPublicKeys:
|
|
let key = ValidatorPubKey.fromHex(item).tryGet()
|
|
res.add(RemoteKeystoreInfo(pubkey: key, url: newPublicKeysUrl))
|
|
# Adding keys which are new
|
|
for item in newPublicKeys:
|
|
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].validating_pubkey,
|
|
url: newPublicKeysUrl))
|
|
res.add(RemoteKeystoreInfo(pubkey: localList[1].validating_pubkey,
|
|
url: newPublicKeysUrl))
|
|
ImportRemoteKeystoresBody(remote_keys: res)
|
|
|
|
deleteRemoteKeystoresBody1 =
|
|
block:
|
|
var res: seq[ValidatorPubKey]
|
|
for item in oldPublicKeys:
|
|
let key = ValidatorPubKey.fromHex(item).tryGet()
|
|
res.add(key)
|
|
DeleteKeystoresBody(pubkeys: res)
|
|
|
|
deleteRemoteKeystoresBody2 =
|
|
block:
|
|
var res: seq[ValidatorPubKey]
|
|
for item in newPublicKeys:
|
|
let key = ValidatorPubKey.fromHex(item).tryGet()
|
|
res.add(key)
|
|
DeleteKeystoresBody(pubkeys: res)
|
|
|
|
deleteRemoteKeystoresBody3 =
|
|
block:
|
|
DeleteKeystoresBody(
|
|
pubkeys: @[
|
|
ValidatorPubKey.fromHex(newPublicKeys[0]).tryGet(),
|
|
ValidatorPubKey.fromHex(newPublicKeys[1]).tryGet()
|
|
]
|
|
)
|
|
|
|
deleteRemoteKeystoresBody4 =
|
|
block:
|
|
DeleteKeystoresBody(
|
|
pubkeys: @[
|
|
ValidatorPubKey.fromHex(oldPublicKeys[0]).tryGet(),
|
|
ValidatorPubKey.fromHex(oldPublicKeys[1]).tryGet(),
|
|
localList[0].validating_pubkey,
|
|
localList[1].validating_pubkey
|
|
]
|
|
)
|
|
|
|
suite "ListKeys requests" & preset():
|
|
asyncTest "Correct token provided" & preset():
|
|
let
|
|
filesystemKeystores = sorted(
|
|
listLocalValidators(validatorsDir, secretsDir))
|
|
apiKeystores = sorted((await client.listKeys(correctTokenValue)).data)
|
|
|
|
check filesystemKeystores == apiKeystores
|
|
|
|
asyncTest "Missing Authorization header" & preset():
|
|
let
|
|
response = await client.listKeysPlain()
|
|
responseJson = Json.decode(response.data, JsonNode)
|
|
|
|
check:
|
|
response.status == 401
|
|
responseJson["code"].getStr() == "401"
|
|
responseJson["message"].getStr() == InvalidAuthorization
|
|
responseJson["stacktraces"][0].getStr() == $noAuthorizationHeader
|
|
|
|
asyncTest "Invalid Authorization Header" & preset():
|
|
let
|
|
response = await client.listKeysPlain(
|
|
extraHeaders = @[("Authorization", "UnknownAuthScheme X")])
|
|
responseJson = Json.decode(response.data, JsonNode)
|
|
|
|
check:
|
|
response.status == 401
|
|
responseJson["code"].getStr() == "401"
|
|
responseJson["message"].getStr() == InvalidAuthorization
|
|
responseJson["stacktraces"][0].getStr() == $missingBearerScheme
|
|
|
|
asyncTest "Invalid Authorization Token" & preset():
|
|
let
|
|
response = await client.listKeysPlain(
|
|
extraHeaders = @[("Authorization", "Bearer InvalidToken")])
|
|
responseJson = Json.decode(response.data, JsonNode)
|
|
|
|
check:
|
|
response.status == 401
|
|
responseJson["code"].getStr() == "401"
|
|
responseJson["message"].getStr() == InvalidAuthorization
|
|
responseJson["stacktraces"][0].getStr() == $incorrectToken
|
|
|
|
expect RestError:
|
|
let keystores = await client.listKeys("Invalid Token")
|
|
|
|
suite "ImportKeystores requests" & preset():
|
|
asyncTest "ImportKeystores/ListKeystores/DeleteKeystores" & preset():
|
|
let
|
|
response1 = await client.importKeystoresPlain(
|
|
importKeystoresBody1,
|
|
extraHeaders = @[("Authorization", "Bearer " & correctTokenValue)])
|
|
responseJson1 = Json.decode(response1.data, JsonNode)
|
|
|
|
check response1.status == 200
|
|
for i in 0 ..< 8:
|
|
check:
|
|
responseJson1["data"][i]["status"].getStr() == "imported"
|
|
responseJson1["data"][i]["message"].getStr() == ""
|
|
|
|
let
|
|
filesystemKeystores1 = sorted(
|
|
listLocalValidators(validatorsDir, secretsDir))
|
|
apiKeystores1 = sorted((await client.listKeys(correctTokenValue)).data)
|
|
|
|
check:
|
|
filesystemKeystores1 == apiKeystores1
|
|
importKeystoresBody1.keystores[0].pubkey in filesystemKeystores1
|
|
importKeystoresBody1.keystores[1].pubkey in filesystemKeystores1
|
|
importKeystoresBody1.keystores[2].pubkey in filesystemKeystores1
|
|
importKeystoresBody1.keystores[3].pubkey in filesystemKeystores1
|
|
importKeystoresBody1.keystores[4].pubkey in filesystemKeystores1
|
|
importKeystoresBody1.keystores[5].pubkey in filesystemKeystores1
|
|
importKeystoresBody1.keystores[6].pubkey in filesystemKeystores1
|
|
importKeystoresBody1.keystores[7].pubkey in filesystemKeystores1
|
|
|
|
let
|
|
response2 = await client.importKeystoresPlain(
|
|
importKeystoresBody1,
|
|
extraHeaders = @[("Authorization", "Bearer " & correctTokenValue)])
|
|
responseJson2 = Json.decode(response2.data, JsonNode)
|
|
|
|
check response2.status == 200
|
|
for i in 0 ..< 8:
|
|
check:
|
|
responseJson2["data"][i]["status"].getStr() == "duplicate"
|
|
responseJson2["data"][i]["message"].getStr() == ""
|
|
|
|
let
|
|
response3 = await client.deleteKeysPlain(
|
|
deleteKeysBody1,
|
|
extraHeaders = @[("Authorization", "Bearer " & correctTokenValue)])
|
|
responseJson3 = Json.decode(response3.data, JsonNode)
|
|
|
|
check response3.status == 200
|
|
for i in 0 ..< 8:
|
|
check:
|
|
responseJson3["data"][i]["status"].getStr() == "deleted"
|
|
responseJson3["data"][i]["message"].getStr() == ""
|
|
|
|
let
|
|
filesystemKeystores2 = sorted(
|
|
listLocalValidators(validatorsDir, secretsDir))
|
|
apiKeystores2 = sorted((await client.listKeys(correctTokenValue)).data)
|
|
|
|
check:
|
|
filesystemKeystores2 == apiKeystores2
|
|
deleteKeysBody1.pubkeys[0] notin filesystemKeystores2
|
|
deleteKeysBody1.pubkeys[1] notin filesystemKeystores2
|
|
deleteKeysBody1.pubkeys[2] notin filesystemKeystores2
|
|
deleteKeysBody1.pubkeys[3] notin filesystemKeystores2
|
|
deleteKeysBody1.pubkeys[4] notin filesystemKeystores2
|
|
deleteKeysBody1.pubkeys[5] notin filesystemKeystores2
|
|
deleteKeysBody1.pubkeys[6] notin filesystemKeystores2
|
|
deleteKeysBody1.pubkeys[7] notin filesystemKeystores2
|
|
|
|
asyncTest "Missing Authorization header" & preset():
|
|
let
|
|
response = await client.importKeystoresPlain(importKeystoresBody)
|
|
responseJson = Json.decode(response.data, JsonNode)
|
|
|
|
check:
|
|
response.status == 401
|
|
responseJson["code"].getStr() == "401"
|
|
responseJson["message"].getStr() == InvalidAuthorization
|
|
responseJson["stacktraces"][0].getStr() == $noAuthorizationHeader
|
|
|
|
asyncTest "Invalid Authorization Header" & preset():
|
|
let
|
|
response = await client.importKeystoresPlain(
|
|
importKeystoresBody,
|
|
extraHeaders = @[("Authorization", "Basic XYZ")])
|
|
responseJson = Json.decode(response.data, JsonNode)
|
|
|
|
check:
|
|
response.status == 401
|
|
responseJson["code"].getStr() == "401"
|
|
responseJson["message"].getStr() == InvalidAuthorization
|
|
responseJson["stacktraces"][0].getStr() == $missingBearerScheme
|
|
|
|
asyncTest "Invalid Authorization Token" & preset():
|
|
let
|
|
response = await client.importKeystoresPlain(
|
|
importKeystoresBody,
|
|
extraHeaders = @[("Authorization", "Bearer InvalidToken")])
|
|
responseJson = Json.decode(response.data, JsonNode)
|
|
|
|
check:
|
|
response.status == 401
|
|
responseJson["code"].getStr() == "401"
|
|
responseJson["message"].getStr() == InvalidAuthorization
|
|
responseJson["stacktraces"][0].getStr() == $incorrectToken
|
|
|
|
suite "DeleteKeys requests" & preset():
|
|
asyncTest "Deleting not existing key" & preset():
|
|
let
|
|
response = await client.deleteKeysPlain(
|
|
deleteKeysBody,
|
|
extraHeaders = @[("Authorization", "Bearer " & correctTokenValue)])
|
|
responseJson = Json.decode(response.data, JsonNode)
|
|
|
|
check:
|
|
response.status == 200
|
|
responseJson["data"][0]["status"].getStr() == "not_found"
|
|
responseJson["data"][0]["message"].getStr() == ""
|
|
|
|
asyncTest "Missing Authorization header" & preset():
|
|
let
|
|
response = await client.deleteKeysPlain(deleteKeysBody)
|
|
responseJson = Json.decode(response.data, JsonNode)
|
|
|
|
check:
|
|
response.status == 401
|
|
responseJson["code"].getStr() == "401"
|
|
responseJson["message"].getStr() == InvalidAuthorization
|
|
responseJson["stacktraces"][0].getStr() == $noAuthorizationHeader
|
|
|
|
asyncTest "Invalid Authorization Header" & preset():
|
|
let
|
|
response = await client.deleteKeysPlain(
|
|
deleteKeysBody,
|
|
extraHeaders = @[("Authorization", "Basic XYZ")])
|
|
responseJson = Json.decode(response.data, JsonNode)
|
|
|
|
check:
|
|
response.status == 401
|
|
responseJson["code"].getStr() == "401"
|
|
responseJson["message"].getStr() == InvalidAuthorization
|
|
responseJson["stacktraces"][0].getStr() == $missingBearerScheme
|
|
|
|
asyncTest "Invalid Authorization Token" & preset():
|
|
let
|
|
response = await client.deleteKeysPlain(
|
|
deleteKeysBody,
|
|
extraHeaders = @[("Authorization", "Bearer XYZ")])
|
|
responseJson = Json.decode(response.data, JsonNode)
|
|
|
|
check:
|
|
response.status == 401
|
|
responseJson["code"].getStr() == "401"
|
|
responseJson["message"].getStr() == InvalidAuthorization
|
|
responseJson["stacktraces"][0].getStr() == $incorrectToken
|
|
|
|
suite "ListRemoteKeys requests" & preset():
|
|
asyncTest "Correct token provided" & preset():
|
|
let
|
|
filesystemKeystores = sorted(
|
|
listRemoteValidators(validatorsDir, secretsDir))
|
|
apiKeystores = sorted((
|
|
await client.listRemoteKeys(correctTokenValue)).data)
|
|
|
|
check filesystemKeystores == apiKeystores
|
|
|
|
asyncTest "Missing Authorization header" & preset():
|
|
let
|
|
response = await client.listRemoteKeysPlain()
|
|
responseJson = Json.decode(response.data, JsonNode)
|
|
|
|
check:
|
|
response.status == 401
|
|
responseJson["code"].getStr() == "401"
|
|
responseJson["message"].getStr() == InvalidAuthorization
|
|
responseJson["stacktraces"][0].getStr() == $noAuthorizationHeader
|
|
|
|
asyncTest "Invalid Authorization Header" & preset():
|
|
let
|
|
response = await client.listRemoteKeysPlain(
|
|
extraHeaders = @[("Authorization", "UnknownAuthScheme X")])
|
|
responseJson = Json.decode(response.data, JsonNode)
|
|
|
|
check:
|
|
response.status == 401
|
|
responseJson["code"].getStr() == "401"
|
|
responseJson["message"].getStr() == InvalidAuthorization
|
|
responseJson["stacktraces"][0].getStr() == $missingBearerScheme
|
|
|
|
asyncTest "Invalid Authorization Token" & preset():
|
|
let
|
|
response = await client.listRemoteKeysPlain(
|
|
extraHeaders = @[("Authorization", "Bearer InvalidToken")])
|
|
responseJson = Json.decode(response.data, JsonNode)
|
|
|
|
check:
|
|
response.status == 401
|
|
responseJson["code"].getStr() == "401"
|
|
responseJson["message"].getStr() == InvalidAuthorization
|
|
responseJson["stacktraces"][0].getStr() == $incorrectToken
|
|
|
|
expect RestError:
|
|
let keystores = await client.listKeys("Invalid Token")
|
|
|
|
suite "ImportRemoteKeys/ListRemoteKeys/DeleteRemoteKeys" & preset():
|
|
asyncTest "Importing list of remote keys" & preset():
|
|
let
|
|
response1 = await client.importRemoteKeysPlain(
|
|
importRemoteKeystoresBody,
|
|
extraHeaders = @[("Authorization", "Bearer " & correctTokenValue)])
|
|
responseJson1 = Json.decode(response1.data, JsonNode)
|
|
|
|
check:
|
|
response1.status == 200
|
|
for i in [0, 1, 2, 3, 4, 5, 6, 7, 16, 17]:
|
|
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]["message"].getStr() == ""
|
|
|
|
let
|
|
filesystemKeystores1 = sorted(
|
|
listRemoteValidators(validatorsDir, secretsDir))
|
|
apiKeystores1 = sorted((
|
|
await client.listRemoteKeys(correctTokenValue)).data)
|
|
|
|
check:
|
|
filesystemKeystores1 == apiKeystores1
|
|
|
|
for item in newPublicKeys:
|
|
let key = ValidatorPubKey.fromHex(item).tryGet()
|
|
let found =
|
|
block:
|
|
var res = false
|
|
for keystore in filesystemKeystores1:
|
|
if keystore.pubkey == key:
|
|
res = true
|
|
break
|
|
res
|
|
check found == true
|
|
|
|
let
|
|
response2 = await client.deleteRemoteKeysPlain(
|
|
deleteRemoteKeystoresBody2,
|
|
extraHeaders = @[("Authorization", "Bearer " & correctTokenValue)])
|
|
responseJson2 = Json.decode(response2.data, JsonNode)
|
|
|
|
check:
|
|
response2.status == 200
|
|
responseJson2["data"][0]["status"].getStr() == "deleted"
|
|
responseJson2["data"][1]["status"].getStr() == "deleted"
|
|
responseJson2["data"][2]["status"].getStr() == "deleted"
|
|
responseJson2["data"][3]["status"].getStr() == "deleted"
|
|
responseJson2["data"][4]["status"].getStr() == "deleted"
|
|
responseJson2["data"][5]["status"].getStr() == "deleted"
|
|
responseJson2["data"][6]["status"].getStr() == "deleted"
|
|
responseJson2["data"][7]["status"].getStr() == "deleted"
|
|
|
|
let
|
|
filesystemKeystores2 = sorted(
|
|
listRemoteValidators(validatorsDir, secretsDir))
|
|
apiKeystores2 = sorted((
|
|
await client.listRemoteKeys(correctTokenValue)).data)
|
|
|
|
check:
|
|
filesystemKeystores2 == apiKeystores2
|
|
|
|
for keystore in filesystemKeystores2:
|
|
let key = "0x" & keystore.pubkey.toHex()
|
|
check:
|
|
key notin newPublicKeys
|
|
|
|
asyncTest "Missing Authorization header" & preset():
|
|
let
|
|
response = await client.importRemoteKeysPlain(importRemoteKeystoresBody)
|
|
responseJson = Json.decode(response.data, JsonNode)
|
|
|
|
check:
|
|
response.status == 401
|
|
responseJson["code"].getStr() == "401"
|
|
responseJson["message"].getStr() == InvalidAuthorization
|
|
responseJson["stacktraces"][0].getStr() == $noAuthorizationHeader
|
|
|
|
asyncTest "Invalid Authorization Header" & preset():
|
|
let
|
|
response = await client.importRemoteKeysPlain(
|
|
importRemoteKeystoresBody,
|
|
extraHeaders = @[("Authorization", "Basic XYZ")])
|
|
responseJson = Json.decode(response.data, JsonNode)
|
|
|
|
check:
|
|
response.status == 401
|
|
responseJson["code"].getStr() == "401"
|
|
responseJson["message"].getStr() == InvalidAuthorization
|
|
responseJson["stacktraces"][0].getStr() == $missingBearerScheme
|
|
|
|
asyncTest "Invalid Authorization Token" & preset():
|
|
let
|
|
response = await client.importRemoteKeysPlain(
|
|
importRemoteKeystoresBody,
|
|
extraHeaders = @[("Authorization", "Bearer InvalidToken")])
|
|
responseJson = Json.decode(response.data, JsonNode)
|
|
|
|
check:
|
|
response.status == 401
|
|
responseJson["code"].getStr() == "401"
|
|
responseJson["message"].getStr() == InvalidAuthorization
|
|
responseJson["stacktraces"][0].getStr() == $incorrectToken
|
|
|
|
suite "DeleteRemoteKeys requests" & preset():
|
|
asyncTest "Deleting not existing key" & preset():
|
|
let
|
|
response = await client.deleteRemoteKeysPlain(
|
|
deleteRemoteKeystoresBody3,
|
|
extraHeaders = @[("Authorization", "Bearer " & correctTokenValue)])
|
|
responseJson = Json.decode(response.data, JsonNode)
|
|
|
|
check:
|
|
response.status == 200
|
|
responseJson["data"][0]["status"].getStr() == "not_found"
|
|
responseJson["data"][1]["status"].getStr() == "not_found"
|
|
|
|
asyncTest "Deleting existing local key and remote key" & preset():
|
|
let
|
|
response = await client.deleteRemoteKeysPlain(
|
|
deleteRemoteKeystoresBody4,
|
|
extraHeaders = @[("Authorization", "Bearer " & correctTokenValue)])
|
|
responseJson = Json.decode(response.data, JsonNode)
|
|
|
|
check:
|
|
response.status == 200
|
|
responseJson["data"][0]["status"].getStr() == "deleted"
|
|
responseJson["data"][1]["status"].getStr() == "deleted"
|
|
responseJson["data"][2]["status"].getStr() == "not_found"
|
|
responseJson["data"][3]["status"].getStr() == "not_found"
|
|
|
|
let
|
|
filesystemKeystores = sorted(
|
|
listRemoteValidators(validatorsDir, secretsDir))
|
|
apiKeystores = sorted((
|
|
await client.listRemoteKeys(correctTokenValue)).data)
|
|
|
|
check:
|
|
filesystemKeystores == apiKeystores
|
|
|
|
let
|
|
removedKey0 = ValidatorPubKey.fromHex(oldPublicKeys[0]).tryGet()
|
|
removedKey1 = ValidatorPubKey.fromHex(oldPublicKeys[1]).tryGet()
|
|
|
|
for item in apiKeystores:
|
|
check:
|
|
removedKey0 != item.pubkey
|
|
removedKey1 != item.pubkey
|
|
|
|
asyncTest "Missing Authorization header" & preset():
|
|
let
|
|
response = await client.deleteRemoteKeysPlain(
|
|
deleteRemoteKeystoresBody1)
|
|
responseJson = Json.decode(response.data, JsonNode)
|
|
|
|
check:
|
|
response.status == 401
|
|
responseJson["code"].getStr() == "401"
|
|
responseJson["message"].getStr() == InvalidAuthorization
|
|
responseJson["stacktraces"][0].getStr() == $noAuthorizationHeader
|
|
|
|
asyncTest "Invalid Authorization Header" & preset():
|
|
let
|
|
response = await client.deleteRemoteKeysPlain(
|
|
deleteRemoteKeystoresBody1,
|
|
extraHeaders = @[("Authorization", "Basic XYZ")])
|
|
responseJson = Json.decode(response.data, JsonNode)
|
|
|
|
check:
|
|
response.status == 401
|
|
responseJson["code"].getStr() == "401"
|
|
responseJson["message"].getStr() == InvalidAuthorization
|
|
responseJson["stacktraces"][0].getStr() == $missingBearerScheme
|
|
|
|
asyncTest "Invalid Authorization Token" & preset():
|
|
let
|
|
response = await client.deleteRemoteKeysPlain(
|
|
deleteRemoteKeystoresBody1,
|
|
extraHeaders = @[("Authorization", "Bearer XYZ")])
|
|
responseJson = Json.decode(response.data, JsonNode)
|
|
|
|
check:
|
|
response.status == 401
|
|
responseJson["code"].getStr() == "401"
|
|
responseJson["message"].getStr() == InvalidAuthorization
|
|
responseJson["stacktraces"][0].getStr() == $incorrectToken
|
|
|
|
bnStatus = BeaconNodeStatus.Stopping
|
|
|
|
proc main() {.async.} =
|
|
asyncSpawn runTests()
|
|
startSingleNodeNetwork()
|
|
|
|
waitFor main()
|