Make transactions in execution layer integration tests (#3320)

## Issue Addressed

Resolves #3159 

## Proposed Changes

Sends transactions to the EE before requesting for a payload in the `execution_integration_tests`. Made some changes to the integration tests in order to be able to sign and publish transactions to the EE:

1. `genesis.json` for both geth and nethermind was modified to include pre-funded accounts that we know private keys for 
2. Using the unauthenticated port again in order to make `eth_sendTransaction` and calls from the `personal` namespace to import keys

Also added a `fcu` call with `PayloadAttributes` before calling `getPayload` in order to give EEs sufficient time to pack transactions into the payload.
This commit is contained in:
Pawan Dhananjay 2022-07-18 01:51:36 +00:00
parent 2ed51c364d
commit da7b7a0f60
9 changed files with 541 additions and 13 deletions

190
Cargo.lock generated
View File

@ -189,6 +189,17 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "async_io_stream"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6d7b9decdf35d8908a7e3ef02f64c5e9b1695e230154c0e8de3969142d9b94c"
dependencies = [
"futures",
"pharos",
"rustc_version 0.4.0",
]
[[package]] [[package]]
name = "asynchronous-codec" name = "asynchronous-codec"
version = "0.6.0" version = "0.6.0"
@ -224,6 +235,18 @@ dependencies = [
"winapi", "winapi",
] ]
[[package]]
name = "auto_impl"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7862e21c893d65a1650125d157eaeec691439379a1cee17ee49031b79236ada4"
dependencies = [
"proc-macro-error",
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "autocfg" name = "autocfg"
version = "0.1.8" version = "0.1.8"
@ -1865,14 +1888,53 @@ dependencies = [
"tiny-keccak", "tiny-keccak",
] ]
[[package]]
name = "ethers-providers"
version = "0.6.0"
source = "git+https://github.com/gakonst/ethers-rs?rev=02ad93a1cfb7b62eb051c77c61dc4c0218428e4a#02ad93a1cfb7b62eb051c77c61dc4c0218428e4a"
dependencies = [
"async-trait",
"auto_impl",
"base64",
"ethers-core",
"futures-channel",
"futures-core",
"futures-timer",
"futures-util",
"hex",
"http",
"once_cell",
"parking_lot 0.11.2",
"pin-project 1.0.11",
"reqwest",
"serde",
"serde_json",
"thiserror",
"tokio",
"tokio-tungstenite 0.17.1",
"tracing",
"tracing-futures",
"url",
"wasm-bindgen",
"wasm-bindgen-futures",
"wasm-timer",
"web-sys",
"ws_stream_wasm",
]
[[package]] [[package]]
name = "execution_engine_integration" name = "execution_engine_integration"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"deposit_contract",
"environment", "environment",
"ethers-core",
"ethers-providers",
"execution_layer", "execution_layer",
"exit-future", "exit-future",
"futures", "futures",
"hex",
"reqwest",
"sensitive_url", "sensitive_url",
"serde_json", "serde_json",
"task_executor", "task_executor",
@ -2620,6 +2682,19 @@ dependencies = [
"want", "want",
] ]
[[package]]
name = "hyper-rustls"
version = "0.23.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d87c48c02e0dc5e3b849a2041db3029fd066650f8f717c07bf8ed78ccb895cac"
dependencies = [
"http",
"hyper",
"rustls 0.20.6",
"tokio",
"tokio-rustls 0.23.4",
]
[[package]] [[package]]
name = "hyper-tls" name = "hyper-tls"
version = "0.5.0" version = "0.5.0"
@ -2758,6 +2833,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"js-sys",
"wasm-bindgen",
"web-sys",
] ]
[[package]] [[package]]
@ -4351,6 +4429,16 @@ dependencies = [
"indexmap", "indexmap",
] ]
[[package]]
name = "pharos"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e9567389417feee6ce15dd6527a8a1ecac205ef62c2932bcf3d9f6fc5b78b414"
dependencies = [
"futures",
"rustc_version 0.4.0",
]
[[package]] [[package]]
name = "pin-project" name = "pin-project"
version = "0.4.30" version = "0.4.30"
@ -5038,6 +5126,7 @@ dependencies = [
"http", "http",
"http-body", "http-body",
"hyper", "hyper",
"hyper-rustls",
"hyper-tls", "hyper-tls",
"ipnet", "ipnet",
"js-sys", "js-sys",
@ -5047,17 +5136,21 @@ dependencies = [
"native-tls", "native-tls",
"percent-encoding", "percent-encoding",
"pin-project-lite 0.2.9", "pin-project-lite 0.2.9",
"rustls 0.20.6",
"rustls-pemfile",
"serde", "serde",
"serde_json", "serde_json",
"serde_urlencoded", "serde_urlencoded",
"tokio", "tokio",
"tokio-native-tls", "tokio-native-tls",
"tokio-rustls 0.23.4",
"tokio-util 0.7.3", "tokio-util 0.7.3",
"tower-service", "tower-service",
"url", "url",
"wasm-bindgen", "wasm-bindgen",
"wasm-bindgen-futures", "wasm-bindgen-futures",
"web-sys", "web-sys",
"webpki-roots",
"winreg 0.10.1", "winreg 0.10.1",
] ]
@ -5219,6 +5312,15 @@ dependencies = [
"webpki 0.22.0", "webpki 0.22.0",
] ]
[[package]]
name = "rustls-pemfile"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7522c9de787ff061458fe9a829dc790a3f5b22dc571694fc5883f448b94d9a9"
dependencies = [
"base64",
]
[[package]] [[package]]
name = "rustversion" name = "rustversion"
version = "1.0.7" version = "1.0.7"
@ -5437,6 +5539,12 @@ dependencies = [
"pest", "pest",
] ]
[[package]]
name = "send_wrapper"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "930c0acf610d3fdb5e2ab6213019aaa04e227ebe9547b0649ba599b16d788bd7"
[[package]] [[package]]
name = "sensitive_url" name = "sensitive_url"
version = "0.1.0" version = "0.1.0"
@ -6398,6 +6506,17 @@ dependencies = [
"webpki 0.21.4", "webpki 0.21.4",
] ]
[[package]]
name = "tokio-rustls"
version = "0.23.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59"
dependencies = [
"rustls 0.20.6",
"tokio",
"webpki 0.22.0",
]
[[package]] [[package]]
name = "tokio-stream" name = "tokio-stream"
version = "0.1.9" version = "0.1.9"
@ -6420,7 +6539,23 @@ dependencies = [
"log", "log",
"pin-project 1.0.11", "pin-project 1.0.11",
"tokio", "tokio",
"tungstenite", "tungstenite 0.14.0",
]
[[package]]
name = "tokio-tungstenite"
version = "0.17.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06cda1232a49558c46f8a504d5b93101d42c0bf7f911f12a105ba48168f821ae"
dependencies = [
"futures-util",
"log",
"rustls 0.20.6",
"tokio",
"tokio-rustls 0.23.4",
"tungstenite 0.17.2",
"webpki 0.22.0",
"webpki-roots",
] ]
[[package]] [[package]]
@ -6502,6 +6637,16 @@ dependencies = [
"valuable", "valuable",
] ]
[[package]]
name = "tracing-futures"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2"
dependencies = [
"pin-project 1.0.11",
"tracing",
]
[[package]] [[package]]
name = "tracing-log" name = "tracing-log"
version = "0.1.3" version = "0.1.3"
@ -6644,6 +6789,27 @@ dependencies = [
"utf-8", "utf-8",
] ]
[[package]]
name = "tungstenite"
version = "0.17.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d96a2dea40e7570482f28eb57afbe42d97551905da6a9400acc5c328d24004f5"
dependencies = [
"base64",
"byteorder",
"bytes",
"http",
"httparse",
"log",
"rand 0.8.5",
"rustls 0.20.6",
"sha-1 0.10.0",
"thiserror",
"url",
"utf-8",
"webpki 0.22.0",
]
[[package]] [[package]]
name = "twoway" name = "twoway"
version = "0.1.8" version = "0.1.8"
@ -6991,9 +7157,9 @@ dependencies = [
"serde_json", "serde_json",
"serde_urlencoded", "serde_urlencoded",
"tokio", "tokio",
"tokio-rustls", "tokio-rustls 0.22.0",
"tokio-stream", "tokio-stream",
"tokio-tungstenite", "tokio-tungstenite 0.15.0",
"tokio-util 0.6.10", "tokio-util 0.6.10",
"tower-service", "tower-service",
"tracing", "tracing",
@ -7378,6 +7544,24 @@ dependencies = [
"winapi", "winapi",
] ]
[[package]]
name = "ws_stream_wasm"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "47ca1ab42f5afed7fc332b22b6e932ca5414b209465412c8cdf0ad23bc0de645"
dependencies = [
"async_io_stream",
"futures",
"js-sys",
"pharos",
"rustc_version 0.4.0",
"send_wrapper",
"thiserror",
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
]
[[package]] [[package]]
name = "wyz" name = "wyz"
version = "0.2.0" version = "0.2.0"

