Add BeaconChainHarness tests for The Merge (#2661)

* Start adding merge tests

* Expose MockExecutionLayer

* Add mock_execution_layer to BeaconChainHarness

* Progress with merge test

* Return more detailed errors with gas limit issues

* Use a better gas limit in block gen

* Ensure TTD is met in block gen

* Fix basic_merge tests

* Start geth testing

* Fix conflicts after rebase

* Remove geth tests

* Improve merge test

* Address clippy lints

* Make pow block gen a pure function

* Add working new test, breaking existing test

* Fix test names

* Add should_panic

* Don't run merge tests in debug

* Detect a tokio runtime when starting MockServer

* Fix clippy lint, include merge tests
This commit is contained in:
Paul Hauner
2021-10-02 11:39:11 +10:00
parent 801f6f7425
commit a1033a9247
14 changed files with 642 additions and 249 deletions

View File

@@ -3,8 +3,8 @@
use crate::engine_api::http::JSONRPC_VERSION;
use bytes::Bytes;
use environment::null_logger;
use execution_block_generator::ExecutionBlockGenerator;
use handle_rpc::handle_rpc;
use parking_lot::{Mutex, RwLock, RwLockWriteGuard};
use serde::{Deserialize, Serialize};
use serde_json::json;
use slog::{info, Logger};
@@ -12,15 +12,19 @@ use std::future::Future;
use std::marker::PhantomData;
use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4};
use std::sync::Arc;
use tokio::sync::{oneshot, Mutex, RwLock, RwLockWriteGuard};
use types::EthSpec;
use tokio::{runtime, sync::oneshot};
use types::{EthSpec, Hash256, Uint256};
use warp::Filter;
pub use execution_block_generator::{generate_pow_block, ExecutionBlockGenerator};
pub use mock_execution_layer::{ExecutionLayerRuntime, MockExecutionLayer};
pub const DEFAULT_TERMINAL_DIFFICULTY: u64 = 6400;
pub const DEFAULT_TERMINAL_BLOCK: u64 = 64;
mod execution_block_generator;
mod handle_rpc;
mod mock_execution_layer;
pub struct MockServer<T: EthSpec> {
_shutdown_tx: oneshot::Sender<()>,
@@ -31,10 +35,24 @@ pub struct MockServer<T: EthSpec> {
impl<T: EthSpec> MockServer<T> {
pub fn unit_testing() -> Self {
Self::new(
&runtime::Handle::current(),
DEFAULT_TERMINAL_DIFFICULTY.into(),
DEFAULT_TERMINAL_BLOCK,
Hash256::zero(),
)
}
pub fn new(
handle: &runtime::Handle,
terminal_difficulty: Uint256,
terminal_block: u64,
terminal_block_hash: Hash256,
) -> Self {
let last_echo_request = Arc::new(RwLock::new(None));
let preloaded_responses = Arc::new(Mutex::new(vec![]));
let execution_block_generator =
ExecutionBlockGenerator::new(DEFAULT_TERMINAL_DIFFICULTY, DEFAULT_TERMINAL_BLOCK);
ExecutionBlockGenerator::new(terminal_difficulty, terminal_block, terminal_block_hash);
let ctx: Arc<Context<T>> = Arc::new(Context {
config: <_>::default(),
@@ -52,9 +70,17 @@ impl<T: EthSpec> MockServer<T> {
let _ = shutdown_rx.await;
};
let (listen_socket_addr, server_future) = serve(ctx.clone(), shutdown_future).unwrap();
// The `serve` function will panic unless it's run inside a tokio runtime, so use `block_on`
// if we're not in a runtime. However, we can't *always* use `block_on` since tokio will
// panic if we try to block inside an async context.
let serve = || serve(ctx.clone(), shutdown_future).unwrap();
let (listen_socket_addr, server_future) = if runtime::Handle::try_current().is_err() {
handle.block_on(async { serve() })
} else {
serve()
};
tokio::spawn(server_future);
handle.spawn(server_future);
Self {
_shutdown_tx: shutdown_tx,
@@ -64,10 +90,8 @@ impl<T: EthSpec> MockServer<T> {
}
}
pub async fn execution_block_generator(
&self,
) -> RwLockWriteGuard<'_, ExecutionBlockGenerator<T>> {
self.ctx.execution_block_generator.write().await
pub fn execution_block_generator(&self) -> RwLockWriteGuard<'_, ExecutionBlockGenerator<T>> {
self.ctx.execution_block_generator.write()
}
pub fn url(&self) -> String {
@@ -78,16 +102,15 @@ impl<T: EthSpec> MockServer<T> {
)
}
pub async fn last_echo_request(&self) -> Bytes {
pub fn last_echo_request(&self) -> Bytes {
self.last_echo_request
.write()
.await
.take()
.expect("last echo request is none")
}
pub async fn push_preloaded_response(&self, response: serde_json::Value) {
self.ctx.preloaded_responses.lock().await.push(response)
pub fn push_preloaded_response(&self, response: serde_json::Value) {
self.ctx.preloaded_responses.lock().push(response)
}
}
@@ -180,7 +203,7 @@ pub fn serve<T: EthSpec>(
.ok_or_else(|| warp::reject::custom(MissingIdField))?;
let preloaded_response = {
let mut preloaded_responses = ctx.preloaded_responses.lock().await;
let mut preloaded_responses = ctx.preloaded_responses.lock();
if !preloaded_responses.is_empty() {
Some(preloaded_responses.remove(0))
} else {
@@ -222,7 +245,7 @@ pub fn serve<T: EthSpec>(
.and(warp::body::bytes())
.and(ctx_filter)
.and_then(|bytes: Bytes, ctx: Arc<Context<T>>| async move {
*ctx.last_echo_request.write().await = Some(bytes.clone());
*ctx.last_echo_request.write() = Some(bytes.clone());
Ok::<_, warp::reject::Rejection>(
warp::http::Response::builder().status(200).body(bytes),
)