Use GetOrCreate for cache
MODIFY
WalletWasabi.Backend/Controllers/BlockchainController.cs
WalletWasabi.Backend/Controllers/BlockchainController.cs
@@ -141,17 +141,12 @@ namespace WalletWasabi.Backend.Controllers
internal async Task<AllFeeEstimate> GetAllFeeEstimateAsync(EstimateSmartFeeMode mode)
{
var cacheKey = $"{nameof(GetAllFeeEstimateAsync)}_{mode}";
-
- if (!Cache.TryGetValue(cacheKey, out AllFeeEstimate allFee))
+ return await Cache.GetOrCreateAsync(cacheKey, entry =>
{
- allFee = await RpcClient.EstimateAllFeeAsync(mode, simulateIfRegTest: true, tolerateBitcoinCoreBrainfuck: true);
-
- var cacheEntryOptions = new MemoryCacheEntryOptions()
- .SetAbsoluteExpiration(TimeSpan.FromSeconds(500));
+ entry.SetAbsoluteExpiration(TimeSpan.FromSeconds(500));
- Cache.Set(cacheKey, allFee, cacheEntryOptions);
- }
- return allFee;
+ return RpcClient.EstimateAllFeeAsync(mode, simulateIfRegTest: true, tolerateBitcoinCoreBrainfuck: true);
+ });
}
/// <summary>
@@ -188,19 +183,13 @@ namespace WalletWasabi.Backend.Controllers
internal async Task<IEnumerable<string>> GetRawMempoolStringsAsync()
{
var cacheKey = $"{nameof(GetRawMempoolStringsAsync)}";
-
- if (!Cache.TryGetValue(cacheKey, out IEnumerable<string> hashes))
+ return await Cache.GetOrCreateAsync(cacheKey, async entry =>
{
- uint256[] transactionHashes = await Global.RpcClient.GetRawMempoolAsync();
-
- hashes = transactionHashes.Select(x => x.ToString());
-
- var cacheEntryOptions = new MemoryCacheEntryOptions()
- .SetAbsoluteExpiration(TimeSpan.FromSeconds(3));
+ entry.SetAbsoluteExpiration(TimeSpan.FromSeconds(3));
- Cache.Set(cacheKey, hashes, cacheEntryOptions);
- }
- return hashes;
+ uint256[] transactionHashes = await Global.RpcClient.GetRawMempoolAsync();
+ return transactionHashes.Select(x => x.ToString());
+ });
}
/// <summary>
@@ -408,18 +397,12 @@ namespace WalletWasabi.Backend.Controllers
private async Task<EstimateSmartFeeResponse> GetEstimateSmartFeeAsync(int target, EstimateSmartFeeMode mode)
{
var cacheKey = $"{nameof(GetEstimateSmartFeeAsync)}_{target}_{mode}";
-
- if (!Cache.TryGetValue(cacheKey, out EstimateSmartFeeResponse feeResponse))
+ return await Cache.GetOrCreateAsync(cacheKey, entry =>
{
- feeResponse = await RpcClient.EstimateSmartFeeAsync(target, mode, simulateIfRegTest: true, tryOtherFeeRates: true);
-
- var cacheEntryOptions = new MemoryCacheEntryOptions()
- .SetAbsoluteExpiration(TimeSpan.FromSeconds(300));
-
- Cache.Set(cacheKey, feeResponse, cacheEntryOptions);
- }
+ entry.SetAbsoluteExpiration(TimeSpan.FromSeconds(300));
- return feeResponse;
+ return RpcClient.EstimateSmartFeeAsync(target, mode, simulateIfRegTest: true, tryOtherFeeRates: true);
+ });
}
[HttpGet("status")]

