nvpn 4.0.96

CLI and daemon for Nostr VPN private mesh networks
Documentation
use super::*;
use nostr_vpn_core::paid_routes::PaidRouteUsage;

#[path = "paid_exit/automatic.rs"]
mod automatic;
pub(crate) use automatic::*;

pub(super) const PAID_EXIT_DAEMON_STREAM_PAYMENT_MIN_INCREMENT_MSAT: u64 = 1;
pub(super) const PAID_EXIT_DAEMON_STREAM_PAYMENT_LIMIT: usize = 4;

pub(super) fn flush_fips_paid_route_usage(
    runtime: &crate::fips_private_mesh::FipsPrivateTunnelRuntime,
    app: &AppConfig,
    config_path: &Path,
    now_unix: u64,
    active_millis_delta: u64,
) -> Result<PaidExitUsageFlush> {
    let store_path = paid_route_store_file_path(config_path);
    let mut store = load_paid_route_store(&store_path)?;
    let mut changed = false;
    let seller_admission_routing_before = if app.paid_exit.enabled {
        paid_route_seller_admission_routing_signature(
            &store.seller_admissions(&app.paid_exit, now_unix),
        )
    } else {
        Vec::new()
    };

    let mut buyer_delta = PaidRouteUsage::default();
    if let Some(seller_pubkey) = app.public_paid_exit_node_pubkey_hex() {
        let mut usage_delta = runtime.drain_paid_route_usage(&seller_pubkey)?;
        usage_delta.active_millis = usage_delta
            .active_millis
            .saturating_add(active_millis_delta);
        buyer_delta = usage_delta.clone();
        if !usage_delta.is_empty() {
            changed |= store
                .record_buyer_usage(RecordPaidRouteBuyerUsageRequest {
                    seller_pubkey,
                    usage_delta,
                    now_unix,
                })?
                .is_some_and(|result| result.changed);
        }
    }

    if app.paid_exit.enabled {
        for admission in store.seller_admissions(&app.paid_exit, now_unix) {
            let mut usage_delta = runtime.drain_paid_route_usage(&admission.buyer_pubkey)?;
            if admission.allow_routing {
                usage_delta.active_millis = usage_delta
                    .active_millis
                    .saturating_add(active_millis_delta);
            }
            if usage_delta.is_empty() {
                continue;
            }
            changed |= store
                .record_seller_usage(RecordPaidRouteSellerUsageRequest {
                    buyer_pubkey: admission.buyer_pubkey,
                    config: app.paid_exit.clone(),
                    usage_delta,
                    now_unix,
                })?
                .is_some_and(|result| result.changed);
        }
    }

    if changed {
        write_paid_route_store(&store_path, &store)?;
    }
    let seller_admission_routing_after = if changed && app.paid_exit.enabled {
        paid_route_seller_admission_routing_signature(
            &store.seller_admissions(&app.paid_exit, now_unix),
        )
    } else {
        seller_admission_routing_before.clone()
    };
    Ok(PaidExitUsageFlush {
        seller_admission_changed: seller_admission_routing_after != seller_admission_routing_before,
        buyer_delta,
    })
}

fn paid_route_seller_admission_routing_signature(
    admissions: &[nostr_vpn_core::paid_route_store::PaidRouteSellerAdmission],
) -> Vec<(String, String, bool)> {
    admissions
        .iter()
        .map(|admission| {
            (
                admission.buyer_pubkey.clone(),
                admission.session_id.clone(),
                admission.allow_routing,
            )
        })
        .collect()
}