Skip to content

Commit

Permalink
Eth1 network exit on wrong network id (#1563)
Browse files Browse the repository at this point in the history
## Issue Addressed

Fixes #1509 

## Proposed Changes

Exit the beacon node if the eth1 endpoint points to an invalid eth1 network. Check the network id before every eth1 cache update and display an error log if the network id has changed to an invalid one.
  • Loading branch information
pawanjay176 committed Aug 31, 2020
1 parent c18d37c commit adea799
Show file tree
Hide file tree
Showing 8 changed files with 97 additions and 3 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 12 additions & 1 deletion beacon_node/client/src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -584,7 +584,7 @@ where
/// Specifies that the `BeaconChain` should cache eth1 blocks/logs from a remote eth1 node
/// (e.g., Parity/Geth) and refer to that cache when collecting deposits or eth1 votes during
/// block production.
pub fn caching_eth1_backend(mut self, config: Eth1Config) -> Result<Self, String> {
pub async fn caching_eth1_backend(mut self, config: Eth1Config) -> Result<Self, String> {
let context = self
.runtime_context
.as_ref()
Expand All @@ -598,6 +598,17 @@ where
.clone()
.ok_or_else(|| "caching_eth1_backend requires a chain spec".to_string())?;

// Check if the eth1 endpoint we connect to is on the correct network id.
let network_id =
eth1::http::get_network_id(&config.endpoint, Duration::from_millis(15_000)).await?;

if network_id != config.network_id {
return Err(format!(
"Invalid eth1 network id. Expected {:?}, got {:?}",
config.network_id, network_id
));
}

let backend = if let Some(eth1_service_from_genesis) = self.eth1_service {
eth1_service_from_genesis.update_config(config)?;

Expand Down
36 changes: 36 additions & 0 deletions beacon_node/eth1/src/http.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@
use futures::future::TryFutureExt;
use reqwest::{header::CONTENT_TYPE, ClientBuilder, StatusCode};
use serde::{Deserialize, Serialize};
use serde_json::{json, Value};
use std::ops::Range;
use std::str::FromStr;
use std::time::Duration;
use types::Hash256;

Expand All @@ -30,6 +32,40 @@ pub const DEPOSIT_COUNT_RESPONSE_BYTES: usize = 96;
/// Number of bytes in deposit contract deposit root (value only).
pub const DEPOSIT_ROOT_BYTES: usize = 32;

#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
pub enum Eth1NetworkId {
Goerli,
Mainnet,
Custom(u64),
}

impl FromStr for Eth1NetworkId {
type Err = String;

fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"1" => Ok(Eth1NetworkId::Mainnet),
"5" => Ok(Eth1NetworkId::Goerli),
custom => {
let network_id = u64::from_str_radix(custom, 10)
.map_err(|e| format!("Failed to parse eth1 network id {}", e))?;
Ok(Eth1NetworkId::Custom(network_id))
}
}
}
}

/// Get the eth1 network id of the given endpoint.
pub async fn get_network_id(endpoint: &str, timeout: Duration) -> Result<Eth1NetworkId, String> {
let response_body = send_rpc_request(endpoint, "net_version", json!([]), timeout).await?;
Eth1NetworkId::from_str(
response_result(&response_body)?
.ok_or_else(|| "No result was returned for block number".to_string())?
.as_str()
.ok_or_else(|| "Data was not string")?,
)
}

#[derive(Debug, PartialEq, Clone)]
pub struct Block {
pub hash: Hash256,
Expand Down
33 changes: 32 additions & 1 deletion beacon_node/eth1/src/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ use crate::metrics;
use crate::{
block_cache::{BlockCache, Error as BlockCacheError, Eth1Block},
deposit_cache::Error as DepositCacheError,
http::{get_block, get_block_number, get_deposit_logs_in_range, Log},
http::{
get_block, get_block_number, get_deposit_logs_in_range, get_network_id, Eth1NetworkId, Log,
},
inner::{DepositUpdater, Inner},
DepositLog,
};
Expand All @@ -16,6 +18,9 @@ use std::time::{SystemTime, UNIX_EPOCH};
use tokio::time::{interval_at, Duration, Instant};
use types::ChainSpec;

/// Indicates the default eth1 network we use for the deposit contract.
pub const DEFAULT_NETWORK_ID: Eth1NetworkId = Eth1NetworkId::Goerli;

const STANDARD_TIMEOUT_MILLIS: u64 = 15_000;

/// Timeout when doing a eth_blockNumber call.
Expand Down Expand Up @@ -76,6 +81,8 @@ pub struct Config {
pub endpoint: String,
/// The address the `BlockCache` and `DepositCache` should assume is the canonical deposit contract.
pub deposit_contract_address: String,
/// The eth1 network id where the deposit contract is deployed (Goerli/Mainnet).
pub network_id: Eth1NetworkId,
/// Defines the first block that the `DepositCache` will start searching for deposit logs.
///
/// Setting too high can result in missed logs. Setting too low will result in unnecessary
Expand Down Expand Up @@ -105,6 +112,7 @@ impl Default for Config {
Self {
endpoint: "http://localhost:8545".into(),
deposit_contract_address: "0x0000000000000000000000000000000000000000".into(),
network_id: DEFAULT_NETWORK_ID,
deposit_contract_deploy_block: 1,
lowest_cached_block_number: 1,
follow_distance: 128,
Expand Down Expand Up @@ -350,6 +358,29 @@ impl Service {
}

async fn do_update(&self, update_interval: Duration) -> Result<(), ()> {
let endpoint = self.config().endpoint.clone();
let config_network = self.config().network_id.clone();
let result =
get_network_id(&endpoint, Duration::from_millis(STANDARD_TIMEOUT_MILLIS)).await;
match result {
Ok(network_id) => {
if network_id != config_network {
error!(
self.log,
"Failed to update eth1 cache";
"reason" => "Invalid eth1 network id",
"expected" => format!("{:?}",DEFAULT_NETWORK_ID),
"got" => format!("{:?}",network_id),
);
return Ok(());
}
}
Err(e) => {
error!(self.log, "Failed to get eth1 network id"; "error" => e);
return Ok(());
}
}

let update_result = self.update().await;
match update_result {
Err(e) => error!(
Expand Down
4 changes: 3 additions & 1 deletion beacon_node/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,9 @@ impl<E: EthSpec> ProductionBeaconNode<E> {
"endpoint" => &client_config.eth1.endpoint,
"method" => "json rpc via http"
);
builder.caching_eth1_backend(client_config.eth1.clone())?
builder
.caching_eth1_backend(client_config.eth1.clone())
.await?
} else if client_config.dummy_eth1_backend {
warn!(
log,
Expand Down
9 changes: 9 additions & 0 deletions testing/eth1_test_rig/src/ganache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ use web3::{
/// How long we will wait for ganache to indicate that it is ready.
const GANACHE_STARTUP_TIMEOUT_MILLIS: u64 = 10_000;

const NETWORK_ID: u64 = 42;

/// Provides a dedicated `ganachi-cli` instance with a connected `Web3` instance.
///
/// Requires that `ganachi-cli` is installed and available on `PATH`.
Expand Down Expand Up @@ -42,6 +44,8 @@ impl GanacheInstance {
.arg(format!("{}", port))
.arg("--mnemonic")
.arg("\"vast thought differ pull jewel broom cook wrist tribe word before omit\"")
.arg("--networkId")
.arg(format!("{}", NETWORK_ID))
.spawn()
.map_err(|e| {
format!(
Expand Down Expand Up @@ -97,6 +101,11 @@ impl GanacheInstance {
endpoint(self.port)
}

/// Returns the network id of the ganache instance
pub fn network_id(&self) -> u64 {
NETWORK_ID
}

/// Increase the timestamp on future blocks by `increase_by` seconds.
pub async fn increase_time(&self, increase_by: u64) -> Result<(), String> {
self.web3
Expand Down
1 change: 1 addition & 0 deletions testing/simulator/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ edition = "2018"

[dependencies]
node_test_rig = { path = "../node_test_rig" }
eth1 = {path = "../../beacon_node/eth1"}
types = { path = "../../consensus/types" }
validator_client = { path = "../../validator_client" }
parking_lot = "0.11.0"
Expand Down
3 changes: 3 additions & 0 deletions testing/simulator/src/eth1_sim.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use crate::{checks, LocalNetwork, E};
use clap::ArgMatches;
use eth1::http::Eth1NetworkId;
use eth1_test_rig::GanacheEth1Instance;
use futures::prelude::*;
use node_test_rig::{
Expand Down Expand Up @@ -73,6 +74,7 @@ pub fn run_eth1_sim(matches: &ArgMatches) -> Result<(), String> {
*/
let ganache_eth1_instance = GanacheEth1Instance::new().await?;
let deposit_contract = ganache_eth1_instance.deposit_contract;
let network_id = ganache_eth1_instance.ganache.network_id();
let ganache = ganache_eth1_instance.ganache;
let eth1_endpoint = ganache.endpoint();
let deposit_contract_address = deposit_contract.address();
Expand Down Expand Up @@ -105,6 +107,7 @@ pub fn run_eth1_sim(matches: &ArgMatches) -> Result<(), String> {
beacon_config.eth1.follow_distance = 1;
beacon_config.dummy_eth1_backend = false;
beacon_config.sync_eth1_chain = true;
beacon_config.eth1.network_id = Eth1NetworkId::Custom(network_id);

beacon_config.network.enr_address = Some(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)));

Expand Down

0 comments on commit adea799

Please sign in to comment.