MODIFY
WalletWasabi/BitcoinCore/CachedRpcClient.cs
WalletWasabi/BitcoinCore/CachedRpcClient.cs
@@ -40,158 +40,114 @@ namespace WalletWasabi.BitcoinCore
public override async Task<uint256> GetBestBlockHashAsync()
{
string cacheKey = nameof(GetBestBlockHashAsync);
- if (!Cache.TryGetValue(cacheKey, out uint256 blockHash))
- {
- blockHash = await base.GetBestBlockHashAsync().ConfigureAwait(false);
- var cacheEntryOptions = new MemoryCacheEntryOptions()
- .SetSize(1)
- .SetAbsoluteExpiration(TimeSpan.FromSeconds(4)) // The best hash doesn't change so often so, keep in cache for 4 seconds.
- .AddExpirationToken(new CancellationChangeToken(TipChangeCancellationTokenSource.Token));
+ return await Cache.GetOrCreateAsync(cacheKey, entry =>
+ {
+ entry.SetSize(1);
+ entry.SetAbsoluteExpiration(TimeSpan.FromSeconds(4)); // The best hash doesn't change so often so, keep in cache for 4 seconds.
+ entry.AddExpirationToken(new CancellationChangeToken(TipChangeCancellationTokenSource.Token));
- // Save data in cache.
- Cache.Set(cacheKey, blockHash, cacheEntryOptions);
- }
- return blockHash;
+ return base.GetBestBlockHashAsync();
+ }).ConfigureAwait(false);
}
public override async Task<Block> GetBlockAsync(uint256 blockHash)
{
string cacheKey = $"{nameof(GetBlockAsync)}:{blockHash}";
- if (!Cache.TryGetValue(cacheKey, out Block block))
+ return await Cache.GetOrCreateAsync(cacheKey, entry =>
{
- block = await base.GetBlockAsync(blockHash).ConfigureAwait(false);
+ entry.SetSize(10);
+ entry.SetAbsoluteExpiration(TimeSpan.FromSeconds(4)); // There is a block every 10 minutes in average so, keep in cache for 4 seconds.
- var cacheEntryOptions = new MemoryCacheEntryOptions()
- .SetSize(10)
- .SetAbsoluteExpiration(TimeSpan.FromSeconds(4)); // There is a block every 10 minutes in average so, keep in cache for 4 seconds.
-
- // Save data in cache.
- Cache.Set(cacheKey, block, cacheEntryOptions);
- }
- return block;
+ return base.GetBlockAsync(blockHash);
+ }).ConfigureAwait(false);
}
public override async Task<Block> GetBlockAsync(uint blockHeight)
{
string cacheKey = $"{nameof(GetBlockAsync)}:{blockHeight}";
- if (!Cache.TryGetValue(cacheKey, out Block block))
+ return await Cache.GetOrCreateAsync(cacheKey, entry =>
{
- block = await base.GetBlockAsync(blockHeight).ConfigureAwait(false);
+ entry.SetSize(10);
+ entry.SetAbsoluteExpiration(TimeSpan.FromSeconds(4)); // There is a block every 10 minutes in average so, keep in cache for 4 seconds.
- var cacheEntryOptions = new MemoryCacheEntryOptions()
- .SetSize(10)
- .SetAbsoluteExpiration(TimeSpan.FromSeconds(4)); // There is a block every 10 minutes in average so, keep in cache for 4 seconds.
-
- // Save data in cache.
- Cache.Set(cacheKey, block, cacheEntryOptions);
- }
- return block;
+ return base.GetBlockAsync(blockHeight);
+ }).ConfigureAwait(false);
}
public override async Task<BlockHeader> GetBlockHeaderAsync(uint256 blockHash)
{
string cacheKey = $"{nameof(GetBlockHeaderAsync)}:{blockHash}";
- if (!Cache.TryGetValue(cacheKey, out BlockHeader blockHeader))
+ return await Cache.GetOrCreateAsync(cacheKey, entry =>
{
- blockHeader = await base.GetBlockHeaderAsync(blockHash).ConfigureAwait(false);
-
- var cacheEntryOptions = new MemoryCacheEntryOptions()
- .SetSize(2)
- .SetAbsoluteExpiration(TimeSpan.FromSeconds(4)); // There is a block every 10 minutes in average so, keep in cache for 4 seconds.
+ entry.SetSize(2);
+ entry.SetAbsoluteExpiration(TimeSpan.FromSeconds(4)); // There is a block every 10 minutes in average so, keep in cache for 4 seconds.
- // Save data in cache.
- Cache.Set(cacheKey, blockHeader, cacheEntryOptions);
- }
- return blockHeader;
+ return base.GetBlockHeaderAsync(blockHash);
+ }).ConfigureAwait(false);
}
public override async Task<int> GetBlockCountAsync()
{
string cacheKey = nameof(GetBlockCountAsync);
- if (!Cache.TryGetValue(cacheKey, out int blockCount))
+ return await Cache.GetOrCreateAsync(cacheKey, entry =>
{
- blockCount = await base.GetBlockCountAsync().ConfigureAwait(false);
-
- var cacheEntryOptions = new MemoryCacheEntryOptions()
- .SetSize(1)
- .SetAbsoluteExpiration(TimeSpan.FromSeconds(2)) // The blockchain info does not change frequently.
- .AddExpirationToken(new CancellationChangeToken(TipChangeCancellationTokenSource.Token));
+ entry.SetSize(1);
+ entry.SetAbsoluteExpiration(TimeSpan.FromSeconds(2)); // The blockchain info does not change frequently.
+ entry.AddExpirationToken(new CancellationChangeToken(TipChangeCancellationTokenSource.Token));
- // Save data in cache.
- Cache.Set(cacheKey, blockCount, cacheEntryOptions);
- }
- return blockCount;
+ return base.GetBlockCountAsync();
+ }).ConfigureAwait(false);
}
public override async Task<PeerInfo[]> GetPeersInfoAsync()
{
string cacheKey = nameof(GetPeersInfoAsync);
- if (!Cache.TryGetValue(cacheKey, out PeerInfo[] peers))
+ return await Cache.GetOrCreateAsync(cacheKey, entry =>
{
- peers = await base.GetPeersInfoAsync().ConfigureAwait(false);
-
- var cacheEntryOptions = new MemoryCacheEntryOptions()
- .SetSize(2)
- .SetAbsoluteExpiration(TimeSpan.FromSeconds(0.5));
+ entry.SetSize(2);
+ entry.SetAbsoluteExpiration(TimeSpan.FromSeconds(0.5));
- // Save data in cache.
- Cache.Set(cacheKey, peers, cacheEntryOptions);
- }
- return peers;
+ return base.GetPeersInfoAsync();
+ }).ConfigureAwait(false);
}
public override async Task<MempoolEntry> GetMempoolEntryAsync(uint256 txid, bool throwIfNotFound = true)
{
string cacheKey = $"{nameof(GetMempoolEntryAsync)}:{txid}";
- if (!Cache.TryGetValue(cacheKey, out MempoolEntry mempoolEntry))
+ return await Cache.GetOrCreateAsync(cacheKey, entry =>
{
- mempoolEntry = await base.GetMempoolEntryAsync(txid, throwIfNotFound).ConfigureAwait(false);
+ entry.SetSize(20);
+ entry.SetAbsoluteExpiration(TimeSpan.FromSeconds(2));
+ entry.AddExpirationToken(new CancellationChangeToken(TipChangeCancellationTokenSource.Token));
- var cacheEntryOptions = new MemoryCacheEntryOptions()
- .SetSize(20)
- .SetAbsoluteExpiration(TimeSpan.FromSeconds(2))
- .AddExpirationToken(new CancellationChangeToken(TipChangeCancellationTokenSource.Token));
-
- // Save data in cache.
- Cache.Set(cacheKey, mempoolEntry, cacheEntryOptions);
- }
- return mempoolEntry;
+ return base.GetMempoolEntryAsync(txid, throwIfNotFound);
+ }).ConfigureAwait(false);
}
public override async Task<uint256[]> GetRawMempoolAsync()
{
string cacheKey = nameof(GetRawMempoolAsync);
- if (!Cache.TryGetValue(cacheKey, out uint256[] mempool))
+ return await Cache.GetOrCreateAsync(cacheKey, entry =>
{
- mempool = await base.GetRawMempoolAsync().ConfigureAwait(false);
+ entry.SetSize(20);
+ entry.SetAbsoluteExpiration(TimeSpan.FromSeconds(2));
+ entry.AddExpirationToken(new CancellationChangeToken(TipChangeCancellationTokenSource.Token));
- var cacheEntryOptions = new MemoryCacheEntryOptions()
- .SetSize(20)
- .SetAbsoluteExpiration(TimeSpan.FromSeconds(2))
- .AddExpirationToken(new CancellationChangeToken(TipChangeCancellationTokenSource.Token));
-
- // Save data in cache.
- Cache.Set(cacheKey, mempool, cacheEntryOptions);
- }
- return mempool;
+ return base.GetRawMempoolAsync();
+ }).ConfigureAwait(false);
}
- public override GetTxOutResponse GetTxOut(uint256 txid, int index, bool includeMempool = true)
+ public override async Task<GetTxOutResponse> GetTxOutAsync(uint256 txid, int index, bool includeMempool = true)
{
- string cacheKey = $"{nameof(GetTxOut)}:{txid}:{index}:{includeMempool}";
- if (!Cache.TryGetValue(cacheKey, out GetTxOutResponse txout))
+ string cacheKey = $"{nameof(GetTxOutAsync)}:{txid}:{index}:{includeMempool}";
+ return await Cache.GetOrCreateAsync(cacheKey, entry =>
{
- txout = base.GetTxOut(txid, index, includeMempool);
+ entry.SetSize(2);
+ entry.SetAbsoluteExpiration(TimeSpan.FromSeconds(2));
- var cacheEntryOptions = new MemoryCacheEntryOptions()
- .SetSize(2)
- .SetAbsoluteExpiration(TimeSpan.FromSeconds(2));
-
- // Save data in cache.
- Cache.Set(cacheKey, txout, cacheEntryOptions);
- }
- return txout;
+ return base.GetTxOutAsync(txid, index, includeMempool);
+ }).ConfigureAwait(false);
}
public override async Task<uint256[]> GenerateAsync(int blockCount)

