Improve public LN node info (#2876)

* Add store name to LN info unavailable pae

* Display multiple node info items if available

Allows to view clearnet and Tor connection info side by side.

* Re-add preferOnion for certain cases

* HTML compatible node ids

* Display more node connection failure details

* Fix syntax error

* Update BTCPayServer/Payments/Lightning/LightningLikePaymentHandler.cs

Co-authored-by: Nicolas Dorier <nicolas.dorier@gmail.com>

* View updates

* Revert previous variable change

* Keep logic out of the view

Co-authored-by: Nicolas Dorier <nicolas.dorier@gmail.com>
This commit is contained in:
d11n 2021-09-23 13:36:42 +02:00 committed by GitHub
parent 5d8bc73063
commit 7f49824783
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 73 additions and 25 deletions

View file

@ -3,6 +3,7 @@ using System.Linq;
using System.Threading.Tasks;
using BTCPayServer.Data;
using BTCPayServer.Filters;
using BTCPayServer.Lightning;
using BTCPayServer.Payments;
using BTCPayServer.Payments.Lightning;
using BTCPayServer.Services.Stores;
@ -40,14 +41,12 @@ namespace BTCPayServer.Controllers
{
var paymentMethodDetails = GetExistingLightningSupportedPaymentMethod(cryptoCode, store);
var network = _BtcPayNetworkProvider.GetNetwork<BTCPayNetwork>(cryptoCode);
var nodeInfo =
await _LightningLikePaymentHandler.GetNodeInfo(Request.IsOnion(), paymentMethodDetails,
network);
var nodeInfo = await _LightningLikePaymentHandler.GetNodeInfo(paymentMethodDetails, network);
return View(new ShowLightningNodeInfoViewModel
{
Available = true,
NodeInfo = nodeInfo.ToString(),
NodeInfo = nodeInfo.Select(n => new ShowLightningNodeInfoViewModel.NodeData(n)).ToArray(),
CryptoCode = cryptoCode,
CryptoImage = GetImage(paymentMethodDetails.PaymentId, network),
StoreName = store.StoreName
@ -55,7 +54,12 @@ namespace BTCPayServer.Controllers
}
catch (Exception)
{
return View(new ShowLightningNodeInfoViewModel { Available = false, CryptoCode = cryptoCode });
return View(new ShowLightningNodeInfoViewModel
{
Available = false,
CryptoCode = cryptoCode,
StoreName = store.StoreName
});
}
}
@ -78,9 +82,26 @@ namespace BTCPayServer.Controllers
}
}
public class ShowLightningNodeInfoViewModel
{
public string NodeInfo { get; set; }
public class NodeData
{
string _connection;
public NodeData(NodeInfo nodeInfo)
{
_connection = nodeInfo.ToString();
Id = $"{nodeInfo.Host}-{nodeInfo.Port}".Replace(".", "-", StringComparison.OrdinalIgnoreCase);
IsTor = nodeInfo.IsTor;
}
public string Id { get; }
public bool IsTor { get; }
public override string ToString()
{
return _connection;
}
}
public NodeData[] NodeInfo { get; set; }
public bool Available { get; set; }
public string CryptoCode { get; set; }
public string CryptoImage { get; set; }

View file

@ -109,13 +109,13 @@ namespace BTCPayServer.Controllers
var handler = _ServiceProvider.GetRequiredService<LightningLikePaymentHandler>();
try
{
var info = await handler.GetNodeInfo(Request.IsOnion(), paymentMethod, network);
var info = await handler.GetNodeInfo(paymentMethod, network, Request.IsOnion());
if (!vm.SkipPortTest)
{
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(20));
await handler.TestConnection(info, cts.Token);
await handler.TestConnection(info.First(), cts.Token);
}
TempData[WellKnownTempData.SuccessMessage] = $"Connection to the Lightning node successful. Your node address: {info}";
TempData[WellKnownTempData.SuccessMessage] = $"Connection to the Lightning node successful. Your node address: {info.First()}";
}
catch (Exception ex)
{

View file

@ -68,7 +68,7 @@ namespace BTCPayServer.Payments.Lightning
}
//direct casting to (BTCPayNetwork) is fixed in other pull requests with better generic interfacing for handlers
var storeBlob = store.GetStoreBlob();
var test = GetNodeInfo(paymentMethod.PreferOnion, supportedPaymentMethod, network);
var test = GetNodeInfo(supportedPaymentMethod, network, paymentMethod.PreferOnion);
var invoice = paymentMethod.ParentEntity;
decimal due = Extensions.RoundUp(invoice.Price / paymentMethod.Rate, network.Divisibility);
@ -108,17 +108,18 @@ namespace BTCPayServer.Payments.Lightning
throw new PaymentMethodUnavailableException($"Impossible to create lightning invoice ({ex.Message})", ex);
}
}
var nodeInfo = await test;
return new LightningLikePaymentMethodDetails
{
Activated = true,
BOLT11 = lightningInvoice.BOLT11,
InvoiceId = lightningInvoice.Id,
NodeInfo = nodeInfo.ToString()
NodeInfo = nodeInfo.First().ToString()
};
}
public async Task<NodeInfo> GetNodeInfo(bool preferOnion, LightningSupportedPaymentMethod supportedPaymentMethod, BTCPayNetwork network)
public async Task<NodeInfo[]> GetNodeInfo(LightningSupportedPaymentMethod supportedPaymentMethod, BTCPayNetwork network, bool? preferOnion = null)
{
if (!_Dashboard.IsFullySynched(network.CryptoCode, out var summary))
throw new PaymentMethodUnavailableException("Full node not available");
@ -137,10 +138,15 @@ namespace BTCPayServer.Payments.Lightning
}
catch (Exception ex)
{
throw new PaymentMethodUnavailableException($"Error while connecting to the API ({ex.Message})");
throw new PaymentMethodUnavailableException($"Error while connecting to the API: {ex.Message}" +
(!string.IsNullOrEmpty(ex.InnerException?.Message) ? $" ({ex.InnerException.Message})" : ""));
}
var nodeInfo = info.NodeInfoList.FirstOrDefault(i => i.IsTor == preferOnion) ?? info.NodeInfoList.FirstOrDefault();
if (nodeInfo == null)
var nodeInfo = preferOnion != null && info.NodeInfoList.Any(i => i.IsTor == preferOnion)
? info.NodeInfoList.Where(i => i.IsTor == preferOnion.Value).ToArray()
: info.NodeInfoList.Select(i => i).ToArray();
if (!nodeInfo.Any())
{
throw new PaymentMethodUnavailableException("No lightning node public address has been configured");
}

