Handle arrivedcoins
MODIFY
WalletWasabi.Gui/Controls/WalletExplorer/CoinListViewModel.cs
WalletWasabi.Gui/Controls/WalletExplorer/CoinListViewModel.cs
@@ -444,7 +444,6 @@ namespace WalletWasabi.Gui.Controls.WalletExplorer
Observable
.Merge(Observable.FromEventPattern<TransactionProcessedResult>(Global.WalletService.TransactionProcessor, nameof(Global.WalletService.TransactionProcessor.WalletRelevantTransactionProcessed)).Select(_ => Unit.Default))
.Merge(Observable.FromEventPattern<TxCoinsEventArgs>(Global.WalletService.TransactionProcessor, nameof(Global.WalletService.TransactionProcessor.CoinsSpent)).Select(_ => Unit.Default))
- .Merge(Observable.FromEventPattern<TxCoinsEventArgs>(Global.WalletService.TransactionProcessor, nameof(Global.WalletService.TransactionProcessor.CoinsReceived)).Select(_ => Unit.Default))
.Throttle(TimeSpan.FromSeconds(2)) // Throttle TransactionProcessor events adds/removes.
.Merge(Observable.FromEventPattern(this, nameof(CoinListShown), RxApp.MainThreadScheduler).Select(_ => Unit.Default)) // Load the list immediately.
.ObserveOn(RxApp.MainThreadScheduler)

MODIFY
WalletWasabi.Gui/Controls/WalletExplorer/HistoryTabViewModel.cs
WalletWasabi.Gui/Controls/WalletExplorer/HistoryTabViewModel.cs
@@ -56,8 +56,8 @@ namespace WalletWasabi.Gui.Controls.WalletExplorer
Observable.FromEventPattern(Global.WalletService, nameof(Global.WalletService.NewBlockProcessed))
.Merge(Observable.FromEventPattern(Global.WalletService.TransactionProcessor, nameof(Global.WalletService.TransactionProcessor.CoinsSpent)))
.Merge(Observable.FromEventPattern(Global.WalletService.TransactionProcessor, nameof(Global.WalletService.TransactionProcessor.SpendersConfirmed)))
- .Merge(Observable.FromEventPattern(Global.WalletService.TransactionProcessor, nameof(Global.WalletService.TransactionProcessor.CoinsReceived)))
- .Throttle(TimeSpan.FromSeconds(5))
+ .Merge(Observable.FromEventPattern(Global.WalletService.TransactionProcessor, nameof(Global.WalletService.TransactionProcessor.WalletRelevantTransactionProcessed)))
+ .Throttle(TimeSpan.FromSeconds(3))
.ObserveOn(RxApp.MainThreadScheduler)
.Subscribe(async _ => await TryRewriteTableAsync())
.DisposeWith(Disposables);

MODIFY
WalletWasabi.Gui/Controls/WalletExplorer/ReceiveTabViewModel.cs
WalletWasabi.Gui/Controls/WalletExplorer/ReceiveTabViewModel.cs
@@ -157,7 +157,7 @@ namespace WalletWasabi.Gui.Controls.WalletExplorer
Disposables = Disposables is null ? new CompositeDisposable() : throw new NotSupportedException($"Cannot open {GetType().Name} before closing it.");
Observable
- .FromEventPattern(Global.WalletService.TransactionProcessor, nameof(Global.WalletService.TransactionProcessor.CoinsReceived))
+ .FromEventPattern(Global.WalletService.TransactionProcessor, nameof(Global.WalletService.TransactionProcessor.WalletRelevantTransactionProcessed))
.ObserveOn(RxApp.MainThreadScheduler)
.Subscribe(_ => InitializeAddresses())
.DisposeWith(Disposables);

