Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Clementine support #22

Draft
wants to merge 10 commits into
base: main
Choose a base branch
from
1 change: 1 addition & 0 deletions src/clementine/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub mod node;
149 changes: 149 additions & 0 deletions src/clementine/node.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
//! # Clementine Node
use std::{fs::File, process::Stdio, time::Duration};

use anyhow::Context;
use async_trait::async_trait;
use tokio::process::Command;
use tracing::info;

use crate::{
client::Client,
config::config_to_file,
log_provider::LogPathProvider,
node::Config,
traits::{NodeT, Restart, SpawnOutput},
utils::get_clementine_path,
Result,
};

pub struct ClementineNode<C: Config + LogPathProvider> {
spawn_output: SpawnOutput,
config: C,
pub client: Client,
}

impl<C: Config + LogPathProvider> ClementineNode<C> {
pub async fn new(config: &C) -> Result<Self> {
let spawn_output = Self::spawn(config)?;

let client = Client::new(config.rpc_bind_host(), config.rpc_bind_port())?;
Ok(Self {
spawn_output,
config: config.clone(),
client,
})
}

fn spawn(config: &C) -> Result<SpawnOutput> {
let clementine = get_clementine_path()?;
let dir = config.dir();

let kind = C::node_kind();

let stdout_path = config.log_path();
let stdout_file = File::create(&stdout_path).context("Failed to create stdout file")?;
info!(
"{} stdout logs available at : {}",
kind,
stdout_path.display()
);

let stderr_path = config.stderr_path();
let stderr_file = File::create(stderr_path).context("Failed to create stderr file")?;

let server_arg = match kind {
crate::node::NodeKind::Verifier => "--verifier-server",
_ => panic!("Wrong kind {}", kind),
};

let config_path = dir.join(format!("{kind}_config.toml"));
config_to_file(&config.clementine_config(), &config_path)?;

Command::new(clementine)
.arg(server_arg)
.envs(config.env())
.stdout(Stdio::from(stdout_file))
.stderr(Stdio::from(stderr_file))
.kill_on_drop(true)
.spawn()
.context(format!("Failed to spawn {kind} process"))
.map(SpawnOutput::Child)
}
}

#[async_trait]
impl<C> NodeT for ClementineNode<C>
where
C: Config + LogPathProvider + Send + Sync,
{
type Config = C;
type Client = Client;

fn spawn(config: &Self::Config) -> Result<SpawnOutput> {
Self::spawn(config)
}

fn spawn_output(&mut self) -> &mut SpawnOutput {
&mut self.spawn_output
}

async fn wait_for_ready(&self, _timeout: Option<Duration>) -> Result<()> {
// let start = Instant::now();
// let timeout = timeout.unwrap_or(Duration::from_secs(30));
// while start.elapsed() < timeout {
// if self
// .client
// .ledger_get_head_soft_confirmation()
// .await
// .is_ok()
// {
return Ok(());
// }
// sleep(Duration::from_millis(500)).await;
// }
// anyhow::bail!(
// "{} failed to become ready within the specified timeout",
// C::node_kind()
// )
}

fn client(&self) -> &Self::Client {
&self.client
}

fn env(&self) -> Vec<(&'static str, &'static str)> {
self.config.env()
}

fn config_mut(&mut self) -> &mut Self::Config {
&mut self.config
}

fn config(&self) -> &Self::Config {
&self.config
}
}

#[async_trait]
impl<C> Restart for ClementineNode<C>
where
C: Config + LogPathProvider + Send + Sync,
{
async fn wait_until_stopped(&mut self) -> Result<()> {
self.stop().await?;
match &mut self.spawn_output {
SpawnOutput::Child(pid) => pid.wait().await?,
SpawnOutput::Container(_) => unimplemented!("L2 nodes don't run in docker yet"),
};
Ok(())
}

async fn start(&mut self, new_config: Option<Self::Config>) -> Result<()> {
let config = self.config_mut();
if let Some(new_config) = new_config {
*config = new_config;
}
*self.spawn_output() = Self::spawn(config)?;
self.wait_for_ready(None).await
}
}
119 changes: 119 additions & 0 deletions src/config/clementine.rs
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, Serialize, Deserialize)]
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"),
}
}
}
81 changes: 81 additions & 0 deletions src/config/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
mod bitcoin;
mod clementine;
mod docker;
mod rollup;
mod test;
Expand All @@ -8,6 +9,7 @@ mod utils;
use std::path::PathBuf;

pub use bitcoin::BitcoinConfig;
pub use clementine::{ClementineClient, ClementineConfig};
pub use docker::DockerConfig;
pub use rollup::{default_rollup_config, RollupConfig};
use serde::Serialize;
Expand Down Expand Up @@ -98,6 +100,10 @@ where
fn rollup_config(&self) -> &RollupConfig {
&self.rollup
}

fn clementine_config(&self) -> &ClementineConfig {
unimplemented!()
}
}

impl<T: Clone + Serialize> LogPathProvider for FullL2NodeConfig<T>
Expand All @@ -116,3 +122,78 @@ where
self.dir().join("stderr.log")
}
}

#[derive(Clone, Debug)]
pub struct ClementineNodeConfig<T> {
pub node: T,
pub config: ClementineConfig,
pub docker_image: Option<String>,
pub dir: PathBuf,
pub env: Vec<(&'static str, &'static str)>,
}

pub type FullVerifierConfig = ClementineNodeConfig<ClementineConfig>;

impl NodeKindMarker for FullVerifierConfig {
const KIND: NodeKind = NodeKind::Verifier;
}

impl<T: Clone + Serialize> Config for ClementineNodeConfig<T>
where
ClementineNodeConfig<T>: NodeKindMarker,
{
type NodeConfig = T;

fn dir(&self) -> &PathBuf {
&self.dir
}

fn rpc_bind_host(&self) -> &str {
&self.config.client.host
}

fn rpc_bind_port(&self) -> u16 {
self.config.client.port
}

fn env(&self) -> Vec<(&'static str, &'static str)> {
self.env.clone()
}

fn node_config(&self) -> Option<&Self::NodeConfig> {
if std::mem::size_of::<T>() == 0 {
None
} else {
Some(&self.node)
}
}

fn node_kind() -> NodeKind {
<Self as NodeKindMarker>::KIND
}

fn rollup_config(&self) -> &RollupConfig {
unimplemented!()
}

fn clementine_config(&self) -> &ClementineConfig {
&self.config
}
}

impl<T: Clone + Serialize> LogPathProvider for ClementineNodeConfig<T>
where
ClementineNodeConfig<T>: Config,
{
fn kind() -> NodeKind {
Self::node_kind()
}

fn log_path(&self) -> PathBuf {
self.dir().join("stdout.log")
}

fn stderr_path(&self) -> PathBuf {
self.dir().join("stderr.log")
}
}
3 changes: 2 additions & 1 deletion src/config/test.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use super::{
bitcoin::BitcoinConfig, test_case::TestCaseConfig, FullBatchProverConfig, FullFullNodeConfig,
FullLightClientProverConfig, FullSequencerConfig,
FullLightClientProverConfig, FullSequencerConfig, FullVerifierConfig,
};

#[derive(Clone)]
Expand All @@ -10,5 +10,6 @@ pub struct TestConfig {
pub sequencer: FullSequencerConfig,
pub batch_prover: FullBatchProverConfig,
pub light_client_prover: FullLightClientProverConfig,
pub verifier: FullVerifierConfig,
pub full_node: FullFullNodeConfig,
}
Loading
Loading