From ebc04289d67527059e8aee6a79fbe213f64ac76a Mon Sep 17 00:00:00 2001 From: refcell Date: Mon, 29 Apr 2024 17:35:51 -0700 Subject: [PATCH] chore(runner): port types and providers to alloy --- Cargo.lock | 72 ++++++++++++++++++++++++++------- Cargo.toml | 7 +++- src/engine/fork.rs | 10 +++-- src/engine/payload.rs | 48 ++++++++++++++++++++++ src/runner/mod.rs | 92 ++++++++++++++++++++++++++----------------- 5 files changed, 172 insertions(+), 57 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1543e90b..a88dc5e0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -156,7 +156,7 @@ checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" [[package]] name = "alloy-consensus" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=e3f2f07#e3f2f075a9e7ad9753a255dbe71dbad6a91ba96d" +source = "git+https://github.com/alloy-rs/alloy#0ac4e49ba5899d4a9db7cc876d9f54f5a24232d4" dependencies = [ "alloy-eips", "alloy-primitives", @@ -170,7 +170,7 @@ dependencies = [ [[package]] name = "alloy-eips" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=e3f2f07#e3f2f075a9e7ad9753a255dbe71dbad6a91ba96d" +source = "git+https://github.com/alloy-rs/alloy#0ac4e49ba5899d4a9db7cc876d9f54f5a24232d4" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -183,28 +183,30 @@ dependencies = [ [[package]] name = "alloy-genesis" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=e3f2f07#e3f2f075a9e7ad9753a255dbe71dbad6a91ba96d" +source = "git+https://github.com/alloy-rs/alloy#0ac4e49ba5899d4a9db7cc876d9f54f5a24232d4" dependencies = [ "alloy-primitives", "alloy-serde", "serde", + "serde_json", ] [[package]] name = "alloy-json-rpc" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=e3f2f07#e3f2f075a9e7ad9753a255dbe71dbad6a91ba96d" +source = "git+https://github.com/alloy-rs/alloy#0ac4e49ba5899d4a9db7cc876d9f54f5a24232d4" dependencies = [ "alloy-primitives", "serde", "serde_json", "thiserror", + "tracing", ] [[package]] name = "alloy-network" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=e3f2f07#e3f2f075a9e7ad9753a255dbe71dbad6a91ba96d" +source = "git+https://github.com/alloy-rs/alloy#0ac4e49ba5899d4a9db7cc876d9f54f5a24232d4" dependencies = [ "alloy-consensus", "alloy-eips", @@ -212,6 +214,7 @@ dependencies = [ "alloy-primitives", "alloy-rpc-types", "alloy-signer", + "alloy-sol-types", "async-trait", "futures-utils-wasm", "thiserror", @@ -242,8 +245,9 @@ dependencies = [ [[package]] name = "alloy-provider" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=e3f2f07#e3f2f075a9e7ad9753a255dbe71dbad6a91ba96d" +source = "git+https://github.com/alloy-rs/alloy#0ac4e49ba5899d4a9db7cc876d9f54f5a24232d4" dependencies = [ + "alloy-eips", "alloy-json-rpc", "alloy-network", "alloy-primitives", @@ -257,6 +261,7 @@ dependencies = [ "auto_impl", "dashmap", "futures", + "futures-utils-wasm", "lru 0.12.3", "reqwest 0.12.4", "serde_json", @@ -290,7 +295,7 @@ dependencies = [ [[package]] name = "alloy-rpc-client" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=e3f2f07#e3f2f075a9e7ad9753a255dbe71dbad6a91ba96d" +source = "git+https://github.com/alloy-rs/alloy#0ac4e49ba5899d4a9db7cc876d9f54f5a24232d4" dependencies = [ "alloy-json-rpc", "alloy-transport", @@ -310,7 +315,7 @@ dependencies = [ [[package]] name = "alloy-rpc-types" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=e3f2f07#e3f2f075a9e7ad9753a255dbe71dbad6a91ba96d" +source = "git+https://github.com/alloy-rs/alloy#0ac4e49ba5899d4a9db7cc876d9f54f5a24232d4" dependencies = [ "alloy-consensus", "alloy-eips", @@ -328,7 +333,7 @@ dependencies = [ [[package]] name = "alloy-rpc-types-trace" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=e3f2f07#e3f2f075a9e7ad9753a255dbe71dbad6a91ba96d" +source = "git+https://github.com/alloy-rs/alloy#0ac4e49ba5899d4a9db7cc876d9f54f5a24232d4" dependencies = [ "alloy-primitives", "alloy-rpc-types", @@ -340,7 +345,7 @@ dependencies = [ [[package]] name = "alloy-serde" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=e3f2f07#e3f2f075a9e7ad9753a255dbe71dbad6a91ba96d" +source = "git+https://github.com/alloy-rs/alloy#0ac4e49ba5899d4a9db7cc876d9f54f5a24232d4" dependencies = [ "alloy-primitives", "serde", @@ -350,7 +355,7 @@ dependencies = [ [[package]] name = "alloy-signer" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=e3f2f07#e3f2f075a9e7ad9753a255dbe71dbad6a91ba96d" +source = "git+https://github.com/alloy-rs/alloy#0ac4e49ba5899d4a9db7cc876d9f54f5a24232d4" dependencies = [ "alloy-primitives", "async-trait", @@ -407,7 +412,7 @@ dependencies = [ [[package]] name = "alloy-transport" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=e3f2f07#e3f2f075a9e7ad9753a255dbe71dbad6a91ba96d" +source = "git+https://github.com/alloy-rs/alloy#0ac4e49ba5899d4a9db7cc876d9f54f5a24232d4" dependencies = [ "alloy-json-rpc", "base64 0.22.0", @@ -425,7 +430,7 @@ dependencies = [ [[package]] name = "alloy-transport-http" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=e3f2f07#e3f2f075a9e7ad9753a255dbe71dbad6a91ba96d" +source = "git+https://github.com/alloy-rs/alloy#0ac4e49ba5899d4a9db7cc876d9f54f5a24232d4" dependencies = [ "alloy-json-rpc", "alloy-transport", @@ -3059,6 +3064,22 @@ dependencies = [ "tokio-native-tls", ] +[[package]] +name = "hyper-tls" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" +dependencies = [ + "bytes", + "http-body-util", + "hyper 1.3.1", + "hyper-util", + "native-tls", + "tokio", + "tokio-native-tls", + "tower-service", +] + [[package]] name = "hyper-util" version = "0.1.3" @@ -3976,6 +3997,7 @@ name = "magi" version = "0.1.0" dependencies = [ "again", + "alloy-consensus", "alloy-primitives", "alloy-provider", "alloy-rpc-types", @@ -5305,7 +5327,7 @@ dependencies = [ "http-body 0.4.6", "hyper 0.14.28", "hyper-rustls", - "hyper-tls", + "hyper-tls 0.5.0", "ipnet", "js-sys", "log", @@ -5315,7 +5337,7 @@ dependencies = [ "percent-encoding", "pin-project-lite", "rustls 0.21.10", - "rustls-pemfile", + "rustls-pemfile 1.0.4", "serde", "serde_json", "serde_urlencoded", @@ -5347,19 +5369,23 @@ dependencies = [ "http-body 1.0.0", "http-body-util", "hyper 1.3.1", + "hyper-tls 0.6.0", "hyper-util", "ipnet", "js-sys", "log", "mime", + "native-tls", "once_cell", "percent-encoding", "pin-project-lite", + "rustls-pemfile 2.1.2", "serde", "serde_json", "serde_urlencoded", "sync_wrapper", "tokio", + "tokio-native-tls", "tower-service", "url", "wasm-bindgen", @@ -5601,6 +5627,22 @@ dependencies = [ "base64 0.21.7", ] +[[package]] +name = "rustls-pemfile" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29993a25686778eb88d4189742cd713c9bce943bc54251a33509dc63cbacf73d" +dependencies = [ + "base64 0.22.0", + "rustls-pki-types", +] + +[[package]] +name = "rustls-pki-types" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "beb461507cee2c2ff151784c52762cf4d9ff6a61f3e80968600ed24fa837fa54" + [[package]] name = "rustls-webpki" version = "0.101.7" diff --git a/Cargo.toml b/Cargo.toml index 7fd7d7e9..686ebf6d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,8 +29,11 @@ again = "0.1" # Alloy types alloy-primitives = { version = "0.7.1", default-features = false, features = ["serde"] } -alloy-provider = { git = "https://github.com/alloy-rs/alloy", rev = "e3f2f07" } -alloy-rpc-types = { git = "https://github.com/alloy-rs/alloy", rev = "e3f2f07" } +alloy-provider = { git = "https://github.com/alloy-rs/alloy" } +alloy-rpc-types = { git = "https://github.com/alloy-rs/alloy" } +alloy-consensus = { git = "https://github.com/alloy-rs/alloy" } +alloy-rlp = { git = "https://github.com/alloy-rs/alloy" } +alloy-eips = { git = "https://github.com/alloy-rs/alloy" } # Logging and Metrics chrono = "0.4.22" diff --git a/src/engine/fork.rs b/src/engine/fork.rs index 8656b95b..c078a5b4 100644 --- a/src/engine/fork.rs +++ b/src/engine/fork.rs @@ -1,3 +1,4 @@ +use alloy_primitives::B256; use ethers::types::H256; use serde::{Deserialize, Serialize}; @@ -33,11 +34,12 @@ pub struct ForkchoiceState { impl ForkchoiceState { /// Creates a new fork choice state with the given head block hash. /// The safe and finalized block hashes are set to the head block hash. - pub fn from_single_head(head_block_hash: H256) -> Self { + pub fn from_single_head(head_block_hash: B256) -> Self { + let hash = H256::from_slice(head_block_hash.as_slice()); Self { - head_block_hash, - safe_block_hash: head_block_hash, - finalized_block_hash: head_block_hash, + head_block_hash: hash, + safe_block_hash: hash, + finalized_block_hash: hash, } } } diff --git a/src/engine/payload.rs b/src/engine/payload.rs index 52b03cd5..61568e2d 100644 --- a/src/engine/payload.rs +++ b/src/engine/payload.rs @@ -1,3 +1,8 @@ +use alloy_consensus::TxEnvelope; +use alloy_eips::eip2718::Encodable2718; +use alloy_rlp::Encodable; +use alloy_rpc_types::Block as AlloyBlock; +use alloy_rpc_types::BlockTransactions; use ethers::types::{Block, Bytes, Transaction, H160, H256, U64}; use eyre::Result; use serde::{Deserialize, Serialize}; @@ -50,6 +55,49 @@ pub struct ExecutionPayload { pub excess_blob_gas: Option, } +impl TryFrom for ExecutionPayload { + type Error = eyre::Report; + + /// Converts a [Block] to an [ExecutionPayload] + fn try_from(value: AlloyBlock) -> Result { + let txs = if let BlockTransactions::Full(txs) = value.transactions { + txs + } else { + return Err(eyre::eyre!("Expected full transactions")); + }; + let encoded_txs = (txs + .into_iter() + .map(|tx| { + let envelope = TxEnvelope::from(tx); + let encoded = envelope.encoded_2718(); + RawTransaction(encoded.to_vec()) + }) + .collect::>()) + .to_vec(); + + Ok(ExecutionPayload { + parent_hash: H256::from_slice(value.header.parent_hash.as_slice()), + fee_recipient: H160::from_slice(SystemAccounts::default().fee_vault.as_slice()), + state_root: H256::from_slice(value.header.state_root.as_slice()), + receipts_root: H256::from_slice(value.header.receipts_root.as_slice()), + logs_bloom: value.header.logs_bloom.0.to_vec().into(), + prev_randao: H256::from_slice(value.header.mix_hash.unwrap().as_slice()), + block_number: value.header.number.unwrap().into(), + gas_limit: (value.header.gas_limit as u64).into(), + gas_used: (value.header.gas_used as u64).into(), + timestamp: (value.header.timestamp as u64).into(), + extra_data: Bytes::from(value.header.extra_data.0), + base_fee_per_gas: (value.header.base_fee_per_gas.unwrap_or_else(|| 0u64.into()) as u64) + .into(), + block_hash: H256::from_slice(value.header.hash.unwrap().as_slice()), + transactions: encoded_txs, + withdrawals: Some(Vec::new()), + blob_gas_used: value.header.blob_gas_used.map(|v| (v as u64).into()), + excess_blob_gas: value.header.excess_blob_gas.map(|v| (v as u64).into()), + }) + } +} + impl TryFrom> for ExecutionPayload { type Error = eyre::Report; diff --git a/src/runner/mod.rs b/src/runner/mod.rs index 251ceeb4..abd877ab 100644 --- a/src/runner/mod.rs +++ b/src/runner/mod.rs @@ -1,9 +1,10 @@ -use std::{process, time::Duration}; +use std::{process, str::FromStr, time::Duration}; + +use alloy_primitives::B256; +use alloy_provider::ext::AdminApi; +use alloy_provider::{Provider, ProviderBuilder, ReqwestProvider}; +use alloy_rpc_types::{BlockId, BlockNumberOrTag, BlockTransactions, RpcBlockHash}; -use ethers::{ - providers::{Http, Middleware, Provider}, - types::{BlockId, BlockNumber, H256}, -}; use eyre::Result; use tokio::{ sync::watch::{channel, Receiver}, @@ -100,21 +101,27 @@ impl Runner { /// /// Note: the `admin` RPC method must be available on the execution client as checkpoint_sync relies on `admin_addPeer` pub async fn checkpoint_sync(&self) -> Result<()> { - let l2_provider = Provider::try_from(&self.config.l2_rpc_url)?; - let checkpoint_sync_url = - Provider::try_from(self.config.checkpoint_sync_url.as_ref().ok_or(eyre::eyre!( - "a checkpoint sync rpc url is required for checkpoint sync" - ))?)?; + let l2_rpc_url = reqwest::Url::parse(&self.config.l2_rpc_url) + .map_err(|err| eyre::eyre!(format!("unable to parse l2_rpc_url: {err}")))?; + let l2_provider = ProviderBuilder::new().on_http(l2_rpc_url); + + let checkpoint_sync_url = self.config.checkpoint_sync_url.as_ref().ok_or(eyre::eyre!( + "checkpoint_sync_url is required for checkpoint sync mode" + ))?; + let checkpoint_sync_url = reqwest::Url::parse(checkpoint_sync_url) + .map_err(|err| eyre::eyre!(format!("unable to parse checkpoint_sync_url: {err}")))?; + let checkpoint_sync_provider = ProviderBuilder::new().on_http(checkpoint_sync_url); let checkpoint_block = match self.checkpoint_hash { Some(ref checkpoint) => { - let block_hash: H256 = checkpoint - .parse() - .expect("invalid checkpoint block hash provided"); - - match Self::is_epoch_boundary(block_hash, &checkpoint_sync_url).await? { - true => checkpoint_sync_url - .get_block_with_txs(block_hash) + let hash = B256::from_str(checkpoint) + .map_err(|_| eyre::eyre!("invalid checkpoint block hash provided"))?; + let block_hash = RpcBlockHash::from_hash(hash, None); + let block_id = BlockId::Hash(block_hash); + + match Self::is_epoch_boundary(block_id, &checkpoint_sync_provider).await? { + true => checkpoint_sync_provider + .get_block(BlockId::Hash(block_hash), true) .await? .expect("could not get checkpoint block"), false => { @@ -126,25 +133,30 @@ impl Runner { None => { tracing::info!("finding the latest epoch boundary to use as checkpoint"); - let mut block_number = checkpoint_sync_url.get_block_number().await?; - while !Self::is_epoch_boundary(block_number, &checkpoint_sync_url).await? { + let mut block_number = checkpoint_sync_provider.get_block_number().await?; + while !Self::is_epoch_boundary(block_number, &checkpoint_sync_provider).await? { self.check_shutdown()?; - block_number -= 1.into(); + block_number -= 1u64; } - let block = checkpoint_sync_url - .get_block(BlockId::Number(BlockNumber::Number(block_number))) + let block = checkpoint_sync_provider + .get_block( + BlockId::Number(BlockNumberOrTag::Number(block_number)), + false, + ) .await? .expect("could not get block"); - checkpoint_sync_url - .get_block_with_txs(block.hash.expect("block hash is missing")) + let block_hash = block.header.hash.expect("block hash is missing"); + let block_hash = BlockId::Hash(RpcBlockHash::from_hash(block_hash, None)); + checkpoint_sync_provider + .get_block(block_hash, true) .await? .expect("could not get checkpoint block") } }; - let checkpoint_hash = checkpoint_block.hash.expect("block hash is missing"); + let checkpoint_hash = checkpoint_block.header.hash.expect("block hash is missing"); tracing::info!("using checkpoint block {}", checkpoint_hash); let engine_api = EngineApi::new(&self.config.l2_engine_url, &self.config.jwt_secret); @@ -154,7 +166,8 @@ impl Runner { } // if the checkpoint block is already synced, start from the finalized head - if l2_provider.get_block(checkpoint_hash).await?.is_some() { + let block_hash = BlockId::Hash(RpcBlockHash::from_hash(checkpoint_hash, None)); + if l2_provider.get_block(block_hash, false).await?.is_some() { tracing::warn!("finalized head is above the checkpoint block"); self.start_driver().await?; return Ok(()); @@ -163,7 +176,7 @@ impl Runner { // this is a temporary fix to allow execution layer peering to work // TODO: use a list of whitelisted bootnodes instead tracing::info!("adding trusted peer to the execution layer"); - l2_provider.add_peer(TRUSTED_PEER_ENODE.to_string()).await?; + l2_provider.add_peer(TRUSTED_PEER_ENODE).await?; // build the execution payload from the checkpoint block and send it to the execution client let checkpoint_payload = ExecutionPayload::try_from(checkpoint_block)?; @@ -186,7 +199,11 @@ impl Runner { tracing::info!("syncing execution client to the checkpoint block...",); - while l2_provider.get_block_number().await? < checkpoint_payload.block_number { + let checkpoint_block_num: u64 = checkpoint_payload + .block_number + .try_into() + .map_err(|_| eyre::eyre!("could not convert checkpoint block number to u64"))?; + while l2_provider.get_block_number().await? < checkpoint_block_num { self.check_shutdown()?; sleep(Duration::from_secs(3)).await; } @@ -223,18 +240,21 @@ impl Runner { /// Returns `true` if the L2 block is the first in an epoch (sequence number 0) async fn is_epoch_boundary + Send + Sync>( block: T, - checkpoint_sync_url: &Provider, + checkpoint_sync_provider: &ReqwestProvider, ) -> Result { - let l2_block = checkpoint_sync_url - .get_block_with_txs(block) + let l2_block = checkpoint_sync_provider + .get_block(block.into(), true) .await? .ok_or_else(|| eyre::eyre!("could not find block"))?; - let predeploy = ethers::types::Address::from_slice( - SystemAccounts::default().attributes_predeploy.as_slice(), - ); - let sequence_number = &l2_block - .transactions + let predeploy = SystemAccounts::default().attributes_predeploy; + let full_txs = if let BlockTransactions::Full(txs) = l2_block.transactions { + txs + } else { + tracing::error!("could not get full transactions from block"); + return Ok(false); + }; + let sequence_number = &full_txs .iter() .find(|tx| tx.to.map_or(false, |to| to == predeploy)) .expect("could not find setL1BlockValues tx in the epoch boundary search")