Exposing LND Rest, providing info in Server/Services (#363)

* Displaying LND Rest connection info in Services

* Code cleanup

* Tweaking UI

* Fix typo
This commit is contained in:
Rockstar Developer
2018-10-27 08:49:39 -05:00
committed by Nicolas Dorier
parent 43bd6587d3
commit e5eb0c79c0
13 changed files with 201 additions and 74 deletions

View File

@@ -127,6 +127,9 @@
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory> <CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
<Pack>$(IncludeRazorContentInPack)</Pack> <Pack>$(IncludeRazorContentInPack)</Pack>
</Content> </Content>
<Content Update="Views\Server\LndRestServices.cshtml">
<Pack>$(IncludeRazorContentInPack)</Pack>
</Content>
<Content Update="Views\Server\SSHService.cshtml"> <Content Update="Views\Server\SSHService.cshtml">
<Pack>$(IncludeRazorContentInPack)</Pack> <Pack>$(IncludeRazorContentInPack)</Pack>
</Content> </Content>
@@ -139,7 +142,7 @@
<Content Update="Views\Public\PayButtonHandle.cshtml"> <Content Update="Views\Public\PayButtonHandle.cshtml">
<Pack>$(IncludeRazorContentInPack)</Pack> <Pack>$(IncludeRazorContentInPack)</Pack>
</Content> </Content>
<Content Update="Views\Server\LNDGRPCServices.cshtml"> <Content Update="Views\Server\LndGrpcServices.cshtml">
<Pack>$(IncludeRazorContentInPack)</Pack> <Pack>$(IncludeRazorContentInPack)</Pack>
</Content> </Content>
<Content Update="Views\Server\Maintenance.cshtml"> <Content Update="Views\Server\Maintenance.cshtml">

View File

@@ -15,6 +15,7 @@ using Renci.SshNet;
using NBitcoin.DataEncoders; using NBitcoin.DataEncoders;
using BTCPayServer.SSH; using BTCPayServer.SSH;
using BTCPayServer.Lightning; using BTCPayServer.Lightning;
using BTCPayServer.Configuration.External;
namespace BTCPayServer.Configuration namespace BTCPayServer.Configuration
{ {
@@ -78,6 +79,7 @@ namespace BTCPayServer.Configuration
setting.ExplorerUri = conf.GetOrDefault<Uri>($"{net.CryptoCode}.explorer.url", net.NBXplorerNetwork.DefaultSettings.DefaultUrl); setting.ExplorerUri = conf.GetOrDefault<Uri>($"{net.CryptoCode}.explorer.url", net.NBXplorerNetwork.DefaultSettings.DefaultUrl);
setting.CookieFile = conf.GetOrDefault<string>($"{net.CryptoCode}.explorer.cookiefile", net.NBXplorerNetwork.DefaultSettings.DefaultCookieFile); setting.CookieFile = conf.GetOrDefault<string>($"{net.CryptoCode}.explorer.cookiefile", net.NBXplorerNetwork.DefaultSettings.DefaultCookieFile);
NBXplorerConnectionSettings.Add(setting); NBXplorerConnectionSettings.Add(setting);
{ {
var lightning = conf.GetOrDefault<string>($"{net.CryptoCode}.lightning", string.Empty); var lightning = conf.GetOrDefault<string>($"{net.CryptoCode}.lightning", string.Empty);
if (lightning.Length != 0) if (lightning.Length != 0)
@@ -99,20 +101,25 @@ namespace BTCPayServer.Configuration
} }
} }
void externalLnd<T>(string code, string lndType)
{ {
var lightning = conf.GetOrDefault<string>($"{net.CryptoCode}.external.lnd.grpc", string.Empty); var lightning = conf.GetOrDefault<string>(code, string.Empty);
if (lightning.Length != 0) if (lightning.Length != 0)
{ {
if (!LightningConnectionString.TryParse(lightning, false, out var connectionString, out var error)) if (!LightningConnectionString.TryParse(lightning, false, out var connectionString, out var error))
{ {
throw new ConfigException($"Invalid setting {net.CryptoCode}.external.lnd.grpc, " + Environment.NewLine + throw new ConfigException($"Invalid setting {code}, " + Environment.NewLine +
$"lnd server: 'type=lnd-grpc;server=https://lnd.example.com;macaroon=abf239...;certthumbprint=2abdf302...'" + Environment.NewLine + $"lnd server: 'type={lndType};server=https://lnd.example.com;macaroon=abf239...;certthumbprint=2abdf302...'" + Environment.NewLine +
$"lnd server: 'type=lnd-grpc;server=https://lnd.example.com;macaroonfilepath=/root/.lnd/admin.macaroon;certthumbprint=2abdf302...'" + Environment.NewLine + $"lnd server: 'type={lndType};server=https://lnd.example.com;macaroonfilepath=/root/.lnd/admin.macaroon;certthumbprint=2abdf302...'" + Environment.NewLine +
error); error);
} }
ExternalServicesByCryptoCode.Add(net.CryptoCode, new ExternalLNDGRPC(connectionString)); var instanceType = typeof(T);
} ExternalServicesByCryptoCode.Add(net.CryptoCode, (ExternalService)Activator.CreateInstance(instanceType, connectionString));
} }
};
externalLnd<ExternalLndGrpc>($"{net.CryptoCode}.external.lnd.grpc", "lnd-grpc");
externalLnd<ExternalLndRest>($"{net.CryptoCode}.external.lnd.rest", "lnd-rest");
} }
Logs.Configuration.LogInformation("Supported chains: " + String.Join(',', supportedChains.ToArray())); Logs.Configuration.LogInformation("Supported chains: " + String.Join(',', supportedChains.ToArray()));
@@ -250,29 +257,4 @@ namespace BTCPayServer.Configuration
return builder.ToString(); return builder.ToString();
} }
} }
public class ExternalServices : MultiValueDictionary<string, ExternalService>
{
public IEnumerable<T> GetServices<T>(string cryptoCode) where T : ExternalService
{
if (!this.TryGetValue(cryptoCode.ToUpperInvariant(), out var services))
return Array.Empty<T>();
return services.OfType<T>();
}
}
public class ExternalService
{
}
public class ExternalLNDGRPC : ExternalService
{
public ExternalLNDGRPC(LightningConnectionString connectionString)
{
ConnectionString = connectionString;
}
public LightningConnectionString ConnectionString { get; set; }
}
} }

