mirror of
https://github.com/aljazceru/BTCPayServerPlugins.git
synced 2025-12-17 15:44:26 +01:00
nip5 improve
This commit is contained in:
@@ -11,7 +11,7 @@
|
||||
<PropertyGroup>
|
||||
<Product>Nostr</Product>
|
||||
<Description>NIP5 addresses, Zap support, Nostr Wallet Connect Lightning support</Description>
|
||||
<Version>1.1.9</Version>
|
||||
<Version>1.1.10</Version>
|
||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||
</PropertyGroup>
|
||||
<!-- Plugin development properties -->
|
||||
|
||||
@@ -4,6 +4,8 @@ using BTCPayServer.Abstractions.Models;
|
||||
using BTCPayServer.Abstractions.Services;
|
||||
using BTCPayServer.Lightning;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||
using NNostr.Client;
|
||||
|
||||
namespace BTCPayServer.Plugins.NIP05
|
||||
{
|
||||
@@ -22,6 +24,7 @@ namespace BTCPayServer.Plugins.NIP05
|
||||
|
||||
applicationBuilder.AddSingleton<IPluginHookFilter, LnurlDescriptionFilter>();
|
||||
applicationBuilder.AddSingleton<IPluginHookFilter, LnurlFilter>();
|
||||
applicationBuilder.TryAddSingleton<NostrClientPool>();
|
||||
applicationBuilder.AddSingleton<Zapper>();
|
||||
applicationBuilder.AddHostedService(sp => sp.GetRequiredService<Zapper>());
|
||||
applicationBuilder.AddSingleton<NostrWalletConnectLightningConnectionStringHandler>();
|
||||
|
||||
@@ -1,77 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using NNostr.Client;
|
||||
using NNostr.Client.Protocols;
|
||||
|
||||
namespace BTCPayServer.Plugins.NIP05;
|
||||
|
||||
public class NostrClientPool
|
||||
{
|
||||
private static readonly ConcurrentDictionary<string, NostrClientWrapper> _clientPool = new();
|
||||
|
||||
private static readonly Timer _cleanupTimer;
|
||||
|
||||
static NostrClientPool()
|
||||
{
|
||||
_cleanupTimer = new Timer(CleanupExpiredClients, null, TimeSpan.FromMinutes(5), TimeSpan.FromMinutes(5));
|
||||
}
|
||||
|
||||
public static (INostrClient, IDisposable) GetClient(string connstring)
|
||||
{
|
||||
var connParams = NIP47.ParseUri(new Uri(connstring));
|
||||
|
||||
var clientWrapper = _clientPool.GetOrAdd(connstring.ToString(),
|
||||
k => new NostrClientWrapper(new CompositeNostrClient(connParams.relays)));
|
||||
|
||||
clientWrapper.IncrementUsage();
|
||||
|
||||
return (clientWrapper.Client, new UsageDisposable(clientWrapper));
|
||||
}
|
||||
public static async Task<(INostrClient, IDisposable)> GetClientAndConnect(string connstring, CancellationToken token)
|
||||
{
|
||||
var result = GetClient(connstring);
|
||||
|
||||
await result.Item1.ConnectAndWaitUntilConnected(token, CancellationToken.None);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static void KillClient(string connstring)
|
||||
{
|
||||
if (_clientPool.TryRemove(connstring, out var clientWrapper))
|
||||
{
|
||||
clientWrapper.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private static void CleanupExpiredClients(object state)
|
||||
{
|
||||
foreach (var key in _clientPool.Keys)
|
||||
{
|
||||
if (_clientPool[key].IsExpired())
|
||||
{
|
||||
if (_clientPool.TryRemove(key, out var clientWrapper))
|
||||
{
|
||||
clientWrapper.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class UsageDisposable : IDisposable
|
||||
{
|
||||
private readonly NostrClientWrapper _clientWrapper;
|
||||
|
||||
public UsageDisposable(NostrClientWrapper clientWrapper)
|
||||
{
|
||||
_clientWrapper = clientWrapper;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_clientWrapper.DecrementUsage();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,14 +16,16 @@ namespace BTCPayServer.Plugins.NIP05;
|
||||
|
||||
public class NostrWalletConnectLightningClient : ILightningClient
|
||||
{
|
||||
private readonly NostrClientPool _nostrClientPool;
|
||||
private readonly Uri _uri;
|
||||
private readonly Network _network;
|
||||
private readonly (string[] Commands, string[] Notifications) _commands;
|
||||
private readonly (ECXOnlyPubKey pubkey, ECPrivKey secret, Uri[] relays, string lud16) _connectParams;
|
||||
|
||||
public NostrWalletConnectLightningClient(Uri uri, Network network,
|
||||
public NostrWalletConnectLightningClient(NostrClientPool nostrClientPool, Uri uri, Network network,
|
||||
(string[] Commands, string[] Notifications) commands)
|
||||
{
|
||||
_nostrClientPool = nostrClientPool;
|
||||
_uri = uri;
|
||||
_network = network;
|
||||
_commands = commands;
|
||||
@@ -77,7 +79,7 @@ public class NostrWalletConnectLightningClient : ILightningClient
|
||||
public async Task<LightningInvoice> GetInvoice(uint256 paymentHash,
|
||||
CancellationToken cancellation = new CancellationToken())
|
||||
{
|
||||
var (nostrClient, usage) = await NostrClientPool.GetClientAndConnect(_uri.ToString(), cancellation);
|
||||
var (nostrClient, usage) = await _nostrClientPool.GetClientAndConnect(_connectParams.relays, cancellation);
|
||||
|
||||
using (usage)
|
||||
{
|
||||
@@ -99,7 +101,7 @@ public class NostrWalletConnectLightningClient : ILightningClient
|
||||
public async Task<LightningInvoice[]> ListInvoices(ListInvoicesParams request,
|
||||
CancellationToken cancellation = new CancellationToken())
|
||||
{
|
||||
var (client, usage) = await NostrClientPool.GetClientAndConnect(_uri.ToString(), cancellation);
|
||||
var (client, usage) = await _nostrClientPool.GetClientAndConnect(_connectParams.relays, cancellation);
|
||||
|
||||
using (usage)
|
||||
{
|
||||
@@ -154,7 +156,7 @@ public class NostrWalletConnectLightningClient : ILightningClient
|
||||
public async Task<LightningPayment> GetPayment(string paymentHash,
|
||||
CancellationToken cancellation = new CancellationToken())
|
||||
{
|
||||
var (client, usage) = await NostrClientPool.GetClientAndConnect(_uri.ToString(), cancellation);
|
||||
var (client, usage) = await _nostrClientPool.GetClientAndConnect(_connectParams.relays, cancellation);
|
||||
|
||||
using (usage)
|
||||
{
|
||||
@@ -175,7 +177,7 @@ public class NostrWalletConnectLightningClient : ILightningClient
|
||||
public async Task<LightningPayment[]> ListPayments(ListPaymentsParams request,
|
||||
CancellationToken cancellation = new CancellationToken())
|
||||
{
|
||||
var (client, usage) = await NostrClientPool.GetClientAndConnect(_uri.ToString(), cancellation);
|
||||
var (client, usage) = await _nostrClientPool.GetClientAndConnect(_connectParams.relays, cancellation);
|
||||
|
||||
using (usage)
|
||||
{
|
||||
@@ -200,7 +202,7 @@ public class NostrWalletConnectLightningClient : ILightningClient
|
||||
public async Task<LightningInvoice> CreateInvoice(CreateInvoiceParams createInvoiceRequest,
|
||||
CancellationToken cancellation = new CancellationToken())
|
||||
{
|
||||
var (client, usage) = await NostrClientPool.GetClientAndConnect(_uri.ToString(), cancellation);
|
||||
var (client, usage) = await _nostrClientPool.GetClientAndConnect(_connectParams.relays, cancellation);
|
||||
|
||||
using (usage)
|
||||
{
|
||||
@@ -221,7 +223,7 @@ public class NostrWalletConnectLightningClient : ILightningClient
|
||||
|
||||
public async Task<ILightningInvoiceListener> Listen(CancellationToken cancellation = new CancellationToken())
|
||||
{
|
||||
var x = await NostrClientPool.GetClientAndConnect(_uri.ToString(), cancellation);
|
||||
var x = await _nostrClientPool.GetClientAndConnect(_connectParams.relays, cancellation);
|
||||
if (_commands.Notifications?.Contains("payment_received") is true)
|
||||
{
|
||||
return new NotificationListener(_network, x, _connectParams);
|
||||
@@ -361,7 +363,7 @@ public class NostrWalletConnectLightningClient : ILightningClient
|
||||
|
||||
public async Task<LightningNodeBalance> GetBalance(CancellationToken cancellation = new CancellationToken())
|
||||
{
|
||||
var (client, usage) = await NostrClientPool.GetClientAndConnect(_uri.ToString(), cancellation);
|
||||
var (client, usage) = await _nostrClientPool.GetClientAndConnect(_connectParams.relays, cancellation);
|
||||
|
||||
using (usage)
|
||||
{
|
||||
@@ -389,7 +391,7 @@ public class NostrWalletConnectLightningClient : ILightningClient
|
||||
{
|
||||
try
|
||||
{
|
||||
var (client, usage) = await NostrClientPool.GetClientAndConnect(_uri.ToString(), cancellation);
|
||||
var (client, usage) = await _nostrClientPool.GetClientAndConnect(_connectParams.relays, cancellation);
|
||||
|
||||
using (usage)
|
||||
{
|
||||
|
||||
@@ -4,13 +4,19 @@ using System.Linq;
|
||||
using System.Threading;
|
||||
using BTCPayServer.Lightning;
|
||||
using NBitcoin;
|
||||
using NNostr.Client;
|
||||
using NNostr.Client.Protocols;
|
||||
|
||||
namespace BTCPayServer.Plugins.NIP05;
|
||||
|
||||
public class NostrWalletConnectLightningConnectionStringHandler : ILightningConnectionStringHandler
|
||||
{
|
||||
private readonly NostrClientPool _nostrClientPool;
|
||||
|
||||
public NostrWalletConnectLightningConnectionStringHandler(NostrClientPool nostrClientPool)
|
||||
{
|
||||
_nostrClientPool = nostrClientPool;
|
||||
}
|
||||
public ILightningClient? Create(string connectionString, Network network, out string? error)
|
||||
{
|
||||
|
||||
@@ -27,7 +33,7 @@ public class NostrWalletConnectLightningConnectionStringHandler : ILightningConn
|
||||
Uri.TryCreate(connectionString, UriKind.Absolute, out var uri);
|
||||
var connectParams = NIP47.ParseUri(uri); var cts = new CancellationTokenSource();
|
||||
cts.CancelAfter(TimeSpan.FromSeconds(10));
|
||||
var (client, disposable) = NostrClientPool.GetClientAndConnect(connectionString, cts.Token).ConfigureAwait(false).GetAwaiter().GetResult();
|
||||
var (client, disposable) = _nostrClientPool.GetClientAndConnect(connectParams.relays, cts.Token).ConfigureAwait(false).GetAwaiter().GetResult();
|
||||
using (disposable)
|
||||
{
|
||||
var commands = client.FetchNIP47AvailableCommands(connectParams.Item1, cancellationToken: cts.Token)
|
||||
@@ -55,7 +61,7 @@ public class NostrWalletConnectLightningConnectionStringHandler : ILightningConn
|
||||
}
|
||||
|
||||
error = null;
|
||||
return new NostrWalletConnectLightningClient(uri, network, commands.Value);
|
||||
return new NostrWalletConnectLightningClient(_nostrClientPool, uri, network, commands.Value);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
|
||||
@@ -15,33 +15,11 @@ using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
using NBitcoin;
|
||||
using NBitcoin.Secp256k1;
|
||||
using Newtonsoft.Json;
|
||||
using NNostr.Client;
|
||||
using JsonSerializer = System.Text.Json.JsonSerializer;
|
||||
|
||||
namespace BTCPayServer.Plugins.NIP05;
|
||||
|
||||
public class ZapperSettings
|
||||
{
|
||||
public ZapperSettings(string ZapperPrivateKey)
|
||||
{
|
||||
this.ZapperPrivateKey = ZapperPrivateKey;
|
||||
}
|
||||
|
||||
public ZapperSettings()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
[JsonIgnore]
|
||||
public ECPrivKey ZappingKey => NostrExtensions.ParseKey(ZapperPrivateKey);
|
||||
[JsonIgnore]
|
||||
public ECXOnlyPubKey ZappingPublicKey => ZappingKey.CreateXOnlyPubKey();
|
||||
[JsonIgnore]
|
||||
public string ZappingPublicKeyHex => ZappingPublicKey.ToHex();
|
||||
public string ZapperPrivateKey { get; set; }
|
||||
}
|
||||
public class Zapper : IHostedService
|
||||
{
|
||||
record PendingZapEvent(string[] relays, NostrEvent nostrEvent);
|
||||
@@ -54,7 +32,7 @@ public class Zapper : IHostedService
|
||||
private readonly InvoiceRepository _invoiceRepository;
|
||||
private IEventAggregatorSubscription _subscription;
|
||||
private readonly ConcurrentBag<PendingZapEvent> _pendingZapEvents = new();
|
||||
private readonly NNostr.Client.NostrClientPool _nostrClientPool;
|
||||
private readonly NostrClientPool _nostrClientPool;
|
||||
|
||||
public async Task<ZapperSettings> GetSettings()
|
||||
{
|
||||
@@ -78,7 +56,8 @@ public class Zapper : IHostedService
|
||||
IMemoryCache memoryCache,
|
||||
ILogger<Zapper> logger,
|
||||
SettingsRepository settingsRepository,
|
||||
InvoiceRepository invoiceRepository)
|
||||
InvoiceRepository invoiceRepository,
|
||||
NostrClientPool nostrClientPool)
|
||||
{
|
||||
_eventAggregator = eventAggregator;
|
||||
_nip5Controller = nip5Controller;
|
||||
@@ -86,7 +65,7 @@ public class Zapper : IHostedService
|
||||
_logger = logger;
|
||||
_settingsRepository = settingsRepository;
|
||||
_invoiceRepository = invoiceRepository;
|
||||
_nostrClientPool = new NNostr.Client.NostrClientPool();
|
||||
_nostrClientPool = nostrClientPool;
|
||||
}
|
||||
|
||||
public Task StartAsync(CancellationToken cancellationToken)
|
||||
|
||||
26
Plugins/BTCPayServer.Plugins.NIP05/ZapperSettings.cs
Normal file
26
Plugins/BTCPayServer.Plugins.NIP05/ZapperSettings.cs
Normal file
@@ -0,0 +1,26 @@
|
||||
using NBitcoin.Secp256k1;
|
||||
using Newtonsoft.Json;
|
||||
using NNostr.Client;
|
||||
|
||||
namespace BTCPayServer.Plugins.NIP05;
|
||||
|
||||
public class ZapperSettings
|
||||
{
|
||||
public ZapperSettings(string ZapperPrivateKey)
|
||||
{
|
||||
this.ZapperPrivateKey = ZapperPrivateKey;
|
||||
}
|
||||
|
||||
public ZapperSettings()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
[JsonIgnore]
|
||||
public ECPrivKey ZappingKey => NostrExtensions.ParseKey(ZapperPrivateKey);
|
||||
[JsonIgnore]
|
||||
public ECXOnlyPubKey ZappingPublicKey => ZappingKey.CreateXOnlyPubKey();
|
||||
[JsonIgnore]
|
||||
public string ZappingPublicKeyHex => ZappingPublicKey.ToHex();
|
||||
public string ZapperPrivateKey { get; set; }
|
||||
}
|
||||
@@ -1,6 +1,10 @@
|
||||
# BTCPay Server NIP05 Support
|
||||
|
||||
This plugin allows your BTCPay Server to support the [Nostr](https://github.com/nostr-protocol/nostr)[ NIP05 protocol](https://github.com/nostr-protocol/nips/blob/master/05.md) to verify accounts.
|
||||
This plugin allows your BTCPay Server to support
|
||||
|
||||
* [Nostr](https://github.com/nostr-protocol/nostr)[ NIP05 protocol](https://github.com/nostr-protocol/nips/blob/master/05.md) to verify accounts.
|
||||
* [Nostr](https://github.com/nostr-protocol/nostr)[ NIP57 protocol](https://github.com/nostr-protocol/nips/blob/master/57.md) to support Zaps.
|
||||
* [Nostr](https://github.com/nostr-protocol/nostr)[ NIP47 protocol](https://github.com/nostr-protocol/nips/blob/master/47.md) to accept payments to your NWC enabled lightning wallet.
|
||||
|
||||
## Usage
|
||||
|
||||
|
||||
Reference in New Issue
Block a user