Finish TransactionStore implementation
MODIFY
WalletWasabi.Tests/UnitTests/TransactionProcessorTests.cs
WalletWasabi.Tests/UnitTests/TransactionProcessorTests.cs
using NBitcoin;
using System;
+using System.IO;
using System.Linq;
+using System.Runtime.CompilerServices;
+using System.Threading.Tasks;
using WalletWasabi.KeyManagement;
using WalletWasabi.Models;
using WalletWasabi.Services;
+using WalletWasabi.Transactions;
using Xunit;
namespace WalletWasabi.Tests.UnitTests
@@ -11,9 +15,9 @@ namespace WalletWasabi.Tests.UnitTests
public class TransactionProcessorTests
{
[Fact]
- public void TransactionDoesNotCointainCoinsForTheWallet()
+ public async Task TransactionDoesNotCointainCoinsForTheWalletAsync()
{
- var transactionProcessor = CreateTransactionProcessor();
+ var transactionProcessor = await CreateTransactionProcessorAsync();
// This transaction doesn't have any coin for the wallet. It is not relevant.
var tx = CreateCreditingTransaction(new Key().PubKey.WitHash.ScriptPubKey, Money.Coins(1.0m));
@@ -22,13 +26,14 @@ namespace WalletWasabi.Tests.UnitTests
Assert.False(relevant);
Assert.Empty(transactionProcessor.Coins);
- Assert.Empty(transactionProcessor.TransactionCache);
+ Assert.True(transactionProcessor.TransactionStore.MempoolStore.IsEmpty());
+ Assert.True(transactionProcessor.TransactionStore.ConfirmedStore.IsEmpty());
}
[Fact]
- public void SpendToLegacyScripts()
+ public async Task SpendToLegacyScriptsAsync()
{
- var transactionProcessor = CreateTransactionProcessor();
+ var transactionProcessor = await CreateTransactionProcessorAsync();
var keys = transactionProcessor.KeyManager.GetKeys().ToArray();
// A payment to a key under our control but using P2PKH script (legacy)
@@ -40,9 +45,9 @@ namespace WalletWasabi.Tests.UnitTests
}
[Fact]
- public void UnconfirmedTransactionIsNotSegWit()
+ public async Task UnconfirmedTransactionIsNotSegWitAsync()
{
- var transactionProcessor = CreateTransactionProcessor();
+ var transactionProcessor = await CreateTransactionProcessorAsync();
// No segwit transaction. Ignore it.
var tx = CreateCreditingTransaction(new Key().PubKey.Hash.ScriptPubKey, Money.Coins(1.0m));
@@ -51,13 +56,14 @@ namespace WalletWasabi.Tests.UnitTests
Assert.False(relevant);
Assert.Empty(transactionProcessor.Coins);
- Assert.Empty(transactionProcessor.TransactionCache);
+ Assert.True(transactionProcessor.TransactionStore.MempoolStore.IsEmpty());
+ Assert.True(transactionProcessor.TransactionStore.ConfirmedStore.IsEmpty());
}
[Fact]
- public void ConfirmedTransactionIsNotSegWit()
+ public async Task ConfirmedTransactionIsNotSegWitAsync()
{
- var transactionProcessor = CreateTransactionProcessor();
+ var transactionProcessor = await CreateTransactionProcessorAsync();
// No segwit transaction. Ignore it.
var tx = CreateCreditingTransaction(new Key().PubKey.Hash.ScriptPubKey, Money.Coins(1.0m), isConfirmed: true);
@@ -66,13 +72,14 @@ namespace WalletWasabi.Tests.UnitTests
Assert.False(relevant);
Assert.Empty(transactionProcessor.Coins);
- Assert.Empty(transactionProcessor.TransactionCache);
+ Assert.True(transactionProcessor.TransactionStore.MempoolStore.IsEmpty());
+ Assert.True(transactionProcessor.TransactionStore.ConfirmedStore.IsEmpty());
}
[Fact]
- public void UpdateTransactionHeightAfterConfirmation()
+ public async Task UpdateTransactionHeightAfterConfirmationAsync()
{
- var transactionProcessor = CreateTransactionProcessor();
+ var transactionProcessor = await CreateTransactionProcessorAsync();
// An unconfirmed segwit transaction for us
var key = transactionProcessor.KeyManager.GetKeys().First();
@@ -80,7 +87,8 @@ namespace WalletWasabi.Tests.UnitTests
var tx = CreateCreditingTransaction(key.PubKey.WitHash.ScriptPubKey, Money.Coins(1.0m));
transactionProcessor.Process(tx);
- var cachedTx = Assert.Single(transactionProcessor.TransactionCache);
+ Assert.True(transactionProcessor.TransactionStore.ConfirmedStore.IsEmpty());
+ var cachedTx = Assert.Single(transactionProcessor.TransactionStore.MempoolStore.GetTransactions());
var coin = Assert.Single(transactionProcessor.Coins);
Assert.Equal(Height.Mempool, cachedTx.Height);
Assert.Equal(Height.Mempool, coin.Height);
@@ -92,16 +100,17 @@ namespace WalletWasabi.Tests.UnitTests
Assert.True(relevant);
Assert.Single(transactionProcessor.Coins);
- cachedTx = Assert.Single(transactionProcessor.TransactionCache);
+ Assert.True(transactionProcessor.TransactionStore.MempoolStore.IsEmpty());
+ cachedTx = Assert.Single(transactionProcessor.TransactionStore.ConfirmedStore.GetTransactions());
Assert.NotEqual(Height.Mempool, cachedTx.Height);
coin = Assert.Single(transactionProcessor.Coins);
Assert.Equal(blockHeight, coin.Height);
}
[Fact]
- public void IgnoreDoubleSpend()
+ public async Task IgnoreDoubleSpendAsync()
{
- var transactionProcessor = CreateTransactionProcessor();
+ var transactionProcessor = await CreateTransactionProcessorAsync();
var keys = transactionProcessor.KeyManager.GetKeys().ToArray();
@@ -121,13 +130,15 @@ namespace WalletWasabi.Tests.UnitTests
Assert.False(relevant);
Assert.Single(transactionProcessor.Coins, coin => coin.Unspent);
Assert.Single(transactionProcessor.Coins, coin => !coin.Unspent);
- Assert.Equal(2, transactionProcessor.TransactionCache.Count());
+ Assert.Equal(
+ 2,
+ transactionProcessor.TransactionStore.GetTransactions().Count());
}
[Fact]
- public void ConfirmedDoubleSpend()
+ public async Task ConfirmedDoubleSpendAsync()
{
- var transactionProcessor = CreateTransactionProcessor();
+ var transactionProcessor = await CreateTransactionProcessorAsync();
var keys = transactionProcessor.KeyManager.GetKeys().ToArray();
@@ -150,9 +161,9 @@ namespace WalletWasabi.Tests.UnitTests
}
[Fact]
- public void HandlesRBF()
+ public async Task HandlesRbfAsync()
{
- var transactionProcessor = CreateTransactionProcessor();
+ var transactionProcessor = await CreateTransactionProcessorAsync();
var keys = transactionProcessor.KeyManager.GetKeys().ToArray();
@@ -178,9 +189,9 @@ namespace WalletWasabi.Tests.UnitTests
}
[Fact]
- public void ReceiveTransactionForWallet()
+ public async Task ReceiveTransactionForWalletAsync()
{
- var transactionProcessor = CreateTransactionProcessor();
+ var transactionProcessor = await CreateTransactionProcessorAsync();
SmartCoin receivedCoin = null;
transactionProcessor.CoinReceived += (s, theCoin) => receivedCoin = theCoin;
var keys = transactionProcessor.KeyManager.GetKeys();
@@ -192,14 +203,14 @@ namespace WalletWasabi.Tests.UnitTests
Assert.True(relevant);
var coin = Assert.Single(transactionProcessor.Coins);
Assert.Equal(Money.Coins(1.0m), coin.Amount);
- Assert.Contains(transactionProcessor.TransactionCache, x => x == tx);
+ Assert.True(transactionProcessor.TransactionStore.TryGetTransaction(tx.GetHash(), out _));
Assert.NotNull(receivedCoin);
}
[Fact]
- public void SpendCoin()
+ public async Task SpendCoinAsync()
{
- var transactionProcessor = CreateTransactionProcessor();
+ var transactionProcessor = await CreateTransactionProcessorAsync();
SmartCoin spentCoin = null;
transactionProcessor.CoinSpent += (s, theCoin) => spentCoin = theCoin;
var keys = transactionProcessor.KeyManager.GetKeys();
@@ -219,9 +230,9 @@ namespace WalletWasabi.Tests.UnitTests
}
[Fact]
- public void ReceiveTransactionWithDustForWallet()
+ public async Task ReceiveTransactionWithDustForWalletAsync()
{
- var transactionProcessor = CreateTransactionProcessor();
+ var transactionProcessor = await CreateTransactionProcessorAsync();
transactionProcessor.CoinReceived += (s, theCoin)
=> throw new Exception("The dust coin raised an event when it shouldn't.");
var keys = transactionProcessor.KeyManager.GetKeys();
@@ -235,9 +246,9 @@ namespace WalletWasabi.Tests.UnitTests
}
[Fact]
- public void ReceiveCoinJoinTransaction()
+ public async Task ReceiveCoinJoinTransactionAsync()
{
- var transactionProcessor = CreateTransactionProcessor();
+ var transactionProcessor = await CreateTransactionProcessorAsync();
var keys = transactionProcessor.KeyManager.GetKeys();
var amount = Money.Coins(0.1m);
@@ -261,9 +272,9 @@ namespace WalletWasabi.Tests.UnitTests
}
[Fact]
- public void ReceiveWasabiCoinJoinTransaction()
+ public async Task ReceiveWasabiCoinJoinTransactionAsync()
{
- var transactionProcessor = CreateTransactionProcessor();
+ var transactionProcessor = await CreateTransactionProcessorAsync();
var keys = transactionProcessor.KeyManager.GetKeys();
var amount = Money.Coins(0.1m);
@@ -291,15 +302,21 @@ namespace WalletWasabi.Tests.UnitTests
Assert.True(coin.IsLikelyCoinJoinOutput); // because we are spanding and receiving almost the same amount
}
- private static TransactionProcessor CreateTransactionProcessor()
+ private async Task<TransactionProcessor> CreateTransactionProcessorAsync([CallerMemberName] string callerName = "")
{
var keyManager = KeyManager.CreateNew(out _, "password");
keyManager.AssertCleanKeysIndexed();
+
+ var txStore = new AllTransactionStore();
+ var dir = Path.Combine(Global.Instance.DataDir, callerName, "TransactionStore");
+ await IoHelpers.DeleteRecursivelyWithMagicDustAsync(dir);
+ await txStore.InitializeAsync(dir, Network.RegTest);
+
return new TransactionProcessor(
+ txStore,
keyManager,
new ObservableConcurrentHashSet<SmartCoin>(),
- Money.Coins(0.0001m),
- new ConcurrentHashSet<SmartTransaction>());
+ Money.Coins(0.0001m));
}
private static SmartTransaction CreateSpendingTransaction(Coin coin, Script scriptPubKey = null, bool isConfirmed = false)

MODIFY
WalletWasabi.Tests/UnitTests/Transactions/TransactionStoreTests.cs
WalletWasabi.Tests/UnitTests/Transactions/TransactionStoreTests.cs
@@ -61,8 +61,8 @@ namespace WalletWasabi.Tests.UnitTests.Transactions
txStore.TryAdd(stx);
txStore.TryAdd(stx);
- Assert.Equal(1, txStore.GetTransactions().Count);
- Assert.Equal(1, txStore.GetTransactionHashes().Count);
+ Assert.Single(txStore.GetTransactions());
+ Assert.Single(txStore.GetTransactionHashes());
}
}
}

MODIFY
WalletWasabi/Services/TransactionHistoryBuilder.cs
WalletWasabi/Services/TransactionHistoryBuilder.cs
@@ -29,8 +29,7 @@ namespace WalletWasabi.Services
?? new Dictionary<Height, DateTimeOffset>();
foreach (SmartCoin coin in walletService.Coins)
{
- var foundTransaction = walletService.TryGetTxFromCache(coin.TransactionId);
- if (foundTransaction is null)
+ if (!walletService.BitcoinStore.TransactionStore.TryGetTransaction(coin.SpenderTransactionId, out SmartTransaction foundTransaction))
{
continue;
}
@@ -55,25 +54,26 @@ namespace WalletWasabi.Services
var found = txRecordList.FirstOrDefault(x => x.TransactionId == coin.TransactionId);
if (found != null) // if found then update
{
- var label = found.Label != string.Empty ? found.Label + ", " : "";
+ var label = !string.IsNullOrEmpty(found.Label) ? found.Label + ", " : "";
found.DateTime = dateTime;
found.Amount += coin.Amount;
found.Label = $"{label}{coin.Label}";
}
else
{
- txRecordList.Add(new TransactionSummary{
+ txRecordList.Add(new TransactionSummary
+ {
DateTime = dateTime,
Height = coin.Height,
Amount = coin.Amount,
Label = coin.Label.ToString(),
- TransactionId = coin.TransactionId});
+ TransactionId = coin.TransactionId
+ });
}
if (!coin.Unspent)
{
- var foundSpenderTransaction = walletService.TryGetTxFromCache(coin.SpenderTransactionId);
- if (foundSpenderTransaction is null)
+ if (!walletService.BitcoinStore.TransactionStore.TryGetTransaction(coin.SpenderTransactionId, out SmartTransaction foundSpenderTransaction))
{
throw new InvalidOperationException($"Transaction {coin.SpenderTransactionId} not found.");
}
@@ -102,12 +102,14 @@ namespace WalletWasabi.Services
}
else
{
- txRecordList.Add(new TransactionSummary{
+ txRecordList.Add(new TransactionSummary
+ {
DateTime = dateTime,
Height = foundSpenderTransaction.Height,
Amount = (Money.Zero - coin.Amount),
Label = "",
- TransactionId = coin.SpenderTransactionId});
+ TransactionId = coin.SpenderTransactionId
+ });
}
}
}

