From f52f2a61b9384f36d7fb1ff80ac99483921553e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ceyhun=20=C5=9Een?= Date: Thu, 10 Oct 2024 15:03:56 +0300 Subject: [PATCH] tmp clementine verifier --- src/clementine/mod.rs | 1 + src/clementine/verifier.rs | 204 +++++++++++++++++++++++++++++++++++++ src/config/clementine.rs | 119 ++++++++++++++++++++++ src/config/mod.rs | 2 + src/lib.rs | 1 + src/node.rs | 2 + 6 files changed, 329 insertions(+) create mode 100644 src/clementine/mod.rs create mode 100644 src/clementine/verifier.rs create mode 100644 src/config/clementine.rs diff --git a/src/clementine/mod.rs b/src/clementine/mod.rs new file mode 100644 index 0000000..9a07220 --- /dev/null +++ b/src/clementine/mod.rs @@ -0,0 +1 @@ +pub mod verifier; diff --git a/src/clementine/verifier.rs b/src/clementine/verifier.rs new file mode 100644 index 0000000..5148f2c --- /dev/null +++ b/src/clementine/verifier.rs @@ -0,0 +1,204 @@ +use std::path::PathBuf; +use std::sync::Arc; +use std::time::{Duration, Instant}; + +use anyhow::{bail, Context}; +use async_trait::async_trait; +use futures::TryStreamExt; +use tokio::process::Command; +use tokio::time::sleep; + +use crate::Result; +use crate::client::Client; +use crate::config::ClementineConfig; +use crate::docker::DockerEnv; +use crate::node::NodeKind; +use crate::traits::{ContainerSpawnOutput, LogProvider, NodeT, Restart, SpawnOutput}; + +pub struct VerifierNode { + spawn_output: SpawnOutput, + pub config: ClementineConfig, + docker_env: Arc>, + client: Client, +} + +impl VerifierNode { + pub async fn new(config: &ClementineConfig, docker: Arc>) -> Result { + let spawn_output = Self::spawn(config, &docker).await?; + + Ok(Self { + spawn_output, + config: config.clone(), + docker_env: docker, + client: Client::new(&config.client.host, config.client.port.try_into().unwrap())?, + }) + } + + async fn spawn( + config: &ClementineConfig, + docker: &Arc>, + ) -> Result { + match docker.as_ref() { + Some(docker) => docker.spawn(config.into()).await, + None => ::spawn(config), + } + } + + async fn wait_for_shutdown(&self) -> Result<()> { + let timeout_duration = Duration::from_secs(30); + let start = std::time::Instant::now(); + + while start.elapsed() < timeout_duration { + if !self.is_process_running().await? { + println!("Bridge backend has stopped successfully"); + return Ok(()); + } + sleep(Duration::from_millis(200)).await; + } + + bail!("Timeout waiting for bridge backend to stop") + } + + async fn is_process_running(&self) -> Result { + // let data_dir = &self.config.data_dir; + // let output = Command::new("pgrep") + // .args(["-f", &format!("bitcoind.*{}", data_dir.display())]) + // .output() + // .await?; + + // Ok(output.status.success()) + todo!() + } +} + +#[async_trait] +impl NodeT for VerifierNode { + type Config = ClementineConfig; + type Client = Client; + + fn spawn(config: &Self::Config) -> Result { + let env = config.get_env(); + println!("Running bridge backend with environment variables: {env:?}"); + + Command::new("npm run server:dev") + .kill_on_drop(true) + .env_clear() + .envs(env.clone()) + .spawn() + .context("Failed to spawn bridge backend server process") + .map(SpawnOutput::Child)?; + + Command::new("npm run worker:dev") + .kill_on_drop(true) + .env_clear() + .envs(env) + .spawn() + .context("Failed to spawn bridge backend worker process") + .map(SpawnOutput::Child) + } + + fn spawn_output(&mut self) -> &mut SpawnOutput { + &mut self.spawn_output + } + + async fn wait_for_ready(&self, timeout: Option) -> Result<()> { + println!("Waiting for ready"); + let start = Instant::now(); + let timeout = timeout.unwrap_or(Duration::from_secs(30)); + while start.elapsed() < timeout { + if true + // TODO: Do this check. + { + return Ok(()); + } + tokio::time::sleep(Duration::from_millis(500)).await; + } + anyhow::bail!("Node failed to become ready within the specified timeout") + } + + fn config_mut(&mut self) -> &mut Self::Config { + &mut self.config + } + + async fn stop(&mut self) -> Result<()> { + match self.spawn_output() { + SpawnOutput::Child(process) => { + process + .kill() + .await + .context("Failed to kill child process")?; + Ok(()) + } + SpawnOutput::Container(ContainerSpawnOutput { id, .. }) => { + std::println!("Stopping container {id}"); + let docker = bollard::Docker::connect_with_local_defaults() + .context("Failed to connect to Docker")?; + docker + .stop_container(id, Some(bollard::container::StopContainerOptions { t: 10 })) + .await + .context("Failed to stop Docker container")?; + Ok(()) + } + } + } + + fn client(&self) -> &Self::Client { + &self.client + } + + fn env(&self) -> Vec<(&'static str, &'static str)> { + // self.config.get_env() + todo!() + } + + fn config(&self) -> &::Config { + &self.config + } +} + +#[async_trait] +impl Restart for VerifierNode { + async fn wait_until_stopped(&mut self) -> Result<()> { + // self.client.stop().await?; + self.stop().await?; + + match &self.spawn_output { + SpawnOutput::Child(_) => self.wait_for_shutdown().await, + SpawnOutput::Container(output) => { + let Some(env) = self.docker_env.as_ref() else { + bail!("Missing docker environment") + }; + env.docker.stop_container(&output.id, None).await?; + + env.docker + .wait_container::(&output.id, None) + .try_collect::>() + .await?; + env.docker.remove_container(&output.id, None).await?; + println!("Docker container {} succesfully removed", output.id); + Ok(()) + } + } + } + + async fn start(&mut self, config: Option) -> Result<()> { + if let Some(config) = config { + self.config = config + } + self.spawn_output = Self::spawn(&self.config, &self.docker_env).await?; + + self.wait_for_ready(None).await?; + + Ok(()) + } +} + +impl LogProvider for VerifierNode { + fn kind(&self) -> NodeKind { + NodeKind::Verifier + } + + fn log_path(&self) -> PathBuf { + todo!() + } +} diff --git a/src/config/clementine.rs b/src/config/clementine.rs new file mode 100644 index 0000000..21c53fe --- /dev/null +++ b/src/config/clementine.rs @@ -0,0 +1,119 @@ +//! # Clementine Configuration Options + +use std::path::PathBuf; + +use bitcoin::{address::NetworkUnchecked, secp256k1, Amount, Network}; +use serde::{Deserialize, Serialize}; + +/// Clementine's configuration options. +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ClementineClient { + /// Host of the operator or the verifier + pub host: String, + /// Port of the operator or the verifier + pub port: u16, + /// Bitcoin network to work on. + pub network: Network, + /// Secret key for the operator or the verifier. + pub secret_key: secp256k1::SecretKey, + /// Verifiers public keys. + pub verifiers_public_keys: Vec, + /// Number of verifiers. + pub num_verifiers: usize, + /// Operators x-only public keys. + pub operators_xonly_pks: Vec, + /// Operators wallet addresses. + pub operator_wallet_addresses: Vec>, + /// Number of operators. + pub num_operators: usize, + /// Operator's fee for withdrawal, in satoshis. + pub operator_withdrawal_fee_sats: Option, + /// Number of blocks after which user can take deposit back if deposit request fails. + pub user_takes_after: u32, + /// Number of blocks after which operator can take reimburse the bridge fund if they are honest. + pub operator_takes_after: u32, + /// Bridge amount in satoshis. + pub bridge_amount_sats: Amount, + /// Operator: number of kickoff UTXOs per funding transaction. + pub operator_num_kickoff_utxos_per_tx: usize, + /// Threshold for confirmation. + pub confirmation_threshold: u32, + /// Bitcoin remote procedure call URL. + pub bitcoin_rpc_url: String, + /// Bitcoin RPC user. + pub bitcoin_rpc_user: String, + /// Bitcoin RPC user password. + pub bitcoin_rpc_password: String, + /// All Secret keys. Just for testing purposes. + pub all_verifiers_secret_keys: Option>, + /// All Secret keys. Just for testing purposes. + pub all_operators_secret_keys: Option>, + /// Verifier endpoints. + pub verifier_endpoints: Option>, + /// PostgreSQL database host address. + pub db_host: String, + /// PostgreSQL database port. + pub db_port: usize, + /// PostgreSQL database user name. + pub db_user: String, + /// PostgreSQL database user password. + pub db_password: String, + /// PostgreSQL database name. + pub db_name: String, + /// Citrea RPC URL. + pub citrea_rpc_url: String, + /// Bridge contract address. + pub bridge_contract_address: String, +} + +impl Default for ClementineClient { + fn default() -> Self { + Self { + host: "127.0.0.1".to_string(), + port: 3030, + secret_key: secp256k1::SecretKey::new(&mut secp256k1::rand::thread_rng()), + verifiers_public_keys: vec![], + num_verifiers: 7, + operators_xonly_pks: vec![], + operator_wallet_addresses: vec![], + num_operators: 3, + operator_withdrawal_fee_sats: None, + user_takes_after: 5, + operator_takes_after: 5, + bridge_amount_sats: Amount::from_sat(100_000_000), + operator_num_kickoff_utxos_per_tx: 10, + confirmation_threshold: 1, + network: Network::Regtest, + bitcoin_rpc_url: "http://127.0.0.1:18443".to_string(), + bitcoin_rpc_user: "admin".to_string(), + bitcoin_rpc_password: "admin".to_string(), + all_verifiers_secret_keys: None, + all_operators_secret_keys: None, + verifier_endpoints: None, + db_host: "127.0.0.1".to_string(), + db_port: 5432, + db_user: "postgres".to_string(), + db_password: "postgres".to_string(), + db_name: "postgres".to_string(), + citrea_rpc_url: "http://127.0.0.1:12345".to_string(), + bridge_contract_address: "3100000000000000000000000000000000000002".to_string(), + } + } +} + +#[derive(Debug, Clone)] +pub struct ClementineConfig { + pub client: ClementineClient, + pub docker_image: Option, + pub data_dir: PathBuf, +} + +impl Default for ClementineConfig { + fn default() -> Self { + Self { + client: ClementineClient::default(), + docker_image: None, + data_dir: PathBuf::from("bridge_backend"), + } + } +} diff --git a/src/config/mod.rs b/src/config/mod.rs index 2d602e8..49badb2 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -1,4 +1,5 @@ mod bitcoin; +mod clementine; mod docker; mod rollup; mod test; @@ -7,6 +8,7 @@ mod utils; use std::path::PathBuf; +pub use clementine::{ClementineClient, ClementineConfig}; pub use bitcoin::BitcoinConfig; pub use docker::DockerConfig; pub use rollup::{default_rollup_config, RollupConfig}; diff --git a/src/lib.rs b/src/lib.rs index 3a11f88..16b28ac 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,5 +12,6 @@ pub mod sequencer; pub mod test_case; pub mod traits; mod utils; +pub mod clementine; pub type Result = anyhow::Result; diff --git a/src/node.rs b/src/node.rs index 8a35840..f0be166 100644 --- a/src/node.rs +++ b/src/node.rs @@ -30,6 +30,7 @@ pub enum NodeKind { LightClientProver, Sequencer, FullNode, + Verifier, } impl fmt::Display for NodeKind { @@ -40,6 +41,7 @@ impl fmt::Display for NodeKind { NodeKind::LightClientProver => write!(f, "light-client-prover"), NodeKind::Sequencer => write!(f, "sequencer"), NodeKind::FullNode => write!(f, "full-node"), + NodeKind::Verifier => write!(f, "verifier"), } } }