Refactoring LndClient, enabling passing of Swagger instance

This commit is contained in:
rockstardev 2018-05-11 14:07:46 -05:00
parent cfbcf0947a
commit b03d271f85
3 changed files with 173 additions and 72 deletions

View file

@ -2,8 +2,10 @@
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using BTCPayServer.Payments.Lightning;
using BTCPayServer.Payments.Lightning.Lnd;
using NBitcoin;
using NBitcoin.RPC;
using Xunit;
using Xunit.Abstractions;
@ -16,31 +18,96 @@ namespace BTCPayServer.Tests.UnitTests
public LndTest(ITestOutputHelper output)
{
this.output = output;
initializeEnvironment();
LndRpc = LndSwaggerClientCustomHttp.Create(new Uri("http://localhost:53280"), Network.RegTest);
InvoiceClient = new LndClient(LndRpc);
}
private LndClient Client
{
get
{
var lnd = new LndClient(new Uri("http://localhost:53280"), Network.RegTest);
return lnd;
}
}
private LndSwaggerClientCustomHttp LndRpc { get; set; }
private LndClient InvoiceClient { get; set; }
[Fact]
public async Task GetInfo()
{
var res = await Client.GetInfo();
var res = await InvoiceClient.GetInfo();
output.WriteLine("Result: " + res.ToJson());
}
[Fact]
public async Task CreateInvoice()
{
var res = await Client.CreateInvoice(10000, "Hello world", TimeSpan.FromSeconds(3600));
var res = await InvoiceClient.CreateInvoice(10000, "Hello world", TimeSpan.FromSeconds(3600));
output.WriteLine("Result: " + res.ToJson());
}
[Fact]
public async Task GetInvoice()
{
var createInvoice = await InvoiceClient.CreateInvoice(10000, "Hello world", TimeSpan.FromSeconds(3600));
var getInvoice = await InvoiceClient.GetInvoice(createInvoice.Id);
Assert.Equal(createInvoice.BOLT11, getInvoice.BOLT11);
}
[Fact]
public async Task SetupWalletForPayment()
{
var nodeInfo = GetInfo();
var addressResponse = await LndRpc.NewWitnessAddressAsync();
var address = BitcoinAddress.Create(addressResponse.Address, Network.RegTest);
await ExplorerNode.SendToAddressAsync(address, Money.Coins(0.2m));
ExplorerNode.Generate(1);
await WaitLNSynched();
await Task.Delay(1000);
// We need two instances of lnd... one for merchant, one for buyer
// prepare that in next commit
//var channelReq = new LnrpcOpenChannelRequest
//{
// Local_funding_amount = 16777215.ToString()
//};
//var channelResp = await LndRpc.OpenChannelSyncAsync(channelReq);
output.WriteLine("Wallet Address: " + address);
}
private async Task<LightningNodeInformation> WaitLNSynched()
{
while (true)
{
var merchantInfo = await InvoiceClient.GetInfo();
var blockCount = await ExplorerNode.GetBlockCountAsync();
if (merchantInfo.BlockHeight != blockCount)
{
await Task.Delay(1000);
}
else
{
return merchantInfo;
}
}
}
//
private void initializeEnvironment()
{
NetworkProvider = new BTCPayNetworkProvider(NetworkType.Regtest);
ExplorerNode = new RPCClient(RPCCredentialString.Parse(GetEnvironment("TESTS_BTCRPCCONNECTION", "server=http://127.0.0.1:43782;ceiwHEbqWI83:DwubwWsoo3")), NetworkProvider.GetNetwork("BTC").NBitcoinNetwork);
}
public BTCPayNetworkProvider NetworkProvider { get; private set; }
public RPCClient ExplorerNode { get; set; }
internal string GetEnvironment(string variable, string defaultValue)
{
var var = Environment.GetEnvironmentVariable(variable);
return String.IsNullOrEmpty(var) ? defaultValue : var;
}
}
}

View file