MODIFY
WalletWasabi/Services/TransactionProcessor.cs
WalletWasabi/Services/TransactionProcessor.cs
@@ -5,12 +5,13 @@ using NBitcoin;
using WalletWasabi.Helpers;
using WalletWasabi.KeyManagement;
using WalletWasabi.Models;
+using WalletWasabi.Transactions;
namespace WalletWasabi.Services
{
public class TransactionProcessor
{
- public ConcurrentHashSet<SmartTransaction> TransactionCache { get; }
+ public AllTransactionStore TransactionStore { get; }
public KeyManager KeyManager { get; }
@@ -23,15 +24,16 @@ namespace WalletWasabi.Services
public event EventHandler<SmartCoin> CoinReceived;
- public TransactionProcessor(KeyManager keyManager,
+ public TransactionProcessor(
+ AllTransactionStore transactionStore,
+ KeyManager keyManager,
ObservableConcurrentHashSet<SmartCoin> coins,
- Money dustThreshold,
- ConcurrentHashSet<SmartTransaction> transactionCache)
+ Money dustThreshold)
{
+ TransactionStore = Guard.NotNull(nameof(transactionStore), transactionStore);
KeyManager = Guard.NotNull(nameof(keyManager), keyManager);
Coins = Guard.NotNull(nameof(coins), coins);
DustThreshold = Guard.NotNull(nameof(dustThreshold), dustThreshold);
- TransactionCache = Guard.NotNull(nameof(transactionCache), transactionCache);
}
public bool Process(SmartTransaction tx)
@@ -42,24 +44,16 @@ namespace WalletWasabi.Services
}
uint256 txId = tx.GetHash();
- if (tx.Confirmed)
- {
- bool isFoundTx = TransactionCache.Contains(tx); // If we have in cache, update height.
- if (isFoundTx)
- {
- SmartTransaction foundTx = TransactionCache.FirstOrDefault(x => x == tx);
- if (foundTx != default) // Must check again, because it's a concurrent collection!
+
+ if (TransactionStore.TryUpdate(tx, out SmartTransaction updatedTx) && updatedTx.Confirmed)
{
- foundTx.Update(tx);
foreach (var coin in Coins.Where(x => x.TransactionId == txId))
{
- coin.Height = tx.Height;
+ coin.Height = updatedTx.Height;
}
return true; // relevant
}
- }
- }
var walletRelevant = false;
@@ -170,7 +164,7 @@ namespace WalletWasabi.Services
// If we did not have it.
if (Coins.TryAdd(newCoin))
{
- TransactionCache.TryAdd(tx);
+ TransactionStore.TryAdd(tx);
CoinReceived?.Invoke(this, newCoin);
// Make sure there's always 21 clean keys generated and indexed.
@@ -207,7 +201,6 @@ namespace WalletWasabi.Services
walletRelevant = true;
var alreadyKnown = foundCoin.SpenderTransactionId == txId;
foundCoin.SpenderTransactionId = txId;
- TransactionCache.TryAdd(tx);
if (!alreadyKnown)
{
@@ -221,6 +214,11 @@ namespace WalletWasabi.Services
}
}
+ if (walletRelevant)
+ {
+ TransactionStore.TryAdd(tx);
+ }
+
return walletRelevant;
}
}

