Skip to content

Commit

Permalink
External receive
Browse files Browse the repository at this point in the history
  • Loading branch information
benthecarman committed Mar 22, 2024
1 parent ed75541 commit 266231c
Show file tree
Hide file tree
Showing 7 changed files with 73 additions and 22 deletions.
4 changes: 3 additions & 1 deletion migrations/2024-02-20-210617_user_info/up.sql
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ CREATE TABLE app_user (
name VARCHAR(255) NOT NULL UNIQUE,
unblinded_msg VARCHAR(255) NOT NULL UNIQUE,
federation_id VARCHAR(64) NOT NULL,
federation_invite_code VARCHAR(255) NOT NULL
federation_invite_code VARCHAR(255) NOT NULL,
invoice_index INTEGER NOT NULL DEFAULT 0
);

CREATE INDEX idx_app_user_unblinded_msg ON app_user (unblinded_msg);
Expand All @@ -16,6 +17,7 @@ CREATE TABLE invoice (
op_id VARCHAR(64) NOT NULL,
preimage VARCHAR(64) NOT NULL,
app_user_id INTEGER NOT NULL references app_user(id),
user_invoice_index INTEGER NOT NULL,
bolt11 VARCHAR(2048) NOT NULL,
amount BIGINT NOT NULL,
state INTEGER NOT NULL DEFAULT 0
Expand Down
6 changes: 6 additions & 0 deletions src/db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ pub(crate) trait DBConnection {
fn set_invoice_state(&self, invoice: Invoice, s: i32) -> anyhow::Result<()>;
fn get_user_by_name(&self, name: String) -> anyhow::Result<Option<AppUser>>;
fn get_user_by_id(&self, id: i32) -> anyhow::Result<Option<AppUser>>;
fn get_user_and_increment_counter(&self, name: &str) -> anyhow::Result<Option<AppUser>>;
fn insert_new_zap(&self, new_zap: NewZap) -> anyhow::Result<Zap>;
fn get_zap_by_id(&self, id: i32) -> anyhow::Result<Option<Zap>>;
fn set_zap_event_id(&self, zap: Zap, event_id: String) -> anyhow::Result<()>;
Expand Down Expand Up @@ -66,6 +67,11 @@ impl DBConnection for PostgresConnection {
AppUser::get_by_id(conn, id)
}

fn get_user_and_increment_counter(&self, name: &str) -> anyhow::Result<Option<AppUser>> {
let conn = &mut self.db.get()?;
AppUser::get_by_name_and_increment_counter(conn, name)
}

fn insert_new_invoice(&self, new_invoice: NewInvoice) -> anyhow::Result<Invoice> {
let conn = &mut self.db.get()?;
new_invoice.insert(conn)
Expand Down
36 changes: 22 additions & 14 deletions src/invoice.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,6 @@ pub(crate) async fn handle_pending_invoices(state: &State) -> Result<()> {
spawn_invoice_subscription(
state.clone(),
invoice,
client.clone(),
user.clone(),
subscription,
)
Expand All @@ -84,8 +83,7 @@ pub(crate) async fn handle_pending_invoices(state: &State) -> Result<()> {
pub(crate) async fn spawn_invoice_subscription(
state: State,
i: Invoice,
client: ClientHandleArc,
userrelays: AppUser,
user: AppUser,
subscription: UpdateStreamOrOutcome<LnReceiveState>,
) {
spawn("waiting for invoice being paid", async move {
Expand All @@ -109,16 +107,7 @@ pub(crate) async fn spawn_invoice_subscription(
}
LnReceiveState::Claimed => {
info!("Payment claimed");
match notify_user(
client,
&nostr,
&state,
i.id,
i.amount as u64,
userrelays.clone(),
)
.await
{
match notify_user(&nostr, &i, user).await {
Ok(_) => {
match state.db.set_invoice_state(i, InvoiceState::Settled as i32) {
Ok(_) => (),
Expand All @@ -140,7 +129,26 @@ pub(crate) async fn spawn_invoice_subscription(
});
}

async fn notify_user(
async fn notify_user(nostr: &Client, invoice: &Invoice, user: AppUser) -> Result<()> {
let dm = nostr
.send_direct_msg(
XOnlyPublicKey::from_str(&user.pubkey)?,
json!({
"tweak_index": invoice.user_invoice_index,
"amount": invoice.amount,
})
.to_string(),
None,
)
.await?;

info!("Sent nostr dm: {dm}");
Ok(())
}

// previous way we used to send ecash
#[allow(dead_code)]
async fn notify_user_with_ecash(
client: ClientHandleArc,
nostr: &Client,
state: &State,
Expand Down
17 changes: 10 additions & 7 deletions src/lnurlp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use anyhow::anyhow;
use fedimint_core::{config::FederationId, Amount, BitcoinHash};
use fedimint_ln_client::LightningClientModule;
use fedimint_ln_common::bitcoin::hashes::sha256;
use fedimint_ln_common::bitcoin::secp256k1::Parity;
use fedimint_ln_common::lightning_invoice::{Bolt11InvoiceDescription, Sha256};
use nostr::{Event, JsonUtil, Kind};

Expand Down Expand Up @@ -50,7 +51,7 @@ pub async fn lnurl_callback(
name: String,
params: LnurlCallbackParams,
) -> anyhow::Result<LnurlCallbackResponse> {
let user = state.db.get_user_by_name(name.clone())?;
let user = state.db.get_user_and_increment_counter(&name)?;
if user.is_none() {
return Err(anyhow!("NotFound"));
}
Expand Down Expand Up @@ -92,13 +93,15 @@ pub async fn lnurl_callback(
}
};

let invoice_index = user.invoice_index;

let (op_id, pr, preimage) = ln
.create_bolt11_invoice(
Amount {
msats: params.amount,
},
.create_bolt11_invoice_for_user_tweaked(
Amount::from_msats(params.amount),
Bolt11InvoiceDescription::Hash(&desc_hash),
None,
Some(86_400), // 1 day expiry
user.pubkey().public_key(Parity::Odd), // todo is this parity correct / easy to work with?
invoice_index as u64,
(),
None, // todo set gateway properly
)
Expand All @@ -110,6 +113,7 @@ pub async fn lnurl_callback(
op_id: op_id.to_string(),
preimage: hex::encode(preimage),
app_user_id: user.id,
user_invoice_index: invoice_index,
bolt11: pr.to_string(),
amount: params.amount as i64,
state: InvoiceState::Pending as i32,
Expand All @@ -135,7 +139,6 @@ pub async fn lnurl_callback(
spawn_invoice_subscription(
state.clone(),
created_invoice,
client,
user.clone(),
subscription,
)
Expand Down
28 changes: 28 additions & 0 deletions src/models/app_user.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use crate::models::schema::app_user;
use diesel::prelude::*;
use fedimint_ln_common::bitcoin::secp256k1::XOnlyPublicKey;
use serde::{Deserialize, Serialize};
use std::str::FromStr;

#[derive(
QueryableByName, Queryable, AsChangeset, Serialize, Deserialize, Debug, Clone, PartialEq,
Expand All @@ -14,9 +16,14 @@ pub struct AppUser {
pub unblinded_msg: String,
pub federation_id: String,
pub federation_invite_code: String,
pub invoice_index: i32,
}

impl AppUser {
pub fn pubkey(&self) -> XOnlyPublicKey {
XOnlyPublicKey::from_str(&self.pubkey).expect("invalid pubkey")
}

pub fn get_app_users(conn: &mut PgConnection) -> anyhow::Result<Vec<AppUser>> {
Ok(app_user::table.load::<Self>(conn)?)
}
Expand All @@ -35,6 +42,27 @@ impl AppUser {
.optional()?)
}

pub fn get_by_name_and_increment_counter(
conn: &mut PgConnection,
name: &str,
) -> anyhow::Result<Option<AppUser>> {
conn.transaction(|conn| {
let user = app_user::table
.filter(app_user::name.eq(name))
.first::<AppUser>(conn)
.optional()?;

// if the user exists, increment their invoice index
if let Some(user) = &user {
diesel::update(app_user::table.filter(app_user::id.eq(user.id)))
.set(app_user::invoice_index.eq(app_user::invoice_index + 1))
.execute(conn)?;
}

Ok(user)
})
}

pub fn check_available_name(conn: &mut PgConnection, name: String) -> anyhow::Result<bool> {
Ok(app_user::table
.filter(app_user::name.eq(name))
Expand Down
2 changes: 2 additions & 0 deletions src/models/invoice.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ pub struct Invoice {
pub op_id: String,
pub preimage: String,
pub app_user_id: i32,
pub user_invoice_index: i32,
pub bolt11: String,
pub amount: i64,
pub state: i32,
Expand Down Expand Up @@ -63,6 +64,7 @@ pub struct NewInvoice {
pub op_id: String,
pub preimage: String,
pub app_user_id: i32,
pub user_invoice_index: i32,
pub bolt11: String,
pub amount: i64,
pub state: i32,
Expand Down
2 changes: 2 additions & 0 deletions src/models/schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ diesel::table! {
federation_id -> Varchar,
#[max_length = 255]
federation_invite_code -> Varchar,
invoice_index -> Int4,
}
}

Expand All @@ -26,6 +27,7 @@ diesel::table! {
#[max_length = 64]
preimage -> Varchar,
app_user_id -> Int4,
user_invoice_index -> Int4,
#[max_length = 2048]
bolt11 -> Varchar,
amount -> Int8,
Expand Down

0 comments on commit 266231c

Please sign in to comment.