Simplify LND implementation

This commit is contained in:
nicolas.dorier
2018-07-08 18:55:48 +09:00
parent 0879895678
commit b9e8408db5
7 changed files with 51 additions and 94 deletions

View File

@@ -15,12 +15,12 @@ namespace BTCPayServer.Tests.Lnd
this._Parent = serverTester; this._Parent = serverTester;
var url = serverTester.GetEnvironment(environmentName, defaultValue); var url = serverTester.GetEnvironment(environmentName, defaultValue);
Swagger = LndSwaggerClientCustomHttp.Create(new Uri(url), network); Swagger = new LndSwaggerClient(new LndRestSettings(new Uri(url)));
Client = new LndInvoiceClient(Swagger); Client = new LndInvoiceClient(Swagger);
P2PHost = _Parent.GetEnvironment(environmentName + "_HOST", defaultHost); P2PHost = _Parent.GetEnvironment(environmentName + "_HOST", defaultHost);
} }
public LndSwaggerClientCustomHttp Swagger { get; set; } public LndSwaggerClient Swagger { get; set; }
public LndInvoiceClient Client { get; set; } public LndInvoiceClient Client { get; set; }
public string P2PHost { get; } public string P2PHost { get; }
} }

View File

@@ -25,16 +25,16 @@ namespace BTCPayServer.Tests.Lnd
this.output = output; this.output = output;
initializeEnvironment(); initializeEnvironment();
MerchantLnd = LndSwaggerClientCustomHttp.Create(new Uri("http://127.0.0.1:53280"), Network.RegTest); MerchantLnd = new LndSwaggerClient(new LndRestSettings(new Uri("http://127.0.0.1:53280")));
InvoiceClient = new LndInvoiceClient(MerchantLnd); InvoiceClient = new LndInvoiceClient(MerchantLnd);
CustomerLnd = LndSwaggerClientCustomHttp.Create(new Uri("http://127.0.0.1:53281"), Network.RegTest); CustomerLnd = new LndSwaggerClient(new LndRestSettings(new Uri("http://127.0.0.1:53281")));
} }
private LndSwaggerClientCustomHttp MerchantLnd { get; set; } private LndSwaggerClient MerchantLnd { get; set; }
private LndInvoiceClient InvoiceClient { get; set; } private LndInvoiceClient InvoiceClient { get; set; }
private LndSwaggerClientCustomHttp CustomerLnd { get; set; } private LndSwaggerClient CustomerLnd { get; set; }
[Fact] [Fact]
public async Task GetInfo() public async Task GetInfo()

View File

@@ -521,7 +521,7 @@ namespace BTCPayServer.Tests
Assert.Equal(lndUri, conn.ToString()); Assert.Equal(lndUri, conn.ToString());
Assert.Equal(LightningConnectionType.LndREST, conn.ConnectionType); Assert.Equal(LightningConnectionType.LndREST, conn.ConnectionType);
Assert.Equal(macaroon, Encoders.Hex.EncodeData(conn.Macaroon)); Assert.Equal(macaroon, Encoders.Hex.EncodeData(conn.Macaroon));
Assert.Equal(tls, Encoders.Hex.EncodeData(conn.Tls)); Assert.Equal(tls, Encoders.Hex.EncodeData(conn.Tls.RawData));
} }
[Fact] [Fact]

View File

@@ -30,8 +30,11 @@ namespace BTCPayServer.Payments.Lightning
} }
else if (connString.ConnectionType == LightningConnectionType.LndREST) else if (connString.ConnectionType == LightningConnectionType.LndREST)
{ {
var swagger = LndSwaggerClientCustomHttp.Create(connString.BaseUri, network, connString.Tls, connString.Macaroon); return new LndInvoiceClient(new LndSwaggerClient(new LndRestSettings(connString.BaseUri)
return new LndInvoiceClient(swagger); {
Macaroon = connString.Macaroon,
TLS = connString.Tls
}));
} }
else else
throw new NotSupportedException($"Unsupported connection string for lightning server ({connString.ConnectionType})"); throw new NotSupportedException($"Unsupported connection string for lightning server ({connString.ConnectionType})");

