/// <summary> /// Tries to perform mempool cleanup with the help of the connected nodes. /// NOTE: This results in heavy network activity! https://github.com/zkSNACKs/WalletWasabi/issues/1273 /// </summary> public async Task <bool> TryPerformMempoolCleanupAsync(NodesGroup nodes, CancellationToken cancel) { // If already cleaning, then no need to run it that often. if (Interlocked.CompareExchange(ref _cleanupInProcess, 1, 0) == 1) { return(false); } // This function is designed to prevent forever growing mempool. try { if (!TransactionHashes.Any()) { return(true); // There's nothing to cleanup. } var delay = TimeSpan.FromMinutes(1); if (nodes?.ConnectedNodes is null) { return(false); } while (nodes.ConnectedNodes.Count != nodes.MaximumNodeConnection && nodes.ConnectedNodes.All(x => x.IsConnected)) { if (cancel.IsCancellationRequested) { return(false); } Logger.LogInfo <MemPoolService>($"Not all nodes were in a connected state. Delaying mempool cleanup for {delay.TotalSeconds} seconds..."); await Task.Delay(delay, cancel); } Logger.LogInfo <MemPoolService>("Start cleaning out mempool..."); var allTxs = new HashSet <uint256>(); foreach (Node node in nodes.ConnectedNodes) { try { if (!node.IsConnected) { continue; } if (cancel.IsCancellationRequested) { return(false); } uint256[] txs = node.GetMempool(cancel); if (cancel.IsCancellationRequested) { return(false); } allTxs.UnionWith(txs); if (cancel.IsCancellationRequested) { return(false); } } catch (Exception ex) when((ex is InvalidOperationException && ex.Message.StartsWith("The node is not in a connected state", StringComparison.InvariantCultureIgnoreCase)) || ex is OperationCanceledException || ex is TaskCanceledException || ex is TimeoutException) { Logger.LogTrace <MemPoolService>(ex); } catch (Exception ex) { Logger.LogDebug <MemPoolService>(ex); } } int removedTxCount = 0; foreach (uint256 tx in TransactionHashes.ToArray()) { if (!allTxs.Contains(tx)) { if (TransactionHashes.TryRemove(tx)) { removedTxCount++; } } } Logger.LogInfo <MemPoolService>($"{removedTxCount} transactions were cleaned from mempool."); return(true); } catch (Exception ex) when(ex is OperationCanceledException || ex is TaskCanceledException || ex is TimeoutException) { Logger.LogTrace <MemPoolService>(ex); } catch (Exception ex) { Logger.LogDebug <MemPoolService>(ex); } finally { Interlocked.Exchange(ref _cleanupInProcess, 0); } return(false); }