diff --git a/Cargo.lock b/Cargo.lock index ddeabbd..f1a4cea 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -896,6 +896,18 @@ dependencies = [ "version_check", ] +[[package]] +name = "filetime" +version = "0.2.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4029edd3e734da6fe05b6cd7bd2960760a616bd2ddd0d59a0124746d6272af0" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall 0.3.5", + "windows-sys 0.48.0", +] + [[package]] name = "fixed-hash" version = "0.8.0" @@ -1535,6 +1547,12 @@ version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" +[[package]] +name = "maplit" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" + [[package]] name = "memchr" version = "2.6.4" @@ -1654,9 +1672,11 @@ version = "0.1.0" dependencies = [ "bollard", "eyre", + "flate2", "futures-util", "pretty_assertions", "serde", + "tar", "tokio", "tracing", "tracing-subscriber", @@ -1727,6 +1747,7 @@ version = "0.1.0" dependencies = [ "async-trait", "eyre", + "maplit", "op-composer", "op-config", "op-contracts", @@ -2752,6 +2773,17 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" +[[package]] +name = "tar" +version = "0.4.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b16afcea1f22891c49a00c751c7b63b2233284064f11a200fc624137c51e2ddb" +dependencies = [ + "filetime", + "libc", + "xattr", +] + [[package]] name = "temp_testdir" version = "0.2.3" @@ -3459,6 +3491,15 @@ dependencies = [ "tap", ] +[[package]] +name = "xattr" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4686009f71ff3e5c4dbcf1a282d0a44db3f021ba69350cd42086b3e5f1c6985" +dependencies = [ + "libc", +] + [[package]] name = "yansi" version = "0.5.1" diff --git a/bin/opup/src/deps.rs b/bin/opup/src/deps.rs index 9470148..8f9b098 100644 --- a/bin/opup/src/deps.rs +++ b/bin/opup/src/deps.rs @@ -82,9 +82,10 @@ impl DependencyManager { return Ok(()); } let version = semver::Version::parse("0.8.17")?; - tracing::info!("Installing Solidity version {:?}", version); + tracing::info!("Installing Solidity version {}", version); let _ = svm_lib::install(&version).await?; - tracing::info!("Solidity version {:?} installed", version); + svm_lib::use_version(&version)?; + tracing::info!("Solidity version {} installed", version); Ok(()) } diff --git a/bin/opup/src/up.rs b/bin/opup/src/up.rs index f327b9f..f0b1944 100644 --- a/bin/opup/src/up.rs +++ b/bin/opup/src/up.rs @@ -59,7 +59,7 @@ impl UpCommand { } /// Entrypoint - #[instrument(name = "up", target = "run")] + #[instrument(name = "up", target = "run", skip(self))] pub fn run(&self) -> Result<()> { crate::runner::run_until_ctrl_c(async { self.execute().await }) } diff --git a/crates/composer/Cargo.toml b/crates/composer/Cargo.toml index 175d8f0..6a23c08 100644 --- a/crates/composer/Cargo.toml +++ b/crates/composer/Cargo.toml @@ -18,6 +18,8 @@ serde.workspace = true tokio = { version = "1.11.0", features = ["full"] } futures-util = "0.3.28" bollard = "0.15.0" +flate2 = "1.0.28" +tar = "0.4.40" [dev-dependencies] tracing-subscriber.workspace = true diff --git a/crates/composer/src/lib.rs b/crates/composer/src/lib.rs index ab22c3d..d8c3766 100644 --- a/crates/composer/src/lib.rs +++ b/crates/composer/src/lib.rs @@ -16,15 +16,22 @@ use bollard::{ StartContainerOptions, StopContainerOptions, }, exec::{CreateExecOptions, StartExecResults}, - image::CreateImageOptions, - service::{ContainerCreateResponse, ContainerSummary}, + image::BuildImageOptions, + service::{ContainerCreateResponse, ContainerSummary, Volume}, Docker, }; use eyre::{bail, Result}; use futures_util::{StreamExt, TryStreamExt}; use serde::Serialize; -pub use bollard::service::ContainerConfig; +pub use bollard::container::Config; +pub use bollard::image::CreateImageOptions; +pub use bollard::service::HostConfig; +pub use bollard::volume::CreateVolumeOptions; +pub use utils::bind_host_port; + +/// Utilities for Docker operations +mod utils; /// The Composer is responsible for managing the OP-UP docker containers. #[derive(Debug)] @@ -85,16 +92,11 @@ impl Composer { }) }) .try_collect::>() - .await - .map_err(|e| { - tracing::error!(target: "composer", "Error creating docker image: {:?}", e); - e - })?; + .await?; - println!("res: {:?}", res); tracing::debug!(target: "composer", "Created docker image: {:?}", res); - match res.get(0) { + match res.first() { Some(info) => match info.id.as_ref() { Some(id) => Ok(id.clone()), None => bail!("No image ID found in response"), @@ -103,11 +105,47 @@ impl Composer { } } + /// Build a Docker image from the specified Dockerfile and build context files. + pub async fn build_image( + &self, + name: &str, + dockerfile: &str, + build_context_files: &[(&str, &[u8])], + ) -> Result<()> { + let build_options = BuildImageOptions { + t: name, + dockerfile: "Dockerfile", + pull: true, + ..Default::default() + }; + + let files = utils::create_dockerfile_build_context(dockerfile, build_context_files)?; + let mut image_build_stream = + self.daemon + .build_image(build_options, None, Some(files.into())); + + while let Some(build_info) = image_build_stream.next().await { + let res = build_info?; + tracing::debug!(target: "composer", "Build info: {:?}", res); + } + + Ok(()) + } + + /// Creates a Docker volume with the specified options. + pub async fn create_volume(&self, config: CreateVolumeOptions) -> Result + where + T: Into + Serialize + Eq + std::hash::Hash, + { + self.daemon.create_volume(config).await.map_err(Into::into) + } + /// Create a Docker container for the specified OP Stack component pub async fn create_container( &self, name: &str, - mut config: ContainerConfig, + mut config: Config, + overwrite: bool, ) -> Result { let create_options = CreateContainerOptions { name, @@ -120,9 +158,43 @@ impl Composer { "op-up".to_string(), ); + // Check if a container already exists with the specified name. If it does: + // - If overwrite is true, remove the existing container and create a new one. + // - If overwrite is false, return the existing container ID. + let containers = self.list_containers(None).await?; + if let Some(container) = containers.iter().find(|container| { + container + .names + .as_ref() + .map(|names| { + names + .iter() + .any(|n| n == name || n == &format!("/{}", name)) + }) + .unwrap_or(false) + }) { + tracing::debug!(target: "composer", "Container {} already exists", name); + let id = container + .id + .clone() + .ok_or_else(|| eyre::eyre!("No container ID found"))?; + + if overwrite { + self.daemon + .remove_container(&id, None::) + .await?; + tracing::debug!(target: "composer", "Removed existing docker container {}", name); + } else { + return Ok(ContainerCreateResponse { + id, + warnings: vec![], + }); + } + } + let res = self .daemon - .create_container(Some(create_options), config.into()) + .create_container(Some(create_options), config) .await?; tracing::debug!(target: "composer", "Created docker container {} with ID: {}", name, res.id); diff --git a/crates/composer/src/utils.rs b/crates/composer/src/utils.rs new file mode 100644 index 0000000..0ec6f04 --- /dev/null +++ b/crates/composer/src/utils.rs @@ -0,0 +1,45 @@ +use std::io::Write; + +use bollard::service::PortBinding; +use eyre::Result; +use flate2::{write::GzEncoder, Compression}; + +/// Given a dockerfile string and any number of files as `(filename, file contents)`, +/// create a tarball and gzip the tarball. Returns the compressed output bytes. +pub(crate) fn create_dockerfile_build_context( + dockerfile: &str, + files: &[(&str, &[u8])], +) -> Result> { + // First create a Dockerfile tarball + let mut header = tar::Header::new_gnu(); + header.set_path("Dockerfile")?; + header.set_size(dockerfile.len() as u64); + header.set_mode(0o755); + header.set_cksum(); + let mut tar = tar::Builder::new(Vec::new()); + tar.append(&header, dockerfile.as_bytes())?; + + // Then append any additional files + for (filename, contents) in files { + let mut header = tar::Header::new_gnu(); + header.set_path(filename)?; + header.set_size(contents.len() as u64); + header.set_mode(0o755); + header.set_cksum(); + tar.append(&header, *contents)?; + } + + // Finally, gzip the tarball + let uncompressed = tar.into_inner()?; + let mut c = GzEncoder::new(Vec::new(), Compression::default()); + c.write_all(&uncompressed)?; + c.finish().map_err(Into::into) +} + +/// Given a host port, bind it to the container. +pub fn bind_host_port(host_port: u16) -> Option> { + Some(vec![PortBinding { + host_ip: Some("127.0.0.1".to_string()), + host_port: Some(host_port.to_string()), + }]) +} diff --git a/crates/composer/tests/basic.rs b/crates/composer/tests/basic.rs index 022f906..8b2f112 100644 --- a/crates/composer/tests/basic.rs +++ b/crates/composer/tests/basic.rs @@ -1,7 +1,7 @@ use std::collections::HashMap; -use bollard::{image::CreateImageOptions, service::ContainerConfig}; -use op_composer::Composer; +use bollard::image::CreateImageOptions; +use op_composer::{Composer, Config}; /// This is a basic test of the Composer functionality to create and start a Docker container, run a simple /// command in the container, and then stop and remove it. If the Docker daemon is not running, this test @@ -22,7 +22,7 @@ pub async fn test_basic_docker_composer() -> eyre::Result<()> { composer.create_image(image_config).await?; // 2. Create the container with the new image - let container_config = ContainerConfig { + let container_config = Config { exposed_ports: Some(HashMap::<_, _>::from_iter([( "7777".to_string(), HashMap::new(), @@ -32,12 +32,8 @@ pub async fn test_basic_docker_composer() -> eyre::Result<()> { }; let container = composer - .create_container("test_basic_docker_composer", container_config) + .create_container("test_basic_docker_composer", container_config, false) .await?; - println!("Created container: {:?}", container); - - let all_containers = composer.list_containers(None).await?; - assert_eq!(all_containers.len(), 1); // 3. Start running container composer.start_container(&container.id).await?; diff --git a/crates/primitives/src/components/challenger.rs b/crates/primitives/src/components/challenger.rs index 664eae0..616e33f 100644 --- a/crates/primitives/src/components/challenger.rs +++ b/crates/primitives/src/components/challenger.rs @@ -5,7 +5,9 @@ use serde::{Deserialize, Serialize}; use strum::EnumIter; /// Challenger Agent Implementations -#[derive(Default, Clone, PartialEq, EnumVariantsStrings, Deserialize, Serialize, EnumIter)] +#[derive( + Default, Copy, Clone, PartialEq, EnumVariantsStrings, Deserialize, Serialize, EnumIter, +)] #[serde(rename_all = "kebab-case")] #[enum_variants_strings_transform(transform = "kebab_case")] pub enum ChallengerAgent { diff --git a/crates/primitives/src/components/layer_one.rs b/crates/primitives/src/components/layer_one.rs index 3bf5adb..7534667 100644 --- a/crates/primitives/src/components/layer_one.rs +++ b/crates/primitives/src/components/layer_one.rs @@ -7,7 +7,9 @@ use strum::EnumIter; /// L1 Client /// /// The OP Stack L1 client is an L1 execution client. -#[derive(Default, Clone, PartialEq, EnumVariantsStrings, Deserialize, Serialize, EnumIter)] +#[derive( + Default, Copy, Clone, PartialEq, EnumVariantsStrings, Deserialize, Serialize, EnumIter, +)] #[serde(rename_all = "kebab-case")] #[enum_variants_strings_transform(transform = "kebab_case")] pub enum L1Client { diff --git a/crates/primitives/src/components/layer_two.rs b/crates/primitives/src/components/layer_two.rs index c42341e..b203d57 100644 --- a/crates/primitives/src/components/layer_two.rs +++ b/crates/primitives/src/components/layer_two.rs @@ -9,7 +9,9 @@ use strum::EnumIter; /// The OP Stack L2 client is a minimally modified version of the L1 client /// that supports deposit transactions as well as a few other small OP-specific /// changes. -#[derive(Default, Clone, PartialEq, EnumVariantsStrings, Deserialize, Serialize, EnumIter)] +#[derive( + Default, Copy, Clone, PartialEq, EnumVariantsStrings, Deserialize, Serialize, EnumIter, +)] #[serde(rename_all = "kebab-case")] #[enum_variants_strings_transform(transform = "kebab_case")] pub enum L2Client { diff --git a/crates/primitives/src/components/rollup.rs b/crates/primitives/src/components/rollup.rs index 1bc3a52..c8c2f35 100644 --- a/crates/primitives/src/components/rollup.rs +++ b/crates/primitives/src/components/rollup.rs @@ -8,7 +8,9 @@ use strum::EnumIter; /// /// The OP Stack rollup client performs the derivation of the rollup state /// from the L1 and L2 clients. -#[derive(Default, Clone, PartialEq, EnumVariantsStrings, Deserialize, Serialize, EnumIter)] +#[derive( + Default, Copy, Clone, PartialEq, EnumVariantsStrings, Deserialize, Serialize, EnumIter, +)] #[serde(rename_all = "kebab-case")] #[enum_variants_strings_transform(transform = "kebab_case")] pub enum RollupClient { diff --git a/crates/stages/Cargo.toml b/crates/stages/Cargo.toml index 8cf9ea7..a3dfb9c 100644 --- a/crates/stages/Cargo.toml +++ b/crates/stages/Cargo.toml @@ -23,3 +23,4 @@ serde_json.workspace = true async-trait = "0.1.73" project-root = "0.2.2" +maplit = "1.0.2" \ No newline at end of file diff --git a/crates/stages/src/stages.rs b/crates/stages/src/stages.rs index 24e1c31..7ec1e9e 100644 --- a/crates/stages/src/stages.rs +++ b/crates/stages/src/stages.rs @@ -59,10 +59,7 @@ impl Stages<'_> { composer: Arc, ) -> Vec> { let genesis_timestamp = genesis::current_timestamp(); - let l1_client = self.config.l1_client.to_string(); - let l2_client = self.config.l2_client.to_string(); - let rollup_client = self.config.rollup_client.to_string(); - let challenge_agent = self.config.challenger.to_string(); + vec![ Box::new(directories::Directories::new( Arc::clone(&artifacts), @@ -79,11 +76,12 @@ impl Stages<'_> { )), Box::new(l1_genesis::L1Genesis::new( Arc::clone(&monorepo), + Arc::clone(&artifacts), genesis_timestamp, )), Box::new(l1_exec::Executor::new( self.config.l1_client_port, - l1_client, + self.config.l1_client, composer, Arc::clone(&artifacts), )), @@ -94,11 +92,11 @@ impl Stages<'_> { Box::new(contracts::Contracts::new()), Box::new(l2_exec::Executor::new( self.config.l2_client_port, - l2_client, + self.config.l2_client, )), Box::new(rollup::Rollup::new( self.config.rollup_client_port, - rollup_client, + self.config.rollup_client, )), Box::new(proposer::Proposer::new(Arc::clone(&artifacts))), Box::new(batcher::Batcher::new( @@ -107,7 +105,7 @@ impl Stages<'_> { )), Box::new(challenger::Challenger::new( Arc::clone(&artifacts), - challenge_agent, + self.config.challenger, )), Box::new(stateviz::Stateviz::new(Arc::clone(&artifacts))), ] diff --git a/crates/stages/src/stages/challenger.rs b/crates/stages/src/stages/challenger.rs index 7ea1f22..18e1b8b 100644 --- a/crates/stages/src/stages/challenger.rs +++ b/crates/stages/src/stages/challenger.rs @@ -1,6 +1,6 @@ use async_trait::async_trait; use eyre::Result; -use op_primitives::Artifacts; +use op_primitives::{Artifacts, ChallengerAgent}; use std::process::Command; use std::sync::Arc; @@ -8,7 +8,7 @@ use std::sync::Arc; #[derive(Debug, Default, Clone, PartialEq)] pub struct Challenger { artifacts: Arc, - challenger: String, + challenger: ChallengerAgent, } #[async_trait] @@ -30,7 +30,7 @@ impl crate::Stage for Challenger { .env("PWD", &docker_dir) .env("L2OO_ADDRESS", addresses["L2OutputOracleProxy"].to_string()) .env("DGF_ADDRESS", addresses["DisputeGameFactory"].to_string()) - .env("CHALLENGER_AGENT_CHOICE", &self.challenger) + .env("CHALLENGER_AGENT_CHOICE", &self.challenger.to_string()) .current_dir(docker_dir) .output()?; @@ -48,7 +48,7 @@ impl crate::Stage for Challenger { impl Challenger { /// Creates a new challenger stage. - pub fn new(artifacts: Arc, challenger: String) -> Self { + pub fn new(artifacts: Arc, challenger: ChallengerAgent) -> Self { Self { artifacts, challenger, diff --git a/crates/stages/src/stages/l1_exec.rs b/crates/stages/src/stages/l1_exec.rs index 6b2d3b3..298d757 100644 --- a/crates/stages/src/stages/l1_exec.rs +++ b/crates/stages/src/stages/l1_exec.rs @@ -1,18 +1,18 @@ use eyre::Result; -use std::collections::HashMap; +use maplit::hashmap; +use op_primitives::L1Client; use std::sync::Arc; use async_trait::async_trait; -use op_composer::Composer; -use op_composer::ContainerConfig; +use op_composer::{bind_host_port, Composer, Config, CreateVolumeOptions, HostConfig}; use op_primitives::Artifacts; /// L1 Execution Client Stage #[derive(Debug)] pub struct Executor { l1_port: Option, - l1_client: String, + l1_client: L1Client, l1_exec: Arc, artifacts: Arc, } @@ -23,50 +23,10 @@ impl crate::Stage for Executor { async fn execute(&self) -> Result<()> { tracing::info!(target: "stages", "Executing l1 execution client stage"); - let working_dir = project_root::get_project_root()?.join("docker"); - let l1_genesis = self.artifacts.l1_genesis(); - let jwt_secret = self.artifacts.jwt_secret(); - let config = ContainerConfig { - cmd: Some(vec![ - "/bin/sh".to_string(), - "geth-entrypoint.sh".to_string(), - ]), - image: Some("ethereum/client-go:v1.12.2".to_string()), - working_dir: Some(working_dir.to_string_lossy().to_string()), - volumes: Some(HashMap::from([ - ("l1_data:/db".to_string(), HashMap::new()), - ( - format!("{}:/genesis.json", l1_genesis.to_string_lossy()), - HashMap::new(), - ), - ( - format!( - "{}:/config/test-jwt-secret.txt", - jwt_secret.to_string_lossy() - ), - HashMap::new(), - ), - ])), - exposed_ports: Some(HashMap::from([ - ("8545:8545".to_string(), HashMap::new()), - ("8546:8546".to_string(), HashMap::new()), - ("7060:6060".to_string(), HashMap::new()), - ])), - ..Default::default() - }; - - let response = self - .l1_exec - .create_container(&self.l1_client, config) - .await?; - tracing::info!(target: "stages", "l1 container created: {:?}", response); - - let l1_port = self.l1_port.unwrap_or(op_config::L1_PORT); - crate::net::wait_up(l1_port, 10, 1)?; - - // todo: do we need to do block here - // can we wait for the l1 client to be ready by polling? - std::thread::sleep(std::time::Duration::from_secs(10)); + match self.l1_client { + L1Client::Geth => self.start_geth().await?, + _ => unimplemented!("l1 client not implemented: {}", self.l1_client), + } Ok(()) } @@ -76,7 +36,7 @@ impl Executor { /// Creates a new stage. pub fn new( l1_port: Option, - l1_client: String, + l1_client: L1Client, l1_exec: Arc, artifacts: Arc, ) -> Self { @@ -87,4 +47,83 @@ impl Executor { artifacts, } } + + /// Starts Geth in a docker container. + pub async fn start_geth(&self) -> Result<()> { + let image_name = "opup-geth".to_string(); + let working_dir = project_root::get_project_root()?.join("docker"); + let l1_genesis = self.artifacts.l1_genesis(); + let l1_genesis = l1_genesis.to_string_lossy(); + let jwt_secret = self.artifacts.jwt_secret(); + let jwt_secret = jwt_secret.to_string_lossy(); + + let dockerfile = r#" + FROM ethereum/client-go:v1.12.2 + RUN apk add --no-cache jq + COPY geth-entrypoint.sh /geth-entrypoint.sh + VOLUME ["/db"] + ENTRYPOINT ["/bin/sh", "/geth-entrypoint.sh"] + "#; + + let geth_entrypoint = std::fs::read(working_dir.join("geth-entrypoint.sh"))?; + let build_context_files = [("geth-entrypoint.sh", geth_entrypoint.as_slice())]; + self.l1_exec + .build_image(&image_name, dockerfile, &build_context_files) + .await?; + + let l1_data_volume = CreateVolumeOptions { + name: "l1_data", + driver: "local", + ..Default::default() + }; + self.l1_exec.create_volume(l1_data_volume).await?; + + let config = Config { + image: Some(image_name), + working_dir: Some(working_dir.to_string_lossy().to_string()), + exposed_ports: Some(hashmap! { + "8545".to_string() => hashmap! {}, + "8546".to_string() => hashmap! {}, + "6060".to_string() => hashmap! {}, + }), + // TODO: add env vars to change values in entrypoint script + host_config: Some(HostConfig { + port_bindings: Some(hashmap! { + "8545".to_string() => bind_host_port(8545), + "8546".to_string() => bind_host_port(8546), + "6060".to_string() => bind_host_port(7060), // TODO: double check this port + }), + binds: Some(vec![ + "l1_data:/db".to_string(), + format!("{}:/genesis.json", l1_genesis), + format!("{}:/config/test-jwt-secret.txt", jwt_secret), + ]), + ..Default::default() + }), + ..Default::default() + }; + + let container_id = self + .l1_exec + .create_container(&self.l1_client.to_string(), config, true) + .await? + .id; + + let all_containers = self.l1_exec.list_containers(None).await?; + tracing::info!(target: "stages", "all containers: {:?}", all_containers); + + tracing::info!(target: "stages", "l1 container created: {}", container_id); + + self.l1_exec.start_container(&container_id).await?; + + let l1_port = self.l1_port.unwrap_or(op_config::L1_PORT); + crate::net::wait_up(l1_port, 10, 3)?; + tracing::info!(target: "stages", "l1 container started on port: {}", l1_port); + + // todo: do we need to do block here + // can we wait for the l1 client to be ready by polling? + std::thread::sleep(std::time::Duration::from_secs(10)); + + Ok(()) + } } diff --git a/crates/stages/src/stages/l1_genesis.rs b/crates/stages/src/stages/l1_genesis.rs index d1611f3..fe9e0c0 100644 --- a/crates/stages/src/stages/l1_genesis.rs +++ b/crates/stages/src/stages/l1_genesis.rs @@ -1,6 +1,6 @@ use async_trait::async_trait; use eyre::Result; -use op_primitives::{path_to_str, Monorepo}; +use op_primitives::{path_to_str, Artifacts, Monorepo}; use std::process::Command; use std::sync::Arc; @@ -8,6 +8,7 @@ use std::sync::Arc; #[derive(Debug, Default, Clone, PartialEq)] pub struct L1Genesis { monorepo: Arc, + artifacts: Arc, genesis_timestamp: u64, } @@ -17,16 +18,27 @@ impl crate::Stage for L1Genesis { async fn execute(&self) -> Result<()> { tracing::info!(target: "stages", "Executing l1 genesis stage"); + // Artifacts paths + let l1_genesis_artifact = self.artifacts.l1_genesis(); + let addresses_json_artifact = self.artifacts.l1_deployments(); + let addresses_json_artifact = path_to_str!(addresses_json_artifact)?; + let jwt_secret_artifact = self.artifacts.jwt_secret(); + + // Monorepo paths let deploy_config = self.monorepo.deploy_config(); let deploy_config = path_to_str!(deploy_config)?; let allocs = self.monorepo.allocs(); let allocs = path_to_str!(allocs)?; - let l1_genesis = self.monorepo.l1_genesis(); - let addresses_json = self.monorepo.addresses_json(); - let addresses_json = path_to_str!(addresses_json)?; let op_node_dir = self.monorepo.op_node_dir(); - if l1_genesis.exists() { + if !jwt_secret_artifact.exists() { + tracing::info!(target: "stages", "Creating jwt secret..."); + // TODO: take this from the TOML stack config + let jwt_secret = "688f5d737bad920bdfb2fc2f488d6b6209eebda1dae949a8de91398d932c517a"; + std::fs::write(&jwt_secret_artifact, jwt_secret)?; + } + + if l1_genesis_artifact.exists() { tracing::info!(target: "stages", "L1 genesis already found."); return Ok(()); } @@ -35,14 +47,14 @@ impl crate::Stage for L1Genesis { let genesis_template = op_primitives::genesis::genesis_template_string(self.genesis_timestamp) .ok_or_else(|| eyre::eyre!("Could not create genesis template"))?; - std::fs::write(&l1_genesis, genesis_template)?; - let l1_genesis_str = path_to_str!(l1_genesis)?; + std::fs::write(&l1_genesis_artifact, genesis_template)?; + let l1_genesis_artifact = path_to_str!(l1_genesis_artifact)?; let l1_genesis = Command::new("go") .args(["run", "cmd/main.go", "genesis", "l1"]) .args(["--deploy-config", deploy_config]) .args(["--l1-allocs", allocs]) - .args(["--l1-deployments", addresses_json]) - .args(["--outfile.l1", l1_genesis_str]) + .args(["--l1-deployments", addresses_json_artifact]) + .args(["--outfile.l1", l1_genesis_artifact]) .current_dir(op_node_dir) .output()?; @@ -59,9 +71,10 @@ impl crate::Stage for L1Genesis { impl L1Genesis { /// Creates a new stage. - pub fn new(monorepo: Arc, genesis_timestamp: u64) -> Self { + pub fn new(monorepo: Arc, artifacts: Arc, genesis_timestamp: u64) -> Self { Self { monorepo, + artifacts, genesis_timestamp, } } diff --git a/crates/stages/src/stages/l2_exec.rs b/crates/stages/src/stages/l2_exec.rs index 97f9543..aa7c8e0 100644 --- a/crates/stages/src/stages/l2_exec.rs +++ b/crates/stages/src/stages/l2_exec.rs @@ -1,12 +1,13 @@ use async_trait::async_trait; use eyre::Result; +use op_primitives::L2Client; use std::process::Command; /// Layer 2 Execution Client Stage #[derive(Debug, Default, Clone, PartialEq)] pub struct Executor { l2_port: Option, - l2_client: String, + l2_client: L2Client, } #[async_trait] @@ -23,7 +24,7 @@ impl crate::Stage for Executor { let start_l2 = Command::new("docker-compose") .args(["up", "-d", "--no-deps", "--build", "l2"]) .env("PWD", &docker_dir) - .env("L2_CLIENT_CHOICE", &self.l2_client) + .env("L2_CLIENT_CHOICE", &self.l2_client.to_string()) .current_dir(docker_dir) .output()?; @@ -41,7 +42,7 @@ impl crate::Stage for Executor { impl Executor { /// Creates a new stage. - pub fn new(l2_port: Option, l2_client: String) -> Self { + pub fn new(l2_port: Option, l2_client: L2Client) -> Self { Self { l2_port, l2_client } } } diff --git a/crates/stages/src/stages/rollup.rs b/crates/stages/src/stages/rollup.rs index 5b8c9c8..b334053 100644 --- a/crates/stages/src/stages/rollup.rs +++ b/crates/stages/src/stages/rollup.rs @@ -1,12 +1,13 @@ use async_trait::async_trait; use eyre::Result; +use op_primitives::RollupClient; use std::process::Command; /// Rollup Stage #[derive(Debug, Default, Clone, PartialEq)] pub struct Rollup { rollup_port: Option, - rollup_client: String, + rollup_client: RollupClient, } #[async_trait] @@ -24,7 +25,7 @@ impl crate::Stage for Rollup { let start_rollup = Command::new("docker-compose") .args(["up", "-d", "--no-deps", "--build", "rollup-client"]) .env("PWD", &docker_dir) - .env("ROLLUP_CLIENT_CHOICE", &self.rollup_client) + .env("ROLLUP_CLIENT_CHOICE", &self.rollup_client.to_string()) .current_dir(docker_dir) .output()?; @@ -42,7 +43,7 @@ impl crate::Stage for Rollup { impl Rollup { /// Creates a new stage. - pub fn new(rollup_port: Option, rollup_client: String) -> Self { + pub fn new(rollup_port: Option, rollup_client: RollupClient) -> Self { Self { rollup_port, rollup_client,