Migrate execution_engine_integration to alloy (#8140)

#6022


  Migrate the `execution_engine_integration`  tests to the `alloy` ecosystem. This removes the last remaining `ethers` dependencies


Co-Authored-By: Mac L <mjladson@pm.me>
This commit is contained in:
Mac L
2025-11-12 08:43:19 +04:00
committed by GitHub
parent 53e73fa376
commit fff248d41b
6 changed files with 546 additions and 1058 deletions

1487
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -147,10 +147,6 @@ ethereum_hashing = "0.7.0"
ethereum_serde_utils = "0.8.0" ethereum_serde_utils = "0.8.0"
ethereum_ssz = "0.9.0" ethereum_ssz = "0.9.0"
ethereum_ssz_derive = "0.9.0" ethereum_ssz_derive = "0.9.0"
ethers-core = "1"
ethers-middleware = { version = "1", default-features = false }
ethers-providers = { version = "1", default-features = false }
ethers-signers = { version = "1", default-features = false }
execution_layer = { path = "beacon_node/execution_layer" } execution_layer = { path = "beacon_node/execution_layer" }
exit-future = "0.2" exit-future = "0.2"
filesystem = { path = "common/filesystem" } filesystem = { path = "common/filesystem" }

View File

@@ -7,12 +7,13 @@ edition = { workspace = true }
portable = ["types/portable"] portable = ["types/portable"]
[dependencies] [dependencies]
alloy-network = "1.0"
alloy-primitives = { workspace = true }
alloy-provider = "1.0"
alloy-rpc-types-eth = { workspace = true }
alloy-signer-local = "1.0"
async-channel = { workspace = true } async-channel = { workspace = true }
deposit_contract = { workspace = true } deposit_contract = { workspace = true }
ethers-core = { workspace = true }
ethers-middleware = { workspace = true }
ethers-providers = { workspace = true }
ethers-signers = { workspace = true }
execution_layer = { workspace = true } execution_layer = { workspace = true }
fork_choice = { workspace = true } fork_choice = { workspace = true }
futures = { workspace = true } futures = { workspace = true }

View File

@@ -1,6 +1,7 @@
use ethers_providers::{Http, Provider}; use alloy_provider::ProviderBuilder;
use execution_layer::DEFAULT_JWT_FILE; use execution_layer::DEFAULT_JWT_FILE;
use network_utils::unused_port::unused_tcp4_port; use network_utils::unused_port::unused_tcp4_port;
use reqwest::Url;
use sensitive_url::SensitiveUrl; use sensitive_url::SensitiveUrl;
use std::path::PathBuf; use std::path::PathBuf;
use std::process::Child; use std::process::Child;
@@ -34,7 +35,7 @@ pub struct ExecutionEngine<E> {
http_port: u16, http_port: u16,
http_auth_port: u16, http_auth_port: u16,
child: Child, child: Child,
pub provider: Provider<Http>, pub provider: Box<dyn alloy_provider::Provider + Send + Sync>,
} }
impl<E> Drop for ExecutionEngine<E> { impl<E> Drop for ExecutionEngine<E> {
@@ -53,8 +54,9 @@ impl<E: GenericExecutionEngine> ExecutionEngine<E> {
let http_port = unused_tcp4_port().unwrap(); let http_port = unused_tcp4_port().unwrap();
let http_auth_port = unused_tcp4_port().unwrap(); let http_auth_port = unused_tcp4_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)) let provider = Box::new(ProviderBuilder::new().connect_http(
.expect("failed to instantiate ethers provider"); Url::parse(&format!("http://localhost:{}", http_port)).expect("failed to parse URL"),
));
Self { Self {
engine, engine,
datadir, datadir,

View File

@@ -2,9 +2,10 @@ use crate::execution_engine::{
ACCOUNT1, ACCOUNT2, ExecutionEngine, GenericExecutionEngine, KEYSTORE_PASSWORD, PRIVATE_KEYS, ACCOUNT1, ACCOUNT2, ExecutionEngine, GenericExecutionEngine, KEYSTORE_PASSWORD, PRIVATE_KEYS,
}; };
use crate::transactions::transactions; use crate::transactions::transactions;
use ethers_middleware::SignerMiddleware; use alloy_network::{EthereumWallet, TransactionBuilder};
use ethers_providers::Middleware; use alloy_primitives::Address as AlloyAddress;
use ethers_signers::LocalWallet; use alloy_provider::{Provider, ProviderBuilder};
use alloy_signer_local::PrivateKeySigner;
use execution_layer::test_utils::DEFAULT_GAS_LIMIT; use execution_layer::test_utils::DEFAULT_GAS_LIMIT;
use execution_layer::{ use execution_layer::{
BlockProposalContentsType, BuilderParams, ChainHealth, ExecutionLayer, PayloadAttributes, BlockProposalContentsType, BuilderParams, ChainHealth, ExecutionLayer, PayloadAttributes,
@@ -202,12 +203,13 @@ impl<Engine: GenericExecutionEngine> TestRig<Engine> {
self.wait_until_synced().await; self.wait_until_synced().await;
// Create a local signer in case we need to sign transactions locally // Create a local signer in case we need to sign transactions locally
let wallet1: LocalWallet = PRIVATE_KEYS[0].parse().expect("Invalid private key"); let private_key_signer: PrivateKeySigner =
let signer = SignerMiddleware::new(&self.ee_a.execution_engine.provider, wallet1); PRIVATE_KEYS[0].parse().expect("Invalid private key");
let wallet = EthereumWallet::from(private_key_signer);
// We hardcode the accounts here since some EEs start with a default unlocked account // 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 account1 = AlloyAddress::from_slice(&hex::decode(ACCOUNT1).unwrap());
let account2 = ethers_core::types::Address::from_slice(&hex::decode(ACCOUNT2).unwrap()); let account2 = AlloyAddress::from_slice(&hex::decode(ACCOUNT2).unwrap());
/* /*
* Read the terminal block hash from both pairs, check it's equal. * Read the terminal block hash from both pairs, check it's equal.
@@ -237,11 +239,18 @@ impl<Engine: GenericExecutionEngine> TestRig<Engine> {
if self.use_local_signing { if self.use_local_signing {
// Sign locally with the Signer middleware // Sign locally with the Signer middleware
for (i, tx) in txs.clone().into_iter().enumerate() { for (i, mut tx) in txs.clone().into_iter().enumerate() {
// The local signer uses eth_sendRawTransaction, so we need to manually set the nonce // The local signer uses eth_sendRawTransaction, so we need to manually set the nonce
let mut tx = tx.clone(); tx = tx.with_nonce(i as u64);
tx.set_nonce(i as u64); let wallet_provider = ProviderBuilder::new().wallet(wallet.clone()).connect_http(
let pending_tx = signer.send_transaction(tx, None).await.unwrap(); self.ee_a
.execution_engine
.http_url()
.to_string()
.parse()
.unwrap(),
);
let pending_tx = wallet_provider.send_transaction(tx).await.unwrap();
pending_txs.push(pending_tx); pending_txs.push(pending_tx);
} }
} else { } else {
@@ -261,7 +270,7 @@ impl<Engine: GenericExecutionEngine> TestRig<Engine> {
.ee_a .ee_a
.execution_engine .execution_engine
.provider .provider
.send_transaction(tx, None) .send_transaction(tx)
.await .await
.unwrap(); .unwrap();
pending_txs.push(pending_tx); pending_txs.push(pending_tx);
@@ -446,11 +455,10 @@ impl<Engine: GenericExecutionEngine> TestRig<Engine> {
// Verify that all submitted txs were successful // Verify that all submitted txs were successful
for pending_tx in pending_txs { for pending_tx in pending_txs {
let tx_receipt = pending_tx.await.unwrap().unwrap(); let tx_receipt = pending_tx.get_receipt().await.unwrap();
assert_eq!( assert!(
tx_receipt.status, tx_receipt.status(),
Some(1.into()), "Tx index {:?} has invalid status ",
"Tx index {} has invalid status ",
tx_receipt.transaction_index tx_receipt.transaction_index
); );
} }

View File

@@ -1,8 +1,7 @@
use alloy_network::TransactionBuilder;
use alloy_primitives::{Address, U256};
use alloy_rpc_types_eth::{AccessList, TransactionRequest};
use deposit_contract::{BYTECODE, CONTRACT_DEPLOY_GAS, DEPOSIT_GAS, encode_eth1_tx_data}; use deposit_contract::{BYTECODE, CONTRACT_DEPLOY_GAS, DEPOSIT_GAS, encode_eth1_tx_data};
use ethers_core::types::{
Address, Bytes, Eip1559TransactionRequest, TransactionRequest, U256,
transaction::{eip2718::TypedTransaction, eip2930::AccessList},
};
use types::{DepositData, EthSpec, FixedBytesExtended, Hash256, Keypair, Signature}; use types::{DepositData, EthSpec, FixedBytesExtended, Hash256, Keypair, Signature};
/// Hardcoded deposit contract address based on sender address and nonce /// Hardcoded deposit contract address based on sender address and nonce
@@ -21,7 +20,7 @@ pub enum Transaction {
} }
/// Get a list of transactions to publish to the execution layer. /// Get a list of transactions to publish to the execution layer.
pub fn transactions<E: EthSpec>(account1: Address, account2: Address) -> Vec<TypedTransaction> { pub fn transactions<E: EthSpec>(account1: Address, account2: Address) -> Vec<TransactionRequest> {
vec![ vec![
Transaction::Transfer(account1, account2).transaction::<E>(), Transaction::Transfer(account1, account2).transaction::<E>(),
Transaction::TransferLegacy(account1, account2).transaction::<E>(), Transaction::TransferLegacy(account1, account2).transaction::<E>(),
@@ -29,7 +28,7 @@ pub fn transactions<E: EthSpec>(account1: Address, account2: Address) -> Vec<Typ
Transaction::DeployDepositContract(account1).transaction::<E>(), Transaction::DeployDepositContract(account1).transaction::<E>(),
Transaction::DepositDepositContract { Transaction::DepositDepositContract {
sender: account1, sender: account1,
deposit_contract_address: ethers_core::types::Address::from_slice( deposit_contract_address: Address::from_slice(
&hex::decode(DEPOSIT_CONTRACT_ADDRESS).unwrap(), &hex::decode(DEPOSIT_CONTRACT_ADDRESS).unwrap(),
), ),
} }
@@ -38,33 +37,36 @@ pub fn transactions<E: EthSpec>(account1: Address, account2: Address) -> Vec<Typ
} }
impl Transaction { impl Transaction {
pub fn transaction<E: EthSpec>(&self) -> TypedTransaction { pub fn transaction<E: EthSpec>(&self) -> TransactionRequest {
match &self { match &self {
Self::TransferLegacy(from, to) => TransactionRequest::new() Self::TransferLegacy(from, to) => TransactionRequest::default()
.from(*from) .from(*from)
.to(*to) .to(*to)
.value(1) .value(U256::from(1))
.into(), .with_gas_price(1_000_000_000u128), // 1 gwei
Self::Transfer(from, to) => Eip1559TransactionRequest::new() Self::Transfer(from, to) => TransactionRequest::default()
.from(*from) .from(*from)
.to(*to) .to(*to)
.value(1) .value(U256::from(1))
.into(), .with_max_fee_per_gas(2_000_000_000u128)
Self::TransferAccessList(from, to) => TransactionRequest::new() .with_max_priority_fee_per_gas(1_000_000_000u128),
Self::TransferAccessList(from, to) => TransactionRequest::default()
.from(*from) .from(*from)
.to(*to) .to(*to)
.value(1) .value(U256::from(1))
.with_access_list(AccessList::default()) .with_access_list(AccessList::default())
.into(), .with_gas_price(1_000_000_000u128), // 1 gwei
Self::DeployDepositContract(addr) => { Self::DeployDepositContract(addr) => {
let mut bytecode = String::from_utf8(BYTECODE.to_vec()).unwrap(); let mut bytecode = String::from_utf8(BYTECODE.to_vec()).unwrap();
bytecode.retain(|c| c.is_ascii_hexdigit()); bytecode.retain(|c| c.is_ascii_hexdigit());
let bytecode = hex::decode(&bytecode[1..]).unwrap(); let bytecode = hex::decode(&bytecode[1..]).unwrap();
TransactionRequest::new() let mut req = TransactionRequest::default()
.from(*addr) .from(*addr)
.data(Bytes::from(bytecode)) .with_input(bytecode)
.gas(CONTRACT_DEPLOY_GAS) .with_gas_limit(CONTRACT_DEPLOY_GAS.try_into().unwrap())
.into() .with_gas_price(1_000_000_000u128); // 1 gwei
req.set_create();
req
} }
Self::DepositDepositContract { Self::DepositDepositContract {
sender, sender,
@@ -80,13 +82,13 @@ impl Transaction {
signature: Signature::empty().into(), signature: Signature::empty().into(),
}; };
deposit.signature = deposit.create_signature(&keypair.sk, &E::default_spec()); deposit.signature = deposit.create_signature(&keypair.sk, &E::default_spec());
TransactionRequest::new() TransactionRequest::default()
.from(*sender) .from(*sender)
.to(*deposit_contract_address) .to(*deposit_contract_address)
.data(Bytes::from(encode_eth1_tx_data(&deposit).unwrap())) .with_input(encode_eth1_tx_data(&deposit).unwrap())
.gas(DEPOSIT_GAS) .with_gas_limit(DEPOSIT_GAS.try_into().unwrap())
.value(U256::from(amount) * U256::exp10(9)) .value(U256::from(amount) * U256::from(10).pow(U256::from(9)))
.into() .with_gas_price(1_000_000_000u128) // 1 gwei
} }
} }
} }