diff --git a/Plugins/BTCPayServer.Plugins.NIP05/BTCPayServer.Plugins.NIP05.csproj b/Plugins/BTCPayServer.Plugins.NIP05/BTCPayServer.Plugins.NIP05.csproj
index 0ee3bf5..ada0a41 100644
--- a/Plugins/BTCPayServer.Plugins.NIP05/BTCPayServer.Plugins.NIP05.csproj
+++ b/Plugins/BTCPayServer.Plugins.NIP05/BTCPayServer.Plugins.NIP05.csproj
@@ -11,7 +11,7 @@
Nostr
NIP5 addresses, Zap support, Nostr Wallet Connect Lightning support
- 1.1.11
+ 1.1.12
true
@@ -36,7 +36,7 @@
-
+
diff --git a/Plugins/BTCPayServer.Plugins.NIP05/NostrWalletConnectLightningClient.cs b/Plugins/BTCPayServer.Plugins.NIP05/NostrWalletConnectLightningClient.cs
index 78d3774..c9615fa 100644
--- a/Plugins/BTCPayServer.Plugins.NIP05/NostrWalletConnectLightningClient.cs
+++ b/Plugins/BTCPayServer.Plugins.NIP05/NostrWalletConnectLightningClient.cs
@@ -2,6 +2,7 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading;
using System.Threading.Channels;
@@ -17,6 +18,8 @@ namespace BTCPayServer.Plugins.NIP05;
public class NostrWalletConnectLightningClient : ILightningClient
{
+
+ [Display] public string DisplayLabel => $"Nostr Wallet Connect {_connectParams.lud16} {_connectParams.relays.First()} ";
private readonly NostrClientPool _nostrClientPool;
private readonly Uri _uri;
private readonly Network _network;
@@ -40,7 +43,7 @@ public class NostrWalletConnectLightningClient : ILightningClient
public async Task GetInvoice(string invoiceId,
- CancellationToken cancellation = new CancellationToken())
+ CancellationToken cancellation = new())
{
return await GetInvoice(uint256.Parse(invoiceId), cancellation);
}
@@ -78,9 +81,11 @@ public class NostrWalletConnectLightningClient : ILightningClient
public async Task GetInvoice(uint256 paymentHash,
- CancellationToken cancellation = new CancellationToken())
+ CancellationToken cancellation = new())
{
- var (nostrClient, usage) = await _nostrClientPool.GetClientAndConnect(_connectParams.relays, cancellation);
+ var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellation);
+ cts.CancelAfter(TimeSpan.FromSeconds(10));
+ var (nostrClient, usage) = await _nostrClientPool.GetClientAndConnect(_connectParams.relays, cts.Token);
using (usage)
{
@@ -89,12 +94,12 @@ public class NostrWalletConnectLightningClient : ILightningClient
new NIP47.LookupInvoiceRequest()
{
PaymentHash = paymentHash.ToString()
- }, cancellation);
+ }, cts.Token);
return ToLightningInvoice(tx, _network)!;
}
}
- public async Task ListInvoices(CancellationToken cancellation = new CancellationToken())
+ public async Task ListInvoices(CancellationToken cancellation = new())
{
return await ListInvoices(new ListInvoicesParams(), cancellation);
}
@@ -102,8 +107,10 @@ public class NostrWalletConnectLightningClient : ILightningClient
public async Task ListInvoices(ListInvoicesParams request,
CancellationToken cancellation = new CancellationToken())
{
- var (client, usage) = await _nostrClientPool.GetClientAndConnect(_connectParams.relays, cancellation);
+ var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellation);
+ cts.CancelAfter(TimeSpan.FromSeconds(10));
+ var (client, usage) = await _nostrClientPool.GetClientAndConnect(_connectParams.relays, cts.Token);
using (usage)
{
var response = await client.SendNIP47Request(_connectParams.pubkey,
@@ -113,7 +120,7 @@ public class NostrWalletConnectLightningClient : ILightningClient
Type = "incoming",
Offset = (int) (request.OffsetIndex ?? 0),
Unpaid = request.PendingOnly ?? false,
- }, cancellation);
+ }, cts.Token);
return response.Transactions.Select(transaction => ToLightningInvoice(transaction, _network))
.Where(i => i is not null).ToArray()!;
@@ -155,59 +162,65 @@ public class NostrWalletConnectLightningClient : ILightningClient
public async Task GetPayment(string paymentHash,
- CancellationToken cancellation = new CancellationToken())
+ CancellationToken cancellation = new())
{
- var (client, usage) = await _nostrClientPool.GetClientAndConnect(_connectParams.relays, cancellation);
-
+ var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellation);
+ cts.CancelAfter(TimeSpan.FromSeconds(10));
+ var (nostrClient, usage) = await _nostrClientPool.GetClientAndConnect(_connectParams.relays, cts.Token);
using (usage)
{
- var tx = await client.SendNIP47Request(_connectParams.pubkey, _connectParams.secret,
+ var tx = await nostrClient.SendNIP47Request(_connectParams.pubkey, _connectParams.secret,
new NIP47.LookupInvoiceRequest()
{
PaymentHash = paymentHash
- }, cancellation);
+ }, cts.Token);
return ToLightningPayment(tx)!;
}
}
- public async Task ListPayments(CancellationToken cancellation = new CancellationToken())
+ public async Task ListPayments(CancellationToken cancellation = new())
{
return await ListPayments(new ListPaymentsParams(), cancellation);
}
public async Task ListPayments(ListPaymentsParams request,
- CancellationToken cancellation = new CancellationToken())
+ CancellationToken cancellation = new())
{
- var (client, usage) = await _nostrClientPool.GetClientAndConnect(_connectParams.relays, cancellation);
+ var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellation);
+ cts.CancelAfter(TimeSpan.FromSeconds(10));
+ var (nostrClient, usage) = await _nostrClientPool.GetClientAndConnect(_connectParams.relays, cts.Token);
using (usage)
{
- var response = await client.SendNIP47Request(_connectParams.pubkey,
+ var response = await nostrClient.SendNIP47Request(_connectParams.pubkey,
_connectParams.secret,
new NIP47.ListTransactionsRequest()
{
Type = "outgoing",
Offset = (int) (request.OffsetIndex ?? 0),
Unpaid = request.IncludePending ?? false,
- }, cancellation);
+ }, cts.Token);
return response.Transactions.Select(ToLightningPayment).Where(i => i is not null).ToArray()!;
}
}
public async Task CreateInvoice(LightMoney amount, string description, TimeSpan expiry,
- CancellationToken cancellation = new CancellationToken())
+ CancellationToken cancellation = new())
{
return await CreateInvoice(new CreateInvoiceParams(amount, description, expiry), cancellation);
}
public async Task CreateInvoice(CreateInvoiceParams createInvoiceRequest,
- CancellationToken cancellation = new CancellationToken())
+ CancellationToken cancellation = new())
{
- var (client, usage) = await _nostrClientPool.GetClientAndConnect(_connectParams.relays, cancellation);
+
+ var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellation);
+ cts.CancelAfter(TimeSpan.FromSeconds(10));
+ var (nostrClient, usage) = await _nostrClientPool.GetClientAndConnect(_connectParams.relays, cts.Token);
using (usage)
{
- var response = await client.SendNIP47Request(_connectParams.pubkey,
+ var response = await nostrClient.SendNIP47Request(_connectParams.pubkey,
_connectParams.secret,
new NIP47.MakeInvoiceRequest()
{
@@ -217,12 +230,12 @@ public class NostrWalletConnectLightningClient : ILightningClient
: createInvoiceRequest.Description,
DescriptionHash = createInvoiceRequest.DescriptionHash?.ToString(),
ExpirySeconds = (int) createInvoiceRequest.Expiry.TotalSeconds,
- }, cancellation);
+ }, cts.Token);
return ToLightningInvoice(response, _network)!;
}
}
- public async Task Listen(CancellationToken cancellation = new CancellationToken())
+ public async Task Listen(CancellationToken cancellation = new())
{
var x = await _nostrClientPool.GetClientAndConnect(_connectParams.relays, cancellation);
if (_commands.Notifications?.Contains("payment_received") is true)
@@ -357,20 +370,22 @@ public class NostrWalletConnectLightningClient : ILightningClient
}
}
- public async Task GetInfo(CancellationToken cancellation = new CancellationToken())
+ public async Task GetInfo(CancellationToken cancellation = new())
{
throw new NotSupportedException();
}
- public async Task GetBalance(CancellationToken cancellation = new CancellationToken())
+ public async Task GetBalance(CancellationToken cancellation = new())
{
- var (client, usage) = await _nostrClientPool.GetClientAndConnect(_connectParams.relays, cancellation);
+ var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellation);
+ cts.CancelAfter(TimeSpan.FromSeconds(5));
+ var (client, usage) = await _nostrClientPool.GetClientAndConnect(_connectParams.relays, cts.Token);
using (usage)
{
var response = await client.SendNIP47Request(_connectParams.pubkey,
_connectParams.secret,
- new NIP47.NIP47Request("get_balance"), cancellation);
+ new NIP47.NIP47Request("get_balance"), cts.Token);
return new LightningNodeBalance()
{
OffchainBalance = new OffchainBalance()
@@ -382,21 +397,24 @@ public class NostrWalletConnectLightningClient : ILightningClient
}
public async Task Pay(PayInvoiceParams payParams,
- CancellationToken cancellation = new CancellationToken())
+ CancellationToken cancellation = new())
{
return await Pay(null, new PayInvoiceParams(), cancellation);
}
public async Task Pay(string bolt11, PayInvoiceParams payParams,
- CancellationToken cancellation = new CancellationToken())
+ CancellationToken cancellation = new())
{
try
{
- var (client, usage) = await _nostrClientPool.GetClientAndConnect(_connectParams.relays, cancellation);
+
+ var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellation);
+ cts.CancelAfter(TimeSpan.FromSeconds(10));
+ var (client, usage) = await _nostrClientPool.GetClientAndConnect(_connectParams.relays, cts.Token);
using (usage)
{
- var response = await client.SendNIP47Request(_connectParams.pubkey,
+ var response = await client.SendNIP47Request(_connectParams.pubkey,
_connectParams.secret,
bolt11 is null
? new NIP47.PayKeysendRequest()
@@ -415,10 +433,9 @@ public class NostrWalletConnectLightningClient : ILightningClient
Amount = payParams.Amount?.MilliSatoshi is not null
? Convert.ToDecimal(payParams.Amount.MilliSatoshi)
: null,
- }, cancellation);
+ }, cts.Token);
- var payHash = response.PaymentHash??
- payParams?.PaymentHash?.ToString()??
+ var payHash = payParams?.PaymentHash?.ToString()??
(response.Preimage is not null?
ConvertHelper.ToHexString(SHA256.HashData(Convert.FromHexString(response.Preimage))):
BOLT11PaymentRequest.Parse(bolt11, _network).PaymentHash.ToString());
@@ -427,7 +444,7 @@ public class NostrWalletConnectLightningClient : ILightningClient
new NIP47 .LookupInvoiceRequest()
{
PaymentHash = payHash
- }, cancellation);
+ }, cts.Token);
var lp = ToLightningPayment(tx)!;
return new PayResponse(lp.Status == LightningPaymentStatus.Complete ? PayResult.Ok : PayResult.Error,
new PayDetails()
@@ -450,34 +467,34 @@ public class NostrWalletConnectLightningClient : ILightningClient
}
}
- public async Task Pay(string bolt11, CancellationToken cancellation = new CancellationToken())
+ public async Task Pay(string bolt11, CancellationToken cancellation = new())
{
return await Pay(bolt11, new PayInvoiceParams(), cancellation);
}
public async Task OpenChannel(OpenChannelRequest openChannelRequest,
- CancellationToken cancellation = new CancellationToken())
+ CancellationToken cancellation = new())
{
throw new NotSupportedException();
}
- public async Task GetDepositAddress(CancellationToken cancellation = new CancellationToken())
+ public async Task GetDepositAddress(CancellationToken cancellation = new())
{
throw new NotSupportedException();
}
public async Task ConnectTo(NodeInfo nodeInfo,
- CancellationToken cancellation = new CancellationToken())
+ CancellationToken cancellation = new())
{
throw new NotSupportedException();
}
- public async Task CancelInvoice(string invoiceId, CancellationToken cancellation = new CancellationToken())
+ public async Task CancelInvoice(string invoiceId, CancellationToken cancellation = new())
{
throw new NotSupportedException();
}
- public async Task ListChannels(CancellationToken cancellation = new CancellationToken())
+ public async Task ListChannels(CancellationToken cancellation = new())
{
throw new NotSupportedException();
}
diff --git a/Plugins/BTCPayServer.Plugins.NIP05/NostrWalletConnectLightningConnectionStringHandler.cs b/Plugins/BTCPayServer.Plugins.NIP05/NostrWalletConnectLightningConnectionStringHandler.cs
index b0caf6c..8e879e5 100644
--- a/Plugins/BTCPayServer.Plugins.NIP05/NostrWalletConnectLightningConnectionStringHandler.cs
+++ b/Plugins/BTCPayServer.Plugins.NIP05/NostrWalletConnectLightningConnectionStringHandler.cs
@@ -31,7 +31,8 @@ public class NostrWalletConnectLightningConnectionStringHandler : ILightningConn
try
{
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));
var (client, disposable) = _nostrClientPool.GetClientAndConnect(connectParams.relays, cts.Token).ConfigureAwait(false).GetAwaiter().GetResult();
using (disposable)
@@ -59,14 +60,23 @@ public class NostrWalletConnectLightningConnectionStringHandler : ILightningConn
$"The network of the wallet ({walletNetwork}) does not match the network of the server ({network.ChainName})";
return null;
}
+ if (response?.Methods is null || requiredCommands.Any(c => !response.Methods.Contains(c)))
+ {
+ error =
+ "No commands available or not all required commands are available (get_info, make_invoice, lookup_invoice, list_transactions)";
+ return null;
+ }
+
+ (string[] Commands, string[] Notifications) values = (response.Methods ?? commands.Value.Commands,
+ response.Notifications ?? commands.Value.Notifications);
error = null;
- return new NostrWalletConnectLightningClient(_nostrClientPool, uri, network, commands.Value);
+ return new NostrWalletConnectLightningClient(_nostrClientPool, uri, network, values);
}
}
catch (Exception e)
{
- error = "Invalid nostr wallet connect uri";
+ error = "Invalid nostr wallet connect uri: " + e.Message;
return null;
}
}