Exclusive keystore locking (#3907)
This commit is contained in:
parent
fe5435ed96
commit
250f7b4bdf
|
@ -13,11 +13,59 @@ else:
|
|||
|
||||
import chronicles
|
||||
import stew/io2
|
||||
export io2
|
||||
import spec/keystore
|
||||
|
||||
when defined(windows):
|
||||
import stew/[windows/acl]
|
||||
|
||||
type
|
||||
ByteChar = byte | char
|
||||
|
||||
const
|
||||
INCOMPLETE_ERROR =
|
||||
when defined(windows):
|
||||
IoErrorCode(996) # ERROR_IO_INCOMPLETE
|
||||
else:
|
||||
IoErrorCode(28) # ENOSPC
|
||||
|
||||
proc openLockedFile*(keystorePath: string): IoResult[FileLockHandle] =
|
||||
let
|
||||
flags = {OpenFlags.Read, OpenFlags.Write, OpenFlags.Exclusive}
|
||||
handle = ? openFile(keystorePath, flags)
|
||||
|
||||
var success = false
|
||||
defer:
|
||||
if not(success):
|
||||
discard closeFile(handle)
|
||||
|
||||
let lock = ? lockFile(handle, LockType.Exclusive)
|
||||
success = true
|
||||
ok(FileLockHandle(ioHandle: lock, opened: true))
|
||||
|
||||
proc getData*(lockHandle: FileLockHandle,
|
||||
maxBufferSize: int): IoResult[string] =
|
||||
let filesize = ? getFileSize(lockHandle.ioHandle.handle)
|
||||
let length = min(filesize, maxBufferSize)
|
||||
var buffer = newString(length)
|
||||
let bytesRead = ? readFile(lockHandle.ioHandle.handle, buffer)
|
||||
if uint64(bytesRead) != uint64(len(buffer)):
|
||||
err(INCOMPLETE_ERROR)
|
||||
else:
|
||||
ok(buffer)
|
||||
|
||||
proc closeLockedFile*(lockHandle: FileLockHandle): IoResult[void] =
|
||||
if lockHandle.opened:
|
||||
var success = false
|
||||
defer:
|
||||
lockHandle.opened = false
|
||||
if not(success):
|
||||
discard lockHandle.ioHandle.handle.closeFile()
|
||||
|
||||
? lockHandle.ioHandle.unlockFile()
|
||||
success = true
|
||||
? lockHandle.ioHandle.handle.closeFile()
|
||||
ok()
|
||||
|
||||
proc secureCreatePath*(path: string): IoResult[void] =
|
||||
when defined(windows):
|
||||
let sres = createFoldersUserOnlySecurityDescriptor()
|
||||
|
@ -31,16 +79,55 @@ proc secureCreatePath*(path: string): IoResult[void] =
|
|||
else:
|
||||
createPath(path, 0o700)
|
||||
|
||||
proc secureWriteFile*[T: byte|char](path: string,
|
||||
data: openArray[T]): IoResult[void] =
|
||||
proc secureWriteFile*[T: ByteChar](path: string,
|
||||
data: openArray[T]): IoResult[void] =
|
||||
when defined(windows):
|
||||
let sres = createFilesUserOnlySecurityDescriptor()
|
||||
if sres.isErr():
|
||||
error "Could not allocate security descriptor", path = path,
|
||||
errorMsg = ioErrorMsg(sres.error), errorCode = $sres.error
|
||||
err(sres.error)
|
||||
err(sres.error())
|
||||
else:
|
||||
var sd = sres.get()
|
||||
writeFile(path, data, 0o600, secDescriptor = sd.getDescriptor())
|
||||
let res = writeFile(path, data, 0o600, sd.getDescriptor())
|
||||
if res.isErr():
|
||||
# writeFile() will not attempt to remove file on failure
|
||||
discard removeFile(path)
|
||||
err(res.error())
|
||||
else:
|
||||
ok()
|
||||
else:
|
||||
writeFile(path, data, 0o600)
|
||||
let res = writeFile(path, data, 0o600)
|
||||
if res.isErr():
|
||||
# writeFile() will not attempt to remove file on failure
|
||||
discard removeFile(path)
|
||||
err(res.error())
|
||||
else:
|
||||
ok()
|
||||
|
||||
proc secureWriteLockedFile*[T: ByteChar](path: string,
|
||||
data: openArray[T]
|
||||
): IoResult[FileLockHandle] =
|
||||
let handle =
|
||||
block:
|
||||
let flags = {OpenFlags.Write, OpenFlags.Truncate, OpenFlags.Create,
|
||||
OpenFlags.Exclusive}
|
||||
when defined(windows):
|
||||
var sd = ? createFilesUserOnlySecurityDescriptor()
|
||||
? openFile(path, flags, 0o600, sd.getDescriptor())
|
||||
else:
|
||||
? openFile(path, flags, 0o600)
|
||||
var success = false
|
||||
defer:
|
||||
if not(success):
|
||||
discard closeFile(handle)
|
||||
# We will try to remove file, if something goes wrong.
|
||||
discard removeFile(path)
|
||||
let bytesWrote = ? writeFile(handle, data)
|
||||
if uint64(bytesWrote) != uint64(len(data)):
|
||||
# Data was partially written, and `write` did not return any errors, so
|
||||
# lets return INCOMPLETE_ERROR.
|
||||
return err(INCOMPLETE_ERROR)
|
||||
let res = ? lockFile(handle, LockType.Exclusive)
|
||||
success = true
|
||||
ok(FileLockHandle(ioHandle: res, opened: true))
|
||||
|
|
|
@ -1562,6 +1562,7 @@ proc stop(node: BeaconNode) =
|
|||
warn "Couldn't stop network", msg = exc.msg
|
||||
|
||||
node.attachedValidators.slashingProtection.close()
|
||||
node.attachedValidators[].close()
|
||||
node.db.close()
|
||||
notice "Databases closed"
|
||||
|
||||
|
|
|
@ -17,14 +17,14 @@ import
|
|||
# Third-party libraries
|
||||
normalize,
|
||||
# Status libraries
|
||||
stew/[results, bitops2, base10], stew/shims/macros,
|
||||
stew/[results, bitops2, base10, io2], stew/shims/macros,
|
||||
eth/keyfile/uuid, blscurve, json_serialization,
|
||||
nimcrypto/[sha2, rijndael, pbkdf2, bcmode, hash, scrypt],
|
||||
# Local modules
|
||||
libp2p/crypto/crypto as lcrypto,
|
||||
./datatypes/base, ./signatures
|
||||
|
||||
export base, uri
|
||||
export base, uri, io2
|
||||
|
||||
# We use `ncrutils` for constant-time hexadecimal encoding/decoding procedures.
|
||||
import nimcrypto/utils as ncrutils
|
||||
|
@ -140,10 +140,15 @@ type
|
|||
id*: uint32
|
||||
pubkey*: ValidatorPubKey
|
||||
|
||||
FileLockHandle* = ref object
|
||||
ioHandle*: IoLockHandle
|
||||
opened*: bool
|
||||
|
||||
KeystoreData* = object
|
||||
version*: uint64
|
||||
pubkey*: ValidatorPubKey
|
||||
description*: Option[string]
|
||||
handle*: FileLockHandle
|
||||
case kind*: KeystoreKind
|
||||
of KeystoreKind.Local:
|
||||
privateKey*: ValidatorPrivKey
|
||||
|
|
|
@ -38,6 +38,7 @@ const
|
|||
NetKeystoreFileName* = "network_keystore.json"
|
||||
FeeRecipientFilename* = "suggested_fee_recipient.hex"
|
||||
KeyNameSize* = 98 # 0x + hexadecimal key representation 96 characters.
|
||||
MaxKeystoreFileSize* = 65536
|
||||
|
||||
type
|
||||
WalletPathPair* = object
|
||||
|
@ -52,6 +53,8 @@ type
|
|||
|
||||
KmResult*[T] = Result[T, cstring]
|
||||
|
||||
AnyKeystore* = RemoteKeystore | Keystore
|
||||
|
||||
RemoveValidatorStatus* {.pure.} = enum
|
||||
deleted = "Deleted"
|
||||
notFound = "Not found"
|
||||
|
@ -82,7 +85,7 @@ proc echoP*(msg: string) =
|
|||
|
||||
func init*(T: type KeystoreData,
|
||||
privateKey: ValidatorPrivKey,
|
||||
keystore: Keystore): T =
|
||||
keystore: Keystore, handle: FileLockHandle): T {.raises: [Defect].} =
|
||||
KeystoreData(
|
||||
kind: KeystoreKind.Local,
|
||||
privateKey: privateKey,
|
||||
|
@ -90,35 +93,39 @@ func init*(T: type KeystoreData,
|
|||
else: some(keystore.description[]),
|
||||
path: keystore.path,
|
||||
uuid: keystore.uuid,
|
||||
handle: handle,
|
||||
version: uint64(keystore.version),
|
||||
pubkey: privateKey.toPubKey().toPubKey()
|
||||
)
|
||||
|
||||
func init*(T: type KeystoreData,
|
||||
keystore: RemoteKeystore): Result[T, cstring] =
|
||||
func init*(T: type KeystoreData, keystore: RemoteKeystore,
|
||||
handle: FileLockHandle): Result[T, cstring] {.raises: [Defect].} =
|
||||
let cookedKey =
|
||||
block:
|
||||
let res = keystore.pubkey.load()
|
||||
if res.isNone():
|
||||
return err("Invalid validator's public key")
|
||||
res.get()
|
||||
ok KeystoreData(
|
||||
ok(KeystoreData(
|
||||
kind: KeystoreKind.Remote,
|
||||
handle: handle,
|
||||
pubkey: cookedKey.toPubKey,
|
||||
description: keystore.description,
|
||||
version: keystore.version,
|
||||
remotes: keystore.remotes,
|
||||
threshold: keystore.threshold
|
||||
)
|
||||
))
|
||||
|
||||
func init*(T: type KeystoreData, cookedKey: CookedPubKey,
|
||||
remotes: seq[RemoteSignerInfo], threshold: uint32): T =
|
||||
remotes: seq[RemoteSignerInfo], threshold: uint32,
|
||||
handle: FileLockHandle): T =
|
||||
KeystoreData(
|
||||
kind: KeystoreKind.Remote,
|
||||
handle: handle,
|
||||
pubkey: cookedKey.toPubKey(),
|
||||
version: 2'u64,
|
||||
remotes: remotes,
|
||||
threshold: threshold
|
||||
threshold: threshold,
|
||||
)
|
||||
|
||||
func init(T: type AddValidatorFailure, status: AddValidatorStatus,
|
||||
|
@ -339,38 +346,12 @@ proc keyboardGetPassword[T](prompt: string, attempts: int,
|
|||
dec(remainingAttempts)
|
||||
err("Failed to decrypt keystore")
|
||||
|
||||
proc loadKeystoreFile*(path: string): KsResult[Keystore] {.
|
||||
raises: [Defect].} =
|
||||
try:
|
||||
ok(Json.loadFile(path, Keystore))
|
||||
except IOError as err:
|
||||
return err("Could not read keystore file")
|
||||
except SerializationError as err:
|
||||
return err("Could not decode keystore file: " & err.formatMsg(path))
|
||||
|
||||
proc loadSecretFile*(path: string): KsResult[KeystorePass] {.
|
||||
raises: [Defect].} =
|
||||
try:
|
||||
ok(KeystorePass.init(readFile(path)))
|
||||
except IOError:
|
||||
return err("Could not read password file")
|
||||
|
||||
proc loadKeystoreUnsafe*(validatorsDir, secretsDir,
|
||||
keyName: string): KsResult[KeystoreData] =
|
||||
## Load keystore without any checks on keystore/secret permissions.
|
||||
let
|
||||
keystorePath = validatorsDir / keyName / KeystoreFileName
|
||||
keystore = ? loadKeystoreFile(keystorePath)
|
||||
|
||||
let
|
||||
passphrasePath = secretsDir / keyName
|
||||
passphrase = ? loadSecretFile(passphrasePath)
|
||||
|
||||
let res = decryptKeystore(keystore, passphrase)
|
||||
if res.isOk():
|
||||
ok(KeystoreData.init(res.get(), keystore))
|
||||
else:
|
||||
err("Failed to decrypt keystore")
|
||||
let res = readAllChars(path)
|
||||
if res.isErr():
|
||||
return err(ioErrorMsg(res.error()))
|
||||
ok(KeystorePass.init(res.get()))
|
||||
|
||||
proc loadRemoteKeystoreImpl(validatorsDir,
|
||||
keyName: string): Option[KeystoreData] =
|
||||
|
@ -381,42 +362,84 @@ proc loadRemoteKeystoreImpl(validatorsDir,
|
|||
key_path = keystorePath
|
||||
return
|
||||
|
||||
let keyStore =
|
||||
let handle =
|
||||
block:
|
||||
let remoteKeystore =
|
||||
try:
|
||||
Json.loadFile(keystorePath, RemoteKeystore)
|
||||
except IOError as err:
|
||||
error "Failed to read remote keystore file", err = err.msg,
|
||||
path = keystorePath
|
||||
return
|
||||
except SerializationError as e:
|
||||
error "Invalid remote keystore file",
|
||||
path = keystorePath,
|
||||
err_msg = e.formatMsg(keystorePath)
|
||||
return
|
||||
let res = init(KeystoreData, remoteKeystore)
|
||||
let res = openLockedFile(keystorePath)
|
||||
if res.isErr():
|
||||
error "Invalid remote keystore file",
|
||||
path = keystorePath
|
||||
error "Unable to lock keystore file", key_path = keystorePath,
|
||||
error_msg = ioErrorMsg(res.error())
|
||||
return
|
||||
res.get()
|
||||
some(keyStore)
|
||||
|
||||
proc loadKeystoreImpl(validatorsDir, secretsDir, keyName: string,
|
||||
nonInteractive: bool): Option[KeystoreData] =
|
||||
var success = false
|
||||
defer:
|
||||
if not(success):
|
||||
discard handle.closeLockedFile()
|
||||
|
||||
let keystore =
|
||||
block:
|
||||
let gres = handle.getData(MaxKeystoreFileSize)
|
||||
if gres.isErr():
|
||||
error "Could not read remote keystore file", key_path = keystorePath,
|
||||
error_msg = ioErrorMsg(gres.error())
|
||||
return
|
||||
let buffer = gres.get()
|
||||
let data =
|
||||
try:
|
||||
Json.decode(buffer, RemoteKeystore, requireAllFields = true,
|
||||
allowUnknownFields = true)
|
||||
except SerializationError as e:
|
||||
error "Invalid remote keystore file", key_path = keystorePath,
|
||||
error_msg = e.formatMsg(keystorePath)
|
||||
return
|
||||
let kres = KeystoreData.init(data, handle)
|
||||
if kres.isErr():
|
||||
error "Invalid remote keystore file", key_path = keystorePath,
|
||||
error_msg = kres.error()
|
||||
return
|
||||
kres.get()
|
||||
|
||||
success = true
|
||||
some(keystore)
|
||||
|
||||
proc loadLocalKeystoreImpl(validatorsDir, secretsDir, keyName: string,
|
||||
nonInteractive: bool): Option[KeystoreData] =
|
||||
let
|
||||
keystorePath = validatorsDir / keyName / KeystoreFileName
|
||||
keystore =
|
||||
passphrasePath = secretsDir / keyName
|
||||
handle =
|
||||
block:
|
||||
let res = loadKeystoreFile(keystorePath)
|
||||
let res = openLockedFile(keystorePath)
|
||||
if res.isErr():
|
||||
error "Failed to read keystore file", error = res.error(),
|
||||
path = keystorePath
|
||||
error "Unable to lock keystore file", key_path = keystorePath,
|
||||
error_msg = ioErrorMsg(res.error())
|
||||
return
|
||||
res.get()
|
||||
|
||||
let passphrasePath = secretsDir / keyName
|
||||
var success = false
|
||||
defer:
|
||||
if not(success):
|
||||
discard handle.closeLockedFile()
|
||||
|
||||
let
|
||||
keystore =
|
||||
block:
|
||||
let gres = handle.getData(MaxKeystoreFileSize)
|
||||
if gres.isErr():
|
||||
error "Could not read local keystore file", key_path = keystorePath,
|
||||
error_msg = ioErrorMsg(gres.error())
|
||||
return
|
||||
let buffer = gres.get()
|
||||
let data =
|
||||
try:
|
||||
Json.decode(buffer, Keystore, requireAllFields = true,
|
||||
allowUnknownFields = true)
|
||||
except SerializationError as e:
|
||||
error "Invalid local keystore file", key_path = keystorePath,
|
||||
error_msg = e.formatMsg(keystorePath)
|
||||
return
|
||||
data
|
||||
|
||||
if fileExists(passphrasePath):
|
||||
if not(checkSensitiveFilePermissions(passphrasePath)):
|
||||
error "Password file has insecure permissions", key_path = keystorePath
|
||||
|
@ -426,21 +449,24 @@ proc loadKeystoreImpl(validatorsDir, secretsDir, keyName: string,
|
|||
block:
|
||||
let res = loadSecretFile(passphrasePath)
|
||||
if res.isErr():
|
||||
error "Failed to read passphrase file", err = res.error(),
|
||||
error "Failed to read passphrase file", error_msg = res.error(),
|
||||
path = passphrasePath
|
||||
return
|
||||
res.get()
|
||||
|
||||
let res = decryptKeystore(keystore, passphrase)
|
||||
if res.isOk():
|
||||
return some(KeystoreData.init(res.get(), keystore))
|
||||
success = true
|
||||
return some(KeystoreData.init(res.get(), keystore, handle))
|
||||
else:
|
||||
error "Failed to decrypt keystore", keystorePath, passphrasePath
|
||||
error "Failed to decrypt keystore", key_path = keystorePath,
|
||||
secure_path = passphrasePath
|
||||
return
|
||||
|
||||
if nonInteractive:
|
||||
error "Unable to load validator key store. Please ensure matching passphrase exists in the secrets dir",
|
||||
keyName, validatorsDir, secretsDir = secretsDir
|
||||
error "Unable to load validator key store. Please ensure matching " &
|
||||
"passphrase exists in the secrets dir", key_path = keystorePath,
|
||||
key_name = keyName, validatorsDir, secretsDir = secretsDir
|
||||
return
|
||||
|
||||
let prompt = "Please enter passphrase for key \"" &
|
||||
|
@ -449,15 +475,17 @@ proc loadKeystoreImpl(validatorsDir, secretsDir, keyName: string,
|
|||
proc (password: string): KsResult[ValidatorPrivKey] =
|
||||
let decrypted = decryptKeystore(keystore, KeystorePass.init password)
|
||||
if decrypted.isErr():
|
||||
error "Keystore decryption failed. Please try again", keystorePath
|
||||
error "Keystore decryption failed. Please try again",
|
||||
keystore_path = keystorePath
|
||||
decrypted
|
||||
)
|
||||
|
||||
if res.isOk():
|
||||
some(KeystoreData.init(res.get(), keystore))
|
||||
else:
|
||||
if res.isErr():
|
||||
return
|
||||
|
||||
success = true
|
||||
some(KeystoreData.init(res.get(), keystore, handle))
|
||||
|
||||
proc loadKeystore*(validatorsDir, secretsDir, keyName: string,
|
||||
nonInteractive: bool): Option[KeystoreData] =
|
||||
let
|
||||
|
@ -466,7 +494,7 @@ proc loadKeystore*(validatorsDir, secretsDir, keyName: string,
|
|||
remoteKeystorePath = keystorePath / RemoteKeystoreFileName
|
||||
|
||||
if fileExists(localKeystorePath):
|
||||
loadKeystoreImpl(validatorsDir, secretsDir, keyName, nonInteractive)
|
||||
loadLocalKeystoreImpl(validatorsDir, secretsDir, keyName, nonInteractive)
|
||||
elif fileExists(remoteKeystorePath):
|
||||
loadRemoteKeystoreImpl(validatorsDir, keyName)
|
||||
else:
|
||||
|
@ -524,20 +552,24 @@ 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].} =
|
||||
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,
|
||||
publicKey: ValidatorPubKey,
|
||||
kind: KeystoreKind): KmResult[RemoveValidatorStatus]
|
||||
{.raises: [Defect].} =
|
||||
kind: KeystoreKind): KmResult[RemoveValidatorStatus] {.
|
||||
raises: [Defect].} =
|
||||
let validator = pool.getValidator(publicKey)
|
||||
if isNil(validator):
|
||||
return ok(RemoveValidatorStatus.notFound)
|
||||
if validator.kind.toKeystoreKind() != kind:
|
||||
return ok(RemoveValidatorStatus.notFound)
|
||||
let cres = validator.data.handle.closeLockedFile()
|
||||
if cres.isErr():
|
||||
return err("Could not unlock validator keystore file")
|
||||
let res = removeValidatorFiles(conf, publicKey.fsName, kind)
|
||||
if res.isErr():
|
||||
return err(res.error())
|
||||
|
@ -573,6 +605,42 @@ proc existsKeystore*(keystoreDir: string,
|
|||
return true
|
||||
false
|
||||
|
||||
iterator listLoadableKeys*(validatorsDir, secretsDir: string,
|
||||
keysMask: set[KeystoreKind]): CookedPubKey =
|
||||
try:
|
||||
for kind, file in walkDir(validatorsDir):
|
||||
if kind == pcDir:
|
||||
let
|
||||
keyName = splitFile(file).name
|
||||
keystoreDir = validatorsDir / keyName
|
||||
|
||||
if not(checkKeyName(keyName)):
|
||||
# Skip folders which name do not satisfy "0x[a-fA-F0-9]{96, 96}".
|
||||
continue
|
||||
|
||||
if not(existsKeystore(keystoreDir, keysMask)):
|
||||
# Skip folder which do not satisfy `keysMask`.
|
||||
continue
|
||||
|
||||
let kres = ValidatorPubKey.fromHex(keyName)
|
||||
if kres.isErr():
|
||||
# Skip folders which could not be decoded to ValidatorPubKey.
|
||||
continue
|
||||
let publicKey = kres.get()
|
||||
|
||||
let cres = publicKey.load()
|
||||
if cres.isNone():
|
||||
# Skip folders which has invalid ValidatorPubKey
|
||||
# (point is not on curve).
|
||||
continue
|
||||
|
||||
yield cres.get()
|
||||
|
||||
except OSError as err:
|
||||
error "Validator keystores directory not accessible",
|
||||
path = validatorsDir, err = err.msg
|
||||
quit 1
|
||||
|
||||
iterator listLoadableKeystores*(validatorsDir, secretsDir: string,
|
||||
nonInteractive: bool,
|
||||
keysMask: set[KeystoreKind]): KeystoreData =
|
||||
|
@ -715,10 +783,11 @@ proc saveNetKeystore*(rng: var HmacDrbgContext, keystorePath: string,
|
|||
key_path = keystorePath
|
||||
res.mapErrTo(FailedToCreateKeystoreFile)
|
||||
|
||||
proc createValidatorFiles*(secretsDir, validatorsDir, keystoreDir, secretFile,
|
||||
passwordAsString, keystoreFile,
|
||||
encodedStorage: string
|
||||
): Result[void, KeystoreGenerationError] =
|
||||
proc createLocalValidatorFiles*(
|
||||
secretsDir, validatorsDir, keystoreDir,
|
||||
secretFile, passwordAsString, keystoreFile,
|
||||
encodedStorage: string
|
||||
): Result[void, KeystoreGenerationError] {.raises: [Defect].} =
|
||||
|
||||
var
|
||||
success = false # becomes true when everything is created successfully
|
||||
|
@ -730,7 +799,7 @@ proc createValidatorFiles*(secretsDir, validatorsDir, keystoreDir, secretFile,
|
|||
if not(secretsDirExisted):
|
||||
? secureCreatePath(secretsDir).mapErrTo(FailedToCreateSecretsDir)
|
||||
defer:
|
||||
if not (success or secretsDirExisted):
|
||||
if not (success or secretsDirExisted):
|
||||
discard io2.removeDir(secretsDir)
|
||||
|
||||
# validatorsDir:
|
||||
|
@ -738,7 +807,7 @@ proc createValidatorFiles*(secretsDir, validatorsDir, keystoreDir, secretFile,
|
|||
if not(validatorsDirExisted):
|
||||
? secureCreatePath(validatorsDir).mapErrTo(FailedToCreateValidatorsDir)
|
||||
defer:
|
||||
if not (success or validatorsDirExisted):
|
||||
if not (success or validatorsDirExisted):
|
||||
discard io2.removeDir(validatorsDir)
|
||||
|
||||
# keystoreDir:
|
||||
|
@ -761,9 +830,57 @@ proc createValidatorFiles*(secretsDir, validatorsDir, keystoreDir, secretFile,
|
|||
success = true
|
||||
ok()
|
||||
|
||||
proc createValidatorFiles*(validatorsDir, keystoreDir, keystoreFile,
|
||||
encodedStorage: string
|
||||
): Result[void, KeystoreGenerationError] =
|
||||
proc createLockedLocalValidatorFiles*(
|
||||
secretsDir, validatorsDir, keystoreDir,
|
||||
secretFile, passwordAsString, keystoreFile,
|
||||
encodedStorage: string
|
||||
): Result[FileLockHandle, KeystoreGenerationError] {.raises: [Defect].} =
|
||||
|
||||
var
|
||||
success = false # becomes true when everything is created successfully
|
||||
cleanupSecretsDir = true # becomes false if secretsDir already existed
|
||||
cleanupValidatorsDir = true # becomes false if validatorsDir already existed
|
||||
|
||||
# secretsDir:
|
||||
let secretsDirExisted: bool = dirExists(secretsDir)
|
||||
if not(secretsDirExisted):
|
||||
? secureCreatePath(secretsDir).mapErrTo(FailedToCreateSecretsDir)
|
||||
defer:
|
||||
if not (success or secretsDirExisted):
|
||||
discard io2.removeDir(secretsDir)
|
||||
|
||||
# validatorsDir:
|
||||
let validatorsDirExisted: bool = dirExists(validatorsDir)
|
||||
if not(validatorsDirExisted):
|
||||
? secureCreatePath(validatorsDir).mapErrTo(FailedToCreateValidatorsDir)
|
||||
defer:
|
||||
if not (success or validatorsDirExisted):
|
||||
discard io2.removeDir(validatorsDir)
|
||||
|
||||
# keystoreDir:
|
||||
? secureCreatePath(keystoreDir).mapErrTo(FailedToCreateKeystoreDir)
|
||||
defer:
|
||||
if not success:
|
||||
discard io2.removeDir(keystoreDir)
|
||||
|
||||
# secretFile:
|
||||
? secureWriteFile(secretFile,
|
||||
passwordAsString).mapErrTo(FailedToCreateSecretFile)
|
||||
defer:
|
||||
if not success:
|
||||
discard io2.removeFile(secretFile)
|
||||
|
||||
# keystoreFile:
|
||||
let lock =
|
||||
? secureWriteLockedFile(keystoreFile,
|
||||
encodedStorage).mapErrTo(FailedToCreateKeystoreFile)
|
||||
|
||||
success = true
|
||||
ok(lock)
|
||||
|
||||
proc createRemoteValidatorFiles*(
|
||||
validatorsDir, keystoreDir, keystoreFile, encodedStorage: string
|
||||
): Result[void, KeystoreGenerationError] {.raises: [Defect].} =
|
||||
var
|
||||
success = false # becomes true when everything is created successfully
|
||||
|
||||
|
@ -787,13 +904,41 @@ proc createValidatorFiles*(validatorsDir, keystoreDir, keystoreFile,
|
|||
success = true
|
||||
ok()
|
||||
|
||||
proc saveKeystore*(rng: var HmacDrbgContext,
|
||||
validatorsDir, secretsDir: string,
|
||||
signingKey: ValidatorPrivKey,
|
||||
signingPubKey: CookedPubKey,
|
||||
signingKeyPath: KeyPath,
|
||||
password: string,
|
||||
mode = Secure): Result[void, KeystoreGenerationError] =
|
||||
proc createLockedRemoteValidatorFiles*(
|
||||
validatorsDir, keystoreDir, keystoreFile, encodedStorage: string
|
||||
): Result[FileLockHandle, KeystoreGenerationError] {.raises: [Defect].} =
|
||||
var
|
||||
success = false # becomes true when everything is created successfully
|
||||
|
||||
# validatorsDir:
|
||||
let validatorsDirExisted: bool = dirExists(validatorsDir)
|
||||
if not(validatorsDirExisted):
|
||||
? secureCreatePath(validatorsDir).mapErrTo(FailedToCreateValidatorsDir)
|
||||
defer:
|
||||
if not (success or validatorsDirExisted):
|
||||
discard io2.removeDir(validatorsDir)
|
||||
|
||||
# keystoreDir:
|
||||
? secureCreatePath(keystoreDir).mapErrTo(FailedToCreateKeystoreDir)
|
||||
defer:
|
||||
if not success:
|
||||
discard io2.removeDir(keystoreDir)
|
||||
|
||||
# keystoreFile:
|
||||
let lock = ? secureWriteLockedFile(
|
||||
keystoreFile, encodedStorage).mapErrTo(FailedToCreateKeystoreFile)
|
||||
success = true
|
||||
ok(lock)
|
||||
|
||||
proc saveKeystore*(
|
||||
rng: var HmacDrbgContext,
|
||||
validatorsDir, secretsDir: string,
|
||||
signingKey: ValidatorPrivKey,
|
||||
signingPubKey: CookedPubKey,
|
||||
signingKeyPath: KeyPath,
|
||||
password: string,
|
||||
mode = Secure
|
||||
): Result[void, KeystoreGenerationError] {.raises: [Defect].} =
|
||||
let
|
||||
keypass = KeystorePass.init(password)
|
||||
keyName = signingPubKey.fsName
|
||||
|
@ -811,29 +956,70 @@ proc saveKeystore*(rng: var HmacDrbgContext,
|
|||
keypass, signingKeyPath,
|
||||
mode = mode)
|
||||
|
||||
var encodedStorage: string
|
||||
try:
|
||||
encodedStorage = Json.encode(keyStore)
|
||||
except SerializationError as e:
|
||||
error "Could not serialize keystorage", key_path = keystoreFile
|
||||
return err(KeystoreGenerationError(
|
||||
kind: FailedToCreateKeystoreFile, error: e.msg))
|
||||
|
||||
? createValidatorFiles(secretsDir, validatorsDir,
|
||||
keystoreDir,
|
||||
secretsDir / keyName, keypass.str,
|
||||
keystoreFile, encodedStorage)
|
||||
let encodedStorage =
|
||||
try:
|
||||
Json.encode(keyStore)
|
||||
except SerializationError as e:
|
||||
error "Could not serialize keystorage", key_path = keystoreFile
|
||||
return err(KeystoreGenerationError(
|
||||
kind: FailedToCreateKeystoreFile, error: e.msg))
|
||||
|
||||
? createLocalValidatorFiles(secretsDir, validatorsDir,
|
||||
keystoreDir,
|
||||
secretsDir / keyName, keypass.str,
|
||||
keystoreFile, encodedStorage)
|
||||
ok()
|
||||
|
||||
proc saveKeystore*(validatorsDir: string,
|
||||
publicKey: ValidatorPubKey,
|
||||
urls: seq[RemoteSignerInfo],
|
||||
threshold: uint32,
|
||||
flags: set[RemoteKeystoreFlag] = {},
|
||||
remoteType = RemoteSignerType.Web3Signer,
|
||||
desc = ""): Result[void, KeystoreGenerationError]
|
||||
{.raises: [Defect].} =
|
||||
proc saveLockedKeystore*(
|
||||
rng: var HmacDrbgContext,
|
||||
validatorsDir, secretsDir: string,
|
||||
signingKey: ValidatorPrivKey,
|
||||
signingPubKey: CookedPubKey,
|
||||
signingKeyPath: KeyPath,
|
||||
password: string,
|
||||
mode = Secure
|
||||
): Result[FileLockHandle, KeystoreGenerationError] {.raises: [Defect].} =
|
||||
let
|
||||
keypass = KeystorePass.init(password)
|
||||
keyName = signingPubKey.fsName
|
||||
keystoreDir = validatorsDir / keyName
|
||||
keystoreFile = keystoreDir / KeystoreFileName
|
||||
|
||||
if dirExists(keystoreDir):
|
||||
return err(KeystoreGenerationError(kind: DuplicateKeystoreDir,
|
||||
error: "Keystore directory already exists"))
|
||||
if fileExists(keystoreFile):
|
||||
return err(KeystoreGenerationError(kind: DuplicateKeystoreFile,
|
||||
error: "Keystore file already exists"))
|
||||
|
||||
let keyStore = createKeystore(kdfPbkdf2, rng, signingKey,
|
||||
keypass, signingKeyPath,
|
||||
mode = mode)
|
||||
|
||||
let encodedStorage =
|
||||
try:
|
||||
Json.encode(keyStore)
|
||||
except SerializationError as e:
|
||||
error "Could not serialize keystorage", key_path = keystoreFile
|
||||
return err(KeystoreGenerationError(
|
||||
kind: FailedToCreateKeystoreFile, error: e.msg))
|
||||
|
||||
let lock = ? createLockedLocalValidatorFiles(secretsDir, validatorsDir,
|
||||
keystoreDir,
|
||||
secretsDir / keyName,
|
||||
keypass.str,
|
||||
keystoreFile, encodedStorage)
|
||||
ok(lock)
|
||||
|
||||
proc saveKeystore*(
|
||||
validatorsDir: string,
|
||||
publicKey: ValidatorPubKey,
|
||||
urls: seq[RemoteSignerInfo],
|
||||
threshold: uint32,
|
||||
flags: set[RemoteKeystoreFlag] = {},
|
||||
remoteType = RemoteSignerType.Web3Signer,
|
||||
desc = ""
|
||||
): Result[void, KeystoreGenerationError] {.raises: [Defect].} =
|
||||
let
|
||||
keyName = publicKey.fsName
|
||||
keystoreDir = validatorsDir / keyName
|
||||
|
@ -863,27 +1049,90 @@ proc saveKeystore*(validatorsDir: string,
|
|||
return err(KeystoreGenerationError(
|
||||
kind: FailedToCreateKeystoreFile, error: exc.msg))
|
||||
|
||||
? createValidatorFiles(validatorsDir, keystoreDir, keystoreFile,
|
||||
encodedStorage)
|
||||
? createRemoteValidatorFiles(validatorsDir, keystoreDir, keystoreFile,
|
||||
encodedStorage)
|
||||
ok()
|
||||
|
||||
proc saveKeystore*(validatorsDir: string,
|
||||
publicKey: ValidatorPubKey,
|
||||
url: HttpHostUri): Result[void, KeystoreGenerationError]
|
||||
{.raises: [Defect].} =
|
||||
proc saveLockedKeystore*(
|
||||
validatorsDir: string,
|
||||
publicKey: ValidatorPubKey,
|
||||
urls: seq[RemoteSignerInfo],
|
||||
threshold: uint32,
|
||||
flags: set[RemoteKeystoreFlag] = {},
|
||||
remoteType = RemoteSignerType.Web3Signer,
|
||||
desc = ""
|
||||
): Result[FileLockHandle, KeystoreGenerationError] {.raises: [Defect].} =
|
||||
let
|
||||
keyName = publicKey.fsName
|
||||
keystoreDir = validatorsDir / keyName
|
||||
keystoreFile = keystoreDir / RemoteKeystoreFileName
|
||||
keystoreDesc = if len(desc) == 0: none[string]() else: some(desc)
|
||||
keyStore = RemoteKeystore(
|
||||
version: 2'u64,
|
||||
description: keystoreDesc,
|
||||
remoteType: remoteType,
|
||||
pubkey: publicKey,
|
||||
threshold: threshold,
|
||||
remotes: urls,
|
||||
flags: flags)
|
||||
|
||||
if dirExists(keystoreDir):
|
||||
return err(KeystoreGenerationError(kind: DuplicateKeystoreDir,
|
||||
error: "Keystore directory already exists"))
|
||||
if fileExists(keystoreFile):
|
||||
return err(KeystoreGenerationError(kind: DuplicateKeystoreFile,
|
||||
error: "Keystore file already exists"))
|
||||
|
||||
let encodedStorage =
|
||||
try:
|
||||
Json.encode(keyStore)
|
||||
except SerializationError as exc:
|
||||
error "Could not serialize keystorage", key_path = keystoreFile
|
||||
return err(KeystoreGenerationError(
|
||||
kind: FailedToCreateKeystoreFile, error: exc.msg))
|
||||
|
||||
let lock = ? createLockedRemoteValidatorFiles(validatorsDir, keystoreDir,
|
||||
keystoreFile, encodedStorage)
|
||||
ok(lock)
|
||||
|
||||
proc saveKeystore*(
|
||||
validatorsDir: string,
|
||||
publicKey: ValidatorPubKey,
|
||||
url: HttpHostUri
|
||||
): Result[void, KeystoreGenerationError] {.raises: [Defect].} =
|
||||
let remoteInfo = RemoteSignerInfo(url: url, id: 0)
|
||||
saveKeystore(validatorsDir, publicKey, @[remoteInfo], 1)
|
||||
|
||||
proc saveKeystore*(conf: AnyConf,
|
||||
publicKey: ValidatorPubKey,
|
||||
remotes: seq[RemoteSignerInfo],
|
||||
threshold: uint32,
|
||||
flags: set[RemoteKeystoreFlag] = {},
|
||||
remoteType = RemoteSignerType.Web3Signer,
|
||||
desc = ""): Result[void, KeystoreGenerationError]
|
||||
{.raises: [Defect].} =
|
||||
saveKeystore(
|
||||
conf.validatorsDir(),
|
||||
proc saveLockedKeystore*(
|
||||
validatorsDir: string,
|
||||
publicKey: ValidatorPubKey,
|
||||
url: HttpHostUri
|
||||
): Result[FileLockHandle, KeystoreGenerationError] {.raises: [Defect].} =
|
||||
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,
|
||||
|
@ -914,11 +1163,13 @@ proc importKeystore*(pool: var ValidatorPool, conf: AnyConf,
|
|||
if existsKeystore(keystoreDir, {KeystoreKind.Local, KeystoreKind.Remote}):
|
||||
return err(AddValidatorFailure.init(AddValidatorStatus.existingArtifacts))
|
||||
|
||||
let res = saveKeystore(conf, publicKey, keystore.remotes, keystore.threshold)
|
||||
let res = saveLockedKeystore(conf, publicKey, keystore.remotes,
|
||||
keystore.threshold)
|
||||
if res.isErr():
|
||||
return err(AddValidatorFailure.init(AddValidatorStatus.failed,
|
||||
$res.error()))
|
||||
ok(KeystoreData.init(cookedKey, keystore.remotes, keystore.threshold))
|
||||
ok(KeystoreData.init(cookedKey, keystore.remotes, keystore.threshold,
|
||||
res.get()))
|
||||
|
||||
proc importKeystore*(pool: var ValidatorPool,
|
||||
rng: var HmacDrbgContext,
|
||||
|
@ -951,14 +1202,14 @@ proc importKeystore*(pool: var ValidatorPool,
|
|||
if existsKeystore(keystoreDir, {KeystoreKind.Local, KeystoreKind.Remote}):
|
||||
return err(AddValidatorFailure.init(AddValidatorStatus.existingArtifacts))
|
||||
|
||||
let res = saveKeystore(rng, validatorsDir, secretsDir,
|
||||
privateKey, publicKey, keystore.path, password)
|
||||
let res = saveLockedKeystore(rng, validatorsDir, secretsDir,
|
||||
privateKey, publicKey, keystore.path, password)
|
||||
|
||||
if res.isErr():
|
||||
return err(AddValidatorFailure.init(AddValidatorStatus.failed,
|
||||
$res.error()))
|
||||
|
||||
ok(KeystoreData.init(privateKey, keystore))
|
||||
ok(KeystoreData.init(privateKey, keystore, res.get()))
|
||||
|
||||
proc generateDistirbutedStore*(rng: var HmacDrbgContext,
|
||||
shares: seq[SecretShare],
|
||||
|
|
|
@ -20,6 +20,7 @@ import
|
|||
../spec/datatypes/[phase0, altair],
|
||||
../spec/eth2_apis/[rest_types, eth2_rest_serialization,
|
||||
rest_remote_signer_calls],
|
||||
../filepath,
|
||||
./slashing_protection
|
||||
|
||||
export
|
||||
|
@ -146,6 +147,14 @@ proc updateValidator*(pool: var ValidatorPool, pubkey: ValidatorPubKey,
|
|||
v.index = some(index)
|
||||
pool.validators[pubkey] = v
|
||||
|
||||
proc close*(pool: var ValidatorPool) =
|
||||
## Unlock and close all validator keystore's files managed by ``pool``.
|
||||
for validator in pool.validators.values():
|
||||
let res = validator.data.handle.closeLockedFile()
|
||||
if res.isErr():
|
||||
notice "Could not unlock validator's keystore file",
|
||||
pubkey = validator.pubkey, validator = shortLog(validator)
|
||||
|
||||
iterator publicKeys*(pool: ValidatorPool): ValidatorPubKey =
|
||||
for item in pool.validators.keys():
|
||||
yield item
|
||||
|
|
|
@ -199,39 +199,55 @@ const
|
|||
secretNetBytes = hexToSeqByte "08021220fe442379443d6e2d7d75d3a58f96fbb35f0a9c7217796825fc9040e3b89c5736"
|
||||
|
||||
proc listLocalValidators(validatorsDir,
|
||||
secretsDir: string): seq[KeystoreInfo] {.
|
||||
secretsDir: string): seq[ValidatorPubKey] {.
|
||||
raises: [Defect].} =
|
||||
var validators: seq[KeystoreInfo]
|
||||
|
||||
var validators: seq[ValidatorPubKey]
|
||||
try:
|
||||
for el in listLoadableKeystores(validatorsDir, secretsDir, true,
|
||||
{KeystoreKind.Local}):
|
||||
validators.add KeystoreInfo(validating_pubkey: el.pubkey,
|
||||
derivation_path: el.path.string,
|
||||
readonly: false)
|
||||
for el in listLoadableKeys(validatorsDir, secretsDir,
|
||||
{KeystoreKind.Local}):
|
||||
validators.add el.toPubKey()
|
||||
except OSError as err:
|
||||
error "Failure to list the validator directories",
|
||||
validatorsDir, secretsDir, err = err.msg
|
||||
|
||||
validators
|
||||
|
||||
proc listRemoteValidators(validatorsDir,
|
||||
secretsDir: string): seq[RemoteKeystoreInfo] {.
|
||||
secretsDir: string): seq[ValidatorPubKey] {.
|
||||
raises: [Defect].} =
|
||||
var validators: seq[RemoteKeystoreInfo]
|
||||
|
||||
var validators: seq[ValidatorPubKey]
|
||||
try:
|
||||
for el in listLoadableKeystores(validatorsDir, secretsDir, true,
|
||||
{KeystoreKind.Remote}):
|
||||
validators.add RemoteKeystoreInfo(pubkey: el.pubkey,
|
||||
url: el.remotes[0].url)
|
||||
|
||||
for el in listLoadableKeys(validatorsDir, secretsDir,
|
||||
{KeystoreKind.Remote}):
|
||||
validators.add el.toPubKey()
|
||||
except OSError as err:
|
||||
error "Failure to list the validator directories",
|
||||
validatorsDir, secretsDir, err = err.msg
|
||||
|
||||
validators
|
||||
|
||||
proc `==`(a: seq[ValidatorPubKey],
|
||||
b: seq[KeystoreInfo | RemoteKeystoreInfo]): bool =
|
||||
if len(a) != len(b):
|
||||
return false
|
||||
var indices: seq[int]
|
||||
for publicKey in a:
|
||||
let index =
|
||||
block:
|
||||
var res = -1
|
||||
for k, v in b.pairs():
|
||||
let key =
|
||||
when b is seq[KeystoreInfo]:
|
||||
v.validating_pubkey
|
||||
else:
|
||||
v.pubkey
|
||||
if key == publicKey:
|
||||
res = k
|
||||
break
|
||||
res
|
||||
if (index == -1) or (index in indices):
|
||||
return false
|
||||
indices.add(index)
|
||||
true
|
||||
|
||||
proc runTests {.async.} =
|
||||
while bnStatus != BeaconNodeStatus.Running:
|
||||
await sleepAsync(1.seconds)
|
||||
|
@ -301,9 +317,9 @@ 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].validating_pubkey,
|
||||
res.add(RemoteKeystoreInfo(pubkey: localList[0],
|
||||
url: newPublicKeysUrl))
|
||||
res.add(RemoteKeystoreInfo(pubkey: localList[1].validating_pubkey,
|
||||
res.add(RemoteKeystoreInfo(pubkey: localList[1],
|
||||
url: newPublicKeysUrl))
|
||||
ImportRemoteKeystoresBody(remote_keys: res)
|
||||
|
||||
|
@ -338,8 +354,8 @@ proc runTests {.async.} =
|
|||
pubkeys: @[
|
||||
ValidatorPubKey.fromHex(oldPublicKeys[0]).tryGet(),
|
||||
ValidatorPubKey.fromHex(oldPublicKeys[1]).tryGet(),
|
||||
localList[0].validating_pubkey,
|
||||
localList[1].validating_pubkey
|
||||
localList[0],
|
||||
localList[1]
|
||||
]
|
||||
)
|
||||
|
||||
|
@ -546,11 +562,11 @@ proc runTests {.async.} =
|
|||
suite "ListKeys requests" & preset():
|
||||
asyncTest "Correct token provided" & preset():
|
||||
let
|
||||
filesystemKeystores = sorted(
|
||||
filesystemKeys = sorted(
|
||||
listLocalValidators(validatorsDir, secretsDir))
|
||||
apiKeystores = sorted((await client.listKeys(correctTokenValue)).data)
|
||||
|
||||
check filesystemKeystores == apiKeystores
|
||||
check filesystemKeys == apiKeystores
|
||||
|
||||
asyncTest "Missing Authorization header" & preset():
|
||||
let
|
||||
|
@ -599,20 +615,20 @@ proc runTests {.async.} =
|
|||
responseJson1["data"][i]["message"].getStr() == ""
|
||||
|
||||
let
|
||||
filesystemKeystores1 = sorted(
|
||||
filesystemKeys1 = 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
|
||||
filesystemKeys1 == apiKeystores1
|
||||
importKeystoresBody1.keystores[0].pubkey in filesystemKeys1
|
||||
importKeystoresBody1.keystores[1].pubkey in filesystemKeys1
|
||||
importKeystoresBody1.keystores[2].pubkey in filesystemKeys1
|
||||
importKeystoresBody1.keystores[3].pubkey in filesystemKeys1
|
||||
importKeystoresBody1.keystores[4].pubkey in filesystemKeys1
|
||||
importKeystoresBody1.keystores[5].pubkey in filesystemKeys1
|
||||
importKeystoresBody1.keystores[6].pubkey in filesystemKeys1
|
||||
importKeystoresBody1.keystores[7].pubkey in filesystemKeys1
|
||||
|
||||
let
|
||||
response2 = await client.importKeystoresPlain(
|
||||
|
@ -639,20 +655,20 @@ proc runTests {.async.} =
|
|||
responseJson3["data"][i]["message"].getStr() == ""
|
||||
|
||||
let
|
||||
filesystemKeystores2 = sorted(
|
||||
filesystemKeys2 = 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
|
||||
filesystemKeys2 == apiKeystores2
|
||||
deleteKeysBody1.pubkeys[0] notin filesystemKeys2
|
||||
deleteKeysBody1.pubkeys[1] notin filesystemKeys2
|
||||
deleteKeysBody1.pubkeys[2] notin filesystemKeys2
|
||||
deleteKeysBody1.pubkeys[3] notin filesystemKeys2
|
||||
deleteKeysBody1.pubkeys[4] notin filesystemKeys2
|
||||
deleteKeysBody1.pubkeys[5] notin filesystemKeys2
|
||||
deleteKeysBody1.pubkeys[6] notin filesystemKeys2
|
||||
deleteKeysBody1.pubkeys[7] notin filesystemKeys2
|
||||
|
||||
asyncTest "Missing Authorization header" & preset():
|
||||
let
|
||||
|
@ -732,12 +748,12 @@ proc runTests {.async.} =
|
|||
suite "ListRemoteKeys requests" & preset():
|
||||
asyncTest "Correct token provided" & preset():
|
||||
let
|
||||
filesystemKeystores = sorted(
|
||||
filesystemKeys = sorted(
|
||||
listRemoteValidators(validatorsDir, secretsDir))
|
||||
apiKeystores = sorted((
|
||||
await client.listRemoteKeys(correctTokenValue)).data)
|
||||
|
||||
check filesystemKeystores == apiKeystores
|
||||
check filesystemKeys == apiKeystores
|
||||
|
||||
asyncTest "Missing Authorization header" & preset():
|
||||
let
|
||||
|
@ -953,21 +969,21 @@ proc runTests {.async.} =
|
|||
responseJson1["data"][i]["message"].getStr() == ""
|
||||
|
||||
let
|
||||
filesystemKeystores1 = sorted(
|
||||
filesystemKeys1 = sorted(
|
||||
listRemoteValidators(validatorsDir, secretsDir))
|
||||
apiKeystores1 = sorted((
|
||||
await client.listRemoteKeys(correctTokenValue)).data)
|
||||
|
||||
check:
|
||||
filesystemKeystores1 == apiKeystores1
|
||||
filesystemKeys1 == 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:
|
||||
for keystore in filesystemKeys1:
|
||||
if keystore == key:
|
||||
res = true
|
||||
break
|
||||
res
|
||||
|
@ -991,16 +1007,16 @@ proc runTests {.async.} =
|
|||
responseJson2["data"][7]["status"].getStr() == "deleted"
|
||||
|
||||
let
|
||||
filesystemKeystores2 = sorted(
|
||||
filesystemKeys2 = sorted(
|
||||
listRemoteValidators(validatorsDir, secretsDir))
|
||||
apiKeystores2 = sorted((
|
||||
await client.listRemoteKeys(correctTokenValue)).data)
|
||||
|
||||
check:
|
||||
filesystemKeystores2 == apiKeystores2
|
||||
filesystemKeys2 == apiKeystores2
|
||||
|
||||
for keystore in filesystemKeystores2:
|
||||
let key = "0x" & keystore.pubkey.toHex()
|
||||
for keystore in filesystemKeys2:
|
||||
let key = "0x" & keystore.toHex()
|
||||
check:
|
||||
key notin newPublicKeys
|
||||
|
||||
|
|
|
@ -432,10 +432,10 @@ suite "createValidatorFiles()":
|
|||
|
||||
test "Add keystore files [LOCAL]":
|
||||
let
|
||||
res = createValidatorFiles(testSecretsDir, testValidatorsDir,
|
||||
keystoreDir,
|
||||
secretFile, password,
|
||||
keystoreFile, keystoreJsonContents)
|
||||
res = createLocalValidatorFiles(testSecretsDir, testValidatorsDir,
|
||||
keystoreDir,
|
||||
secretFile, password,
|
||||
keystoreFile, keystoreJsonContents)
|
||||
|
||||
validatorsCount = directoryItemsCount(testValidatorsDir)
|
||||
secretsCount = directoryItemsCount(testSecretsDir)
|
||||
|
@ -458,15 +458,15 @@ suite "createValidatorFiles()":
|
|||
|
||||
test "Add keystore files twice [LOCAL]":
|
||||
let
|
||||
res1 = createValidatorFiles(testSecretsDir, testValidatorsDir,
|
||||
keystoreDir,
|
||||
secretFile, password,
|
||||
keystoreFile, keystoreJsonContents)
|
||||
res1 = createLocalValidatorFiles(testSecretsDir, testValidatorsDir,
|
||||
keystoreDir,
|
||||
secretFile, password,
|
||||
keystoreFile, keystoreJsonContents)
|
||||
|
||||
res2 = createValidatorFiles(testSecretsDir, testValidatorsDir,
|
||||
keystoreDir,
|
||||
secretFile, password,
|
||||
keystoreFile, keystoreJsonContents)
|
||||
res2 = createLocalValidatorFiles(testSecretsDir, testValidatorsDir,
|
||||
keystoreDir,
|
||||
secretFile, password,
|
||||
keystoreFile, keystoreJsonContents)
|
||||
|
||||
validatorsCount = directoryItemsCount(testValidatorsDir)
|
||||
secretsCount = directoryItemsCount(testSecretsDir)
|
||||
|
@ -493,9 +493,9 @@ suite "createValidatorFiles()":
|
|||
remoteKeystoreFile = curKeystoreDir / RemoteKeystoreFileName
|
||||
localKeystoreFile = curKeystoreDir / KeystoreFileName
|
||||
|
||||
res = createValidatorFiles(testValidatorsDir, curKeystoreDir,
|
||||
remoteKeystoreFile,
|
||||
MultipleRemoteKeystoreJsons[0])
|
||||
res = createRemoteValidatorFiles(testValidatorsDir, curKeystoreDir,
|
||||
remoteKeystoreFile,
|
||||
MultipleRemoteKeystoreJsons[0])
|
||||
|
||||
validatorsCount = directoryItemsCount(testValidatorsDir)
|
||||
secretsCount = directoryItemsCount(testSecretsDir)
|
||||
|
@ -525,13 +525,13 @@ suite "createValidatorFiles()":
|
|||
remoteKeystoreFile = curKeystoreDir / RemoteKeystoreFileName
|
||||
localKeystoreFile = curKeystoreDir / KeystoreFileName
|
||||
|
||||
res1 = createValidatorFiles(testValidatorsDir, curKeystoreDir,
|
||||
remoteKeystoreFile,
|
||||
MultipleRemoteKeystoreJsons[0])
|
||||
res1 = createRemoteValidatorFiles(testValidatorsDir, curKeystoreDir,
|
||||
remoteKeystoreFile,
|
||||
MultipleRemoteKeystoreJsons[0])
|
||||
|
||||
res2 = createValidatorFiles(testValidatorsDir, curKeystoreDir,
|
||||
remoteKeystoreFile,
|
||||
MultipleRemoteKeystoreJsons[0])
|
||||
res2 = createRemoteValidatorFiles(testValidatorsDir, curKeystoreDir,
|
||||
remoteKeystoreFile,
|
||||
MultipleRemoteKeystoreJsons[0])
|
||||
|
||||
validatorsCount = directoryItemsCount(testValidatorsDir)
|
||||
secretsCount = directoryItemsCount(testSecretsDir)
|
||||
|
@ -557,16 +557,16 @@ suite "createValidatorFiles()":
|
|||
# TODO The following tests are disabled on Windows because the io2 module
|
||||
# doesn't implement the permission/mode parameter at the moment:
|
||||
when not defined(windows):
|
||||
test "`createValidatorFiles` with `secretsDir` without permissions":
|
||||
test "`createLocalValidatorFiles` with `secretsDir` without permissions":
|
||||
# Creating `secrets` dir with `UserRead` permissions before
|
||||
# calling `createValidatorFiles` which should result in problem
|
||||
# with creating a secret file inside the dir:
|
||||
let
|
||||
secretsDirNoPermissions = createPath(testSecretsDir, 0o400)
|
||||
res = createValidatorFiles(testSecretsDir, testValidatorsDir,
|
||||
keystoreDir,
|
||||
secretFile, password,
|
||||
keystoreFile, keystoreJsonContents)
|
||||
res = createLocalValidatorFiles(testSecretsDir, testValidatorsDir,
|
||||
keystoreDir,
|
||||
secretFile, password,
|
||||
keystoreFile, keystoreJsonContents)
|
||||
check:
|
||||
res.isErr and res.error.kind == FailedToCreateSecretFile
|
||||
|
||||
|
@ -578,16 +578,16 @@ suite "createValidatorFiles()":
|
|||
# The creation of the validators dir should be rolled-back
|
||||
not dirExists(testValidatorsDir)
|
||||
|
||||
test "`createValidatorFiles` with `validatorsDir` without permissions":
|
||||
test "`createLocalValidatorFiles` with `validatorsDir` without permissions":
|
||||
# Creating `validators` dir with `UserRead` permissions before
|
||||
# calling `createValidatorFiles` which should result in problems
|
||||
# creating `keystoreDir` inside the dir.
|
||||
let
|
||||
validatorsDirNoPermissions = createPath(testValidatorsDir, 0o400)
|
||||
res = createValidatorFiles(testSecretsDir, testValidatorsDir,
|
||||
keystoreDir,
|
||||
secretFile, password,
|
||||
keystoreFile, keystoreJsonContents)
|
||||
res = createLocalValidatorFiles(testSecretsDir, testValidatorsDir,
|
||||
keystoreDir,
|
||||
secretFile, password,
|
||||
keystoreFile, keystoreJsonContents)
|
||||
check:
|
||||
res.isErr and res.error.kind == FailedToCreateKeystoreDir
|
||||
|
||||
|
@ -599,17 +599,17 @@ suite "createValidatorFiles()":
|
|||
dirExists(testValidatorsDir)
|
||||
testValidatorsDir.isEmptyDir
|
||||
|
||||
test "`createValidatorFiles` with `keystoreDir` without permissions":
|
||||
test "`createLocalValidatorFiles` with `keystoreDir` without permissions":
|
||||
# Creating `keystore` dir with `UserRead` permissions before
|
||||
# calling `createValidatorFiles` which should result in problems
|
||||
# creating keystore file inside this dir:
|
||||
let
|
||||
validatorsDir = createPath(testValidatorsDir, 0o700)
|
||||
keystoreDirNoPermissions = createPath(keystoreDir, 0o400)
|
||||
res = createValidatorFiles(testSecretsDir, testValidatorsDir,
|
||||
keystoreDir,
|
||||
secretFile, password,
|
||||
keystoreFile, keystoreJsonContents)
|
||||
res = createLocalValidatorFiles(testSecretsDir, testValidatorsDir,
|
||||
keystoreDir,
|
||||
secretFile, password,
|
||||
keystoreFile, keystoreJsonContents)
|
||||
check:
|
||||
res.isErr and res.error.kind == FailedToCreateKeystoreFile
|
||||
|
||||
|
@ -634,14 +634,14 @@ suite "createValidatorFiles()":
|
|||
validatorsCountBefore = directoryItemsCount(testValidatorsDir)
|
||||
secretsCountBefore = directoryItemsCount(testSecretsDir)
|
||||
|
||||
# Creating `keystore` dir with `UserRead` permissions before calling `createValidatorFiles`
|
||||
# which will result in error
|
||||
# Creating `keystore` dir with `UserRead` permissions before calling
|
||||
# `createValidatorFiles` which will result in error
|
||||
keystoreDirNoPermissions = createPath(keystoreDir, 0o400)
|
||||
|
||||
res = createValidatorFiles(testSecretsDir, testValidatorsDir,
|
||||
keystoreDir,
|
||||
secretFile, password,
|
||||
keystoreFile, keystoreJsonContents)
|
||||
res = createLocalValidatorFiles(testSecretsDir, testValidatorsDir,
|
||||
keystoreDir,
|
||||
secretFile, password,
|
||||
keystoreFile, keystoreJsonContents)
|
||||
|
||||
validatorsCountAfter = directoryItemsCount(testValidatorsDir)
|
||||
secretsCountAfter = directoryItemsCount(testSecretsDir)
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 9a3130eb5b76c6504d6e6f8f86556c34f70300ef
|
||||
Subproject commit 0476bcad1b580a627af056ae78e872c790029958
|
Loading…
Reference in New Issue