View File

@@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Security.Cryptography.X509Certificates;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
@@ -197,11 +198,11 @@ namespace BTCPayServer.Payments.Lightning
{ {
var tls = Take(keyValues, "tls"); var tls = Take(keyValues, "tls");
if (tls != null) if (tls != null)
result.Tls = Encoder.DecodeData(tls); result.Tls = new X509Certificate2(Encoder.DecodeData(tls));
} }
catch catch
{ {
error = $"The key 'tls' format should be in hex"; error = $"The key 'tls' should be the X509 certificate in hex";
return false; return false;
} }
} }
@@ -301,7 +302,7 @@ namespace BTCPayServer.Payments.Lightning
private set; private set;
} }
public byte[] Macaroon { get; set; } public byte[] Macaroon { get; set; }
public byte[] Tls { get; set; } public X509Certificate2 Tls { get; set; }
public Uri ToUri(bool withCredentials) public Uri ToUri(bool withCredentials)
{ {
@@ -350,7 +351,7 @@ namespace BTCPayServer.Payments.Lightning
} }
if (Tls != null) if (Tls != null)
{ {
builder.Append($";tls={Encoder.EncodeData(Tls)}"); builder.Append($";tls={Encoder.EncodeData(Tls.RawData)}");
} }
break; break;
default: default:

View File