View File

@ -15,3 +15,8 @@ execution_layer = { path = "../../beacon_node/execution_layer" }
sensitive_url = { path = "../../common/sensitive_url" } sensitive_url = { path = "../../common/sensitive_url" }
types = { path = "../../consensus/types" } types = { path = "../../consensus/types" }
unused_port = { path = "../../common/unused_port" } unused_port = { path = "../../common/unused_port" }
ethers-core = { git = "https://github.com/gakonst/ethers-rs", rev = "02ad93a1cfb7b62eb051c77c61dc4c0218428e4a" }
ethers-providers = { git = "https://github.com/gakonst/ethers-rs", rev = "02ad93a1cfb7b62eb051c77c61dc4c0218428e4a" }
deposit_contract = { path = "../../common/deposit_contract" }
reqwest = { version = "0.11.0", features = ["json"] }
hex = "0.4.2"

View File

@ -1,3 +1,4 @@
use ethers_providers::{Http, Provider};
use execution_layer::DEFAULT_JWT_FILE; use execution_layer::DEFAULT_JWT_FILE;
use sensitive_url::SensitiveUrl; use sensitive_url::SensitiveUrl;
use std::path::PathBuf; use std::path::PathBuf;
@ -5,6 +6,14 @@ use std::process::Child;
use tempfile::TempDir; use tempfile::TempDir;
use unused_port::unused_tcp_port; use unused_port::unused_tcp_port;
pub const KEYSTORE_PASSWORD: &str = "testpwd";
pub const ACCOUNT1: &str = "7b8C3a386C0eea54693fFB0DA17373ffC9228139";
pub const ACCOUNT2: &str = "dA2DD7560DB7e212B945fC72cEB54B7D8C886D77";
pub const PRIVATE_KEYS: [&str; 2] = [
"115fe42a60e5ef45f5490e599add1f03c73aeaca129c2c41451eca6cf8ff9e04",
"6a692e710077d9000be1326acbe32f777b403902ac8779b19eb1398b849c99c3",
];
/// Defined for each EE type (e.g., Geth, Nethermind, etc). /// Defined for each EE type (e.g., Geth, Nethermind, etc).
pub trait GenericExecutionEngine: Clone { pub trait GenericExecutionEngine: Clone {
fn init_datadir() -> TempDir; fn init_datadir() -> TempDir;
@ -22,8 +31,10 @@ pub struct ExecutionEngine<E> {
engine: E, engine: E,
#[allow(dead_code)] #[allow(dead_code)]
datadir: TempDir, datadir: TempDir,
http_port: u16,
http_auth_port: u16, http_auth_port: u16,
child: Child, child: Child,
pub provider: Provider<Http>,
} }
impl<E> Drop for ExecutionEngine<E> { impl<E> Drop for ExecutionEngine<E> {
@ -42,11 +53,15 @@ impl<E: GenericExecutionEngine> ExecutionEngine<E> {
let http_port = unused_tcp_port().unwrap(); let http_port = unused_tcp_port().unwrap();
let http_auth_port = unused_tcp_port().unwrap(); let http_auth_port = unused_tcp_port().unwrap();
let child = E::start_client(&datadir, http_port, http_auth_port, jwt_secret_path); let child = E::start_client(&datadir, http_port, http_auth_port, jwt_secret_path);
let provider = Provider::<Http>::try_from(format!("http://localhost:{}", http_port))
.expect("failed to instantiate ethers provider");
Self { Self {
engine, engine,
datadir, datadir,
http_port,
http_auth_port, http_auth_port,
child, child,
provider,
} }
} }
@ -54,6 +69,10 @@ impl<E: GenericExecutionEngine> ExecutionEngine<E> {
SensitiveUrl::parse(&format!("http://127.0.0.1:{}", self.http_auth_port)).unwrap() SensitiveUrl::parse(&format!("http://127.0.0.1:{}", self.http_auth_port)).unwrap()
} }
pub fn http_url(&self) -> SensitiveUrl {
SensitiveUrl::parse(&format!("http://127.0.0.1:{}", self.http_port)).unwrap()
}
pub fn datadir(&self) -> PathBuf { pub fn datadir(&self) -> PathBuf {
self.datadir.path().to_path_buf() self.datadir.path().to_path_buf()
} }

View File

@ -32,7 +32,12 @@ pub fn geth_genesis_json() -> Value {
"mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000", "mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000",
"coinbase":"0x0000000000000000000000000000000000000000", "coinbase":"0x0000000000000000000000000000000000000000",
"alloc":{ "alloc":{
"0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b":{"balance":"0x6d6172697573766477000000"} "0x7b8C3a386C0eea54693fFB0DA17373ffC9228139": {
"balance": "10000000000000000000000000"
},
"0xdA2DD7560DB7e212B945fC72cEB54B7D8C886D77": {
"balance": "10000000000000000000000000"
},
}, },
"number":"0x0", "number":"0x0",
"gasUsed":"0x0", "gasUsed":"0x0",
@ -40,3 +45,87 @@ pub fn geth_genesis_json() -> Value {
"baseFeePerGas":"0x7" "baseFeePerGas":"0x7"
}) })
} }
/// Modified kiln config
pub fn nethermind_genesis_json() -> Value {
json!(
{
"name": "lighthouse_test_network",
"engine": {
"Ethash": {
"params": {
"minimumDifficulty": "0x20000",
"difficultyBoundDivisor": "0x800",
"durationLimit": "0xd",
"blockReward": {
"0x0": "0x1BC16D674EC80000"
},
"homesteadTransition": "0x0",
"eip100bTransition": "0x0",
"difficultyBombDelays": {}
}
}
},
"params": {
"gasLimitBoundDivisor": "0x400",
"registrar": "0x0000000000000000000000000000000000000000",
"accountStartNonce": "0x0",
"maximumExtraDataSize": "0x20",
"minGasLimit": "0x1388",
"networkID": "0x1469ca",
"MergeForkIdTransition": "0x3e8",
"eip150Transition": "0x0",
"eip158Transition": "0x0",
"eip160Transition": "0x0",
"eip161abcTransition": "0x0",
"eip161dTransition": "0x0",
"eip155Transition": "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": "0x1234",
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000"
}
},
"difficulty": "0x01",
"author": "0x0000000000000000000000000000000000000000",
"timestamp": "0x0",
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"extraData": "",
"gasLimit": "0x1C9C380"
},
"accounts": {
"0x7b8C3a386C0eea54693fFB0DA17373ffC9228139": {
"balance": "10000000000000000000000000"
},
"0xdA2DD7560DB7e212B945fC72cEB54B7D8C886D77": {
"balance": "10000000000000000000000000"
},
},
"nodes": []
}
)
}

