Refine spentcoin check
MODIFY
WalletWasabi.Tests/RegTests.cs
WalletWasabi.Tests/RegTests.cs
@@ -2284,28 +2284,28 @@ namespace WalletWasabi.Tests
chaumianClient2.Start();
smartCoin1.Locked = true;
- Assert.True(0 == chaumianClient1.QueueCoinsToMix(password, smartCoin1).Count());
+ Assert.True(0 == (await chaumianClient1.QueueCoinsToMixAsync(password, smartCoin1)).Count());
Assert.True(smartCoin1.Locked);
- Assert.Throws<SecurityException>(() => chaumianClient1.QueueCoinsToMix("asdasdasd", smartCoin1, smartCoin2));
+ await Assert.ThrowsAsync<SecurityException>(async () => await chaumianClient1.QueueCoinsToMixAsync("asdasdasd", smartCoin1, smartCoin2));
Assert.True(smartCoin1.Locked);
Assert.False(smartCoin2.Locked);
smartCoin1.Locked = false;
- chaumianClient1.QueueCoinsToMix(password, smartCoin1, smartCoin2);
+ await chaumianClient1.QueueCoinsToMixAsync(password, smartCoin1, smartCoin2);
Assert.True(smartCoin1.Locked);
Assert.True(smartCoin2.Locked);
// Make sure it doesn't throw.
await chaumianClient1.DequeueCoinsFromMixAsync(new SmartCoin((new Transaction()).GetHash(), 1, new Script(), Money.Parse("3"), new TxoRef[] { new TxoRef((new Transaction()).GetHash(), 0) }, Height.MemPool, rbf: false));
- Assert.True(2 == chaumianClient1.QueueCoinsToMix(password, smartCoin1, smartCoin2).Count());
+ Assert.True(2 == (await chaumianClient1.QueueCoinsToMixAsync(password, smartCoin1, smartCoin2)).Count());
await chaumianClient1.DequeueCoinsFromMixAsync(smartCoin1);
Assert.False(smartCoin1.Locked);
await chaumianClient1.DequeueCoinsFromMixAsync(smartCoin1, smartCoin2);
Assert.False(smartCoin1.Locked);
Assert.False(smartCoin2.Locked);
- Assert.True(2 == chaumianClient1.QueueCoinsToMix(password, smartCoin1, smartCoin2).Count());
+ Assert.True(2 == (await chaumianClient1.QueueCoinsToMixAsync(password, smartCoin1, smartCoin2)).Count());
Assert.True(smartCoin1.Locked);
Assert.True(smartCoin2.Locked);
await chaumianClient1.DequeueCoinsFromMixAsync(smartCoin1);
@@ -2313,10 +2313,10 @@ namespace WalletWasabi.Tests
Assert.False(smartCoin1.Locked);
Assert.False(smartCoin2.Locked);
- Assert.True(2 == chaumianClient1.QueueCoinsToMix(password, smartCoin1, smartCoin2).Count());
+ Assert.True(2 == (await chaumianClient1.QueueCoinsToMixAsync(password, smartCoin1, smartCoin2)).Count());
Assert.True(smartCoin1.Locked);
Assert.True(smartCoin2.Locked);
- Assert.True(1 == chaumianClient2.QueueCoinsToMix(password, smartCoin3).Count());
+ Assert.True(1 == (await chaumianClient2.QueueCoinsToMixAsync(password, smartCoin3)).Count());
Task timeout = Task.Delay(TimeSpan.FromSeconds(connectionConfirmationTimeout * 2 + 7 * 2 + 7 * 2 + 7 * 2));
while ((await rpc.GetRawMempoolAsync()).Length == 0)

