diff --git a/Cargo.lock b/Cargo.lock index 0f06b586..869476f7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9502,6 +9502,7 @@ dependencies = [ "scroll-network", "scroll-wire", "serde_json", + "tempfile", "tokio", "tracing", ] @@ -9627,6 +9628,8 @@ dependencies = [ "alloy-eips 1.0.9", "alloy-primitives", "alloy-rpc-types-engine 1.0.9", + "alloy-signer-local", + "eyre", "futures", "metrics", "metrics-derive", @@ -9636,14 +9639,17 @@ dependencies = [ "reth-scroll-node", "reth-tracing", "rollup-node", + "rollup-node-manager", "rollup-node-primitives", "rollup-node-providers", "rollup-node-sequencer", + "rollup-node-signer", "scroll-alloy-consensus", "scroll-alloy-provider", "scroll-alloy-rpc-types-engine", "scroll-db", "scroll-engine", + "tempfile", "thiserror 2.0.12", "tokio", "tracing", @@ -9656,11 +9662,13 @@ dependencies = [ "alloy-signer", "alloy-signer-local", "futures", + "hex", "metrics", "metrics-derive", "reth-scroll-primitives", "reth-tracing", "rollup-node-primitives", + "tempfile", "thiserror 2.0.12", "tokio", "tokio-stream", diff --git a/README.md b/README.md index afc9fa21..7aa9aa3e 100644 --- a/README.md +++ b/README.md @@ -123,9 +123,11 @@ To run a sequencer node you should build the binary in release mode using the in Then, you can run the sequencer node with the following command: ```sh -./target/release/rollup-node node --chain dev -d --scroll-sequencer-enabled --http --http.api admin,debug,eth,net,trace,txpool,web3,rpc,reth,ots,flashbots,miner,mev +./target/release/rollup-node node --chain dev -d --sequencer.enabled --signer.key-file /path/to/your/private.key --http --http.api admin,debug,eth,net,trace,txpool,web3,rpc,reth,ots,flashbots,miner,mev ``` +**Note**: When running a sequencer, a signer key file is required unless the `--test` flag is specified. Use the `--signer.key-file` option to specify the path to your private key file. Keep your private key file secure and never commit it to version control. + This will start a dev node in sequencer mode with all rpc apis enabled. You can adjust the `--http.api` flag to include or exclude specific APIs as needed. The chain will be configured with a genesis that funds 20 addresses derived from the mnemonic: @@ -147,6 +149,8 @@ A list of sequencer specific configuration options can be seen below: The max L1 messages per block for the sequencer [default: 4] --fee-recipient The fee recipient for the sequencer [default: 0x5300000000000000000000000000000000000005] + --signer.key-file + Path to the signer's hex-encoded private key file (optional 0x prefix). Required when sequencer is enabled ``` ## Contributing diff --git a/crates/manager/src/manager/event.rs b/crates/manager/src/manager/event.rs index 97f8215b..9a49d744 100644 --- a/crates/manager/src/manager/event.rs +++ b/crates/manager/src/manager/event.rs @@ -1,5 +1,6 @@ use reth_scroll_primitives::ScrollBlock; use rollup_node_primitives::L2BlockInfoWithL1Messages; +use rollup_node_signer::SignerEvent; use scroll_network::NewBlockWithPeer; /// An event that can be emitted by the rollup node manager. @@ -15,4 +16,6 @@ pub enum RollupManagerEvent { L1DerivedBlockConsolidated(L2BlockInfoWithL1Messages), /// An L1 message with the given index has been indexed. L1MessageIndexed(u64), + /// A new event from the signer. + SignerEvent(SignerEvent), } diff --git a/crates/manager/src/manager/mod.rs b/crates/manager/src/manager/mod.rs index ed249a82..9de22386 100644 --- a/crates/manager/src/manager/mod.rs +++ b/crates/manager/src/manager/mod.rs @@ -455,6 +455,13 @@ where match event { SignerEvent::SignedBlock { block, signature } => { trace!(target: "scroll::node::manager", ?block, ?signature, "Received signed block from signer, announcing to the network"); + // Send SignerEvent for test monitoring + if let Some(event_sender) = this.event_sender.as_ref() { + event_sender.notify(RollupManagerEvent::SignerEvent( + SignerEvent::SignedBlock { block: block.clone(), signature }, + )); + } + this.network.handle().announce_block(block, signature); } } diff --git a/crates/node/Cargo.toml b/crates/node/Cargo.toml index 18326d38..bf25aea7 100644 --- a/crates/node/Cargo.toml +++ b/crates/node/Cargo.toml @@ -93,6 +93,7 @@ reth-tracing.workspace = true rollup-node = { workspace = true, features = ["test-utils"] } scroll-alloy-rpc-types-engine.workspace = true serde_json = { version = "1.0.94", default-features = false, features = ["alloc"] } +tempfile = "3.0" [features] test-utils = [ diff --git a/crates/node/src/add_ons/rollup.rs b/crates/node/src/add_ons/rollup.rs index 3224d817..c4e0d620 100644 --- a/crates/node/src/add_ons/rollup.rs +++ b/crates/node/src/add_ons/rollup.rs @@ -3,6 +3,7 @@ use crate::{ constants::PROVIDER_BLOB_CACHE_SIZE, }; +use alloy_primitives::hex; use alloy_provider::ProviderBuilder; use alloy_rpc_client::RpcClient; use alloy_signer_local::PrivateKeySigner; @@ -32,7 +33,7 @@ use scroll_engine::{EngineDriver, ForkchoiceState}; use scroll_migration::traits::ScrollMigrator; use scroll_network::ScrollNetworkManager; use scroll_wire::{ScrollWireConfig, ScrollWireProtocolHandler}; -use std::{sync::Arc, time::Duration}; +use std::{fs, sync::Arc, time::Duration}; use tokio::sync::mpsc::Sender; /// Implementing the trait allows the type to return whether it is configured for dev chain. @@ -211,7 +212,32 @@ impl RollupManagerAddOn { .then_some(ctx.node.network().eth_wire_block_listener().await?); // Instantiate the signer - let signer = self.config.test.then_some(Signer::spawn(PrivateKeySigner::random())); + let signer = if self.config.test { + Some(Signer::spawn(PrivateKeySigner::random())) + } else if let Some(key_file_path) = &self.config.signer_args.key_file { + let key_content = fs::read_to_string(key_file_path) + .map_err(|e| { + eyre::eyre!("Failed to read signer key file {}: {}", key_file_path.display(), e) + })? + .trim() + .to_string(); + + let hex_str = key_content.strip_prefix("0x").unwrap_or(&key_content); + let key_bytes = hex::decode(hex_str).map_err(|e| { + eyre::eyre!( + "Failed to decode hex private key from file {}: {}", + key_file_path.display(), + e + ) + })?; + + let private_key_signer = PrivateKeySigner::from_slice(&key_bytes) + .map_err(|e| eyre::eyre!("Failed to create signer from key file: {}", e))?; + + Some(Signer::spawn(private_key_signer)) + } else { + None + }; // Spawn the rollup node manager let rnm = RollupNodeManager::new( diff --git a/crates/node/src/args.rs b/crates/node/src/args.rs index 753fd6da..164f8fa6 100644 --- a/crates/node/src/args.rs +++ b/crates/node/src/args.rs @@ -28,6 +28,22 @@ pub struct ScrollRollupNodeConfig { /// The network arguments #[command(flatten)] pub network_args: NetworkArgs, + /// The signer arguments + #[command(flatten)] + pub signer_args: SignerArgs, +} + +impl ScrollRollupNodeConfig { + /// Validate that signer key file is provided when sequencer is enabled + pub fn validate(&self) -> Result<(), String> { + if !self.test && + self.sequencer_args.sequencer_enabled && + self.signer_args.key_file.is_none() + { + return Err("Signer key file is required when sequencer is enabled".to_string()); + } + Ok(()) + } } /// The database arguments. @@ -122,3 +138,89 @@ pub struct SequencerArgs { )] pub l1_message_inclusion_mode: L1MessageInclusionMode, } + +/// The arguments for the signer. +#[derive(Debug, Default, Clone, clap::Args)] +pub struct SignerArgs { + /// Path to the file containing the signer's private key + #[arg( + long = "signer.key-file", + value_name = "FILE_PATH", + help = "Path to the signer's hex-encoded private key file (optional 0x prefix). Required when sequencer is enabled" + )] + pub key_file: Option, +} + +#[cfg(test)] +mod tests { + use super::*; + use std::path::PathBuf; + + #[test] + fn test_validate_sequencer_enabled_without_key_file_fails() { + let config = ScrollRollupNodeConfig { + test: false, + sequencer_args: SequencerArgs { sequencer_enabled: true, ..Default::default() }, + signer_args: SignerArgs { key_file: None }, + database_args: DatabaseArgs::default(), + engine_driver_args: EngineDriverArgs::default(), + l1_provider_args: L1ProviderArgs::default(), + beacon_provider_args: BeaconProviderArgs::default(), + network_args: NetworkArgs::default(), + }; + + let result = config.validate(); + assert!(result.is_err()); + assert!(result + .unwrap_err() + .contains("Signer key file is required when sequencer is enabled")); + } + + #[test] + fn test_validate_sequencer_enabled_with_key_file_succeeds() { + let config = ScrollRollupNodeConfig { + test: false, + sequencer_args: SequencerArgs { sequencer_enabled: true, ..Default::default() }, + signer_args: SignerArgs { key_file: Some(PathBuf::from("/path/to/key")) }, + database_args: DatabaseArgs::default(), + engine_driver_args: EngineDriverArgs::default(), + l1_provider_args: L1ProviderArgs::default(), + beacon_provider_args: BeaconProviderArgs::default(), + network_args: NetworkArgs::default(), + }; + + assert!(config.validate().is_ok()); + } + + #[test] + fn test_validate_test_mode_without_key_file_succeeds() { + let config = ScrollRollupNodeConfig { + test: true, + sequencer_args: SequencerArgs { sequencer_enabled: true, ..Default::default() }, + signer_args: SignerArgs { key_file: None }, + database_args: DatabaseArgs::default(), + engine_driver_args: EngineDriverArgs::default(), + l1_provider_args: L1ProviderArgs::default(), + beacon_provider_args: BeaconProviderArgs::default(), + network_args: NetworkArgs::default(), + }; + + assert!(config.validate().is_ok()); + } + + #[test] + fn test_validate_sequencer_disabled_without_key_file_succeeds() { + let config = ScrollRollupNodeConfig { + test: false, + sequencer_args: SequencerArgs { sequencer_enabled: false, ..Default::default() }, + signer_args: SignerArgs { key_file: None }, + database_args: DatabaseArgs::default(), + engine_driver_args: EngineDriverArgs::default(), + l1_provider_args: L1ProviderArgs::default(), + beacon_provider_args: BeaconProviderArgs::default(), + network_args: NetworkArgs::default(), + }; + + assert!(config.validate().is_ok()); + } +} diff --git a/crates/node/src/node.rs b/crates/node/src/node.rs index 6f9a1aab..c4c914ab 100644 --- a/crates/node/src/node.rs +++ b/crates/node/src/node.rs @@ -21,7 +21,11 @@ pub struct ScrollRollupNode { impl ScrollRollupNode { /// Create a new instance of [`ScrollRollupNode`]. - pub const fn new(config: ScrollRollupNodeConfig) -> Self { + pub fn new(config: ScrollRollupNodeConfig) -> Self { + config + .validate() + .map_err(|e| eyre::eyre!("Configuration validation failed: {}", e)) + .expect("Configuration validation failed"); Self { config } } } diff --git a/crates/node/src/test_utils.rs b/crates/node/src/test_utils.rs index c55d1635..662dc2bb 100644 --- a/crates/node/src/test_utils.rs +++ b/crates/node/src/test_utils.rs @@ -139,6 +139,7 @@ pub fn default_test_scroll_rollup_node_config() -> ScrollRollupNodeConfig { engine_driver_args: EngineDriverArgs { en_sync_trigger: 100 }, sequencer_args: SequencerArgs::default(), beacon_provider_args: BeaconProviderArgs::default(), + signer_args: Default::default(), } } @@ -162,5 +163,6 @@ pub fn default_sequencer_test_scroll_rollup_node_config() -> ScrollRollupNodeCon l1_message_inclusion_mode: L1MessageInclusionMode::BlockDepth(0), }, beacon_provider_args: BeaconProviderArgs::default(), + signer_args: Default::default(), } } diff --git a/crates/node/tests/e2e.rs b/crates/node/tests/e2e.rs index f29835f4..557de772 100644 --- a/crates/node/tests/e2e.rs +++ b/crates/node/tests/e2e.rs @@ -43,6 +43,7 @@ async fn can_bridge_l1_messages() -> eyre::Result<()> { ..SequencerArgs::default() }, beacon_provider_args: BeaconProviderArgs::default(), + signer_args: Default::default(), }; let (mut nodes, _tasks, _wallet) = setup_engine(node_args, 1, chain_spec, false).await?; let node = nodes.pop().unwrap(); @@ -106,6 +107,7 @@ async fn can_sequence_and_gossip_blocks() { ..SequencerArgs::default() }, beacon_provider_args: BeaconProviderArgs::default(), + signer_args: Default::default(), }; let (nodes, _tasks, wallet) = diff --git a/crates/sequencer/Cargo.toml b/crates/sequencer/Cargo.toml index fd053755..6de4b053 100644 --- a/crates/sequencer/Cargo.toml +++ b/crates/sequencer/Cargo.toml @@ -31,12 +31,16 @@ metrics-derive.workspace = true thiserror.workspace = true tokio.workspace = true tracing.workspace = true +eyre.workspace = true [dev-dependencies] # alloy alloy-consensus.workspace = true alloy-primitives.workspace = true +rollup-node-manager.workspace = true +rollup-node-signer.workspace = true + # scroll-alloy scroll-alloy-consensus.workspace = true @@ -59,3 +63,5 @@ scroll-engine.workspace = true # misc futures.workspace = true +tempfile = "3.20.0" +alloy-signer-local.workspace = true diff --git a/crates/sequencer/tests/e2e.rs b/crates/sequencer/tests/e2e.rs index 5a99ca28..2665c939 100644 --- a/crates/sequencer/tests/e2e.rs +++ b/crates/sequencer/tests/e2e.rs @@ -1,21 +1,28 @@ //! e2e tests for the sequencer. use alloy_consensus::BlockHeader; -use alloy_primitives::{Address, U256}; +use alloy_primitives::{hex, Address, U256}; use futures::stream::StreamExt; use reth_e2e_test_utils::transaction::TransactionTestContext; use reth_node_core::primitives::SignedTransaction; use reth_scroll_chainspec::SCROLL_DEV; use reth_scroll_node::test_utils::setup; -use rollup_node::test_utils::{default_test_scroll_rollup_node_config, setup_engine}; -use rollup_node_primitives::{BlockInfo, L1MessageEnvelope}; +use rollup_node::{ + test_utils::{default_test_scroll_rollup_node_config, setup_engine}, + BeaconProviderArgs, DatabaseArgs, EngineDriverArgs, L1ProviderArgs, NetworkArgs, + ScrollRollupNodeConfig, SequencerArgs, SignerArgs, +}; +use rollup_node_manager::RollupManagerEvent; +use rollup_node_primitives::{sig_encode_hash, BlockInfo, L1MessageEnvelope}; use rollup_node_providers::{DatabaseL1MessageProvider, ScrollRootProvider}; use rollup_node_sequencer::{L1MessageInclusionMode, Sequencer}; +use rollup_node_signer::SignerEvent; use scroll_alloy_consensus::TxL1Message; use scroll_alloy_provider::ScrollAuthApiEngineClient; use scroll_db::{test_utils::setup_test_db, DatabaseOperations}; use scroll_engine::{EngineDriver, EngineDriverEvent, ForkchoiceState}; -use std::sync::Arc; +use std::{io::Write, path::PathBuf, sync::Arc}; +use tempfile::NamedTempFile; use tokio::{sync::Mutex, time::Duration}; #[tokio::test] @@ -392,3 +399,163 @@ async fn can_build_blocks_with_finalized_l1_messages() { assert_eq!(block.body.transactions.len(), 1); assert_eq!(block.body.transactions.first().unwrap().tx_hash(), &unfinalized_message_hash); } + +#[tokio::test] +async fn can_sequence_blocks_with_private_key_file() -> eyre::Result<()> { + reth_tracing::init_test_tracing(); + + // Create temporary private key file + let mut temp_file = NamedTempFile::new().unwrap(); + let private_key_hex = "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef"; + temp_file.write_all(private_key_hex.as_bytes()).unwrap(); + temp_file.flush().unwrap(); + + // Create expected signer + let expected_key_bytes = + hex::decode("1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef").unwrap(); + let expected_signer = + alloy_signer_local::PrivateKeySigner::from_slice(&expected_key_bytes).unwrap(); + let expected_address = expected_signer.address(); + + let chain_spec = (*SCROLL_DEV).clone(); + let rollup_manager_args = ScrollRollupNodeConfig { + test: false, // disable test mode to enable real signing + network_args: NetworkArgs { enable_eth_scroll_wire_bridge: true, enable_scroll_wire: true }, + database_args: DatabaseArgs { path: Some(PathBuf::from("sqlite::memory:")) }, + l1_provider_args: L1ProviderArgs::default(), + engine_driver_args: EngineDriverArgs::default(), + sequencer_args: SequencerArgs { + sequencer_enabled: true, + block_time: 0, + max_l1_messages_per_block: 4, + l1_message_inclusion_mode: L1MessageInclusionMode::BlockDepth(0), + ..SequencerArgs::default() + }, + beacon_provider_args: BeaconProviderArgs::default(), + signer_args: SignerArgs { key_file: Some(temp_file.path().to_path_buf()) }, + }; + + let (nodes, _tasks, wallet) = setup_engine(rollup_manager_args, 1, chain_spec, false).await?; + let wallet = Arc::new(Mutex::new(wallet)); + + let sequencer_rnm_handle = nodes[0].inner.add_ons_handle.rollup_manager_handle.clone(); + let mut sequencer_events = sequencer_rnm_handle.get_event_listener().await?; + + // Generate and inject transaction + let mut wallet_lock = wallet.lock().await; + let raw_tx = TransactionTestContext::transfer_tx_nonce_bytes( + wallet_lock.chain_id, + wallet_lock.inner.clone(), + wallet_lock.inner_nonce, + ) + .await; + wallet_lock.inner_nonce += 1; + drop(wallet_lock); + let tx_hash = nodes[0].rpc.inject_tx(raw_tx).await?; + + // Build block + sequencer_rnm_handle.build_block().await; + + // Verify block was successfully sequenced + if let Some(RollupManagerEvent::BlockSequenced(block)) = sequencer_events.next().await { + assert_eq!(block.body.transactions.len(), 1); + assert_eq!(block.body.transactions.first().unwrap().tx_hash(), &tx_hash); + } else { + panic!("Failed to receive BlockSequenced event"); + } + + // Verify signing event and signature correctness + if let Some(RollupManagerEvent::SignerEvent(SignerEvent::SignedBlock { + block: signed_block, + signature, + })) = sequencer_events.next().await + { + let hash = sig_encode_hash(&signed_block); + let recovered_address = signature.recover_address_from_prehash(&hash)?; + assert_eq!(recovered_address, expected_address); + } else { + panic!("Failed to receive SignerEvent with signed block"); + } + + Ok(()) +} + +#[tokio::test] +async fn can_sequence_blocks_with_hex_key_file_without_prefix() -> eyre::Result<()> { + reth_tracing::init_test_tracing(); + + // Create temporary private key file (without 0x prefix) + let mut temp_file = NamedTempFile::new().unwrap(); + let private_key_hex = "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef"; + temp_file.write_all(private_key_hex.as_bytes()).unwrap(); + temp_file.flush().unwrap(); + + // Create expected signer + let expected_key_bytes = + hex::decode("1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef").unwrap(); + let expected_signer = + alloy_signer_local::PrivateKeySigner::from_slice(&expected_key_bytes).unwrap(); + let expected_address = expected_signer.address(); + + let chain_spec = (*SCROLL_DEV).clone(); + let rollup_manager_args = ScrollRollupNodeConfig { + test: false, // disable test mode to enable real signing + network_args: NetworkArgs { enable_eth_scroll_wire_bridge: true, enable_scroll_wire: true }, + database_args: DatabaseArgs { path: Some(PathBuf::from("sqlite::memory:")) }, + l1_provider_args: L1ProviderArgs::default(), + engine_driver_args: EngineDriverArgs::default(), + sequencer_args: SequencerArgs { + sequencer_enabled: true, + block_time: 0, + max_l1_messages_per_block: 4, + l1_message_inclusion_mode: L1MessageInclusionMode::BlockDepth(0), + ..SequencerArgs::default() + }, + beacon_provider_args: BeaconProviderArgs::default(), + signer_args: SignerArgs { key_file: Some(temp_file.path().to_path_buf()) }, + }; + + let (nodes, _tasks, wallet) = setup_engine(rollup_manager_args, 1, chain_spec, false).await?; + let wallet = Arc::new(Mutex::new(wallet)); + + let sequencer_rnm_handle = nodes[0].inner.add_ons_handle.rollup_manager_handle.clone(); + let mut sequencer_events = sequencer_rnm_handle.get_event_listener().await?; + + // Generate and inject transaction + let mut wallet_lock = wallet.lock().await; + let raw_tx = TransactionTestContext::transfer_tx_nonce_bytes( + wallet_lock.chain_id, + wallet_lock.inner.clone(), + wallet_lock.inner_nonce, + ) + .await; + wallet_lock.inner_nonce += 1; + drop(wallet_lock); + let tx_hash = nodes[0].rpc.inject_tx(raw_tx).await?; + + // Build block + sequencer_rnm_handle.build_block().await; + + // Verify block was successfully sequenced + if let Some(RollupManagerEvent::BlockSequenced(block)) = sequencer_events.next().await { + assert_eq!(block.body.transactions.len(), 1); + assert_eq!(block.body.transactions.first().unwrap().tx_hash(), &tx_hash); + } else { + panic!("Failed to receive BlockSequenced event"); + } + + // Verify signing event and signature correctness + if let Some(RollupManagerEvent::SignerEvent(SignerEvent::SignedBlock { + block: signed_block, + signature, + })) = sequencer_events.next().await + { + let hash = sig_encode_hash(&signed_block); + let recovered_address = signature.recover_address_from_prehash(&hash)?; + assert_eq!(recovered_address, expected_address); + } else { + panic!("Failed to receive SignerEvent with signed block"); + } + + Ok(()) +} diff --git a/crates/signer/Cargo.toml b/crates/signer/Cargo.toml index 120d0a97..a606ca00 100644 --- a/crates/signer/Cargo.toml +++ b/crates/signer/Cargo.toml @@ -30,7 +30,9 @@ tracing.workspace = true [dev-dependencies] alloy-signer-local.workspace = true +hex = "0.4.3" reth-tracing.workspace = true +tempfile = "3.20.0" [features] default = [] diff --git a/crates/signer/src/event.rs b/crates/signer/src/event.rs index 0bfc63d4..5d28886d 100644 --- a/crates/signer/src/event.rs +++ b/crates/signer/src/event.rs @@ -2,7 +2,7 @@ use alloy_signer::Signature; use reth_scroll_primitives::ScrollBlock; /// An enum representing the events that can be emitted by the signer. -#[derive(Debug)] +#[derive(Debug, Clone)] pub enum SignerEvent { /// A block has been signed by the signer. SignedBlock { diff --git a/docker-compose/docker-compose.yml b/docker-compose/docker-compose.yml index 0171a582..aa1207b3 100644 --- a/docker-compose/docker-compose.yml +++ b/docker-compose/docker-compose.yml @@ -16,7 +16,7 @@ services: --log.stdout.format log-fmt -vvv --l1.url http://l1reth-rpc.sepolia.scroll.tech:8545 --beacon.url http://l1reth-cl.sepolia.scroll.tech:5052 --network.scroll-wire --network.bridge --trusted-peers "enode://29cee709c400533ae038a875b9ca975c8abef9eade956dcf3585e940acd5c0ae916968f514bd37d1278775aad1b7db30f7032a70202a87fd7365bd8de3c9f5fc@44.242.39.33:30303,enode://ceb1636bac5cbb262e5ad5b2cd22014bdb35ffe7f58b3506970d337a63099481814a338dbcd15f2d28757151e3ecd40ba38b41350b793cd0d910ff0436654f8c@35.85.84.250:30303,enode://dd1ac5433c5c2b04ca3166f4cb726f8ff6d2da83dbc16d9b68b1ea83b7079b371eb16ef41c00441b6e85e32e33087f3b7753ea9e8b1e3f26d3e4df9208625e7f@54.148.111.168:30303" elif [ "$${ENV:-}" = "mainnet" ]; then - exec rollup-node node --chain scroll --datadir=/l2reth --metrics=0.0.0.0:6060 --disable-discovery \ + exec rollup-node node --chain scroll-mainnet --datadir=/l2reth --metrics=0.0.0.0:6060 --disable-discovery \ --http --http.addr=0.0.0.0 --http.port=8545 --http.corsdomain "*" --http.api admin,debug,eth,net,trace,txpool,web3,rpc,reth,ots,flashbots,miner,mev --ws --ws.addr 0.0.0.0 --ws.port 8546 --ws.api admin,debug,eth,net,trace,txpool,web3,rpc,reth,ots,flashbots,miner,mev \ --log.stdout.format log-fmt -vvv --l1.url http://l1geth-rpc.mainnet.scroll.tech:8545/l1 --beacon.url http://l1reth-cl.mainnet.scroll.tech:5052 --network.scroll-wire --network.bridge \