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