View File

@@ -0,0 +1,35 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using BTCPayServer.Lightning;
namespace BTCPayServer.Configuration.External
{
public abstract class ExternalLnd : ExternalService
{
public ExternalLnd(LightningConnectionString connectionString, LndTypes type)
{
ConnectionString = connectionString;
Type = type;
}
public LndTypes Type { get; set; }
public LightningConnectionString ConnectionString { get; set; }
}
public enum LndTypes
{
gRPC, Rest
}
public class ExternalLndGrpc : ExternalLnd
{
public ExternalLndGrpc(LightningConnectionString connectionString) : base(connectionString, LndTypes.gRPC) { }
}
public class ExternalLndRest : ExternalLnd
{
public ExternalLndRest(LightningConnectionString connectionString) : base(connectionString, LndTypes.Rest) { }
}
}

View File

@@ -0,0 +1,22 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using BTCPayServer.Lightning;
namespace BTCPayServer.Configuration.External
{
public class ExternalServices : MultiValueDictionary<string, ExternalService>
{
public IEnumerable<T> GetServices<T>(string cryptoCode) where T : ExternalService
{
if (!this.TryGetValue(cryptoCode.ToUpperInvariant(), out var services))
return Array.Empty<T>();
return services.OfType<T>();
}
}
public class ExternalService
{
}
}

View File

