From ef05a4d1edaa3b1236b8d4083dbd113724082ab1 Mon Sep 17 00:00:00 2001 From: Michael Sproul Date: Fri, 3 Sep 2021 15:07:18 +1000 Subject: [PATCH] Archive `remote_signer` code --- Cargo.lock | 396 ----------------- Cargo.toml | 6 - common/remote_signer_consumer/Cargo.toml | 16 - .../remote_signer_consumer/src/http_client.rs | 89 ---- common/remote_signer_consumer/src/lib.rs | 213 ---------- .../tests/message_preparation.rs | 181 -------- common/remote_signer_consumer/tests/mock.rs | 168 -------- common/remote_signer_consumer/tests/post.rs | 242 ----------- .../tests/sign_attestation.rs | 49 --- .../tests/sign_block.rs | 49 --- .../tests/sign_randao.rs | 50 --- lighthouse/Cargo.toml | 1 - lighthouse/src/main.rs | 11 - remote_signer/Cargo.toml | 24 -- remote_signer/README.md | 134 ------ remote_signer/backend/Cargo.toml | 20 - remote_signer/backend/src/error.rs | 46 -- remote_signer/backend/src/lib.rs | 343 --------------- remote_signer/backend/src/storage.rs | 10 - remote_signer/backend/src/storage_raw_dir.rs | 181 -------- remote_signer/backend/src/utils.rs | 123 ------ remote_signer/backend/src/zeroize_string.rs | 222 ---------- remote_signer/client/Cargo.toml | 19 - remote_signer/client/src/api_error.rs | 57 --- remote_signer/client/src/api_response.rs | 18 - remote_signer/client/src/backend.rs | 70 --- remote_signer/client/src/config.rs | 20 - remote_signer/client/src/handler.rs | 111 ----- remote_signer/client/src/lib.rs | 58 --- remote_signer/client/src/rest_api.rs | 91 ---- remote_signer/client/src/router.rs | 101 ----- remote_signer/client/src/signing_root.rs | 78 ---- remote_signer/client/src/upcheck.rs | 7 - remote_signer/src/cli.rs | 37 -- remote_signer/src/lib.rs | 34 -- remote_signer/tests/get_keys.rs | 84 ---- remote_signer/tests/sign.rs | 402 ------------------ remote_signer/tests/sign_attestation.rs | 89 ---- remote_signer/tests/sign_block.rs | 81 ---- remote_signer/tests/sign_randao.rs | 83 ---- remote_signer/tests/upcheck.rs | 16 - testing/remote_signer_test/Cargo.toml | 25 -- .../remote_signer_test/src/api_test_signer.rs | 141 ------ testing/remote_signer_test/src/constants.rs | 98 ----- testing/remote_signer_test/src/consumer.rs | 152 ------- testing/remote_signer_test/src/lib.rs | 18 - .../src/local_signer_test_data.rs | 103 ----- testing/remote_signer_test/src/mock.rs | 20 - .../src/remote_signer_test_data.rs | 69 --- testing/remote_signer_test/src/utils.rs | 302 ------------- 50 files changed, 4958 deletions(-) delete mode 100644 common/remote_signer_consumer/Cargo.toml delete mode 100644 common/remote_signer_consumer/src/http_client.rs delete mode 100644 common/remote_signer_consumer/src/lib.rs delete mode 100644 common/remote_signer_consumer/tests/message_preparation.rs delete mode 100644 common/remote_signer_consumer/tests/mock.rs delete mode 100644 common/remote_signer_consumer/tests/post.rs delete mode 100644 common/remote_signer_consumer/tests/sign_attestation.rs delete mode 100644 common/remote_signer_consumer/tests/sign_block.rs delete mode 100644 common/remote_signer_consumer/tests/sign_randao.rs delete mode 100644 remote_signer/Cargo.toml delete mode 100644 remote_signer/README.md delete mode 100644 remote_signer/backend/Cargo.toml delete mode 100644 remote_signer/backend/src/error.rs delete mode 100644 remote_signer/backend/src/lib.rs delete mode 100644 remote_signer/backend/src/storage.rs delete mode 100644 remote_signer/backend/src/storage_raw_dir.rs delete mode 100644 remote_signer/backend/src/utils.rs delete mode 100644 remote_signer/backend/src/zeroize_string.rs delete mode 100644 remote_signer/client/Cargo.toml delete mode 100644 remote_signer/client/src/api_error.rs delete mode 100644 remote_signer/client/src/api_response.rs delete mode 100644 remote_signer/client/src/backend.rs delete mode 100644 remote_signer/client/src/config.rs delete mode 100644 remote_signer/client/src/handler.rs delete mode 100644 remote_signer/client/src/lib.rs delete mode 100644 remote_signer/client/src/rest_api.rs delete mode 100644 remote_signer/client/src/router.rs delete mode 100644 remote_signer/client/src/signing_root.rs delete mode 100644 remote_signer/client/src/upcheck.rs delete mode 100644 remote_signer/src/cli.rs delete mode 100644 remote_signer/src/lib.rs delete mode 100644 remote_signer/tests/get_keys.rs delete mode 100644 remote_signer/tests/sign.rs delete mode 100644 remote_signer/tests/sign_attestation.rs delete mode 100644 remote_signer/tests/sign_block.rs delete mode 100644 remote_signer/tests/sign_randao.rs delete mode 100644 remote_signer/tests/upcheck.rs delete mode 100644 testing/remote_signer_test/Cargo.toml delete mode 100644 testing/remote_signer_test/src/api_test_signer.rs delete mode 100644 testing/remote_signer_test/src/constants.rs delete mode 100644 testing/remote_signer_test/src/consumer.rs delete mode 100644 testing/remote_signer_test/src/lib.rs delete mode 100644 testing/remote_signer_test/src/local_signer_test_data.rs delete mode 100644 testing/remote_signer_test/src/mock.rs delete mode 100644 testing/remote_signer_test/src/remote_signer_test_data.rs delete mode 100644 testing/remote_signer_test/src/utils.rs diff --git a/Cargo.lock b/Cargo.lock index 4950dd4bc07..8ff1955cb96 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -205,31 +205,12 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be4dc07131ffa69b8072d35f5007352af944213cde02545e2103680baed38fcd" -[[package]] -name = "ascii-canvas" -version = "3.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8824ecca2e851cec16968d54a01dd372ef8f95b244fb84b84e70128be347c3c6" -dependencies = [ - "term", -] - [[package]] name = "asn1_der" version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d6e24d2cce90c53b948c46271bfb053e4bdc2db9b5d3f65e20f8cf28a1b7fc3" -[[package]] -name = "assert-json-diff" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50f1c3703dd33532d7f0ca049168930e9099ecac238e23cf932f3a69c42f06da" -dependencies = [ - "serde", - "serde_json", -] - [[package]] name = "async-channel" version = "1.6.1" @@ -308,32 +289,6 @@ dependencies = [ "event-listener", ] -[[package]] -name = "async-object-pool" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aeb901c30ebc2fc4ab46395bbfbdba9542c16559d853645d75190c3056caf3bc" -dependencies = [ - "async-std", -] - -[[package]] -name = "async-process" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b21b63ab5a0db0369deb913540af2892750e42d949faacc7a61495ac418a1692" -dependencies = [ - "async-io", - "blocking", - "cfg-if 1.0.0", - "event-listener", - "futures-lite", - "libc", - "once_cell", - "signal-hook", - "winapi", -] - [[package]] name = "async-std" version = "1.9.0" @@ -344,7 +299,6 @@ dependencies = [ "async-global-executor", "async-io", "async-lock", - "async-process", "crossbeam-utils", "futures-channel", "futures-core", @@ -488,17 +442,6 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" -[[package]] -name = "basic-cookies" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb53b6b315f924c7f113b162e53b3901c05fc9966baf84d201dfcc7432a4bb38" -dependencies = [ - "lalrpop", - "lalrpop-util", - "regex", -] - [[package]] name = "beacon_chain" version = "0.2.0" @@ -600,21 +543,6 @@ dependencies = [ "serde", ] -[[package]] -name = "bit-set" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e11e16035ea35e4e5997b393eacbf6f63983188f7a2ad25bfb13465f5ad59de" -dependencies = [ - "bit-vec", -] - -[[package]] -name = "bit-vec" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" - [[package]] name = "bitflags" version = "1.3.2" @@ -1284,37 +1212,6 @@ dependencies = [ "rand 0.7.3", ] -[[package]] -name = "curl" -version = "0.4.38" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "003cb79c1c6d1c93344c7e1201bb51c2148f24ec2bd9c253709d6b2efb796515" -dependencies = [ - "curl-sys", - "libc", - "openssl-probe", - "openssl-sys", - "schannel", - "socket2 0.4.1", - "winapi", -] - -[[package]] -name = "curl-sys" -version = "0.4.45+curl-7.78.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de9e5a72b1c744eb5dd20b2be4d7eb84625070bb5c4ab9b347b70464ab1e62eb" -dependencies = [ - "cc", - "libc", - "libnghttp2-sys", - "libz-sys", - "openssl-sys", - "pkg-config", - "vcpkg", - "winapi", -] - [[package]] name = "curve25519-dalek" version = "3.2.0" @@ -1454,18 +1351,6 @@ dependencies = [ "syn", ] -[[package]] -name = "diff" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e25ea47919b1560c4e3b7fe0aaab9becf5b84a10325ddf7db0f0ba5e1026499" - -[[package]] -name = "difference" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198" - [[package]] name = "digest" version = "0.9.0" @@ -1666,15 +1551,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "ena" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7402b94a93c24e742487327a7cd839dc9d36fec9de9fb25b09f2dae459f36c3" -dependencies = [ - "log", -] - [[package]] name = "encoding_rs" version = "0.8.28" @@ -2874,33 +2750,6 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6456b8a6c8f33fee7d958fcd1b60d55b11940a79e63ae87013e6d22e26034440" -[[package]] -name = "httpmock" -version = "0.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b217899bcbe8ad3bdee7a46727bd3754b908831462755567852fb20eac585d46" -dependencies = [ - "assert-json-diff", - "async-object-pool", - "async-trait", - "base64 0.13.0", - "basic-cookies", - "crossbeam-utils", - "difference", - "futures-util", - "hyper", - "isahc", - "lazy_static", - "levenshtein", - "log", - "qstring", - "regex", - "serde", - "serde_json", - "serde_regex", - "tokio", -] - [[package]] name = "humantime" version = "2.1.0" @@ -3131,32 +2980,6 @@ version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68f2d64f2edebec4ce84ad108148e67e1064789bee435edc5b60ad398714a3a9" -[[package]] -name = "isahc" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "431445cb4ba85a80cb1438a9ae8042dadb78ae4046ecee89ad027b614aa0ddb7" -dependencies = [ - "async-channel", - "crossbeam-utils", - "curl", - "curl-sys", - "encoding_rs", - "event-listener", - "futures-lite", - "http", - "log", - "mime", - "once_cell", - "polling", - "slab", - "sluice", - "tracing", - "tracing-futures", - "url", - "waker-fn", -] - [[package]] name = "itertools" version = "0.10.1" @@ -3223,38 +3046,6 @@ dependencies = [ "log", ] -[[package]] -name = "lalrpop" -version = "0.19.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b15174f1c529af5bf1283c3bc0058266b483a67156f79589fab2a25e23cf8988" -dependencies = [ - "ascii-canvas", - "atty", - "bit-set", - "diff", - "ena", - "itertools", - "lalrpop-util", - "petgraph", - "pico-args", - "regex", - "regex-syntax", - "string_cache", - "term", - "tiny-keccak 2.0.2", - "unicode-xid", -] - -[[package]] -name = "lalrpop-util" -version = "0.19.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3e58cce361efcc90ba8a0a5f982c741ff86b603495bb15a998412e957dcd278" -dependencies = [ - "regex", -] - [[package]] name = "lazy_static" version = "1.4.0" @@ -3324,12 +3115,6 @@ dependencies = [ "num_cpus", ] -[[package]] -name = "levenshtein" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db13adb97ab515a3691f56e4dbab09283d0b86cb45abd991d8634a9d6f501760" - [[package]] name = "libc" version = "0.2.100" @@ -3362,16 +3147,6 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7d73b3f436185384286bd8098d17ec07c9a7d2388a6599f824d8502b529702a" -[[package]] -name = "libnghttp2-sys" -version = "0.1.6+1.43.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0af55541a8827e138d59ec9e5877fb6095ece63fb6f4da45e7491b4fbd262855" -dependencies = [ - "cc", - "libc", -] - [[package]] name = "libp2p" version = "0.40.0" @@ -3904,7 +3679,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "de5435b8549c16d423ed0c03dbaafe57cf6c3344744f1242520d59c9d8ecec66" dependencies = [ "cc", - "libc", "pkg-config", "vcpkg", ] @@ -3932,7 +3706,6 @@ dependencies = [ "lighthouse_version", "logging", "malloc_utils", - "remote_signer", "serde_json", "slashing_protection", "slog", @@ -4394,12 +4167,6 @@ dependencies = [ "types", ] -[[package]] -name = "new_debug_unreachable" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54" - [[package]] name = "nix" version = "0.17.0" @@ -4757,21 +4524,6 @@ dependencies = [ "indexmap", ] -[[package]] -name = "phf_shared" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c00cf8b9eafe68dde5e9eaa2cef8ee84a9336a47d566ec55ca16589633b65af7" -dependencies = [ - "siphasher", -] - -[[package]] -name = "pico-args" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db8bcd96cb740d03149cbad5518db9fd87126a10ab519c011893b1754134c468" - [[package]] name = "pin-project" version = "0.4.28" @@ -4922,12 +4674,6 @@ version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" -[[package]] -name = "precomputed-hash" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" - [[package]] name = "primitive-types" version = "0.7.3" @@ -5124,15 +4870,6 @@ dependencies = [ "unescape", ] -[[package]] -name = "qstring" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d464fae65fff2680baf48019211ce37aaec0c78e9264c84a3e484717f965104e" -dependencies = [ - "percent-encoding", -] - [[package]] name = "quick-error" version = "1.2.3" @@ -5381,90 +5118,6 @@ version = "0.6.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" -[[package]] -name = "remote_signer" -version = "0.2.0" -dependencies = [ - "bls", - "clap", - "environment", - "remote_signer_backend", - "remote_signer_client", - "remote_signer_test", - "serde_json", - "slog", - "types", -] - -[[package]] -name = "remote_signer_backend" -version = "0.2.0" -dependencies = [ - "bls", - "clap", - "hex", - "lazy_static", - "regex", - "remote_signer_test", - "slog", - "sloggers 1.0.1", - "tempfile", - "types", - "zeroize", -] - -[[package]] -name = "remote_signer_client" -version = "0.2.0" -dependencies = [ - "clap", - "environment", - "futures", - "hyper", - "lazy_static", - "regex", - "remote_signer_backend", - "serde", - "serde_json", - "slog", - "task_executor", - "types", -] - -[[package]] -name = "remote_signer_consumer" -version = "0.2.0" -dependencies = [ - "rand 0.7.3", - "remote_signer_test", - "reqwest", - "sensitive_url", - "serde", - "tokio", - "types", -] - -[[package]] -name = "remote_signer_test" -version = "0.2.0" -dependencies = [ - "clap", - "environment", - "hex", - "httpmock", - "remote_signer_client", - "remote_signer_consumer", - "reqwest", - "sensitive_url", - "serde", - "serde_json", - "tempfile", - "tokio", - "types", - "winapi", - "windows-acl", -] - [[package]] name = "remove_dir_all" version = "0.5.3" @@ -5875,16 +5528,6 @@ dependencies = [ "serde", ] -[[package]] -name = "serde_regex" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8136f1a4ea815d7eac4101cfd0b16dc0cb5e1fe1b8609dfd728058656b7badf" -dependencies = [ - "regex", - "serde", -] - [[package]] name = "serde_repr" version = "0.1.7" @@ -5973,16 +5616,6 @@ dependencies = [ "lazy_static", ] -[[package]] -name = "signal-hook" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "470c5a6397076fae0094aaf06a08e6ba6f37acb77d3b1b91ea92b4d6c8650c39" -dependencies = [ - "libc", - "signal-hook-registry", -] - [[package]] name = "signal-hook-registry" version = "1.4.0" @@ -6033,12 +5666,6 @@ dependencies = [ "validator_client", ] -[[package]] -name = "siphasher" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "729a25c17d72b06c68cb47955d44fda88ad2d3e7d77e025663fdd69b93dd71a1" - [[package]] name = "slab" version = "0.4.4" @@ -6237,17 +5864,6 @@ dependencies = [ "types", ] -[[package]] -name = "sluice" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d7400c0eff44aa2fcb5e31a5f24ba9716ed90138769e4977a2ba6014ae63eb5" -dependencies = [ - "async-channel", - "futures-core", - "futures-io", -] - [[package]] name = "smallvec" version = "1.6.1" @@ -6474,18 +6090,6 @@ dependencies = [ "types", ] -[[package]] -name = "string_cache" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ddb1139b5353f96e429e1a5e19fbaf663bddedaa06d1dbd49f82e352601209a" -dependencies = [ - "lazy_static", - "new_debug_unreachable", - "phf_shared", - "precomputed-hash", -] - [[package]] name = "strsim" version = "0.8.0" diff --git a/Cargo.toml b/Cargo.toml index e5da9af382f..0be230ffd03 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,7 +33,6 @@ members = [ "common/logging", "common/lru_cache", "common/malloc_utils", - "common/remote_signer_consumer", "common/sensitive_url", "common/slot_clock", "common/task_executor", @@ -69,17 +68,12 @@ members = [ "lighthouse", "lighthouse/environment", - "remote_signer", - "remote_signer/backend", - "remote_signer/client", - "slasher", "slasher/service", "testing/ef_tests", "testing/eth1_test_rig", "testing/node_test_rig", - "testing/remote_signer_test", "testing/simulator", "testing/state_transition_vectors", diff --git a/common/remote_signer_consumer/Cargo.toml b/common/remote_signer_consumer/Cargo.toml deleted file mode 100644 index 950a8da5d5e..00000000000 --- a/common/remote_signer_consumer/Cargo.toml +++ /dev/null @@ -1,16 +0,0 @@ -[package] -name = "remote_signer_consumer" -version = "0.2.0" -authors = ["Herman Junge "] -edition = "2018" - -[dev-dependencies] -rand = "0.7.3" -remote_signer_test = { path = "../../testing/remote_signer_test" } - -[dependencies] -reqwest = { version = "0.11.0", features = ["json"] } -serde = { version = "1.0.116", features = ["derive"] } -tokio = { version = "1.10.0", features = ["time"] } -types = { path = "../../consensus/types" } -sensitive_url = { path = "../sensitive_url" } diff --git a/common/remote_signer_consumer/src/http_client.rs b/common/remote_signer_consumer/src/http_client.rs deleted file mode 100644 index b5b22265b84..00000000000 --- a/common/remote_signer_consumer/src/http_client.rs +++ /dev/null @@ -1,89 +0,0 @@ -use crate::{ - Error, RemoteSignerObject, RemoteSignerRequestBody, RemoteSignerResponseBodyError, - RemoteSignerResponseBodyOK, -}; -use reqwest::StatusCode; -pub use reqwest::Url; -use sensitive_url::SensitiveUrl; -use types::{Domain, Fork, Hash256}; - -/// A wrapper around `reqwest::Client` which provides convenience methods -/// to interface with a BLS Remote Signer. -pub struct RemoteSignerHttpConsumer { - client: reqwest::Client, - server: SensitiveUrl, -} - -impl RemoteSignerHttpConsumer { - pub fn from_components(server: SensitiveUrl, client: reqwest::Client) -> Self { - Self { client, server } - } - - /// `POST /sign/:public-key` - /// - /// # Arguments - /// - /// * `public_key` - Goes within the url to identify the key we want to use as signer. - /// * `bls_domain` - BLS Signature domain. Supporting `BeaconProposer`, `BeaconAttester`,`Randao`. - /// * `data` - A `BeaconBlock`, `AttestationData`, or `Epoch`. - /// * `fork` - A `Fork` object containing previous and current versions. - /// * `genesis_validators_root` - A `Hash256` for domain separation and chain versioning. - /// - /// It sends through the wire a serialized `RemoteSignerRequestBody`. - pub async fn sign( - &self, - public_key: &str, - bls_domain: Domain, - data: R, - fork: Fork, - genesis_validators_root: Hash256, - ) -> Result { - if public_key.is_empty() { - return Err(Error::InvalidParameter( - "Empty parameter public_key".to_string(), - )); - } - - let mut path = self.server.full.clone(); - path.path_segments_mut() - .map_err(|()| Error::InvalidUrl(self.server.clone()))? - .push("sign") - .push(public_key); - - let bls_domain = match bls_domain { - Domain::BeaconProposer => data.validate_object(bls_domain), - Domain::BeaconAttester => data.validate_object(bls_domain), - Domain::Randao => data.validate_object(bls_domain), - _ => Err(Error::InvalidParameter(format!( - "Unsupported BLS Domain: {:?}", - bls_domain - ))), - }?; - - let body = RemoteSignerRequestBody { - bls_domain, - data, - fork, - genesis_validators_root, - }; - - let response = self - .client - .post(path) - .json(&body) - .send() - .await - .map_err(Error::Reqwest)?; - - match response.status() { - StatusCode::OK => match response.json::().await { - Ok(resp_json) => Ok(resp_json.signature), - Err(e) => Err(Error::Reqwest(e)), - }, - _ => match response.json::().await { - Ok(resp_json) => Err(Error::ServerMessage(resp_json.error)), - Err(e) => Err(Error::Reqwest(e)), - }, - } - } -} diff --git a/common/remote_signer_consumer/src/lib.rs b/common/remote_signer_consumer/src/lib.rs deleted file mode 100644 index b8ad33f9860..00000000000 --- a/common/remote_signer_consumer/src/lib.rs +++ /dev/null @@ -1,213 +0,0 @@ -//! Enables the [Lighthouse Ethereum 2.0 Client] to consume signatures from the -//! [BLS Remote Signer]. -//! -//! ## About -//! -//! The lighthouse client needs to include this crate, and implement the -//! adequate bypasses and CLI flags needed to find the remote signer and perform -//! the HTTP requests. -//! -//! As defined by the [EIP-3030] specification, this crate will take the -//! received object data and parameters, and send them to the remote signer -//! for the production of a signing root hash and signature (the latter if the -//! signer has in storage the key identified at request). -//! -//! ## Usage -//! -//! ### RemoteSignerHttpConsumer -//! -//! Just provide an `Url` and a timeout -//! -//! ``` -//! use remote_signer_consumer::RemoteSignerHttpConsumer; -//! use reqwest::ClientBuilder; -//! use sensitive_url::SensitiveUrl; -//! use tokio::time::Duration; -//! -//! let url = SensitiveUrl::parse("http://127.0.0.1:9000").unwrap(); -//! let reqwest_client = ClientBuilder::new() -//! .timeout(Duration::from_secs(2)) -//! .build() -//! .unwrap(); -//! -//! let signer = RemoteSignerHttpConsumer::from_components(url, reqwest_client); -//! -//! ``` -//! -//! ## sign API -//! -//! `POST /sign/:identifier` -//! -//! ### Arguments -//! -//! #### `public_key` -//! -//! Goes within the url to identify the key we want to use as signer. -//! -//! #### `bls_domain` -//! -//! [BLS Signature domain]. Supporting `BeaconProposer`, `BeaconAttester`, -//! `Randao`. -//! -//! #### `data` -//! -//! A `BeaconBlock`, `AttestationData`, or `Epoch`. -//! -//! #### `fork` -//! -//! A [`Fork`] object, containing previous and current versions. -//! -//! #### `genesis_validators_root` -//! -//! A [`Hash256`] for domain separation and chain versioning. -//! -//! ### Behavior -//! -//! Upon receiving and validating the parameters, the signer sends through the -//! wire a serialized `RemoteSignerRequestBody`. Receiving a `200` message with -//! the `signature` field inside a JSON payload, or an error. -//! -//! ## How it works -//! -//! The production of a _local_ signature (i.e. inside the Lighthouse client) -//! has slight variations among the kind of objects (block, attestation, -//! randao). -//! -//! To sign a message, the following procedures are needed: -//! -//! * Get the `fork_version` - From the objects `Fork` and `Epoch`. -//! * Compute the [`fork_data_root`] - From the `fork_version` and the -//! `genesis_validators_root`. -//! * Compute the [`domain`] - From the `fork_data_root` and the `bls_domain`. -//! * With the `domain`, the object (or `epoch` in the case of [`randao`]) -//! can be merkelized into its [`signing_root`] to be signed. -//! -//! In short, to obtain a signature from the remote signer, we need to produce -//! (and serialize) the following objects: -//! -//! * `bls_domain`. -//! * `data` of the object, if this is a block proposal, an attestation, or an epoch. -//! * `epoch`, obtained from the object. -//! * `fork`. -//! * `genesis_validators_root`. -//! -//! And, of course, the identifier of the secret key, the `public_key`. -//! -//! ## Future Work -//! -//! ### EIP-3030 -//! -//! Work is being done to [standardize the API of the remote signers]. -//! -//! [`domain`]: https://github.com/ethereum/eth2.0-specs/blob/dev/specs/phase0/beacon-chain.md#compute_domain -//! [`Epoch`]: https://github.com/ethereum/eth2.0-specs/blob/dev/specs/phase0/beacon-chain.md#custom-types -//! [`fork_data_root`]: https://github.com/ethereum/eth2.0-specs/blob/dev/specs/phase0/beacon-chain.md#compute_fork_data_root -//! [`Fork`]: https://github.com/ethereum/eth2.0-specs/blob/dev/specs/phase0/beacon-chain.md#fork -//! [`Hash256`]: https://docs.rs/ethereum-types/0.9.2/ethereum_types/struct.H256.html -//! [`randao`]: https://github.com/ethereum/eth2.0-specs/blob/dev/specs/phase0/beacon-chain.md#randao -//! [`signing_root`]: https://github.com/ethereum/eth2.0-specs/blob/dev/specs/phase0/beacon-chain.md#compute_signing_root -//! [BLS Remote Signer]: https://github.com/sigp/rust-bls-remote-signer -//! [BLS Signature domain]: https://github.com/ethereum/eth2.0-specs/blob/dev/specs/phase0/beacon-chain.md#domain-types -//! [EIP-3030]: https://github.com/ethereum/EIPs/blob/master/EIPS/eip-3030.md -//! [Lighthouse Ethereum 2.0 Client]: https://github.com/sigp/lighthouse -//! [standardize the API of the remote signers]: https://github.com/ethereum/EIPs/blob/master/EIPS/eip-3030.md - -mod http_client; - -pub use http_client::RemoteSignerHttpConsumer; -pub use reqwest::Url; -use sensitive_url::SensitiveUrl; -use serde::{Deserialize, Serialize}; -use types::{AttestationData, BeaconBlock, Domain, Epoch, EthSpec, Fork, Hash256, SignedRoot}; - -#[derive(Debug)] -pub enum Error { - /// The `reqwest` client raised an error. - Reqwest(reqwest::Error), - /// The server returned an error message where the body was able to be parsed. - ServerMessage(String), - /// The supplied URL is badly formatted. It should look something like `http://127.0.0.1:5052`. - InvalidUrl(SensitiveUrl), - /// The supplied parameter is invalid. - InvalidParameter(String), -} - -#[derive(Serialize)] -struct RemoteSignerRequestBody { - /// BLS Signature domain. Supporting `BeaconProposer`, `BeaconAttester`,`Randao`. - bls_domain: String, - - /// A `BeaconBlock`, `AttestationData`, or `Epoch`. - data: T, - - /// A `Fork` object containing previous and current versions. - fork: Fork, - - /// A `Hash256` for domain separation and chain versioning. - genesis_validators_root: Hash256, -} - -#[derive(Deserialize)] -struct RemoteSignerResponseBodyOK { - signature: String, -} - -#[derive(Deserialize)] -struct RemoteSignerResponseBodyError { - error: String, -} - -/// Allows the verification of the BeaconBlock and AttestationData objects -/// to be sent through the wire, against their BLS Domains. -pub trait RemoteSignerObject: SignedRoot + Serialize { - fn validate_object(&self, domain: Domain) -> Result; - fn get_epoch(&self) -> Epoch; -} - -impl RemoteSignerObject for BeaconBlock { - fn validate_object(&self, domain: Domain) -> Result { - match domain { - Domain::BeaconProposer => Ok("beacon_proposer".to_string()), - _ => Err(Error::InvalidParameter(format!( - "Domain mismatch for the BeaconBlock object. Expected BeaconProposer, got {:?}", - domain - ))), - } - } - - fn get_epoch(&self) -> Epoch { - self.epoch() - } -} - -impl RemoteSignerObject for AttestationData { - fn validate_object(&self, domain: Domain) -> Result { - match domain { - Domain::BeaconAttester => Ok("beacon_attester".to_string()), - _ => Err(Error::InvalidParameter(format!( - "Domain mismatch for the AttestationData object. Expected BeaconAttester, got {:?}", - domain - ))), - } - } - - fn get_epoch(&self) -> Epoch { - self.target.epoch - } -} - -impl RemoteSignerObject for Epoch { - fn validate_object(&self, domain: Domain) -> Result { - match domain { - Domain::Randao => Ok("randao".to_string()), - _ => Err(Error::InvalidParameter(format!( - "Domain mismatch for the Epoch object. Expected Randao, got {:?}", - domain - ))), - } - } - - fn get_epoch(&self) -> Epoch { - *self - } -} diff --git a/common/remote_signer_consumer/tests/message_preparation.rs b/common/remote_signer_consumer/tests/message_preparation.rs deleted file mode 100644 index 967c67997db..00000000000 --- a/common/remote_signer_consumer/tests/message_preparation.rs +++ /dev/null @@ -1,181 +0,0 @@ -mod message_preparation { - use remote_signer_consumer::Error; - use remote_signer_test::*; - use types::Domain; - - #[test] - fn beacon_block_and_bls_domain_mismatch() { - let (test_signer, _tmp_dir) = set_up_api_test_signer_to_sign_message(); - let test_client = set_up_test_consumer(&test_signer.address); - - macro_rules! test_case { - ($f: expr, $bls_domain: expr, $msg: expr) => { - match do_sign_request(&test_client, get_input_data_and_set_domain($f, $bls_domain)) - .unwrap_err() - { - Error::InvalidParameter(message) => assert_eq!(message, $msg), - e => panic!("{:?}", e), - } - }; - } - - test_case!( - get_input_data_block, - Domain::BeaconAttester, - "Domain mismatch for the BeaconBlock object. Expected BeaconProposer, got BeaconAttester" - ); - test_case!( - get_input_data_block, - Domain::Randao, - "Domain mismatch for the BeaconBlock object. Expected BeaconProposer, got Randao" - ); - test_case!( - get_input_data_attestation, - Domain::BeaconProposer, - "Domain mismatch for the AttestationData object. Expected BeaconAttester, got BeaconProposer" - ); - test_case!( - get_input_data_attestation, - Domain::Randao, - "Domain mismatch for the AttestationData object. Expected BeaconAttester, got Randao" - ); - test_case!( - get_input_data_randao, - Domain::BeaconProposer, - "Domain mismatch for the Epoch object. Expected Randao, got BeaconProposer" - ); - test_case!( - get_input_data_randao, - Domain::BeaconAttester, - "Domain mismatch for the Epoch object. Expected Randao, got BeaconAttester" - ); - - test_signer.shutdown(); - } - - #[test] - fn empty_public_key_parameter() { - let (test_signer, _tmp_dir) = set_up_api_test_signer_to_sign_message(); - let test_client = set_up_test_consumer(&test_signer.address); - - macro_rules! test_case { - ($f: expr, $p: expr, $msg: expr) => { - match do_sign_request(&test_client, get_input_data_and_set_public_key($f, $p)) - .unwrap_err() - { - Error::InvalidParameter(message) => assert_eq!(message, $msg), - e => panic!("{:?}", e), - } - }; - } - - test_case!(get_input_data_block, "", "Empty parameter public_key"); - test_case!(get_input_data_attestation, "", "Empty parameter public_key"); - test_case!(get_input_data_randao, "", "Empty parameter public_key"); - - test_signer.shutdown(); - } - - #[test] - fn invalid_public_key_param() { - let (test_signer, _tmp_dir) = set_up_api_test_signer_to_sign_message(); - let test_client = set_up_test_consumer(&test_signer.address); - - macro_rules! test_case { - ($f: expr, $p: expr, $msg: expr) => { - match do_sign_request(&test_client, get_input_data_and_set_public_key($f, $p)) - .unwrap_err() - { - Error::ServerMessage(message) => assert_eq!(message, $msg), - e => panic!("{:?}", e), - } - }; - } - - test_case!(get_input_data_block, "/", "Invalid public key: %2F"); - test_case!(get_input_data_attestation, "/", "Invalid public key: %2F"); - test_case!(get_input_data_randao, "/", "Invalid public key: %2F"); - test_case!(get_input_data_block, "//", "Invalid public key: %2F%2F"); - test_case!(get_input_data_block, "///", "Invalid public key: %2F%2F%2F"); - test_case!( - get_input_data_block, - "/?'or 1 = 1 --", - "Invalid public key: %2F%3F\'or%201%20=%201%20--" - ); - - test_signer.shutdown(); - } - - #[test] - fn unsupported_bls_domain() { - let (test_signer, _tmp_dir) = set_up_api_test_signer_to_sign_message(); - let test_client = set_up_test_consumer(&test_signer.address); - - let test_case = |bls_domain, msg| { - let mut test_input = get_input_data_block(0xc137); - test_input.bls_domain = bls_domain; - let signature = do_sign_request(&test_client, test_input); - - match signature.unwrap_err() { - Error::InvalidParameter(message) => assert_eq!(message, msg), - e => panic!("{:?}", e), - } - }; - - test_case(Domain::Deposit, "Unsupported BLS Domain: Deposit"); - test_case( - Domain::VoluntaryExit, - "Unsupported BLS Domain: VoluntaryExit", - ); - test_case( - Domain::SelectionProof, - "Unsupported BLS Domain: SelectionProof", - ); - test_case( - Domain::AggregateAndProof, - "Unsupported BLS Domain: AggregateAndProof", - ); - - test_signer.shutdown(); - } - - #[test] - fn invalid_public_key_param_additional_path_segments() { - let (test_signer, _tmp_dir) = set_up_api_test_signer_to_sign_message(); - let test_client = set_up_test_consumer(&test_signer.address); - - macro_rules! test_case { - ($f: expr, $p: expr, $msg: expr) => { - match do_sign_request(&test_client, get_input_data_and_set_public_key($f, $p)) - .unwrap_err() - { - Error::ServerMessage(message) => assert_eq!(message, $msg), - e => panic!("{:?}", e), - } - }; - } - - test_case!( - get_input_data_block, - "this/receipt", - "Invalid public key: this%2Freceipt" - ); - test_case!( - get_input_data_attestation, - "/this/receipt/please", - "Invalid public key: %2Fthis%2Freceipt%2Fplease" - ); - test_case!( - get_input_data_randao, - "this/receipt/please?", - "Invalid public key: this%2Freceipt%2Fplease%3F" - ); - test_case!( - get_input_data_block, - &format!("{}/valid/pk", PUBLIC_KEY_1), - format!("Invalid public key: {}%2Fvalid%2Fpk", PUBLIC_KEY_1) - ); - - test_signer.shutdown(); - } -} diff --git a/common/remote_signer_consumer/tests/mock.rs b/common/remote_signer_consumer/tests/mock.rs deleted file mode 100644 index aa1d20ea64d..00000000000 --- a/common/remote_signer_consumer/tests/mock.rs +++ /dev/null @@ -1,168 +0,0 @@ -mod mock { - use remote_signer_consumer::Error; - use remote_signer_test::*; - - #[test] - fn timeout() { - let mock_server = - set_up_mock_server_with_timeout(200, "{\"signature\":\"irrelevant_value\"}", 2); - let test_client = set_up_test_consumer_with_timeout(&mock_server.url(""), 1); - let test_input = get_input_data_block(0xc137); - - let r = do_sign_request(&test_client, test_input).unwrap_err(); - - match r { - Error::Reqwest(e) => { - let error_msg = e.to_string(); - assert!(error_msg.contains("error sending request for url (http://127.0.0.1:")); - assert!(error_msg.contains("/sign/")); - assert!(error_msg.contains(PUBLIC_KEY_1)); - assert!(error_msg.contains("): operation timed out")); - } - e => panic!("{:?}", e), - } - } - - #[test] - fn no_json_in_ok_response() { - let mock_server = set_up_mock_server(200, "NO JSON"); - let test_client = set_up_test_consumer(&mock_server.url("")); - let test_input = get_input_data_block(0xc137); - - let r = do_sign_request(&test_client, test_input).unwrap_err(); - - match r { - Error::Reqwest(e) => { - let error_msg = e.to_string(); - assert_eq!( - error_msg, - "error decoding response body: expected value at line 1 column 1" - ); - } - e => panic!("{:?}", e), - } - } - - #[test] - fn missing_signature_in_ok_json() { - let mock_server = set_up_mock_server(200, "{\"foo\":\"bar\"}"); - let test_client = set_up_test_consumer(&mock_server.url("")); - let test_input = get_input_data_block(0xc137); - - let r = do_sign_request(&test_client, test_input).unwrap_err(); - - match r { - Error::Reqwest(e) => { - let error_msg = e.to_string(); - assert_eq!( - error_msg, - "error decoding response body: missing field `signature` at line 1 column 13" - ); - } - e => panic!("{:?}", e), - } - } - - #[test] - fn empty_signature_in_ok_json() { - let mock_server = set_up_mock_server(200, "{\"signature\":\"\"}"); - let test_client = set_up_test_consumer(&mock_server.url("")); - let test_input = get_input_data_block(0xc137); - - let r = do_sign_request(&test_client, test_input).unwrap(); - - assert_eq!(r, ""); - } - - #[test] - fn extra_fields_in_ok_json() { - let mock_server = set_up_mock_server( - 200, - &format!( - "{{\"signature\":\"{}\", \"foo\":\"bar\", \"red\":\"green\"}}", - EXPECTED_SIGNATURE_1 - ), - ); - let test_client = set_up_test_consumer(&mock_server.url("")); - let test_input = get_input_data_block(0xc137); - - let r = do_sign_request(&test_client, test_input).unwrap(); - - assert_eq!(r, EXPECTED_SIGNATURE_1); - } - - #[test] - fn no_json_in_error_response() { - let mock_server = set_up_mock_server(500, "NO JSON"); - let test_client = set_up_test_consumer(&mock_server.url("")); - let test_input = get_input_data_block(0xc137); - - let r = do_sign_request(&test_client, test_input).unwrap_err(); - - match r { - Error::Reqwest(e) => { - let error_msg = e.to_string(); - assert_eq!( - error_msg, - "error decoding response body: expected value at line 1 column 1" - ); - } - e => panic!("{:?}", e), - } - } - - #[test] - fn missing_error_field_in_error_json() { - let mock_server = set_up_mock_server(500, "{\"foo\":\"bar\"}"); - let test_client = set_up_test_consumer(&mock_server.url("")); - let test_input = get_input_data_block(0xc137); - - let r = do_sign_request(&test_client, test_input).unwrap_err(); - - match r { - Error::Reqwest(e) => { - let error_msg = e.to_string(); - assert_eq!( - error_msg, - "error decoding response body: missing field `error` at line 1 column 13" - ); - } - e => panic!("{:?}", e), - } - } - - #[test] - fn empty_error_field_in_error_json() { - let mock_server = set_up_mock_server(500, "{\"error\":\"\"}"); - let test_client = set_up_test_consumer(&mock_server.url("")); - let test_input = get_input_data_block(0xc137); - - let r = do_sign_request(&test_client, test_input).unwrap_err(); - - match r { - Error::ServerMessage(msg) => { - assert_eq!(msg, ""); - } - e => panic!("{:?}", e), - } - } - - #[test] - fn extra_fields_in_error_json() { - let mock_server = set_up_mock_server( - 500, - "{\"error\":\"some_error_msg\", \"foo\":\"bar\", \"red\":\"green\"}", - ); - let test_client = set_up_test_consumer(&mock_server.url("")); - let test_input = get_input_data_block(0xc137); - - let r = do_sign_request(&test_client, test_input).unwrap_err(); - - match r { - Error::ServerMessage(msg) => { - assert_eq!(msg, "some_error_msg"); - } - e => panic!("{:?}", e), - } - } -} diff --git a/common/remote_signer_consumer/tests/post.rs b/common/remote_signer_consumer/tests/post.rs deleted file mode 100644 index 1108980b172..00000000000 --- a/common/remote_signer_consumer/tests/post.rs +++ /dev/null @@ -1,242 +0,0 @@ -mod post { - use remote_signer_consumer::{Error, RemoteSignerHttpConsumer}; - use remote_signer_test::*; - use reqwest::ClientBuilder; - use sensitive_url::SensitiveUrl; - use tokio::time::Duration; - - #[test] - fn server_unavailable() { - let (test_signer, _tmp_dir) = set_up_api_test_signer_to_sign_message(); - let test_client = set_up_test_consumer(&test_signer.address); - - test_signer.shutdown(); - - let test_input = get_input_data_block(0xc137); - let signature = do_sign_request(&test_client, test_input); - - match signature.unwrap_err() { - Error::Reqwest(e) => { - let error_msg = e.to_string(); - let pubkey_string = PUBLIC_KEY_1.to_string(); - let msgs = vec![ - "error sending request for url", - &pubkey_string, - "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), - } - } - - #[test] - fn server_error() { - let (test_signer, tmp_dir) = set_up_api_test_signer_to_sign_message(); - restrict_permissions(tmp_dir.path()); - restrict_permissions(&tmp_dir.path().join(PUBLIC_KEY_1)); - - let test_client = set_up_test_consumer(&test_signer.address); - let test_input = get_input_data_block(0xc137); - let signature = do_sign_request(&test_client, test_input); - - unrestrict_permissions(tmp_dir.path()); - unrestrict_permissions(&tmp_dir.path().join(PUBLIC_KEY_1)); - - match signature.unwrap_err() { - Error::ServerMessage(message) => assert_eq!(message, "Storage error: PermissionDenied"), - e => panic!("{:?}", e), - } - - test_signer.shutdown(); - } - - #[test] - fn invalid_url() { - let (test_signer, _tmp_dir) = set_up_api_test_signer_to_sign_message(); - - let run_testcase = |u: &str| -> Result { - let url = SensitiveUrl::parse(u).map_err(|e| format!("{:?}", e))?; - - let reqwest_client = ClientBuilder::new() - .timeout(Duration::from_secs(12)) - .build() - .unwrap(); - - let test_client = RemoteSignerHttpConsumer::from_components(url, reqwest_client); - - let test_input = get_input_data_block(0xc137); - let signature = do_sign_request(&test_client, test_input); - - signature.map_err(|e| match e { - Error::InvalidUrl(message) => format!("{:?}", message), - Error::Reqwest(re) => { - if re.is_builder() { - format!("[Reqwest - Builder] {:?}", re.url().unwrap()) - } else if re.is_request() { - format!("[Reqwest - Request] {:?}", re.url().unwrap()) - } else { - format!("[Reqwest] {:?}", re) - } - } - _ => format!("{:?}", e), - }) - }; - - let testcase = |u: &str, msg: &str| assert_eq!(run_testcase(u).unwrap_err(), msg); - - // url::parser::ParseError. - // These cases don't even make it to the step of building a RemoteSignerHttpConsumer. - testcase("", "ParseError(RelativeUrlWithoutBase)"); - testcase("/4/8/15/16/23/42", "ParseError(RelativeUrlWithoutBase)"); - testcase("localhost", "ParseError(RelativeUrlWithoutBase)"); - testcase(":", "ParseError(RelativeUrlWithoutBase)"); - testcase("0.0:0", "ParseError(RelativeUrlWithoutBase)"); - testcase(":aa", "ParseError(RelativeUrlWithoutBase)"); - testcase("0:", "ParseError(RelativeUrlWithoutBase)"); - testcase("ftp://", "ParseError(EmptyHost)"); - testcase("http://", "ParseError(EmptyHost)"); - testcase("http://127.0.0.1:abcd", "ParseError(InvalidPort)"); - testcase("http://280.0.0.1", "ParseError(InvalidIpv4Address)"); - - // `Error::InvalidUrl`. - // The RemoteSignerHttpConsumer is created, but fails at `path_segments_mut()`. - testcase("localhost:abcd", "InvalidUrl(\"URL cannot be a base.\")"); - testcase("localhost:", "InvalidUrl(\"URL cannot be a base.\")"); - - // `Reqwest::Error` of the `Builder` kind. - // POST is not made. - testcase( - "unix:/run/foo.socket", - &format!( - "[Reqwest - Builder] Url {{ scheme: \"unix\", cannot_be_a_base: false, username: \"\", password: None, host: None, port: None, path: \"/run/foo.socket/sign/{}\", query: None, fragment: None }}", - PUBLIC_KEY_1 - ), - ); - // `Reqwest::Error` of the `Request` kind. - testcase( - "http://127.0.0.1:0", - &format!( - "[Reqwest - Request] Url {{ scheme: \"http\", cannot_be_a_base: false, username: \"\", password: None, host: Some(Ipv4(127.0.0.1)), port: Some(0), path: \"/sign/{}\", query: None, fragment: None }}", - PUBLIC_KEY_1 - ), - ); - - test_signer.shutdown(); - } - - #[test] - fn wrong_url() { - let (test_signer, _tmp_dir) = set_up_api_test_signer_to_sign_message(); - - let run_testcase = |u: &str| -> Result { - let url = SensitiveUrl::parse(u).unwrap(); - - let reqwest_client = ClientBuilder::new() - .timeout(Duration::from_secs(12)) - .build() - .unwrap(); - - let test_client = RemoteSignerHttpConsumer::from_components(url, reqwest_client); - - let test_input = get_input_data_block(0xc137); - let signature = do_sign_request(&test_client, test_input); - - signature.map_err(|e| format!("{:?}", e)) - }; - - let testcase = |u: &str, msgs: Vec<&str>| { - let r = run_testcase(u).unwrap_err(); - for msg in msgs.iter() { - assert!(r.contains(msg), "{:?} should contain {:?}", r, msg); - } - }; - - testcase( - "http://error-dns", - vec![ - "reqwest::Error", - "kind: Request", - &format!("/sign/{}", PUBLIC_KEY_1), - "hyper::Error(Connect, ConnectError", - "dns error", - match cfg!(windows) { - true => "No such host is known.", - false => "failed to lookup address information", - }, - ], - ); - - test_signer.shutdown(); - } - - #[test] - fn wrong_public_key() { - let (test_signer, _tmp_dir) = set_up_api_test_signer_to_sign_message(); - let test_client = set_up_test_consumer(&test_signer.address); - - let mut test_input = get_input_data_block(0xc137); - test_input.public_key = ABSENT_PUBLIC_KEY.to_string(); - - let signature = do_sign_request(&test_client, test_input); - - match signature.unwrap_err() { - Error::ServerMessage(msg) => { - assert_eq!(msg, format!("Key not found: {}", ABSENT_PUBLIC_KEY)) - } - e => panic!("{:?}", e), - } - } - - #[test] - fn invalid_secret_key() { - let (test_signer, _tmp_dir) = set_up_api_test_signer_to_sign_message(); - let test_client = set_up_test_consumer(&test_signer.address); - - let mut test_input = get_input_data_block(0xc137); - test_input.public_key = PUBLIC_KEY_FOR_INVALID_SECRET_KEY.to_string(); - - let signature = do_sign_request(&test_client, test_input); - - match signature.unwrap_err() { - Error::ServerMessage(msg) => assert_eq!( - msg, - format!( - "Invalid secret key: public_key: {}; Invalid hex character: W at index 0", - PUBLIC_KEY_FOR_INVALID_SECRET_KEY - ) - ), - e => panic!("{:?}", e), - } - } - - #[test] - fn key_mismatch() { - let (test_signer, _tmp_dir) = set_up_api_test_signer_to_sign_message(); - let test_client = set_up_test_consumer(&test_signer.address); - - let mut test_input = get_input_data_block(0xc137); - test_input.public_key = MISMATCHED_PUBLIC_KEY.to_string(); - - let signature = do_sign_request(&test_client, test_input); - - match signature.unwrap_err() { - Error::ServerMessage(msg) => { - assert_eq!(msg, format!("Key mismatch: {}", MISMATCHED_PUBLIC_KEY)) - } - e => panic!("{:?}", e), - } - } -} diff --git a/common/remote_signer_consumer/tests/sign_attestation.rs b/common/remote_signer_consumer/tests/sign_attestation.rs deleted file mode 100644 index 49e1bd9c307..00000000000 --- a/common/remote_signer_consumer/tests/sign_attestation.rs +++ /dev/null @@ -1,49 +0,0 @@ -mod sign_attestation { - use rand::Rng; - use remote_signer_test::*; - - #[test] - fn sanity_check_deterministic() { - let test_input_local = get_input_local_signer_attestation(0xc137); - let local_signature = test_input_local.sign(); - - let (test_signer, _tmp_dir) = set_up_api_test_signer_to_sign_message(); - let test_client = set_up_test_consumer(&test_signer.address); - let test_input = get_input_data_attestation(0xc137); - - let remote_signature = do_sign_request(&test_client, test_input); - - assert_eq!(local_signature, remote_signature.unwrap()); - assert_eq!(local_signature, HAPPY_PATH_ATT_SIGNATURE_C137); - } - - #[test] - fn sanity_check_random() { - let mut rng = rand::thread_rng(); - let seed = rng.gen::() / 1024; - - let test_input_local = get_input_local_signer_attestation(seed); - let local_signature = test_input_local.sign(); - - let (test_signer, _tmp_dir) = set_up_api_test_signer_to_sign_message(); - let test_client = set_up_test_consumer(&test_signer.address); - let test_input = get_input_data_attestation(seed); - - let remote_signature = do_sign_request(&test_client, test_input); - - assert_eq!(local_signature, remote_signature.unwrap()); - } - - #[test] - fn happy_path() { - let (test_signer, _tmp_dir) = set_up_api_test_signer_to_sign_message(); - let test_client = set_up_test_consumer(&test_signer.address); - let test_input = get_input_data_attestation(0xc137); - - let signature = do_sign_request(&test_client, test_input); - - assert_eq!(signature.unwrap(), HAPPY_PATH_ATT_SIGNATURE_C137); - - test_signer.shutdown(); - } -} diff --git a/common/remote_signer_consumer/tests/sign_block.rs b/common/remote_signer_consumer/tests/sign_block.rs deleted file mode 100644 index bec0cfb426b..00000000000 --- a/common/remote_signer_consumer/tests/sign_block.rs +++ /dev/null @@ -1,49 +0,0 @@ -mod sign_block { - use rand::Rng; - use remote_signer_test::*; - - #[test] - fn sanity_check_deterministic() { - let test_input_local = get_input_local_signer_block(0xc137); - let local_signature = test_input_local.sign(); - - let (test_signer, _tmp_dir) = set_up_api_test_signer_to_sign_message(); - let test_client = set_up_test_consumer(&test_signer.address); - let test_input = get_input_data_block(0xc137); - - let remote_signature = do_sign_request(&test_client, test_input); - - assert_eq!(local_signature, remote_signature.unwrap()); - assert_eq!(local_signature, HAPPY_PATH_BLOCK_SIGNATURE_C137); - } - - #[test] - fn sanity_check_random() { - let mut rng = rand::thread_rng(); - let seed = rng.gen::() / 1024; - - let test_input_local = get_input_local_signer_block(seed); - let local_signature = test_input_local.sign(); - - let (test_signer, _tmp_dir) = set_up_api_test_signer_to_sign_message(); - let test_client = set_up_test_consumer(&test_signer.address); - let test_input = get_input_data_block(seed); - - let remote_signature = do_sign_request(&test_client, test_input); - - assert_eq!(local_signature, remote_signature.unwrap()); - } - - #[test] - fn happy_path() { - let (test_signer, _tmp_dir) = set_up_api_test_signer_to_sign_message(); - let test_client = set_up_test_consumer(&test_signer.address); - let test_input = get_input_data_block(0xc137); - - let signature = do_sign_request(&test_client, test_input); - - assert_eq!(signature.unwrap(), HAPPY_PATH_BLOCK_SIGNATURE_C137); - - test_signer.shutdown(); - } -} diff --git a/common/remote_signer_consumer/tests/sign_randao.rs b/common/remote_signer_consumer/tests/sign_randao.rs deleted file mode 100644 index 62baf1edc6c..00000000000 --- a/common/remote_signer_consumer/tests/sign_randao.rs +++ /dev/null @@ -1,50 +0,0 @@ -mod sign_randao { - use rand::Rng; - use remote_signer_test::*; - - #[test] - fn sanity_check_deterministic() { - let test_input_local = get_input_local_signer_randao(0xc137); - let local_signature = test_input_local.sign(); - - let (test_signer, _tmp_dir) = set_up_api_test_signer_to_sign_message(); - let test_client = set_up_test_consumer(&test_signer.address); - - let test_input = get_input_data_randao(0xc137); - - let remote_signature = do_sign_request(&test_client, test_input); - - assert_eq!(local_signature, remote_signature.unwrap()); - assert_eq!(local_signature, HAPPY_PATH_RANDAO_SIGNATURE_C137); - } - - #[test] - fn sanity_check_random() { - let mut rng = rand::thread_rng(); - let seed = rng.gen::(); - - let test_input_local = get_input_local_signer_randao(seed); - let local_signature = test_input_local.sign(); - - let (test_signer, _tmp_dir) = set_up_api_test_signer_to_sign_message(); - let test_client = set_up_test_consumer(&test_signer.address); - - let test_input = get_input_data_randao(seed); - - let remote_signature = do_sign_request(&test_client, test_input); - - assert_eq!(local_signature, remote_signature.unwrap()); - } - - #[test] - fn happy_path() { - let (test_signer, _tmp_dir) = set_up_api_test_signer_to_sign_message(); - let test_client = set_up_test_consumer(&test_signer.address); - - let test_input = get_input_data_randao(0xc137); - - let remote_signature = do_sign_request(&test_client, test_input); - - assert_eq!(remote_signature.unwrap(), HAPPY_PATH_RANDAO_SIGNATURE_C137); - } -} diff --git a/lighthouse/Cargo.toml b/lighthouse/Cargo.toml index 108e4e6dee6..229138d9f34 100644 --- a/lighthouse/Cargo.toml +++ b/lighthouse/Cargo.toml @@ -40,7 +40,6 @@ eth2_network_config = { path = "../common/eth2_network_config" } directory = { path = "../common/directory" } lighthouse_version = { path = "../common/lighthouse_version" } account_utils = { path = "../common/account_utils" } -remote_signer = { "path" = "../remote_signer" } lighthouse_metrics = { path = "../common/lighthouse_metrics" } lazy_static = "1.4.0" serde_json = "1.0.59" diff --git a/lighthouse/src/main.rs b/lighthouse/src/main.rs index ccfe121efe9..10f53ff7b20 100644 --- a/lighthouse/src/main.rs +++ b/lighthouse/src/main.rs @@ -164,7 +164,6 @@ fn main() { .subcommand(boot_node::cli_app()) .subcommand(validator_client::cli_app()) .subcommand(account_manager::cli_app()) - .subcommand(remote_signer::cli_app()) .get_matches(); // Configure the allocator early in the process, before it has the chance to use the default values for @@ -394,16 +393,6 @@ fn run( )); } } - ("remote_signer", Some(matches)) => { - if let Err(e) = remote_signer::run(&mut environment, matches) { - crit!(log, "Failed to start remote signer"; "reason" => e); - let _ = environment - .core_context() - .executor - .shutdown_sender() - .try_send(ShutdownReason::Failure("Failed to start remote signer")); - } - } _ => { crit!(log, "No subcommand supplied. See --help ."); return Err("No subcommand supplied.".into()); diff --git a/remote_signer/Cargo.toml b/remote_signer/Cargo.toml deleted file mode 100644 index a3b1fff638d..00000000000 --- a/remote_signer/Cargo.toml +++ /dev/null @@ -1,24 +0,0 @@ -[package] -name = "remote_signer" -version = "0.2.0" -authors = ["Sigma Prime "] -edition = "2018" - -[features] -# Compiles the BLS crypto code so that the binary is portable across machines. -portable = ["bls/supranational-portable"] -# Uses the slower Milagro BLS library, which is written in native Rust. -milagro = ["bls/milagro"] - -[dev-dependencies] -client_backend = { path = "./backend", package = "remote_signer_backend" } -helpers = { path = "../testing/remote_signer_test", package = "remote_signer_test" } - -[dependencies] -bls = { path = "../crypto/bls" } -clap = "2.33.3" -client = { path = "./client", package = "remote_signer_client" } -environment = { path = "../lighthouse/environment" } -serde_json = "1.0.58" -slog = { version = "2.5.2", features = ["max_level_trace"] } -types = { path = "../consensus/types"} diff --git a/remote_signer/README.md b/remote_signer/README.md deleted file mode 100644 index 94a5f50fd71..00000000000 --- a/remote_signer/README.md +++ /dev/null @@ -1,134 +0,0 @@ -# Remote BLS Signer - -## Overview - -Simple HTTP BLS signer service. - -This service is designed to be consumed by Ethereum 2.0 clients, looking for a more secure avenue to store their BLS12-381 secret keys, while running their validators in more permisive and/or scalable environments. - -One goal of this package is to be standard compliant. There is a [current draft for an Ethereum Improvement Proposal (EIP)](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-3030.md) in progress. Please refer to the [roadmap](#roadmap) for a list of advanced features. - -## API - -### Standard - -### `GET /upcheck` - -_**Responses**_ - -Success |
---- | --- -Code | `200` -Content | `{"status": "OK"}` - ---- - -### `GET /keys` - -Returns the identifiers of the keys available to the signer. - -_**Responses**_ - -Success |
---- | --- -Code | `200` -Content | `{"keys": "[identifier]"}` - ---- - -### `POST /sign/:identifier` - -URL Parameter |
---- | --- -`:identifier` | `public_key_hex_string_without_0x` - -_**Request**_ - -JSON Body |
|
---- | --- | --- -`bls_domain` | **Required** | The BLS Signature domain.
As defined in the [specification](https://github.com/ethereum/eth2.0-specs/blob/dev/specs/phase0/beacon-chain.md#domain-types), in lowercase, omitting the `domain` prefix.
Supporting `beacon_proposer`, `beacon_attester`, and `randao`. -`data` | **Required** | The data to be signed.
As defined in the specifications for [block](https://github.com/ethereum/eth2.0-APIs/blob/master/types/block.yaml), [attestation](https://github.com/ethereum/eth2.0-APIs/blob/master/types/attestation.yaml), and [epoch](https://github.com/ethereum/eth2.0-APIs/blob/master/types/misc.yaml). -`fork` | **Required** | A `Fork` object containing previous and current versions.
As defined in the [specification](https://github.com/ethereum/eth2.0-APIs/blob/master/types/misc.yaml) -`genesis_validators_root` | **Required** | A `Hash256` for domain separation and chain versioning. -
| Optional | Any other field will be ignored by the signer - -_**Responses**_ - -Success |
---- | --- -Code | `200` -Content | `{"signature": ""}` - -_or_ - -Error |
---- | --- -Code | `400` -Content | `{"error": ""}` - -_or_ - -Error |
---- | --- -Code | `404` -Content | `{"error": "Key not found: "}` - -## Build instructions - -1. [Get Rust](https://www.rust-lang.org/learn/get-started). -2. Go to the root directory of this repository. -3. Execute `make` -4. The binary `lighthouse` will most likely be found in `./target/release`. -5. Run it as `lighthouse remote_signer` or `lighthouse rs`. - -## Running the signer - -### Storing the secret keys as raw files - -* Steps to store a secret key - * Choose an empty directory, as the backend will parse every file looking for keys. - * Create a file named after the **hex representation of the public key without 0x**. - * Write the **hex representation of the secret key without 0x**. - * Store the file in your chosen directory. - * Use this directory as a command line parameter (`--storage-raw-dir`) - -### Command line flags - -``` -USAGE: - lighthouse remote_signer [OPTIONS] - -FLAGS: - -h, --help Prints help information - -V, --version Prints version information - -OPTIONS: - --debug-level The verbosity level for emitting logs. [default: info] [possible values: - info, debug, trace, warn, error, crit] - --listen-address
The address to listen for TCP connections. [default: 0.0.0.0] - --log-format Specifies the format used for logging. [possible values: JSON] - --logfile File path where output will be written. - --port The TCP port to listen on. [default: 9000] - --spec Specifies the default eth2 spec type. [default: mainnet] [possible values: - mainnet, minimal, interop] - --storage-raw-dir <DIR> Data directory for secret keys in raw files. -``` - -## Roadmap - -- [X] EIP standard compliant -- [ ] Metrics -- [ ] Benchmarking & Profiling -- [ ] Release management -- [ ] Architecture builds -- [ ] Support EIP-2335, BLS12-381 keystore -- [ ] Support storage in AWS Cloud HSM -- [ ] Route with the `warp` library -- [ ] Slashing protection pipeline -- [ ] TLS/SSL support for requests -- [ ] Authentication by HTTP Header support -- [ ] Confidential computing support (e.g. Intel SGX) - -## LICENSE - -* Apache 2.0. diff --git a/remote_signer/backend/Cargo.toml b/remote_signer/backend/Cargo.toml deleted file mode 100644 index 1abb9ed8850..00000000000 --- a/remote_signer/backend/Cargo.toml +++ /dev/null @@ -1,20 +0,0 @@ -[package] -name = "remote_signer_backend" -version = "0.2.0" -authors = ["Herman Junge <herman@sigmaprime.io>"] -edition = "2018" - -[dev-dependencies] -helpers = { path = "../../testing/remote_signer_test", package = "remote_signer_test" } -sloggers = "1.0.1" -tempfile = "3.1.0" - -[dependencies] -bls = { path = "../../crypto/bls" } -clap = "2.33.3" -hex = "0.4.2" -lazy_static = "1.4.0" -regex = "1.3.9" -slog = "2.5.2" -types = { path = "../../consensus/types" } -zeroize = { version = "1.1.1", features = ["zeroize_derive"] } diff --git a/remote_signer/backend/src/error.rs b/remote_signer/backend/src/error.rs deleted file mode 100644 index 63a87710c5f..00000000000 --- a/remote_signer/backend/src/error.rs +++ /dev/null @@ -1,46 +0,0 @@ -#[derive(Debug)] -pub enum BackendError { - /// Parameter is not a hexadecimal representation of a BLS public key. - InvalidPublicKey(String), - - /// Retrieved value is not a hexadecimal representation of a BLS secret key. - InvalidSecretKey(String), - - /// Public and Secret key won't match. - KeyMismatch(String), - - /// Item requested by its public key is not found. - KeyNotFound(String), - - /// Errors from the storage medium. - /// - /// When converted from `std::io::Error`, stores `std::io::ErrorKind` - /// and `std::io::Error` both formatted to string. - StorageError(String, String), -} - -impl std::fmt::Display for BackendError { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - match self { - BackendError::InvalidPublicKey(e) => write!(f, "Invalid public key: {}", e), - - // Feed it with the public key value used to retrieve it. - BackendError::InvalidSecretKey(e) => write!(f, "Invalid secret key: {}", e), - - // Feed it with the public key value used to retrieve it. - BackendError::KeyMismatch(e) => write!(f, "Key mismatch: {}", e), - - BackendError::KeyNotFound(e) => write!(f, "Key not found: {}", e), - - // Only outputs to string the first component of the tuple, accounting - // for potential differences on error displays between OS distributions. - BackendError::StorageError(e, _) => write!(f, "Storage error: {}", e), - } - } -} - -impl From<std::io::Error> for BackendError { - fn from(e: std::io::Error) -> BackendError { - BackendError::StorageError(format!("{:?}", e.kind()), format!("{}", e)) - } -} diff --git a/remote_signer/backend/src/lib.rs b/remote_signer/backend/src/lib.rs deleted file mode 100644 index 4c3c2464e18..00000000000 --- a/remote_signer/backend/src/lib.rs +++ /dev/null @@ -1,343 +0,0 @@ -mod error; -mod storage; -mod storage_raw_dir; -mod utils; -mod zeroize_string; - -use crate::zeroize_string::ZeroizeString; -use bls::SecretKey; -use clap::ArgMatches; -pub use error::BackendError; -use lazy_static::lazy_static; -use regex::Regex; -use slog::{info, Logger}; -pub use storage::Storage; -use storage_raw_dir::StorageRawDir; -use types::Hash256; -use utils::{bytes96_to_hex_string, validate_bls_pair}; - -lazy_static! { - static ref PUBLIC_KEY_REGEX: Regex = Regex::new(r"[0-9a-fA-F]{96}").unwrap(); -} - -/// A backend to be used by the Remote Signer HTTP API. -/// -/// Designed to support several types of storages. -#[derive(Clone)] -pub struct Backend<T> { - storage: T, -} - -impl Backend<StorageRawDir> { - /// Creates a Backend with the given storage type at the CLI arguments. - /// - /// # Storage types supported - /// - /// * Raw files in directory: `--storage-raw-dir <DIR>` - /// - pub fn new(cli_args: &ArgMatches<'_>, log: &Logger) -> Result<Self, String> { - // Storage types are mutually exclusive. - if let Some(path) = cli_args.value_of("storage-raw-dir") { - info!( - log, - "Loading Backend"; - "storage type" => "raw dir", - "directory" => path - ); - - StorageRawDir::new(path) - .map(|storage| Self { storage }) - .map_err(|e| format!("Storage Raw Dir: {}", e)) - } else { - Err("No storage type supplied.".to_string()) - } - } -} - -impl<T: Storage> Backend<T> { - /// Returns the available public keys in storage. - pub fn get_keys(&self) -> Result<Vec<String>, BackendError> { - self.storage.get_keys() - } - - /// Signs the message with the requested key in storage. - pub fn sign_message( - &self, - public_key: &str, - signing_root: Hash256, - ) -> Result<String, BackendError> { - if !PUBLIC_KEY_REGEX.is_match(public_key) || public_key.len() != 96 { - return Err(BackendError::InvalidPublicKey(public_key.to_string())); - } - - let secret_key: ZeroizeString = self.storage.get_secret_key(public_key)?; - let secret_key: SecretKey = validate_bls_pair(public_key, secret_key)?; - - let signature = secret_key.sign(signing_root); - - let signature: String = bytes96_to_hex_string(signature.serialize()) - .expect("Writing to a string should never error."); - - Ok(signature) - } -} - -#[cfg(test)] -pub mod tests_commons { - use super::*; - pub use crate::Storage; - use helpers::*; - use sloggers::{null::NullLoggerBuilder, Build}; - use tempfile::{tempdir, TempDir}; - - type T = StorageRawDir; - - pub fn new_storage_with_tmp_dir() -> (T, TempDir) { - let tmp_dir = tempdir().unwrap(); - let storage = StorageRawDir::new(tmp_dir.path().to_str().unwrap()).unwrap(); - (storage, tmp_dir) - } - - pub fn get_null_logger() -> Logger { - let log_builder = NullLoggerBuilder; - log_builder.build().unwrap() - } - - pub fn new_backend_for_get_keys() -> (Backend<T>, TempDir) { - let tmp_dir = tempdir().unwrap(); - - let matches = set_matches(vec![ - "this_test", - "--storage-raw-dir", - tmp_dir.path().to_str().unwrap(), - ]); - - let backend = match Backend::new(&matches, &get_null_logger()) { - Ok(backend) => (backend), - Err(e) => panic!("We should not be getting an err here: {}", e), - }; - - (backend, tmp_dir) - } - - pub fn new_backend_for_signing() -> (Backend<T>, TempDir) { - let (backend, tmp_dir) = new_backend_for_get_keys(); - - // This one has the whole fauna. - add_sub_dirs(&tmp_dir); - add_key_files(&tmp_dir); - add_non_key_files(&tmp_dir); - add_mismatched_key_file(&tmp_dir); - - (backend, tmp_dir) - } - - pub fn assert_backend_new_error(matches: &ArgMatches, error_msg: &str) { - match Backend::new(matches, &get_null_logger()) { - Ok(_) => panic!("This invocation to Backend::new() should return error"), - Err(e) => assert_eq!(e, error_msg), - } - } -} - -#[cfg(test)] -pub mod backend_new { - use super::*; - use crate::tests_commons::*; - use helpers::*; - use tempfile::tempdir; - - #[test] - fn no_storage_type_supplied() { - let matches = set_matches(vec!["this_test"]); - - assert_backend_new_error(&matches, "No storage type supplied."); - } - - #[test] - fn given_path_does_not_exist() { - let matches = set_matches(vec!["this_test", "--storage-raw-dir", "/dev/null/foo"]); - - assert_backend_new_error(&matches, "Storage Raw Dir: Path does not exist."); - } - - #[test] - fn given_path_is_not_a_dir() { - 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."); - } - - #[test] - fn given_inaccessible() { - let tmp_dir = tempdir().unwrap(); - restrict_permissions(tmp_dir.path()); - - let matches = set_matches(vec![ - "this_test", - "--storage-raw-dir", - tmp_dir.path().to_str().unwrap(), - ]); - - let result = Backend::new(&matches, &get_null_logger()); - - // A `d-wx--x--x` directory is innaccesible but not unwrittable. - // By switching back to `drwxr-xr-x` we can get rid of the - // temporal directory once we leave this scope. - unrestrict_permissions(tmp_dir.path()); - - match result { - Ok(_) => panic!("This invocation to Backend::new() should return error"), - Err(e) => assert_eq!(e, "Storage Raw Dir: PermissionDenied",), - } - } - - #[test] - fn happy_path() { - let (_backend, _tmp_dir) = new_backend_for_get_keys(); - } -} - -#[cfg(test)] -pub mod backend_raw_dir_get_keys { - use crate::tests_commons::*; - use helpers::*; - - #[test] - fn empty_dir() { - let (backend, _tmp_dir) = new_backend_for_get_keys(); - - assert_eq!(backend.get_keys().unwrap().len(), 0); - } - - #[test] - fn some_files_are_not_public_keys() { - let (backend, tmp_dir) = new_backend_for_get_keys(); - - add_sub_dirs(&tmp_dir); - add_key_files(&tmp_dir); - add_non_key_files(&tmp_dir); - - assert_eq!(backend.get_keys().unwrap().len(), 3); - } - - #[test] - fn all_files_are_public_keys() { - let (backend, tmp_dir) = new_backend_for_get_keys(); - add_key_files(&tmp_dir); - - assert_eq!(backend.get_keys().unwrap().len(), 3); - } -} - -#[cfg(test)] -pub mod backend_raw_dir_sign_message { - use crate::tests_commons::*; - use helpers::*; - use types::Hash256; - - #[test] - fn invalid_public_key() { - let (backend, _tmp_dir) = new_backend_for_signing(); - - let test_case = |public_key_param: &str| { - assert_eq!( - backend - .clone() - .sign_message( - public_key_param, - Hash256::from_slice(&hex::decode(SIGNING_ROOT).unwrap()) - ) - .unwrap_err() - .to_string(), - format!("Invalid public key: {}", public_key_param) - ); - }; - - test_case("abcdef"); // Length < 96. - test_case(&format!("{}55", PUBLIC_KEY_1)); // Length > 96. - test_case(SILLY_FILE_NAME_1); // Length == 96; Invalid hex characters. - } - - #[test] - fn storage_error() { - let (backend, tmp_dir) = new_backend_for_signing(); - - restrict_permissions(tmp_dir.path()); - restrict_permissions(&tmp_dir.path().join(PUBLIC_KEY_1)); - - let result = backend.sign_message( - PUBLIC_KEY_1, - Hash256::from_slice(&hex::decode(SIGNING_ROOT).unwrap()), - ); - - unrestrict_permissions(tmp_dir.path()); - unrestrict_permissions(&tmp_dir.path().join(PUBLIC_KEY_1)); - - assert_eq!( - result.unwrap_err().to_string(), - "Storage error: PermissionDenied" - ); - } - - #[test] - fn key_not_found() { - let (backend, _tmp_dir) = new_backend_for_signing(); - - assert_eq!( - backend - .sign_message( - ABSENT_PUBLIC_KEY, - Hash256::from_slice(&hex::decode(SIGNING_ROOT).unwrap()) - ) - .unwrap_err() - .to_string(), - format!("Key not found: {}", ABSENT_PUBLIC_KEY) - ); - } - - #[test] - fn key_mismatch() { - let (backend, _tmp_dir) = new_backend_for_signing(); - - assert_eq!( - backend - .sign_message( - MISMATCHED_PUBLIC_KEY, - Hash256::from_slice(&hex::decode(SIGNING_ROOT).unwrap()) - ) - .unwrap_err() - .to_string(), - format!("Key mismatch: {}", MISMATCHED_PUBLIC_KEY) - ); - } - - #[test] - fn happy_path() { - let (backend, _tmp_dir) = new_backend_for_signing(); - - let test_case = |public_key: &str, signature: &str| { - assert_eq!( - backend - .clone() - .sign_message( - public_key, - Hash256::from_slice(&hex::decode(SIGNING_ROOT).unwrap()) - ) - .unwrap(), - signature - ); - }; - - test_case(PUBLIC_KEY_1, EXPECTED_SIGNATURE_1); - test_case(PUBLIC_KEY_2, EXPECTED_SIGNATURE_2); - test_case(PUBLIC_KEY_3, EXPECTED_SIGNATURE_3); - } -} diff --git a/remote_signer/backend/src/storage.rs b/remote_signer/backend/src/storage.rs deleted file mode 100644 index 6b413c393dc..00000000000 --- a/remote_signer/backend/src/storage.rs +++ /dev/null @@ -1,10 +0,0 @@ -use crate::{BackendError, ZeroizeString}; - -/// The storage medium for the secret keys used by a `Backend`. -pub trait Storage: 'static + Clone + Send + Sync { - /// Queries storage for the available keys to sign. - fn get_keys(&self) -> Result<Vec<String>, BackendError>; - - /// Retrieves secret key from storage, using its public key as reference. - fn get_secret_key(&self, input: &str) -> Result<ZeroizeString, BackendError>; -} diff --git a/remote_signer/backend/src/storage_raw_dir.rs b/remote_signer/backend/src/storage_raw_dir.rs deleted file mode 100644 index 8ae5dfd6766..00000000000 --- a/remote_signer/backend/src/storage_raw_dir.rs +++ /dev/null @@ -1,181 +0,0 @@ -use crate::{BackendError, Storage, ZeroizeString, PUBLIC_KEY_REGEX}; -use std::fs::read_dir; -use std::fs::File; -use std::io::prelude::Read; -use std::io::BufReader; -use std::path::Path; -use std::path::PathBuf; - -#[derive(Clone)] -pub struct StorageRawDir { - path: PathBuf, -} - -impl StorageRawDir { - /// Initializes the storage with the given path, verifying - /// whether it is a directory and if its available to the user. - /// Does not list, nor verify the contents of the directory. - pub fn new<P: AsRef<Path>>(path: P) -> Result<Self, String> { - let path = path.as_ref(); - - if !path.exists() { - return Err("Path does not exist.".to_string()); - } - - if !path.is_dir() { - return Err("Path is not a directory.".to_string()); - } - - read_dir(path).map_err(|e| format!("{:?}", e.kind()))?; - - Ok(Self { - path: path.to_path_buf(), - }) - } -} - -impl Storage for StorageRawDir { - /// List all the files in the directory having a BLS public key name. - /// This function DOES NOT check the contents of each file. - fn get_keys(&self) -> Result<Vec<String>, BackendError> { - let entries = read_dir(&self.path).map_err(BackendError::from)?; - - // We are silently suppressing errors in this chain - // because we only care about files actually passing these filters. - let keys: Vec<String> = entries - .filter_map(|entry| entry.ok()) - .filter(|entry| !entry.path().is_dir()) - .map(|entry| entry.file_name().into_string()) - .filter_map(|entry| entry.ok()) - .filter(|name| PUBLIC_KEY_REGEX.is_match(name)) - .collect(); - - Ok(keys) - } - - /// Gets a requested secret key by their reference, its public key. - /// This function DOES NOT check the contents of the retrieved file. - fn get_secret_key(&self, input: &str) -> Result<ZeroizeString, BackendError> { - let file = File::open(self.path.join(input)).map_err(|e| match e.kind() { - std::io::ErrorKind::NotFound => BackendError::KeyNotFound(input.to_string()), - _ => e.into(), - })?; - let mut buf_reader = BufReader::new(file); - - let mut secret_key = String::new(); - buf_reader.read_to_string(&mut secret_key)?; - - // Remove that `\n` without cloning. - secret_key.pop(); - - Ok(ZeroizeString::from(secret_key)) - } -} - -#[cfg(test)] -mod get_keys { - use crate::tests_commons::*; - use helpers::*; - - #[test] - fn problem_with_path() { - let (storage, tmp_dir) = new_storage_with_tmp_dir(); - add_key_files(&tmp_dir); - - // All good and fancy, let's make the dir innacessible now. - restrict_permissions(tmp_dir.path()); - - let result = storage.get_keys(); - - // Give permissions back, we want the tempdir to be deleted. - unrestrict_permissions(tmp_dir.path()); - - assert_eq!( - result.unwrap_err().to_string(), - "Storage error: PermissionDenied" - ); - } - - #[test] - fn no_files_in_dir() { - let (storage, _tmp_dir) = new_storage_with_tmp_dir(); - - assert_eq!(storage.get_keys().unwrap().len(), 0); - } - - #[test] - fn no_files_in_dir_are_public_keys() { - let (storage, tmp_dir) = new_storage_with_tmp_dir(); - add_sub_dirs(&tmp_dir); - add_non_key_files(&tmp_dir); - - assert_eq!(storage.get_keys().unwrap().len(), 0); - } - - #[test] - fn not_all_files_have_public_key_names() { - let (storage, tmp_dir) = new_storage_with_tmp_dir(); - add_sub_dirs(&tmp_dir); - add_key_files(&tmp_dir); - add_non_key_files(&tmp_dir); - - assert_eq!(storage.get_keys().unwrap().len(), 3); - } - - #[test] - fn all_files_do_have_public_key_names() { - let (storage, tmp_dir) = new_storage_with_tmp_dir(); - add_key_files(&tmp_dir); - - assert_eq!(storage.get_keys().unwrap().len(), 3); - } -} - -#[cfg(test)] -mod get_secret_key { - use crate::tests_commons::*; - use helpers::*; - - #[test] - fn unaccessible_file() { - let (storage, tmp_dir) = new_storage_with_tmp_dir(); - add_key_files(&tmp_dir); - - restrict_permissions(tmp_dir.path()); - restrict_permissions(&tmp_dir.path().join(PUBLIC_KEY_1)); - - let result = storage.get_secret_key(PUBLIC_KEY_1); - - unrestrict_permissions(tmp_dir.path()); - unrestrict_permissions(&tmp_dir.path().join(PUBLIC_KEY_1)); - - assert_eq!( - result.unwrap_err().to_string(), - "Storage error: PermissionDenied" - ); - } - - #[test] - fn key_does_not_exist() { - let (storage, _tmp_dir) = new_storage_with_tmp_dir(); - - assert_eq!( - storage - .get_secret_key(PUBLIC_KEY_1) - .unwrap_err() - .to_string(), - format!("Key not found: {}", PUBLIC_KEY_1) - ); - } - - #[test] - fn happy_path() { - let (storage, tmp_dir) = new_storage_with_tmp_dir(); - add_key_files(&tmp_dir); - - assert_eq!( - storage.get_secret_key(PUBLIC_KEY_1).unwrap().as_ref(), - SECRET_KEY_1.as_bytes() - ); - } -} diff --git a/remote_signer/backend/src/utils.rs b/remote_signer/backend/src/utils.rs deleted file mode 100644 index 56859a28e1c..00000000000 --- a/remote_signer/backend/src/utils.rs +++ /dev/null @@ -1,123 +0,0 @@ -use crate::{BackendError, ZeroizeString}; -use bls::SecretKey; -use hex::decode; -use std::fmt::{Error, Write}; -use std::str; - -// hex::encode only allows up to 32 bytes. -pub fn bytes96_to_hex_string(data: [u8; 96]) -> Result<String, Error> { - static CHARS: &[u8] = b"0123456789abcdef"; - let mut s = String::with_capacity(96 * 2 + 2); - - s.write_char('0')?; - s.write_char('x')?; - - for &byte in data.iter() { - s.write_char(CHARS[(byte >> 4) as usize].into())?; - s.write_char(CHARS[(byte & 0xf) as usize].into())?; - } - - Ok(s) -} - -/// Validates the match as a BLS pair of the public and secret keys given, -/// consuming the secret key parameter, and returning a deserialized SecretKey. -pub fn validate_bls_pair( - public_key: &str, - secret_key: ZeroizeString, -) -> Result<SecretKey, BackendError> { - let secret_key: SecretKey = secret_key.into_bls_sk().map_err(|e| { - BackendError::InvalidSecretKey(format!("public_key: {}; {}", public_key, e)) - })?; - - let pk_param_as_bytes = decode(&public_key) - .map_err(|e| BackendError::InvalidPublicKey(format!("{}; {}", public_key, e)))?; - - if &secret_key.public_key().serialize()[..] != pk_param_as_bytes.as_slice() { - return Err(BackendError::KeyMismatch(public_key.to_string())); - } - - Ok(secret_key) -} - -#[cfg(test)] -mod functions { - use super::*; - use helpers::*; - - #[test] - fn fn_bytes96_to_hex_string() { - assert_eq!( - bytes96_to_hex_string(EXPECTED_SIGNATURE_1_BYTES).unwrap(), - EXPECTED_SIGNATURE_1 - ); - - assert_eq!( - bytes96_to_hex_string(EXPECTED_SIGNATURE_2_BYTES).unwrap(), - EXPECTED_SIGNATURE_2 - ); - - assert_eq!( - bytes96_to_hex_string(EXPECTED_SIGNATURE_3_BYTES).unwrap(), - EXPECTED_SIGNATURE_3 - ); - } - - #[test] - fn fn_validate_bls_pair() { - let test_ok_case = |pk: &str, sk: ZeroizeString, sk_bytes: &[u8; 32]| { - let serialized_secret_key = validate_bls_pair(pk, sk).unwrap().serialize(); - assert_eq!(serialized_secret_key.as_bytes().to_vec(), sk_bytes.to_vec()); - }; - - test_ok_case( - PUBLIC_KEY_1, - ZeroizeString::from(SECRET_KEY_1.to_string()), - &SECRET_KEY_1_BYTES, - ); - - let test_error_case = |pk: &str, sk: ZeroizeString, expected_error: &str| { - assert_eq!( - validate_bls_pair(pk, sk).err().unwrap().to_string(), - expected_error - ); - }; - - test_error_case( - PUBLIC_KEY_2, - ZeroizeString::from("TamperedKey%#$#%#$$&##00£$%$$£%$".to_string()), - &format!( - "Invalid secret key: public_key: {}; Invalid hex character: T at index 0", - PUBLIC_KEY_2 - ), - ); - - test_error_case( - PUBLIC_KEY_2, - ZeroizeString::from("deadbeef".to_string()), - &format!( - "Invalid secret key: public_key: {}; InvalidSecretKeyLength {{ got: 4, expected: 32 }}", - PUBLIC_KEY_2 - ), - ); - - let bad_pk_param = "not_validated_by_the_api_handler!"; - test_error_case( - bad_pk_param, - ZeroizeString::from(SECRET_KEY_1.to_string()), - &format!("Invalid public key: {}; Odd number of digits", bad_pk_param), - ); - - test_error_case( - PUBLIC_KEY_1, - ZeroizeString::from(SECRET_KEY_2.to_string()), - &format!("Key mismatch: {}", PUBLIC_KEY_1), - ); - - test_error_case( - PUBLIC_KEY_2, - ZeroizeString::from(SECRET_KEY_3.to_string()), - &format!("Key mismatch: {}", PUBLIC_KEY_2), - ); - } -} diff --git a/remote_signer/backend/src/zeroize_string.rs b/remote_signer/backend/src/zeroize_string.rs deleted file mode 100644 index bb76e1e87e7..00000000000 --- a/remote_signer/backend/src/zeroize_string.rs +++ /dev/null @@ -1,222 +0,0 @@ -use bls::SecretKey; -use std::str; -use zeroize::Zeroize; - -/// Provides a new-type wrapper around `String` that is zeroized on `Drop`. -/// -/// Useful for ensuring that secret key memory is zeroed-out on drop. -#[derive(Debug, Zeroize)] -#[zeroize(drop)] -pub struct ZeroizeString(String); - -impl From<String> for ZeroizeString { - fn from(s: String) -> Self { - Self(s) - } -} - -impl AsRef<[u8]> for ZeroizeString { - fn as_ref(&self) -> &[u8] { - self.0.as_bytes() - } -} - -impl ZeroizeString { - /// Consumes the ZeroizeString, attempting to return a BLS SecretKey. - pub fn into_bls_sk(self) -> Result<SecretKey, String> { - let mut decoded_bytes = hex_string_to_bytes(&self.0)?; - - let secret_key = SecretKey::deserialize(&decoded_bytes).map_err(|e| format!("{:?}", e))?; - decoded_bytes.zeroize(); - - Ok(secret_key) - } -} - -// An alternative to `hex::decode`, to allow for more control of -// the objects created while decoding the secret key. -fn hex_string_to_bytes(data: &str) -> Result<Vec<u8>, String> { - if data.len() % 2 != 0 { - return Err("Odd length".to_string()); - } - - let mut vec: Vec<u8> = Vec::new(); - for i in 0..data.len() / 2 { - vec.push( - val(&data.as_bytes()[2 * i], 2 * i)? << 4 - | val(&data.as_bytes()[2 * i + 1], 2 * i + 1)?, - ); - } - - Ok(vec) -} - -// Auxiliar function for `hex_string_to_bytes`. -fn val(c: &u8, idx: usize) -> Result<u8, String> { - match c { - b'A'..=b'F' => Ok(c - b'A' + 10), - b'a'..=b'f' => Ok(c - b'a' + 10), - b'0'..=b'9' => Ok(c - b'0'), - _ => Err(format!( - "Invalid hex character: {} at index {}", - *c as char, idx - )), - } -} - -#[cfg(test)] -mod object { - use super::*; - use helpers::*; - use zeroize::Zeroize; - - #[test] - fn v_u8_zeroized() { - // Create from `hex_string_to_bytes`, and record the pointer to its buffer. - let mut decoded_bytes = hex_string_to_bytes(SECRET_KEY_1).unwrap(); - let old_pointer = decoded_bytes.as_ptr() as usize; - - // Do something with the borrowed vector, and zeroize. - let _ = SecretKey::deserialize(&decoded_bytes) - .map_err(|e| format!("{:?}", e)) - .unwrap(); - decoded_bytes.zeroize(); - - // Check it is pointing to the same buffer, and that it was deleted. - assert_eq!(old_pointer as usize, decoded_bytes.as_ptr() as usize); - assert!(decoded_bytes.is_empty()); - - // Check if the underlying bytes were zeroized. - for i in 0..SECRET_KEY_1.len() / 2 { - unsafe { - assert_eq!(*((old_pointer + i) as *const u8), 0); - } - } - } - - #[test] - fn fn_to_bls_sk() { - let test_ok_case = |sk: &str, sk_b: &[u8]| { - let z = ZeroizeString::from(sk.to_string()); - let sk: SecretKey = z.into_bls_sk().unwrap(); - assert_eq!(sk.serialize().as_bytes(), sk_b); - }; - - let test_error_case = |sk: &str, err_msg: &str| { - let z = ZeroizeString::from(sk.to_string()); - let err = z.into_bls_sk().err(); - assert_eq!(err, Some(err_msg.to_string())); - }; - - test_ok_case(SECRET_KEY_1, &SECRET_KEY_1_BYTES); - - test_error_case("Trolololololo", "Odd length"); - test_error_case("Trololololol", "Invalid hex character: T at index 0"); - test_error_case( - "そんなことないでしょうけどう", - "Invalid hex character: ã at index 0", - ); - } - - #[test] - fn zeroized_after_drop() { - let some_scope = |s: &str| -> usize { - // Convert our literal into a String, then store the pointer - // to the first byte of its slice. - let s: String = s.to_string(); - let s_ptr = s.as_ptr(); - - // Just to make sure that the pointer of the string is NOT - // the same as the pointer of the underlying buffer. - assert_ne!(&s as *const String as usize, s_ptr as usize); - - let z = ZeroizeString::from(s); - - // Get the pointer to the underlying buffer, - // We want to make sure is the same as the received string literal. - // That is, no copies of the contents. - let ptr_to_buf = z.as_ref().as_ptr(); - assert_eq!(ptr_to_buf, s_ptr); - - // We exit this scope, returning to the caller the pointer to - // the buffer, that we'll use to verify the zeroization. - ptr_to_buf as usize - }; - - // Call the closure. - let ptr_to_buf = some_scope(SECRET_KEY_1); - - // Check if the underlying bytes were zeroized. - // At this point the first half is already reclaimed and assigned, - // so we will just examine the other half. - for i in SECRET_KEY_1.len() / 2..SECRET_KEY_1.len() { - unsafe { - assert_eq!(*((ptr_to_buf + i) as *const u8), 0); - } - } - } -} - -#[cfg(test)] -mod functions { - use super::*; - use helpers::*; - - #[test] - fn fn_hex_string_to_bytes() { - assert_eq!( - hex_string_to_bytes(&"0aa".to_string()).err(), - Some("Odd length".to_string()) - ); - - assert_eq!( - hex_string_to_bytes(&"0xdeadbeef".to_string()).err(), - Some("Invalid hex character: x at index 1".to_string()) - ); - - assert_eq!( - hex_string_to_bytes(&"n00bn00b".to_string()).err(), - Some("Invalid hex character: n at index 0".to_string()) - ); - - assert_eq!( - hex_string_to_bytes(&"abcdefgh".to_string()).err(), - Some("Invalid hex character: g at index 6".to_string()) - ); - - assert_eq!( - hex_string_to_bytes(SECRET_KEY_1).unwrap(), - SECRET_KEY_1_BYTES - ); - - assert_eq!( - hex_string_to_bytes(PUBLIC_KEY_1).unwrap(), - PUBLIC_KEY_1_BYTES.to_vec() - ); - - assert_eq!( - hex_string_to_bytes(SIGNING_ROOT).unwrap(), - SIGNING_ROOT_BYTES.to_vec() - ); - - assert_eq!( - hex_string_to_bytes(&EXPECTED_SIGNATURE_1[2..]).unwrap(), - EXPECTED_SIGNATURE_1_BYTES.to_vec() - ); - - assert_eq!( - hex_string_to_bytes(&EXPECTED_SIGNATURE_2[2..]).unwrap(), - EXPECTED_SIGNATURE_2_BYTES.to_vec() - ); - - assert_eq!( - hex_string_to_bytes(&EXPECTED_SIGNATURE_3[2..]).unwrap(), - EXPECTED_SIGNATURE_3_BYTES.to_vec() - ); - - assert_eq!( - hex_string_to_bytes(&"0a0b11".to_string()).unwrap(), - vec![10, 11, 17] - ); - } -} diff --git a/remote_signer/client/Cargo.toml b/remote_signer/client/Cargo.toml deleted file mode 100644 index 7b6b3f24782..00000000000 --- a/remote_signer/client/Cargo.toml +++ /dev/null @@ -1,19 +0,0 @@ -[package] -name = "remote_signer_client" -version = "0.2.0" -authors = ["Herman Junge <herman@sigmaprime.io>"] -edition = "2018" - -[dependencies] -clap = "2.33.3" -client_backend = { path = "../backend", package = "remote_signer_backend" } -environment = { path = "../../lighthouse/environment" } -futures = "0.3.6" -hyper = "0.14.4" -lazy_static = "1.4.0" -regex = "1.3.9" -serde = { version = "1.0.116", features = ["derive"] } -serde_json = "1.0.58" -slog = "2.5.2" -types = { path = "../../consensus/types" } -task_executor = { "path" = "../../common/task_executor" } diff --git a/remote_signer/client/src/api_error.rs b/remote_signer/client/src/api_error.rs deleted file mode 100644 index 1fdb3ec8792..00000000000 --- a/remote_signer/client/src/api_error.rs +++ /dev/null @@ -1,57 +0,0 @@ -use hyper::{Body, Response, StatusCode}; -use serde::{Deserialize, Serialize}; -use serde_json::to_string; -use std::error::Error as StdError; - -#[derive(PartialEq, Debug, Clone)] -pub enum ApiError { - ServerError(String), - NotImplemented(String), - BadRequest(String), - NotFound(String), -} - -#[derive(Deserialize, Serialize)] -pub struct ApiErrorDesc { - pub error: String, -} - -pub type ApiResult = Result<Response<Body>, ApiError>; - -impl ApiError { - pub fn status_code(self) -> (StatusCode, String) { - match self { - ApiError::ServerError(desc) => (StatusCode::INTERNAL_SERVER_ERROR, desc), - ApiError::NotImplemented(desc) => (StatusCode::NOT_IMPLEMENTED, desc), - ApiError::BadRequest(desc) => (StatusCode::BAD_REQUEST, desc), - ApiError::NotFound(desc) => (StatusCode::NOT_FOUND, desc), - } - } -} - -impl Into<Response<Body>> for ApiError { - fn into(self) -> Response<Body> { - let (status_code, desc) = self.status_code(); - - let json_desc = to_string(&ApiErrorDesc { error: desc }) - .expect("The struct ApiErrorDesc should always serialize."); - - Response::builder() - .status(status_code) - .body(Body::from(json_desc)) - .expect("Response should always be created.") - } -} - -impl StdError for ApiError { - fn cause(&self) -> Option<&dyn StdError> { - None - } -} - -impl std::fmt::Display for ApiError { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - let status = self.clone().status_code(); - write!(f, "{:?}: {:?}", status.0, status.1) - } -} diff --git a/remote_signer/client/src/api_response.rs b/remote_signer/client/src/api_response.rs deleted file mode 100644 index f65be75308f..00000000000 --- a/remote_signer/client/src/api_response.rs +++ /dev/null @@ -1,18 +0,0 @@ -use serde::{Deserialize, Serialize}; - -#[derive(Serialize)] -pub struct UpcheckApiResponse { - pub status: String, -} - -/// Contains the response to the `get_keys` API. -#[derive(Deserialize, Serialize)] -pub struct KeysApiResponse { - pub keys: Vec<String>, -} - -/// Contains the response to the `sign_message` API. -#[derive(Deserialize, Serialize)] -pub struct SignatureApiResponse { - pub signature: String, -} diff --git a/remote_signer/client/src/backend.rs b/remote_signer/client/src/backend.rs deleted file mode 100644 index 3dfd8a210c4..00000000000 --- a/remote_signer/client/src/backend.rs +++ /dev/null @@ -1,70 +0,0 @@ -use crate::api_error::ApiError; -use crate::api_response::{KeysApiResponse, SignatureApiResponse}; -use crate::rest_api::Context; -use crate::signing_root::get_signing_root; -use client_backend::{BackendError, Storage}; -use hyper::Request; -use lazy_static::lazy_static; -use regex::Regex; -use std::sync::Arc; -use types::EthSpec; - -lazy_static! { - static ref PUBLIC_KEY_FROM_PATH_REGEX: Regex = Regex::new(r"^/[^/]+/([^/]*)").unwrap(); -} - -/// HTTP handler to get the list of public keys in the backend. -pub fn get_keys<E: EthSpec, S: Storage, U>( - _: U, - ctx: Arc<Context<E, S>>, -) -> Result<KeysApiResponse, ApiError> { - let keys = ctx - .backend - .get_keys() - .map_err(|e| ApiError::ServerError(format!("{}", e)))?; - - if keys.is_empty() { - return Err(ApiError::NotFound("No keys found in storage.".to_string())); - } - - Ok(KeysApiResponse { keys }) -} - -/// HTTP handler to sign a message with the requested key. -pub fn sign_message<E: EthSpec, S: Storage>( - req: Request<Vec<u8>>, - ctx: Arc<Context<E, S>>, -) -> Result<SignatureApiResponse, ApiError> { - // Parse the request body and compute the signing root. - let signing_root = get_signing_root::<E>(&req, ctx.spec.clone())?; - - // This public key parameter should have been validated by the router. - // We are just going to extract it from the request. - let path = req.uri().path().to_string(); - - let rc = |path: &str| -> Result<String, String> { - let caps = PUBLIC_KEY_FROM_PATH_REGEX.captures(path).ok_or("")?; - let re_match = caps.get(1).ok_or("")?; - Ok(re_match.as_str().to_string()) - }; - - let public_key = rc(&path).map_err(|_| { - ApiError::BadRequest(format!("Unable to get public key from path: {:?}", path)) - })?; - - match ctx.backend.sign_message(&public_key, signing_root) { - Ok(signature) => Ok(SignatureApiResponse { signature }), - - Err(BackendError::KeyNotFound(_)) => { - Err(ApiError::NotFound(format!("Key not found: {}", public_key))) - } - - Err(BackendError::InvalidPublicKey(_)) => Err(ApiError::BadRequest(format!( - "Invalid public key: {}", - public_key - ))), - - // Catches InvalidSecretKey, KeyMismatch and StorageError. - Err(e) => Err(ApiError::ServerError(e.to_string())), - } -} diff --git a/remote_signer/client/src/config.rs b/remote_signer/client/src/config.rs deleted file mode 100644 index 434f0030bc3..00000000000 --- a/remote_signer/client/src/config.rs +++ /dev/null @@ -1,20 +0,0 @@ -use serde::{Deserialize, Serialize}; -use std::net::Ipv4Addr; - -/// HTTP REST API Configuration -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct Config { - /// The IPv4 address the REST API HTTP server will listen on. - pub listen_address: Ipv4Addr, - /// The port the REST API HTTP server will listen on. - pub port: u16, -} - -impl Default for Config { - fn default() -> Self { - Config { - listen_address: Ipv4Addr::new(127, 0, 0, 1), - port: 9000, - } - } -} diff --git a/remote_signer/client/src/handler.rs b/remote_signer/client/src/handler.rs deleted file mode 100644 index 50390c559be..00000000000 --- a/remote_signer/client/src/handler.rs +++ /dev/null @@ -1,111 +0,0 @@ -use crate::api_error::{ApiError, ApiResult}; -use crate::rest_api::Context; -use hyper::{Body, Request, Response, StatusCode}; -use serde::Serialize; -use std::sync::Arc; -use types::EthSpec; - -/// Provides a HTTP request handler with specific functionality. -pub struct Handler<E: EthSpec, S: Send + Sync> { - req: Request<()>, - body: Body, - ctx: Arc<Context<E, S>>, - allow_body: bool, -} - -impl<E: EthSpec, S: 'static + Send + Sync> Handler<E, S> { - /// Start handling a new request. - pub fn new(req: Request<Body>, ctx: Arc<Context<E, S>>) -> Result<Self, ApiError> { - let (req_parts, body) = req.into_parts(); - let req = Request::from_parts(req_parts, ()); - - Ok(Self { - req, - body, - ctx, - allow_body: false, - }) - } - - /// Return a simple static value. - /// - /// Does not use the blocking executor. - pub async fn static_value<V>(self, value: V) -> Result<HandledRequest<V>, ApiError> { - // Always check and disallow a body for a static value. - let _ = Self::get_body(self.body, false).await?; - - Ok(HandledRequest { value }) - } - - /// The default behaviour is to return an error if any body is supplied in the request. Calling - /// this function disables that error. - pub fn allow_body(mut self) -> Self { - self.allow_body = true; - self - } - - /// Spawns `func` on the blocking executor. - /// - /// This method is suitable for handling long-running or intensive tasks. - pub async fn in_blocking_task<F, V>(self, func: F) -> Result<HandledRequest<V>, ApiError> - where - V: Send + Sync + 'static, - F: Fn(Request<Vec<u8>>, Arc<Context<E, S>>) -> Result<V, ApiError> + Send + Sync + 'static, - { - let ctx = self.ctx; - let executor = ctx.executor.clone(); - let body = Self::get_body(self.body, self.allow_body).await?; - let (req_parts, _) = self.req.into_parts(); - let req = Request::from_parts(req_parts, body); - - // NOTE: The task executor now holds a weak reference to the global runtime. On shutdown - // there may be no runtime available. - // All these edge cases must be handled here. - let value = executor - .spawn_blocking_handle(move || func(req, ctx), "remote_signer_request") - .ok_or_else(|| ApiError::ServerError("Runtime does not exist".to_string()))? - .await - .map_err(|_| ApiError::ServerError("Panic during execution".to_string()))??; - - Ok(HandledRequest { value }) - } - - /// Downloads the bytes for `body`. - async fn get_body(body: Body, allow_body: bool) -> Result<Vec<u8>, ApiError> { - let bytes = hyper::body::to_bytes(body) - .await - .map_err(|e| ApiError::ServerError(format!("Unable to get request body: {:?}", e)))?; - - if !allow_body && !bytes[..].is_empty() { - Err(ApiError::BadRequest( - "The request body must be empty".to_string(), - )) - } else { - Ok(bytes.into_iter().collect()) - } - } -} - -/// A request that has been "handled" and now a result (`value`) needs to be serialized and -/// returned. -pub struct HandledRequest<V> { - value: V, -} - -impl<V: Serialize> HandledRequest<V> { - /// Suitable for items which only implement `serde`. - pub fn serde_encodings(self) -> ApiResult { - let body = Body::from(serde_json::to_string(&self.value).map_err(|e| { - ApiError::ServerError(format!( - "Unable to serialize response body as JSON: {:?}", - e - )) - })?); - - Response::builder() - .status(StatusCode::OK) - .header("content-type", "application/json") - .body(body) - .map_err(|e| ApiError::ServerError(format!("Failed to build response: {:?}", e))) - } -} diff --git a/remote_signer/client/src/lib.rs b/remote_signer/client/src/lib.rs deleted file mode 100644 index 54aa7779cc0..00000000000 --- a/remote_signer/client/src/lib.rs +++ /dev/null @@ -1,58 +0,0 @@ -pub mod api_error; -pub mod api_response; - -mod backend; -mod config; -mod handler; -mod rest_api; -mod router; -mod signing_root; -mod upcheck; - -use clap::ArgMatches; -use client_backend::Backend; -use config::Config; -use environment::RuntimeContext; -use std::net::Ipv4Addr; -use std::net::SocketAddr; -use types::EthSpec; - -pub struct Client { - listening_address: SocketAddr, -} - -impl Client { - pub async fn new<E: EthSpec>( - context: RuntimeContext<E>, - cli_args: &ArgMatches<'_>, - ) -> Result<Self, String> { - let log = context.executor.log(); - - let mut config = Config::default(); - - if let Some(address) = cli_args.value_of("listen-address") { - config.listen_address = address - .parse::<Ipv4Addr>() - .map_err(|_| "listen-address is not a valid IPv4 address.")?; - } - - if let Some(port) = cli_args.value_of("port") { - config.port = port - .parse::<u16>() - .map_err(|_| "port is not a valid u16.")?; - } - - let backend = Backend::new(cli_args, log)?; - - // It is useful to get the listening address if you have set up your port to be 0. - let listening_address = - rest_api::start_server(context.executor, config, backend, context.eth_spec_instance) - .map_err(|e| format!("Failed to start HTTP API: {:?}", e))?; - - Ok(Self { listening_address }) - } - - pub fn get_listening_address(&self) -> SocketAddr { - self.listening_address - } -} diff --git a/remote_signer/client/src/rest_api.rs b/remote_signer/client/src/rest_api.rs deleted file mode 100644 index 46179b9107e..00000000000 --- a/remote_signer/client/src/rest_api.rs +++ /dev/null @@ -1,91 +0,0 @@ -use crate::config::Config; -use client_backend::{Backend, Storage}; -use futures::future::TryFutureExt; -use hyper::server::conn::AddrStream; -use hyper::service::{make_service_fn, service_fn}; -use hyper::{Body, Request, Server}; -use slog::{info, warn}; -use std::net::SocketAddr; -use std::sync::Arc; -use task_executor::TaskExecutor; -use types::{ChainSpec, EthSpec}; - -pub struct Context<E: EthSpec, S: Send + Sync> { - pub config: Config, - pub executor: TaskExecutor, - pub log: slog::Logger, - pub backend: Backend<S>, - pub eth_spec_instance: E, - pub spec: ChainSpec, -} - -pub fn start_server<E: EthSpec, S: Storage>( - executor: TaskExecutor, - config: Config, - backend: Backend<S>, - eth_spec_instance: E, -) -> Result<SocketAddr, hyper::Error> { - let log = executor.log(); - - let context = Arc::new(Context { - executor: executor.clone(), - log: log.clone(), - config: config.clone(), - backend, - eth_spec_instance, - spec: E::default_spec(), - }); - - // Define the function that will build the request handler. - let make_service = make_service_fn(move |_socket: &AddrStream| { - let ctx = context.clone(); - - async move { - Ok::<_, hyper::Error>(service_fn(move |req: Request<Body>| { - crate::router::on_http_request(req, ctx.clone()) - })) - } - }); - - let bind_addr = (config.listen_address, config.port).into(); - let server = Server::bind(&bind_addr).serve(make_service); - - // Determine the address the server is actually listening on. - // - // This may be different to `bind_addr` if bind port was 0 (this allows the OS to choose a free - // port). - let actual_listen_addr = server.local_addr(); - - // Build a channel to kill the HTTP server. - let exit = executor.exit(); - let inner_log = log.clone(); - let server_exit = async move { - let _ = exit.await; - info!(inner_log, "HTTP service shutdown"); - }; - - // Configure the `hyper` server to gracefully shutdown when the shutdown channel is triggered. - let inner_log = log.clone(); - let server_future = server - .with_graceful_shutdown(async { - server_exit.await; - }) - .map_err(move |e| { - warn!( - inner_log, - "HTTP server failed to start, Unable to bind"; "address" => format!("{:?}", e) - ) - }) - .unwrap_or_else(|_| ()); - - info!( - log, - "HTTP API started"; - "address" => format!("{}", actual_listen_addr.ip()), - "port" => actual_listen_addr.port(), - ); - - executor.spawn_without_exit(server_future, "http"); - - Ok(actual_listen_addr) -} diff --git a/remote_signer/client/src/router.rs b/remote_signer/client/src/router.rs deleted file mode 100644 index 2586d00dfd5..00000000000 --- a/remote_signer/client/src/router.rs +++ /dev/null @@ -1,101 +0,0 @@ -use crate::api_error::ApiError; -use crate::backend::{get_keys, sign_message}; -use crate::handler::Handler; -use crate::rest_api::Context; -use crate::upcheck::upcheck; -use client_backend::Storage; -use hyper::{Body, Method, Request, Response}; -use slog::debug; -use std::sync::Arc; -use std::time::Instant; -use types::EthSpec; - -pub async fn on_http_request<E: EthSpec, S: Storage>( - req: Request<Body>, - ctx: Arc<Context<E, S>>, -) -> Result<Response<Body>, ApiError> { - let path = req.uri().path().to_string(); - let received_instant = Instant::now(); - let log = ctx.log.clone(); - - match route(req, ctx).await { - Ok(response) => { - debug!( - log, - "HTTP API request successful"; - "path" => path, - "duration_ms" => Instant::now().duration_since(received_instant).as_millis() - ); - Ok(response) - } - - Err(error) => { - debug!( - log, - "HTTP API request failure"; - "path" => path, - "duration_ms" => Instant::now().duration_since(received_instant).as_millis() - ); - Ok(error.into()) - } - } -} - -async fn route<E: EthSpec, S: Storage>( - req: Request<Body>, - ctx: Arc<Context<E, S>>, -) -> Result<Response<Body>, ApiError> { - let path = req.uri().path().to_string(); - let method = req.method().clone(); - let ctx = ctx.clone(); - let handler = Handler::new(req, ctx)?; - - match (method, path.as_ref()) { - (Method::GET, "/upcheck") => handler.static_value(upcheck()).await?.serde_encodings(), - - (Method::GET, "/keys") => handler.in_blocking_task(get_keys).await?.serde_encodings(), - - (Method::POST, _) => route_post(&path, handler).await, - - _ => Err(ApiError::NotFound( - "Request path and/or method not found.".to_string(), - )), - } -} - -/// Responds to all the POST requests. -/// -/// Should be deprecated once a better routing library is used, such as `warp` -async fn route_post<E: EthSpec, S: Storage>( - path: &str, - handler: Handler<E, S>, -) -> Result<Response<Body>, ApiError> { - let mut path_segments = path[1..].trim_end_matches('/').split('/'); - - match path_segments.next() { - Some("sign") => { - let path_segments_count = path_segments.clone().count(); - - if path_segments_count == 0 { - return Err(ApiError::BadRequest( - "Parameter public_key needed in route /sign/:public_key".to_string(), - )); - } - - if path_segments_count > 1 { - return Err(ApiError::BadRequest( - "Only one path segment is allowed after /sign".to_string(), - )); - } - - handler - .allow_body() - .in_blocking_task(sign_message) - .await? - .serde_encodings() - } - _ => Err(ApiError::NotFound( - "Request path and/or method not found.".to_string(), - )), - } -} diff --git a/remote_signer/client/src/signing_root.rs b/remote_signer/client/src/signing_root.rs deleted file mode 100644 index f92740bc80e..00000000000 --- a/remote_signer/client/src/signing_root.rs +++ /dev/null @@ -1,78 +0,0 @@ -use crate::api_error::ApiError; -use serde::Deserialize; -use serde_json::{from_value, Value}; - -use types::{ - AttestationData, BeaconBlock, ChainSpec, Domain, Epoch, EthSpec, Fork, Hash256, SignedRoot, -}; - -#[derive(Deserialize)] -pub struct SignMessageRequestBody { - /// BLS Signature domain. - /// Supporting `beacon_proposer`, `beacon_attester`, and `randao`. - /// As defined in - /// * https://github.com/ethereum/eth2.0-specs/blob/dev/specs/phase0/beacon-chain.md#domain-types - /// * in lowercase, omitting the `domain` prefix. - bls_domain: String, - - /// Supporting `block`, `attestation`, and `epoch`. - /// (In LH these are `BeaconBlock`, `AttestationData`, and `Epoch`). - /// As defined in - /// * https://github.com/ethereum/eth2.0-APIs/blob/master/types/block.yaml - /// * https://github.com/ethereum/eth2.0-APIs/blob/master/types/attestation.yaml - /// * https://github.com/ethereum/eth2.0-APIs/blob/master/types/misc.yaml - data: Value, - - /// A `Fork` object containing previous and current versions. - /// As defined in - /// * https://github.com/ethereum/eth2.0-APIs/blob/master/types/misc.yaml - fork: Fork, - - /// A `Hash256` for domain separation and chain versioning. - genesis_validators_root: Hash256, -} - -pub fn get_signing_root<E: EthSpec>( - req: &hyper::Request<std::vec::Vec<u8>>, - spec: ChainSpec, -) -> Result<Hash256, ApiError> { - let body: SignMessageRequestBody = serde_json::from_slice(req.body()).map_err(|e| { - ApiError::BadRequest(format!("Unable to parse body message from JSON: {:?}", e)) - })?; - - let get_domain = |epoch, bls_domain| { - spec.get_domain(epoch, bls_domain, &body.fork, body.genesis_validators_root) - }; - - match body.bls_domain.as_str() { - "beacon_proposer" => { - let block = from_value::<BeaconBlock<E>>(body.data.clone()).map_err(|e| { - ApiError::BadRequest(format!("Unable to parse block from JSON: {:?}", e)) - })?; - - Ok(block.signing_root(get_domain(block.epoch(), Domain::BeaconProposer))) - } - - "beacon_attester" => { - let attestation = from_value::<AttestationData>(body.data.clone()).map_err(|e| { - ApiError::BadRequest(format!("Unable to parse attestation from JSON: {:?}", e)) - })?; - - Ok(attestation - .signing_root(get_domain(attestation.target.epoch, Domain::BeaconAttester))) - } - - "randao" => { - let epoch = from_value::<Epoch>(body.data.clone()).map_err(|e| { - ApiError::BadRequest(format!("Unable to parse attestation from JSON: {:?}", e)) - })?; - - Ok(epoch.signing_root(get_domain(epoch, Domain::Randao))) - } - - s => Err(ApiError::BadRequest(format!( - "Unsupported bls_domain parameter: {}", - s - ))), - } -} diff --git a/remote_signer/client/src/upcheck.rs b/remote_signer/client/src/upcheck.rs deleted file mode 100644 index 145c0e3037e..00000000000 --- a/remote_signer/client/src/upcheck.rs +++ /dev/null @@ -1,7 +0,0 @@ -use crate::api_response::UpcheckApiResponse; - -pub fn upcheck() -> UpcheckApiResponse { - UpcheckApiResponse { - status: "OK".to_string(), - } -} diff --git a/remote_signer/src/cli.rs b/remote_signer/src/cli.rs deleted file mode 100644 index 091410473c1..00000000000 --- a/remote_signer/src/cli.rs +++ /dev/null @@ -1,37 +0,0 @@ -use clap::{App, Arg}; - -pub fn cli_app<'a, 'b>() -> App<'a, 'b> { - // Parse the CLI parameters. - App::new("remote_signer") - .visible_alias("rs") - .author("Sigma Prime <contact@sigmaprime.io>") - .setting(clap::AppSettings::ColoredHelp) - .about( - "Simple HTTP BLS signer service. \ - This service is designed to be consumed by Ethereum 2.0 clients, \ - looking for a more secure avenue to store their BLS12-381 secret keys, \ - while running their validators in more permisive and/or scalable environments.", - ) - .arg( - Arg::with_name("storage-raw-dir") - .long("storage-raw-dir") - .value_name("DIR") - .help("Data directory for secret keys in raw files."), - ) - .arg( - Arg::with_name("listen-address") - .long("listen-address") - .value_name("ADDRESS") - .help("The address to listen for TCP connections.") - .default_value("0.0.0.0") - .takes_value(true), - ) - .arg( - Arg::with_name("port") - .long("port") - .value_name("PORT") - .help("The TCP port to listen on.") - .default_value("9000") - .takes_value(true), - ) -} diff --git a/remote_signer/src/lib.rs b/remote_signer/src/lib.rs deleted file mode 100644 index d37ef6069d7..00000000000 --- a/remote_signer/src/lib.rs +++ /dev/null @@ -1,34 +0,0 @@ -mod cli; - -use clap::ArgMatches; -use client::Client; -use environment::Environment; -use slog::info; -use types::EthSpec; - -pub use cli::cli_app; - -pub fn run<E: EthSpec>( - environment: &mut Environment<E>, - matches: &ArgMatches, -) -> Result<(), String> { - let context = environment.core_context(); - let exit = context.executor.exit(); - - info!( - context.log(), - "Starting remote signer"; - ); - - let client = environment - .runtime() - .block_on(Client::new(context, matches)) - .map_err(|e| format!("Failed to init Rest API: {}", e))?; - - environment.runtime().spawn(async move { - exit.await; - drop(client); - }); - - Ok(()) -} diff --git a/remote_signer/tests/get_keys.rs b/remote_signer/tests/get_keys.rs deleted file mode 100644 index 887a16a80d3..00000000000 --- a/remote_signer/tests/get_keys.rs +++ /dev/null @@ -1,84 +0,0 @@ -mod get_keys { - use client::api_response::KeysApiResponse; - use helpers::*; - - fn assert_ok(resp: ApiTestResponse, expected_keys_len: usize) { - assert_eq!(resp.status, 200); - assert_eq!( - serde_json::from_value::<KeysApiResponse>(resp.json) - .unwrap() - .keys - .len(), - expected_keys_len - ); - } - - fn assert_error(resp: ApiTestResponse, http_status: u16, error_msg: &str) { - assert_eq!(resp.status, http_status); - assert_eq!(resp.json["error"], error_msg); - } - - #[test] - fn all_files_in_dir_are_public_keys() { - let (test_signer, tmp_dir) = set_up_api_test_signer_raw_dir(); - add_key_files(&tmp_dir); - - let url = format!("{}/keys", test_signer.address); - - let resp = http_get(&url); - assert_ok(resp, 3); - - test_signer.shutdown(); - } - - #[test] - fn some_files_in_dir_are_public_keys() { - let (test_signer, tmp_dir) = set_up_api_test_signer_raw_dir(); - add_sub_dirs(&tmp_dir); - add_key_files(&tmp_dir); - add_non_key_files(&tmp_dir); - - let url = format!("{}/keys", test_signer.address); - - let resp = http_get(&url); - assert_ok(resp, 3); - - test_signer.shutdown(); - } - - #[test] - fn no_files_in_dir_are_public_keys() { - let (test_signer, tmp_dir) = set_up_api_test_signer_raw_dir(); - add_sub_dirs(&tmp_dir); - add_non_key_files(&tmp_dir); - - let url = format!("{}/keys", test_signer.address); - - let resp = http_get(&url); - assert_error(resp, 404, "No keys found in storage."); - - test_signer.shutdown(); - } - - #[test] - fn directory_failure() { - let (test_signer, tmp_dir) = set_up_api_test_signer_raw_dir(); - add_sub_dirs(&tmp_dir); - add_key_files(&tmp_dir); - add_non_key_files(&tmp_dir); - - // Somebody tripped over a wire. - restrict_permissions(tmp_dir.path()); - - let url = format!("{}/keys", test_signer.address); - - let resp = http_get(&url); - - // Be able to delete the tempdir afterward, regardless of this test result. - unrestrict_permissions(tmp_dir.path()); - - assert_error(resp, 500, "Storage error: PermissionDenied"); - - test_signer.shutdown(); - } -} diff --git a/remote_signer/tests/sign.rs b/remote_signer/tests/sign.rs deleted file mode 100644 index 7b07c4c3af1..00000000000 --- a/remote_signer/tests/sign.rs +++ /dev/null @@ -1,402 +0,0 @@ -mod sign { - use helpers::*; - - #[test] - fn additional_field() { - let (test_signer, _tmp_dir) = set_up_api_test_signer_to_sign_message(); - let url = format!("{}/sign/{}", test_signer.address, PUBLIC_KEY_1); - - let test_block_body = get_test_block_body(0xc137).replace( - ",\"genesis_validators_root\":\"0x000000000000000000000000000000000000000000000000000000000000c137\"", - ",\"genesis_validators_root\":\"0x000000000000000000000000000000000000000000000000000000000000c137\", \"foo\":\"bar\"", - ); - let response = http_post_custom_body(&url, &test_block_body); - assert_sign_ok(response, HAPPY_PATH_BLOCK_SIGNATURE_C137); - - test_signer.shutdown(); - } - - #[test] - fn storage_error() { - let (test_signer, tmp_dir) = set_up_api_test_signer_to_sign_message(); - let test_block_body = get_test_block_body(0xc137); - restrict_permissions(tmp_dir.path()); - restrict_permissions(&tmp_dir.path().join(PUBLIC_KEY_1)); - - let url = format!("{}/sign/{}", test_signer.address, PUBLIC_KEY_1); - - let response = http_post_custom_body(&url, &test_block_body); - unrestrict_permissions(tmp_dir.path()); - unrestrict_permissions(&tmp_dir.path().join(PUBLIC_KEY_1)); - - assert_sign_error(response, 500, "Storage error: PermissionDenied"); - - test_signer.shutdown(); - } - - #[test] - fn no_public_key_in_path() { - let (test_signer, _tmp_dir) = set_up_api_test_signer_to_sign_message(); - let test_block_body = get_test_block_body(0xc137); - - let testcase = |url: String| { - let response = http_post_custom_body(&url, &test_block_body); - assert_sign_error( - response, - 400, - "Parameter public_key needed in route /sign/:public_key", - ); - }; - - testcase(format!("{}/sign/", test_signer.address)); - testcase(format!("{}/sign", test_signer.address)); - testcase(format!("{}/sign//", test_signer.address)); - testcase(format!("{}/sign///", test_signer.address)); - testcase(format!("{}/sign/?'or 1 = 1 --", test_signer.address)); - - test_signer.shutdown(); - } - - #[test] - fn additional_path_segments() { - let (test_signer, _tmp_dir) = set_up_api_test_signer_to_sign_message(); - let test_block_body = get_test_block_body(0xc137); - - let testcase = |url: String| { - let response = http_post_custom_body(&url, &test_block_body); - assert_sign_error( - response, - 400, - "Only one path segment is allowed after /sign", - ); - }; - - testcase(format!("{}/sign/this/receipt", test_signer.address)); - testcase(format!("{}/sign/this/receipt/please", test_signer.address)); - testcase(format!("{}/sign/this/receipt/please?", test_signer.address)); - testcase(format!( - "{}/sign//{}/valid/pk", - test_signer.address, PUBLIC_KEY_1 - )); - - test_signer.shutdown(); - } - - #[test] - fn invalid_public_key() { - let (test_signer, _tmp_dir) = set_up_api_test_signer_to_sign_message(); - let test_block_body = get_test_block_body(0xc137); - - let testcase = |url: String, expected_err: &str| { - let response = http_post_custom_body(&url, &test_block_body); - assert_sign_error(response, 400, expected_err); - }; - - testcase( - format!("{}/sign/{}", test_signer.address, "ScottBakula"), - "Invalid public key: ScottBakula", - ); - testcase( - format!("{}/sign/{}", test_signer.address, "deadbeef"), - "Invalid public key: deadbeef", - ); - testcase( - format!("{}/sign/{}", test_signer.address, SILLY_FILE_NAME_1), - &format!("Invalid public key: {}", SILLY_FILE_NAME_1), - ); - testcase( - format!("{}/sign/{}", test_signer.address, SILLY_FILE_NAME_1), - &format!("Invalid public key: {}", SILLY_FILE_NAME_1), - ); - testcase( - format!("{}/sign/0x{}", test_signer.address, PUBLIC_KEY_1), - &format!("Invalid public key: 0x{}", PUBLIC_KEY_1), - ); - testcase( - format!("{}/sign/{}55", test_signer.address, PUBLIC_KEY_1), - &format!("Invalid public key: {}55", PUBLIC_KEY_1), - ); - - test_signer.shutdown(); - } - - #[test] - fn key_not_found() { - let (test_signer, _tmp_dir) = set_up_api_test_signer_to_sign_message(); - let url = format!("{}/sign/{}", test_signer.address, ABSENT_PUBLIC_KEY); - let test_block_body = get_test_block_body(0xc137); - - let response = http_post_custom_body(&url, &test_block_body); - assert_sign_error( - response, - 404, - &format!("Key not found: {}", ABSENT_PUBLIC_KEY), - ); - - test_signer.shutdown(); - } - - #[test] - fn invalid_secret_key() { - let (test_signer, _tmp_dir) = set_up_api_test_signer_to_sign_message(); - let url = format!( - "{}/sign/{}", - test_signer.address, PUBLIC_KEY_FOR_INVALID_SECRET_KEY - ); - let test_block_body = get_test_block_body(0xc137); - - let response = http_post_custom_body(&url, &test_block_body); - assert_sign_error( - response, - 500, - &format!( - "Invalid secret key: public_key: {}; Invalid hex character: W at index 0", - PUBLIC_KEY_FOR_INVALID_SECRET_KEY - ), - ); - - test_signer.shutdown(); - } - - #[test] - fn key_mismatch() { - let (test_signer, _tmp_dir) = set_up_api_test_signer_to_sign_message(); - let url = format!("{}/sign/{}", test_signer.address, MISMATCHED_PUBLIC_KEY); - let test_block_body = get_test_block_body(0xc137); - - let response = http_post_custom_body(&url, &test_block_body); - assert_sign_error( - response, - 500, - &format!("Key mismatch: {}", MISMATCHED_PUBLIC_KEY), - ); - - test_signer.shutdown(); - } - - #[test] - fn invalid_json() { - let (test_signer, _tmp_dir) = set_up_api_test_signer_to_sign_message(); - let url = format!("{}/sign/{}", test_signer.address, PUBLIC_KEY_1); - - let testcase = |custom_body: &str, expected_err: &str| { - let response = http_post_custom_body(&url, custom_body); - assert_sign_error(response, 400, expected_err); - }; - - testcase( - "Trolololololo", - "Unable to parse body message from JSON: Error(\"expected value\", line: 1, column: 1)", - ); - testcase( - "{\"bls_domain\"}", - "Unable to parse body message from JSON: Error(\"expected `:`\", line: 1, column: 14)", - ); - testcase( - "{\"bls_domain\":}", - "Unable to parse body message from JSON: Error(\"expected value\", line: 1, column: 15)", - ); - - testcase( - "{\"bls_domain\":\"}", - "Unable to parse body message from JSON: Error(\"EOF while parsing a string\", line: 1, column: 16)", - ); - - test_signer.shutdown(); - } - - #[test] - fn invalid_field_bls_domain() { - let (test_signer, _tmp_dir) = set_up_api_test_signer_to_sign_message(); - let url = format!("{}/sign/{}", test_signer.address, PUBLIC_KEY_1); - - let testcase = |json_patch, expected_err| { - let test_block_body = get_test_block_body(0xc137).replace( - "\"bls_domain\":\"beacon_proposer\"", - &format!("\"bls_domain\":{}", json_patch), - ); - let response = http_post_custom_body(&url, &test_block_body); - assert_sign_error(response, 400, expected_err); - }; - - testcase("\"blah\"", "Unsupported bls_domain parameter: blah"); - testcase("\"domain\"", "Unsupported bls_domain parameter: domain"); - testcase("\"\"", "Unsupported bls_domain parameter: "); - testcase("", "Unable to parse body message from JSON: Error(\"expected value\", line: 1, column: 15)"); - testcase("1", "Unable to parse body message from JSON: Error(\"invalid type: integer `1`, expected a string\", line: 1, column: 15)"); - testcase("true", "Unable to parse body message from JSON: Error(\"invalid type: boolean `true`, expected a string\", line: 1, column: 18)"); - testcase("{\"cats\":\"3\"}", "Unable to parse body message from JSON: Error(\"invalid type: map, expected a string\", line: 1, column: 14)"); - testcase("[\"a\"]", "Unable to parse body message from JSON: Error(\"invalid type: sequence, expected a string\", line: 1, column: 14)"); - - test_signer.shutdown(); - } - - #[test] - fn missing_field_bls_domain() { - let (test_signer, _tmp_dir) = set_up_api_test_signer_to_sign_message(); - let url = format!("{}/sign/{}", test_signer.address, PUBLIC_KEY_1); - - let test_block_body = - get_test_block_body(0xc137).replace("\"bls_domain\":\"beacon_proposer\",", ""); - let response = http_post_custom_body(&url, &test_block_body); - assert_sign_error(response, 400, "Unable to parse body message from JSON: Error(\"missing field `bls_domain`\", line: 1, column: 237203)"); - - test_signer.shutdown(); - } - - #[test] - fn invalid_field_fork() { - let (test_signer, _tmp_dir) = set_up_api_test_signer_to_sign_message(); - let url = format!("{}/sign/{}", test_signer.address, PUBLIC_KEY_1); - - let testcase = |json_patch, expected_err| { - let test_block_body = get_test_block_body(0xc137).replace( - "\"fork\":{\"previous_version\":\"0x01010101\",\"current_version\":\"0x02020202\",\"epoch\":\"1545\"},", - json_patch, - ); - - let response = http_post_custom_body(&url, &test_block_body); - assert_sign_error(response, 400, expected_err); - }; - - testcase( - "\"fork\":{\"current_version\":\"0x02020202\",\"epoch\":\"1545\"},", - "Unable to parse body message from JSON: Error(\"missing field `previous_version`\", line: 1, column: 237106)", - ); - testcase( - "\"fork\":{\"previous_version\":\"0x01010101\",\"epoch\":\"1545\"},", - "Unable to parse body message from JSON: Error(\"missing field `current_version`\", line: 1, column: 237107)", - ); - testcase( - "\"fork\":{\"previous_version\":\"0x01010101\",\"current_version\":\"0x02020202\",", - "Unable to parse body message from JSON: Error(\"missing field `epoch`\", line: 1, column: 237218)", - ); - testcase( - "\"fork\":{\"previous_version\":\"INVALID_VALUE\",\"current_version\":\"0x02020202\",\"epoch\":\"1545\"},", - "Unable to parse body message from JSON: Error(\"missing 0x prefix\", line: 1, column: 237094)", - ); - testcase( - "\"fork\":{\"previous_version\":\"0xINVALID_VALUE\",\"current_version\":\"0x02020202\",\"epoch\":\"1545\"},", - "Unable to parse body message from JSON: Error(\"invalid hex (OddLength)\", line: 1, column: 237096)", - ); - testcase( - "\"fork\":{\"previous_version\":\"0xINVALID_VALUE_\",\"current_version\":\"0x02020202\",\"epoch\":\"1545\"},", - "Unable to parse body message from JSON: Error(\"invalid hex (InvalidHexCharacter { c: 'I', index: 0 })\", line: 1, column: 237097)", - ); - testcase( - "\"fork\":{\"previous_version\":\"0x01010101\",\"current_version\":\"INVALID_VALUE\",\"epoch\":\"1545\"},", - "Unable to parse body message from JSON: Error(\"missing 0x prefix\", line: 1, column: 237125)" - ); - testcase( - "\"fork\":{\"previous_version\":\"0x01010101\",\"current_version\":\"0xINVALID_VALUE\",\"epoch\":\"1545\"},", - "Unable to parse body message from JSON: Error(\"invalid hex (OddLength)\", line: 1, column: 237127)" - ); - testcase( - "\"fork\":{\"previous_version\":\"0x01010101\",\"current_version\":\"0xINVALID_VALUE_\",\"epoch\":\"1545\"},", - "Unable to parse body message from JSON: Error(\"invalid hex (InvalidHexCharacter { c: 'I', index: 0 })\", line: 1, column: 237128)" - ); - testcase( - "\"fork\":{\"previous_version\":\"0x01010101\",\"current_version\":\"0x02020202\",\"epoch\":},", - "Unable to parse body message from JSON: Error(\"expected value\", line: 1, column: 237132)" - ); - testcase( - "\"fork\":{\"previous_version\":\"0x01010101\",\"current_version\":\"0x02020202\",\"epoch\":\"zzz\"},", - "Unable to parse body message from JSON: Error(\"invalid digit found in string\", line: 1, column: 237136)" - ); - testcase( - "\"fork\":{\"previous_version\":\"0x01010101\",\"current_version\":\"0x02020202\",\"epoch\":true},", - "Unable to parse body message from JSON: Error(\"invalid type: boolean `true`, expected a quoted or unquoted integer\", line: 1, column: 237135)" - ); - testcase( - "\"fork\":{\"previous_version\":\"0x01010101\",\"current_version\":\"0x02020202\",\"epoch\":[\"a\"]},", - "Unable to parse body message from JSON: Error(\"invalid type: sequence, expected a quoted or unquoted integer\", line: 1, column: 237132)" - ); - - test_signer.shutdown(); - } - - #[test] - fn missing_field_fork() { - let (test_signer, _tmp_dir) = set_up_api_test_signer_to_sign_message(); - let url = format!("{}/sign/{}", test_signer.address, PUBLIC_KEY_1); - - let test_block_body = get_test_block_body(0xc137).replace( - "\"fork\":{\"previous_version\":\"0x01010101\",\"current_version\":\"0x02020202\",\"epoch\":\"1545\"},", - "", - ); - let response = http_post_custom_body(&url, &test_block_body); - assert_sign_error(response, 400, "Unable to parse body message from JSON: Error(\"missing field `fork`\", line: 1, column: 237147)"); - - test_signer.shutdown(); - } - - #[test] - fn missing_field_data() { - let (test_signer, _tmp_dir) = set_up_api_test_signer_to_sign_message(); - let url = format!("{}/sign/{}", test_signer.address, PUBLIC_KEY_1); - - let test_block_body = get_test_block_body(0xc137).replace("\"data\":", "\"not-data\":"); - - let response = http_post_custom_body(&url, &test_block_body); - assert_sign_error(response, 400, "Unable to parse body message from JSON: Error(\"missing field `data`\", line: 1, column: 237830)"); - - test_signer.shutdown(); - } - - #[test] - fn invalid_field_genesis_validators_root() { - let (test_signer, _tmp_dir) = set_up_api_test_signer_to_sign_message(); - let url = format!("{}/sign/{}", test_signer.address, PUBLIC_KEY_1); - - let testcase = |json_patch, expected_err| { - let test_block_body = get_test_block_body(0xc137).replace( - ",\"genesis_validators_root\":\"0x000000000000000000000000000000000000000000000000000000000000c137\"", - &format!(",\"genesis_validators_root\":{}", json_patch), - ); - - let response = http_post_custom_body(&url, &test_block_body); - assert_sign_error(response, 400, expected_err); - }; - - testcase("\"0\"", "Unable to parse body message from JSON: Error(\"0x prefix is missing\", line: 1, column: 237168)"); - testcase("\"0x\"", "Unable to parse body message from JSON: Error(\"invalid length 0, expected a 0x-prefixed hex string with length of 64\", line: 1, column: 237169)"); - testcase("\"0xa\"", "Unable to parse body message from JSON: Error(\"invalid length 1, expected a 0x-prefixed hex string with length of 64\", line: 1, column: 237170)"); - testcase("\"deadbeef\"", "Unable to parse body message from JSON: Error(\"0x prefix is missing\", line: 1, column: 237175)"); - testcase("\"0xdeadbeefzz\"", "Unable to parse body message from JSON: Error(\"invalid length 10, expected a 0x-prefixed hex string with length of 64\", line: 1, column: 237179)"); - testcase("\"0xdeadbeef1\"", "Unable to parse body message from JSON: Error(\"invalid length 9, expected a 0x-prefixed hex string with length of 64\", line: 1, column: 237178)"); - testcase("", "Unable to parse body message from JSON: Error(\"expected value\", line: 1, column: 237166)"); - testcase("1", "Unable to parse body message from JSON: Error(\"invalid type: integer `1`, expected a 0x-prefixed hex string with length of 64\", line: 1, column: 237166)"); - testcase("true", "Unable to parse body message from JSON: Error(\"invalid type: boolean `true`, expected a 0x-prefixed hex string with length of 64\", line: 1, column: 237169)"); - testcase("{\"cats\":\"3\"}", "Unable to parse body message from JSON: Error(\"invalid type: map, expected a 0x-prefixed hex string with length of 64\", line: 1, column: 237165)"); - testcase("[\"a\"]", "Unable to parse body message from JSON: Error(\"invalid type: sequence, expected a 0x-prefixed hex string with length of 64\", line: 1, column: 237165)"); - testcase( - "\"0x000000000000000000000000000000000000000000000000000000000000c1370\"", - "Unable to parse body message from JSON: Error(\"invalid length 65, expected a 0x-prefixed hex string with length of 64\", line: 1, column: 237234)", - ); - testcase( - "\"0x000000000000000000000000000000000000000000000000000000000000c13700\"", - "Unable to parse body message from JSON: Error(\"invalid length 66, expected a 0x-prefixed hex string with length of 64\", line: 1, column: 237235)", - ); - testcase( - "\"0x000000000000000000000000000000000000000000000000000000000000c1370000\"", - "Unable to parse body message from JSON: Error(\"invalid length 68, expected a 0x-prefixed hex string with length of 64\", line: 1, column: 237237)", - ); - - test_signer.shutdown(); - } - - #[test] - fn missing_field_genesis_validators_root() { - let (test_signer, _tmp_dir) = set_up_api_test_signer_to_sign_message(); - let url = format!("{}/sign/{}", test_signer.address, PUBLIC_KEY_1); - - let test_block_body = get_test_block_body(0xc137).replace( - ",\"genesis_validators_root\":\"0x000000000000000000000000000000000000000000000000000000000000c137\"", - "", - ); - let response = http_post_custom_body(&url, &test_block_body); - assert_sign_error(response, 400, "Unable to parse body message from JSON: Error(\"missing field `genesis_validators_root`\", line: 1, column: 237139)"); - - test_signer.shutdown(); - } -} diff --git a/remote_signer/tests/sign_attestation.rs b/remote_signer/tests/sign_attestation.rs deleted file mode 100644 index bebefa44276..00000000000 --- a/remote_signer/tests/sign_attestation.rs +++ /dev/null @@ -1,89 +0,0 @@ -mod sign_attestation { - use helpers::*; - - #[test] - fn happy_path() { - let (test_signer, _tmp_dir) = set_up_api_test_signer_to_sign_message(); - let url = format!("{}/sign/{}", test_signer.address, PUBLIC_KEY_1); - let test_attestation_body = get_test_attestation_body(0xc137); - - let response = http_post_custom_body(&url, &test_attestation_body); - - assert_sign_ok(response, HAPPY_PATH_ATT_SIGNATURE_C137); - - test_signer.shutdown(); - } - - #[test] - fn domain_mismatch() { - let (test_signer, _tmp_dir) = set_up_api_test_signer_to_sign_message(); - let url = format!("{}/sign/{}", test_signer.address, PUBLIC_KEY_1); - - let testcase = |json_patch, expected_err| { - let test_attestation_body = get_test_attestation_body(0xc137).replace( - "\"bls_domain\":\"beacon_attester\"", - &format!("\"bls_domain\":{}", json_patch), - ); - let response = http_post_custom_body(&url, &test_attestation_body); - assert_sign_error(response, 400, expected_err); - }; - - testcase( - "\"beacon_proposer\"", - "Unable to parse block from JSON: Error(\"data did not match any variant of untagged enum BeaconBlock\", line: 0, column: 0)" - ); - testcase( - "\"randao\"", - "Unable to parse attestation from JSON: Error(\"invalid type: map, expected a quoted or unquoted integer\", line: 0, column: 0)" - ); - testcase("\"blah\"", "Unsupported bls_domain parameter: blah"); - - test_signer.shutdown(); - } - - #[test] - fn missing_or_invalid_fields_within_attestation_data() { - let (test_signer, _tmp_dir) = set_up_api_test_signer_to_sign_message(); - let url = format!("{}/sign/{}", test_signer.address, PUBLIC_KEY_1); - - let testcase = |json_patch, expected_err| { - let test_attestation_body = get_test_attestation_body(0xc137).replace( - "\"data\":{\"slot\":\"49463\",\"index\":\"49463\"", - json_patch, - ); - let response = http_post_custom_body(&url, &test_attestation_body); - assert_sign_error(response, 400, expected_err); - }; - - testcase( - "\"data\":{\"slot\":\"a\",\"index\":49463", - "Unable to parse attestation from JSON: Error(\"invalid digit found in string\", line: 0, column: 0)" - ); - testcase( - "\"data\":{\"slot\":\"\",\"index\":\"49463\"", - "Unable to parse attestation from JSON: Error(\"cannot parse integer from empty string\", line: 0, column: 0)" - ); - testcase( - "\"data\":{\"slot\":-1,\"index\":\"49463\"", - "Unable to parse attestation from JSON: Error(\"invalid type: integer `-1`, expected a quoted or unquoted integer\", line: 0, column: 0)" - ); - testcase( - "\"data\":{\"slot\":\"-1\",\"index\":\"49463\"", - "Unable to parse attestation from JSON: Error(\"invalid digit found in string\", line: 0, column: 0)" - ); - testcase( - "\"data\":{\"index\":\"49463\"", - "Unable to parse attestation from JSON: Error(\"missing field `slot`\", line: 0, column: 0)", - ); - testcase( - "\"data\":{\"slot\":\"49463\"", - "Unable to parse attestation from JSON: Error(\"missing field `index`\", line: 0, column: 0)" - ); - testcase( - "\"data\":{\"slot\":\"49463\",\"index\":\"\"", - "Unable to parse attestation from JSON: Error(\"cannot parse integer from empty string\", line: 0, column: 0)", - ); - - test_signer.shutdown(); - } -} diff --git a/remote_signer/tests/sign_block.rs b/remote_signer/tests/sign_block.rs deleted file mode 100644 index 3ae1327211f..00000000000 --- a/remote_signer/tests/sign_block.rs +++ /dev/null @@ -1,81 +0,0 @@ -mod sign_block { - use helpers::*; - - #[test] - fn happy_path() { - let (test_signer, _tmp_dir) = set_up_api_test_signer_to_sign_message(); - let url = format!("{}/sign/{}", test_signer.address, PUBLIC_KEY_1); - let test_block_body = get_test_block_body(0xc137); - - let response = http_post_custom_body(&url, &test_block_body); - - assert_sign_ok(response, HAPPY_PATH_BLOCK_SIGNATURE_C137); - - test_signer.shutdown(); - } - - #[test] - fn domain_mismatch() { - let (test_signer, _tmp_dir) = set_up_api_test_signer_to_sign_message(); - let url = format!("{}/sign/{}", test_signer.address, PUBLIC_KEY_1); - - let testcase = |json_patch, expected_err| { - let test_block_body = get_test_block_body(0xc137).replace( - "\"bls_domain\":\"beacon_proposer\"", - &format!("\"bls_domain\":{}", json_patch), - ); - let response = http_post_custom_body(&url, &test_block_body); - assert_sign_error(response, 400, expected_err); - }; - - testcase( - "\"beacon_attester\"", - "Unable to parse attestation from JSON: Error(\"missing field `index`\", line: 0, column: 0)", - ); - testcase( - "\"randao\"", - "Unable to parse attestation from JSON: Error(\"invalid type: map, expected a quoted or unquoted integer\", line: 0, column: 0)" - ); - testcase("\"blah\"", "Unsupported bls_domain parameter: blah"); - - test_signer.shutdown(); - } - - #[test] - fn missing_or_invalid_fields_within_block_data() { - let (test_signer, _tmp_dir) = set_up_api_test_signer_to_sign_message(); - let url = format!("{}/sign/{}", test_signer.address, PUBLIC_KEY_1); - - let testcase = |json_patch, expected_err| { - let test_block_body = get_test_block_body(0xc137).replace( - "\"data\":{\"slot\":\"49463\",\"proposer_index\":\"0\"", - json_patch, - ); - let response = http_post_custom_body(&url, &test_block_body); - assert_sign_error(response, 400, expected_err); - }; - - testcase( - "\"data\":{\"slot\":\"\",\"proposer_index\":\"0\"", - "Unable to parse block from JSON: Error(\"data did not match any variant of untagged enum BeaconBlock\", line: 0, column: 0)" - ); - testcase( - "\"data\":{\"slot\":\"-1\",\"proposer_index\":\"0\"", - "Unable to parse block from JSON: Error(\"data did not match any variant of untagged enum BeaconBlock\", line: 0, column: 0)" - ); - testcase( - "\"data\":{\"proposer_index\":\"0\"", - "Unable to parse block from JSON: Error(\"data did not match any variant of untagged enum BeaconBlock\", line: 0, column: 0)" - ); - testcase( - "\"data\":{\"slot\":\"49463\"", - "Unable to parse block from JSON: Error(\"data did not match any variant of untagged enum BeaconBlock\", line: 0, column: 0)" - ); - testcase( - "\"data\":{\"slot\":\"49463\",\"proposer_index\":\"\"", - "Unable to parse block from JSON: Error(\"data did not match any variant of untagged enum BeaconBlock\", line: 0, column: 0)" - ); - - test_signer.shutdown(); - } -} diff --git a/remote_signer/tests/sign_randao.rs b/remote_signer/tests/sign_randao.rs deleted file mode 100644 index 030c5b9ca49..00000000000 --- a/remote_signer/tests/sign_randao.rs +++ /dev/null @@ -1,83 +0,0 @@ -mod sign_randao { - - use helpers::*; - - #[test] - fn happy_path() { - let (test_signer, _tmp_dir) = set_up_api_test_signer_to_sign_message(); - let url = format!("{}/sign/{}", test_signer.address, PUBLIC_KEY_1); - let test_randao_body = get_test_randao_body(0xc137); - - let response = http_post_custom_body(&url, &test_randao_body); - assert_sign_ok(response, HAPPY_PATH_RANDAO_SIGNATURE_C137); - - test_signer.shutdown(); - } - - #[test] - fn domain_mismatch() { - let (test_signer, _tmp_dir) = set_up_api_test_signer_to_sign_message(); - let url = format!("{}/sign/{}", test_signer.address, PUBLIC_KEY_1); - - let testcase = |json_patch, expected_err| { - let test_randao_body = get_test_randao_body(0xc137).replace( - "\"bls_domain\":\"randao\"", - &format!("\"bls_domain\":{}", json_patch), - ); - let response = http_post_custom_body(&url, &test_randao_body); - assert_sign_error(response, 400, expected_err); - }; - - testcase( - "\"beacon_proposer\"", - "Unable to parse block from JSON: Error(\"data did not match any variant of untagged enum BeaconBlock\", line: 0, column: 0)", - ); - testcase( - "\"beacon_attester\"", - "Unable to parse attestation from JSON: Error(\"invalid type: string \"49463\", expected struct AttestationData\", line: 0, column: 0)" - ); - testcase("\"blah\"", "Unsupported bls_domain parameter: blah"); - - test_signer.shutdown(); - } - - #[test] - fn invalid_field_data() { - let (test_signer, _tmp_dir) = set_up_api_test_signer_to_sign_message(); - let url = format!("{}/sign/{}", test_signer.address, PUBLIC_KEY_1); - - let testcase = |json_patch, expected_err| { - let test_randao_body = get_test_randao_body(0xc137) - .replace(",\"data\":\"49463\"", &format!(",\"data\":{}", json_patch)); - let response = http_post_custom_body(&url, &test_randao_body); - assert_sign_error(response, 400, expected_err); - }; - - testcase( - "", - "Unable to parse body message from JSON: Error(\"expected value\", line: 1, column: 31)" - ); - testcase( - "\"\"", - "Unable to parse attestation from JSON: Error(\"cannot parse integer from empty string\", line: 0, column: 0)" - ); - testcase( - "\"-1\"", - "Unable to parse attestation from JSON: Error(\"invalid digit found in string\", line: 0, column: 0)" - ); - testcase( - "true", - "Unable to parse attestation from JSON: Error(\"invalid type: boolean `true`, expected a quoted or unquoted integer\", line: 0, column: 0)" - ); - testcase( - "{\"cats\":\"3\"}", - "Unable to parse attestation from JSON: Error(\"invalid type: map, expected a quoted or unquoted integer\", line: 0, column: 0)", - ); - testcase( - "[\"a\"]", - "Unable to parse attestation from JSON: Error(\"invalid type: sequence, expected a quoted or unquoted integer\", line: 0, column: 0)" - ); - - test_signer.shutdown(); - } -} diff --git a/remote_signer/tests/upcheck.rs b/remote_signer/tests/upcheck.rs deleted file mode 100644 index 4c69d21085d..00000000000 --- a/remote_signer/tests/upcheck.rs +++ /dev/null @@ -1,16 +0,0 @@ -mod upcheck { - use helpers::*; - - #[test] - fn happy_path() { - let (test_signer, _tmp_dir) = set_up_api_test_signer_raw_dir(); - - let url = format!("{}/upcheck", test_signer.address); - - let resp = http_get(&url); - assert_eq!(resp.status, 200); - assert_eq!(resp.json["status"], "OK"); - - test_signer.shutdown(); - } -} diff --git a/testing/remote_signer_test/Cargo.toml b/testing/remote_signer_test/Cargo.toml deleted file mode 100644 index c63c6eb847b..00000000000 --- a/testing/remote_signer_test/Cargo.toml +++ /dev/null @@ -1,25 +0,0 @@ -[package] -name = "remote_signer_test" -version = "0.2.0" -authors = ["Herman Junge <herman@sigmaprime.io>"] -edition = "2018" - -[dependencies] -clap = "2.33.3" -environment = { path = "../../lighthouse/environment" } -hex = "0.4.2" -httpmock = "0.5.8" -remote_signer_client = { path = "../../remote_signer/client" } -remote_signer_consumer = { path = "../../common/remote_signer_consumer" } -reqwest = { version = "0.11.0", features = ["blocking", "json"] } -serde = { version = "1.0.116", features = ["derive"] } -serde_json = "1.0.58" -tempfile = "3.1.0" -tokio = { version = "1.10.0", features = ["time"] } -types = { path = "../../consensus/types" } -sensitive_url = { path = "../../common/sensitive_url" } - -[target.'cfg(windows)'.dependencies] -winapi = "~0.3.5" -windows-acl = "~0.3.0" - diff --git a/testing/remote_signer_test/src/api_test_signer.rs b/testing/remote_signer_test/src/api_test_signer.rs deleted file mode 100644 index 8c9b6dc44b0..00000000000 --- a/testing/remote_signer_test/src/api_test_signer.rs +++ /dev/null @@ -1,141 +0,0 @@ -use crate::*; -use clap::{App, Arg, ArgMatches}; -use environment::{Environment, EnvironmentBuilder}; -pub use local_signer_test_data::*; -use remote_signer_client::Client; -use serde_json::Value; -use std::collections::HashMap; -use tempfile::{Builder as TempBuilder, TempDir}; -use types::EthSpec; - -pub struct ApiTestSigner<E: EthSpec> { - pub address: String, - environment: Environment<E>, -} - -pub struct ApiTestResponse { - pub status: u16, - pub json: Value, -} - -impl ApiTestSigner<E> { - pub fn new(arg_vec: Vec<&str>) -> Self { - let matches = set_matches(arg_vec); - let mut environment = get_environment(false); - let runtime_context = environment.core_context(); - - let client = environment - .runtime() - .block_on(Client::new(runtime_context, &matches)) - .map_err(|e| format!("Failed to init Rest API: {}", e)) - .unwrap(); - - let address = get_address(&client); - - Self { - address, - environment, - } - } - - pub fn shutdown(mut self) { - self.environment.fire_signal() - } -} - -pub fn set_matches(arg_vec: Vec<&str>) -> ArgMatches<'static> { - let matches = App::new("BLS_Remote_Signer") - .arg( - Arg::with_name("storage-raw-dir") - .long("storage-raw-dir") - .value_name("DIR"), - ) - .arg( - Arg::with_name("port") - .long("port") - .value_name("PORT") - .default_value("9000") - .takes_value(true), - ); - - matches.get_matches_from(arg_vec) -} - -pub fn get_environment(is_log_active: bool) -> Environment<E> { - let environment_builder = EnvironmentBuilder::mainnet(); - - let builder = if is_log_active { - environment_builder.async_logger("info", None).unwrap() - } else { - environment_builder.null_logger().unwrap() - }; - - builder - .multi_threaded_tokio_runtime() - .unwrap() - .build() - .unwrap() -} - -pub fn set_up_api_test_signer_raw_dir() -> (ApiTestSigner<E>, TempDir) { - let tmp_dir = TempBuilder::new() - .prefix("bls-remote-signer-test") - .tempdir() - .unwrap(); - let arg_vec = vec![ - "this_test", - "--port", - "0", - "--storage-raw-dir", - tmp_dir.path().to_str().unwrap(), - ]; - let test_signer = ApiTestSigner::new(arg_vec); - - (test_signer, tmp_dir) -} - -pub fn set_up_api_test_signer_to_sign_message() -> (ApiTestSigner<E>, TempDir) { - let (test_signer, tmp_dir) = set_up_api_test_signer_raw_dir(); - add_sub_dirs(&tmp_dir); - add_key_files(&tmp_dir); - add_non_key_files(&tmp_dir); - add_mismatched_key_file(&tmp_dir); - add_invalid_secret_key_file(&tmp_dir); - - (test_signer, tmp_dir) -} - -pub fn http_get(url: &str) -> ApiTestResponse { - let response = reqwest::blocking::get(url).unwrap(); - - ApiTestResponse { - status: response.status().as_u16(), - json: serde_json::from_str(&response.text().unwrap()).unwrap(), - } -} - -pub fn http_post(url: &str, hashmap: HashMap<&str, &str>) -> ApiTestResponse { - let response = reqwest::blocking::Client::new() - .post(url) - .json(&hashmap) - .send() - .unwrap(); - - ApiTestResponse { - status: response.status().as_u16(), - json: serde_json::from_str(&response.text().unwrap()).unwrap(), - } -} - -pub fn http_post_custom_body(url: &str, body: &str) -> ApiTestResponse { - let response = reqwest::blocking::Client::new() - .post(url) - .body(body.to_string()) - .send() - .unwrap(); - - ApiTestResponse { - status: response.status().as_u16(), - json: serde_json::from_str(&response.text().unwrap()).unwrap(), - } -} diff --git a/testing/remote_signer_test/src/constants.rs b/testing/remote_signer_test/src/constants.rs deleted file mode 100644 index 3d2480c6307..00000000000 --- a/testing/remote_signer_test/src/constants.rs +++ /dev/null @@ -1,98 +0,0 @@ -// Legit BLS pairs. -pub const PUBLIC_KEY_1: &str = "b7354252aa5bce27ab9537fd0158515935f3c3861419e1b4b6c8219b5dbd15fcf907bddf275442f3e32f904f79807a2a"; -pub const SECRET_KEY_1: &str = "68081afeb7ad3e8d469f87010804c3e8d53ef77d393059a55132637206cc59ec"; - -pub const PUBLIC_KEY_1_BYTES: [u8; 48] = [ - 183, 53, 66, 82, 170, 91, 206, 39, 171, 149, 55, 253, 1, 88, 81, 89, 53, 243, 195, 134, 20, 25, - 225, 180, 182, 200, 33, 155, 93, 189, 21, 252, 249, 7, 189, 223, 39, 84, 66, 243, 227, 47, 144, - 79, 121, 128, 122, 42, -]; - -pub const SECRET_KEY_1_BYTES: [u8; 32] = [ - 104, 8, 26, 254, 183, 173, 62, 141, 70, 159, 135, 1, 8, 4, 195, 232, 213, 62, 247, 125, 57, 48, - 89, 165, 81, 50, 99, 114, 6, 204, 89, 236, -]; - -pub const PUBLIC_KEY_2: &str = "9324739760579527b4f8c34c5df42f9fd89f59fdbe8a01d58675769f60fec5da9b9c8d7a3203cf2217692e49e7b98d97"; -pub const SECRET_KEY_2: &str = "45b5e876e5e57b23af3e86c37d708626cf1dcca6a650091bba2ddb3e0b7304ae"; - -pub const PUBLIC_KEY_3: &str = "8244ac66a8bffa0ce0af04d69ed7ed009951061259173a7c7ae1f25c049f0fcbbf2fad67b6d2b276a697315be755dac5"; -pub const SECRET_KEY_3: &str = "1e52a4e54e89ccba813d5f902545749c356f6187341b4e765bf43ece401762f6"; - -// It is valid (from 0731e07e99a0b1c69f0de13ad65e5c374e72d0a997d43387ad70448485879ca1), -// But we are not uploading it. -pub const ABSENT_PUBLIC_KEY: &str = "827803e94e4b8d306735df9002465b310fabb39802341dc5c616a204e4e8dc7dbb6caa4733b5da54f8cdeec7788e7500"; - -// This is the public key of 0e5faaa97a63929cecb8597949ae148c0607f1b30bd057a7487efeb4c701fbf8. -pub const MISMATCHED_PUBLIC_KEY: &str = "83d40dfb1cbcf2a55c139faa3feec14bdae92dd485009ac8c5670d241f71c2ce064afa48dbaf091e16d0e4356038b948"; - -// The valid secret key is 3d703bd0dfdf2abb925b2d6bf1adf045ce8d93b8baff07e3313c5e150b043e89 -pub const PUBLIC_KEY_FOR_INVALID_SECRET_KEY: &str = "aac313c0bc04880c4e9f4b0b69a9f310b09b9325027666cc7f255f88c7f35b82a82b2aa004c9be655b5696fea67f7300"; -pub const INVALID_SECRET_KEY: &str = "WubbaLubbaDubDub"; - -// This is the public key of 34e62afe7c4402009a46bf8af574f9d6701c2cf72b3868eeeb59dfa6e7ff6bcf. -pub const SUB_DIR_NAME: &str = "aadbe2d5c0316dd3c9a522029f332cde578730e61d759685d7ad3bf1166c5f0bf094c3abc105384506f052e2b7a1bae0"; - -// Silly files with long names (96 chars) to fill your BLS raw file directory. -pub const SILLY_FILE_NAME_1: &str = - "IAmAdamPrinceofEterniaDefenderofthesecretsoftheCastleGrayskullThisisCringermyfearlessfriendFabul"; -pub const SILLY_CONTENT_1: &str = "HemanandtheMastersoftheUniverse"; - -pub const SILLY_FILE_NAME_2: &str = - "InthenearfutureDocTerrorandhiscyborgcompanionHackerunleashtheirforcestoconquerEarthOnlyoneforcec"; -pub const SILLY_CONTENT_2: &str = "Centurions"; - -pub const SILLY_FILE_NAME_3: &str = - "OurworldisinperilGaiathespiritoftheearthcannolongerstandtheterribledestructionplaguingourplanetS"; -pub const SILLY_CONTENT_3: &str = "CaptainPlanet"; - -// Taken from some random string. -pub const SIGNING_ROOT: &str = "b6bb8f3765f93f4f1e7c7348479289c9261399a3c6906685e320071a1a13955c"; - -pub const SIGNING_ROOT_BYTES: [u8; 32] = [ - 182, 187, 143, 55, 101, 249, 63, 79, 30, 124, 115, 72, 71, 146, 137, 201, 38, 19, 153, 163, - 198, 144, 102, 133, 227, 32, 7, 26, 26, 19, 149, 92, -]; - -// Expected signature for the message 0xb6bb8f3765f93f4f1e7c7348479289c9261399a3c6906685e320071a1a13955c -// using 68081afeb7ad3e8d469f87010804c3e8d53ef77d393059a55132637206cc59ec as secret key -pub const EXPECTED_SIGNATURE_1: &str = "0xb5d0c01cef3b028e2c5f357c2d4b886f8e374d09dd660cd7dd14680d4f956778808b4d3b2ab743e890fc1a77ae62c3c90d613561b23c6adaeb5b0e288832304fddc08c7415080be73e556e8862a1b4d0f6aa8084e34a901544d5bb6aeed3a612"; - -pub const EXPECTED_SIGNATURE_1_BYTES: [u8; 96] = [ - 181, 208, 192, 28, 239, 59, 2, 142, 44, 95, 53, 124, 45, 75, 136, 111, 142, 55, 77, 9, 221, - 102, 12, 215, 221, 20, 104, 13, 79, 149, 103, 120, 128, 139, 77, 59, 42, 183, 67, 232, 144, - 252, 26, 119, 174, 98, 195, 201, 13, 97, 53, 97, 178, 60, 106, 218, 235, 91, 14, 40, 136, 50, - 48, 79, 221, 192, 140, 116, 21, 8, 11, 231, 62, 85, 110, 136, 98, 161, 180, 208, 246, 170, 128, - 132, 227, 74, 144, 21, 68, 213, 187, 106, 238, 211, 166, 18, -]; - -// Expected signature for the message 0xb6bb8f3765f93f4f1e7c7348479289c9261399a3c6906685e320071a1a13955c -// using 45b5e876e5e57b23af3e86c37d708626cf1dcca6a650091bba2ddb3e0b7304ae as secret key -pub const EXPECTED_SIGNATURE_2: &str = "0xb6b63e3cecd0967d9f9b90e3ee113dfb21ecd3901dbc654ca69649ac5a0746758661306627f18bb6d7a6ea03ace069500ee79a28154c172dd71ffe4b711875e48b60466a90f3a4dcacdbc9b5f5434ad68c91e603fe1703324d83617f5270aead"; - -pub const EXPECTED_SIGNATURE_2_BYTES: [u8; 96] = [ - 182, 182, 62, 60, 236, 208, 150, 125, 159, 155, 144, 227, 238, 17, 61, 251, 33, 236, 211, 144, - 29, 188, 101, 76, 166, 150, 73, 172, 90, 7, 70, 117, 134, 97, 48, 102, 39, 241, 139, 182, 215, - 166, 234, 3, 172, 224, 105, 80, 14, 231, 154, 40, 21, 76, 23, 45, 215, 31, 254, 75, 113, 24, - 117, 228, 139, 96, 70, 106, 144, 243, 164, 220, 172, 219, 201, 181, 245, 67, 74, 214, 140, 145, - 230, 3, 254, 23, 3, 50, 77, 131, 97, 127, 82, 112, 174, 173, -]; - -// Expected signature for the message 0xb6bb8f3765f93f4f1e7c7348479289c9261399a3c6906685e320071a1a13955c -// using 1e52a4e54e89ccba813d5f902545749c356f6187341b4e765bf43ece401762f6 as secret key -pub const EXPECTED_SIGNATURE_3: &str = "0x874f7d6d4174df1088ab40bd9a3c808554c55d6de1dffcacc7ef56c3ca22e20b52a23dd5bb6568a123b59df0bacef3de14d4c197a2fb2a5868a18c4b11f6d7957673d9a302bf6812b1d5df9b264504f682b43dfbcf4f9130cb5ebb9b8e3737de"; - -pub const EXPECTED_SIGNATURE_3_BYTES: [u8; 96] = [ - 135, 79, 125, 109, 65, 116, 223, 16, 136, 171, 64, 189, 154, 60, 128, 133, 84, 197, 93, 109, - 225, 223, 252, 172, 199, 239, 86, 195, 202, 34, 226, 11, 82, 162, 61, 213, 187, 101, 104, 161, - 35, 181, 157, 240, 186, 206, 243, 222, 20, 212, 193, 151, 162, 251, 42, 88, 104, 161, 140, 75, - 17, 246, 215, 149, 118, 115, 217, 163, 2, 191, 104, 18, 177, 213, 223, 155, 38, 69, 4, 246, - 130, 180, 61, 251, 207, 79, 145, 48, 203, 94, 187, 155, 142, 55, 55, 222, -]; - -// These HAPPY_PATH constants were obtained running "sanity check" tests. i.e. Usign the non-remote way for producing the signature. -pub const HAPPY_PATH_BLOCK_SIGNATURE_C137: &str = "0x87c2a5bbd71d6277802e8bf5319b84c9fbb7441c4ce56dc39e721ba4371e7521a04a36c166c5cba37e8c645e91cc31fc02884bf44fdeb51c52173e642934fd3f3e8f72f53c2ec7da284630dc86e49da75cb2578761403ed24c3a8e4bccf33e4c"; - -pub const HAPPY_PATH_ATT_SIGNATURE_C137: &str = "0xada13507d81feb5a5057565f45abed9248be56a463efa944598090bdcdd61c3fa51bb5ef34f845100efe0c14dc0c0fa20d068a7ea4f14c3e9b43aa1c44f14cb73371e48338a90622b0bee4c3a988b726d3ad87ea8a111115a0d6e1e908c421d8"; - -pub const HAPPY_PATH_RANDAO_SIGNATURE_C137: &str = "0x8c5c88491486d0c4572e4043fd42b3f774778c6f9c44464b61272c9667d18f3ca894ae08344fcd7f6dd8b6954572b90a10ce7457367cecaa9f6ef7bf105aa2e79ae7e9568317d933ac2a8e45fb06f3edfc3f6f5881ca96c8eed0c2a83fa9bc2d"; diff --git a/testing/remote_signer_test/src/consumer.rs b/testing/remote_signer_test/src/consumer.rs deleted file mode 100644 index 3465232702a..00000000000 --- a/testing/remote_signer_test/src/consumer.rs +++ /dev/null @@ -1,152 +0,0 @@ -use crate::*; -use remote_signer_client::api_response::SignatureApiResponse; -use remote_signer_consumer::{Error, RemoteSignerHttpConsumer, RemoteSignerObject}; -use reqwest::ClientBuilder; -use sensitive_url::SensitiveUrl; -use serde::Serialize; -use tokio::runtime::Builder; -use tokio::time::Duration; -use types::{AttestationData, BeaconBlock, Epoch, EthSpec, Fork, Hash256}; - -pub fn set_up_test_consumer(test_signer_address: &str) -> RemoteSignerHttpConsumer { - set_up_test_consumer_with_timeout(test_signer_address, 12) -} - -pub fn set_up_test_consumer_with_timeout( - test_signer_address: &str, - timeout: u64, -) -> RemoteSignerHttpConsumer { - let url = SensitiveUrl::parse(test_signer_address).unwrap(); - let reqwest_client = ClientBuilder::new() - .timeout(Duration::from_secs(timeout)) - .build() - .unwrap(); - - RemoteSignerHttpConsumer::from_components(url, reqwest_client) -} - -pub fn do_sign_request<E: EthSpec, T: RemoteSignerObject>( - test_client: &RemoteSignerHttpConsumer, - test_input: RemoteSignerTestData<E, T>, -) -> Result<String, Error> { - let runtime = Builder::new_multi_thread().enable_all().build().unwrap(); - - runtime.block_on(test_client.sign( - &test_input.public_key, - test_input.bls_domain, - test_input.data, - test_input.fork, - test_input.genesis_validators_root, - )) -} - -#[derive(Serialize)] -pub struct BlockRequestBody<E: EthSpec> { - bls_domain: String, - data: BeaconBlock<E>, - fork: Fork, - genesis_validators_root: Hash256, -} - -pub fn get_test_block_body(seed: u64) -> String { - let block: BeaconBlock<E> = get_block(seed); - let epoch = block.epoch(); - - let fork = Fork { - previous_version: [1; 4], - current_version: [2; 4], - epoch, - }; - - let genesis_validators_root = Hash256::from_low_u64_be(seed); - - let block_request_body = BlockRequestBody { - bls_domain: "beacon_proposer".to_string(), - data: block, - fork, - genesis_validators_root, - }; - - serde_json::to_string(&block_request_body).unwrap() -} - -#[derive(Serialize)] -pub struct AttestationRequestBody { - bls_domain: String, - data: AttestationData, - fork: Fork, - genesis_validators_root: Hash256, -} - -pub fn get_test_attestation_body(seed: u64) -> String { - let attestation = get_attestation::<E>(seed); - let epoch = attestation.target.epoch; - - let fork = Fork { - previous_version: [1; 4], - current_version: [2; 4], - epoch, - }; - - let genesis_validators_root = Hash256::from_low_u64_be(seed); - - let attestation_request_body = AttestationRequestBody { - bls_domain: "beacon_attester".to_string(), - data: attestation, - fork, - genesis_validators_root, - }; - - serde_json::to_string(&attestation_request_body).unwrap() -} - -#[derive(Serialize)] -pub struct RandaoRequestBody { - bls_domain: String, - data: Epoch, - fork: Fork, - genesis_validators_root: Hash256, -} - -pub fn get_test_randao_body(seed: u64) -> String { - let epoch = Epoch::new(seed); - - let fork = Fork { - previous_version: [1; 4], - current_version: [2; 4], - epoch, - }; - - let genesis_validators_root = Hash256::from_low_u64_be(seed); - - let randao_request_body = RandaoRequestBody { - bls_domain: "randao".to_string(), - data: epoch, - fork, - genesis_validators_root, - }; - - serde_json::to_string(&randao_request_body).unwrap() -} - -pub fn assert_sign_ok(resp: ApiTestResponse, expected_signature: &str) { - assert_eq!(resp.status, 200); - assert_eq!( - serde_json::from_value::<SignatureApiResponse>(resp.json) - .unwrap() - .signature, - expected_signature - ); -} - -pub fn assert_sign_error(resp: ApiTestResponse, http_status: u16, error_msg: &str) { - assert_eq!(resp.status, http_status); - assert_eq!( - resp.json["error"] - .as_str() - .unwrap() - // cross-platform compatiblity - .replace("\\", ""), - error_msg - ); -} diff --git a/testing/remote_signer_test/src/lib.rs b/testing/remote_signer_test/src/lib.rs deleted file mode 100644 index c0daeaa926a..00000000000 --- a/testing/remote_signer_test/src/lib.rs +++ /dev/null @@ -1,18 +0,0 @@ -mod api_test_signer; -mod constants; -mod consumer; -mod local_signer_test_data; -mod mock; -mod remote_signer_test_data; -mod utils; - -pub use api_test_signer::*; -pub use constants::*; -pub use consumer::*; -pub use local_signer_test_data::*; -pub use mock::*; -pub use remote_signer_test_data::*; -use types::MainnetEthSpec; -pub use utils::*; - -pub type E = MainnetEthSpec; diff --git a/testing/remote_signer_test/src/local_signer_test_data.rs b/testing/remote_signer_test/src/local_signer_test_data.rs deleted file mode 100644 index 69692c6a250..00000000000 --- a/testing/remote_signer_test/src/local_signer_test_data.rs +++ /dev/null @@ -1,103 +0,0 @@ -use crate::*; -use hex::decode; -use remote_signer_consumer::RemoteSignerObject; -use std::mem; -use types::{ - AttestationData, BeaconBlock, ChainSpec, Domain, Epoch, EthSpec, Fork, Hash256, SecretKey, - SignedRoot, -}; - -pub struct LocalSignerTestData<T: RemoteSignerObject> { - secret_key: SecretKey, - spec: ChainSpec, - fork: Fork, - genesis_validators_root: Hash256, - obj: T, -} - -impl<T: RemoteSignerObject> LocalSignerTestData<T> { - pub fn new(obj: T) -> Self { - let epoch = obj.get_epoch(); - - Self { - secret_key: SecretKey::deserialize(&decode(SECRET_KEY_1).unwrap()).unwrap(), - spec: E::default_spec(), - fork: Fork { - previous_version: [1; 4], - current_version: [2; 4], - epoch, - }, - genesis_validators_root: Hash256::from_low_u64_be(0xc137), - obj, - } - } -} - -impl LocalSignerTestData<BeaconBlock<E>> { - pub fn sign(&self) -> String { - let signed_block = self.obj.clone().sign( - &self.secret_key, - &self.fork, - self.genesis_validators_root, - &self.spec, - ); - - signed_block.signature().to_string() - } -} - -impl LocalSignerTestData<AttestationData> { - pub fn sign(&self) -> String { - let domain = self.spec.get_domain( - self.obj.target.epoch, - Domain::BeaconAttester, - &self.fork, - self.genesis_validators_root, - ); - - let message = self.obj.signing_root(domain); - let signature = &self.secret_key.sign(message); - - signature.to_string() - } -} - -impl LocalSignerTestData<Epoch> { - pub fn sign(&self) -> String { - let domain = self.spec.get_domain( - self.obj, - Domain::Randao, - &self.fork, - self.genesis_validators_root, - ); - - let message = self.obj.signing_root(domain); - let signature = &self.secret_key.sign(message); - - signature.to_string() - } -} - -pub fn get_input_local_signer_block(seed: u64) -> LocalSignerTestData<BeaconBlock<E>> { - let block: BeaconBlock<E>; - - unsafe { - block = mem::transmute(get_block::<E>(seed)); - } - - LocalSignerTestData::new(block) -} - -pub fn get_input_local_signer_attestation(seed: u64) -> LocalSignerTestData<AttestationData> { - let attestation: AttestationData; - - unsafe { - attestation = mem::transmute(get_attestation::<E>(seed)); - } - - LocalSignerTestData::new(attestation) -} - -pub fn get_input_local_signer_randao(seed: u64) -> LocalSignerTestData<Epoch> { - LocalSignerTestData::new(Epoch::new(seed)) -} diff --git a/testing/remote_signer_test/src/mock.rs b/testing/remote_signer_test/src/mock.rs deleted file mode 100644 index a22d2db979a..00000000000 --- a/testing/remote_signer_test/src/mock.rs +++ /dev/null @@ -1,20 +0,0 @@ -use crate::*; -use httpmock::{Method::POST, MockServer}; -use tokio::time::Duration; - -pub fn set_up_mock_server(status: u16, body: &str) -> MockServer { - set_up_mock_server_with_timeout(status, body, 0) -} - -pub fn set_up_mock_server_with_timeout(status: u16, body: &str, delay: u64) -> MockServer { - let server = MockServer::start(); - - server.mock(|when, then| { - when.method(POST).path(format!("/sign/{}", PUBLIC_KEY_1)); - then.status(status) - .delay(Duration::from_secs(delay)) - .body(body); - }); - - server -} diff --git a/testing/remote_signer_test/src/remote_signer_test_data.rs b/testing/remote_signer_test/src/remote_signer_test_data.rs deleted file mode 100644 index d3aeeca0e55..00000000000 --- a/testing/remote_signer_test/src/remote_signer_test_data.rs +++ /dev/null @@ -1,69 +0,0 @@ -use crate::*; -use remote_signer_consumer::RemoteSignerObject; -use std::marker::PhantomData; -use types::{AttestationData, BeaconBlock, Domain, Epoch, EthSpec, Fork, Hash256}; - -pub struct RemoteSignerTestData<E: EthSpec, T: RemoteSignerObject> { - pub public_key: String, - pub bls_domain: Domain, - pub data: T, - pub fork: Fork, - pub genesis_validators_root: Hash256, - - _phantom: PhantomData<E>, -} - -impl<'a, E: EthSpec, T: RemoteSignerObject> RemoteSignerTestData<E, T> { - pub fn new(public_key: &str, data: T, bls_domain: Domain) -> Self { - let epoch = data.get_epoch(); - - Self { - public_key: public_key.to_string(), - bls_domain, - data, - fork: Fork { - previous_version: [1; 4], - current_version: [2; 4], - epoch, - }, - genesis_validators_root: Hash256::from_low_u64_be(0xc137), - - _phantom: PhantomData, - } - } -} - -pub fn get_input_data_block(seed: u64) -> RemoteSignerTestData<E, BeaconBlock<E>> { - let block = get_block::<E>(seed); - RemoteSignerTestData::new(PUBLIC_KEY_1, block, Domain::BeaconProposer) -} - -pub fn get_input_data_attestation(seed: u64) -> RemoteSignerTestData<E, AttestationData> { - let attestation = get_attestation::<E>(seed); - RemoteSignerTestData::new(PUBLIC_KEY_1, attestation, Domain::BeaconAttester) -} - -pub fn get_input_data_randao(seed: u64) -> RemoteSignerTestData<E, Epoch> { - let epoch = Epoch::new(seed); - RemoteSignerTestData::new(PUBLIC_KEY_1, epoch, Domain::Randao) -} - -pub fn get_input_data_and_set_domain<E: EthSpec, T: RemoteSignerObject>( - f: fn(u64) -> RemoteSignerTestData<E, T>, - bls_domain: Domain, -) -> RemoteSignerTestData<E, T> { - let mut test_input = f(0xc137); - test_input.bls_domain = bls_domain; - - test_input -} - -pub fn get_input_data_and_set_public_key<E: EthSpec, T: RemoteSignerObject>( - f: fn(u64) -> RemoteSignerTestData<E, T>, - p: &str, -) -> RemoteSignerTestData<E, T> { - let mut test_input = f(0xc137); - test_input.public_key = p.to_string(); - - test_input -} diff --git a/testing/remote_signer_test/src/utils.rs b/testing/remote_signer_test/src/utils.rs deleted file mode 100644 index 18bbdc53878..00000000000 --- a/testing/remote_signer_test/src/utils.rs +++ /dev/null @@ -1,302 +0,0 @@ -use crate::*; -pub use constants::*; -pub use consumer::*; -pub use local_signer_test_data::*; -pub use mock::*; -use remote_signer_client::Client; -pub use remote_signer_test_data::*; -use std::fs::{create_dir, File}; -use std::io::Write; -use std::net::IpAddr::{V4, V6}; -use std::path::Path; -use tempfile::TempDir; -use types::{ - AggregateSignature, Attestation, AttestationData, AttesterSlashing, BeaconBlock, - BeaconBlockHeader, BitList, Checkpoint, Deposit, DepositData, Epoch, EthSpec, FixedVector, - Hash256, IndexedAttestation, ProposerSlashing, PublicKeyBytes, Signature, SignatureBytes, - 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 { - let listening_address = client.get_listening_address(); - let ip = match listening_address.ip() { - V4(ip) => ip.to_string(), - V6(ip) => ip.to_string(), - }; - - format!("http://{}:{}", ip, listening_address.port()) -} - -pub fn restrict_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(0o0311); // set to '*-wx--x--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(); - 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) { - let pairs = vec![ - (PUBLIC_KEY_1, SECRET_KEY_1), - (PUBLIC_KEY_2, SECRET_KEY_2), - (PUBLIC_KEY_3, SECRET_KEY_3), - ]; - - add_files(tmp_dir, pairs); -} - -pub fn add_mismatched_key_file(tmp_dir: &TempDir) { - let pairs = vec![(MISMATCHED_PUBLIC_KEY, SECRET_KEY_1)]; - - add_files(tmp_dir, pairs); -} - -pub fn add_invalid_secret_key_file(tmp_dir: &TempDir) { - let pairs = vec![(PUBLIC_KEY_FOR_INVALID_SECRET_KEY, INVALID_SECRET_KEY)]; - - add_files(tmp_dir, pairs); -} - -pub fn add_non_key_files(tmp_dir: &TempDir) { - let pairs = vec![ - (SILLY_FILE_NAME_1, SILLY_CONTENT_1), - (SILLY_FILE_NAME_2, SILLY_CONTENT_2), - (SILLY_FILE_NAME_3, SILLY_CONTENT_3), - ]; - - add_files(tmp_dir, pairs); -} - -fn add_files(tmp_dir: &TempDir, pairs: Vec<(&str, &str)>) { - for pair in pairs { - let file_path = tmp_dir.path().join(pair.0); - let mut tmp_file = File::create(file_path).unwrap(); - writeln!(tmp_file, "{}", pair.1).unwrap(); - } -} - -pub fn add_sub_dirs(tmp_dir: &TempDir) { - let random_sub_dir_path = tmp_dir.path().join("random_sub_dir_name"); - create_dir(random_sub_dir_path).unwrap(); - - let another_sub_dir_path = tmp_dir.path().join(SUB_DIR_NAME); - create_dir(another_sub_dir_path).unwrap(); -} - -/// We spice up some of the values, based on a given `seed` parameter. -pub fn get_block<E: EthSpec>(seed: u64) -> BeaconBlock<E> { - let spec = &mut E::default_spec(); - spec.genesis_slot = Slot::new(seed); - - let header = BeaconBlockHeader { - slot: Slot::new(seed), - proposer_index: 0, - parent_root: Hash256::from_low_u64_be(222 * seed), - state_root: Hash256::from_low_u64_be(333 * seed), - body_root: Hash256::from_low_u64_be(444 * seed), - }; - - let signed_header = SignedBeaconBlockHeader { - message: header, - signature: Signature::empty(), - }; - let indexed_attestation: IndexedAttestation<E> = IndexedAttestation { - attesting_indices: VariableList::new(vec![0_u64; E::MaxValidatorsPerCommittee::to_usize()]) - .unwrap(), - data: AttestationData::default(), - signature: AggregateSignature::empty(), - }; - - let deposit_data = DepositData { - pubkey: PublicKeyBytes::empty(), - withdrawal_credentials: Hash256::from_low_u64_be(555 * seed), - amount: 0, - signature: SignatureBytes::empty(), - }; - let proposer_slashing = ProposerSlashing { - signed_header_1: signed_header.clone(), - signed_header_2: signed_header, - }; - - let attester_slashing = AttesterSlashing { - attestation_1: indexed_attestation.clone(), - attestation_2: indexed_attestation, - }; - - let attestation: Attestation<E> = Attestation { - aggregation_bits: BitList::with_capacity(E::MaxValidatorsPerCommittee::to_usize()).unwrap(), - data: AttestationData::default(), - signature: AggregateSignature::empty(), - }; - - let deposit = Deposit { - proof: FixedVector::from_elem(Hash256::from_low_u64_be(666 * seed)), - data: deposit_data, - }; - - let voluntary_exit = VoluntaryExit { - epoch: Epoch::new(1), - validator_index: 1, - }; - - let signed_voluntary_exit = SignedVoluntaryExit { - message: voluntary_exit, - signature: Signature::empty(), - }; - - let mut block: BeaconBlock<E> = BeaconBlock::empty(spec); - for _ in 0..E::MaxProposerSlashings::to_usize() { - block - .body_mut() - .proposer_slashings_mut() - .push(proposer_slashing.clone()) - .unwrap(); - } - for _ in 0..E::MaxDeposits::to_usize() { - block - .body_mut() - .deposits_mut() - .push(deposit.clone()) - .unwrap(); - } - for _ in 0..E::MaxVoluntaryExits::to_usize() { - block - .body_mut() - .voluntary_exits_mut() - .push(signed_voluntary_exit.clone()) - .unwrap(); - } - for _ in 0..E::MaxAttesterSlashings::to_usize() { - block - .body_mut() - .attester_slashings_mut() - .push(attester_slashing.clone()) - .unwrap(); - } - - for _ in 0..E::MaxAttestations::to_usize() { - block - .body_mut() - .attestations_mut() - .push(attestation.clone()) - .unwrap(); - } - block -} - -pub fn get_attestation<E: EthSpec>(seed: u64) -> AttestationData { - let slot = Slot::from(seed); - let epoch = slot.epoch(E::slots_per_epoch()); - - let build_checkpoint = |epoch_u64: u64| -> Checkpoint { - Checkpoint { - epoch: Epoch::new(epoch_u64), - root: Hash256::from_low_u64_be(333 * seed), - } - }; - - let source = build_checkpoint(epoch.as_u64().saturating_sub(2)); - let target = build_checkpoint(epoch.as_u64()); - - let index = 0xc137u64; - - AttestationData { - slot, - index, - beacon_block_root: Hash256::from_low_u64_be(666 * seed), - source, - target, - } -}