@@ -19,9 +19,11 @@ namespace BTCPayServer.Payments.Lightning.Lnd
{ {
public LndSwaggerClient _rpcClient; public LndSwaggerClient _rpcClient;
public LndInvoiceClient(LndSwaggerClient rpcClient) public LndInvoiceClient(LndSwaggerClient swaggerClient)
{ {
_rpcClient = rpcClient; if (swaggerClient == null)
throw new ArgumentNullException(nameof(swaggerClient));
_rpcClient = swaggerClient;
} }
public async Task<LightningInvoice> CreateInvoice(LightMoney amount, string description, TimeSpan expiry, public async Task<LightningInvoice> CreateInvoice(LightMoney amount, string description, TimeSpan expiry,

View File

@@ -16,102 +16,53 @@ using Newtonsoft.Json.Linq;
namespace BTCPayServer.Payments.Lightning.Lnd namespace BTCPayServer.Payments.Lightning.Lnd
{ {
public class LndSwaggerClientCustomHttp : LndSwaggerClient, IDisposable public class LndRestSettings
{ {
protected LndSwaggerClientCustomHttp(string baseUrl, HttpClient httpClient) public LndRestSettings()
: base(baseUrl, httpClient)
{ {
_HttpClient = httpClient;
} }
public LndRestSettings(Uri uri)
private HttpClient _HttpClient;
public void Dispose()
{ {
_HttpClient.Dispose(); Uri = uri;
}
//
public static LndSwaggerClientCustomHttp Create(Uri uri, Network network, byte[] tlsCertificate = null, byte[] grpcMacaroon = null)
{
var factory = new HttpClientFactoryForLnd(tlsCertificate, grpcMacaroon);
var httpClient = factory.Generate();
var swagger = new LndSwaggerClientCustomHttp(uri.ToString().TrimEnd('/'), httpClient);
swagger.HttpClientFactory = factory;
return swagger;
} }
public Uri Uri { get; set; }
public X509Certificate2 TLS { get; set; }
public byte[] Macaroon { get; set; }
} }
internal class HttpClientFactoryForLnd public partial class LndSwaggerClient
{ {
public HttpClientFactoryForLnd(byte[] tlsCertificate = null, byte[] grpcMacaroon = null) public LndSwaggerClient(LndRestSettings settings)
: this(settings.Uri.AbsoluteUri.TrimEnd('/'), CreateHttpClient(settings))
{ {
TlsCertificate = tlsCertificate; _Settings = settings;
GrpcMacaroon = grpcMacaroon;
} }
LndRestSettings _Settings;
public byte[] TlsCertificate { get; set; } private static HttpClient CreateHttpClient(LndRestSettings settings)
public byte[] GrpcMacaroon { get; set; }
public HttpClient Generate()
{
var httpClient = new HttpClient(GetCertificate(TlsCertificate));
if (GrpcMacaroon != null)
{
var macaroonHex = BitConverter.ToString(GrpcMacaroon).Replace("-", "", StringComparison.InvariantCulture);
httpClient.DefaultRequestHeaders.Add("Grpc-Metadata-macaroon", macaroonHex);
}
return httpClient;
}
private static HttpClientHandler GetCertificate(byte[] certFile)
{ {
var handler = new HttpClientHandler var handler = new HttpClientHandler
{ {
SslProtocols = SslProtocols.Tls12 SslProtocols = SslProtocols.Tls12
}; };
if (certFile == null)
var expectedCertificate = settings.TLS;
if (expectedCertificate != null)
{ {
handler.ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator; handler.ServerCertificateCustomValidationCallback = (request, cert, chain, errors) =>
return handler;
}
// if certificate is not null, try with custom accepting logic
X509Certificate2 clientCertificate = null;
if (certFile != null)
clientCertificate = new X509Certificate2(certFile);
handler.ServerCertificateCustomValidationCallback = (request, cert, chain, errors) =>
{
const SslPolicyErrors unforgivableErrors =
SslPolicyErrors.RemoteCertificateNotAvailable |
SslPolicyErrors.RemoteCertificateNameMismatch;
if ((errors & unforgivableErrors) != 0)
{ {
return false; X509Certificate2 remoteRoot = chain.ChainElements[chain.ChainElements.Count - 1].Certificate;
} return expectedCertificate.RawData.SequenceEqual(remoteRoot.RawData);
};
if (clientCertificate == null) }
return true; var httpClient = new HttpClient(handler);
if (settings.Macaroon != null)
X509Certificate2 remoteRoot = chain.ChainElements[chain.ChainElements.Count - 1].Certificate; {
var res = clientCertificate.RawData.SequenceEqual(remoteRoot.RawData); var macaroonHex = BitConverter.ToString(settings.Macaroon).Replace("-", "", StringComparison.InvariantCulture);
httpClient.DefaultRequestHeaders.Add("Grpc-Metadata-macaroon", macaroonHex);
return res; }
}; return httpClient;
return handler;
} }
}
public partial class LndSwaggerClient
{
internal HttpClientFactoryForLnd HttpClientFactory { get; set; }
public TaskCompletionSource<LnrpcInvoice> InvoiceResponse = new TaskCompletionSource<LnrpcInvoice>(); public TaskCompletionSource<LnrpcInvoice> InvoiceResponse = new TaskCompletionSource<LnrpcInvoice>();
public TaskCompletionSource<LndSwaggerClient> SubscribeLost = new TaskCompletionSource<LndSwaggerClient>(); public TaskCompletionSource<LndSwaggerClient> SubscribeLost = new TaskCompletionSource<LndSwaggerClient>();
@@ -122,7 +73,7 @@ namespace BTCPayServer.Payments.Lightning.Lnd
var urlBuilder = new StringBuilder(); var urlBuilder = new StringBuilder();
urlBuilder.Append(BaseUrl).Append("/v1/invoices/subscribe"); urlBuilder.Append(BaseUrl).Append("/v1/invoices/subscribe");
using (var client = HttpClientFactory.Generate()) using (var client = CreateHttpClient(_Settings))
{ {
client.Timeout = TimeSpan.FromMilliseconds(Timeout.Infinite); client.Timeout = TimeSpan.FromMilliseconds(Timeout.Infinite);