Add IConfig, progress Coordinator
MODIFY
WalletWasabi.Backend/CcjRoundConfig.cs
WalletWasabi.Backend/CcjRoundConfig.cs
@@ -9,13 +9,17 @@ using System.Text;
using System.Threading.Tasks;
using WalletWasabi.Converters;
using WalletWasabi.Helpers;
+using WalletWasabi.Interfaces;
using WalletWasabi.Logging;
namespace WalletWasabi.Backend
{
[JsonObject(MemberSerialization.OptIn)]
- public class CcjRoundConfig
+ public class CcjRoundConfig : IConfig
{
+ /// <inheritdoc />
+ public string FilePath { get; private set; }
+
[JsonProperty(PropertyName = "Denomination")]
[JsonConverter(typeof(MoneyConverter))]
public Money Denomination { get; private set; }
@@ -29,45 +33,49 @@ namespace WalletWasabi.Backend
[JsonProperty(PropertyName = "AnonymitySet")]
public int? AnonymitySet { get; private set; }
- public CcjRoundConfig()
+ public CcjRoundConfig(string filePath)
{
-
+ SetFilePath(filePath);
}
+ [JsonConstructor]
public CcjRoundConfig(Money denomination, int? confirmationTarget, decimal? coordinatorFeePercent, int? anonymitySet)
{
+ FilePath = null;
Denomination = Guard.NotNull(nameof(denomination), denomination);
ConfirmationTarget = Guard.NotNull(nameof(confirmationTarget), confirmationTarget);
CoordinatorFeePercent = Guard.NotNull(nameof(coordinatorFeePercent), coordinatorFeePercent);
AnonymitySet = Guard.NotNull(nameof(anonymitySet), anonymitySet);
}
- public async Task ToFileAsync(string path)
+ /// <inheritdoc />
+ public async Task ToFileAsync()
{
- if (string.IsNullOrWhiteSpace(path)) throw new ArgumentException(nameof(path));
+ AssertFilePathSet();
string jsonString = JsonConvert.SerializeObject(this, Formatting.Indented);
- await File.WriteAllTextAsync(path,
+ await File.WriteAllTextAsync(FilePath,
jsonString,
Encoding.UTF8);
}
- public async Task LoadOrCreateDefaultFileAsync(string path)
+ /// <inheritdoc />
+ public async Task LoadOrCreateDefaultFileAsync()
{
- if (string.IsNullOrWhiteSpace(path)) throw new ArgumentException(nameof(path));
+ AssertFilePathSet();
Denomination = new Money(0.1m, MoneyUnit.BTC);
ConfirmationTarget = 144; // 1 day
CoordinatorFeePercent = 0.1m;
AnonymitySet = 100;
- if (!File.Exists(path))
+ if (!File.Exists(FilePath))
{
- Logger.LogInfo<CcjRoundConfig>($"{nameof(CcjRoundConfig)} file did not exist. Created at path: `{path}`.");
+ Logger.LogInfo<CcjRoundConfig>($"{nameof(CcjRoundConfig)} file did not exist. Created at path: `{FilePath}`.");
}
else
{
- string jsonString = await File.ReadAllTextAsync(path, Encoding.UTF8);
+ string jsonString = await File.ReadAllTextAsync(FilePath, Encoding.UTF8);
var config = JsonConvert.DeserializeObject<CcjRoundConfig>(jsonString);
Denomination = config.Denomination ?? Denomination;
@@ -76,17 +84,20 @@ namespace WalletWasabi.Backend
AnonymitySet = config.AnonymitySet ?? AnonymitySet;
}
- await ToFileAsync(path);
+ await ToFileAsync();
}
- public async Task<bool> CheckFileChangeAsync(string path)
+ /// <inheritdoc />
+ public async Task<bool> CheckFileChangeAsync()
{
- if (!File.Exists(path))
+ AssertFilePathSet();
+
+ if (!File.Exists(FilePath))
{
- throw new FileNotFoundException($"{nameof(CcjRoundConfig)} file did not exist at path: `{path}`.");
+ throw new FileNotFoundException($"{nameof(CcjRoundConfig)} file did not exist at path: `{FilePath}`.");
}
- string jsonString = await File.ReadAllTextAsync(path, Encoding.UTF8);
+ string jsonString = await File.ReadAllTextAsync(FilePath, Encoding.UTF8);
var config = JsonConvert.DeserializeObject<CcjRoundConfig>(jsonString);
if (Denomination != config.Denomination)
@@ -108,5 +119,17 @@ namespace WalletWasabi.Backend
return false;
}
+
+ /// <inheritdoc />
+ public void SetFilePath(string path)
+ {
+ FilePath = Guard.NotNullOrEmptyOrWhitespace(nameof(path), path, trim: true);
+ }
+
+ /// <inheritdoc />
+ public void AssertFilePathSet()
+ {
+ if (FilePath == null) throw new NotSupportedException($"{nameof(FilePath)} is not set. Use {nameof(SetFilePath)} to set it.");
+ }
}
}

MODIFY
WalletWasabi.Backend/CcjRoundConfigWatcher.cs
WalletWasabi.Backend/CcjRoundConfigWatcher.cs
@@ -18,20 +18,19 @@ namespace WalletWasabi.Backend
public bool IsRunning => Interlocked.Read(ref _running) == 1;
public bool IsStopping => Interlocked.Read(ref _running) == 2;
- private CancellationTokenSource StopWaiting { get; }
+ private CancellationTokenSource Stop { get; }
public CcjRoundConfig RoundConfig { get; }
- public string RoundConfigFilePath { get; }
public CcjCoordinator Coordinator { get; }
- public CcjRoundConfigWatcher(CcjRoundConfig roundConfig, string roundConfigFilePath, CcjCoordinator coordinator)
+ public CcjRoundConfigWatcher(CcjRoundConfig roundConfig, CcjCoordinator coordinator)
{
RoundConfig = Guard.NotNull(nameof(roundConfig), roundConfig);
+ roundConfig.AssertFilePathSet();
Coordinator = Guard.NotNull(nameof(coordinator), coordinator);
- RoundConfigFilePath = Guard.NotNull(nameof(roundConfigFilePath), roundConfigFilePath);
_running = 0;
- StopWaiting = new CancellationTokenSource();
+ Stop = new CancellationTokenSource();
}
public void Start(TimeSpan period)
@@ -49,13 +48,13 @@ namespace WalletWasabi.Backend
// If stop was requested return.
if (IsRunning == false) return;
- await Task.Delay(period, StopWaiting.Token);
+ await Task.Delay(period, Stop.Token);
- if (await RoundConfig.CheckFileChangeAsync(RoundConfigFilePath))
+ if (await RoundConfig.CheckFileChangeAsync())
{
- Coordinator.FailRoundsInInputRegistration();
+ Coordinator.FailAllRoundsInInputRegistration();
- await RoundConfig.LoadOrCreateDefaultFileAsync(RoundConfigFilePath);
+ await RoundConfig.LoadOrCreateDefaultFileAsync();
await Coordinator.StartNewRoundAsync(Global.RpcClient, RoundConfig.Denomination, (int)RoundConfig.ConfirmationTarget, (decimal) RoundConfig.CoordinatorFeePercent, (int)RoundConfig.AnonymitySet);
}
@@ -86,12 +85,12 @@ namespace WalletWasabi.Backend
{
Interlocked.Exchange(ref _running, 2);
}
- StopWaiting?.Cancel();
+ Stop?.Cancel();
while (IsStopping)
{
await Task.Delay(50);
}
- StopWaiting?.Dispose();
+ Stop?.Dispose();
}
}
}

