Add BanAlice method
MODIFY
.gitignore
.gitignore
@@ -144,7 +144,7 @@ publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
-# TODO: Comment the next line if you want to checkin your web deploy settings
+# Comment the next line if you want to checkin your web deploy settings
# but database connection strings (with potential passwords) will be unencrypted
#*.pubxml
*.publishproj

MODIFY
WalletWasabi.Backend/Controllers/ChaumianCoinJoinController.cs
WalletWasabi.Backend/Controllers/ChaumianCoinJoinController.cs
@@ -130,7 +130,10 @@ namespace WalletWasabi.Backend.Controllers
}
}
- // ToDo: Refuse banned UTXO here!
+ if(Coordinator.UtxoReferee.BannedUtxos.Keys.Contains(inputProof.Input))
+ {
+ return BadRequest($"Input is banned from participation: {inputProof.Input.N}:{inputProof.Input.Hash}.");
+ }
GetTxOutResponse getTxOutResponse = await RpcClient.GetTxOutAsync(inputProof.Input.Hash, (int)inputProof.Input.N, includeMempool: true);

MODIFY
WalletWasabi/ChaumianCoinJoin/CcjRound.cs
WalletWasabi/ChaumianCoinJoin/CcjRound.cs
@@ -303,12 +303,12 @@ namespace WalletWasabi.ChaumianCoinJoin
// Delay asyncronously to the requested timeout.
await Task.Delay(timeout);
- var runFailureToDo = false;
+ var executeRunFailure = false;
using (await RoundSyncronizerLock.LockAsync())
{
- runFailureToDo = Status == CcjRoundStatus.Running && Phase == expectedPhase;
+ executeRunFailure = Status == CcjRoundStatus.Running && Phase == expectedPhase;
}
- if (runFailureToDo)
+ if (executeRunFailure)
{
// This will happen outside the lock.
Task.Run(async () =>
@@ -361,7 +361,6 @@ namespace WalletWasabi.ChaumianCoinJoin
case CcjRoundPhase.Signing:
{
Fail();
- // ToDo: Ban Alices those states are not SignedCoinJoin
}
break;
default: throw new InvalidOperationException("This is impossible to happen.");
@@ -453,6 +452,22 @@ namespace WalletWasabi.ChaumianCoinJoin
}
}
+ public IEnumerable<Alice> GetAlicesBy(AliceState state)
+ {
+ using (RoundSyncronizerLock.Lock())
+ {
+ return Alices.Where(x => x.State == state).ToList();
+ }
+ }
+
+ public IEnumerable<Alice> GetAlicesByNot(AliceState state)
+ {
+ using (RoundSyncronizerLock.Lock())
+ {
+ return Alices.Where(x => x.State != state).ToList();
+ }
+ }
+
public void StartAliceTimeout(Guid uniqueId)
{
// 1. Find Alice and set its LastSeen propery.

MODIFY
WalletWasabi/Crypto/BlindingRsaKey.cs
WalletWasabi/Crypto/BlindingRsaKey.cs
@@ -104,7 +104,7 @@ namespace WalletWasabi.Crypto
public static bool operator ==(BlindingRsaKey x, BlindingRsaKey y)
{
- // todo add null comparisions (== must not throw from nulls)
+ // ToDo: add null comparisons for PrivateModulus, PrivateExponent, PublicModulus and PublicExponent, too, because == MUST never throw from nulls
return
x.PrivateModulus.Equals(y.PrivateModulus)
&& x.PrivateExponent.Equals(y.PrivateExponent)

MODIFY
WalletWasabi/Crypto/BlindingRsaPubKey.cs
WalletWasabi/Crypto/BlindingRsaPubKey.cs
@@ -102,7 +102,8 @@ namespace WalletWasabi.Crypto
public static bool operator ==(BlindingRsaPubKey x, BlindingRsaPubKey y)
{
- // todo add null comparisions (== must not throw from nulls)
+ // ToDo: add null comparisons for Exponent and Modulus, too, because == MUST never throw from nulls
+
return
x.Modulus.Equals(y.Modulus)
&& x.Exponent.Equals(y.Exponent);

MODIFY
WalletWasabi/Services/CcjCoordinator.cs
WalletWasabi/Services/CcjCoordinator.cs
@@ -35,6 +35,8 @@ namespace WalletWasabi.Services
public BlindingRsaKey RsaKey { get; }
+ public UtxoReferee UtxoReferee { get; }
+
public CcjCoordinator(Network network, string folderPath, RPCClient rpc, CcjRoundConfig roundConfig)
{
Network = Guard.NotNull(nameof(network), network);
@@ -51,6 +53,8 @@ namespace WalletWasabi.Services
Directory.CreateDirectory(FolderPath);
+ UtxoReferee = new UtxoReferee(Network, FolderPath, RpcClient);
+
// Initialize RsaKey
string rsaKeyPath = Path.Combine(FolderPath, "RsaKey.json");
if (File.Exists(rsaKeyPath))
@@ -150,6 +154,15 @@ namespace WalletWasabi.Services
}
}
+ // If failed in signing phase, then ban Alices those didn't sign.
+ if(status == CcjRoundStatus.Failed && round.Phase == CcjRoundPhase.Signing)
+ {
+ foreach (Alice alice in round.GetAlicesByNot(AliceState.SignedCoinJoin))
+ {
+ await UtxoReferee.BanAliceAsync(alice);
+ }
+ }
+
// If finished start a new round.
if (status == CcjRoundStatus.Failed || status == CcjRoundStatus.Succeded)
{

MODIFY
WalletWasabi/Services/ConfigWatcher.cs
WalletWasabi/Services/ConfigWatcher.cs
@@ -31,7 +31,7 @@ namespace WalletWasabi.Services
Stop = new CancellationTokenSource();
}
- public void Start(TimeSpan period, Func<Task> toDoWhenChangedAsync)
+ public void Start(TimeSpan period, Func<Task> executeWhenChangedAsync)
{
Interlocked.Exchange(ref _running, 1);
@@ -52,7 +52,7 @@ namespace WalletWasabi.Services
{
await Config.LoadOrCreateDefaultFileAsync();
- await toDoWhenChangedAsync();
+ await executeWhenChangedAsync();
}
}
catch (TaskCanceledException ex)

MODIFY
WalletWasabi/Services/UtxoReferee.cs
WalletWasabi/Services/UtxoReferee.cs
@@ -6,6 +6,8 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
+using System.Threading.Tasks;
+using WalletWasabi.ChaumianCoinJoin;
using WalletWasabi.Helpers;
using WalletWasabi.Logging;
using WalletWasabi.Models;
@@ -46,7 +48,7 @@ namespace WalletWasabi.Services
foreach (string line in allLines)
{
var parts = line.Split(':');
- var utxo = new OutPoint(new uint256(parts[1]), int.Parse(parts[2]));
+ var utxo = new OutPoint(new uint256(parts[2]), int.Parse(parts[1]));
var severity = int.Parse(parts[0]);
GetTxOutResponse getTxOutResponse = RpcClient.GetTxOut(utxo.Hash, (int)utxo.N, includeMempool: true);
@@ -75,5 +77,18 @@ namespace WalletWasabi.Services
}
}
}
+
+ public async Task BanAliceAsync(Alice alice)
+ {
+ var lines = new List<string>();
+ foreach(var utxo in alice.Inputs.Select(x=>x.OutPoint))
+ {
+ BannedUtxos.TryAdd(utxo, 1);
+ string line = $"1:{utxo.N}:{utxo.Hash}";
+ lines.Add(line);
+ }
+
+ await File.AppendAllLinesAsync(BannedUtxosFilePath, lines);
+ }
}
}

Add BanAlice method