Fix and test timeout bug.
Dump version.
Fixes:
MODIFY
WalletWasabi.Gui/Global.cs
WalletWasabi.Gui/Global.cs
@@ -147,7 +147,7 @@ namespace WalletWasabi.Gui
ChaumianClient = new CcjClient(Network, BlindingPubKey, keyManager, Config.GetCurrentBackendUri());
WalletService = new WalletService(keyManager, IndexDownloader, ChaumianClient, MemPoolService, Nodes, DataDir);
- ChaumianClient.Start(7);
+ ChaumianClient.Start(0, 7);
Logger.LogInfo("Start Chaumian CoinJoin service...");
using (CancelWalletServiceInitialization = new CancellationTokenSource())

MODIFY
WalletWasabi.Tests/RegTests.cs
WalletWasabi.Tests/RegTests.cs
@@ -468,7 +468,7 @@ namespace WalletWasabi.Tests
nodes.Connect(); // Start connection service.
node.VersionHandshake(); // Start mempool service.
indexDownloader.Synchronize(requestInterval: TimeSpan.FromSeconds(3)); // Start index downloader service.
- chaumianClient.Start(0); // Start chaumian coinjoin client.
+ chaumianClient.Start(0, 0); // Start chaumian coinjoin client.
// Wait until the filter our previous transaction is present.
var blockCount = await rpc.GetBlockCountAsync();
@@ -706,7 +706,7 @@ namespace WalletWasabi.Tests
nodes.Connect(); // Start connection service.
node.VersionHandshake(); // Start mempool service.
indexDownloader.Synchronize(requestInterval: TimeSpan.FromSeconds(3)); // Start index downloader service.
- chaumianClient.Start(0); // Start chaumian coinjoin client.
+ chaumianClient.Start(0, 0); // Start chaumian coinjoin client.
// Wait until the filter our previous transaction is present.
var blockCount = await rpc.GetBlockCountAsync();
@@ -1200,7 +1200,7 @@ namespace WalletWasabi.Tests
nodes.Connect(); // Start connection service.
node.VersionHandshake(); // Start mempool service.
indexDownloader.Synchronize(requestInterval: TimeSpan.FromSeconds(3)); // Start index downloader service.
- chaumianClient.Start(0); // Start chaumian coinjoin client.
+ chaumianClient.Start(0, 0); // Start chaumian coinjoin client.
// Wait until the filter our previous transaction is present.
var blockCount = await rpc.GetBlockCountAsync();
@@ -1321,7 +1321,7 @@ namespace WalletWasabi.Tests
nodes.Connect(); // Start connection service.
node.VersionHandshake(); // Start mempool service.
indexDownloader.Synchronize(requestInterval: TimeSpan.FromSeconds(3)); // Start index downloader service.
- chaumianClient.Start(0); // Start chaumian coinjoin client.
+ chaumianClient.Start(0, 0); // Start chaumian coinjoin client.
// Wait until the filter our previous transaction is present.
var blockCount = await rpc.GetBlockCountAsync();
@@ -1486,7 +1486,7 @@ namespace WalletWasabi.Tests
nodes.Connect(); // Start connection service.
node.VersionHandshake(); // Start mempool service.
indexDownloader.Synchronize(requestInterval: TimeSpan.FromSeconds(3)); // Start index downloader service.
- chaumianClient.Start(0); // Start chaumian coinjoin client.
+ chaumianClient.Start(0, 0); // Start chaumian coinjoin client.
// Wait until the filter our previous transaction is present.
var blockCount = await rpc.GetBlockCountAsync();
@@ -2511,8 +2511,8 @@ namespace WalletWasabi.Tests
var chaumianClient2 = new CcjClient(rpc.Network, coordinator.RsaKey.PubKey, keyManager, new Uri(RegTestFixture.BackendEndPoint));
try
{
- chaumianClient1.Start(0);
- chaumianClient2.Start(0);
+ chaumianClient1.Start(2, 2); // Exactly delay it for 2 seconds, this will make sure of timeout later.
+ chaumianClient2.Start(0, 0);
smartCoin1.CoinJoinInProcess = true;
Assert.True(!(await chaumianClient1.QueueCoinsToMixAsync(password, smartCoin1)).Any());
@@ -2569,10 +2569,24 @@ namespace WalletWasabi.Tests
roundConfig = new CcjRoundConfig(denomination, 140, coordinatorFeePercent, anonymitySet, 240, connectionConfirmationTimeout, 50, 50, 1);
coordinator.UpdateRoundConfig(roundConfig);
coordinator.FailAllRoundsInInputRegistration();
+ Assert.NotEmpty(chaumianClient1.State.GetAllCoins());
+ await chaumianClient1.DequeueCoinsFromMixAsync(chaumianClient1.State.GetAllCoins().ToArray());
+ Assert.Empty(chaumianClient1.State.GetAllCoins());
await chaumianClient1.QueueCoinsToMixAsync(password, smartCoin4);
Assert.NotEmpty(chaumianClient1.State.GetAllCoins());
- await Task.Delay(10000);
- //Assert.Empty(chaumianClient1.State.GetAllCoins());
+ Assert.NotEmpty(chaumianClient1.State.GetAllWaitingCoins());
+ Assert.Empty(chaumianClient1.State.GetAllRegisteredCoins());
+ while (chaumianClient1.State.GetAllWaitingCoins().Any())
+ {
+ await Task.Delay(1000);
+ }
+ Assert.NotEmpty(chaumianClient1.State.GetAllCoins());
+ Assert.Empty(chaumianClient1.State.GetAllWaitingCoins());
+ Assert.NotEmpty(chaumianClient1.State.GetAllRegisteredCoins());
+ await Task.Delay(3000); // Make sure to wait until times out.
+ Assert.NotEmpty(chaumianClient1.State.GetAllCoins());
+ Assert.NotEmpty(chaumianClient1.State.GetAllWaitingCoins());
+ Assert.Empty(chaumianClient1.State.GetAllRegisteredCoins());
}
finally
{
@@ -2635,8 +2649,8 @@ namespace WalletWasabi.Tests
var chaumianClient2 = new CcjClient(rpc.Network, coordinator.RsaKey.PubKey, keyManager, new Uri(RegTestFixture.BackendEndPoint));
try
{
- chaumianClient1.Start(0);
- chaumianClient2.Start(0);
+ chaumianClient1.Start(0, 0);
+ chaumianClient2.Start(0, 0);
var customChange1 = new Key().PubKey.GetAddress(network);
var customActive1 = new Key().PubKey.GetAddress(network);
@@ -2769,11 +2783,11 @@ namespace WalletWasabi.Tests
nodes.Connect(); // Start connection service.
node.VersionHandshake(); // Start mempool service.
indexDownloader.Synchronize(requestInterval: TimeSpan.FromSeconds(3)); // Start index downloader service.
- chaumianClient.Start(0); // Start chaumian coinjoin client.
+ chaumianClient.Start(0, 0); // Start chaumian coinjoin client.
nodes2.Connect(); // Start connection service.
node2.VersionHandshake(); // Start mempool service.
indexDownloader2.Synchronize(requestInterval: TimeSpan.FromSeconds(3)); // Start index downloader service.
- chaumianClient2.Start(0); // Start chaumian coinjoin client.
+ chaumianClient2.Start(0, 0); // Start chaumian coinjoin client.
// Wait until the filter our previous transaction is present.
var blockCount = await rpc.GetBlockCountAsync();

MODIFY
WalletWasabi/Helpers/Constants.cs
WalletWasabi/Helpers/Constants.cs
@@ -7,7 +7,7 @@ namespace WalletWasabi.Helpers
{
public static class Constants
{
- public static readonly Version ClientVersion = new Version(0, 7, 2);
+ public static readonly Version ClientVersion = new Version(0, 7, 3);
public const string BackendMajorVersion = "1";
public static readonly VersionsResponse VersionsResponse = new VersionsResponse { ClientVersion = ClientVersion.ToString(), BackenMajordVersion = BackendMajorVersion };

MODIFY
WalletWasabi/Models/ChaumianCoinJoin/CcjClientState.cs
WalletWasabi/Models/ChaumianCoinJoin/CcjClientState.cs
@@ -103,6 +103,22 @@ namespace WalletWasabi.Models.ChaumianCoinJoin
}
}
+ public IEnumerable<(uint256 txid, uint index)> GetAllWaitingCoins()
+ {
+ lock (StateLock)
+ {
+ return WaitingList.Select(x => (x.TransactionId, x.Index)).ToArray();
+ }
+ }
+
+ public IEnumerable<(uint256 txid, uint index)> GetAllRegisteredCoins()
+ {
+ lock (StateLock)
+ {
+ return Rounds.SelectMany(x => x.CoinsRegistered).Select(x => (x.TransactionId, x.Index)).ToArray();
+ }
+ }
+
public IEnumerable<(uint256 txid, uint index)> GetRegistrableCoins(int maximumInputCountPerPeer, Money denomination, Money feePerInputs, Money feePerOutputs)
{
lock (StateLock)

MODIFY
WalletWasabi/Services/CcjClient.cs
WalletWasabi/Services/CcjClient.cs
@@ -72,7 +72,7 @@ namespace WalletWasabi.Services
CustomActiveAddressesLock = new object();
}
- public void Start(int maxDelayReplySeconds)
+ public void Start(int minDelayReplySeconds, int maxDelayReplySeconds)
{
Interlocked.Exchange(ref _running, 1);
@@ -90,7 +90,7 @@ namespace WalletWasabi.Services
{
try
{
- await ProcessStatusAsync(maxDelayReplySeconds);
+ await ProcessStatusAsync(minDelayReplySeconds, maxDelayReplySeconds);
Logger.LogInfo<CcjClient>("CcjClient is successfully initialized.");
@@ -125,7 +125,7 @@ namespace WalletWasabi.Services
}
await Task.Delay(TimeSpan.FromSeconds(delay), Cancel.Token);
- await ProcessStatusAsync(maxDelayReplySeconds);
+ await ProcessStatusAsync(minDelayReplySeconds, maxDelayReplySeconds);
}
catch (TaskCanceledException ex)
{
@@ -147,7 +147,7 @@ namespace WalletWasabi.Services
});
}
- private async Task ProcessStatusAsync(int maxDelayReplySeconds)
+ private async Task ProcessStatusAsync(int minDelayReplySeconds, int maxDelayReplySeconds)
{
try
{
@@ -160,13 +160,17 @@ namespace WalletWasabi.Services
states = await SatoshiClient.GetAllRoundStatesAsync();
State.UpdateRoundsByStates(states.ToArray());
StateUpdated?.Invoke(this, null);
- if (maxDelayReplySeconds <= 0)
+ if (maxDelayReplySeconds == minDelayReplySeconds)
+ {
+ delay = minDelayReplySeconds;
+ }
+ if (maxDelayReplySeconds < minDelayReplySeconds || maxDelayReplySeconds <= 0)
{
delay = 0;
}
else
{
- delay = new Random().Next(0, maxDelayReplySeconds); // delay the response to defend timing attack privacy
+ delay = new Random().Next(minDelayReplySeconds, maxDelayReplySeconds); // delay the response to defend timing attack privacy
}
}
@@ -570,6 +574,16 @@ namespace WalletWasabi.Services
}
}
+ internal async Task DequeueCoinsFromMixAsync(params (uint256 txid, uint index)[] coins)
+ {
+ using (await MixLock.LockAsync())
+ {
+ await DequeueCoinsFromMixNoLockAsync(State.GetSpentCoins().ToArray());
+
+ await DequeueCoinsFromMixNoLockAsync(coins);
+ }
+ }
+
private async Task DequeueCoinsFromMixNoLockAsync(params (uint256 txid, uint index)[] coins)
{
List<Exception> exceptions = new List<Exception>();

- Fix and test timeout bug.
- Dump version.
Fixes: https://github.com/zkSNACKs/WalletWasabi/issues/567