Add states to sync
MODIFY
WalletWasabi.Gui/Global.cs
WalletWasabi.Gui/Global.cs
@@ -244,10 +244,10 @@ namespace WalletWasabi.Gui
public static async Task InitializeWalletServiceAsync(KeyManager keyManager)
{
- ChaumianClient = new CcjClient(Network, BlindingPubKey, keyManager, Config.GetCurrentBackendUri(), Config.GetTorSocks5EndPoint());
+ ChaumianClient = new CcjClient(Synchronizer, Network, BlindingPubKey, keyManager, Config.GetCurrentBackendUri(), Config.GetTorSocks5EndPoint());
WalletService = new WalletService(keyManager, Synchronizer, ChaumianClient, MemPoolService, Nodes, DataDir);
- ChaumianClient.Start(0, 3);
+ ChaumianClient.Start();
Logger.LogInfo("Start Chaumian CoinJoin service...");
using (CancelWalletServiceInitialization = new CancellationTokenSource())

MODIFY
WalletWasabi.Tests/P2pTests.cs
WalletWasabi.Tests/P2pTests.cs
@@ -83,10 +83,11 @@ namespace WalletWasabi.Tests
var nodes = new NodesGroup(network, connectionParameters, requirements: Helpers.Constants.NodeRequirements);
KeyManager keyManager = KeyManager.CreateNew(out _, "password");
+ WasabiSynchronizer syncer = new WasabiSynchronizer(network, Path.Combine(SharedFixture.DataDir, nameof(TestServicesAsync), "IndexDownloader.txt"), new Uri("http://localhost:12345"));
WalletService walletService = new WalletService(
keyManager,
- new WasabiSynchronizer(network, Path.Combine(SharedFixture.DataDir, nameof(TestServicesAsync), "IndexDownloader.txt"), new Uri("http://localhost:12345")),
- new CcjClient(network, new BlindingRsaKey().PubKey, keyManager, new Uri("http://localhost:12345")),
+ syncer,
+ new CcjClient(syncer, network, new BlindingRsaKey().PubKey, keyManager, new Uri("http://localhost:12345")),
memPoolService,
nodes,
SharedFixture.DataDir);

MODIFY
WalletWasabi.Tests/RegTests.cs
WalletWasabi.Tests/RegTests.cs
@@ -504,7 +504,7 @@ namespace WalletWasabi.Tests
var keyManager = KeyManager.CreateNew(out _, password);
// 5. Create chaumian coinjoin client.
- var chaumianClient = new CcjClient(rpc.Network, coordinator.RsaKey.PubKey, keyManager, new Uri(RegTestFixture.BackendEndPoint));
+ var chaumianClient = new CcjClient(synchronizer, rpc.Network, coordinator.RsaKey.PubKey, keyManager, new Uri(RegTestFixture.BackendEndPoint));
// 5. Create wallet service.
var workDir = Path.Combine(SharedFixture.DataDir, nameof(WalletTestsAsync));
@@ -522,7 +522,7 @@ namespace WalletWasabi.Tests
nodes.Connect(); // Start connection service.
node.VersionHandshake(); // Start mempool service.
synchronizer.Start(requestInterval: TimeSpan.FromSeconds(3), TimeSpan.FromSeconds(5), 1000); // Start wasabi synchronizer service.
- chaumianClient.Start(0, 0); // Start chaumian coinjoin client.
+ chaumianClient.Start(); // Start chaumian coinjoin client.
// Wait until the filter our previous transaction is present.
var blockCount = await rpc.GetBlockCountAsync();
@@ -733,7 +733,7 @@ namespace WalletWasabi.Tests
var keyManager = KeyManager.CreateNew(out _, password);
// 5. Create chaumian coinjoin client.
- var chaumianClient = new CcjClient(rpc.Network, coordinator.RsaKey.PubKey, keyManager, new Uri(RegTestFixture.BackendEndPoint));
+ var chaumianClient = new CcjClient(synchronizer, rpc.Network, coordinator.RsaKey.PubKey, keyManager, new Uri(RegTestFixture.BackendEndPoint));
// 6. Create wallet service.
var workDir = Path.Combine(SharedFixture.DataDir, nameof(SendTestsFromHiddenWalletAsync));
@@ -755,7 +755,7 @@ namespace WalletWasabi.Tests
nodes.Connect(); // Start connection service.
node.VersionHandshake(); // Start mempool service.
synchronizer.Start(requestInterval: TimeSpan.FromSeconds(3), TimeSpan.FromSeconds(5), 10000); // Start wasabi synchronizer service.
- chaumianClient.Start(0, 0); // Start chaumian coinjoin client.
+ chaumianClient.Start(); // Start chaumian coinjoin client.
// Wait until the filter our previous transaction is present.
var blockCount = await rpc.GetBlockCountAsync();
@@ -1177,7 +1177,7 @@ namespace WalletWasabi.Tests
var keyManager = KeyManager.CreateNew(out _, password);
// 5. Create chaumian coinjoin client.
- var chaumianClient = new CcjClient(rpc.Network, coordinator.RsaKey.PubKey, keyManager, new Uri(RegTestFixture.BackendEndPoint));
+ var chaumianClient = new CcjClient(synchronizer, rpc.Network, coordinator.RsaKey.PubKey, keyManager, new Uri(RegTestFixture.BackendEndPoint));
// 6. Create wallet service.
var workDir = Path.Combine(SharedFixture.DataDir, nameof(SendTestsFromHiddenWalletAsync));
@@ -1251,7 +1251,7 @@ namespace WalletWasabi.Tests
nodes.Connect(); // Start connection service.
node.VersionHandshake(); // Start mempool service.
synchronizer.Start(requestInterval: TimeSpan.FromSeconds(3), TimeSpan.FromSeconds(5), 10000); // Start wasabi synchronizer service.
- chaumianClient.Start(0, 0); // Start chaumian coinjoin client.
+ chaumianClient.Start(); // Start chaumian coinjoin client.
// Wait until the filter our previous transaction is present.
var blockCount = await rpc.GetBlockCountAsync();
@@ -1341,7 +1341,7 @@ namespace WalletWasabi.Tests
var keyManager = KeyManager.CreateNew(out _, password);
// 5. Create chaumian coinjoin client.
- var chaumianClient = new CcjClient(rpc.Network, coordinator.RsaKey.PubKey, keyManager, new Uri(RegTestFixture.BackendEndPoint));
+ var chaumianClient = new CcjClient(synchronizer, rpc.Network, coordinator.RsaKey.PubKey, keyManager, new Uri(RegTestFixture.BackendEndPoint));
// 6. Create wallet service.
var workDir = Path.Combine(SharedFixture.DataDir, nameof(SendTestsFromHiddenWalletAsync));
@@ -1366,7 +1366,7 @@ namespace WalletWasabi.Tests
nodes.Connect(); // Start connection service.
node.VersionHandshake(); // Start mempool service.
synchronizer.Start(requestInterval: TimeSpan.FromSeconds(3), TimeSpan.FromSeconds(5), 10000); // Start wasabi synchronizer service.
- chaumianClient.Start(0, 0); // Start chaumian coinjoin client.
+ chaumianClient.Start(); // Start chaumian coinjoin client.
// Wait until the filter our previous transaction is present.
var blockCount = await rpc.GetBlockCountAsync();
@@ -1508,7 +1508,7 @@ namespace WalletWasabi.Tests
var keyManager = KeyManager.CreateNew(out _, password);
// 5. Create chaumian coinjoin client.
- var chaumianClient = new CcjClient(rpc.Network, coordinator.RsaKey.PubKey, keyManager, new Uri(RegTestFixture.BackendEndPoint));
+ var chaumianClient = new CcjClient(synchronizer, rpc.Network, coordinator.RsaKey.PubKey, keyManager, new Uri(RegTestFixture.BackendEndPoint));
// 6. Create wallet service.
var workDir = Path.Combine(SharedFixture.DataDir, nameof(SendTestsFromHiddenWalletAsync));
@@ -1525,7 +1525,7 @@ namespace WalletWasabi.Tests
nodes.Connect(); // Start connection service.
node.VersionHandshake(); // Start mempool service.
synchronizer.Start(requestInterval: TimeSpan.FromSeconds(3), TimeSpan.FromSeconds(5), 10000); // Start wasabi synchronizer service.
- chaumianClient.Start(0, 0); // Start chaumian coinjoin client.
+ chaumianClient.Start(); // Start chaumian coinjoin client.
// Wait until the filter our previous transaction is present.
var blockCount = await rpc.GetBlockCountAsync();
@@ -2654,6 +2654,10 @@ namespace WalletWasabi.Tests
{
(string password, RPCClient rpc, Network network, CcjCoordinator coordinator) = await InitializeTestEnvironmentAsync(1);
+ var indexFilePath = Path.Combine(SharedFixture.DataDir, nameof(CcjClientTestsAsync), $"Index{network}.dat");
+ var synchronizer = new WasabiSynchronizer(network, indexFilePath, new Uri(RegTestFixture.BackendEndPoint));
+ synchronizer.Start(requestInterval: TimeSpan.FromSeconds(3), TimeSpan.FromSeconds(5), 10000); // Start wasabi synchronizer service.
+
Money denomination = Money.Coins(0.1m);
decimal coordinatorFeePercent = 0.1m;
int anonymitySet = 2;
@@ -2699,12 +2703,12 @@ namespace WalletWasabi.Tests
var smartCoin3 = new SmartCoin(bech3Coin, tx3.Inputs.Select(x => new TxoRef(x.PrevOut)).ToArray(), height, rbf: false, mixin: tx3.GetMixin(bech3Coin.Outpoint.N));
var smartCoin4 = new SmartCoin(bech4Coin, tx4.Inputs.Select(x => new TxoRef(x.PrevOut)).ToArray(), height, rbf: false, mixin: tx4.GetMixin(bech4Coin.Outpoint.N));
- var chaumianClient1 = new CcjClient(rpc.Network, coordinator.RsaKey.PubKey, keyManager, new Uri(RegTestFixture.BackendEndPoint));
- var chaumianClient2 = new CcjClient(rpc.Network, coordinator.RsaKey.PubKey, keyManager, new Uri(RegTestFixture.BackendEndPoint));
+ var chaumianClient1 = new CcjClient(synchronizer, rpc.Network, coordinator.RsaKey.PubKey, keyManager, new Uri(RegTestFixture.BackendEndPoint));
+ var chaumianClient2 = new CcjClient(synchronizer, rpc.Network, coordinator.RsaKey.PubKey, keyManager, new Uri(RegTestFixture.BackendEndPoint));
try
{
- chaumianClient1.Start(2, 2); // Exactly delay it for 2 seconds, this will make sure of timeout later.
- chaumianClient2.Start(0, 0);
+ chaumianClient1.Start(); // Exactly delay it for 2 seconds, this will make sure of timeout later.
+ chaumianClient2.Start();
smartCoin1.CoinJoinInProgress = true;
Assert.True(!(await chaumianClient1.QueueCoinsToMixAsync(password, smartCoin1)).Any());
@@ -2798,6 +2802,10 @@ namespace WalletWasabi.Tests
{
(string password, RPCClient rpc, Network network, CcjCoordinator coordinator) = await InitializeTestEnvironmentAsync(1);
+ var indexFilePath = Path.Combine(SharedFixture.DataDir, nameof(CcjClientCustomOutputScriptTestsAsync), $"Index{network}.dat");
+ var synchronizer = new WasabiSynchronizer(network, indexFilePath, new Uri(RegTestFixture.BackendEndPoint));
+ synchronizer.Start(requestInterval: TimeSpan.FromSeconds(3), TimeSpan.FromSeconds(5), 10000); // Start wasabi synchronizer service.
+
Money denomination = Money.Coins(0.1m);
decimal coordinatorFeePercent = 0.1m;
int anonymitySet = 2;
@@ -2836,12 +2844,12 @@ namespace WalletWasabi.Tests
var smartCoin2 = new SmartCoin(bech2Coin, tx2.Inputs.Select(x => new TxoRef(x.PrevOut)).ToArray(), height, rbf: false, mixin: tx2.GetMixin(bech2Coin.Outpoint.N));
var smartCoin3 = new SmartCoin(bech3Coin, tx3.Inputs.Select(x => new TxoRef(x.PrevOut)).ToArray(), height, rbf: false, mixin: tx3.GetMixin(bech3Coin.Outpoint.N));
- var chaumianClient1 = new CcjClient(rpc.Network, coordinator.RsaKey.PubKey, keyManager, new Uri(RegTestFixture.BackendEndPoint));
- var chaumianClient2 = new CcjClient(rpc.Network, coordinator.RsaKey.PubKey, keyManager, new Uri(RegTestFixture.BackendEndPoint));
+ var chaumianClient1 = new CcjClient(synchronizer, rpc.Network, coordinator.RsaKey.PubKey, keyManager, new Uri(RegTestFixture.BackendEndPoint));
+ var chaumianClient2 = new CcjClient(synchronizer, rpc.Network, coordinator.RsaKey.PubKey, keyManager, new Uri(RegTestFixture.BackendEndPoint));
try
{
- chaumianClient1.Start(0, 0);
- chaumianClient2.Start(0, 0);
+ chaumianClient1.Start();
+ chaumianClient2.Start();
var customChange1 = new Key().PubKey.GetAddress(network);
var customActive1 = new Key().PubKey.GetAddress(network);
@@ -2932,9 +2940,9 @@ namespace WalletWasabi.Tests
var keyManager2 = KeyManager.CreateNew(out _, password);
// 5. Create chaumian coinjoin client.
- var chaumianClient = new CcjClient(network, coordinator.RsaKey.PubKey, keyManager, new Uri(RegTestFixture.BackendEndPoint));
+ var chaumianClient = new CcjClient(synchronizer, network, coordinator.RsaKey.PubKey, keyManager, new Uri(RegTestFixture.BackendEndPoint));
- var chaumianClient2 = new CcjClient(network, coordinator.RsaKey.PubKey, keyManager2, new Uri(RegTestFixture.BackendEndPoint));
+ var chaumianClient2 = new CcjClient(synchronizer, network, coordinator.RsaKey.PubKey, keyManager2, new Uri(RegTestFixture.BackendEndPoint));
// 6. Create wallet service.
var workDir = Path.Combine(SharedFixture.DataDir, nameof(CoinJoinMultipleRoundTestsAsync));
@@ -2963,11 +2971,11 @@ namespace WalletWasabi.Tests
nodes.Connect(); // Start connection service.
node.VersionHandshake(); // Start mempool service.
synchronizer.Start(requestInterval: TimeSpan.FromSeconds(3), TimeSpan.FromSeconds(5), 10000); // Start wasabi synchronizer service.
- chaumianClient.Start(0, 0); // Start chaumian coinjoin client.
+ chaumianClient.Start(); // Start chaumian coinjoin client.
nodes2.Connect(); // Start connection service.
node2.VersionHandshake(); // Start mempool service.
synchronizer2.Start(requestInterval: TimeSpan.FromSeconds(3), TimeSpan.FromSeconds(5), 10000); // Start wasabi synchronizer service.
- chaumianClient2.Start(0, 0); // Start chaumian coinjoin client.
+ chaumianClient2.Start(); // Start chaumian coinjoin client.
// Wait until the filter our previous transaction is present.
var blockCount = await rpc.GetBlockCountAsync();
@@ -3006,7 +3014,7 @@ namespace WalletWasabi.Tests
Assert.True(1 == (await chaumianClient.QueueCoinsToMixAsync(password, wallet.Coins.ToArray())).Count());
Assert.True(3 == (await chaumianClient2.QueueCoinsToMixAsync(password, wallet2.Coins.ToArray())).Count());
- Task timeout = Task.Delay(TimeSpan.FromSeconds(2 * (connectionConfirmationTimeout * 2 + 7 * 2 + 7 * 2 + 7 * 2)));
+ Task timeout = Task.Delay(TimeSpan.FromSeconds(2 * (1 + 11 + 7 + 3 * (3 + 7))));
while (wallet.Coins.Count != 7)
{
if (timeout.IsCompletedSuccessfully)

MODIFY
WalletWasabi/Services/CcjClient.cs
WalletWasabi/Services/CcjClient.cs
@@ -31,8 +31,8 @@ namespace WalletWasabi.Services
public List<BitcoinAddress> CustomActiveAddresses { get; }
private object CustomActiveAddressesLock { get; }
- public SatoshiClient SatoshiClient { get; }
public Uri CcjHostUri { get; }
+ public WasabiSynchronizer Synchronizer { get; }
private IPEndPoint TorSocks5EndPoint { get; }
private decimal? CoordinatorFeepercentToCheck { get; set; }
@@ -64,16 +64,16 @@ namespace WalletWasabi.Services
private CancellationTokenSource Cancel { get; }
- public CcjClient(Network network, BlindingRsaPubKey coordinatorPubKey, KeyManager keyManager, Uri ccjHostUri, IPEndPoint torSocks5EndPoint = null)
+ public CcjClient(WasabiSynchronizer synchronizer, Network network, BlindingRsaPubKey coordinatorPubKey, KeyManager keyManager, Uri ccjHostUri, IPEndPoint torSocks5EndPoint = null)
{
AccessCache = new ConcurrentDictionary<HdPubKey, DateTimeOffset>();
Network = Guard.NotNull(nameof(network), network);
CoordinatorPubKey = Guard.NotNull(nameof(coordinatorPubKey), coordinatorPubKey);
KeyManager = Guard.NotNull(nameof(keyManager), keyManager);
CcjHostUri = Guard.NotNull(nameof(ccjHostUri), ccjHostUri);
+ Synchronizer = Guard.NotNull(nameof(synchronizer), synchronizer);
TorSocks5EndPoint = torSocks5EndPoint;
CoordinatorFeepercentToCheck = null;
- SatoshiClient = new SatoshiClient(ccjHostUri, torSocks5EndPoint);
_running = 0;
Cancel = new CancellationTokenSource();
@@ -85,9 +85,18 @@ namespace WalletWasabi.Services
CustomActiveAddresses = new List<BitcoinAddress>();
CustomChangeAddressesLock = new object();
CustomActiveAddressesLock = new object();
+
+ Synchronizer.ResponseArrived += Synchronizer_ResponseArrivedAsync;
+ }
+
+ private async void Synchronizer_ResponseArrivedAsync(object sender, SynchronizeResponse e)
+ {
+ IEnumerable<CcjRunningRoundState> newRoundStates = e.CcjRoundStates;
+
+ await ProcessStatusAsync(newRoundStates);
}
- public void Start(int minDelayReplySeconds, int maxDelayReplySeconds)
+ public void Start()
{
Interlocked.Exchange(ref _running, 1);
@@ -105,15 +114,12 @@ namespace WalletWasabi.Services
{
try
{
- await ProcessStatusAsync(minDelayReplySeconds, maxDelayReplySeconds);
-
Logger.LogInfo<CcjClient>("CcjClient is successfully initialized.");
while (IsRunning)
{
try
{
- int delaySeconds;
using (await MixLock.LockAsync())
{
await DequeueCoinsFromMixNoLockAsync(State.GetSpentCoins().ToArray());
@@ -121,34 +127,36 @@ namespace WalletWasabi.Services
// If stop was requested return.
if (IsRunning == false) return;
- // if mixing >= connConf: delay = new Random().Next(1, 3);
+ // if mixing >= connConf
if (State.GetActivelyMixingRounds().Any())
{
- delaySeconds = new Random().Next(1, 3);
+ Synchronizer.MinRequestIntervalForMixing = TimeSpan.FromSeconds(3);
}
else if (Interlocked.Read(ref _frequentStatusProcessingIfNotMixing) == 1 || State.GetPassivelyMixingRounds().Any() || State.GetWaitingListCount() > 0)
{
- double rand = double.Parse($"0.{new Random().Next(1, 6)}"); // randomly between every 0.1 * connConfTimeout - 7 and 0.6 * connConfTimeout
- delaySeconds = Math.Max(0, (int)(rand * State.GetSmallestRegistrationTimeout() - 7));
+ Synchronizer.MinRequestIntervalForMixing = TimeSpan.FromSeconds(11);
}
else
{
// dormant
- await Task.Delay(1000); // dormant
- continue;
+ Synchronizer.MinRequestIntervalForMixing = TimeSpan.FromMinutes(3);
}
}
-
- await Task.Delay(TimeSpan.FromSeconds(delaySeconds), Cancel.Token);
- await ProcessStatusAsync(minDelayReplySeconds, maxDelayReplySeconds);
+ }
+ catch (Exception ex)
+ {
+ Logger.LogError<CcjClient>(ex);
+ }
+ finally
+ {
+ try
+ {
+ await Task.Delay(1000, Cancel.Token);
}
catch (TaskCanceledException ex)
{
Logger.LogTrace<CcjClient>(ex);
}
- catch (Exception ex)
- {
- Logger.LogError<CcjClient>(ex);
}
}
}
@@ -162,17 +170,14 @@ namespace WalletWasabi.Services
});
}
- private async Task ProcessStatusAsync(int minDelayReplySeconds, int maxDelayReplySeconds)
+ private async Task ProcessStatusAsync(IEnumerable<CcjRunningRoundState> states)
{
try
{
- IEnumerable<CcjRunningRoundState> states;
- int delay;
using (await MixLock.LockAsync())
{
await DequeueCoinsFromMixNoLockAsync(State.GetSpentCoins().ToArray());
- states = await SatoshiClient.GetAllRoundStatesAsync();
State.UpdateRoundsByStates(states.ToArray());
// If we don't have enough coin queued to register a round, then dequeue all.
@@ -192,22 +197,10 @@ namespace WalletWasabi.Services
await DequeueAllCoinsFromMixNoLockAsync();
}
}
-
- if (maxDelayReplySeconds == minDelayReplySeconds)
- {
- delay = minDelayReplySeconds;
- }
- if (maxDelayReplySeconds < minDelayReplySeconds || maxDelayReplySeconds <= 0)
- {
- delay = 0;
- }
- else
- {
- delay = new Random().Next(minDelayReplySeconds, maxDelayReplySeconds); // delay the response to defend timing attack privacy
- }
}
StateUpdated?.Invoke(this, null);
+ int delay = new Random().Next(0, 7); // delay the response to defend timing attack privacy.
await Task.Delay(TimeSpan.FromSeconds(delay), Cancel.Token);
using (await MixLock.LockAsync())
@@ -883,6 +876,8 @@ namespace WalletWasabi.Services
public async Task StopAsync()
{
+ Synchronizer.ResponseArrived -= Synchronizer_ResponseArrivedAsync;
+
if (IsRunning)
{
Interlocked.Exchange(ref _running, 2);
@@ -899,7 +894,6 @@ namespace WalletWasabi.Services
{
await DequeueCoinsFromMixNoLockAsync(State.GetSpentCoins().ToArray());
- SatoshiClient?.Dispose();
State.DisposeAllAliceClients();
IEnumerable<(uint256 txid, uint index)> allCoins = State.GetAllQueuedCoins();

MODIFY
WalletWasabi/Services/WasabiSynchronizer.cs
WalletWasabi/Services/WasabiSynchronizer.cs
@@ -92,6 +92,8 @@ namespace WalletWasabi.Services
}
}
+ public TimeSpan MinRequestIntervalForMixing { get; set; }
+
public string IndexFilePath { get; private set; }
private List<FilterModel> Index { get; set; }
private object IndexLock { get; set; }
@@ -104,6 +106,8 @@ namespace WalletWasabi.Services
public event EventHandler<bool> ResponseArrivedIsGenSocksServFail;
+ public event EventHandler<SynchronizeResponse> ResponseArrived;
+
/// <summary>
/// 0: Not started, 1: Running, 2: Stopping, 3: Stopped
/// </summary>
@@ -187,6 +191,8 @@ namespace WalletWasabi.Services
Guard.MinimumAndNotNull(nameof(feeQueryRequestInterval), feeQueryRequestInterval, requestInterval);
Guard.MinimumAndNotNull(nameof(maxFiltersToSyncAtInitialization), maxFiltersToSyncAtInitialization, 0);
+ MinRequestIntervalForMixing = requestInterval; // Let's start with this, it'll be modified from outside.
+
Interlocked.Exchange(ref _running, 1);
Task.Run(async () =>
@@ -320,6 +326,8 @@ namespace WalletWasabi.Services
{
// We are syced.
}
+
+ ResponseArrived?.Invoke(this, response);
}
catch (Exception ex)
{
@@ -350,7 +358,8 @@ namespace WalletWasabi.Services
{
try
{
- await Task.Delay(requestInterval, Cancel.Token); // Ask for new index in every requestInterval.
+ int delay = (int)Math.Min(requestInterval.TotalMilliseconds, MinRequestIntervalForMixing.TotalMilliseconds);
+ await Task.Delay(delay, Cancel.Token); // Ask for new index in every requestInterval.
}
catch (TaskCanceledException ex)
{

Add states to sync