mirror of
https://github.com/aljazceru/BTCPayServerPlugins.git
synced 2026-01-31 13:34:52 +01:00
Optimize zappert
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
@@ -11,6 +12,7 @@ using BTCPayServer.Payments;
|
||||
using BTCPayServer.Services.Invoices;
|
||||
using LNURL;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Caching.Memory;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Newtonsoft.Json;
|
||||
@@ -23,7 +25,7 @@ namespace BTCPayServer.Plugins.NIP05
|
||||
{
|
||||
public override IBTCPayServerPlugin.PluginDependency[] Dependencies { get; } =
|
||||
{
|
||||
new() {Identifier = nameof(BTCPayServer), Condition = ">=1.7.7"}
|
||||
new() {Identifier = nameof(BTCPayServer), Condition = ">=1.9.0"}
|
||||
};
|
||||
|
||||
public override void Execute(IServiceCollection applicationBuilder)
|
||||
@@ -35,26 +37,95 @@ namespace BTCPayServer.Plugins.NIP05
|
||||
applicationBuilder.AddHostedService<Zapper>();
|
||||
base.Execute(applicationBuilder);
|
||||
}
|
||||
|
||||
public static string GetZapRequestCacheKey(string invoiceid)
|
||||
{
|
||||
return nameof(Nip05Plugin)+ invoiceid;
|
||||
}
|
||||
}
|
||||
|
||||
public class Zapper : IHostedService
|
||||
{
|
||||
record PendingZapEvent(string[] relays, NostrEvent nostrEvent);
|
||||
|
||||
private readonly EventAggregator _eventAggregator;
|
||||
private readonly Nip5Controller _nip5Controller;
|
||||
private readonly IMemoryCache _memoryCache;
|
||||
private IEventAggregatorSubscription _subscription;
|
||||
private ConcurrentBag<PendingZapEvent> _pendingZapEvents = new();
|
||||
|
||||
public Zapper(EventAggregator eventAggregator, Nip5Controller nip5Controller)
|
||||
public Zapper(EventAggregator eventAggregator, Nip5Controller nip5Controller, IMemoryCache memoryCache)
|
||||
{
|
||||
_eventAggregator = eventAggregator;
|
||||
_nip5Controller = nip5Controller;
|
||||
_memoryCache = memoryCache;
|
||||
}
|
||||
|
||||
public Task StartAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
_subscription = _eventAggregator.SubscribeAsync<InvoiceEvent>(Subscription);
|
||||
_ = SendZapReceipts(cancellationToken);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private async Task SendZapReceipts(CancellationToken cancellationToken)
|
||||
{
|
||||
while (!cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
|
||||
if (_pendingZapEvents.Any())
|
||||
{
|
||||
var pendingZaps = _pendingZapEvents.Take(Range.All).ToArray();
|
||||
var relaysToConnectTo = pendingZaps.SelectMany(@event => @event.relays).Distinct();
|
||||
var relaysToZap =relaysToConnectTo.ToDictionary(s => s, s => pendingZaps.Where(@event => @event.relays.Contains(s)).Select(@event => @event.nostrEvent).ToArray());
|
||||
|
||||
await Task.WhenAll(relaysToZap.Select(async relay =>
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
var cts = new CancellationTokenSource();
|
||||
cts.CancelAfter(TimeSpan.FromSeconds(30));
|
||||
var tcs = new TaskCompletionSource();
|
||||
using var c = new NostrClient(new Uri(relay.Key));
|
||||
await c.ConnectAndWaitUntilConnected(cts.Token);
|
||||
var pendingOksOnIds = relay.Value.Select(a => a.Id).ToHashSet();
|
||||
c.OkReceived += (sender, okargs) =>
|
||||
{
|
||||
pendingOksOnIds.Remove(okargs.eventId);
|
||||
if(!pendingOksOnIds.Any())
|
||||
tcs.SetResult();
|
||||
};
|
||||
foreach (var nostrEvent in relay.Value)
|
||||
{
|
||||
await c.PublishEvent(nostrEvent, cts.Token);
|
||||
|
||||
}
|
||||
await tcs.Task.WaitAsync(cts.Token);
|
||||
await c.Disconnect();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
}
|
||||
}));
|
||||
|
||||
}
|
||||
var waitingToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
|
||||
waitingToken.CancelAfter(TimeSpan.FromMinutes(2));
|
||||
while (!waitingToken.IsCancellationRequested)
|
||||
{
|
||||
if (_pendingZapEvents.Count > 10)
|
||||
{
|
||||
waitingToken.Cancel();
|
||||
}
|
||||
else
|
||||
{
|
||||
await Task.Delay(100, waitingToken.Token);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task Subscription(InvoiceEvent arg)
|
||||
{
|
||||
if (arg.EventCode != InvoiceEventCode.Completed)
|
||||
@@ -64,12 +135,11 @@ namespace BTCPayServer.Plugins.NIP05
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var zapRequest = arg.Invoice.Metadata.GetAdditionalData<string>("zapRequest");
|
||||
if (zapRequest is null)
|
||||
if(!_memoryCache.TryGetValue(Nip05Plugin.GetZapRequestCacheKey(arg.Invoice.Id), out var zapRequestEntry) || zapRequestEntry is not string zapRequest)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var pmd = (LNURLPayPaymentMethodDetails) pm.GetPaymentMethodDetails();
|
||||
var name = pmd.ConsumedLightningAddress.Split("@")[0];
|
||||
var settings = await _nip5Controller.Get(name);
|
||||
@@ -113,32 +183,7 @@ namespace BTCPayServer.Plugins.NIP05
|
||||
|
||||
await zapReceipt.ComputeIdAndSignAsync(key);
|
||||
|
||||
|
||||
await Task.WhenAll(relays.Concat(settings.settings.Relays?? Array.Empty<string>()).Distinct().Select(async relay =>
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
var cts = new CancellationTokenSource();
|
||||
cts.CancelAfter(TimeSpan.FromSeconds(30));
|
||||
var tcs = new TaskCompletionSource();
|
||||
using var c = new NostrClient(new Uri(relay));
|
||||
await c.ConnectAndWaitUntilConnected(cts.Token);
|
||||
|
||||
c.OkReceived += (sender, okargs) =>
|
||||
{
|
||||
if(okargs.eventId == zapReceipt.Id)
|
||||
tcs.SetResult();
|
||||
};
|
||||
await c.PublishEvent(zapReceipt, cts.Token);
|
||||
await tcs.Task.WaitAsync(cts.Token);
|
||||
await c.Disconnect();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
}
|
||||
}));
|
||||
|
||||
_pendingZapEvents.Add(new PendingZapEvent(relays.Concat(settings.settings.Relays?? Array.Empty<string>()).Distinct().ToArray(), zapReceipt));
|
||||
}
|
||||
|
||||
public Task StopAsync(CancellationToken cancellationToken)
|
||||
@@ -192,16 +237,16 @@ namespace BTCPayServer.Plugins.NIP05
|
||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||
private readonly Nip5Controller _nip5Controller;
|
||||
private readonly LightningAddressService _lightningAddressService;
|
||||
private readonly InvoiceRepository _invoiceRepository;
|
||||
private readonly IMemoryCache _memoryCache;
|
||||
|
||||
public LnurlDescriptionFilter(IHttpContextAccessor httpContextAccessor,
|
||||
Nip5Controller nip5Controller, LightningAddressService lightningAddressService,
|
||||
InvoiceRepository invoiceRepository)
|
||||
InvoiceRepository invoiceRepository, IMemoryCache memoryCache)
|
||||
{
|
||||
_httpContextAccessor = httpContextAccessor;
|
||||
_nip5Controller = nip5Controller;
|
||||
_lightningAddressService = lightningAddressService;
|
||||
_invoiceRepository = invoiceRepository;
|
||||
_memoryCache = memoryCache;
|
||||
}
|
||||
|
||||
public override string Hook => "lnurlp-description";
|
||||
@@ -236,19 +281,18 @@ namespace BTCPayServer.Plugins.NIP05
|
||||
var parsedNote = System.Text.Json.JsonSerializer.Deserialize<NostrEvent>(nostr);
|
||||
if (parsedNote?.Kind != 9734)
|
||||
{
|
||||
throw new InvalidOperationException("Invalid zap note, kind must be 9734");
|
||||
return arg;
|
||||
}
|
||||
|
||||
if (!parsedNote.Verify())
|
||||
{
|
||||
throw new InvalidOperationException("Zap note sig check failed");
|
||||
return arg;
|
||||
}
|
||||
|
||||
var invoice = await _invoiceRepository.GetInvoice(invoiceId.ToString());
|
||||
|
||||
invoice.Metadata.SetAdditionalData("zapRequest", nostr);
|
||||
await _invoiceRepository.UpdateInvoiceMetadata(invoiceId.ToString(), invoice.StoreId,
|
||||
invoice.Metadata.ToJObject());
|
||||
var entry =_memoryCache.CreateEntry(Nip05Plugin.GetZapRequestCacheKey(invoiceId.ToString()));
|
||||
entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(5);
|
||||
entry.SetAbsoluteExpiration(TimeSpan.FromMinutes(5));
|
||||
entry.SetValue(nostr);
|
||||
return nostr;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user