Refactor TorService (#2388)

* TorServices Refactor to make value passing easier

* Allow specifying Tor services through config

Format:

BTCPAY_TORSERVICES: "BTCPAYSERVER:URL.ONION:VIRTUALPORT;BTC-P2P:URL.ONION:VIRTUALPORT;BTC-RPC:URL.ONION:VIRTUALPORT;SOMEOTHERONIONSERVICE:URL.ONION:VIRTUALPORT"

* add tests

* Optimize Tor Services loader and ensure it is loaded as a hosted service

* Remove Task from Tor service loader

* Use options to parse Tor services

* Fix booboo

* Fix test after fixing booboo

* Adding timeout on long running CanEnumeratetorServices test

(cherry picked from commit 274b77e3175960158b803410037e2c7ff31984be)

* Renaming timeout variable to better name

* Only allow one of torrcfile or torservices

Co-authored-by: Kukks <evilkukka@gmail.com>
Co-authored-by: rockstardev <rockstardev@users.noreply.github.com>
This commit is contained in:
Jonathan Underwood
2021-04-18 11:26:06 +09:00
committed by GitHub
parent 475a68924e
commit ee0fa71605
8 changed files with 181 additions and 119 deletions

View File

@@ -131,5 +131,10 @@ namespace BTCPayServer
} }
return network as T; return network as T;
} }
public bool TryGetNetwork<T>(string cryptoCode, out T network) where T : BTCPayNetworkBase
{
network = GetNetwork<T>(cryptoCode);
return network != null;
}
} }
} }

View File

