-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
329 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
pub mod verifier; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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<Option<DockerEnv>>, | ||
client: Client, | ||
} | ||
|
||
impl VerifierNode { | ||
pub async fn new(config: &ClementineConfig, docker: Arc<Option<DockerEnv>>) -> Result<Self> { | ||
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<Option<DockerEnv>>, | ||
) -> Result<SpawnOutput> { | ||
match docker.as_ref() { | ||
Some(docker) => docker.spawn(config.into()).await, | ||
None => <Self as NodeT>::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<bool> { | ||
// 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<SpawnOutput> { | ||
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<Duration>) -> 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) -> &<Self as NodeT>::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::<String>(&output.id, None) | ||
.try_collect::<Vec<_>>() | ||
.await?; | ||
env.docker.remove_container(&output.id, None).await?; | ||
println!("Docker container {} succesfully removed", output.id); | ||
Ok(()) | ||
} | ||
} | ||
} | ||
|
||
async fn start(&mut self, config: Option<Self::Config>) -> 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!() | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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<secp256k1::PublicKey>, | ||
/// Number of verifiers. | ||
pub num_verifiers: usize, | ||
/// Operators x-only public keys. | ||
pub operators_xonly_pks: Vec<secp256k1::XOnlyPublicKey>, | ||
/// Operators wallet addresses. | ||
pub operator_wallet_addresses: Vec<bitcoin::Address<NetworkUnchecked>>, | ||
/// Number of operators. | ||
pub num_operators: usize, | ||
/// Operator's fee for withdrawal, in satoshis. | ||
pub operator_withdrawal_fee_sats: Option<Amount>, | ||
/// 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<Vec<secp256k1::SecretKey>>, | ||
/// All Secret keys. Just for testing purposes. | ||
pub all_operators_secret_keys: Option<Vec<secp256k1::SecretKey>>, | ||
/// Verifier endpoints. | ||
pub verifier_endpoints: Option<Vec<String>>, | ||
/// 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<String>, | ||
pub data_dir: PathBuf, | ||
} | ||
|
||
impl Default for ClementineConfig { | ||
fn default() -> Self { | ||
Self { | ||
client: ClementineClient::default(), | ||
docker_image: None, | ||
data_dir: PathBuf::from("bridge_backend"), | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters