diff --git a/BTCPayServer.Tests/Lnd/LndMockTester.cs b/BTCPayServer.Tests/Lnd/LndMockTester.cs index 3ee701614..dd5dd96f3 100644 --- a/BTCPayServer.Tests/Lnd/LndMockTester.cs +++ b/BTCPayServer.Tests/Lnd/LndMockTester.cs @@ -15,12 +15,12 @@ namespace BTCPayServer.Tests.Lnd this._Parent = serverTester; 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); P2PHost = _Parent.GetEnvironment(environmentName + "_HOST", defaultHost); } - public LndSwaggerClientCustomHttp Swagger { get; set; } + public LndSwaggerClient Swagger { get; set; } public LndInvoiceClient Client { get; set; } public string P2PHost { get; } } diff --git a/BTCPayServer.Tests/Lnd/UnitTests.cs b/BTCPayServer.Tests/Lnd/UnitTests.cs index 25c91f0bc..4eb354912 100644 --- a/BTCPayServer.Tests/Lnd/UnitTests.cs +++ b/BTCPayServer.Tests/Lnd/UnitTests.cs @@ -25,16 +25,16 @@ namespace BTCPayServer.Tests.Lnd this.output = output; 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); - 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 LndSwaggerClientCustomHttp CustomerLnd { get; set; } + private LndSwaggerClient CustomerLnd { get; set; } [Fact] public async Task GetInfo() diff --git a/BTCPayServer.Tests/UnitTest1.cs b/BTCPayServer.Tests/UnitTest1.cs index 10ccf40c1..60aa1693b 100644 --- a/BTCPayServer.Tests/UnitTest1.cs +++ b/BTCPayServer.Tests/UnitTest1.cs @@ -521,7 +521,7 @@ namespace BTCPayServer.Tests Assert.Equal(lndUri, conn.ToString()); Assert.Equal(LightningConnectionType.LndREST, conn.ConnectionType); 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] diff --git a/BTCPayServer/Payments/Lightning/LightningClientFactory.cs b/BTCPayServer/Payments/Lightning/LightningClientFactory.cs index d395a5c93..3879ac526 100644 --- a/BTCPayServer/Payments/Lightning/LightningClientFactory.cs +++ b/BTCPayServer/Payments/Lightning/LightningClientFactory.cs @@ -30,8 +30,11 @@ namespace BTCPayServer.Payments.Lightning } else if (connString.ConnectionType == LightningConnectionType.LndREST) { - var swagger = LndSwaggerClientCustomHttp.Create(connString.BaseUri, network, connString.Tls, connString.Macaroon); - return new LndInvoiceClient(swagger); + return new LndInvoiceClient(new LndSwaggerClient(new LndRestSettings(connString.BaseUri) + { + Macaroon = connString.Macaroon, + TLS = connString.Tls + })); } else throw new NotSupportedException($"Unsupported connection string for lightning server ({connString.ConnectionType})"); diff --git a/BTCPayServer/Payments/Lightning/LightningConnectionString.cs b/BTCPayServer/Payments/Lightning/LightningConnectionString.cs index c698b4af5..9cdd79017 100644 --- a/BTCPayServer/Payments/Lightning/LightningConnectionString.cs +++ b/BTCPayServer/Payments/Lightning/LightningConnectionString.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Security.Cryptography.X509Certificates; using System.Text; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; @@ -197,11 +198,11 @@ namespace BTCPayServer.Payments.Lightning { var tls = Take(keyValues, "tls"); if (tls != null) - result.Tls = Encoder.DecodeData(tls); + result.Tls = new X509Certificate2(Encoder.DecodeData(tls)); } catch { - error = $"The key 'tls' format should be in hex"; + error = $"The key 'tls' should be the X509 certificate in hex"; return false; } } @@ -301,7 +302,7 @@ namespace BTCPayServer.Payments.Lightning private set; } public byte[] Macaroon { get; set; } - public byte[] Tls { get; set; } + public X509Certificate2 Tls { get; set; } public Uri ToUri(bool withCredentials) { @@ -350,7 +351,7 @@ namespace BTCPayServer.Payments.Lightning } if (Tls != null) { - builder.Append($";tls={Encoder.EncodeData(Tls)}"); + builder.Append($";tls={Encoder.EncodeData(Tls.RawData)}"); } break; default: diff --git a/BTCPayServer/Payments/Lightning/Lnd/LndInvoiceClient.cs b/BTCPayServer/Payments/Lightning/Lnd/LndInvoiceClient.cs index 3f1771a5b..2a1b1e4da 100644 --- a/BTCPayServer/Payments/Lightning/Lnd/LndInvoiceClient.cs +++ b/BTCPayServer/Payments/Lightning/Lnd/LndInvoiceClient.cs @@ -19,9 +19,11 @@ namespace BTCPayServer.Payments.Lightning.Lnd { 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 CreateInvoice(LightMoney amount, string description, TimeSpan expiry, diff --git a/BTCPayServer/Payments/Lightning/Lnd/LndSwaggerClientCustomHttp.cs b/BTCPayServer/Payments/Lightning/Lnd/LndSwaggerClient.partial.cs similarity index 53% rename from BTCPayServer/Payments/Lightning/Lnd/LndSwaggerClientCustomHttp.cs rename to BTCPayServer/Payments/Lightning/Lnd/LndSwaggerClient.partial.cs index 18a67f219..5af28e805 100644 --- a/BTCPayServer/Payments/Lightning/Lnd/LndSwaggerClientCustomHttp.cs +++ b/BTCPayServer/Payments/Lightning/Lnd/LndSwaggerClient.partial.cs @@ -16,102 +16,53 @@ using Newtonsoft.Json.Linq; namespace BTCPayServer.Payments.Lightning.Lnd { - public class LndSwaggerClientCustomHttp : LndSwaggerClient, IDisposable + public class LndRestSettings { - protected LndSwaggerClientCustomHttp(string baseUrl, HttpClient httpClient) - : base(baseUrl, httpClient) + public LndRestSettings() { - _HttpClient = httpClient; + } - - private HttpClient _HttpClient; - - public void Dispose() + public LndRestSettings(Uri uri) { - _HttpClient.Dispose(); - } - - // - 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; + Uri = uri; } + 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; - GrpcMacaroon = grpcMacaroon; + _Settings = settings; } - - public byte[] TlsCertificate { get; set; } - 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) + LndRestSettings _Settings; + private static HttpClient CreateHttpClient(LndRestSettings settings) { var handler = new HttpClientHandler { SslProtocols = SslProtocols.Tls12 }; - if (certFile == null) + + var expectedCertificate = settings.TLS; + if (expectedCertificate != null) { - handler.ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator; - 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) + handler.ServerCertificateCustomValidationCallback = (request, cert, chain, errors) => { - return false; - } - - if (clientCertificate == null) - return true; - - X509Certificate2 remoteRoot = chain.ChainElements[chain.ChainElements.Count - 1].Certificate; - var res = clientCertificate.RawData.SequenceEqual(remoteRoot.RawData); - - return res; - }; - - return handler; + X509Certificate2 remoteRoot = chain.ChainElements[chain.ChainElements.Count - 1].Certificate; + return expectedCertificate.RawData.SequenceEqual(remoteRoot.RawData); + }; + } + var httpClient = new HttpClient(handler); + if (settings.Macaroon != null) + { + var macaroonHex = BitConverter.ToString(settings.Macaroon).Replace("-", "", StringComparison.InvariantCulture); + httpClient.DefaultRequestHeaders.Add("Grpc-Metadata-macaroon", macaroonHex); + } + return httpClient; } - } - - public partial class LndSwaggerClient - { - internal HttpClientFactoryForLnd HttpClientFactory { get; set; } public TaskCompletionSource InvoiceResponse = new TaskCompletionSource(); public TaskCompletionSource SubscribeLost = new TaskCompletionSource(); @@ -122,7 +73,7 @@ namespace BTCPayServer.Payments.Lightning.Lnd var urlBuilder = new StringBuilder(); urlBuilder.Append(BaseUrl).Append("/v1/invoices/subscribe"); - using (var client = HttpClientFactory.Generate()) + using (var client = CreateHttpClient(_Settings)) { client.Timeout = TimeSpan.FromMilliseconds(Timeout.Infinite);