Use `CancellationToken` in many places
MODIFY
WalletWasabi.Backend/Controllers/ChaumianCoinJoinController.cs
WalletWasabi.Backend/Controllers/ChaumianCoinJoinController.cs
@@ -10,6 +10,7 @@ using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Net;
+using System.Threading;
using System.Threading.Tasks;
using WalletWasabi.BitcoinCore.Rpc;
using WalletWasabi.CoinJoin.Common.Models;
@@ -248,7 +249,7 @@ namespace WalletWasabi.Backend.Controllers
{
// Check if mempool would accept a fake transaction created with the registered inputs.
// Fake outputs: mixlevels + 1 maximum, +1 because there can be a change.
- var result = await RpcClient.TestMempoolAcceptAsync(inputs, fakeOutputCount: round.MixingLevels.Count() + 1, round.FeePerInputs, round.FeePerOutputs);
+ var result = await RpcClient.TestMempoolAcceptAsync(inputs, fakeOutputCount: round.MixingLevels.Count() + 1, round.FeePerInputs, round.FeePerOutputs, CancellationToken.None);
if (!result.accept)
{
return BadRequest($"Provided input is from an unconfirmed coinjoin, but a limit is reached: {result.rejectReason}");

MODIFY
WalletWasabi.Backend/Global.cs
WalletWasabi.Backend/Global.cs
@@ -51,7 +51,7 @@ namespace WalletWasabi.Backend
RpcClient = Guard.NotNull(nameof(rpc), rpc);
// Make sure RPC works.
- await AssertRpcNodeFullyInitializedAsync();
+ await AssertRpcNodeFullyInitializedAsync(cancel);
// Make sure P2P works.
await InitializeP2pAsync(config.Network, config.GetBitcoinP2pEndPoint(), cancel);
@@ -109,11 +109,11 @@ namespace WalletWasabi.Backend
HostedServices.Register<BlockNotifier>(new BlockNotifier(TimeSpan.FromSeconds(7), RpcClient, P2pNode), "Block Notifier");
}
- private async Task AssertRpcNodeFullyInitializedAsync()
+ private async Task AssertRpcNodeFullyInitializedAsync(CancellationToken cancellationToken)
{
try
{
- var blockchainInfo = await RpcClient.GetBlockchainInfoAsync();
+ var blockchainInfo = await RpcClient.GetBlockchainInfoAsync(cancellationToken);
var blocks = blockchainInfo.Blocks;
if (blocks == 0 && Config.Network != Network.RegTest)
@@ -138,13 +138,13 @@ namespace WalletWasabi.Backend
{
if (blocks < 101)
{
- var generateBlocksResponse = await RpcClient.GenerateAsync(101);
+ var generateBlocksResponse = await RpcClient.GenerateAsync(101, cancellationToken);
if (generateBlocksResponse is null)
{
throw new NotSupportedException($"{Constants.BuiltinBitcoinNodeName} cannot generate blocks on the {Network.RegTest}.");
}
- blockchainInfo = await RpcClient.GetBlockchainInfoAsync();
+ blockchainInfo = await RpcClient.GetBlockchainInfoAsync(cancellationToken);
blocks = blockchainInfo.Blocks;
if (blocks == 0)
{

MODIFY
WalletWasabi/BitcoinCore/CoreNode.cs
WalletWasabi/BitcoinCore/CoreNode.cs
@@ -231,7 +231,7 @@ namespace WalletWasabi.BitcoinCore
cancel.ThrowIfCancellationRequested();
// If it isn't already running, then we run it.
- if (await coreNode.RpcClient.TestAsync().ConfigureAwait(false) is null)
+ if (await coreNode.RpcClient.TestAsync(cancel).ConfigureAwait(false) is null)
{
Logger.LogInfo("A Bitcoin node is already running.");
}

MODIFY
WalletWasabi/BitcoinCore/Mempool/MempoolMirror.cs
WalletWasabi/BitcoinCore/Mempool/MempoolMirror.cs
@@ -102,7 +102,7 @@ namespace WalletWasabi.BitcoinCore.Mempool
private async Task<int> MirrorMempoolAsync(CancellationToken cancel)
{
- var mempoolHashes = await Rpc.GetRawMempoolAsync().ConfigureAwait(false);
+ var mempoolHashes = await Rpc.GetRawMempoolAsync(cancel).ConfigureAwait(false);
IEnumerable<uint256> missing;
lock (MempoolLock)

MODIFY
WalletWasabi/BitcoinCore/Monitoring/RpcFeeProvider.cs
WalletWasabi/BitcoinCore/Monitoring/RpcFeeProvider.cs
@@ -33,7 +33,7 @@ namespace WalletWasabi.BitcoinCore.Monitoring
// If Core was running for a day already && it's synchronized, then we can be pretty sure that the estimate is accurate.
// It could also be accurate if Core was only shut down for a few minutes, but that's hard to figure out.
- allFeeEstimate.IsAccurate = RpcMonitor.RpcStatus.Synchronized && await RpcClient.UptimeAsync().ConfigureAwait(false) > TimeSpan.FromDays(1);
+ allFeeEstimate.IsAccurate = RpcMonitor.RpcStatus.Synchronized && await RpcClient.UptimeAsync(cancel).ConfigureAwait(false) > TimeSpan.FromDays(1);
LastAllFeeEstimate = allFeeEstimate;
if (allFeeEstimate?.Estimations?.Any() is true)

MODIFY
WalletWasabi/BitcoinCore/Processes/BitcoindRpcProcessBridge.cs
WalletWasabi/BitcoinCore/Processes/BitcoindRpcProcessBridge.cs
@@ -69,7 +69,7 @@ namespace WalletWasabi.BitcoinCore.Processes
{
try
{
- TimeSpan timeSpan = await RpcClient.UptimeAsync().ConfigureAwait(false);
+ TimeSpan timeSpan = await RpcClient.UptimeAsync(cancel).ConfigureAwait(false);
Logger.LogInfo("RPC connection is successfully established.");
Logger.LogDebug($"RPC uptime is: {timeSpan}.");

MODIFY
WalletWasabi/Blockchain/Blocks/BlockNotifier.cs
WalletWasabi/Blockchain/Blocks/BlockNotifier.cs
@@ -65,7 +65,7 @@ namespace WalletWasabi.Blockchain.Blocks
}
else
{
- bestBlockHash = await RpcClient.GetBestBlockHashAsync().ConfigureAwait(false);
+ bestBlockHash = await RpcClient.GetBestBlockHashAsync(cancel).ConfigureAwait(false);
}
// If there's no new block.
@@ -74,7 +74,7 @@ namespace WalletWasabi.Blockchain.Blocks
return;
}
- var arrivedBlock = await RpcClient.GetBlockAsync(bestBlockHash).ConfigureAwait(false);
+ var arrivedBlock = await RpcClient.GetBlockAsync(bestBlockHash, cancel).ConfigureAwait(false);
var arrivedHeader = arrivedBlock.Header;
arrivedHeader.PrecomputeHash(false, true);
@@ -93,7 +93,7 @@ namespace WalletWasabi.Blockchain.Blocks
var currentHeader = arrivedHeader;
while (reorgProtection7Headers.Count < 7 && currentHeader.GetHash() != Network.GenesisHash)
{
- currentHeader = await RpcClient.GetBlockHeaderAsync(currentHeader.HashPrevBlock).ConfigureAwait(false);
+ currentHeader = await RpcClient.GetBlockHeaderAsync(currentHeader.HashPrevBlock, cancel).ConfigureAwait(false);
reorgProtection7Headers.Add(currentHeader);
}
@@ -135,13 +135,13 @@ namespace WalletWasabi.Blockchain.Blocks
return;
}
- await HandleMissedBlocksAsync(arrivedBlock).ConfigureAwait(false);
+ await HandleMissedBlocksAsync(arrivedBlock, cancel).ConfigureAwait(false);
BestBlockHash = bestBlockHash;
return;
}
- private async Task HandleMissedBlocksAsync(Block arrivedBlock)
+ private async Task HandleMissedBlocksAsync(Block arrivedBlock, CancellationToken cancellationToken)
{
List<Block> missedBlocks = new()
{
@@ -150,7 +150,7 @@ namespace WalletWasabi.Blockchain.Blocks
var currentHeader = arrivedBlock.Header;
while (true)
{
- Block missedBlock = await RpcClient.GetBlockAsync(currentHeader.HashPrevBlock).ConfigureAwait(false);
+ Block missedBlock = await RpcClient.GetBlockAsync(currentHeader.HashPrevBlock, cancellationToken).ConfigureAwait(false);
if (missedBlocks.Count > 144)
{

MODIFY
WalletWasabi/CoinJoin/Coordinator/Rounds/CoordinatorRound.cs
WalletWasabi/CoinJoin/Coordinator/Rounds/CoordinatorRound.cs
@@ -866,7 +866,7 @@ namespace WalletWasabi.CoinJoin.Coordinator.Rounds
var originalConfirmationTarget = AdjustedConfirmationTarget;
// Note that only dependents matter, spenders do not matter much or at all, they just allow this transaction to be confirmed faster.
- var dependents = await RpcClient.GetAllDependentsAsync(transactionHashes, includingProvided: true, likelyProvidedManyConfirmedOnes: true).ConfigureAwait(false);
+ var dependents = await RpcClient.GetAllDependentsAsync(transactionHashes, includingProvided: true, likelyProvidedManyConfirmedOnes: true, CancellationToken.None).ConfigureAwait(false);
AdjustedConfirmationTarget = AdjustConfirmationTarget(dependents.Count, ConfiguredConfirmationTarget, ConfiguredConfirmationTargetReductionRate);
if (originalConfirmationTarget != AdjustedConfirmationTarget)
@@ -1350,7 +1350,7 @@ namespace WalletWasabi.CoinJoin.Coordinator.Rounds
// Add the change outputs.
outputCount += Alices.Count;
- return await RpcClient.TestMempoolAcceptAsync(coinsToTest, fakeOutputCount: outputCount, FeePerInputs, FeePerOutputs).ConfigureAwait(false);
+ return await RpcClient.TestMempoolAcceptAsync(coinsToTest, fakeOutputCount: outputCount, FeePerInputs, FeePerOutputs, CancellationToken.None).ConfigureAwait(false);
}
public int RemoveAlicesBy(params Guid[] ids)

MODIFY
WalletWasabi/Extensions/RPCClientExtensions.cs
WalletWasabi/Extensions/RPCClientExtensions.cs
@@ -13,7 +13,7 @@ namespace NBitcoin.RPC
{
public static class RPCClientExtensions
{
- public static async Task<EstimateSmartFeeResponse> EstimateSmartFeeAsync(this IRPCClient rpc, int confirmationTarget, EstimateSmartFeeMode estimateMode = EstimateSmartFeeMode.Conservative, bool simulateIfRegTest = false)
+ public static async Task<EstimateSmartFeeResponse> EstimateSmartFeeAsync(this IRPCClient rpc, int confirmationTarget, EstimateSmartFeeMode estimateMode = EstimateSmartFeeMode.Conservative, bool simulateIfRegTest = false, CancellationToken cancellationToken = default)
{
EstimateSmartFeeResponse result;
if (simulateIfRegTest && rpc.Network == Network.RegTest)
@@ -22,9 +22,9 @@ namespace NBitcoin.RPC
}
else
{
- result = await rpc.EstimateSmartFeeAsync(confirmationTarget, estimateMode).ConfigureAwait(false);
+ result = await rpc.EstimateSmartFeeAsync(confirmationTarget, estimateMode, cancellationToken).ConfigureAwait(false);
- var mempoolInfo = await rpc.GetMempoolInfoAsync().ConfigureAwait(false);
+ var mempoolInfo = await rpc.GetMempoolInfoAsync(cancellationToken).ConfigureAwait(false);
result.FeeRate = FeeRate.Max(mempoolInfo.GetSanityFeeRate(), result.FeeRate);
}
@@ -51,11 +51,11 @@ namespace NBitcoin.RPC
/// <summary>
/// If null is returned, no exception is thrown, so the test was successful.
/// </summary>
- public static async Task<Exception?> TestAsync(this IRPCClient rpc)
+ public static async Task<Exception?> TestAsync(this IRPCClient rpc, CancellationToken cancellationToken = default)
{
try
{
- await rpc.UptimeAsync().ConfigureAwait(false);
+ await rpc.UptimeAsync(cancellationToken).ConfigureAwait(false);
}
catch (Exception ex)
{
@@ -108,7 +108,7 @@ namespace NBitcoin.RPC
.Select(target => batchClient.EstimateSmartFeeAsync(target, estimateMode))
.ToList();
- await batchClient.SendBatchAsync().ConfigureAwait(false);
+ await batchClient.SendBatchAsync(cancel).ConfigureAwait(false);
cancel.ThrowIfCancellationRequested();
try
@@ -196,9 +196,8 @@ namespace NBitcoin.RPC
{
try
{
- var bci = await rpc.GetBlockchainInfoAsync().ConfigureAwait(false);
- cancel.ThrowIfCancellationRequested();
- var pi = await rpc.GetPeersInfoAsync().ConfigureAwait(false);
+ var bci = await rpc.GetBlockchainInfoAsync(cancel).ConfigureAwait(false);
+ var pi = await rpc.GetPeersInfoAsync(cancel).ConfigureAwait(false);
return RpcStatus.Responsive(bci.Headers, bci.Blocks, pi.Length);
}
@@ -209,7 +208,7 @@ namespace NBitcoin.RPC
}
}
- public static async Task<(bool accept, string rejectReason)> TestMempoolAcceptAsync(this IRPCClient rpc, IEnumerable<Coin> coins, int fakeOutputCount, Money feePerInputs, Money feePerOutputs)
+ public static async Task<(bool accept, string rejectReason)> TestMempoolAcceptAsync(this IRPCClient rpc, IEnumerable<Coin> coins, int fakeOutputCount, Money feePerInputs, Money feePerOutputs, CancellationToken cancellationToken)
{
// Check if mempool would accept a fake transaction created with the registered inputs.
// This will catch ascendant/descendant count and size limits for example.
@@ -229,7 +228,7 @@ namespace NBitcoin.RPC
var fakeOutputValue = totalFakeOutputsValue / fakeOutputCount;
fakeTransaction.Outputs.Add(fakeOutputValue, new Key());
}
- MempoolAcceptResult testMempoolAcceptResult = await rpc.TestMempoolAcceptAsync(fakeTransaction).ConfigureAwait(false);
+ MempoolAcceptResult testMempoolAcceptResult = await rpc.TestMempoolAcceptAsync(fakeTransaction, cancellationToken).ConfigureAwait(false);
if (!testMempoolAcceptResult.IsAllowed)
{
@@ -248,9 +247,9 @@ namespace NBitcoin.RPC
/// Gets the transactions that are unconfirmed using getrawmempool.
/// This is efficient when many transaction ids are provided.
/// </summary>
- public static async Task<IEnumerable<uint256>> GetUnconfirmedAsync(this IRPCClient rpc, IEnumerable<uint256> transactionHashes)
+ public static async Task<IEnumerable<uint256>> GetUnconfirmedAsync(this IRPCClient rpc, IEnumerable<uint256> transactionHashes, CancellationToken cancellationToken)
{
- uint256[] unconfirmedTransactionHashes = await rpc.GetRawMempoolAsync().ConfigureAwait(false);
+ uint256[] unconfirmedTransactionHashes = await rpc.GetRawMempoolAsync(cancellationToken).ConfigureAwait(false);
// If there are common elements, then there's unconfirmed.
return transactionHashes.Intersect(unconfirmedTransactionHashes);
@@ -263,17 +262,17 @@ namespace NBitcoin.RPC
/// <param name="includingProvided">Should it include in the result the unconfirmed ones from the provided transactionHashes.</param>
/// <param name="likelyProvidedManyConfirmedOnes">If many provided transactionHashes are not confirmed then it optimizes by doing a check in the beginning of which ones are unconfirmed.</param>
/// <returns>All the dependents of the provided transactionHashes.</returns>
- public static async Task<ISet<uint256>> GetAllDependentsAsync(this IRPCClient rpc, IEnumerable<uint256> transactionHashes, bool includingProvided, bool likelyProvidedManyConfirmedOnes)
+ public static async Task<ISet<uint256>> GetAllDependentsAsync(this IRPCClient rpc, IEnumerable<uint256> transactionHashes, bool includingProvided, bool likelyProvidedManyConfirmedOnes, CancellationToken cancellationToken)
{
IEnumerable<uint256> workingTxHashes = likelyProvidedManyConfirmedOnes // If confirmed txIds are provided, then do a big check first.
- ? await rpc.GetUnconfirmedAsync(transactionHashes).ConfigureAwait(false)
+ ? await rpc.GetUnconfirmedAsync(transactionHashes, cancellationToken).ConfigureAwait(false)
: transactionHashes;
var hashSet = new HashSet<uint256>();
foreach (var txId in workingTxHashes)
{
// Go through all the txIds provided and getmempoolentry to get the dependents and the confirmation status.
- var entry = await rpc.GetMempoolEntryAsync(txId, throwIfNotFound: false).ConfigureAwait(false);
+ var entry = await rpc.GetMempoolEntryAsync(txId, throwIfNotFound: false, cancellationToken).ConfigureAwait(false);
if (entry is { })
{
// If we asked to include the provided transaction hashes into the result then check which ones are confirmed and do so.
@@ -284,7 +283,7 @@ namespace NBitcoin.RPC
// Get all the dependents of all the dependents except the ones we already know of.
var except = entry.Depends.Except(hashSet);
- var dependentsOfDependents = await rpc.GetAllDependentsAsync(except, includingProvided: true, likelyProvidedManyConfirmedOnes: false).ConfigureAwait(false);
+ var dependentsOfDependents = await rpc.GetAllDependentsAsync(except, includingProvided: true, likelyProvidedManyConfirmedOnes: false, cancellationToken).ConfigureAwait(false);
// Add them to the hashset.
hashSet.UnionWith(dependentsOfDependents);

MODIFY
WalletWasabi/WabiSabi/Backend/Rounds/Arena.cs
WalletWasabi/WabiSabi/Backend/Rounds/Arena.cs
@@ -103,11 +103,11 @@ namespace WalletWasabi.WabiSabi.Backend.Rounds
var batchedRpc = Rpc.PrepareBatch();
var aliceCheckingTaskPairs = chunckOfAlices
- .Select(x => (Alice: x, StatusTask: Rpc.GetTxOutAsync(x.Coin.Outpoint.Hash, (int)x.Coin.Outpoint.N, includeMempool: true)))
+ .Select(x => (Alice: x, StatusTask: Rpc.GetTxOutAsync(x.Coin.Outpoint.Hash, (int)x.Coin.Outpoint.N, includeMempool: true, cancellationToken)))
.ToList();
cancellationToken.ThrowIfCancellationRequested();
- await batchedRpc.SendBatchAsync().ConfigureAwait(false);
+ await batchedRpc.SendBatchAsync(cancellationToken).ConfigureAwait(false);
var spendStatusCheckingTasks = aliceCheckingTaskPairs.Select(async x => (x.Alice, Status: await x.StatusTask.ConfigureAwait(false)));
var alices = await Task.WhenAll(spendStatusCheckingTasks).ConfigureAwait(false);
@@ -210,7 +210,7 @@ namespace WalletWasabi.WabiSabi.Backend.Rounds
round.LogInfo($"There are {indistinguishableOutputs.Count(x => x.count == 1)} occurrences of unique outputs.");
// Broadcasting.
- await Rpc.SendRawTransactionAsync(coinjoin).ConfigureAwait(false);
+ await Rpc.SendRawTransactionAsync(coinjoin, cancellationToken).ConfigureAwait(false);
round.WasTransactionBroadcast = true;
round.SetPhase(Phase.Ended);
@@ -257,7 +257,7 @@ namespace WalletWasabi.WabiSabi.Backend.Rounds
private async Task CreateBlameRoundAsync(Round round, CancellationToken cancellationToken)
{
- var feeRate = (await Rpc.EstimateSmartFeeAsync((int)Config.ConfirmationTarget, EstimateSmartFeeMode.Conservative, simulateIfRegTest: true).ConfigureAwait(false)).FeeRate;
+ var feeRate = (await Rpc.EstimateSmartFeeAsync((int)Config.ConfirmationTarget, EstimateSmartFeeMode.Conservative, simulateIfRegTest: true, cancellationToken).ConfigureAwait(false)).FeeRate;
RoundParameters parameters = new(Config, Network, Random, feeRate, blameOf: round, Prison);
Round blameRound = new(parameters);
Rounds.Add(blameRound);
@@ -267,7 +267,7 @@ namespace WalletWasabi.WabiSabi.Backend.Rounds
{
if (!Rounds.Any(x => !x.IsBlameRound && x.Phase == Phase.InputRegistration))
{
- var feeRate = (await Rpc.EstimateSmartFeeAsync((int)Config.ConfirmationTarget, EstimateSmartFeeMode.Conservative, simulateIfRegTest: true).ConfigureAwait(false)).FeeRate;
+ var feeRate = (await Rpc.EstimateSmartFeeAsync((int)Config.ConfirmationTarget, EstimateSmartFeeMode.Conservative, simulateIfRegTest: true, cancellationToken).ConfigureAwait(false)).FeeRate;
RoundParameters roundParams = new(Config, Network, Random, feeRate);
Round r = new(roundParams);
@@ -637,7 +637,7 @@ namespace WalletWasabi.WabiSabi.Backend.Rounds
throw new WabiSabiProtocolException(WabiSabiProtocolErrorCode.InputBanned);
}
- var txOutResponse = await Rpc.GetTxOutAsync(input.Hash, (int)input.N, includeMempool: true).ConfigureAwait(false);
+ var txOutResponse = await Rpc.GetTxOutAsync(input.Hash, (int)input.N, includeMempool: true, cancellationToken).ConfigureAwait(false);
if (txOutResponse is null)
{
throw new WabiSabiProtocolException(WabiSabiProtocolErrorCode.InputSpent);

MODIFY
WalletWasabi/Wallets/P2pBlockProvider.cs
WalletWasabi/Wallets/P2pBlockProvider.cs
@@ -234,7 +234,7 @@ namespace WalletWasabi.Wallets
{
try
{
- var block = await CoreNode.RpcClient.GetBlockAsync(hash).ConfigureAwait(false);
+ var block = await CoreNode.RpcClient.GetBlockAsync(hash, cancellationToken).ConfigureAwait(false);
Logger.LogInfo($"Block acquired from RPC connection: {hash}.");
return block;
}

Use `CancellationToken` in many places