@@ -23,7 +23,7 @@ namespace BTCPayServer.Tests
Logs.LogProvider = new XUnitLogProvider(helper); Logs.LogProvider = new XUnitLogProvider(helper);
} }
[Fact(Timeout = TestTimeout)] [Fact(Timeout = LongRunningTestTimeout)]
[Trait("Integration", "Integration")] [Trait("Integration", "Integration")]
public async Task CanCreateAndDeleteCrowdfundApp() public async Task CanCreateAndDeleteCrowdfundApp()
{ {
@@ -63,7 +63,7 @@ namespace BTCPayServer.Tests
[Fact(Timeout = TestTimeout)] [Fact(Timeout = LongRunningTestTimeout)]
[Trait("Integration", "Integration")] [Trait("Integration", "Integration")]
public async Task CanContributeOnlyWhenAllowed() public async Task CanContributeOnlyWhenAllowed()
{ {
@@ -155,7 +155,7 @@ namespace BTCPayServer.Tests
} }
} }
[Fact(Timeout = TestTimeout)] [Fact(Timeout = LongRunningTestTimeout)]
[Trait("Integration", "Integration")] [Trait("Integration", "Integration")]
public async Task CanComputeCrowdfundModel() public async Task CanComputeCrowdfundModel()
{ {

View File

@@ -47,7 +47,10 @@ using BTCPayServer.Validation;
using ExchangeSharp; using ExchangeSharp;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Configuration.Memory;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using NBitcoin; using NBitcoin;
using NBitcoin.DataEncoders; using NBitcoin.DataEncoders;
using NBitcoin.Payment; using NBitcoin.Payment;
@@ -67,7 +70,7 @@ namespace BTCPayServer.Tests
{ {
public class UnitTest1 public class UnitTest1
{ {
public const int TestTimeout = 60_000; public const int LongRunningTestTimeout = 60_000; // 60s
public UnitTest1(ITestOutputHelper helper) public UnitTest1(ITestOutputHelper helper)
{ {
@@ -870,18 +873,55 @@ namespace BTCPayServer.Tests
} }
} }
[Fact] [Fact(Timeout = LongRunningTestTimeout)]
[Trait("Fast", "Fast")] [Trait("Fast", "Fast")]
public async Task CanEnumerateTorServices() public async Task CanEnumerateTorServices()
{ {
var tor = new TorServices(new BTCPayNetworkProvider(ChainName.Regtest), var tor = new TorServices(new BTCPayNetworkProvider(ChainName.Regtest),
new BTCPayServerOptions() { TorrcFile = TestUtils.GetTestDataFullPath("Tor/torrc") }); new OptionsWrapper<BTCPayServerOptions>(new BTCPayServerOptions()
{
TorrcFile = TestUtils.GetTestDataFullPath("Tor/torrc")
}));
await tor.Refresh(); await tor.Refresh();
Assert.Single(tor.Services.Where(t => t.ServiceType == TorServiceType.BTCPayServer)); 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.P2P));
Assert.Single(tor.Services.Where(t => t.ServiceType == TorServiceType.RPC)); 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<BTCPayServerOptions>(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")] [Trait("Integration", "Integration")]
public async Task CanUseServerInitiatedPairingCode() public async Task CanUseServerInitiatedPairingCode()
{ {
@@ -1073,7 +1113,7 @@ namespace BTCPayServer.Tests
} }
} }
[Fact(Timeout = TestTimeout)] [Fact(Timeout = LongRunningTestTimeout)]
[Trait("Integration", "Integration")] [Trait("Integration", "Integration")]
public async Task CanSendIPN() public async Task CanSendIPN()
{ {
@@ -1143,7 +1183,7 @@ namespace BTCPayServer.Tests
} }
} }
[Fact(Timeout = TestTimeout)] [Fact(Timeout = LongRunningTestTimeout)]
[Trait("Integration", "Integration")] [Trait("Integration", "Integration")]
public async Task CantPairTwiceWithSamePubkey() public async Task CantPairTwiceWithSamePubkey()
{ {
@@ -1167,7 +1207,7 @@ namespace BTCPayServer.Tests
} }
} }
[Fact(Timeout = TestTimeout)] [Fact(Timeout = LongRunningTestTimeout)]
[Trait("Integration", "Integration")] [Trait("Integration", "Integration")]
public void CanSolveTheDogesRatesOnKraken() public void CanSolveTheDogesRatesOnKraken()
{ {
@@ -1185,7 +1225,7 @@ namespace BTCPayServer.Tests
} }
[Fact(Timeout = TestTimeout)] [Fact(Timeout = LongRunningTestTimeout)]
[Trait("Integration", "Integration")] [Trait("Integration", "Integration")]
public async Task CanUseTorClient() public async Task CanUseTorClient()
{ {
@@ -1238,7 +1278,7 @@ namespace BTCPayServer.Tests
} }
} }
[Fact(Timeout = TestTimeout)] [Fact(Timeout = LongRunningTestTimeout)]
[Trait("Integration", "Integration")] [Trait("Integration", "Integration")]
public async Task CanRescanWallet() public async Task CanRescanWallet()
{ {
@@ -1340,7 +1380,7 @@ namespace BTCPayServer.Tests
} }
} }
[Fact(Timeout = TestTimeout)] [Fact(Timeout = LongRunningTestTimeout)]
[Trait("Integration", "Integration")] [Trait("Integration", "Integration")]
public async Task CanListInvoices() public async Task CanListInvoices()
{ {
@@ -1391,7 +1431,7 @@ namespace BTCPayServer.Tests
} }
} }
[Fact(Timeout = TestTimeout)] [Fact(Timeout = LongRunningTestTimeout)]
[Trait("Integration", "Integration")] [Trait("Integration", "Integration")]
public async Task CanListNotifications() public async Task CanListNotifications()
{ {
@@ -1675,7 +1715,7 @@ namespace BTCPayServer.Tests
Assert.NotNull(paymentData.KeyPath); Assert.NotNull(paymentData.KeyPath);
} }
[Fact(Timeout = TestTimeout)] [Fact(Timeout = LongRunningTestTimeout)]
[Trait("Fast", "Fast")] [Trait("Fast", "Fast")]
public void CanParseFilter() public void CanParseFilter()
{ {
@@ -1703,7 +1743,7 @@ namespace BTCPayServer.Tests
Assert.Equal("hekki", search.TextSearch); Assert.Equal("hekki", search.TextSearch);
} }
[Fact(Timeout = TestTimeout)] [Fact(Timeout = LongRunningTestTimeout)]
[Trait("Fast", "Fast")] [Trait("Fast", "Fast")]
public void CanParseFingerprint() public void CanParseFingerprint()
{ {
@@ -1720,7 +1760,7 @@ namespace BTCPayServer.Tests
Assert.Equal(f1.ToString(), f2.ToString()); Assert.Equal(f1.ToString(), f2.ToString());
} }
[Fact(Timeout = TestTimeout)] [Fact(Timeout = LongRunningTestTimeout)]
[Trait("Integration", "Integration")] [Trait("Integration", "Integration")]
public async void CheckCORSSetOnBitpayAPI() public async void CheckCORSSetOnBitpayAPI()
{ {
@@ -1755,7 +1795,7 @@ namespace BTCPayServer.Tests
} }
} }
[Fact(Timeout = TestTimeout)] [Fact(Timeout = LongRunningTestTimeout)]
[Trait("Integration", "Integration")] [Trait("Integration", "Integration")]
public async Task TestAccessBitpayAPI() public async Task TestAccessBitpayAPI()
{ {
@@ -1834,7 +1874,7 @@ namespace BTCPayServer.Tests
} }
} }
[Fact(Timeout = TestTimeout)] [Fact(Timeout = LongRunningTestTimeout)]
[Trait("Integration", "Integration")] [Trait("Integration", "Integration")]
public async Task CanUseExchangeSpecificRate() public async Task CanUseExchangeSpecificRate()
{ {
@@ -1879,7 +1919,7 @@ namespace BTCPayServer.Tests
return invoice2.CryptoInfo[0].Rate; return invoice2.CryptoInfo[0].Rate;
} }
[Fact(Timeout = TestTimeout)] [Fact(Timeout = LongRunningTestTimeout)]
[Trait("Integration", "Integration")] [Trait("Integration", "Integration")]
public async Task CanUseAnyoneCanCreateInvoice() public async Task CanUseAnyoneCanCreateInvoice()
{ {
@@ -1931,7 +1971,7 @@ namespace BTCPayServer.Tests
} }
} }
[Fact(Timeout = TestTimeout)] [Fact(Timeout = LongRunningTestTimeout)]
[Trait("Integration", "Integration")] [Trait("Integration", "Integration")]
public async Task CanTweakRate() public async Task CanTweakRate()
{ {
@@ -1978,7 +2018,7 @@ namespace BTCPayServer.Tests
} }
} }
[Fact(Timeout = TestTimeout)] [Fact(Timeout = LongRunningTestTimeout)]
[Trait("Integration", "Integration")] [Trait("Integration", "Integration")]
public async Task CanModifyRates() public async Task CanModifyRates()
{ {
@@ -2307,7 +2347,7 @@ namespace BTCPayServer.Tests
Assert.True(client.WaitAllRunning(default).Wait(100)); Assert.True(client.WaitAllRunning(default).Wait(100));
} }
[Fact(Timeout = TestTimeout)] [Fact(Timeout = LongRunningTestTimeout)]
[Trait("Fast", "Fast")] [Trait("Fast", "Fast")]
public void PosDataParser_ParsesCorrectly() public void PosDataParser_ParsesCorrectly()
{ {
@@ -2333,7 +2373,7 @@ namespace BTCPayServer.Tests
}); });
} }
[Fact(Timeout = TestTimeout)] [Fact(Timeout = LongRunningTestTimeout)]
[Trait("Integration", "Integration")] [Trait("Integration", "Integration")]
public async Task PosDataParser_ParsesCorrectly_Slower() public async Task PosDataParser_ParsesCorrectly_Slower()
{ {
@@ -2383,7 +2423,7 @@ namespace BTCPayServer.Tests
} }
} }
[Fact(Timeout = TestTimeout)] [Fact(Timeout = LongRunningTestTimeout)]
[Trait("Integration", "Integration")] [Trait("Integration", "Integration")]
public async Task CanExportInvoicesJson() public async Task CanExportInvoicesJson()
{ {
@@ -2462,7 +2502,7 @@ namespace BTCPayServer.Tests
} }
} }
[Fact(Timeout = TestTimeout)] [Fact(Timeout = LongRunningTestTimeout)]
[Trait("Integration", "Integration")] [Trait("Integration", "Integration")]
public async Task CanChangeNetworkFeeMode() public async Task CanChangeNetworkFeeMode()
{ {
@@ -2553,7 +2593,7 @@ namespace BTCPayServer.Tests
} }
} }
[Fact(Timeout = TestTimeout)] [Fact(Timeout = LongRunningTestTimeout)]
[Trait("Integration", "Integration")] [Trait("Integration", "Integration")]
public async Task CanExportInvoicesCsv() public async Task CanExportInvoicesCsv()
{ {
@@ -2595,7 +2635,7 @@ namespace BTCPayServer.Tests
} }
[Fact(Timeout = TestTimeout)] [Fact(Timeout = LongRunningTestTimeout)]
[Trait("Integration", "Integration")] [Trait("Integration", "Integration")]
public async Task CanCreateAndDeleteApps() public async Task CanCreateAndDeleteApps()
{ {
@@ -2633,7 +2673,7 @@ namespace BTCPayServer.Tests
} }
} }
[Fact(Timeout = TestTimeout)] [Fact(Timeout = LongRunningTestTimeout)]
[Trait("Integration", "Integration")] [Trait("Integration", "Integration")]
public async Task CanCreateStrangeInvoice() public async Task CanCreateStrangeInvoice()
{ {
@@ -2679,7 +2719,7 @@ namespace BTCPayServer.Tests
} }
} }
[Fact(Timeout = TestTimeout)] [Fact(Timeout = LongRunningTestTimeout)]
[Trait("Integration", "Integration")] [Trait("Integration", "Integration")]
public async Task InvoiceFlowThroughDifferentStatesCorrectly() public async Task InvoiceFlowThroughDifferentStatesCorrectly()
{ {
@@ -2869,7 +2909,7 @@ namespace BTCPayServer.Tests
} }
} }
[Fact(Timeout = TestTimeout)] [Fact(Timeout = LongRunningTestTimeout)]
[Trait("Integration", "Integration")] [Trait("Integration", "Integration")]
public void CanQueryDirectProviders() public void CanQueryDirectProviders()
{ {
@@ -2940,7 +2980,7 @@ namespace BTCPayServer.Tests
} }
[Fact(Timeout = TestTimeout)] [Fact(Timeout = LongRunningTestTimeout)]
[Trait("Integration", "Integration")] [Trait("Integration", "Integration")]
public async Task CanExportBackgroundFetcherState() public async Task CanExportBackgroundFetcherState()
{ {
@@ -2982,7 +3022,7 @@ namespace BTCPayServer.Tests
} }
[Fact(Timeout = TestTimeout)] [Fact(Timeout = LongRunningTestTimeout)]
[Trait("Integration", "Integration")] [Trait("Integration", "Integration")]
public void CanGetRateCryptoCurrenciesByDefault() public void CanGetRateCryptoCurrenciesByDefault()
{ {
@@ -3034,7 +3074,7 @@ namespace BTCPayServer.Tests
} }
} }
[Fact(Timeout = TestTimeout)] [Fact(Timeout = LongRunningTestTimeout)]
[Trait("Integration", "Integration")] [Trait("Integration", "Integration")]
public async Task CheckLogsRoute() public async Task CheckLogsRoute()
{ {
@@ -3117,7 +3157,7 @@ namespace BTCPayServer.Tests
return name; return name;
} }
[Fact(Timeout = TestTimeout)] [Fact(Timeout = LongRunningTestTimeout)]
[Trait("Fast", "Fast")] [Trait("Fast", "Fast")]
public void CanCheckFileNameValid() public void CanCheckFileNameValid()
{ {
@@ -3135,7 +3175,7 @@ namespace BTCPayServer.Tests
} }
} }
[Fact(Timeout = TestTimeout)] [Fact(Timeout = LongRunningTestTimeout)]
[Trait("Fast", "Fast")] [Trait("Fast", "Fast")]
public async Task CanCreateSqlitedb() public async Task CanCreateSqlitedb()
{ {
@@ -3147,7 +3187,7 @@ namespace BTCPayServer.Tests
await new ApplicationDbContext(builder.Options).Database.MigrateAsync(); await new ApplicationDbContext(builder.Options).Database.MigrateAsync();
} }
[Fact(Timeout = TestTimeout)] [Fact(Timeout = LongRunningTestTimeout)]
[Trait("Fast", "Fast")] [Trait("Fast", "Fast")]
public void CanUsePermission() public void CanUsePermission()
{ {
@@ -3172,7 +3212,7 @@ namespace BTCPayServer.Tests
.Contains(Permission.Create(Policies.CanModifyStoreSettings))); .Contains(Permission.Create(Policies.CanModifyStoreSettings)));
} }
[Fact(Timeout = TestTimeout)] [Fact(Timeout = LongRunningTestTimeout)]
[Trait("Fast", "Fast")] [Trait("Fast", "Fast")]
public void CheckRatesProvider() public void CheckRatesProvider()
{ {
@@ -3260,7 +3300,7 @@ namespace BTCPayServer.Tests
} }
[Fact(Timeout = TestTimeout)] [Fact(Timeout = LongRunningTestTimeout)]
[Trait("Integration", "Integration")] [Trait("Integration", "Integration")]
public async Task CanLoginWithNoSecondaryAuthSystemsOrRequestItWhenAdded() public async Task CanLoginWithNoSecondaryAuthSystemsOrRequestItWhenAdded()
{ {
@@ -3336,7 +3376,7 @@ namespace BTCPayServer.Tests
} }
} }
[Fact(Timeout = TestTimeout)] [Fact(Timeout = LongRunningTestTimeout)]
[Trait("Integration", "Integration")] [Trait("Integration", "Integration")]
public async void CheckOnionlocationForNonOnionHtmlRequests() public async void CheckOnionlocationForNonOnionHtmlRequests()
{ {
@@ -3382,7 +3422,7 @@ namespace BTCPayServer.Tests
} }
} }
[Fact(Timeout = TestTimeout)] [Fact(Timeout = LongRunningTestTimeout)]
[Trait("Integration", "Integration")] [Trait("Integration", "Integration")]
public async Task CanCheckForNewVersion() public async Task CanCheckForNewVersion()
{ {
@@ -3428,7 +3468,7 @@ namespace BTCPayServer.Tests
} }
} }
[Fact(Timeout = TestTimeout)] [Fact(Timeout = LongRunningTestTimeout)]
[Trait("Integration", "Integration")] [Trait("Integration", "Integration")]
public async Task CanDoLightningInternalNodeMigration() public async Task CanDoLightningInternalNodeMigration()
{ {
@@ -3507,7 +3547,7 @@ namespace BTCPayServer.Tests
} }
[Fact(Timeout = TestTimeout)] [Fact(Timeout = LongRunningTestTimeout)]
[Trait("Integration", "Integration")] [Trait("Integration", "Integration")]
public async Task CanDoInvoiceMigrations() public async Task CanDoInvoiceMigrations()
{ {
@@ -3588,7 +3628,7 @@ namespace BTCPayServer.Tests
await migrationStartupTask.ExecuteAsync(); await migrationStartupTask.ExecuteAsync();
} }
[Fact(Timeout = TestTimeout)] [Fact(Timeout = LongRunningTestTimeout)]
[Trait("Integration", "Integration")] [Trait("Integration", "Integration")]
public async Task EmailSenderTests() public async Task EmailSenderTests()
{ {

View File

@@ -69,7 +69,12 @@ namespace BTCPayServer.Configuration
BundleJsCss = conf.GetOrDefault<bool>("bundlejscss", true); BundleJsCss = conf.GetOrDefault<bool>("bundlejscss", true);
DockerDeployment = conf.GetOrDefault<bool>("dockerdeployment", true); DockerDeployment = conf.GetOrDefault<bool>("dockerdeployment", true);
AllowAdminRegistration = conf.GetOrDefault<bool>("allow-admin-registration", false); AllowAdminRegistration = conf.GetOrDefault<bool>("allow-admin-registration", false);
TorrcFile = conf.GetOrDefault<string>("torrcfile", null); TorrcFile = conf.GetOrDefault<string>("torrcfile", null);
TorServices = conf.GetOrDefault<string>("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<string>("socksendpoint", null); var socksEndpointString = conf.GetOrDefault<string>("socksendpoint", null);
if (!string.IsNullOrEmpty(socksEndpointString)) if (!string.IsNullOrEmpty(socksEndpointString))
@@ -195,6 +200,7 @@ namespace BTCPayServer.Configuration
set; set;
} }
public string TorrcFile { get; set; } public string TorrcFile { get; set; }
public string[] TorServices { get; set; }
public Uri UpdateUrl { get; set; } public Uri UpdateUrl { get; set; }
} }
} }