View File

@ -90,13 +90,14 @@ impl GenericExecutionEngine for GethEngine {
.arg(datadir.path().to_str().unwrap()) .arg(datadir.path().to_str().unwrap())
.arg("--http") .arg("--http")
.arg("--http.api") .arg("--http.api")
.arg("engine,eth") .arg("engine,eth,personal")
.arg("--http.port") .arg("--http.port")
.arg(http_port.to_string()) .arg(http_port.to_string())
.arg("--authrpc.port") .arg("--authrpc.port")
.arg(http_auth_port.to_string()) .arg(http_auth_port.to_string())
.arg("--port") .arg("--port")
.arg(network_port.to_string()) .arg(network_port.to_string())
.arg("--allow-insecure-unlock")
.arg("--authrpc.jwtsecret") .arg("--authrpc.jwtsecret")
.arg(jwt_secret_path.as_path().to_str().unwrap()) .arg(jwt_secret_path.as_path().to_str().unwrap())
.stdout(build_utils::build_stdio()) .stdout(build_utils::build_stdio())

View File

@ -1,3 +1,4 @@
#![recursion_limit = "1024"]
/// This binary runs integration tests between Lighthouse and execution engines. /// 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. /// It will first attempt to build any supported integration clients, then it will run tests.
@ -9,6 +10,7 @@ mod genesis_json;
mod geth; mod geth;
mod nethermind; mod nethermind;
mod test_rig; mod test_rig;
mod transactions;
use geth::GethEngine; use geth::GethEngine;
use nethermind::NethermindEngine; use nethermind::NethermindEngine;

View File

@ -1,6 +1,8 @@
use crate::build_utils; use crate::build_utils;
use crate::execution_engine::GenericExecutionEngine; use crate::execution_engine::GenericExecutionEngine;
use crate::genesis_json::nethermind_genesis_json;
use std::env; use std::env;
use std::fs::File;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::process::{Child, Command, Output}; use std::process::{Child, Command, Output};
use tempfile::TempDir; use tempfile::TempDir;
@ -69,33 +71,43 @@ impl NethermindEngine {
impl GenericExecutionEngine for NethermindEngine { impl GenericExecutionEngine for NethermindEngine {
fn init_datadir() -> TempDir { fn init_datadir() -> TempDir {
TempDir::new().unwrap() 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( fn start_client(
datadir: &TempDir, datadir: &TempDir,
_http_port: u16, http_port: u16,
http_auth_port: u16, http_auth_port: u16,
jwt_secret_path: PathBuf, jwt_secret_path: PathBuf,
) -> Child { ) -> Child {
let network_port = unused_tcp_port().unwrap(); let network_port = unused_tcp_port().unwrap();
let genesis_json_path = datadir.path().join("genesis.json");
Command::new(Self::binary_path()) Command::new(Self::binary_path())
.arg("--datadir") .arg("--datadir")
.arg(datadir.path().to_str().unwrap()) .arg(datadir.path().to_str().unwrap())
.arg("--config") .arg("--config")
.arg("kiln") .arg("kiln")
.arg("--Init.ChainSpecPath")
.arg(genesis_json_path.to_str().unwrap())
.arg("--Merge.TerminalTotalDifficulty") .arg("--Merge.TerminalTotalDifficulty")
.arg("0") .arg("0")
.arg("--JsonRpc.Enabled")
.arg("true")
.arg("--JsonRpc.EnabledModules")
.arg("net,eth,subscribe,web3,admin,personal")
.arg("--JsonRpc.Port")
.arg(http_port.to_string())
.arg("--JsonRpc.AdditionalRpcUrls") .arg("--JsonRpc.AdditionalRpcUrls")
.arg(format!( .arg(format!(
"http://localhost:{}|http;ws|net;eth;subscribe;engine;web3;client", "http://localhost:{}|http;ws|net;eth;subscribe;engine;web3;client",
http_auth_port http_auth_port
)) ))
.arg("--JsonRpc.EnabledModules")
.arg("net,eth,subscribe,web3,admin,engine")
.arg("--JsonRpc.Port")
.arg(http_auth_port.to_string())
.arg("--Network.DiscoveryPort") .arg("--Network.DiscoveryPort")
.arg(network_port.to_string()) .arg(network_port.to_string())
.arg("--Network.P2PPort") .arg("--Network.P2PPort")

View File

@ -1,5 +1,12 @@
use crate::execution_engine::{ExecutionEngine, GenericExecutionEngine}; use crate::execution_engine::{
ExecutionEngine, GenericExecutionEngine, ACCOUNT1, ACCOUNT2, KEYSTORE_PASSWORD, PRIVATE_KEYS,
};
use crate::transactions::transactions;
use ethers_providers::Middleware;
use execution_layer::{ExecutionLayer, PayloadAttributes, PayloadStatus}; use execution_layer::{ExecutionLayer, PayloadAttributes, PayloadStatus};
use reqwest::{header::CONTENT_TYPE, Client};
use sensitive_url::SensitiveUrl;
use serde_json::{json, Value};
use std::sync::Arc; use std::sync::Arc;
use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH}; use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
use task_executor::TaskExecutor; use task_executor::TaskExecutor;
@ -8,7 +15,6 @@ use types::{
Address, ChainSpec, EthSpec, ExecutionBlockHash, ExecutionPayload, FullPayload, Hash256, Address, ChainSpec, EthSpec, ExecutionBlockHash, ExecutionPayload, FullPayload, Hash256,
MainnetEthSpec, Slot, Uint256, MainnetEthSpec, Slot, Uint256,
}; };
const EXECUTION_ENGINE_START_TIMEOUT: Duration = Duration::from_secs(20); const EXECUTION_ENGINE_START_TIMEOUT: Duration = Duration::from_secs(20);
struct ExecutionPair<E, T: EthSpec> { struct ExecutionPair<E, T: EthSpec> {
@ -32,6 +38,63 @@ pub struct TestRig<E, T: EthSpec = MainnetEthSpec> {
_runtime_shutdown: exit_future::Signal, _runtime_shutdown: exit_future::Signal,
} }
/// Import a private key into the execution engine and unlock it so that we can
/// make transactions with the corresponding account.
async fn import_and_unlock(http_url: SensitiveUrl, priv_keys: &[&str], password: &str) {
for priv_key in priv_keys {
let body = json!(
{
"jsonrpc":"2.0",
"method":"personal_importRawKey",
"params":[priv_key, password],
"id":1
}
);
let client = Client::builder().build().unwrap();
let request = client
.post(http_url.full.clone())
.header(CONTENT_TYPE, "application/json")
.json(&body);
let response: Value = request
.send()
.await
.unwrap()
.error_for_status()
.unwrap()
.json()
.await
.unwrap();
let account = response.get("result").unwrap().as_str().unwrap();
let body = json!(
{
"jsonrpc":"2.0",
"method":"personal_unlockAccount",
"params":[account, password],
"id":1
}
);
let request = client
.post(http_url.full.clone())
.header(CONTENT_TYPE, "application/json")
.json(&body);
let _response: Value = request
.send()
.await
.unwrap()
.error_for_status()
.unwrap()
.json()
.await
.unwrap();
}
}
impl<E: GenericExecutionEngine> TestRig<E> { impl<E: GenericExecutionEngine> TestRig<E> {
pub fn new(generic_engine: E) -> Self { pub fn new(generic_engine: E) -> Self {
let log = environment::null_logger().unwrap(); let log = environment::null_logger().unwrap();
@ -125,6 +188,20 @@ impl<E: GenericExecutionEngine> TestRig<E> {
pub async fn perform_tests(&self) { pub async fn perform_tests(&self) {
self.wait_until_synced().await; self.wait_until_synced().await;
// Import and unlock all private keys to sign transactions
let _ = futures::future::join_all([&self.ee_a, &self.ee_b].iter().map(|ee| {
import_and_unlock(
ee.execution_engine.http_url(),
&PRIVATE_KEYS,
KEYSTORE_PASSWORD,
)
}))
.await;
// We hardcode the accounts here since some EEs start with a default unlocked account
let account1 = ethers_core::types::Address::from_slice(&hex::decode(&ACCOUNT1).unwrap());
let account2 = ethers_core::types::Address::from_slice(&hex::decode(&ACCOUNT2).unwrap());
/* /*
* Check the transition config endpoint. * Check the transition config endpoint.
*/ */
@ -157,6 +234,17 @@ impl<E: GenericExecutionEngine> TestRig<E> {
.unwrap() .unwrap()
); );
// Submit transactions before getting payload
let txs = transactions::<MainnetEthSpec>(account1, account2);
for tx in txs.clone().into_iter() {
self.ee_a
.execution_engine
.provider
.send_transaction(tx, None)
.await
.unwrap();
}
/* /*
* Execution Engine A: * Execution Engine A:
* *
@ -168,6 +256,45 @@ impl<E: GenericExecutionEngine> TestRig<E> {
let prev_randao = Hash256::zero(); let prev_randao = Hash256::zero();
let finalized_block_hash = ExecutionBlockHash::zero(); let finalized_block_hash = ExecutionBlockHash::zero();
let proposer_index = 0; let proposer_index = 0;
let prepared = self
.ee_a
.execution_layer
.insert_proposer(
Slot::new(1), // Insert proposer for the next slot
Hash256::zero(),
proposer_index,
PayloadAttributes {
timestamp,
prev_randao,
suggested_fee_recipient: Address::zero(),
},
)
.await;
assert!(!prepared, "Inserting proposer for the first time");
// Make a fcu call with the PayloadAttributes that we inserted previously
let prepare = self
.ee_a
.execution_layer
.notify_forkchoice_updated(
parent_hash,
finalized_block_hash,
Slot::new(0),
Hash256::zero(),
)
.await
.unwrap();
assert_eq!(prepare, PayloadStatus::Valid);
// Add a delay to give the EE sufficient time to pack the
// submitted transactions into a payload.
// This is required when running on under resourced nodes and
// in CI.
sleep(Duration::from_secs(3)).await;
let valid_payload = self let valid_payload = self
.ee_a .ee_a
.execution_layer .execution_layer
@ -184,6 +311,8 @@ impl<E: GenericExecutionEngine> TestRig<E> {
.unwrap() .unwrap()
.execution_payload; .execution_payload;
assert_eq!(valid_payload.transactions.len(), txs.len());
/* /*
* Execution Engine A: * Execution Engine A:
* *

View File

@ -0,0 +1,87 @@
use deposit_contract::{encode_eth1_tx_data, BYTECODE, CONTRACT_DEPLOY_GAS, DEPOSIT_GAS};
use ethers_core::types::{
transaction::{eip2718::TypedTransaction, eip2930::AccessList},
Address, Bytes, Eip1559TransactionRequest, TransactionRequest,
};
use types::{DepositData, EthSpec, Hash256, Keypair, Signature};
/// Hardcoded deposit contract address based on sender address and nonce
pub const DEPOSIT_CONTRACT_ADDRESS: &str = "64f43BEc7F86526686C931d65362bB8698872F90";
#[derive(Debug)]
pub enum Transaction {
Transfer(Address, Address),
TransferLegacy(Address, Address),
TransferAccessList(Address, Address),
DeployDepositContract(Address),
DepositDepositContract {
sender: Address,
deposit_contract_address: Address,
},
}
/// Get a list of transactions to publish to the execution layer.
pub fn transactions<E: EthSpec>(account1: Address, account2: Address) -> Vec<TypedTransaction> {
vec![
Transaction::Transfer(account1, account2).transaction::<E>(),
Transaction::TransferLegacy(account1, account2).transaction::<E>(),
Transaction::TransferAccessList(account1, account2).transaction::<E>(),
Transaction::DeployDepositContract(account1).transaction::<E>(),
Transaction::DepositDepositContract {
sender: account1,
deposit_contract_address: ethers_core::types::Address::from_slice(
&hex::decode(&DEPOSIT_CONTRACT_ADDRESS).unwrap(),
),
}
.transaction::<E>(),
]
}
impl Transaction {
pub fn transaction<E: EthSpec>(&self) -> TypedTransaction {
match &self {
Self::TransferLegacy(from, to) => TransactionRequest::new()
.from(*from)
.to(*to)
.value(1)
.into(),
Self::Transfer(from, to) => Eip1559TransactionRequest::new()
.from(*from)
.to(*to)
.value(1)
.into(),
Self::TransferAccessList(from, to) => TransactionRequest::new()
.from(*from)
.to(*to)
.value(1)
.with_access_list(AccessList::default())
.into(),
Self::DeployDepositContract(addr) => TransactionRequest::new()
.from(*addr)
.data(Bytes::from(BYTECODE.to_vec()))
.gas(CONTRACT_DEPLOY_GAS)
.into(),
Self::DepositDepositContract {
sender,
deposit_contract_address,
} => {
let keypair = Keypair::random();
let mut deposit = DepositData {
pubkey: keypair.pk.into(),
withdrawal_credentials: Hash256::zero(),
amount: 32_000_000_000,
signature: Signature::empty().into(),
};
deposit.signature = deposit.create_signature(&keypair.sk, &E::default_spec());
TransactionRequest::new()
.from(*sender)
.to(*deposit_contract_address)
.data(Bytes::from(encode_eth1_tx_data(&deposit).unwrap()))
.gas(DEPOSIT_GAS)
.into()
}
}
}
}