Use OS file locks in validator client (#1958)

## Issue Addressed

Closes #1823

## Proposed Changes

* Use OS-level file locking for validator keystores, eliminating problems with lockfiles lingering after ungraceful shutdowns (`SIGKILL`, power outage). I'm using the `fs2` crate because it's cross-platform (unlike `file-lock`), and it seems to have the most downloads on crates.io.
* Deprecate + disable `--delete-lockfiles` CLI param, it's no longer necessary
* Delete the `validator_dir::Manager`, as it was mostly dead code and was only used in the `validator list` command, which has been rewritten to read the validator definitions YAML instead.

## Additional Info

Tested on:

- [x] Linux
- [x] macOS
- [x] Docker Linux
- [x] Docker macOS
- [ ] Windows
This commit is contained in:
Michael Sproul
2020-11-26 11:25:46 +00:00
parent fc07cc3fdf
commit 3486d6a809
21 changed files with 282 additions and 411 deletions

View File

@@ -9,6 +9,7 @@ edition = "2018"
[dependencies]
eth2_keystore = { path = "../../crypto/eth2_keystore" }
eth2_wallet = { path = "../../crypto/eth2_wallet" }
lockfile = { path = "../lockfile" }
[dev-dependencies]
tempfile = "3.1.0"

View File

@@ -3,7 +3,7 @@ use crate::{
Error,
};
use eth2_wallet::{Uuid, ValidatorKeystores, Wallet};
use std::fs::{remove_file, OpenOptions};
use lockfile::Lockfile;
use std::path::{Path, PathBuf};
pub const LOCK_FILE: &str = ".lock";
@@ -26,6 +26,7 @@ pub const LOCK_FILE: &str = ".lock";
pub struct LockedWallet {
wallet_dir: PathBuf,
wallet: Wallet,
_lockfile: Lockfile,
}
impl LockedWallet {
@@ -49,20 +50,12 @@ impl LockedWallet {
return Err(Error::MissingWalletDir(wallet_dir));
}
let lockfile = wallet_dir.join(LOCK_FILE);
if lockfile.exists() {
return Err(Error::WalletIsLocked(wallet_dir));
} else {
OpenOptions::new()
.write(true)
.create_new(true)
.open(lockfile)
.map_err(Error::UnableToCreateLockfile)?;
}
let _lockfile = Lockfile::new(wallet_dir.join(LOCK_FILE))?;
Ok(Self {
wallet: read(&wallet_dir, uuid)?,
wallet_dir,
_lockfile,
})
}
@@ -99,13 +92,3 @@ impl LockedWallet {
Ok(keystores)
}
}
impl Drop for LockedWallet {
/// Clean-up the lockfile.
fn drop(&mut self) {
let lockfile = self.wallet_dir.clone().join(LOCK_FILE);
if let Err(e) = remove_file(&lockfile) {
eprintln!("Unable to remove {:?}: {:?}", lockfile, e);
}
}
}

View File

@@ -3,6 +3,7 @@ use crate::{
LockedWallet,
};
use eth2_wallet::{bip39::Mnemonic, Error as WalletError, Uuid, Wallet, WalletBuilder};
use lockfile::LockfileError;
use std::collections::HashMap;
use std::ffi::OsString;
use std::fs::{create_dir_all, read_dir, OpenOptions};
@@ -21,10 +22,9 @@ pub enum Error {
WalletNameUnknown(String),
WalletDirExists(PathBuf),
IoError(io::Error),
WalletIsLocked(PathBuf),
MissingWalletDir(PathBuf),
UnableToCreateLockfile(io::Error),
UuidMismatch((Uuid, Uuid)),
LockfileError(LockfileError),
}
impl From<io::Error> for Error {
@@ -45,6 +45,12 @@ impl From<FilesystemError> for Error {
}
}
impl From<LockfileError> for Error {
fn from(e: LockfileError) -> Error {
Error::LockfileError(e)
}
}
/// Defines the type of an EIP-2386 wallet.
///
/// Presently only `Hd` wallets are supported.
@@ -358,7 +364,7 @@ mod tests {
);
match LockedWallet::open(&base_dir, &uuid_a) {
Err(Error::WalletIsLocked(_)) => {}
Err(Error::LockfileError(_)) => {}
_ => panic!("did not get locked error"),
};
@@ -368,7 +374,7 @@ mod tests {
.expect("should open wallet a after previous instance is dropped");
match LockedWallet::open(&base_dir, &uuid_b) {
Err(Error::WalletIsLocked(_)) => {}
Err(Error::LockfileError(_)) => {}
_ => panic!("did not get locked error"),
};