diff --git a/src/api/server/convert.rs b/src/api/server/convert.rs index e89fbb99..053d23c7 100644 --- a/src/api/server/convert.rs +++ b/src/api/server/convert.rs @@ -233,7 +233,7 @@ impl From for proto::PayoutQueue { proto::payout_queue_config::Trigger::IntervalSecs(seconds.as_secs() as u32) } PayoutQueueTrigger::Manual => proto::payout_queue_config::Trigger::Manual(true), - PayoutQueueTrigger::Payjoin => proto::payout_queue_config::Trigger::Payjoin(true) + PayoutQueueTrigger::Payjoin => proto::payout_queue_config::Trigger::Payjoin(true), }; let tx_priority: proto::TxPriority = payout_queue.config.tx_priority.into(); let config = Some(proto::PayoutQueueConfig { diff --git a/src/lib.rs b/src/lib.rs index e7c95bf9..f41d51d3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -17,6 +17,7 @@ pub mod fees; mod job; pub mod ledger; mod outbox; +mod payjoin; pub mod payout; pub mod payout_queue; pub mod primitives; diff --git a/src/payjoin/app.rs b/src/payjoin/app.rs new file mode 100644 index 00000000..8862caf7 --- /dev/null +++ b/src/payjoin/app.rs @@ -0,0 +1,32 @@ +use tracing::instrument; + +use super::error::*; +use crate::{account::*, dev_constants, ledger::Ledger, primitives::bitcoin, profile::*}; + +const BOOTSTRAP_KEY_NAME: &str = "payjoin_bootstrap_key"; + +pub struct PayjoinApp { + accounts: Accounts, + profiles: Profiles, + ledger: Ledger, + pool: sqlx::PgPool, + network: bitcoin::Network, +} + +impl PayjoinApp { + pub fn new(pool: sqlx::PgPool, network: bitcoin::Network) -> Self { + Self { + accounts: Accounts::new(&pool), + profiles: Profiles::new(&pool), + ledger: Ledger::new(&pool), + pool, + network, + } + } +} + +impl PayjoinApp { + pub fn run() { + println!("run PayjoinApp"); + } +} diff --git a/src/payjoin/config.rs b/src/payjoin/config.rs new file mode 100644 index 00000000..2333ae5f --- /dev/null +++ b/src/payjoin/config.rs @@ -0,0 +1,18 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct PayjoinConfig { + #[serde(default = "default_port")] + pub listen_port: u16, +} +impl Default for PayjoinConfig { + fn default() -> Self { + Self { + listen_port: default_port(), + } + } +} + +fn default_port() -> u16 { + 8088 +} diff --git a/src/payjoin/error.rs b/src/payjoin/error.rs new file mode 100644 index 00000000..ee21f20c --- /dev/null +++ b/src/payjoin/error.rs @@ -0,0 +1,8 @@ +use thiserror::Error; + +#[allow(clippy::large_enum_variant)] +#[derive(Error, Debug)] +pub enum PayjoinApiError { + #[error("PayjoinApiError - Error")] + Error, +} diff --git a/src/payjoin/mod.rs b/src/payjoin/mod.rs new file mode 100644 index 00000000..dceafda0 --- /dev/null +++ b/src/payjoin/mod.rs @@ -0,0 +1,68 @@ +mod app; +mod config; +pub mod error; +mod server; + +pub use app::*; +pub use config::*; +pub use error::*; +pub use server::*; + +// pub async fn run_dev( +// pool: sqlx::PgPool, +// config: ApiConfig, +// app_cfg: AppConfig, +// xpub: Option<(String, String)>, +// derivation_path: Option, +// ) -> Result<(), ApplicationError> { +// use crate::{ +// dev_constants, +// payout_queue::*, +// profile::Profiles, +// xpub::{BitcoindSignerConfig, SignerConfig}, +// }; + +// let app = App::run(pool.clone(), app_cfg).await?; +// if let Some((xpub, signer_endpoint)) = xpub { +// println!("Creating dev entities"); +// let profile = Profiles::new(&pool) +// .find_by_key(dev_constants::BRIA_DEV_KEY) +// .await?; +// let (_, xpubs) = app +// .create_wpkh_wallet( +// &profile, +// dev_constants::DEV_WALLET_NAME.to_string(), +// xpub, +// derivation_path, +// ) +// .await?; +// app.set_signer_config( +// &profile, +// xpubs[0].to_string(), +// SignerConfig::Bitcoind(BitcoindSignerConfig { +// endpoint: signer_endpoint, +// rpc_user: dev_constants::DEFAULT_BITCOIND_RPC_USER.to_string(), +// rpc_password: dev_constants::DEFAULT_BITCOIND_RPC_PASSWORD.to_string(), +// }), +// ) +// .await?; +// app.create_payout_queue( +// &profile, +// dev_constants::DEV_QUEUE_NAME.to_string(), +// None, +// Some(PayoutQueueConfig { +// trigger: PayoutQueueTrigger::Payjoin, +// ..PayoutQueueConfig::default() +// }), +// ) +// .await?; +// } +// server::start(config, app).await?; +// Ok(()) +// } + +pub async fn run(pool: sqlx::PgPool, config: PayjoinConfig) -> Result<(), PayjoinApiError> { + let app = PayjoinApp::run().await?; + server::start(config, app).await?; + Ok(()) +} diff --git a/src/payjoin/server/convert.rs b/src/payjoin/server/convert.rs new file mode 100644 index 00000000..853cf36b --- /dev/null +++ b/src/payjoin/server/convert.rs @@ -0,0 +1,7 @@ +use crate::payjoin::PayjoinApiError; + +impl From for tonic::Status { + fn from(err: PayjoinApiError) -> Self { + tonic::Status::new(tonic::Code::Unknown, format!("{err}")) + } +} diff --git a/src/payjoin/server/mod.rs b/src/payjoin/server/mod.rs new file mode 100644 index 00000000..fb851d69 --- /dev/null +++ b/src/payjoin/server/mod.rs @@ -0,0 +1,41 @@ +mod convert; + +use futures::StreamExt; +use opentelemetry::propagation::{Extractor, TextMapPropagator}; +use opentelemetry_sdk::propagation::TraceContextPropagator; +use tonic::{transport::Server, Request, Response, Status}; +use tracing::instrument; +use tracing_opentelemetry::OpenTelemetrySpanExt; + +use super::{config::*, PayjoinApp}; +use crate::{ + app::{error::ApplicationError, *}, + payout_queue, + primitives::*, + profile, +}; + +pub const PROFILE_API_KEY_HEADER: &str = "x-bria-payjoin-api-key"; + +pub struct Payjoin { + app: PayjoinApp, +} + +#[tonic::async_trait] +impl PayjoinService for Payjoin {} + +pub(crate) async fn start( + server_config: PayjoinConfig, + app: PayjoinApp, +) -> Result<(), PayjoinApiError> { + let payjoin = Payjoin { app }; + println!( + "Starting payjoin server on port {}", + server_config.listen_port + ); + + Server::builder() + .serve(([0, 0, 0, 0], server_config.listen_port).into()) + .await?; + Ok(()) +}