MODIFY
WalletWasabi/BitcoinCore/IRPCClient.cs
WalletWasabi/BitcoinCore/IRPCClient.cs
@@ -37,8 +37,6 @@ namespace WalletWasabi.BitcoinCore
Task<MempoolAcceptResult> TestMempoolAcceptAsync(Transaction transaction, bool allowHighFees = false);
- GetTxOutResponse GetTxOut(uint256 txid, int index, bool includeMempool = true);
-
Task<EstimateSmartFeeResponse> EstimateSmartFeeAsync(int confirmationTarget, EstimateSmartFeeMode estimateMode = EstimateSmartFeeMode.Conservative);
Task<GetTxOutResponse> GetTxOutAsync(uint256 txid, int index, bool includeMempool = true);

MODIFY
WalletWasabi/BitcoinCore/RpcClientBase.cs
WalletWasabi/BitcoinCore/RpcClientBase.cs
@@ -60,9 +60,9 @@ namespace WalletWasabi.BitcoinCore
return await Rpc.GetRawMempoolAsync().ConfigureAwait(false);
}
- public virtual GetTxOutResponse GetTxOut(uint256 txid, int index, bool includeMempool = true)
+ public virtual async Task<GetTxOutResponse> GetTxOutAsync(uint256 txid, int index, bool includeMempool = true)
{
- return Rpc.GetTxOut(txid, index, includeMempool);
+ return await Rpc.GetTxOutAsync(txid, index, includeMempool).ConfigureAwait(false);
}
public virtual async Task<MempoolAcceptResult> TestMempoolAcceptAsync(Transaction transaction, bool allowHighFees = false)
@@ -153,11 +153,6 @@ namespace WalletWasabi.BitcoinCore
return await Rpc.GetNewAddressAsync().ConfigureAwait(false);
}
- public virtual async Task<GetTxOutResponse> GetTxOutAsync(uint256 txid, int index, bool includeMempool = true)
- {
- return await Rpc.GetTxOutAsync(txid, index, includeMempool).ConfigureAwait(false);
- }
-
public virtual async Task<SignRawTransactionResponse> SignRawTransactionWithWalletAsync(SignRawTransactionRequest request)
{
return await Rpc.SignRawTransactionWithWalletAsync(request).ConfigureAwait(false);

MODIFY
WalletWasabi/CoinJoin/Coordinator/Banning/UtxoReferee.cs
WalletWasabi/CoinJoin/Coordinator/Banning/UtxoReferee.cs
@@ -37,7 +37,7 @@ namespace WalletWasabi.CoinJoin.Coordinator.Banning
{
var bannedRecord = BannedUtxo.FromString(line);
- GetTxOutResponse getTxOutResponse = RpcClient.GetTxOut(bannedRecord.Utxo.Hash, (int)bannedRecord.Utxo.N, includeMempool: true);
+ GetTxOutResponse getTxOutResponse = RpcClient.GetTxOutAsync(bannedRecord.Utxo.Hash, (int)bannedRecord.Utxo.N, includeMempool: true).GetAwaiter().GetResult();
// Check if inputs are unspent.
if (getTxOutResponse is null)

MODIFY
WalletWasabi/Wallets/SmartBlockProvider.cs
WalletWasabi/Wallets/SmartBlockProvider.cs
@@ -29,20 +29,15 @@ namespace WalletWasabi.Wallets
{
lock (Lock)
{
- string cacheKey = $"{nameof(SmartBlockProvider):nameof(GetBlockAsync)}:{blockHash}";
- if (!Cache.TryGetValue(cacheKey, out Task<Block> getBlockTask))
+ string cacheKey = $"{nameof(SmartBlockProvider)}:{nameof(GetBlockAsync)}:{blockHash}";
+ return Cache.GetOrCreate(cacheKey, entry =>
{
- getBlockTask = InnerBlockProvider.GetBlockAsync(blockHash, cancel);
+ entry.SetSize(10);
+ entry.SetSlidingExpiration(TimeSpan.FromSeconds(4));
+ entry.RegisterPostEvictionCallback(callback: EvictionCallback, state: this);
- var cacheEntryOptions = new MemoryCacheEntryOptions()
- .SetSize(10)
- .SetSlidingExpiration(TimeSpan.FromSeconds(4))
- .RegisterPostEvictionCallback(callback: EvictionCallback, state: this);
-
- // Save data in cache.
- Cache.Set(cacheKey, getBlockTask, cacheEntryOptions);
- }
- return getBlockTask;
+ return InnerBlockProvider.GetBlockAsync(blockHash, cancel);
+ });
}
}

Use GetOrCreate for cache