From fb5eb1dafed06e18dd4c4fec3c54241dc9061e6a Mon Sep 17 00:00:00 2001 From: Richard Safier Date: Mon, 25 Mar 2024 23:20:29 -0400 Subject: [PATCH 1/9] wip --- LNUnit.LND/LNDSimpleHtlcInterceptorHandler.cs | 10 +- .../Fixture/PostgresLightningFixture.cs | 348 ++++++++++++++++++ LNUnit.Tests/LNUnit.Tests.csproj | 1 + LNUnit/Setup/LNUnitBuilder.cs | 45 ++- 4 files changed, 386 insertions(+), 18 deletions(-) create mode 100644 LNUnit.Tests/Fixture/PostgresLightningFixture.cs diff --git a/LNUnit.LND/LNDSimpleHtlcInterceptorHandler.cs b/LNUnit.LND/LNDSimpleHtlcInterceptorHandler.cs index ce0fbb0..dc0cc2f 100644 --- a/LNUnit.LND/LNDSimpleHtlcInterceptorHandler.cs +++ b/LNUnit.LND/LNDSimpleHtlcInterceptorHandler.cs @@ -1,3 +1,4 @@ +using System.Diagnostics; using Grpc.Core; using Routerrpc; @@ -17,7 +18,7 @@ public LNDSimpleHtlcInterceptorHandler(LNDNodeConnection connection, Func> interceptLogic) { Node = connection; - _task = Task.Factory.StartNew(AttachInterceptor, _cancellationTokenSource.Token); + _task = Task.Factory.StartNew(AttachInterceptor, _cancellationTokenSource.Token, TaskCreationOptions.LongRunning, TaskScheduler.Current); OnIntercept = interceptLogic; while (!Running) Task.Delay(100).Wait(); @@ -42,17 +43,20 @@ public void Dispose() private async Task AttachInterceptor() { + Debug.Print($"AttachInterceptor: {Node.LocalAlias}"); try { using (var streamingEvents = Node.RouterClient.HtlcInterceptor(cancellationToken: _cancellationTokenSource.Token)) { Running = true; - while (await streamingEvents.ResponseStream.MoveNext()) + while (await streamingEvents.ResponseStream.MoveNext().ConfigureAwait(false)) { + Debug.Print($"Event: {Node.LocalAlias}"); + var message = streamingEvents.ResponseStream.Current; var result = OnIntercept(message); - await streamingEvents.RequestStream.WriteAsync(await result); + await streamingEvents.RequestStream.WriteAsync(await result).ConfigureAwait(false); InterceptCount++; } } diff --git a/LNUnit.Tests/Fixture/PostgresLightningFixture.cs b/LNUnit.Tests/Fixture/PostgresLightningFixture.cs new file mode 100644 index 0000000..fdec1ad --- /dev/null +++ b/LNUnit.Tests/Fixture/PostgresLightningFixture.cs @@ -0,0 +1,348 @@ +using System.Collections.Immutable; +using System.Net; +using System.Net.Sockets; +using Docker.DotNet; +using Docker.DotNet.Models; +using Lnrpc; +using LNUnit.LND; +using LNUnit.Setup; +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.DependencyInjection; +using Npgsql; +using Routerrpc; +using Serilog; +using ServiceStack; +using ServiceStack.Text; +using Assert = NUnit.Framework.Assert; +using HostConfig = Docker.DotNet.Models.HostConfig; + +namespace LNUnit.Fixtures; + +//[TestFixture] +// [SingleThreaded] +public class PostgresLightningFixture : IDisposable +{ + + [SetUp] + public async Task PerTestSetUp() + { + Builder.CancelAllInterceptors(); + await Builder.NewBlock(); + } + + [Test] + [NonParallelizable] + public async Task InterceptorTest() + { + List invoices = new(); + for (var i = 0; i < 10; i++) + { + var invoice = await Builder.GeneratePaymentRequestFromAlias("alice", new Invoice + { + Memo = "This path is a trap, better have max_fees set", + ValueMsat = 1000, + Expiry = 60 * 60 * 24 + }); + invoices.Add(invoice); + } + + //Apply HTLC hold to prevent payment from settling + var htlcEvents = 0; + List monitors = new(); + foreach (var n in Builder.LNDNodePool.ReadyNodes.ToImmutableList()) + //This disposes so should use Clone of connection + monitors.Add(new LNDHTLCMonitor(n.Clone(), htlc => { htlcEvents++; })); + + + await Builder.DelayAllHTLCsOnAlias("alice", 1); + await Builder.DelayAllHTLCsOnAlias("bob", 1); + await Builder.DelayAllHTLCsOnAlias("carol", 1); + await Task.Delay(1000); + Assert.That(await Builder.IsInterceptorActiveForAlias("alice")); + Assert.That((await Builder.GetInterceptor("bob")).InterceptCount == 0); + Assert.That(await Builder.IsInterceptorActiveForAlias("bob")); + Assert.That(await Builder.IsInterceptorActiveForAlias("carol")); + await Task.Delay(5000); + var ii = 0; + foreach (var invoice in invoices) + { + ii++; + ii.PrintDump(); + var payment = await Builder.MakeLightningPaymentFromAlias("carol", new SendPaymentRequest + { + PaymentRequest = invoice.PaymentRequest, + FeeLimitMsat = 1000000000000000, //plenty of money + TimeoutSeconds = 20000, + NoInflightUpdates = true + }); + Assert.That(payment.Status == Payment.Types.PaymentStatus.Succeeded); + } + + Assert.That(await Builder.IsInterceptorActiveForAlias("alice"), "alice not intercepting"); + Assert.That((await Builder.GetInterceptor("bob")).InterceptCount >= 10, "bob not intercepting"); + Assert.That(await Builder.IsInterceptorActiveForAlias("bob"), "Bob not intercepting"); + Assert.That(await Builder.IsInterceptorActiveForAlias("carol"), "carol not intercepting"); + Assert.That(htlcEvents > 10); //just what it spit out didn't do math for that + } + + public string DbContainerName { get; set; } = "postgres"; + private readonly DockerClient _client = new DockerClientConfiguration().CreateClient(); + private string _containerId; + private string _ip; + + [OneTimeSetUp] + public async Task OneTimeSetup() + { + var builder = WebApplication.CreateBuilder(new string[] { }); + + // builder.Services.AddSingleton(builder.Configuration.GetAWSOptions()); + + var loggerConfiguration = new LoggerConfiguration().Enrich.FromLogContext(); + loggerConfiguration.ReadFrom.Configuration(builder.Configuration); + + Log.Logger = loggerConfiguration.CreateLogger(); + builder.Host.UseSerilog(dispose: true); //Log.Logger is implicitly used + builder.Services.Configure>(o => + { + // o.AddRange(lndSettings); + }); + + builder.Services.Configure(c => + { + // c.AddNode(new LNDNodeConnection(lndSettings.First())); + // + // foreach (var z in lndSettings.Skip(1)) + // { + // c.AddConnectionSettings(z); + // } + // c.UpdateReadyStatesPeriod(1); + }); + + builder.Services.AddTransient(); + // Add services to the container. + builder.Services.AddMemoryCache(); + builder.Services.AddControllers(); + builder.Services.AddOptions(); + _host = builder.Build(); + var host_running = _host.RunAsync(); + Builder = ActivatorUtilities.CreateInstance(_host.Services); + + await StartPostgres(); + AddDb("alice"); + AddDb("bob"); + AddDb("carol"); + await _client.CreateDockerImageFromPath("../../../../Docker/lnd", ["custom_lnd", "custom_lnd:latest"]); + await SetupNetwork("custom_lnd","latest", "/home/lnd/.lnd"); + } + + private void AddDb(string dbName) + { + using (NpgsqlConnection connection = new(DbConnectionString)) + { + connection.Open(); + using var checkIfExistsCommand = new NpgsqlCommand($"SELECT 1 FROM pg_catalog.pg_database WHERE datname = '{dbName}'", connection); + var result = checkIfExistsCommand.ExecuteScalar(); + + if (result == null) + { + + using var command = new NpgsqlCommand($"CREATE DATABASE \"{dbName}\"", connection); + command.ExecuteNonQuery(); + } + } + LNDConnectionStrings.Add(dbName, + $"postgresql://superuser:superuser@{_ip}:5432/{dbName}?sslmode=disable"); + } + + public string DbConnectionStringLND { get; private set; } + public string DbConnectionString { get; private set; } + + public Dictionary LNDConnectionStrings = new(); + private WebApplication _host; + + public void Dispose() + { + GC.SuppressFinalize(this); + + // Remove containers + RemoveContainer(DbContainerName).Wait(); + RemoveContainer("miner").Wait(); + RemoveContainer("alice").Wait(); + RemoveContainer("bob").Wait(); + RemoveContainer("carol").Wait(); + + Builder?.Destroy(); + _client.Dispose(); + } + + public LNUnitBuilder? Builder { get; private set; } + + + public async Task SetupNetwork(string image = "lightninglabs/lnd", string tag = "daily-testing-only", string lndRoot = "/root/.lnd", bool pullImage = false) + { + await RemoveContainer("miner"); + await RemoveContainer("alice"); + await RemoveContainer("bob"); + await RemoveContainer("carol"); + + Builder.AddBitcoinCoreNode(); + + if (pullImage) + { + await _client.PullImageAndWaitForCompleted(image, tag); + } + + Builder.AddPolarLNDNode("alice", + [ + new() + { + ChannelSize = 10_000_000, //10MSat + RemoteName = "bob" + } + ], imageName: image, tagName: tag, pullImage: false, postgresDSN: LNDConnectionStrings["alice"]); + + Builder.AddPolarLNDNode("bob", + [ + new() + { + ChannelSize = 10_000_000, //10MSat + RemotePushOnStart = 1_000_000, // 1MSat + RemoteName = "alice" + } + ], imageName: image, tagName: tag, pullImage: false, postgresDSN: LNDConnectionStrings["bob"]); + + Builder.AddPolarLNDNode("carol", + [ + // new() + // { + // ChannelSize = 10_000_000, //10MSat + // RemotePushOnStart = 1_000_000, // 1MSat + // RemoteName = "alice" + // }, + new() + { + ChannelSize = 10_000_000, //10MSat + RemotePushOnStart = 1_000_000, // 1MSat + RemoteName = "bob" + } + ], imageName: image, tagName: tag, pullImage: false);//, postgresDSN: LNDConnectionStrings["carol"]); + + await Builder.Build(lndRoot: lndRoot); + var a = Builder.WaitUntilAliasIsServerReady("alice"); + var b = Builder.WaitUntilAliasIsServerReady("bob"); + var c = Builder.WaitUntilAliasIsServerReady("carol"); + + Task.WaitAll(a, b, c); + + var graphReady = false; + while (!graphReady) + { + var graph = await Builder.GetGraphFromAlias("alice"); + if (graph.Nodes.Count < 3) + { + "Graph not ready...".Print(); + await Task.Delay(250); //let the graph sync + } + else + { + graphReady = true; + } + } + } + + public async Task StartPostgres() + { + await _client.PullImageAndWaitForCompleted("postgres", "16.2-alpine"); + await RemoveContainer(DbContainerName); + var nodeContainer = await _client.Containers.CreateContainerAsync(new CreateContainerParameters + { + Image = "postgres:16.2-alpine", + HostConfig = new HostConfig + { + NetworkMode = "bridge" + }, + Name = $"{DbContainerName}", + Hostname = $"{DbContainerName}", + Env = + [ + "POSTGRES_PASSWORD=superuser", + "POSTGRES_USER=superuser", + "POSTGRES_DB=postgres" + ] + }); + Assert.NotNull(nodeContainer); + _containerId = nodeContainer.ID; + var started = await _client.Containers.StartContainerAsync(_containerId, new ContainerStartParameters()); + + //Build connection string + var ipAddressReady = false; + while (!ipAddressReady) + { + var listContainers = await _client.Containers.ListContainersAsync(new ContainersListParameters()); + + var db = listContainers.FirstOrDefault(x => x.ID == nodeContainer.ID); + if (db != null) + { + _ip = db.NetworkSettings.Networks.First().Value.IPAddress; + DbConnectionString = $"Host={_ip};Database=postgres;Username=superuser;Password=superuser"; + ipAddressReady = true; + } + else + { + await Task.Delay(100); + } + + } + //wait for TCP socket to open + var tcpConnectable = false; + while (!tcpConnectable) + { + try + { + TcpClient c = new() + { + ReceiveTimeout = 1, + SendTimeout = 1 + }; + await c.ConnectAsync(new IPEndPoint(IPAddress.Parse(_ip), 5432)); + if (c.Connected) + { + tcpConnectable = true; + } + } + catch (Exception e) + { + await Task.Delay(50); + } + } + } + + private async Task RemoveContainer(string name) + { + try + { + await _client.Containers.RemoveContainerAsync(name, + new ContainerRemoveParameters { Force = true, RemoveVolumes = true }); + } + catch + { + // ignored + } + } + + public async Task IsRunning() + { + try + { + var inspect = await _client.Containers.InspectContainerAsync(DbContainerName); + return inspect.State.Running; + } + catch + { + // ignored + } + + return false; + } +} + \ No newline at end of file diff --git a/LNUnit.Tests/LNUnit.Tests.csproj b/LNUnit.Tests/LNUnit.Tests.csproj index f9c4ed3..0b2a1f1 100644 --- a/LNUnit.Tests/LNUnit.Tests.csproj +++ b/LNUnit.Tests/LNUnit.Tests.csproj @@ -18,6 +18,7 @@ + diff --git a/LNUnit/Setup/LNUnitBuilder.cs b/LNUnit/Setup/LNUnitBuilder.cs index 7c151bc..87a5ae2 100644 --- a/LNUnit/Setup/LNUnitBuilder.cs +++ b/LNUnit/Setup/LNUnitBuilder.cs @@ -94,7 +94,7 @@ await _dockerClient.Containers.RemoveContainerAsync(n.DockerContainerId, IsDestoryed = true; } - public async Task Build(bool setupNetwork = false) + public async Task Build(bool setupNetwork = false, string lndRoot = "/home/lnd/.lnd") { _logger?.LogInformation("Building LNUnit scenerio."); //Validation @@ -168,16 +168,16 @@ public async Task Build(bool setupNetwork = false) var inspectionResponse = await _dockerClient.Containers.InspectContainerAsync(n.DockerContainerId); var ipAddress = inspectionResponse.NetworkSettings.Networks.First().Value.IPAddress; - var txt = await GetStringFromFS(n.DockerContainerId, "/home/lnd/.lnd/tls.cert"); + var txt = await GetStringFromFS(n.DockerContainerId, $"{lndRoot}/tls.cert"); var tlsCertBase64 = Convert.ToBase64String(Encoding.UTF8.GetBytes(txt)); var data = await GetBytesFromFS(n.DockerContainerId, - "/home/lnd/.lnd/data/chain/bitcoin/regtest/admin.macaroon"); + $"{lndRoot}/data/chain/bitcoin/regtest/admin.macaroon"); var adminMacaroonBase64String = Convert.ToBase64String(data); var adminMacaroonTar = await GetTarStreamFromFS(n.DockerContainerId, - "/home/lnd/.lnd/data/chain/bitcoin/regtest/admin.macaroon"); + $"{lndRoot}/data/chain/bitcoin/regtest/admin.macaroon"); var lndConfig = new LNDSettings { @@ -207,19 +207,19 @@ await GetTarStreamFromFS(n.DockerContainerId, }); dependentContainer.DockerContainerId = nodeContainer.ID; var dataReadonly = await GetBytesFromFS(n.DockerContainerId, - "/home/lnd/.lnd/data/chain/bitcoin/regtest/readonly.macaroon"); + $"{lndRoot}/data/chain/bitcoin/regtest/readonly.macaroon"); var readonlyBase64String = Convert.ToBase64String(dataReadonly); var invoices = await GetBytesFromFS(n.DockerContainerId, - "/home/lnd/.lnd/data/chain/bitcoin/regtest/invoices.macaroon"); + $"{lndRoot}/data/chain/bitcoin/regtest/invoices.macaroon"); var chainnotifier = await GetBytesFromFS(n.DockerContainerId, - "/home/lnd/.lnd/data/chain/bitcoin/regtest/chainnotifier.macaroon"); + $"{lndRoot}/data/chain/bitcoin/regtest/chainnotifier.macaroon"); var signer = await GetBytesFromFS(n.DockerContainerId, - "/home/lnd/.lnd/data/chain/bitcoin/regtest/signer.macaroon"); + $"{lndRoot}/data/chain/bitcoin/regtest/signer.macaroon"); var walletkit = await GetBytesFromFS(n.DockerContainerId, - "/home/lnd/.lnd/data/chain/bitcoin/regtest/walletkit.macaroon"); + $"{lndRoot}/data/chain/bitcoin/regtest/walletkit.macaroon"); var router = await GetBytesFromFS(n.DockerContainerId, - "/home/lnd/.lnd/data/chain/bitcoin/regtest/router.macaroon"); + $"{lndRoot}/data/chain/bitcoin/regtest/router.macaroon"); File.WriteAllBytes("./loopserver-test/tls.cert", Convert.FromBase64String(tlsCertBase64)); File.WriteAllBytes("./loopserver-test/admin.macaroon", Convert.FromBase64String(adminMacaroonBase64String)); @@ -262,9 +262,16 @@ await _dockerClient.Containers.StartContainerAsync(loopServer?.ID, n.DockerContainerId = nodeContainer.ID; var success = await _dockerClient.Containers.StartContainerAsync(nodeContainer.ID, new ContainerStartParameters()); - var inspectionResponse = await _dockerClient.Containers.InspectContainerAsync(n.DockerContainerId); - var ipAddress = inspectionResponse.NetworkSettings.Networks.First().Value.IPAddress; - var basePath = !n.Image.Contains("lightning-terminal") ? "/home/lnd/.lnd" : "/root/lnd/.lnd"; + //Not always having IP yet. + var ipAddress = string.Empty; + ContainerInspectResponse? inspectionResponse = null; + while (ipAddress.IsEmpty()) + { + inspectionResponse = await _dockerClient.Containers.InspectContainerAsync(n.DockerContainerId); + ipAddress = inspectionResponse.NetworkSettings.Networks.First().Value.IPAddress; + } + + var basePath = !n.Image.Contains("lightning-terminal") ? lndRoot : "/root/lnd/.lnd"; // "/home/lnd/.lnd" : "/root/lnd/.lnd"; if (n.Image.Contains("lightning-terminal")) await Task.Delay(2000); var txt = await GetStringFromFS(n.DockerContainerId, $"{basePath}/tls.cert"); var tlsCertBase64 = Convert.ToBase64String(Encoding.UTF8.GetBytes(txt)); @@ -826,8 +833,8 @@ public static LNUnitBuilder AddBitcoinCoreNode(this LNUnitBuilder b, LNUnitNetwo public static LNUnitBuilder AddPolarLNDNode(this LNUnitBuilder b, string aliasHostname, List? channels = null, string bitcoinMinerHost = "miner", string rpcUser = "bitcoin", string rpcPass = "bitcoin", string imageName = "polarlightning/lnd", - string tagName = "0.16.2-beta", bool acceptKeysend = true, bool pullImage = true, bool mapTotmp = false, - bool gcInvoiceOnStartup = false, bool gcInvoiceOnFly = false) + string tagName = "0.17.4-beta", bool acceptKeysend = true, bool pullImage = true, bool mapTotmp = false, + bool gcInvoiceOnStartup = false, bool gcInvoiceOnFly = false, string postgresDSN = null) { var cmd = new List { @@ -855,6 +862,14 @@ public static LNUnitBuilder AddPolarLNDNode(this LNUnitBuilder b, string aliasHo "--gossip.max-channel-update-burst=100", "--gossip.channel-update-interval=1s" }; + + if (!postgresDSN.IsEmpty()) + { + cmd.Add("--db.backend=postgres"); + cmd.Add($"--db.postgres.dsn={postgresDSN}"); + cmd.Add($"--db.postgres.timeout=300s"); + cmd.Add($"--db.postgres.maxconnections=16"); + } if (gcInvoiceOnStartup) cmd.Add("--gc-canceled-invoices-on-startup"); if (gcInvoiceOnFly) cmd.Add("--gc-canceled-invoices-on-the-fly"); From bf3c16a927cc77bc5ff5e4ffc5855e4caf57682a Mon Sep 17 00:00:00 2001 From: Richard Safier Date: Tue, 26 Mar 2024 02:08:01 -0400 Subject: [PATCH 2/9] reorg, cleanup. postgres and boltdb testing. --- ...ningScenario.cs => AbcLightningFixture.cs} | 602 +++++++++--------- LNUnit.Tests/DataSync.cs | 226 +++---- LNUnit.Tests/Fixture/PostgresFixture.cs | 168 +++++ .../Fixture/PostgresLightningFixture.cs | 348 ---------- LNUnit/Setup/DockerHelper.cs | 22 + 5 files changed, 597 insertions(+), 769 deletions(-) rename LNUnit.Tests/{ABCLightningScenario.cs => AbcLightningFixture.cs} (57%) create mode 100644 LNUnit.Tests/Fixture/PostgresFixture.cs delete mode 100644 LNUnit.Tests/Fixture/PostgresLightningFixture.cs diff --git a/LNUnit.Tests/ABCLightningScenario.cs b/LNUnit.Tests/AbcLightningFixture.cs similarity index 57% rename from LNUnit.Tests/ABCLightningScenario.cs rename to LNUnit.Tests/AbcLightningFixture.cs index 87a4243..98b6082 100644 --- a/LNUnit.Tests/ABCLightningScenario.cs +++ b/LNUnit.Tests/AbcLightningFixture.cs @@ -1,230 +1,154 @@ -using System.Buffers.Text; -using System.Collections.Immutable; -using System.IO.Compression; -using System.Text.Unicode; +using System.Collections.Immutable; using Dasync.Collections; -using Docker.DotNet; -using Docker.DotNet.Models; +using Docker.DotNet; using Google.Protobuf; using Grpc.Core; using Lnrpc; using LNUnit.Extentions; using LNUnit.LND; -using LNUnit.Setup; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.HttpOverrides; -using Microsoft.AspNetCore.HttpsPolicy; -using Microsoft.AspNetCore.ResponseCompression; +using LNUnit.Setup; using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection; using Routerrpc; -using Serilog; +using Serilog; using ServiceStack; using ServiceStack.Text; +using Assert = NUnit.Framework.Assert; +using LNUnit.Fixtures; -namespace LNUnit.Tests; -public partial class ABCLightningScenario +[TestFixture("postgres")] +[TestFixture("boltdb")] +public class AbcLightningFixture : IDisposable { - private readonly MemoryCache _aliasCache = new(new MemoryCacheOptions { SizeLimit = 10000 }); - private readonly DockerClient _client = new DockerClientConfiguration().CreateClient(); - private readonly Random _random = new(); - private WebApplication _host; - - private bool _isGitlab; - private LNUnitBuilder _LNUnitBuilder; - + public AbcLightningFixture(string dbType) + { + _dbType = dbType; + } + [SetUp] public async Task PerTestSetUp() { - _LNUnitBuilder.CancelAllInterceptors(); - await _LNUnitBuilder.NewBlock(); - await WaitForNodesReady(); + Builder.CancelAllInterceptors(); + Builder.NewBlock(); //tick toc new block + await Builder.NewBlock(); } [OneTimeSetUp] - public async Task OneTimeSetUp() - { - var builder = WebApplication.CreateBuilder(new string[] { }); - - // builder.Services.AddSingleton(builder.Configuration.GetAWSOptions()); - + public async Task OneTimeSetup() + { + var configuration = new ConfigurationBuilder() + .SetBasePath(Directory.GetCurrentDirectory()) // Set the current directory as the base path + .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) + .AddJsonFile("appsettings.Development.json", optional: false, reloadOnChange: true) + .Build(); + var services = new ServiceCollection(); var loggerConfiguration = new LoggerConfiguration().Enrich.FromLogContext(); - loggerConfiguration.ReadFrom.Configuration(builder.Configuration); - + loggerConfiguration.ReadFrom.Configuration(configuration); Log.Logger = loggerConfiguration.CreateLogger(); - builder.Host.UseSerilog(dispose: true); //Log.Logger is implicitly used + services.AddLogging(); + services.AddSerilog(Log.Logger, dispose: true); + _serviceProvider = services.BuildServiceProvider(); + Builder = ActivatorUtilities.CreateInstance(_serviceProvider); - - builder.Services.Configure(options => + if (_dbType == "postgres") { - options.Level = CompressionLevel.Fastest; - }); + PostgresFixture.AddDb("alice"); + PostgresFixture.AddDb("bob"); + PostgresFixture.AddDb("carol"); + } + await _client.CreateDockerImageFromPath("../../../../Docker/lnd", ["custom_lnd", "custom_lnd:latest"]); + await SetupNetwork("custom_lnd", "latest", "/home/lnd/.lnd"); + } + + public string DbContainerName { get; set; } = "postgres"; + private readonly DockerClient _client = new DockerClientConfiguration().CreateClient(); + private string _containerId; + private string _ip; + + private ServiceProvider _serviceProvider; - builder.Services.Configure(options => - { - options.Level = CompressionLevel.Fastest; - }); + public void Dispose() + { + GC.SuppressFinalize(this); - builder.Services.Configure(options => - { - options.ForwardLimit = 1; - options.ForwardedHeaders = - ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto | ForwardedHeaders.XForwardedHost; - options.KnownNetworks.Clear(); - options.KnownProxies.Clear(); - }); - builder.Services.Configure(options => { options.HttpsPort = 443; }); - builder.Services.Configure(o => - { - o.ExpirationScanFrequency = new TimeSpan(0, 10, 0); // we can have stale records up to 10m - o.SizeLimit = 100_000; //Should cover whole never work for a while, it is in record, not size - }); - builder.Services.AddResponseCompression(options => - { - options.EnableForHttps = true; - options.Providers.Add(); - options.Providers.Add(); - }); + // Remove containers + _client.RemoveContainer(DbContainerName).Wait(); + _client.RemoveContainer("miner").Wait(); + _client.RemoveContainer("alice").Wait(); + _client.RemoveContainer("bob").Wait(); + _client.RemoveContainer("carol").Wait(); - // Add services to the container. - builder.Services.AddMemoryCache(); - builder.Services.AddControllers(); - builder.Services.AddOptions(); - - - // builder.Services.Configure(o => - // { - // var port = 8332; - // switch (builder.Configuration["LNMetricsSettings:Network"]) - // { - // case "mainnet": - // port = 8332; - // break; - // case "signet": - // port = 38332; - // break; - // case "testnet": - // port = 28332; - // break; - // case "regtest": - // case "simnet": - // port = 18332; - // break; - // } - // }); - - - builder.Services.Configure>(o => - { - // o.AddRange(lndSettings); - }); + Builder?.Destroy(); + _client.Dispose(); + } - builder.Services.Configure(c => - { - // c.AddNode(new LNDNodeConnection(lndSettings.First())); - // - // foreach (var z in lndSettings.Skip(1)) - // { - // c.AddConnectionSettings(z); - // } - // c.UpdateReadyStatesPeriod(1); - }); + public LNUnitBuilder? Builder { get; private set; } - builder.Services.AddTransient(); - _host = builder.Build(); - var host_running = _host.RunAsync(); - // await Task.Delay(100); - // var f = _host.Services.GetService(); - // _nodePool = _host.Services.GetService(); - // f.Database.Migrate(); - - var tag = "polar_lnd_0_16_3:latest"; - await _client.CreateDockerImageFromPath("./../../../../Docker/lnd", new List { tag }); - await _client.CreateDockerImageFromPath("./../../../../Docker/bitcoin/27.0rc1", new List { "bitcoin:27.0rc1" }); - await _client.Networks.PruneNetworksAsync(new NetworksDeleteUnusedParameters()); - await RemoveContainer("miner"); - await RemoveContainer("alice"); - await RemoveContainer("bob"); - await RemoveContainer("carol"); - - var id = await _client.GetGitlabRunnerNetworkId(); - _LNUnitBuilder = ActivatorUtilities.CreateInstance(_host.Services); - - _isGitlab = !id.IsEmpty(); - if (_isGitlab) - { - _LNUnitBuilder.Configuration.BaseName = "bridge"; - _LNUnitBuilder.Configuration.DockerNetworkId = "bridge"; - } - // if (System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) - // { - // _LNUnitBuilder.Configuration.BaseName = "bridge"; - // _LNUnitBuilder.Configuration.DockerNetworkId = "bridge"; - // } + public async Task SetupNetwork(string image = "lightninglabs/lnd", string tag = "daily-testing-only", + string lndRoot = "/root/.lnd", bool pullImage = false) + { + await _client.RemoveContainer("miner"); + await _client.RemoveContainer("alice"); + await _client.RemoveContainer("bob"); + await _client.RemoveContainer("carol"); - _LNUnitBuilder.AddBitcoinCoreNode("miner", "bitcoin", "27.0rc1", pullImage: false); - _LNUnitBuilder.AddPolarLNDNode("alice", new List - { - new() + Builder.AddBitcoinCoreNode(); + + if (pullImage) await _client.PullImageAndWaitForCompleted(image, tag); + + + Builder.AddPolarLNDNode("alice", + [ + new LNUnitNetworkDefinition.Channel { ChannelSize = 10_000_000, //10MSat - RemotePushOnStart = 5_000_000, // 5MSat - MinHtlcMsat = 0, - MaxHtlcMsat = 1_000_000_000_000_000_000, - TimeLockDelta = 120, - MaintainLocalBalanceRatio = 0.9, RemoteName = "bob" } - }, imageName: "polar_lnd_0_16_3", tagName: "latest", pullImage: false); - _LNUnitBuilder.AddPolarLNDNode("bob", new List - { - new() - { - ChannelSize = 10_000_000, //10MSat - RemotePushOnStart = 5_000_000, // 5MSat - RemoteName = "alice", - BaseFeeMsat = 1_000, // Absurd 1 sats base fee - FeeRatePpm = 1_000_000 // and add a 100% fee to that transfer - }, - new() - { - ChannelSize = 10_000_000, //10MSat - RemotePushOnStart = 5_000_000, // 5MSat - RemoteName = "carol", - BaseFeeMsat = 1_000, // Absurd 1 sats base fee - FeeRatePpm = 1_000_000 // and add a 100% fee to that transfer - // MaxHtlcMsat = 1_000_000_000 //1Msat max amount can flow - }, - - new() + ], imageName: image, tagName: tag, pullImage: false, postgresDSN:_dbType == "postgres" ? PostgresFixture.LNDConnectionStrings["alice"] : null); + + Builder.AddPolarLNDNode("bob", + [ + new LNUnitNetworkDefinition.Channel { ChannelSize = 10_000_000, //10MSat RemotePushOnStart = 1_000_000, // 1MSat - MaxHtlcMsat = 1000_000, // 1000 sats - RemoteName = "carol" + RemoteName = "alice" } - }, imageName: "polar_lnd_0_16_3", tagName: "latest", pullImage: false); - _LNUnitBuilder.AddPolarLNDNode("carol", new List - { - new() + ], imageName: image, tagName: tag, pullImage: false, postgresDSN: _dbType == "postgres" ? PostgresFixture.LNDConnectionStrings["bob"] : null); + + Builder.AddPolarLNDNode("carol", + [ + new LNUnitNetworkDefinition.Channel { ChannelSize = 10_000_000, //10MSat RemotePushOnStart = 1_000_000, // 1MSat RemoteName = "bob" } - }, imageName: "polar_lnd_0_16_3", tagName: "latest", pullImage: false); - await _LNUnitBuilder - .Build(!_isGitlab); // || System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) ); + ], imageName: image, tagName: tag, pullImage: false , postgresDSN: _dbType == "postgres" ? PostgresFixture.LNDConnectionStrings["carol"] : null); + + await Builder.Build(lndRoot: lndRoot); + + WaitNodesReady(); + await WaitGraphReady(); + } + + private void WaitNodesReady() + { + var a = Builder.WaitUntilAliasIsServerReady("alice"); + var b = Builder.WaitUntilAliasIsServerReady("bob"); + var c = Builder.WaitUntilAliasIsServerReady("carol"); + Task.WaitAll(a, b, c); + } - await _LNUnitBuilder.NewBlock(); - await WaitForNodesReady(); + private async Task WaitGraphReady() + { var graphReady = false; while (!graphReady) { - var graph = await _LNUnitBuilder.GetGraphFromAlias("alice"); + var graph = await Builder.GetGraphFromAlias("alice"); if (graph.Nodes.Count < 3) { "Graph not ready...".Print(); @@ -237,59 +161,45 @@ await _LNUnitBuilder } } + + - private async Task WaitForNodesReady() - { - var a = _LNUnitBuilder.WaitUntilAliasIsServerReady("alice"); - var b = _LNUnitBuilder.WaitUntilAliasIsServerReady("bob"); - var c = _LNUnitBuilder.WaitUntilAliasIsServerReady("carol"); - - Task.WaitAll(a, b, c); - } - - [OneTimeTearDown] - public async Task TearDown() - { - await _LNUnitBuilder.Destroy(!_isGitlab); - _LNUnitBuilder.Dispose(); - _client.Dispose(); - await _host.DisposeAsync(); - } - - private async Task RemoveContainer(string name, bool removeLinks = false) + public async Task IsRunning() { try { - await _client.Containers.StopContainerAsync(name, - new ContainerStopParameters { WaitBeforeKillSeconds = 0 }); + var inspect = await _client.Containers.InspectContainerAsync(DbContainerName); + return inspect.State.Running; } - catch (Exception e) + catch { // ignored } - try - { - await _client.Containers.RemoveContainerAsync(name, - new ContainerRemoveParameters { Force = true, RemoveVolumes = true, RemoveLinks = removeLinks }); - } - catch (Exception e) - { - // ignored - } + return false; } - + + + + + /////// + /// + /// + /// /// + + + [Test] [Category("Payment")] [NonParallelizable] public async Task FailureNoRouteBecauseFeesAreTooHigh() { - var invoice = await _LNUnitBuilder.GeneratePaymentRequestFromAlias("carol", new Invoice + var invoice = await Builder.GeneratePaymentRequestFromAlias("carol", new Invoice { Memo = "This path is a trap, better have max_fees set", ValueMsat = 10000 //1 satoshi, looks cheap, but is gonna be expensive with the fees from bob -> carol a hidden cost }); - var payment = await _LNUnitBuilder.MakeLightningPaymentFromAlias("alice", new SendPaymentRequest + var payment = await Builder.MakeLightningPaymentFromAlias("alice", new SendPaymentRequest { PaymentRequest = invoice.PaymentRequest, FeeLimitMsat = 0, //max of 1 satoshi, we ain't gonna be fee attacked @@ -304,12 +214,12 @@ public async Task FailureNoRouteBecauseFeesAreTooHigh() [NonParallelizable] public async Task Successful() { - var invoice = await _LNUnitBuilder.GeneratePaymentRequestFromAlias("bob", new Invoice + var invoice = await Builder.GeneratePaymentRequestFromAlias("bob", new Invoice { Memo = "This path is a trap, better have max_fees set", ValueMsat = 1000 //1 satoshi, fees will be 0 because it is direct peer }); - var payment = await _LNUnitBuilder.MakeLightningPaymentFromAlias("alice", new SendPaymentRequest + var payment = await Builder.MakeLightningPaymentFromAlias("alice", new SendPaymentRequest { PaymentRequest = invoice.PaymentRequest, FeeLimitSat = 100000, //max of 1 satoshi, won't be issue as direct peer so fee is 0 @@ -323,54 +233,23 @@ public async Task Successful() [NonParallelizable] public async Task PoolRebalance() { - var stats = await _LNUnitBuilder.LNDNodePool.RebalanceNodePool(); + var stats = await Builder.LNDNodePool.RebalanceNodePool(); stats.PrintDump(); //Moves around because I should be resetting tests. Assert.That(stats.TotalRebalanceCount >= 2); Assert.That(stats.TotalAmount >= 7993060); - stats = await _LNUnitBuilder.LNDNodePool.RebalanceNodePool(); + stats = await Builder.LNDNodePool.RebalanceNodePool(); Assert.That(stats.TotalRebalanceCount == 0); stats.PrintDump(); } - // [Test] - // [Category("Messaging")] - // [NonParallelizable] - // public async Task SendPeerMessages() - // { - // var nodes = _LNUnitBuilder.LNDNodePool.ReadyNodes.ToImmutableList(); - // var handlers = new Dictionary(); - // foreach (var node in nodes) - // { - // var h = new LNDCustomMessageHandler(node); - // h.OnMessage += (sender, message) => - // { - // $"Peer: {Convert.ToHexString( message.Peer.ToByteArray())} Message: {System.Text.Encoding.UTF8.GetString(message.Data.ToByteArray())}".Print(); - // }; - // handlers.Add(node,h); - // } - // - // await Task.Delay(100); - // for (int i = 0; i < 10; i++) - // { - // await handlers.Last().Value.SendCustomMessageRequest(new CustomMessage() - // { - // Data = ByteString.CopyFrom(new byte[65533]), - // Peer = ByteString.CopyFrom(Convert.FromHexString(handlers.First().Key.LocalNodePubKey)), - // Type = 513 - // }); - // - // } - // - // await Task.Delay(1000); - // } - // + [Test] [Category("Version")] [NonParallelizable] public async Task CheckLNDVersion() { - var n = _LNUnitBuilder.LNDNodePool.ReadyNodes.First(); + var n = Builder.LNDNodePool.ReadyNodes.First(); var info = n.LightningClient.GetInfo(new GetInfoRequest()); Assert.That(info.Version == "0.17.4-beta commit=v0.17.4-beta"); info.Version.Print(); @@ -383,7 +262,7 @@ public async Task CheckLNDVersion() public async Task ChannelAcceptorDeny() { var acceptorTasks = new List(); - foreach (var lnd in _LNUnitBuilder.LNDNodePool.ReadyNodes.ToImmutableList()) + foreach (var lnd in Builder.LNDNodePool.ReadyNodes.ToImmutableList()) { var channelAcceptor = new LNDChannelAcceptor(lnd, async req => @@ -414,8 +293,8 @@ public async Task ChannelAcceptorDeny() acceptorTasks.Add(channelAcceptor); } - var alice = _LNUnitBuilder.GetNodeFromAlias("alice"); - var bob = _LNUnitBuilder.GetNodeFromAlias("bob"); + var alice = Builder.GetNodeFromAlias("alice"); + var bob = Builder.GetNodeFromAlias("bob"); //Fail Private Assert.CatchAsync(async () => { @@ -452,7 +331,7 @@ public async Task ChannelAcceptorDeny() //Move things along so it is confirmed - await _LNUnitBuilder.NewBlock(10); + await Builder.NewBlock(10); //Close var close = alice.LightningClient.CloseChannel(new @@ -462,7 +341,7 @@ public async Task ChannelAcceptorDeny() SatPerVbyte = 10 }); //Move things along so it is confirmed - await _LNUnitBuilder.NewBlock(10); + await Builder.NewBlock(10); acceptorTasks.ForEach(x => x.Dispose()); } @@ -473,7 +352,7 @@ public async Task ChannelAcceptorDeny() public async Task UpdateChannelPolicyPerNode() { var acceptorTasks = new List(); - foreach (var lnd in _LNUnitBuilder.LNDNodePool.ReadyNodes.ToImmutableList()) + foreach (var lnd in Builder.LNDNodePool.ReadyNodes.ToImmutableList()) { var channelsResponse = await lnd.LightningClient.ListChannelsAsync(new ListChannelsRequest { @@ -529,8 +408,8 @@ public async Task UpdateChannelPolicyPerNode() [NonParallelizable] public async Task SuccessfulKeysend() { - var aliceSettings = await _LNUnitBuilder.GetLNDSettingsFromContainer("alice"); - var bobSettings = await _LNUnitBuilder.GetLNDSettingsFromContainer("bob"); + var aliceSettings = await Builder.GetLNDSettingsFromContainer("alice"); + var bobSettings = await Builder.GetLNDSettingsFromContainer("bob"); var alice = new LNDNodeConnection(aliceSettings); var bob = new LNDNodeConnection(bobSettings); var payment = await alice.KeysendPayment(bob.LocalNodePubKey, 10, 100000000, "Hello World", 10, @@ -544,7 +423,7 @@ public async Task SuccessfulKeysend() [NonParallelizable] public async Task ExportGraph() { - var data = await _LNUnitBuilder.GetGraphFromAlias("alice"); + var data = await Builder.GetGraphFromAlias("alice"); data.PrintDump(); Assert.That(data.Nodes.Count == 3); } @@ -554,9 +433,9 @@ public async Task ExportGraph() [NonParallelizable] public async Task GetChannelsFromAlias() { - var alice = await _LNUnitBuilder.GetChannelsFromAlias("alice"); - var bob = await _LNUnitBuilder.GetChannelsFromAlias("bob"); - var carol = await _LNUnitBuilder.GetChannelsFromAlias("carol"); + var alice = await Builder.GetChannelsFromAlias("alice"); + var bob = await Builder.GetChannelsFromAlias("bob"); + var carol = await Builder.GetChannelsFromAlias("carol"); Assert.That(alice.Channels.Any()); "Alice".Print(); alice.Channels.PrintDump(); @@ -573,7 +452,7 @@ public async Task GetChannelsFromAlias() [NonParallelizable] public async Task GetChannelPointFromAliases() { - var data = _LNUnitBuilder.GetChannelPointFromAliases("alice", "bob"); + var data = Builder.GetChannelPointFromAliases("alice", "bob"); data.PrintDump(); Assert.That(data.First().FundingTxidCase != ChannelPoint.FundingTxidOneofCase.None); @@ -584,12 +463,12 @@ public async Task GetChannelPointFromAliases() [NonParallelizable] public async Task GetNodeConnectionFromPool() { - var data = _LNUnitBuilder.LNDNodePool.GetLNDNodeConnection(); + var data = Builder.LNDNodePool.GetLNDNodeConnection(); Assert.That(data.IsRPCReady); Assert.That(data.IsServerReady); - var found = _LNUnitBuilder.LNDNodePool.GetLNDNodeConnection(data.LocalNodePubKey); + var found = Builder.LNDNodePool.GetLNDNodeConnection(data.LocalNodePubKey); Assert.That(data.LocalNodePubKey == found.LocalNodePubKey); - var notFound = _LNUnitBuilder.LNDNodePool.GetLNDNodeConnection("not valid"); + var notFound = Builder.LNDNodePool.GetLNDNodeConnection("not valid"); Assert.That(notFound == null); } @@ -598,7 +477,7 @@ public async Task GetNodeConnectionFromPool() [NonParallelizable] public async Task UpdateChannelPolicy() { - var data = _LNUnitBuilder.UpdateGlobalFeePolicyOnAlias("alice", new LNUnitNetworkDefinition.Channel()); + var data = Builder.UpdateGlobalFeePolicyOnAlias("alice", new LNUnitNetworkDefinition.Channel()); data.PrintDump(); Assert.That(data.FailedUpdates.Count == 0); } @@ -609,22 +488,22 @@ public async Task UpdateChannelPolicy() [NonParallelizable] public async Task FailureInvoiceTimeout() { - var invoice = await _LNUnitBuilder.GeneratePaymentRequestFromAlias("alice", new Invoice + var invoice = await Builder.GeneratePaymentRequestFromAlias("alice", new Invoice { Memo = "Things are too slow, never gonna work", Expiry = 1, ValueMsat = 1003 //1 satoshi, fees will be 0 because it is direct peer, }); //Apply HTLC hold to prevent payment from settling - await _LNUnitBuilder.DelayAllHTLCsOnAlias("alice", 2_000); //6s - await _LNUnitBuilder.DelayAllHTLCsOnAlias("bob", 2_000); //6s - await _LNUnitBuilder.DelayAllHTLCsOnAlias("carol", 2_000); //6s + await Builder.DelayAllHTLCsOnAlias("alice", 2_000); //6s + await Builder.DelayAllHTLCsOnAlias("bob", 2_000); //6s + await Builder.DelayAllHTLCsOnAlias("carol", 2_000); //6s await Task.Delay(1000); var failed = false; try { - var payment = await _LNUnitBuilder.MakeLightningPaymentFromAlias("carol", new SendPaymentRequest + var payment = await Builder.MakeLightningPaymentFromAlias("carol", new SendPaymentRequest { PaymentRequest = invoice.PaymentRequest, FeeLimitSat = 100000000, //max of 1 satoshi, won't be issue as direct peer so fee is 0 @@ -637,10 +516,10 @@ public async Task FailureInvoiceTimeout() } Assert.That(failed); - _LNUnitBuilder.CancelInterceptorOnAlias("alice"); - _LNUnitBuilder.CancelInterceptorOnAlias("bob"); - _LNUnitBuilder.CancelInterceptorOnAlias("carol"); - var invoiceLookup = await _LNUnitBuilder.LookupInvoice("alice", invoice.RHash); + Builder.CancelInterceptorOnAlias("alice"); + Builder.CancelInterceptorOnAlias("bob"); + Builder.CancelInterceptorOnAlias("carol"); + var invoiceLookup = await Builder.LookupInvoice("alice", invoice.RHash); Assert.That(invoiceLookup != null); Assert.That(invoiceLookup.RHash == invoice.RHash); Assert.That(invoiceLookup.State == Invoice.Types.InvoiceState.Canceled); @@ -650,9 +529,9 @@ public async Task FailureInvoiceTimeout() [Category("Payment")] [Category("Interceptor")] [NonParallelizable] - public async Task FailureAtNextHopUnknownNextPeer() + public async Task FailureReasonNoRoute() { - var invoice = await _LNUnitBuilder.GeneratePaymentRequestFromAlias("carol", new Invoice + var invoice = await Builder.GeneratePaymentRequestFromAlias("carol", new Invoice { Memo = "This path is a trap, better have max_fees set", ValueMsat = 10004, @@ -660,9 +539,9 @@ public async Task FailureAtNextHopUnknownNextPeer() }); invoice.PrintDump(); //Apply HTLC hold to prevent payment from settling - await _LNUnitBuilder.DelayAllHTLCsOnAlias("bob", 600_000); //600s - - var paymentTask = _LNUnitBuilder.MakeLightningPaymentFromAlias("alice", new SendPaymentRequest + await Builder.DelayAllHTLCsOnAlias("bob", 600_000); //600s + + var paymentTask = Builder.MakeLightningPaymentFromAlias("alice", new SendPaymentRequest { PaymentRequest = invoice.PaymentRequest, FeeLimitMsat = 10000000000000, @@ -671,18 +550,18 @@ public async Task FailureAtNextHopUnknownNextPeer() }); "Carol shutdown".Print(); - await _LNUnitBuilder.ShutdownByAlias("carol", 0); + await Builder.ShutdownByAlias("carol", 0); await Task.Delay(1000); paymentTask.Status.PrintDump(); - _LNUnitBuilder.CancelInterceptorOnAlias("bob"); + Builder.CancelInterceptorOnAlias("bob"); paymentTask.Status.PrintDump(); var payment = await paymentTask; - await _LNUnitBuilder.RestartByAlias("carol", 0, true); - await _LNUnitBuilder.WaitUntilAliasIsServerReady("carol"); + await Builder.RestartByAlias("carol", 0, true); + await Builder.WaitUntilAliasIsServerReady("carol"); payment.PrintDump(); paymentTask.Dispose(); Assert.That(payment != null && payment.Status == Payment.Types.PaymentStatus.Failed); - Assert.That(payment.Htlcs.Last().Failure.Code == Failure.Types.FailureCode.UnknownNextPeer); + Assert.That(payment != null && payment.FailureReason == PaymentFailureReason.FailureReasonNoRoute); } @@ -695,7 +574,7 @@ public async Task InterceptorTest() List invoices = new(); for (var i = 0; i < 10; i++) { - var invoice = await _LNUnitBuilder.GeneratePaymentRequestFromAlias("alice", new Invoice + var invoice = await Builder.GeneratePaymentRequestFromAlias("alice", new Invoice { Memo = "This path is a trap, better have max_fees set", ValueMsat = 1000, @@ -707,26 +586,26 @@ public async Task InterceptorTest() //Apply HTLC hold to prevent payment from settling var htlcEvents = 0; List monitors = new(); - foreach (var n in _LNUnitBuilder.LNDNodePool.ReadyNodes.ToImmutableList()) + foreach (var n in Builder.LNDNodePool.ReadyNodes.ToImmutableList()) //This disposes so should use Clone of connection monitors.Add(new LNDHTLCMonitor(n.Clone(), htlc => { htlcEvents++; })); - await _LNUnitBuilder.DelayAllHTLCsOnAlias("alice", 1); - await _LNUnitBuilder.DelayAllHTLCsOnAlias("bob", 1); - await _LNUnitBuilder.DelayAllHTLCsOnAlias("carol", 1); + await Builder.DelayAllHTLCsOnAlias("alice", 1); + await Builder.DelayAllHTLCsOnAlias("bob", 1); + await Builder.DelayAllHTLCsOnAlias("carol", 1); await Task.Delay(1000); - Assert.That(await _LNUnitBuilder.IsInterceptorActiveForAlias("alice")); - Assert.That((await _LNUnitBuilder.GetInterceptor("bob")).InterceptCount == 0); - Assert.That(await _LNUnitBuilder.IsInterceptorActiveForAlias("bob")); - Assert.That(await _LNUnitBuilder.IsInterceptorActiveForAlias("carol")); + Assert.That(await Builder.IsInterceptorActiveForAlias("alice")); + Assert.That((await Builder.GetInterceptor("bob")).InterceptCount == 0); + Assert.That(await Builder.IsInterceptorActiveForAlias("bob")); + Assert.That(await Builder.IsInterceptorActiveForAlias("carol")); await Task.Delay(5000); var ii = 0; foreach (var invoice in invoices) { ii++; ii.PrintDump(); - var payment = await _LNUnitBuilder.MakeLightningPaymentFromAlias("carol", new SendPaymentRequest + var payment = await Builder.MakeLightningPaymentFromAlias("carol", new SendPaymentRequest { PaymentRequest = invoice.PaymentRequest, FeeLimitMsat = 1000000000000000, //plenty of money @@ -737,10 +616,10 @@ public async Task InterceptorTest() Assert.That(payment.Status == Payment.Types.PaymentStatus.Succeeded); } - Assert.That(await _LNUnitBuilder.IsInterceptorActiveForAlias("alice"), "alice not intercepting"); - Assert.That((await _LNUnitBuilder.GetInterceptor("bob")).InterceptCount >= 10, "bob not intercepting"); - Assert.That(await _LNUnitBuilder.IsInterceptorActiveForAlias("bob"), "Bob not intercepting"); - Assert.That(await _LNUnitBuilder.IsInterceptorActiveForAlias("carol"), "carol not intercepting"); + Assert.That(await Builder.IsInterceptorActiveForAlias("alice"), "alice not intercepting"); + Assert.That((await Builder.GetInterceptor("bob")).InterceptCount >= 10, "bob not intercepting"); + Assert.That(await Builder.IsInterceptorActiveForAlias("bob"), "Bob not intercepting"); + Assert.That(await Builder.IsInterceptorActiveForAlias("carol"), "carol not intercepting"); Assert.That(htlcEvents > 10); //just what it spit out didn't do math for that } @@ -753,7 +632,7 @@ public async Task GetPaymentFailureData() //Setup - var invoice = await _LNUnitBuilder.GeneratePaymentRequestFromAlias("carol", new Invoice + var invoice = await Builder.GeneratePaymentRequestFromAlias("carol", new Invoice { Memo = "This path is a trap, better have max_fees set", ValueMsat = 10004, //1 satoshi, fees will be 0 because it is direct peer, @@ -761,9 +640,9 @@ public async Task GetPaymentFailureData() }); invoice.PrintDump(); //Apply HTLC hold to prevent payment from settling - await _LNUnitBuilder.DelayAllHTLCsOnAlias("bob", 600_000); //600s + await Builder.DelayAllHTLCsOnAlias("bob", 600_000); //600s - var paymentTask = _LNUnitBuilder.MakeLightningPaymentFromAlias("alice", new SendPaymentRequest + var paymentTask = Builder.MakeLightningPaymentFromAlias("alice", new SendPaymentRequest { PaymentRequest = invoice.PaymentRequest, FeeLimitMsat = 10000000000000, //max of 1 satoshi, won't be issue as direct peer so fee is 0 @@ -772,14 +651,14 @@ public async Task GetPaymentFailureData() }); "Carol shutdown".Print(); - await _LNUnitBuilder.ShutdownByAlias("carol", 0); + await Builder.ShutdownByAlias("carol", 0); await Task.Delay(1000); paymentTask.Status.PrintDump(); - _LNUnitBuilder.CancelInterceptorOnAlias("bob"); + Builder.CancelInterceptorOnAlias("bob"); paymentTask.Status.PrintDump(); var payment = await paymentTask; - await _LNUnitBuilder.RestartByAlias("carol", 0, true); - await _LNUnitBuilder.WaitUntilAliasIsServerReady("carol"); + await Builder.RestartByAlias("carol", 0, true); + await Builder.WaitUntilAliasIsServerReady("carol"); payment.PrintDump(); paymentTask.Dispose(); Assert.That(payment != null && payment.Status == Payment.Types.PaymentStatus.Failed); @@ -789,7 +668,7 @@ public async Task GetPaymentFailureData() var res = new List(); var consolidated = new Dictionary(); - var client = (await _LNUnitBuilder.GetLNDSettingsFromContainer("alice")).GetClient(); + var client = (await Builder.GetLNDSettingsFromContainer("alice")).GetClient(); var payments = await client.LightningClient.ListPaymentsAsync(new ListPaymentsRequest { //CountTotalPayments = true, @@ -843,6 +722,8 @@ async Task Record(bool success, Payment p, LNDNodeConnection c) } } } + private readonly MemoryCache _aliasCache = new(new MemoryCacheOptions { SizeLimit = 10000 }); + private readonly string _dbType; private async Task ToAlias(LNDNodeConnection c, string remotePubkey) { @@ -866,6 +747,111 @@ private async Task ToAlias(LNDNodeConnection c, string remotePubkey) return alias; } + + [Test] + [Category("Payment")] + [Category("Invoice")] + [Category("Sync")] + [NonParallelizable] + public async Task ListInvoiceAndPaymentPaging() + { + var invoices = await Builder.GeneratePaymentsRequestFromAlias("alice", 10, new Invoice + { + Memo = "Things are too slow, never gonna work", + Expiry = 100, + ValueMsat = 1003 //1 satoshi, fees will be 0 because it is direct peer, + }); + + var alice = await Builder.WaitUntilAliasIsServerReady("alice"); + var bob = await Builder.WaitUntilAliasIsServerReady("bob"); + + //purge data + await bob.LightningClient.DeleteAllPaymentsAsync(new DeleteAllPaymentsRequest()); + + foreach (var invoice in invoices) + { + var payment = await Builder.MakeLightningPaymentFromAlias("bob", new SendPaymentRequest + { + PaymentRequest = invoice.PaymentRequest, + FeeLimitSat = 100000000, + TimeoutSeconds = 50 + }); + Assert.That(payment.Status == Payment.Types.PaymentStatus.Succeeded); + } + + + await Task.Delay(2000); + + var lp = await bob.LightningClient.ListPaymentsAsync(new ListPaymentsRequest + { + CreationDateStart = (ulong)DateTime.UtcNow.Subtract(TimeSpan.FromDays(1)).ToUnixTime(), + CreationDateEnd = (ulong)DateTime.UtcNow.AddDays(1).ToUnixTime(), + IncludeIncomplete = false, + Reversed = true, + MaxPayments = 10 + }); + Assert.That(lp.Payments.Count == 10); + await Task.Delay(200); + + var li = await alice.LightningClient.ListInvoicesAsync(new ListInvoiceRequest + { + CreationDateStart = (ulong)DateTime.UtcNow.Subtract(TimeSpan.FromDays(1)).ToUnixTime(), + CreationDateEnd = (ulong)DateTime.UtcNow.AddDays(1).ToUnixTime(), + NumMaxInvoices = 10, + Reversed = true, + PendingOnly = false + }); + Assert.That(li.Invoices.Count == 10); + Assert.That(li.Invoices.First().State == Invoice.Types.InvoiceState.Settled); + } + + [Test] + [Category("Payment")] + [Category("Invoice")] + [Category("Sync")] + [NonParallelizable] + public async Task ListInvoiceAndPaymentNoDatePage() + { + var invoice = await Builder.GeneratePaymentRequestFromAlias("alice", new Invoice + { + Memo = "Things are too slow, never gonna work", + Expiry = 100, + ValueMsat = 1003 //1 satoshi, fees will be 0 because it is direct peer, + }); + + var alice = await Builder.WaitUntilAliasIsServerReady("alice"); + var bob = await Builder.WaitUntilAliasIsServerReady("bob"); + + //purge data + await bob.LightningClient.DeleteAllPaymentsAsync(new DeleteAllPaymentsRequest()); + + var payment = await Builder.MakeLightningPaymentFromAlias("bob", new SendPaymentRequest + { + PaymentRequest = invoice.PaymentRequest, + FeeLimitSat = 100000000, + TimeoutSeconds = 5 + }); + Assert.That(payment.Status == Payment.Types.PaymentStatus.Succeeded); + + await Task.Delay(1000); + + var lp = await bob.LightningClient.ListPaymentsAsync(new ListPaymentsRequest + { + IncludeIncomplete = false, + MaxPayments = 10 + }); + Assert.That(lp.Payments.Count == 1); + await Task.Delay(2000); + + var li = await alice.LightningClient.ListInvoicesAsync(new ListInvoiceRequest + { + NumMaxInvoices = 10, + Reversed = true, + PendingOnly = false + }); + Assert.That(li.Invoices.Count > 0); + Assert.That(li.Invoices.First().State == Invoice.Types.InvoiceState.Settled); + } public class PaymentStats diff --git a/LNUnit.Tests/DataSync.cs b/LNUnit.Tests/DataSync.cs index bef8fe9..dbf13ba 100644 --- a/LNUnit.Tests/DataSync.cs +++ b/LNUnit.Tests/DataSync.cs @@ -1,113 +1,113 @@ -using Lnrpc; -using Routerrpc; -using ServiceStack.Text; - -namespace LNUnit.Tests; - -public partial class ABCLightningScenario -{ - [Test] - [Category("Payment")] - [Category("Invoice")] - [Category("Sync")] - [NonParallelizable] - public async Task ListInvoiceAndPaymentPaging() - { - var invoices = await _LNUnitBuilder.GeneratePaymentsRequestFromAlias("alice", 10, new Invoice - { - Memo = "Things are too slow, never gonna work", - Expiry = 100, - ValueMsat = 1003 //1 satoshi, fees will be 0 because it is direct peer, - }); - - var alice = await _LNUnitBuilder.WaitUntilAliasIsServerReady("alice"); - var bob = await _LNUnitBuilder.WaitUntilAliasIsServerReady("bob"); - - //purge data - await bob.LightningClient.DeleteAllPaymentsAsync(new DeleteAllPaymentsRequest()); - - foreach (var invoice in invoices) - { - var payment = await _LNUnitBuilder.MakeLightningPaymentFromAlias("bob", new SendPaymentRequest - { - PaymentRequest = invoice.PaymentRequest, - FeeLimitSat = 100000000, - TimeoutSeconds = 50 - }); - Assert.That(payment.Status == Payment.Types.PaymentStatus.Succeeded); - } - - - await Task.Delay(2000); - - var lp = await bob.LightningClient.ListPaymentsAsync(new ListPaymentsRequest - { - CreationDateStart = (ulong)DateTime.UtcNow.Subtract(TimeSpan.FromDays(1)).ToUnixTime(), - CreationDateEnd = (ulong)DateTime.UtcNow.AddDays(1).ToUnixTime(), - IncludeIncomplete = false, - Reversed = true, - MaxPayments = 10 - }); - Assert.That(lp.Payments.Count == 10); - await Task.Delay(200); - - var li = await alice.LightningClient.ListInvoicesAsync(new ListInvoiceRequest - { - CreationDateStart = (ulong)DateTime.UtcNow.Subtract(TimeSpan.FromDays(1)).ToUnixTime(), - CreationDateEnd = (ulong)DateTime.UtcNow.AddDays(1).ToUnixTime(), - NumMaxInvoices = 10, - Reversed = true, - PendingOnly = false - }); - Assert.That(li.Invoices.Count == 10); - Assert.That(li.Invoices.First().State == Invoice.Types.InvoiceState.Settled); - } - - [Test] - [Category("Payment")] - [Category("Invoice")] - [Category("Sync")] - [NonParallelizable] - public async Task ListInvoiceAndPaymentNoDatePage() - { - var invoice = await _LNUnitBuilder.GeneratePaymentRequestFromAlias("alice", new Invoice - { - Memo = "Things are too slow, never gonna work", - Expiry = 100, - ValueMsat = 1003 //1 satoshi, fees will be 0 because it is direct peer, - }); - - var alice = await _LNUnitBuilder.WaitUntilAliasIsServerReady("alice"); - var bob = await _LNUnitBuilder.WaitUntilAliasIsServerReady("bob"); - - //purge data - await bob.LightningClient.DeleteAllPaymentsAsync(new DeleteAllPaymentsRequest()); - - var payment = await _LNUnitBuilder.MakeLightningPaymentFromAlias("bob", new SendPaymentRequest - { - PaymentRequest = invoice.PaymentRequest, - FeeLimitSat = 100000000, - TimeoutSeconds = 5 - }); - Assert.That(payment.Status == Payment.Types.PaymentStatus.Succeeded); - - await Task.Delay(1000); - - var lp = await bob.LightningClient.ListPaymentsAsync(new ListPaymentsRequest - { - IncludeIncomplete = false, - MaxPayments = 10 - }); - Assert.That(lp.Payments.Count == 1); - await Task.Delay(2000); - - var li = await alice.LightningClient.ListInvoicesAsync(new ListInvoiceRequest - { - NumMaxInvoices = 10, - Reversed = true, - PendingOnly = false - }); - Assert.That(li.Invoices.Count > 0); - Assert.That(li.Invoices.First().State == Invoice.Types.InvoiceState.Settled); - } -} \ No newline at end of file +// using Lnrpc; +// using Routerrpc; +// using ServiceStack.Text; +// +// namespace LNUnit.Tests; +// +// public partial class ABCLightningScenario +// { +// [Test] +// [Category("Payment")] +// [Category("Invoice")] +// [Category("Sync")] +// [NonParallelizable] +// public async Task ListInvoiceAndPaymentPaging() +// { +// var invoices = await _LNUnitBuilder.GeneratePaymentsRequestFromAlias("alice", 10, new Invoice +// { +// Memo = "Things are too slow, never gonna work", +// Expiry = 100, +// ValueMsat = 1003 //1 satoshi, fees will be 0 because it is direct peer, +// }); +// +// var alice = await _LNUnitBuilder.WaitUntilAliasIsServerReady("alice"); +// var bob = await _LNUnitBuilder.WaitUntilAliasIsServerReady("bob"); +// +// //purge data +// await bob.LightningClient.DeleteAllPaymentsAsync(new DeleteAllPaymentsRequest()); +// +// foreach (var invoice in invoices) +// { +// var payment = await _LNUnitBuilder.MakeLightningPaymentFromAlias("bob", new SendPaymentRequest +// { +// PaymentRequest = invoice.PaymentRequest, +// FeeLimitSat = 100000000, +// TimeoutSeconds = 50 +// }); +// Assert.That(payment.Status == Payment.Types.PaymentStatus.Succeeded); +// } +// +// +// await Task.Delay(2000); +// +// var lp = await bob.LightningClient.ListPaymentsAsync(new ListPaymentsRequest +// { +// CreationDateStart = (ulong)DateTime.UtcNow.Subtract(TimeSpan.FromDays(1)).ToUnixTime(), +// CreationDateEnd = (ulong)DateTime.UtcNow.AddDays(1).ToUnixTime(), +// IncludeIncomplete = false, +// Reversed = true, +// MaxPayments = 10 +// }); +// Assert.That(lp.Payments.Count == 10); +// await Task.Delay(200); +// +// var li = await alice.LightningClient.ListInvoicesAsync(new ListInvoiceRequest +// { +// CreationDateStart = (ulong)DateTime.UtcNow.Subtract(TimeSpan.FromDays(1)).ToUnixTime(), +// CreationDateEnd = (ulong)DateTime.UtcNow.AddDays(1).ToUnixTime(), +// NumMaxInvoices = 10, +// Reversed = true, +// PendingOnly = false +// }); +// Assert.That(li.Invoices.Count == 10); +// Assert.That(li.Invoices.First().State == Invoice.Types.InvoiceState.Settled); +// } +// +// [Test] +// [Category("Payment")] +// [Category("Invoice")] +// [Category("Sync")] +// [NonParallelizable] +// public async Task ListInvoiceAndPaymentNoDatePage() +// { +// var invoice = await _LNUnitBuilder.GeneratePaymentRequestFromAlias("alice", new Invoice +// { +// Memo = "Things are too slow, never gonna work", +// Expiry = 100, +// ValueMsat = 1003 //1 satoshi, fees will be 0 because it is direct peer, +// }); +// +// var alice = await _LNUnitBuilder.WaitUntilAliasIsServerReady("alice"); +// var bob = await _LNUnitBuilder.WaitUntilAliasIsServerReady("bob"); +// +// //purge data +// await bob.LightningClient.DeleteAllPaymentsAsync(new DeleteAllPaymentsRequest()); +// +// var payment = await _LNUnitBuilder.MakeLightningPaymentFromAlias("bob", new SendPaymentRequest +// { +// PaymentRequest = invoice.PaymentRequest, +// FeeLimitSat = 100000000, +// TimeoutSeconds = 5 +// }); +// Assert.That(payment.Status == Payment.Types.PaymentStatus.Succeeded); +// +// await Task.Delay(1000); +// +// var lp = await bob.LightningClient.ListPaymentsAsync(new ListPaymentsRequest +// { +// IncludeIncomplete = false, +// MaxPayments = 10 +// }); +// Assert.That(lp.Payments.Count == 1); +// await Task.Delay(2000); +// +// var li = await alice.LightningClient.ListInvoicesAsync(new ListInvoiceRequest +// { +// NumMaxInvoices = 10, +// Reversed = true, +// PendingOnly = false +// }); +// Assert.That(li.Invoices.Count > 0); +// Assert.That(li.Invoices.First().State == Invoice.Types.InvoiceState.Settled); +// } +// } \ No newline at end of file diff --git a/LNUnit.Tests/Fixture/PostgresFixture.cs b/LNUnit.Tests/Fixture/PostgresFixture.cs new file mode 100644 index 0000000..8c834cf --- /dev/null +++ b/LNUnit.Tests/Fixture/PostgresFixture.cs @@ -0,0 +1,168 @@ +using System.Collections.Immutable; +using System.Net; +using System.Net.Sockets; +using Dasync.Collections; +using Docker.DotNet; +using Docker.DotNet.Models; +using Lnrpc; +using LNUnit.Extentions; +using LNUnit.LND; +using LNUnit.Setup; +using LNUnit.Tests; +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.Logging; +using Npgsql; +using Routerrpc; +using Serilog; +using Serilog.Extensions.Logging; +using ServiceStack.Text; +using Assert = NUnit.Framework.Assert; +using HostConfig = Docker.DotNet.Models.HostConfig; + +namespace LNUnit.Fixtures; + + +/// +/// Used Globally to get Postgres support +/// +public static class PostgresFixture +{ + public static Dictionary LNDConnectionStrings = new(); + public static string DbConnectionStringDotNet { get;internal set; } = string.Empty; + public static string DbContainerName { get; internal set; } = "postgres"; + + public static async Task IsReady() => await Instance?.IsRunning(); + + internal static PostgresInstanceFixture? Instance { get; set; } + public static void AddDb(string dbName) + { + Instance?.AddDb(dbName); + } +} + +[SetUpFixture] +public class PostgresInstanceFixture +{ + private readonly DockerClient _client = new DockerClientConfiguration().CreateClient(); + private string _containerId; + private string _ip; + + public readonly Dictionary LNDConnectionStrings = PostgresFixture.LNDConnectionStrings; + + public string DbConnectionStringDotNet { get; private set; } + public string DbContainerName { get; set; } = "postgres"; + + [OneTimeSetUp] + public async Task RunBeforeAnyTests() + { + await StartPostgres(); + PostgresFixture.Instance = this; + } + + [OneTimeTearDown] + public async Task RunAfterAnyTests() + { + _client.Dispose(); + } + + internal void AddDb(string dbName) + { + using (NpgsqlConnection connection = new(DbConnectionStringDotNet)) + { + connection.Open(); + using var checkIfExistsCommand = + new NpgsqlCommand($"SELECT 1 FROM pg_catalog.pg_database WHERE datname = '{dbName}'", connection); + var result = checkIfExistsCommand.ExecuteScalar(); + + if (result == null) + { + using var command = new NpgsqlCommand($"CREATE DATABASE \"{dbName}\"", connection); + command.ExecuteNonQuery(); + } + } + + LNDConnectionStrings.Add(dbName, + $"postgresql://superuser:superuser@{_ip}:5432/{dbName}?sslmode=disable"); + } + + private async Task StartPostgres( + string image = "postgres", + string tag = "16.2-alpine", + string password = "superuser", + string username = "superuser") + { + await _client.PullImageAndWaitForCompleted(image, tag); + await _client.RemoveContainer(DbContainerName); + var nodeContainer = await _client.Containers.CreateContainerAsync(new CreateContainerParameters + { + Image = $"{image}:{tag}", + HostConfig = new HostConfig + { + NetworkMode = "bridge" + }, + Name = $"{DbContainerName}", + Hostname = $"{DbContainerName}", + Env = + [ + $"POSTGRES_PASSWORD={username}", + $"POSTGRES_USER={password}", + $"POSTGRES_DB=postgres" + ] + }); + Assert.NotNull(nodeContainer); + _containerId = nodeContainer.ID; + var started = await _client.Containers.StartContainerAsync(_containerId, new ContainerStartParameters()); + + //Build connection string + var ipAddressReady = false; + while (!ipAddressReady) + { + var listContainers = await _client.Containers.ListContainersAsync(new ContainersListParameters()); + var db = listContainers.FirstOrDefault(x => x.ID == nodeContainer.ID); + if (db != null) + { + _ip = db.NetworkSettings.Networks.First().Value.IPAddress; + DbConnectionStringDotNet = $"Host={_ip};Database=postgres;Username=superuser;Password=superuser"; + ipAddressReady = true; + } + else + { + await Task.Delay(100); + } + } + + //wait for TCP socket to open + var tcpConnectable = false; + while (!tcpConnectable) + try + { + TcpClient c = new() + { + ReceiveTimeout = 1, + SendTimeout = 1 + }; + await c.ConnectAsync(new IPEndPoint(IPAddress.Parse(_ip), 5432)); + if (c.Connected) tcpConnectable = true; + } + catch (Exception e) + { + await Task.Delay(50); + } + } + + + public async Task IsRunning() + { + try + { + var inspect = await _client.Containers.InspectContainerAsync(DbContainerName); + return inspect.State.Running; + } + catch + { + // ignored + } + + return false; + } +} diff --git a/LNUnit.Tests/Fixture/PostgresLightningFixture.cs b/LNUnit.Tests/Fixture/PostgresLightningFixture.cs deleted file mode 100644 index fdec1ad..0000000 --- a/LNUnit.Tests/Fixture/PostgresLightningFixture.cs +++ /dev/null @@ -1,348 +0,0 @@ -using System.Collections.Immutable; -using System.Net; -using System.Net.Sockets; -using Docker.DotNet; -using Docker.DotNet.Models; -using Lnrpc; -using LNUnit.LND; -using LNUnit.Setup; -using Microsoft.AspNetCore.Builder; -using Microsoft.Extensions.DependencyInjection; -using Npgsql; -using Routerrpc; -using Serilog; -using ServiceStack; -using ServiceStack.Text; -using Assert = NUnit.Framework.Assert; -using HostConfig = Docker.DotNet.Models.HostConfig; - -namespace LNUnit.Fixtures; - -//[TestFixture] -// [SingleThreaded] -public class PostgresLightningFixture : IDisposable -{ - - [SetUp] - public async Task PerTestSetUp() - { - Builder.CancelAllInterceptors(); - await Builder.NewBlock(); - } - - [Test] - [NonParallelizable] - public async Task InterceptorTest() - { - List invoices = new(); - for (var i = 0; i < 10; i++) - { - var invoice = await Builder.GeneratePaymentRequestFromAlias("alice", new Invoice - { - Memo = "This path is a trap, better have max_fees set", - ValueMsat = 1000, - Expiry = 60 * 60 * 24 - }); - invoices.Add(invoice); - } - - //Apply HTLC hold to prevent payment from settling - var htlcEvents = 0; - List monitors = new(); - foreach (var n in Builder.LNDNodePool.ReadyNodes.ToImmutableList()) - //This disposes so should use Clone of connection - monitors.Add(new LNDHTLCMonitor(n.Clone(), htlc => { htlcEvents++; })); - - - await Builder.DelayAllHTLCsOnAlias("alice", 1); - await Builder.DelayAllHTLCsOnAlias("bob", 1); - await Builder.DelayAllHTLCsOnAlias("carol", 1); - await Task.Delay(1000); - Assert.That(await Builder.IsInterceptorActiveForAlias("alice")); - Assert.That((await Builder.GetInterceptor("bob")).InterceptCount == 0); - Assert.That(await Builder.IsInterceptorActiveForAlias("bob")); - Assert.That(await Builder.IsInterceptorActiveForAlias("carol")); - await Task.Delay(5000); - var ii = 0; - foreach (var invoice in invoices) - { - ii++; - ii.PrintDump(); - var payment = await Builder.MakeLightningPaymentFromAlias("carol", new SendPaymentRequest - { - PaymentRequest = invoice.PaymentRequest, - FeeLimitMsat = 1000000000000000, //plenty of money - TimeoutSeconds = 20000, - NoInflightUpdates = true - }); - Assert.That(payment.Status == Payment.Types.PaymentStatus.Succeeded); - } - - Assert.That(await Builder.IsInterceptorActiveForAlias("alice"), "alice not intercepting"); - Assert.That((await Builder.GetInterceptor("bob")).InterceptCount >= 10, "bob not intercepting"); - Assert.That(await Builder.IsInterceptorActiveForAlias("bob"), "Bob not intercepting"); - Assert.That(await Builder.IsInterceptorActiveForAlias("carol"), "carol not intercepting"); - Assert.That(htlcEvents > 10); //just what it spit out didn't do math for that - } - - public string DbContainerName { get; set; } = "postgres"; - private readonly DockerClient _client = new DockerClientConfiguration().CreateClient(); - private string _containerId; - private string _ip; - - [OneTimeSetUp] - public async Task OneTimeSetup() - { - var builder = WebApplication.CreateBuilder(new string[] { }); - - // builder.Services.AddSingleton(builder.Configuration.GetAWSOptions()); - - var loggerConfiguration = new LoggerConfiguration().Enrich.FromLogContext(); - loggerConfiguration.ReadFrom.Configuration(builder.Configuration); - - Log.Logger = loggerConfiguration.CreateLogger(); - builder.Host.UseSerilog(dispose: true); //Log.Logger is implicitly used - builder.Services.Configure>(o => - { - // o.AddRange(lndSettings); - }); - - builder.Services.Configure(c => - { - // c.AddNode(new LNDNodeConnection(lndSettings.First())); - // - // foreach (var z in lndSettings.Skip(1)) - // { - // c.AddConnectionSettings(z); - // } - // c.UpdateReadyStatesPeriod(1); - }); - - builder.Services.AddTransient(); - // Add services to the container. - builder.Services.AddMemoryCache(); - builder.Services.AddControllers(); - builder.Services.AddOptions(); - _host = builder.Build(); - var host_running = _host.RunAsync(); - Builder = ActivatorUtilities.CreateInstance(_host.Services); - - await StartPostgres(); - AddDb("alice"); - AddDb("bob"); - AddDb("carol"); - await _client.CreateDockerImageFromPath("../../../../Docker/lnd", ["custom_lnd", "custom_lnd:latest"]); - await SetupNetwork("custom_lnd","latest", "/home/lnd/.lnd"); - } - - private void AddDb(string dbName) - { - using (NpgsqlConnection connection = new(DbConnectionString)) - { - connection.Open(); - using var checkIfExistsCommand = new NpgsqlCommand($"SELECT 1 FROM pg_catalog.pg_database WHERE datname = '{dbName}'", connection); - var result = checkIfExistsCommand.ExecuteScalar(); - - if (result == null) - { - - using var command = new NpgsqlCommand($"CREATE DATABASE \"{dbName}\"", connection); - command.ExecuteNonQuery(); - } - } - LNDConnectionStrings.Add(dbName, - $"postgresql://superuser:superuser@{_ip}:5432/{dbName}?sslmode=disable"); - } - - public string DbConnectionStringLND { get; private set; } - public string DbConnectionString { get; private set; } - - public Dictionary LNDConnectionStrings = new(); - private WebApplication _host; - - public void Dispose() - { - GC.SuppressFinalize(this); - - // Remove containers - RemoveContainer(DbContainerName).Wait(); - RemoveContainer("miner").Wait(); - RemoveContainer("alice").Wait(); - RemoveContainer("bob").Wait(); - RemoveContainer("carol").Wait(); - - Builder?.Destroy(); - _client.Dispose(); - } - - public LNUnitBuilder? Builder { get; private set; } - - - public async Task SetupNetwork(string image = "lightninglabs/lnd", string tag = "daily-testing-only", string lndRoot = "/root/.lnd", bool pullImage = false) - { - await RemoveContainer("miner"); - await RemoveContainer("alice"); - await RemoveContainer("bob"); - await RemoveContainer("carol"); - - Builder.AddBitcoinCoreNode(); - - if (pullImage) - { - await _client.PullImageAndWaitForCompleted(image, tag); - } - - Builder.AddPolarLNDNode("alice", - [ - new() - { - ChannelSize = 10_000_000, //10MSat - RemoteName = "bob" - } - ], imageName: image, tagName: tag, pullImage: false, postgresDSN: LNDConnectionStrings["alice"]); - - Builder.AddPolarLNDNode("bob", - [ - new() - { - ChannelSize = 10_000_000, //10MSat - RemotePushOnStart = 1_000_000, // 1MSat - RemoteName = "alice" - } - ], imageName: image, tagName: tag, pullImage: false, postgresDSN: LNDConnectionStrings["bob"]); - - Builder.AddPolarLNDNode("carol", - [ - // new() - // { - // ChannelSize = 10_000_000, //10MSat - // RemotePushOnStart = 1_000_000, // 1MSat - // RemoteName = "alice" - // }, - new() - { - ChannelSize = 10_000_000, //10MSat - RemotePushOnStart = 1_000_000, // 1MSat - RemoteName = "bob" - } - ], imageName: image, tagName: tag, pullImage: false);//, postgresDSN: LNDConnectionStrings["carol"]); - - await Builder.Build(lndRoot: lndRoot); - var a = Builder.WaitUntilAliasIsServerReady("alice"); - var b = Builder.WaitUntilAliasIsServerReady("bob"); - var c = Builder.WaitUntilAliasIsServerReady("carol"); - - Task.WaitAll(a, b, c); - - var graphReady = false; - while (!graphReady) - { - var graph = await Builder.GetGraphFromAlias("alice"); - if (graph.Nodes.Count < 3) - { - "Graph not ready...".Print(); - await Task.Delay(250); //let the graph sync - } - else - { - graphReady = true; - } - } - } - - public async Task StartPostgres() - { - await _client.PullImageAndWaitForCompleted("postgres", "16.2-alpine"); - await RemoveContainer(DbContainerName); - var nodeContainer = await _client.Containers.CreateContainerAsync(new CreateContainerParameters - { - Image = "postgres:16.2-alpine", - HostConfig = new HostConfig - { - NetworkMode = "bridge" - }, - Name = $"{DbContainerName}", - Hostname = $"{DbContainerName}", - Env = - [ - "POSTGRES_PASSWORD=superuser", - "POSTGRES_USER=superuser", - "POSTGRES_DB=postgres" - ] - }); - Assert.NotNull(nodeContainer); - _containerId = nodeContainer.ID; - var started = await _client.Containers.StartContainerAsync(_containerId, new ContainerStartParameters()); - - //Build connection string - var ipAddressReady = false; - while (!ipAddressReady) - { - var listContainers = await _client.Containers.ListContainersAsync(new ContainersListParameters()); - - var db = listContainers.FirstOrDefault(x => x.ID == nodeContainer.ID); - if (db != null) - { - _ip = db.NetworkSettings.Networks.First().Value.IPAddress; - DbConnectionString = $"Host={_ip};Database=postgres;Username=superuser;Password=superuser"; - ipAddressReady = true; - } - else - { - await Task.Delay(100); - } - - } - //wait for TCP socket to open - var tcpConnectable = false; - while (!tcpConnectable) - { - try - { - TcpClient c = new() - { - ReceiveTimeout = 1, - SendTimeout = 1 - }; - await c.ConnectAsync(new IPEndPoint(IPAddress.Parse(_ip), 5432)); - if (c.Connected) - { - tcpConnectable = true; - } - } - catch (Exception e) - { - await Task.Delay(50); - } - } - } - - private async Task RemoveContainer(string name) - { - try - { - await _client.Containers.RemoveContainerAsync(name, - new ContainerRemoveParameters { Force = true, RemoveVolumes = true }); - } - catch - { - // ignored - } - } - - public async Task IsRunning() - { - try - { - var inspect = await _client.Containers.InspectContainerAsync(DbContainerName); - return inspect.State.Running; - } - catch - { - // ignored - } - - return false; - } -} - \ No newline at end of file diff --git a/LNUnit/Setup/DockerHelper.cs b/LNUnit/Setup/DockerHelper.cs index 586795a..fd10d5f 100644 --- a/LNUnit/Setup/DockerHelper.cs +++ b/LNUnit/Setup/DockerHelper.cs @@ -43,6 +43,28 @@ await client.Images.CreateImageAsync( await Task.Delay(1); } + public static async Task RemoveContainer(this DockerClient client,string name, bool removeLinks = false) + { + try + { + await client.Containers.StopContainerAsync(name, + new ContainerStopParameters { WaitBeforeKillSeconds = 0 }); + } + catch (Exception e) + { + // ignored + } + + try + { + await client.Containers.RemoveContainerAsync(name, + new ContainerRemoveParameters { Force = true, RemoveVolumes = true, RemoveLinks = removeLinks }); + } + catch (Exception e) + { + // ignored + } + } private static string GetRandomHexString(int size = 8) { var b = new byte[size]; From 3665af8cba62e2354c858b0d133eb410c252e425 Mon Sep 17 00:00:00 2001 From: Richard Safier Date: Tue, 26 Mar 2024 02:10:44 -0400 Subject: [PATCH 3/9] style and format cleanup --- LNBolt.Tests/Bech32Tests.cs | 24 ++-- LNBolt.Tests/LNBolt.Tests.csproj | 6 +- LNBolt/LNBolt.csproj | 2 +- LNUnit.LND/LNDNodePool.cs | 2 +- LNUnit.LND/LNDSimpleHtlcInterceptorHandler.cs | 3 +- LNUnit.LND/LNUnit.LND.csproj | 10 +- LNUnit.Tests/AbcLightningFixture.cs | 116 +++++++++--------- LNUnit.Tests/DataSync.cs | 3 +- LNUnit.Tests/DockerTest.cs | 7 +- LNUnit.Tests/Fixture/PostgresFixture.cs | 37 +++--- LNUnit.Tests/LNUnit.Tests.csproj | 16 +-- LNUnit.Tests/appsettings.json | 1 - LNUnit/LNUnit.csproj | 8 +- LNUnit/Setup/DockerHelper.cs | 3 +- LNUnit/Setup/LNUnitBuilder.cs | 27 ++-- 15 files changed, 130 insertions(+), 135 deletions(-) diff --git a/LNBolt.Tests/Bech32Tests.cs b/LNBolt.Tests/Bech32Tests.cs index 121bafe..ef278fd 100644 --- a/LNBolt.Tests/Bech32Tests.cs +++ b/LNBolt.Tests/Bech32Tests.cs @@ -4,35 +4,25 @@ using LNBolt.BOLT11; using NUnit.Framework; using org.ldk.structs; -using ServiceStack; namespace LNBolt.Tests; public class Bech32Tests { - - public record PaymentRequest - { - public byte[] PaymentHash { get; set; } - public Option_u64Z AmountMilliSatoshis { get; set; } - public long expiry_time { get; set; } - public byte[] payee_pub_key { get; set; } - } - [Test] public async Task ldk() { - for (int i = 0; i < 10000; i++) + for (var i = 0; i < 10000; i++) { var bolt11 = Bolt11Invoice.from_str( "lnbc100p1psj9jhxdqud3jxktt5w46x7unfv9kz6mn0v3jsnp4q0d3p2sfluzdx45tqcsh2pu5qc7lgq0xs578ngs6s0s68ua4h7cvspp5q6rmq35js88zp5dvwrv9m459tnk2zunwj5jalqtyxqulh0l5gflssp5nf55ny5gcrfl30xuhzj3nphgj27rstekmr9fw3ny5989s300gyus9qyysgqcqpcrzjqw2sxwe993h5pcm4dxzpvttgza8zhkqxpgffcrf5v25nwpr3cmfg7z54kuqq8rgqqqqqqqq2qqqqq9qq9qrzjqd0ylaqclj9424x9m8h2vcukcgnm6s56xfgu3j78zyqzhgs4hlpzvznlugqq9vsqqqqqqqlgqqqqqeqq9qrzjqwldmj9dha74df76zhx6l9we0vjdquygcdt3kssupehe64g6yyp5yz5rhuqqwccqqyqqqqlgqqqqjcqq9qrzjqf9e58aguqr0rcun0ajlvmzq3ek63cw2w282gv3z5uupmuwvgjtq2z55qsqqg6qqqyqqqrtnqqqzq3cqygrzjqvphmsywntrrhqjcraumvc4y6r8v4z5v593trte429v4hredj7ms5z52usqq9ngqqqqqqqlgqqqqqqgq9qrzjq2v0vp62g49p7569ev48cmulecsxe59lvaw3wlxm7r982zxa9zzj7z5l0cqqxusqqyqqqqlgqqqqqzsqygarl9fh38s0gyuxjjgux34w75dnc6xp2l35j7es3jd4ugt3lu0xzre26yg5m7ke54n2d5sym4xcmxtl8238xxvw5h5h5j5r6drg6k6zcqj0fcwg"); var x = bolt11 as Result_Bolt11InvoiceParseOrSemanticErrorZ.Result_Bolt11InvoiceParseOrSemanticErrorZ_OK; - var res = new PaymentRequest() + var res = new PaymentRequest { PaymentHash = x.res.payment_hash(), AmountMilliSatoshis = x.res.amount_milli_satoshis(), expiry_time = x.res.expiry_time(), - payee_pub_key = x.res.payee_pub_key(), + payee_pub_key = x.res.payee_pub_key() //RouteHints = x.res.route_hints(), }; var y = x.res.payment_hash(); @@ -102,4 +92,12 @@ public void LNURLDecode() Assert.AreEqual("https://service.com/api?q=3fc3645b439ce8e7f2553a69e5267081d96dcd340693afabe04be7b0ccd178df", url); } + + public record PaymentRequest + { + public byte[] PaymentHash { get; set; } + public Option_u64Z AmountMilliSatoshis { get; set; } + public long expiry_time { get; set; } + public byte[] payee_pub_key { get; set; } + } } \ No newline at end of file diff --git a/LNBolt.Tests/LNBolt.Tests.csproj b/LNBolt.Tests/LNBolt.Tests.csproj index 2f76277..4509ee0 100644 --- a/LNBolt.Tests/LNBolt.Tests.csproj +++ b/LNBolt.Tests/LNBolt.Tests.csproj @@ -9,9 +9,9 @@ - - - + + + diff --git a/LNBolt/LNBolt.csproj b/LNBolt/LNBolt.csproj index 3ca087f..bd5ba00 100644 --- a/LNBolt/LNBolt.csproj +++ b/LNBolt/LNBolt.csproj @@ -17,7 +17,7 @@ - + diff --git a/LNUnit.LND/LNDNodePool.cs b/LNUnit.LND/LNDNodePool.cs index 47ce087..5a7fbec 100644 --- a/LNUnit.LND/LNDNodePool.cs +++ b/LNUnit.LND/LNDNodePool.cs @@ -271,7 +271,7 @@ public async Task RebalanceNodePool() { Value = valueInSataoshis, Memo = "InvoicePayRebalance", - Expiry = 60, //1 minute + Expiry = 60 //1 minute }); _logger?.LogDebug("InvoicePayRebalance: {PaymentRequest} for {Value} sats from {Source}", invoice.PaymentRequest, valueInSataoshis, src.LocalAlias); diff --git a/LNUnit.LND/LNDSimpleHtlcInterceptorHandler.cs b/LNUnit.LND/LNDSimpleHtlcInterceptorHandler.cs index dc0cc2f..9516533 100644 --- a/LNUnit.LND/LNDSimpleHtlcInterceptorHandler.cs +++ b/LNUnit.LND/LNDSimpleHtlcInterceptorHandler.cs @@ -18,7 +18,8 @@ public LNDSimpleHtlcInterceptorHandler(LNDNodeConnection connection, Func> interceptLogic) { Node = connection; - _task = Task.Factory.StartNew(AttachInterceptor, _cancellationTokenSource.Token, TaskCreationOptions.LongRunning, TaskScheduler.Current); + _task = Task.Factory.StartNew(AttachInterceptor, _cancellationTokenSource.Token, + TaskCreationOptions.LongRunning, TaskScheduler.Current); OnIntercept = interceptLogic; while (!Running) Task.Delay(100).Wait(); diff --git a/LNUnit.LND/LNUnit.LND.csproj b/LNUnit.LND/LNUnit.LND.csproj index fe4b51c..3c05021 100644 --- a/LNUnit.LND/LNUnit.LND.csproj +++ b/LNUnit.LND/LNUnit.LND.csproj @@ -19,7 +19,7 @@ - + @@ -33,13 +33,13 @@ - + - - - + + + diff --git a/LNUnit.Tests/AbcLightningFixture.cs b/LNUnit.Tests/AbcLightningFixture.cs index 98b6082..a95b194 100644 --- a/LNUnit.Tests/AbcLightningFixture.cs +++ b/LNUnit.Tests/AbcLightningFixture.cs @@ -1,32 +1,27 @@ -using System.Collections.Immutable; +using System.Collections.Immutable; using Dasync.Collections; -using Docker.DotNet; +using Docker.DotNet; using Google.Protobuf; using Grpc.Core; using Lnrpc; using LNUnit.Extentions; +using LNUnit.Fixtures; using LNUnit.LND; -using LNUnit.Setup; +using LNUnit.Setup; using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection; using Routerrpc; -using Serilog; +using Serilog; using ServiceStack; using ServiceStack.Text; -using Assert = NUnit.Framework.Assert; -using LNUnit.Fixtures; +using Assert = NUnit.Framework.Assert; [TestFixture("postgres")] [TestFixture("boltdb")] public class AbcLightningFixture : IDisposable { - public AbcLightningFixture(string dbType) - { - _dbType = dbType; - } - [SetUp] public async Task PerTestSetUp() { @@ -37,18 +32,18 @@ public async Task PerTestSetUp() [OneTimeSetUp] public async Task OneTimeSetup() - { + { var configuration = new ConfigurationBuilder() .SetBasePath(Directory.GetCurrentDirectory()) // Set the current directory as the base path - .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) - .AddJsonFile("appsettings.Development.json", optional: false, reloadOnChange: true) + .AddJsonFile("appsettings.json", false, true) + .AddJsonFile("appsettings.Development.json", false, true) .Build(); - var services = new ServiceCollection(); + var services = new ServiceCollection(); var loggerConfiguration = new LoggerConfiguration().Enrich.FromLogContext(); loggerConfiguration.ReadFrom.Configuration(configuration); Log.Logger = loggerConfiguration.CreateLogger(); services.AddLogging(); - services.AddSerilog(Log.Logger, dispose: true); + services.AddSerilog(Log.Logger, true); _serviceProvider = services.BuildServiceProvider(); Builder = ActivatorUtilities.CreateInstance(_serviceProvider); @@ -58,15 +53,21 @@ public async Task OneTimeSetup() PostgresFixture.AddDb("bob"); PostgresFixture.AddDb("carol"); } + await _client.CreateDockerImageFromPath("../../../../Docker/lnd", ["custom_lnd", "custom_lnd:latest"]); await SetupNetwork("custom_lnd", "latest", "/home/lnd/.lnd"); } - + + public AbcLightningFixture(string dbType) + { + _dbType = dbType; + } + public string DbContainerName { get; set; } = "postgres"; private readonly DockerClient _client = new DockerClientConfiguration().CreateClient(); private string _containerId; private string _ip; - + private ServiceProvider _serviceProvider; public void Dispose() @@ -99,38 +100,41 @@ public async Task SetupNetwork(string image = "lightninglabs/lnd", string tag = if (pullImage) await _client.PullImageAndWaitForCompleted(image, tag); - + Builder.AddPolarLNDNode("alice", - [ - new LNUnitNetworkDefinition.Channel - { - ChannelSize = 10_000_000, //10MSat - RemoteName = "bob" - } - ], imageName: image, tagName: tag, pullImage: false, postgresDSN:_dbType == "postgres" ? PostgresFixture.LNDConnectionStrings["alice"] : null); + [ + new LNUnitNetworkDefinition.Channel + { + ChannelSize = 10_000_000, //10MSat + RemoteName = "bob" + } + ], imageName: image, tagName: tag, pullImage: false, + postgresDSN: _dbType == "postgres" ? PostgresFixture.LNDConnectionStrings["alice"] : null); Builder.AddPolarLNDNode("bob", - [ - new LNUnitNetworkDefinition.Channel - { - ChannelSize = 10_000_000, //10MSat - RemotePushOnStart = 1_000_000, // 1MSat - RemoteName = "alice" - } - ], imageName: image, tagName: tag, pullImage: false, postgresDSN: _dbType == "postgres" ? PostgresFixture.LNDConnectionStrings["bob"] : null); + [ + new LNUnitNetworkDefinition.Channel + { + ChannelSize = 10_000_000, //10MSat + RemotePushOnStart = 1_000_000, // 1MSat + RemoteName = "alice" + } + ], imageName: image, tagName: tag, pullImage: false, + postgresDSN: _dbType == "postgres" ? PostgresFixture.LNDConnectionStrings["bob"] : null); Builder.AddPolarLNDNode("carol", - [ - new LNUnitNetworkDefinition.Channel - { - ChannelSize = 10_000_000, //10MSat - RemotePushOnStart = 1_000_000, // 1MSat - RemoteName = "bob" - } - ], imageName: image, tagName: tag, pullImage: false , postgresDSN: _dbType == "postgres" ? PostgresFixture.LNDConnectionStrings["carol"] : null); + [ + new LNUnitNetworkDefinition.Channel + { + ChannelSize = 10_000_000, //10MSat + RemotePushOnStart = 1_000_000, // 1MSat + RemoteName = "bob" + } + ], imageName: image, tagName: tag, pullImage: false, + postgresDSN: _dbType == "postgres" ? PostgresFixture.LNDConnectionStrings["carol"] : null); await Builder.Build(lndRoot: lndRoot); - + WaitNodesReady(); await WaitGraphReady(); } @@ -161,8 +165,6 @@ private async Task WaitGraphReady() } } - - public async Task IsRunning() { @@ -178,17 +180,10 @@ public async Task IsRunning() return false; } - - - - + + /////// - /// - /// /// /// - - - [Test] [Category("Payment")] [NonParallelizable] @@ -243,7 +238,7 @@ public async Task PoolRebalance() stats.PrintDump(); } - + [Test] [Category("Version")] [NonParallelizable] @@ -540,7 +535,7 @@ public async Task FailureReasonNoRoute() invoice.PrintDump(); //Apply HTLC hold to prevent payment from settling await Builder.DelayAllHTLCsOnAlias("bob", 600_000); //600s - + var paymentTask = Builder.MakeLightningPaymentFromAlias("alice", new SendPaymentRequest { PaymentRequest = invoice.PaymentRequest, @@ -561,7 +556,7 @@ public async Task FailureReasonNoRoute() payment.PrintDump(); paymentTask.Dispose(); Assert.That(payment != null && payment.Status == Payment.Types.PaymentStatus.Failed); - Assert.That(payment != null && payment.FailureReason == PaymentFailureReason.FailureReasonNoRoute); + Assert.That(payment != null && payment.FailureReason == PaymentFailureReason.FailureReasonNoRoute); } @@ -679,7 +674,7 @@ public async Task GetPaymentFailureData() IncludeIncomplete = true // this included failures per docs }); var finalized = new List - {Payment.Types.PaymentStatus.Failed}; + { Payment.Types.PaymentStatus.Failed }; await foreach (var p in payments.Payments.Where(x => finalized.Contains(x.Status) && x.Htlcs.Any())) { var destinationNode = p.Htlcs.Last().Route.Hops.Last().PubKey; @@ -722,6 +717,7 @@ async Task Record(bool success, Payment p, LNDNodeConnection c) } } } + private readonly MemoryCache _aliasCache = new(new MemoryCacheOptions { SizeLimit = 10000 }); private readonly string _dbType; @@ -747,7 +743,7 @@ private async Task ToAlias(LNDNodeConnection c, string remotePubkey) return alias; } - + [Test] [Category("Payment")] [Category("Invoice")] diff --git a/LNUnit.Tests/DataSync.cs b/LNUnit.Tests/DataSync.cs index dbf13ba..040b717 100644 --- a/LNUnit.Tests/DataSync.cs +++ b/LNUnit.Tests/DataSync.cs @@ -110,4 +110,5 @@ // Assert.That(li.Invoices.Count > 0); // Assert.That(li.Invoices.First().State == Invoice.Types.InvoiceState.Settled); // } -// } \ No newline at end of file +// } + diff --git a/LNUnit.Tests/DockerTest.cs b/LNUnit.Tests/DockerTest.cs index c9d3de8..e5447e5 100644 --- a/LNUnit.Tests/DockerTest.cs +++ b/LNUnit.Tests/DockerTest.cs @@ -111,13 +111,16 @@ private string GetRandomHexString(int size = 8) [Category("Docker")] public async Task BuildDockerImage() { - await _client.CreateDockerImageFromPath("./../../../../Docker/lnd", new List { "custom_lnd", "custom_lnd:latest" }); + await _client.CreateDockerImageFromPath("./../../../../Docker/lnd", + new List { "custom_lnd", "custom_lnd:latest" }); } + [Test] [Category("Docker")] public async Task BuildBitcoin_27_0_rc1_DockerImage() { - await _client.CreateDockerImageFromPath("./../../../../Docker/bitcoin/27.0rc1", new List { "bitcoin:27.0rc1" }); + await _client.CreateDockerImageFromPath("./../../../../Docker/bitcoin/27.0rc1", + new List { "bitcoin:27.0rc1" }); } diff --git a/LNUnit.Tests/Fixture/PostgresFixture.cs b/LNUnit.Tests/Fixture/PostgresFixture.cs index 8c834cf..7878577 100644 --- a/LNUnit.Tests/Fixture/PostgresFixture.cs +++ b/LNUnit.Tests/Fixture/PostgresFixture.cs @@ -1,39 +1,30 @@ -using System.Collections.Immutable; using System.Net; using System.Net.Sockets; -using Dasync.Collections; using Docker.DotNet; using Docker.DotNet.Models; -using Lnrpc; -using LNUnit.Extentions; -using LNUnit.LND; using LNUnit.Setup; -using LNUnit.Tests; -using Microsoft.AspNetCore.Builder; -using Microsoft.Extensions.Logging; using Npgsql; -using Routerrpc; -using Serilog; -using Serilog.Extensions.Logging; -using ServiceStack.Text; using Assert = NUnit.Framework.Assert; using HostConfig = Docker.DotNet.Models.HostConfig; namespace LNUnit.Fixtures; - /// -/// Used Globally to get Postgres support +/// Used Globally to get Postgres support /// public static class PostgresFixture { public static Dictionary LNDConnectionStrings = new(); - public static string DbConnectionStringDotNet { get;internal set; } = string.Empty; + public static string DbConnectionStringDotNet { get; internal set; } = string.Empty; public static string DbContainerName { get; internal set; } = "postgres"; - public static async Task IsReady() => await Instance?.IsRunning(); - internal static PostgresInstanceFixture? Instance { get; set; } + + public static async Task IsReady() + { + return await Instance?.IsRunning(); + } + public static void AddDb(string dbName) { Instance?.AddDb(dbName); @@ -44,11 +35,11 @@ public static void AddDb(string dbName) public class PostgresInstanceFixture { private readonly DockerClient _client = new DockerClientConfiguration().CreateClient(); + + public readonly Dictionary LNDConnectionStrings = PostgresFixture.LNDConnectionStrings; private string _containerId; private string _ip; - public readonly Dictionary LNDConnectionStrings = PostgresFixture.LNDConnectionStrings; - public string DbConnectionStringDotNet { get; private set; } public string DbContainerName { get; set; } = "postgres"; @@ -86,7 +77,7 @@ internal void AddDb(string dbName) } private async Task StartPostgres( - string image = "postgres", + string image = "postgres", string tag = "16.2-alpine", string password = "superuser", string username = "superuser") @@ -106,7 +97,7 @@ private async Task StartPostgres( [ $"POSTGRES_PASSWORD={username}", $"POSTGRES_USER={password}", - $"POSTGRES_DB=postgres" + "POSTGRES_DB=postgres" ] }); Assert.NotNull(nodeContainer); @@ -150,7 +141,7 @@ private async Task StartPostgres( } } - + public async Task IsRunning() { try @@ -165,4 +156,4 @@ public async Task IsRunning() return false; } -} +} \ No newline at end of file diff --git a/LNUnit.Tests/LNUnit.Tests.csproj b/LNUnit.Tests/LNUnit.Tests.csproj index 0b2a1f1..cdbf322 100644 --- a/LNUnit.Tests/LNUnit.Tests.csproj +++ b/LNUnit.Tests/LNUnit.Tests.csproj @@ -12,14 +12,14 @@ - - + + - + - - + + all @@ -29,11 +29,11 @@ runtime; build; native; contentfiles; analyzers; buildtransitive all - - + + - + diff --git a/LNUnit.Tests/appsettings.json b/LNUnit.Tests/appsettings.json index 7244bf1..4d3eedc 100644 --- a/LNUnit.Tests/appsettings.json +++ b/LNUnit.Tests/appsettings.json @@ -26,7 +26,6 @@ } ] }, - "Kestrel": { "EndpointDefaults": { "Protocols": "Http1AndHttp2" diff --git a/LNUnit/LNUnit.csproj b/LNUnit/LNUnit.csproj index a31e14d..40c30c4 100644 --- a/LNUnit/LNUnit.csproj +++ b/LNUnit/LNUnit.csproj @@ -7,17 +7,17 @@ Linux 1.5.4 true - LNUnit + LNUnit Lightning Network Unit Testing Framework - - + + - + diff --git a/LNUnit/Setup/DockerHelper.cs b/LNUnit/Setup/DockerHelper.cs index fd10d5f..2fcc539 100644 --- a/LNUnit/Setup/DockerHelper.cs +++ b/LNUnit/Setup/DockerHelper.cs @@ -43,7 +43,7 @@ await client.Images.CreateImageAsync( await Task.Delay(1); } - public static async Task RemoveContainer(this DockerClient client,string name, bool removeLinks = false) + public static async Task RemoveContainer(this DockerClient client, string name, bool removeLinks = false) { try { @@ -65,6 +65,7 @@ await client.Containers.RemoveContainerAsync(name, // ignored } } + private static string GetRandomHexString(int size = 8) { var b = new byte[size]; diff --git a/LNUnit/Setup/LNUnitBuilder.cs b/LNUnit/Setup/LNUnitBuilder.cs index 87a5ae2..fc31360 100644 --- a/LNUnit/Setup/LNUnitBuilder.cs +++ b/LNUnit/Setup/LNUnitBuilder.cs @@ -26,10 +26,10 @@ public class LNUnitBuilder : IDisposable private readonly ILogger? _logger; private readonly IServiceProvider? _serviceProvider; private bool _loopLNDReady; + private int _waitForBitcoinNodeStartup = 30_000; //ms timeout public Dictionary ChannelHandlers = new(); public Dictionary InterceptorHandlers = new(); - private int _waitForBitcoinNodeStartup = 30_000; //ms timeout public LNUnitBuilder(LNUnitNetworkDefinition c = null, ILogger? logger = null, IServiceProvider serviceProvider = null) @@ -271,7 +271,10 @@ await _dockerClient.Containers.StartContainerAsync(loopServer?.ID, ipAddress = inspectionResponse.NetworkSettings.Networks.First().Value.IPAddress; } - var basePath = !n.Image.Contains("lightning-terminal") ? lndRoot : "/root/lnd/.lnd"; // "/home/lnd/.lnd" : "/root/lnd/.lnd"; + var basePath = + !n.Image.Contains("lightning-terminal") + ? lndRoot + : "/root/lnd/.lnd"; // "/home/lnd/.lnd" : "/root/lnd/.lnd"; if (n.Image.Contains("lightning-terminal")) await Task.Delay(2000); var txt = await GetStringFromFS(n.DockerContainerId, $"{basePath}/tls.cert"); var tlsCertBase64 = Convert.ToBase64String(Encoding.UTF8.GetBytes(txt)); @@ -790,7 +793,8 @@ public static class LNUnitBuilderExtensions } - public static LNUnitBuilder AddBitcoinCoreNode(this LNUnitBuilder b, string name = "miner", string image = "polarlightning/bitcoind", string tag = "26.0", bool txIndex = true, bool pullImage = true) + public static LNUnitBuilder AddBitcoinCoreNode(this LNUnitBuilder b, string name = "miner", + string image = "polarlightning/bitcoind", string tag = "26.0", bool txIndex = true, bool pullImage = true) { return b.AddBitcoinCoreNode(new LNUnitNetworkDefinition.BitcoinNode { @@ -867,9 +871,10 @@ public static LNUnitBuilder AddPolarLNDNode(this LNUnitBuilder b, string aliasHo { cmd.Add("--db.backend=postgres"); cmd.Add($"--db.postgres.dsn={postgresDSN}"); - cmd.Add($"--db.postgres.timeout=300s"); - cmd.Add($"--db.postgres.maxconnections=16"); + cmd.Add("--db.postgres.timeout=300s"); + cmd.Add("--db.postgres.maxconnections=16"); } + if (gcInvoiceOnStartup) cmd.Add("--gc-canceled-invoices-on-startup"); if (gcInvoiceOnFly) cmd.Add("--gc-canceled-invoices-on-the-fly"); @@ -883,7 +888,7 @@ public static LNUnitBuilder AddPolarLNDNode(this LNUnitBuilder b, string aliasHo BitcoinBackendName = "miner", EnvironmentVariables = new Dictionary { - {"FLAG", "true"} + { "FLAG", "true" } }, Cmd = cmd, PullImage = pullImage, @@ -939,7 +944,7 @@ public static LNUnitBuilder AddPolarEclairNode(this LNUnitBuilder b, string alia BitcoinBackendName = "miner", EnvironmentVariables = new Dictionary { - {"FLAG", "true"} + { "FLAG", "true" } }, Cmd = cmd, PullImage = pullImage, @@ -983,7 +988,7 @@ public static LNUnitBuilder AddPolarCLNNode(this LNUnitBuilder b, string aliasHo }; - var node = new LNUnitNetworkDefinition.CLNNode() + var node = new LNUnitNetworkDefinition.CLNNode { Image = imageName, Tag = tagName, @@ -991,7 +996,7 @@ public static LNUnitBuilder AddPolarCLNNode(this LNUnitBuilder b, string aliasHo BitcoinBackendName = "miner", EnvironmentVariables = new Dictionary { - {"FLAG", "true"} + { "FLAG", "true" } }, Cmd = cmd, PullImage = pullImage, @@ -1063,7 +1068,7 @@ public static LNUnitBuilder AddLightningTerminalNode(this LNUnitBuilder b, strin BitcoinBackendName = "miner", EnvironmentVariables = new Dictionary { - {"FLAG", "true"} + { "FLAG", "true" } }, Cmd = cmd, PullImage = pullImage, @@ -1124,7 +1129,7 @@ public static LNUnitBuilder AddLoopServerRegtest(this LNUnitBuilder b, }, ExposedPorts = new Dictionary { - {"11009", new EmptyStruct()} + { "11009", new EmptyStruct() } }, Binds = new List { $"{Path.GetFullPath("./loopserver-test/")}:/home/lnd/.lnd/" }, From 3e782645a83e5b203c7fb1ec777b7eb480548a1c Mon Sep 17 00:00:00 2001 From: Richard Safier Date: Tue, 26 Mar 2024 02:30:15 -0400 Subject: [PATCH 4/9] must be in same namespace or fixture won't run. --- LNUnit.Tests/AbcLightningFixture.cs | 3 ++- LNUnit/Setup/LNUnitBuilder.cs | 9 +++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/LNUnit.Tests/AbcLightningFixture.cs b/LNUnit.Tests/AbcLightningFixture.cs index a95b194..1b7bbcf 100644 --- a/LNUnit.Tests/AbcLightningFixture.cs +++ b/LNUnit.Tests/AbcLightningFixture.cs @@ -17,6 +17,7 @@ using ServiceStack.Text; using Assert = NUnit.Framework.Assert; +namespace LNUnit.Fixtures; [TestFixture("postgres")] [TestFixture("boltdb")] @@ -75,13 +76,13 @@ public void Dispose() GC.SuppressFinalize(this); // Remove containers - _client.RemoveContainer(DbContainerName).Wait(); _client.RemoveContainer("miner").Wait(); _client.RemoveContainer("alice").Wait(); _client.RemoveContainer("bob").Wait(); _client.RemoveContainer("carol").Wait(); Builder?.Destroy(); + Builder?.Dispose(); _client.Dispose(); } diff --git a/LNUnit/Setup/LNUnitBuilder.cs b/LNUnit/Setup/LNUnitBuilder.cs index fc31360..3168cf1 100644 --- a/LNUnit/Setup/LNUnitBuilder.cs +++ b/LNUnit/Setup/LNUnitBuilder.cs @@ -26,7 +26,7 @@ public class LNUnitBuilder : IDisposable private readonly ILogger? _logger; private readonly IServiceProvider? _serviceProvider; private bool _loopLNDReady; - private int _waitForBitcoinNodeStartup = 30_000; //ms timeout + public int WaitForBitcoinNodeStartupTimeout { get; set; } = 30_000; //ms timeout public Dictionary ChannelHandlers = new(); public Dictionary InterceptorHandlers = new(); @@ -136,8 +136,9 @@ public async Task Build(bool setupNetwork = false, string lndRoot = "/home/lnd/. bitcoinNode = listContainers.First(x => x.ID == nodeContainer.ID); BitcoinRpcClient = new RPCClient("bitcoin:bitcoin", bitcoinNode.NetworkSettings.Networks.First().Value.IPAddress, Bitcoin.Instance.Regtest); - _waitForBitcoinNodeStartup = 10000; - BitcoinRpcClient.HttpClient.Timeout = TimeSpan.FromMilliseconds(_waitForBitcoinNodeStartup); //10s + WaitForBitcoinNodeStartupTimeout = 30000; + BitcoinRpcClient.HttpClient = new HttpClient() { Timeout = WaitForBitcoinNodeStartupTimeout }; + await BitcoinRpcClient.CreateWalletAsync("default", new CreateWalletOptions { LoadOnStartup = true }); var utxos = await BitcoinRpcClient.GenerateAsync(200); } @@ -838,7 +839,7 @@ public static LNUnitBuilder AddPolarLNDNode(this LNUnitBuilder b, string aliasHo List? channels = null, string bitcoinMinerHost = "miner", string rpcUser = "bitcoin", string rpcPass = "bitcoin", string imageName = "polarlightning/lnd", string tagName = "0.17.4-beta", bool acceptKeysend = true, bool pullImage = true, bool mapTotmp = false, - bool gcInvoiceOnStartup = false, bool gcInvoiceOnFly = false, string postgresDSN = null) + bool gcInvoiceOnStartup = false, bool gcInvoiceOnFly = false, string? postgresDSN = null) { var cmd = new List { From b27c1893260172bb26b7d7095ca406288264d74f Mon Sep 17 00:00:00 2001 From: Richard Safier Date: Tue, 26 Mar 2024 02:41:41 -0400 Subject: [PATCH 5/9] cleanup --- .github/workflows/dotnet.yml | 5 ++++- LNUnit.Tests/AbcLightningFixture.cs | 3 +-- LNUnit.Tests/Fixture/PostgresFixture.cs | 2 +- LNUnit/Setup/LNUnitBuilder.cs | 7 ++++++- 4 files changed, 12 insertions(+), 5 deletions(-) diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index 9959ef6..2c1084f 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -26,7 +26,10 @@ jobs: - name: Build run: dotnet build --no-restore - name: Test - run: dotnet test --no-build --verbosity normal -l "console;verbosity=detailed" --collect:"XPlat Code Coverage" --logger "trx;LogFileName=test-results.trx" --results-directory ./coverage + run: | + cdir=pwd + cd LNUnit.Tests + dotnet test -f net8.0 --filter FullyQualifiedName~LNUnit.Test --no-build --verbosity normal -l "console;verbosity=detailed" --collect:"XPlat Code Coverage" --logger "trx;LogFileName=test-results.trx" --results-directory $cdir/coverage - name: Test Report uses: dorny/test-reporter@v1 if: success() || failure() # run this step even if previous step failed diff --git a/LNUnit.Tests/AbcLightningFixture.cs b/LNUnit.Tests/AbcLightningFixture.cs index 1b7bbcf..27216cd 100644 --- a/LNUnit.Tests/AbcLightningFixture.cs +++ b/LNUnit.Tests/AbcLightningFixture.cs @@ -5,7 +5,6 @@ using Grpc.Core; using Lnrpc; using LNUnit.Extentions; -using LNUnit.Fixtures; using LNUnit.LND; using LNUnit.Setup; using Microsoft.Extensions.Caching.Memory; @@ -17,7 +16,7 @@ using ServiceStack.Text; using Assert = NUnit.Framework.Assert; -namespace LNUnit.Fixtures; +namespace LNUnit.Test.Fixtures; [TestFixture("postgres")] [TestFixture("boltdb")] diff --git a/LNUnit.Tests/Fixture/PostgresFixture.cs b/LNUnit.Tests/Fixture/PostgresFixture.cs index 7878577..97e9cf8 100644 --- a/LNUnit.Tests/Fixture/PostgresFixture.cs +++ b/LNUnit.Tests/Fixture/PostgresFixture.cs @@ -7,7 +7,7 @@ using Assert = NUnit.Framework.Assert; using HostConfig = Docker.DotNet.Models.HostConfig; -namespace LNUnit.Fixtures; +namespace LNUnit.Test.Fixtures; /// /// Used Globally to get Postgres support diff --git a/LNUnit/Setup/LNUnitBuilder.cs b/LNUnit/Setup/LNUnitBuilder.cs index 3168cf1..c1d068e 100644 --- a/LNUnit/Setup/LNUnitBuilder.cs +++ b/LNUnit/Setup/LNUnitBuilder.cs @@ -137,7 +137,7 @@ public async Task Build(bool setupNetwork = false, string lndRoot = "/home/lnd/. BitcoinRpcClient = new RPCClient("bitcoin:bitcoin", bitcoinNode.NetworkSettings.Networks.First().Value.IPAddress, Bitcoin.Instance.Regtest); WaitForBitcoinNodeStartupTimeout = 30000; - BitcoinRpcClient.HttpClient = new HttpClient() { Timeout = WaitForBitcoinNodeStartupTimeout }; + BitcoinRpcClient.HttpClient = new HttpClient() { Timeout = TimeSpan.FromMilliseconds(WaitForBitcoinNodeStartupTimeout) }; await BitcoinRpcClient.CreateWalletAsync("default", new CreateWalletOptions { LoadOnStartup = true }); var utxos = await BitcoinRpcClient.GenerateAsync(200); @@ -322,6 +322,11 @@ await GetTarStreamFromFS(n.DockerContainerId, foreach (var remoteNode in remotes) await ConnectPeers(localNode, remoteNode); } + while (LNDNodePool.ReadyNodes.Count < Configuration.LNDNodes.Count) + { + await Task.Delay(250); + if (cancelSource.IsCancellationRequested) throw new Exception("CANCELED"); + } //Setup Channels (this includes sending funds and waiting) foreach (var n in Configuration.LNDNodes) //TODO: can do multiple at once { From 904bc4ef38214cff5eea85456815eacd2152813a Mon Sep 17 00:00:00 2001 From: Richard Safier Date: Tue, 26 Mar 2024 03:11:53 -0400 Subject: [PATCH 6/9] run only LNUnit.Tests, run both 0.17.4 and daily LND --- .github/workflows/dotnet.yml | 2 +- LNUnit.Tests/AbcLightningFixture.cs | 26 ++++++++++++++++++------- LNUnit.Tests/Fixture/PostgresFixture.cs | 13 ++++--------- LNUnit/LNUnit.csproj | 2 +- LNUnit/Setup/LNUnitBuilder.cs | 7 +++++-- 5 files changed, 30 insertions(+), 20 deletions(-) diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index 2c1084f..e8c0819 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -27,7 +27,7 @@ jobs: run: dotnet build --no-restore - name: Test run: | - cdir=pwd + cdir=`pwd` cd LNUnit.Tests dotnet test -f net8.0 --filter FullyQualifiedName~LNUnit.Test --no-build --verbosity normal -l "console;verbosity=detailed" --collect:"XPlat Code Coverage" --logger "trx;LogFileName=test-results.trx" --results-directory $cdir/coverage - name: Test Report diff --git a/LNUnit.Tests/AbcLightningFixture.cs b/LNUnit.Tests/AbcLightningFixture.cs index 27216cd..4a0f9a1 100644 --- a/LNUnit.Tests/AbcLightningFixture.cs +++ b/LNUnit.Tests/AbcLightningFixture.cs @@ -18,8 +18,10 @@ namespace LNUnit.Test.Fixtures; -[TestFixture("postgres")] -[TestFixture("boltdb")] +[TestFixture("postgres", "custom_lnd", "latest", "/home/lnd/.lnd", false)] +[TestFixture("boltdb", "custom_lnd", "latest", "/home/lnd/.lnd", false)] +[TestFixture("postgres", "lightninglabs/lnd", "daily-testing-only", "/root/.lnd", true)] +[TestFixture("boltdb", "lightninglabs/lnd", "daily-testing-only", "/root/.lnd", true)] public class AbcLightningFixture : IDisposable { [SetUp] @@ -55,18 +57,25 @@ public async Task OneTimeSetup() } await _client.CreateDockerImageFromPath("../../../../Docker/lnd", ["custom_lnd", "custom_lnd:latest"]); - await SetupNetwork("custom_lnd", "latest", "/home/lnd/.lnd"); + await SetupNetwork(_lndImage, _tag, _lndRoot, _pullImage); } - public AbcLightningFixture(string dbType) + public AbcLightningFixture(string dbType, + string lndImage = "custom_lnd", + string tag = "latest", + string lndRoot = "/root/.lnd", + bool pullImage = false + ) { _dbType = dbType; + _lndImage = lndImage; + _tag = tag; + _lndRoot = lndRoot; + _pullImage = pullImage; } public string DbContainerName { get; set; } = "postgres"; private readonly DockerClient _client = new DockerClientConfiguration().CreateClient(); - private string _containerId; - private string _ip; private ServiceProvider _serviceProvider; @@ -246,7 +255,6 @@ public async Task CheckLNDVersion() { var n = Builder.LNDNodePool.ReadyNodes.First(); var info = n.LightningClient.GetInfo(new GetInfoRequest()); - Assert.That(info.Version == "0.17.4-beta commit=v0.17.4-beta"); info.Version.Print(); } @@ -720,6 +728,10 @@ async Task Record(bool success, Payment p, LNDNodeConnection c) private readonly MemoryCache _aliasCache = new(new MemoryCacheOptions { SizeLimit = 10000 }); private readonly string _dbType; + private readonly string _lndImage; + private readonly string _tag; + private readonly string _lndRoot; + private readonly bool _pullImage; private async Task ToAlias(LNDNodeConnection c, string remotePubkey) { diff --git a/LNUnit.Tests/Fixture/PostgresFixture.cs b/LNUnit.Tests/Fixture/PostgresFixture.cs index 97e9cf8..6c0535b 100644 --- a/LNUnit.Tests/Fixture/PostgresFixture.cs +++ b/LNUnit.Tests/Fixture/PostgresFixture.cs @@ -58,18 +58,13 @@ public async Task RunAfterAnyTests() internal void AddDb(string dbName) { + LNDConnectionStrings.Remove(dbName); using (NpgsqlConnection connection = new(DbConnectionStringDotNet)) { connection.Open(); - using var checkIfExistsCommand = - new NpgsqlCommand($"SELECT 1 FROM pg_catalog.pg_database WHERE datname = '{dbName}'", connection); - var result = checkIfExistsCommand.ExecuteScalar(); - - if (result == null) - { - using var command = new NpgsqlCommand($"CREATE DATABASE \"{dbName}\"", connection); - command.ExecuteNonQuery(); - } + using var command = new NpgsqlCommand($"DROP DATABASE IF EXISTS \"{dbName}\"; CREATE DATABASE \"{dbName}\"", + connection); + command.ExecuteNonQuery(); } LNDConnectionStrings.Add(dbName, diff --git a/LNUnit/LNUnit.csproj b/LNUnit/LNUnit.csproj index 40c30c4..8abddb0 100644 --- a/LNUnit/LNUnit.csproj +++ b/LNUnit/LNUnit.csproj @@ -5,7 +5,7 @@ enable enable Linux - 1.5.4 + 1.6.0 true LNUnit Lightning Network Unit Testing Framework diff --git a/LNUnit/Setup/LNUnitBuilder.cs b/LNUnit/Setup/LNUnitBuilder.cs index c1d068e..a4c63fb 100644 --- a/LNUnit/Setup/LNUnitBuilder.cs +++ b/LNUnit/Setup/LNUnitBuilder.cs @@ -26,7 +26,6 @@ public class LNUnitBuilder : IDisposable private readonly ILogger? _logger; private readonly IServiceProvider? _serviceProvider; private bool _loopLNDReady; - public int WaitForBitcoinNodeStartupTimeout { get; set; } = 30_000; //ms timeout public Dictionary ChannelHandlers = new(); public Dictionary InterceptorHandlers = new(); @@ -39,6 +38,8 @@ public LNUnitBuilder(LNUnitNetworkDefinition c = null, ILogger? l _serviceProvider = serviceProvider; } + public int WaitForBitcoinNodeStartupTimeout { get; set; } = 30_000; //ms timeout + public bool IsBuilt { get; internal set; } public bool IsDestoryed { get; internal set; } public RPCClient? BitcoinRpcClient { get; internal set; } @@ -137,7 +138,8 @@ public async Task Build(bool setupNetwork = false, string lndRoot = "/home/lnd/. BitcoinRpcClient = new RPCClient("bitcoin:bitcoin", bitcoinNode.NetworkSettings.Networks.First().Value.IPAddress, Bitcoin.Instance.Regtest); WaitForBitcoinNodeStartupTimeout = 30000; - BitcoinRpcClient.HttpClient = new HttpClient() { Timeout = TimeSpan.FromMilliseconds(WaitForBitcoinNodeStartupTimeout) }; + BitcoinRpcClient.HttpClient = new HttpClient + { Timeout = TimeSpan.FromMilliseconds(WaitForBitcoinNodeStartupTimeout) }; await BitcoinRpcClient.CreateWalletAsync("default", new CreateWalletOptions { LoadOnStartup = true }); var utxos = await BitcoinRpcClient.GenerateAsync(200); @@ -327,6 +329,7 @@ await GetTarStreamFromFS(n.DockerContainerId, await Task.Delay(250); if (cancelSource.IsCancellationRequested) throw new Exception("CANCELED"); } + //Setup Channels (this includes sending funds and waiting) foreach (var n in Configuration.LNDNodes) //TODO: can do multiple at once { From 0af78f009b0ac8f7476f02853554bf077c32be58 Mon Sep 17 00:00:00 2001 From: Richard Safier Date: Tue, 26 Mar 2024 03:54:47 -0400 Subject: [PATCH 7/9] . --- LNUnit.Tests/AbcLightningFixture.cs | 32 ++++++++++++++++++++--------- LNUnit/Setup/LNUnitBuilder.cs | 12 +++++------ 2 files changed, 28 insertions(+), 16 deletions(-) diff --git a/LNUnit.Tests/AbcLightningFixture.cs b/LNUnit.Tests/AbcLightningFixture.cs index 4a0f9a1..692f67b 100644 --- a/LNUnit.Tests/AbcLightningFixture.cs +++ b/LNUnit.Tests/AbcLightningFixture.cs @@ -1,4 +1,5 @@ using System.Collections.Immutable; +using System.Diagnostics; using Dasync.Collections; using Docker.DotNet; using Google.Protobuf; @@ -117,7 +118,7 @@ public async Task SetupNetwork(string image = "lightninglabs/lnd", string tag = ChannelSize = 10_000_000, //10MSat RemoteName = "bob" } - ], imageName: image, tagName: tag, pullImage: false, + ], imageName: image, tagName: tag, pullImage: false, acceptKeysend: true, postgresDSN: _dbType == "postgres" ? PostgresFixture.LNDConnectionStrings["alice"] : null); Builder.AddPolarLNDNode("bob", @@ -128,7 +129,7 @@ public async Task SetupNetwork(string image = "lightninglabs/lnd", string tag = RemotePushOnStart = 1_000_000, // 1MSat RemoteName = "alice" } - ], imageName: image, tagName: tag, pullImage: false, + ], imageName: image, tagName: tag, pullImage: false, acceptKeysend: true, postgresDSN: _dbType == "postgres" ? PostgresFixture.LNDConnectionStrings["bob"] : null); Builder.AddPolarLNDNode("carol", @@ -139,7 +140,7 @@ public async Task SetupNetwork(string image = "lightninglabs/lnd", string tag = RemotePushOnStart = 1_000_000, // 1MSat RemoteName = "bob" } - ], imageName: image, tagName: tag, pullImage: false, + ], imageName: image, tagName: tag, pullImage: false, acceptKeysend: true, postgresDSN: _dbType == "postgres" ? PostgresFixture.LNDConnectionStrings["carol"] : null); await Builder.Build(lndRoot: lndRoot); @@ -156,12 +157,12 @@ private void WaitNodesReady() Task.WaitAll(a, b, c); } - private async Task WaitGraphReady() + private async Task WaitGraphReady(string fromAlias = "alice") { var graphReady = false; while (!graphReady) { - var graph = await Builder.GetGraphFromAlias("alice"); + var graph = await Builder.GetGraphFromAlias(fromAlias); if (graph.Nodes.Count < 3) { "Graph not ready...".Print(); @@ -411,14 +412,25 @@ public async Task UpdateChannelPolicyPerNode() [NonParallelizable] public async Task SuccessfulKeysend() { - var aliceSettings = await Builder.GetLNDSettingsFromContainer("alice"); - var bobSettings = await Builder.GetLNDSettingsFromContainer("bob"); + Builder.CancelAllInterceptors(); + var aliceSettings = await Builder.GetLNDSettingsFromContainer("alice", _lndRoot); + var bobSettings = await Builder.GetLNDSettingsFromContainer("bob", _lndRoot); var alice = new LNDNodeConnection(aliceSettings); var bob = new LNDNodeConnection(bobSettings); + // WaitNodesReady(); + // await WaitGraphReady("bob"); + // var channels = bob.LightningClient.ListChannels(new ListChannelsRequest() { ActiveOnly = true }); + // while (!channels.Channels.Any(x => x.RemotePubkey.EqualsIgnoreCase(alice.LocalNodePubKey) && x.Active)) + // { + // Debug.Print("Channel not up yet."); + // await Task.Delay(250); + // channels = bob.LightningClient.ListChannels(new ListChannelsRequest() { ActiveOnly = true }); + // } + await Task.Delay(1000); //TODO: why? we are not checking channels are up var payment = await alice.KeysendPayment(bob.LocalNodePubKey, 10, 100000000, "Hello World", 10, new Dictionary { { 99999, new byte[] { 11, 11, 11 } } }); - Assert.That(payment.Status == Payment.Types.PaymentStatus.Succeeded); + Assert.That(payment.Status, Is.EqualTo(Payment.Types.PaymentStatus.Succeeded)); } [Test] @@ -559,7 +571,7 @@ public async Task FailureReasonNoRoute() Builder.CancelInterceptorOnAlias("bob"); paymentTask.Status.PrintDump(); var payment = await paymentTask; - await Builder.RestartByAlias("carol", 0, true); + await Builder.RestartByAlias("carol", 0, true, lndRoot: _lndRoot); await Builder.WaitUntilAliasIsServerReady("carol"); payment.PrintDump(); paymentTask.Dispose(); @@ -660,7 +672,7 @@ public async Task GetPaymentFailureData() Builder.CancelInterceptorOnAlias("bob"); paymentTask.Status.PrintDump(); var payment = await paymentTask; - await Builder.RestartByAlias("carol", 0, true); + await Builder.RestartByAlias("carol", 0, true, lndRoot: _lndRoot); await Builder.WaitUntilAliasIsServerReady("carol"); payment.PrintDump(); paymentTask.Dispose(); diff --git a/LNUnit/Setup/LNUnitBuilder.cs b/LNUnit/Setup/LNUnitBuilder.cs index a4c63fb..ed40225 100644 --- a/LNUnit/Setup/LNUnitBuilder.cs +++ b/LNUnit/Setup/LNUnitBuilder.cs @@ -407,7 +407,7 @@ await GetTarStreamFromFS(n.DockerContainerId, } - public async Task GetLNDSettingsFromContainer(string containerId) + public async Task GetLNDSettingsFromContainer(string containerId, string lndRoot = "/home/lnd/.lnd") { var inspectionResponse = await _dockerClient.Containers.InspectContainerAsync(containerId); while (inspectionResponse.State.Running != true) @@ -423,14 +423,14 @@ public async Task GetLNDSettingsFromContainer(string containerId) //Wait until LND actually has files started - var tlsTar = await GetTarStreamFromFS(containerId, "/home/lnd/.lnd/tls.cert"); + var tlsTar = await GetTarStreamFromFS(containerId, $"{lndRoot}/tls.cert"); var txt = GetStringFromTar(tlsTar); //GetStringFromFS(containerId, "/home/lnd/.lnd/tls.cert"); var tlsCertBase64 = Convert.ToBase64String(Encoding.UTF8.GetBytes(txt)); var adminMacaroonTar = - await GetTarStreamFromFS(containerId, "/home/lnd/.lnd/data/chain/bitcoin/regtest/admin.macaroon"); + await GetTarStreamFromFS(containerId, $"{lndRoot}/data/chain/bitcoin/regtest/admin.macaroon"); var data = GetBytesFromTar( - adminMacaroonTar); //await GetBytesFromFS(containerId, "/home/lnd/.lnd/data/chain/bitcoin/regtest/admin.macaroon"); + adminMacaroonTar); var adminMacaroonBase64String = Convert.ToBase64String(data); return new LNDSettings @@ -568,7 +568,7 @@ public async Task ShutdownByAlias(string alias, uint waitBeforeKillSeconds } public async Task RestartByAlias(string alias, uint waitBeforeKillSeconds = 1, bool isLND = false, - bool resetChannels = true) + bool resetChannels = true, string lndRoot = "/hone/lnd/.lnd") { await _dockerClient.Containers.RestartContainerAsync(alias, new ContainerRestartParameters { @@ -577,7 +577,7 @@ public async Task RestartByAlias(string alias, uint waitBeforeKillSeconds = 1, b if (isLND) { - LNDNodePool?.AddNode(await GetLNDSettingsFromContainer(alias)); + LNDNodePool?.AddNode(await GetLNDSettingsFromContainer(alias, lndRoot)); var node = await WaitUntilAliasIsServerReady(alias); //reset channels From 4f30bf8171d425145ffdc2b6c7b7a12d63f0ae0b Mon Sep 17 00:00:00 2001 From: Richard Safier Date: Tue, 26 Mar 2024 04:18:31 -0400 Subject: [PATCH 8/9] . --- LNUnit.Tests/AbcLightningFixture.cs | 6 +++--- LNUnit/Setup/LNUnitBuilder.cs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/LNUnit.Tests/AbcLightningFixture.cs b/LNUnit.Tests/AbcLightningFixture.cs index 692f67b..aff6f48 100644 --- a/LNUnit.Tests/AbcLightningFixture.cs +++ b/LNUnit.Tests/AbcLightningFixture.cs @@ -565,7 +565,7 @@ public async Task FailureReasonNoRoute() }); "Carol shutdown".Print(); - await Builder.ShutdownByAlias("carol", 0); + await Builder.ShutdownByAlias("carol", 0, true); await Task.Delay(1000); paymentTask.Status.PrintDump(); Builder.CancelInterceptorOnAlias("bob"); @@ -666,7 +666,7 @@ public async Task GetPaymentFailureData() }); "Carol shutdown".Print(); - await Builder.ShutdownByAlias("carol", 0); + await Builder.ShutdownByAlias("carol", 0, true); await Task.Delay(1000); paymentTask.Status.PrintDump(); Builder.CancelInterceptorOnAlias("bob"); @@ -683,7 +683,7 @@ public async Task GetPaymentFailureData() var res = new List(); var consolidated = new Dictionary(); - var client = (await Builder.GetLNDSettingsFromContainer("alice")).GetClient(); + var client = (await Builder.GetLNDSettingsFromContainer("alice", _lndRoot)).GetClient(); var payments = await client.LightningClient.ListPaymentsAsync(new ListPaymentsRequest { //CountTotalPayments = true, diff --git a/LNUnit/Setup/LNUnitBuilder.cs b/LNUnit/Setup/LNUnitBuilder.cs index ed40225..6fed7c8 100644 --- a/LNUnit/Setup/LNUnitBuilder.cs +++ b/LNUnit/Setup/LNUnitBuilder.cs @@ -672,7 +672,7 @@ public async Task WaitUntilAliasIsServerReady(string alias) } while (!ready.IsServerReady) await Task.Delay(100); - return ready; + return ready.Clone(); } public async Task GetGraphFromAlias(string alias) From 7bc03f32d7f94655c84d5c452fd4b7496f1d3039 Mon Sep 17 00:00:00 2001 From: Richard Safier Date: Tue, 26 Mar 2024 04:29:34 -0400 Subject: [PATCH 9/9] Add timeouts --- LNUnit.Tests/AbcLightningFixture.cs | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/LNUnit.Tests/AbcLightningFixture.cs b/LNUnit.Tests/AbcLightningFixture.cs index aff6f48..8df025b 100644 --- a/LNUnit.Tests/AbcLightningFixture.cs +++ b/LNUnit.Tests/AbcLightningFixture.cs @@ -197,6 +197,8 @@ public async Task IsRunning() [Test] [Category("Payment")] [NonParallelizable] + [Timeout(5000)] + public async Task FailureNoRouteBecauseFeesAreTooHigh() { var invoice = await Builder.GeneratePaymentRequestFromAlias("carol", new Invoice @@ -217,6 +219,7 @@ public async Task FailureNoRouteBecauseFeesAreTooHigh() [Test] [Category("Payment")] [NonParallelizable] + [Timeout(5000)] public async Task Successful() { var invoice = await Builder.GeneratePaymentRequestFromAlias("bob", new Invoice @@ -236,6 +239,7 @@ public async Task Successful() [Test] [Category("Balancing")] [NonParallelizable] + [Timeout(5000)] public async Task PoolRebalance() { var stats = await Builder.LNDNodePool.RebalanceNodePool(); @@ -250,6 +254,7 @@ public async Task PoolRebalance() [Test] + [Timeout(2000)] [Category("Version")] [NonParallelizable] public async Task CheckLNDVersion() @@ -262,6 +267,7 @@ public async Task CheckLNDVersion() [Test] [Category("ChannelAcceptor")] + [Timeout(5000)] [NonParallelizable] public async Task ChannelAcceptorDeny() { @@ -353,6 +359,7 @@ public async Task ChannelAcceptorDeny() [Test] [Category("Fees")] [NonParallelizable] + [Timeout(60000)] public async Task UpdateChannelPolicyPerNode() { var acceptorTasks = new List(); @@ -410,6 +417,7 @@ public async Task UpdateChannelPolicyPerNode() [Test] [Category("Payment")] [NonParallelizable] + [Timeout(5000)] public async Task SuccessfulKeysend() { Builder.CancelAllInterceptors(); @@ -436,6 +444,7 @@ public async Task SuccessfulKeysend() [Test] [Category("LNUnit")] [NonParallelizable] + [Timeout(5000)] public async Task ExportGraph() { var data = await Builder.GetGraphFromAlias("alice"); @@ -446,6 +455,7 @@ public async Task ExportGraph() [Test] [Category("LNUnit")] [NonParallelizable] + [Timeout(1000)] public async Task GetChannelsFromAlias() { var alice = await Builder.GetChannelsFromAlias("alice"); @@ -465,6 +475,8 @@ public async Task GetChannelsFromAlias() [Test] [Category("LNUnit")] [NonParallelizable] + [Timeout(5000)] + public async Task GetChannelPointFromAliases() { var data = Builder.GetChannelPointFromAliases("alice", "bob"); @@ -476,6 +488,7 @@ public async Task GetChannelPointFromAliases() [Test] [Category("LNDNodePool")] [NonParallelizable] + [Timeout(1000)] public async Task GetNodeConnectionFromPool() { var data = Builder.LNDNodePool.GetLNDNodeConnection(); @@ -490,6 +503,7 @@ public async Task GetNodeConnectionFromPool() [Test] [Category("Fees")] [NonParallelizable] + [Timeout(5000)] public async Task UpdateChannelPolicy() { var data = Builder.UpdateGlobalFeePolicyOnAlias("alice", new LNUnitNetworkDefinition.Channel()); @@ -501,6 +515,8 @@ public async Task UpdateChannelPolicy() [Category("Payment")] [Category("Invoice")] [NonParallelizable] + [Timeout(5000)] + public async Task FailureInvoiceTimeout() { var invoice = await Builder.GeneratePaymentRequestFromAlias("alice", new Invoice @@ -544,6 +560,7 @@ public async Task FailureInvoiceTimeout() [Category("Payment")] [Category("Interceptor")] [NonParallelizable] + [Timeout(15000)] public async Task FailureReasonNoRoute() { var invoice = await Builder.GeneratePaymentRequestFromAlias("carol", new Invoice @@ -584,6 +601,7 @@ public async Task FailureReasonNoRoute() [Category("Payment")] [Category("Interceptor")] [NonParallelizable] + [Timeout(30000)] public async Task InterceptorTest() { List invoices = new(); @@ -642,6 +660,7 @@ public async Task InterceptorTest() [Category("Payment")] [Category("Interceptor")] [NonParallelizable] + [Timeout(15000)] public async Task GetPaymentFailureData() { //Setup @@ -773,6 +792,7 @@ private async Task ToAlias(LNDNodeConnection c, string remotePubkey) [Category("Invoice")] [Category("Sync")] [NonParallelizable] + [Timeout(15000)] public async Task ListInvoiceAndPaymentPaging() { var invoices = await Builder.GeneratePaymentsRequestFromAlias("alice", 10, new Invoice @@ -830,6 +850,7 @@ public async Task ListInvoiceAndPaymentPaging() [Category("Invoice")] [Category("Sync")] [NonParallelizable] + [Timeout(15000)] public async Task ListInvoiceAndPaymentNoDatePage() { var invoice = await Builder.GeneratePaymentRequestFromAlias("alice", new Invoice