View File

@@ -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("--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("--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("--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("--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("--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); app.Option("--debuglog", "A rolling log file for debug messages.", CommandOptionType.SingleValue);

View File

@@ -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);
}
}
}

View File

@@ -93,6 +93,7 @@ namespace BTCPayServer.Hosting
services.TryAddSingleton<ISettingsRepository>(provider => provider.GetService<SettingsRepository>()); services.TryAddSingleton<ISettingsRepository>(provider => provider.GetService<SettingsRepository>());
services.TryAddSingleton<LabelFactory>(); services.TryAddSingleton<LabelFactory>();
services.TryAddSingleton<TorServices>(); services.TryAddSingleton<TorServices>();
services.AddSingleton<IHostedService>(provider => provider.GetRequiredService<TorServices>());
services.TryAddSingleton<SocketFactory>(); services.TryAddSingleton<SocketFactory>();
services.TryAddSingleton<LightningClientFactoryService>(); services.TryAddSingleton<LightningClientFactoryService>();
services.TryAddSingleton<InvoicePaymentNotification>(); services.TryAddSingleton<InvoicePaymentNotification>();
@@ -346,7 +347,6 @@ namespace BTCPayServer.Hosting
services.AddSingleton<IHostedService, TransactionLabelMarkerHostedService>(); services.AddSingleton<IHostedService, TransactionLabelMarkerHostedService>();
services.AddSingleton<IHostedService, UserEventHostedService>(); services.AddSingleton<IHostedService, UserEventHostedService>();
services.AddSingleton<IHostedService, DynamicDnsHostedService>(); services.AddSingleton<IHostedService, DynamicDnsHostedService>();
services.AddSingleton<IHostedService, TorServicesHostedService>();
services.AddSingleton<IHostedService, PaymentRequestStreamer>(); services.AddSingleton<IHostedService, PaymentRequestStreamer>();
services.AddSingleton<IBackgroundJobClient, BackgroundJobClient>(); services.AddSingleton<IBackgroundJobClient, BackgroundJobClient>();
services.AddScoped<IAuthorizationHandler, CookieAuthorizationHandler>(); services.AddScoped<IAuthorizationHandler, CookieAuthorizationHandler>();

