Enable Compatibility with Windows (#2333)
## Issue Addressed Windows incompatibility. ## Proposed Changes On windows, lighthouse needs to default to STDIN as tty doesn't exist. Also Windows uses ACLs for file permissions. So to mirror chmod 600, we will remove every entry in a file's ACL and add only a single SID that is an alias for the file owner. Beyond that, there were several changes made to different unit tests because windows has slightly different error messages as well as frustrating nuances around killing a process :/ ## Additional Info Tested on my Windows VM and it appears to work, also compiled & tested on Linux with these changes. Permissions look correct on both platforms now. Just waiting for my validator to activate on Prater so I can test running full validator client on windows. Co-authored-by: ethDreamer <37123614+ethDreamer@users.noreply.github.com> Co-authored-by: Michael Sproul <micsproul@gmail.com>
This commit is contained in:
parent
58e52f8f40
commit
ba55e140ae
14
.github/workflows/test-suite.yml
vendored
14
.github/workflows/test-suite.yml
vendored
@ -44,6 +44,20 @@ jobs:
|
|||||||
run: sudo npm install -g ganache-cli
|
run: sudo npm install -g ganache-cli
|
||||||
- name: Run tests in release
|
- name: Run tests in release
|
||||||
run: make test-release
|
run: make test-release
|
||||||
|
release-tests-windows:
|
||||||
|
name: release-tests-windows
|
||||||
|
runs-on: windows-latest
|
||||||
|
needs: cargo-fmt
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v1
|
||||||
|
- name: Get latest version of stable Rust
|
||||||
|
run: rustup update stable
|
||||||
|
- name: Install ganache-cli
|
||||||
|
run: npm install -g ganache-cli
|
||||||
|
- name: Install make
|
||||||
|
run: choco install -y make
|
||||||
|
- name: Run tests in release
|
||||||
|
run: make test-release
|
||||||
debug-tests-ubuntu:
|
debug-tests-ubuntu:
|
||||||
name: debug-tests-ubuntu
|
name: debug-tests-ubuntu
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
98
Cargo.lock
generated
98
Cargo.lock
generated
@ -19,6 +19,7 @@ dependencies = [
|
|||||||
"eth2_ssz_derive",
|
"eth2_ssz_derive",
|
||||||
"eth2_wallet",
|
"eth2_wallet",
|
||||||
"eth2_wallet_manager",
|
"eth2_wallet_manager",
|
||||||
|
"filesystem",
|
||||||
"futures 0.3.14",
|
"futures 0.3.14",
|
||||||
"hex",
|
"hex",
|
||||||
"libc",
|
"libc",
|
||||||
@ -44,6 +45,7 @@ dependencies = [
|
|||||||
"directory",
|
"directory",
|
||||||
"eth2_keystore",
|
"eth2_keystore",
|
||||||
"eth2_wallet",
|
"eth2_wallet",
|
||||||
|
"filesystem",
|
||||||
"rand 0.7.3",
|
"rand 0.7.3",
|
||||||
"regex",
|
"regex",
|
||||||
"rpassword",
|
"rpassword",
|
||||||
@ -986,7 +988,7 @@ version = "0.2.5"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cc38c385bfd7e444464011bb24820f40dd1c76bcdfa1b78611cb7c2e5cafab75"
|
checksum = "cc38c385bfd7e444464011bb24820f40dd1c76bcdfa1b78611cb7c2e5cafab75"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"rustc_version",
|
"rustc_version 0.2.3",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -2242,6 +2244,24 @@ dependencies = [
|
|||||||
"subtle 2.4.0",
|
"subtle 2.4.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "field-offset"
|
||||||
|
version = "0.3.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cf539fba70056b50f40a22e0da30639518a12ee18c35807858a63b158cb6dde7"
|
||||||
|
dependencies = [
|
||||||
|
"memoffset",
|
||||||
|
"rustc_version 0.3.3",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "filesystem"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"winapi 0.3.9",
|
||||||
|
"windows-acl",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fixed-hash"
|
name = "fixed-hash"
|
||||||
version = "0.6.1"
|
version = "0.6.1"
|
||||||
@ -4480,6 +4500,15 @@ version = "2.1.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
|
checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pest"
|
||||||
|
version = "2.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53"
|
||||||
|
dependencies = [
|
||||||
|
"ucd-trie",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "petgraph"
|
name = "petgraph"
|
||||||
version = "0.5.1"
|
version = "0.5.1"
|
||||||
@ -4734,7 +4763,7 @@ dependencies = [
|
|||||||
"byteorder",
|
"byteorder",
|
||||||
"libc",
|
"libc",
|
||||||
"nom",
|
"nom",
|
||||||
"rustc_version",
|
"rustc_version 0.2.3",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -5189,6 +5218,8 @@ dependencies = [
|
|||||||
"tempfile",
|
"tempfile",
|
||||||
"tokio 1.5.0",
|
"tokio 1.5.0",
|
||||||
"types",
|
"types",
|
||||||
|
"winapi 0.3.9",
|
||||||
|
"windows-acl",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -5336,7 +5367,16 @@ version = "0.2.3"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
|
checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"semver",
|
"semver 0.9.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustc_version"
|
||||||
|
version = "0.3.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee"
|
||||||
|
dependencies = [
|
||||||
|
"semver 0.11.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -5503,7 +5543,16 @@ version = "0.9.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
|
checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"semver-parser",
|
"semver-parser 0.7.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "semver"
|
||||||
|
version = "0.11.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6"
|
||||||
|
dependencies = [
|
||||||
|
"semver-parser 0.10.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -5512,6 +5561,15 @@ version = "0.7.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
|
checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "semver-parser"
|
||||||
|
version = "0.10.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7"
|
||||||
|
dependencies = [
|
||||||
|
"pest",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sensitive_url"
|
name = "sensitive_url"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
@ -5761,6 +5819,7 @@ dependencies = [
|
|||||||
"byteorder",
|
"byteorder",
|
||||||
"eth2_ssz",
|
"eth2_ssz",
|
||||||
"eth2_ssz_derive",
|
"eth2_ssz_derive",
|
||||||
|
"filesystem",
|
||||||
"flate2",
|
"flate2",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"lighthouse_metrics",
|
"lighthouse_metrics",
|
||||||
@ -5803,6 +5862,7 @@ dependencies = [
|
|||||||
name = "slashing_protection"
|
name = "slashing_protection"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"filesystem",
|
||||||
"parking_lot",
|
"parking_lot",
|
||||||
"r2d2",
|
"r2d2",
|
||||||
"r2d2_sqlite",
|
"r2d2_sqlite",
|
||||||
@ -5956,7 +6016,7 @@ dependencies = [
|
|||||||
"rand 0.7.3",
|
"rand 0.7.3",
|
||||||
"rand_core 0.5.1",
|
"rand_core 0.5.1",
|
||||||
"ring",
|
"ring",
|
||||||
"rustc_version",
|
"rustc_version 0.2.3",
|
||||||
"sha2 0.9.3",
|
"sha2 0.9.3",
|
||||||
"subtle 2.4.0",
|
"subtle 2.4.0",
|
||||||
"x25519-dalek",
|
"x25519-dalek",
|
||||||
@ -6063,7 +6123,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "d022496b16281348b52d0e30ae99e01a73d737b2f45d38fed4edf79f9325a1d5"
|
checksum = "d022496b16281348b52d0e30ae99e01a73d737b2f45d38fed4edf79f9325a1d5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"discard",
|
"discard",
|
||||||
"rustc_version",
|
"rustc_version 0.2.3",
|
||||||
"stdweb-derive",
|
"stdweb-derive",
|
||||||
"stdweb-internal-macros",
|
"stdweb-internal-macros",
|
||||||
"stdweb-internal-runtime",
|
"stdweb-internal-runtime",
|
||||||
@ -6903,6 +6963,12 @@ dependencies = [
|
|||||||
"tree_hash_derive",
|
"tree_hash_derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ucd-trie"
|
||||||
|
version = "0.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uint"
|
name = "uint"
|
||||||
version = "0.8.5"
|
version = "0.8.5"
|
||||||
@ -7058,6 +7124,7 @@ dependencies = [
|
|||||||
"eth2_ssz_derive",
|
"eth2_ssz_derive",
|
||||||
"exit-future",
|
"exit-future",
|
||||||
"fallback",
|
"fallback",
|
||||||
|
"filesystem",
|
||||||
"futures 0.3.14",
|
"futures 0.3.14",
|
||||||
"hex",
|
"hex",
|
||||||
"hyper 0.14.7",
|
"hyper 0.14.7",
|
||||||
@ -7102,6 +7169,7 @@ dependencies = [
|
|||||||
"deposit_contract",
|
"deposit_contract",
|
||||||
"derivative",
|
"derivative",
|
||||||
"eth2_keystore",
|
"eth2_keystore",
|
||||||
|
"filesystem",
|
||||||
"hex",
|
"hex",
|
||||||
"lockfile",
|
"lockfile",
|
||||||
"rand 0.7.3",
|
"rand 0.7.3",
|
||||||
@ -7420,6 +7488,12 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "widestring"
|
||||||
|
version = "0.4.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c168940144dd21fd8046987c16a46a33d5fc84eec29ef9dcddc2ac9e31526b7c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winapi"
|
name = "winapi"
|
||||||
version = "0.2.8"
|
version = "0.2.8"
|
||||||
@ -7463,6 +7537,18 @@ version = "0.4.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-acl"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "177b1723986bcb4c606058e77f6e8614b51c7f9ad2face6f6fd63dd5c8b3cec3"
|
||||||
|
dependencies = [
|
||||||
|
"field-offset",
|
||||||
|
"libc",
|
||||||
|
"widestring",
|
||||||
|
"winapi 0.3.9",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winreg"
|
name = "winreg"
|
||||||
version = "0.7.0"
|
version = "0.7.0"
|
||||||
|
@ -34,6 +34,7 @@ slashing_protection = { path = "../validator_client/slashing_protection" }
|
|||||||
eth2 = {path = "../common/eth2"}
|
eth2 = {path = "../common/eth2"}
|
||||||
safe_arith = {path = "../consensus/safe_arith"}
|
safe_arith = {path = "../consensus/safe_arith"}
|
||||||
slot_clock = { path = "../common/slot_clock" }
|
slot_clock = { path = "../common/slot_clock" }
|
||||||
|
filesystem = { path = "../common/filesystem" }
|
||||||
sensitive_url = { path = "../common/sensitive_url" }
|
sensitive_url = { path = "../common/sensitive_url" }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
@ -105,6 +105,8 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
|
|||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name(STDIN_INPUTS_FLAG)
|
Arg::with_name(STDIN_INPUTS_FLAG)
|
||||||
|
.takes_value(false)
|
||||||
|
.hidden(cfg!(windows))
|
||||||
.long(STDIN_INPUTS_FLAG)
|
.long(STDIN_INPUTS_FLAG)
|
||||||
.help("If present, read all user inputs from stdin instead of tty."),
|
.help("If present, read all user inputs from stdin instead of tty."),
|
||||||
)
|
)
|
||||||
@ -118,7 +120,8 @@ pub fn cli_run<T: EthSpec>(
|
|||||||
let spec = env.core_context().eth2_config.spec;
|
let spec = env.core_context().eth2_config.spec;
|
||||||
|
|
||||||
let name: Option<String> = clap_utils::parse_optional(matches, WALLET_NAME_FLAG)?;
|
let name: Option<String> = clap_utils::parse_optional(matches, WALLET_NAME_FLAG)?;
|
||||||
let stdin_inputs = matches.is_present(STDIN_INPUTS_FLAG);
|
let stdin_inputs = cfg!(windows) || matches.is_present(STDIN_INPUTS_FLAG);
|
||||||
|
|
||||||
let wallet_base_dir = if matches.value_of("datadir").is_some() {
|
let wallet_base_dir = if matches.value_of("datadir").is_some() {
|
||||||
let path: PathBuf = clap_utils::parse_required(matches, "datadir")?;
|
let path: PathBuf = clap_utils::parse_required(matches, "datadir")?;
|
||||||
path.join(DEFAULT_WALLET_DIR)
|
path.join(DEFAULT_WALLET_DIR)
|
||||||
|
@ -61,6 +61,8 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
|
|||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name(STDIN_INPUTS_FLAG)
|
Arg::with_name(STDIN_INPUTS_FLAG)
|
||||||
|
.takes_value(false)
|
||||||
|
.hidden(cfg!(windows))
|
||||||
.long(STDIN_INPUTS_FLAG)
|
.long(STDIN_INPUTS_FLAG)
|
||||||
.help("If present, read all user inputs from stdin instead of tty."),
|
.help("If present, read all user inputs from stdin instead of tty."),
|
||||||
)
|
)
|
||||||
@ -70,7 +72,8 @@ pub fn cli_run<E: EthSpec>(matches: &ArgMatches, env: Environment<E>) -> Result<
|
|||||||
let keystore_path: PathBuf = clap_utils::parse_required(matches, KEYSTORE_FLAG)?;
|
let keystore_path: PathBuf = clap_utils::parse_required(matches, KEYSTORE_FLAG)?;
|
||||||
let password_file_path: Option<PathBuf> =
|
let password_file_path: Option<PathBuf> =
|
||||||
clap_utils::parse_optional(matches, PASSWORD_FILE_FLAG)?;
|
clap_utils::parse_optional(matches, PASSWORD_FILE_FLAG)?;
|
||||||
let stdin_inputs = matches.is_present(STDIN_INPUTS_FLAG);
|
|
||||||
|
let stdin_inputs = cfg!(windows) || matches.is_present(STDIN_INPUTS_FLAG);
|
||||||
let no_wait = matches.is_present(NO_WAIT);
|
let no_wait = matches.is_present(NO_WAIT);
|
||||||
|
|
||||||
let spec = env.eth2_config().spec.clone();
|
let spec = env.eth2_config().spec.clone();
|
||||||
|
@ -57,6 +57,8 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
|
|||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name(STDIN_INPUTS_FLAG)
|
Arg::with_name(STDIN_INPUTS_FLAG)
|
||||||
|
.takes_value(false)
|
||||||
|
.hidden(cfg!(windows))
|
||||||
.long(STDIN_INPUTS_FLAG)
|
.long(STDIN_INPUTS_FLAG)
|
||||||
.help("If present, read all user inputs from stdin instead of tty."),
|
.help("If present, read all user inputs from stdin instead of tty."),
|
||||||
)
|
)
|
||||||
@ -83,7 +85,7 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
|
|||||||
pub fn cli_run(matches: &ArgMatches, validator_dir: PathBuf) -> Result<(), String> {
|
pub fn cli_run(matches: &ArgMatches, validator_dir: PathBuf) -> Result<(), String> {
|
||||||
let keystore: Option<PathBuf> = clap_utils::parse_optional(matches, KEYSTORE_FLAG)?;
|
let keystore: Option<PathBuf> = clap_utils::parse_optional(matches, KEYSTORE_FLAG)?;
|
||||||
let keystores_dir: Option<PathBuf> = clap_utils::parse_optional(matches, DIR_FLAG)?;
|
let keystores_dir: Option<PathBuf> = clap_utils::parse_optional(matches, DIR_FLAG)?;
|
||||||
let stdin_inputs = matches.is_present(STDIN_INPUTS_FLAG);
|
let stdin_inputs = cfg!(windows) || matches.is_present(STDIN_INPUTS_FLAG);
|
||||||
let reuse_password = matches.is_present(REUSE_PASSWORD_FLAG);
|
let reuse_password = matches.is_present(REUSE_PASSWORD_FLAG);
|
||||||
let keystore_password_path: Option<PathBuf> =
|
let keystore_password_path: Option<PathBuf> =
|
||||||
clap_utils::parse_optional(matches, PASSWORD_FLAG)?;
|
clap_utils::parse_optional(matches, PASSWORD_FLAG)?;
|
||||||
|
@ -71,6 +71,8 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
|
|||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name(STDIN_INPUTS_FLAG)
|
Arg::with_name(STDIN_INPUTS_FLAG)
|
||||||
|
.takes_value(false)
|
||||||
|
.hidden(cfg!(windows))
|
||||||
.long(STDIN_INPUTS_FLAG)
|
.long(STDIN_INPUTS_FLAG)
|
||||||
.help("If present, read all user inputs from stdin instead of tty."),
|
.help("If present, read all user inputs from stdin instead of tty."),
|
||||||
)
|
)
|
||||||
@ -86,7 +88,7 @@ pub fn cli_run(matches: &ArgMatches, validator_dir: PathBuf) -> Result<(), Strin
|
|||||||
let first_index: u32 = clap_utils::parse_required(matches, FIRST_INDEX_FLAG)?;
|
let first_index: u32 = clap_utils::parse_required(matches, FIRST_INDEX_FLAG)?;
|
||||||
let count: u32 = clap_utils::parse_required(matches, COUNT_FLAG)?;
|
let count: u32 = clap_utils::parse_required(matches, COUNT_FLAG)?;
|
||||||
let mnemonic_path: Option<PathBuf> = clap_utils::parse_optional(matches, MNEMONIC_FLAG)?;
|
let mnemonic_path: Option<PathBuf> = clap_utils::parse_optional(matches, MNEMONIC_FLAG)?;
|
||||||
let stdin_inputs = matches.is_present(STDIN_INPUTS_FLAG);
|
let stdin_inputs = cfg!(windows) || matches.is_present(STDIN_INPUTS_FLAG);
|
||||||
|
|
||||||
eprintln!("secrets-dir path: {:?}", secrets_dir);
|
eprintln!("secrets-dir path: {:?}", secrets_dir);
|
||||||
|
|
||||||
|
@ -9,11 +9,9 @@ use eth2_wallet::{
|
|||||||
PlainText,
|
PlainText,
|
||||||
};
|
};
|
||||||
use eth2_wallet_manager::{LockedWallet, WalletManager, WalletType};
|
use eth2_wallet_manager::{LockedWallet, WalletManager, WalletType};
|
||||||
|
use filesystem::create_with_600_perms;
|
||||||
use std::ffi::OsStr;
|
use std::ffi::OsStr;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::fs::File;
|
|
||||||
use std::io::prelude::*;
|
|
||||||
use std::os::unix::fs::PermissionsExt;
|
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
pub const CMD: &str = "create";
|
pub const CMD: &str = "create";
|
||||||
@ -83,6 +81,8 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
|
|||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name(STDIN_INPUTS_FLAG)
|
Arg::with_name(STDIN_INPUTS_FLAG)
|
||||||
|
.takes_value(false)
|
||||||
|
.hidden(cfg!(windows))
|
||||||
.long(STDIN_INPUTS_FLAG)
|
.long(STDIN_INPUTS_FLAG)
|
||||||
.help("If present, read all user inputs from stdin instead of tty."),
|
.help("If present, read all user inputs from stdin instead of tty."),
|
||||||
)
|
)
|
||||||
@ -153,8 +153,7 @@ pub fn create_wallet_from_mnemonic(
|
|||||||
let name: Option<String> = clap_utils::parse_optional(matches, NAME_FLAG)?;
|
let name: Option<String> = clap_utils::parse_optional(matches, NAME_FLAG)?;
|
||||||
let wallet_password_path: Option<PathBuf> = clap_utils::parse_optional(matches, PASSWORD_FLAG)?;
|
let wallet_password_path: Option<PathBuf> = clap_utils::parse_optional(matches, PASSWORD_FLAG)?;
|
||||||
let type_field: String = clap_utils::parse_required(matches, TYPE_FLAG)?;
|
let type_field: String = clap_utils::parse_required(matches, TYPE_FLAG)?;
|
||||||
let stdin_inputs = matches.is_present(STDIN_INPUTS_FLAG);
|
let stdin_inputs = cfg!(windows) || matches.is_present(STDIN_INPUTS_FLAG);
|
||||||
|
|
||||||
let wallet_type = match type_field.as_ref() {
|
let wallet_type = match type_field.as_ref() {
|
||||||
HD_TYPE => WalletType::Hd,
|
HD_TYPE => WalletType::Hd,
|
||||||
unknown => return Err(format!("--{} {} is not supported", TYPE_FLAG, unknown)),
|
unknown => return Err(format!("--{} {} is not supported", TYPE_FLAG, unknown)),
|
||||||
@ -237,26 +236,3 @@ pub fn read_new_wallet_password_from_cli(
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a file with `600 (-rw-------)` permissions.
|
|
||||||
pub fn create_with_600_perms<P: AsRef<Path>>(path: P, bytes: &[u8]) -> Result<(), String> {
|
|
||||||
let path = path.as_ref();
|
|
||||||
|
|
||||||
let mut file =
|
|
||||||
File::create(&path).map_err(|e| format!("Unable to create {:?}: {}", path, e))?;
|
|
||||||
|
|
||||||
let mut perm = file
|
|
||||||
.metadata()
|
|
||||||
.map_err(|e| format!("Unable to get {:?} metadata: {}", path, e))?
|
|
||||||
.permissions();
|
|
||||||
|
|
||||||
perm.set_mode(0o600);
|
|
||||||
|
|
||||||
file.set_permissions(perm)
|
|
||||||
.map_err(|e| format!("Unable to set {:?} permissions: {}", path, e))?;
|
|
||||||
|
|
||||||
file.write_all(bytes)
|
|
||||||
.map_err(|e| format!("Unable to write to {:?}: {}", path, e))?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
@ -54,6 +54,8 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
|
|||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name(STDIN_INPUTS_FLAG)
|
Arg::with_name(STDIN_INPUTS_FLAG)
|
||||||
|
.takes_value(false)
|
||||||
|
.hidden(cfg!(windows))
|
||||||
.long(STDIN_INPUTS_FLAG)
|
.long(STDIN_INPUTS_FLAG)
|
||||||
.help("If present, read all user inputs from stdin instead of tty."),
|
.help("If present, read all user inputs from stdin instead of tty."),
|
||||||
)
|
)
|
||||||
@ -61,7 +63,7 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
|
|||||||
|
|
||||||
pub fn cli_run(matches: &ArgMatches, wallet_base_dir: PathBuf) -> Result<(), String> {
|
pub fn cli_run(matches: &ArgMatches, wallet_base_dir: PathBuf) -> Result<(), String> {
|
||||||
let mnemonic_path: Option<PathBuf> = clap_utils::parse_optional(matches, MNEMONIC_FLAG)?;
|
let mnemonic_path: Option<PathBuf> = clap_utils::parse_optional(matches, MNEMONIC_FLAG)?;
|
||||||
let stdin_inputs = matches.is_present(STDIN_INPUTS_FLAG);
|
let stdin_inputs = cfg!(windows) || matches.is_present(STDIN_INPUTS_FLAG);
|
||||||
|
|
||||||
eprintln!();
|
eprintln!();
|
||||||
eprintln!("WARNING: KEY RECOVERY CAN LEAD TO DUPLICATING VALIDATORS KEYS, WHICH CAN LEAD TO SLASHING.");
|
eprintln!("WARNING: KEY RECOVERY CAN LEAD TO DUPLICATING VALIDATORS KEYS, WHICH CAN LEAD TO SLASHING.");
|
||||||
|
@ -805,7 +805,7 @@ fn verify_block_for_gossip_slashing_detection() {
|
|||||||
let slasher_dir = tempdir().unwrap();
|
let slasher_dir = tempdir().unwrap();
|
||||||
let slasher = Arc::new(
|
let slasher = Arc::new(
|
||||||
Slasher::open(
|
Slasher::open(
|
||||||
SlasherConfig::new(slasher_dir.path().into()),
|
SlasherConfig::new(slasher_dir.path().into()).for_testing(),
|
||||||
harness.logger().clone(),
|
harness.logger().clone(),
|
||||||
)
|
)
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
@ -824,4 +824,8 @@ fn verify_block_for_gossip_slashing_detection() {
|
|||||||
slasher.process_queued(Epoch::new(0)).unwrap();
|
slasher.process_queued(Epoch::new(0)).unwrap();
|
||||||
let proposer_slashings = slasher.get_proposer_slashings();
|
let proposer_slashings = slasher.get_proposer_slashings();
|
||||||
assert_eq!(proposer_slashings.len(), 1);
|
assert_eq!(proposer_slashings.len(), 1);
|
||||||
|
// windows won't delete the temporary directory if you don't do this..
|
||||||
|
drop(harness);
|
||||||
|
drop(slasher);
|
||||||
|
slasher_dir.close().unwrap();
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
# Installation: Build from Source
|
# Installation: Build from Source
|
||||||
|
|
||||||
Lighthouse builds on Linux, macOS, and Windows (via [WSL][] only).
|
Lighthouse builds on Linux, macOS, and Windows (native Windows support in
|
||||||
|
BETA, we also support Windows via [WSL][]).
|
||||||
|
|
||||||
Compilation should be easy. In fact, if you already have Rust installed all you
|
Compilation should be easy. In fact, if you already have Rust and the build
|
||||||
need is:
|
dependencies installed, all you need is:
|
||||||
|
|
||||||
- `git clone https://github.com/sigp/lighthouse.git`
|
- `git clone https://github.com/sigp/lighthouse.git`
|
||||||
- `cd lighthouse`
|
- `cd lighthouse`
|
||||||
@ -26,35 +27,43 @@ directory will be the location you cloned Lighthouse to during the installation
|
|||||||
- `git checkout ${VERSION}`
|
- `git checkout ${VERSION}`
|
||||||
- `make`
|
- `make`
|
||||||
|
|
||||||
|
|
||||||
## Detailed Instructions
|
## Detailed Instructions
|
||||||
|
|
||||||
1. Install Rust and Cargo with [rustup](https://rustup.rs/).
|
1. Install the build dependencies for your platform
|
||||||
- Use the `stable` toolchain (it's the default).
|
- Check the [Dependencies](#dependencies) section for additional
|
||||||
- Check the [Troubleshooting](#troubleshooting) section for additional
|
information.
|
||||||
dependencies (e.g., `cmake`).
|
|
||||||
1. Clone the Lighthouse repository.
|
1. Clone the Lighthouse repository.
|
||||||
- Run `$ git clone https://github.com/sigp/lighthouse.git`
|
- Run `$ git clone https://github.com/sigp/lighthouse.git`
|
||||||
- Change into the newly created directory with `$ cd lighthouse`
|
- Change into the newly created directory with `$ cd lighthouse`
|
||||||
1. Build Lighthouse with `$ make`.
|
1. Build Lighthouse with `$ make`.
|
||||||
1. Installation was successful if `$ lighthouse --help` displays the
|
1. Installation was successful if `$ lighthouse --help` displays the command-line documentation.
|
||||||
command-line documentation.
|
|
||||||
|
|
||||||
> First time compilation may take several minutes. If you experience any
|
> First time compilation may take several minutes. If you experience any
|
||||||
> failures, please reach out on [discord](https://discord.gg/cyAszAh) or
|
> failures, please reach out on [discord](https://discord.gg/cyAszAh) or
|
||||||
> [create an issue](https://github.com/sigp/lighthouse/issues/new).
|
> [create an issue](https://github.com/sigp/lighthouse/issues/new).
|
||||||
|
|
||||||
## Windows Support
|
|
||||||
|
|
||||||
Compiling or running Lighthouse natively on Windows is not currently supported. However,
|
## Dependencies
|
||||||
Lighthouse can run successfully under the [Windows Subsystem for Linux (WSL)][WSL]. If using
|
|
||||||
Ubuntu under WSL, you can should install the Ubuntu dependencies listed in the [Dependencies
|
#### Installing Rust
|
||||||
(Ubuntu)](#dependencies-ubuntu) section.
|
|
||||||
|
The best way to install Rust (regardless of platform) is usually with [rustup](https://rustup.rs/)
|
||||||
|
- Use the `stable` toolchain (it's the default).
|
||||||
|
|
||||||
|
#### Windows Support
|
||||||
|
|
||||||
|
These instructions are for compiling or running Lighthouse natively on Windows, which is currently in
|
||||||
|
BETA testing. Lighthouse can also run successfully under the [Windows Subsystem for Linux (WSL)][WSL].
|
||||||
|
If using Ubuntu under WSL, you should follow the instructions for Ubuntu listed in the
|
||||||
|
[Dependencies (Ubuntu)](#ubuntu) section.
|
||||||
|
|
||||||
[WSL]: https://docs.microsoft.com/en-us/windows/wsl/about
|
[WSL]: https://docs.microsoft.com/en-us/windows/wsl/about
|
||||||
|
|
||||||
## Troubleshooting
|
1. Install [Git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git)
|
||||||
|
1. Install [Chocolatey](https://chocolatey.org/install) Package Manager for Windows
|
||||||
### Dependencies
|
- Install `make` via `choco install make`
|
||||||
|
- Install `cmake` via `choco install cmake --installargs 'ADD_CMAKE_TO_PATH=System'`
|
||||||
|
|
||||||
#### Ubuntu
|
#### Ubuntu
|
||||||
|
|
||||||
@ -71,6 +80,9 @@ You will need `cmake`. You can install via homebrew:
|
|||||||
|
|
||||||
brew install cmake
|
brew install cmake
|
||||||
|
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
### Command is not found
|
### Command is not found
|
||||||
|
|
||||||
Lighthouse will be installed to `CARGO_HOME` or `$HOME/.cargo`. This directory
|
Lighthouse will be installed to `CARGO_HOME` or `$HOME/.cargo`. This directory
|
||||||
|
@ -12,6 +12,7 @@ of the immaturity of the slasher UX and the extra resources required.
|
|||||||
* Quad-core CPU
|
* Quad-core CPU
|
||||||
* 16 GB RAM
|
* 16 GB RAM
|
||||||
* 256 GB solid state storage (in addition to space for the beacon node DB)
|
* 256 GB solid state storage (in addition to space for the beacon node DB)
|
||||||
|
* ⚠️ **If you are running natively on Windows**: LMDB will pre-allocate the entire 256 GB for the slasher database
|
||||||
|
|
||||||
## How to Run
|
## How to Run
|
||||||
|
|
||||||
|
@ -10,6 +10,7 @@ edition = "2018"
|
|||||||
rand = "0.7.3"
|
rand = "0.7.3"
|
||||||
eth2_wallet = { path = "../../crypto/eth2_wallet" }
|
eth2_wallet = { path = "../../crypto/eth2_wallet" }
|
||||||
eth2_keystore = { path = "../../crypto/eth2_keystore" }
|
eth2_keystore = { path = "../../crypto/eth2_keystore" }
|
||||||
|
filesystem = { path = "../filesystem" }
|
||||||
zeroize = { version = "1.1.1", features = ["zeroize_derive"] }
|
zeroize = { version = "1.1.1", features = ["zeroize_derive"] }
|
||||||
serde = "1.0.116"
|
serde = "1.0.116"
|
||||||
serde_derive = "1.0.116"
|
serde_derive = "1.0.116"
|
||||||
|
@ -6,12 +6,12 @@ use eth2_wallet::{
|
|||||||
bip39::{Language, Mnemonic, MnemonicType},
|
bip39::{Language, Mnemonic, MnemonicType},
|
||||||
Wallet,
|
Wallet,
|
||||||
};
|
};
|
||||||
|
use filesystem::{create_with_600_perms, Error as FsError};
|
||||||
use rand::{distributions::Alphanumeric, Rng};
|
use rand::{distributions::Alphanumeric, Rng};
|
||||||
use serde_derive::{Deserialize, Serialize};
|
use serde_derive::{Deserialize, Serialize};
|
||||||
use std::fs::{self, File};
|
use std::fs::{self, File};
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::io::prelude::*;
|
use std::io::prelude::*;
|
||||||
use std::os::unix::fs::PermissionsExt;
|
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use zeroize::Zeroize;
|
use zeroize::Zeroize;
|
||||||
|
|
||||||
@ -59,23 +59,6 @@ pub fn read_password<P: AsRef<Path>>(path: P) -> Result<PlainText, io::Error> {
|
|||||||
fs::read(path).map(strip_off_newlines).map(Into::into)
|
fs::read(path).map(strip_off_newlines).map(Into::into)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a file with `600 (-rw-------)` permissions.
|
|
||||||
pub fn create_with_600_perms<P: AsRef<Path>>(path: P, bytes: &[u8]) -> Result<(), io::Error> {
|
|
||||||
let path = path.as_ref();
|
|
||||||
|
|
||||||
let mut file = File::create(&path)?;
|
|
||||||
|
|
||||||
let mut perm = file.metadata()?.permissions();
|
|
||||||
|
|
||||||
perm.set_mode(0o600);
|
|
||||||
|
|
||||||
file.set_permissions(perm)?;
|
|
||||||
|
|
||||||
file.write_all(bytes)?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Write a file atomically by using a temporary file as an intermediate.
|
/// Write a file atomically by using a temporary file as an intermediate.
|
||||||
///
|
///
|
||||||
/// Care is taken to preserve the permissions of the file at `file_path` being written.
|
/// Care is taken to preserve the permissions of the file at `file_path` being written.
|
||||||
@ -86,18 +69,18 @@ pub fn write_file_via_temporary(
|
|||||||
file_path: &Path,
|
file_path: &Path,
|
||||||
temp_path: &Path,
|
temp_path: &Path,
|
||||||
bytes: &[u8],
|
bytes: &[u8],
|
||||||
) -> Result<(), io::Error> {
|
) -> Result<(), FsError> {
|
||||||
// If the file already exists, preserve its permissions by copying it.
|
// If the file already exists, preserve its permissions by copying it.
|
||||||
// Otherwise, create a new file with restricted permissions.
|
// Otherwise, create a new file with restricted permissions.
|
||||||
if file_path.exists() {
|
if file_path.exists() {
|
||||||
fs::copy(&file_path, &temp_path)?;
|
fs::copy(&file_path, &temp_path).map_err(FsError::UnableToCopyFile)?;
|
||||||
fs::write(&temp_path, &bytes)?;
|
fs::write(&temp_path, &bytes).map_err(FsError::UnableToWriteFile)?;
|
||||||
} else {
|
} else {
|
||||||
create_with_600_perms(&temp_path, &bytes)?;
|
create_with_600_perms(&temp_path, &bytes)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// With the temporary file created, perform an atomic rename.
|
// With the temporary file created, perform an atomic rename.
|
||||||
fs::rename(&temp_path, &file_path)?;
|
fs::rename(&temp_path, &file_path).map_err(FsError::UnableToRenameFile)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -36,7 +36,7 @@ pub enum Error {
|
|||||||
/// The config file could not be serialized as YAML.
|
/// The config file could not be serialized as YAML.
|
||||||
UnableToEncodeFile(serde_yaml::Error),
|
UnableToEncodeFile(serde_yaml::Error),
|
||||||
/// The config file or temp file could not be written to the filesystem.
|
/// The config file or temp file could not be written to the filesystem.
|
||||||
UnableToWriteFile(io::Error),
|
UnableToWriteFile(filesystem::Error),
|
||||||
/// The public key from the keystore is invalid.
|
/// The public key from the keystore is invalid.
|
||||||
InvalidKeystorePubkey,
|
InvalidKeystorePubkey,
|
||||||
/// The keystore was unable to be opened.
|
/// The keystore was unable to be opened.
|
||||||
|
14
common/filesystem/Cargo.toml
Normal file
14
common/filesystem/Cargo.toml
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
[package]
|
||||||
|
name = "filesystem"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Mark Mackey <mark@sigmaprime.io>"]
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
|
||||||
|
[target.'cfg(windows)'.dependencies]
|
||||||
|
winapi = "~0.3.5"
|
||||||
|
windows-acl = "~0.3.0"
|
||||||
|
|
144
common/filesystem/src/lib.rs
Normal file
144
common/filesystem/src/lib.rs
Normal file
@ -0,0 +1,144 @@
|
|||||||
|
use std::fs::File;
|
||||||
|
use std::io;
|
||||||
|
use std::io::Write;
|
||||||
|
use std::path::Path;
|
||||||
|
#[cfg(windows)]
|
||||||
|
use winapi::um::winnt::{FILE_GENERIC_READ, FILE_GENERIC_WRITE, STANDARD_RIGHTS_ALL};
|
||||||
|
|
||||||
|
/// This is the security identifier in Windows for the owner of a file. See:
|
||||||
|
/// - https://docs.microsoft.com/en-us/troubleshoot/windows-server/identity/security-identifiers-in-windows#well-known-sids-all-versions-of-windows
|
||||||
|
#[cfg(windows)]
|
||||||
|
const OWNER_SID_STR: &str = "S-1-3-4";
|
||||||
|
/// We don't need any of the `AceFlags` listed here:
|
||||||
|
/// - https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-ace_header
|
||||||
|
#[cfg(windows)]
|
||||||
|
const OWNER_ACL_ENTRY_FLAGS: u8 = 0;
|
||||||
|
/// Generic Rights:
|
||||||
|
/// - https://docs.microsoft.com/en-us/windows/win32/fileio/file-security-and-access-rights
|
||||||
|
/// Individual Read/Write/Execute Permissions (referenced in generic rights link):
|
||||||
|
/// - https://docs.microsoft.com/en-us/windows/win32/wmisdk/file-and-directory-access-rights-constants
|
||||||
|
/// STANDARD_RIGHTS_ALL
|
||||||
|
/// - https://docs.microsoft.com/en-us/windows/win32/secauthz/access-mask
|
||||||
|
#[cfg(windows)]
|
||||||
|
const OWNER_ACL_ENTRY_MASK: u32 = FILE_GENERIC_READ | FILE_GENERIC_WRITE | STANDARD_RIGHTS_ALL;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Error {
|
||||||
|
/// The file could not be created
|
||||||
|
UnableToCreateFile(io::Error),
|
||||||
|
/// The file could not be copied
|
||||||
|
UnableToCopyFile(io::Error),
|
||||||
|
/// The file could not be opened
|
||||||
|
UnableToOpenFile(io::Error),
|
||||||
|
/// The file could not be renamed
|
||||||
|
UnableToRenameFile(io::Error),
|
||||||
|
/// Failed to set permissions
|
||||||
|
UnableToSetPermissions(io::Error),
|
||||||
|
/// Failed to retrieve file metadata
|
||||||
|
UnableToRetrieveMetadata(io::Error),
|
||||||
|
/// Failed to write bytes to file
|
||||||
|
UnableToWriteFile(io::Error),
|
||||||
|
/// Failed to obtain file path
|
||||||
|
UnableToObtainFilePath,
|
||||||
|
/// Failed to convert string to SID
|
||||||
|
UnableToConvertSID(u32),
|
||||||
|
/// Failed to retrieve ACL for file
|
||||||
|
UnableToRetrieveACL(u32),
|
||||||
|
/// Failed to enumerate ACL entries
|
||||||
|
UnableToEnumerateACLEntries(u32),
|
||||||
|
/// Failed to add new ACL entry
|
||||||
|
UnableToAddACLEntry(String),
|
||||||
|
/// Failed to remove ACL entry
|
||||||
|
UnableToRemoveACLEntry(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a file with `600 (-rw-------)` permissions.
|
||||||
|
pub fn create_with_600_perms<P: AsRef<Path>>(path: P, bytes: &[u8]) -> Result<(), Error> {
|
||||||
|
let path = path.as_ref();
|
||||||
|
let mut file = File::create(&path).map_err(Error::UnableToCreateFile)?;
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
{
|
||||||
|
use std::os::unix::fs::PermissionsExt;
|
||||||
|
let mut perm = file
|
||||||
|
.metadata()
|
||||||
|
.map_err(Error::UnableToRetrieveMetadata)?
|
||||||
|
.permissions();
|
||||||
|
perm.set_mode(0o600);
|
||||||
|
file.set_permissions(perm)
|
||||||
|
.map_err(Error::UnableToSetPermissions)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
file.write_all(bytes).map_err(Error::UnableToWriteFile)?;
|
||||||
|
#[cfg(windows)]
|
||||||
|
{
|
||||||
|
restrict_file_permissions(path)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn restrict_file_permissions<P: AsRef<Path>>(path: P) -> Result<(), Error> {
|
||||||
|
#[cfg(unix)]
|
||||||
|
{
|
||||||
|
use std::os::unix::fs::PermissionsExt;
|
||||||
|
let file = File::open(path.as_ref()).map_err(Error::UnableToOpenFile)?;
|
||||||
|
let mut perm = file
|
||||||
|
.metadata()
|
||||||
|
.map_err(Error::UnableToRetrieveMetadata)?
|
||||||
|
.permissions();
|
||||||
|
perm.set_mode(0o600);
|
||||||
|
file.set_permissions(perm)
|
||||||
|
.map_err(Error::UnableToSetPermissions)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
{
|
||||||
|
use winapi::um::winnt::PSID;
|
||||||
|
use windows_acl::acl::{AceType, ACL};
|
||||||
|
use windows_acl::helper::sid_to_string;
|
||||||
|
|
||||||
|
let path_str = path
|
||||||
|
.as_ref()
|
||||||
|
.to_str()
|
||||||
|
.ok_or(Error::UnableToObtainFilePath)?;
|
||||||
|
let mut acl = ACL::from_file_path(&path_str, false).map_err(Error::UnableToRetrieveACL)?;
|
||||||
|
|
||||||
|
let owner_sid =
|
||||||
|
windows_acl::helper::string_to_sid(OWNER_SID_STR).map_err(Error::UnableToConvertSID)?;
|
||||||
|
|
||||||
|
let entries = acl.all().map_err(Error::UnableToEnumerateACLEntries)?;
|
||||||
|
|
||||||
|
// add single entry for file owner
|
||||||
|
acl.add_entry(
|
||||||
|
owner_sid.as_ptr() as PSID,
|
||||||
|
AceType::AccessAllow,
|
||||||
|
OWNER_ACL_ENTRY_FLAGS,
|
||||||
|
OWNER_ACL_ENTRY_MASK,
|
||||||
|
)
|
||||||
|
.map_err(|code| {
|
||||||
|
Error::UnableToAddACLEntry(format!(
|
||||||
|
"Failed to add ACL entry for SID {} error={}",
|
||||||
|
OWNER_SID_STR, code
|
||||||
|
))
|
||||||
|
})?;
|
||||||
|
// remove all AccessAllow entries from the file that aren't the owner_sid
|
||||||
|
for entry in &entries {
|
||||||
|
if let Some(ref entry_sid) = entry.sid {
|
||||||
|
let entry_sid_str = sid_to_string(entry_sid.as_ptr() as PSID)
|
||||||
|
.unwrap_or_else(|_| "BadFormat".to_string());
|
||||||
|
if entry_sid_str != OWNER_SID_STR {
|
||||||
|
acl.remove(entry_sid.as_ptr() as PSID, Some(AceType::AccessAllow), None)
|
||||||
|
.map_err(|_| {
|
||||||
|
Error::UnableToRemoveACLEntry(format!(
|
||||||
|
"Failed to remove ACL entry for SID {}",
|
||||||
|
entry_sid_str
|
||||||
|
))
|
||||||
|
})?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
@ -81,13 +81,20 @@ mod test {
|
|||||||
fn new_lock() {
|
fn new_lock() {
|
||||||
let temp = tempdir().unwrap();
|
let temp = tempdir().unwrap();
|
||||||
let path = temp.path().join("lockfile");
|
let path = temp.path().join("lockfile");
|
||||||
|
|
||||||
let _lock = Lockfile::new(path.clone()).unwrap();
|
let _lock = Lockfile::new(path.clone()).unwrap();
|
||||||
|
if cfg!(windows) {
|
||||||
|
assert!(matches!(
|
||||||
|
Lockfile::new(path).unwrap_err(),
|
||||||
|
// windows returns an IoError because the lockfile is already open :/
|
||||||
|
LockfileError::IoError(..),
|
||||||
|
));
|
||||||
|
} else {
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
Lockfile::new(path).unwrap_err(),
|
Lockfile::new(path).unwrap_err(),
|
||||||
LockfileError::FileLocked(..)
|
LockfileError::FileLocked(..)
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn relock_after_drop() {
|
fn relock_after_drop() {
|
||||||
|
@ -18,11 +18,25 @@ mod post {
|
|||||||
match signature.unwrap_err() {
|
match signature.unwrap_err() {
|
||||||
Error::Reqwest(e) => {
|
Error::Reqwest(e) => {
|
||||||
let error_msg = e.to_string();
|
let error_msg = e.to_string();
|
||||||
assert!(error_msg.contains("error sending request for url"));
|
let pubkey_string = PUBLIC_KEY_1.to_string();
|
||||||
assert!(error_msg.contains(PUBLIC_KEY_1));
|
let msgs = vec![
|
||||||
assert!(error_msg.contains("error trying to connect"));
|
"error sending request for url",
|
||||||
assert!(error_msg.contains("tcp connect error"));
|
&pubkey_string,
|
||||||
assert!(error_msg.contains("Connection refused"));
|
"error trying to connect",
|
||||||
|
"tcp connect error",
|
||||||
|
match cfg!(windows) {
|
||||||
|
true => "No connection could be made because the target machine actively refused it",
|
||||||
|
false => "Connection refused",
|
||||||
|
}
|
||||||
|
];
|
||||||
|
for msg in msgs.iter() {
|
||||||
|
assert!(
|
||||||
|
error_msg.contains(msg),
|
||||||
|
"{:?} should contain {:?}",
|
||||||
|
error_msg,
|
||||||
|
msg
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
e => panic!("{:?}", e),
|
e => panic!("{:?}", e),
|
||||||
}
|
}
|
||||||
@ -31,15 +45,15 @@ mod post {
|
|||||||
#[test]
|
#[test]
|
||||||
fn server_error() {
|
fn server_error() {
|
||||||
let (test_signer, tmp_dir) = set_up_api_test_signer_to_sign_message();
|
let (test_signer, tmp_dir) = set_up_api_test_signer_to_sign_message();
|
||||||
set_permissions(tmp_dir.path(), 0o40311);
|
restrict_permissions(tmp_dir.path());
|
||||||
set_permissions(&tmp_dir.path().join(PUBLIC_KEY_1), 0o40311);
|
restrict_permissions(&tmp_dir.path().join(PUBLIC_KEY_1));
|
||||||
|
|
||||||
let test_client = set_up_test_consumer(&test_signer.address);
|
let test_client = set_up_test_consumer(&test_signer.address);
|
||||||
let test_input = get_input_data_block(0xc137);
|
let test_input = get_input_data_block(0xc137);
|
||||||
let signature = do_sign_request(&test_client, test_input);
|
let signature = do_sign_request(&test_client, test_input);
|
||||||
|
|
||||||
set_permissions(tmp_dir.path(), 0o40755);
|
unrestrict_permissions(tmp_dir.path());
|
||||||
set_permissions(&tmp_dir.path().join(PUBLIC_KEY_1), 0o40755);
|
unrestrict_permissions(&tmp_dir.path().join(PUBLIC_KEY_1));
|
||||||
|
|
||||||
match signature.unwrap_err() {
|
match signature.unwrap_err() {
|
||||||
Error::ServerMessage(message) => assert_eq!(message, "Storage error: PermissionDenied"),
|
Error::ServerMessage(message) => assert_eq!(message, "Storage error: PermissionDenied"),
|
||||||
@ -145,7 +159,6 @@ mod post {
|
|||||||
|
|
||||||
let testcase = |u: &str, msgs: Vec<&str>| {
|
let testcase = |u: &str, msgs: Vec<&str>| {
|
||||||
let r = run_testcase(u).unwrap_err();
|
let r = run_testcase(u).unwrap_err();
|
||||||
|
|
||||||
for msg in msgs.iter() {
|
for msg in msgs.iter() {
|
||||||
assert!(r.contains(msg), "{:?} should contain {:?}", r, msg);
|
assert!(r.contains(msg), "{:?} should contain {:?}", r, msg);
|
||||||
}
|
}
|
||||||
@ -159,7 +172,10 @@ mod post {
|
|||||||
&format!("/sign/{}", PUBLIC_KEY_1),
|
&format!("/sign/{}", PUBLIC_KEY_1),
|
||||||
"hyper::Error(Connect, ConnectError",
|
"hyper::Error(Connect, ConnectError",
|
||||||
"dns error",
|
"dns error",
|
||||||
"failed to lookup address information",
|
match cfg!(windows) {
|
||||||
|
true => "No such host is known.",
|
||||||
|
false => "failed to lookup address information",
|
||||||
|
},
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -12,6 +12,7 @@ insecure_keys = []
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
bls = { path = "../../crypto/bls" }
|
bls = { path = "../../crypto/bls" }
|
||||||
eth2_keystore = { path = "../../crypto/eth2_keystore" }
|
eth2_keystore = { path = "../../crypto/eth2_keystore" }
|
||||||
|
filesystem = { path = "../filesystem" }
|
||||||
types = { path = "../../consensus/types" }
|
types = { path = "../../consensus/types" }
|
||||||
rand = "0.7.3"
|
rand = "0.7.3"
|
||||||
deposit_contract = { path = "../deposit_contract" }
|
deposit_contract = { path = "../deposit_contract" }
|
||||||
|
@ -2,10 +2,10 @@ use crate::{Error as DirError, ValidatorDir};
|
|||||||
use bls::get_withdrawal_credentials;
|
use bls::get_withdrawal_credentials;
|
||||||
use deposit_contract::{encode_eth1_tx_data, Error as DepositError};
|
use deposit_contract::{encode_eth1_tx_data, Error as DepositError};
|
||||||
use eth2_keystore::{Error as KeystoreError, Keystore, KeystoreBuilder, PlainText};
|
use eth2_keystore::{Error as KeystoreError, Keystore, KeystoreBuilder, PlainText};
|
||||||
|
use filesystem::create_with_600_perms;
|
||||||
use rand::{distributions::Alphanumeric, Rng};
|
use rand::{distributions::Alphanumeric, Rng};
|
||||||
use std::fs::{create_dir_all, File, OpenOptions};
|
use std::fs::{create_dir_all, OpenOptions};
|
||||||
use std::io::{self, Write};
|
use std::io::{self, Write};
|
||||||
use std::os::unix::fs::PermissionsExt;
|
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use types::{ChainSpec, DepositData, Hash256, Keypair, Signature};
|
use types::{ChainSpec, DepositData, Hash256, Keypair, Signature};
|
||||||
|
|
||||||
@ -33,7 +33,7 @@ pub enum Error {
|
|||||||
KeystoreAlreadyExists(PathBuf),
|
KeystoreAlreadyExists(PathBuf),
|
||||||
UnableToSaveKeystore(io::Error),
|
UnableToSaveKeystore(io::Error),
|
||||||
PasswordAlreadyExists(PathBuf),
|
PasswordAlreadyExists(PathBuf),
|
||||||
UnableToSavePassword(io::Error),
|
UnableToSavePassword(filesystem::Error),
|
||||||
KeystoreError(KeystoreError),
|
KeystoreError(KeystoreError),
|
||||||
UnableToOpenDir(DirError),
|
UnableToOpenDir(DirError),
|
||||||
UninitializedVotingKeystore,
|
UninitializedVotingKeystore,
|
||||||
@ -283,19 +283,7 @@ pub fn write_password_to_file<P: AsRef<Path>>(path: P, bytes: &[u8]) -> Result<(
|
|||||||
return Err(Error::PasswordAlreadyExists(path.into()));
|
return Err(Error::PasswordAlreadyExists(path.into()));
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut file = File::create(&path).map_err(Error::UnableToSavePassword)?;
|
create_with_600_perms(path, bytes).map_err(Error::UnableToSavePassword)?;
|
||||||
|
|
||||||
let mut perm = file
|
|
||||||
.metadata()
|
|
||||||
.map_err(Error::UnableToSavePassword)?
|
|
||||||
.permissions();
|
|
||||||
|
|
||||||
perm.set_mode(0o600);
|
|
||||||
|
|
||||||
file.set_permissions(perm)
|
|
||||||
.map_err(Error::UnableToSavePassword)?;
|
|
||||||
|
|
||||||
file.write_all(bytes).map_err(Error::UnableToSavePassword)?;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -681,6 +681,7 @@ fn compact_db_flag() {
|
|||||||
fn slasher_flag() {
|
fn slasher_flag() {
|
||||||
CommandLineTest::new()
|
CommandLineTest::new()
|
||||||
.flag("slasher", None)
|
.flag("slasher", None)
|
||||||
|
.flag("slasher-max-db-size", Some("16"))
|
||||||
.run()
|
.run()
|
||||||
.with_config_and_dir(|config, dir| {
|
.with_config_and_dir(|config, dir| {
|
||||||
if let Some(slasher_config) = &config.slasher {
|
if let Some(slasher_config) = &config.slasher {
|
||||||
@ -699,6 +700,7 @@ fn slasher_dir_flag() {
|
|||||||
CommandLineTest::new()
|
CommandLineTest::new()
|
||||||
.flag("slasher", None)
|
.flag("slasher", None)
|
||||||
.flag("slasher-dir", dir.path().as_os_str().to_str())
|
.flag("slasher-dir", dir.path().as_os_str().to_str())
|
||||||
|
.flag("slasher-max-db-size", Some("16"))
|
||||||
.run()
|
.run()
|
||||||
.with_config(|config| {
|
.with_config(|config| {
|
||||||
if let Some(slasher_config) = &config.slasher {
|
if let Some(slasher_config) = &config.slasher {
|
||||||
@ -712,6 +714,7 @@ fn slasher_dir_flag() {
|
|||||||
fn slasher_update_period_flag() {
|
fn slasher_update_period_flag() {
|
||||||
CommandLineTest::new()
|
CommandLineTest::new()
|
||||||
.flag("slasher", None)
|
.flag("slasher", None)
|
||||||
|
.flag("slasher-max-db-size", Some("16"))
|
||||||
.flag("slasher-update-period", Some("100"))
|
.flag("slasher-update-period", Some("100"))
|
||||||
.run()
|
.run()
|
||||||
.with_config(|config| {
|
.with_config(|config| {
|
||||||
@ -726,6 +729,7 @@ fn slasher_update_period_flag() {
|
|||||||
fn slasher_history_length_flag() {
|
fn slasher_history_length_flag() {
|
||||||
CommandLineTest::new()
|
CommandLineTest::new()
|
||||||
.flag("slasher", None)
|
.flag("slasher", None)
|
||||||
|
.flag("slasher-max-db-size", Some("16"))
|
||||||
.flag("slasher-history-length", Some("2048"))
|
.flag("slasher-history-length", Some("2048"))
|
||||||
.run()
|
.run()
|
||||||
.with_config(|config| {
|
.with_config(|config| {
|
||||||
@ -755,6 +759,7 @@ fn slasher_chunk_size_flag() {
|
|||||||
CommandLineTest::new()
|
CommandLineTest::new()
|
||||||
.flag("slasher", None)
|
.flag("slasher", None)
|
||||||
.flag("slasher-chunk-size", Some("32"))
|
.flag("slasher-chunk-size", Some("32"))
|
||||||
|
.flag("slasher-max-db-size", Some("16"))
|
||||||
.run()
|
.run()
|
||||||
.with_config(|config| {
|
.with_config(|config| {
|
||||||
let slasher_config = config
|
let slasher_config = config
|
||||||
@ -768,6 +773,7 @@ fn slasher_chunk_size_flag() {
|
|||||||
fn slasher_validator_chunk_size_flag() {
|
fn slasher_validator_chunk_size_flag() {
|
||||||
CommandLineTest::new()
|
CommandLineTest::new()
|
||||||
.flag("slasher", None)
|
.flag("slasher", None)
|
||||||
|
.flag("slasher-max-db-size", Some("16"))
|
||||||
.flag("slasher-validator-chunk-size", Some("512"))
|
.flag("slasher-validator-chunk-size", Some("512"))
|
||||||
.run()
|
.run()
|
||||||
.with_config(|config| {
|
.with_config(|config| {
|
||||||
@ -783,6 +789,7 @@ fn slasher_broadcast_flag() {
|
|||||||
CommandLineTest::new()
|
CommandLineTest::new()
|
||||||
.flag("slasher", None)
|
.flag("slasher", None)
|
||||||
.flag("slasher-broadcast", None)
|
.flag("slasher-broadcast", None)
|
||||||
|
.flag("slasher-max-db-size", Some("16"))
|
||||||
.run()
|
.run()
|
||||||
.with_config(|config| {
|
.with_config(|config| {
|
||||||
let slasher_config = config
|
let slasher_config = config
|
||||||
|
@ -163,7 +163,14 @@ pub mod backend_new {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn given_path_is_not_a_dir() {
|
fn given_path_is_not_a_dir() {
|
||||||
let matches = set_matches(vec!["this_test", "--storage-raw-dir", "/dev/null"]);
|
let matches = set_matches(vec![
|
||||||
|
"this_test",
|
||||||
|
"--storage-raw-dir",
|
||||||
|
match cfg!(windows) {
|
||||||
|
true => "C:\\Windows\\system.ini",
|
||||||
|
false => "/dev/null",
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
assert_backend_new_error(&matches, "Storage Raw Dir: Path is not a directory.");
|
assert_backend_new_error(&matches, "Storage Raw Dir: Path is not a directory.");
|
||||||
}
|
}
|
||||||
@ -171,7 +178,7 @@ pub mod backend_new {
|
|||||||
#[test]
|
#[test]
|
||||||
fn given_inaccessible() {
|
fn given_inaccessible() {
|
||||||
let tmp_dir = tempdir().unwrap();
|
let tmp_dir = tempdir().unwrap();
|
||||||
set_permissions(tmp_dir.path(), 0o40311);
|
restrict_permissions(tmp_dir.path());
|
||||||
|
|
||||||
let matches = set_matches(vec![
|
let matches = set_matches(vec![
|
||||||
"this_test",
|
"this_test",
|
||||||
@ -184,7 +191,7 @@ pub mod backend_new {
|
|||||||
// A `d-wx--x--x` directory is innaccesible but not unwrittable.
|
// A `d-wx--x--x` directory is innaccesible but not unwrittable.
|
||||||
// By switching back to `drwxr-xr-x` we can get rid of the
|
// By switching back to `drwxr-xr-x` we can get rid of the
|
||||||
// temporal directory once we leave this scope.
|
// temporal directory once we leave this scope.
|
||||||
set_permissions(tmp_dir.path(), 0o40755);
|
unrestrict_permissions(tmp_dir.path());
|
||||||
|
|
||||||
match result {
|
match result {
|
||||||
Ok(_) => panic!("This invocation to Backend::new() should return error"),
|
Ok(_) => panic!("This invocation to Backend::new() should return error"),
|
||||||
@ -263,16 +270,16 @@ pub mod backend_raw_dir_sign_message {
|
|||||||
fn storage_error() {
|
fn storage_error() {
|
||||||
let (backend, tmp_dir) = new_backend_for_signing();
|
let (backend, tmp_dir) = new_backend_for_signing();
|
||||||
|
|
||||||
set_permissions(tmp_dir.path(), 0o40311);
|
restrict_permissions(tmp_dir.path());
|
||||||
set_permissions(&tmp_dir.path().join(PUBLIC_KEY_1), 0o40311);
|
restrict_permissions(&tmp_dir.path().join(PUBLIC_KEY_1));
|
||||||
|
|
||||||
let result = backend.sign_message(
|
let result = backend.sign_message(
|
||||||
PUBLIC_KEY_1,
|
PUBLIC_KEY_1,
|
||||||
Hash256::from_slice(&hex::decode(SIGNING_ROOT).unwrap()),
|
Hash256::from_slice(&hex::decode(SIGNING_ROOT).unwrap()),
|
||||||
);
|
);
|
||||||
|
|
||||||
set_permissions(tmp_dir.path(), 0o40755);
|
unrestrict_permissions(tmp_dir.path());
|
||||||
set_permissions(&tmp_dir.path().join(PUBLIC_KEY_1), 0o40755);
|
unrestrict_permissions(&tmp_dir.path().join(PUBLIC_KEY_1));
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
result.unwrap_err().to_string(),
|
result.unwrap_err().to_string(),
|
||||||
|
@ -83,12 +83,12 @@ mod get_keys {
|
|||||||
add_key_files(&tmp_dir);
|
add_key_files(&tmp_dir);
|
||||||
|
|
||||||
// All good and fancy, let's make the dir innacessible now.
|
// All good and fancy, let's make the dir innacessible now.
|
||||||
set_permissions(tmp_dir.path(), 0o40311);
|
restrict_permissions(tmp_dir.path());
|
||||||
|
|
||||||
let result = storage.get_keys();
|
let result = storage.get_keys();
|
||||||
|
|
||||||
// Give permissions back, we want the tempdir to be deleted.
|
// Give permissions back, we want the tempdir to be deleted.
|
||||||
set_permissions(tmp_dir.path(), 0o40755);
|
unrestrict_permissions(tmp_dir.path());
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
result.unwrap_err().to_string(),
|
result.unwrap_err().to_string(),
|
||||||
@ -141,13 +141,13 @@ mod get_secret_key {
|
|||||||
let (storage, tmp_dir) = new_storage_with_tmp_dir();
|
let (storage, tmp_dir) = new_storage_with_tmp_dir();
|
||||||
add_key_files(&tmp_dir);
|
add_key_files(&tmp_dir);
|
||||||
|
|
||||||
set_permissions(tmp_dir.path(), 0o40311);
|
restrict_permissions(tmp_dir.path());
|
||||||
set_permissions(&tmp_dir.path().join(PUBLIC_KEY_1), 0o40311);
|
restrict_permissions(&tmp_dir.path().join(PUBLIC_KEY_1));
|
||||||
|
|
||||||
let result = storage.get_secret_key(PUBLIC_KEY_1);
|
let result = storage.get_secret_key(PUBLIC_KEY_1);
|
||||||
|
|
||||||
set_permissions(tmp_dir.path(), 0o40755);
|
unrestrict_permissions(tmp_dir.path());
|
||||||
set_permissions(&tmp_dir.path().join(PUBLIC_KEY_1), 0o40755);
|
unrestrict_permissions(&tmp_dir.path().join(PUBLIC_KEY_1));
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
result.unwrap_err().to_string(),
|
result.unwrap_err().to_string(),
|
||||||
|
@ -68,14 +68,14 @@ mod get_keys {
|
|||||||
add_non_key_files(&tmp_dir);
|
add_non_key_files(&tmp_dir);
|
||||||
|
|
||||||
// Somebody tripped over a wire.
|
// Somebody tripped over a wire.
|
||||||
set_permissions(tmp_dir.path(), 0o40311);
|
restrict_permissions(tmp_dir.path());
|
||||||
|
|
||||||
let url = format!("{}/keys", test_signer.address);
|
let url = format!("{}/keys", test_signer.address);
|
||||||
|
|
||||||
let resp = http_get(&url);
|
let resp = http_get(&url);
|
||||||
|
|
||||||
// Be able to delete the tempdir afterward, regardless of this test result.
|
// Be able to delete the tempdir afterward, regardless of this test result.
|
||||||
set_permissions(tmp_dir.path(), 0o40755);
|
unrestrict_permissions(tmp_dir.path());
|
||||||
|
|
||||||
assert_error(resp, 500, "Storage error: PermissionDenied");
|
assert_error(resp, 500, "Storage error: PermissionDenied");
|
||||||
|
|
||||||
|
@ -20,14 +20,14 @@ mod sign {
|
|||||||
fn storage_error() {
|
fn storage_error() {
|
||||||
let (test_signer, tmp_dir) = set_up_api_test_signer_to_sign_message();
|
let (test_signer, tmp_dir) = set_up_api_test_signer_to_sign_message();
|
||||||
let test_block_body = get_test_block_body(0xc137);
|
let test_block_body = get_test_block_body(0xc137);
|
||||||
set_permissions(tmp_dir.path(), 0o40311);
|
restrict_permissions(tmp_dir.path());
|
||||||
set_permissions(&tmp_dir.path().join(PUBLIC_KEY_1), 0o40311);
|
restrict_permissions(&tmp_dir.path().join(PUBLIC_KEY_1));
|
||||||
|
|
||||||
let url = format!("{}/sign/{}", test_signer.address, PUBLIC_KEY_1);
|
let url = format!("{}/sign/{}", test_signer.address, PUBLIC_KEY_1);
|
||||||
|
|
||||||
let response = http_post_custom_body(&url, &test_block_body);
|
let response = http_post_custom_body(&url, &test_block_body);
|
||||||
set_permissions(tmp_dir.path(), 0o40755);
|
unrestrict_permissions(tmp_dir.path());
|
||||||
set_permissions(&tmp_dir.path().join(PUBLIC_KEY_1), 0o40755);
|
unrestrict_permissions(&tmp_dir.path().join(PUBLIC_KEY_1));
|
||||||
|
|
||||||
assert_sign_error(response, 500, "Storage error: PermissionDenied");
|
assert_sign_error(response, 500, "Storage error: PermissionDenied");
|
||||||
|
|
||||||
|
@ -12,6 +12,7 @@ eth2_ssz_derive = { path = "../consensus/ssz_derive" }
|
|||||||
flate2 = { version = "1.0.14", features = ["zlib"], default-features = false }
|
flate2 = { version = "1.0.14", features = ["zlib"], default-features = false }
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
lighthouse_metrics = { path = "../common/lighthouse_metrics" }
|
lighthouse_metrics = { path = "../common/lighthouse_metrics" }
|
||||||
|
filesystem = { path = "../common/filesystem" }
|
||||||
lmdb = "0.8"
|
lmdb = "0.8"
|
||||||
lmdb-sys = "0.8"
|
lmdb-sys = "0.8"
|
||||||
parking_lot = "0.11.0"
|
parking_lot = "0.11.0"
|
||||||
|
@ -10,6 +10,13 @@ pub const DEFAULT_UPDATE_PERIOD: u64 = 12;
|
|||||||
pub const DEFAULT_MAX_DB_SIZE: usize = 256 * 1024; // 256 GiB
|
pub const DEFAULT_MAX_DB_SIZE: usize = 256 * 1024; // 256 GiB
|
||||||
pub const DEFAULT_BROADCAST: bool = false;
|
pub const DEFAULT_BROADCAST: bool = false;
|
||||||
|
|
||||||
|
/// Database size to use for tests.
|
||||||
|
///
|
||||||
|
/// Mostly a workaround for Windows due to a bug in LMDB, see:
|
||||||
|
///
|
||||||
|
/// https://github.com/sigp/lighthouse/issues/2342
|
||||||
|
pub const TESTING_MAX_DB_SIZE: usize = 16; // MiB
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
pub database_path: PathBuf,
|
pub database_path: PathBuf,
|
||||||
@ -38,6 +45,12 @@ impl Config {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Use a smaller max DB size for testing.
|
||||||
|
pub fn for_testing(mut self) -> Self {
|
||||||
|
self.max_db_size_mbs = TESTING_MAX_DB_SIZE;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
pub fn validate(&self) -> Result<(), Error> {
|
pub fn validate(&self) -> Result<(), Error> {
|
||||||
if self.chunk_size == 0
|
if self.chunk_size == 0
|
||||||
|| self.validator_chunk_size == 0
|
|| self.validator_chunk_size == 0
|
||||||
|
@ -195,6 +195,15 @@ impl<E: EthSpec> SlasherDB<E> {
|
|||||||
let proposers_db = env.create_db(Some(PROPOSERS_DB), Self::db_flags())?;
|
let proposers_db = env.create_db(Some(PROPOSERS_DB), Self::db_flags())?;
|
||||||
let metadata_db = env.create_db(Some(METADATA_DB), Self::db_flags())?;
|
let metadata_db = env.create_db(Some(METADATA_DB), Self::db_flags())?;
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
{
|
||||||
|
use filesystem::restrict_file_permissions;
|
||||||
|
let data = config.database_path.join("data.mdb");
|
||||||
|
let lock = config.database_path.join("lock.mdb");
|
||||||
|
restrict_file_permissions(data).map_err(Error::DatabasePermissionsError)?;
|
||||||
|
restrict_file_permissions(lock).map_err(Error::DatabasePermissionsError)?;
|
||||||
|
}
|
||||||
|
|
||||||
let db = Self {
|
let db = Self {
|
||||||
env,
|
env,
|
||||||
indexed_attestation_db,
|
indexed_attestation_db,
|
||||||
|
@ -6,6 +6,7 @@ use types::{Epoch, Hash256};
|
|||||||
pub enum Error {
|
pub enum Error {
|
||||||
DatabaseError(lmdb::Error),
|
DatabaseError(lmdb::Error),
|
||||||
DatabaseIOError(io::Error),
|
DatabaseIOError(io::Error),
|
||||||
|
DatabasePermissionsError(filesystem::Error),
|
||||||
SszDecodeError(ssz::DecodeError),
|
SszDecodeError(ssz::DecodeError),
|
||||||
BincodeError(bincode::Error),
|
BincodeError(bincode::Error),
|
||||||
ArithError(safe_arith::ArithError),
|
ArithError(safe_arith::ArithError),
|
||||||
|
@ -170,7 +170,7 @@ fn slasher_test(
|
|||||||
should_process_after: impl Fn(usize) -> bool,
|
should_process_after: impl Fn(usize) -> bool,
|
||||||
) {
|
) {
|
||||||
let tempdir = tempdir().unwrap();
|
let tempdir = tempdir().unwrap();
|
||||||
let config = Config::new(tempdir.path().into());
|
let config = Config::new(tempdir.path().into()).for_testing();
|
||||||
let slasher = Slasher::open(config, logger()).unwrap();
|
let slasher = Slasher::open(config, logger()).unwrap();
|
||||||
let current_epoch = Epoch::new(current_epoch);
|
let current_epoch = Epoch::new(current_epoch);
|
||||||
|
|
||||||
@ -189,6 +189,8 @@ fn slasher_test(
|
|||||||
|
|
||||||
// Pruning should not error.
|
// Pruning should not error.
|
||||||
slasher.prune_database(current_epoch).unwrap();
|
slasher.prune_database(current_epoch).unwrap();
|
||||||
|
// windows won't delete the temporary directory if you don't do this..
|
||||||
|
drop(slasher);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parallel_slasher_test(
|
fn parallel_slasher_test(
|
||||||
@ -197,7 +199,7 @@ fn parallel_slasher_test(
|
|||||||
current_epoch: u64,
|
current_epoch: u64,
|
||||||
) {
|
) {
|
||||||
let tempdir = tempdir().unwrap();
|
let tempdir = tempdir().unwrap();
|
||||||
let config = Config::new(tempdir.path().into());
|
let config = Config::new(tempdir.path().into()).for_testing();
|
||||||
let slasher = Slasher::open(config, logger()).unwrap();
|
let slasher = Slasher::open(config, logger()).unwrap();
|
||||||
let current_epoch = Epoch::new(current_epoch);
|
let current_epoch = Epoch::new(current_epoch);
|
||||||
|
|
||||||
@ -212,4 +214,6 @@ fn parallel_slasher_test(
|
|||||||
let slashings = slasher.get_attester_slashings();
|
let slashings = slasher.get_attester_slashings();
|
||||||
let slashed_validators = slashed_validators_from_slashings(&slashings);
|
let slashed_validators = slashed_validators_from_slashings(&slashings);
|
||||||
assert_eq!(slashed_validators, expected_slashed_validators);
|
assert_eq!(slashed_validators, expected_slashed_validators);
|
||||||
|
// windows won't delete the temporary directory if you don't do this..
|
||||||
|
drop(slasher);
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ use types::{Epoch, EthSpec};
|
|||||||
#[test]
|
#[test]
|
||||||
fn empty_pruning() {
|
fn empty_pruning() {
|
||||||
let tempdir = tempdir().unwrap();
|
let tempdir = tempdir().unwrap();
|
||||||
let config = Config::new(tempdir.path().into());
|
let config = Config::new(tempdir.path().into()).for_testing();
|
||||||
let slasher = Slasher::<E>::open(config, logger()).unwrap();
|
let slasher = Slasher::<E>::open(config, logger()).unwrap();
|
||||||
slasher.prune_database(Epoch::new(0)).unwrap();
|
slasher.prune_database(Epoch::new(0)).unwrap();
|
||||||
}
|
}
|
||||||
@ -18,7 +18,7 @@ fn block_pruning() {
|
|||||||
let slots_per_epoch = E::slots_per_epoch();
|
let slots_per_epoch = E::slots_per_epoch();
|
||||||
|
|
||||||
let tempdir = tempdir().unwrap();
|
let tempdir = tempdir().unwrap();
|
||||||
let mut config = Config::new(tempdir.path().into());
|
let mut config = Config::new(tempdir.path().into()).for_testing();
|
||||||
config.chunk_size = 2;
|
config.chunk_size = 2;
|
||||||
config.history_length = 2;
|
config.history_length = 2;
|
||||||
|
|
||||||
|
@ -40,7 +40,7 @@ fn random_test(seed: u64, test_config: TestConfig) {
|
|||||||
|
|
||||||
let tempdir = tempdir().unwrap();
|
let tempdir = tempdir().unwrap();
|
||||||
|
|
||||||
let mut config = Config::new(tempdir.path().into());
|
let mut config = Config::new(tempdir.path().into()).for_testing();
|
||||||
config.validator_chunk_size = 1 << rng.gen_range(1, 4);
|
config.validator_chunk_size = 1 << rng.gen_range(1, 4);
|
||||||
|
|
||||||
let chunk_size_exponent = rng.gen_range(1, 4);
|
let chunk_size_exponent = rng.gen_range(1, 4);
|
||||||
|
@ -8,7 +8,7 @@ use types::Epoch;
|
|||||||
#[test]
|
#[test]
|
||||||
fn attestation_pruning_empty_wrap_around() {
|
fn attestation_pruning_empty_wrap_around() {
|
||||||
let tempdir = tempdir().unwrap();
|
let tempdir = tempdir().unwrap();
|
||||||
let mut config = Config::new(tempdir.path().into());
|
let mut config = Config::new(tempdir.path().into()).for_testing();
|
||||||
config.validator_chunk_size = 1;
|
config.validator_chunk_size = 1;
|
||||||
config.chunk_size = 16;
|
config.chunk_size = 16;
|
||||||
config.history_length = 16;
|
config.history_length = 16;
|
||||||
@ -42,7 +42,7 @@ fn attestation_pruning_empty_wrap_around() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn pruning_with_map_full() {
|
fn pruning_with_map_full() {
|
||||||
let tempdir = tempdir().unwrap();
|
let tempdir = tempdir().unwrap();
|
||||||
let mut config = Config::new(tempdir.path().into());
|
let mut config = Config::new(tempdir.path().into()).for_testing();
|
||||||
config.validator_chunk_size = 1;
|
config.validator_chunk_size = 1;
|
||||||
config.chunk_size = 16;
|
config.chunk_size = 16;
|
||||||
config.history_length = 1024;
|
config.history_length = 1024;
|
||||||
|
@ -74,8 +74,11 @@ impl GanacheInstance {
|
|||||||
/// RPC connections.
|
/// RPC connections.
|
||||||
pub fn new(network_id: u64, chain_id: u64) -> Result<Self, String> {
|
pub fn new(network_id: u64, chain_id: u64) -> Result<Self, String> {
|
||||||
let port = unused_port()?;
|
let port = unused_port()?;
|
||||||
|
let binary = match cfg!(windows) {
|
||||||
let child = Command::new("ganache-cli")
|
true => "ganache-cli.cmd",
|
||||||
|
false => "ganache-cli",
|
||||||
|
};
|
||||||
|
let child = Command::new(binary)
|
||||||
.stdout(Stdio::piped())
|
.stdout(Stdio::piped())
|
||||||
.arg("--defaultBalanceEther")
|
.arg("--defaultBalanceEther")
|
||||||
.arg("1000000000")
|
.arg("1000000000")
|
||||||
@ -83,6 +86,8 @@ impl GanacheInstance {
|
|||||||
.arg("1000000000")
|
.arg("1000000000")
|
||||||
.arg("--accounts")
|
.arg("--accounts")
|
||||||
.arg("10")
|
.arg("10")
|
||||||
|
.arg("--keepAliveTimeout")
|
||||||
|
.arg("0")
|
||||||
.arg("--port")
|
.arg("--port")
|
||||||
.arg(format!("{}", port))
|
.arg(format!("{}", port))
|
||||||
.arg("--mnemonic")
|
.arg("--mnemonic")
|
||||||
@ -94,9 +99,9 @@ impl GanacheInstance {
|
|||||||
.spawn()
|
.spawn()
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
format!(
|
format!(
|
||||||
"Failed to start ganache-cli. \
|
"Failed to start {}. \
|
||||||
Is it ganache-cli installed and available on $PATH? Error: {:?}",
|
Is it installed and available on $PATH? Error: {:?}",
|
||||||
e
|
binary, e
|
||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
@ -105,21 +110,26 @@ impl GanacheInstance {
|
|||||||
|
|
||||||
pub fn fork(&self) -> Result<Self, String> {
|
pub fn fork(&self) -> Result<Self, String> {
|
||||||
let port = unused_port()?;
|
let port = unused_port()?;
|
||||||
|
let binary = match cfg!(windows) {
|
||||||
let child = Command::new("ganache-cli")
|
true => "ganache-cli.cmd",
|
||||||
|
false => "ganache-cli",
|
||||||
|
};
|
||||||
|
let child = Command::new(binary)
|
||||||
.stdout(Stdio::piped())
|
.stdout(Stdio::piped())
|
||||||
.arg("--fork")
|
.arg("--fork")
|
||||||
.arg(self.endpoint())
|
.arg(self.endpoint())
|
||||||
.arg("--port")
|
.arg("--port")
|
||||||
.arg(format!("{}", port))
|
.arg(format!("{}", port))
|
||||||
|
.arg("--keepAliveTimeout")
|
||||||
|
.arg("0")
|
||||||
.arg("--chainId")
|
.arg("--chainId")
|
||||||
.arg(format!("{}", self.chain_id))
|
.arg(format!("{}", self.chain_id))
|
||||||
.spawn()
|
.spawn()
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
format!(
|
format!(
|
||||||
"Failed to start ganache-cli. \
|
"Failed to start {}. \
|
||||||
Is it ganache-cli installed and available on $PATH? Error: {:?}",
|
Is it installed and available on $PATH? Error: {:?}",
|
||||||
e
|
binary, e
|
||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
@ -202,6 +212,23 @@ pub fn unused_port() -> Result<u16, String> {
|
|||||||
|
|
||||||
impl Drop for GanacheInstance {
|
impl Drop for GanacheInstance {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
|
if cfg!(windows) {
|
||||||
|
// Calling child.kill() in Windows will only kill the process
|
||||||
|
// that spawned ganache, leaving the actual ganache process
|
||||||
|
// intact. You have to kill the whole process tree. What's more,
|
||||||
|
// if you don't spawn ganache with --keepAliveTimeout=0, Windows
|
||||||
|
// will STILL keep the server running even after you've ended
|
||||||
|
// the process tree and it's disappeared from the task manager.
|
||||||
|
// Unbelievable...
|
||||||
|
Command::new("taskkill")
|
||||||
|
.arg("/pid")
|
||||||
|
.arg(self.child.id().to_string())
|
||||||
|
.arg("/T")
|
||||||
|
.arg("/F")
|
||||||
|
.output()
|
||||||
|
.expect("failed to execute taskkill");
|
||||||
|
} else {
|
||||||
let _ = self.child.kill();
|
let _ = self.child.kill();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,3 +18,8 @@ tempfile = "3.1.0"
|
|||||||
tokio = { version = "1.1.0", features = ["time"] }
|
tokio = { version = "1.1.0", features = ["time"] }
|
||||||
types = { path = "../../consensus/types" }
|
types = { path = "../../consensus/types" }
|
||||||
sensitive_url = { path = "../../common/sensitive_url" }
|
sensitive_url = { path = "../../common/sensitive_url" }
|
||||||
|
|
||||||
|
[target.'cfg(windows)'.dependencies]
|
||||||
|
winapi = "~0.3.5"
|
||||||
|
windows-acl = "~0.3.0"
|
||||||
|
|
||||||
|
@ -5,11 +5,9 @@ pub use local_signer_test_data::*;
|
|||||||
pub use mock::*;
|
pub use mock::*;
|
||||||
use remote_signer_client::Client;
|
use remote_signer_client::Client;
|
||||||
pub use remote_signer_test_data::*;
|
pub use remote_signer_test_data::*;
|
||||||
use std::fs;
|
|
||||||
use std::fs::{create_dir, File};
|
use std::fs::{create_dir, File};
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::net::IpAddr::{V4, V6};
|
use std::net::IpAddr::{V4, V6};
|
||||||
use std::os::unix::fs::PermissionsExt;
|
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use tempfile::TempDir;
|
use tempfile::TempDir;
|
||||||
use types::{
|
use types::{
|
||||||
@ -18,6 +16,32 @@ use types::{
|
|||||||
Hash256, IndexedAttestation, ProposerSlashing, PublicKeyBytes, Signature, SignatureBytes,
|
Hash256, IndexedAttestation, ProposerSlashing, PublicKeyBytes, Signature, SignatureBytes,
|
||||||
SignedBeaconBlockHeader, SignedVoluntaryExit, Slot, Unsigned, VariableList, VoluntaryExit,
|
SignedBeaconBlockHeader, SignedVoluntaryExit, Slot, Unsigned, VariableList, VoluntaryExit,
|
||||||
};
|
};
|
||||||
|
#[cfg(windows)]
|
||||||
|
use winapi::um::winnt::{
|
||||||
|
FILE_GENERIC_READ, FILE_GENERIC_WRITE, FILE_READ_ATTRIBUTES, FILE_READ_EA, READ_CONTROL,
|
||||||
|
STANDARD_RIGHTS_ALL, SYNCHRONIZE, WRITE_DAC,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// This is the security identifier in Windows for the owner of a file. See:
|
||||||
|
/// - https://docs.microsoft.com/en-us/troubleshoot/windows-server/identity/security-identifiers-in-windows#well-known-sids-all-versions-of-windows
|
||||||
|
#[cfg(windows)]
|
||||||
|
const OWNER_SID_STR: &str = "S-1-3-4";
|
||||||
|
/// We don't need any of the `AceFlags` listed here:
|
||||||
|
/// - https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-ace_header
|
||||||
|
#[cfg(windows)]
|
||||||
|
const OWNER_ACL_ENTRY_FLAGS: u8 = 0;
|
||||||
|
/// See here for explanation:
|
||||||
|
/// - https://docs.microsoft.com/en-us/windows/win32/wmisdk/file-and-directory-access-rights-constants
|
||||||
|
#[cfg(windows)]
|
||||||
|
const OWNER_ACL_ENTRY_RESTRICT_MASK: u32 =
|
||||||
|
FILE_READ_ATTRIBUTES | FILE_READ_EA | READ_CONTROL | WRITE_DAC | SYNCHRONIZE;
|
||||||
|
/// Generic Rights:
|
||||||
|
/// - https://docs.microsoft.com/en-us/windows/win32/fileio/file-security-and-access-rights
|
||||||
|
/// STANDARD_RIGHTS_ALL
|
||||||
|
/// - https://docs.microsoft.com/en-us/windows/win32/secauthz/access-mask
|
||||||
|
#[cfg(windows)]
|
||||||
|
const OWNER_ACL_ENTRY_UNRESTRICT_MASK: u32 =
|
||||||
|
FILE_GENERIC_READ | FILE_GENERIC_WRITE | STANDARD_RIGHTS_ALL;
|
||||||
|
|
||||||
pub fn get_address(client: &Client) -> String {
|
pub fn get_address(client: &Client) -> String {
|
||||||
let listening_address = client.get_listening_address();
|
let listening_address = client.get_listening_address();
|
||||||
@ -29,11 +53,78 @@ pub fn get_address(client: &Client) -> String {
|
|||||||
format!("http://{}:{}", ip, listening_address.port())
|
format!("http://{}:{}", ip, listening_address.port())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_permissions(path: &Path, perm_octal: u32) {
|
pub fn restrict_permissions(path: &Path) {
|
||||||
|
#[cfg(unix)]
|
||||||
|
{
|
||||||
|
use std::fs;
|
||||||
|
use std::os::unix::fs::PermissionsExt;
|
||||||
|
|
||||||
let metadata = fs::metadata(path).unwrap();
|
let metadata = fs::metadata(path).unwrap();
|
||||||
let mut permissions = metadata.permissions();
|
let mut permissions = metadata.permissions();
|
||||||
permissions.set_mode(perm_octal);
|
permissions.set_mode(0o0311); // set to '*-wx--x--x'
|
||||||
fs::set_permissions(path, permissions).unwrap();
|
fs::set_permissions(path, permissions).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
{
|
||||||
|
use winapi::um::winnt::PSID;
|
||||||
|
use windows_acl::acl::{AceType, ACL};
|
||||||
|
|
||||||
|
let path_str = path.to_str().unwrap();
|
||||||
|
let mut acl = ACL::from_file_path(&path_str, false).unwrap();
|
||||||
|
|
||||||
|
let owner_sid = windows_acl::helper::string_to_sid(OWNER_SID_STR).unwrap();
|
||||||
|
let entries = acl.all().unwrap();
|
||||||
|
// remove all AccessAllow entries
|
||||||
|
for entry in &entries {
|
||||||
|
if let Some(ref entry_sid) = entry.sid {
|
||||||
|
acl.remove(entry_sid.as_ptr() as PSID, Some(AceType::AccessAllow), None)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// add single entry for minimal access to file owner
|
||||||
|
// allowing them only to read attributes of the file
|
||||||
|
// and read/modify permissions
|
||||||
|
acl.add_entry(
|
||||||
|
owner_sid.as_ptr() as PSID,
|
||||||
|
AceType::AccessAllow,
|
||||||
|
OWNER_ACL_ENTRY_FLAGS,
|
||||||
|
OWNER_ACL_ENTRY_RESTRICT_MASK,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn unrestrict_permissions(path: &Path) {
|
||||||
|
#[cfg(unix)]
|
||||||
|
{
|
||||||
|
use std::fs;
|
||||||
|
use std::os::unix::fs::PermissionsExt;
|
||||||
|
|
||||||
|
let metadata = fs::metadata(path).unwrap();
|
||||||
|
let mut permissions = metadata.permissions();
|
||||||
|
permissions.set_mode(0o0755); // set to '*rwxr-xr-x'
|
||||||
|
fs::set_permissions(path, permissions).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
{
|
||||||
|
use winapi::um::winnt::PSID;
|
||||||
|
use windows_acl::acl::{AceType, ACL};
|
||||||
|
|
||||||
|
let path_str = path.to_str().unwrap();
|
||||||
|
let mut acl = ACL::from_file_path(&path_str, false).unwrap();
|
||||||
|
|
||||||
|
let owner_sid = windows_acl::helper::string_to_sid(OWNER_SID_STR).unwrap();
|
||||||
|
// add single entry for file owner
|
||||||
|
acl.add_entry(
|
||||||
|
owner_sid.as_ptr() as PSID,
|
||||||
|
AceType::AccessAllow,
|
||||||
|
OWNER_ACL_ENTRY_FLAGS,
|
||||||
|
OWNER_ACL_ENTRY_UNRESTRICT_MASK,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_key_files(tmp_dir: &TempDir) {
|
pub fn add_key_files(tmp_dir: &TempDir) {
|
||||||
|
@ -39,6 +39,7 @@ logging = { path = "../common/logging" }
|
|||||||
environment = { path = "../lighthouse/environment" }
|
environment = { path = "../lighthouse/environment" }
|
||||||
parking_lot = "0.11.0"
|
parking_lot = "0.11.0"
|
||||||
exit-future = "0.2.0"
|
exit-future = "0.2.0"
|
||||||
|
filesystem = { path = "../common/filesystem" }
|
||||||
libc = "0.2.79"
|
libc = "0.2.79"
|
||||||
eth2_ssz_derive = "0.1.0"
|
eth2_ssz_derive = "0.1.0"
|
||||||
hex = "0.4.2"
|
hex = "0.4.2"
|
||||||
|
@ -16,6 +16,7 @@ serde = "1.0.116"
|
|||||||
serde_derive = "1.0.116"
|
serde_derive = "1.0.116"
|
||||||
serde_json = "1.0.58"
|
serde_json = "1.0.58"
|
||||||
serde_utils = { path = "../../consensus/serde_utils" }
|
serde_utils = { path = "../../consensus/serde_utils" }
|
||||||
|
filesystem = { path = "../../common/filesystem" }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
rayon = "1.4.1"
|
rayon = "1.4.1"
|
||||||
|
@ -4,8 +4,21 @@ OUTPUT_DIR := interchange-tests
|
|||||||
TARBALL := $(OUTPUT_DIR)-$(TESTS_TAG).tar.gz
|
TARBALL := $(OUTPUT_DIR)-$(TESTS_TAG).tar.gz
|
||||||
ARCHIVE_URL := https://github.com/eth2-clients/slashing-protection-interchange-tests/tarball/$(TESTS_TAG)
|
ARCHIVE_URL := https://github.com/eth2-clients/slashing-protection-interchange-tests/tarball/$(TESTS_TAG)
|
||||||
|
|
||||||
|
ifeq ($(OS),Windows_NT)
|
||||||
|
ifeq (, $(shell where rm))
|
||||||
|
rmfile = if exist $(1) (del /F /Q $(1))
|
||||||
|
rmdir = if exist $(1) (rmdir /Q /S $(1))
|
||||||
|
else
|
||||||
|
rmfile = rm -f $(1)
|
||||||
|
rmdir = rm -rf $(1)
|
||||||
|
endif
|
||||||
|
else
|
||||||
|
rmfile = rm -f $(1)
|
||||||
|
rmdir = rm -rf $(1)
|
||||||
|
endif
|
||||||
|
|
||||||
$(OUTPUT_DIR): $(TARBALL)
|
$(OUTPUT_DIR): $(TARBALL)
|
||||||
rm -rf $@
|
$(call rmdir,$@)
|
||||||
mkdir $@
|
mkdir $@
|
||||||
tar --strip-components=1 -xzf $^ -C $@
|
tar --strip-components=1 -xzf $^ -C $@
|
||||||
|
|
||||||
@ -13,13 +26,13 @@ $(TARBALL):
|
|||||||
curl --fail -L -o $@ $(ARCHIVE_URL)
|
curl --fail -L -o $@ $(ARCHIVE_URL)
|
||||||
|
|
||||||
clean-test-files:
|
clean-test-files:
|
||||||
rm -rf $(OUTPUT_DIR)
|
$(call rmdir,$(OUTPUT_DIR))
|
||||||
|
|
||||||
clean-archives:
|
clean-archives:
|
||||||
rm -f $(TARBALL)
|
$(call rmfile,$(TARBALL))
|
||||||
|
|
||||||
generate:
|
generate:
|
||||||
rm -rf $(GENERATE_DIR)
|
$(call rmdir,$(GENERATE_DIR))
|
||||||
cargo run --release --bin test_generator -- $(GENERATE_DIR)
|
cargo run --release --bin test_generator -- $(GENERATE_DIR)
|
||||||
|
|
||||||
clean: clean-test-files clean-archives
|
clean: clean-test-files clean-archives
|
||||||
|
@ -30,6 +30,7 @@ pub enum NotSafe {
|
|||||||
UnregisteredValidator(PublicKeyBytes),
|
UnregisteredValidator(PublicKeyBytes),
|
||||||
InvalidBlock(InvalidBlock),
|
InvalidBlock(InvalidBlock),
|
||||||
InvalidAttestation(InvalidAttestation),
|
InvalidAttestation(InvalidAttestation),
|
||||||
|
PermissionsError,
|
||||||
IOError(ErrorKind),
|
IOError(ErrorKind),
|
||||||
SQLError(String),
|
SQLError(String),
|
||||||
SQLPoolError(String),
|
SQLPoolError(String),
|
||||||
|
@ -5,9 +5,10 @@ use crate::interchange::{
|
|||||||
use crate::signed_attestation::InvalidAttestation;
|
use crate::signed_attestation::InvalidAttestation;
|
||||||
use crate::signed_block::InvalidBlock;
|
use crate::signed_block::InvalidBlock;
|
||||||
use crate::{hash256_from_row, NotSafe, Safe, SignedAttestation, SignedBlock, SigningRoot};
|
use crate::{hash256_from_row, NotSafe, Safe, SignedAttestation, SignedBlock, SigningRoot};
|
||||||
|
use filesystem::restrict_file_permissions;
|
||||||
use r2d2_sqlite::SqliteConnectionManager;
|
use r2d2_sqlite::SqliteConnectionManager;
|
||||||
use rusqlite::{params, OptionalExtension, Transaction, TransactionBehavior};
|
use rusqlite::{params, OptionalExtension, Transaction, TransactionBehavior};
|
||||||
use std::fs::{File, OpenOptions};
|
use std::fs::OpenOptions;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use types::{AttestationData, BeaconBlockHeader, Epoch, Hash256, PublicKeyBytes, SignedRoot, Slot};
|
use types::{AttestationData, BeaconBlockHeader, Epoch, Hash256, PublicKeyBytes, SignedRoot, Slot};
|
||||||
@ -46,13 +47,13 @@ impl SlashingDatabase {
|
|||||||
///
|
///
|
||||||
/// Error if a database (or any file) already exists at `path`.
|
/// Error if a database (or any file) already exists at `path`.
|
||||||
pub fn create(path: &Path) -> Result<Self, NotSafe> {
|
pub fn create(path: &Path) -> Result<Self, NotSafe> {
|
||||||
let file = OpenOptions::new()
|
let _file = OpenOptions::new()
|
||||||
.write(true)
|
.write(true)
|
||||||
.read(true)
|
.read(true)
|
||||||
.create_new(true)
|
.create_new(true)
|
||||||
.open(path)?;
|
.open(path)?;
|
||||||
|
|
||||||
Self::set_db_file_permissions(&file)?;
|
restrict_file_permissions(path).map_err(|_| NotSafe::PermissionsError)?;
|
||||||
let conn_pool = Self::open_conn_pool(path)?;
|
let conn_pool = Self::open_conn_pool(path)?;
|
||||||
let conn = conn_pool.get()?;
|
let conn = conn_pool.get()?;
|
||||||
|
|
||||||
@ -121,21 +122,6 @@ impl SlashingDatabase {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the database file to readable and writable only by its owner (0600).
|
|
||||||
#[cfg(unix)]
|
|
||||||
fn set_db_file_permissions(file: &File) -> Result<(), NotSafe> {
|
|
||||||
use std::os::unix::fs::PermissionsExt;
|
|
||||||
|
|
||||||
let mut perm = file.metadata()?.permissions();
|
|
||||||
perm.set_mode(0o600);
|
|
||||||
file.set_permissions(perm)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: add support for Windows ACLs
|
|
||||||
#[cfg(windows)]
|
|
||||||
fn set_db_file_permissions(file: &File) -> Result<(), NotSafe> {}
|
|
||||||
|
|
||||||
/// Creates an empty transaction and drops it. Used to test whether the database is locked.
|
/// Creates an empty transaction and drops it. Used to test whether the database is locked.
|
||||||
pub fn test_transaction(&self) -> Result<(), NotSafe> {
|
pub fn test_transaction(&self) -> Result<(), NotSafe> {
|
||||||
let mut conn = self.conn_pool.get()?;
|
let mut conn = self.conn_pool.get()?;
|
||||||
|
@ -146,7 +146,7 @@ impl KeyCache {
|
|||||||
let bytes = serde_json::to_vec(self).map_err(Error::UnableToEncodeFile)?;
|
let bytes = serde_json::to_vec(self).map_err(Error::UnableToEncodeFile)?;
|
||||||
|
|
||||||
write_file_via_temporary(&cache_path, &temp_path, &bytes)
|
write_file_via_temporary(&cache_path, &temp_path, &bytes)
|
||||||
.map_err(Error::UnableToWriteFile)?;
|
.map_err(Error::UnableToCreateFile)?;
|
||||||
|
|
||||||
self.state = State::DecryptedAndSaved;
|
self.state = State::DecryptedAndSaved;
|
||||||
Ok(true)
|
Ok(true)
|
||||||
@ -245,6 +245,7 @@ pub enum Error {
|
|||||||
UnableToEncodeFile(serde_json::Error),
|
UnableToEncodeFile(serde_json::Error),
|
||||||
/// The cache file or its temporary could not be written to the filesystem.
|
/// The cache file or its temporary could not be written to the filesystem.
|
||||||
UnableToWriteFile(io::Error),
|
UnableToWriteFile(io::Error),
|
||||||
|
UnableToCreateFile(filesystem::Error),
|
||||||
/// Couldn't decrypt the cache file
|
/// Couldn't decrypt the cache file
|
||||||
UnableToDecrypt(KeystoreError),
|
UnableToDecrypt(KeystoreError),
|
||||||
UnableToEncrypt(KeystoreError),
|
UnableToEncrypt(KeystoreError),
|
||||||
|
Loading…
Reference in New Issue
Block a user