From 5ab06126ffad250d3e26463850c309365c104a11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ceyhun=20=C5=9Een?= Date: Mon, 30 Sep 2024 14:37:28 +0300 Subject: [PATCH 01/24] bridge_backenend: Add initial which copied from bitcoin. --- src/bridge_backend.rs | 283 +++++++++++++++++++++++++++++++++++ src/config/bridge_backend.rs | 65 ++++++++ src/config/mod.rs | 2 + src/config/test.rs | 2 + src/full_node.rs | 2 +- src/lib.rs | 1 + src/node.rs | 4 +- 7 files changed, 357 insertions(+), 2 deletions(-) create mode 100644 src/bridge_backend.rs create mode 100644 src/config/bridge_backend.rs diff --git a/src/bridge_backend.rs b/src/bridge_backend.rs new file mode 100644 index 0000000..3fdc258 --- /dev/null +++ b/src/bridge_backend.rs @@ -0,0 +1,283 @@ +use std::collections::HashSet; +use std::path::PathBuf; +use std::sync::Arc; +use std::time::{Duration, Instant}; + +use anyhow::{bail, Context}; +use async_trait::async_trait; +use bitcoin::Address; +use bitcoincore_rpc::json::AddressType::Bech32m; +use bitcoincore_rpc::{Auth, Client, RpcApi}; +use futures::TryStreamExt; +use tokio::process::Command; +use tokio::sync::OnceCell; +use tokio::time::sleep; + +use super::config::BridgeBackendConfig; +use super::docker::DockerEnv; +use super::framework::TestContext; +use super::node::{LogProvider, Node, Restart, SpawnOutput}; +use super::Result; +use crate::node::NodeKind; + +pub struct BridgeBackend { + spawn_output: SpawnOutput, + pub config: BridgeBackendConfig, + client: Client, + gen_addr: OnceCell
, + docker_env: Arc>, +} + +impl BridgeBackend { + pub async fn new(config: &BridgeBackendConfig, docker: Arc>) -> Result { + let spawn_output = Self::spawn(config, &docker).await?; + + let rpc_url = format!( + "http://127.0.0.1:{}/wallet/{}", + config.rpc_port, + NodeKind::Bitcoin + ); + let client = Client::new( + &rpc_url, + Auth::UserPass(config.rpc_user.clone(), config.rpc_password.clone()), + ) + .await + .context("Failed to create RPC client")?; + + wait_for_rpc_ready(&client, None).await?; + + Ok(Self { + spawn_output, + config: config.clone(), + client, + gen_addr: OnceCell::new(), + docker_env: docker, + }) + } + + // Switch this over to Node signature once we add support for docker to citrea nodes + async fn spawn( + config: &BridgeBackendConfig, + docker: &Arc>, + ) -> Result { + match docker.as_ref() { + Some(docker) => docker.spawn(config.into()).await, + None => ::spawn(config), + } + } +} + +#[async_trait] +impl RpcApi for BridgeBackend { + async fn call serde::de::Deserialize<'a>>( + &self, + cmd: &str, + args: &[serde_json::Value], + ) -> bitcoincore_rpc::Result { + self.client.call(cmd, args).await + } + + // Override deprecated generate method. + // Uses or lazy init gen_addr and forward to `generate_to_address` + async fn generate( + &self, + block_num: u64, + _maxtries: Option, + ) -> bitcoincore_rpc::Result> { + let addr = self + .gen_addr + .get_or_init(|| async { + self.client + .get_new_address(None, Some(Bech32m)) + .await + .expect("Failed to generate address") + .assume_checked() + }) + .await; + + self.generate_to_address(block_num, addr).await + } +} + +impl Node for BridgeBackend { + type Config = BridgeBackendConfig; + type Client = Client; + + fn spawn(config: &Self::Config) -> Result { + let args = config.args(); + println!("Running bitcoind with args : {args:?}"); + + Command::new("bitcoind") + .args(&args) + .kill_on_drop(true) + .envs(config.env.clone()) + .spawn() + .context("Failed to spawn bitcoind 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 wait_for_rpc_ready(&self.client, Some(timeout)) + .await + .is_ok() + { + return Ok(()); + } + tokio::time::sleep(Duration::from_millis(500)).await; + } + anyhow::bail!("Node failed to become ready within the specified timeout") + } + + fn client(&self) -> &Self::Client { + &self.client + } + + fn env(&self) -> Vec<(&'static str, &'static str)> { + self.config.env.clone() + } + + fn config_mut(&mut self) -> &mut Self::Config { + &mut self.config + } +} + +impl Restart for BridgeBackend { + 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?; + + // Reload wallets after restart + self.load_wallets().await; + + Ok(()) + } +} + +impl LogProvider for BridgeBackend { + fn kind(&self) -> NodeKind { + NodeKind::Bitcoin + } + + fn log_path(&self) -> PathBuf { + self.config.data_dir.join("regtest").join("debug.log") + } +} + +pub struct BitcoinNodeCluster { + inner: Vec, +} + +impl BitcoinNodeCluster { + pub async fn new(ctx: &TestContext) -> Result { + let n_nodes = ctx.config.test_case.n_nodes; + let mut cluster = Self { + inner: Vec::with_capacity(n_nodes), + }; + for config in ctx.config.bitcoin.iter() { + let node = BridgeBackend::new(config, Arc::clone(&ctx.docker)).await?; + cluster.inner.push(node) + } + + Ok(cluster) + } + + pub async fn stop_all(&mut self) -> Result<()> { + for node in &mut self.inner { + RpcApi::stop(node).await?; + node.stop().await?; + } + Ok(()) + } + + pub async fn wait_for_sync(&self, timeout: Duration) -> Result<()> { + let start = Instant::now(); + while start.elapsed() < timeout { + let mut heights = HashSet::new(); + for node in &self.inner { + let height = node.get_block_count().await?; + heights.insert(height); + } + + if heights.len() == 1 { + return Ok(()); + } + + sleep(Duration::from_secs(1)).await; + } + bail!("Nodes failed to sync within the specified timeout") + } + + // Connect all bitcoin nodes between them + pub async fn connect_nodes(&self) -> Result<()> { + for (i, from_node) in self.inner.iter().enumerate() { + for (j, to_node) in self.inner.iter().enumerate() { + if i != j { + let ip = match &to_node.spawn_output { + SpawnOutput::Container(container) => container.ip.clone(), + _ => "127.0.0.1".to_string(), + }; + + let add_node_arg = format!("{}:{}", ip, to_node.config.p2p_port); + from_node.add_node(&add_node_arg).await?; + } + } + } + Ok(()) + } + + pub fn get(&self, index: usize) -> Option<&BridgeBackend> { + self.inner.get(index) + } + + #[allow(unused)] + pub fn get_mut(&mut self, index: usize) -> Option<&mut BridgeBackend> { + self.inner.get_mut(index) + } +} + +async fn wait_for_rpc_ready(client: &Client, timeout: Option) -> Result<()> { + let start = Instant::now(); + let timeout = timeout.unwrap_or(Duration::from_secs(300)); + while start.elapsed() < timeout { + match client.get_blockchain_info().await { + Ok(_) => return Ok(()), + Err(_) => sleep(Duration::from_millis(500)).await, + } + } + Err(anyhow::anyhow!("Timeout waiting for RPC to be ready")) +} diff --git a/src/config/bridge_backend.rs b/src/config/bridge_backend.rs new file mode 100644 index 0000000..9b876d2 --- /dev/null +++ b/src/config/bridge_backend.rs @@ -0,0 +1,65 @@ +//! Copied directly from Bitcoin config TODO: convert it to support bridge backend + +use bitcoin::Network; +use std::path::PathBuf; +use tempfile::TempDir; + +#[derive(Debug, Clone)] +pub struct BridgeBackendConfig { + pub p2p_port: u16, + pub rpc_port: u16, + pub rpc_user: String, + pub rpc_password: String, + pub data_dir: PathBuf, + pub extra_args: Vec<&'static str>, + pub network: Network, + pub docker_image: Option, + pub env: Vec<(&'static str, &'static str)>, + pub idx: usize, +} + +impl Default for BridgeBackendConfig { + fn default() -> Self { + Self { + p2p_port: 0, + rpc_port: 0, + rpc_user: "user".to_string(), + rpc_password: "password".to_string(), + data_dir: TempDir::new() + .expect("Failed to create temporary directory") + .into_path(), + extra_args: Vec::new(), + network: Network::Regtest, + docker_image: Some("bitcoin/bitcoin:latest".to_string()), + env: Vec::new(), + idx: 0, + } + } +} + +impl BridgeBackendConfig { + fn base_args(&self) -> Vec { + vec![ + "-regtest".to_string(), + format!("-datadir={}", self.data_dir.display()), + format!("-port={}", self.p2p_port), + format!("-rpcport={}", self.rpc_port), + format!("-rpcuser={}", self.rpc_user), + format!("-rpcpassword={}", self.rpc_password), + "-server".to_string(), + "-daemonwait".to_string(), + "-txindex".to_string(), + "-addresstype=bech32m".to_string(), + "-debug=net".to_string(), + "-debug=rpc".to_string(), + ] + } + + pub fn args(&self) -> Vec { + [ + self.base_args(), + self.extra_args.iter().map(|&s| s.to_string()).collect(), + ] + .concat() + } +} diff --git a/src/config/mod.rs b/src/config/mod.rs index 9bb8453..2d9df97 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -1,4 +1,5 @@ mod bitcoin; +mod bridge_backend; mod docker; mod rollup; mod test; @@ -8,6 +9,7 @@ mod utils; use std::path::PathBuf; pub use bitcoin::BitcoinConfig; +pub use bridge_backend::BridgeBackendConfig; pub use citrea_sequencer::SequencerConfig; pub use docker::DockerConfig; pub use rollup::{default_rollup_config, RollupConfig}; diff --git a/src/config/test.rs b/src/config/test.rs index 3f33d4f..351157e 100644 --- a/src/config/test.rs +++ b/src/config/test.rs @@ -1,4 +1,5 @@ use super::bitcoin::BitcoinConfig; +use super::bridge_backend::BridgeBackendConfig; use super::test_case::TestCaseConfig; use super::{FullFullNodeConfig, FullProverConfig, FullSequencerConfig}; @@ -6,6 +7,7 @@ use super::{FullFullNodeConfig, FullProverConfig, FullSequencerConfig}; pub struct TestConfig { pub test_case: TestCaseConfig, pub bitcoin: Vec, + pub bridge_backend: BridgeBackendConfig, pub sequencer: FullSequencerConfig, pub prover: FullProverConfig, pub full_node: FullFullNodeConfig, diff --git a/src/full_node.rs b/src/full_node.rs index d53d8ae..cb0d929 100644 --- a/src/full_node.rs +++ b/src/full_node.rs @@ -13,9 +13,9 @@ use super::framework::TestContext; use super::node::{LogProvider, Node, NodeKind, SpawnOutput}; use super::utils::{get_citrea_path, get_stderr_path, get_stdout_path, retry}; use super::Result; -use crate::utils::get_genesis_path; use crate::evm::make_test_client; use crate::test_client::TestClient; +use crate::utils::get_genesis_path; #[allow(unused)] pub struct FullNode { diff --git a/src/lib.rs b/src/lib.rs index 32cc181..8f9315c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,5 @@ mod bitcoin; +mod bridge_backend; pub mod config; mod docker; pub mod framework; diff --git a/src/node.rs b/src/node.rs index d81ddb1..3c3881c 100644 --- a/src/node.rs +++ b/src/node.rs @@ -14,6 +14,7 @@ use crate::test_helpers::wait_for_l2_block; #[derive(Debug)] pub enum NodeKind { Bitcoin, + BridgeBackend, Prover, Sequencer, FullNode, @@ -23,6 +24,7 @@ impl fmt::Display for NodeKind { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { NodeKind::Bitcoin => write!(f, "bitcoin"), + NodeKind::BridgeBackend => write!(f, "bridge-backend"), NodeKind::Prover => write!(f, "prover"), NodeKind::Sequencer => write!(f, "sequencer"), NodeKind::FullNode => write!(f, "full-node"), @@ -43,7 +45,7 @@ pub enum SpawnOutput { } /// The Node trait defines the common interface shared between -/// BitcoinNode, Prover, Sequencer and FullNode +/// BitcoinNode, Bridge Backend, Prover, Sequencer and FullNode pub(crate) trait Node { type Config; type Client; From 5263145e84419c8f42bd6dd33cd99e1293c4f2bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ceyhun=20=C5=9Een?= Date: Mon, 30 Sep 2024 15:15:55 +0300 Subject: [PATCH 02/24] config: Update bridge backend configuration. --- src/config/bridge_backend.rs | 181 +++++++++++++++++++++++++---------- 1 file changed, 131 insertions(+), 50 deletions(-) diff --git a/src/config/bridge_backend.rs b/src/config/bridge_backend.rs index 9b876d2..9237a15 100644 --- a/src/config/bridge_backend.rs +++ b/src/config/bridge_backend.rs @@ -1,65 +1,146 @@ -//! Copied directly from Bitcoin config TODO: convert it to support bridge backend - -use bitcoin::Network; -use std::path::PathBuf; -use tempfile::TempDir; - #[derive(Debug, Clone)] pub struct BridgeBackendConfig { - pub p2p_port: u16, - pub rpc_port: u16, - pub rpc_user: String, - pub rpc_password: String, - pub data_dir: PathBuf, - pub extra_args: Vec<&'static str>, - pub network: Network, - pub docker_image: Option, - pub env: Vec<(&'static str, &'static str)>, - pub idx: usize, + // Server + pub host: String, + pub port: u32, + + // Postgress + pub pghost: String, + pub pgport: u32, + pub pguser: String, + pub pgpassword: String, + pub pgdatabase: String, + + // Redis + pub redis_url: String, + + // Bridge + pub user_takes_after: String, + pub verifier_pks: String, + pub bridge_amount_btc: u32, + pub confirmation_threshold: u32, + + // Bitcoin + pub bitcoin_rest_endpoint: String, + pub bitcoin_rest_auth_header: String, + + // Citrea + pub citrea_rest_endpoint: String, + pub bitcoin_lightclient_contract_address: String, + pub bridge_contract_address: String, + pub declare_withdraw_filler_private_key: String, + + // Faucet + pub faucet_private_key: String, + pub faucet_amount: String, + pub faucet_amount_limit: String, + pub faucet_count_limit: String, + + // Operator & Verifier & Aggregator + pub operator_urls: String, + pub verifier_urls: String, + pub aggregator_url: String, } impl Default for BridgeBackendConfig { fn default() -> Self { Self { - p2p_port: 0, - rpc_port: 0, - rpc_user: "user".to_string(), - rpc_password: "password".to_string(), - data_dir: TempDir::new() - .expect("Failed to create temporary directory") - .into_path(), - extra_args: Vec::new(), - network: Network::Regtest, - docker_image: Some("bitcoin/bitcoin:latest".to_string()), - env: Vec::new(), - idx: 0, + host: "localhost".to_string(), + port: 8080, + + pghost: "localhost".to_string(), + pgport: 5432, + pguser: "citrea".to_string(), + pgpassword: "".to_string(), + pgdatabase: "citrea_bridge".to_string(), + + redis_url: "redis://localhost:6379".to_string(), + + user_takes_after: "c800".to_string(), + verifier_pks: "7c4803421956db53eed29ee45bddbe60d16e66560f918a94270ea5272b2b4e90".to_string(), + bridge_amount_btc: 1, + confirmation_threshold: 6, + + bitcoin_rest_endpoint: "".to_string(), + bitcoin_rest_auth_header: "".to_string(), + + citrea_rest_endpoint: "".to_string(), + bitcoin_lightclient_contract_address: "0x3100000000000000000000000000000000000001".to_string(), + bridge_contract_address: "".to_string(), + declare_withdraw_filler_private_key: "".to_string(), + + faucet_private_key: "".to_string(), + faucet_amount: "".to_string(), + faucet_amount_limit: "".to_string(), + faucet_count_limit: "".to_string(), + + operator_urls: "http://localhost:17007,http://localhost:17008,http://localhost:17009".to_string(), + verifier_urls: "http://localhost:17000,http://localhost:17001,http://localhost:17002,http://localhost:17003,http://localhost:17004,http://localhost:17005,http://localhost:17006".to_string(), + aggregator_url: "http://localhost:17010".to_string(), } } } impl BridgeBackendConfig { - fn base_args(&self) -> Vec { - vec![ - "-regtest".to_string(), - format!("-datadir={}", self.data_dir.display()), - format!("-port={}", self.p2p_port), - format!("-rpcport={}", self.rpc_port), - format!("-rpcuser={}", self.rpc_user), - format!("-rpcpassword={}", self.rpc_password), - "-server".to_string(), - "-daemonwait".to_string(), - "-txindex".to_string(), - "-addresstype=bech32m".to_string(), - "-debug=net".to_string(), - "-debug=rpc".to_string(), - ] - } + /// Sets current configuration to environment variables and returns it. + /// Bridge backend checks environment variables for it's parameters. + fn set_env(&self) -> String { + let mut env = String::new(); + + env += &format!("NODE_ENV=development "); + env += &format!("NODE_DEBUG=jsonrpc,db "); + + env += &format!("HOST={} ", self.host); + env += &format!("PORT={} ", self.port); + env += &format!("CORS_ORIGIN_PATTERN=^http://localhost "); + + env += &format!("PGHOST={} ", self.pghost); + env += &format!("PGPORT={} ", self.pgport); + env += &format!("PGUSER={} ", self.pguser); + env += &format!("PGPASSWORD={} ", self.pgpassword); + env += &format!("PGDATABASE={} ", self.pgdatabase); + env += &format!("PGSSLMODE=prefer "); + + env += &format!("REDIS_URL={} ", self.redis_url); + + env += &format!("NETWORK=regtest "); + env += &format!("USER_TAKES_AFTER={} ", self.user_takes_after); + env += &format!("VERIFIER_PKS={} ", self.verifier_pks); + env += &format!("BRIDGE_AMOUNT_BTC={} ", self.bridge_amount_btc); + env += &format!("CONFIRMATION_THRESHOLD={} ", self.confirmation_threshold); + + env += &format!("BITCOIN_REST_ENDPOINT={} ", self.bitcoin_rest_endpoint); + env += &format!( + "BITCOIN_REST_AUTH_HEADER={} ", + self.bitcoin_rest_auth_header + ); + + env += &format!("CITREA_REST_ENDPOINT={} ", self.citrea_rest_endpoint); + env += &format!( + "BITCOIN_LIGHTCLIENT_CONTRACT_ADDRESS={} ", + self.bitcoin_lightclient_contract_address + ); + env += &format!("BRIDGE_CONTRACT_ADDRESS= {}", self.bridge_contract_address); + env += &format!( + "DECLARE_WITHDRAW_FILLER_PRIVATE_KEY= {}", + self.declare_withdraw_filler_private_key + ); + + env += &format!("FAUCET_PRIVATE_KEY= {}", self.faucet_private_key); + env += &format!("FAUCET_AMOUNT= {}", self.faucet_amount); + env += &format!("FAUCET_AMOUNT_LIMIT= {}", self.faucet_amount_limit); + env += &format!("FAUCET_COUNT_LIMIT= {}", self.faucet_count_limit); + + env += &format!("OPERATOR_URLS={} ", self.operator_urls); + env += &format!("VERIFIER_URLS={} ", self.verifier_urls); + env += &format!("AGGREGATOR_URL={} ", self.aggregator_url); + + env += &format!("TELEGRAM_BOT_API_KEY= "); + env += &format!("TELEGRAM_CHAT_ID= "); + + env += &format!("CLOUDFLARE_SITE_KEY=1x00000000000000000000AA "); + env += &format!("CLOUDFLARE_SECRET_KEY=1x0000000000000000000000000000000AA "); - pub fn args(&self) -> Vec { - [ - self.base_args(), - self.extra_args.iter().map(|&s| s.to_string()).collect(), - ] - .concat() + env } } From bb6ae961d3e723bf28720d1aa682a3ee0cacb53b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ceyhun=20=C5=9Een?= Date: Mon, 30 Sep 2024 15:56:21 +0300 Subject: [PATCH 03/24] config: bridgeLconfig: Convert get_env to return hash map. --- src/config/bridge_backend.rs | 134 ++++++++++++++++++++++------------- 1 file changed, 86 insertions(+), 48 deletions(-) diff --git a/src/config/bridge_backend.rs b/src/config/bridge_backend.rs index 9237a15..99b7228 100644 --- a/src/config/bridge_backend.rs +++ b/src/config/bridge_backend.rs @@ -1,3 +1,5 @@ +use std::collections::HashMap; + #[derive(Debug, Clone)] pub struct BridgeBackendConfig { // Server @@ -84,62 +86,98 @@ impl Default for BridgeBackendConfig { impl BridgeBackendConfig { /// Sets current configuration to environment variables and returns it. /// Bridge backend checks environment variables for it's parameters. - fn set_env(&self) -> String { - let mut env = String::new(); - - env += &format!("NODE_ENV=development "); - env += &format!("NODE_DEBUG=jsonrpc,db "); - - env += &format!("HOST={} ", self.host); - env += &format!("PORT={} ", self.port); - env += &format!("CORS_ORIGIN_PATTERN=^http://localhost "); - - env += &format!("PGHOST={} ", self.pghost); - env += &format!("PGPORT={} ", self.pgport); - env += &format!("PGUSER={} ", self.pguser); - env += &format!("PGPASSWORD={} ", self.pgpassword); - env += &format!("PGDATABASE={} ", self.pgdatabase); - env += &format!("PGSSLMODE=prefer "); - - env += &format!("REDIS_URL={} ", self.redis_url); - - env += &format!("NETWORK=regtest "); - env += &format!("USER_TAKES_AFTER={} ", self.user_takes_after); - env += &format!("VERIFIER_PKS={} ", self.verifier_pks); - env += &format!("BRIDGE_AMOUNT_BTC={} ", self.bridge_amount_btc); - env += &format!("CONFIRMATION_THRESHOLD={} ", self.confirmation_threshold); - - env += &format!("BITCOIN_REST_ENDPOINT={} ", self.bitcoin_rest_endpoint); - env += &format!( - "BITCOIN_REST_AUTH_HEADER={} ", - self.bitcoin_rest_auth_header + pub fn get_env(&mut self) -> HashMap { + let mut env = HashMap::new(); + + env.insert("NODE_ENV".to_string(), "development".to_string()); + env.insert("NODE_DEBUG".to_string(), "jsonrpc,db".to_string()); + + env.insert("HOST".to_string(), self.host.clone()); + env.insert("PORT".to_string(), self.port.to_string()); + env.insert( + "CORS_ORIGIN_PATTERN".to_string(), + "=^http://localhost ".to_string(), ); - env += &format!("CITREA_REST_ENDPOINT={} ", self.citrea_rest_endpoint); - env += &format!( - "BITCOIN_LIGHTCLIENT_CONTRACT_ADDRESS={} ", - self.bitcoin_lightclient_contract_address + env.insert("PGHOST".to_string(), self.pghost.clone()); + env.insert("PGPORT".to_string(), self.pgport.to_string()); + env.insert("PGUSER".to_string(), self.pguser.clone()); + env.insert("PGPASSWORD".to_string(), self.pgpassword.clone()); + env.insert("PGDATABASE".to_string(), self.pgdatabase.clone()); + env.insert("PGSSLMODE".to_string(), "=prefer".to_string()); + + env.insert("REDIS_URL".to_string(), self.redis_url.clone()); + + env.insert("NETWORK".to_string(), "=regtest".to_string()); + env.insert( + "USER_TAKES_AFTER".to_string(), + self.user_takes_after.clone(), ); - env += &format!("BRIDGE_CONTRACT_ADDRESS= {}", self.bridge_contract_address); - env += &format!( - "DECLARE_WITHDRAW_FILLER_PRIVATE_KEY= {}", - self.declare_withdraw_filler_private_key + env.insert("VERIFIER_PKS".to_string(), self.verifier_pks.clone()); + env.insert( + "BRIDGE_AMOUNT_BTC".to_string(), + self.bridge_amount_btc.to_string(), + ); + env.insert( + "CONFIRMATION_THRESHOLD".to_string(), + self.confirmation_threshold.to_string(), ); - env += &format!("FAUCET_PRIVATE_KEY= {}", self.faucet_private_key); - env += &format!("FAUCET_AMOUNT= {}", self.faucet_amount); - env += &format!("FAUCET_AMOUNT_LIMIT= {}", self.faucet_amount_limit); - env += &format!("FAUCET_COUNT_LIMIT= {}", self.faucet_count_limit); + env.insert( + "BITCOIN_REST_ENDPOINT".to_string(), + self.bitcoin_rest_endpoint.clone(), + ); + env.insert( + "BITCOIN_REST_AUTH_HEADER".to_string(), + self.bitcoin_rest_auth_header.clone(), + ); - env += &format!("OPERATOR_URLS={} ", self.operator_urls); - env += &format!("VERIFIER_URLS={} ", self.verifier_urls); - env += &format!("AGGREGATOR_URL={} ", self.aggregator_url); + env.insert( + "CITREA_REST_ENDPOINT".to_string(), + self.citrea_rest_endpoint.clone(), + ); + env.insert( + "BITCOIN_LIGHTCLIENT_CONTRACT_ADDRESS".to_string(), + self.bitcoin_lightclient_contract_address.clone(), + ); + env.insert( + "BRIDGE_CONTRACT_ADDRESS".to_string(), + self.bridge_contract_address.clone(), + ); + env.insert( + "DECLARE_WITHDRAW_FILLER_PRIVATE_KEY".to_string(), + self.declare_withdraw_filler_private_key.clone(), + ); - env += &format!("TELEGRAM_BOT_API_KEY= "); - env += &format!("TELEGRAM_CHAT_ID= "); + env.insert( + "FAUCET_PRIVATE_KEY".to_string(), + self.faucet_private_key.clone(), + ); + env.insert("FAUCET_AMOUNT".to_string(), self.faucet_amount.clone()); + env.insert( + "FAUCET_AMOUNT_LIMIT".to_string(), + self.faucet_amount_limit.clone(), + ); + env.insert( + "FAUCET_COUNT_LIMIT".to_string(), + self.faucet_count_limit.clone(), + ); - env += &format!("CLOUDFLARE_SITE_KEY=1x00000000000000000000AA "); - env += &format!("CLOUDFLARE_SECRET_KEY=1x0000000000000000000000000000000AA "); + env.insert("OPERATOR_URLS".to_string(), self.operator_urls.clone()); + env.insert("VERIFIER_URLS".to_string(), self.verifier_urls.clone()); + env.insert("AGGREGATOR_URL".to_string(), self.aggregator_url.clone()); + + env.insert("TELEGRAM_BOT_API_KEY".to_string(), "".to_string()); + env.insert("TELEGRAM_CHAT_ID".to_string(), "".to_string()); + + env.insert( + "CLOUDFLARE_SITE_KEY".to_string(), + "1x00000000000000000000AA".to_string(), + ); + env.insert( + "CLOUDFLARE_SECRET_KEY".to_string(), + "1x0000000000000000000000000000000AA".to_string(), + ); env } From 1011b7ce07bbf89dce1521386931f6cc574514e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ceyhun=20=C5=9Een?= Date: Mon, 30 Sep 2024 16:01:42 +0300 Subject: [PATCH 04/24] bridge_backend: Use get_env and delete unnecessary code. --- src/bridge_backend.rs | 103 +++++++++++------------------------ src/config/bridge_backend.rs | 2 +- 2 files changed, 34 insertions(+), 71 deletions(-) diff --git a/src/bridge_backend.rs b/src/bridge_backend.rs index 3fdc258..3967461 100644 --- a/src/bridge_backend.rs +++ b/src/bridge_backend.rs @@ -4,13 +4,8 @@ use std::sync::Arc; use std::time::{Duration, Instant}; use anyhow::{bail, Context}; -use async_trait::async_trait; -use bitcoin::Address; -use bitcoincore_rpc::json::AddressType::Bech32m; -use bitcoincore_rpc::{Auth, Client, RpcApi}; use futures::TryStreamExt; use tokio::process::Command; -use tokio::sync::OnceCell; use tokio::time::sleep; use super::config::BridgeBackendConfig; @@ -19,12 +14,11 @@ use super::framework::TestContext; use super::node::{LogProvider, Node, Restart, SpawnOutput}; use super::Result; use crate::node::NodeKind; +use crate::test_client::TestClient; pub struct BridgeBackend { spawn_output: SpawnOutput, pub config: BridgeBackendConfig, - client: Client, - gen_addr: OnceCell
, docker_env: Arc>, } @@ -32,25 +26,9 @@ impl BridgeBackend { pub async fn new(config: &BridgeBackendConfig, docker: Arc>) -> Result { let spawn_output = Self::spawn(config, &docker).await?; - let rpc_url = format!( - "http://127.0.0.1:{}/wallet/{}", - config.rpc_port, - NodeKind::Bitcoin - ); - let client = Client::new( - &rpc_url, - Auth::UserPass(config.rpc_user.clone(), config.rpc_password.clone()), - ) - .await - .context("Failed to create RPC client")?; - - wait_for_rpc_ready(&client, None).await?; - Ok(Self { spawn_output, config: config.clone(), - client, - gen_addr: OnceCell::new(), docker_env: docker, }) } @@ -67,52 +45,20 @@ impl BridgeBackend { } } -#[async_trait] -impl RpcApi for BridgeBackend { - async fn call serde::de::Deserialize<'a>>( - &self, - cmd: &str, - args: &[serde_json::Value], - ) -> bitcoincore_rpc::Result { - self.client.call(cmd, args).await - } - - // Override deprecated generate method. - // Uses or lazy init gen_addr and forward to `generate_to_address` - async fn generate( - &self, - block_num: u64, - _maxtries: Option, - ) -> bitcoincore_rpc::Result> { - let addr = self - .gen_addr - .get_or_init(|| async { - self.client - .get_new_address(None, Some(Bech32m)) - .await - .expect("Failed to generate address") - .assume_checked() - }) - .await; - - self.generate_to_address(block_num, addr).await - } -} - impl Node for BridgeBackend { type Config = BridgeBackendConfig; - type Client = Client; + type Client = TestClient; fn spawn(config: &Self::Config) -> Result { - let args = config.args(); - println!("Running bitcoind with args : {args:?}"); + let env = config.get_env(); + println!("Running bridge backend with environment variables: {env:?}"); - Command::new("bitcoind") - .args(&args) + Command::new("TODO") .kill_on_drop(true) - .envs(config.env.clone()) + .env_clear() + .envs(env) .spawn() - .context("Failed to spawn bitcoind process") + .context("Failed to spawn bridge backend process") .map(SpawnOutput::Child) } @@ -125,9 +71,8 @@ impl Node for BridgeBackend { let start = Instant::now(); let timeout = timeout.unwrap_or(Duration::from_secs(30)); while start.elapsed() < timeout { - if wait_for_rpc_ready(&self.client, Some(timeout)) - .await - .is_ok() + if true + // TODO: Do this check. { return Ok(()); } @@ -137,16 +82,34 @@ impl Node for BridgeBackend { } fn client(&self) -> &Self::Client { - &self.client - } - - fn env(&self) -> Vec<(&'static str, &'static str)> { - self.config.env.clone() + &self.client() } 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(crate::node::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(()) + } + } + } } impl Restart for BridgeBackend { diff --git a/src/config/bridge_backend.rs b/src/config/bridge_backend.rs index 99b7228..bc4990a 100644 --- a/src/config/bridge_backend.rs +++ b/src/config/bridge_backend.rs @@ -86,7 +86,7 @@ impl Default for BridgeBackendConfig { impl BridgeBackendConfig { /// Sets current configuration to environment variables and returns it. /// Bridge backend checks environment variables for it's parameters. - pub fn get_env(&mut self) -> HashMap { + pub fn get_env(&self) -> HashMap { let mut env = HashMap::new(); env.insert("NODE_ENV".to_string(), "development".to_string()); From 0e561dd73ca3bcdccdfda7eaccf2e43429850da1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ceyhun=20=C5=9Een?= Date: Mon, 30 Sep 2024 17:27:49 +0300 Subject: [PATCH 05/24] bridge_backend: Rename BridgeBackendNode and add 2 commands for spawn(). --- src/bridge_backend.rs | 66 ++++++++++++++++++++----------------------- 1 file changed, 30 insertions(+), 36 deletions(-) diff --git a/src/bridge_backend.rs b/src/bridge_backend.rs index 3967461..2a08acb 100644 --- a/src/bridge_backend.rs +++ b/src/bridge_backend.rs @@ -1,4 +1,3 @@ -use std::collections::HashSet; use std::path::PathBuf; use std::sync::Arc; use std::time::{Duration, Instant}; @@ -6,7 +5,6 @@ use std::time::{Duration, Instant}; use anyhow::{bail, Context}; use futures::TryStreamExt; use tokio::process::Command; -use tokio::time::sleep; use super::config::BridgeBackendConfig; use super::docker::DockerEnv; @@ -16,13 +14,13 @@ use super::Result; use crate::node::NodeKind; use crate::test_client::TestClient; -pub struct BridgeBackend { +pub struct BridgeBackendNode { spawn_output: SpawnOutput, pub config: BridgeBackendConfig, docker_env: Arc>, } -impl BridgeBackend { +impl BridgeBackendNode { pub async fn new(config: &BridgeBackendConfig, docker: Arc>) -> Result { let spawn_output = Self::spawn(config, &docker).await?; @@ -45,7 +43,7 @@ impl BridgeBackend { } } -impl Node for BridgeBackend { +impl Node for BridgeBackendNode { type Config = BridgeBackendConfig; type Client = TestClient; @@ -53,12 +51,20 @@ impl Node for BridgeBackend { let env = config.get_env(); println!("Running bridge backend with environment variables: {env:?}"); - Command::new("TODO") + 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 process") + .context("Failed to spawn bridge backend worker process") .map(SpawnOutput::Child) } @@ -112,7 +118,7 @@ impl Node for BridgeBackend { } } -impl Restart for BridgeBackend { +impl Restart for BridgeBackendNode { async fn wait_until_stopped(&mut self) -> Result<()> { self.client.stop().await?; self.stop().await?; @@ -151,9 +157,9 @@ impl Restart for BridgeBackend { } } -impl LogProvider for BridgeBackend { +impl LogProvider for BridgeBackendNode { fn kind(&self) -> NodeKind { - NodeKind::Bitcoin + NodeKind::BridgeBackend } fn log_path(&self) -> PathBuf { @@ -162,7 +168,7 @@ impl LogProvider for BridgeBackend { } pub struct BitcoinNodeCluster { - inner: Vec, + inner: Vec, } impl BitcoinNodeCluster { @@ -172,7 +178,7 @@ impl BitcoinNodeCluster { inner: Vec::with_capacity(n_nodes), }; for config in ctx.config.bitcoin.iter() { - let node = BridgeBackend::new(config, Arc::clone(&ctx.docker)).await?; + let node = BridgeBackendNode::new(config, Arc::clone(&ctx.docker)).await?; cluster.inner.push(node) } @@ -181,7 +187,7 @@ impl BitcoinNodeCluster { pub async fn stop_all(&mut self) -> Result<()> { for node in &mut self.inner { - RpcApi::stop(node).await?; + // RpcApi::stop(node).await?; node.stop().await?; } Ok(()) @@ -190,17 +196,17 @@ impl BitcoinNodeCluster { pub async fn wait_for_sync(&self, timeout: Duration) -> Result<()> { let start = Instant::now(); while start.elapsed() < timeout { - let mut heights = HashSet::new(); - for node in &self.inner { - let height = node.get_block_count().await?; - heights.insert(height); - } + // let mut heights = HashSet::new(); + // for node in &self.inner { + // let height = node.get_block_count().await?; + // heights.insert(height); + // } - if heights.len() == 1 { - return Ok(()); - } + // if heights.len() == 1 { + return Ok(()); + // } - sleep(Duration::from_secs(1)).await; + // sleep(Duration::from_secs(1)).await; } bail!("Nodes failed to sync within the specified timeout") } @@ -223,24 +229,12 @@ impl BitcoinNodeCluster { Ok(()) } - pub fn get(&self, index: usize) -> Option<&BridgeBackend> { + pub fn get(&self, index: usize) -> Option<&BridgeBackendNode> { self.inner.get(index) } #[allow(unused)] - pub fn get_mut(&mut self, index: usize) -> Option<&mut BridgeBackend> { + pub fn get_mut(&mut self, index: usize) -> Option<&mut BridgeBackendNode> { self.inner.get_mut(index) } } - -async fn wait_for_rpc_ready(client: &Client, timeout: Option) -> Result<()> { - let start = Instant::now(); - let timeout = timeout.unwrap_or(Duration::from_secs(300)); - while start.elapsed() < timeout { - match client.get_blockchain_info().await { - Ok(_) => return Ok(()), - Err(_) => sleep(Duration::from_millis(500)).await, - } - } - Err(anyhow::anyhow!("Timeout waiting for RPC to be ready")) -} From a2830c79b2b15fb0b241851bc0f823f8b4044aef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ceyhun=20=C5=9Een?= Date: Fri, 4 Oct 2024 11:35:25 +0300 Subject: [PATCH 06/24] configs: Remove unnecessary =. --- src/config/bridge_backend.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/config/bridge_backend.rs b/src/config/bridge_backend.rs index bc4990a..c8d721a 100644 --- a/src/config/bridge_backend.rs +++ b/src/config/bridge_backend.rs @@ -96,7 +96,7 @@ impl BridgeBackendConfig { env.insert("PORT".to_string(), self.port.to_string()); env.insert( "CORS_ORIGIN_PATTERN".to_string(), - "=^http://localhost ".to_string(), + "^http://localhost ".to_string(), ); env.insert("PGHOST".to_string(), self.pghost.clone()); @@ -104,11 +104,11 @@ impl BridgeBackendConfig { env.insert("PGUSER".to_string(), self.pguser.clone()); env.insert("PGPASSWORD".to_string(), self.pgpassword.clone()); env.insert("PGDATABASE".to_string(), self.pgdatabase.clone()); - env.insert("PGSSLMODE".to_string(), "=prefer".to_string()); + env.insert("PGSSLMODE".to_string(), "prefer".to_string()); env.insert("REDIS_URL".to_string(), self.redis_url.clone()); - env.insert("NETWORK".to_string(), "=regtest".to_string()); + env.insert("NETWORK".to_string(), "regtest".to_string()); env.insert( "USER_TAKES_AFTER".to_string(), self.user_takes_after.clone(), From e4c488a425025f9bfd90d9bbb9215b41a7b81b96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ceyhun=20=C5=9Een?= Date: Fri, 4 Oct 2024 15:55:09 +0300 Subject: [PATCH 07/24] bridge_backend: Add initial client. --- src/bridge_backend.rs | 105 ++++++++++++++++++----------------- src/bridge_backend_client.rs | 36 ++++++++++++ src/config/bridge_backend.rs | 9 +++ src/config/docker.rs | 20 ++++++- src/config/mod.rs | 2 +- src/config/test.rs | 6 +- src/docker.rs | 2 +- src/framework.rs | 5 +- src/lib.rs | 1 + src/test_case.rs | 13 ++++- 10 files changed, 139 insertions(+), 60 deletions(-) create mode 100644 src/bridge_backend_client.rs diff --git a/src/bridge_backend.rs b/src/bridge_backend.rs index 2a08acb..1f82f6a 100644 --- a/src/bridge_backend.rs +++ b/src/bridge_backend.rs @@ -1,33 +1,40 @@ +use std::net::SocketAddr; use std::path::PathBuf; +use std::str::FromStr; 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 super::config::BridgeBackendConfig; use super::docker::DockerEnv; use super::framework::TestContext; -use super::node::{LogProvider, Node, Restart, SpawnOutput}; use super::Result; +use crate::bridge_backend_client::BridgeBackendClient; use crate::node::NodeKind; -use crate::test_client::TestClient; +use crate::traits::{ContainerSpawnOutput, LogProvider, Node, Restart, SpawnOutput}; pub struct BridgeBackendNode { spawn_output: SpawnOutput, pub config: BridgeBackendConfig, docker_env: Arc>, + client: BridgeBackendClient, } impl BridgeBackendNode { pub async fn new(config: &BridgeBackendConfig, docker: Arc>) -> Result { let spawn_output = Self::spawn(config, &docker).await?; + let rpc_url = SocketAddr::from_str(&(config.host.clone() + &config.port.to_string()))?; Ok(Self { spawn_output, config: config.clone(), docker_env: docker, + client: BridgeBackendClient::new(rpc_url).await?, }) } @@ -41,11 +48,38 @@ impl BridgeBackendNode { 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 Node for BridgeBackendNode { type Config = BridgeBackendConfig; - type Client = TestClient; + type Client = BridgeBackendClient; fn spawn(config: &Self::Config) -> Result { let env = config.get_env(); @@ -87,10 +121,6 @@ impl Node for BridgeBackendNode { anyhow::bail!("Node failed to become ready within the specified timeout") } - fn client(&self) -> &Self::Client { - &self.client() - } - fn config_mut(&mut self) -> &mut Self::Config { &mut self.config } @@ -104,7 +134,7 @@ impl Node for BridgeBackendNode { .context("Failed to kill child process")?; Ok(()) } - SpawnOutput::Container(crate::node::ContainerSpawnOutput { id, .. }) => { + SpawnOutput::Container(ContainerSpawnOutput { id, .. }) => { std::println!("Stopping container {id}"); let docker = bollard::Docker::connect_with_local_defaults() .context("Failed to connect to Docker")?; @@ -116,11 +146,21 @@ impl Node for BridgeBackendNode { } } } + + fn client(&self) -> &Self::Client { + &self.client + } + + fn env(&self) -> Vec<(&'static str, &'static str)> { + // self.config.get_env() + todo!() + } } +#[async_trait] impl Restart for BridgeBackendNode { async fn wait_until_stopped(&mut self) -> Result<()> { - self.client.stop().await?; + // self.client.stop().await?; self.stop().await?; match &self.spawn_output { @@ -150,9 +190,6 @@ impl Restart for BridgeBackendNode { self.wait_for_ready(None).await?; - // Reload wallets after restart - self.load_wallets().await; - Ok(()) } } @@ -163,21 +200,21 @@ impl LogProvider for BridgeBackendNode { } fn log_path(&self) -> PathBuf { - self.config.data_dir.join("regtest").join("debug.log") + todo!() } } -pub struct BitcoinNodeCluster { +pub struct BridgeBackendNodeCluster { inner: Vec, } -impl BitcoinNodeCluster { +impl BridgeBackendNodeCluster { pub async fn new(ctx: &TestContext) -> Result { let n_nodes = ctx.config.test_case.n_nodes; let mut cluster = Self { inner: Vec::with_capacity(n_nodes), }; - for config in ctx.config.bitcoin.iter() { + for config in ctx.config.bridge_backend.iter() { let node = BridgeBackendNode::new(config, Arc::clone(&ctx.docker)).await?; cluster.inner.push(node) } @@ -193,42 +230,6 @@ impl BitcoinNodeCluster { Ok(()) } - pub async fn wait_for_sync(&self, timeout: Duration) -> Result<()> { - let start = Instant::now(); - while start.elapsed() < timeout { - // let mut heights = HashSet::new(); - // for node in &self.inner { - // let height = node.get_block_count().await?; - // heights.insert(height); - // } - - // if heights.len() == 1 { - return Ok(()); - // } - - // sleep(Duration::from_secs(1)).await; - } - bail!("Nodes failed to sync within the specified timeout") - } - - // Connect all bitcoin nodes between them - pub async fn connect_nodes(&self) -> Result<()> { - for (i, from_node) in self.inner.iter().enumerate() { - for (j, to_node) in self.inner.iter().enumerate() { - if i != j { - let ip = match &to_node.spawn_output { - SpawnOutput::Container(container) => container.ip.clone(), - _ => "127.0.0.1".to_string(), - }; - - let add_node_arg = format!("{}:{}", ip, to_node.config.p2p_port); - from_node.add_node(&add_node_arg).await?; - } - } - } - Ok(()) - } - pub fn get(&self, index: usize) -> Option<&BridgeBackendNode> { self.inner.get(index) } diff --git a/src/bridge_backend_client.rs b/src/bridge_backend_client.rs new file mode 100644 index 0000000..1d185a1 --- /dev/null +++ b/src/bridge_backend_client.rs @@ -0,0 +1,36 @@ +use std::{net::SocketAddr, time::Duration}; + +use jsonrpsee::{ + http_client::{HttpClient, HttpClientBuilder}, + ws_client::{PingConfig, WsClient, WsClientBuilder}, +}; + +pub const MAX_FEE_PER_GAS: u128 = 1000000001; + +pub struct BridgeBackendClient { + http_client: HttpClient, + ws_client: WsClient, + pub rpc_addr: SocketAddr, +} + +impl BridgeBackendClient { + pub async fn new(rpc_addr: SocketAddr) -> anyhow::Result { + let http_host = format!("http://localhost:{}", rpc_addr.port()); + let ws_host = format!("ws://localhost:{}", rpc_addr.port()); + + let http_client = HttpClientBuilder::default() + .request_timeout(Duration::from_secs(120)) + .build(http_host)?; + + let ws_client = WsClientBuilder::default() + .enable_ws_ping(PingConfig::default().inactive_limit(Duration::from_secs(10))) + .build(ws_host) + .await?; + + Ok(Self { + ws_client, + http_client, + rpc_addr, + }) + } +} diff --git a/src/config/bridge_backend.rs b/src/config/bridge_backend.rs index c8d721a..631672e 100644 --- a/src/config/bridge_backend.rs +++ b/src/config/bridge_backend.rs @@ -42,6 +42,11 @@ pub struct BridgeBackendConfig { pub operator_urls: String, pub verifier_urls: String, pub aggregator_url: String, + + // Non bridge backend. + pub docker_image: Option, + pub env: Vec<(&'static str, &'static str)>, + pub idx: usize, } impl Default for BridgeBackendConfig { @@ -79,6 +84,10 @@ impl Default for BridgeBackendConfig { operator_urls: "http://localhost:17007,http://localhost:17008,http://localhost:17009".to_string(), verifier_urls: "http://localhost:17000,http://localhost:17001,http://localhost:17002,http://localhost:17003,http://localhost:17004,http://localhost:17005,http://localhost:17006".to_string(), aggregator_url: "http://localhost:17010".to_string(), + + docker_image: None, + env: Vec::new(), + idx: 0, } } } diff --git a/src/config/docker.rs b/src/config/docker.rs index 1e5c0ab..ea543ab 100644 --- a/src/config/docker.rs +++ b/src/config/docker.rs @@ -1,6 +1,6 @@ use std::path::PathBuf; -use super::{BitcoinConfig, FullSequencerConfig}; +use super::{BitcoinConfig, BridgeBackendConfig, FullSequencerConfig}; use crate::utils::get_genesis_path; #[derive(Debug)] @@ -45,6 +45,24 @@ impl From<&BitcoinConfig> for DockerConfig { } } +impl From<&BridgeBackendConfig> for DockerConfig { + fn from(v: &BridgeBackendConfig) -> Self { + Self { + ports: vec![v.port.try_into().unwrap()], + image: v + .docker_image + .clone() + .unwrap_or_else(|| "bitcoin/bitcoin:latest".to_string()), + cmd: vec![], + log_path: PathBuf::new(), + volume: VolumeConfig { + name: format!("bridge-backend-{}", v.idx), + target: "/home/bridge_backend/.bridge_backend".to_string(), + }, + } + } +} + impl From<&FullSequencerConfig> for DockerConfig { fn from(v: &FullSequencerConfig) -> Self { let args = vec![ diff --git a/src/config/mod.rs b/src/config/mod.rs index f559d3d..7dbca4d 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -9,8 +9,8 @@ mod utils; use std::path::PathBuf; pub use bitcoin::BitcoinConfig; -pub use bridge_backend::BridgeBackendConfig; pub use bitcoin_da::service::BitcoinServiceConfig; +pub use bridge_backend::BridgeBackendConfig; pub use citrea_sequencer::SequencerConfig; pub use docker::DockerConfig; pub use rollup::{default_rollup_config, RollupConfig}; diff --git a/src/config/test.rs b/src/config/test.rs index fe202ff..ee223a6 100644 --- a/src/config/test.rs +++ b/src/config/test.rs @@ -1,13 +1,13 @@ use super::{ - bitcoin::BitcoinConfig, test_case::TestCaseConfig, FullFullNodeConfig, FullProverConfig, - FullSequencerConfig, BridgeBackendConfig + bitcoin::BitcoinConfig, test_case::TestCaseConfig, BridgeBackendConfig, FullFullNodeConfig, + FullProverConfig, FullSequencerConfig, }; #[derive(Clone)] pub struct TestConfig { pub test_case: TestCaseConfig, pub bitcoin: Vec, - pub bridge_backend: BridgeBackendConfig, + pub bridge_backend: Vec, pub sequencer: FullSequencerConfig, pub prover: FullProverConfig, pub full_node: FullFullNodeConfig, diff --git a/src/docker.rs b/src/docker.rs index e248a52..5cf2830 100644 --- a/src/docker.rs +++ b/src/docker.rs @@ -51,7 +51,7 @@ impl DockerEnv { test_case_id: &str, n_nodes: usize, ) -> Result> { - let volume_configs = vec![("bitcoin", n_nodes)]; + let volume_configs = vec![("bitcoin", n_nodes), ("bridge-backend", n_nodes)]; let mut volumes = HashSet::new(); for (name, n) in volume_configs { diff --git a/src/framework.rs b/src/framework.rs index 756bc01..d6a495e 100644 --- a/src/framework.rs +++ b/src/framework.rs @@ -12,7 +12,7 @@ use super::{ traits::{LogProvider, LogProviderErased, Node}, Result, }; -use crate::{prover::Prover, utils::tail_file}; +use crate::{bridge_backend::BridgeBackendNodeCluster, prover::Prover, utils::tail_file}; pub struct TestContext { pub config: TestConfig, @@ -36,6 +36,7 @@ impl TestContext { pub struct TestFramework { ctx: TestContext, pub bitcoin_nodes: BitcoinNodeCluster, + pub bridge_backend_nodes: BridgeBackendNodeCluster, pub sequencer: Option, pub prover: Option, pub full_node: Option, @@ -61,10 +62,12 @@ impl TestFramework { let ctx = TestContext::new(config).await; let bitcoin_nodes = BitcoinNodeCluster::new(&ctx).await?; + let bridge_backend_nodes = BridgeBackendNodeCluster::new(&ctx).await?; // tokio::time::sleep(std::time::Duration::from_secs(30)).await; Ok(Self { bitcoin_nodes, + bridge_backend_nodes, sequencer: None, prover: None, full_node: None, diff --git a/src/lib.rs b/src/lib.rs index eab2542..925a77f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,6 @@ mod bitcoin; mod bridge_backend; +pub mod bridge_backend_client; pub mod client; pub mod config; mod docker; diff --git a/src/test_case.rs b/src/test_case.rs index e406aab..6574237 100644 --- a/src/test_case.rs +++ b/src/test_case.rs @@ -23,7 +23,8 @@ use super::{ }; use crate::{ config::{ - BitcoinServiceConfig, ProverConfig, RpcConfig, RunnerConfig, SequencerConfig, StorageConfig, + BitcoinServiceConfig, BridgeBackendConfig, ProverConfig, RpcConfig, RunnerConfig, + SequencerConfig, StorageConfig, }, traits::Node, utils::{get_default_genesis_path, get_workspace_root}, @@ -138,6 +139,15 @@ impl TestCaseRunner { }) } + let mut bridge_backend_confs = vec![]; + for i in 0..test_case.n_nodes { + let data_dir = bitcoin_dir.join(i.to_string()); + std::fs::create_dir_all(&data_dir) + .with_context(|| format!("Failed to create {} directory", data_dir.display()))?; + + bridge_backend_confs.push(BridgeBackendConfig::default()) + } + // Target first bitcoin node as DA for now let da_config: BitcoinServiceConfig = bitcoin_confs[0].clone().into(); @@ -252,6 +262,7 @@ impl TestCaseRunner { env: env.full_node(), }, test_case, + bridge_backend: bridge_backend_confs, }) } } From fc51959b16e119f55bcbc9d30cd35744eb759ea1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ceyhun=20=C5=9Een?= Date: Fri, 4 Oct 2024 17:50:46 +0300 Subject: [PATCH 08/24] config: Add with_bridge_backend option to TestCaseConfig. --- src/config/test_case.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/config/test_case.rs b/src/config/test_case.rs index bd7acae..8c05749 100644 --- a/src/config/test_case.rs +++ b/src/config/test_case.rs @@ -41,6 +41,7 @@ impl TestCaseEnv { #[derive(Clone)] pub struct TestCaseConfig { pub n_nodes: usize, + pub with_bridge_backend: bool, pub with_sequencer: bool, pub with_full_node: bool, pub with_prover: bool, @@ -58,6 +59,7 @@ impl Default for TestCaseConfig { fn default() -> Self { TestCaseConfig { n_nodes: 1, + with_bridge_backend: false, with_sequencer: true, with_prover: false, with_full_node: false, From 9475152ed5e0571d05a24a40b38223634e682812 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ceyhun=20=C5=9Een?= Date: Mon, 7 Oct 2024 10:58:58 +0300 Subject: [PATCH 09/24] test_case: Add needed /. --- src/test_case.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test_case.rs b/src/test_case.rs index da82a57..17237b0 100644 --- a/src/test_case.rs +++ b/src/test_case.rs @@ -30,7 +30,7 @@ use crate::{ utils::{get_default_genesis_path, get_workspace_root}, }; -// TestCaseRunner manages the lifecycle of a test case, including setup, execution, and cleanup. +/// TestCaseRunner manages the lifecycle of a test case, including setup, execution, and cleanup. /// It creates a test framework with the associated configs, spawns required nodes, connects them, /// runs the test case, and performs cleanup afterwards. The `run` method handles any panics that /// might occur during test execution and takes care of cleaning up and stopping the child processes. From 8f33f9282036a8f4b78074068cb5e2fdae22a071 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ceyhun=20=C5=9Een?= Date: Mon, 7 Oct 2024 10:59:24 +0300 Subject: [PATCH 10/24] bridge_backend_client: Fix unused vars. --- src/bridge_backend_client.rs | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/bridge_backend_client.rs b/src/bridge_backend_client.rs index 1d185a1..643855c 100644 --- a/src/bridge_backend_client.rs +++ b/src/bridge_backend_client.rs @@ -5,11 +5,9 @@ use jsonrpsee::{ ws_client::{PingConfig, WsClient, WsClientBuilder}, }; -pub const MAX_FEE_PER_GAS: u128 = 1000000001; - pub struct BridgeBackendClient { - http_client: HttpClient, - ws_client: WsClient, + _http_client: HttpClient, + _ws_client: WsClient, pub rpc_addr: SocketAddr, } @@ -18,18 +16,18 @@ impl BridgeBackendClient { let http_host = format!("http://localhost:{}", rpc_addr.port()); let ws_host = format!("ws://localhost:{}", rpc_addr.port()); - let http_client = HttpClientBuilder::default() + let _http_client = HttpClientBuilder::default() .request_timeout(Duration::from_secs(120)) .build(http_host)?; - let ws_client = WsClientBuilder::default() + let _ws_client = WsClientBuilder::default() .enable_ws_ping(PingConfig::default().inactive_limit(Duration::from_secs(10))) .build(ws_host) .await?; Ok(Self { - ws_client, - http_client, + _ws_client, + _http_client, rpc_addr, }) } From 54a4022f8f8f31d45823b218327bc2d762d00f86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ceyhun=20=C5=9Een?= Date: Mon, 7 Oct 2024 11:00:07 +0300 Subject: [PATCH 11/24] lib: Add bridge backend stuff. --- src/lib.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 3bb6283..970c5bd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,6 @@ pub mod bitcoin; +pub mod bridge_backend; +mod bridge_backend_client; mod client; pub mod config; mod docker; From 098bafa0dc9f59f2bc731fc32b27e8cf13ff59f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ceyhun=20=C5=9Een?= Date: Mon, 7 Oct 2024 11:01:53 +0300 Subject: [PATCH 12/24] bridge_backend: Use the new NodeT type. --- src/bridge_backend.rs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/bridge_backend.rs b/src/bridge_backend.rs index 1f82f6a..59cd0a7 100644 --- a/src/bridge_backend.rs +++ b/src/bridge_backend.rs @@ -16,7 +16,7 @@ use super::framework::TestContext; use super::Result; use crate::bridge_backend_client::BridgeBackendClient; use crate::node::NodeKind; -use crate::traits::{ContainerSpawnOutput, LogProvider, Node, Restart, SpawnOutput}; +use crate::traits::{ContainerSpawnOutput, LogProvider, NodeT, Restart, SpawnOutput}; pub struct BridgeBackendNode { spawn_output: SpawnOutput, @@ -45,7 +45,7 @@ impl BridgeBackendNode { ) -> Result { match docker.as_ref() { Some(docker) => docker.spawn(config.into()).await, - None => ::spawn(config), + None => ::spawn(config), } } @@ -77,7 +77,7 @@ impl BridgeBackendNode { } #[async_trait] -impl Node for BridgeBackendNode { +impl NodeT for BridgeBackendNode { type Config = BridgeBackendConfig; type Client = BridgeBackendClient; @@ -155,6 +155,10 @@ impl Node for BridgeBackendNode { // self.config.get_env() todo!() } + + fn config(&self) -> &::Config { + todo!() + } } #[async_trait] @@ -224,7 +228,6 @@ impl BridgeBackendNodeCluster { pub async fn stop_all(&mut self) -> Result<()> { for node in &mut self.inner { - // RpcApi::stop(node).await?; node.stop().await?; } Ok(()) From 31dc63982ae68125b3d216181a3fcf8ff7879d55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ceyhun=20=C5=9Een?= Date: Mon, 7 Oct 2024 11:18:17 +0300 Subject: [PATCH 13/24] node: Add todo. --- src/config/bridge_backend.rs | 11 +++++++++++ src/node.rs | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/config/bridge_backend.rs b/src/config/bridge_backend.rs index 631672e..eb4ea3b 100644 --- a/src/config/bridge_backend.rs +++ b/src/config/bridge_backend.rs @@ -190,4 +190,15 @@ impl BridgeBackendConfig { env } + + #[allow(unused)] + pub fn convert_hashmap_to_vec(input: &HashMap) -> Vec<(&str, &str)> { + let mut result: Vec<(&str, &str)> = Vec::new(); + + for val in input { + result.push((val.0.as_str(), val.1.as_str())); + } + + result + } } diff --git a/src/node.rs b/src/node.rs index ed63019..33f9b2f 100644 --- a/src/node.rs +++ b/src/node.rs @@ -43,7 +43,7 @@ pub trait Config: Clone { fn dir(&self) -> &PathBuf; fn rpc_bind_host(&self) -> &str; fn rpc_bind_port(&self) -> u16; - fn env(&self) -> Vec<(&'static str, &'static str)>; + fn env(&self) -> Vec<(&'static str, &'static str)>; // TODO: Isn't HashMap more appropriate fn node_config(&self) -> Option<&Self::NodeConfig>; fn node_kind() -> NodeKind; fn rollup_config(&self) -> &RollupConfig; From 79d0f3aaa059203a200b6fea9eaa1ccf1d84fc6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ceyhun=20=C5=9Een?= Date: Mon, 7 Oct 2024 11:44:39 +0300 Subject: [PATCH 14/24] tests: Add initial bridge backend test. --- tests/bridge_backend.rs | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 tests/bridge_backend.rs diff --git a/tests/bridge_backend.rs b/tests/bridge_backend.rs new file mode 100644 index 0000000..9434a9a --- /dev/null +++ b/tests/bridge_backend.rs @@ -0,0 +1,37 @@ +use anyhow::bail; +use async_trait::async_trait; + +use citrea_e2e::{ + config::TestCaseConfig, + framework::TestFramework, + test_case::{TestCase, TestCaseRunner}, +}; + +struct BasicBridgeBackendTest; + +#[async_trait] +impl TestCase for BasicBridgeBackendTest { + fn test_config() -> TestCaseConfig { + TestCaseConfig { + with_bridge_backend: true, + with_sequencer: false, + ..Default::default() + } + } + + async fn run_test(&mut self, f: &mut TestFramework) -> citrea_e2e::Result<()> { + let Some(_da) = f.bitcoin_nodes.get(0) else { + bail!("bitcoind not running!") + }; + let Some(_bridge_backend) = f.bridge_backend_nodes.get(0) else { + bail!("Bridge backend is not running!") + }; + + Ok(()) + } +} + +#[tokio::test] +async fn basic_prover_test() -> citrea_e2e::Result<()> { + TestCaseRunner::new(BasicBridgeBackendTest).run().await +} From 61d157b0c38adb01960751a12a61d85f6ee8c8bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ceyhun=20=C5=9Een?= Date: Mon, 7 Oct 2024 11:45:30 +0300 Subject: [PATCH 15/24] test_case: Add bridge backend config. --- src/test_case.rs | 6 ++++++ src/traits.rs | 4 ++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/test_case.rs b/src/test_case.rs index aa59b50..debd493 100644 --- a/src/test_case.rs +++ b/src/test_case.rs @@ -294,6 +294,12 @@ pub trait TestCase: Send + Sync + 'static { BitcoinConfig::default() } + /// Returns the bridge backend configuration for the test. + /// Override this method to provide a custom bridge backend configuration. + fn bridge_backend_config() -> BridgeBackendConfig { + BridgeBackendConfig::default() + } + /// Returns the sequencer configuration for the test. /// Override this method to provide a custom sequencer configuration. fn sequencer_config() -> SequencerConfig { diff --git a/src/traits.rs b/src/traits.rs index 945d7fa..f8f7f10 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -20,8 +20,8 @@ pub enum SpawnOutput { Container(ContainerSpawnOutput), } -/// The Node trait defines the common interface shared between -/// BitcoinNode, Prover, Sequencer and FullNode +/// The [`NodeT`] trait defines the common interface shared between +/// BitcoinNode, Bridge Backend, Prover, Sequencer and FullNode #[async_trait] pub trait NodeT: Send { type Config: Send; From 3189d49e6879a3fd367e5f58351e003ee5d6d402 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ceyhun=20=C5=9Een?= Date: Mon, 7 Oct 2024 12:03:16 +0300 Subject: [PATCH 16/24] client: Remove extra bridge backend client. --- src/bridge_backend.rs | 11 ++++------- src/lib.rs | 2 +- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/src/bridge_backend.rs b/src/bridge_backend.rs index 59cd0a7..9a2e501 100644 --- a/src/bridge_backend.rs +++ b/src/bridge_backend.rs @@ -1,6 +1,4 @@ -use std::net::SocketAddr; use std::path::PathBuf; -use std::str::FromStr; use std::sync::Arc; use std::time::{Duration, Instant}; @@ -14,7 +12,7 @@ use super::config::BridgeBackendConfig; use super::docker::DockerEnv; use super::framework::TestContext; use super::Result; -use crate::bridge_backend_client::BridgeBackendClient; +use crate::client::Client; use crate::node::NodeKind; use crate::traits::{ContainerSpawnOutput, LogProvider, NodeT, Restart, SpawnOutput}; @@ -22,19 +20,18 @@ pub struct BridgeBackendNode { spawn_output: SpawnOutput, pub config: BridgeBackendConfig, docker_env: Arc>, - client: BridgeBackendClient, + client: Client, } impl BridgeBackendNode { pub async fn new(config: &BridgeBackendConfig, docker: Arc>) -> Result { let spawn_output = Self::spawn(config, &docker).await?; - let rpc_url = SocketAddr::from_str(&(config.host.clone() + &config.port.to_string()))?; Ok(Self { spawn_output, config: config.clone(), docker_env: docker, - client: BridgeBackendClient::new(rpc_url).await?, + client: Client::new(&config.host, config.port.try_into().unwrap())?, }) } @@ -79,7 +76,7 @@ impl BridgeBackendNode { #[async_trait] impl NodeT for BridgeBackendNode { type Config = BridgeBackendConfig; - type Client = BridgeBackendClient; + type Client = Client; fn spawn(config: &Self::Config) -> Result { let env = config.get_env(); diff --git a/src/lib.rs b/src/lib.rs index 970c5bd..30d413c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,6 @@ pub mod bitcoin; pub mod bridge_backend; -mod bridge_backend_client; +// mod bridge_backend_client; mod client; pub mod config; mod docker; From 6ceb339c3f00f263f3192a85e6a73dcb551174a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ceyhun=20=C5=9Een?= Date: Mon, 7 Oct 2024 12:47:40 +0300 Subject: [PATCH 17/24] config: Add extra layer for bridge backend. --- src/bridge_backend.rs | 3 +- src/config/bridge_backend.rs | 123 ++++++++++++++++++++--------------- src/config/docker.rs | 7 +- 3 files changed, 74 insertions(+), 59 deletions(-) diff --git a/src/bridge_backend.rs b/src/bridge_backend.rs index 9a2e501..30994a0 100644 --- a/src/bridge_backend.rs +++ b/src/bridge_backend.rs @@ -31,11 +31,10 @@ impl BridgeBackendNode { spawn_output, config: config.clone(), docker_env: docker, - client: Client::new(&config.host, config.port.try_into().unwrap())?, + client: Client::new(&config.client.host, config.client.port.try_into().unwrap())?, }) } - // Switch this over to Node signature once we add support for docker to citrea nodes async fn spawn( config: &BridgeBackendConfig, docker: &Arc>, diff --git a/src/config/bridge_backend.rs b/src/config/bridge_backend.rs index eb4ea3b..88c695c 100644 --- a/src/config/bridge_backend.rs +++ b/src/config/bridge_backend.rs @@ -1,7 +1,8 @@ use std::collections::HashMap; +/// Real config values for bridge backend itself. #[derive(Debug, Clone)] -pub struct BridgeBackendConfig { +pub struct BridgeBackendClient { // Server pub host: String, pub port: u32, @@ -42,8 +43,12 @@ pub struct BridgeBackendConfig { pub operator_urls: String, pub verifier_urls: String, pub aggregator_url: String, +} + +#[derive(Debug, Clone)] +pub struct BridgeBackendConfig { + pub client: BridgeBackendClient, - // Non bridge backend. pub docker_image: Option, pub env: Vec<(&'static str, &'static str)>, pub idx: usize, @@ -52,38 +57,40 @@ pub struct BridgeBackendConfig { impl Default for BridgeBackendConfig { fn default() -> Self { Self { - host: "localhost".to_string(), - port: 8080, + client: BridgeBackendClient { + host: "localhost".to_string(), + port: 8080, - pghost: "localhost".to_string(), - pgport: 5432, - pguser: "citrea".to_string(), - pgpassword: "".to_string(), - pgdatabase: "citrea_bridge".to_string(), + pghost: "localhost".to_string(), + pgport: 5432, + pguser: "citrea".to_string(), + pgpassword: "".to_string(), + pgdatabase: "citrea_bridge".to_string(), - redis_url: "redis://localhost:6379".to_string(), + redis_url: "redis://localhost:6379".to_string(), - user_takes_after: "c800".to_string(), - verifier_pks: "7c4803421956db53eed29ee45bddbe60d16e66560f918a94270ea5272b2b4e90".to_string(), - bridge_amount_btc: 1, - confirmation_threshold: 6, + user_takes_after: "c800".to_string(), + verifier_pks: "7c4803421956db53eed29ee45bddbe60d16e66560f918a94270ea5272b2b4e90".to_string(), + bridge_amount_btc: 1, + confirmation_threshold: 6, - bitcoin_rest_endpoint: "".to_string(), - bitcoin_rest_auth_header: "".to_string(), + bitcoin_rest_endpoint: "".to_string(), + bitcoin_rest_auth_header: "".to_string(), - citrea_rest_endpoint: "".to_string(), - bitcoin_lightclient_contract_address: "0x3100000000000000000000000000000000000001".to_string(), - bridge_contract_address: "".to_string(), - declare_withdraw_filler_private_key: "".to_string(), + citrea_rest_endpoint: "".to_string(), + bitcoin_lightclient_contract_address: "0x3100000000000000000000000000000000000001".to_string(), + bridge_contract_address: "".to_string(), + declare_withdraw_filler_private_key: "".to_string(), - faucet_private_key: "".to_string(), - faucet_amount: "".to_string(), - faucet_amount_limit: "".to_string(), - faucet_count_limit: "".to_string(), + faucet_private_key: "".to_string(), + faucet_amount: "".to_string(), + faucet_amount_limit: "".to_string(), + faucet_count_limit: "".to_string(), - operator_urls: "http://localhost:17007,http://localhost:17008,http://localhost:17009".to_string(), - verifier_urls: "http://localhost:17000,http://localhost:17001,http://localhost:17002,http://localhost:17003,http://localhost:17004,http://localhost:17005,http://localhost:17006".to_string(), - aggregator_url: "http://localhost:17010".to_string(), + operator_urls: "http://localhost:17007,http://localhost:17008,http://localhost:17009".to_string(), + verifier_urls: "http://localhost:17000,http://localhost:17001,http://localhost:17002,http://localhost:17003,http://localhost:17004,http://localhost:17005,http://localhost:17006".to_string(), + aggregator_url: "http://localhost:17010".to_string(), + }, docker_image: None, env: Vec::new(), @@ -101,80 +108,92 @@ impl BridgeBackendConfig { env.insert("NODE_ENV".to_string(), "development".to_string()); env.insert("NODE_DEBUG".to_string(), "jsonrpc,db".to_string()); - env.insert("HOST".to_string(), self.host.clone()); - env.insert("PORT".to_string(), self.port.to_string()); + env.insert("HOST".to_string(), self.client.host.clone()); + env.insert("PORT".to_string(), self.client.port.to_string()); env.insert( "CORS_ORIGIN_PATTERN".to_string(), "^http://localhost ".to_string(), ); - env.insert("PGHOST".to_string(), self.pghost.clone()); - env.insert("PGPORT".to_string(), self.pgport.to_string()); - env.insert("PGUSER".to_string(), self.pguser.clone()); - env.insert("PGPASSWORD".to_string(), self.pgpassword.clone()); - env.insert("PGDATABASE".to_string(), self.pgdatabase.clone()); + env.insert("PGHOST".to_string(), self.client.pghost.clone()); + env.insert("PGPORT".to_string(), self.client.pgport.to_string()); + env.insert("PGUSER".to_string(), self.client.pguser.clone()); + env.insert("PGPASSWORD".to_string(), self.client.pgpassword.clone()); + env.insert("PGDATABASE".to_string(), self.client.pgdatabase.clone()); env.insert("PGSSLMODE".to_string(), "prefer".to_string()); - env.insert("REDIS_URL".to_string(), self.redis_url.clone()); + env.insert("REDIS_URL".to_string(), self.client.redis_url.clone()); env.insert("NETWORK".to_string(), "regtest".to_string()); env.insert( "USER_TAKES_AFTER".to_string(), - self.user_takes_after.clone(), + self.client.user_takes_after.clone(), ); - env.insert("VERIFIER_PKS".to_string(), self.verifier_pks.clone()); + env.insert("VERIFIER_PKS".to_string(), self.client.verifier_pks.clone()); env.insert( "BRIDGE_AMOUNT_BTC".to_string(), - self.bridge_amount_btc.to_string(), + self.client.bridge_amount_btc.to_string(), ); env.insert( "CONFIRMATION_THRESHOLD".to_string(), - self.confirmation_threshold.to_string(), + self.client.confirmation_threshold.to_string(), ); env.insert( "BITCOIN_REST_ENDPOINT".to_string(), - self.bitcoin_rest_endpoint.clone(), + self.client.bitcoin_rest_endpoint.clone(), ); env.insert( "BITCOIN_REST_AUTH_HEADER".to_string(), - self.bitcoin_rest_auth_header.clone(), + self.client.bitcoin_rest_auth_header.clone(), ); env.insert( "CITREA_REST_ENDPOINT".to_string(), - self.citrea_rest_endpoint.clone(), + self.client.citrea_rest_endpoint.clone(), ); env.insert( "BITCOIN_LIGHTCLIENT_CONTRACT_ADDRESS".to_string(), - self.bitcoin_lightclient_contract_address.clone(), + self.client.bitcoin_lightclient_contract_address.clone(), ); env.insert( "BRIDGE_CONTRACT_ADDRESS".to_string(), - self.bridge_contract_address.clone(), + self.client.bridge_contract_address.clone(), ); env.insert( "DECLARE_WITHDRAW_FILLER_PRIVATE_KEY".to_string(), - self.declare_withdraw_filler_private_key.clone(), + self.client.declare_withdraw_filler_private_key.clone(), ); env.insert( "FAUCET_PRIVATE_KEY".to_string(), - self.faucet_private_key.clone(), + self.client.faucet_private_key.clone(), + ); + env.insert( + "FAUCET_AMOUNT".to_string(), + self.client.faucet_amount.clone(), ); - env.insert("FAUCET_AMOUNT".to_string(), self.faucet_amount.clone()); env.insert( "FAUCET_AMOUNT_LIMIT".to_string(), - self.faucet_amount_limit.clone(), + self.client.faucet_amount_limit.clone(), ); env.insert( "FAUCET_COUNT_LIMIT".to_string(), - self.faucet_count_limit.clone(), + self.client.faucet_count_limit.clone(), ); - env.insert("OPERATOR_URLS".to_string(), self.operator_urls.clone()); - env.insert("VERIFIER_URLS".to_string(), self.verifier_urls.clone()); - env.insert("AGGREGATOR_URL".to_string(), self.aggregator_url.clone()); + env.insert( + "OPERATOR_URLS".to_string(), + self.client.operator_urls.clone(), + ); + env.insert( + "VERIFIER_URLS".to_string(), + self.client.verifier_urls.clone(), + ); + env.insert( + "AGGREGATOR_URL".to_string(), + self.client.aggregator_url.clone(), + ); env.insert("TELEGRAM_BOT_API_KEY".to_string(), "".to_string()); env.insert("TELEGRAM_CHAT_ID".to_string(), "".to_string()); diff --git a/src/config/docker.rs b/src/config/docker.rs index ea543ab..de0b92f 100644 --- a/src/config/docker.rs +++ b/src/config/docker.rs @@ -48,11 +48,8 @@ impl From<&BitcoinConfig> for DockerConfig { impl From<&BridgeBackendConfig> for DockerConfig { fn from(v: &BridgeBackendConfig) -> Self { Self { - ports: vec![v.port.try_into().unwrap()], - image: v - .docker_image - .clone() - .unwrap_or_else(|| "bitcoin/bitcoin:latest".to_string()), + ports: vec![v.client.port.try_into().unwrap()], + image: v.docker_image.clone().unwrap(), cmd: vec![], log_path: PathBuf::new(), volume: VolumeConfig { From 15493d467e3646207272b18c15007f5be51f8e7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ceyhun=20=C5=9Een?= Date: Mon, 7 Oct 2024 12:49:18 +0300 Subject: [PATCH 18/24] config: Delete unnecessary configs. --- src/config/bridge_backend.rs | 4 ---- src/config/docker.rs | 2 +- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/src/config/bridge_backend.rs b/src/config/bridge_backend.rs index 88c695c..224aa7d 100644 --- a/src/config/bridge_backend.rs +++ b/src/config/bridge_backend.rs @@ -50,8 +50,6 @@ pub struct BridgeBackendConfig { pub client: BridgeBackendClient, pub docker_image: Option, - pub env: Vec<(&'static str, &'static str)>, - pub idx: usize, } impl Default for BridgeBackendConfig { @@ -93,8 +91,6 @@ impl Default for BridgeBackendConfig { }, docker_image: None, - env: Vec::new(), - idx: 0, } } } diff --git a/src/config/docker.rs b/src/config/docker.rs index de0b92f..ca0e684 100644 --- a/src/config/docker.rs +++ b/src/config/docker.rs @@ -53,7 +53,7 @@ impl From<&BridgeBackendConfig> for DockerConfig { cmd: vec![], log_path: PathBuf::new(), volume: VolumeConfig { - name: format!("bridge-backend-{}", v.idx), + name: format!("bridge-backend"), target: "/home/bridge_backend/.bridge_backend".to_string(), }, } From 084fca677daa70c3b95d2c15f3f47291963f62ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ceyhun=20=C5=9Een?= Date: Mon, 7 Oct 2024 15:22:55 +0300 Subject: [PATCH 19/24] config: Seperation of bridge backend config and test config. --- src/bridge_backend.rs | 38 +---------------- src/config/bridge_backend.rs | 80 +++++++++++++++++++----------------- src/config/docker.rs | 2 +- src/config/mod.rs | 2 +- src/config/test.rs | 2 +- tests/bridge_backend.rs | 2 +- 6 files changed, 48 insertions(+), 78 deletions(-) diff --git a/src/bridge_backend.rs b/src/bridge_backend.rs index 30994a0..1ed7b56 100644 --- a/src/bridge_backend.rs +++ b/src/bridge_backend.rs @@ -10,7 +10,6 @@ use tokio::time::sleep; use super::config::BridgeBackendConfig; use super::docker::DockerEnv; -use super::framework::TestContext; use super::Result; use crate::client::Client; use crate::node::NodeKind; @@ -153,7 +152,7 @@ impl NodeT for BridgeBackendNode { } fn config(&self) -> &::Config { - todo!() + &self.config } } @@ -203,38 +202,3 @@ impl LogProvider for BridgeBackendNode { todo!() } } - -pub struct BridgeBackendNodeCluster { - inner: Vec, -} - -impl BridgeBackendNodeCluster { - pub async fn new(ctx: &TestContext) -> Result { - let n_nodes = ctx.config.test_case.n_nodes; - let mut cluster = Self { - inner: Vec::with_capacity(n_nodes), - }; - for config in ctx.config.bridge_backend.iter() { - let node = BridgeBackendNode::new(config, Arc::clone(&ctx.docker)).await?; - cluster.inner.push(node) - } - - Ok(cluster) - } - - pub async fn stop_all(&mut self) -> Result<()> { - for node in &mut self.inner { - node.stop().await?; - } - Ok(()) - } - - pub fn get(&self, index: usize) -> Option<&BridgeBackendNode> { - self.inner.get(index) - } - - #[allow(unused)] - pub fn get_mut(&mut self, index: usize) -> Option<&mut BridgeBackendNode> { - self.inner.get_mut(index) - } -} diff --git a/src/config/bridge_backend.rs b/src/config/bridge_backend.rs index 224aa7d..f3ae67a 100644 --- a/src/config/bridge_backend.rs +++ b/src/config/bridge_backend.rs @@ -1,4 +1,4 @@ -use std::collections::HashMap; +use std::{collections::HashMap, path::PathBuf}; /// Real config values for bridge backend itself. #[derive(Debug, Clone)] @@ -45,52 +45,58 @@ pub struct BridgeBackendClient { pub aggregator_url: String, } +impl Default for BridgeBackendClient { + fn default() -> Self { + Self { + host: "localhost".to_string(), + port: 8080, + + pghost: "localhost".to_string(), + pgport: 5432, + pguser: "citrea".to_string(), + pgpassword: "".to_string(), + pgdatabase: "citrea_bridge".to_string(), + + redis_url: "redis://localhost:6379".to_string(), + + user_takes_after: "c800".to_string(), + verifier_pks: "7c4803421956db53eed29ee45bddbe60d16e66560f918a94270ea5272b2b4e90".to_string(), + bridge_amount_btc: 1, + confirmation_threshold: 6, + + bitcoin_rest_endpoint: "".to_string(), + bitcoin_rest_auth_header: "".to_string(), + + citrea_rest_endpoint: "".to_string(), + bitcoin_lightclient_contract_address: "0x3100000000000000000000000000000000000001".to_string(), + bridge_contract_address: "".to_string(), + declare_withdraw_filler_private_key: "".to_string(), + + faucet_private_key: "".to_string(), + faucet_amount: "".to_string(), + faucet_amount_limit: "".to_string(), + faucet_count_limit: "".to_string(), + + operator_urls: "http://localhost:17007,http://localhost:17008,http://localhost:17009".to_string(), + verifier_urls: "http://localhost:17000,http://localhost:17001,http://localhost:17002,http://localhost:17003,http://localhost:17004,http://localhost:17005,http://localhost:17006".to_string(), + aggregator_url: "http://localhost:17010".to_string(), + } + } +} + #[derive(Debug, Clone)] pub struct BridgeBackendConfig { pub client: BridgeBackendClient, - pub docker_image: Option, + pub data_dir: PathBuf, } impl Default for BridgeBackendConfig { fn default() -> Self { Self { - client: BridgeBackendClient { - host: "localhost".to_string(), - port: 8080, - - pghost: "localhost".to_string(), - pgport: 5432, - pguser: "citrea".to_string(), - pgpassword: "".to_string(), - pgdatabase: "citrea_bridge".to_string(), - - redis_url: "redis://localhost:6379".to_string(), - - user_takes_after: "c800".to_string(), - verifier_pks: "7c4803421956db53eed29ee45bddbe60d16e66560f918a94270ea5272b2b4e90".to_string(), - bridge_amount_btc: 1, - confirmation_threshold: 6, - - bitcoin_rest_endpoint: "".to_string(), - bitcoin_rest_auth_header: "".to_string(), - - citrea_rest_endpoint: "".to_string(), - bitcoin_lightclient_contract_address: "0x3100000000000000000000000000000000000001".to_string(), - bridge_contract_address: "".to_string(), - declare_withdraw_filler_private_key: "".to_string(), - - faucet_private_key: "".to_string(), - faucet_amount: "".to_string(), - faucet_amount_limit: "".to_string(), - faucet_count_limit: "".to_string(), - - operator_urls: "http://localhost:17007,http://localhost:17008,http://localhost:17009".to_string(), - verifier_urls: "http://localhost:17000,http://localhost:17001,http://localhost:17002,http://localhost:17003,http://localhost:17004,http://localhost:17005,http://localhost:17006".to_string(), - aggregator_url: "http://localhost:17010".to_string(), - }, - + client: BridgeBackendClient::default(), docker_image: None, + data_dir: PathBuf::from("bridge_backend"), } } } diff --git a/src/config/docker.rs b/src/config/docker.rs index ca0e684..c2d8980 100644 --- a/src/config/docker.rs +++ b/src/config/docker.rs @@ -51,7 +51,7 @@ impl From<&BridgeBackendConfig> for DockerConfig { ports: vec![v.client.port.try_into().unwrap()], image: v.docker_image.clone().unwrap(), cmd: vec![], - log_path: PathBuf::new(), + log_path: v.data_dir.join("stdout"), volume: VolumeConfig { name: format!("bridge-backend"), target: "/home/bridge_backend/.bridge_backend".to_string(), diff --git a/src/config/mod.rs b/src/config/mod.rs index 7b76a24..2cf61ba 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -10,7 +10,7 @@ use std::path::PathBuf; pub use bitcoin::BitcoinConfig; pub use bitcoin_da::service::BitcoinServiceConfig; -pub use bridge_backend::BridgeBackendConfig; +pub use bridge_backend::{BridgeBackendClient, BridgeBackendConfig}; pub use citrea_sequencer::SequencerConfig; pub use docker::DockerConfig; pub use rollup::{default_rollup_config, RollupConfig}; diff --git a/src/config/test.rs b/src/config/test.rs index ee223a6..1f3073b 100644 --- a/src/config/test.rs +++ b/src/config/test.rs @@ -7,7 +7,7 @@ use super::{ pub struct TestConfig { pub test_case: TestCaseConfig, pub bitcoin: Vec, - pub bridge_backend: Vec, + pub bridge_backend: BridgeBackendConfig, pub sequencer: FullSequencerConfig, pub prover: FullProverConfig, pub full_node: FullFullNodeConfig, diff --git a/tests/bridge_backend.rs b/tests/bridge_backend.rs index 9434a9a..a3c77f1 100644 --- a/tests/bridge_backend.rs +++ b/tests/bridge_backend.rs @@ -23,7 +23,7 @@ impl TestCase for BasicBridgeBackendTest { let Some(_da) = f.bitcoin_nodes.get(0) else { bail!("bitcoind not running!") }; - let Some(_bridge_backend) = f.bridge_backend_nodes.get(0) else { + let Some(_bridge_backend) = &f.bridge_backend else { bail!("Bridge backend is not running!") }; From abfc06c906f815f280e64fcf97a6b1bdacd41ee0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ceyhun=20=C5=9Een?= Date: Mon, 7 Oct 2024 15:38:17 +0300 Subject: [PATCH 20/24] delete bridge backend client --- src/bridge_backend_client.rs | 34 ---------------------------------- src/framework.rs | 8 +++----- src/lib.rs | 1 - 3 files changed, 3 insertions(+), 40 deletions(-) delete mode 100644 src/bridge_backend_client.rs diff --git a/src/bridge_backend_client.rs b/src/bridge_backend_client.rs deleted file mode 100644 index 643855c..0000000 --- a/src/bridge_backend_client.rs +++ /dev/null @@ -1,34 +0,0 @@ -use std::{net::SocketAddr, time::Duration}; - -use jsonrpsee::{ - http_client::{HttpClient, HttpClientBuilder}, - ws_client::{PingConfig, WsClient, WsClientBuilder}, -}; - -pub struct BridgeBackendClient { - _http_client: HttpClient, - _ws_client: WsClient, - pub rpc_addr: SocketAddr, -} - -impl BridgeBackendClient { - pub async fn new(rpc_addr: SocketAddr) -> anyhow::Result { - let http_host = format!("http://localhost:{}", rpc_addr.port()); - let ws_host = format!("ws://localhost:{}", rpc_addr.port()); - - let _http_client = HttpClientBuilder::default() - .request_timeout(Duration::from_secs(120)) - .build(http_host)?; - - let _ws_client = WsClientBuilder::default() - .enable_ws_ping(PingConfig::default().inactive_limit(Duration::from_secs(10))) - .build(ws_host) - .await?; - - Ok(Self { - _ws_client, - _http_client, - rpc_addr, - }) - } -} diff --git a/src/framework.rs b/src/framework.rs index ac2e735..1940557 100644 --- a/src/framework.rs +++ b/src/framework.rs @@ -12,7 +12,7 @@ use super::{ traits::{LogProvider, LogProviderErased, NodeT}, Result, }; -use crate::{bridge_backend::BridgeBackendNodeCluster, prover::Prover, utils::tail_file}; +use crate::{bridge_backend::BridgeBackendNode, prover::Prover, utils::tail_file}; pub struct TestContext { pub config: TestConfig, @@ -36,7 +36,7 @@ impl TestContext { pub struct TestFramework { ctx: TestContext, pub bitcoin_nodes: BitcoinNodeCluster, - pub bridge_backend_nodes: BridgeBackendNodeCluster, + pub bridge_backend: Option, pub sequencer: Option, pub prover: Option, pub full_node: Option, @@ -62,12 +62,10 @@ impl TestFramework { let ctx = TestContext::new(config).await; let bitcoin_nodes = BitcoinNodeCluster::new(&ctx).await?; - let bridge_backend_nodes = BridgeBackendNodeCluster::new(&ctx).await?; - // tokio::time::sleep(std::time::Duration::from_secs(30)).await; Ok(Self { bitcoin_nodes, - bridge_backend_nodes, + bridge_backend: None, sequencer: None, prover: None, full_node: None, diff --git a/src/lib.rs b/src/lib.rs index 30d413c..b4ec8aa 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,5 @@ pub mod bitcoin; pub mod bridge_backend; -// mod bridge_backend_client; mod client; pub mod config; mod docker; From 2be6799df6069495fc66a8dc4378dc9e930c82b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ceyhun=20=C5=9Een?= Date: Mon, 7 Oct 2024 15:52:29 +0300 Subject: [PATCH 21/24] docker: Delete backend. --- src/docker.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/docker.rs b/src/docker.rs index 44839db..f30eaec 100644 --- a/src/docker.rs +++ b/src/docker.rs @@ -51,7 +51,7 @@ impl DockerEnv { test_case_id: &str, n_nodes: usize, ) -> Result> { - let volume_configs = vec![("bitcoin", n_nodes), ("bridge-backend", n_nodes)]; + let volume_configs = vec![("bitcoin", n_nodes)]; let mut volumes = HashSet::new(); for (name, n) in volume_configs { From d6d7855f37eed7d5928fecd3175ab230d69f29a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ceyhun=20=C5=9Een?= Date: Mon, 7 Oct 2024 15:53:24 +0300 Subject: [PATCH 22/24] test_case: Remove multiple backend nodes. --- src/test_case.rs | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/src/test_case.rs b/src/test_case.rs index debd493..9a0042d 100644 --- a/src/test_case.rs +++ b/src/test_case.rs @@ -111,6 +111,7 @@ impl TestCaseRunner { let test_case = T::test_config(); let env = T::test_env(); let bitcoin = T::bitcoin_config(); + let bridge_backend = T::bridge_backend_config(); let prover = T::prover_config(); let sequencer = T::sequencer_config(); let sequencer_rollup = default_rollup_config(); @@ -141,15 +142,6 @@ impl TestCaseRunner { }); } - let mut bridge_backend_confs = vec![]; - for i in 0..test_case.n_nodes { - let data_dir = bitcoin_dir.join(i.to_string()); - std::fs::create_dir_all(&data_dir) - .with_context(|| format!("Failed to create {} directory", data_dir.display()))?; - - bridge_backend_confs.push(BridgeBackendConfig::default()) - } - // Target first bitcoin node as DA for now let da_config: BitcoinServiceConfig = bitcoin_confs[0].clone().into(); @@ -242,6 +234,7 @@ impl TestCaseRunner { Ok(TestConfig { bitcoin: bitcoin_confs, + bridge_backend, sequencer: FullSequencerConfig { rollup: sequencer_rollup, dir: sequencer_dir, @@ -264,7 +257,6 @@ impl TestCaseRunner { env: env.full_node(), }, test_case, - bridge_backend: bridge_backend_confs, }) } } From 9af3a2b6a5accea3ad6db37ad61f3111068ea42b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ceyhun=20=C5=9Een?= Date: Mon, 7 Oct 2024 16:10:27 +0300 Subject: [PATCH 23/24] docker: Apply clippy suggestion. --- src/config/docker.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/docker.rs b/src/config/docker.rs index c2d8980..e2d89f9 100644 --- a/src/config/docker.rs +++ b/src/config/docker.rs @@ -53,7 +53,7 @@ impl From<&BridgeBackendConfig> for DockerConfig { cmd: vec![], log_path: v.data_dir.join("stdout"), volume: VolumeConfig { - name: format!("bridge-backend"), + name: "bridge-backend".to_string(), target: "/home/bridge_backend/.bridge_backend".to_string(), }, } From b29cf19390224c8fa531cce66ca76613c70d8a0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ceyhun=20=C5=9Een?= Date: Mon, 7 Oct 2024 17:05:19 +0300 Subject: [PATCH 24/24] test_case: Add bridge_backend. --- src/config/test_case.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/config/test_case.rs b/src/config/test_case.rs index 8c05749..acacb73 100644 --- a/src/config/test_case.rs +++ b/src/config/test_case.rs @@ -9,6 +9,7 @@ pub struct TestCaseEnv { pub sequencer: Vec<(&'static str, &'static str)>, pub prover: Vec<(&'static str, &'static str)>, pub bitcoin: Vec<(&'static str, &'static str)>, + pub bridge_backend: Vec<(&'static str, &'static str)>, } impl TestCaseEnv { @@ -36,6 +37,10 @@ impl TestCaseEnv { pub fn bitcoin(&self) -> Vec<(&'static str, &'static str)> { [self.test_env(), self.bitcoin.clone()].concat() } + + pub fn bridge_backend(&self) -> Vec<(&'static str, &'static str)> { + [self.test_env(), self.bridge_backend.clone()].concat() + } } #[derive(Clone)]