Some test lines for CcjCoordinator class
MODIFY
WalletWasabi.Tests/RegTests.cs
WalletWasabi.Tests/RegTests.cs
@@ -1514,6 +1514,57 @@ namespace WalletWasabi.Tests
}
}
+ [Fact]
+ public async Task CcjCoordinatorCtorTestsAsync()
+ {
+ var rpc = Global.RpcClient;
+ var network = Global.RpcClient.Network;
+
+ var bestBlockHash = await rpc.GetBestBlockHashAsync();
+ var bestBlock = await rpc.GetBlockAsync(bestBlockHash);
+ var coinbaseTxId = bestBlock.Transactions[0].GetHash();
+ var offchainTxId = new Transaction().GetHash();
+ var mempoolTxId = rpc.SendToAddress(new Key().PubKey.GetSegwitAddress(network), Money.Coins(1));
+
+ var folder = Path.Combine(Global.DataDir, nameof(CcjCoordinatorCtorTestsAsync));
+ await IoHelpers.DeleteRecursivelyWithMagicDustAsync(folder);
+ Directory.CreateDirectory(folder);
+ var cjfile = Path.Combine(folder, $"CoinJoins{network}.txt");
+ File.WriteAllLines(cjfile, new[]{
+ coinbaseTxId.ToString(),
+ offchainTxId.ToString(),
+ mempoolTxId.ToString()
+ });
+
+ var coordinator = new CcjCoordinator(network, folder, rpc, Global.Coordinator.RoundConfig);
+ var txIds = await File.ReadAllLinesAsync(cjfile);
+
+ Assert.Contains(coinbaseTxId.ToString(), txIds);
+ Assert.Contains(mempoolTxId.ToString(), txIds);
+ Assert.DoesNotContain(offchainTxId.ToString(), txIds);
+
+
+ await IoHelpers.DeleteRecursivelyWithMagicDustAsync(folder);
+ Directory.CreateDirectory(folder);
+ File.WriteAllLines(cjfile, new[]{
+ coinbaseTxId.ToString(),
+ "This line is invalid (the file is corrupted)",
+ offchainTxId.ToString(),
+ });
+ coordinator = new CcjCoordinator(network, folder, rpc, Global.Coordinator.RoundConfig);
+ Assert.False(File.Exists(cjfile));
+
+
+ coordinator = Global.Coordinator;
+ Money denomination = new Money(0.2m, MoneyUnit.BTC);
+ decimal coordinatorFeePercent = 0.2m;
+ int anonymitySet = 2;
+ int connectionConfirmationTimeout = 50;
+ var roundConfig = new CcjRoundConfig(denomination, 2, coordinatorFeePercent, anonymitySet, 100, connectionConfirmationTimeout, 50, 50, 1);
+ coordinator.UpdateRoundConfig(roundConfig);
+ coordinator.FailAllRoundsInInputRegistration();
+ }
+
[Fact]
public async Task CcjTestsAsync()
{
@@ -1524,7 +1575,7 @@ namespace WalletWasabi.Tests
decimal coordinatorFeePercent = 0.2m;
int anonymitySet = 2;
int connectionConfirmationTimeout = 50;
- var roundConfig = new CcjRoundConfig(denomination, 2, coordinatorFeePercent, anonymitySet, 100, connectionConfirmationTimeout, 50, 50, 1);
+ var roundConfig = new CcjRoundConfig(denomination, 2, coordinatorFeePercent, anonymitySet, 100, connectionConfirmationTimeout, 50, 50, 2);
coordinator.UpdateRoundConfig(roundConfig);
coordinator.FailAllRoundsInInputRegistration();
@@ -1715,7 +1766,7 @@ namespace WalletWasabi.Tests
Assert.True(inputsResponse.RoundId > 0);
roundId = inputsResponse.RoundId;
- await Task.Delay(50);
+ await Task.Delay(250);
roundState = await satoshiClient.GetRoundStateAsync(roundId);
Assert.Equal(CcjRoundPhase.ConnectionConfirmation, roundState.Phase);
Assert.Equal(2, roundState.RegisteredPeerCount);
@@ -1738,6 +1789,20 @@ namespace WalletWasabi.Tests
httpRequestException = await Assert.ThrowsAsync<HttpRequestException>(async () => await aliceClient.PostInputsAsync(inputsRequest));
Assert.StartsWith($"{HttpStatusCode.BadRequest.ToReasonString()}\nInput is banned from participation for", httpRequestException.Message);
+ var spendingTx = new Transaction();
+ var bannedCoin = inputsRequest.Inputs.First().Input;
+ var utxos = Global.Coordinator.UtxoReferee;
+ Assert.True(utxos.BannedUtxos.Any(x => x.Key == bannedCoin));
+ spendingTx.Inputs.Add(new TxIn(bannedCoin));
+ spendingTx.Outputs.Add(new TxOut(Money.Coins(1), new Key().PubKey.GetSegwitAddress(network)));
+ var fakeBlockWithSpendingBannedCoins = network.Consensus.ConsensusFactory.CreateBlock();
+ fakeBlockWithSpendingBannedCoins.Transactions.Add(spendingTx);
+
+ await Global.Coordinator.ProcessBlockAsync(fakeBlockWithSpendingBannedCoins);
+
+ Assert.True(utxos.BannedUtxos.Any(x => x.Key == new OutPoint(spendingTx.GetHash(), 0)));
+ Assert.False(utxos.BannedUtxos.Any(x => x.Key == bannedCoin));
+
states = await satoshiClient.GetAllRoundStatesAsync();
foreach (var rs in states.Where(x => x.Phase == CcjRoundPhase.InputRegistration))
{

MODIFY
WalletWasabi/Services/CcjCoordinator.cs
WalletWasabi/Services/CcjCoordinator.cs
@@ -78,17 +78,23 @@ namespace WalletWasabi.Services
foreach (string line in allLines)
{
uint256 txHash = new uint256(line);
+ try{
RPCResponse getRawTransactionResponse = RpcClient.SendCommand(RPCOperations.getrawtransaction, txHash.ToString(), true);
- if (string.IsNullOrWhiteSpace(getRawTransactionResponse?.ResultString))
+ CoinJoins.Add(txHash);
+ if (getRawTransactionResponse.Result.Value<int>("confirmations") <= 0)
+ {
+ UnconfirmedCoinJoins.Add(txHash);
+ }
+ }
+ catch(RPCException rpce)
+ {
+ if(rpce.RPCCode == RPCErrorCode.RPC_INVALID_ADDRESS_OR_KEY)
{
toRemove.Add(line);
}
else
{
- CoinJoins.Add(txHash);
- if (getRawTransactionResponse.Result.Value<int>("confirmations") <= 0)
- {
- UnconfirmedCoinJoins.Add(txHash);
+ throw;
}
}
}
@@ -118,7 +124,9 @@ namespace WalletWasabi.Services
foreach(Transaction tx in block.Transactions)
{
if (RoundConfig.DosSeverity <= 1) return;
+ var txId = tx.GetHash();
+ var outputIndex = 0;
foreach(TxIn input in tx.Inputs)
{
OutPoint prevOut = input.PrevOut;
@@ -131,7 +139,8 @@ namespace WalletWasabi.Services
int newSeverity = found.Value.severity + 1;
if(RoundConfig.DosSeverity >= newSeverity)
{
- await UtxoReferee.BanUtxosAsync(newSeverity, found.Value.timeOfBan, prevOut);
+ var txCoins = tx.Outputs.Select(x=> new OutPoint(txId, outputIndex));
+ await UtxoReferee.BanUtxosAsync(newSeverity, found.Value.timeOfBan, txCoins.ToArray());
}
await UtxoReferee.UnbanAsync(prevOut); // since it's not an UTXO anymore
}
@@ -140,6 +149,7 @@ namespace WalletWasabi.Services
{
await UtxoReferee.UnbanAsync(prevOut); // since it's not an UTXO anymore
}
+ outputIndex++;
}
}
}

Some test lines for CcjCoordinator class