MODIFY
WalletWasabi.Backend/Config.cs
WalletWasabi.Backend/Config.cs
@@ -9,12 +9,16 @@ using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
+using WalletWasabi.Interfaces;
namespace WalletWasabi.Backend
{
[JsonObject(MemberSerialization.OptIn)]
- public class Config
+ public class Config : IConfig
{
+ /// <inheritdoc />
+ public string FilePath { get; private set; }
+
[JsonProperty(PropertyName = "Network")]
[JsonConverter(typeof(NetworkConverter))]
public Network Network { get; private set; }
@@ -25,43 +29,46 @@ namespace WalletWasabi.Backend
[JsonProperty(PropertyName = "BitcoinRpcPassword")]
public string BitcoinRpcPassword { get; private set; }
- public Config()
+ public Config(string filePath)
{
-
+ SetFilePath(filePath);
}
- public Config(Network network, string rpcuser, string rpcpassword)
+ [JsonConstructor]
+ public Config(Network network, string bitcoinRpcUser, string bitcoinRpcPassword)
{
Network = Guard.NotNull(nameof(network), network);
- BitcoinRpcUser = Guard.NotNullOrEmptyOrWhitespace(nameof(rpcuser), rpcuser);
- BitcoinRpcPassword = Guard.NotNull(nameof(rpcpassword), rpcpassword);
+ BitcoinRpcUser = Guard.NotNullOrEmptyOrWhitespace(nameof(bitcoinRpcUser), bitcoinRpcUser);
+ BitcoinRpcPassword = Guard.NotNull(nameof(bitcoinRpcPassword), bitcoinRpcPassword);
}
- public async Task ToFileAsync(string path)
+ /// <inheritdoc />
+ public async Task ToFileAsync()
{
- if (string.IsNullOrWhiteSpace(path)) throw new ArgumentException(nameof(path));
+ AssertFilePathSet();
string jsonString = JsonConvert.SerializeObject(this, Formatting.Indented);
- await File.WriteAllTextAsync(path,
+ await File.WriteAllTextAsync(FilePath,
jsonString,
Encoding.UTF8);
}
- public async Task LoadOrCreateDefaultFileAsync(string path)
+ /// <inheritdoc />
+ public async Task LoadOrCreateDefaultFileAsync()
{
- if (string.IsNullOrWhiteSpace(path)) throw new ArgumentException(nameof(path));
+ AssertFilePathSet();
Network = Network.Main;
BitcoinRpcUser = "user";
BitcoinRpcPassword = "password";
- if (!File.Exists(path))
+ if (!File.Exists(FilePath))
{
- Logger.LogInfo<Config>($"{nameof(Config)} file did not exist. Created at path: `{path}`.");
+ Logger.LogInfo<Config>($"{nameof(Config)} file did not exist. Created at path: `{FilePath}`.");
}
else
{
- string jsonString = await File.ReadAllTextAsync(path, Encoding.UTF8);
+ string jsonString = await File.ReadAllTextAsync(FilePath, Encoding.UTF8);
var config = JsonConvert.DeserializeObject<Config>(jsonString);
Network = config.Network ?? Network;
@@ -69,7 +76,48 @@ namespace WalletWasabi.Backend
BitcoinRpcPassword = config.BitcoinRpcPassword ?? BitcoinRpcPassword;
}
- await ToFileAsync(path);
+ await ToFileAsync();
+ }
+
+ /// <inheritdoc />
+ public async Task<bool> CheckFileChangeAsync()
+ {
+ AssertFilePathSet();
+
+ if (!File.Exists(FilePath))
+ {
+ throw new FileNotFoundException($"{nameof(Config)} file did not exist at path: `{FilePath}`.");
+ }
+
+ string jsonString = await File.ReadAllTextAsync(FilePath, Encoding.UTF8);
+ var config = JsonConvert.DeserializeObject<Config>(jsonString);
+
+ if (Network != config.Network)
+ {
+ return true;
+ }
+ if (BitcoinRpcPassword != config.BitcoinRpcPassword)
+ {
+ return true;
+ }
+ if (BitcoinRpcUser != config.BitcoinRpcUser)
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+ /// <inheritdoc />
+ public void SetFilePath(string path)
+ {
+ FilePath = Guard.NotNullOrEmptyOrWhitespace(nameof(path), path, trim: true);
+ }
+
+ /// <inheritdoc />
+ public void AssertFilePathSet()
+ {
+ if (FilePath == null) throw new NotSupportedException($"{nameof(FilePath)} is not set. Use {nameof(SetFilePath)} to set it.");
}
}
}

MODIFY
WalletWasabi.Backend/Global.cs
WalletWasabi.Backend/Global.cs
@@ -29,10 +29,6 @@ namespace WalletWasabi.Backend
}
}
- public static string ConfigFilePath { get; private set; }
-
- public static string RoundConfigFilePath { get; private set; }
-
public static RPCClient RpcClient { get; private set; }
public static BlindingRsaKey RsaKey { get; private set; }
@@ -51,21 +47,22 @@ namespace WalletWasabi.Backend
{
_dataDir = null;
- ConfigFilePath = Path.Combine(DataDir, "Config.json");
+ var configFilePath = Path.Combine(DataDir, "Config.json");
// Initialize Config
if (network != null || rpcuser != null || rpcpassword != null)
{
Config = new Config(network, rpcuser, rpcpassword);
+ Config.SetFilePath(configFilePath);
}
else
{
- Config = new Config();
- await Config.LoadOrCreateDefaultFileAsync(ConfigFilePath);
+ Config = new Config(configFilePath);
+ await Config.LoadOrCreateDefaultFileAsync();
}
- RoundConfigFilePath = Path.Combine(DataDir, "CcjRoundConfig.json");
- RoundConfig = new CcjRoundConfig();
- await RoundConfig.LoadOrCreateDefaultFileAsync(RoundConfigFilePath);
+ var roundConfigFilePath = Path.Combine(DataDir, "CcjRoundConfig.json");
+ RoundConfig = new CcjRoundConfig(roundConfigFilePath);
+ await RoundConfig.LoadOrCreateDefaultFileAsync();
// Initialize RsaKey
string rsaKeyPath = Path.Combine(DataDir, "RsaKey.json");
@@ -107,7 +104,7 @@ namespace WalletWasabi.Backend
Coordinator = new CcjCoordinator();
await Coordinator.StartNewRoundAsync(RpcClient, RoundConfig.Denomination, (int)RoundConfig.ConfirmationTarget, (decimal)RoundConfig.CoordinatorFeePercent, (int)RoundConfig.AnonymitySet);
- RoundConfigWatcher = new CcjRoundConfigWatcher(RoundConfig, RoundConfigFilePath, Coordinator);
+ RoundConfigWatcher = new CcjRoundConfigWatcher(RoundConfig, Coordinator);
RoundConfigWatcher.Start(TimeSpan.FromSeconds(10)); // Every 10 seconds check the config
}
@@ -163,7 +160,7 @@ namespace WalletWasabi.Backend
}
catch(WebException)
{
- Logger.LogInfo($"Bitcoin Core is not running, or incorrect RPC credentials or network is given in the config file: `{ConfigFilePath}`.");
+ Logger.LogInfo($"Bitcoin Core is not running, or incorrect RPC credentials or network is given in the config file: `{Config.FilePath}`.");
throw;
}
}