View File

@@ -2,47 +2,61 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using BTCPayServer.Configuration; using BTCPayServer.Configuration;
using BTCPayServer.HostedServices;
using BTCPayServer.Logging; using BTCPayServer.Logging;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
namespace BTCPayServer.Services namespace BTCPayServer.Services
{ {
public class TorServices public class TorServices : BaseAsyncService
{ {
private readonly BTCPayNetworkProvider _networks; private readonly BTCPayNetworkProvider _btcPayNetworkProvider;
readonly BTCPayServerOptions _Options; private readonly IOptions<BTCPayServerOptions> _options;
public TorServices(BTCPayServer.BTCPayNetworkProvider networks, BTCPayServerOptions options)
public TorServices(BTCPayNetworkProvider btcPayNetworkProvider, IOptions<BTCPayServerOptions> options)
{ {
_networks = networks; _btcPayNetworkProvider = btcPayNetworkProvider;
_Options = options; _options = options;
} }
public TorService[] Services { get; internal set; } = Array.Empty<TorService>(); public TorService[] Services { get; internal set; } = Array.Empty<TorService>();
private bool firstRun = true;
internal async Task Refresh() internal async Task Refresh()
{ {
if (string.IsNullOrEmpty(_Options.TorrcFile) || !File.Exists(_Options.TorrcFile)) if (firstRun)
{ {
if (!string.IsNullOrEmpty(_Options.TorrcFile)) firstRun = false;
Logs.PayServer.LogWarning("Torrc file is not found"); }
Services = Array.Empty<TorService>(); else
return; {
await Task.Delay(TimeSpan.FromSeconds(120), Cancellation);
} }
List<TorService> result = new List<TorService>(); List<TorService> result = new List<TorService>();
try 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<TorService>();
return;
}
var torrcContent = await File.ReadAllTextAsync(_options.Value.TorrcFile);
if (!Torrc.TryParse(torrcContent, out var torrc)) if (!Torrc.TryParse(torrcContent, out var torrc))
{ {
Logs.PayServer.LogWarning("Torrc file could not be parsed"); Logs.PayServer.LogWarning("Torrc file could not be parsed");
Services = Array.Empty<TorService>(); Services = Array.Empty<TorService>();
return; return;
} }
var torrcDir = Path.GetDirectoryName(_Options.TorrcFile);
var services = torrc.ServiceDirectories.SelectMany(d => d.ServicePorts.Select(p => (Directory: GetDirectory(d, torrcDir), VirtualPort: p.VirtualPort))) 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, .Select(d => (ServiceName: d.Directory.Name,
ReadingLines: System.IO.File.ReadAllLinesAsync(Path.Combine(d.Directory.FullName, "hostname")), ReadingLines: System.IO.File.ReadAllLinesAsync(Path.Combine(d.Directory.FullName, "hostname")),
VirtualPort: d.VirtualPort)) VirtualPort: d.VirtualPort))
@@ -52,24 +66,13 @@ namespace BTCPayServer.Services
try try
{ {
var onionHost = (await service.ReadingLines)[0].Trim(); var onionHost = (await service.ReadingLines)[0].Trim();
var torService = new TorService() var torService = ParseService(service.ServiceName, onionHost, service.VirtualPort);
{
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;
}
result.Add(torService); result.Add(torService);
} }
catch (Exception ex) 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"); Logs.PayServer.LogWarning(ex, $"Error while reading torrc file");
} }
Services = result.ToArray(); 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<TorServiceType>(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) private static DirectoryInfo GetDirectory(HiddenServiceDir hs, string relativeTo)
{ {
if (Path.IsPathRooted(hs.DirectoryPath)) if (Path.IsPathRooted(hs.DirectoryPath))
@@ -87,25 +110,42 @@ namespace BTCPayServer.Services
return new DirectoryInfo(Path.Combine(relativeTo, hs.DirectoryPath)); 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; network = null;
serviceType = TorServiceType.Other; serviceType = TorServiceType.Other;
var splitted = name.Trim().Split('-'); 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);
serviceType = TorServiceType.P2P;
} }
else if (splitted.Length == 2 && splitted[1] == "RPC")
public override async Task StartAsync(CancellationToken cancellationToken)
{ {
serviceType = TorServiceType.RPC; if (string.IsNullOrEmpty(_options.Value.TorrcFile))
{
LoadFromConfig();
} }
else else
{ {
return false; await Refresh();
await base.StartAsync(cancellationToken);
} }
network = _networks.GetNetwork<BTCPayNetworkBase>(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();
} }
} }