@@ -25,6 +25,7 @@ using System.Threading.Tasks;
using Renci.SshNet; using Renci.SshNet;
using BTCPayServer.Logging; using BTCPayServer.Logging;
using BTCPayServer.Lightning; using BTCPayServer.Lightning;
using BTCPayServer.Configuration.External;
namespace BTCPayServer.Controllers namespace BTCPayServer.Controllers
{ {
@@ -420,36 +421,34 @@ namespace BTCPayServer.Controllers
{ {
var result = new ServicesViewModel(); var result = new ServicesViewModel();
foreach (var cryptoCode in _Options.ExternalServicesByCryptoCode.Keys) foreach (var cryptoCode in _Options.ExternalServicesByCryptoCode.Keys)
{
{ {
int i = 0; int i = 0;
foreach (var grpcService in _Options.ExternalServicesByCryptoCode.GetServices<ExternalLNDGRPC>(cryptoCode)) foreach (var grpcService in _Options.ExternalServicesByCryptoCode.GetServices<ExternalLnd>(cryptoCode))
{ {
result.LNDServices.Add(new ServicesViewModel.LNDServiceViewModel() result.LNDServices.Add(new ServicesViewModel.LNDServiceViewModel()
{ {
Crypto = cryptoCode, Crypto = cryptoCode,
Type = "gRPC", Type = grpcService.Type,
Index = i++, Index = i++,
}); });
} }
} }
}
result.HasSSH = _Options.SSHSettings != null; result.HasSSH = _Options.SSHSettings != null;
return View(result); return View(result);
} }
[Route("server/services/lnd-grpc/{cryptoCode}/{index}")] [Route("server/services/lnd-grpc/{cryptoCode}/{index}")]
public IActionResult LNDGRPCServices(string cryptoCode, int index, uint? nonce) public IActionResult LndGrpcServices(string cryptoCode, int index, uint? nonce)
{ {
if (!_dashBoard.IsFullySynched(cryptoCode, out var unusud)) if (!_dashBoard.IsFullySynched(cryptoCode, out var unusud))
{ {
StatusMessage = $"Error: {cryptoCode} is not fully synched"; StatusMessage = $"Error: {cryptoCode} is not fully synched";
return RedirectToAction(nameof(Services)); return RedirectToAction(nameof(Services));
} }
var external = GetExternalLNDConnectionString(cryptoCode, index); var external = GetExternalLndConnectionString(cryptoCode, index);
if (external == null) if (external == null)
return NotFound(); return NotFound();
var model = new LNDGRPCServicesViewModel(); var model = new LndGrpcServicesViewModel();
model.Host = $"{external.BaseUri.DnsSafeHost}:{external.BaseUri.Port}"; model.Host = $"{external.BaseUri.DnsSafeHost}:{external.BaseUri.Port}";
model.SSL = external.BaseUri.Scheme == "https"; model.SSL = external.BaseUri.Scheme == "https";
@@ -493,9 +492,9 @@ namespace BTCPayServer.Controllers
[Route("server/services/lnd-grpc/{cryptoCode}/{index}")] [Route("server/services/lnd-grpc/{cryptoCode}/{index}")]
[HttpPost] [HttpPost]
public IActionResult LNDGRPCServicesPOST(string cryptoCode, int index) public IActionResult LndGrpcServicesPost(string cryptoCode, int index)
{ {
var external = GetExternalLNDConnectionString(cryptoCode, index); var external = GetExternalLndConnectionString(cryptoCode, index);
if (external == null) if (external == null)
return NotFound(); return NotFound();
LightningConfigurations confs = new LightningConfigurations(); LightningConfigurations confs = new LightningConfigurations();
@@ -513,12 +512,12 @@ namespace BTCPayServer.Controllers
var nonce = RandomUtils.GetUInt32(); var nonce = RandomUtils.GetUInt32();
var configKey = GetConfigKey("lnd-grpc", cryptoCode, index, nonce); var configKey = GetConfigKey("lnd-grpc", cryptoCode, index, nonce);
_LnConfigProvider.KeepConfig(configKey, confs); _LnConfigProvider.KeepConfig(configKey, confs);
return RedirectToAction(nameof(LNDGRPCServices), new { cryptoCode = cryptoCode, nonce = nonce }); return RedirectToAction(nameof(LndGrpcServices), new { cryptoCode = cryptoCode, nonce = nonce });
} }
private LightningConnectionString GetExternalLNDConnectionString(string cryptoCode, int index) private LightningConnectionString GetExternalLndConnectionString(string cryptoCode, int index)
{ {
var connectionString = _Options.ExternalServicesByCryptoCode.GetServices<ExternalLNDGRPC>(cryptoCode).Skip(index).Select(c => c.ConnectionString).FirstOrDefault(); var connectionString = _Options.ExternalServicesByCryptoCode.GetServices<ExternalLnd>(cryptoCode).Skip(index).Select(c => c.ConnectionString).FirstOrDefault();
if (connectionString == null) if (connectionString == null)
return null; return null;
connectionString = connectionString.Clone(); connectionString = connectionString.Clone();
@@ -531,13 +530,35 @@ namespace BTCPayServer.Controllers
} }
catch catch
{ {
Logging.Logs.Configuration.LogWarning($"{cryptoCode}: The macaroon file path of the external LND grpc config was not found ({connectionString.MacaroonFilePath})"); Logs.Configuration.LogWarning($"{cryptoCode}: The macaroon file path of the external LND grpc config was not found ({connectionString.MacaroonFilePath})");
return null; return null;
} }
} }
return connectionString; return connectionString;
} }
[Route("server/services/lnd-rest/{cryptoCode}/{index}")]
public IActionResult LndRestServices(string cryptoCode, int index, uint? nonce)
{
if (!_dashBoard.IsFullySynched(cryptoCode, out var unusud))
{
StatusMessage = $"Error: {cryptoCode} is not fully synched";
return RedirectToAction(nameof(Services));
}
var external = GetExternalLndConnectionString(cryptoCode, index);
if (external == null)
return NotFound();
var model = new LndRestServicesViewModel();
model.BaseApiUrl = external.BaseUri.ToString();
if (external.CertificateThumbprint != null)
model.CertificateThumbprint = Encoders.Hex.EncodeData(external.CertificateThumbprint);
if (external.Macaroon != null)
model.Macaroon = Encoders.Hex.EncodeData(external.Macaroon);
return View(model);
}
[Route("server/services/ssh")] [Route("server/services/ssh")]
public IActionResult SSHService(bool downloadKeyFile = false) public IActionResult SSHService(bool downloadKeyFile = false)
{ {

View File

@@ -114,7 +114,7 @@ namespace BTCPayServer.Controllers
} }
if(!System.IO.File.Exists(connectionString.MacaroonFilePath)) if(!System.IO.File.Exists(connectionString.MacaroonFilePath))
{ {
ModelState.AddModelError(nameof(vm.ConnectionString), "The macaroonfilepath file does exist"); ModelState.AddModelError(nameof(vm.ConnectionString), "The macaroonfilepath file does not exist");
return View(vm); return View(vm);
} }
if(!System.IO.Path.IsPathRooted(connectionString.MacaroonFilePath)) if(!System.IO.Path.IsPathRooted(connectionString.MacaroonFilePath))

