From 8165914237b66ef91e0d2319d832a45b63d31cde Mon Sep 17 00:00:00 2001 From: leo Date: Fri, 22 Nov 2024 13:08:55 +0000 Subject: [PATCH] feat(jstzd): spawn rollup in jstzd server --- crates/jstzd/src/task/jstzd.rs | 57 +++++- crates/jstzd/src/task/utils.rs | 16 ++ crates/jstzd/tests/jstzd_test.rs | 232 +++++++++++++++++++++++-- crates/jstzd/tests/octez_baker_test.rs | 8 +- crates/jstzd/tests/utils.rs | 36 ++-- 5 files changed, 299 insertions(+), 50 deletions(-) diff --git a/crates/jstzd/src/task/jstzd.rs b/crates/jstzd/src/task/jstzd.rs index 2d4e3be8c..3bd9af8ac 100644 --- a/crates/jstzd/src/task/jstzd.rs +++ b/crates/jstzd/src/task/jstzd.rs @@ -1,5 +1,11 @@ -use super::{octez_baker::OctezBaker, octez_node::OctezNode, utils::retry, Task}; -use anyhow::Result; +use super::{ + octez_baker::OctezBaker, + octez_node::OctezNode, + octez_rollup::OctezRollup, + utils::{get_block_level, retry}, + Task, +}; +use anyhow::{bail, Context, Result}; use async_dropper_simple::{AsyncDrop, AsyncDropper}; use async_trait::async_trait; use axum::{ @@ -11,8 +17,10 @@ use axum::{ use octez::r#async::{ baker::OctezBakerConfig, client::{OctezClient, OctezClientConfig}, + endpoint::Endpoint, node_config::OctezNodeConfig, protocol::ProtocolParameter, + rollup::OctezRollupConfig, }; use serde::Serialize; use std::sync::Arc; @@ -21,6 +29,7 @@ use tokio::{net::TcpListener, sync::RwLock, task::JoinHandle}; struct Jstzd { octez_node: Arc>, baker: Arc>, + rollup: Arc>, } #[derive(Clone, Serialize)] @@ -32,6 +41,8 @@ pub struct JstzdConfig { #[serde(rename(serialize = "octez-client"))] octez_client_config: OctezClientConfig, #[serde(skip_serializing)] + octez_rollup_config: OctezRollupConfig, + #[serde(skip_serializing)] protocol_params: ProtocolParameter, } @@ -40,12 +51,14 @@ impl JstzdConfig { octez_node_config: OctezNodeConfig, baker_config: OctezBakerConfig, octez_client_config: OctezClientConfig, + octez_rollup_config: OctezRollupConfig, protocol_params: ProtocolParameter, ) -> Self { Self { octez_node_config, baker_config, octez_client_config, + octez_rollup_config, protocol_params, } } @@ -72,13 +85,16 @@ impl Task for Jstzd { let octez_client = OctezClient::new(config.octez_client_config.clone()); Self::wait_for_node(&octez_node).await?; - Self::import_activator(&octez_client).await; + Self::import_activator(&octez_client).await?; + Self::import_rollup_operator(&octez_client).await?; Self::activate_protocol(&octez_client, &config.protocol_params).await?; - let baker = OctezBaker::spawn(config.baker_config.clone()).await?; + Self::wait_for_block_level(&config.octez_node_config.rpc_endpoint, 3).await?; + let rollup = OctezRollup::spawn(config.octez_rollup_config.clone()).await?; Ok(Self { octez_node: Arc::new(RwLock::new(octez_node)), baker: Arc::new(RwLock::new(baker)), + rollup: Arc::new(RwLock::new(rollup)), }) } @@ -86,6 +102,7 @@ impl Task for Jstzd { let results = futures::future::join_all([ self.octez_node.write().await.kill(), self.baker.write().await.kill(), + self.rollup.write().await.kill(), ]) .await; @@ -107,6 +124,7 @@ impl Task for Jstzd { let check_results = futures::future::join_all([ self.octez_node.read().await.health_check(), self.baker.read().await.health_check(), + self.rollup.read().await.health_check(), ]) .await; @@ -131,12 +149,25 @@ impl Jstzd { const ACTIVATOR_ACCOUNT_SK: &'static str = "unencrypted:edsk31vznjHSSpGExDMHYASz45VZqXN4DPxvsa4hAyY8dHM28cZzp6"; const ACTIVATOR_ACCOUNT_ALIAS: &'static str = "activator"; + const ROLLUP_OPERATOR_ACCOUNT_SK: &'static str = + "unencrypted:edsk3gUfUPyBSfrS9CCgmCiQsTCHGkviBDusMxDJstFtojtc1zcpsh"; + const ROLLUP_OPERATOR_ACCOUNT_ALIAS: &'static str = "bootstrap1"; - async fn import_activator(octez_client: &OctezClient) { + async fn import_activator(octez_client: &OctezClient) -> Result<()> { octez_client .import_secret_key(Self::ACTIVATOR_ACCOUNT_ALIAS, Self::ACTIVATOR_ACCOUNT_SK) .await - .expect("Failed to import account 'activator'"); + .context("Failed to import account 'activator'") + } + + async fn import_rollup_operator(octez_client: &OctezClient) -> Result<()> { + octez_client + .import_secret_key( + Self::ROLLUP_OPERATOR_ACCOUNT_ALIAS, + Self::ROLLUP_OPERATOR_ACCOUNT_SK, + ) + .await + .context("Failed to import account 'rollup_operator'") } async fn activate_protocol( @@ -165,6 +196,20 @@ impl Jstzd { } Ok(()) } + + /// Wait for the baker to bake at least `level` blocks. + async fn wait_for_block_level(node_endpoint: &Endpoint, level: i64) -> Result<()> { + let ready = retry(10, 1000, || async { + get_block_level(&node_endpoint.to_string()) + .await + .map(|l| l >= level) + }) + .await; + if !ready { + bail!("baker is not ready after retries"); + } + Ok(()) + } } #[derive(Clone, Default)] diff --git a/crates/jstzd/src/task/utils.rs b/crates/jstzd/src/task/utils.rs index 9c61c40f5..0e6622889 100644 --- a/crates/jstzd/src/task/utils.rs +++ b/crates/jstzd/src/task/utils.rs @@ -1,3 +1,6 @@ +use anyhow::{anyhow, Result}; +use serde_json::Value; + pub async fn retry<'a, F>(retries: u16, interval_ms: u64, f: impl Fn() -> F) -> bool where F: std::future::Future> + Send + 'a, @@ -13,3 +16,16 @@ where } false } + +pub async fn get_block_level(rpc_endpoint: &str) -> Result { + let blocks_head_endpoint = format!("{}/chains/main/blocks/head", rpc_endpoint); + let response: Value = reqwest::get(&blocks_head_endpoint).await?.json().await?; + + let level = response + .get("header") + .and_then(|header| header.get("level")) + .ok_or_else(|| anyhow!("Failed to extract level from head block"))?; + level + .as_i64() + .ok_or_else(|| anyhow!("Level is not a valid i64")) +} diff --git a/crates/jstzd/tests/jstzd_test.rs b/crates/jstzd/tests/jstzd_test.rs index 0bb56b1b9..f6114104d 100644 --- a/crates/jstzd/tests/jstzd_test.rs +++ b/crates/jstzd/tests/jstzd_test.rs @@ -1,32 +1,65 @@ mod utils; +use std::io::Read; use std::path::PathBuf; +use std::str::FromStr; +use anyhow::Result; + +use http::Uri; use jstzd::task::jstzd::{JstzdConfig, JstzdServer}; use jstzd::task::utils::retry; -use jstzd::{EXCHANGER_ADDRESS, JSTZ_NATIVE_BRIDGE_ADDRESS}; +use jstzd::{EXCHANGER_ADDRESS, JSTZ_NATIVE_BRIDGE_ADDRESS, JSTZ_ROLLUP_ADDRESS}; use octez::r#async::baker::{BakerBinaryPath, OctezBakerConfigBuilder}; use octez::r#async::client::{OctezClient, OctezClientConfigBuilder}; use octez::r#async::endpoint::Endpoint; use octez::r#async::node_config::{OctezNodeConfigBuilder, OctezNodeRunOptionsBuilder}; use octez::r#async::protocol::{ - BootstrapAccount, BootstrapContract, ProtocolParameterBuilder, + BootstrapAccount, BootstrapContract, BootstrapSmartRollup, ProtocolParameterBuilder, + SmartRollupPvmKind, }; +use octez::r#async::rollup::{OctezRollupConfigBuilder, RollupDataDir}; use octez::unused_port; +use serde::Deserialize; +use serde_json::from_str; +use std::fs; +use tempfile::NamedTempFile; +use tezos_crypto_rs::hash::SmartRollupHash; + +include!(concat!(env!("OUT_DIR"), "/jstz_rollup_path.rs")); +const ACTIVATOR_PK: &str = "edpkuSLWfVU1Vq7Jg9FucPyKmma6otcMHac9zG4oU1KMHSTBpJuGQ2"; const CONTRACT_INIT_BALANCE: f64 = 1.0; const CONTRACT_NAMES: [(&str, &str); 2] = [ ("exchanger", EXCHANGER_ADDRESS), ("jstz_native_bridge", JSTZ_NATIVE_BRIDGE_ADDRESS), ]; +pub const JSTZ_ROLLUP_OPERATOR_PK: &str = + "edpkuBknW28nW72KG6RoHtYW7p12T6GKc7nAbwYX5m8Wd9sDVC9yav"; +pub const JSTZ_ROLLUP_OPERATOR_ALIAS: &str = "bootstrap1"; #[tokio::test(flavor = "multi_thread")] async fn jstzd_test() { - let rpc_endpoint = Endpoint::localhost(unused_port()); + let octez_node_rpc_endpoint = Endpoint::localhost(unused_port()); + let rollup_rpc_endpoint = Endpoint::try_from( + Uri::from_str(&format!("http://127.0.0.1:{}", unused_port())).unwrap(), + ) + .unwrap(); let jstzd_port = unused_port(); - let (mut jstzd, config) = create_jstzd_server(&rpc_endpoint, jstzd_port).await; + let (mut jstzd, config, kernel_debug_file) = + create_jstzd_server(&octez_node_rpc_endpoint, &rollup_rpc_endpoint, jstzd_port) + .await; jstzd.run().await.unwrap(); - ensure_jstzd_components_are_up(&jstzd, &rpc_endpoint, jstzd_port).await; + ensure_jstzd_components_are_up( + &jstzd, + &octez_node_rpc_endpoint, + &rollup_rpc_endpoint, + jstzd_port, + ) + .await; + + ensure_rollup_is_logging_to(kernel_debug_file).await; + let octez_client = OctezClient::new(config.octez_client_config().clone()); check_bootstrap_contracts(&octez_client).await; @@ -38,7 +71,13 @@ async fn jstzd_test() { .await .unwrap(); - ensure_jstzd_components_are_down(&jstzd, &rpc_endpoint, jstzd_port).await; + ensure_jstzd_components_are_down( + &jstzd, + &octez_node_rpc_endpoint, + &rollup_rpc_endpoint, + jstzd_port, + ) + .await; // calling `run` after calling `stop` should fail because all states should have been cleared assert_eq!( @@ -49,8 +88,9 @@ async fn jstzd_test() { async fn create_jstzd_server( octez_node_rpc_endpoint: &Endpoint, + rollup_rpc_endpoint: &Endpoint, jstzd_port: u16, -) -> (JstzdServer, JstzdConfig) { +) -> (JstzdServer, JstzdConfig, NamedTempFile) { let run_options = OctezNodeRunOptionsBuilder::new() .set_synchronisation_threshold(0) .set_network("sandbox") @@ -61,14 +101,28 @@ async fn create_jstzd_server( .set_run_options(&run_options) .build() .unwrap(); + + let (rollup_kernel_installer, rollup_preimages_dir, rollup_parameters_ty) = + jstz_rollup_files(); + let protocol_params = ProtocolParameterBuilder::new() // this is the activator account // fund the account so that it can be used by the baker // give it at least 12000 (6000 for bootstrap + 6000 for baking) tez // so that it does not run out of fund - .set_bootstrap_accounts([BootstrapAccount::new( - "edpkuSLWfVU1Vq7Jg9FucPyKmma6otcMHac9zG4oU1KMHSTBpJuGQ2", - 15_000_000_000, + .set_bootstrap_accounts([ + // activator is given at least 12000 (6000 for bootstrap + 6000 for baking) tez for baking + BootstrapAccount::new(ACTIVATOR_PK, 15_000_000_000).unwrap(), + BootstrapAccount::new(JSTZ_ROLLUP_OPERATOR_PK, 60_000_000_000).unwrap(), + ]) + .set_bootstrap_smart_rollups([BootstrapSmartRollup::new( + JSTZ_ROLLUP_ADDRESS, + SmartRollupPvmKind::Wasm, + fs::read_to_string(rollup_kernel_installer.as_path()) + .unwrap() + .as_str(), + from_str(fs::read_to_string(rollup_parameters_ty).unwrap().as_str()) + .expect("failed to stringify JSON"), ) .unwrap()]) .set_bootstrap_contracts(read_bootstrap_contracts().await) @@ -88,27 +142,48 @@ async fn create_jstzd_server( .set_octez_node_endpoint(&octez_node_config.rpc_endpoint) .build() .expect("Failed to build baker config"); + let kernel_debug_file = NamedTempFile::new().unwrap(); + let rollup_config = OctezRollupConfigBuilder::new( + octez_node_rpc_endpoint.clone(), + octez_client_config.base_dir().into(), + SmartRollupHash::from_base58_check(JSTZ_ROLLUP_ADDRESS).unwrap(), + JSTZ_ROLLUP_OPERATOR_ALIAS.to_string(), + rollup_kernel_installer, + ) + .set_data_dir(RollupDataDir::TempWithPreImages { + preimages_dir: rollup_preimages_dir, + }) + .set_rpc_endpoint(rollup_rpc_endpoint) + .set_kernel_debug_file(kernel_debug_file.path()) + .build() + .expect("failed to build rollup config"); let config = JstzdConfig::new( octez_node_config, baker_config, octez_client_config.clone(), + rollup_config.clone(), protocol_params, ); - (JstzdServer::new(config.clone(), jstzd_port), config) + ( + JstzdServer::new(config.clone(), jstzd_port), + config, + kernel_debug_file, + ) } async fn ensure_jstzd_components_are_up( jstzd: &JstzdServer, octez_node_rpc_endpoint: &Endpoint, + rollup_rpc_endpoint: &Endpoint, jstzd_port: u16, ) { - let jstz_health_check_endpoint = format!("http://localhost:{}/health", jstzd_port); + let jstzd_health_check_endpoint = format!("http://localhost:{}/health", jstzd_port); let octez_node_health_check_endpoint = format!("{}/health/ready", octez_node_rpc_endpoint); let jstzd_running = retry(30, 1000, || async { - let res = reqwest::get(&jstz_health_check_endpoint).await; + let res = reqwest::get(&jstzd_health_check_endpoint).await; Ok(res.is_ok()) }) .await; @@ -119,21 +194,29 @@ async fn ensure_jstzd_components_are_up( .await .is_ok()); + // check if baker is running assert!(jstzd.baker_healthy().await); + + // check if rollup is running + let rollup_running = + retry(30, 1000, || rollup_health_check(rollup_rpc_endpoint)).await; + assert!(rollup_running); + assert!(jstzd.health_check().await); } async fn ensure_jstzd_components_are_down( jstzd: &JstzdServer, octez_node_rpc_endpoint: &Endpoint, + rollup_rpc_endpoint: &Endpoint, jstzd_port: u16, ) { - let jstz_health_check_endpoint = format!("http://localhost:{}/health", jstzd_port); + let jstzd_health_check_endpoint = format!("http://localhost:{}/health", jstzd_port); let octez_node_health_check_endpoint = format!("{}/health/ready", octez_node_rpc_endpoint); let jstzd_stopped = retry(30, 1000, || async { - let res = reqwest::get(&jstz_health_check_endpoint).await; + let res = reqwest::get(&jstzd_health_check_endpoint).await; if let Err(e) = res { return Ok(e.to_string().contains("Connection refused")); } @@ -154,8 +237,9 @@ async fn ensure_jstzd_components_are_down( }) .await; assert!(node_destroyed); - assert!(!jstzd.baker_healthy().await); + let rollup_health = rollup_health_check(&rollup_rpc_endpoint.clone()).await; + assert!(rollup_health.is_err()); assert!(!jstzd.health_check().await); } @@ -212,6 +296,104 @@ async fn fetch_config_test(jstzd_config: JstzdConfig, jstzd_port: u16) { ); } +// async fn build_configs( +// octez_node_rpc_endpoint: &Endpoint, +// rollup_rpc_endpoint: &Endpoint, +// ) -> (JstzdConfig, OctezClientConfig, NamedTempFile) { +// let run_options = OctezNodeRunOptionsBuilder::new() +// .set_synchronisation_threshold(0) +// .set_network("sandbox") +// .build(); +// let octez_node_config = OctezNodeConfigBuilder::new() +// .set_network("sandbox") +// .set_rpc_endpoint(octez_node_rpc_endpoint) +// .set_run_options(&run_options) +// .build() +// .unwrap(); + +// let (rollup_kernel_installer, rollup_preimages_dir, rollup_parameters_ty) = +// jstz_rollup_files(); + +// let protocol_params = +// build_protocol_params(&rollup_kernel_installer, &rollup_parameters_ty).await; + +// let octez_client_config = +// OctezClientConfigBuilder::new(octez_node_rpc_endpoint.clone()) +// .build() +// .unwrap(); + +// let baker_config = OctezBakerConfigBuilder::new() +// .set_binary_path(BakerBinaryPath::Env(protocol_params.protocol().clone())) +// .set_octez_client_base_dir( +// PathBuf::from(octez_client_config.base_dir()) +// .to_str() +// .unwrap(), +// ) +// .set_octez_node_endpoint(octez_node_rpc_endpoint) +// .build() +// .expect("Failed to build baker config"); + +// let kernel_debug_file = NamedTempFile::new().unwrap(); +// let rollup_config = OctezRollupConfigBuilder::new( +// octez_node_rpc_endpoint.clone(), +// octez_client_config.base_dir().into(), +// SmartRollupHash::from_base58_check(JSTZ_ROLLUP_ADDRESS).unwrap(), +// JSTZ_ROLLUP_OPERATOR_ALIAS.to_string(), +// rollup_kernel_installer, +// ) +// .set_data_dir(RollupDataDir::TempWithPreImages { +// preimages_dir: rollup_preimages_dir, +// }) +// .set_rpc_endpoint(rollup_rpc_endpoint) +// .set_kernel_debug_file(kernel_debug_file.path()) +// .build() +// .expect("failed to build rollup config"); + +// let config = jstzd::task::jstzd::JstzdConfig::new( +// octez_node_config, +// baker_config, +// octez_client_config.clone(), +// rollup_config, +// protocol_params, +// ); + +// (config, octez_client_config, kernel_debug_file) +// } + +fn jstz_rollup_files() -> (PathBuf, PathBuf, PathBuf) { + ( + kernel_installer_path(), + preimages_path(), + parameters_ty_path(), + ) +} + +// async fn build_protocol_params( +// rollup_kernel_installer: &Path, +// rollup_parameters_ty: &Path, +// ) -> ProtocolParameter { +// ProtocolParameterBuilder::new() +// .set_protocol(Protocol::Alpha) +// .set_bootstrap_accounts([ +// // activator is given at least 12000 (6000 for bootstrap + 6000 for baking) tez for baking +// BootstrapAccount::new(ACTIVATOR_PK, 15_000_000_000).unwrap(), +// BootstrapAccount::new(JSTZ_ROLLUP_OPERATOR_PK, 60_000_000_000).unwrap(), +// ]) +// .set_bootstrap_smart_rollups([BootstrapSmartRollup::new( +// JSTZ_ROLLUP_ADDRESS, +// SmartRollupPvmKind::Wasm, +// fs::read_to_string(rollup_kernel_installer) +// .unwrap() +// .as_str(), +// from_str(fs::read_to_string(rollup_parameters_ty).unwrap().as_str()) +// .expect("failed to stringify JSON"), +// ) +// .unwrap()]) +// .set_bootstrap_contracts(read_bootstrap_contracts().await) +// .build() +// .expect("failed to build protocol parameters") +// } + async fn read_bootstrap_contracts() -> Vec { let mut contracts = vec![]; for (contract_name, hash) in CONTRACT_NAMES { @@ -247,3 +429,21 @@ async fn check_bootstrap_contracts(octez_client: &OctezClient) { ); } } + +#[derive(Debug, Deserialize)] +struct HealthCheckResponse { + healthy: bool, +} +pub async fn rollup_health_check(rollup_rpc_endpoint: &Endpoint) -> Result { + let res = reqwest::get(format!("{}/health", rollup_rpc_endpoint)).await?; + let body = res.json::().await?; + Ok(body.healthy) +} + +async fn ensure_rollup_is_logging_to(kernel_debug_file: NamedTempFile) { + let mut file = kernel_debug_file.reopen().unwrap(); + let mut contents = String::new(); + file.read_to_string(&mut contents).unwrap(); + assert!(contents.contains("Internal message: start of level")); + assert!(contents.contains("Internal message: end of level")); +} diff --git a/crates/jstzd/tests/octez_baker_test.rs b/crates/jstzd/tests/octez_baker_test.rs index c9b367fe1..8a46f6f8a 100644 --- a/crates/jstzd/tests/octez_baker_test.rs +++ b/crates/jstzd/tests/octez_baker_test.rs @@ -1,6 +1,6 @@ mod utils; -use jstzd::task::Task; -use utils::{get_block_level, setup}; +use jstzd::task::{utils::get_block_level, Task}; +use utils::setup; #[tokio::test(flavor = "multi_thread")] async fn test_baker() { @@ -10,9 +10,9 @@ async fn test_baker() { let _ = baker.kill().await; assert!(!baker.health_check().await.unwrap()); // check if the block level stops increasing after killing - let last_level = get_block_level(&node_endpoint.to_string()).await; + let last_level = get_block_level(&node_endpoint.to_string()).await.unwrap(); tokio::time::sleep(tokio::time::Duration::from_secs(2)).await; - let current_level = get_block_level(&node_endpoint.to_string()).await; + let current_level = get_block_level(&node_endpoint.to_string()).await.unwrap(); assert_eq!(last_level, current_level); let _ = octez_node.kill().await; } diff --git a/crates/jstzd/tests/utils.rs b/crates/jstzd/tests/utils.rs index a19758cef..895bffe0f 100644 --- a/crates/jstzd/tests/utils.rs +++ b/crates/jstzd/tests/utils.rs @@ -1,5 +1,11 @@ #![allow(dead_code)] -use jstzd::task::{octez_baker, octez_node::OctezNode, octez_rollup, utils::retry, Task}; +use jstzd::task::{ + octez_baker, + octez_node::OctezNode, + octez_rollup, + utils::{get_block_level, retry}, + Task, +}; use octez::r#async::{ baker::{BakerBinaryPath, OctezBakerConfigBuilder}, client::{OctezClient, OctezClientConfigBuilder}, @@ -8,14 +14,14 @@ use octez::r#async::{ protocol::Protocol, rollup::{OctezRollupConfigBuilder, RollupDataDir}, }; -use regex::Regex; use std::path::{Path, PathBuf}; use tezos_crypto_rs::hash::{BlockHash, OperationHash, SmartRollupHash}; use tokio::io::AsyncReadExt; pub const ACTIVATOR_SECRET_KEY: &str = "unencrypted:edsk31vznjHSSpGExDMHYASz45VZqXN4DPxvsa4hAyY8dHM28cZzp6"; -pub const ROLLUP_ADDRESS: &str = "sr1PuFMgaRUN12rKQ3J2ae5psNtwCxPNmGNK"; +pub const TOY_ROLLUP_ADDRESS: &str = "sr1JVr8SmBYRRFq38HZGM7nJUa9VcfwxGSXc"; +pub const TOY_ROLLUP_OPERATOR_ALIAS: &str = "bootstrap1"; pub async fn setup() -> (OctezNode, OctezClient, octez_baker::OctezBaker) { let octez_node = spawn_octez_node().await; @@ -40,8 +46,8 @@ pub async fn spawn_rollup( let config = OctezRollupConfigBuilder::new( octez_node.rpc_endpoint().clone(), octez_client.base_dir().into(), - SmartRollupHash::from_base58_check(ROLLUP_ADDRESS).unwrap(), - "bootstrap1".to_string(), + SmartRollupHash::from_base58_check(TOY_ROLLUP_ADDRESS).unwrap(), + TOY_ROLLUP_OPERATOR_ALIAS.to_string(), kernel_installer, ) .set_data_dir(RollupDataDir::TempWithPreImages { preimages_dir }) @@ -74,7 +80,7 @@ pub async fn spawn_baker( assert!(baker_node.health_check().await.unwrap()); let node_endpoint = octez_node.rpc_endpoint(); let block_baked = retry(10, 1000, || async { - let level = get_block_level(&node_endpoint.to_string()).await; + let level = get_block_level(&node_endpoint.to_string()).await?; Ok(level > 1) }) .await; @@ -104,24 +110,6 @@ pub fn create_client(node_endpoint: &Endpoint) -> OctezClient { OctezClient::new(config) } -pub async fn get_block_level(rpc_endpoint: &str) -> i32 { - let blocks_head_endpoint = - format!("{}/chains/main/blocks/head", rpc_endpoint.to_owned()); - let response = get_request(&blocks_head_endpoint).await; - extract_level(&response) -} - -fn extract_level(input: &str) -> i32 { - // Create a regex to match "level": followed by a number - let re = Regex::new(r#""level":\s*(\d+)"#).unwrap(); - // Extract the number as a string and parse it to i32 - re.captures(input) - .unwrap() - .get(1) - .map(|level_match| level_match.as_str().parse::().unwrap()) - .unwrap() -} - pub async fn get_head_block_hash(rpc_endpoint: &str) -> BlockHash { let blocks_head_endpoint = format!("{}/chains/main/blocks/head", rpc_endpoint.to_owned());