don't fail Bob scp duplications
MODIFY
WalletWasabi.Backend/Controllers/ChaumianCoinJoinController.cs
WalletWasabi.Backend/Controllers/ChaumianCoinJoinController.cs
@@ -522,6 +522,11 @@ namespace WalletWasabi.Backend.Controllers
return BadRequest($"Invalid mixing Level is provided. Provided: {request.Level}. Maximum: {round.MixingLevels.GetMaxLevel()}.");
}
+ if (round.ContainsRegisteredUnblindedSignature(request.UnblindedSignature))
+ {
+ return NoContent();
+ }
+
MixingLevel mixinglevel = round.MixingLevels.GetLevel(request.Level);
Signer signer = mixinglevel.Signer;
@@ -534,13 +539,16 @@ namespace WalletWasabi.Backend.Controllers
{
bob = new Bob(request.OutputAddress, mixinglevel);
round.AddBob(bob);
+ round.AddRegisteredUnblindedSignature(request.UnblindedSignature);
}
catch (Exception ex)
{
return BadRequest($"Invalid outputAddress is provided. Details: {ex.Message}");
}
- if (round.CountBobs() == round.CountBlindSignatures()) // If there'll be more bobs, then round failed. Someone may broke the crypto.
+ int bobCount = round.CountBobs();
+ int blindSigCount = round.CountBlindSignatures();
+ if (bobCount == blindSigCount) // If there'll be more bobs, then round failed. Someone may broke the crypto.
{
await round.ExecuteNextPhaseAsync(CcjRoundPhase.Signing);
}

MODIFY
WalletWasabi.Tests/RegTests.cs
WalletWasabi.Tests/RegTests.cs
@@ -2932,7 +2932,7 @@ namespace WalletWasabi.Tests
chaumianClient2.Start();
smartCoin1.CoinJoinInProgress = true;
- Assert.True(!(await chaumianClient1.QueueCoinsToMixAsync(password, smartCoin1)).Any());
+ Assert.False((await chaumianClient1.QueueCoinsToMixAsync(password, smartCoin1)).Any()); // Inconsistent internal state, so don't try to add.
Assert.True(smartCoin1.CoinJoinInProgress);
await Assert.ThrowsAsync<SecurityException>(async () => await chaumianClient1.QueueCoinsToMixAsync("asdasdasd", smartCoin1, smartCoin2));

MODIFY
WalletWasabi/Models/ChaumianCoinJoin/CcjRound.cs
WalletWasabi/Models/ChaumianCoinJoin/CcjRound.cs
@@ -33,8 +33,6 @@ namespace WalletWasabi.Models.ChaumianCoinJoin
public Transaction UnsignedCoinJoin { get; private set; }
private string _unsignedCoinJoinHex;
- private bool OutputRegistrationFailed { get; set; }
-
public MixingLevels MixingLevels { get; }
public string GetUnsignedCoinJoinHex()
@@ -49,7 +47,10 @@ namespace WalletWasabi.Models.ChaumianCoinJoin
public Transaction SignedCoinJoin { get; private set; }
private List<Alice> Alices { get; }
- private List<Bob> Bobs { get; }
+ private List<Bob> Bobs { get; } // Don't make it a hashset or don't make Bob IEquitable!!!
+
+ private List<UnblindedSignature> RegisteredUnblindedSignatures { get; }
+ private object RegisteredUnblindedSignaturesLock { get; }
private static AsyncLock RoundSynchronizerLock { get; } = new AsyncLock();
@@ -158,6 +159,9 @@ namespace WalletWasabi.Models.ChaumianCoinJoin
StatusLock = new object();
Status = CcjRoundStatus.NotStarted;
+ RegisteredUnblindedSignatures = new List<UnblindedSignature>();
+ RegisteredUnblindedSignaturesLock = new object();
+
MixingLevels = new MixingLevels(config.Denomination, new Signer(new Key()));
for (int i = 0; i < config.MaximumMixingLevelCount - 1; i++)
{
@@ -169,8 +173,6 @@ namespace WalletWasabi.Models.ChaumianCoinJoin
UnsignedCoinJoin = null;
SignedCoinJoin = null;
- OutputRegistrationFailed = false;
-
Alices = new List<Alice>();
Bobs = new List<Bob>();
@@ -423,7 +425,6 @@ namespace WalletWasabi.Models.ChaumianCoinJoin
}
else if (expectedPhase == CcjRoundPhase.OutputRegistration)
{
- OutputRegistrationFailed = true;
Logger.LogInfo<CcjRound>($"{timedOutLogString} Progressing to signing phase to blame...");
}
else
@@ -591,6 +592,22 @@ namespace WalletWasabi.Models.ChaumianCoinJoin
return tinkerWithAdditionalMixingLevels;
}
+ public void AddRegisteredUnblindedSignature(UnblindedSignature unblindedSignature)
+ {
+ lock (RegisteredUnblindedSignaturesLock)
+ {
+ RegisteredUnblindedSignatures.Add(unblindedSignature);
+ }
+ }
+
+ public bool ContainsRegisteredUnblindedSignature(UnblindedSignature unblindedSignature)
+ {
+ lock (RegisteredUnblindedSignaturesLock)
+ {
+ return RegisteredUnblindedSignatures.Any(x => x.C.Equals(unblindedSignature.C) && x.S.Equals(unblindedSignature.S));
+ }
+ }
+
public int EstimateBestMixingLevel(Alice alice)
{
Money newDenomination = CalculateNewDenomination();
@@ -941,12 +958,6 @@ namespace WalletWasabi.Models.ChaumianCoinJoin
{
Logger.LogInfo<CcjRound>($"Round ({RoundId}): Trying to broadcast coinjoin.");
- if (OutputRegistrationFailed)
- {
- Abort(nameof(CcjRound), $"Round ({RoundId}): Although all alices signed, OutputRegistration failed, so the CoinJoin won't be broadcasted.", syncLock: false);
- }
- else
- {
try
{
Coin[] spentCoins = Alices.SelectMany(x => x.Inputs).ToArray();
@@ -977,7 +988,6 @@ namespace WalletWasabi.Models.ChaumianCoinJoin
}
}
}
- }
public void UpdateAnonymitySet(int anonymitySet, bool syncLock = true)
{
@@ -1027,11 +1037,8 @@ namespace WalletWasabi.Models.ChaumianCoinJoin
{
throw new InvalidOperationException("Adding Bob is only allowed in OutputRegistration phase.");
}
- if (Bobs.Any(x => x.ActiveOutputAddress == bob.ActiveOutputAddress))
- {
- return; // Bob is already added.
- }
+ // If Bob is already added with the same scriptpubkey and level, that's fine.
Bobs.Add(bob);
}

don't fail Bob scp duplications