Skip to content

Commit

Permalink
Merge branch 'master' into SendManualControl2
Browse files Browse the repository at this point in the history
  • Loading branch information
ichthus1604 committed Apr 29, 2024
2 parents 917f169 + 06ede9d commit 260c70d
Show file tree
Hide file tree
Showing 16 changed files with 444 additions and 201 deletions.
12 changes: 12 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,9 @@ dotnet_diagnostic.IDE0004.severity = error
# IDE0019: Use pattern matching
csharp_style_pattern_matching_over_as_with_null_check = true:error

# IDE0028: Simplify collection initialization
dotnet_diagnostic.IDE0028.severity = silent

# IDE0032: Use auto property
dotnet_style_prefer_auto_properties = true:error

Expand All @@ -249,6 +252,15 @@ dotnet_style_prefer_simplified_interpolation = true:error
# IDE0130: Warn and provide code fixes for when namespaces do not match the folder structure.
dotnet_diagnostic.IDE0130.severity = warning

# IDE0290: Use primary constructor
dotnet_diagnostic.IDE0290.severity = silent

# IDE0300: Simplify collection initialization
dotnet_diagnostic.IDE0300.severity = silent

# IDE0305: Simplify collection initialization
dotnet_diagnostic.IDE0305.severity = silent

# CA1304: Specify CultureInfo
dotnet_diagnostic.CA1304.severity = warning

Expand Down
8 changes: 1 addition & 7 deletions WalletWasabi.Backend/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,6 @@
using WalletWasabi.WabiSabi;
using WalletWasabi.WabiSabi.Models.Serialization;
using WalletWasabi.WebClients;
using System.Net.WebSockets;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using System.Threading.Tasks;
using System.Net;
using WalletWasabi.Services;

[assembly: ApiController]
Expand Down Expand Up @@ -187,7 +181,7 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env, Global g

var serviceScopeFactory = app.ApplicationServices.GetRequiredService<IServiceScopeFactory>();
var serviceProvider = serviceScopeFactory.CreateScope().ServiceProvider;
app.MapWebSocketManager("/api/satoshi", serviceProvider.GetService<SatoshiWebSocketHandler>());
app.MapWebSocketManager("/api/satoshi", serviceProvider.GetRequiredService<SatoshiWebSocketHandler>());

app.UseResponseCompression();

Expand Down
52 changes: 50 additions & 2 deletions WalletWasabi.Daemon/Config.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using Microsoft.Extensions.Logging;
using NBitcoin;
using System;
using System.Collections;
Expand All @@ -8,6 +9,7 @@
using System.Net;
using WalletWasabi.Exceptions;
using WalletWasabi.Helpers;
using WalletWasabi.Logging;
using WalletWasabi.Models;
using WalletWasabi.Tor;
using WalletWasabi.Userfacing;
Expand All @@ -23,6 +25,14 @@ public Config(PersistentConfig persistentConfig, string[] cliArgs)
PersistentConfig = persistentConfig;
CliArgs = cliArgs;

LogMode[] defaultLogModes;

#if RELEASE
defaultLogModes = [LogMode.Console, LogMode.File];
#else
defaultLogModes = [LogMode.Debug, LogMode.Console, LogMode.File];
#endif

