mirror of
https://github.com/btcpayserver/btcpayserver.git
synced 2024-11-19 01:43:50 +01:00
641bdcff31
* Histograms: Add Lightning data and API endpoints Ported over from the mobile-working-branch. Adds histogram data for Lightning and exposes the wallet/lightning histogram data via the API. It also add a dashboard graph for the Lightning balance. Caveat: The Lightning histogram is calculated by using the current channel balance and going backwards through as much invoices and transactions as we have. The "start" of the LN graph data might not be accurate though. That's because we don't track (and not even have) the LN onchain data. It is calculated by using the current channel balance and going backwards through as much invoices and transactions as we have. So the historic graph data for LN is basically a best effort of trying to reconstruct it with what we have: The LN channel transactions. * More timeframes * Refactoring: Remove redundant WalletHistogram types * Remove store property from dashboard tile view models * JS error fixes
139 lines
5.6 KiB
C#
139 lines
5.6 KiB
C#
using System;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using BTCPayServer.Client;
|
|
using BTCPayServer.Client.Models;
|
|
using BTCPayServer.Configuration;
|
|
using BTCPayServer.Data;
|
|
using BTCPayServer.Lightning;
|
|
using BTCPayServer.Payments;
|
|
using BTCPayServer.Payments.Lightning;
|
|
using BTCPayServer.Security;
|
|
using BTCPayServer.Services;
|
|
using BTCPayServer.Services.Invoices;
|
|
using BTCPayServer.Services.Rates;
|
|
using BTCPayServer.Services.Stores;
|
|
using Microsoft.AspNetCore.Authorization;
|
|
using Microsoft.AspNetCore.Mvc;
|
|
using Microsoft.Extensions.Options;
|
|
using StoreData = BTCPayServer.Data.StoreData;
|
|
|
|
namespace BTCPayServer.Components.StoreLightningBalance;
|
|
|
|
public class StoreLightningBalance : ViewComponent
|
|
{
|
|
private const HistogramType DefaultType = HistogramType.Week;
|
|
|
|
private readonly StoreRepository _storeRepo;
|
|
private readonly CurrencyNameTable _currencies;
|
|
private readonly BTCPayServerOptions _btcpayServerOptions;
|
|
private readonly BTCPayNetworkProvider _networkProvider;
|
|
private readonly LightningClientFactoryService _lightningClientFactory;
|
|
private readonly IOptions<LightningNetworkOptions> _lightningNetworkOptions;
|
|
private readonly IOptions<ExternalServicesOptions> _externalServiceOptions;
|
|
private readonly IAuthorizationService _authorizationService;
|
|
private readonly PaymentMethodHandlerDictionary _handlers;
|
|
private readonly LightningHistogramService _lnHistogramService;
|
|
|
|
public StoreLightningBalance(
|
|
StoreRepository storeRepo,
|
|
CurrencyNameTable currencies,
|
|
BTCPayNetworkProvider networkProvider,
|
|
BTCPayServerOptions btcpayServerOptions,
|
|
LightningClientFactoryService lightningClientFactory,
|
|
IOptions<LightningNetworkOptions> lightningNetworkOptions,
|
|
IOptions<ExternalServicesOptions> externalServiceOptions,
|
|
IAuthorizationService authorizationService,
|
|
PaymentMethodHandlerDictionary handlers,
|
|
LightningHistogramService lnHistogramService)
|
|
{
|
|
_storeRepo = storeRepo;
|
|
_currencies = currencies;
|
|
_networkProvider = networkProvider;
|
|
_btcpayServerOptions = btcpayServerOptions;
|
|
_externalServiceOptions = externalServiceOptions;
|
|
_authorizationService = authorizationService;
|
|
_handlers = handlers;
|
|
_lightningClientFactory = lightningClientFactory;
|
|
_lightningNetworkOptions = lightningNetworkOptions;
|
|
_lnHistogramService = lnHistogramService;
|
|
}
|
|
|
|
public async Task<IViewComponentResult> InvokeAsync(StoreData store, string cryptoCode, bool initialRendering)
|
|
{
|
|
var defaultCurrency = store.GetStoreBlob().DefaultCurrency;
|
|
var vm = new StoreLightningBalanceViewModel
|
|
{
|
|
StoreId = store.Id,
|
|
CryptoCode = cryptoCode,
|
|
InitialRendering = initialRendering,
|
|
DefaultCurrency = defaultCurrency,
|
|
CurrencyData = _currencies.GetCurrencyData(defaultCurrency, true),
|
|
DataUrl = Url.Action("LightningBalanceDashboard", "UIStores", new { storeId = store.Id, cryptoCode })
|
|
};
|
|
|
|
if (vm.InitialRendering)
|
|
return View(vm);
|
|
|
|
try
|
|
{
|
|
var lightningClient = await GetLightningClient(store, vm.CryptoCode);
|
|
|
|
// balance
|
|
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(10));
|
|
var balance = await lightningClient.GetBalance(cts.Token);
|
|
vm.Balance = balance;
|
|
vm.TotalOnchain = balance.OnchainBalance != null
|
|
? (balance.OnchainBalance.Confirmed ?? 0L) + (balance.OnchainBalance.Reserved ?? 0L) +
|
|
(balance.OnchainBalance.Unconfirmed ?? 0L)
|
|
: null;
|
|
vm.TotalOffchain = balance.OffchainBalance != null
|
|
? (balance.OffchainBalance.Opening ?? 0) + (balance.OffchainBalance.Local ?? 0) +
|
|
(balance.OffchainBalance.Closing ?? 0)
|
|
: null;
|
|
|
|
// histogram
|
|
var data = await _lnHistogramService.GetHistogram(lightningClient, DefaultType, cts.Token);
|
|
if (data != null)
|
|
{
|
|
vm.Type = data.Type;
|
|
vm.Series = data.Series;
|
|
vm.Labels = data.Labels;
|
|
}
|
|
}
|
|
catch (Exception ex) when (ex is NotImplementedException or NotSupportedException)
|
|
{
|
|
// not all implementations support balance fetching
|
|
vm.ProblemDescription = "Your node does not support balance fetching.";
|
|
}
|
|
catch
|
|
{
|
|
// general error
|
|
vm.ProblemDescription = "Could not fetch Lightning balance.";
|
|
}
|
|
return View(vm);
|
|
}
|
|
|
|
private async Task<ILightningClient> GetLightningClient(StoreData store, string cryptoCode)
|
|
{
|
|
var network = _networkProvider.GetNetwork<BTCPayNetwork>(cryptoCode);
|
|
var id = PaymentTypes.LN.GetPaymentMethodId(cryptoCode);
|
|
var existing = store.GetPaymentMethodConfig<LightningPaymentMethodConfig>(id, _handlers);
|
|
if (existing == null)
|
|
return null;
|
|
|
|
if (existing.GetExternalLightningUrl() is { } connectionString)
|
|
{
|
|
return _lightningClientFactory.Create(connectionString, network);
|
|
}
|
|
if (existing.IsInternalNode && _lightningNetworkOptions.Value.InternalLightningByCryptoCode.TryGetValue(cryptoCode, out var internalLightningNode))
|
|
{
|
|
var result = await _authorizationService.AuthorizeAsync(HttpContext.User, null,
|
|
new PolicyRequirement(Policies.CanUseInternalLightningNode));
|
|
return result.Succeeded ? internalLightningNode : null;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
}
|