MODIFY
WalletWasabi/Models/ChaumianCoinJoin/CcjClientState.cs
WalletWasabi/Models/ChaumianCoinJoin/CcjClientState.cs
@@ -34,11 +34,11 @@ namespace WalletWasabi.Models.ChaumianCoinJoin
}
}
- public void RemoveSpentCoinsFromWaitingList()
+ public IEnumerable<(uint256 txid, int index)> GetSpentCoins()
{
lock (StateLock)
{
- WaitingList.RemoveAll(x => !x.Unspent);
+ return WaitingList.Concat(Rounds.SelectMany(x => x.CoinsRegistered)).Where(x => !x.Unspent).Select(x => (x.TransactionId, x.Index)).ToArray();
}
}
@@ -69,6 +69,14 @@ namespace WalletWasabi.Models.ChaumianCoinJoin
}
}
+ public SmartCoin GetSingleOrDefaultCoin((uint256 txid, int index) coinReference)
+ {
+ lock (StateLock)
+ {
+ return WaitingList.Concat(Rounds.SelectMany(x => x.CoinsRegistered)).SingleOrDefault(x => x.TransactionId == coinReference.txid && x.Index == coinReference.index);
+ }
+ }
+
public int GetWaitingListCount()
{
lock (StateLock)

MODIFY
WalletWasabi/Services/CcjClient.cs
WalletWasabi/Services/CcjClient.cs
@@ -89,7 +89,7 @@ namespace WalletWasabi.Services
int delay;
using (await MixLock.LockAsync())
{
- State.RemoveSpentCoinsFromWaitingList();
+ await DequeueCoinsFromMixNoLockAsync(State.GetSpentCoins().ToArray());
// If stop was requested return.
if (IsRunning == false) return;
@@ -143,7 +143,7 @@ namespace WalletWasabi.Services
int delay;
using (await MixLock.LockAsync())
{
- State.RemoveSpentCoinsFromWaitingList();
+ await DequeueCoinsFromMixNoLockAsync(State.GetSpentCoins().ToArray());
states = await SatoshiClient.GetAllRoundStatesAsync();
State.UpdateRoundsByStates(states.ToArray());
@@ -154,7 +154,7 @@ namespace WalletWasabi.Services
using (await MixLock.LockAsync())
{
- State.RemoveSpentCoinsFromWaitingList();
+ await DequeueCoinsFromMixNoLockAsync(State.GetSpentCoins().ToArray());
CcjClientRound inputRegistrableRound = State.GetRegistrableRoundOrDefault();
if (inputRegistrableRound != null)
@@ -360,11 +360,11 @@ namespace WalletWasabi.Services
Interlocked.Exchange(ref _frequentStatusProcessingIfNotMixing, 0);
}
- public IEnumerable<SmartCoin> QueueCoinsToMix(string password, params SmartCoin[] coins)
+ public async Task<IEnumerable<SmartCoin>> QueueCoinsToMixAsync(string password, params SmartCoin[] coins)
{
- using (MixLock.Lock())
+ using (await MixLock.LockAsync())
{
- State.RemoveSpentCoinsFromWaitingList();
+ await DequeueCoinsFromMixNoLockAsync(State.GetSpentCoins().ToArray());
var successful = new List<SmartCoin>();
@@ -397,18 +397,21 @@ namespace WalletWasabi.Services
{
using (await MixLock.LockAsync())
{
- State.RemoveSpentCoinsFromWaitingList();
+ await DequeueCoinsFromMixNoLockAsync(State.GetSpentCoins().ToArray());
- await DequeueCoinsFromMixNoLockAsync(coins);
+ await DequeueCoinsFromMixNoLockAsync(coins.Select(x=>(x.TransactionId, x.Index)).ToArray());
}
}
- private async Task DequeueCoinsFromMixNoLockAsync(params SmartCoin[] coins)
+ private async Task DequeueCoinsFromMixNoLockAsync(params (uint256 txid, int index)[] coins)
{
List<Exception> exceptions = new List<Exception>();
- foreach (var coinToDequeue in coins)
+ foreach (var coinReference in coins)
{
+ var coinToDequeue = State.GetSingleOrDefaultCoin(coinReference);
+ if (coinToDequeue == null) continue;
+
foreach (int roundId in State.GetPassivelyMixingRounds())
{
var round = State.GetSingleOrDefaultRound(roundId);
@@ -422,17 +425,29 @@ namespace WalletWasabi.Services
State.ClearRoundRegistration(round.State.RoundId);
}
catch (Exception ex)
+ {
+ if (!coinToDequeue.Unspent)
+ {
+
+ }
+ else
{
exceptions.Add(ex);
}
}
}
+ }
foreach (int roundId in State.GetActivelyMixingRounds())
{
var round = State.GetSingleOrDefaultRound(roundId);
- if (round == null) throw new NotSupportedException("This is impossible.");
+ if (round == null) continue;
+ if (!coinToDequeue.Unspent) // If coin was spent, well that sucks, except if it was spent by the tumbler in signing phase.
+ {
+ State.ClearRoundRegistration(round.State.RoundId);
+ continue;
+ }
if (round.CoinsRegistered.Contains(coinToDequeue))
{
exceptions.Add(new NotSupportedException($"Cannot deque coin in {round.State.Phase} phase. Coin: {coinToDequeue.Index}:{coinToDequeue.TransactionId}."));
@@ -474,7 +489,7 @@ namespace WalletWasabi.Services
using (await MixLock.LockAsync())
{
- State.RemoveSpentCoinsFromWaitingList();
+ await DequeueCoinsFromMixNoLockAsync(State.GetSpentCoins().ToArray());
SatoshiClient?.Dispose();
State.DisposeAllAliceClients();
@@ -489,7 +504,7 @@ namespace WalletWasabi.Services
{
continue; // The coin isn't present anymore. Good. This should never happen though.
}
- await DequeueCoinsFromMixNoLockAsync(coin);
+ await DequeueCoinsFromMixNoLockAsync((coin.TransactionId, coin.Index));
}
catch (Exception ex)
{

Refine spentcoin check