Storing the uc ccj tx instead of memppol request.
MODIFY
WalletWasabi.Backend/Controllers/BlockchainController.cs
WalletWasabi.Backend/Controllers/BlockchainController.cs
@@ -223,10 +223,17 @@ namespace WalletWasabi.Backend.Controllers
return BadRequest($"Maximum {maxTxToRequest} transactions can be requested.");
}
- IEnumerable<uint256> parsedIds;
+ var parsedIds = new List<uint256>();
try
{
- parsedIds = transactionIds.Distinct().Select(x => new uint256(x));
+ // Remove duplicates, do not use Distinct(), order is not guaranteed.
+ foreach (var txid in transactionIds.Select(x => new uint256(x)))
+ {
+ if (!parsedIds.Contains(txid))
+ {
+ parsedIds.Add(txid);
+ }
+ }
}
catch
{
@@ -235,7 +242,7 @@ namespace WalletWasabi.Backend.Controllers
try
{
- var hexes = new List<string>();
+ var hexes = new Dictionary<uint256, string>();
var queryRpc = false;
RPCClient batchingRpc = null;
List<Task<Transaction>> tasks = null;
@@ -245,7 +252,7 @@ namespace WalletWasabi.Backend.Controllers
{
if (TransactionHexCache.TryGetValue(txid, out string hex))
{
- hexes.Add(hex);
+ hexes.Add(txid, hex);
}
else
{
@@ -268,7 +275,7 @@ namespace WalletWasabi.Backend.Controllers
{
var tx = await txTask;
string hex = tx.ToHex();
- hexes.Add(hex);
+ hexes.Add(tx.GetHash(), hex);
lock (TransactionHexCacheLock)
{
@@ -280,7 +287,9 @@ namespace WalletWasabi.Backend.Controllers
}
}
- return Ok(hexes);
+ // Order hexes according to the order of the query.
+ var orderedResult = parsedIds.Where(x => hexes.ContainsKey(x)).Select(x => hexes[x]);
+ return Ok(orderedResult);
}
catch (Exception ex)
{

MODIFY
WalletWasabi.Backend/Controllers/ChaumianCoinJoinController.cs
WalletWasabi.Backend/Controllers/ChaumianCoinJoinController.cs
@@ -35,16 +35,14 @@ namespace WalletWasabi.Backend.Controllers
[Route("api/v" + Constants.BackendMajorVersion + "/btc/[controller]")]
public class ChaumianCoinJoinController : Controller
{
- public BlockchainController BlockchainController { get; }
private IMemoryCache Cache { get; }
public Global Global { get; }
private RPCClient RpcClient => Global.RpcClient;
private Network Network => Global.Config.Network;
private Coordinator Coordinator => Global.Coordinator;
- public ChaumianCoinJoinController(BlockchainController blockchainController, IMemoryCache memoryCache, Global global)
+ public ChaumianCoinJoinController(IMemoryCache memoryCache, Global global)
{
- BlockchainController = blockchainController;
Cache = memoryCache;
Global = global;
}
@@ -222,7 +220,7 @@ namespace WalletWasabi.Backend.Controllers
if (getTxOutResponse.Confirmations <= 0)
{
// If it spends a CJ then it may be acceptable to register.
- if (!await Coordinator.ContainsCoinJoinAsync(inputProof.Input.TransactionId))
+ if (!await Coordinator.ContainsUnconfirmedCoinJoinAsync(inputProof.Input.TransactionId))
{
return BadRequest("Provided input is neither confirmed, nor is from an unconfirmed coinjoin.");
}
@@ -746,10 +744,7 @@ namespace WalletWasabi.Backend.Controllers
[ProducesResponseType(200)]
public async Task<IActionResult> GetUnconfirmedCoinjoinAsync()
{
- var coinJoins = await Global.Coordinator.GetCoinJoinsAsync();
- var mempool = await BlockchainController.GetRawMempoolStringsAsync();
- var coinJoinsStrings = coinJoins.Select(x => x.ToString());
- var unconfirmedCoinJoins = coinJoinsStrings.Intersect(mempool);
+ var unconfirmedCoinJoins = await Global.Coordinator.GetUnconfirmedCoinJoinsAsync();
return Ok(unconfirmedCoinJoins);
}

MODIFY
WalletWasabi.Tests/IntegrationTests/RegTests.cs
WalletWasabi.Tests/IntegrationTests/RegTests.cs
@@ -2185,7 +2185,7 @@ namespace WalletWasabi.Tests.IntegrationTests
Assert.NotNull(await utxos.TryGetBannedAsync(bannedCoin.ToOutPoint(), false));
spendingTx.Inputs.Add(new TxIn(bannedCoin.ToOutPoint()));
spendingTx.Outputs.Add(new TxOut(Money.Coins(1), new Key().PubKey.GetSegwitAddress(network)));
- await coordinator.ProcessTransactionAsync(spendingTx);
+ await coordinator.ProcessConfirmedTransactionAsync(spendingTx);
Assert.NotNull(await utxos.TryGetBannedAsync(new OutPoint(spendingTx.GetHash(), 0), false));
Assert.Null(await utxos.TryGetBannedAsync(bannedCoin.ToOutPoint(), false));

MODIFY
WalletWasabi/CoinJoin/Coordinator/Coordinator.cs
WalletWasabi/CoinJoin/Coordinator/Coordinator.cs
@@ -29,6 +29,8 @@ namespace WalletWasabi.CoinJoin.Coordinator
public string CoinJoinsFilePath => Path.Combine(FolderPath, $"CoinJoins{Network}.txt");
private AsyncLock CoinJoinsLock { get; }
+ private List<uint256> UnconfirmedCoinJoins { get; }
+
public event EventHandler<Transaction> CoinJoinBroadcasted;
public RPCClient RpcClient { get; }
@@ -55,6 +57,7 @@ namespace WalletWasabi.CoinJoin.Coordinator
RoundsListLock = new AsyncLock();
CoinJoins = new List<uint256>();
+ UnconfirmedCoinJoins = new List<uint256>();
CoinJoinsLock = new AsyncLock();
Directory.CreateDirectory(FolderPath);
@@ -120,6 +123,9 @@ namespace WalletWasabi.CoinJoin.Coordinator
Logger.LogWarning($"CoinJoins file got corrupted. Deleting {CoinJoinsFilePath}. {ex.GetType()}: {ex.Message}");
File.Delete(CoinJoinsFilePath);
}
+
+ uint256[] mempoolHashes = RpcClient.GetRawMempoolAsync().GetAwaiter().GetResult();
+ UnconfirmedCoinJoins.AddRange(CoinJoins.Intersect(mempoolHashes));
}
try
@@ -149,10 +155,13 @@ namespace WalletWasabi.CoinJoin.Coordinator
private async void BlockNotifier_OnBlockAsync(object sender, Block block)
{
try
+ {
+ using (await CoinJoinsLock.LockAsync())
{
foreach (Transaction tx in block.Transactions)
{
- await ProcessTransactionAsync(tx).ConfigureAwait(false);
+ await ProcessConfirmedTransactionAsync(tx).ConfigureAwait(false);
+ }
}
}
catch (Exception ex)
@@ -161,7 +170,7 @@ namespace WalletWasabi.CoinJoin.Coordinator
}
}
- public async Task ProcessTransactionAsync(Transaction tx)
+ public async Task ProcessConfirmedTransactionAsync(Transaction tx)
{
// This should not be needed until we would only accept unconfirmed CJ outputs an no other unconf outs. But it'll be more bulletproof for future extensions.
// Turns out you shouldn't accept RBF at all never. (See below.)
@@ -169,6 +178,9 @@ namespace WalletWasabi.CoinJoin.Coordinator
// https://github.com/zkSNACKs/WalletWasabi/issues/145
// if it spends a banned output AND it's not CJ output
// ban all the outputs of the transaction
+ tx.PrecomputeHash(false, true);
+
+ UnconfirmedCoinJoins.Remove(tx.GetHash()); // Locked outside.
if (RoundConfig.DosSeverity <= 1)
{
@@ -239,18 +251,17 @@ namespace WalletWasabi.CoinJoin.Coordinator
{
try
{
- uint256[] mempoolHashes = await RpcClient.GetRawMempoolAsync().ConfigureAwait(false);
int unconfirmedCoinJoinsCount = 0;
if (lockCoinJoins)
{
using (await CoinJoinsLock.LockAsync().ConfigureAwait(false))
{
- unconfirmedCoinJoinsCount = CoinJoins.Intersect(mempoolHashes).Count();
+ unconfirmedCoinJoinsCount = UnconfirmedCoinJoins.Count;
}
}
else
{
- unconfirmedCoinJoinsCount = CoinJoins.Intersect(mempoolHashes).Count();
+ unconfirmedCoinJoinsCount = UnconfirmedCoinJoins.Count;
}
int confirmationTarget = CoordinatorRound.AdjustConfirmationTarget(unconfirmedCoinJoinsCount, RoundConfig.ConfirmationTarget, RoundConfig.ConfirmationTargetReductionRate);
@@ -286,6 +297,7 @@ namespace WalletWasabi.CoinJoin.Coordinator
{
uint256 coinJoinHash = round.SignedCoinJoin.GetHash();
CoinJoins.Add(coinJoinHash);
+ UnconfirmedCoinJoins.Add(coinJoinHash);
await File.AppendAllLinesAsync(CoinJoinsFilePath, new[] { coinJoinHash.ToString() }).ConfigureAwait(false);
// When a round succeeded, adjust the denomination as to users still be able to register with the latest round's active output amount.
@@ -425,11 +437,11 @@ namespace WalletWasabi.CoinJoin.Coordinator
}
}
- public async Task<bool> ContainsCoinJoinAsync(uint256 hash)
+ public async Task<bool> ContainsUnconfirmedCoinJoinAsync(uint256 hash)
{
using (await CoinJoinsLock.LockAsync().ConfigureAwait(false))
{
- return CoinJoins.Contains(hash);
+ return UnconfirmedCoinJoins.Contains(hash);
}
}
@@ -438,11 +450,11 @@ namespace WalletWasabi.CoinJoin.Coordinator
return CoinJoins.Count;
}
- public async Task<IEnumerable<uint256>> GetCoinJoinsAsync()
+ public async Task<IEnumerable<uint256>> GetUnconfirmedCoinJoinsAsync()
{
using (await CoinJoinsLock.LockAsync())
{
- return CoinJoins.ToArray();
+ return UnconfirmedCoinJoins.ToArray();
}
}

Storing the uc ccj tx instead of memppol request.