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>
|
<PropertyGroup>
|
||||||
<Product>Nostr</Product>
|
<Product>Nostr</Product>
|
||||||
<Description>NIP5 addresses, Zap support, Nostr Wallet Connect Lightning support</Description>
|
<Description>NIP5 addresses, Zap support, Nostr Wallet Connect Lightning support</Description>
|
||||||
<Version>1.1.9</Version>
|
<Version>1.1.10</Version>
|
||||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<!-- Plugin development properties -->
|
<!-- Plugin development properties -->
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ using BTCPayServer.Abstractions.Models;
|
|||||||
using BTCPayServer.Abstractions.Services;
|
using BTCPayServer.Abstractions.Services;
|
||||||
using BTCPayServer.Lightning;
|
using BTCPayServer.Lightning;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||||
|
using NNostr.Client;
|
||||||
|
|
||||||
namespace BTCPayServer.Plugins.NIP05
|
namespace BTCPayServer.Plugins.NIP05
|
||||||
{
|
{
|
||||||
@@ -22,6 +24,7 @@ namespace BTCPayServer.Plugins.NIP05
|
|||||||
|
|
||||||
applicationBuilder.AddSingleton<IPluginHookFilter, LnurlDescriptionFilter>();
|
applicationBuilder.AddSingleton<IPluginHookFilter, LnurlDescriptionFilter>();
|
||||||
applicationBuilder.AddSingleton<IPluginHookFilter, LnurlFilter>();
|
applicationBuilder.AddSingleton<IPluginHookFilter, LnurlFilter>();
|
||||||
|
applicationBuilder.TryAddSingleton<NostrClientPool>();
|
||||||
applicationBuilder.AddSingleton<Zapper>();
|
applicationBuilder.AddSingleton<Zapper>();
|
||||||
applicationBuilder.AddHostedService(sp => sp.GetRequiredService<Zapper>());
|
applicationBuilder.AddHostedService(sp => sp.GetRequiredService<Zapper>());
|
||||||
applicationBuilder.AddSingleton<NostrWalletConnectLightningConnectionStringHandler>();
|
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
|
public class NostrWalletConnectLightningClient : ILightningClient
|
||||||
{
|
{
|
||||||
|
private readonly NostrClientPool _nostrClientPool;
|
||||||
private readonly Uri _uri;
|
private readonly Uri _uri;
|
||||||
private readonly Network _network;
|
private readonly Network _network;
|
||||||
private readonly (string[] Commands, string[] Notifications) _commands;
|
private readonly (string[] Commands, string[] Notifications) _commands;
|
||||||
private readonly (ECXOnlyPubKey pubkey, ECPrivKey secret, Uri[] relays, string lud16) _connectParams;
|
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)
|
(string[] Commands, string[] Notifications) commands)
|
||||||
{
|
{
|
||||||
|
_nostrClientPool = nostrClientPool;
|
||||||
_uri = uri;
|
_uri = uri;
|
||||||
_network = network;
|
_network = network;
|
||||||
_commands = commands;
|
_commands = commands;
|
||||||
@@ -77,7 +79,7 @@ public class NostrWalletConnectLightningClient : ILightningClient
|
|||||||
public async Task<LightningInvoice> GetInvoice(uint256 paymentHash,
|
public async Task<LightningInvoice> GetInvoice(uint256 paymentHash,
|
||||||
CancellationToken cancellation = new CancellationToken())
|
CancellationToken cancellation = new CancellationToken())
|
||||||
{
|
{
|
||||||
var (nostrClient, usage) = await NostrClientPool.GetClientAndConnect(_uri.ToString(), cancellation);
|
var (nostrClient, usage) = await _nostrClientPool.GetClientAndConnect(_connectParams.relays, cancellation);
|
||||||
|
|
||||||
using (usage)
|
using (usage)
|
||||||
{
|
{
|
||||||
@@ -99,7 +101,7 @@ public class NostrWalletConnectLightningClient : ILightningClient
|
|||||||
public async Task<LightningInvoice[]> ListInvoices(ListInvoicesParams request,
|
public async Task<LightningInvoice[]> ListInvoices(ListInvoicesParams request,
|
||||||
CancellationToken cancellation = new CancellationToken())
|
CancellationToken cancellation = new CancellationToken())
|
||||||
{
|
{
|
||||||
var (client, usage) = await NostrClientPool.GetClientAndConnect(_uri.ToString(), cancellation);
|
var (client, usage) = await _nostrClientPool.GetClientAndConnect(_connectParams.relays, cancellation);
|
||||||
|
|
||||||
using (usage)
|
using (usage)
|
||||||
{
|
{
|
||||||
@@ -154,7 +156,7 @@ public class NostrWalletConnectLightningClient : ILightningClient
|
|||||||
public async Task<LightningPayment> GetPayment(string paymentHash,
|
public async Task<LightningPayment> GetPayment(string paymentHash,
|
||||||
CancellationToken cancellation = new CancellationToken())
|
CancellationToken cancellation = new CancellationToken())
|
||||||
{
|
{
|
||||||
var (client, usage) = await NostrClientPool.GetClientAndConnect(_uri.ToString(), cancellation);
|
var (client, usage) = await _nostrClientPool.GetClientAndConnect(_connectParams.relays, cancellation);
|
||||||
|
|
||||||
using (usage)
|
using (usage)
|
||||||
{
|
{
|
||||||
@@ -175,7 +177,7 @@ public class NostrWalletConnectLightningClient : ILightningClient
|
|||||||
public async Task<LightningPayment[]> ListPayments(ListPaymentsParams request,
|
public async Task<LightningPayment[]> ListPayments(ListPaymentsParams request,
|
||||||
CancellationToken cancellation = new CancellationToken())
|
CancellationToken cancellation = new CancellationToken())
|
||||||
{
|
{
|
||||||
var (client, usage) = await NostrClientPool.GetClientAndConnect(_uri.ToString(), cancellation);
|
var (client, usage) = await _nostrClientPool.GetClientAndConnect(_connectParams.relays, cancellation);
|
||||||
|
|
||||||
using (usage)
|
using (usage)
|
||||||
{
|
{
|
||||||
@@ -200,7 +202,7 @@ public class NostrWalletConnectLightningClient : ILightningClient
|
|||||||
public async Task<LightningInvoice> CreateInvoice(CreateInvoiceParams createInvoiceRequest,
|
public async Task<LightningInvoice> CreateInvoice(CreateInvoiceParams createInvoiceRequest,
|
||||||
CancellationToken cancellation = new CancellationToken())
|
CancellationToken cancellation = new CancellationToken())
|
||||||
{
|
{
|
||||||
var (client, usage) = await NostrClientPool.GetClientAndConnect(_uri.ToString(), cancellation);
|
var (client, usage) = await _nostrClientPool.GetClientAndConnect(_connectParams.relays, cancellation);
|
||||||
|
|
||||||
using (usage)
|
using (usage)
|
||||||
{
|
{
|
||||||
@@ -221,7 +223,7 @@ public class NostrWalletConnectLightningClient : ILightningClient
|
|||||||
|
|
||||||
public async Task<ILightningInvoiceListener> Listen(CancellationToken cancellation = new CancellationToken())
|
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)
|
if (_commands.Notifications?.Contains("payment_received") is true)
|
||||||
{
|
{
|
||||||
return new NotificationListener(_network, x, _connectParams);
|
return new NotificationListener(_network, x, _connectParams);
|
||||||
@@ -361,7 +363,7 @@ public class NostrWalletConnectLightningClient : ILightningClient
|
|||||||
|
|
||||||
public async Task<LightningNodeBalance> GetBalance(CancellationToken cancellation = new CancellationToken())
|
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)
|
using (usage)
|
||||||
{
|
{
|
||||||
@@ -389,7 +391,7 @@ public class NostrWalletConnectLightningClient : ILightningClient
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var (client, usage) = await NostrClientPool.GetClientAndConnect(_uri.ToString(), cancellation);
|
var (client, usage) = await _nostrClientPool.GetClientAndConnect(_connectParams.relays, cancellation);
|
||||||
|
|
||||||
using (usage)
|
using (usage)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -4,13 +4,19 @@ using System.Linq;
|
|||||||
using System.Threading;
|
using System.Threading;
|
||||||
using BTCPayServer.Lightning;
|
using BTCPayServer.Lightning;
|
||||||
using NBitcoin;
|
using NBitcoin;
|
||||||
|
using NNostr.Client;
|
||||||
using NNostr.Client.Protocols;
|
using NNostr.Client.Protocols;
|
||||||
|
|
||||||
namespace BTCPayServer.Plugins.NIP05;
|
namespace BTCPayServer.Plugins.NIP05;
|
||||||
|
|
||||||
public class NostrWalletConnectLightningConnectionStringHandler : ILightningConnectionStringHandler
|
public class NostrWalletConnectLightningConnectionStringHandler : ILightningConnectionStringHandler
|
||||||
{
|
{
|
||||||
|
private readonly NostrClientPool _nostrClientPool;
|
||||||
|
|
||||||
|
public NostrWalletConnectLightningConnectionStringHandler(NostrClientPool nostrClientPool)
|
||||||
|
{
|
||||||
|
_nostrClientPool = nostrClientPool;
|
||||||
|
}
|
||||||
public ILightningClient? Create(string connectionString, Network network, out string? error)
|
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);
|
Uri.TryCreate(connectionString, UriKind.Absolute, out var uri);
|
||||||
var connectParams = NIP47.ParseUri(uri); var cts = new CancellationTokenSource();
|
var connectParams = NIP47.ParseUri(uri); var cts = new CancellationTokenSource();
|
||||||
cts.CancelAfter(TimeSpan.FromSeconds(10));
|
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)
|
using (disposable)
|
||||||
{
|
{
|
||||||
var commands = client.FetchNIP47AvailableCommands(connectParams.Item1, cancellationToken: cts.Token)
|
var commands = client.FetchNIP47AvailableCommands(connectParams.Item1, cancellationToken: cts.Token)
|
||||||
@@ -55,7 +61,7 @@ public class NostrWalletConnectLightningConnectionStringHandler : ILightningConn
|
|||||||
}
|
}
|
||||||
|
|
||||||
error = null;
|
error = null;
|
||||||
return new NostrWalletConnectLightningClient(uri, network, commands.Value);
|
return new NostrWalletConnectLightningClient(_nostrClientPool, uri, network, commands.Value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
|
|||||||
@@ -15,33 +15,11 @@ using Microsoft.Extensions.Hosting;
|
|||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Microsoft.Extensions.Primitives;
|
using Microsoft.Extensions.Primitives;
|
||||||
using NBitcoin;
|
using NBitcoin;
|
||||||
using NBitcoin.Secp256k1;
|
|
||||||
using Newtonsoft.Json;
|
|
||||||
using NNostr.Client;
|
using NNostr.Client;
|
||||||
using JsonSerializer = System.Text.Json.JsonSerializer;
|
using JsonSerializer = System.Text.Json.JsonSerializer;
|
||||||
|
|
||||||
namespace BTCPayServer.Plugins.NIP05;
|
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
|
public class Zapper : IHostedService
|
||||||
{
|
{
|
||||||
record PendingZapEvent(string[] relays, NostrEvent nostrEvent);
|
record PendingZapEvent(string[] relays, NostrEvent nostrEvent);
|
||||||
@@ -54,7 +32,7 @@ public class Zapper : IHostedService
|
|||||||
private readonly InvoiceRepository _invoiceRepository;
|
private readonly InvoiceRepository _invoiceRepository;
|
||||||
private IEventAggregatorSubscription _subscription;
|
private IEventAggregatorSubscription _subscription;
|
||||||
private readonly ConcurrentBag<PendingZapEvent> _pendingZapEvents = new();
|
private readonly ConcurrentBag<PendingZapEvent> _pendingZapEvents = new();
|
||||||
private readonly NNostr.Client.NostrClientPool _nostrClientPool;
|
private readonly NostrClientPool _nostrClientPool;
|
||||||
|
|
||||||
public async Task<ZapperSettings> GetSettings()
|
public async Task<ZapperSettings> GetSettings()
|
||||||
{
|
{
|
||||||
@@ -78,7 +56,8 @@ public class Zapper : IHostedService
|
|||||||
IMemoryCache memoryCache,
|
IMemoryCache memoryCache,
|
||||||
ILogger<Zapper> logger,
|
ILogger<Zapper> logger,
|
||||||
SettingsRepository settingsRepository,
|
SettingsRepository settingsRepository,
|
||||||
InvoiceRepository invoiceRepository)
|
InvoiceRepository invoiceRepository,
|
||||||
|
NostrClientPool nostrClientPool)
|
||||||
{
|
{
|
||||||
_eventAggregator = eventAggregator;
|
_eventAggregator = eventAggregator;
|
||||||
_nip5Controller = nip5Controller;
|
_nip5Controller = nip5Controller;
|
||||||
@@ -86,7 +65,7 @@ public class Zapper : IHostedService
|
|||||||
_logger = logger;
|
_logger = logger;
|
||||||
_settingsRepository = settingsRepository;
|
_settingsRepository = settingsRepository;
|
||||||
_invoiceRepository = invoiceRepository;
|
_invoiceRepository = invoiceRepository;
|
||||||
_nostrClientPool = new NNostr.Client.NostrClientPool();
|
_nostrClientPool = nostrClientPool;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task StartAsync(CancellationToken cancellationToken)
|
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
|
# 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
|
## Usage
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user