From 6a84d88cee0cac7cb374e847ccaf9eb4683174e9 Mon Sep 17 00:00:00 2001 From: Prudhvi Rampey Date: Thu, 7 Nov 2024 20:30:00 -0800 Subject: [PATCH 01/12] feat: verify signer on client start --- Cargo.lock | 2 + core/src/lib.rs | 3 +- ethereum/src/config/mod.rs | 21 +++++++++++ opstack/Cargo.toml | 2 + opstack/src/consensus.rs | 75 ++++++++++++++++++++++++++++++++++++-- 5 files changed, 97 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 696a1d8d..538cd809 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3153,7 +3153,9 @@ dependencies = [ "figment", "futures", "getrandom 0.2.15", + "helios-consensus-core", "helios-core", + "helios-ethereum", "hex", "libp2p", "libp2p-identity", diff --git a/core/src/lib.rs b/core/src/lib.rs index 91378f0a..88c4377e 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -4,5 +4,4 @@ pub mod errors; pub mod network_spec; pub mod time; pub mod types; - -mod execution; +pub mod execution; diff --git a/ethereum/src/config/mod.rs b/ethereum/src/config/mod.rs index 55af3e37..6a9dcb78 100644 --- a/ethereum/src/config/mod.rs +++ b/ethereum/src/config/mod.rs @@ -91,3 +91,24 @@ impl Config { } } } + +impl From for Config { + fn from(base: BaseConfig) -> Self { + Config { + rpc_bind_ip: Some(base.rpc_bind_ip), + rpc_port: Some(base.rpc_port), + consensus_rpc: base.consensus_rpc.unwrap_or_default(), + execution_rpc: String::new(), + checkpoint: None, + default_checkpoint: base.default_checkpoint, + chain: base.chain, + forks: base.forks, + max_checkpoint_age: base.max_checkpoint_age, + data_dir: base.data_dir, + fallback: None, + load_external_fallback: base.load_external_fallback, + strict_checkpoint_age: base.strict_checkpoint_age, + database_type: None, + } + } +} diff --git a/opstack/Cargo.toml b/opstack/Cargo.toml index 7affdca1..1315e96c 100644 --- a/opstack/Cargo.toml +++ b/opstack/Cargo.toml @@ -37,6 +37,8 @@ snap = "1" figment = { version = "0.10.7", features = ["toml", "env"] } helios-core = { path = "../core" } +helios-ethereum = { path = "../ethereum" } +helios-consensus-core = { path = "../ethereum/consensus-core" } [target.'cfg(not(target_arch = "wasm32"))'.dependencies] # server diff --git a/opstack/src/consensus.rs b/opstack/src/consensus.rs index 32bb4ebd..8a388f5d 100644 --- a/opstack/src/consensus.rs +++ b/opstack/src/consensus.rs @@ -1,12 +1,11 @@ -use std::time::Duration; - use alloy::consensus::Transaction as TxTrait; use alloy::primitives::{b256, fixed_bytes, keccak256, Address, B256, U256, U64}; use alloy::rlp::Decodable; -use alloy::rpc::types::{Parity, Signature, Transaction}; +use alloy::rpc::types::{EIP1186AccountProofResponse, Parity, Signature, Transaction}; use alloy_rlp::encode; -use eyre::Result; +use eyre::{Result, eyre}; use op_alloy_consensus::OpTxEnvelope; +use std::time::Duration; use tokio::sync::mpsc::Sender; use tokio::sync::{ mpsc::{channel, Receiver}, @@ -14,12 +13,21 @@ use tokio::sync::{ }; use triehash_ethereum::ordered_trie_root; +use helios_consensus_core::consensus_spec::MainnetConsensusSpec; use helios_core::consensus::Consensus; use helios_core::time::{interval, SystemTime, UNIX_EPOCH}; use helios_core::types::{Block, Transactions}; +use helios_ethereum::config::networks::mainnet; +use helios_ethereum::consensus::ConsensusClient as EthConsensusClient; +use std::sync::Arc; use crate::{config::Config, types::ExecutionPayload, SequencerCommitment}; +use helios_core::execution::proof::{encode_account, verify_proof}; +use helios_ethereum::database::ConfigDB; +use helios_ethereum::rpc::http_rpc::HttpRpc; +use tracing::{info, warn}; + pub struct ConsensusClient { block_recv: Option>>, finalized_block_recv: Option>>>, @@ -47,6 +55,7 @@ impl ConsensusClient { let run = wasm_bindgen_futures::spawn_local; run(async move { + inner.verify_unsafe_signer().await.unwrap(); // TODO: make this async let mut interval = interval(Duration::from_secs(1)); loop { _ = inner.advance().await; @@ -134,6 +143,64 @@ impl Inner { Ok(()) } + + async fn verify_unsafe_signer(&mut self) -> Result<()> { + let eth_config = mainnet(); // TODO: Do we account for testnets too? + let mut eth_consensus = EthConsensusClient::::new( + ð_config.consensus_rpc.clone().unwrap(), + Arc::new(eth_config.into()), + )?; + let block = eth_consensus.block_recv().unwrap().recv().await.unwrap(); + // Query proof from op consensus server + let req = format!( + "{}signer_proof/{}", + self.server_url, + block.number.to::() + ); + let proof = reqwest::get(req) + .await? + .json::() + .await?; + + // Verify unsafe signer + // with account proof + let account_path = keccak256(proof.address).to_vec(); + let account_encoded = encode_account(&proof); + let is_valid = verify_proof( + &proof.account_proof, + block.state_root.as_slice(), + &account_path, + &account_encoded, + ); + if !is_valid { + warn!(target: "helios::opstack", "account proof invalid"); + return Err(eyre!("account proof invalid")); + } + // with storage proof + let storage_proof = proof.storage_proof[0].clone(); + let key = storage_proof.key.0; + let key_hash = keccak256(key); + let value = encode(storage_proof.value); + let is_valid = verify_proof( + &storage_proof.proof, + proof.storage_hash.as_slice(), + key_hash.as_slice(), + &value, + ); + if !is_valid { + warn!(target: "helios::opstack", "storage proof invalid"); + return Err(eyre!("storage proof invalid")); + } + // Replace unsafe signer if different + let verified_signer = Address::from_slice(&storage_proof.value.to_be_bytes::<32>()[12..32]); + if verified_signer != self.unsafe_signer { + info!(target: "helios::opstack", "unsafe signer updated: {}", verified_signer); + self.unsafe_signer = verified_signer; + } + // Shutdown eth consensus client + eth_consensus.shutdown()?; + Ok(()) + } } fn payload_to_block(value: ExecutionPayload) -> Result> { From 691b873b6a84ad52fd9351c6c12a146efe7e2b98 Mon Sep 17 00:00:00 2001 From: Prudhvi Rampey Date: Thu, 7 Nov 2024 21:06:28 -0800 Subject: [PATCH 02/12] feat: add proof endpoint to consensus server --- core/src/execution/mod.rs | 3 +-- opstack/bin/server.rs | 6 +++++ opstack/src/builder.rs | 1 + opstack/src/config.rs | 5 +++++ opstack/src/server/mod.rs | 46 +++++++++++++++++++++++++++++++++++++-- 5 files changed, 57 insertions(+), 4 deletions(-) diff --git a/core/src/execution/mod.rs b/core/src/execution/mod.rs index bc8979d6..0849758e 100644 --- a/core/src/execution/mod.rs +++ b/core/src/execution/mod.rs @@ -25,8 +25,7 @@ pub mod evm; pub mod rpc; pub mod state; pub mod types; - -mod proof; +pub mod proof; #[derive(Clone)] pub struct ExecutionClient> { diff --git a/opstack/bin/server.rs b/opstack/bin/server.rs index 636232f7..c1fc5944 100644 --- a/opstack/bin/server.rs +++ b/opstack/bin/server.rs @@ -21,16 +21,20 @@ async fn main() -> Result<()> { let chain_id = config.chain.chain_id; let unsafe_signer = config.chain.unsafe_signer; + let system_config_contract = config.chain.system_config_contract; let server_addr = cli.server_address; let gossip_addr = cli.gossip_address; let replica_urls = cli.replica_urls.unwrap_or_default(); + let execution_rpc = cli.execution_rpc; start_server( server_addr, gossip_addr, chain_id, unsafe_signer, + system_config_contract, replica_urls, + execution_rpc, ) .await?; @@ -67,4 +71,6 @@ struct Cli { gossip_address: SocketAddr, #[clap(short, long, value_delimiter = ',')] replica_urls: Option>, + #[clap(short, long)] + execution_rpc: Url, } diff --git a/opstack/src/builder.rs b/opstack/src/builder.rs index 0516075f..4ef2e0c5 100644 --- a/opstack/src/builder.rs +++ b/opstack/src/builder.rs @@ -82,6 +82,7 @@ impl OpStackClientBuilder { chain: ChainConfig { chain_id, unsafe_signer, + system_config_contract: Address::ZERO, }, } }; diff --git a/opstack/src/config.rs b/opstack/src/config.rs index 547b33f0..97b77461 100644 --- a/opstack/src/config.rs +++ b/opstack/src/config.rs @@ -24,6 +24,7 @@ pub struct Config { pub struct ChainConfig { pub chain_id: u64, pub unsafe_signer: Address, + pub system_config_contract: Address, } #[derive(Serialize, Deserialize)] @@ -73,6 +74,7 @@ impl From for NetworkConfig { chain: ChainConfig { chain_id: 10, unsafe_signer: address!("AAAA45d9549EDA09E70937013520214382Ffc4A2"), + system_config_contract: address!("229047fed2591dbec1eF1118d64F7aF3dB9EB290"), }, }, Network::Base => NetworkConfig { @@ -80,6 +82,7 @@ impl From for NetworkConfig { chain: ChainConfig { chain_id: 8453, unsafe_signer: address!("Af6E19BE0F9cE7f8afd49a1824851023A8249e8a"), + system_config_contract: address!("73a79Fab69143498Ed3712e519A88a918e1f4072"), }, }, Network::Worldchain => NetworkConfig { @@ -91,6 +94,7 @@ impl From for NetworkConfig { chain: ChainConfig { chain_id: 480, unsafe_signer: address!("2270d6eC8E760daA317DD978cFB98C8f144B1f3A"), + system_config_contract: address!("6ab0777fD0e609CE58F939a7F70Fe41F5Aa6300A"), }, }, Network::Zora => NetworkConfig { @@ -98,6 +102,7 @@ impl From for NetworkConfig { chain: ChainConfig { chain_id: 7777777, unsafe_signer: address!("3Dc8Dfd0709C835cAd15a6A27e089FF4cF4C9228"), + system_config_contract: address!("A3cAB0126d5F504B071b81a3e8A2BBBF17930d86"), }, }, } diff --git a/opstack/src/server/mod.rs b/opstack/src/server/mod.rs index 6850be3a..62a23224 100644 --- a/opstack/src/server/mod.rs +++ b/opstack/src/server/mod.rs @@ -1,8 +1,14 @@ use std::{net::SocketAddr, sync::Arc, time::Duration}; -use alloy::primitives::Address; -use axum::{extract::State, routing::get, Json, Router}; +use alloy::{primitives::Address, rpc::types::EIP1186AccountProofResponse}; +use axum::{ + extract::{Path, State}, + http::StatusCode, + routing::get, + Json, Router, +}; use eyre::Result; +use revm::primitives::B256; use tokio::{ sync::{ mpsc::{channel, Receiver}, @@ -15,22 +21,33 @@ use url::Url; use crate::{types::ExecutionPayload, SequencerCommitment}; use self::net::{block_handler::BlockHandler, gossip::GossipService}; +use helios_core::execution::rpc::{http_rpc::HttpRpc, ExecutionRpc}; +use helios_ethereum::spec::Ethereum; +use std::str::FromStr; pub mod net; mod poller; +// Storage slot containing the unsafe signer address in all superchain system config contracts +const UNSAFE_SIGNER_SLOT: &str = + "0x65a7ed542fb37fe237fdfbdd70b31598523fe5b32879e307bae27a0bd9581c08"; + pub async fn start_server( server_addr: SocketAddr, gossip_addr: SocketAddr, chain_id: u64, signer: Address, + system_config_contract: Address, replica_urls: Vec, + execution_rpc: Url, ) -> Result<()> { let state = Arc::new(RwLock::new(ServerState::new( gossip_addr, chain_id, signer, + system_config_contract, replica_urls, + execution_rpc, )?)); let state_copy = state.clone(); @@ -44,6 +61,7 @@ pub async fn start_server( let router = Router::new() .route("/latest", get(latest_handler)) .route("/chain_id", get(chain_id_handler)) + .route("/signer_proof/:block_number", get(signer_proof_handler)) .with_state(state); let listener = tokio::net::TcpListener::bind(server_addr).await?; @@ -62,10 +80,30 @@ async fn chain_id_handler(State(state): State>>) -> Json Json(state.read().await.chain_id) } +async fn signer_proof_handler( + State(state): State>>, + Path(block_number): Path, +) -> Result, StatusCode> { + let rpc = HttpRpc::::new(&state.read().await.execution_rpc.to_string()) + .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; + let signer_slot = B256::from_str(UNSAFE_SIGNER_SLOT) + .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; + let proof = rpc + .get_proof( + state.read().await.system_config_contract, + &[signer_slot], + block_number, + ) + .await + .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; + Ok(Json(proof)) +} struct ServerState { chain_id: u64, commitment_recv: Receiver, latest_commitment: Option<(SequencerCommitment, u64)>, + execution_rpc: Url, + system_config_contract: Address, } impl ServerState { @@ -73,7 +111,9 @@ impl ServerState { addr: SocketAddr, chain_id: u64, signer: Address, + system_config_contract: Address, replica_urls: Vec, + execution_rpc: Url, ) -> Result { let (send, commitment_recv) = channel(256); poller::start(replica_urls, signer, chain_id, send.clone()); @@ -85,6 +125,8 @@ impl ServerState { chain_id, commitment_recv, latest_commitment: None, + execution_rpc, + system_config_contract, }) } From 351c0be993bdc3364d2804b4d9d71baa63651d30 Mon Sep 17 00:00:00 2001 From: Prudhvi Rampey Date: Thu, 7 Nov 2024 22:21:21 -0800 Subject: [PATCH 03/12] feat: run signer verify and advance async --- opstack/src/consensus.rs | 64 +++++++++++++++++++++++++++------------- 1 file changed, 43 insertions(+), 21 deletions(-) diff --git a/opstack/src/consensus.rs b/opstack/src/consensus.rs index 8a388f5d..a8e1ed43 100644 --- a/opstack/src/consensus.rs +++ b/opstack/src/consensus.rs @@ -3,7 +3,7 @@ use alloy::primitives::{b256, fixed_bytes, keccak256, Address, B256, U256, U64}; use alloy::rlp::Decodable; use alloy::rpc::types::{EIP1186AccountProofResponse, Parity, Signature, Transaction}; use alloy_rlp::encode; -use eyre::{Result, eyre}; +use eyre::{eyre, OptionExt, Result}; use op_alloy_consensus::OpTxEnvelope; use std::time::Duration; use tokio::sync::mpsc::Sender; @@ -19,14 +19,14 @@ use helios_core::time::{interval, SystemTime, UNIX_EPOCH}; use helios_core::types::{Block, Transactions}; use helios_ethereum::config::networks::mainnet; use helios_ethereum::consensus::ConsensusClient as EthConsensusClient; -use std::sync::Arc; +use std::sync::{Arc, Mutex}; use crate::{config::Config, types::ExecutionPayload, SequencerCommitment}; use helios_core::execution::proof::{encode_account, verify_proof}; use helios_ethereum::database::ConfigDB; use helios_ethereum::rpc::http_rpc::HttpRpc; -use tracing::{info, warn}; +use tracing::{error, info, warn}; pub struct ConsensusClient { block_recv: Option>>, @@ -41,13 +41,15 @@ impl ConsensusClient { let mut inner = Inner { server_url: config.consensus_rpc.to_string(), - unsafe_signer: config.chain.unsafe_signer, + unsafe_signer: Arc::new(Mutex::new(config.chain.unsafe_signer)), chain_id: config.chain.chain_id, latest_block: None, block_send, finalized_block_send, }; + let _ = verify_unsafe_signer(inner.server_url.clone(), inner.unsafe_signer.clone()); + #[cfg(not(target_arch = "wasm32"))] let run = tokio::spawn; @@ -55,10 +57,11 @@ impl ConsensusClient { let run = wasm_bindgen_futures::spawn_local; run(async move { - inner.verify_unsafe_signer().await.unwrap(); // TODO: make this async let mut interval = interval(Duration::from_secs(1)); loop { - _ = inner.advance().await; + if let Err(e) = inner.advance().await { + error!(target: "helios::opstack", "failed to advance: {}", e); + } interval.tick().await; } }); @@ -96,7 +99,7 @@ impl Consensus for ConsensusClient { #[allow(dead_code)] struct Inner { server_url: String, - unsafe_signer: Address, + unsafe_signer: Arc>, chain_id: u64, latest_block: Option, block_send: Sender>, @@ -111,7 +114,11 @@ impl Inner { .json::() .await?; - if commitment.verify(self.unsafe_signer, self.chain_id).is_ok() { + let curr_signer = *self + .unsafe_signer + .lock() + .map_err(|_| eyre!("failed to lock signer"))?; + if commitment.verify(curr_signer, self.chain_id).is_ok() { let payload = ExecutionPayload::try_from(&commitment)?; if self .latest_block @@ -143,20 +150,32 @@ impl Inner { Ok(()) } +} + +fn verify_unsafe_signer(server_url: String, signer: Arc>) { + #[cfg(not(target_arch = "wasm32"))] + let run = tokio::spawn; - async fn verify_unsafe_signer(&mut self) -> Result<()> { - let eth_config = mainnet(); // TODO: Do we account for testnets too? + #[cfg(target_arch = "wasm32")] + let run = wasm_bindgen_futures::spawn_local; + + run(async move { + let eth_config = mainnet(); let mut eth_consensus = EthConsensusClient::::new( - ð_config.consensus_rpc.clone().unwrap(), + ð_config + .consensus_rpc + .clone() + .ok_or_else(|| eyre!("missing consensus rpc"))?, Arc::new(eth_config.into()), )?; - let block = eth_consensus.block_recv().unwrap().recv().await.unwrap(); + let block = eth_consensus + .block_recv() + .unwrap() + .recv() + .await + .ok_or_eyre("failed to receive block")?; // Query proof from op consensus server - let req = format!( - "{}signer_proof/{}", - self.server_url, - block.number.to::() - ); + let req = format!("{}signer_proof/{}", server_url, block.number.to::()); let proof = reqwest::get(req) .await? .json::() @@ -193,14 +212,17 @@ impl Inner { } // Replace unsafe signer if different let verified_signer = Address::from_slice(&storage_proof.value.to_be_bytes::<32>()[12..32]); - if verified_signer != self.unsafe_signer { - info!(target: "helios::opstack", "unsafe signer updated: {}", verified_signer); - self.unsafe_signer = verified_signer; + { + let mut curr_signer = signer.lock().map_err(|_| eyre!("failed to lock signer"))?; + if verified_signer != *curr_signer { + info!(target: "helios::opstack", "unsafe signer updated: {}", verified_signer); + *curr_signer = verified_signer; + } } // Shutdown eth consensus client eth_consensus.shutdown()?; Ok(()) - } + }); } fn payload_to_block(value: ExecutionPayload) -> Result> { From fe79646ffea70a6c8f31f37171dbb9e28a69dc06 Mon Sep 17 00:00:00 2001 From: Prudhvi Rampey Date: Fri, 8 Nov 2024 00:37:53 -0800 Subject: [PATCH 04/12] feat: impl shutdown for eth consensus client --- ethereum/src/consensus.rs | 78 ++++++++++++++++++++++++++------------- opstack/src/consensus.rs | 2 +- 2 files changed, 54 insertions(+), 26 deletions(-) diff --git a/ethereum/src/consensus.rs b/ethereum/src/consensus.rs index 1b640ea0..c030a88f 100644 --- a/ethereum/src/consensus.rs +++ b/ethereum/src/consensus.rs @@ -42,6 +42,7 @@ pub struct ConsensusClient, DB: Database> { pub block_recv: Option>>, pub finalized_block_recv: Option>>>, pub checkpoint_recv: watch::Receiver>, + shutdown_send: watch::Sender, genesis_time: u64, config: Arc, phantom: PhantomData<(S, R, DB)>, @@ -79,6 +80,7 @@ impl, DB: Database> Consensus } fn shutdown(&self) -> Result<()> { + self.shutdown_send.send(true)?; Ok(()) } } @@ -88,6 +90,7 @@ impl, DB: Database> ConsensusClient, DB: Database> ConsensusClient::new( &rpc, @@ -138,28 +143,42 @@ impl, DB: Database> ConsensusClient { + if *shutdown_rx.borrow() { + info!(target: "helios::consensus", "shutting down consensus client"); + break; + } + } + _ = interval.tick() => { + let res = inner.advance().await; + if let Err(err) = res { + warn!(target: "helios::consensus", "advance error: {}", err); + continue; + } + + let res = inner.send_blocks().await; + if let Err(err) = res { + warn!(target: "helios::consensus", "send error: {}", err); + continue; + } + } } } }); - save_new_checkpoints(checkpoint_recv.clone(), db.clone(), initial_checkpoint); + save_new_checkpoints( + checkpoint_recv.clone(), + db.clone(), + initial_checkpoint, + shutdown_recv, + ); Ok(ConsensusClient { block_recv: Some(block_recv), finalized_block_recv: Some(finalized_block_recv), checkpoint_recv, + shutdown_send, genesis_time, config: config_clone, phantom: PhantomData, @@ -177,6 +196,7 @@ fn save_new_checkpoints( mut checkpoint_recv: watch::Receiver>, db: Arc, initial_checkpoint: B256, + mut shutdown_recv: watch::Receiver, ) { #[cfg(not(target_arch = "wasm32"))] let run = tokio::spawn; @@ -187,20 +207,28 @@ fn save_new_checkpoints( run(async move { let mut last_saved_checkpoint = initial_checkpoint; loop { - let new_checkpoint = *checkpoint_recv.borrow_and_update(); - if let Some(new_checkpoint) = new_checkpoint.as_ref() { - if *new_checkpoint != last_saved_checkpoint { - // There is a more recent checkpoint to save - if db.save_checkpoint(*new_checkpoint).is_err() { - warn!(target: "helios::consensus", "failed to save checkpoint"); - } else { - info!(target: "helios::consensus", "saved checkpoint to DB: 0x{}", hex::encode(*new_checkpoint)); - last_saved_checkpoint = *new_checkpoint; + tokio::select! { + _ = shutdown_recv.changed() => { + if *shutdown_recv.borrow() { + break; + } + } + checkpoint_result = checkpoint_recv.changed() => { + if checkpoint_result.is_err() { + break; + } + let new_checkpoint = *checkpoint_recv.borrow_and_update(); + if let Some(new_checkpoint) = new_checkpoint.as_ref() { + if *new_checkpoint != last_saved_checkpoint { + if db.save_checkpoint(*new_checkpoint).is_err() { + warn!(target: "helios::consensus", "failed to save checkpoint"); + } else { + info!(target: "helios::consensus", "saved checkpoint to DB: 0x{}", hex::encode(*new_checkpoint)); + last_saved_checkpoint = *new_checkpoint; + } + } } } - } - if checkpoint_recv.changed().await.is_err() { - break; } } }); diff --git a/opstack/src/consensus.rs b/opstack/src/consensus.rs index a8e1ed43..baf9ea02 100644 --- a/opstack/src/consensus.rs +++ b/opstack/src/consensus.rs @@ -48,7 +48,7 @@ impl ConsensusClient { finalized_block_send, }; - let _ = verify_unsafe_signer(inner.server_url.clone(), inner.unsafe_signer.clone()); + verify_unsafe_signer(inner.server_url.clone(), inner.unsafe_signer.clone()); #[cfg(not(target_arch = "wasm32"))] let run = tokio::spawn; From 7677f593b7d396c7b2e9b90b9dba58afedba6ef8 Mon Sep 17 00:00:00 2001 From: Prudhvi Rampey Date: Fri, 8 Nov 2024 00:40:32 -0800 Subject: [PATCH 05/12] chore: fmt --- core/src/execution/mod.rs | 2 +- core/src/lib.rs | 2 +- opstack/src/server/mod.rs | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/core/src/execution/mod.rs b/core/src/execution/mod.rs index 0849758e..4111aa9a 100644 --- a/core/src/execution/mod.rs +++ b/core/src/execution/mod.rs @@ -22,10 +22,10 @@ use self::types::Account; pub mod constants; pub mod errors; pub mod evm; +pub mod proof; pub mod rpc; pub mod state; pub mod types; -pub mod proof; #[derive(Clone)] pub struct ExecutionClient> { diff --git a/core/src/lib.rs b/core/src/lib.rs index 88c4377e..1c24b63b 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -1,7 +1,7 @@ pub mod client; pub mod consensus; pub mod errors; +pub mod execution; pub mod network_spec; pub mod time; pub mod types; -pub mod execution; diff --git a/opstack/src/server/mod.rs b/opstack/src/server/mod.rs index 62a23224..491a312d 100644 --- a/opstack/src/server/mod.rs +++ b/opstack/src/server/mod.rs @@ -86,8 +86,8 @@ async fn signer_proof_handler( ) -> Result, StatusCode> { let rpc = HttpRpc::::new(&state.read().await.execution_rpc.to_string()) .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; - let signer_slot = B256::from_str(UNSAFE_SIGNER_SLOT) - .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; + let signer_slot = + B256::from_str(UNSAFE_SIGNER_SLOT).map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; let proof = rpc .get_proof( state.read().await.system_config_contract, From 2f6b3089da24b5dce6a930100cccaaa480734402 Mon Sep 17 00:00:00 2001 From: Prudhvi Rampey Date: Mon, 11 Nov 2024 16:52:01 -0800 Subject: [PATCH 06/12] fix: review changes --- core/src/types.rs | 1 + opstack/src/consensus.rs | 11 ++++++++++- opstack/src/server/mod.rs | 18 ++++++++++++------ 3 files changed, 23 insertions(+), 7 deletions(-) diff --git a/core/src/types.rs b/core/src/types.rs index b15390c2..e4868038 100644 --- a/core/src/types.rs +++ b/core/src/types.rs @@ -25,6 +25,7 @@ pub struct Block { pub size: U64, pub state_root: B256, pub timestamp: U64, + #[serde(default)] pub total_difficulty: U64, pub transactions: Transactions, pub transactions_root: B256, diff --git a/opstack/src/consensus.rs b/opstack/src/consensus.rs index baf9ea02..55caff33 100644 --- a/opstack/src/consensus.rs +++ b/opstack/src/consensus.rs @@ -5,6 +5,7 @@ use alloy::rpc::types::{EIP1186AccountProofResponse, Parity, Signature, Transact use alloy_rlp::encode; use eyre::{eyre, OptionExt, Result}; use op_alloy_consensus::OpTxEnvelope; +use std::str::FromStr; use std::time::Duration; use tokio::sync::mpsc::Sender; use tokio::sync::{ @@ -28,6 +29,10 @@ use helios_ethereum::database::ConfigDB; use helios_ethereum::rpc::http_rpc::HttpRpc; use tracing::{error, info, warn}; +// Storage slot containing the unsafe signer address in all superchain system config contracts +const UNSAFE_SIGNER_SLOT: &str = + "0x65a7ed542fb37fe237fdfbdd70b31598523fe5b32879e307bae27a0bd9581c08"; + pub struct ConsensusClient { block_recv: Option>>, finalized_block_recv: Option>>>, @@ -175,7 +180,7 @@ fn verify_unsafe_signer(server_url: String, signer: Arc>) { .await .ok_or_eyre("failed to receive block")?; // Query proof from op consensus server - let req = format!("{}signer_proof/{}", server_url, block.number.to::()); + let req = format!("{}unsafe_signer_proof/{}", server_url, block.hash); let proof = reqwest::get(req) .await? .json::() @@ -198,6 +203,10 @@ fn verify_unsafe_signer(server_url: String, signer: Arc>) { // with storage proof let storage_proof = proof.storage_proof[0].clone(); let key = storage_proof.key.0; + if key != B256::from_str(UNSAFE_SIGNER_SLOT)? { + warn!(target: "helios::opstack", "account proof invalid"); + return Err(eyre!("account proof invalid")); + } let key_hash = keccak256(key); let value = encode(storage_proof.value); let is_valid = verify_proof( diff --git a/opstack/src/server/mod.rs b/opstack/src/server/mod.rs index 491a312d..837a7098 100644 --- a/opstack/src/server/mod.rs +++ b/opstack/src/server/mod.rs @@ -1,6 +1,9 @@ use std::{net::SocketAddr, sync::Arc, time::Duration}; -use alloy::{primitives::Address, rpc::types::EIP1186AccountProofResponse}; +use alloy::{ + primitives::{Address, FixedBytes, B256}, + rpc::types::EIP1186AccountProofResponse, +}; use axum::{ extract::{Path, State}, http::StatusCode, @@ -8,7 +11,6 @@ use axum::{ Json, Router, }; use eyre::Result; -use revm::primitives::B256; use tokio::{ sync::{ mpsc::{channel, Receiver}, @@ -61,7 +63,7 @@ pub async fn start_server( let router = Router::new() .route("/latest", get(latest_handler)) .route("/chain_id", get(chain_id_handler)) - .route("/signer_proof/:block_number", get(signer_proof_handler)) + .route("/unsafe_signer_proof/:block_hash", get(unsafe_signer_proof_handler)) .with_state(state); let listener = tokio::net::TcpListener::bind(server_addr).await?; @@ -80,19 +82,23 @@ async fn chain_id_handler(State(state): State>>) -> Json Json(state.read().await.chain_id) } -async fn signer_proof_handler( +async fn unsafe_signer_proof_handler( State(state): State>>, - Path(block_number): Path, + Path(block_hash): Path>, ) -> Result, StatusCode> { let rpc = HttpRpc::::new(&state.read().await.execution_rpc.to_string()) .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; let signer_slot = B256::from_str(UNSAFE_SIGNER_SLOT).map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; + let block = rpc + .get_block(block_hash) + .await + .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; let proof = rpc .get_proof( state.read().await.system_config_contract, &[signer_slot], - block_number, + block.number.to(), ) .await .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; From 64a26b031bafa9b57c8bf3214d6a596743f03980 Mon Sep 17 00:00:00 2001 From: Prudhvi Rampey Date: Mon, 11 Nov 2024 18:16:35 -0800 Subject: [PATCH 07/12] feat: add cli options for eth consensus client --- cli/src/main.rs | 30 ++++++++++++++++++++++++++---- helios-ts/src/opstack.rs | 2 ++ opstack/src/builder.rs | 2 ++ opstack/src/config.rs | 8 +++++--- opstack/src/consensus.rs | 12 ++++++++---- opstack/src/server/mod.rs | 5 ++++- 6 files changed, 47 insertions(+), 12 deletions(-) diff --git a/cli/src/main.rs b/cli/src/main.rs index 6263e1af..a9a6417f 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -7,6 +7,7 @@ use std::{ sync::{Arc, Mutex}, }; +use alloy::primitives::hex; use alloy::primitives::B256; use clap::{Args, Parser, Subcommand}; use dirs::home_dir; @@ -14,10 +15,6 @@ use eyre::Result; use figment::providers::Serialized; use figment::value::Value; use futures::executor::block_on; -use tracing::{error, info}; -use tracing_subscriber::filter::{EnvFilter, LevelFilter}; -use tracing_subscriber::FmtSubscriber; - use helios_core::client::Client; use helios_core::consensus::Consensus; use helios_core::network_spec::NetworkSpec; @@ -25,6 +22,9 @@ use helios_ethereum::config::{cli::CliConfig, Config as EthereumConfig}; use helios_ethereum::database::FileDB; use helios_ethereum::{EthereumClient, EthereumClientBuilder}; use helios_opstack::{config::Config as OpStackConfig, OpStackClient, OpStackClientBuilder}; +use tracing::{error, info}; +use tracing_subscriber::filter::{EnvFilter, LevelFilter}; +use tracing_subscriber::FmtSubscriber; #[tokio::main] async fn main() -> Result<()> { @@ -191,6 +191,20 @@ struct OpStackArgs { execution_rpc: Option, #[clap(short, long, env)] consensus_rpc: Option, + #[clap( + short = 'w', + long = "eth-checkpoint", + env = "ETH_CHECKPOINT", + help = "Set custom weak subjectivity checkpoint for Ethereum mainnet. Helios uses this to sync and verify the unsafe signer address used by " + )] + checkpoint: Option, + #[clap( + short = 'l', + long = "eth-load-external-fallback", + env = "ETH_LOAD_EXTERNAL_FALLBACK", + help = "Enable fallback for weak subjectivity checkpoint. Use if --eth-checkpoint fails." + )] + load_external_fallback: bool, } impl OpStackArgs { @@ -232,6 +246,14 @@ impl OpStackArgs { user_dict.insert("rpc_port", Value::from(port)); } + if self.load_external_fallback { + user_dict.insert("load_external_fallback", Value::from(true)); + } + + if let Some(checkpoint) = self.checkpoint { + user_dict.insert("checkpoint", Value::from(hex::encode(checkpoint))); + } + Serialized::from(user_dict, &self.network) } } diff --git a/helios-ts/src/opstack.rs b/helios-ts/src/opstack.rs index 47fe946b..2fd7d58b 100644 --- a/helios-ts/src/opstack.rs +++ b/helios-ts/src/opstack.rs @@ -43,6 +43,8 @@ impl OpStackClient { consensus_rpc, chain: network_config.chain, rpc_socket: None, + load_external_fallback: None, + checkpoint: None, }; let inner = map_err(OpStackClientBuilder::new().config(config).build())?; diff --git a/opstack/src/builder.rs b/opstack/src/builder.rs index 4ef2e0c5..8e831f41 100644 --- a/opstack/src/builder.rs +++ b/opstack/src/builder.rs @@ -84,6 +84,8 @@ impl OpStackClientBuilder { unsafe_signer, system_config_contract: Address::ZERO, }, + load_external_fallback: None, + checkpoint: None, } }; diff --git a/opstack/src/config.rs b/opstack/src/config.rs index 97b77461..687e395a 100644 --- a/opstack/src/config.rs +++ b/opstack/src/config.rs @@ -2,7 +2,7 @@ use std::{ collections::HashMap, fmt::Display, net::SocketAddr, path::PathBuf, process::exit, str::FromStr, }; -use alloy::primitives::{address, Address}; +use alloy::primitives::{address, Address, B256}; use eyre::Result; use figment::{ providers::{Format, Serialized, Toml}, @@ -12,15 +12,17 @@ use figment::{ use serde::{Deserialize, Serialize}; use url::Url; -#[derive(Serialize, Deserialize)] +#[derive(Serialize, Deserialize, Clone)] pub struct Config { pub consensus_rpc: Url, pub execution_rpc: Url, pub rpc_socket: Option, pub chain: ChainConfig, + pub load_external_fallback: Option, + pub checkpoint: Option, } -#[derive(Serialize, Deserialize)] +#[derive(Serialize, Deserialize, Clone)] pub struct ChainConfig { pub chain_id: u64, pub unsafe_signer: Address, diff --git a/opstack/src/consensus.rs b/opstack/src/consensus.rs index 55caff33..77cc1b37 100644 --- a/opstack/src/consensus.rs +++ b/opstack/src/consensus.rs @@ -53,7 +53,7 @@ impl ConsensusClient { finalized_block_send, }; - verify_unsafe_signer(inner.server_url.clone(), inner.unsafe_signer.clone()); + verify_unsafe_signer(config.clone(), inner.unsafe_signer.clone()); #[cfg(not(target_arch = "wasm32"))] let run = tokio::spawn; @@ -157,7 +157,7 @@ impl Inner { } } -fn verify_unsafe_signer(server_url: String, signer: Arc>) { +fn verify_unsafe_signer(config: Config, signer: Arc>) { #[cfg(not(target_arch = "wasm32"))] let run = tokio::spawn; @@ -165,7 +165,11 @@ fn verify_unsafe_signer(server_url: String, signer: Arc>) { let run = wasm_bindgen_futures::spawn_local; run(async move { - let eth_config = mainnet(); + let mut eth_config = mainnet(); + eth_config.load_external_fallback = config.load_external_fallback.unwrap_or(false); + if let Some(checkpoint) = config.checkpoint { + eth_config.default_checkpoint = checkpoint; + } let mut eth_consensus = EthConsensusClient::::new( ð_config .consensus_rpc @@ -180,7 +184,7 @@ fn verify_unsafe_signer(server_url: String, signer: Arc>) { .await .ok_or_eyre("failed to receive block")?; // Query proof from op consensus server - let req = format!("{}unsafe_signer_proof/{}", server_url, block.hash); + let req = format!("{}unsafe_signer_proof/{}", config.consensus_rpc, block.hash); let proof = reqwest::get(req) .await? .json::() diff --git a/opstack/src/server/mod.rs b/opstack/src/server/mod.rs index 837a7098..1f2c6ea3 100644 --- a/opstack/src/server/mod.rs +++ b/opstack/src/server/mod.rs @@ -63,7 +63,10 @@ pub async fn start_server( let router = Router::new() .route("/latest", get(latest_handler)) .route("/chain_id", get(chain_id_handler)) - .route("/unsafe_signer_proof/:block_hash", get(unsafe_signer_proof_handler)) + .route( + "/unsafe_signer_proof/:block_hash", + get(unsafe_signer_proof_handler), + ) .with_state(state); let listener = tokio::net::TcpListener::bind(server_addr).await?; From b0d12a4f20b0f32f54dfc4589e765c5e436dd656 Mon Sep 17 00:00:00 2001 From: Prudhvi Rampey Date: Mon, 11 Nov 2024 18:21:41 -0800 Subject: [PATCH 08/12] nit: small doc change --- cli/src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/src/main.rs b/cli/src/main.rs index a9a6417f..15a7031f 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -195,7 +195,7 @@ struct OpStackArgs { short = 'w', long = "eth-checkpoint", env = "ETH_CHECKPOINT", - help = "Set custom weak subjectivity checkpoint for Ethereum mainnet. Helios uses this to sync and verify the unsafe signer address used by " + help = "Set custom weak subjectivity checkpoint for Ethereum mainnet. Helios uses this to sync and trustlessly fetch the correct unsafe signer address used by " )] checkpoint: Option, #[clap( From 5d0713d6db48562aaf1d7208a31e3ad8b9a22164 Mon Sep 17 00:00:00 2001 From: Prudhvi Rampey Date: Wed, 20 Nov 2024 13:03:14 -0800 Subject: [PATCH 09/12] fix: add eth_network to config --- cli/src/main.rs | 24 ++++++++++++++++++------ helios-ts/src/opstack.rs | 9 ++++++++- opstack/src/builder.rs | 13 ++++++++++++- opstack/src/config.rs | 2 ++ opstack/src/consensus.rs | 3 +-- 5 files changed, 41 insertions(+), 10 deletions(-) diff --git a/cli/src/main.rs b/cli/src/main.rs index 15a7031f..17202324 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -193,18 +193,26 @@ struct OpStackArgs { consensus_rpc: Option, #[clap( short = 'w', - long = "eth-checkpoint", - env = "ETH_CHECKPOINT", - help = "Set custom weak subjectivity checkpoint for Ethereum mainnet. Helios uses this to sync and trustlessly fetch the correct unsafe signer address used by " + long = "ethereum-checkpoint", + env = "ETHEREUM_CHECKPOINT", + help = "Set custom weak subjectivity checkpoint for chosen Ethereum network. Helios uses this to sync and trustlessly fetch the correct unsafe signer address used by " )] checkpoint: Option, #[clap( short = 'l', - long = "eth-load-external-fallback", - env = "ETH_LOAD_EXTERNAL_FALLBACK", - help = "Enable fallback for weak subjectivity checkpoint. Use if --eth-checkpoint fails." + long = "ethereum-load-external-fallback", + env = "ETHEREUM_LOAD_EXTERNAL_FALLBACK", + help = "Enable fallback for weak subjectivity checkpoint. Use if --ethereum-checkpoint fails." )] load_external_fallback: bool, + #[clap( + short = 's', + long = "ethereum-network", + env = "ETHEREUM_NETWORK", + default_value = "MAINNET", + help = "Set the Ethereum network to use with the opstack (MAINNET, GOERLI, HOLESKY, SEPOLIA)" + )] + eth_network: Option, } impl OpStackArgs { @@ -254,6 +262,10 @@ impl OpStackArgs { user_dict.insert("checkpoint", Value::from(hex::encode(checkpoint))); } + if let Some(eth_network) = &self.eth_network { + user_dict.insert("eth_network", Value::from(eth_network.clone())); + } + Serialized::from(user_dict, &self.network) } } diff --git a/helios-ts/src/opstack.rs b/helios-ts/src/opstack.rs index 2fd7d58b..8221681b 100644 --- a/helios-ts/src/opstack.rs +++ b/helios-ts/src/opstack.rs @@ -22,7 +22,11 @@ pub struct OpStackClient { #[wasm_bindgen] impl OpStackClient { #[wasm_bindgen(constructor)] - pub fn new(execution_rpc: String, network: String) -> Result { + pub fn new( + execution_rpc: String, + network: String, + eth_network: String, + ) -> Result { console_error_panic_hook::set_once(); let network_config = match network.as_str() { @@ -45,6 +49,9 @@ impl OpStackClient { rpc_socket: None, load_external_fallback: None, checkpoint: None, + eth_network: eth_network + .parse() + .map_err(|_| JsError::new("invalid ethereum network"))?, }; let inner = map_err(OpStackClientBuilder::new().config(config).build())?; diff --git a/opstack/src/builder.rs b/opstack/src/builder.rs index 8e831f41..c6a54ffb 100644 --- a/opstack/src/builder.rs +++ b/opstack/src/builder.rs @@ -9,6 +9,7 @@ use crate::{ consensus::ConsensusClient, OpStackClient, }; +use helios_ethereum::config::networks::Network as EthNetwork; #[derive(Default)] pub struct OpStackClientBuilder { @@ -18,6 +19,7 @@ pub struct OpStackClientBuilder { consensus_rpc: Option, execution_rpc: Option, rpc_socket: Option, + eth_network: Option, } impl OpStackClientBuilder { @@ -55,6 +57,11 @@ impl OpStackClientBuilder { self } + pub fn eth_network(mut self, network: EthNetwork) -> Self { + self.eth_network = Some(network); + self + } + pub fn build(self) -> Result { let config = if let Some(config) = self.config { config @@ -75,6 +82,10 @@ impl OpStackClientBuilder { eyre::bail!("execution rpc required"); }; + let Some(eth_network) = self.eth_network else { + eyre::bail!("ethereum network required"); + }; + Config { consensus_rpc, execution_rpc, @@ -86,9 +97,9 @@ impl OpStackClientBuilder { }, load_external_fallback: None, checkpoint: None, + eth_network, } }; - let consensus = ConsensusClient::new(&config); OpStackClient::new( &config.execution_rpc.to_string(), diff --git a/opstack/src/config.rs b/opstack/src/config.rs index 687e395a..59b6ecde 100644 --- a/opstack/src/config.rs +++ b/opstack/src/config.rs @@ -9,6 +9,7 @@ use figment::{ value::Value, Figment, }; +use helios_ethereum::config::networks::Network as EthNetwork; use serde::{Deserialize, Serialize}; use url::Url; @@ -20,6 +21,7 @@ pub struct Config { pub chain: ChainConfig, pub load_external_fallback: Option, pub checkpoint: Option, + pub eth_network: EthNetwork, } #[derive(Serialize, Deserialize, Clone)] diff --git a/opstack/src/consensus.rs b/opstack/src/consensus.rs index 77cc1b37..d0bce96f 100644 --- a/opstack/src/consensus.rs +++ b/opstack/src/consensus.rs @@ -18,7 +18,6 @@ use helios_consensus_core::consensus_spec::MainnetConsensusSpec; use helios_core::consensus::Consensus; use helios_core::time::{interval, SystemTime, UNIX_EPOCH}; use helios_core::types::{Block, Transactions}; -use helios_ethereum::config::networks::mainnet; use helios_ethereum::consensus::ConsensusClient as EthConsensusClient; use std::sync::{Arc, Mutex}; @@ -165,7 +164,7 @@ fn verify_unsafe_signer(config: Config, signer: Arc>) { let run = wasm_bindgen_futures::spawn_local; run(async move { - let mut eth_config = mainnet(); + let mut eth_config = config.eth_network.to_base_config(); eth_config.load_external_fallback = config.load_external_fallback.unwrap_or(false); if let Some(checkpoint) = config.checkpoint { eth_config.default_checkpoint = checkpoint; From 3d9c248c57b5889e1dd7ad1e93036472cd829826 Mon Sep 17 00:00:00 2001 From: Prudhvi Rampey Date: Wed, 20 Nov 2024 14:55:14 -0800 Subject: [PATCH 10/12] fix: add to chain_config --- cli/src/main.rs | 12 ------------ helios-ts/src/opstack.rs | 9 +-------- opstack/src/builder.rs | 2 +- opstack/src/config.rs | 6 +++++- opstack/src/consensus.rs | 2 +- 5 files changed, 8 insertions(+), 23 deletions(-) diff --git a/cli/src/main.rs b/cli/src/main.rs index 17202324..6bf93739 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -205,14 +205,6 @@ struct OpStackArgs { help = "Enable fallback for weak subjectivity checkpoint. Use if --ethereum-checkpoint fails." )] load_external_fallback: bool, - #[clap( - short = 's', - long = "ethereum-network", - env = "ETHEREUM_NETWORK", - default_value = "MAINNET", - help = "Set the Ethereum network to use with the opstack (MAINNET, GOERLI, HOLESKY, SEPOLIA)" - )] - eth_network: Option, } impl OpStackArgs { @@ -262,10 +254,6 @@ impl OpStackArgs { user_dict.insert("checkpoint", Value::from(hex::encode(checkpoint))); } - if let Some(eth_network) = &self.eth_network { - user_dict.insert("eth_network", Value::from(eth_network.clone())); - } - Serialized::from(user_dict, &self.network) } } diff --git a/helios-ts/src/opstack.rs b/helios-ts/src/opstack.rs index 8221681b..2fd7d58b 100644 --- a/helios-ts/src/opstack.rs +++ b/helios-ts/src/opstack.rs @@ -22,11 +22,7 @@ pub struct OpStackClient { #[wasm_bindgen] impl OpStackClient { #[wasm_bindgen(constructor)] - pub fn new( - execution_rpc: String, - network: String, - eth_network: String, - ) -> Result { + pub fn new(execution_rpc: String, network: String) -> Result { console_error_panic_hook::set_once(); let network_config = match network.as_str() { @@ -49,9 +45,6 @@ impl OpStackClient { rpc_socket: None, load_external_fallback: None, checkpoint: None, - eth_network: eth_network - .parse() - .map_err(|_| JsError::new("invalid ethereum network"))?, }; let inner = map_err(OpStackClientBuilder::new().config(config).build())?; diff --git a/opstack/src/builder.rs b/opstack/src/builder.rs index c6a54ffb..61862ab7 100644 --- a/opstack/src/builder.rs +++ b/opstack/src/builder.rs @@ -94,10 +94,10 @@ impl OpStackClientBuilder { chain_id, unsafe_signer, system_config_contract: Address::ZERO, + eth_network, }, load_external_fallback: None, checkpoint: None, - eth_network, } }; let consensus = ConsensusClient::new(&config); diff --git a/opstack/src/config.rs b/opstack/src/config.rs index 59b6ecde..76357307 100644 --- a/opstack/src/config.rs +++ b/opstack/src/config.rs @@ -21,7 +21,6 @@ pub struct Config { pub chain: ChainConfig, pub load_external_fallback: Option, pub checkpoint: Option, - pub eth_network: EthNetwork, } #[derive(Serialize, Deserialize, Clone)] @@ -29,6 +28,7 @@ pub struct ChainConfig { pub chain_id: u64, pub unsafe_signer: Address, pub system_config_contract: Address, + pub eth_network: EthNetwork, } #[derive(Serialize, Deserialize)] @@ -79,6 +79,7 @@ impl From for NetworkConfig { chain_id: 10, unsafe_signer: address!("AAAA45d9549EDA09E70937013520214382Ffc4A2"), system_config_contract: address!("229047fed2591dbec1eF1118d64F7aF3dB9EB290"), + eth_network: EthNetwork::MAINNET, }, }, Network::Base => NetworkConfig { @@ -87,6 +88,7 @@ impl From for NetworkConfig { chain_id: 8453, unsafe_signer: address!("Af6E19BE0F9cE7f8afd49a1824851023A8249e8a"), system_config_contract: address!("73a79Fab69143498Ed3712e519A88a918e1f4072"), + eth_network: EthNetwork::MAINNET, }, }, Network::Worldchain => NetworkConfig { @@ -99,6 +101,7 @@ impl From for NetworkConfig { chain_id: 480, unsafe_signer: address!("2270d6eC8E760daA317DD978cFB98C8f144B1f3A"), system_config_contract: address!("6ab0777fD0e609CE58F939a7F70Fe41F5Aa6300A"), + eth_network: EthNetwork::MAINNET, }, }, Network::Zora => NetworkConfig { @@ -107,6 +110,7 @@ impl From for NetworkConfig { chain_id: 7777777, unsafe_signer: address!("3Dc8Dfd0709C835cAd15a6A27e089FF4cF4C9228"), system_config_contract: address!("A3cAB0126d5F504B071b81a3e8A2BBBF17930d86"), + eth_network: EthNetwork::MAINNET, }, }, } diff --git a/opstack/src/consensus.rs b/opstack/src/consensus.rs index d0bce96f..15fb5abb 100644 --- a/opstack/src/consensus.rs +++ b/opstack/src/consensus.rs @@ -164,7 +164,7 @@ fn verify_unsafe_signer(config: Config, signer: Arc>) { let run = wasm_bindgen_futures::spawn_local; run(async move { - let mut eth_config = config.eth_network.to_base_config(); + let mut eth_config = config.chain.eth_network.to_base_config(); eth_config.load_external_fallback = config.load_external_fallback.unwrap_or(false); if let Some(checkpoint) = config.checkpoint { eth_config.default_checkpoint = checkpoint; From 61b3304c6ee1850b5afda27712d19ec821f4ea46 Mon Sep 17 00:00:00 2001 From: Prudhvi Rampey Date: Wed, 20 Nov 2024 16:56:49 -0800 Subject: [PATCH 11/12] fix: use block hash to query proof --- core/src/execution/mod.rs | 2 +- core/src/execution/rpc/http_rpc.rs | 4 ++-- core/src/execution/rpc/mock_rpc.rs | 6 ++++-- core/src/execution/rpc/mod.rs | 6 ++++-- opstack/src/server/mod.rs | 6 +----- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/core/src/execution/mod.rs b/core/src/execution/mod.rs index 4111aa9a..193fc466 100644 --- a/core/src/execution/mod.rs +++ b/core/src/execution/mod.rs @@ -62,7 +62,7 @@ impl> ExecutionClient { let proof = self .rpc - .get_proof(address, slots, block.number.to()) + .get_proof(address, slots, block.number.into()) .await?; let account_path = keccak256(address).to_vec(); diff --git a/core/src/execution/rpc/http_rpc.rs b/core/src/execution/rpc/http_rpc.rs index 4eb52b96..8f77319d 100644 --- a/core/src/execution/rpc/http_rpc.rs +++ b/core/src/execution/rpc/http_rpc.rs @@ -53,12 +53,12 @@ impl ExecutionRpc for HttpRpc { &self, address: Address, slots: &[B256], - block: u64, + block: BlockId, ) -> Result { let proof_response = self .provider .get_proof(address, slots.to_vec()) - .block_id(block.into()) + .block_id(block) .await .map_err(|e| RpcError::new("get_proof", e))?; diff --git a/core/src/execution/rpc/mock_rpc.rs b/core/src/execution/rpc/mock_rpc.rs index 17c977bc..3da2d207 100644 --- a/core/src/execution/rpc/mock_rpc.rs +++ b/core/src/execution/rpc/mock_rpc.rs @@ -1,7 +1,9 @@ use std::{fs::read_to_string, path::PathBuf}; use alloy::primitives::{Address, B256, U256}; -use alloy::rpc::types::{AccessList, EIP1186AccountProofResponse, FeeHistory, Filter, Log}; +use alloy::rpc::types::{ + AccessList, BlockId, EIP1186AccountProofResponse, FeeHistory, Filter, Log, +}; use async_trait::async_trait; use eyre::{eyre, Result}; @@ -26,7 +28,7 @@ impl ExecutionRpc for MockRpc { &self, _address: Address, _slots: &[B256], - _block: u64, + _block: BlockId, ) -> Result { let proof = read_to_string(self.path.join("proof.json"))?; Ok(serde_json::from_str(&proof)?) diff --git a/core/src/execution/rpc/mod.rs b/core/src/execution/rpc/mod.rs index 56706dc4..e8a7d68a 100644 --- a/core/src/execution/rpc/mod.rs +++ b/core/src/execution/rpc/mod.rs @@ -1,5 +1,7 @@ use alloy::primitives::{Address, B256, U256}; -use alloy::rpc::types::{AccessList, EIP1186AccountProofResponse, FeeHistory, Filter, Log}; +use alloy::rpc::types::{ + AccessList, BlockId, EIP1186AccountProofResponse, FeeHistory, Filter, Log, +}; use async_trait::async_trait; use eyre::Result; @@ -20,7 +22,7 @@ pub trait ExecutionRpc: Send + Clone + Sync + 'static { &self, address: Address, slots: &[B256], - block: u64, + block: BlockId, ) -> Result; async fn create_access_list( diff --git a/opstack/src/server/mod.rs b/opstack/src/server/mod.rs index 1f2c6ea3..651d1b8e 100644 --- a/opstack/src/server/mod.rs +++ b/opstack/src/server/mod.rs @@ -93,15 +93,11 @@ async fn unsafe_signer_proof_handler( .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; let signer_slot = B256::from_str(UNSAFE_SIGNER_SLOT).map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; - let block = rpc - .get_block(block_hash) - .await - .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; let proof = rpc .get_proof( state.read().await.system_config_contract, &[signer_slot], - block.number.to(), + block_hash.into(), ) .await .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; From 2ff43762f168064cac3a01a9bfd484f41cc56473 Mon Sep 17 00:00:00 2001 From: Prudhvi Rampey Date: Fri, 22 Nov 2024 13:06:41 -0800 Subject: [PATCH 12/12] add Network to builder --- opstack/src/builder.rs | 42 ++++++++---------------------------------- 1 file changed, 8 insertions(+), 34 deletions(-) diff --git a/opstack/src/builder.rs b/opstack/src/builder.rs index 61862ab7..97a58eab 100644 --- a/opstack/src/builder.rs +++ b/opstack/src/builder.rs @@ -1,25 +1,22 @@ use std::net::SocketAddr; -use alloy::primitives::Address; use eyre::Result; use reqwest::{IntoUrl, Url}; use crate::{ - config::{ChainConfig, Config}, + config::Network, + config::{Config, NetworkConfig}, consensus::ConsensusClient, OpStackClient, }; -use helios_ethereum::config::networks::Network as EthNetwork; #[derive(Default)] pub struct OpStackClientBuilder { config: Option, - chain_id: Option, - unsafe_signer: Option
, + network: Option, consensus_rpc: Option, execution_rpc: Option, rpc_socket: Option, - eth_network: Option, } impl OpStackClientBuilder { @@ -32,16 +29,6 @@ impl OpStackClientBuilder { self } - pub fn chain_id(mut self, chain_id: u64) -> Self { - self.chain_id = Some(chain_id); - self - } - - pub fn unsafe_signer(mut self, signer: Address) -> Self { - self.unsafe_signer = Some(signer); - self - } - pub fn consensus_rpc(mut self, consensus_rpc: T) -> Self { self.consensus_rpc = Some(consensus_rpc.into_url().unwrap()); self @@ -57,8 +44,8 @@ impl OpStackClientBuilder { self } - pub fn eth_network(mut self, network: EthNetwork) -> Self { - self.eth_network = Some(network); + pub fn network(mut self, network: Network) -> Self { + self.network = Some(network); self } @@ -66,12 +53,8 @@ impl OpStackClientBuilder { let config = if let Some(config) = self.config { config } else { - let Some(chain_id) = self.chain_id else { - eyre::bail!("chain id required"); - }; - - let Some(unsafe_signer) = self.unsafe_signer else { - eyre::bail!("unsafe signer required"); + let Some(network) = self.network else { + eyre::bail!("network required"); }; let Some(consensus_rpc) = self.consensus_rpc else { @@ -82,20 +65,11 @@ impl OpStackClientBuilder { eyre::bail!("execution rpc required"); }; - let Some(eth_network) = self.eth_network else { - eyre::bail!("ethereum network required"); - }; - Config { consensus_rpc, execution_rpc, rpc_socket: self.rpc_socket, - chain: ChainConfig { - chain_id, - unsafe_signer, - system_config_contract: Address::ZERO, - eth_network, - }, + chain: NetworkConfig::from(network).chain, load_external_fallback: None, checkpoint: None, }