diff --git a/BTCPayServer.Common/BTCPayNetworkProvider.cs b/BTCPayServer.Common/BTCPayNetworkProvider.cs index e2990f52b..c010bd5fc 100644 --- a/BTCPayServer.Common/BTCPayNetworkProvider.cs +++ b/BTCPayServer.Common/BTCPayNetworkProvider.cs @@ -131,5 +131,10 @@ namespace BTCPayServer } return network as T; } + public bool TryGetNetwork(string cryptoCode, out T network) where T : BTCPayNetworkBase + { + network = GetNetwork(cryptoCode); + return network != null; + } } } diff --git a/BTCPayServer.Tests/CrowdfundTests.cs b/BTCPayServer.Tests/CrowdfundTests.cs index 6553ffc2f..a01f3c9d4 100644 --- a/BTCPayServer.Tests/CrowdfundTests.cs +++ b/BTCPayServer.Tests/CrowdfundTests.cs @@ -23,7 +23,7 @@ namespace BTCPayServer.Tests Logs.LogProvider = new XUnitLogProvider(helper); } - [Fact(Timeout = TestTimeout)] + [Fact(Timeout = LongRunningTestTimeout)] [Trait("Integration", "Integration")] public async Task CanCreateAndDeleteCrowdfundApp() { @@ -63,7 +63,7 @@ namespace BTCPayServer.Tests - [Fact(Timeout = TestTimeout)] + [Fact(Timeout = LongRunningTestTimeout)] [Trait("Integration", "Integration")] public async Task CanContributeOnlyWhenAllowed() { @@ -155,7 +155,7 @@ namespace BTCPayServer.Tests } } - [Fact(Timeout = TestTimeout)] + [Fact(Timeout = LongRunningTestTimeout)] [Trait("Integration", "Integration")] public async Task CanComputeCrowdfundModel() { diff --git a/BTCPayServer.Tests/UnitTest1.cs b/BTCPayServer.Tests/UnitTest1.cs index 297a7bcad..0dbf55809 100644 --- a/BTCPayServer.Tests/UnitTest1.cs +++ b/BTCPayServer.Tests/UnitTest1.cs @@ -47,7 +47,10 @@ using BTCPayServer.Validation; using ExchangeSharp; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Configuration.Memory; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; using NBitcoin; using NBitcoin.DataEncoders; using NBitcoin.Payment; @@ -67,7 +70,7 @@ namespace BTCPayServer.Tests { public class UnitTest1 { - public const int TestTimeout = 60_000; + public const int LongRunningTestTimeout = 60_000; // 60s public UnitTest1(ITestOutputHelper helper) { @@ -870,18 +873,55 @@ namespace BTCPayServer.Tests } } - [Fact] + [Fact(Timeout = LongRunningTestTimeout)] [Trait("Fast", "Fast")] public async Task CanEnumerateTorServices() { var tor = new TorServices(new BTCPayNetworkProvider(ChainName.Regtest), - new BTCPayServerOptions() { TorrcFile = TestUtils.GetTestDataFullPath("Tor/torrc") }); + new OptionsWrapper(new BTCPayServerOptions() + { + TorrcFile = TestUtils.GetTestDataFullPath("Tor/torrc") + })); await tor.Refresh(); Assert.Single(tor.Services.Where(t => t.ServiceType == TorServiceType.BTCPayServer)); Assert.Single(tor.Services.Where(t => t.ServiceType == TorServiceType.P2P)); Assert.Single(tor.Services.Where(t => t.ServiceType == TorServiceType.RPC)); - Assert.True(tor.Services.Where(t => t.ServiceType == TorServiceType.Other).Count() > 1); + Assert.True(tor.Services.Count(t => t.ServiceType == TorServiceType.Other) > 1); + + tor = new TorServices(new BTCPayNetworkProvider(ChainName.Regtest), + new OptionsWrapper(new BTCPayServerOptions() + { + TorrcFile = null, + TorServices = "btcpayserver:host.onion:80;btc-p2p:host2.onion:81,BTC-RPC:host3.onion:82,UNKNOWN:host4.onion:83,INVALID:ddd".Split(new[] {';', ','}, StringSplitOptions.RemoveEmptyEntries) + })); + await Task.WhenAll(tor.StartAsync(CancellationToken.None)); + + var btcpayS = Assert.Single(tor.Services.Where(t => t.ServiceType == TorServiceType.BTCPayServer)); + Assert.Null(btcpayS.Network); + Assert.Equal("host.onion", btcpayS.OnionHost); + Assert.Equal(80, btcpayS.VirtualPort); + + var p2p = Assert.Single(tor.Services.Where(t => t.ServiceType == TorServiceType.P2P)); + Assert.NotNull(p2p.Network); + Assert.Equal("BTC", p2p.Network.CryptoCode); + Assert.Equal("host2.onion", p2p.OnionHost); + Assert.Equal(81, p2p.VirtualPort); + + var rpc = Assert.Single(tor.Services.Where(t => t.ServiceType == TorServiceType.RPC)); + Assert.NotNull(p2p.Network); + Assert.Equal("BTC", rpc.Network.CryptoCode); + Assert.Equal("host3.onion", rpc.OnionHost); + Assert.Equal(82, rpc.VirtualPort); + + var unknown = Assert.Single(tor.Services.Where(t => t.ServiceType == TorServiceType.Other)); + Assert.Null(unknown.Network); + Assert.Equal("host4.onion", unknown.OnionHost); + Assert.Equal(83, unknown.VirtualPort); + Assert.Equal("UNKNOWN", unknown.Name); + + Assert.Equal(4, tor.Services.Length); + } @@ -1046,7 +1086,7 @@ namespace BTCPayServer.Tests }); } - [Fact(Timeout = TestTimeout)] + [Fact(Timeout = LongRunningTestTimeout)] [Trait("Integration", "Integration")] public async Task CanUseServerInitiatedPairingCode() { @@ -1073,7 +1113,7 @@ namespace BTCPayServer.Tests } } - [Fact(Timeout = TestTimeout)] + [Fact(Timeout = LongRunningTestTimeout)] [Trait("Integration", "Integration")] public async Task CanSendIPN() { @@ -1143,7 +1183,7 @@ namespace BTCPayServer.Tests } } - [Fact(Timeout = TestTimeout)] + [Fact(Timeout = LongRunningTestTimeout)] [Trait("Integration", "Integration")] public async Task CantPairTwiceWithSamePubkey() { @@ -1167,7 +1207,7 @@ namespace BTCPayServer.Tests } } - [Fact(Timeout = TestTimeout)] + [Fact(Timeout = LongRunningTestTimeout)] [Trait("Integration", "Integration")] public void CanSolveTheDogesRatesOnKraken() { @@ -1185,7 +1225,7 @@ namespace BTCPayServer.Tests } - [Fact(Timeout = TestTimeout)] + [Fact(Timeout = LongRunningTestTimeout)] [Trait("Integration", "Integration")] public async Task CanUseTorClient() { @@ -1238,7 +1278,7 @@ namespace BTCPayServer.Tests } } - [Fact(Timeout = TestTimeout)] + [Fact(Timeout = LongRunningTestTimeout)] [Trait("Integration", "Integration")] public async Task CanRescanWallet() { @@ -1340,7 +1380,7 @@ namespace BTCPayServer.Tests } } - [Fact(Timeout = TestTimeout)] + [Fact(Timeout = LongRunningTestTimeout)] [Trait("Integration", "Integration")] public async Task CanListInvoices() { @@ -1391,7 +1431,7 @@ namespace BTCPayServer.Tests } } - [Fact(Timeout = TestTimeout)] + [Fact(Timeout = LongRunningTestTimeout)] [Trait("Integration", "Integration")] public async Task CanListNotifications() { @@ -1675,7 +1715,7 @@ namespace BTCPayServer.Tests Assert.NotNull(paymentData.KeyPath); } - [Fact(Timeout = TestTimeout)] + [Fact(Timeout = LongRunningTestTimeout)] [Trait("Fast", "Fast")] public void CanParseFilter() { @@ -1703,7 +1743,7 @@ namespace BTCPayServer.Tests Assert.Equal("hekki", search.TextSearch); } - [Fact(Timeout = TestTimeout)] + [Fact(Timeout = LongRunningTestTimeout)] [Trait("Fast", "Fast")] public void CanParseFingerprint() { @@ -1720,7 +1760,7 @@ namespace BTCPayServer.Tests Assert.Equal(f1.ToString(), f2.ToString()); } - [Fact(Timeout = TestTimeout)] + [Fact(Timeout = LongRunningTestTimeout)] [Trait("Integration", "Integration")] public async void CheckCORSSetOnBitpayAPI() { @@ -1755,7 +1795,7 @@ namespace BTCPayServer.Tests } } - [Fact(Timeout = TestTimeout)] + [Fact(Timeout = LongRunningTestTimeout)] [Trait("Integration", "Integration")] public async Task TestAccessBitpayAPI() { @@ -1834,7 +1874,7 @@ namespace BTCPayServer.Tests } } - [Fact(Timeout = TestTimeout)] + [Fact(Timeout = LongRunningTestTimeout)] [Trait("Integration", "Integration")] public async Task CanUseExchangeSpecificRate() { @@ -1879,7 +1919,7 @@ namespace BTCPayServer.Tests return invoice2.CryptoInfo[0].Rate; } - [Fact(Timeout = TestTimeout)] + [Fact(Timeout = LongRunningTestTimeout)] [Trait("Integration", "Integration")] public async Task CanUseAnyoneCanCreateInvoice() { @@ -1931,7 +1971,7 @@ namespace BTCPayServer.Tests } } - [Fact(Timeout = TestTimeout)] + [Fact(Timeout = LongRunningTestTimeout)] [Trait("Integration", "Integration")] public async Task CanTweakRate() { @@ -1978,7 +2018,7 @@ namespace BTCPayServer.Tests } } - [Fact(Timeout = TestTimeout)] + [Fact(Timeout = LongRunningTestTimeout)] [Trait("Integration", "Integration")] public async Task CanModifyRates() { @@ -2307,7 +2347,7 @@ namespace BTCPayServer.Tests Assert.True(client.WaitAllRunning(default).Wait(100)); } - [Fact(Timeout = TestTimeout)] + [Fact(Timeout = LongRunningTestTimeout)] [Trait("Fast", "Fast")] public void PosDataParser_ParsesCorrectly() { @@ -2333,7 +2373,7 @@ namespace BTCPayServer.Tests }); } - [Fact(Timeout = TestTimeout)] + [Fact(Timeout = LongRunningTestTimeout)] [Trait("Integration", "Integration")] public async Task PosDataParser_ParsesCorrectly_Slower() { @@ -2383,7 +2423,7 @@ namespace BTCPayServer.Tests } } - [Fact(Timeout = TestTimeout)] + [Fact(Timeout = LongRunningTestTimeout)] [Trait("Integration", "Integration")] public async Task CanExportInvoicesJson() { @@ -2462,7 +2502,7 @@ namespace BTCPayServer.Tests } } - [Fact(Timeout = TestTimeout)] + [Fact(Timeout = LongRunningTestTimeout)] [Trait("Integration", "Integration")] public async Task CanChangeNetworkFeeMode() { @@ -2553,7 +2593,7 @@ namespace BTCPayServer.Tests } } - [Fact(Timeout = TestTimeout)] + [Fact(Timeout = LongRunningTestTimeout)] [Trait("Integration", "Integration")] public async Task CanExportInvoicesCsv() { @@ -2595,7 +2635,7 @@ namespace BTCPayServer.Tests } - [Fact(Timeout = TestTimeout)] + [Fact(Timeout = LongRunningTestTimeout)] [Trait("Integration", "Integration")] public async Task CanCreateAndDeleteApps() { @@ -2633,7 +2673,7 @@ namespace BTCPayServer.Tests } } - [Fact(Timeout = TestTimeout)] + [Fact(Timeout = LongRunningTestTimeout)] [Trait("Integration", "Integration")] public async Task CanCreateStrangeInvoice() { @@ -2679,7 +2719,7 @@ namespace BTCPayServer.Tests } } - [Fact(Timeout = TestTimeout)] + [Fact(Timeout = LongRunningTestTimeout)] [Trait("Integration", "Integration")] public async Task InvoiceFlowThroughDifferentStatesCorrectly() { @@ -2869,7 +2909,7 @@ namespace BTCPayServer.Tests } } - [Fact(Timeout = TestTimeout)] + [Fact(Timeout = LongRunningTestTimeout)] [Trait("Integration", "Integration")] public void CanQueryDirectProviders() { @@ -2940,7 +2980,7 @@ namespace BTCPayServer.Tests } - [Fact(Timeout = TestTimeout)] + [Fact(Timeout = LongRunningTestTimeout)] [Trait("Integration", "Integration")] public async Task CanExportBackgroundFetcherState() { @@ -2982,7 +3022,7 @@ namespace BTCPayServer.Tests } - [Fact(Timeout = TestTimeout)] + [Fact(Timeout = LongRunningTestTimeout)] [Trait("Integration", "Integration")] public void CanGetRateCryptoCurrenciesByDefault() { @@ -3034,7 +3074,7 @@ namespace BTCPayServer.Tests } } - [Fact(Timeout = TestTimeout)] + [Fact(Timeout = LongRunningTestTimeout)] [Trait("Integration", "Integration")] public async Task CheckLogsRoute() { @@ -3117,7 +3157,7 @@ namespace BTCPayServer.Tests return name; } - [Fact(Timeout = TestTimeout)] + [Fact(Timeout = LongRunningTestTimeout)] [Trait("Fast", "Fast")] public void CanCheckFileNameValid() { @@ -3135,7 +3175,7 @@ namespace BTCPayServer.Tests } } - [Fact(Timeout = TestTimeout)] + [Fact(Timeout = LongRunningTestTimeout)] [Trait("Fast", "Fast")] public async Task CanCreateSqlitedb() { @@ -3147,7 +3187,7 @@ namespace BTCPayServer.Tests await new ApplicationDbContext(builder.Options).Database.MigrateAsync(); } - [Fact(Timeout = TestTimeout)] + [Fact(Timeout = LongRunningTestTimeout)] [Trait("Fast", "Fast")] public void CanUsePermission() { @@ -3172,7 +3212,7 @@ namespace BTCPayServer.Tests .Contains(Permission.Create(Policies.CanModifyStoreSettings))); } - [Fact(Timeout = TestTimeout)] + [Fact(Timeout = LongRunningTestTimeout)] [Trait("Fast", "Fast")] public void CheckRatesProvider() { @@ -3260,7 +3300,7 @@ namespace BTCPayServer.Tests } - [Fact(Timeout = TestTimeout)] + [Fact(Timeout = LongRunningTestTimeout)] [Trait("Integration", "Integration")] public async Task CanLoginWithNoSecondaryAuthSystemsOrRequestItWhenAdded() { @@ -3336,7 +3376,7 @@ namespace BTCPayServer.Tests } } - [Fact(Timeout = TestTimeout)] + [Fact(Timeout = LongRunningTestTimeout)] [Trait("Integration", "Integration")] public async void CheckOnionlocationForNonOnionHtmlRequests() { @@ -3382,7 +3422,7 @@ namespace BTCPayServer.Tests } } - [Fact(Timeout = TestTimeout)] + [Fact(Timeout = LongRunningTestTimeout)] [Trait("Integration", "Integration")] public async Task CanCheckForNewVersion() { @@ -3428,7 +3468,7 @@ namespace BTCPayServer.Tests } } - [Fact(Timeout = TestTimeout)] + [Fact(Timeout = LongRunningTestTimeout)] [Trait("Integration", "Integration")] public async Task CanDoLightningInternalNodeMigration() { @@ -3507,7 +3547,7 @@ namespace BTCPayServer.Tests } - [Fact(Timeout = TestTimeout)] + [Fact(Timeout = LongRunningTestTimeout)] [Trait("Integration", "Integration")] public async Task CanDoInvoiceMigrations() { @@ -3588,7 +3628,7 @@ namespace BTCPayServer.Tests await migrationStartupTask.ExecuteAsync(); } - [Fact(Timeout = TestTimeout)] + [Fact(Timeout = LongRunningTestTimeout)] [Trait("Integration", "Integration")] public async Task EmailSenderTests() { diff --git a/BTCPayServer/Configuration/BTCPayServerOptions.cs b/BTCPayServer/Configuration/BTCPayServerOptions.cs index a8330e394..259064a3a 100644 --- a/BTCPayServer/Configuration/BTCPayServerOptions.cs +++ b/BTCPayServer/Configuration/BTCPayServerOptions.cs @@ -69,7 +69,12 @@ namespace BTCPayServer.Configuration BundleJsCss = conf.GetOrDefault("bundlejscss", true); DockerDeployment = conf.GetOrDefault("dockerdeployment", true); AllowAdminRegistration = conf.GetOrDefault("allow-admin-registration", false); + TorrcFile = conf.GetOrDefault("torrcfile", null); + TorServices = conf.GetOrDefault("torservices", null) + ?.Split(new[] {';', ','}, StringSplitOptions.RemoveEmptyEntries); + if (!string.IsNullOrEmpty(TorrcFile) && TorServices != null) + throw new ConfigException($"torrcfile or torservices should be provided, but not both"); var socksEndpointString = conf.GetOrDefault("socksendpoint", null); if (!string.IsNullOrEmpty(socksEndpointString)) @@ -195,6 +200,7 @@ namespace BTCPayServer.Configuration set; } public string TorrcFile { get; set; } + public string[] TorServices { get; set; } public Uri UpdateUrl { get; set; } } } diff --git a/BTCPayServer/Configuration/DefaultConfiguration.cs b/BTCPayServer/Configuration/DefaultConfiguration.cs index f9fd2464d..99edafb82 100644 --- a/BTCPayServer/Configuration/DefaultConfiguration.cs +++ b/BTCPayServer/Configuration/DefaultConfiguration.cs @@ -39,6 +39,7 @@ namespace BTCPayServer.Configuration app.Option("--sshauthorizedkeys", "Path to a authorized_keys file that BTCPayServer can modify from the website (default: empty)", CommandOptionType.SingleValue); app.Option("--sshtrustedfingerprints", "SSH Host public key fingerprint or sha256 (default: empty, it will allow untrusted connections)", CommandOptionType.SingleValue); app.Option("--torrcfile", "Path to torrc file containing hidden services directories (default: empty)", CommandOptionType.SingleValue); + app.Option("--torservices", "Tor hostnames of available services added to Server Settings (and sets onion header for btcpay). Format: btcpayserver:host.onion:80;btc-p2p:host2.onion:81,BTC-RPC:host3.onion:82,UNKNOWN:host4.onion:83. (default: empty)", CommandOptionType.SingleValue); app.Option("--socksendpoint", "Socks endpoint to connect to onion urls (default: empty)", CommandOptionType.SingleValue); app.Option("--updateurl", $"Url used for once a day new release version check. Check performed only if value is not empty (default: empty)", CommandOptionType.SingleValue); app.Option("--debuglog", "A rolling log file for debug messages.", CommandOptionType.SingleValue); diff --git a/BTCPayServer/HostedServices/TorServicesHostedService.cs b/BTCPayServer/HostedServices/TorServicesHostedService.cs deleted file mode 100644 index 086a30d0e..000000000 --- a/BTCPayServer/HostedServices/TorServicesHostedService.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System; -using System.Threading.Tasks; -using BTCPayServer.Configuration; -using BTCPayServer.Services; - -namespace BTCPayServer.HostedServices -{ - public class TorServicesHostedService : BaseAsyncService - { - private readonly BTCPayServerOptions _options; - private readonly TorServices _torServices; - - public TorServicesHostedService(BTCPayServerOptions options, TorServices torServices) - { - _options = options; - _torServices = torServices; - } - - internal override Task[] InitializeTasks() - { - return new Task[] { CreateLoopTask(RefreshTorServices) }; - } - - async Task RefreshTorServices() - { - await _torServices.Refresh(); - await Task.Delay(TimeSpan.FromSeconds(120), Cancellation); - } - } -} diff --git a/BTCPayServer/Hosting/BTCPayServerServices.cs b/BTCPayServer/Hosting/BTCPayServerServices.cs index bcba06b5b..748b39b85 100644 --- a/BTCPayServer/Hosting/BTCPayServerServices.cs +++ b/BTCPayServer/Hosting/BTCPayServerServices.cs @@ -93,6 +93,7 @@ namespace BTCPayServer.Hosting services.TryAddSingleton(provider => provider.GetService()); services.TryAddSingleton(); services.TryAddSingleton(); + services.AddSingleton(provider => provider.GetRequiredService()); services.TryAddSingleton(); services.TryAddSingleton(); services.TryAddSingleton(); @@ -346,7 +347,6 @@ namespace BTCPayServer.Hosting services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); - services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddScoped(); diff --git a/BTCPayServer/Services/TorServices.cs b/BTCPayServer/Services/TorServices.cs index 758f5601c..f8dd1df2a 100644 --- a/BTCPayServer/Services/TorServices.cs +++ b/BTCPayServer/Services/TorServices.cs @@ -2,74 +2,77 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; +using System.Threading; using System.Threading.Tasks; using BTCPayServer.Configuration; +using BTCPayServer.HostedServices; using BTCPayServer.Logging; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; namespace BTCPayServer.Services { - public class TorServices + public class TorServices : BaseAsyncService { - private readonly BTCPayNetworkProvider _networks; - readonly BTCPayServerOptions _Options; - public TorServices(BTCPayServer.BTCPayNetworkProvider networks, BTCPayServerOptions options) + private readonly BTCPayNetworkProvider _btcPayNetworkProvider; + private readonly IOptions _options; + + public TorServices(BTCPayNetworkProvider btcPayNetworkProvider, IOptions options) { - _networks = networks; - _Options = options; + _btcPayNetworkProvider = btcPayNetworkProvider; + _options = options; } public TorService[] Services { get; internal set; } = Array.Empty(); - + private bool firstRun = true; internal async Task Refresh() { - if (string.IsNullOrEmpty(_Options.TorrcFile) || !File.Exists(_Options.TorrcFile)) + if (firstRun) { - if (!string.IsNullOrEmpty(_Options.TorrcFile)) - Logs.PayServer.LogWarning("Torrc file is not found"); - Services = Array.Empty(); - return; + firstRun = false; + } + else + { + await Task.Delay(TimeSpan.FromSeconds(120), Cancellation); } List result = new List(); try { - var torrcContent = await File.ReadAllTextAsync(_Options.TorrcFile); + if (!File.Exists(_options.Value.TorrcFile)) + { + Logs.PayServer.LogWarning("Torrc file is not found"); + Services = Array.Empty(); + return; + } + + var torrcContent = await File.ReadAllTextAsync(_options.Value.TorrcFile); if (!Torrc.TryParse(torrcContent, out var torrc)) { Logs.PayServer.LogWarning("Torrc file could not be parsed"); Services = Array.Empty(); return; } - var torrcDir = Path.GetDirectoryName(_Options.TorrcFile); - var services = torrc.ServiceDirectories.SelectMany(d => d.ServicePorts.Select(p => (Directory: GetDirectory(d, torrcDir), VirtualPort: p.VirtualPort))) - .Select(d => (ServiceName: d.Directory.Name, - ReadingLines: System.IO.File.ReadAllLinesAsync(Path.Combine(d.Directory.FullName, "hostname")), - VirtualPort: d.VirtualPort)) - .ToArray(); + + var torrcDir = Path.GetDirectoryName(_options.Value.TorrcFile); + var services = torrc.ServiceDirectories.SelectMany(d => + d.ServicePorts.Select(p => (Directory: GetDirectory(d, torrcDir), VirtualPort: p.VirtualPort))) + .Select(d => (ServiceName: d.Directory.Name, + ReadingLines: System.IO.File.ReadAllLinesAsync(Path.Combine(d.Directory.FullName, "hostname")), + VirtualPort: d.VirtualPort)) + .ToArray(); foreach (var service in services) { try { var onionHost = (await service.ReadingLines)[0].Trim(); - var torService = new TorService() - { - Name = service.ServiceName, - OnionHost = onionHost, - VirtualPort = service.VirtualPort - }; - if (service.ServiceName.Equals("BTCPayServer", StringComparison.OrdinalIgnoreCase)) - torService.ServiceType = TorServiceType.BTCPayServer; - else if (TryParseP2PService(service.ServiceName, out var network, out var serviceType)) - { - torService.ServiceType = serviceType; - torService.Network = network; - } + var torService = ParseService(service.ServiceName, onionHost, service.VirtualPort); result.Add(torService); } catch (Exception ex) { - Logs.PayServer.LogWarning(ex, $"Error while reading hidden service {service.ServiceName} configuration"); + Logs.PayServer.LogWarning(ex, + $"Error while reading hidden service {service.ServiceName} configuration"); } } } @@ -77,9 +80,29 @@ namespace BTCPayServer.Services { Logs.PayServer.LogWarning(ex, $"Error while reading torrc file"); } + Services = result.ToArray(); } + private TorService ParseService(string serviceName, string onionHost, int virtualPort) + { + var torService = new TorService() {Name = serviceName, OnionHost = onionHost, VirtualPort = virtualPort}; + + if (Enum.TryParse(serviceName, true, out var serviceType)) + torService.ServiceType = serviceType; + else if (TryParseCryptoSpecificService(serviceName, out var network, out serviceType)) + { + torService.ServiceType = serviceType; + torService.Network = network; + } + else + { + torService.ServiceType = TorServiceType.Other; + } + + return torService; + } + private static DirectoryInfo GetDirectory(HiddenServiceDir hs, string relativeTo) { if (Path.IsPathRooted(hs.DirectoryPath)) @@ -87,25 +110,42 @@ namespace BTCPayServer.Services return new DirectoryInfo(Path.Combine(relativeTo, hs.DirectoryPath)); } - private bool TryParseP2PService(string name, out BTCPayNetworkBase network, out TorServiceType serviceType) + private bool TryParseCryptoSpecificService(string name, out BTCPayNetworkBase network, + out TorServiceType serviceType) { network = null; serviceType = TorServiceType.Other; var splitted = name.Trim().Split('-'); - if (splitted.Length == 2 && splitted[1] == "P2P") + return splitted.Length == 2 && Enum.TryParse(splitted[1], true, out serviceType) && + _btcPayNetworkProvider.TryGetNetwork(splitted[0], out network); + } + + public override async Task StartAsync(CancellationToken cancellationToken) + { + if (string.IsNullOrEmpty(_options.Value.TorrcFile)) { - serviceType = TorServiceType.P2P; - } - else if (splitted.Length == 2 && splitted[1] == "RPC") - { - serviceType = TorServiceType.RPC; + LoadFromConfig(); } else { - return false; + await Refresh(); + await base.StartAsync(cancellationToken); } - network = _networks.GetNetwork(splitted[0]); - return network != null; + } + + internal override Task[] InitializeTasks() + { + return new[] {CreateLoopTask(Refresh)}; + } + + private void LoadFromConfig() + { + Services = _options.Value.TorServices.Select(p => p.Split(":", StringSplitOptions.RemoveEmptyEntries)) + .Where(p => p.Length == 3) + .Select(strings => + int.TryParse(strings[2], out var port) ? ParseService(strings[0], strings[1], port) : null) + .Where(p => p != null) + .ToArray(); } }