View File

@@ -5,7 +5,7 @@ using System.Threading.Tasks;
namespace BTCPayServer.Models.ServerViewModels namespace BTCPayServer.Models.ServerViewModels
{ {
public class LNDGRPCServicesViewModel public class LndGrpcServicesViewModel
{ {
public string Host { get; set; } public string Host { get; set; }
public bool SSL { get; set; } public bool SSL { get; set; }

View File

@@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace BTCPayServer.Models.ServerViewModels
{
public class LndRestServicesViewModel
{
public string BaseApiUrl { get; set; }
public string Macaroon { get; set; }
public string CertificateThumbprint { get; set; }
}
}

View File

@@ -2,6 +2,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using BTCPayServer.Configuration.External;
namespace BTCPayServer.Models.ServerViewModels namespace BTCPayServer.Models.ServerViewModels
{ {
@@ -10,7 +11,7 @@ namespace BTCPayServer.Models.ServerViewModels
public class LNDServiceViewModel public class LNDServiceViewModel
{ {
public string Crypto { get; set; } public string Crypto { get; set; }
public string Type { get; set; } public LndTypes Type { get; set; }
public int Index { get; set; } public int Index { get; set; }
} }
public List<LNDServiceViewModel> LNDServices { get; set; } = new List<LNDServiceViewModel>(); public List<LNDServiceViewModel> LNDServices { get; set; } = new List<LNDServiceViewModel>();

View File

@@ -10,6 +10,7 @@
"BTCPAY_LTCEXPLORERURL": "http://127.0.0.1:32838/", "BTCPAY_LTCEXPLORERURL": "http://127.0.0.1:32838/",
"BTCPAY_BTCLIGHTNING": "type=charge;server=http://127.0.0.1:54938/;api-token=foiewnccewuify", "BTCPAY_BTCLIGHTNING": "type=charge;server=http://127.0.0.1:54938/;api-token=foiewnccewuify",
"BTCPAY_BTCEXTERNALLNDGRPC": "type=lnd-grpc;server=https://lnd:lnd@127.0.0.1:53280/;allowinsecure=true", "BTCPAY_BTCEXTERNALLNDGRPC": "type=lnd-grpc;server=https://lnd:lnd@127.0.0.1:53280/;allowinsecure=true",
"BTCPAY_BTCEXTERNALLNDREST": "type=lnd-rest;server=https://lnd:lnd@127.0.0.1:53280/lnd-rest/btc/;allowinsecure=true;macaroonfilepath=D:\\admin.macaroon",
"BTCPAY_BTCEXPLORERURL": "http://127.0.0.1:32838/", "BTCPAY_BTCEXPLORERURL": "http://127.0.0.1:32838/",
"ASPNETCORE_ENVIRONMENT": "Development", "ASPNETCORE_ENVIRONMENT": "Development",
"BTCPAY_CHAINS": "btc,ltc", "BTCPAY_CHAINS": "btc,ltc",

View File

@@ -1,10 +1,10 @@
@model BTCPayServer.Models.ServerViewModels.LNDGRPCServicesViewModel @model LndGrpcServicesViewModel
@{ @{
ViewData.SetActivePageAndTitle(ServerNavPages.Services); ViewData.SetActivePageAndTitle(ServerNavPages.Services);
} }
<h4>GRPC settings</h4> <h4>LND GRPC</h4>
<partial name="_StatusMessage" for="@TempData["StatusMessage"]" /> <partial name="_StatusMessage" for="@TempData["StatusMessage"]" />
<div class="row"> <div class="row">
<div class="col-md-6"> <div class="col-md-6">

View File

@@ -0,0 +1,41 @@
@model LndRestServicesViewModel
@{
ViewData.SetActivePageAndTitle(ServerNavPages.Services);
}
<h4>LND REST</h4>
<partial name="_StatusMessage" for="@TempData["StatusMessage"]" />
<div class="row">
<div class="col-md-6">
<div asp-validation-summary="All" class="text-danger"></div>
</div>
</div>
<div class="row">
<div class="col-md-8">
<div class="form-group">
<p>BTCPay exposes LND Rest services for outside consumption. See connection information below</p>
</div>
<div class="form-group">
<label asp-for="BaseApiUrl">Base API Url</label>
<input asp-for="BaseApiUrl" readonly class="form-control" />
</div>
@if (Model.Macaroon != null)
{
<div class="form-group">
<label asp-for="Macaroon"></label>
<input asp-for="Macaroon" readonly class="form-control" />
</div>
}
@if (Model.CertificateThumbprint != null)
{
<div class="form-group">
<label asp-for="CertificateThumbprint"></label>
<input asp-for="CertificateThumbprint" readonly class="form-control" />
</div>
}
</div>
</div>

View File

@@ -16,7 +16,7 @@
<div class="col-md-8"> <div class="col-md-8">
<div class="form-group"> <div class="form-group">
<span>You can get access here to LND-gRPC or SSH services exposed by your server</span> <span>You can get access here to LND (gRPC, Rest) or SSH services exposed by your server</span>
</div> </div>
<div class="form-group"> <div class="form-group">
@@ -33,9 +33,16 @@
{ {
<tr> <tr>
<td>@lnd.Crypto</td> <td>@lnd.Crypto</td>
<td>@lnd.Type</td> <td>LND @lnd.Type.ToString()</td>
<td style="text-align:right"> <td style="text-align:right">
@if (lnd.Type == BTCPayServer.Configuration.External.LndTypes.gRPC)
{
<a asp-action="LNDGRPCServices" asp-route-cryptoCode="@lnd.Crypto" asp-route-index="@lnd.Index">See information</a> <a asp-action="LNDGRPCServices" asp-route-cryptoCode="@lnd.Crypto" asp-route-index="@lnd.Index">See information</a>
}
else if (lnd.Type == BTCPayServer.Configuration.External.LndTypes.Rest)
{
<a asp-action="LndRestServices" asp-route-cryptoCode="@lnd.Crypto" asp-route-index="@lnd.Index">See information</a>
}
</td> </td>
</tr> </tr>
} }