View file

@ -1,7 +1,8 @@
@addTagHelper *, BundlerMinifier.TagHelpers
@addTagHelper *, BundlerMinifier.TagHelpers
@inject ISettingsRepository _settingsRepository
@using BTCPayServer.Abstractions.Contracts
@using BTCPayServer.Lightning
@model BTCPayServer.Controllers.ShowLightningNodeInfoViewModel
@{
Layout = null;
@ -28,7 +29,7 @@
<div class="card border-0">
<div class="card-body p-4">
<h1 class="card-title text-center">@Model.StoreName</h1>
<h2 class="card-subtitle text-center text-secondary mb-2">
<h2 class="card-subtitle text-center text-secondary my-3">
<span>@Model.CryptoCode</span>
Lightning Node
</h2>
@ -42,13 +43,33 @@
</h3>
@if (Model.Available)
{
<div class="qr-container my-3">
<img alt="@Model.CryptoCode" class="qr-icon" src="@Model.CryptoImage" />
<vc:qr-code data="@Model.NodeInfo"> </vc:qr-code>
</div>
<div class="input-group d-flex" data-clipboard="@Model.NodeInfo">
<input type="text" class="form-control" style="cursor: copy" readonly="readonly" value="@Model.NodeInfo" id="peer-info"/>
<button type="button" class="btn btn-outline-secondary" data-clipboard-confirm>Copy node info</button>
@if (Model.NodeInfo.Length > 1)
{
<ul class="nav nav-pills justify-content-center mt-4" id="nodeInfo-tab" role="tablist">
@for (int i = 0; i < Model.NodeInfo.Length; i++)
{
var nodeInfo = Model.NodeInfo[i];
<li class="nav-item" role="presentation">
<button class="nav-link w-100px @(i == 0 ? "active" : "")" id="nodeInfo-tab-@nodeInfo.Id" data-bs-toggle="pill" data-bs-target="#nodeInfo-@nodeInfo.Id" type="button" role="tab" aria-controls="nodeInfo-@nodeInfo.Id" aria-selected="true">@(Model.NodeInfo[i].IsTor ? "Tor" : "Clearnet")</button>
</li>
}
</ul>
}
<div class="tab-content" id="nodeInfo-tabContent">
@for (int i = 0; i < Model.NodeInfo.Length; i++)
{
var nodeInfo = Model.NodeInfo[i];
<div class="tab-pane fade @(i == 0 ? "show active" : "")" id="nodeInfo-@nodeInfo.Id" role="tabpanel" aria-labelledby="nodeInfo-tab-@nodeInfo.Id">
<div class="qr-container my-3">
<img alt="@Model.CryptoCode" class="qr-icon" src="@Model.CryptoImage"/>
<vc:qr-code data="@nodeInfo.ToString()"/>
</div>
<div class="input-group" data-clipboard="@nodeInfo.ToString()">
<input type="text" class="form-control" style="cursor:copy" readonly="readonly" value="@nodeInfo.ToString()" id="nodeInfo-addr-@nodeInfo.Id"/>
<button type="button" class="btn btn-outline-secondary" data-clipboard-confirm>Copy</button>
</div>
</div>
}
</div>
}
</div>
@ -56,6 +77,6 @@
</div>
</div>
</div>
<script src="~/js/copy-to-clipboard.js" asp-append-version="true"></script>
<partial name="LayoutFoot" />
</body>
</html>