Fix: Nostr Lighthning Client was always failing

This commit is contained in:
nicolas.dorier
2024-11-08 21:08:22 +09:00
parent beda7741a3
commit d5f664ad9b

View File

@@ -63,7 +63,7 @@ public class NostrWalletConnectLightningClient : ILightningClient
var expired = !isPaid && expiresAt < DateTimeOffset.UtcNow; var expired = !isPaid && expiresAt < DateTimeOffset.UtcNow;
var s = tx.SettledAt is not null var s = tx.SettledAt is not null
? DateTimeOffset.FromUnixTimeSeconds(tx.SettledAt.Value) ? DateTimeOffset.FromUnixTimeSeconds(tx.SettledAt.Value)
: (DateTimeOffset?) null; : (DateTimeOffset?)null;
return new LightningInvoice() return new LightningInvoice()
{ {
PaymentHash = tx.PaymentHash, PaymentHash = tx.PaymentHash,
@@ -118,7 +118,7 @@ public class NostrWalletConnectLightningClient : ILightningClient
new NIP47.ListTransactionsRequest() new NIP47.ListTransactionsRequest()
{ {
Type = "incoming", Type = "incoming",
Offset = (int) (request.OffsetIndex ?? 0), Offset = (int)(request.OffsetIndex ?? 0),
Unpaid = request.PendingOnly ?? false, Unpaid = request.PendingOnly ?? false,
}, cts.Token); }, cts.Token);
@@ -144,7 +144,7 @@ public class NostrWalletConnectLightningClient : ILightningClient
var expired = !isPaid && expiresAt < DateTimeOffset.UtcNow; var expired = !isPaid && expiresAt < DateTimeOffset.UtcNow;
var s = tx.SettledAt is not null var s = tx.SettledAt is not null
? DateTimeOffset.FromUnixTimeSeconds(tx.SettledAt.Value) ? DateTimeOffset.FromUnixTimeSeconds(tx.SettledAt.Value)
: (DateTimeOffset?) null; : (DateTimeOffset?)null;
return new LightningPayment() return new LightningPayment()
{ {
PaymentHash = tx.PaymentHash, PaymentHash = tx.PaymentHash,
@@ -169,11 +169,21 @@ public class NostrWalletConnectLightningClient : ILightningClient
var (nostrClient, usage) = await _nostrClientPool.GetClientAndConnect(_connectParams.relays, cts.Token); var (nostrClient, usage) = await _nostrClientPool.GetClientAndConnect(_connectParams.relays, cts.Token);
using (usage) using (usage)
{ {
var tx = await nostrClient.SendNIP47Request<NIP47.Nip47Transaction>(_connectParams.pubkey, _connectParams.secret, NIP47.Nip47Transaction tx;
new NIP47.LookupInvoiceRequest() try
{ {
PaymentHash = paymentHash tx = await nostrClient.SendNIP47Request<NIP47.Nip47Transaction>(_connectParams.pubkey, _connectParams.secret,
}, cts.Token); new NIP47.LookupInvoiceRequest()
{
PaymentHash = paymentHash
}, cts.Token);
}
// The standard says it returns NOT_FOUND error, but
// Alby returns INTERNAL error... Probably safer to catch all
catch (Exception)
{
return null;
}
return ToLightningPayment(tx)!; return ToLightningPayment(tx)!;
} }
} }
@@ -197,7 +207,7 @@ public class NostrWalletConnectLightningClient : ILightningClient
new NIP47.ListTransactionsRequest() new NIP47.ListTransactionsRequest()
{ {
Type = "outgoing", Type = "outgoing",
Offset = (int) (request.OffsetIndex ?? 0), Offset = (int)(request.OffsetIndex ?? 0),
Unpaid = request.IncludePending ?? false, Unpaid = request.IncludePending ?? false,
}, cts.Token); }, cts.Token);
return response.Transactions.Select(ToLightningPayment).Where(i => i is not null).ToArray()!; return response.Transactions.Select(ToLightningPayment).Where(i => i is not null).ToArray()!;
@@ -229,7 +239,7 @@ public class NostrWalletConnectLightningClient : ILightningClient
? null ? null
: createInvoiceRequest.Description, : createInvoiceRequest.Description,
DescriptionHash = createInvoiceRequest.DescriptionHash?.ToString(), DescriptionHash = createInvoiceRequest.DescriptionHash?.ToString(),
ExpirySeconds = (int) createInvoiceRequest.Expiry.TotalSeconds, ExpirySeconds = (int)createInvoiceRequest.Expiry.TotalSeconds,
}, cts.Token); }, cts.Token);
return ToLightningInvoice(response, _network)!; return ToLightningInvoice(response, _network)!;
} }
@@ -237,7 +247,7 @@ public class NostrWalletConnectLightningClient : ILightningClient
public async Task<ILightningInvoiceListener> Listen(CancellationToken cancellation = new()) public async Task<ILightningInvoiceListener> Listen(CancellationToken cancellation = new())
{ {
var x = await _nostrClientPool.GetClientAndConnect(_connectParams.relays, 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);
@@ -319,7 +329,7 @@ public class NostrWalletConnectLightningClient : ILightningClient
while (!_cts.IsCancellationRequested) while (!_cts.IsCancellationRequested)
{ {
var cts = CancellationTokenSource.CreateLinkedTokenSource(_cts.Token); var cts = CancellationTokenSource.CreateLinkedTokenSource(_cts.Token);
cts.CancelAfter(TimeSpan.FromSeconds(10)); cts.CancelAfter(TimeSpan.FromSeconds(10));
var paid = await _client.SendNIP47Request<NIP47.ListTransactionsResponse>(_connectparams.pubkey, var paid = await _client.SendNIP47Request<NIP47.ListTransactionsResponse>(_connectparams.pubkey,
_connectparams.secret, new NIP47.ListTransactionsRequest() _connectparams.secret, new NIP47.ListTransactionsRequest()
{ {
@@ -327,7 +337,7 @@ public class NostrWalletConnectLightningClient : ILightningClient
// Unpaid = true, //seems like this is ignored... so we only get paid ones // Unpaid = true, //seems like this is ignored... so we only get paid ones
Limit = 300 Limit = 300
}, cancellationToken: cts.Token); }, cancellationToken: cts.Token);
paid.Transactions = paid.Transactions.Where(i => i is {Type: "incoming", SettledAt: not null}).ToArray(); paid.Transactions = paid.Transactions.Where(i => i is { Type: "incoming", SettledAt: not null }).ToArray();
if (_lastPaid is not null) if (_lastPaid is not null)
{ {
@@ -342,7 +352,7 @@ public class NostrWalletConnectLightningClient : ILightningClient
// .Select(i => ToLightningInvoice(i, _network)!); // .Select(i => ToLightningInvoice(i, _network)!);
foreach (var invoice in paidInvoicesSinceLastPoll) foreach (var invoice in paidInvoicesSinceLastPoll)
{ {
await queue.Writer.WriteAsync(invoice,_cts.Token); await queue.Writer.WriteAsync(invoice, _cts.Token);
} }
} }
@@ -379,13 +389,13 @@ public class NostrWalletConnectLightningClient : ILightningClient
{ {
var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellation); var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellation);
cts.CancelAfter(TimeSpan.FromSeconds(5)); cts.CancelAfter(TimeSpan.FromSeconds(5));
var (client, usage) = await _nostrClientPool.GetClientAndConnect(_connectParams.relays, cts.Token); var (client, usage) = await _nostrClientPool.GetClientAndConnect(_connectParams.relays, cts.Token);
using (usage) using (usage)
{ {
var response = await client.SendNIP47Request<NIP47.GetBalanceResponse>(_connectParams.pubkey, var response = await client.SendNIP47Request<NIP47.GetBalanceResponse>(_connectParams.pubkey,
_connectParams.secret, _connectParams.secret,
new NIP47.NIP47Request("get_balance"), cts.Token); new NIP47.NIP47Request("get_balance"), cts.Token);
return new LightningNodeBalance() return new LightningNodeBalance()
{ {
OffchainBalance = new OffchainBalance() OffchainBalance = new OffchainBalance()
@@ -405,43 +415,45 @@ public class NostrWalletConnectLightningClient : ILightningClient
public async Task<PayResponse> Pay(string bolt11, PayInvoiceParams payParams, public async Task<PayResponse> Pay(string bolt11, PayInvoiceParams payParams,
CancellationToken cancellation = new()) CancellationToken cancellation = new())
{ {
try var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellation);
cts.CancelAfter(TimeSpan.FromSeconds(10));
var (client, usage) = await _nostrClientPool.GetClientAndConnect(_connectParams.relays, cts.Token);
using (usage)
{ {
NIP47.INIP47Request request;
var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellation); if (bolt11 is null)
cts.CancelAfter(TimeSpan.FromSeconds(10));
var (client, usage) = await _nostrClientPool.GetClientAndConnect(_connectParams.relays, cts.Token);
using (usage)
{ {
var response = await client.SendNIP47Request<NIP47.PayInvoiceResponse>(_connectParams.pubkey, request = new NIP47.PayKeysendRequest()
_connectParams.secret, {
bolt11 is null Amount = Convert.ToDecimal(payParams.Amount.MilliSatoshi),
? new NIP47.PayKeysendRequest() Pubkey = payParams.Destination.ToHex(),
{ TlvRecords = payParams.CustomRecords?.Select(kv => new NIP47.TlvRecord()
Amount = Convert.ToDecimal(payParams.Amount.MilliSatoshi), {
Pubkey = payParams.Destination.ToHex(), Type = kv.Key.ToString(),
TlvRecords = payParams.CustomRecords?.Select(kv => new NIP47.TlvRecord() Value = kv.Value
{ }).ToArray()
Type = kv.Key.ToString(), };
Value = kv.Value
}).ToArray()
}
: new NIP47.PayInvoiceRequest()
{
Invoice = bolt11,
Amount = payParams.Amount?.MilliSatoshi is not null
? Convert.ToDecimal(payParams.Amount.MilliSatoshi)
: null,
}, cts.Token);
var payHash = payParams?.PaymentHash?.ToString()?? }
(response.Preimage is not null? else
ConvertHelper.ToHexString(SHA256.HashData(Convert.FromHexString(response.Preimage))): {
BOLT11PaymentRequest.Parse(bolt11, _network).PaymentHash.ToString()); request = new NIP47.PayInvoiceRequest()
{
Invoice = bolt11,
Amount = payParams.Amount?.MilliSatoshi is not null
? Convert.ToDecimal(payParams.Amount.MilliSatoshi)
: null,
};
}
var response = await client.SendNIP47Request<NIP47.PayInvoiceResponse>(_connectParams.pubkey, _connectParams.secret, request, cts.Token);
var payHash = ConvertHelper.ToHexString(SHA256.HashData(Convert.FromHexString(response.Preimage)));
try
{
var tx = await client.SendNIP47Request<NIP47.Nip47Transaction>(_connectParams.pubkey, _connectParams.secret, var tx = await client.SendNIP47Request<NIP47.Nip47Transaction>(_connectParams.pubkey, _connectParams.secret,
new NIP47 .LookupInvoiceRequest() new NIP47.LookupInvoiceRequest()
{ {
PaymentHash = payHash PaymentHash = payHash
}, cts.Token); }, cts.Token);
@@ -456,15 +468,18 @@ public class NostrWalletConnectLightningClient : ILightningClient
FeeAmount = lp.Fee FeeAmount = lp.Fee
}); });
} }
} catch (Exception e)
catch (Exception e)
{
return new PayResponse()
{ {
Result = PayResult.Error, return new PayResponse(PayResult.Ok, new PayDetails()
ErrorDetail = e.Message {
}; Status = LightningPaymentStatus.Complete,
PaymentHash = new uint256(payHash),
Preimage = new uint256(response.Preimage),
})
{ ErrorDetail = e.Message };
}
} }
} }
public async Task<PayResponse> Pay(string bolt11, CancellationToken cancellation = new()) public async Task<PayResponse> Pay(string bolt11, CancellationToken cancellation = new())