From fde0b12f22479a7aa12cf197874bb63b6ab66949 Mon Sep 17 00:00:00 2001 From: Will O'Beirne Date: Fri, 8 Sep 2023 14:58:37 -0500 Subject: [PATCH 1/4] add POST /channel endpoint for opening channels --- src/channel.rs | 84 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 13 ++++++++ 2 files changed, 97 insertions(+) create mode 100644 src/channel.rs diff --git a/src/channel.rs b/src/channel.rs new file mode 100644 index 0000000..080bee7 --- /dev/null +++ b/src/channel.rs @@ -0,0 +1,84 @@ +use serde::{Deserialize, Serialize}; + +use std::sync::{Arc, Mutex}; +use tonic_openssl_lnd::lnrpc::{self, channel_point}; + +use crate::{AppState, MAX_SEND_AMOUNT}; + +#[derive(Clone, Deserialize)] +pub struct ChannelRequest { + capacity: i64, + push_amount: i64, + pubkey: String, + host: String, +} + +#[derive(Clone, Serialize)] +pub struct ChannelResponse { + pub txid: String, +} + +pub async fn open_channel( + state: Arc>, + payload: ChannelRequest, +) -> anyhow::Result { + if payload.capacity > MAX_SEND_AMOUNT.try_into().unwrap() { + anyhow::bail!("max capacity is 1,000,000"); + } + if payload.push_amount < 0 { + anyhow::bail!("push_amount must be positive"); + } + if payload.push_amount > payload.capacity { + anyhow::bail!("push_amount must be less than or equal to capacity"); + } + + println!("pubkey: {:?}", payload.pubkey); + println!("capacity: {:?}", payload.capacity); + println!("push_amount: {:?}", payload.push_amount); + + let node_pubkey_result = hex::decode(payload.pubkey.clone()); + let node_pubkey = match node_pubkey_result { + Ok(pubkey) => pubkey, + Err(e) => anyhow::bail!("invalid pubkey: {}", e), + }; + + let channel_point = { + let mut lightning_client = state + .clone() + .lock() + .map_err(|_| anyhow::anyhow!("failed to get lock"))? + .lightning_client + .clone(); + + lightning_client + .connect_peer(lnrpc::ConnectPeerRequest { + addr: Some(lnrpc::LightningAddress { + pubkey: payload.pubkey.clone(), + host: payload.host, + }), + ..Default::default() + }) + .await + .ok(); + + lightning_client + .open_channel_sync(lnrpc::OpenChannelRequest { + node_pubkey, + local_funding_amount: payload.capacity, + push_sat: payload.push_amount, + ..Default::default() + }) + .await? + .into_inner() + }; + + println!("channel_point: {:?}", channel_point); + + let txid = match &channel_point.funding_txid { + Some(channel_point::FundingTxid::FundingTxidBytes(bytes)) => hex::encode(bytes.clone()), + Some(channel_point::FundingTxid::FundingTxidStr(string)) => string.clone(), + None => anyhow::bail!("failed to open channel"), + }; + + Ok(txid) +} diff --git a/src/main.rs b/src/main.rs index 39770d1..29fb9bf 100644 --- a/src/main.rs +++ b/src/main.rs @@ -16,11 +16,13 @@ use tonic_openssl_lnd::LndLightningClient; use tower_http::cors::{Any, CorsLayer}; mod bolt11; +mod channel; mod lightning; mod onchain; mod setup; use bolt11::{request_bolt11, Bolt11Request, Bolt11Response}; +use channel::{open_channel, ChannelRequest, ChannelResponse}; use lightning::{pay_lightning, LightningRequest, LightningResponse}; use onchain::{pay_onchain, OnchainRequest, OnchainResponse}; use setup::setup; @@ -57,6 +59,7 @@ async fn main() -> anyhow::Result<()> { .route("/api/onchain", post(onchain_handler)) .route("/api/lightning", post(lightning_handler)) .route("/api/bolt11", post(bolt11_handler)) + .route("/api/channel", post(channel_handler)) .with_state(state) .layer( CorsLayer::new() @@ -105,6 +108,16 @@ async fn bolt11_handler( Ok(Json(Bolt11Response { bolt11 })) } +#[axum::debug_handler] +async fn channel_handler( + State(state): State, + Json(payload): Json, +) -> Result, AppError> { + let txid = open_channel(state.clone(), payload.clone()).await?; + + Ok(Json(ChannelResponse { txid })) +} + // Make our own error that wraps `anyhow::Error`. struct AppError(anyhow::Error); From 4c34123ebdb2f18066f14e6015a8011eb092ffb0 Mon Sep 17 00:00:00 2001 From: Will O'Beirne Date: Fri, 8 Sep 2023 15:03:36 -0500 Subject: [PATCH 2/4] Add channel endpoint and basic instructions to README --- README.md | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 40e6b32..07fbf27 100644 --- a/README.md +++ b/README.md @@ -1,20 +1,34 @@ -``` +# MutinyNet Faucet API + +1. Copy `.env.sample` to `.env.local` and fill it out with bitcoind and lnd connection info +2. Run `cargo build && cargo start` + +## Endpoint examples + +```sh curl -X POST \ http://localhost:3001/api/onchain \ -H 'Content-Type: application/json' \ -d '{"sats":10000,"address":"bcrt1..."}' ``` -``` +```sh curl -X POST \ http://localhost:3001/api/lightning \ -H 'Content-Type: application/json' \ -d '{"bolt11": "..."}' ``` -``` +```sh curl -X POST \ http://localhost:3001/api/bolt11 \ -H 'Content-Type: application/json' \ -d '{"amount_sats": 1234}' -``` \ No newline at end of file +``` + +```sh +curl -X POST \ + http://localhost:3001/api/channel \ + -H 'Content-Type: application/json' \ + -d '{"capacity": 2468,"push_amount": 1234,"pubkey":"023...","host":"127.0.0.1:9735"}' +``` From a3ed557916f0352d49326f1bb24ae30d19078b13 Mon Sep 17 00:00:00 2001 From: Will O'Beirne Date: Mon, 11 Sep 2023 13:00:21 -0500 Subject: [PATCH 3/4] make host optional, clean up logs and borrowing --- src/channel.rs | 36 ++++++++++++++++-------------------- 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/src/channel.rs b/src/channel.rs index 080bee7..07cc2dc 100644 --- a/src/channel.rs +++ b/src/channel.rs @@ -10,7 +10,7 @@ pub struct ChannelRequest { capacity: i64, push_amount: i64, pubkey: String, - host: String, + host: Option, } #[derive(Clone, Serialize)] @@ -32,10 +32,6 @@ pub async fn open_channel( anyhow::bail!("push_amount must be less than or equal to capacity"); } - println!("pubkey: {:?}", payload.pubkey); - println!("capacity: {:?}", payload.capacity); - println!("push_amount: {:?}", payload.push_amount); - let node_pubkey_result = hex::decode(payload.pubkey.clone()); let node_pubkey = match node_pubkey_result { Ok(pubkey) => pubkey, @@ -50,16 +46,18 @@ pub async fn open_channel( .lightning_client .clone(); - lightning_client - .connect_peer(lnrpc::ConnectPeerRequest { - addr: Some(lnrpc::LightningAddress { - pubkey: payload.pubkey.clone(), - host: payload.host, - }), - ..Default::default() - }) - .await - .ok(); + if let Some(host) = payload.host { + lightning_client + .connect_peer(lnrpc::ConnectPeerRequest { + addr: Some(lnrpc::LightningAddress { + pubkey: payload.pubkey.clone(), + host, + }), + ..Default::default() + }) + .await + .ok(); + } lightning_client .open_channel_sync(lnrpc::OpenChannelRequest { @@ -72,11 +70,9 @@ pub async fn open_channel( .into_inner() }; - println!("channel_point: {:?}", channel_point); - - let txid = match &channel_point.funding_txid { - Some(channel_point::FundingTxid::FundingTxidBytes(bytes)) => hex::encode(bytes.clone()), - Some(channel_point::FundingTxid::FundingTxidStr(string)) => string.clone(), + let txid = match channel_point.funding_txid { + Some(channel_point::FundingTxid::FundingTxidBytes(bytes)) => hex::encode(bytes), + Some(channel_point::FundingTxid::FundingTxidStr(string)) => string, None => anyhow::bail!("failed to open channel"), }; From ae1de6d267b78cd354555910b5df15bbde820ed9 Mon Sep 17 00:00:00 2001 From: Will O'Beirne Date: Mon, 11 Sep 2023 15:01:18 -0500 Subject: [PATCH 4/4] fix max amount strings --- src/channel.rs | 2 +- src/onchain.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/channel.rs b/src/channel.rs index 07cc2dc..63351cd 100644 --- a/src/channel.rs +++ b/src/channel.rs @@ -23,7 +23,7 @@ pub async fn open_channel( payload: ChannelRequest, ) -> anyhow::Result { if payload.capacity > MAX_SEND_AMOUNT.try_into().unwrap() { - anyhow::bail!("max capacity is 1,000,000"); + anyhow::bail!("max capacity is 10,000,000"); } if payload.push_amount < 0 { anyhow::bail!("push_amount must be positive"); diff --git a/src/onchain.rs b/src/onchain.rs index 76f9402..17522c8 100644 --- a/src/onchain.rs +++ b/src/onchain.rs @@ -24,7 +24,7 @@ pub async fn pay_onchain( payload: OnchainRequest, ) -> anyhow::Result { if payload.sats > MAX_SEND_AMOUNT { - anyhow::bail!("max amount is 1,000,000"); + anyhow::bail!("max amount is 10,000,000"); } let txid = {