diff --git a/Cargo.lock b/Cargo.lock index 10793dfb9..0c98ea3e9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4782,6 +4782,7 @@ version = "0.2.0" dependencies = [ "clap", "env_logger", + "eth1", "eth1_test_rig", "futures 0.3.5", "node_test_rig", diff --git a/beacon_node/client/src/builder.rs b/beacon_node/client/src/builder.rs index d9edbe1d2..c77ddecef 100644 --- a/beacon_node/client/src/builder.rs +++ b/beacon_node/client/src/builder.rs @@ -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 { + pub async fn caching_eth1_backend(mut self, config: Eth1Config) -> Result { let context = self .runtime_context .as_ref() @@ -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)?; diff --git a/beacon_node/eth1/src/http.rs b/beacon_node/eth1/src/http.rs index c8b5961a7..6dffdaa7c 100644 --- a/beacon_node/eth1/src/http.rs +++ b/beacon_node/eth1/src/http.rs @@ -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; @@ -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 { + 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 { + 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, diff --git a/beacon_node/eth1/src/service.rs b/beacon_node/eth1/src/service.rs index bf3c1940a..b7149560b 100644 --- a/beacon_node/eth1/src/service.rs +++ b/beacon_node/eth1/src/service.rs @@ -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, }; @@ -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. @@ -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 @@ -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, @@ -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!( diff --git a/beacon_node/src/lib.rs b/beacon_node/src/lib.rs index cc143c442..199319160 100644 --- a/beacon_node/src/lib.rs +++ b/beacon_node/src/lib.rs @@ -100,7 +100,9 @@ impl ProductionBeaconNode { "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, diff --git a/testing/eth1_test_rig/src/ganache.rs b/testing/eth1_test_rig/src/ganache.rs index faaae0ef3..9452d524f 100644 --- a/testing/eth1_test_rig/src/ganache.rs +++ b/testing/eth1_test_rig/src/ganache.rs @@ -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`. @@ -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!( @@ -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 diff --git a/testing/simulator/Cargo.toml b/testing/simulator/Cargo.toml index 07e1e3448..6670e3557 100644 --- a/testing/simulator/Cargo.toml +++ b/testing/simulator/Cargo.toml @@ -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" diff --git a/testing/simulator/src/eth1_sim.rs b/testing/simulator/src/eth1_sim.rs index edb56d901..75f2256a1 100644 --- a/testing/simulator/src/eth1_sim.rs +++ b/testing/simulator/src/eth1_sim.rs @@ -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::{ @@ -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(); @@ -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)));