implement spentaccordingtobackend (both for cj response and send response)
MODIFY
WalletWasabi.Gui/Controls/WalletExplorer/CoinViewModel.cs
WalletWasabi.Gui/Controls/WalletExplorer/CoinViewModel.cs
@@ -44,6 +44,11 @@ namespace WalletWasabi.Gui.Controls.WalletExplorer
this.RaisePropertyChanged(nameof(BannedCoinToolTip));
});
+ model.WhenAnyValue(x => x.SpentAccordingToBackend).ObserveOn(RxApp.MainThreadScheduler).Subscribe(_ =>
+ {
+ this.RaisePropertyChanged(nameof(Status));
+ });
+
Global.IndexDownloader.WhenAnyValue(x => x.BestHeight).ObserveOn(RxApp.MainThreadScheduler).Subscribe(_ =>
{
this.RaisePropertyChanged(nameof(Confirmations));
@@ -142,6 +147,11 @@ namespace WalletWasabi.Gui.Controls.WalletExplorer
}
}
+ if (Model.SpentAccordingToBackend)
+ {
+ return SmartCoinStatus.SpentAccordingToBackend;
+ }
+
if (Model.Confirmed)
{
if (Model.CoinJoinInProgress)

MODIFY
WalletWasabi/Models/SmartCoin.cs
WalletWasabi/Models/SmartCoin.cs
@@ -19,6 +19,7 @@ namespace WalletWasabi.Models
private uint256 _spenderTransactionId;
private bool _coinJoinInProgress;
private DateTimeOffset? _bannedUntilUtc;
+ private bool _spentAccordingToBackend;
[JsonProperty(Order = 1)]
[JsonConverter(typeof(Uint256JsonConverter))]
@@ -157,14 +158,8 @@ namespace WalletWasabi.Models
}
}
- /// <summary>
- /// If the backend thinks it's spent, but Wasabi doesn't yet know.
- /// </summary>
- [JsonProperty(Order = 13)]
- public bool SpentAccordingToBackend { get; set; }
-
- public bool SpentOrCoinJoinInProgress => !(SpenderTransactionId is null) || CoinJoinInProgress || SpentAccordingToBackend;
- public bool Unspent => SpenderTransactionId is null && !SpentAccordingToBackend;
+ public bool SpentOrCoinJoinInProgress => !Unspent || CoinJoinInProgress;
+ public bool Unspent => SpenderTransactionId is null;
public bool Confirmed => Height != Height.MemPool && Height != Height.Unknown;
public bool IsBanned => BannedUntilUtc != null && BannedUntilUtc > DateTimeOffset.UtcNow;
@@ -178,8 +173,24 @@ namespace WalletWasabi.Models
/// </summary>
public ISecret Secret { get; set; }
+ /// <summary>
+ /// If the backend thinks it's spent, but Wasabi doesn't yet know.
+ /// </summary>
+ public bool SpentAccordingToBackend
+ {
+ get { return _spentAccordingToBackend; }
+ set
+ {
+ if (value != _spentAccordingToBackend)
+ {
+ _spentAccordingToBackend = value;
+ OnPropertyChanged(nameof(SpentAccordingToBackend));
+ }
+ }
+ }
+
[JsonConstructor]
- public SmartCoin(uint256 transactionId, uint index, Script scriptPubKey, Money amount, TxoRef[] spentOutputs, Height height, bool rbf, int mixin, string label = "", uint256 spenderTransactionId = null, bool coinJoinInProgress = false, bool spentAccordingToBackend = false)
+ public SmartCoin(uint256 transactionId, uint index, Script scriptPubKey, Money amount, TxoRef[] spentOutputs, Height height, bool rbf, int mixin, string label = "", uint256 spenderTransactionId = null, bool coinJoinInProgress = false)
{
TransactionId = Guard.NotNull(nameof(transactionId), transactionId);
Index = Guard.NotNull(nameof(index), index);
@@ -193,11 +204,11 @@ namespace WalletWasabi.Models
RBF = rbf;
CoinJoinInProgress = coinJoinInProgress;
Secret = null;
+ SpentAccordingToBackend = false;
BannedUntilUtc = null;
- SpentAccordingToBackend = spentAccordingToBackend;
}
- public SmartCoin(Coin coin, TxoRef[] spentOutputs, Height height, bool rbf, int mixin, string label = "", uint256 spenderTransactionId = null, bool coinJoinInProgress = false, bool spentAccordingToBackend = false)
+ public SmartCoin(Coin coin, TxoRef[] spentOutputs, Height height, bool rbf, int mixin, string label = "", uint256 spenderTransactionId = null, bool coinJoinInProgress = false)
{
Guard.NotNull(nameof(coin), coin);
TransactionId = coin.Outpoint.Hash;
@@ -212,7 +223,8 @@ namespace WalletWasabi.Models
RBF = rbf;
CoinJoinInProgress = coinJoinInProgress;
Secret = null;
- SpentAccordingToBackend = spentAccordingToBackend;
+ SpentAccordingToBackend = false;
+ BannedUntilUtc = null;
}
public event PropertyChangedEventHandler PropertyChanged;

MODIFY
WalletWasabi/Services/CcjClient.cs
WalletWasabi/Services/CcjClient.cs
@@ -525,7 +525,7 @@ namespace WalletWasabi.Services
await DequeueCoinsFromMixNoLockAsync(coinReference);
return;
}
- catch (HttpRequestException ex) when (ex.Message.StartsWith("Provided input is not unspent", StringComparison.InvariantCultureIgnoreCase))
+ catch (HttpRequestException ex) when (ex.Message.Contains("Provided input is not unspent", StringComparison.InvariantCultureIgnoreCase))
{
string[] parts = ex.Message.Split(new[] { "Provided input is not unspent: " }, StringSplitOptions.RemoveEmptyEntries);
string spentInputString = parts[1].TrimEnd('.');

MODIFY
WalletWasabi/Services/WalletService.cs
WalletWasabi/Services/WalletService.cs
@@ -21,6 +21,7 @@ using WalletWasabi.WebClients.Wasabi;
using Newtonsoft.Json;
using System.Collections.Concurrent;
using NBitcoin.DataEncoders;
+using System.Net.Http;
namespace WalletWasabi.Services
{
@@ -1028,9 +1029,25 @@ namespace WalletWasabi.Services
public async Task SendTransactionAsync(SmartTransaction transaction)
{
using (var client = new WasabiClient(IndexDownloader.WasabiClient.TorClient.DestinationUri, IndexDownloader.WasabiClient.TorClient.TorSocks5EndPoint))
+ {
+ try
{
await client.BroadcastAsync(transaction);
}
+ catch (HttpRequestException ex) when (ex.Message.Contains("txn-mempool-conflict", StringComparison.InvariantCultureIgnoreCase))
+ {
+ if (transaction.Transaction.Inputs.Count == 1)
+ {
+ OutPoint input = transaction.Transaction.Inputs.First().PrevOut;
+ SmartCoin coin = Coins.FirstOrDefault(x => x.TransactionId == input.Hash && x.Index == input.N);
+ if (coin != default)
+ {
+ coin.SpentAccordingToBackend = true;
+ }
+ }
+ throw;
+ }
+ }
ProcessTransaction(new SmartTransaction(transaction.Transaction, Height.MemPool));
MemPool.TransactionHashes.Add(transaction.GetHash());

implement spentaccordingtobackend (both for cj response and send response)