MODIFY
WalletWasabi/Services/WalletService.cs
WalletWasabi/Services/WalletService.cs
@@ -60,8 +60,6 @@ namespace WalletWasabi.Services
public CcjClient ChaumianClient { get; }
public NodesGroup Nodes { get; }
public string BlocksFolderPath { get; }
- public string TransactionsFolderPath { get; }
- public string TransactionsFilePath { get; }
private AsyncLock HandleFiltersLock { get; }
@@ -83,8 +81,6 @@ namespace WalletWasabi.Services
public TransactionProcessor TransactionProcessor { get; }
- private ConcurrentHashSet<SmartTransaction> TransactionCache { get; }
-
public WalletService(
BitcoinStore bitcoinStore,
KeyManager keyManager,
@@ -107,7 +103,6 @@ namespace WalletWasabi.Services
Coins = new ObservableConcurrentHashSet<SmartCoin>();
BlocksFolderPath = Path.Combine(workFolderDir, "Blocks", Network.ToString());
- TransactionsFolderPath = Path.Combine(workFolderDir, "Transactions", Network.ToString());
RuntimeParams.SetDataDir(workFolderDir);
BlockFolderLock = new AsyncLock();
@@ -115,9 +110,7 @@ namespace WalletWasabi.Services
KeyManager.AssertCleanKeysIndexed();
KeyManager.AssertLockedInternalKeysIndexed(14);
- TransactionCache = new ConcurrentHashSet<SmartTransaction>();
-
- TransactionProcessor = new TransactionProcessor(KeyManager, Coins, ServiceConfiguration.DustThreshold, TransactionCache);
+ TransactionProcessor = new TransactionProcessor(BitcoinStore.TransactionStore, KeyManager, Coins, ServiceConfiguration.DustThreshold);
TransactionProcessor.CoinSpent += TransactionProcessor_CoinSpent;
TransactionProcessor.CoinReceived += TransactionProcessor_CoinReceivedAsync;
@@ -134,25 +127,11 @@ namespace WalletWasabi.Services
Directory.CreateDirectory(BlocksFolderPath);
}
- if (Directory.Exists(TransactionsFolderPath))
- {
- if (Synchronizer.Network == Network.RegTest)
- {
- Directory.Delete(TransactionsFolderPath, true);
- Directory.CreateDirectory(TransactionsFolderPath);
- }
- }
- else
- {
- Directory.CreateDirectory(TransactionsFolderPath);
- }
-
var walletName = "UnnamedWallet";
if (!string.IsNullOrWhiteSpace(KeyManager.FilePath))
{
walletName = Path.GetFileNameWithoutExtension(KeyManager.FilePath);
}
- TransactionsFilePath = Path.Combine(TransactionsFolderPath, $"{walletName}Transactions.json");
BitcoinStore.IndexStore.NewFilter += IndexDownloader_NewFilterAsync;
BitcoinStore.IndexStore.Reorged += IndexDownloader_ReorgedAsync;
@@ -195,12 +174,6 @@ namespace WalletWasabi.Services
Coins.TryRemove(toAlsoRemove);
}
}
-
- var txToRemove = TryGetTxFromCache(toRemove.TransactionId);
- if (txToRemove != default)
- {
- TransactionCache.TryRemove(txToRemove);
- }
}
}
@@ -215,10 +188,7 @@ namespace WalletWasabi.Services
{
lock (TransactionProcessingLock)
{
- if (TransactionProcessor.Process(tx))
- {
- SerializeTransactionCache();
- }
+ TransactionProcessor.Process(tx);
}
}
catch (Exception ex)
@@ -298,57 +268,21 @@ namespace WalletWasabi.Services
using (await HandleFiltersLock.LockAsync())
{
- var unconfirmedTransactions = new SmartTransaction[0];
- var confirmedTransactions = new SmartTransaction[0];
-
- // Fetch previous wallet state from transactions file
- if (File.Exists(TransactionsFilePath))
- {
- try
- {
- IEnumerable<SmartTransaction> transactions = null;
- string jsonString = File.ReadAllText(TransactionsFilePath, Encoding.UTF8);
- transactions = JsonConvert.DeserializeObject<IEnumerable<SmartTransaction>>(jsonString)?.OrderByBlockchain();
-
- confirmedTransactions = transactions?.Where(x => x.Confirmed)?.ToArray() ?? new SmartTransaction[0];
-
- unconfirmedTransactions = transactions?.Where(x => !x.Confirmed)?.ToArray() ?? new SmartTransaction[0];
- }
- catch (Exception ex)
- {
- Logger.LogWarning(ex);
- Logger.LogWarning($"Transaction cache got corrupted. Deleting {TransactionsFilePath}.");
- File.Delete(TransactionsFilePath);
- }
- }
-
- await LoadWalletStateAsync(confirmedTransactions, cancel);
-
- // Load in dummy mempool
- try
- {
- if (unconfirmedTransactions != null && unconfirmedTransactions.Any())
- {
- await LoadDummyMempoolAsync(unconfirmedTransactions);
- }
- }
- finally
- {
- UnconfirmedTransactionsInitialized = true;
- }
+ await LoadWalletStateAsync(cancel);
+ await LoadDummyMempoolAsync();
}
Coins.CollectionChanged += Coins_CollectionChanged;
RefreshCoinHistories();
}
- private async Task LoadWalletStateAsync(SmartTransaction[] confirmedTransactions, CancellationToken cancel)
+ private async Task LoadWalletStateAsync(CancellationToken cancel)
{
KeyManager.AssertNetworkOrClearBlockState(Network);
Height bestKeyManagerHeight = KeyManager.GetBestHeight();
foreach (BlockState blockState in KeyManager.GetTransactionIndex())
{
- var relevantTransactions = confirmedTransactions.Where(x => x.BlockHash == blockState.BlockHash).ToArray();
+ var relevantTransactions = BitcoinStore.TransactionStore.ConfirmedStore.GetTransactions().Where(x => x.BlockHash == blockState.BlockHash).ToArray();
var block = await FetchBlockAsync(blockState.BlockHash, cancel);
ProcessBlock(blockState.BlockHeight, block, blockState.TransactionIndices, relevantTransactions);
}
@@ -364,8 +298,13 @@ namespace WalletWasabi.Services
new Height(bestKeyManagerHeight.Value + 1));
}
- private async Task LoadDummyMempoolAsync(SmartTransaction[] unconfirmedTransactions)
+ private async Task LoadDummyMempoolAsync()
{
+ if (BitcoinStore.TransactionStore.MempoolStore.IsEmpty())
+ {
+ return;
+ }
+
try
{
using var client = new WasabiClient(Synchronizer.WasabiClient.TorClient.DestinationUriAction, Synchronizer.WasabiClient.TorClient.TorSocks5EndPoint);
@@ -375,28 +314,26 @@ namespace WalletWasabi.Services
lock (TransactionProcessingLock)
{
- var count = 0;
- foreach (var tx in unconfirmedTransactions)
+ foreach (var tx in BitcoinStore.TransactionStore.MempoolStore.GetTransactions())
{
- if (mempoolHashes.Contains(tx.GetHash().ToString().Substring(0, compactness)))
+ uint256 hash = tx.GetHash();
+ if (mempoolHashes.Contains(hash.ToString().Substring(0, compactness)))
{
TransactionProcessor.Process(tx);
- Logger.LogInfo($"Transaction was successfully tested against the backend's mempool hashes: {tx.GetHash()}.");
- count++;
+ Logger.LogInfo($"Transaction was successfully tested against the backend's mempool hashes: {hash}.");
}
- }
-
- if (count != unconfirmedTransactions.Length)
+ else
{
- SerializeTransactionCache();
+ BitcoinStore.TransactionStore.MempoolStore.TryRemove(tx.GetHash(), out _);
+ }
}
}
}
catch (Exception ex)
{
// When there's a connection failure do not clean the transactions, add them to processing.
- foreach (var tx in unconfirmedTransactions)
+ foreach (var tx in BitcoinStore.TransactionStore.MempoolStore.GetTransactions())
{
TransactionProcessor.Process(tx);
}
@@ -419,11 +356,7 @@ namespace WalletWasabi.Services
}
Block currentBlock = await FetchBlockAsync(filterModel.BlockHash, cancel); // Wait until not downloaded.
-
- if (ProcessBlock(filterModel.BlockHeight, currentBlock))
- {
- SerializeTransactionCache();
- }
+ ProcessBlock(filterModel.BlockHeight, currentBlock);
}
public HdPubKey GetReceiveKey(SmartLabel label, IEnumerable<HdPubKey> dontTouch = null)
@@ -518,9 +451,8 @@ namespace WalletWasabi.Services
return clusters;
}
- private bool ProcessBlock(Height height, Block block, IEnumerable<int> filterByTxIndexes = null, IEnumerable<SmartTransaction> skeletonBlock = null)
+ private void ProcessBlock(Height height, Block block, IEnumerable<int> filterByTxIndexes = null, IEnumerable<SmartTransaction> skeletonBlock = null)
{
- var ret = false;
lock (TransactionProcessingLock)
{
if (filterByTxIndexes is null)
@@ -532,7 +464,6 @@ namespace WalletWasabi.Services
if (TransactionProcessor.Process(new SmartTransaction(tx, height, block.GetHash(), i)))
{
relevantIndices.Add(i);
- ret = true;
}
}
@@ -551,10 +482,7 @@ namespace WalletWasabi.Services
foreach (var i in filterByTxIndexes.OrderBy(x => x))
{
var tx = skeletonBlock?.FirstOrDefault(x => x.BlockIndex == i) ?? new SmartTransaction(block.Transactions[i], height, block.GetHash(), i);
- if (TransactionProcessor.Process(tx))
- {
- ret = true;
- }
+ TransactionProcessor.Process(tx);
}
}
}
@@ -562,8 +490,6 @@ namespace WalletWasabi.Services
ProcessedBlocks.TryAdd(block.GetHash(), (height, block.Header.BlockTime));
NewBlockProcessed?.Invoke(this, block);
-
- return ret;
}
private Node _localBitcoinCoreNode = null;
@@ -1235,10 +1161,7 @@ namespace WalletWasabi.Services
lock (TransactionProcessingLock)
{
- if (TransactionProcessor.Process(new SmartTransaction(transaction.Transaction, Height.Mempool)))
- {
- SerializeTransactionCache();
- }
+ TransactionProcessor.Process(new SmartTransaction(transaction.Transaction, Height.Mempool));
}
Logger.LogInfo($"Transaction is successfully broadcasted to backend: {transaction.GetHash()}.");
@@ -1344,22 +1267,6 @@ namespace WalletWasabi.Services
}
}
- public bool UnconfirmedTransactionsInitialized { get; private set; } = false;
-
- private void SerializeTransactionCache()
- {
- if (!UnconfirmedTransactionsInitialized) // If unconfirmed ones are not yet initialized, then do not serialize because unconfirmed are going to be lost.
- {
- return;
- }
-
- IoHelpers.EnsureContainingDirectoryExists(TransactionsFilePath);
- string jsonString = JsonConvert.SerializeObject(TransactionCache.OrderByBlockchain(), Formatting.Indented);
- File.WriteAllText(TransactionsFilePath,
- jsonString,
- Encoding.UTF8);
- }
-
/// <summary>
/// Current timeout used when downloading a block from the remote node. It is defined in seconds.
/// </summary>
@@ -1425,10 +1332,5 @@ namespace WalletWasabi.Services
DisconnectDisposeNullLocalBitcoinCoreNode();
}
-
- public SmartTransaction TryGetTxFromCache(uint256 txId)
- {
- return TransactionCache.FindBy(txId.GetHashCode(), x => x.GetHash() == txId);
- }
}
}