MODIFY
WalletWasabi/ChaumianCoinJoin/CcjRound.cs
WalletWasabi/ChaumianCoinJoin/CcjRound.cs
@@ -65,7 +65,8 @@ namespace WalletWasabi.ChaumianCoinJoin
BobsLock = new AsyncLock();
}
- public async Task ExecuteNextPhaseAsync()
+ /// <returns>The next phase or null, if the round is not running.</returns>
+ public async Task<CcjRoundPhase?> ExecuteNextPhaseAsync()
{
using (await PhaseExecutionLock.LockAsync())
using (await AlicesLock.LockAsync())
@@ -105,20 +106,23 @@ namespace WalletWasabi.ChaumianCoinJoin
}
Status = CcjRoundStatus.Running;
+ return CcjRoundPhase.InputRegistration;
}
else if (Status != CcjRoundStatus.Running) // Failed or succeeded, swallow
{
- return;
+ return null;
}
else if (Phase == CcjRoundPhase.InputRegistration)
{
RoundHash = NBitcoinHelpers.HashOutpoints(Alices.SelectMany(x => x.Inputs).Select(y => y.Key));
Phase = CcjRoundPhase.ConnectionConfirmation;
+ return CcjRoundPhase.ConnectionConfirmation;
}
else if (Phase == CcjRoundPhase.ConnectionConfirmation)
{
Phase = CcjRoundPhase.OutputRegistration;
+ return CcjRoundPhase.OutputRegistration;
}
else if (Phase == CcjRoundPhase.OutputRegistration)
{
@@ -159,6 +163,7 @@ namespace WalletWasabi.ChaumianCoinJoin
.BuildTransaction(false);
Phase = CcjRoundPhase.Signing;
+ return CcjRoundPhase.Signing;
}
else
{

MODIFY
WalletWasabi/Services/CcjCoordinator.cs
WalletWasabi/Services/CcjCoordinator.cs
@@ -38,7 +38,7 @@ namespace WalletWasabi.Services
}
}
- public void FailRoundsInInputRegistration()
+ public void FailAllRoundsInInputRegistration()
{
using (RoundsListLock.Lock())
{

Add IConfig, progress Coordinator