From c1cfe3599f91f27dfa6e07dd06d579e26b6ed877 Mon Sep 17 00:00:00 2001 From: Huan-Cheng Chang Date: Tue, 10 Dec 2024 12:03:29 +0000 Subject: [PATCH] feat(jstzd): launch jstzd server with cli --- Cargo.lock | 14 ++++++++ Cargo.toml | 4 +-- crates/jstzd/src/config.rs | 25 ++++++++++++-- crates/jstzd/src/lib.rs | 20 ++++++++--- crates/jstzd/src/task/jstzd.rs | 13 +++++++- crates/jstzd/tests/main_test.rs | 59 ++++++++++++++++++++++++--------- 6 files changed, 109 insertions(+), 26 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 608f2d539..489628196 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1757,6 +1757,19 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" +[[package]] +name = "globset" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15f1ce686646e7f1e19bf7d5533fe443a45dbfb990e00629110797578b42fb19" +dependencies = [ + "aho-corasick", + "bstr", + "log", + "regex-automata 0.4.8", + "regex-syntax 0.8.5", +] + [[package]] name = "group" version = "0.10.0" @@ -4252,6 +4265,7 @@ version = "8.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2e5347777e9aacb56039b0e1f28785929a8a3b709e87482e7442c72e7c12529d" dependencies = [ + "globset", "sha2 0.10.8", "walkdir", ] diff --git a/Cargo.toml b/Cargo.toml index 2a6b3199b..6abb3ef38 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -81,9 +81,9 @@ pretty_assertions = "1.4.1" proptest = "1.1" rand = "0.8" regex = "1" -reqwest = { version = "0.11.24", features = ["json"] } +reqwest = { version = "0.11.24", features = ["json", "blocking"] } reqwest-eventsource = "0.5.0" -rust-embed = { version = "8.5.0", features = ["interpolate-folder-path"] } +rust-embed = { version = "8.5.0", features = ["interpolate-folder-path", "include-exclude"] } rustyline = "14.0.0" serde = { version = "1.0.196", features = ["derive", "rc"] } serde-wasm-bindgen = "0.6.5" diff --git a/crates/jstzd/src/config.rs b/crates/jstzd/src/config.rs index ea98f7c50..bba058a1b 100644 --- a/crates/jstzd/src/config.rs +++ b/crates/jstzd/src/config.rs @@ -9,7 +9,9 @@ use crate::{ use anyhow::{Context, Result}; use jstz_node::config::JstzNodeConfig; use octez::r#async::endpoint::Endpoint; -use octez::r#async::protocol::{BootstrapContract, ProtocolParameter}; +use octez::r#async::protocol::{ + BootstrapContract, BootstrapSmartRollup, ProtocolParameter, SmartRollupPvmKind, +}; use octez::r#async::{ baker::{BakerBinaryPath, OctezBakerConfig, OctezBakerConfigBuilder}, client::{OctezClientConfig, OctezClientConfigBuilder}, @@ -42,6 +44,11 @@ pub(crate) const ROLLUP_OPERATOR_ACCOUNT_ALIAS: &str = "bootstrap1"; #[folder = "$CARGO_MANIFEST_DIR/resources/bootstrap_contract/"] pub struct BootstrapContractFile; +#[derive(Embed)] +#[folder = "$CARGO_MANIFEST_DIR/resources/jstz_rollup"] +#[include = "*.json"] +struct BootstrapRollupFile; + #[derive(Deserialize, Default)] struct Config { server_port: Option, @@ -185,8 +192,20 @@ async fn build_protocol_params( contracts.push(contract); } - // TODO: insert jstz rollup - builder.set_bootstrap_contracts(contracts).build() + builder + .set_bootstrap_smart_rollups([BootstrapSmartRollup::new( + JSTZ_ROLLUP_ADDRESS, + SmartRollupPvmKind::Wasm, + &tokio::fs::read_to_string(jstz_rollup_path::kernel_installer_path()).await?, + serde_json::from_slice( + &BootstrapRollupFile::get("parameters_ty.json") + .ok_or(anyhow::anyhow!("file not found"))? + .data, + )?, + ) + .unwrap()]) + .set_bootstrap_contracts(contracts) + .build() } #[cfg(test)] diff --git a/crates/jstzd/src/lib.rs b/crates/jstzd/src/lib.rs index 7fccaf067..aeadefc08 100644 --- a/crates/jstzd/src/lib.rs +++ b/crates/jstzd/src/lib.rs @@ -2,6 +2,7 @@ mod config; pub mod docker; pub mod task; +use crate::task::jstzd::{JstzdConfig, JstzdServer}; pub use config::BOOTSTRAP_CONTRACT_NAMES; pub mod jstz_rollup_path { include!(concat!(env!("OUT_DIR"), "/jstz_rollup_path.rs")); @@ -15,10 +16,7 @@ pub const JSTZ_NATIVE_BRIDGE_ADDRESS: &str = "KT1GFiPkkTjd14oHe6MrBPiRh5djzRkVWc /// The `main` function for running jstzd pub async fn main(config_path: &Option) { match config::build_config(config_path).await { - Ok((_port, _config)) => { - // TODO: run JstzdServer here - println!("ready"); - } + Ok((port, config)) => run(port, config).await, Err(e) => { match config_path { Some(p) => eprintln!("failed to build config from {}: {:?}", p, e), @@ -28,3 +26,17 @@ pub async fn main(config_path: &Option) { } } } + +async fn run(port: u16, config: JstzdConfig) { + let mut server = JstzdServer::new(config, port); + if let Err(e) = server.run().await { + eprintln!("failed to run jstzd server: {:?}", e); + let _ = server.stop().await; + exit(1); + } + + server.wait().await; + + println!("Shutting down"); + server.stop().await.unwrap(); +} diff --git a/crates/jstzd/src/task/jstzd.rs b/crates/jstzd/src/task/jstzd.rs index 1179ed457..e96a50b74 100644 --- a/crates/jstzd/src/task/jstzd.rs +++ b/crates/jstzd/src/task/jstzd.rs @@ -317,7 +317,7 @@ impl JstzdServer { } pub async fn run(&mut self) -> Result<()> { - let jstzd = Jstzd::spawn( + let jstzd = Self::spawn_jstzd( self.inner .state .read() @@ -379,6 +379,17 @@ impl JstzdServer { None => false, } } + + async fn spawn_jstzd(jstzd_config: JstzdConfig) -> Result { + let mut jstzd = Jstzd::spawn(jstzd_config).await?; + + let jstzd_healthy = retry(60, 500, || async { jstzd.health_check().await }).await; + if !jstzd_healthy { + let _ = jstzd.kill().await; + bail!("jstzd never turns healthy"); + } + Ok(jstzd) + } } async fn health_check(state: &ServerState) -> bool { diff --git a/crates/jstzd/tests/main_test.rs b/crates/jstzd/tests/main_test.rs index 2e8bbd7fb..6dc83b590 100644 --- a/crates/jstzd/tests/main_test.rs +++ b/crates/jstzd/tests/main_test.rs @@ -1,5 +1,8 @@ use assert_cmd::prelude::{CommandCargoExt, OutputAssertExt}; +use octez::unused_port; use predicates::prelude::predicate; +use std::thread; +use std::time::Duration; use std::{io::Write, process::Command}; use tempfile::NamedTempFile; @@ -13,26 +16,50 @@ fn unknown_command() { .stderr(predicate::str::contains("unrecognized subcommand \'test\'")); } -#[test] -fn default_config() { - let mut cmd = Command::cargo_bin("jstzd").unwrap(); - - cmd.arg("run") - .assert() - .success() - .stdout(predicate::str::contains("ready")); -} - +#[cfg_attr(feature = "skip-rollup-tests", ignore)] #[test] fn valid_config_file() { - let mut cmd = Command::cargo_bin("jstzd").unwrap(); + let port = unused_port(); let mut tmp_file = NamedTempFile::new().unwrap(); - tmp_file.write_all(r#"{"protocol":{"bootstrap_accounts":[["edpkuSLWfVU1Vq7Jg9FucPyKmma6otcMHac9zG4oU1KMHSTBpJuGQ2","6000000000"]]}}"#.as_bytes()).unwrap(); + tmp_file.write_all(format!(r#"{{"protocol":{{"bootstrap_accounts":[["edpkuSLWfVU1Vq7Jg9FucPyKmma6otcMHac9zG4oU1KMHSTBpJuGQ2","15000000000"]]}},"server_port":{}}}"#, port).as_bytes()).unwrap(); - cmd.args(["run", &tmp_file.path().to_string_lossy()]) - .assert() - .success() - .stdout(predicate::str::contains("ready")); + let handle = thread::spawn(move || { + Command::cargo_bin("jstzd") + .unwrap() + .args(["run", &tmp_file.path().to_string_lossy()]) + .assert() + .success() + // baker log writes to stderr + .stderr(predicate::str::contains( + "block ready for delegate: activator", + )); + }); + + let client = reqwest::blocking::Client::new(); + for _ in 0..30 { + thread::sleep(Duration::from_secs(1)); + if let Ok(r) = client + .get(&format!("http://localhost:{port}/health")) + .send() + { + if r.status().is_success() { + break; + } + } + } + + // wait for 5 more seconds to ensure that the baker starts baking in order to + // observe the expected log line above + thread::sleep(Duration::from_secs(5)); + assert!(client + .put(&format!("http://localhost:{port}/shutdown")) + .send() + .unwrap() + .status() + .is_success()); + handle + .join() + .expect("jstzd should have been taken down without any error"); } #[test]