MODIFY
WalletWasabi/Transactions/AllTransactionStore.cs
WalletWasabi/Transactions/AllTransactionStore.cs
@@ -129,6 +129,36 @@ namespace WalletWasabi.Transactions
}
}
+ public void TryAdd(SmartTransaction tx)
+ {
+ lock (Lock)
+ {
+ TryAddNoLock(tx);
+ }
+ }
+
+ public bool TryUpdate(SmartTransaction tx, out SmartTransaction originalTx)
+ {
+ var hash = tx.GetHash();
+ lock (Lock)
+ {
+ // Do Contains first, because it's fast.
+ if (ConfirmedStore.Contains(hash) && ConfirmedStore.TryGetTransaction(hash, out originalTx))
+ {
+ originalTx.Update(tx);
+ return true;
+ }
+ else if (MempoolStore.Contains(hash) && MempoolStore.TryGetTransaction(hash, out originalTx))
+ {
+ originalTx.Update(tx);
+ return true;
+ }
+ }
+
+ originalTx = null;
+ return false;
+ }
+
public bool TryGetTransaction(uint256 hash, out SmartTransaction sameStx)
{
lock (Lock)

MODIFY
WalletWasabi/Transactions/TransactionStore.cs
WalletWasabi/Transactions/TransactionStore.cs
@@ -195,19 +195,19 @@ namespace WalletWasabi.Transactions
}
}
- public ISet<SmartTransaction> GetTransactions()
+ public IEnumerable<SmartTransaction> GetTransactions()
{
lock (TransactionsLock)
{
- return Transactions.Values.ToHashSet();
+ return Transactions.Values.OrderByBlockchain().ToList();
}
}
- public ISet<uint256> GetTransactionHashes()
+ public IEnumerable<uint256> GetTransactionHashes()
{
lock (TransactionsLock)
{
- return Transactions.Keys.ToHashSet();
+ return Transactions.Values.OrderByBlockchain().Select(x => x.GetHash()).ToList();
}
}

Finish TransactionStore implementation