Prepare Tor fallback work
MODIFY
WalletWasabi.Gui/Config.cs
WalletWasabi.Gui/Config.cs
@@ -12,6 +12,7 @@ using WalletWasabi.Crypto;
using WalletWasabi.Helpers;
using WalletWasabi.Interfaces;
using WalletWasabi.Logging;
+using WalletWasabi.TorSocks5;
namespace WalletWasabi.Gui
{
@@ -31,6 +32,12 @@ namespace WalletWasabi.Gui
[JsonProperty(PropertyName = "TestNetBackendUriV3")]
public string TestNetBackendUriV3 { get; private set; }
+ [JsonProperty(PropertyName = "MainNetFallbackBackendUri")]
+ public string MainNetFallbackBackendUri { get; private set; }
+
+ [JsonProperty(PropertyName = "TestNetFallbackBackendUri")]
+ public string TestNetFallbackBackendUri { get; private set; }
+
[JsonProperty(PropertyName = "RegTestBackendUriV3")]
public string RegTestBackendUriV3 { get; private set; }
@@ -50,10 +57,16 @@ namespace WalletWasabi.Gui
public int? TorSocks5Port { get; internal set; }
private Uri _backendUri;
+ private Uri _fallbackBackendUri;
public Uri GetCurrentBackendUri()
{
- if (!(_backendUri is null)) return _backendUri;
+ if (TorProcessManager.RequestFallbackAddressUsage)
+ {
+ return GetFallbackBackendUri();
+ }
+
+ if (_backendUri != null) return _backendUri;
if (Network == Network.Main)
{
@@ -71,6 +84,26 @@ namespace WalletWasabi.Gui
return _backendUri;
}
+ public Uri GetFallbackBackendUri()
+ {
+ if (_fallbackBackendUri != null) return _fallbackBackendUri;
+
+ if (Network == Network.Main)
+ {
+ _fallbackBackendUri = new Uri(MainNetFallbackBackendUri);
+ }
+ else if (Network == Network.TestNet)
+ {
+ _fallbackBackendUri = new Uri(TestNetFallbackBackendUri);
+ }
+ else // RegTest
+ {
+ _fallbackBackendUri = new Uri(RegTestBackendUriV3);
+ }
+
+ return _fallbackBackendUri;
+ }
+
private IPEndPoint _torSocks5EndPoint;
public IPEndPoint GetTorSocks5EndPoint()
@@ -119,12 +152,15 @@ namespace WalletWasabi.Gui
SetFilePath(filePath);
}
- public Config(Network network, string mainNetBackendUriV3, string testNetBackendUriV3, string regTestBackendUriV3, string mainNetBlindingRsaPubKey, string testNetBlindingRsaPubKey, string regTestBlindingRsaPubKey, string torHost, int? torSocks5Port)
+ public Config(Network network, string mainNetBackendUriV3, string testNetBackendUriV3, string mainNetFallbackBackendUri, string testNetFallbackBackendUri, string regTestBackendUriV3, string mainNetBlindingRsaPubKey, string testNetBlindingRsaPubKey, string regTestBlindingRsaPubKey, string torHost, int? torSocks5Port)
{
Network = Guard.NotNull(nameof(network), network);
MainNetBackendUriV3 = Guard.NotNullOrEmptyOrWhitespace(nameof(mainNetBackendUriV3), mainNetBackendUriV3);
TestNetBackendUriV3 = Guard.NotNullOrEmptyOrWhitespace(nameof(testNetBackendUriV3), testNetBackendUriV3);
+ MainNetFallbackBackendUri = Guard.NotNullOrEmptyOrWhitespace(nameof(mainNetFallbackBackendUri), mainNetFallbackBackendUri);
+ TestNetFallbackBackendUri = Guard.NotNullOrEmptyOrWhitespace(nameof(testNetFallbackBackendUri), testNetFallbackBackendUri);
+ TestNetBackendUriV3 = Guard.NotNullOrEmptyOrWhitespace(nameof(testNetBackendUriV3), testNetBackendUriV3);
RegTestBackendUriV3 = Guard.NotNullOrEmptyOrWhitespace(nameof(regTestBackendUriV3), regTestBackendUriV3);
MainNetBlindingRsaPubKey = Guard.NotNullOrEmptyOrWhitespace(nameof(mainNetBlindingRsaPubKey), mainNetBlindingRsaPubKey);
@@ -155,6 +191,8 @@ namespace WalletWasabi.Gui
MainNetBackendUriV3 = "http://wasabiukrxmkdgve5kynjztuovbg43uxcbcxn6y2okcrsg7gb6jdmbad.onion/";
TestNetBackendUriV3 = "http://testwnp3fugjln6vh5vpj7mvq3lkqqwjj3c2aafyu7laxz42kgwh2rad.onion/";
+ MainNetFallbackBackendUri = "https://wasabiwallet.io/";
+ TestNetFallbackBackendUri = "https://wasabiwallet.co/";
RegTestBackendUriV3 = "http://localhost:37127/";
MainNetBlindingRsaPubKey = "16421152619146079007287475569112871971988560541093277613438316709041030720662622782033859387192362542996510605015506477964793447620206674394713753349543444988246276357919473682408472170521463339860947351211455351029147665615454176157348164935212551240942809518428851690991984017733153078846480521091423447691527000770982623947706172997649440619968085147635776736938871139581019988225202983052255684151711253254086264386774936200194229277914886876824852466823571396538091430866082004097086602287294474304344865162932126041736158327600847754258634325228417149098062181558798532036659383679712667027126535424484318399849";
@@ -189,6 +227,8 @@ namespace WalletWasabi.Gui
MainNetBackendUriV3 = config.MainNetBackendUriV3 ?? MainNetBackendUriV3;
TestNetBackendUriV3 = config.TestNetBackendUriV3 ?? TestNetBackendUriV3;
+ MainNetFallbackBackendUri = config.MainNetFallbackBackendUri ?? MainNetFallbackBackendUri;
+ TestNetFallbackBackendUri = config.TestNetFallbackBackendUri ?? TestNetFallbackBackendUri;
RegTestBackendUriV3 = config.RegTestBackendUriV3 ?? RegTestBackendUriV3;
MainNetBlindingRsaPubKey = config.MainNetBlindingRsaPubKey ?? MainNetBlindingRsaPubKey;
@@ -221,17 +261,23 @@ namespace WalletWasabi.Gui
return true;
}
+ if (!MainNetBackendUriV3.Equals(config.MainNetBackendUriV3, StringComparison.OrdinalIgnoreCase))
+ {
+ return true;
+ }
if (!TestNetBackendUriV3.Equals(config.TestNetBackendUriV3, StringComparison.OrdinalIgnoreCase))
{
return true;
}
-
- if (!RegTestBackendUriV3.Equals(config.RegTestBackendUriV3, StringComparison.OrdinalIgnoreCase))
+ if (!MainNetFallbackBackendUri.Equals(config.MainNetFallbackBackendUri, StringComparison.OrdinalIgnoreCase))
{
return true;
}
-
- if (!MainNetBackendUriV3.Equals(config.MainNetBackendUriV3, StringComparison.OrdinalIgnoreCase))
+ if (!TestNetFallbackBackendUri.Equals(config.TestNetFallbackBackendUri, StringComparison.OrdinalIgnoreCase))
+ {
+ return true;
+ }
+ if (!RegTestBackendUriV3.Equals(config.RegTestBackendUriV3, StringComparison.OrdinalIgnoreCase))
{
return true;
}

MODIFY
WalletWasabi.Gui/Global.cs
WalletWasabi.Gui/Global.cs
@@ -137,7 +137,8 @@ namespace WalletWasabi.Gui
TorManager = new TorProcessManager(Config.GetTorSocks5EndPoint(), TorLogsFile);
TorManager.Start(false, DataDir);
- TorManager.StartMonitor(TimeSpan.FromSeconds(3), TimeSpan.FromSeconds(7), DataDir);
+ var fallbackRequestTestUri = new Uri(Config.GetFallbackBackendUri(), "/api/software/versions");
+ TorManager.StartMonitor(TimeSpan.FromSeconds(3), TimeSpan.FromSeconds(7), DataDir, fallbackRequestTestUri);
Logger.LogInfo<TorProcessManager>($"{nameof(TorProcessManager)} is initialized.");

MODIFY
WalletWasabi/TorSocks5/TorHttpClient.cs
WalletWasabi/TorSocks5/TorHttpClient.cs
@@ -20,7 +20,25 @@ namespace WalletWasabi.TorSocks5
{
public class TorHttpClient : IDisposable
{
- public static DateTimeOffset? TorDoesntWorkSince { get; private set; } = null; // This sets the first time the request failed with exception.
+ private static DateTimeOffset? TorDoesntWorkSinceBacking = null;
+
+ public static DateTimeOffset? TorDoesntWorkSince
+ {
+ get => TorDoesntWorkSinceBacking;
+ private set
+ {
+ if (value != TorDoesntWorkSinceBacking)
+ {
+ TorDoesntWorkSinceBacking = value;
+ if (value is null)
+ {
+ LatestTorException = null;
+ }
+ }
+ }
+ }
+
+ public static Exception LatestTorException { get; private set; } = null;
public Uri DestinationUri { get; }
public IPEndPoint TorSocks5EndPoint { get; }
@@ -112,26 +130,29 @@ namespace WalletWasabi.TorSocks5
}
catch (TaskCanceledException ex)
{
- if (TorDoesntWorkSince == null)
+ SetTorNotWorkingState(ex);
+ throw new OperationCanceledException(ex.Message, ex, cancel);
+ }
+ catch (Exception ex)
{
- TorDoesntWorkSince = DateTimeOffset.UtcNow;
+ SetTorNotWorkingState(ex);
+ throw;
}
- throw new OperationCanceledException(ex.Message, ex, cancel);
}
- catch
+
+ private static void SetTorNotWorkingState(Exception ex)
{
if (TorDoesntWorkSince == null)
{
TorDoesntWorkSince = DateTimeOffset.UtcNow;
}
- throw;
- }
+ LatestTorException = ex;
}
/// <remarks>
/// Throws OperationCancelledException if <paramref name="cancel"/> is set.
/// </remarks>
- private async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancel = default)
+ public async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancel = default)
{
Guard.NotNull(nameof(request), request);

MODIFY
WalletWasabi/TorSocks5/TorProcessManager.cs
WalletWasabi/TorSocks5/TorProcessManager.cs
using System.Diagnostics;
using System.IO;
using System.Net;
+using System.Net.Http;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using WalletWasabi.Exceptions;
using WalletWasabi.Helpers;
using WalletWasabi.Logging;
+using WalletWasabi.TorSocks5.Models.Fields.OctetFields;
+using WalletWasabi.WebClients.Wasabi;
namespace WalletWasabi.TorSocks5
{
@@ -16,6 +19,8 @@ namespace WalletWasabi.TorSocks5
public IPEndPoint TorSocks5EndPoint { get; }
public string LogFile { get; }
+ public static bool RequestFallbackAddressUsage { get; private set; } = false;
+
public Process TorProcess { get; private set; }
/// <param name="torSocks5EndPoint">Opt out Tor with null.</param>
@@ -206,7 +211,7 @@ namespace WalletWasabi.TorSocks5
private CancellationTokenSource Stop { get; }
- public void StartMonitor(TimeSpan torMisbehaviorCheckPeriod, TimeSpan checkIfRunningAfterTorMisbehavedFor, string dataDirToStartWith)
+ public void StartMonitor(TimeSpan torMisbehaviorCheckPeriod, TimeSpan checkIfRunningAfterTorMisbehavedFor, string dataDirToStartWith, Uri fallBackTestRequestUri)
{
Logger.LogInfo<TorProcessManager>("Starting Tor monitor...");
Interlocked.Exchange(ref _running, 1);
@@ -224,11 +229,31 @@ namespace WalletWasabi.TorSocks5
await Task.Delay(torMisbehaviorCheckPeriod, Stop.Token).ConfigureAwait(false);
- if (!(TorHttpClient.TorDoesntWorkSince is null)) // If Tor misbehaves.
+ if (TorHttpClient.TorDoesntWorkSince != null) // If Tor misbehaves.
{
TimeSpan torMisbehavedFor = (DateTimeOffset.UtcNow - TorHttpClient.TorDoesntWorkSince) ?? TimeSpan.Zero;
if (torMisbehavedFor > checkIfRunningAfterTorMisbehavedFor)
+ {
+ if (TorHttpClient.LatestTorException is TorSocks5FailureResponseException torEx)
+ {
+ if (torEx.RepField == RepField.HostUnreachable)
+ {
+ using (var client = new TorHttpClient(new Uri(fallBackTestRequestUri.DnsSafeHost), TorSocks5EndPoint))
+ {
+ var message = new HttpRequestMessage(HttpMethod.Get, fallBackTestRequestUri);
+ await client.SendAsync(message, Stop.Token);
+ }
+
+ // Check if it changed in the meantime...
+ if (TorHttpClient.LatestTorException is TorSocks5FailureResponseException torEx2 && torEx2.RepField == RepField.HostUnreachable)
+ {
+ // Fallback here...
+ RequestFallbackAddressUsage = true;
+ }
+ }
+ }
+ else
{
Logger.LogInfo<TorProcessManager>($"Tor didn't work properly for {(int)torMisbehavedFor.TotalSeconds} seconds. Maybe it crashed. Attempting to start it...");
Start(true, dataDirToStartWith); // Try starting Tor, if doesn't work it'll be another issue.
@@ -236,10 +261,19 @@ namespace WalletWasabi.TorSocks5
}
}
}
+ }
catch (TaskCanceledException ex)
{
Logger.LogTrace<TorProcessManager>(ex);
}
+ catch (OperationCanceledException ex)
+ {
+ Logger.LogTrace<TorProcessManager>(ex);
+ }
+ catch (TimeoutException ex)
+ {
+ Logger.LogTrace<TorProcessManager>(ex);
+ }
catch (Exception ex)
{
Logger.LogDebug<TorProcessManager>(ex);

MODIFY
WalletWasabi/WebClients/Wasabi/WasabiClient.cs
WalletWasabi/WebClients/Wasabi/WasabiClient.cs
@@ -115,7 +115,7 @@ namespace WalletWasabi.WebClients.Wasabi
#region software
- public async Task<(Version ClientVersion, int BackendMajorVersion)> GetVersionsAsync(CancellationToken cancel)
+ public async Task<(Version ClientVersion, int BackendMajorVersion)> GetVersionsAsync(CancellationToken cancel, bool setErrorState = true)
{
using (var response = await TorClient.SendAndRetryAsync(HttpMethod.Get, HttpStatusCode.OK, "/api/software/versions", cancel: cancel))
{

Prepare Tor fallback work