Add SensitiveUrl to redact user secrets from endpoints (#2326)

## Issue Addressed

#2276 

## Proposed Changes

Add the `SensitiveUrl` struct which wraps `Url` and implements custom `Display` and `Debug` traits to redact user secrets from being logged in eth1 endpoints, beacon node endpoints and metrics.

## Additional Info

This also includes a small rewrite of the eth1 crate to make requests using `Url` instead of `&str`. 
Some error messages have also been changed to remove `Url` data.
This commit is contained in:
Mac L 2021-05-04 01:59:51 +00:00
parent 2ccb358d87
commit 4cc613d644
38 changed files with 362 additions and 143 deletions

20
Cargo.lock generated
View File

@ -25,6 +25,7 @@ dependencies = [
"rand 0.7.3", "rand 0.7.3",
"rayon", "rayon",
"safe_arith", "safe_arith",
"sensitive_url",
"slashing_protection", "slashing_protection",
"slog", "slog",
"slog-async", "slog-async",
@ -650,6 +651,7 @@ dependencies = [
"logging", "logging",
"node_test_rig", "node_test_rig",
"rand 0.7.3", "rand 0.7.3",
"sensitive_url",
"serde", "serde",
"slasher", "slasher",
"slog", "slog",
@ -1896,6 +1898,7 @@ dependencies = [
"merkle_proof", "merkle_proof",
"parking_lot", "parking_lot",
"reqwest", "reqwest",
"sensitive_url",
"serde", "serde",
"serde_json", "serde_json",
"slog", "slog",
@ -1942,6 +1945,7 @@ dependencies = [
"psutil", "psutil",
"reqwest", "reqwest",
"ring", "ring",
"sensitive_url",
"serde", "serde",
"serde_json", "serde_json",
"serde_utils", "serde_utils",
@ -2514,6 +2518,7 @@ dependencies = [
"merkle_proof", "merkle_proof",
"parking_lot", "parking_lot",
"rayon", "rayon",
"sensitive_url",
"serde", "serde",
"serde_derive", "serde_derive",
"slog", "slog",
@ -2844,6 +2849,7 @@ dependencies = [
"lighthouse_version", "lighthouse_version",
"network", "network",
"parking_lot", "parking_lot",
"sensitive_url",
"serde", "serde",
"slog", "slog",
"slot_clock", "slot_clock",
@ -3347,6 +3353,7 @@ dependencies = [
"log", "log",
"rand 0.7.3", "rand 0.7.3",
"regex", "regex",
"sensitive_url",
"serde", "serde",
"serde_yaml", "serde_yaml",
"simple_logger", "simple_logger",
@ -4189,6 +4196,7 @@ dependencies = [
"futures 0.3.14", "futures 0.3.14",
"genesis", "genesis",
"reqwest", "reqwest",
"sensitive_url",
"serde", "serde",
"tempfile", "tempfile",
"types", "types",
@ -5155,6 +5163,7 @@ dependencies = [
"rand 0.7.3", "rand 0.7.3",
"remote_signer_test", "remote_signer_test",
"reqwest", "reqwest",
"sensitive_url",
"serde", "serde",
"tokio 1.5.0", "tokio 1.5.0",
"types", "types",
@ -5171,6 +5180,7 @@ dependencies = [
"remote_signer_client", "remote_signer_client",
"remote_signer_consumer", "remote_signer_consumer",
"reqwest", "reqwest",
"sensitive_url",
"serde", "serde",
"serde_json", "serde_json",
"tempfile", "tempfile",
@ -5499,6 +5509,14 @@ version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
[[package]]
name = "sensitive_url"
version = "0.1.0"
dependencies = [
"serde",
"url",
]
[[package]] [[package]]
name = "serde" name = "serde"
version = "1.0.125" version = "1.0.125"
@ -5714,6 +5732,7 @@ dependencies = [
"node_test_rig", "node_test_rig",
"parking_lot", "parking_lot",
"rayon", "rayon",
"sensitive_url",
"tokio 1.5.0", "tokio 1.5.0",
"types", "types",
"validator_client", "validator_client",
@ -7052,6 +7071,7 @@ dependencies = [
"ring", "ring",
"safe_arith", "safe_arith",
"scrypt", "scrypt",
"sensitive_url",
"serde", "serde",
"serde_derive", "serde_derive",
"serde_json", "serde_json",

View File

@ -33,6 +33,7 @@ members = [
"common/logging", "common/logging",
"common/lru_cache", "common/lru_cache",
"common/remote_signer_consumer", "common/remote_signer_consumer",
"common/sensitive_url",
"common/slot_clock", "common/slot_clock",
"common/task_executor", "common/task_executor",
"common/test_random_derive", "common/test_random_derive",

View File

@ -34,6 +34,7 @@ slashing_protection = { path = "../validator_client/slashing_protection" }
eth2 = {path = "../common/eth2"} eth2 = {path = "../common/eth2"}
safe_arith = {path = "../consensus/safe_arith"} safe_arith = {path = "../consensus/safe_arith"}
slot_clock = { path = "../common/slot_clock" } slot_clock = { path = "../common/slot_clock" }
sensitive_url = { path = "../common/sensitive_url" }
[dev-dependencies] [dev-dependencies]
tempfile = "3.1.0" tempfile = "3.1.0"

View File

@ -4,11 +4,12 @@ use clap::{App, Arg, ArgMatches};
use environment::Environment; use environment::Environment;
use eth2::{ use eth2::{
types::{GenesisData, StateId, ValidatorData, ValidatorId, ValidatorStatus}, types::{GenesisData, StateId, ValidatorData, ValidatorId, ValidatorStatus},
BeaconNodeHttpClient, Url, BeaconNodeHttpClient,
}; };
use eth2_keystore::Keystore; use eth2_keystore::Keystore;
use eth2_network_config::Eth2NetworkConfig; use eth2_network_config::Eth2NetworkConfig;
use safe_arith::SafeArith; use safe_arith::SafeArith;
use sensitive_url::SensitiveUrl;
use slot_clock::{SlotClock, SystemTimeSlotClock}; use slot_clock::{SlotClock, SystemTimeSlotClock};
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::time::Duration; use std::time::Duration;
@ -75,7 +76,7 @@ pub fn cli_run<E: EthSpec>(matches: &ArgMatches, env: Environment<E>) -> Result<
let spec = env.eth2_config().spec.clone(); let spec = env.eth2_config().spec.clone();
let server_url: String = clap_utils::parse_required(matches, BEACON_SERVER_FLAG)?; let server_url: String = clap_utils::parse_required(matches, BEACON_SERVER_FLAG)?;
let client = BeaconNodeHttpClient::new( let client = BeaconNodeHttpClient::new(
Url::parse(&server_url) SensitiveUrl::parse(&server_url)
.map_err(|e| format!("Failed to parse beacon http server: {:?}", e))?, .map_err(|e| format!("Failed to parse beacon http server: {:?}", e))?,
); );

View File

@ -44,3 +44,4 @@ hyper = "0.14.4"
lighthouse_version = { path = "../common/lighthouse_version" } lighthouse_version = { path = "../common/lighthouse_version" }
hex = "0.4.2" hex = "0.4.2"
slasher = { path = "../slasher" } slasher = { path = "../slasher" }
sensitive_url = { path = "../common/sensitive_url" }

View File

@ -34,3 +34,4 @@ lazy_static = "1.4.0"
task_executor = { path = "../../common/task_executor" } task_executor = { path = "../../common/task_executor" }
eth2 = { path = "../../common/eth2" } eth2 = { path = "../../common/eth2" }
fallback = { path = "../../common/fallback" } fallback = { path = "../../common/fallback" }
sensitive_url = { path = "../../common/sensitive_url" }

View File

@ -12,6 +12,7 @@
use futures::future::TryFutureExt; use futures::future::TryFutureExt;
use reqwest::{header::CONTENT_TYPE, ClientBuilder, StatusCode}; use reqwest::{header::CONTENT_TYPE, ClientBuilder, StatusCode};
use sensitive_url::SensitiveUrl;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_json::{json, Value}; use serde_json::{json, Value};
use std::ops::Range; use std::ops::Range;
@ -79,7 +80,7 @@ impl FromStr for Eth1Id {
} }
/// Get the eth1 network id of the given endpoint. /// Get the eth1 network id of the given endpoint.
pub async fn get_network_id(endpoint: &str, timeout: Duration) -> Result<Eth1Id, String> { pub async fn get_network_id(endpoint: &SensitiveUrl, timeout: Duration) -> Result<Eth1Id, String> {
let response_body = send_rpc_request(endpoint, "net_version", json!([]), timeout).await?; let response_body = send_rpc_request(endpoint, "net_version", json!([]), timeout).await?;
Eth1Id::from_str( Eth1Id::from_str(
response_result(&response_body)? response_result(&response_body)?
@ -90,7 +91,7 @@ pub async fn get_network_id(endpoint: &str, timeout: Duration) -> Result<Eth1Id,
} }
/// Get the eth1 chain id of the given endpoint. /// Get the eth1 chain id of the given endpoint.
pub async fn get_chain_id(endpoint: &str, timeout: Duration) -> Result<Eth1Id, String> { pub async fn get_chain_id(endpoint: &SensitiveUrl, timeout: Duration) -> Result<Eth1Id, String> {
let response_body = send_rpc_request(endpoint, "eth_chainId", json!([]), timeout).await?; let response_body = send_rpc_request(endpoint, "eth_chainId", json!([]), timeout).await?;
hex_to_u64_be( hex_to_u64_be(
response_result(&response_body)? response_result(&response_body)?
@ -111,7 +112,7 @@ pub struct Block {
/// Returns the current block number. /// Returns the current block number.
/// ///
/// Uses HTTP JSON RPC at `endpoint`. E.g., `http://localhost:8545`. /// Uses HTTP JSON RPC at `endpoint`. E.g., `http://localhost:8545`.
pub async fn get_block_number(endpoint: &str, timeout: Duration) -> Result<u64, String> { pub async fn get_block_number(endpoint: &SensitiveUrl, timeout: Duration) -> Result<u64, String> {
let response_body = send_rpc_request(endpoint, "eth_blockNumber", json!([]), timeout).await?; let response_body = send_rpc_request(endpoint, "eth_blockNumber", json!([]), timeout).await?;
hex_to_u64_be( hex_to_u64_be(
response_result(&response_body)? response_result(&response_body)?
@ -126,7 +127,7 @@ pub async fn get_block_number(endpoint: &str, timeout: Duration) -> Result<u64,
/// ///
/// Uses HTTP JSON RPC at `endpoint`. E.g., `http://localhost:8545`. /// Uses HTTP JSON RPC at `endpoint`. E.g., `http://localhost:8545`.
pub async fn get_block( pub async fn get_block(
endpoint: &str, endpoint: &SensitiveUrl,
query: BlockQuery, query: BlockQuery,
timeout: Duration, timeout: Duration,
) -> Result<Block, String> { ) -> Result<Block, String> {
@ -191,7 +192,7 @@ pub async fn get_block(
/// ///
/// Uses HTTP JSON RPC at `endpoint`. E.g., `http://localhost:8545`. /// Uses HTTP JSON RPC at `endpoint`. E.g., `http://localhost:8545`.
pub async fn get_deposit_count( pub async fn get_deposit_count(
endpoint: &str, endpoint: &SensitiveUrl,
address: &str, address: &str,
block_number: u64, block_number: u64,
timeout: Duration, timeout: Duration,
@ -229,7 +230,7 @@ pub async fn get_deposit_count(
/// ///
/// Uses HTTP JSON RPC at `endpoint`. E.g., `http://localhost:8545`. /// Uses HTTP JSON RPC at `endpoint`. E.g., `http://localhost:8545`.
pub async fn get_deposit_root( pub async fn get_deposit_root(
endpoint: &str, endpoint: &SensitiveUrl,
address: &str, address: &str,
block_number: u64, block_number: u64,
timeout: Duration, timeout: Duration,
@ -266,7 +267,7 @@ pub async fn get_deposit_root(
/// ///
/// Uses HTTP JSON RPC at `endpoint`. E.g., `http://localhost:8545`. /// Uses HTTP JSON RPC at `endpoint`. E.g., `http://localhost:8545`.
async fn call( async fn call(
endpoint: &str, endpoint: &SensitiveUrl,
address: &str, address: &str,
hex_data: &str, hex_data: &str,
block_number: u64, block_number: u64,
@ -308,7 +309,7 @@ pub struct Log {
/// ///
/// Uses HTTP JSON RPC at `endpoint`. E.g., `http://localhost:8545`. /// Uses HTTP JSON RPC at `endpoint`. E.g., `http://localhost:8545`.
pub async fn get_deposit_logs_in_range( pub async fn get_deposit_logs_in_range(
endpoint: &str, endpoint: &SensitiveUrl,
address: &str, address: &str,
block_height_range: Range<u64>, block_height_range: Range<u64>,
timeout: Duration, timeout: Duration,
@ -353,7 +354,7 @@ pub async fn get_deposit_logs_in_range(
/// ///
/// Tries to receive the response and parse the body as a `String`. /// Tries to receive the response and parse the body as a `String`.
pub async fn send_rpc_request( pub async fn send_rpc_request(
endpoint: &str, endpoint: &SensitiveUrl,
method: &str, method: &str,
params: Value, params: Value,
timeout: Duration, timeout: Duration,
@ -374,7 +375,7 @@ pub async fn send_rpc_request(
.timeout(timeout) .timeout(timeout)
.build() .build()
.expect("The builder should always build a client") .expect("The builder should always build a client")
.post(endpoint) .post(endpoint.full.clone())
.header(CONTENT_TYPE, "application/json") .header(CONTENT_TYPE, "application/json")
.body(body) .body(body)
.send() .send()

View File

@ -11,6 +11,7 @@ use crate::{
use fallback::{Fallback, FallbackError}; use fallback::{Fallback, FallbackError};
use futures::future::TryFutureExt; use futures::future::TryFutureExt;
use parking_lot::{RwLock, RwLockReadGuard}; use parking_lot::{RwLock, RwLockReadGuard};
use sensitive_url::SensitiveUrl;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use slog::{crit, debug, error, info, trace, warn, Logger}; use slog::{crit, debug, error, info, trace, warn, Logger};
use std::fmt::Debug; use std::fmt::Debug;
@ -26,6 +27,8 @@ use types::{ChainSpec, EthSpec, Unsigned};
pub const DEFAULT_NETWORK_ID: Eth1Id = Eth1Id::Goerli; pub const DEFAULT_NETWORK_ID: Eth1Id = Eth1Id::Goerli;
/// Indicates the default eth1 chain id we use for the deposit contract. /// Indicates the default eth1 chain id we use for the deposit contract.
pub const DEFAULT_CHAIN_ID: Eth1Id = Eth1Id::Goerli; pub const DEFAULT_CHAIN_ID: Eth1Id = Eth1Id::Goerli;
/// Indicates the default eth1 endpoint.
pub const DEFAULT_ETH1_ENDPOINT: &str = "http://localhost:8545";
const STANDARD_TIMEOUT_MILLIS: u64 = 15_000; const STANDARD_TIMEOUT_MILLIS: u64 = 15_000;
@ -51,7 +54,7 @@ pub enum EndpointError {
type EndpointState = Result<(), EndpointError>; type EndpointState = Result<(), EndpointError>;
type EndpointWithState = (String, TRwLock<Option<EndpointState>>); type EndpointWithState = (SensitiveUrl, TRwLock<Option<EndpointState>>);
/// A cache structure to lazily check usability of endpoints. An endpoint is usable if it is /// A cache structure to lazily check usability of endpoints. An endpoint is usable if it is
/// reachable and has the correct network id and chain id. Emits a `WARN` log if a checked endpoint /// reachable and has the correct network id and chain id. Emits a `WARN` log if a checked endpoint
@ -74,7 +77,10 @@ impl EndpointsCache {
if let Some(result) = *value { if let Some(result) = *value {
return result; return result;
} }
crate::metrics::inc_counter_vec(&crate::metrics::ENDPOINT_REQUESTS, &[&endpoint.0]); crate::metrics::inc_counter_vec(
&crate::metrics::ENDPOINT_REQUESTS,
&[&endpoint.0.to_string()],
);
let state = endpoint_state( let state = endpoint_state(
&endpoint.0, &endpoint.0,
&self.config_network_id, &self.config_network_id,
@ -84,7 +90,10 @@ impl EndpointsCache {
.await; .await;
*value = Some(state); *value = Some(state);
if state.is_err() { if state.is_err() {
crate::metrics::inc_counter_vec(&crate::metrics::ENDPOINT_ERRORS, &[&endpoint.0]); crate::metrics::inc_counter_vec(
&crate::metrics::ENDPOINT_ERRORS,
&[&endpoint.0.to_string()],
);
} }
state state
} }
@ -94,7 +103,7 @@ impl EndpointsCache {
func: F, func: F,
) -> Result<O, FallbackError<SingleEndpointError>> ) -> Result<O, FallbackError<SingleEndpointError>>
where where
F: Fn(&'a str) -> R, F: Fn(&'a SensitiveUrl) -> R,
R: Future<Output = Result<O, SingleEndpointError>>, R: Future<Output = Result<O, SingleEndpointError>>,
{ {
let func = &func; let func = &func;
@ -102,7 +111,7 @@ impl EndpointsCache {
.first_success(|endpoint| async move { .first_success(|endpoint| async move {
match self.state(endpoint).await { match self.state(endpoint).await {
Ok(()) => { Ok(()) => {
let endpoint_str = &endpoint.0; let endpoint_str = &endpoint.0.to_string();
crate::metrics::inc_counter_vec( crate::metrics::inc_counter_vec(
&crate::metrics::ENDPOINT_REQUESTS, &crate::metrics::ENDPOINT_REQUESTS,
&[endpoint_str], &[endpoint_str],
@ -131,7 +140,7 @@ impl EndpointsCache {
/// Returns `Ok` if the endpoint is usable, i.e. is reachable and has a correct network id and /// Returns `Ok` if the endpoint is usable, i.e. is reachable and has a correct network id and
/// chain id. Otherwise it returns `Err`. /// chain id. Otherwise it returns `Err`.
async fn endpoint_state( async fn endpoint_state(
endpoint: &str, endpoint: &SensitiveUrl,
config_network_id: &Eth1Id, config_network_id: &Eth1Id,
config_chain_id: &Eth1Id, config_chain_id: &Eth1Id,
log: &Logger, log: &Logger,
@ -140,7 +149,7 @@ async fn endpoint_state(
warn!( warn!(
log, log,
"Error connecting to eth1 node endpoint"; "Error connecting to eth1 node endpoint";
"endpoint" => endpoint, "endpoint" => %endpoint,
"action" => "trying fallbacks" "action" => "trying fallbacks"
); );
EndpointError::NotReachable EndpointError::NotReachable
@ -152,7 +161,7 @@ async fn endpoint_state(
warn!( warn!(
log, log,
"Invalid eth1 network id on endpoint. Please switch to correct network id"; "Invalid eth1 network id on endpoint. Please switch to correct network id";
"endpoint" => endpoint, "endpoint" => %endpoint,
"action" => "trying fallbacks", "action" => "trying fallbacks",
"expected" => format!("{:?}",config_network_id), "expected" => format!("{:?}",config_network_id),
"received" => format!("{:?}",network_id), "received" => format!("{:?}",network_id),
@ -168,7 +177,7 @@ async fn endpoint_state(
warn!( warn!(
log, log,
"Remote eth1 node is not synced"; "Remote eth1 node is not synced";
"endpoint" => endpoint, "endpoint" => %endpoint,
"action" => "trying fallbacks" "action" => "trying fallbacks"
); );
return Err(EndpointError::FarBehind); return Err(EndpointError::FarBehind);
@ -177,7 +186,7 @@ async fn endpoint_state(
warn!( warn!(
log, log,
"Invalid eth1 chain id. Please switch to correct chain id on endpoint"; "Invalid eth1 chain id. Please switch to correct chain id on endpoint";
"endpoint" => endpoint, "endpoint" => %endpoint,
"action" => "trying fallbacks", "action" => "trying fallbacks",
"expected" => format!("{:?}",config_chain_id), "expected" => format!("{:?}",config_chain_id),
"received" => format!("{:?}", chain_id), "received" => format!("{:?}", chain_id),
@ -198,7 +207,7 @@ pub enum HeadType {
/// Returns the head block and the new block ranges relevant for deposits and the block cache /// Returns the head block and the new block ranges relevant for deposits and the block cache
/// from the given endpoint. /// from the given endpoint.
async fn get_remote_head_and_new_block_ranges( async fn get_remote_head_and_new_block_ranges(
endpoint: &str, endpoint: &SensitiveUrl,
service: &Service, service: &Service,
node_far_behind_seconds: u64, node_far_behind_seconds: u64,
) -> Result< ) -> Result<
@ -218,7 +227,7 @@ async fn get_remote_head_and_new_block_ranges(
warn!( warn!(
service.log, service.log,
"Eth1 endpoint is not synced"; "Eth1 endpoint is not synced";
"endpoint" => endpoint, "endpoint" => %endpoint,
"last_seen_block_unix_timestamp" => remote_head_block.timestamp, "last_seen_block_unix_timestamp" => remote_head_block.timestamp,
"action" => "trying fallback" "action" => "trying fallback"
); );
@ -230,7 +239,7 @@ async fn get_remote_head_and_new_block_ranges(
warn!( warn!(
service.log, service.log,
"Eth1 endpoint is not synced"; "Eth1 endpoint is not synced";
"endpoint" => endpoint, "endpoint" => %endpoint,
"action" => "trying fallbacks" "action" => "trying fallbacks"
); );
} }
@ -252,7 +261,7 @@ async fn get_remote_head_and_new_block_ranges(
/// Returns the range of new block numbers to be considered for the given head type from the given /// Returns the range of new block numbers to be considered for the given head type from the given
/// endpoint. /// endpoint.
async fn relevant_new_block_numbers_from_endpoint( async fn relevant_new_block_numbers_from_endpoint(
endpoint: &str, endpoint: &SensitiveUrl,
service: &Service, service: &Service,
head_type: HeadType, head_type: HeadType,
) -> Result<Option<RangeInclusive<u64>>, SingleEndpointError> { ) -> Result<Option<RangeInclusive<u64>>, SingleEndpointError> {
@ -319,7 +328,7 @@ pub struct DepositCacheUpdateOutcome {
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Config { pub struct Config {
/// An Eth1 node (e.g., Geth) running a HTTP JSON-RPC endpoint. /// An Eth1 node (e.g., Geth) running a HTTP JSON-RPC endpoint.
pub endpoints: Vec<String>, pub endpoints: Vec<SensitiveUrl>,
/// The address the `BlockCache` and `DepositCache` should assume is the canonical deposit contract. /// The address the `BlockCache` and `DepositCache` should assume is the canonical deposit contract.
pub deposit_contract_address: String, pub deposit_contract_address: String,
/// The eth1 network id where the deposit contract is deployed (Goerli/Mainnet). /// The eth1 network id where the deposit contract is deployed (Goerli/Mainnet).
@ -383,7 +392,8 @@ impl Config {
impl Default for Config { impl Default for Config {
fn default() -> Self { fn default() -> Self {
Self { Self {
endpoints: vec!["http://localhost:8545".into()], endpoints: vec![SensitiveUrl::parse(DEFAULT_ETH1_ENDPOINT)
.expect("The default Eth1 endpoint must always be a valid URL.")],
deposit_contract_address: "0x0000000000000000000000000000000000000000".into(), deposit_contract_address: "0x0000000000000000000000000000000000000000".into(),
network_id: DEFAULT_NETWORK_ID, network_id: DEFAULT_NETWORK_ID,
chain_id: DEFAULT_CHAIN_ID, chain_id: DEFAULT_CHAIN_ID,
@ -1137,7 +1147,7 @@ fn relevant_block_range(
/// ///
/// Performs three async calls to an Eth1 HTTP JSON RPC endpoint. /// Performs three async calls to an Eth1 HTTP JSON RPC endpoint.
async fn download_eth1_block( async fn download_eth1_block(
endpoint: &str, endpoint: &SensitiveUrl,
cache: Arc<Inner>, cache: Arc<Inner>,
block_number_opt: Option<u64>, block_number_opt: Option<u64>,
) -> Result<Eth1Block, SingleEndpointError> { ) -> Result<Eth1Block, SingleEndpointError> {
@ -1182,6 +1192,12 @@ mod tests {
use super::*; use super::*;
use types::MainnetEthSpec; use types::MainnetEthSpec;
#[test]
// Ensures the default config does not panic.
fn default_config() {
Config::default();
}
#[test] #[test]
fn serde_serialize() { fn serde_serialize() {
let serialized = let serialized =

View File

@ -5,6 +5,7 @@ use eth1::{Config, Service};
use eth1::{DepositCache, DEFAULT_CHAIN_ID, DEFAULT_NETWORK_ID}; use eth1::{DepositCache, DEFAULT_CHAIN_ID, DEFAULT_NETWORK_ID};
use eth1_test_rig::GanacheEth1Instance; use eth1_test_rig::GanacheEth1Instance;
use merkle_proof::verify_merkle_proof; use merkle_proof::verify_merkle_proof;
use sensitive_url::SensitiveUrl;
use slog::Logger; use slog::Logger;
use sloggers::{null::NullLoggerBuilder, Build}; use sloggers::{null::NullLoggerBuilder, Build};
use std::ops::Range; use std::ops::Range;
@ -53,7 +54,7 @@ fn random_deposit_data() -> DepositData {
/// Blocking operation to get the deposit logs from the `deposit_contract`. /// Blocking operation to get the deposit logs from the `deposit_contract`.
async fn blocking_deposit_logs(eth1: &GanacheEth1Instance, range: Range<u64>) -> Vec<Log> { async fn blocking_deposit_logs(eth1: &GanacheEth1Instance, range: Range<u64>) -> Vec<Log> {
get_deposit_logs_in_range( get_deposit_logs_in_range(
&eth1.endpoint(), &SensitiveUrl::parse(eth1.endpoint().as_str()).unwrap(),
&eth1.deposit_contract.address(), &eth1.deposit_contract.address(),
range, range,
timeout(), timeout(),
@ -65,7 +66,7 @@ async fn blocking_deposit_logs(eth1: &GanacheEth1Instance, range: Range<u64>) ->
/// Blocking operation to get the deposit root from the `deposit_contract`. /// Blocking operation to get the deposit root from the `deposit_contract`.
async fn blocking_deposit_root(eth1: &GanacheEth1Instance, block_number: u64) -> Option<Hash256> { async fn blocking_deposit_root(eth1: &GanacheEth1Instance, block_number: u64) -> Option<Hash256> {
get_deposit_root( get_deposit_root(
&eth1.endpoint(), &SensitiveUrl::parse(eth1.endpoint().as_str()).unwrap(),
&eth1.deposit_contract.address(), &eth1.deposit_contract.address(),
block_number, block_number,
timeout(), timeout(),
@ -77,7 +78,7 @@ async fn blocking_deposit_root(eth1: &GanacheEth1Instance, block_number: u64) ->
/// Blocking operation to get the deposit count from the `deposit_contract`. /// Blocking operation to get the deposit count from the `deposit_contract`.
async fn blocking_deposit_count(eth1: &GanacheEth1Instance, block_number: u64) -> Option<u64> { async fn blocking_deposit_count(eth1: &GanacheEth1Instance, block_number: u64) -> Option<u64> {
get_deposit_count( get_deposit_count(
&eth1.endpoint(), &SensitiveUrl::parse(eth1.endpoint().as_str()).unwrap(),
&eth1.deposit_contract.address(), &eth1.deposit_contract.address(),
block_number, block_number,
timeout(), timeout(),
@ -119,7 +120,7 @@ mod eth1_cache {
let service = Service::new( let service = Service::new(
Config { Config {
endpoints: vec![eth1.endpoint()], endpoints: vec![SensitiveUrl::parse(eth1.endpoint().as_str()).unwrap()],
deposit_contract_address: deposit_contract.address(), deposit_contract_address: deposit_contract.address(),
lowest_cached_block_number: initial_block_number, lowest_cached_block_number: initial_block_number,
follow_distance, follow_distance,
@ -200,7 +201,7 @@ mod eth1_cache {
let service = Service::new( let service = Service::new(
Config { Config {
endpoints: vec![eth1.endpoint()], endpoints: vec![SensitiveUrl::parse(eth1.endpoint().as_str()).unwrap()],
deposit_contract_address: deposit_contract.address(), deposit_contract_address: deposit_contract.address(),
lowest_cached_block_number: get_block_number(&web3).await, lowest_cached_block_number: get_block_number(&web3).await,
follow_distance: 0, follow_distance: 0,
@ -255,7 +256,7 @@ mod eth1_cache {
let service = Service::new( let service = Service::new(
Config { Config {
endpoints: vec![eth1.endpoint()], endpoints: vec![SensitiveUrl::parse(eth1.endpoint().as_str()).unwrap()],
deposit_contract_address: deposit_contract.address(), deposit_contract_address: deposit_contract.address(),
lowest_cached_block_number: get_block_number(&web3).await, lowest_cached_block_number: get_block_number(&web3).await,
follow_distance: 0, follow_distance: 0,
@ -306,7 +307,7 @@ mod eth1_cache {
let service = Service::new( let service = Service::new(
Config { Config {
endpoints: vec![eth1.endpoint()], endpoints: vec![SensitiveUrl::parse(eth1.endpoint().as_str()).unwrap()],
deposit_contract_address: deposit_contract.address(), deposit_contract_address: deposit_contract.address(),
lowest_cached_block_number: get_block_number(&web3).await, lowest_cached_block_number: get_block_number(&web3).await,
follow_distance: 0, follow_distance: 0,
@ -359,7 +360,7 @@ mod deposit_tree {
let service = Service::new( let service = Service::new(
Config { Config {
endpoints: vec![eth1.endpoint()], endpoints: vec![SensitiveUrl::parse(eth1.endpoint().as_str()).unwrap()],
deposit_contract_address: deposit_contract.address(), deposit_contract_address: deposit_contract.address(),
deposit_contract_deploy_block: start_block, deposit_contract_deploy_block: start_block,
follow_distance: 0, follow_distance: 0,
@ -440,7 +441,7 @@ mod deposit_tree {
let service = Service::new( let service = Service::new(
Config { Config {
endpoints: vec![eth1.endpoint()], endpoints: vec![SensitiveUrl::parse(eth1.endpoint().as_str()).unwrap()],
deposit_contract_address: deposit_contract.address(), deposit_contract_address: deposit_contract.address(),
deposit_contract_deploy_block: start_block, deposit_contract_deploy_block: start_block,
lowest_cached_block_number: start_block, lowest_cached_block_number: start_block,
@ -582,7 +583,7 @@ mod http {
async fn get_block(eth1: &GanacheEth1Instance, block_number: u64) -> Block { async fn get_block(eth1: &GanacheEth1Instance, block_number: u64) -> Block {
eth1::http::get_block( eth1::http::get_block(
&eth1.endpoint(), &SensitiveUrl::parse(eth1.endpoint().as_str()).unwrap(),
BlockQuery::Number(block_number), BlockQuery::Number(block_number),
timeout(), timeout(),
) )
@ -698,7 +699,7 @@ mod fast {
let now = get_block_number(&web3).await; let now = get_block_number(&web3).await;
let service = Service::new( let service = Service::new(
Config { Config {
endpoints: vec![eth1.endpoint()], endpoints: vec![SensitiveUrl::parse(eth1.endpoint().as_str()).unwrap()],
deposit_contract_address: deposit_contract.address(), deposit_contract_address: deposit_contract.address(),
deposit_contract_deploy_block: now, deposit_contract_deploy_block: now,
lowest_cached_block_number: now, lowest_cached_block_number: now,
@ -775,7 +776,7 @@ mod persist {
let now = get_block_number(&web3).await; let now = get_block_number(&web3).await;
let config = Config { let config = Config {
endpoints: vec![eth1.endpoint()], endpoints: vec![SensitiveUrl::parse(eth1.endpoint().as_str()).unwrap()],
deposit_contract_address: deposit_contract.address(), deposit_contract_address: deposit_contract.address(),
deposit_contract_deploy_block: now, deposit_contract_deploy_block: now,
lowest_cached_block_number: now, lowest_cached_block_number: now,
@ -885,7 +886,10 @@ mod fallbacks {
let service = Service::new( let service = Service::new(
Config { Config {
endpoints: vec![endpoint1.endpoint(), endpoint2.endpoint()], endpoints: vec![
SensitiveUrl::parse(endpoint1.endpoint().as_str()).unwrap(),
SensitiveUrl::parse(endpoint2.endpoint().as_str()).unwrap(),
],
deposit_contract_address: deposit_contract.address(), deposit_contract_address: deposit_contract.address(),
lowest_cached_block_number: initial_block_number, lowest_cached_block_number: initial_block_number,
follow_distance: 0, follow_distance: 0,
@ -961,7 +965,10 @@ mod fallbacks {
let service = Service::new( let service = Service::new(
Config { Config {
endpoints: vec![endpoint2.endpoint(), endpoint1.endpoint()], endpoints: vec![
SensitiveUrl::parse(endpoint2.endpoint().as_str()).unwrap(),
SensitiveUrl::parse(endpoint1.endpoint().as_str()).unwrap(),
],
deposit_contract_address: deposit_contract.address(), deposit_contract_address: deposit_contract.address(),
lowest_cached_block_number: initial_block_number, lowest_cached_block_number: initial_block_number,
follow_distance: 0, follow_distance: 0,
@ -1028,7 +1035,10 @@ mod fallbacks {
let service = Service::new( let service = Service::new(
Config { Config {
endpoints: vec![endpoint2.endpoint(), endpoint1.endpoint()], endpoints: vec![
SensitiveUrl::parse(endpoint2.endpoint().as_str()).unwrap(),
SensitiveUrl::parse(endpoint1.endpoint().as_str()).unwrap(),
],
deposit_contract_address: deposit_contract.address(), deposit_contract_address: deposit_contract.address(),
lowest_cached_block_number: initial_block_number, lowest_cached_block_number: initial_block_number,
follow_distance: 0, follow_distance: 0,
@ -1081,7 +1091,10 @@ mod fallbacks {
let service = Service::new( let service = Service::new(
Config { Config {
endpoints: vec![endpoint1.endpoint(), endpoint2.endpoint()], endpoints: vec![
SensitiveUrl::parse(endpoint1.endpoint().as_str()).unwrap(),
SensitiveUrl::parse(endpoint2.endpoint().as_str()).unwrap(),
],
deposit_contract_address: deposit_contract.address(), deposit_contract_address: deposit_contract.address(),
lowest_cached_block_number: initial_block_number, lowest_cached_block_number: initial_block_number,
follow_distance: 0, follow_distance: 0,

View File

@ -7,6 +7,7 @@ edition = "2018"
[dev-dependencies] [dev-dependencies]
eth1_test_rig = { path = "../../testing/eth1_test_rig" } eth1_test_rig = { path = "../../testing/eth1_test_rig" }
tokio-compat-02 = "0.1" tokio-compat-02 = "0.1"
sensitive_url = { path = "../../common/sensitive_url" }
[dependencies] [dependencies]
futures = "0.3.7" futures = "0.3.7"

View File

@ -7,6 +7,7 @@ use environment::{Environment, EnvironmentBuilder};
use eth1::{DEFAULT_CHAIN_ID, DEFAULT_NETWORK_ID}; use eth1::{DEFAULT_CHAIN_ID, DEFAULT_NETWORK_ID};
use eth1_test_rig::{DelayThenDeposit, GanacheEth1Instance}; use eth1_test_rig::{DelayThenDeposit, GanacheEth1Instance};
use genesis::{Eth1Config, Eth1GenesisService}; use genesis::{Eth1Config, Eth1GenesisService};
use sensitive_url::SensitiveUrl;
use state_processing::is_valid_genesis_state; use state_processing::is_valid_genesis_state;
use std::time::Duration; use std::time::Duration;
use tokio_compat_02::FutureExt; use tokio_compat_02::FutureExt;
@ -46,7 +47,7 @@ fn basic() {
let service = Eth1GenesisService::new( let service = Eth1GenesisService::new(
Eth1Config { Eth1Config {
endpoints: vec![eth1.endpoint()], endpoints: vec![SensitiveUrl::parse(eth1.endpoint().as_str()).unwrap()],
deposit_contract_address: deposit_contract.address(), deposit_contract_address: deposit_contract.address(),
deposit_contract_deploy_block: now, deposit_contract_deploy_block: now,
lowest_cached_block_number: now, lowest_cached_block_number: now,

View File

@ -35,3 +35,4 @@ store = { path = "../store" }
environment = { path = "../../lighthouse/environment" } environment = { path = "../../lighthouse/environment" }
tree_hash = "0.1.1" tree_hash = "0.1.1"
discv5 = { git = "https://github.com/sigp/discv5 ", rev = "02d2c896c66f8dc2b848c3996fedcd98e1dfec69", features = ["libp2p"] } discv5 = { git = "https://github.com/sigp/discv5 ", rev = "02d2c896c66f8dc2b848c3996fedcd98e1dfec69", features = ["libp2p"] }
sensitive_url = { path = "../../common/sensitive_url" }

View File

@ -8,7 +8,7 @@ use discv5::enr::{CombinedKey, EnrBuilder};
use environment::null_logger; use environment::null_logger;
use eth2::Error; use eth2::Error;
use eth2::StatusCode; use eth2::StatusCode;
use eth2::{types::*, BeaconNodeHttpClient, Url}; use eth2::{types::*, BeaconNodeHttpClient};
use eth2_libp2p::{ use eth2_libp2p::{
rpc::methods::MetaData, rpc::methods::MetaData,
types::{EnrBitfield, SyncState}, types::{EnrBitfield, SyncState},
@ -18,6 +18,7 @@ use futures::stream::{Stream, StreamExt};
use futures::FutureExt; use futures::FutureExt;
use http_api::{Config, Context}; use http_api::{Config, Context};
use network::NetworkMessage; use network::NetworkMessage;
use sensitive_url::SensitiveUrl;
use slot_clock::SlotClock; use slot_clock::SlotClock;
use state_processing::per_slot_processing; use state_processing::per_slot_processing;
use std::convert::TryInto; use std::convert::TryInto;
@ -200,7 +201,7 @@ impl ApiTester {
tokio::spawn(async { server.await }); tokio::spawn(async { server.await });
let client = BeaconNodeHttpClient::new( let client = BeaconNodeHttpClient::new(
Url::parse(&format!( SensitiveUrl::parse(&format!(
"http://{}:{}", "http://{}:{}",
listening_socket.ip(), listening_socket.ip(),
listening_socket.port() listening_socket.port()
@ -307,7 +308,7 @@ impl ApiTester {
tokio::spawn(async { server.await }); tokio::spawn(async { server.await });
let client = BeaconNodeHttpClient::new( let client = BeaconNodeHttpClient::new(
Url::parse(&format!( SensitiveUrl::parse(&format!(
"http://{}:{}", "http://{}:{}",
listening_socket.ip(), listening_socket.ip(),
listening_socket.port() listening_socket.port()

View File

@ -4,6 +4,7 @@ use client::{ClientConfig, ClientGenesis};
use directory::{DEFAULT_BEACON_NODE_DIR, DEFAULT_NETWORK_DIR, DEFAULT_ROOT_DIR}; use directory::{DEFAULT_BEACON_NODE_DIR, DEFAULT_NETWORK_DIR, DEFAULT_ROOT_DIR};
use eth2_libp2p::{multiaddr::Protocol, Enr, Multiaddr, NetworkConfig, PeerIdSerialized}; use eth2_libp2p::{multiaddr::Protocol, Enr, Multiaddr, NetworkConfig, PeerIdSerialized};
use eth2_network_config::{Eth2NetworkConfig, DEFAULT_HARDCODED_NETWORK}; use eth2_network_config::{Eth2NetworkConfig, DEFAULT_HARDCODED_NETWORK};
use sensitive_url::SensitiveUrl;
use slog::{info, warn, Logger}; use slog::{info, warn, Logger};
use std::cmp; use std::cmp;
use std::cmp::max; use std::cmp::max;
@ -163,17 +164,21 @@ pub fn get_config<E: EthSpec>(
} }
// Defines the URL to reach the eth1 node. // Defines the URL to reach the eth1 node.
if let Some(val) = cli_args.value_of("eth1-endpoint") { if let Some(endpoint) = cli_args.value_of("eth1-endpoint") {
warn!( warn!(
log, log,
"The --eth1-endpoint flag is deprecated"; "The --eth1-endpoint flag is deprecated";
"msg" => "please use --eth1-endpoints instead" "msg" => "please use --eth1-endpoints instead"
); );
client_config.sync_eth1_chain = true; client_config.sync_eth1_chain = true;
client_config.eth1.endpoints = vec![val.to_string()]; client_config.eth1.endpoints = vec![SensitiveUrl::parse(endpoint)
} else if let Some(val) = cli_args.value_of("eth1-endpoints") { .map_err(|e| format!("eth1-endpoint was an invalid URL: {:?}", e))?];
client_config.sync_eth1_chain = true; } else if let Some(endpoints) = cli_args.value_of("eth1-endpoints") {
client_config.eth1.endpoints = val.split(',').map(String::from).collect(); client_config.eth1.endpoints = endpoints
.split(',')
.map(|s| SensitiveUrl::parse(s))
.collect::<Result<_, _>>()
.map_err(|e| format!("eth1-endpoints contains an invalid URL {:?}", e))?;
} }
if let Some(val) = cli_args.value_of("eth1-blocks-per-log-query") { if let Some(val) = cli_args.value_of("eth1-blocks-per-log-query") {

View File

@ -21,6 +21,7 @@ libsecp256k1 = "0.3.5"
ring = "0.16.19" ring = "0.16.19"
bytes = "1.0.1" bytes = "1.0.1"
account_utils = { path = "../../common/account_utils" } account_utils = { path = "../../common/account_utils" }
sensitive_url = { path = "../../common/sensitive_url" }
eth2_ssz = "0.1.2" eth2_ssz = "0.1.2"
eth2_ssz_derive = "0.1.0" eth2_ssz_derive = "0.1.0"
futures-util = "0.3.8" futures-util = "0.3.8"

View File

@ -19,6 +19,7 @@ use futures_util::StreamExt;
pub use reqwest; pub use reqwest;
use reqwest::{IntoUrl, Response}; use reqwest::{IntoUrl, Response};
pub use reqwest::{StatusCode, Url}; pub use reqwest::{StatusCode, Url};
use sensitive_url::SensitiveUrl;
use serde::{de::DeserializeOwned, Serialize}; use serde::{de::DeserializeOwned, Serialize};
use ssz::Decode; use ssz::Decode;
use std::convert::TryFrom; use std::convert::TryFrom;
@ -36,7 +37,7 @@ pub enum Error {
/// The server returned an error message where the body was unable to be parsed. /// The server returned an error message where the body was unable to be parsed.
StatusCode(StatusCode), StatusCode(StatusCode),
/// The supplied URL is badly formatted. It should look something like `http://127.0.0.1:5052`. /// The supplied URL is badly formatted. It should look something like `http://127.0.0.1:5052`.
InvalidUrl(Url), InvalidUrl(SensitiveUrl),
/// The supplied validator client secret is invalid. /// The supplied validator client secret is invalid.
InvalidSecret(String), InvalidSecret(String),
/// The server returned a response with an invalid signature. It may be an impostor. /// The server returned a response with an invalid signature. It may be an impostor.
@ -81,7 +82,7 @@ impl fmt::Display for Error {
#[derive(Clone)] #[derive(Clone)]
pub struct BeaconNodeHttpClient { pub struct BeaconNodeHttpClient {
client: reqwest::Client, client: reqwest::Client,
server: Url, server: SensitiveUrl,
} }
impl fmt::Display for BeaconNodeHttpClient { impl fmt::Display for BeaconNodeHttpClient {
@ -92,25 +93,25 @@ impl fmt::Display for BeaconNodeHttpClient {
impl AsRef<str> for BeaconNodeHttpClient { impl AsRef<str> for BeaconNodeHttpClient {
fn as_ref(&self) -> &str { fn as_ref(&self) -> &str {
self.server.as_str() self.server.as_ref()
} }
} }
impl BeaconNodeHttpClient { impl BeaconNodeHttpClient {
pub fn new(server: Url) -> Self { pub fn new(server: SensitiveUrl) -> Self {
Self { Self {
client: reqwest::Client::new(), client: reqwest::Client::new(),
server, server,
} }
} }
pub fn from_components(server: Url, client: reqwest::Client) -> Self { pub fn from_components(server: SensitiveUrl, client: reqwest::Client) -> Self {
Self { client, server } Self { client, server }
} }
/// Return the path with the standard `/eth1/v1` prefix applied. /// Return the path with the standard `/eth1/v1` prefix applied.
fn eth_path(&self) -> Result<Url, Error> { fn eth_path(&self) -> Result<Url, Error> {
let mut path = self.server.clone(); let mut path = self.server.full.clone();
path.path_segments_mut() path.path_segments_mut()
.map_err(|()| Error::InvalidUrl(self.server.clone()))? .map_err(|()| Error::InvalidUrl(self.server.clone()))?

View File

@ -214,7 +214,7 @@ impl BeaconNodeHttpClient {
/// `GET lighthouse/health` /// `GET lighthouse/health`
pub async fn get_lighthouse_health(&self) -> Result<GenericResponse<Health>, Error> { pub async fn get_lighthouse_health(&self) -> Result<GenericResponse<Health>, Error> {
let mut path = self.server.clone(); let mut path = self.server.full.clone();
path.path_segments_mut() path.path_segments_mut()
.map_err(|()| Error::InvalidUrl(self.server.clone()))? .map_err(|()| Error::InvalidUrl(self.server.clone()))?
@ -226,7 +226,7 @@ impl BeaconNodeHttpClient {
/// `GET lighthouse/syncing` /// `GET lighthouse/syncing`
pub async fn get_lighthouse_syncing(&self) -> Result<GenericResponse<SyncState>, Error> { pub async fn get_lighthouse_syncing(&self) -> Result<GenericResponse<SyncState>, Error> {
let mut path = self.server.clone(); let mut path = self.server.full.clone();
path.path_segments_mut() path.path_segments_mut()
.map_err(|()| Error::InvalidUrl(self.server.clone()))? .map_err(|()| Error::InvalidUrl(self.server.clone()))?
@ -246,7 +246,7 @@ impl BeaconNodeHttpClient {
/// `GET lighthouse/proto_array` /// `GET lighthouse/proto_array`
pub async fn get_lighthouse_proto_array(&self) -> Result<GenericResponse<ProtoArray>, Error> { pub async fn get_lighthouse_proto_array(&self) -> Result<GenericResponse<ProtoArray>, Error> {
let mut path = self.server.clone(); let mut path = self.server.full.clone();
path.path_segments_mut() path.path_segments_mut()
.map_err(|()| Error::InvalidUrl(self.server.clone()))? .map_err(|()| Error::InvalidUrl(self.server.clone()))?
@ -261,7 +261,7 @@ impl BeaconNodeHttpClient {
&self, &self,
epoch: Epoch, epoch: Epoch,
) -> Result<GenericResponse<GlobalValidatorInclusionData>, Error> { ) -> Result<GenericResponse<GlobalValidatorInclusionData>, Error> {
let mut path = self.server.clone(); let mut path = self.server.full.clone();
path.path_segments_mut() path.path_segments_mut()
.map_err(|()| Error::InvalidUrl(self.server.clone()))? .map_err(|()| Error::InvalidUrl(self.server.clone()))?
@ -279,7 +279,7 @@ impl BeaconNodeHttpClient {
epoch: Epoch, epoch: Epoch,
validator_id: ValidatorId, validator_id: ValidatorId,
) -> Result<GenericResponse<Option<ValidatorInclusionData>>, Error> { ) -> Result<GenericResponse<Option<ValidatorInclusionData>>, Error> {
let mut path = self.server.clone(); let mut path = self.server.full.clone();
path.path_segments_mut() path.path_segments_mut()
.map_err(|()| Error::InvalidUrl(self.server.clone()))? .map_err(|()| Error::InvalidUrl(self.server.clone()))?
@ -295,7 +295,7 @@ impl BeaconNodeHttpClient {
pub async fn get_lighthouse_eth1_syncing( pub async fn get_lighthouse_eth1_syncing(
&self, &self,
) -> Result<GenericResponse<Eth1SyncStatusData>, Error> { ) -> Result<GenericResponse<Eth1SyncStatusData>, Error> {
let mut path = self.server.clone(); let mut path = self.server.full.clone();
path.path_segments_mut() path.path_segments_mut()
.map_err(|()| Error::InvalidUrl(self.server.clone()))? .map_err(|()| Error::InvalidUrl(self.server.clone()))?
@ -310,7 +310,7 @@ impl BeaconNodeHttpClient {
pub async fn get_lighthouse_eth1_block_cache( pub async fn get_lighthouse_eth1_block_cache(
&self, &self,
) -> Result<GenericResponse<Vec<Eth1Block>>, Error> { ) -> Result<GenericResponse<Vec<Eth1Block>>, Error> {
let mut path = self.server.clone(); let mut path = self.server.full.clone();
path.path_segments_mut() path.path_segments_mut()
.map_err(|()| Error::InvalidUrl(self.server.clone()))? .map_err(|()| Error::InvalidUrl(self.server.clone()))?
@ -325,7 +325,7 @@ impl BeaconNodeHttpClient {
pub async fn get_lighthouse_eth1_deposit_cache( pub async fn get_lighthouse_eth1_deposit_cache(
&self, &self,
) -> Result<GenericResponse<Vec<DepositLog>>, Error> { ) -> Result<GenericResponse<Vec<DepositLog>>, Error> {
let mut path = self.server.clone(); let mut path = self.server.full.clone();
path.path_segments_mut() path.path_segments_mut()
.map_err(|()| Error::InvalidUrl(self.server.clone()))? .map_err(|()| Error::InvalidUrl(self.server.clone()))?
@ -341,7 +341,7 @@ impl BeaconNodeHttpClient {
&self, &self,
state_id: &StateId, state_id: &StateId,
) -> Result<Option<BeaconState<E>>, Error> { ) -> Result<Option<BeaconState<E>>, Error> {
let mut path = self.server.clone(); let mut path = self.server.full.clone();
path.path_segments_mut() path.path_segments_mut()
.map_err(|()| Error::InvalidUrl(self.server.clone()))? .map_err(|()| Error::InvalidUrl(self.server.clone()))?
@ -359,7 +359,7 @@ impl BeaconNodeHttpClient {
/// `GET lighthouse/staking` /// `GET lighthouse/staking`
pub async fn get_lighthouse_staking(&self) -> Result<bool, Error> { pub async fn get_lighthouse_staking(&self) -> Result<bool, Error> {
let mut path = self.server.clone(); let mut path = self.server.full.clone();
path.path_segments_mut() path.path_segments_mut()
.map_err(|()| Error::InvalidUrl(self.server.clone()))? .map_err(|()| Error::InvalidUrl(self.server.clone()))?

View File

@ -8,6 +8,7 @@ use reqwest::{
}; };
use ring::digest::{digest, SHA256}; use ring::digest::{digest, SHA256};
use secp256k1::{Message, PublicKey, Signature}; use secp256k1::{Message, PublicKey, Signature};
use sensitive_url::SensitiveUrl;
use serde::{de::DeserializeOwned, Serialize}; use serde::{de::DeserializeOwned, Serialize};
pub use reqwest; pub use reqwest;
@ -18,7 +19,7 @@ pub use reqwest::{Response, StatusCode, Url};
#[derive(Clone)] #[derive(Clone)]
pub struct ValidatorClientHttpClient { pub struct ValidatorClientHttpClient {
client: reqwest::Client, client: reqwest::Client,
server: Url, server: SensitiveUrl,
secret: ZeroizeString, secret: ZeroizeString,
server_pubkey: PublicKey, server_pubkey: PublicKey,
} }
@ -53,7 +54,7 @@ pub fn parse_pubkey(secret: &str) -> Result<PublicKey, Error> {
} }
impl ValidatorClientHttpClient { impl ValidatorClientHttpClient {
pub fn new(server: Url, secret: String) -> Result<Self, Error> { pub fn new(server: SensitiveUrl, secret: String) -> Result<Self, Error> {
Ok(Self { Ok(Self {
client: reqwest::Client::new(), client: reqwest::Client::new(),
server, server,
@ -63,7 +64,7 @@ impl ValidatorClientHttpClient {
} }
pub fn from_components( pub fn from_components(
server: Url, server: SensitiveUrl,
client: reqwest::Client, client: reqwest::Client,
secret: String, secret: String,
) -> Result<Self, Error> { ) -> Result<Self, Error> {
@ -187,7 +188,7 @@ impl ValidatorClientHttpClient {
/// `GET lighthouse/version` /// `GET lighthouse/version`
pub async fn get_lighthouse_version(&self) -> Result<GenericResponse<VersionData>, Error> { pub async fn get_lighthouse_version(&self) -> Result<GenericResponse<VersionData>, Error> {
let mut path = self.server.clone(); let mut path = self.server.full.clone();
path.path_segments_mut() path.path_segments_mut()
.map_err(|()| Error::InvalidUrl(self.server.clone()))? .map_err(|()| Error::InvalidUrl(self.server.clone()))?
@ -199,7 +200,7 @@ impl ValidatorClientHttpClient {
/// `GET lighthouse/health` /// `GET lighthouse/health`
pub async fn get_lighthouse_health(&self) -> Result<GenericResponse<Health>, Error> { pub async fn get_lighthouse_health(&self) -> Result<GenericResponse<Health>, Error> {
let mut path = self.server.clone(); let mut path = self.server.full.clone();
path.path_segments_mut() path.path_segments_mut()
.map_err(|()| Error::InvalidUrl(self.server.clone()))? .map_err(|()| Error::InvalidUrl(self.server.clone()))?
@ -211,7 +212,7 @@ impl ValidatorClientHttpClient {
/// `GET lighthouse/spec` /// `GET lighthouse/spec`
pub async fn get_lighthouse_spec(&self) -> Result<GenericResponse<YamlConfig>, Error> { pub async fn get_lighthouse_spec(&self) -> Result<GenericResponse<YamlConfig>, Error> {
let mut path = self.server.clone(); let mut path = self.server.full.clone();
path.path_segments_mut() path.path_segments_mut()
.map_err(|()| Error::InvalidUrl(self.server.clone()))? .map_err(|()| Error::InvalidUrl(self.server.clone()))?
@ -225,7 +226,7 @@ impl ValidatorClientHttpClient {
pub async fn get_lighthouse_validators( pub async fn get_lighthouse_validators(
&self, &self,
) -> Result<GenericResponse<Vec<ValidatorData>>, Error> { ) -> Result<GenericResponse<Vec<ValidatorData>>, Error> {
let mut path = self.server.clone(); let mut path = self.server.full.clone();
path.path_segments_mut() path.path_segments_mut()
.map_err(|()| Error::InvalidUrl(self.server.clone()))? .map_err(|()| Error::InvalidUrl(self.server.clone()))?
@ -240,7 +241,7 @@ impl ValidatorClientHttpClient {
&self, &self,
validator_pubkey: &PublicKeyBytes, validator_pubkey: &PublicKeyBytes,
) -> Result<Option<GenericResponse<ValidatorData>>, Error> { ) -> Result<Option<GenericResponse<ValidatorData>>, Error> {
let mut path = self.server.clone(); let mut path = self.server.full.clone();
path.path_segments_mut() path.path_segments_mut()
.map_err(|()| Error::InvalidUrl(self.server.clone()))? .map_err(|()| Error::InvalidUrl(self.server.clone()))?
@ -256,7 +257,7 @@ impl ValidatorClientHttpClient {
&self, &self,
validators: Vec<ValidatorRequest>, validators: Vec<ValidatorRequest>,
) -> Result<GenericResponse<PostValidatorsResponseData>, Error> { ) -> Result<GenericResponse<PostValidatorsResponseData>, Error> {
let mut path = self.server.clone(); let mut path = self.server.full.clone();
path.path_segments_mut() path.path_segments_mut()
.map_err(|()| Error::InvalidUrl(self.server.clone()))? .map_err(|()| Error::InvalidUrl(self.server.clone()))?
@ -271,7 +272,7 @@ impl ValidatorClientHttpClient {
&self, &self,
request: &CreateValidatorsMnemonicRequest, request: &CreateValidatorsMnemonicRequest,
) -> Result<GenericResponse<Vec<CreatedValidator>>, Error> { ) -> Result<GenericResponse<Vec<CreatedValidator>>, Error> {
let mut path = self.server.clone(); let mut path = self.server.full.clone();
path.path_segments_mut() path.path_segments_mut()
.map_err(|()| Error::InvalidUrl(self.server.clone()))? .map_err(|()| Error::InvalidUrl(self.server.clone()))?
@ -287,7 +288,7 @@ impl ValidatorClientHttpClient {
&self, &self,
request: &KeystoreValidatorsPostRequest, request: &KeystoreValidatorsPostRequest,
) -> Result<GenericResponse<ValidatorData>, Error> { ) -> Result<GenericResponse<ValidatorData>, Error> {
let mut path = self.server.clone(); let mut path = self.server.full.clone();
path.path_segments_mut() path.path_segments_mut()
.map_err(|()| Error::InvalidUrl(self.server.clone()))? .map_err(|()| Error::InvalidUrl(self.server.clone()))?
@ -304,7 +305,7 @@ impl ValidatorClientHttpClient {
voting_pubkey: &PublicKeyBytes, voting_pubkey: &PublicKeyBytes,
enabled: bool, enabled: bool,
) -> Result<(), Error> { ) -> Result<(), Error> {
let mut path = self.server.clone(); let mut path = self.server.full.clone();
path.path_segments_mut() path.path_segments_mut()
.map_err(|()| Error::InvalidUrl(self.server.clone()))? .map_err(|()| Error::InvalidUrl(self.server.clone()))?

View File

@ -13,3 +13,4 @@ reqwest = { version = "0.11.0", features = ["json"] }
serde = { version = "1.0.116", features = ["derive"] } serde = { version = "1.0.116", features = ["derive"] }
tokio = { version = "1.1.0", features = ["time"] } tokio = { version = "1.1.0", features = ["time"] }
types = { path = "../../consensus/types" } types = { path = "../../consensus/types" }
sensitive_url = { path = "../sensitive_url" }

View File

@ -4,17 +4,18 @@ use crate::{
}; };
use reqwest::StatusCode; use reqwest::StatusCode;
pub use reqwest::Url; pub use reqwest::Url;
use sensitive_url::SensitiveUrl;
use types::{Domain, Fork, Hash256}; use types::{Domain, Fork, Hash256};
/// A wrapper around `reqwest::Client` which provides convenience methods /// A wrapper around `reqwest::Client` which provides convenience methods
/// to interface with a BLS Remote Signer. /// to interface with a BLS Remote Signer.
pub struct RemoteSignerHttpConsumer { pub struct RemoteSignerHttpConsumer {
client: reqwest::Client, client: reqwest::Client,
server: Url, server: SensitiveUrl,
} }
impl RemoteSignerHttpConsumer { impl RemoteSignerHttpConsumer {
pub fn from_components(server: Url, client: reqwest::Client) -> Self { pub fn from_components(server: SensitiveUrl, client: reqwest::Client) -> Self {
Self { client, server } Self { client, server }
} }
@ -43,7 +44,7 @@ impl RemoteSignerHttpConsumer {
)); ));
} }
let mut path = self.server.clone(); let mut path = self.server.full.clone();
path.path_segments_mut() path.path_segments_mut()
.map_err(|()| Error::InvalidUrl(self.server.clone()))? .map_err(|()| Error::InvalidUrl(self.server.clone()))?
.push("sign") .push("sign")

View File

@ -20,10 +20,11 @@
//! //!
//! ``` //! ```
//! use remote_signer_consumer::RemoteSignerHttpConsumer; //! use remote_signer_consumer::RemoteSignerHttpConsumer;
//! use reqwest::{ClientBuilder, Url}; //! use reqwest::ClientBuilder;
//! use sensitive_url::SensitiveUrl;
//! use tokio::time::Duration; //! use tokio::time::Duration;
//! //!
//! let url: Url = "http://127.0.0.1:9000".parse().unwrap(); //! let url = SensitiveUrl::parse("http://127.0.0.1:9000").unwrap();
//! let reqwest_client = ClientBuilder::new() //! let reqwest_client = ClientBuilder::new()
//! .timeout(Duration::from_secs(2)) //! .timeout(Duration::from_secs(2))
//! .build() //! .build()
@ -115,6 +116,7 @@ mod http_client;
pub use http_client::RemoteSignerHttpConsumer; pub use http_client::RemoteSignerHttpConsumer;
pub use reqwest::Url; pub use reqwest::Url;
use sensitive_url::SensitiveUrl;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use types::{AttestationData, BeaconBlock, Domain, Epoch, EthSpec, Fork, Hash256, SignedRoot}; use types::{AttestationData, BeaconBlock, Domain, Epoch, EthSpec, Fork, Hash256, SignedRoot};
@ -125,7 +127,7 @@ pub enum Error {
/// The server returned an error message where the body was able to be parsed. /// The server returned an error message where the body was able to be parsed.
ServerMessage(String), ServerMessage(String),
/// The supplied URL is badly formatted. It should look something like `http://127.0.0.1:5052`. /// The supplied URL is badly formatted. It should look something like `http://127.0.0.1:5052`.
InvalidUrl(Url), InvalidUrl(SensitiveUrl),
/// The supplied parameter is invalid. /// The supplied parameter is invalid.
InvalidParameter(String), InvalidParameter(String),
} }

View File

@ -1,7 +1,8 @@
mod post { mod post {
use remote_signer_consumer::{Error, RemoteSignerHttpConsumer}; use remote_signer_consumer::{Error, RemoteSignerHttpConsumer};
use remote_signer_test::*; use remote_signer_test::*;
use reqwest::{ClientBuilder, Url}; use reqwest::ClientBuilder;
use sensitive_url::SensitiveUrl;
use tokio::time::Duration; use tokio::time::Duration;
#[test] #[test]
@ -53,7 +54,7 @@ mod post {
let (test_signer, _tmp_dir) = set_up_api_test_signer_to_sign_message(); let (test_signer, _tmp_dir) = set_up_api_test_signer_to_sign_message();
let run_testcase = |u: &str| -> Result<String, String> { let run_testcase = |u: &str| -> Result<String, String> {
let url: Url = u.parse().map_err(|e| format!("[ParseError] {:?}", e))?; let url = SensitiveUrl::parse(u).map_err(|e| format!("{:?}", e))?;
let reqwest_client = ClientBuilder::new() let reqwest_client = ClientBuilder::new()
.timeout(Duration::from_secs(12)) .timeout(Duration::from_secs(12))
@ -66,7 +67,7 @@ mod post {
let signature = do_sign_request(&test_client, test_input); let signature = do_sign_request(&test_client, test_input);
signature.map_err(|e| match e { signature.map_err(|e| match e {
Error::InvalidUrl(message) => format!("[InvalidUrl] {:?}", message), Error::InvalidUrl(message) => format!("{:?}", message),
Error::Reqwest(re) => { Error::Reqwest(re) => {
if re.is_builder() { if re.is_builder() {
format!("[Reqwest - Builder] {:?}", re.url().unwrap()) format!("[Reqwest - Builder] {:?}", re.url().unwrap())
@ -84,25 +85,22 @@ mod post {
// url::parser::ParseError. // url::parser::ParseError.
// These cases don't even make it to the step of building a RemoteSignerHttpConsumer. // These cases don't even make it to the step of building a RemoteSignerHttpConsumer.
testcase("", "[ParseError] RelativeUrlWithoutBase"); testcase("", "ParseError(RelativeUrlWithoutBase)");
testcase("/4/8/15/16/23/42", "[ParseError] RelativeUrlWithoutBase"); testcase("/4/8/15/16/23/42", "ParseError(RelativeUrlWithoutBase)");
testcase("localhost", "[ParseError] RelativeUrlWithoutBase"); testcase("localhost", "ParseError(RelativeUrlWithoutBase)");
testcase(":", "[ParseError] RelativeUrlWithoutBase"); testcase(":", "ParseError(RelativeUrlWithoutBase)");
testcase("0.0:0", "[ParseError] RelativeUrlWithoutBase"); testcase("0.0:0", "ParseError(RelativeUrlWithoutBase)");
testcase(":aa", "[ParseError] RelativeUrlWithoutBase"); testcase(":aa", "ParseError(RelativeUrlWithoutBase)");
testcase("0:", "[ParseError] RelativeUrlWithoutBase"); testcase("0:", "ParseError(RelativeUrlWithoutBase)");
testcase("ftp://", "[ParseError] EmptyHost"); testcase("ftp://", "ParseError(EmptyHost)");
testcase("http://", "[ParseError] EmptyHost"); testcase("http://", "ParseError(EmptyHost)");
testcase("http://127.0.0.1:abcd", "[ParseError] InvalidPort"); testcase("http://127.0.0.1:abcd", "ParseError(InvalidPort)");
testcase("http://280.0.0.1", "[ParseError] InvalidIpv4Address"); testcase("http://280.0.0.1", "ParseError(InvalidIpv4Address)");
// `Error::InvalidUrl`. // `Error::InvalidUrl`.
// The RemoteSignerHttpConsumer is created, but fails at `path_segments_mut()`. // The RemoteSignerHttpConsumer is created, but fails at `path_segments_mut()`.
testcase( testcase("localhost:abcd", "InvalidUrl(\"URL cannot be a base.\")");
"localhost:abcd", testcase("localhost:", "InvalidUrl(\"URL cannot be a base.\")");
"[InvalidUrl] Url { scheme: \"localhost\", username: \"\", password: None, host: None, port: None, path: \"abcd\", query: None, fragment: None }",
);
testcase("localhost:", "[InvalidUrl] Url { scheme: \"localhost\", username: \"\", password: None, host: None, port: None, path: \"\", query: None, fragment: None }");
// `Reqwest::Error` of the `Builder` kind. // `Reqwest::Error` of the `Builder` kind.
// POST is not made. // POST is not made.
@ -130,7 +128,7 @@ mod post {
let (test_signer, _tmp_dir) = set_up_api_test_signer_to_sign_message(); let (test_signer, _tmp_dir) = set_up_api_test_signer_to_sign_message();
let run_testcase = |u: &str| -> Result<String, String> { let run_testcase = |u: &str| -> Result<String, String> {
let url: Url = u.parse().unwrap(); let url = SensitiveUrl::parse(u).unwrap();
let reqwest_client = ClientBuilder::new() let reqwest_client = ClientBuilder::new()
.timeout(Duration::from_secs(12)) .timeout(Duration::from_secs(12))

View File

@ -0,0 +1,11 @@
[package]
name = "sensitive_url"
version = "0.1.0"
authors = ["Mac L <mjladson@pm.me>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
url = "2.2.1"
serde = "1.0.116"

View File

@ -0,0 +1,105 @@
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
use std::fmt;
use url::Url;
#[derive(Debug)]
pub enum SensitiveError {
InvalidUrl(String),
ParseError(url::ParseError),
RedactError(String),
}
// Wrapper around Url which provides a custom `Display` implementation to protect user secrets.
#[derive(Clone)]
pub struct SensitiveUrl {
pub full: Url,
pub redacted: String,
}
impl fmt::Display for SensitiveUrl {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.redacted.fmt(f)
}
}
impl fmt::Debug for SensitiveUrl {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.redacted.fmt(f)
}
}
impl AsRef<str> for SensitiveUrl {
fn as_ref(&self) -> &str {
self.redacted.as_str()
}
}
impl Serialize for SensitiveUrl {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(&self.full.to_string())
}
}
impl<'de> Deserialize<'de> for SensitiveUrl {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let s: String = Deserialize::deserialize(deserializer)?;
SensitiveUrl::parse(&s)
.map_err(|e| de::Error::custom(format!("Failed to deserialize sensitive URL {:?}", e)))
}
}
impl SensitiveUrl {
pub fn parse(url: &str) -> Result<Self, SensitiveError> {
let surl = Url::parse(url).map_err(SensitiveError::ParseError)?;
SensitiveUrl::new(surl)
}
fn new(full: Url) -> Result<Self, SensitiveError> {
let mut redacted = full.clone();
redacted
.path_segments_mut()
.map_err(|_| SensitiveError::InvalidUrl("URL cannot be a base.".to_string()))?
.clear();
redacted.set_query(None);
if redacted.has_authority() {
redacted.set_username("").map_err(|_| {
SensitiveError::RedactError("Unable to redact username.".to_string())
})?;
redacted.set_password(None).map_err(|_| {
SensitiveError::RedactError("Unable to redact password.".to_string())
})?;
}
Ok(Self {
full,
redacted: redacted.to_string(),
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn redact_remote_url() {
let full = "https://project:secret@example.com/example?somequery";
let surl = SensitiveUrl::parse(full).unwrap();
assert_eq!(surl.to_string(), "https://example.com/");
assert_eq!(surl.full.to_string(), full);
}
#[test]
fn redact_localhost_url() {
let full = "http://localhost:5052/";
let surl = SensitiveUrl::parse(full).unwrap();
assert_eq!(surl.to_string(), "http://localhost:5052/");
assert_eq!(surl.full.to_string(), full);
}
}

View File

@ -39,3 +39,4 @@ account_utils = { path = "../common/account_utils" }
eth2_wallet = { path = "../crypto/eth2_wallet" } eth2_wallet = { path = "../crypto/eth2_wallet" }
web3 = "0.14.0" web3 = "0.14.0"
eth1_test_rig = { path = "../testing/eth1_test_rig" } eth1_test_rig = { path = "../testing/eth1_test_rig" }
sensitive_url = { path = "../common/sensitive_url" }

View File

@ -2,6 +2,7 @@ use clap::ArgMatches;
use environment::Environment; use environment::Environment;
use eth2_network_config::Eth2NetworkConfig; use eth2_network_config::Eth2NetworkConfig;
use genesis::{Eth1Config, Eth1GenesisService}; use genesis::{Eth1Config, Eth1GenesisService};
use sensitive_url::SensitiveUrl;
use ssz::Encode; use ssz::Encode;
use std::cmp::max; use std::cmp::max;
use std::path::PathBuf; use std::path::PathBuf;
@ -50,7 +51,11 @@ pub fn run<T: EthSpec>(mut env: Environment<T>, matches: &ArgMatches<'_>) -> Res
let mut config = Eth1Config::default(); let mut config = Eth1Config::default();
if let Some(v) = endpoints.clone() { if let Some(v) = endpoints.clone() {
config.endpoints = v; config.endpoints = v
.iter()
.map(|s| SensitiveUrl::parse(s))
.collect::<Result<_, _>>()
.map_err(|e| format!("Unable to parse eth1 endpoint URL: {:?}", e))?;
} }
config.deposit_contract_address = format!("{:?}", spec.deposit_contract_address); config.deposit_contract_address = format!("{:?}", spec.deposit_contract_address);
config.deposit_contract_deploy_block = eth2_network_config.deposit_contract_deploy_block; config.deposit_contract_deploy_block = eth2_network_config.deposit_contract_deploy_block;

View File

@ -18,3 +18,4 @@ genesis = { path = "../../beacon_node/genesis" }
eth2 = { path = "../../common/eth2" } eth2 = { path = "../../common/eth2" }
validator_client = { path = "../../validator_client" } validator_client = { path = "../../validator_client" }
validator_dir = { path = "../../common/validator_dir", features = ["insecure_keys"] } validator_dir = { path = "../../common/validator_dir", features = ["insecure_keys"] }
sensitive_url = { path = "../../common/sensitive_url" }

View File

@ -4,10 +4,8 @@
use beacon_node::ProductionBeaconNode; use beacon_node::ProductionBeaconNode;
use environment::RuntimeContext; use environment::RuntimeContext;
use eth2::{ use eth2::{reqwest::ClientBuilder, BeaconNodeHttpClient};
reqwest::{ClientBuilder, Url}, use sensitive_url::SensitiveUrl;
BeaconNodeHttpClient,
};
use std::path::PathBuf; use std::path::PathBuf;
use std::time::Duration; use std::time::Duration;
use std::time::{SystemTime, UNIX_EPOCH}; use std::time::{SystemTime, UNIX_EPOCH};
@ -68,9 +66,10 @@ impl<E: EthSpec> LocalBeaconNode<E> {
.http_api_listen_addr() .http_api_listen_addr()
.ok_or("A remote beacon node must have a http server")?; .ok_or("A remote beacon node must have a http server")?;
let beacon_node_url: Url = format!("http://{}:{}", listen_addr.ip(), listen_addr.port()) let beacon_node_url: SensitiveUrl = SensitiveUrl::parse(
.parse() format!("http://{}:{}", listen_addr.ip(), listen_addr.port()).as_str(),
.map_err(|e| format!("Unable to parse beacon node URL: {:?}", e))?; )
.map_err(|e| format!("Unable to parse beacon node URL: {:?}", e))?;
let beacon_node_http_client = ClientBuilder::new() let beacon_node_http_client = ClientBuilder::new()
.timeout(HTTP_TIMEOUT) .timeout(HTTP_TIMEOUT)
.build() .build()

View File

@ -17,3 +17,4 @@ serde_json = "1.0.58"
tempfile = "3.1.0" tempfile = "3.1.0"
tokio = { version = "1.1.0", features = ["time"] } tokio = { version = "1.1.0", features = ["time"] }
types = { path = "../../consensus/types" } types = { path = "../../consensus/types" }
sensitive_url = { path = "../../common/sensitive_url" }

View File

@ -1,7 +1,8 @@
use crate::*; use crate::*;
use remote_signer_client::api_response::SignatureApiResponse; use remote_signer_client::api_response::SignatureApiResponse;
use remote_signer_consumer::{Error, RemoteSignerHttpConsumer, RemoteSignerObject, Url}; use remote_signer_consumer::{Error, RemoteSignerHttpConsumer, RemoteSignerObject};
use reqwest::ClientBuilder; use reqwest::ClientBuilder;
use sensitive_url::SensitiveUrl;
use serde::Serialize; use serde::Serialize;
use tokio::runtime::Builder; use tokio::runtime::Builder;
use tokio::time::Duration; use tokio::time::Duration;
@ -15,7 +16,7 @@ pub fn set_up_test_consumer_with_timeout(
test_signer_address: &str, test_signer_address: &str,
timeout: u64, timeout: u64,
) -> RemoteSignerHttpConsumer { ) -> RemoteSignerHttpConsumer {
let url: Url = test_signer_address.parse().unwrap(); let url = SensitiveUrl::parse(test_signer_address).unwrap();
let reqwest_client = ClientBuilder::new() let reqwest_client = ClientBuilder::new()
.timeout(Duration::from_secs(timeout)) .timeout(Duration::from_secs(timeout))
.build() .build()

View File

@ -18,3 +18,4 @@ eth1_test_rig = { path = "../eth1_test_rig" }
env_logger = "0.8.2" env_logger = "0.8.2"
clap = "2.33.3" clap = "2.33.3"
rayon = "1.4.1" rayon = "1.4.1"
sensitive_url = { path = "../../common/sensitive_url" }

View File

@ -10,6 +10,7 @@ use node_test_rig::{
ClientGenesis, ValidatorFiles, ClientGenesis, ValidatorFiles,
}; };
use rayon::prelude::*; use rayon::prelude::*;
use sensitive_url::SensitiveUrl;
use std::cmp::max; use std::cmp::max;
use std::net::{IpAddr, Ipv4Addr}; use std::net::{IpAddr, Ipv4Addr};
use std::time::Duration; use std::time::Duration;
@ -84,7 +85,8 @@ pub fn run_eth1_sim(matches: &ArgMatches) -> Result<(), String> {
let network_id = ganache_eth1_instance.ganache.network_id(); let network_id = ganache_eth1_instance.ganache.network_id();
let chain_id = ganache_eth1_instance.ganache.chain_id(); let chain_id = ganache_eth1_instance.ganache.chain_id();
let ganache = ganache_eth1_instance.ganache; let ganache = ganache_eth1_instance.ganache;
let eth1_endpoint = ganache.endpoint(); let eth1_endpoint = SensitiveUrl::parse(ganache.endpoint().as_str())
.expect("Unable to parse ganache endpoint.");
let deposit_contract_address = deposit_contract.address(); let deposit_contract_address = deposit_contract.address();
// Start a timer that produces eth1 blocks on an interval. // Start a timer that produces eth1 blocks on an interval.
@ -133,7 +135,10 @@ pub fn run_eth1_sim(matches: &ArgMatches) -> Result<(), String> {
for i in 0..node_count - 1 { for i in 0..node_count - 1 {
let mut config = beacon_config.clone(); let mut config = beacon_config.clone();
if i % 2 == 0 { if i % 2 == 0 {
config.eth1.endpoints.insert(0, INVALID_ADDRESS.to_string()); config.eth1.endpoints.insert(
0,
SensitiveUrl::parse(INVALID_ADDRESS).expect("Unable to parse invalid address"),
);
} }
network.add_beacon_node(config).await?; network.add_beacon_node(config).await?;
} }

View File

@ -4,6 +4,7 @@ use node_test_rig::{
ClientConfig, LocalBeaconNode, LocalValidatorClient, ValidatorConfig, ValidatorFiles, ClientConfig, LocalBeaconNode, LocalValidatorClient, ValidatorConfig, ValidatorFiles,
}; };
use parking_lot::RwLock; use parking_lot::RwLock;
use sensitive_url::SensitiveUrl;
use std::{ use std::{
ops::Deref, ops::Deref,
time::{SystemTime, UNIX_EPOCH}, time::{SystemTime, UNIX_EPOCH},
@ -140,9 +141,12 @@ impl<E: EthSpec> LocalNetwork<E> {
.expect("Must have http started") .expect("Must have http started")
}; };
let beacon_node = format!("http://{}:{}", socket_addr.ip(), socket_addr.port()); let beacon_node = SensitiveUrl::parse(
format!("http://{}:{}", socket_addr.ip(), socket_addr.port()).as_str(),
)
.unwrap();
validator_config.beacon_nodes = if invalid_first_beacon_node { validator_config.beacon_nodes = if invalid_first_beacon_node {
vec![INVALID_ADDRESS.to_string(), beacon_node] vec![SensitiveUrl::parse(INVALID_ADDRESS).unwrap(), beacon_node]
} else { } else {
vec![beacon_node] vec![beacon_node]
}; };

View File

@ -63,3 +63,4 @@ scrypt = { version = "0.5.0", default-features = false }
lighthouse_metrics = { path = "../common/lighthouse_metrics" } lighthouse_metrics = { path = "../common/lighthouse_metrics" }
lazy_static = "1.4.0" lazy_static = "1.4.0"
fallback = { path = "../common/fallback" } fallback = { path = "../common/fallback" }
sensitive_url = { path = "../common/sensitive_url" }

View File

@ -7,6 +7,7 @@ use directory::{
DEFAULT_VALIDATOR_DIR, DEFAULT_VALIDATOR_DIR,
}; };
use eth2::types::Graffiti; use eth2::types::Graffiti;
use sensitive_url::SensitiveUrl;
use serde_derive::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
use slog::{info, warn, Logger}; use slog::{info, warn, Logger};
use std::fs; use std::fs;
@ -26,7 +27,7 @@ pub struct Config {
/// The http endpoints of the beacon node APIs. /// The http endpoints of the beacon node APIs.
/// ///
/// Should be similar to `["http://localhost:8080"]` /// Should be similar to `["http://localhost:8080"]`
pub beacon_nodes: Vec<String>, pub beacon_nodes: Vec<SensitiveUrl>,
/// If true, the validator client will still poll for duties and produce blocks even if the /// If true, the validator client will still poll for duties and produce blocks even if the
/// beacon node is not synced at startup. /// beacon node is not synced at startup.
pub allow_unsynced_beacon_node: bool, pub allow_unsynced_beacon_node: bool,
@ -55,10 +56,13 @@ impl Default for Config {
.join(DEFAULT_HARDCODED_NETWORK); .join(DEFAULT_HARDCODED_NETWORK);
let validator_dir = base_dir.join(DEFAULT_VALIDATOR_DIR); let validator_dir = base_dir.join(DEFAULT_VALIDATOR_DIR);
let secrets_dir = base_dir.join(DEFAULT_SECRET_DIR); let secrets_dir = base_dir.join(DEFAULT_SECRET_DIR);
let beacon_nodes = vec![SensitiveUrl::parse(DEFAULT_BEACON_NODE)
.expect("beacon_nodes must always be a valid url.")];
Self { Self {
validator_dir, validator_dir,
secrets_dir, secrets_dir,
beacon_nodes: vec![DEFAULT_BEACON_NODE.to_string()], beacon_nodes,
allow_unsynced_beacon_node: false, allow_unsynced_beacon_node: false,
disable_auto_discover: false, disable_auto_discover: false,
init_slashing_protection: false, init_slashing_protection: false,
@ -111,25 +115,31 @@ impl Config {
} }
if let Some(beacon_nodes) = parse_optional::<String>(cli_args, "beacon-nodes")? { if let Some(beacon_nodes) = parse_optional::<String>(cli_args, "beacon-nodes")? {
config.beacon_nodes = beacon_nodes.as_str().split(',').map(String::from).collect() config.beacon_nodes = beacon_nodes
.split(',')
.map(|s| SensitiveUrl::parse(s))
.collect::<Result<_, _>>()
.map_err(|e| format!("Unable to parse beacon node URL: {:?}", e))?;
} }
// To be deprecated. // To be deprecated.
else if let Some(beacon_node) = parse_optional(cli_args, "beacon-node")? { else if let Some(beacon_node) = parse_optional::<String>(cli_args, "beacon-node")? {
warn!( warn!(
log, log,
"The --beacon-node flag is deprecated"; "The --beacon-node flag is deprecated";
"msg" => "please use --beacon-nodes instead" "msg" => "please use --beacon-nodes instead"
); );
config.beacon_nodes = vec![beacon_node]; config.beacon_nodes = vec![SensitiveUrl::parse(&beacon_node)
.map_err(|e| format!("Unable to parse beacon node URL: {:?}", e))?];
} }
// To be deprecated. // To be deprecated.
else if let Some(server) = parse_optional(cli_args, "server")? { else if let Some(server) = parse_optional::<String>(cli_args, "server")? {
warn!( warn!(
log, log,
"The --server flag is deprecated"; "The --server flag is deprecated";
"msg" => "please use --beacon-nodes instead" "msg" => "please use --beacon-nodes instead"
); );
config.beacon_nodes = vec![server]; config.beacon_nodes = vec![SensitiveUrl::parse(&server)
.map_err(|e| format!("Unable to parse beacon node URL: {:?}", e))?];
} }
if cli_args.is_present("delete-lockfiles") { if cli_args.is_present("delete-lockfiles") {
@ -227,3 +237,14 @@ impl Config {
Ok(config) Ok(config)
} }
} }
#[cfg(test)]
mod tests {
use super::*;
#[test]
// Ensures the default config does not panic.
fn default_config() {
Config::default();
}
}

View File

@ -84,7 +84,7 @@ impl<E: EthSpec> ForkServiceBuilder<slot_clock::TestingSlotClock, E> {
std::time::Duration::from_secs(42), std::time::Duration::from_secs(42),
); );
let candidates = vec![CandidateBeaconNode::new(eth2::BeaconNodeHttpClient::new( let candidates = vec![CandidateBeaconNode::new(eth2::BeaconNodeHttpClient::new(
eth2::Url::parse("http://127.0.0.1").unwrap(), sensitive_url::SensitiveUrl::parse("http://127.0.0.1").unwrap(),
))]; ))];
let mut beacon_nodes = BeaconNodeFallback::new(candidates, spec, log.clone()); let mut beacon_nodes = BeaconNodeFallback::new(candidates, spec, log.clone());
beacon_nodes.set_slot_clock(slot_clock); beacon_nodes.set_slot_clock(slot_clock);

View File

@ -11,12 +11,10 @@ use account_utils::{
}; };
use deposit_contract::decode_eth1_tx_data; use deposit_contract::decode_eth1_tx_data;
use environment::null_logger; use environment::null_logger;
use eth2::{ use eth2::lighthouse_vc::{http_client::ValidatorClientHttpClient, types::*};
lighthouse_vc::{http_client::ValidatorClientHttpClient, types::*},
Url,
};
use eth2_keystore::KeystoreBuilder; use eth2_keystore::KeystoreBuilder;
use parking_lot::RwLock; use parking_lot::RwLock;
use sensitive_url::SensitiveUrl;
use slashing_protection::{SlashingDatabase, SLASHING_PROTECTION_FILENAME}; use slashing_protection::{SlashingDatabase, SLASHING_PROTECTION_FILENAME};
use slot_clock::TestingSlotClock; use slot_clock::TestingSlotClock;
use std::marker::PhantomData; use std::marker::PhantomData;
@ -33,7 +31,7 @@ type E = MainnetEthSpec;
struct ApiTester { struct ApiTester {
client: ValidatorClientHttpClient, client: ValidatorClientHttpClient,
initialized_validators: Arc<RwLock<InitializedValidators>>, initialized_validators: Arc<RwLock<InitializedValidators>>,
url: Url, url: SensitiveUrl,
_server_shutdown: oneshot::Sender<()>, _server_shutdown: oneshot::Sender<()>,
_validator_dir: TempDir, _validator_dir: TempDir,
} }
@ -117,7 +115,7 @@ impl ApiTester {
tokio::spawn(async { server.await }); tokio::spawn(async { server.await });
let url = Url::parse(&format!( let url = SensitiveUrl::parse(&format!(
"http://{}:{}", "http://{}:{}",
listening_socket.ip(), listening_socket.ip(),
listening_socket.port() listening_socket.port()

View File

@ -28,7 +28,7 @@ use clap::ArgMatches;
use duties_service::DutiesService; use duties_service::DutiesService;
use environment::RuntimeContext; use environment::RuntimeContext;
use eth2::types::StateId; use eth2::types::StateId;
use eth2::{reqwest::ClientBuilder, BeaconNodeHttpClient, StatusCode, Url}; use eth2::{reqwest::ClientBuilder, BeaconNodeHttpClient, StatusCode};
use fork_service::{ForkService, ForkServiceBuilder}; use fork_service::{ForkService, ForkServiceBuilder};
use http_api::ApiSecret; use http_api::ApiSecret;
use initialized_validators::InitializedValidators; use initialized_validators::InitializedValidators;
@ -209,13 +209,9 @@ impl<T: EthSpec> ProductionValidatorClient<T> {
})?; })?;
} }
let beacon_node_urls: Vec<Url> = config let beacon_nodes: Vec<BeaconNodeHttpClient> = config
.beacon_nodes .beacon_nodes
.iter() .clone()
.map(|s| s.parse())
.collect::<Result<_, _>>()
.map_err(|e| format!("Unable to parse beacon node URL: {:?}", e))?;
let beacon_nodes: Vec<BeaconNodeHttpClient> = beacon_node_urls
.into_iter() .into_iter()
.map(|url| { .map(|url| {
let beacon_node_http_client = ClientBuilder::new() let beacon_node_http_client = ClientBuilder::new()