2017-09-13 08:47:34 +02:00
|
|
|
|
using BTCPayServer.Controllers;
|
2017-12-13 07:49:19 +01:00
|
|
|
|
using System.Linq;
|
2017-09-13 08:47:34 +02:00
|
|
|
|
using BTCPayServer.Models.AccountViewModels;
|
2017-10-12 09:33:53 +02:00
|
|
|
|
using Microsoft.AspNetCore.Http;
|
2017-09-13 08:47:34 +02:00
|
|
|
|
using Microsoft.AspNetCore.Mvc;
|
|
|
|
|
using NBitcoin;
|
2017-10-02 17:41:03 +02:00
|
|
|
|
using NBitcoin.RPC;
|
2017-09-13 08:47:34 +02:00
|
|
|
|
using NBitpayClient;
|
2017-10-02 17:41:03 +02:00
|
|
|
|
using NBXplorer;
|
2017-10-12 09:33:53 +02:00
|
|
|
|
using NBXplorer.Models;
|
2017-09-13 08:47:34 +02:00
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
2017-10-02 17:41:03 +02:00
|
|
|
|
using System.Diagnostics;
|
2017-09-13 08:47:34 +02:00
|
|
|
|
using System.IO;
|
2017-10-12 09:33:53 +02:00
|
|
|
|
using System.Net.Http;
|
2017-09-13 08:47:34 +02:00
|
|
|
|
using System.Runtime.CompilerServices;
|
|
|
|
|
using System.Text;
|
|
|
|
|
using System.Threading.Tasks;
|
2017-10-12 09:33:53 +02:00
|
|
|
|
using System.Threading;
|
2018-02-17 05:18:16 +01:00
|
|
|
|
using System.Globalization;
|
2018-03-17 07:45:44 +01:00
|
|
|
|
using BTCPayServer.Payments.Lightning.CLightning.RPC;
|
2017-09-13 08:47:34 +02:00
|
|
|
|
|
|
|
|
|
namespace BTCPayServer.Tests
|
|
|
|
|
{
|
2017-10-27 10:53:04 +02:00
|
|
|
|
public class ServerTester : IDisposable
|
|
|
|
|
{
|
|
|
|
|
public static ServerTester Create([CallerMemberNameAttribute]string scope = null)
|
|
|
|
|
{
|
|
|
|
|
return new ServerTester(scope);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
string _Directory;
|
|
|
|
|
public ServerTester(string scope)
|
|
|
|
|
{
|
|
|
|
|
_Directory = scope;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool Dockerized
|
|
|
|
|
{
|
|
|
|
|
get; set;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Start()
|
|
|
|
|
{
|
|
|
|
|
if (Directory.Exists(_Directory))
|
|
|
|
|
Utils.DeleteDirectory(_Directory);
|
|
|
|
|
if (!Directory.Exists(_Directory))
|
|
|
|
|
Directory.CreateDirectory(_Directory);
|
|
|
|
|
|
|
|
|
|
|
2018-01-11 14:52:28 +01:00
|
|
|
|
NetworkProvider = new BTCPayNetworkProvider(ChainType.Regtest);
|
2018-01-12 03:54:57 +01:00
|
|
|
|
ExplorerNode = new RPCClient(RPCCredentialString.Parse(GetEnvironment("TESTS_BTCRPCCONNECTION", "server=http://127.0.0.1:43782;ceiwHEbqWI83:DwubwWsoo3")), NetworkProvider.GetNetwork("BTC").NBitcoinNetwork);
|
2018-01-11 14:52:28 +01:00
|
|
|
|
LTCExplorerNode = new RPCClient(RPCCredentialString.Parse(GetEnvironment("TESTS_LTCRPCCONNECTION", "server=http://127.0.0.1:43783;ceiwHEbqWI83:DwubwWsoo3")), NetworkProvider.GetNetwork("LTC").NBitcoinNetwork);
|
2018-01-11 09:29:48 +01:00
|
|
|
|
|
2018-01-12 03:54:57 +01:00
|
|
|
|
ExplorerClient = new ExplorerClient(NetworkProvider.GetNetwork("BTC").NBXplorerNetwork, new Uri(GetEnvironment("TESTS_BTCNBXPLORERURL", "http://127.0.0.1:32838/")));
|
|
|
|
|
LTCExplorerClient = new ExplorerClient(NetworkProvider.GetNetwork("LTC").NBXplorerNetwork, new Uri(GetEnvironment("TESTS_LTCNBXPLORERURL", "http://127.0.0.1:32838/")));
|
2018-01-11 09:29:48 +01:00
|
|
|
|
|
2018-02-26 10:58:02 +01:00
|
|
|
|
var btc = NetworkProvider.GetNetwork("BTC").NBitcoinNetwork;
|
2018-03-17 07:45:44 +01:00
|
|
|
|
CustomerLightningD = new CLightningRPCClient(new Uri(GetEnvironment("TEST_CUSTOMERLIGHTNINGD", "http://127.0.0.1:30992/")), btc);
|
2018-03-17 17:59:16 +01:00
|
|
|
|
MerchantCharge = new ChargeTester(this, "TEST_MERCHANTCHARGE", "http://api-token:foiewnccewuify@127.0.0.1:54938/", "merchant_lightningd", btc);
|
2018-02-26 10:58:02 +01:00
|
|
|
|
|
2017-10-27 10:53:04 +02:00
|
|
|
|
PayTester = new BTCPayServerTester(Path.Combine(_Directory, "pay"))
|
|
|
|
|
{
|
|
|
|
|
NBXplorerUri = ExplorerClient.Address,
|
2018-01-11 09:29:48 +01:00
|
|
|
|
LTCNBXplorerUri = LTCExplorerClient.Address,
|
2018-02-26 10:58:02 +01:00
|
|
|
|
Postgres = GetEnvironment("TESTS_POSTGRES", "User ID=postgres;Host=127.0.0.1;Port=39372;Database=btcpayserver"),
|
|
|
|
|
IntegratedLightning = MerchantCharge.Client.Uri
|
2017-10-27 10:53:04 +02:00
|
|
|
|
};
|
2018-02-17 05:18:16 +01:00
|
|
|
|
PayTester.Port = int.Parse(GetEnvironment("TESTS_PORT", Utils.FreeTcpPort().ToString(CultureInfo.InvariantCulture)), CultureInfo.InvariantCulture);
|
2017-10-27 10:53:04 +02:00
|
|
|
|
PayTester.HostName = GetEnvironment("TESTS_HOSTNAME", "127.0.0.1");
|
|
|
|
|
PayTester.Start();
|
2017-12-13 07:49:19 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2018-03-17 07:45:44 +01:00
|
|
|
|
/// Connect a customer LN node to the merchant LN node
|
2017-12-13 07:49:19 +01:00
|
|
|
|
/// </summary>
|
|
|
|
|
public void PrepareLightning()
|
|
|
|
|
{
|
|
|
|
|
PrepareLightningAsync().GetAwaiter().GetResult();
|
|
|
|
|
}
|
|
|
|
|
|
2018-03-17 07:45:44 +01:00
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Connect a customer LN node to the merchant LN node
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <returns></returns>
|
2017-12-13 07:49:19 +01:00
|
|
|
|
public async Task PrepareLightningAsync()
|
|
|
|
|
{
|
2018-03-17 07:45:44 +01:00
|
|
|
|
while (true)
|
|
|
|
|
{
|
|
|
|
|
var channel = (await CustomerLightningD.ListPeersAsync())
|
|
|
|
|
.SelectMany(p => p.Channels)
|
|
|
|
|
.FirstOrDefault();
|
|
|
|
|
switch (channel?.State)
|
|
|
|
|
{
|
|
|
|
|
case null:
|
|
|
|
|
var merchantInfo = await WaitLNSynched();
|
|
|
|
|
var clightning = new NodeInfo(merchantInfo.Id, MerchantCharge.P2PHost, merchantInfo.Port);
|
|
|
|
|
await CustomerLightningD.ConnectAsync(clightning);
|
|
|
|
|
var address = await CustomerLightningD.NewAddressAsync();
|
|
|
|
|
await ExplorerNode.SendToAddressAsync(address, Money.Coins(0.2m));
|
|
|
|
|
ExplorerNode.Generate(1);
|
|
|
|
|
await WaitLNSynched();
|
2018-03-17 11:26:30 +01:00
|
|
|
|
await Task.Delay(1000);
|
2018-03-17 07:45:44 +01:00
|
|
|
|
await CustomerLightningD.FundChannelAsync(clightning, Money.Satoshis(16777215));
|
|
|
|
|
break;
|
|
|
|
|
case "CHANNELD_AWAITING_LOCKIN":
|
|
|
|
|
ExplorerNode.Generate(1);
|
|
|
|
|
await WaitLNSynched();
|
|
|
|
|
break;
|
|
|
|
|
case "CHANNELD_NORMAL":
|
|
|
|
|
return;
|
|
|
|
|
default:
|
|
|
|
|
throw new NotSupportedException(channel?.State ?? "");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private async Task<Payments.Lightning.CLightning.GetInfoResponse> WaitLNSynched()
|
|
|
|
|
{
|
|
|
|
|
while (true)
|
2018-02-25 16:48:12 +01:00
|
|
|
|
{
|
2018-03-17 07:45:44 +01:00
|
|
|
|
var merchantInfo = await MerchantCharge.Client.GetInfoAsync();
|
|
|
|
|
var blockCount = await ExplorerNode.GetBlockCountAsync();
|
|
|
|
|
if (merchantInfo.BlockHeight != blockCount)
|
|
|
|
|
{
|
|
|
|
|
await Task.Delay(1000);
|
|
|
|
|
}
|
|
|
|
|
else
|
2018-02-25 16:48:12 +01:00
|
|
|
|
{
|
2018-03-17 07:45:44 +01:00
|
|
|
|
return merchantInfo;
|
2018-02-25 16:48:12 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void SendLightningPayment(Invoice invoice)
|
|
|
|
|
{
|
|
|
|
|
SendLightningPaymentAsync(invoice).GetAwaiter().GetResult();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async Task SendLightningPaymentAsync(Invoice invoice)
|
|
|
|
|
{
|
|
|
|
|
var bolt11 = invoice.CryptoInfo.Where(o => o.PaymentUrls.BOLT11 != null).First().PaymentUrls.BOLT11;
|
|
|
|
|
bolt11 = bolt11.Replace("lightning:", "", StringComparison.OrdinalIgnoreCase);
|
2018-03-17 07:45:44 +01:00
|
|
|
|
await CustomerLightningD.SendAsync(bolt11);
|
2017-10-27 10:53:04 +02:00
|
|
|
|
}
|
|
|
|
|
|
2018-03-17 07:45:44 +01:00
|
|
|
|
public CLightningRPCClient CustomerLightningD { get; set; }
|
2018-02-23 07:21:42 +01:00
|
|
|
|
public ChargeTester MerchantCharge { get; private set; }
|
2017-12-13 07:49:19 +01:00
|
|
|
|
|
|
|
|
|
internal string GetEnvironment(string variable, string defaultValue)
|
2017-10-27 10:53:04 +02:00
|
|
|
|
{
|
|
|
|
|
var var = Environment.GetEnvironmentVariable(variable);
|
|
|
|
|
return String.IsNullOrEmpty(var) ? defaultValue : var;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public TestAccount NewAccount()
|
|
|
|
|
{
|
|
|
|
|
return new TestAccount(this);
|
|
|
|
|
}
|
2018-01-11 14:52:28 +01:00
|
|
|
|
|
|
|
|
|
public BTCPayNetworkProvider NetworkProvider { get; private set; }
|
2017-10-27 10:53:04 +02:00
|
|
|
|
public RPCClient ExplorerNode
|
|
|
|
|
{
|
|
|
|
|
get; set;
|
|
|
|
|
}
|
|
|
|
|
|
2018-01-11 09:29:48 +01:00
|
|
|
|
public RPCClient LTCExplorerNode
|
|
|
|
|
{
|
|
|
|
|
get; set;
|
|
|
|
|
}
|
|
|
|
|
|
2017-10-27 10:53:04 +02:00
|
|
|
|
public ExplorerClient ExplorerClient
|
|
|
|
|
{
|
|
|
|
|
get; set;
|
|
|
|
|
}
|
2018-01-11 09:29:48 +01:00
|
|
|
|
public ExplorerClient LTCExplorerClient { get; set; }
|
2017-10-27 10:53:04 +02:00
|
|
|
|
|
|
|
|
|
HttpClient _Http = new HttpClient();
|
|
|
|
|
|
|
|
|
|
class MockHttpRequest : HttpRequest
|
|
|
|
|
{
|
|
|
|
|
Uri serverUri;
|
|
|
|
|
public MockHttpRequest(Uri serverUri)
|
|
|
|
|
{
|
|
|
|
|
this.serverUri = serverUri;
|
|
|
|
|
}
|
|
|
|
|
public override HttpContext HttpContext => throw new NotImplementedException();
|
|
|
|
|
|
|
|
|
|
public override string Method
|
|
|
|
|
{
|
|
|
|
|
get => throw new NotImplementedException();
|
|
|
|
|
set => throw new NotImplementedException();
|
|
|
|
|
}
|
|
|
|
|
public override string Scheme
|
|
|
|
|
{
|
|
|
|
|
get => serverUri.Scheme;
|
|
|
|
|
set => throw new NotImplementedException();
|
|
|
|
|
}
|
|
|
|
|
public override bool IsHttps
|
|
|
|
|
{
|
|
|
|
|
get => throw new NotImplementedException();
|
|
|
|
|
set => throw new NotImplementedException();
|
|
|
|
|
}
|
|
|
|
|
public override HostString Host
|
|
|
|
|
{
|
|
|
|
|
get => new HostString(serverUri.Host, serverUri.Port);
|
|
|
|
|
set => throw new NotImplementedException();
|
|
|
|
|
}
|
|
|
|
|
public override PathString PathBase
|
|
|
|
|
{
|
|
|
|
|
get => "";
|
|
|
|
|
set => throw new NotImplementedException();
|
|
|
|
|
}
|
|
|
|
|
public override PathString Path
|
|
|
|
|
{
|
|
|
|
|
get => throw new NotImplementedException();
|
|
|
|
|
set => throw new NotImplementedException();
|
|
|
|
|
}
|
|
|
|
|
public override QueryString QueryString
|
|
|
|
|
{
|
|
|
|
|
get => throw new NotImplementedException();
|
|
|
|
|
set => throw new NotImplementedException();
|
|
|
|
|
}
|
|
|
|
|
public override IQueryCollection Query
|
|
|
|
|
{
|
|
|
|
|
get => throw new NotImplementedException();
|
|
|
|
|
set => throw new NotImplementedException();
|
|
|
|
|
}
|
|
|
|
|
public override string Protocol
|
|
|
|
|
{
|
|
|
|
|
get => throw new NotImplementedException();
|
|
|
|
|
set => throw new NotImplementedException();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override IHeaderDictionary Headers => throw new NotImplementedException();
|
|
|
|
|
|
|
|
|
|
public override IRequestCookieCollection Cookies
|
|
|
|
|
{
|
|
|
|
|
get => throw new NotImplementedException();
|
|
|
|
|
set => throw new NotImplementedException();
|
|
|
|
|
}
|
|
|
|
|
public override long? ContentLength
|
|
|
|
|
{
|
|
|
|
|
get => throw new NotImplementedException();
|
|
|
|
|
set => throw new NotImplementedException();
|
|
|
|
|
}
|
|
|
|
|
public override string ContentType
|
|
|
|
|
{
|
|
|
|
|
get => throw new NotImplementedException();
|
|
|
|
|
set => throw new NotImplementedException();
|
|
|
|
|
}
|
|
|
|
|
public override Stream Body
|
|
|
|
|
{
|
|
|
|
|
get => throw new NotImplementedException();
|
|
|
|
|
set => throw new NotImplementedException();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override bool HasFormContentType => throw new NotImplementedException();
|
|
|
|
|
|
|
|
|
|
public override IFormCollection Form
|
|
|
|
|
{
|
|
|
|
|
get => throw new NotImplementedException();
|
|
|
|
|
set => throw new NotImplementedException();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override Task<IFormCollection> ReadFormAsync(CancellationToken cancellationToken = default(CancellationToken))
|
|
|
|
|
{
|
|
|
|
|
throw new NotImplementedException();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public BTCPayServerTester PayTester
|
|
|
|
|
{
|
|
|
|
|
get; set;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Dispose()
|
|
|
|
|
{
|
|
|
|
|
if (PayTester != null)
|
|
|
|
|
PayTester.Dispose();
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-09-13 08:47:34 +02:00
|
|
|
|
}
|