mirror of
https://github.com/sigp/lighthouse.git
synced 2026-04-18 21:38:31 +00:00
Merge remote-tracking branch 'origin/unstable' into tree-states
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
TESTS_TAG := v1.1.9
|
||||
TESTS_TAG := v1.1.10
|
||||
TESTS = general minimal mainnet
|
||||
TARBALLS = $(patsubst %,%-$(TESTS_TAG).tar.gz,$(TESTS))
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ pub fn log_file_access<P: AsRef<Path>>(file_accessed: P) {
|
||||
let passed_test_list_path =
|
||||
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(ACCESSED_FILE_LOG_FILENAME);
|
||||
|
||||
let mut file = fs::OpenOptions::new()
|
||||
let mut file = fs::File::options()
|
||||
.append(true)
|
||||
.create(true)
|
||||
.open(passed_test_list_path)
|
||||
|
||||
@@ -36,14 +36,14 @@ impl GanacheInstance {
|
||||
loop {
|
||||
if start + Duration::from_millis(GANACHE_STARTUP_TIMEOUT_MILLIS) <= Instant::now() {
|
||||
break Err(
|
||||
"Timed out waiting for ganache to start. Is ganache-cli installed?".to_string(),
|
||||
"Timed out waiting for ganache to start. Is ganache installed?".to_string(),
|
||||
);
|
||||
}
|
||||
|
||||
let mut line = String::new();
|
||||
if let Err(e) = reader.read_line(&mut line) {
|
||||
break Err(format!("Failed to read line from ganache process: {:?}", e));
|
||||
} else if line.starts_with("Listening on") {
|
||||
} else if line.starts_with("RPC Listening on") {
|
||||
break Ok(());
|
||||
} else {
|
||||
continue;
|
||||
@@ -69,13 +69,13 @@ impl GanacheInstance {
|
||||
})
|
||||
}
|
||||
|
||||
/// Start a new `ganache-cli` process, waiting until it indicates that it is ready to accept
|
||||
/// Start a new `ganache` process, waiting until it indicates that it is ready to accept
|
||||
/// RPC connections.
|
||||
pub fn new(network_id: u64, chain_id: u64) -> Result<Self, String> {
|
||||
let port = unused_tcp_port()?;
|
||||
let binary = match cfg!(windows) {
|
||||
true => "ganache-cli.cmd",
|
||||
false => "ganache-cli",
|
||||
true => "ganache.cmd",
|
||||
false => "ganache",
|
||||
};
|
||||
let child = Command::new(binary)
|
||||
.stdout(Stdio::piped())
|
||||
@@ -85,15 +85,13 @@ impl GanacheInstance {
|
||||
.arg("1000000000")
|
||||
.arg("--accounts")
|
||||
.arg("10")
|
||||
.arg("--keepAliveTimeout")
|
||||
.arg("0")
|
||||
.arg("--port")
|
||||
.arg(format!("{}", port))
|
||||
.arg("--mnemonic")
|
||||
.arg("\"vast thought differ pull jewel broom cook wrist tribe word before omit\"")
|
||||
.arg("--networkId")
|
||||
.arg(format!("{}", network_id))
|
||||
.arg("--chainId")
|
||||
.arg("--chain.chainId")
|
||||
.arg(format!("{}", chain_id))
|
||||
.spawn()
|
||||
.map_err(|e| {
|
||||
@@ -110,8 +108,8 @@ impl GanacheInstance {
|
||||
pub fn fork(&self) -> Result<Self, String> {
|
||||
let port = unused_tcp_port()?;
|
||||
let binary = match cfg!(windows) {
|
||||
true => "ganache-cli.cmd",
|
||||
false => "ganache-cli",
|
||||
true => "ganache.cmd",
|
||||
false => "ganache",
|
||||
};
|
||||
let child = Command::new(binary)
|
||||
.stdout(Stdio::piped())
|
||||
@@ -119,9 +117,7 @@ impl GanacheInstance {
|
||||
.arg(self.endpoint())
|
||||
.arg("--port")
|
||||
.arg(format!("{}", port))
|
||||
.arg("--keepAliveTimeout")
|
||||
.arg("0")
|
||||
.arg("--chainId")
|
||||
.arg("--chain.chainId")
|
||||
.arg(format!("{}", self.chain_id))
|
||||
.spawn()
|
||||
.map_err(|e| {
|
||||
@@ -178,8 +174,7 @@ impl GanacheInstance {
|
||||
.await
|
||||
.map(|_| ())
|
||||
.map_err(|_| {
|
||||
"utils should mine new block with evm_mine (only works with ganache-cli!)"
|
||||
.to_string()
|
||||
"utils should mine new block with evm_mine (only works with ganache!)".to_string()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//! Provides utilities for deploying and manipulating the eth2 deposit contract on the eth1 chain.
|
||||
//!
|
||||
//! Presently used with [`ganache-cli`](https://github.com/trufflesuite/ganache-cli) to simulate
|
||||
//! Presently used with [`ganache`](https://github.com/trufflesuite/ganache) to simulate
|
||||
//! the deposit contract for testing beacon node eth1 integration.
|
||||
//!
|
||||
//! Not tested to work with actual clients (e.g., geth). It should work fine, however there may be
|
||||
@@ -23,7 +23,7 @@ use web3::Web3;
|
||||
pub const DEPLOYER_ACCOUNTS_INDEX: usize = 0;
|
||||
pub const DEPOSIT_ACCOUNTS_INDEX: usize = 0;
|
||||
|
||||
/// Provides a dedicated ganache-cli instance with the deposit contract already deployed.
|
||||
/// Provides a dedicated ganache instance with the deposit contract already deployed.
|
||||
pub struct GanacheEth1Instance {
|
||||
pub ganache: GanacheInstance,
|
||||
pub deposit_contract: DepositContract,
|
||||
|
||||
@@ -3,8 +3,6 @@ name = "execution_engine_integration"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
build = "build.rs"
|
||||
|
||||
[dependencies]
|
||||
tempfile = "3.1.0"
|
||||
serde_json = "1.0.58"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
test:
|
||||
cargo test --release --locked
|
||||
cargo run --release --locked
|
||||
|
||||
clean:
|
||||
rm -rf execution_clients
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
use std::env;
|
||||
use std::fs;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::Command;
|
||||
|
||||
const GETH_BRANCH: &str = "merge-kiln";
|
||||
const GETH_REPO_URL: &str = "https://github.com/MariusVanDerWijden/go-ethereum";
|
||||
|
||||
fn main() {
|
||||
let manifest_dir: PathBuf = env::var("CARGO_MANIFEST_DIR").unwrap().into();
|
||||
let execution_clients_dir = manifest_dir.join("execution_clients");
|
||||
|
||||
if !execution_clients_dir.exists() {
|
||||
fs::create_dir(&execution_clients_dir).unwrap();
|
||||
}
|
||||
|
||||
build_geth(&execution_clients_dir);
|
||||
}
|
||||
|
||||
fn build_geth(execution_clients_dir: &Path) {
|
||||
let repo_dir = execution_clients_dir.join("go-ethereum");
|
||||
|
||||
if !repo_dir.exists() {
|
||||
// Clone the repo
|
||||
assert!(Command::new("git")
|
||||
.arg("clone")
|
||||
.arg(GETH_REPO_URL)
|
||||
.current_dir(&execution_clients_dir)
|
||||
.output()
|
||||
.expect("failed to clone geth repo")
|
||||
.status
|
||||
.success());
|
||||
}
|
||||
|
||||
// Checkout the correct branch
|
||||
assert!(Command::new("git")
|
||||
.arg("checkout")
|
||||
.arg(GETH_BRANCH)
|
||||
.current_dir(&repo_dir)
|
||||
.output()
|
||||
.expect("failed to checkout geth branch")
|
||||
.status
|
||||
.success());
|
||||
|
||||
// Update the branch
|
||||
assert!(Command::new("git")
|
||||
.arg("pull")
|
||||
.current_dir(&repo_dir)
|
||||
.output()
|
||||
.expect("failed to update geth branch")
|
||||
.status
|
||||
.success());
|
||||
|
||||
// Build geth
|
||||
assert!(Command::new("make")
|
||||
.arg("geth")
|
||||
.current_dir(&repo_dir)
|
||||
.output()
|
||||
.expect("failed to make geth")
|
||||
.status
|
||||
.success());
|
||||
}
|
||||
73
testing/execution_engine_integration/src/build_utils.rs
Normal file
73
testing/execution_engine_integration/src/build_utils.rs
Normal file
@@ -0,0 +1,73 @@
|
||||
use crate::SUPPRESS_LOGS;
|
||||
use std::env;
|
||||
use std::fs;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::{Command, Output, Stdio};
|
||||
|
||||
pub fn prepare_dir() -> PathBuf {
|
||||
let manifest_dir: PathBuf = env::var("CARGO_MANIFEST_DIR").unwrap().into();
|
||||
let execution_clients_dir = manifest_dir.join("execution_clients");
|
||||
|
||||
if !execution_clients_dir.exists() {
|
||||
fs::create_dir(&execution_clients_dir).unwrap();
|
||||
}
|
||||
|
||||
execution_clients_dir
|
||||
}
|
||||
|
||||
pub fn clone_repo(repo_dir: &Path, repo_url: &str) -> bool {
|
||||
Command::new("git")
|
||||
.arg("clone")
|
||||
.arg(repo_url)
|
||||
.arg("--recursive")
|
||||
.current_dir(repo_dir)
|
||||
.output()
|
||||
.unwrap_or_else(|_| panic!("failed to clone repo at {}", repo_url))
|
||||
.status
|
||||
.success()
|
||||
}
|
||||
|
||||
pub fn checkout_branch(repo_dir: &Path, branch_name: &str) -> bool {
|
||||
Command::new("git")
|
||||
.arg("checkout")
|
||||
.arg(branch_name)
|
||||
.current_dir(repo_dir)
|
||||
.output()
|
||||
.unwrap_or_else(|_| {
|
||||
panic!(
|
||||
"failed to checkout branch at {:?}/{}",
|
||||
repo_dir, branch_name,
|
||||
)
|
||||
})
|
||||
.status
|
||||
.success()
|
||||
}
|
||||
|
||||
pub fn update_branch(repo_dir: &Path, branch_name: &str) -> bool {
|
||||
Command::new("git")
|
||||
.arg("pull")
|
||||
.current_dir(repo_dir)
|
||||
.output()
|
||||
.unwrap_or_else(|_| panic!("failed to update branch at {:?}/{}", repo_dir, branch_name))
|
||||
.status
|
||||
.success()
|
||||
}
|
||||
|
||||
pub fn check_command_output(output: Output, failure_msg: &'static str) {
|
||||
if !output.status.success() {
|
||||
if !SUPPRESS_LOGS {
|
||||
dbg!(String::from_utf8_lossy(&output.stdout));
|
||||
dbg!(String::from_utf8_lossy(&output.stderr));
|
||||
}
|
||||
panic!("{}", failure_msg);
|
||||
}
|
||||
}
|
||||
|
||||
/// Builds the stdout/stderr handler for commands which might output to the terminal.
|
||||
pub fn build_stdio() -> Stdio {
|
||||
if SUPPRESS_LOGS {
|
||||
Stdio::null()
|
||||
} else {
|
||||
Stdio::inherit()
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,19 @@
|
||||
use crate::{genesis_json::geth_genesis_json, SUPPRESS_LOGS};
|
||||
use execution_layer::DEFAULT_JWT_FILE;
|
||||
use sensitive_url::SensitiveUrl;
|
||||
use std::path::PathBuf;
|
||||
use std::process::{Child, Command, Output, Stdio};
|
||||
use std::{env, fs::File};
|
||||
use std::process::Child;
|
||||
use tempfile::TempDir;
|
||||
use unused_port::unused_tcp_port;
|
||||
|
||||
/// Defined for each EE type (e.g., Geth, Nethermind, etc).
|
||||
pub trait GenericExecutionEngine: Clone {
|
||||
fn init_datadir() -> TempDir;
|
||||
fn start_client(datadir: &TempDir, http_port: u16) -> Child;
|
||||
fn start_client(
|
||||
datadir: &TempDir,
|
||||
http_port: u16,
|
||||
http_auth_port: u16,
|
||||
jwt_secret_path: PathBuf,
|
||||
) -> Child;
|
||||
}
|
||||
|
||||
/// Holds handle to a running EE process, plus some other metadata.
|
||||
@@ -19,6 +23,7 @@ pub struct ExecutionEngine<E> {
|
||||
#[allow(dead_code)]
|
||||
datadir: TempDir,
|
||||
http_port: u16,
|
||||
http_auth_port: u16,
|
||||
child: Child,
|
||||
}
|
||||
|
||||
@@ -34,12 +39,15 @@ impl<E> Drop for ExecutionEngine<E> {
|
||||
impl<E: GenericExecutionEngine> ExecutionEngine<E> {
|
||||
pub fn new(engine: E) -> Self {
|
||||
let datadir = E::init_datadir();
|
||||
let jwt_secret_path = datadir.path().join(DEFAULT_JWT_FILE);
|
||||
let http_port = unused_tcp_port().unwrap();
|
||||
let child = E::start_client(&datadir, http_port);
|
||||
let http_auth_port = unused_tcp_port().unwrap();
|
||||
let child = E::start_client(&datadir, http_port, http_auth_port, jwt_secret_path);
|
||||
Self {
|
||||
engine,
|
||||
datadir,
|
||||
http_port,
|
||||
http_auth_port,
|
||||
child,
|
||||
}
|
||||
}
|
||||
@@ -47,85 +55,12 @@ impl<E: GenericExecutionEngine> ExecutionEngine<E> {
|
||||
pub fn http_url(&self) -> SensitiveUrl {
|
||||
SensitiveUrl::parse(&format!("http://127.0.0.1:{}", self.http_port)).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Geth-specific Implementation
|
||||
*/
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Geth;
|
||||
|
||||
impl Geth {
|
||||
fn binary_path() -> PathBuf {
|
||||
let manifest_dir: PathBuf = env::var("CARGO_MANIFEST_DIR").unwrap().into();
|
||||
manifest_dir
|
||||
.join("execution_clients")
|
||||
.join("go-ethereum")
|
||||
.join("build")
|
||||
.join("bin")
|
||||
.join("geth")
|
||||
}
|
||||
}
|
||||
|
||||
impl GenericExecutionEngine for Geth {
|
||||
fn init_datadir() -> TempDir {
|
||||
let datadir = TempDir::new().unwrap();
|
||||
|
||||
let genesis_json_path = datadir.path().join("genesis.json");
|
||||
let mut file = File::create(&genesis_json_path).unwrap();
|
||||
let json = geth_genesis_json();
|
||||
serde_json::to_writer(&mut file, &json).unwrap();
|
||||
|
||||
let output = Command::new(Self::binary_path())
|
||||
.arg("--datadir")
|
||||
.arg(datadir.path().to_str().unwrap())
|
||||
.arg("init")
|
||||
.arg(genesis_json_path.to_str().unwrap())
|
||||
.output()
|
||||
.expect("failed to init geth");
|
||||
|
||||
check_command_output(output, "geth init failed");
|
||||
|
||||
datadir
|
||||
pub fn http_auth_url(&self) -> SensitiveUrl {
|
||||
SensitiveUrl::parse(&format!("http://127.0.0.1:{}", self.http_auth_port)).unwrap()
|
||||
}
|
||||
|
||||
fn start_client(datadir: &TempDir, http_port: u16) -> Child {
|
||||
let network_port = unused_tcp_port().unwrap();
|
||||
|
||||
Command::new(Self::binary_path())
|
||||
.arg("--datadir")
|
||||
.arg(datadir.path().to_str().unwrap())
|
||||
.arg("--http")
|
||||
.arg("--http.api")
|
||||
.arg("engine,eth")
|
||||
.arg("--http.port")
|
||||
.arg(http_port.to_string())
|
||||
.arg("--port")
|
||||
.arg(network_port.to_string())
|
||||
.stdout(build_stdio())
|
||||
.stderr(build_stdio())
|
||||
.spawn()
|
||||
.expect("failed to start beacon node")
|
||||
}
|
||||
}
|
||||
|
||||
fn check_command_output(output: Output, failure_msg: &'static str) {
|
||||
if !output.status.success() {
|
||||
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||
|
||||
dbg!(stdout);
|
||||
dbg!(stderr);
|
||||
panic!("{}", failure_msg);
|
||||
}
|
||||
}
|
||||
|
||||
/// Builds the stdout/stderr handler for commands which might output to the terminal.
|
||||
fn build_stdio() -> Stdio {
|
||||
if SUPPRESS_LOGS {
|
||||
Stdio::null()
|
||||
} else {
|
||||
Stdio::inherit()
|
||||
pub fn datadir(&self) -> PathBuf {
|
||||
self.datadir.path().to_path_buf()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,3 +40,77 @@ pub fn geth_genesis_json() -> Value {
|
||||
"baseFeePerGas":"0x7"
|
||||
})
|
||||
}
|
||||
|
||||
/// Sourced from:
|
||||
///
|
||||
/// https://github.com/NethermindEth/nethermind/blob/themerge_kintsugi/src/Nethermind/Chains/themerge_kintsugi_m2.json
|
||||
pub fn nethermind_genesis_json() -> Value {
|
||||
json!({
|
||||
"name": "TheMerge_Devnet",
|
||||
"engine": {
|
||||
"clique": {
|
||||
"params": {
|
||||
"period": 5,
|
||||
"epoch": 30000
|
||||
}
|
||||
}
|
||||
},
|
||||
"params": {
|
||||
"gasLimitBoundDivisor": "0x400",
|
||||
"accountStartNonce": "0x0",
|
||||
"maximumExtraDataSize": "0x20",
|
||||
"minGasLimit": "0x1388",
|
||||
"networkID": 1,
|
||||
"eip150Transition": "0x0",
|
||||
"eip155Transition": "0x0",
|
||||
"eip158Transition": "0x0",
|
||||
"eip160Transition": "0x0",
|
||||
"eip161abcTransition": "0x0",
|
||||
"eip161dTransition": "0x0",
|
||||
"eip140Transition": "0x0",
|
||||
"eip211Transition": "0x0",
|
||||
"eip214Transition": "0x0",
|
||||
"eip658Transition": "0x0",
|
||||
"eip145Transition": "0x0",
|
||||
"eip1014Transition": "0x0",
|
||||
"eip1052Transition": "0x0",
|
||||
"eip1283Transition": "0x0",
|
||||
"eip1283DisableTransition": "0x0",
|
||||
"eip152Transition": "0x0",
|
||||
"eip1108Transition": "0x0",
|
||||
"eip1344Transition": "0x0",
|
||||
"eip1884Transition": "0x0",
|
||||
"eip2028Transition": "0x0",
|
||||
"eip2200Transition": "0x0",
|
||||
"eip2565Transition": "0x0",
|
||||
"eip2929Transition": "0x0",
|
||||
"eip2930Transition": "0x0",
|
||||
"eip1559Transition": "0x0",
|
||||
"eip3198Transition": "0x0",
|
||||
"eip3529Transition": "0x0",
|
||||
"eip3541Transition": "0x0"
|
||||
},
|
||||
"genesis": {
|
||||
"seal": {
|
||||
"ethereum": {
|
||||
"nonce": "0x42",
|
||||
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000"
|
||||
}
|
||||
},
|
||||
"difficulty": "0x000000000",
|
||||
"author": "0x0000000000000000000000000000000000000000",
|
||||
"timestamp": "0x0",
|
||||
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"extraData":"0x0000000000000000000000000000000000000000000000000000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"gasLimit":"0x1C9C380",
|
||||
"author": "0x0000000000000000000000000000000000000000",
|
||||
"parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"baseFeePerGas":"0x7"
|
||||
},
|
||||
"accounts": {
|
||||
"0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b": {
|
||||
"balance":"0x6d6172697573766477000000"
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
110
testing/execution_engine_integration/src/geth.rs
Normal file
110
testing/execution_engine_integration/src/geth.rs
Normal file
@@ -0,0 +1,110 @@
|
||||
use crate::build_utils;
|
||||
use crate::execution_engine::GenericExecutionEngine;
|
||||
use crate::genesis_json::geth_genesis_json;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::{Child, Command, Output};
|
||||
use std::{env, fs::File};
|
||||
use tempfile::TempDir;
|
||||
use unused_port::unused_tcp_port;
|
||||
|
||||
const GETH_BRANCH: &str = "merge-kiln-v2";
|
||||
const GETH_REPO_URL: &str = "https://github.com/MariusVanDerWijden/go-ethereum";
|
||||
|
||||
pub fn build_result(repo_dir: &Path) -> Output {
|
||||
Command::new("make")
|
||||
.arg("geth")
|
||||
.current_dir(&repo_dir)
|
||||
.output()
|
||||
.expect("failed to make geth")
|
||||
}
|
||||
|
||||
pub fn build(execution_clients_dir: &Path) {
|
||||
let repo_dir = execution_clients_dir.join("go-ethereum");
|
||||
|
||||
if !repo_dir.exists() {
|
||||
// Clone the repo
|
||||
assert!(build_utils::clone_repo(
|
||||
execution_clients_dir,
|
||||
GETH_REPO_URL
|
||||
));
|
||||
}
|
||||
|
||||
// Checkout the correct branch
|
||||
assert!(build_utils::checkout_branch(&repo_dir, GETH_BRANCH));
|
||||
|
||||
// Update the branch
|
||||
assert!(build_utils::update_branch(&repo_dir, GETH_BRANCH));
|
||||
|
||||
// Build geth
|
||||
build_utils::check_command_output(build_result(&repo_dir), "make failed");
|
||||
}
|
||||
|
||||
/*
|
||||
* Geth-specific Implementation for GenericExecutionEngine
|
||||
*/
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct GethEngine;
|
||||
|
||||
impl GethEngine {
|
||||
fn binary_path() -> PathBuf {
|
||||
let manifest_dir: PathBuf = env::var("CARGO_MANIFEST_DIR").unwrap().into();
|
||||
manifest_dir
|
||||
.join("execution_clients")
|
||||
.join("go-ethereum")
|
||||
.join("build")
|
||||
.join("bin")
|
||||
.join("geth")
|
||||
}
|
||||
}
|
||||
|
||||
impl GenericExecutionEngine for GethEngine {
|
||||
fn init_datadir() -> TempDir {
|
||||
let datadir = TempDir::new().unwrap();
|
||||
|
||||
let genesis_json_path = datadir.path().join("genesis.json");
|
||||
let mut file = File::create(&genesis_json_path).unwrap();
|
||||
let json = geth_genesis_json();
|
||||
serde_json::to_writer(&mut file, &json).unwrap();
|
||||
|
||||
let output = Command::new(Self::binary_path())
|
||||
.arg("--datadir")
|
||||
.arg(datadir.path().to_str().unwrap())
|
||||
.arg("init")
|
||||
.arg(genesis_json_path.to_str().unwrap())
|
||||
.output()
|
||||
.expect("failed to init geth");
|
||||
|
||||
build_utils::check_command_output(output, "geth init failed");
|
||||
|
||||
datadir
|
||||
}
|
||||
|
||||
fn start_client(
|
||||
datadir: &TempDir,
|
||||
http_port: u16,
|
||||
http_auth_port: u16,
|
||||
jwt_secret_path: PathBuf,
|
||||
) -> Child {
|
||||
let network_port = unused_tcp_port().unwrap();
|
||||
|
||||
Command::new(Self::binary_path())
|
||||
.arg("--datadir")
|
||||
.arg(datadir.path().to_str().unwrap())
|
||||
.arg("--http")
|
||||
.arg("--http.api")
|
||||
.arg("engine,eth")
|
||||
.arg("--http.port")
|
||||
.arg(http_port.to_string())
|
||||
.arg("--authrpc.port")
|
||||
.arg(http_auth_port.to_string())
|
||||
.arg("--port")
|
||||
.arg(network_port.to_string())
|
||||
.arg("--authrpc.jwtsecret")
|
||||
.arg(jwt_secret_path.as_path().to_str().unwrap())
|
||||
.stdout(build_utils::build_stdio())
|
||||
.stderr(build_utils::build_stdio())
|
||||
.spawn()
|
||||
.expect("failed to start geth")
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
/// This library provides integration testing between Lighthouse and other execution engines.
|
||||
///
|
||||
/// See the `tests/tests.rs` file to run tests.
|
||||
mod execution_engine;
|
||||
mod genesis_json;
|
||||
mod test_rig;
|
||||
|
||||
pub use execution_engine::Geth;
|
||||
pub use test_rig::TestRig;
|
||||
|
||||
/// Set to `false` to send logs to the console during tests. Logs are useful when debugging.
|
||||
const SUPPRESS_LOGS: bool = true;
|
||||
39
testing/execution_engine_integration/src/main.rs
Normal file
39
testing/execution_engine_integration/src/main.rs
Normal file
@@ -0,0 +1,39 @@
|
||||
/// This binary runs integration tests between Lighthouse and execution engines.
|
||||
///
|
||||
/// It will first attempt to build any supported integration clients, then it will run tests.
|
||||
///
|
||||
/// A return code of `0` indicates the tests succeeded.
|
||||
mod build_utils;
|
||||
mod execution_engine;
|
||||
mod genesis_json;
|
||||
mod geth;
|
||||
mod nethermind;
|
||||
mod test_rig;
|
||||
|
||||
use geth::GethEngine;
|
||||
use nethermind::NethermindEngine;
|
||||
use test_rig::TestRig;
|
||||
|
||||
/// Set to `false` to send logs to the console during tests. Logs are useful when debugging.
|
||||
const SUPPRESS_LOGS: bool = true;
|
||||
|
||||
fn main() {
|
||||
if cfg!(windows) {
|
||||
panic!("windows is not supported, only linux");
|
||||
}
|
||||
|
||||
test_geth();
|
||||
test_nethermind();
|
||||
}
|
||||
|
||||
fn test_geth() {
|
||||
let test_dir = build_utils::prepare_dir();
|
||||
geth::build(&test_dir);
|
||||
TestRig::new(GethEngine).perform_tests_blocking();
|
||||
}
|
||||
|
||||
fn test_nethermind() {
|
||||
let test_dir = build_utils::prepare_dir();
|
||||
nethermind::build(&test_dir);
|
||||
TestRig::new(NethermindEngine).perform_tests_blocking();
|
||||
}
|
||||
117
testing/execution_engine_integration/src/nethermind.rs
Normal file
117
testing/execution_engine_integration/src/nethermind.rs
Normal file
@@ -0,0 +1,117 @@
|
||||
use crate::build_utils;
|
||||
use crate::execution_engine::GenericExecutionEngine;
|
||||
use crate::genesis_json::nethermind_genesis_json;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::{Child, Command, Output};
|
||||
use std::{env, fs::File};
|
||||
use tempfile::TempDir;
|
||||
use unused_port::unused_tcp_port;
|
||||
|
||||
const NETHERMIND_BRANCH: &str = "kiln";
|
||||
const NETHERMIND_REPO_URL: &str = "https://github.com/NethermindEth/nethermind";
|
||||
|
||||
fn build_result(repo_dir: &Path) -> Output {
|
||||
Command::new("dotnet")
|
||||
.arg("build")
|
||||
.arg("src/Nethermind/Nethermind.sln")
|
||||
.arg("-c")
|
||||
.arg("Release")
|
||||
.current_dir(repo_dir)
|
||||
.output()
|
||||
.expect("failed to make nethermind")
|
||||
}
|
||||
|
||||
pub fn build(execution_clients_dir: &Path) {
|
||||
let repo_dir = execution_clients_dir.join("nethermind");
|
||||
|
||||
if !repo_dir.exists() {
|
||||
// Clone the repo
|
||||
assert!(build_utils::clone_repo(
|
||||
execution_clients_dir,
|
||||
NETHERMIND_REPO_URL
|
||||
));
|
||||
}
|
||||
|
||||
// Checkout the correct branch
|
||||
assert!(build_utils::checkout_branch(&repo_dir, NETHERMIND_BRANCH));
|
||||
|
||||
// Update the branch
|
||||
assert!(build_utils::update_branch(&repo_dir, NETHERMIND_BRANCH));
|
||||
|
||||
// Build nethermind
|
||||
build_utils::check_command_output(build_result(&repo_dir), "dotnet build failed");
|
||||
|
||||
// Build nethermind a second time to enable Merge-related features.
|
||||
// Not sure why this is necessary.
|
||||
build_utils::check_command_output(build_result(&repo_dir), "dotnet build failed");
|
||||
}
|
||||
|
||||
/*
|
||||
* Nethermind-specific Implementation for GenericExecutionEngine
|
||||
*/
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct NethermindEngine;
|
||||
|
||||
impl NethermindEngine {
|
||||
fn binary_path() -> PathBuf {
|
||||
let manifest_dir: PathBuf = env::var("CARGO_MANIFEST_DIR").unwrap().into();
|
||||
manifest_dir
|
||||
.join("execution_clients")
|
||||
.join("nethermind")
|
||||
.join("src")
|
||||
.join("Nethermind")
|
||||
.join("Nethermind.Runner")
|
||||
.join("bin")
|
||||
.join("Release")
|
||||
.join("net6.0")
|
||||
.join("Nethermind.Runner")
|
||||
}
|
||||
}
|
||||
|
||||
impl GenericExecutionEngine for NethermindEngine {
|
||||
fn init_datadir() -> TempDir {
|
||||
let datadir = TempDir::new().unwrap();
|
||||
|
||||
let genesis_json_path = datadir.path().join("genesis.json");
|
||||
let mut file = File::create(&genesis_json_path).unwrap();
|
||||
let json = nethermind_genesis_json();
|
||||
serde_json::to_writer(&mut file, &json).unwrap();
|
||||
|
||||
datadir
|
||||
}
|
||||
|
||||
fn start_client(
|
||||
datadir: &TempDir,
|
||||
http_port: u16,
|
||||
http_auth_port: u16,
|
||||
jwt_secret_path: PathBuf,
|
||||
) -> Child {
|
||||
let network_port = unused_tcp_port().unwrap();
|
||||
let genesis_json_path = datadir.path().join("genesis.json");
|
||||
|
||||
Command::new(Self::binary_path())
|
||||
.arg("--datadir")
|
||||
.arg(datadir.path().to_str().unwrap())
|
||||
.arg("--config")
|
||||
.arg("themerge_kiln_testvectors")
|
||||
.arg("--Init.ChainSpecPath")
|
||||
.arg(genesis_json_path.to_str().unwrap())
|
||||
.arg("--JsonRpc.AdditionalRpcUrls")
|
||||
.arg(format!("http://localhost:{}|http;ws|net;eth;subscribe;engine;web3;client|no-auth,http://localhost:{}|http;ws|net;eth;subscribe;engine;web3;client", http_port, http_auth_port))
|
||||
.arg("--JsonRpc.EnabledModules")
|
||||
.arg("net,eth,subscribe,web3,admin,engine")
|
||||
.arg("--JsonRpc.Port")
|
||||
.arg(http_port.to_string())
|
||||
.arg("--Network.DiscoveryPort")
|
||||
.arg(network_port.to_string())
|
||||
.arg("--Network.P2PPort")
|
||||
.arg(network_port.to_string())
|
||||
.arg("--JsonRpc.JwtSecretFile")
|
||||
.arg(jwt_secret_path.as_path().to_str().unwrap())
|
||||
.stdout(build_utils::build_stdio())
|
||||
.stderr(build_utils::build_stdio())
|
||||
.spawn()
|
||||
.expect("failed to start nethermind")
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,9 @@ use std::sync::Arc;
|
||||
use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
|
||||
use task_executor::TaskExecutor;
|
||||
use tokio::time::sleep;
|
||||
use types::{Address, ChainSpec, EthSpec, ExecutionBlockHash, Hash256, MainnetEthSpec, Uint256};
|
||||
use types::{
|
||||
Address, ChainSpec, EthSpec, ExecutionBlockHash, Hash256, MainnetEthSpec, Slot, Uint256,
|
||||
};
|
||||
|
||||
const EXECUTION_ENGINE_START_TIMEOUT: Duration = Duration::from_secs(10);
|
||||
|
||||
@@ -46,10 +48,17 @@ impl<E: GenericExecutionEngine> TestRig<E> {
|
||||
|
||||
let ee_a = {
|
||||
let execution_engine = ExecutionEngine::new(generic_engine.clone());
|
||||
let urls = vec![execution_engine.http_url()];
|
||||
let urls = vec![execution_engine.http_auth_url()];
|
||||
|
||||
let config = execution_layer::Config {
|
||||
execution_endpoints: urls,
|
||||
secret_files: vec![],
|
||||
suggested_fee_recipient: Some(Address::repeat_byte(42)),
|
||||
default_datadir: execution_engine.datadir(),
|
||||
..Default::default()
|
||||
};
|
||||
let execution_layer =
|
||||
ExecutionLayer::from_urls(urls, fee_recipient, executor.clone(), log.clone())
|
||||
.unwrap();
|
||||
ExecutionLayer::from_config(config, executor.clone(), log.clone()).unwrap();
|
||||
ExecutionPair {
|
||||
execution_engine,
|
||||
execution_layer,
|
||||
@@ -59,8 +68,16 @@ impl<E: GenericExecutionEngine> TestRig<E> {
|
||||
let ee_b = {
|
||||
let execution_engine = ExecutionEngine::new(generic_engine);
|
||||
let urls = vec![execution_engine.http_url()];
|
||||
|
||||
let config = execution_layer::Config {
|
||||
execution_endpoints: urls,
|
||||
secret_files: vec![],
|
||||
suggested_fee_recipient: fee_recipient,
|
||||
default_datadir: execution_engine.datadir(),
|
||||
..Default::default()
|
||||
};
|
||||
let execution_layer =
|
||||
ExecutionLayer::from_urls(urls, fee_recipient, executor, log).unwrap();
|
||||
ExecutionLayer::from_config(config, executor, log.clone()).unwrap();
|
||||
ExecutionPair {
|
||||
execution_engine,
|
||||
execution_layer,
|
||||
@@ -108,6 +125,16 @@ impl<E: GenericExecutionEngine> TestRig<E> {
|
||||
pub async fn perform_tests(&self) {
|
||||
self.wait_until_synced().await;
|
||||
|
||||
/*
|
||||
* Check the transition config endpoint.
|
||||
*/
|
||||
for ee in [&self.ee_a, &self.ee_b] {
|
||||
ee.execution_layer
|
||||
.exchange_transition_configuration(&self.spec)
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
/*
|
||||
* Read the terminal block hash from both pairs, check it's equal.
|
||||
*/
|
||||
@@ -138,7 +165,7 @@ impl<E: GenericExecutionEngine> TestRig<E> {
|
||||
|
||||
let parent_hash = terminal_pow_block_hash;
|
||||
let timestamp = timestamp_now();
|
||||
let random = Hash256::zero();
|
||||
let prev_randao = Hash256::zero();
|
||||
let finalized_block_hash = ExecutionBlockHash::zero();
|
||||
let proposer_index = 0;
|
||||
let valid_payload = self
|
||||
@@ -147,7 +174,7 @@ impl<E: GenericExecutionEngine> TestRig<E> {
|
||||
.get_payload::<MainnetEthSpec>(
|
||||
parent_hash,
|
||||
timestamp,
|
||||
random,
|
||||
prev_randao,
|
||||
finalized_block_hash,
|
||||
proposer_index,
|
||||
)
|
||||
@@ -162,11 +189,12 @@ impl<E: GenericExecutionEngine> TestRig<E> {
|
||||
*/
|
||||
let head_block_hash = valid_payload.block_hash;
|
||||
let finalized_block_hash = ExecutionBlockHash::zero();
|
||||
let payload_attributes = None;
|
||||
let slot = Slot::new(42);
|
||||
let head_block_root = Hash256::repeat_byte(42);
|
||||
let status = self
|
||||
.ee_a
|
||||
.execution_layer
|
||||
.notify_forkchoice_updated(head_block_hash, finalized_block_hash, payload_attributes)
|
||||
.notify_forkchoice_updated(head_block_hash, finalized_block_hash, slot, head_block_root)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(status, PayloadStatus::Syncing);
|
||||
@@ -194,11 +222,12 @@ impl<E: GenericExecutionEngine> TestRig<E> {
|
||||
*/
|
||||
let head_block_hash = valid_payload.block_hash;
|
||||
let finalized_block_hash = ExecutionBlockHash::zero();
|
||||
let payload_attributes = None;
|
||||
let slot = Slot::new(42);
|
||||
let head_block_root = Hash256::repeat_byte(42);
|
||||
let status = self
|
||||
.ee_a
|
||||
.execution_layer
|
||||
.notify_forkchoice_updated(head_block_hash, finalized_block_hash, payload_attributes)
|
||||
.notify_forkchoice_updated(head_block_hash, finalized_block_hash, slot, head_block_root)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(status, PayloadStatus::Valid);
|
||||
@@ -210,7 +239,7 @@ impl<E: GenericExecutionEngine> TestRig<E> {
|
||||
*/
|
||||
|
||||
let mut invalid_payload = valid_payload.clone();
|
||||
invalid_payload.random = Hash256::from_low_u64_be(42);
|
||||
invalid_payload.prev_randao = Hash256::from_low_u64_be(42);
|
||||
let status = self
|
||||
.ee_a
|
||||
.execution_layer
|
||||
@@ -227,7 +256,7 @@ impl<E: GenericExecutionEngine> TestRig<E> {
|
||||
|
||||
let parent_hash = valid_payload.block_hash;
|
||||
let timestamp = valid_payload.timestamp + 1;
|
||||
let random = Hash256::zero();
|
||||
let prev_randao = Hash256::zero();
|
||||
let finalized_block_hash = ExecutionBlockHash::zero();
|
||||
let proposer_index = 0;
|
||||
let second_payload = self
|
||||
@@ -236,7 +265,7 @@ impl<E: GenericExecutionEngine> TestRig<E> {
|
||||
.get_payload::<MainnetEthSpec>(
|
||||
parent_hash,
|
||||
timestamp,
|
||||
random,
|
||||
prev_randao,
|
||||
finalized_block_hash,
|
||||
proposer_index,
|
||||
)
|
||||
@@ -264,15 +293,22 @@ impl<E: GenericExecutionEngine> TestRig<E> {
|
||||
*/
|
||||
let head_block_hash = valid_payload.block_hash;
|
||||
let finalized_block_hash = ExecutionBlockHash::zero();
|
||||
let payload_attributes = Some(PayloadAttributes {
|
||||
let payload_attributes = PayloadAttributes {
|
||||
timestamp: second_payload.timestamp + 1,
|
||||
random: Hash256::zero(),
|
||||
prev_randao: Hash256::zero(),
|
||||
suggested_fee_recipient: Address::zero(),
|
||||
});
|
||||
};
|
||||
let slot = Slot::new(42);
|
||||
let head_block_root = Hash256::repeat_byte(100);
|
||||
let validator_index = 0;
|
||||
self.ee_a
|
||||
.execution_layer
|
||||
.insert_proposer(slot, head_block_root, validator_index, payload_attributes)
|
||||
.await;
|
||||
let status = self
|
||||
.ee_a
|
||||
.execution_layer
|
||||
.notify_forkchoice_updated(head_block_hash, finalized_block_hash, payload_attributes)
|
||||
.notify_forkchoice_updated(head_block_hash, finalized_block_hash, slot, head_block_root)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(status, PayloadStatus::Valid);
|
||||
@@ -297,11 +333,12 @@ impl<E: GenericExecutionEngine> TestRig<E> {
|
||||
*/
|
||||
let head_block_hash = second_payload.block_hash;
|
||||
let finalized_block_hash = ExecutionBlockHash::zero();
|
||||
let payload_attributes = None;
|
||||
let slot = Slot::new(42);
|
||||
let head_block_root = Hash256::repeat_byte(42);
|
||||
let status = self
|
||||
.ee_b
|
||||
.execution_layer
|
||||
.notify_forkchoice_updated(head_block_hash, finalized_block_hash, payload_attributes)
|
||||
.notify_forkchoice_updated(head_block_hash, finalized_block_hash, slot, head_block_root)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(status, PayloadStatus::Syncing);
|
||||
@@ -340,11 +377,12 @@ impl<E: GenericExecutionEngine> TestRig<E> {
|
||||
*/
|
||||
let head_block_hash = second_payload.block_hash;
|
||||
let finalized_block_hash = ExecutionBlockHash::zero();
|
||||
let payload_attributes = None;
|
||||
let slot = Slot::new(42);
|
||||
let head_block_root = Hash256::repeat_byte(42);
|
||||
let status = self
|
||||
.ee_b
|
||||
.execution_layer
|
||||
.notify_forkchoice_updated(head_block_hash, finalized_block_hash, payload_attributes)
|
||||
.notify_forkchoice_updated(head_block_hash, finalized_block_hash, slot, head_block_root)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(status, PayloadStatus::Valid);
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
#[cfg(not(target_family = "windows"))]
|
||||
mod not_windows {
|
||||
use execution_engine_integration::{Geth, TestRig};
|
||||
#[test]
|
||||
fn geth() {
|
||||
TestRig::new(Geth).perform_tests_blocking()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_family = "windows")]
|
||||
mod windows {
|
||||
#[test]
|
||||
fn all_tests_skipped_on_windows() {
|
||||
//
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,7 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
|
||||
.about(
|
||||
"Lighthouse Beacon Chain Simulator creates `n` beacon node and validator clients, \
|
||||
each with `v` validators. A deposit contract is deployed at the start of the \
|
||||
simulation using a local `ganache-cli` instance (you must have `ganache-cli` \
|
||||
simulation using a local `ganache` instance (you must have `ganache` \
|
||||
installed and avaliable on your path). All beacon nodes independently listen \
|
||||
for genesis from the deposit contract, then start operating. \
|
||||
\
|
||||
|
||||
@@ -55,6 +55,7 @@ impl<E: EthSpec> LocalNetwork<E> {
|
||||
beacon_config.network.libp2p_port = BOOTNODE_PORT;
|
||||
beacon_config.network.enr_udp_port = Some(BOOTNODE_PORT);
|
||||
beacon_config.network.enr_tcp_port = Some(BOOTNODE_PORT);
|
||||
beacon_config.network.discv5_config.table_filter = |_| true;
|
||||
let beacon_node =
|
||||
LocalBeaconNode::production(context.service_context("boot_node".into()), beacon_config)
|
||||
.await?;
|
||||
@@ -103,6 +104,7 @@ impl<E: EthSpec> LocalNetwork<E> {
|
||||
beacon_config.network.libp2p_port = BOOTNODE_PORT + count;
|
||||
beacon_config.network.enr_udp_port = Some(BOOTNODE_PORT + count);
|
||||
beacon_config.network.enr_tcp_port = Some(BOOTNODE_PORT + count);
|
||||
beacon_config.network.discv5_config.table_filter = |_| true;
|
||||
}
|
||||
|
||||
let mut write_lock = self_1.beacon_nodes.write();
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
//! This crate provides a simluation that creates `n` beacon node and validator clients, each with
|
||||
//! `v` validators. A deposit contract is deployed at the start of the simulation using a local
|
||||
//! `ganache-cli` instance (you must have `ganache-cli` installed and avaliable on your path). All
|
||||
//! `ganache` instance (you must have `ganache` installed and avaliable on your path). All
|
||||
//! beacon nodes independently listen for genesis from the deposit contract, then start operating.
|
||||
//!
|
||||
//! As the simulation runs, there are checks made to ensure that all components are running
|
||||
|
||||
Reference in New Issue
Block a user