MODIFY
WalletWasabi.Gui/Controls/WalletExplorer/WalletViewModel.cs
WalletWasabi.Gui/Controls/WalletExplorer/WalletViewModel.cs
@@ -93,9 +93,9 @@ namespace WalletWasabi.Gui.Controls.WalletExplorer
Disposables = Disposables is null ? new CompositeDisposable() : throw new NotSupportedException($"Cannot open {GetType().Name} before closing it.");
Observable.Merge(
- Observable.FromEventPattern(Global.WalletService.TransactionProcessor, nameof(Global.WalletService.TransactionProcessor.CoinsReceived)).Select(_ => Unit.Default),
+ Observable.FromEventPattern(Global.WalletService.TransactionProcessor, nameof(Global.WalletService.TransactionProcessor.WalletRelevantTransactionProcessed)).Select(_ => Unit.Default),
Observable.FromEventPattern(Global.WalletService.TransactionProcessor, nameof(Global.WalletService.TransactionProcessor.CoinsSpent)).Select(_ => Unit.Default))
- .Throttle(TimeSpan.FromSeconds(0.5))
+ .Throttle(TimeSpan.FromSeconds(0.1))
.Merge(Global.UiConfig.WhenAnyValue(x => x.LurkingWifeMode).Select(_ => Unit.Default))
.ObserveOn(RxApp.MainThreadScheduler)
.Subscribe(_ =>

MODIFY
WalletWasabi.Gui/Global.cs
WalletWasabi.Gui/Global.cs
@@ -475,7 +475,7 @@ namespace WalletWasabi.Gui
Logger.LogInfo($"{nameof(WalletService)} started.");
token.ThrowIfCancellationRequested();
- WalletService.TransactionProcessor.CoinsReceived += CoinsReceived;
+ WalletService.TransactionProcessor.WalletRelevantTransactionProcessed += TransactionProcessor_WalletRelevantTransactionProcessed;
TransactionBroadcaster.AddWalletService(WalletService);
CoinJoinProcessor.AddWalletService(WalletService);
@@ -483,6 +483,22 @@ namespace WalletWasabi.Gui
_cancelWalletServiceInitialization = null; // Must make it null explicitly, because dispose won't make it null.
}
+ private void TransactionProcessor_WalletRelevantTransactionProcessed(object sender, TransactionProcessedResult e)
+ {
+ if (UiConfig?.LurkingWifeMode is true)
+ {
+ return;
+ }
+
+ if (e.NewlyReceivedCoins.Any())
+ {
+ Money satSum = e.NewlyReceivedCoins.Where(x => !x.HdPubKey.IsInternal).Sum(x => x.Amount);
+ string amountString = satSum.ToString(false, true);
+
+ NotificationHelpers.Information($"{amountString} BTC", "Received");
+ }
+ }
+
public string GetWalletFullPath(string walletName)
{
walletName = walletName.TrimEnd(".json", StringComparison.OrdinalIgnoreCase);
@@ -545,30 +561,12 @@ namespace WalletWasabi.Gui
return keyManager;
}
- private void CoinsReceived(object sender, TxCoinsEventArgs args)
- {
- if (UiConfig?.LurkingWifeMode is true)
- {
- return;
- }
-
- Money satSum = args.Coins.Where(x => !x.HdPubKey.IsInternal).Sum(x => x.Amount);
- if (satSum == Money.Zero)
- {
- return;
- }
-
- string amountString = satSum.ToString(false, true);
-
- NotificationHelpers.Information($"{amountString} BTC", "Received");
- }
-
public async Task DisposeInWalletDependentServicesAsync()
{
var walletService = WalletService;
if (walletService is { })
{
- walletService.TransactionProcessor.CoinsReceived -= CoinsReceived;
+ WalletService.TransactionProcessor.WalletRelevantTransactionProcessed -= TransactionProcessor_WalletRelevantTransactionProcessed;
}
try

MODIFY
WalletWasabi.Tests/IntegrationTests/RegTests.cs
WalletWasabi.Tests/IntegrationTests/RegTests.cs
@@ -1504,12 +1504,12 @@ namespace WalletWasabi.Tests.IntegrationTests
// Get some money, make it confirm.
// this is necesary because we are in a fork now.
- var coinAwaiter = new EventAwaiter<TxCoinsEventArgs>(
- h => wallet.TransactionProcessor.CoinsReceived += h,
- h => wallet.TransactionProcessor.CoinsReceived -= h);
+ var eventAwaiter = new EventAwaiter<TransactionProcessedResult>(
+ h => wallet.TransactionProcessor.WalletRelevantTransactionProcessed += h,
+ h => wallet.TransactionProcessor.WalletRelevantTransactionProcessed -= h);
fundingTxId = await rpc.SendToAddressAsync(key.GetP2wpkhAddress(network), Money.Coins(1m), replaceable: true);
- var coinArrived = await coinAwaiter.WaitAsync(TimeSpan.FromSeconds(21));
- Assert.Equal(fundingTxId, coinArrived.Coins.Single().TransactionId);
+ var eventArrived = await eventAwaiter.WaitAsync(TimeSpan.FromSeconds(21));
+ Assert.Equal(fundingTxId, eventArrived.NewlyReceivedCoins.Single().TransactionId);
Assert.Contains(fundingTxId, wallet.Coins.Select(x => x.TransactionId));
var fundingBumpTxId = await rpc.BumpFeeAsync(fundingTxId);
@@ -1596,15 +1596,15 @@ namespace WalletWasabi.Tests.IntegrationTests
// Get some money, make it confirm.
// this is necesary because we are in a fork now.
- var receiveWaiter = new EventAwaiter<TxCoinsEventArgs>(
- h => wallet.TransactionProcessor.CoinsReceived += h,
- h => wallet.TransactionProcessor.CoinsReceived -= h);
+ var eventAwaiter = new EventAwaiter<TransactionProcessedResult>(
+ h => wallet.TransactionProcessor.WalletRelevantTransactionProcessed += h,
+ h => wallet.TransactionProcessor.WalletRelevantTransactionProcessed -= h);
var tx0Id = await rpc.SendToAddressAsync(
key.GetP2wpkhAddress(network),
Money.Coins(1m),
replaceable: true);
- var receivedCoinsArgs = await receiveWaiter.WaitAsync(TimeSpan.FromSeconds(21));
- Assert.Equal(tx0Id, receivedCoinsArgs.Coins.Single().TransactionId);
+ var eventArg = await eventAwaiter.WaitAsync(TimeSpan.FromSeconds(21));
+ Assert.Equal(tx0Id, eventArg.NewlyReceivedCoins.Single().TransactionId);
Assert.Single(wallet.Coins);
var broadcaster = new TransactionBroadcaster(network, bitcoinStore, synchronizer, nodes, rpc);
@@ -1621,17 +1621,17 @@ namespace WalletWasabi.Tests.IntegrationTests
// Spend the unconfirmed coin (send it to ourself)
operations = new PaymentIntent(key.PubKey.WitHash.ScriptPubKey, Money.Coins(0.5m));
tx1Res = wallet.BuildTransaction(password, operations, FeeStrategy.TwentyMinutesConfirmationTargetStrategy, allowUnconfirmed: true);
- receiveWaiter = new EventAwaiter<TxCoinsEventArgs>(
- h => wallet.TransactionProcessor.CoinsReceived += h,
- h => wallet.TransactionProcessor.CoinsReceived -= h);
+ eventAwaiter = new EventAwaiter<TransactionProcessedResult>(
+ h => wallet.TransactionProcessor.WalletRelevantTransactionProcessed += h,
+ h => wallet.TransactionProcessor.WalletRelevantTransactionProcessed -= h);
var spendWaiter = new EventAwaiter<TxCoinsEventArgs>(
h => wallet.TransactionProcessor.CoinsSpent += h,
h => wallet.TransactionProcessor.CoinsSpent -= h);
await broadcaster.SendTransactionAsync(tx1Res.Transaction);
var spentCoins = await spendWaiter.WaitAsync(TimeSpan.FromSeconds(21));
Assert.Equal(tx0Id, spentCoins.Coins.Single().TransactionId);
- receivedCoinsArgs = await receiveWaiter.WaitAsync(TimeSpan.FromSeconds(21));
- Assert.Equal(tx1Res.Transaction.GetHash(), receivedCoinsArgs.Coins.First().TransactionId);
+ var eventArgs = await eventAwaiter.WaitAsync(TimeSpan.FromSeconds(21));
+ Assert.Equal(tx1Res.Transaction.GetHash(), eventArgs.NewlyReceivedCoins.First().TransactionId);
// There is a coin created by the latest spending transaction
Assert.Contains(wallet.Coins, x => x.TransactionId == tx1Res.Transaction.GetHash());
@@ -1650,16 +1650,16 @@ namespace WalletWasabi.Tests.IntegrationTests
operations = new PaymentIntent(key.PubKey.WitHash.ScriptPubKey, Money.Coins(0.5m), subtractFee: true);
var tx2Res = wallet.BuildTransaction(password, operations, FeeStrategy.TwentyMinutesConfirmationTargetStrategy, allowUnconfirmed: true);
- var receivesWaiter = new EventAwaiter<TxCoinsEventArgs>(
- h => wallet.TransactionProcessor.CoinsReceived += h,
- h => wallet.TransactionProcessor.CoinsReceived -= h);
+ eventAwaiter = new EventAwaiter<TransactionProcessedResult>(
+ h => wallet.TransactionProcessor.WalletRelevantTransactionProcessed += h,
+ h => wallet.TransactionProcessor.WalletRelevantTransactionProcessed -= h);
spendWaiter = new EventAwaiter<TxCoinsEventArgs>(
h => wallet.TransactionProcessor.CoinsSpent += h,
h => wallet.TransactionProcessor.CoinsSpent -= h);
await broadcaster.SendTransactionAsync(tx2Res.Transaction);
spentCoins = await spendWaiter.WaitAsync(TimeSpan.FromSeconds(21));
Assert.Equal(tx1Res.Transaction.GetHash(), spentCoins.Coins.First().TransactionId);
- var receivedCoins = (await receivesWaiter.WaitAsync(TimeSpan.FromSeconds(21))).Coins.ToArray();
+ var receivedCoins = (await eventAwaiter.WaitAsync(TimeSpan.FromSeconds(21))).NewlyReceivedCoins.ToArray();
uint256 tx2Hash = tx2Res.Transaction.GetHash();
Assert.Equal(tx2Hash, receivedCoins[0].TransactionId);
Assert.Equal(tx2Hash, receivedCoins[1].TransactionId);

MODIFY
WalletWasabi.Tests/UnitTests/Transactions/TransactionProcessorTests.cs
WalletWasabi.Tests/UnitTests/Transactions/TransactionProcessorTests.cs
@@ -170,9 +170,9 @@ namespace WalletWasabi.Tests.UnitTests.Transactions
int coinReceivedCalled = 0;
// The coin with the confirmed tx should win.
- transactionProcessor.CoinsReceived += (s, cs) =>
+ transactionProcessor.WalletRelevantTransactionProcessed += (s, e) =>
{
- foreach (var c in cs.Coins)
+ foreach (var c in e.NewlyReceivedCoins)
{
switch (coinReceivedCalled)
{
@@ -303,7 +303,10 @@ namespace WalletWasabi.Tests.UnitTests.Transactions
var keys = transactionProcessor.KeyManager.GetKeys().ToArray();
int coinsReceived = 0;
int confirmed = 0;
- transactionProcessor.CoinsReceived += (s, e) => coinsReceived++;
+ transactionProcessor.WalletRelevantTransactionProcessed += (s, e) =>
+ {
+ coinsReceived += e.NewlyReceivedCoins.Count;
+ };
transactionProcessor.CoinsSpent += (s, e) => throw new InvalidOperationException("We are not spending the coin.");
transactionProcessor.SpendersConfirmed += (s, e) => confirmed++;
@@ -457,7 +460,7 @@ namespace WalletWasabi.Tests.UnitTests.Transactions
{
var transactionProcessor = await CreateTransactionProcessorAsync();
SmartCoin receivedCoin = null;
- transactionProcessor.CoinsReceived += (s, theCoin) => receivedCoin = theCoin.Coins.Single();
+ transactionProcessor.WalletRelevantTransactionProcessed += (s, e) => receivedCoin = e.NewlyReceivedCoins.Single();
var keys = transactionProcessor.KeyManager.GetKeys();
var tx = CreateCreditingTransaction(keys.First().P2wpkhScript, Money.Coins(1.0m));
@@ -511,7 +514,7 @@ namespace WalletWasabi.Tests.UnitTests.Transactions
public async Task ReceiveTransactionWithDustForWalletAsync()
{
var transactionProcessor = await CreateTransactionProcessorAsync();
- transactionProcessor.CoinsReceived += (s, theCoin)
+ transactionProcessor.WalletRelevantTransactionProcessed += (s, e)
=> throw new Exception("The dust coin raised an event when it shouldn't.");
var keys = transactionProcessor.KeyManager.GetKeys();
var tx = CreateCreditingTransaction(keys.First().P2wpkhScript, Money.Coins(0.000099m));

MODIFY
WalletWasabi/Blockchain/TransactionProcessing/TransactionProcessedResult.cs
WalletWasabi/Blockchain/TransactionProcessing/TransactionProcessedResult.cs
@@ -11,9 +11,31 @@ namespace WalletWasabi.Blockchain.TransactionProcessing
public class TransactionProcessedResult
{
public SmartTransaction Transaction { get; }
- public bool IsWalletRelevant => SuccessfullyDoubleSpentCoins.Any() || ReplacedCoins.Any() || RestoredCoins.Any();
+
+ public bool IsWalletRelevant =>
+ SuccessfullyDoubleSpentCoins.Any()
+ || ReplacedCoins.Any()
+ || RestoredCoins.Any()
+ || NewlyReceivedCoins.Any()
+ || NewlyConfirmedReceivedCoins.Any();
+
public bool IsLikelyOwnCoinJoin { get; set; } = false;
+ /// <summary>
+ /// Coins we received in this transaction.
+ /// </summary>
+ public List<SmartCoin> ReceivedCoins { get; set; } = new List<SmartCoin>();
+
+ /// <summary>
+ /// Coins we received in this transaction and we did not previously known about.
+ /// </summary>
+ public List<SmartCoin> NewlyReceivedCoins { get; set; } = new List<SmartCoin>();
+
+ /// <summary>
+ /// Coins we received in this transaction, we have known already about, but it just got confirmed.
+ /// </summary>
+ public List<SmartCoin> NewlyConfirmedReceivedCoins { get; set; } = new List<SmartCoin>();
+
/// <summary>
/// Coins those we previously had in the mempool, but this confirmed
/// transaction has successfully invalidated them, because it spends

MODIFY
WalletWasabi/Blockchain/TransactionProcessing/TransactionProcessor.cs
WalletWasabi/Blockchain/TransactionProcessing/TransactionProcessor.cs
@@ -26,8 +26,6 @@ namespace WalletWasabi.Blockchain.TransactionProcessing
public event EventHandler<TxCoinsEventArgs> SpendersConfirmed;
- public event EventHandler<TxCoinsEventArgs> CoinsReceived;
-
public TransactionProcessor(
AllTransactionStore transactionStore,
KeyManager keyManager,
@@ -135,7 +133,7 @@ namespace WalletWasabi.Blockchain.TransactionProcessing
var (replaced, restored) = Coins.Undo(replacedTxId);
result.ReplacedCoins.AddRange(replaced);
- result.ReplacedCoins.AddRange(restored);
+ result.RestoredCoins.AddRange(restored);
foreach (var replacedTransactionId in replaced.Select(coin => coin.TransactionId))
{
@@ -186,7 +184,6 @@ namespace WalletWasabi.Blockchain.TransactionProcessing
}
}
- List<SmartCoin> newCoins = new List<SmartCoin>();
List<SmartCoin> coinsSpent = new List<SmartCoin>();
List<SmartCoin> spendersConfirmed = new List<SmartCoin>();
List<SmartCoin> spentOwnCoins = null;
@@ -213,10 +210,12 @@ namespace WalletWasabi.Blockchain.TransactionProcessing
}
SmartCoin newCoin = new SmartCoin(txId, i, output.ScriptPubKey, output.Value, tx.Transaction.Inputs.ToTxoRefs().ToArray(), tx.Height, tx.IsRBF, anonset, result.IsLikelyOwnCoinJoin, foundKey.Label, spenderTransactionId: null, false, pubKey: foundKey); // Do not inherit locked status from key, that's different.
+
+ result.ReceivedCoins.Add(newCoin);
// If we did not have it.
if (Coins.TryAdd(newCoin))
{
- newCoins.Add(newCoin);
+ result.NewlyReceivedCoins.Add(newCoin);
// Make sure there's always 21 clean keys generated and indexed.
KeyManager.AssertCleanKeysIndexed(isInternal: foundKey.IsInternal);
@@ -234,6 +233,7 @@ namespace WalletWasabi.Blockchain.TransactionProcessing
SmartCoin oldCoin = Coins.AsAllCoinsView().GetByOutPoint(new OutPoint(txId, i));
if (oldCoin != null) // Just to be sure, it is a concurrent collection.
{
+ result.NewlyConfirmedReceivedCoins.Add(newCoin);
oldCoin.Height = newCoin.Height;
}
}
@@ -279,10 +279,6 @@ namespace WalletWasabi.Blockchain.TransactionProcessing
{
SpendersConfirmed?.Invoke(this, new TxCoinsEventArgs(tx, spendersConfirmed));
}
- if (newCoins.Any())
- {
- CoinsReceived?.Invoke(this, new TxCoinsEventArgs(tx, newCoins));
- }
}
return result;

MODIFY
WalletWasabi/Services/WalletService.cs
WalletWasabi/Services/WalletService.cs
@@ -116,7 +116,7 @@ namespace WalletWasabi.Services
Coins = TransactionProcessor.Coins;
TransactionProcessor.CoinsSpent += TransactionProcessor_CoinsSpent;
- TransactionProcessor.CoinsReceived += TransactionProcessor_CoinsReceivedAsync;
+ TransactionProcessor.WalletRelevantTransactionProcessed += TransactionProcessor_WalletRelevantTransactionProcessedAsync;
if (Directory.Exists(BlocksFolderPath))
{
@@ -157,14 +157,14 @@ namespace WalletWasabi.Services
}
}
- private async void TransactionProcessor_CoinsReceivedAsync(object sender, TxCoinsEventArgs args)
+ private async void TransactionProcessor_WalletRelevantTransactionProcessedAsync(object sender, TransactionProcessedResult e)
{
try
{
- if (ChaumianClient.State.Contains(args.SmartTransaction.Transaction.Inputs.Select(x => x.PrevOut.ToTxoRef())))
+ if (ChaumianClient.State.Contains(e.Transaction.Transaction.Inputs.Select(x => x.PrevOut.ToTxoRef())))
{
- var coinsToQueue = new List<SmartCoin>();
- foreach (var newCoin in args.Coins)
+ var coinsToQueue = new HashSet<SmartCoin>();
+ foreach (var newCoin in e.NewlyReceivedCoins.Concat(e.RestoredCoins).Distinct())
{
// If it's being mixed and anonset is not sufficient, then queue it.
if (newCoin.Unspent && ChaumianClient.HasIngredients
@@ -823,7 +823,7 @@ namespace WalletWasabi.Services
BitcoinStore.IndexStore.Reorged -= IndexDownloader_ReorgedAsync;
BitcoinStore.MempoolService.TransactionReceived -= Mempool_TransactionReceived;
TransactionProcessor.CoinsSpent -= TransactionProcessor_CoinsSpent;
- TransactionProcessor.CoinsReceived -= TransactionProcessor_CoinsReceivedAsync;
+ TransactionProcessor.WalletRelevantTransactionProcessed -= TransactionProcessor_WalletRelevantTransactionProcessedAsync;
DisconnectDisposeNullLocalBitcoinCoreNode();
}

Handle arrivedcoins