Skip to content
This repository has been archived by the owner on Apr 26, 2024. It is now read-only.

Commit

Permalink
feat: very close on multimint in callback, dont like how it's drillin…
Browse files Browse the repository at this point in the history
…g down from state so much
  • Loading branch information
Kodylow committed Jan 22, 2024
1 parent 5df9a58 commit 6432319
Show file tree
Hide file tree
Showing 6 changed files with 82 additions and 59 deletions.
81 changes: 35 additions & 46 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
use std::collections::HashMap;
use std::str::FromStr;

use anyhow::Result;
use fedimint_core::config::FederationId;
use fedimint_ln_client::LightningClientModule;
use itertools::Itertools;
use tracing::{error, info};
Expand All @@ -10,12 +14,12 @@ mod router;
mod state;

mod utils;
use state::{load_fedimint_client, AppState};
use state::AppState;

use crate::model::app_user_relays::AppUserRelaysBmc;
use crate::model::invoice::InvoiceBmc;
use crate::router::handlers::lnurlp::callback::spawn_invoice_subscription;
use crate::{config::CONFIG, model::ModelManager, state::load_nostr_client};
use crate::config::CONFIG;

#[tokio::main]
async fn main() -> Result<()> {
Expand Down Expand Up @@ -44,55 +48,40 @@ async fn main() -> Result<()> {
/// Starts subscription for all pending invoices from previous run
async fn handle_pending_invoices(state: AppState) -> Result<()> {
let invoices = InvoiceBmc::get_pending(&state.mm).await?;
for (id, client) in state.fm.clients.lock().await.into_iter() {
if let ln = client.get_first_module::<LightningClientModule>() {
for invoice in invoices.iter() {
if let Ok(subscription) = ln
.subscribe_ln_receive(invoice.op_id.parse().expect("invalid op_id"))
.await
{
let nip05relays = AppUserRelaysBmc::get_by_id(&state.mm, invoice.app_user_id)
.await?;
spawn_invoice_subscription(
state.clone(),
invoice.id,
nip05relays,
subscription,
)
.await;
}
}
}
}
let ln = state.fm.get_first_module::<LightningClientModule>();

// sort invoices by user for efficiency
let invoices_by_user = invoices
// Group invoices by federation_id
let invoices_by_federation = invoices
.into_iter()
.sorted_by_key(|i| i.app_user_id)
.group_by(|i| i.app_user_id)
.group_by(|i| i.federation_id.clone())
.into_iter()
.map(|(user, invs)| (user, invs.collect::<Vec<_>>()))
.collect::<Vec<_>>();

for (user, invoices) in invoices_by_user {
let nip05relays = AppUserRelaysBmc::get_by_id(&state.mm, user).await?;
for invoice in invoices {
// create subscription to operation if it exists
if let Ok(subscription) = ln
.subscribe_ln_receive(invoice.op_id.parse().expect("invalid op_id"))
.await
{
spawn_invoice_subscription(
state.clone(),
invoice.id,
nip05relays.clone(),
subscription,
)
.await;
.map(|(federation_id, invs)| (federation_id, invs.collect::<Vec<_>>()))
.collect::<HashMap<_, _>>();

for (federation_id, invoices) in invoices_by_federation {
// Get the corresponding multimint client for the federation_id
if let Ok(federation_id) = FederationId::from_str(&federation_id) {
if let Some(client) = state.fm.clients.lock().await.get(&federation_id) {
let ln = client.get_first_module::<LightningClientModule>();
for invoice in invoices {
// Create subscription to operation if it exists
if let Ok(subscription) = ln
.subscribe_ln_receive(invoice.op_id.parse().expect("invalid op_id"))
.await
{
let nip05relays = AppUserRelaysBmc::get_by_id(&state.mm, invoice.app_user_id)
.await?;
spawn_invoice_subscription(
state.clone(),
invoice.id,
nip05relays.clone(),
subscription,
)
.await;
}
}
}
}
}

Ok(())
Ok(())
}
2 changes: 2 additions & 0 deletions src/model/app_user_relays.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ impl AppUserRelaysBmc {
pubkey: user.pubkey,
name: user.name,
dm_type: user.dm_type,
federation_id: user.federation_id,
relays: relays
.into_iter()
.map(|relay| relay.relay.to_string())
Expand Down Expand Up @@ -130,6 +131,7 @@ impl AppUserRelaysBmc {
pubkey: user.pubkey,
name: user.name,
dm_type: user.dm_type,
federation_id: user.federation_id,
relays: relays
.into_iter()
.map(|relay| relay.relay.to_string())
Expand Down
2 changes: 2 additions & 0 deletions src/model/invoice.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use sqlx::FromRow;
#[derive(Debug, Clone, Fields, FromRow, Serialize)]
pub struct Invoice {
pub id: i32,
pub federation_id: String,
pub op_id: String,
pub app_user_id: i32,
pub bolt11: String,
Expand All @@ -22,6 +23,7 @@ pub struct Invoice {
#[derive(Debug, Clone, Fields, FromRow, Serialize)]
pub struct InvoiceForCreate {
pub op_id: String,
pub federation_id: String,
pub app_user_id: i32,
pub bolt11: String,
pub amount: i64,
Expand Down
44 changes: 31 additions & 13 deletions src/router/handlers/lnurlp/callback.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
use std::{str::FromStr, time::Duration};

use nostr_sdk::Client;
use anyhow::Result;
use axum::{
extract::{Path, Query, State},
http::StatusCode,
Json,
};
use fedimint_client::oplog::UpdateStreamOrOutcome;
use fedimint_core::{core::OperationId, task::spawn, Amount};
use fedimint_client::{oplog::UpdateStreamOrOutcome, ClientArc};
use fedimint_core::{core::OperationId, task::spawn, Amount, config::FederationId};
use fedimint_ln_client::{LightningClientModule, LnReceiveState};
use fedimint_mint_client::{MintClientModule, OOBNotes};
use futures::StreamExt;
Expand All @@ -25,7 +26,7 @@ use tracing::{error, info};
use url::Url;
use xmpp::{parsers::message::MessageType, Jid};

use crate::model::invoice_state::InvoiceState;
use crate::model::{invoice_state::InvoiceState, ModelManager};
use crate::model::zap::{Zap, ZapBmc};
use crate::{
config::CONFIG,
Expand Down Expand Up @@ -105,8 +106,23 @@ pub async fn handle_callback(
}

let nip05relays = AppUserRelaysBmc::get_by(&state.mm, NameOrPubkey::Name, &username).await?;
let federation_id = FederationId::from_str(&nip05relays.federation_id).map_err(|e| {
AppError::new(
StatusCode::BAD_REQUEST,
anyhow::anyhow!("Invalid federation_id: {}", e),
)
})?;

let locked_clients = state.fm.clients.lock().await.clone();
let client = locked_clients.get(&federation_id).ok_or_else(|| {
AppError::new(
StatusCode::BAD_REQUEST,
anyhow::anyhow!("FederationId not found in multimint map"),
)
})?;

let ln = client.get_first_module::<LightningClientModule>();

let ln = state.fm.get_first_module::<LightningClientModule>();
let (op_id, pr) = ln
.create_bolt11_invoice(
Amount {
Expand All @@ -123,6 +139,7 @@ pub async fn handle_callback(
&state.mm,
InvoiceForCreate {
op_id: op_id.to_string(),
federation_id: nip05relays.federation_id.clone(),
app_user_id: nip05relays.app_user_id,
amount: params.amount as i64,
bolt11: pr.to_string(),
Expand Down Expand Up @@ -202,44 +219,45 @@ pub(crate) async fn spawn_invoice_subscription(
}

async fn notify_user(
state: &AppState,
client: &ClientArc,
nostr: &Client,
mm: &ModelManager,
id: i32,
amount: u64,
app_user_relays: AppUserRelays,
) -> Result<(), Box<dyn std::error::Error>> {
let mint = state.fm.get_first_module::<MintClientModule>();
let mint = client.get_first_module::<MintClientModule>();
let (operation_id, notes) = mint
.spend_notes(Amount::from_msats(amount), Duration::from_secs(604800), ())
.await?;
match app_user_relays.dm_type.as_str() {
"nostr" => send_nostr_dm(state, &app_user_relays, operation_id, amount, notes).await,
"nostr" => send_nostr_dm(nostr, &app_user_relays, operation_id, amount, notes).await,
"xmpp" => send_xmpp_msg(&app_user_relays, operation_id, amount, notes).await,
_ => Err(anyhow::anyhow!("Unsupported dm_type")),
}?;

// Send zap if needed
if let Ok(zap) = ZapBmc::get(&state.mm, id).await {
if let Ok(zap) = ZapBmc::get(&mm, id).await {
let request = Event::from_json(zap.request)?;
let event = create_zap_event(request, amount)?;

let event_id = state.nostr.send_event(event).await?;
let event_id = nostr.send_event(event).await?;
info!("Broadcasted zap {event_id}!");

ZapBmc::set_event_id(&state.mm, id, event_id).await?;
ZapBmc::set_event_id(&mm, id, event_id).await?;
}

Ok(())
}

async fn send_nostr_dm(
state: &AppState,
nostr: &Client,
app_user_relays: &AppUserRelays,
operation_id: OperationId,
amount: u64,
notes: OOBNotes,
) -> Result<()> {
let dm = state
.nostr
let dm = nostr
.send_direct_msg(
XOnlyPublicKey::from_str(&app_user_relays.pubkey).unwrap(),
json!({
Expand Down
1 change: 1 addition & 0 deletions src/router/handlers/nostr/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@ pub struct AppUserRelays {
pub pubkey: String,
pub name: String,
pub dm_type: String,
pub federation_id: String,
pub relays: Vec<String>,
}
11 changes: 11 additions & 0 deletions src/router/handlers/nostr/register.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use anyhow::anyhow;
use axum::{extract::State, http::StatusCode, Json};
use fedimint_core::config::FederationId;
use serde::Deserialize;
use tracing::info;

Expand All @@ -17,6 +18,7 @@ pub struct UserParams {
pub pubkey: String,
pub name: String,
pub dm_type: SupportedDmType,
pub federation_id: FederationId,
pub relays: Option<Vec<String>>,
}

Expand All @@ -27,6 +29,14 @@ pub async fn handle_register(
) -> Result<Json<bool>, AppError> {
info!("register called with pubkey: {:?}", params.pubkey);

// Check if the federationId is in the multimint map
if !state.fm.clients.lock().await.contains_key(&params.federation_id) {
return Err(AppError::new(
StatusCode::BAD_REQUEST,
anyhow!("FederationId not found in multimint map"),
));
}

let relays = match params.dm_type {
SupportedDmType::Nostr => params
.relays
Expand All @@ -47,6 +57,7 @@ pub async fn handle_register(

let nip05relays_c = AppUserRelaysForCreate {
pubkey: params.pubkey,
federation_id: params.federation_id.to_string(),
name: params.name,
dm_type: params.dm_type.to_string(),
relays,
Expand Down

0 comments on commit 6432319

Please sign in to comment.