Data = new()
{
[ nameof(Network)] = (
Expand All @@ -49,13 +59,19 @@ [ nameof(RegTestCoordinatorUri)] = (
[ nameof(UseTor)] = (
"All the communications go through the Tor network",
GetBoolValue("UseTor", PersistentConfig.UseTor, cliArgs)),
[ nameof(TorFolder)] = (
"Folder where Tor binary is located",
GetNullableStringValue("TorFolder", null, cliArgs)),
[ nameof(TorSocksPort)] = (
"Tor is started to listen with the specified SOCKS5 port",
GetLongValue("TorSocksPort", TorSettings.DefaultSocksPort, cliArgs)),
[ nameof(TorControlPort)] = (
"Tor is started to listen with the specified control port",
GetLongValue("TorControlPort", TorSettings.DefaultControlPort, cliArgs)),
[ nameof(TerminateTorOnExit)] = (
[nameof(TorBridges)] = (
"Tor is started with the set of specified bridges",
GetStringArrayValue("TorBridges", PersistentConfig.TorBridges, cliArgs)),
[nameof(TerminateTorOnExit)] = (
"Stop the Tor process when Wasabi is closed",
GetBoolValue("TerminateTorOnExit", PersistentConfig.TerminateTorOnExit, cliArgs)),
[ nameof(DownloadNewVersion)] = (
Expand Down Expand Up @@ -103,6 +119,9 @@ [ nameof(BlockOnlyMode)] = (
[ nameof(LogLevel)] = (
"The level of detail in the logs: trace, debug, info, warning, error, or critical",
GetStringValue("LogLevel", value: "", cliArgs)),
[ nameof(LogModes)] = (
"The logging modes: console, and file (for multiple values use comma as a separator)",
GetLogModeArrayValue("LogModes", arrayValues: defaultLogModes, cliArgs)),
[ nameof(EnableGpu)] = (
"Use a GPU to render the user interface",
GetBoolValue("EnableGpu", PersistentConfig.EnableGpu, cliArgs)),
Expand Down Expand Up @@ -136,8 +155,10 @@ [ nameof(CoordinatorIdentifier)] = (
public string? TestNetCoordinatorUri => GetEffectiveValue<NullableStringValue, string?>(nameof(TestNetCoordinatorUri));
public string? RegTestCoordinatorUri => GetEffectiveValue<NullableStringValue, string?>(nameof(RegTestCoordinatorUri));
public bool UseTor => GetEffectiveValue<BoolValue, bool>(nameof(UseTor)) && Network != Network.RegTest;
public string? TorFolder => GetEffectiveValue<NullableStringValue, string?>(nameof(TorFolder));
public int TorSocksPort => GetEffectiveValue<IntValue, int>(nameof(TorSocksPort));
public int TorControlPort => GetEffectiveValue<IntValue, int>(nameof(TorControlPort));
public string[] TorBridges => GetEffectiveValue<StringArrayValue, string[]>(nameof(TorBridges));
public bool TerminateTorOnExit => GetEffectiveValue<BoolValue, bool>(nameof(TerminateTorOnExit));
public bool DownloadNewVersion => GetEffectiveValue<BoolValue, bool>(nameof(DownloadNewVersion));
public bool StartLocalBitcoinCoreOnStartup => GetEffectiveValue<BoolValue, bool>(nameof(StartLocalBitcoinCoreOnStartup));
Expand All @@ -154,6 +175,7 @@ [ nameof(CoordinatorIdentifier)] = (
public Money DustThreshold => GetEffectiveValue<MoneyValue, Money>(nameof(DustThreshold));
public bool BlockOnlyMode => GetEffectiveValue<BoolValue, bool>(nameof(BlockOnlyMode));
public string LogLevel => GetEffectiveValue<StringValue, string>(nameof(LogLevel));
public LogMode[] LogModes => GetEffectiveValue<LogModeArrayValue, LogMode[]>(nameof(LogModes));

public bool EnableGpu => GetEffectiveValue<BoolValue, bool>(nameof(EnableGpu));
public string CoordinatorIdentifier => GetEffectiveValue<StringValue, string>(nameof(CoordinatorIdentifier));
Expand Down Expand Up @@ -316,12 +338,37 @@ private static StringArrayValue GetStringArrayValue(string key, string[] arrayVa
{
if (GetOverrideValue(key, cliArgs, out string? overrideValue, out ValueSource? valueSource))
{
return new StringArrayValue(arrayValues, new string[] { overrideValue }, valueSource.Value);
string[] overrideValues = overrideValue.Split(';', StringSplitOptions.None);
return new StringArrayValue(arrayValues, overrideValues, valueSource.Value);
}

return new StringArrayValue(arrayValues, arrayValues, ValueSource.Disk);
}

private static LogModeArrayValue GetLogModeArrayValue(string key, LogMode[] arrayValues, string[] cliArgs)
{
if (GetOverrideValue(key, cliArgs, out string? overrideValue, out ValueSource? valueSource))
{
LogMode[] logModes = overrideValue.Split(',', StringSplitOptions.RemoveEmptyEntries)
.Where(x => !string.IsNullOrWhiteSpace(x)) // Filter our whitespace-only elements.
.Select(x =>
{
if (!Enum.TryParse(x.Trim(), ignoreCase: true, out LogMode logMode))
{
throw new NotSupportedException($"Logging mode '{x}' is not supported.");
}
return logMode;
})
.ToHashSet() // Remove duplicates.
.ToArray();

return new LogModeArrayValue(arrayValues, logModes, valueSource.Value);
}

return new LogModeArrayValue(arrayValues, arrayValues, ValueSource.Disk);
}

private static bool GetOverrideValue(string key, string[] cliArgs, [NotNullWhen(true)] out string? overrideValue, [NotNullWhen(true)] out ValueSource? valueSource)
{
// CLI arguments have higher precedence than environment variables.
Expand Down Expand Up @@ -412,6 +459,7 @@ private record IntValue(int Value, int EffectiveValue, ValueSource ValueSource)
private record StringValue(string Value, string EffectiveValue, ValueSource ValueSource) : ITypedValue<string>;
private record NullableStringValue(string? Value, string? EffectiveValue, ValueSource ValueSource) : ITypedValue<string?>;
private record StringArrayValue(string[] Value, string[] EffectiveValue, ValueSource ValueSource) : ITypedValue<string[]>;
private record LogModeArrayValue(LogMode[] Value, LogMode[] EffectiveValue, ValueSource ValueSource) : ITypedValue<LogMode[]>;
private record NetworkValue(Network Value, Network EffectiveValue, ValueSource ValueSource) : ITypedValue<Network>;
private record MoneyValue(Money Value, Money EffectiveValue, ValueSource ValueSource) : ITypedValue<Money>;
private record EndPointValue(EndPoint Value, EndPoint EffectiveValue, ValueSource ValueSource) : ITypedValue<EndPoint>;
Expand Down
5 changes: 4 additions & 1 deletion WalletWasabi.Daemon/Global.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,10 @@ public Global(string dataDir, string configFilePath, Config config)
Config.TerminateTorOnExit,
socksPort: config.TorSocksPort,
controlPort: config.TorControlPort,
owningProcessId: Environment.ProcessId);
torFolder: config.TorFolder,
bridges: config.TorBridges,
owningProcessId: Environment.ProcessId,
log: Config.LogModes.Contains(LogMode.File));

HostedServices = new HostedServices();
EventBus = new EventBus();
Expand Down
4 changes: 4 additions & 0 deletions WalletWasabi.Daemon/PersistentConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ public record PersistentConfig : IConfigNg
[JsonPropertyName("TerminateTorOnExit")]
public bool TerminateTorOnExit { get; init; } = false;

[DefaultValue(true)]
[JsonPropertyName("TorBridges")]
public string[] TorBridges { get; init; } = [];

[DefaultValue(true)]
[JsonPropertyName("DownloadNewVersion")]
public bool DownloadNewVersion { get; init; } = true;
Expand Down
161 changes: 0 additions & 161 deletions WalletWasabi.Daemon/WasabiAppBuilder.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,7 @@
using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using WalletWasabi.Bases;
using WalletWasabi.Extensions;
using WalletWasabi.Logging;
using WalletWasabi.Services;
using WalletWasabi.Services.Terminate;
using Constants = WalletWasabi.Helpers.Constants;

namespace WalletWasabi.Daemon;

Expand All @@ -18,161 +12,6 @@ public enum ExitCode
FailedAlreadyRunningError,
}

public class WasabiApplication
{
public WasabiAppBuilder AppConfig { get; }
public Global? Global { get; private set; }
public string ConfigFilePath { get; }
public Config Config { get; }
public SingleInstanceChecker SingleInstanceChecker { get; }
public TerminateService TerminateService { get; }

public WasabiApplication(WasabiAppBuilder wasabiAppBuilder)
{
AppConfig = wasabiAppBuilder;

ConfigFilePath = Path.Combine(Config.DataDir, "Config.json");
Directory.CreateDirectory(Config.DataDir);
Config = new Config(LoadOrCreateConfigs(), wasabiAppBuilder.Arguments);

SetupLogger();
Logger.LogDebug($"Wasabi was started with these argument(s): {string.Join(" ", AppConfig.Arguments.DefaultIfEmpty("none"))}.");
SingleInstanceChecker = new(Config.Network);
TerminateService = new(TerminateApplicationAsync, AppConfig.Terminate);
}

public async Task<ExitCode> RunAsync(Func<Task> afterStarting)
{
if (AppConfig.Arguments.Contains("--version"))
{
Console.WriteLine($"{AppConfig.AppName} {Constants.ClientVersion}");
return ExitCode.Ok;
}
if (AppConfig.Arguments.Contains("--help"))
{
ShowHelp();
return ExitCode.Ok;
}

if (AppConfig.MustCheckSingleInstance)
{
var instanceResult = await SingleInstanceChecker.CheckSingleInstanceAsync();
if (instanceResult == WasabiInstanceStatus.AnotherInstanceIsRunning)
{
Logger.LogDebug("Wasabi is already running, signaled the first instance.");
return ExitCode.FailedAlreadyRunningSignaled;
}
if (instanceResult == WasabiInstanceStatus.Error)
{
Logger.LogCritical($"Wasabi is already running, but cannot be signaled");
return ExitCode.FailedAlreadyRunningError;
}
}

try
{
TerminateService.Activate();

BeforeStarting();

await afterStarting();
return ExitCode.Ok;
}
finally
{
BeforeStopping();
}
}

private void BeforeStarting()
{
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException;

Logger.LogSoftwareStarted(AppConfig.AppName);

Global = CreateGlobal();
}

private void BeforeStopping()
{
AppDomain.CurrentDomain.UnhandledException -= CurrentDomain_UnhandledException;
TaskScheduler.UnobservedTaskException -= TaskScheduler_UnobservedTaskException;

// Start termination/disposal of the application.
TerminateService.Terminate();
SingleInstanceChecker.Dispose();
Logger.LogSoftwareStopped(AppConfig.AppName);
}

private Global CreateGlobal()
=> new(Config.DataDir, ConfigFilePath, Config);

private PersistentConfig LoadOrCreateConfigs()
{
PersistentConfig persistentConfig = ConfigManagerNg.LoadFile<PersistentConfig>(ConfigFilePath, createIfMissing: true);

if (persistentConfig.MigrateOldDefaultBackendUris(out PersistentConfig? newConfig))
{
persistentConfig = newConfig;
ConfigManagerNg.ToFile(ConfigFilePath, persistentConfig);
}

return persistentConfig;
}

private void TaskScheduler_UnobservedTaskException(object? sender, UnobservedTaskExceptionEventArgs e)
{
AppConfig.UnobservedTaskExceptionsEventHandler?.Invoke(this, e.Exception);
}

private void CurrentDomain_UnhandledException(object? sender, UnhandledExceptionEventArgs e)
{
if (e.ExceptionObject is Exception ex)
{
AppConfig.UnhandledExceptionEventHandler?.Invoke(this, ex);
}
}

private async Task TerminateApplicationAsync()
{
Logger.LogSoftwareStopped(AppConfig.AppName);

if (Global is { } global)
{
await global.DisposeAsync().ConfigureAwait(false);
}
}

private void SetupLogger()
{
LogLevel logLevel = Enum.TryParse(Config.LogLevel, ignoreCase: true, out LogLevel parsedLevel)
? parsedLevel
: LogLevel.Info;
Logger.InitializeDefaults(Path.Combine(Config.DataDir, "Logs.txt"), logLevel);
}

private void ShowHelp()
{
Console.WriteLine($"{AppConfig.AppName} {Constants.ClientVersion}");
Console.WriteLine($"Usage: {AppConfig.AppName} [OPTION]...");
Console.WriteLine();
Console.WriteLine("Available options are:");

foreach (var (parameter, hint) in Config.GetConfigOptionsMetadata().OrderBy(x => x.ParameterName))
{
Console.Write($" --{parameter.ToLower(),-30} ");
var hintLines = hint.SplitLines(lineWidth: 40);
Console.WriteLine(hintLines[0]);
foreach (var hintLine in hintLines.Skip(1))
{
Console.WriteLine($"{' ',-35}{hintLine}");
}
Console.WriteLine();
}
}
}

public record WasabiAppBuilder(string AppName, string[] Arguments)
{
internal bool MustCheckSingleInstance { get; init; }
Expand Down
Loading

0 comments on commit 260c70d

Please sign in to comment.