@ -17,18 +17,13 @@ namespace BTCPayServer.Payments.Lightning.Lnd
{
public class LndClient : ILightningInvoiceClient, ILightningListenInvoiceSession
{
public LndClient(Uri uri, Network network, byte[] tlsCertificate = null, byte[] grpcMacaroon = null)
public LndSwaggerClient _Decorator;
public LndClient(LndSwaggerClient decorator)
{
// for now working with custom build of lnd that has no macaroons and is on http
//_HttpClient = HttpClientFactoryForLnd.Generate(tlsCertificate, grpcMacaroon);
_HttpClient = new HttpClient();
_Decorator = new LndSwaggerClient(uri.ToString().TrimEnd('/'), _HttpClient);
_Decorator = decorator;
}
private HttpClient _HttpClient;
private LndSwaggerClient _Decorator;
public async Task<LightningInvoice> CreateInvoice(LightMoney amount, string description, TimeSpan expiry,
CancellationToken cancellation = default(CancellationToken))
{
@ -84,11 +79,8 @@ namespace BTCPayServer.Payments.Lightning.Lnd
}
public void Dispose()
{
_HttpClient?.Dispose();
}
// utility static methods... maybe move to separate class
private static string BitString(byte[] bytes)
{
return BitConverter.ToString(bytes)
@ -123,6 +115,11 @@ namespace BTCPayServer.Payments.Lightning.Lnd
return invoice;
}
public void Dispose()
{
throw new NotImplementedException();
}
// Invariant culture conversion
public static class ConvertInv
{
@ -143,53 +140,7 @@ namespace BTCPayServer.Payments.Lightning.Lnd
}
}
internal class HttpClientFactoryForLnd
{
internal static HttpClient Generate(byte[] tlsCertificate, byte[] grpcMacaroon)
{
var httpClient = new HttpClient(GetCertificate(tlsCertificate));
var macaroonHex = BitConverter.ToString(grpcMacaroon).Replace("-", "", StringComparison.InvariantCulture);
httpClient.DefaultRequestHeaders.Add("Grpc-Metadata-macaroon", macaroonHex);
return httpClient;
}
private static HttpClientHandler GetCertificate(byte[] certFile)
{
X509Certificate2 clientCertificate = null;
if (certFile != null)
clientCertificate = new X509Certificate2(certFile);
var handler = new HttpClientHandler
{
SslProtocols = SslProtocols.Tls12
};
handler.ServerCertificateCustomValidationCallback = (request, cert, chain, errors) =>
{
const SslPolicyErrors unforgivableErrors =
SslPolicyErrors.RemoteCertificateNotAvailable |
SslPolicyErrors.RemoteCertificateNameMismatch;
if ((errors & unforgivableErrors) != 0)
{
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;
}
}
partial class LndSwaggerClient
public partial class LndSwaggerClient
{
}
}

View file

@ -0,0 +1,83 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Net.Security;
using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates;
using System.Threading.Tasks;
using NBitcoin;
namespace BTCPayServer.Payments.Lightning.Lnd
{
public class LndSwaggerClientCustomHttp : LndSwaggerClient, IDisposable
{
public LndSwaggerClientCustomHttp(string baseUrl, HttpClient httpClient)
: base(baseUrl, httpClient)
{
_HttpClient = httpClient;
}
private HttpClient _HttpClient;
public void Dispose()
{
_HttpClient.Dispose();
}
//
public static LndSwaggerClientCustomHttp Create(Uri uri, Network network, byte[] tlsCertificate = null, byte[] grpcMacaroon = null)
{
// for now working with custom build of lnd that has no macaroons and is on http
//_HttpClient = HttpClientFactoryForLnd.Generate(tlsCertificate, grpcMacaroon);
var httpClient = new HttpClient();
return new LndSwaggerClientCustomHttp(uri.ToString().TrimEnd('/'), httpClient);
}
}
internal class HttpClientFactoryForLnd
{
internal static HttpClient Generate(byte[] tlsCertificate, byte[] grpcMacaroon)
{
var httpClient = new HttpClient(GetCertificate(tlsCertificate));
var macaroonHex = BitConverter.ToString(grpcMacaroon).Replace("-", "", StringComparison.InvariantCulture);
httpClient.DefaultRequestHeaders.Add("Grpc-Metadata-macaroon", macaroonHex);
return httpClient;
}
private static HttpClientHandler GetCertificate(byte[] certFile)
{
X509Certificate2 clientCertificate = null;
if (certFile != null)
clientCertificate = new X509Certificate2(certFile);
var handler = new HttpClientHandler
{
SslProtocols = SslProtocols.Tls12
};
handler.ServerCertificateCustomValidationCallback = (request, cert, chain, errors) =>
{
const SslPolicyErrors unforgivableErrors =
SslPolicyErrors.RemoteCertificateNotAvailable |
SslPolicyErrors.RemoteCertificateNameMismatch;
if ((errors & unforgivableErrors) != 0)
{
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;
}
}
}