From da7b7a0f60dbc341346247e96f3a49b27ead1de2 Mon Sep 17 00:00:00 2001 From: Pawan Dhananjay Date: Mon, 18 Jul 2022 01:51:36 +0000 Subject: [PATCH] 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. --- Cargo.lock | 190 +++++++++++++++++- .../execution_engine_integration/Cargo.toml | 5 + .../src/execution_engine.rs | 19 ++ .../src/genesis_json.rs | 91 ++++++++- .../execution_engine_integration/src/geth.rs | 3 +- .../execution_engine_integration/src/main.rs | 2 + .../src/nethermind.rs | 24 ++- .../src/test_rig.rs | 133 +++++++++++- .../src/transactions.rs | 87 ++++++++ 9 files changed, 541 insertions(+), 13 deletions(-) create mode 100644 testing/execution_engine_integration/src/transactions.rs diff --git a/Cargo.lock b/Cargo.lock index a31a6b382..dfeac97cf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -189,6 +189,17 @@ dependencies = [ "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]] name = "asynchronous-codec" version = "0.6.0" @@ -224,6 +235,18 @@ dependencies = [ "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]] name = "autocfg" version = "0.1.8" @@ -1865,14 +1888,53 @@ dependencies = [ "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]] name = "execution_engine_integration" version = "0.1.0" dependencies = [ + "deposit_contract", "environment", + "ethers-core", + "ethers-providers", "execution_layer", "exit-future", "futures", + "hex", + "reqwest", "sensitive_url", "serde_json", "task_executor", @@ -2620,6 +2682,19 @@ dependencies = [ "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]] name = "hyper-tls" version = "0.5.0" @@ -2758,6 +2833,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" dependencies = [ "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", ] [[package]] @@ -4351,6 +4429,16 @@ dependencies = [ "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]] name = "pin-project" version = "0.4.30" @@ -5038,6 +5126,7 @@ dependencies = [ "http", "http-body", "hyper", + "hyper-rustls", "hyper-tls", "ipnet", "js-sys", @@ -5047,17 +5136,21 @@ dependencies = [ "native-tls", "percent-encoding", "pin-project-lite 0.2.9", + "rustls 0.20.6", + "rustls-pemfile", "serde", "serde_json", "serde_urlencoded", "tokio", "tokio-native-tls", + "tokio-rustls 0.23.4", "tokio-util 0.7.3", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", + "webpki-roots", "winreg 0.10.1", ] @@ -5219,6 +5312,15 @@ dependencies = [ "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]] name = "rustversion" version = "1.0.7" @@ -5437,6 +5539,12 @@ dependencies = [ "pest", ] +[[package]] +name = "send_wrapper" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "930c0acf610d3fdb5e2ab6213019aaa04e227ebe9547b0649ba599b16d788bd7" + [[package]] name = "sensitive_url" version = "0.1.0" @@ -6398,6 +6506,17 @@ dependencies = [ "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]] name = "tokio-stream" version = "0.1.9" @@ -6420,7 +6539,23 @@ dependencies = [ "log", "pin-project 1.0.11", "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]] @@ -6502,6 +6637,16 @@ dependencies = [ "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]] name = "tracing-log" version = "0.1.3" @@ -6644,6 +6789,27 @@ dependencies = [ "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]] name = "twoway" version = "0.1.8" @@ -6991,9 +7157,9 @@ dependencies = [ "serde_json", "serde_urlencoded", "tokio", - "tokio-rustls", + "tokio-rustls 0.22.0", "tokio-stream", - "tokio-tungstenite", + "tokio-tungstenite 0.15.0", "tokio-util 0.6.10", "tower-service", "tracing", @@ -7378,6 +7544,24 @@ dependencies = [ "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]] name = "wyz" version = "0.2.0" diff --git a/testing/execution_engine_integration/Cargo.toml b/testing/execution_engine_integration/Cargo.toml index fc8230c7a..f42a7f6ab 100644 --- a/testing/execution_engine_integration/Cargo.toml +++ b/testing/execution_engine_integration/Cargo.toml @@ -15,3 +15,8 @@ execution_layer = { path = "../../beacon_node/execution_layer" } sensitive_url = { path = "../../common/sensitive_url" } types = { path = "../../consensus/types" } 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" diff --git a/testing/execution_engine_integration/src/execution_engine.rs b/testing/execution_engine_integration/src/execution_engine.rs index 7df88aa0d..ad5af5315 100644 --- a/testing/execution_engine_integration/src/execution_engine.rs +++ b/testing/execution_engine_integration/src/execution_engine.rs @@ -1,3 +1,4 @@ +use ethers_providers::{Http, Provider}; use execution_layer::DEFAULT_JWT_FILE; use sensitive_url::SensitiveUrl; use std::path::PathBuf; @@ -5,6 +6,14 @@ use std::process::Child; use tempfile::TempDir; 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). pub trait GenericExecutionEngine: Clone { fn init_datadir() -> TempDir; @@ -22,8 +31,10 @@ pub struct ExecutionEngine { engine: E, #[allow(dead_code)] datadir: TempDir, + http_port: u16, http_auth_port: u16, child: Child, + pub provider: Provider, } impl Drop for ExecutionEngine { @@ -42,11 +53,15 @@ impl ExecutionEngine { let http_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 provider = Provider::::try_from(format!("http://localhost:{}", http_port)) + .expect("failed to instantiate ethers provider"); Self { engine, datadir, + http_port, http_auth_port, child, + provider, } } @@ -54,6 +69,10 @@ impl ExecutionEngine { 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 { self.datadir.path().to_path_buf() } diff --git a/testing/execution_engine_integration/src/genesis_json.rs b/testing/execution_engine_integration/src/genesis_json.rs index 87fdaec14..17654b292 100644 --- a/testing/execution_engine_integration/src/genesis_json.rs +++ b/testing/execution_engine_integration/src/genesis_json.rs @@ -32,7 +32,12 @@ pub fn geth_genesis_json() -> Value { "mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000", "coinbase":"0x0000000000000000000000000000000000000000", "alloc":{ - "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b":{"balance":"0x6d6172697573766477000000"} + "0x7b8C3a386C0eea54693fFB0DA17373ffC9228139": { + "balance": "10000000000000000000000000" + }, + "0xdA2DD7560DB7e212B945fC72cEB54B7D8C886D77": { + "balance": "10000000000000000000000000" + }, }, "number":"0x0", "gasUsed":"0x0", @@ -40,3 +45,87 @@ pub fn geth_genesis_json() -> Value { "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": [] + } + ) +} diff --git a/testing/execution_engine_integration/src/geth.rs b/testing/execution_engine_integration/src/geth.rs index 129faea90..8c751ed65 100644 --- a/testing/execution_engine_integration/src/geth.rs +++ b/testing/execution_engine_integration/src/geth.rs @@ -90,13 +90,14 @@ impl GenericExecutionEngine for GethEngine { .arg(datadir.path().to_str().unwrap()) .arg("--http") .arg("--http.api") - .arg("engine,eth") + .arg("engine,eth,personal") .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("--allow-insecure-unlock") .arg("--authrpc.jwtsecret") .arg(jwt_secret_path.as_path().to_str().unwrap()) .stdout(build_utils::build_stdio()) diff --git a/testing/execution_engine_integration/src/main.rs b/testing/execution_engine_integration/src/main.rs index a4ec0f921..bd3436602 100644 --- a/testing/execution_engine_integration/src/main.rs +++ b/testing/execution_engine_integration/src/main.rs @@ -1,3 +1,4 @@ +#![recursion_limit = "1024"] /// 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. @@ -9,6 +10,7 @@ mod genesis_json; mod geth; mod nethermind; mod test_rig; +mod transactions; use geth::GethEngine; use nethermind::NethermindEngine; diff --git a/testing/execution_engine_integration/src/nethermind.rs b/testing/execution_engine_integration/src/nethermind.rs index df345f36b..1fe7bf0f0 100644 --- a/testing/execution_engine_integration/src/nethermind.rs +++ b/testing/execution_engine_integration/src/nethermind.rs @@ -1,6 +1,8 @@ use crate::build_utils; use crate::execution_engine::GenericExecutionEngine; +use crate::genesis_json::nethermind_genesis_json; use std::env; +use std::fs::File; use std::path::{Path, PathBuf}; use std::process::{Child, Command, Output}; use tempfile::TempDir; @@ -69,33 +71,43 @@ impl NethermindEngine { impl GenericExecutionEngine for NethermindEngine { 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( datadir: &TempDir, - _http_port: u16, + 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("kiln") + .arg("--Init.ChainSpecPath") + .arg(genesis_json_path.to_str().unwrap()) .arg("--Merge.TerminalTotalDifficulty") .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(format!( "http://localhost:{}|http;ws|net;eth;subscribe;engine;web3;client", 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_port.to_string()) .arg("--Network.P2PPort") diff --git a/testing/execution_engine_integration/src/test_rig.rs b/testing/execution_engine_integration/src/test_rig.rs index 5b23af4fa..ee5e9cf2c 100644 --- a/testing/execution_engine_integration/src/test_rig.rs +++ b/testing/execution_engine_integration/src/test_rig.rs @@ -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 reqwest::{header::CONTENT_TYPE, Client}; +use sensitive_url::SensitiveUrl; +use serde_json::{json, Value}; use std::sync::Arc; use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH}; use task_executor::TaskExecutor; @@ -8,7 +15,6 @@ use types::{ Address, ChainSpec, EthSpec, ExecutionBlockHash, ExecutionPayload, FullPayload, Hash256, MainnetEthSpec, Slot, Uint256, }; - const EXECUTION_ENGINE_START_TIMEOUT: Duration = Duration::from_secs(20); struct ExecutionPair { @@ -32,6 +38,63 @@ pub struct TestRig { _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 TestRig { pub fn new(generic_engine: E) -> Self { let log = environment::null_logger().unwrap(); @@ -125,6 +188,20 @@ impl TestRig { pub async fn perform_tests(&self) { 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. */ @@ -157,6 +234,17 @@ impl TestRig { .unwrap() ); + // Submit transactions before getting payload + let txs = transactions::(account1, account2); + for tx in txs.clone().into_iter() { + self.ee_a + .execution_engine + .provider + .send_transaction(tx, None) + .await + .unwrap(); + } + /* * Execution Engine A: * @@ -168,6 +256,45 @@ impl TestRig { let prev_randao = Hash256::zero(); let finalized_block_hash = ExecutionBlockHash::zero(); 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 .ee_a .execution_layer @@ -184,6 +311,8 @@ impl TestRig { .unwrap() .execution_payload; + assert_eq!(valid_payload.transactions.len(), txs.len()); + /* * Execution Engine A: * diff --git a/testing/execution_engine_integration/src/transactions.rs b/testing/execution_engine_integration/src/transactions.rs new file mode 100644 index 000000000..144946682 --- /dev/null +++ b/testing/execution_engine_integration/src/transactions.rs @@ -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(account1: Address, account2: Address) -> Vec { + vec![ + Transaction::Transfer(account1, account2).transaction::(), + Transaction::TransferLegacy(account1, account2).transaction::(), + Transaction::TransferAccessList(account1, account2).transaction::(), + Transaction::DeployDepositContract(account1).transaction::(), + Transaction::DepositDepositContract { + sender: account1, + deposit_contract_address: ethers_core::types::Address::from_slice( + &hex::decode(&DEPOSIT_CONTRACT_ADDRESS).unwrap(), + ), + } + .transaction::(), + ] +} + +impl Transaction { + pub fn transaction(&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() + } + } + } +}