nip5 improve

This commit is contained in:
Kukks
2024-06-06 13:13:29 +02:00
parent 72dd7aa1be
commit 01ba71f419
8 changed files with 59 additions and 116 deletions

View File

@@ -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 -->

View File

@@ -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>();

View File

@@ -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();
}
}
}

View File

@@ -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)
{

View File

@@ -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)

View File

@@ -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)

View 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; }
}

View File

@@ -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