diff --git a/GingerCommon/Crypto/Random/RandomExtensions.cs b/GingerCommon/Crypto/Random/RandomExtensions.cs
index 34cd6d6ea2..e670106ae6 100644
--- a/GingerCommon/Crypto/Random/RandomExtensions.cs
+++ b/GingerCommon/Crypto/Random/RandomExtensions.cs
@@ -1,8 +1,4 @@
-using System;
using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
namespace GingerCommon.Crypto.Random;
diff --git a/GingerCommon/Static/HttpUtils.cs b/GingerCommon/Static/HttpUtils.cs
index 05d971a42f..20937a2cd4 100644
--- a/GingerCommon/Static/HttpUtils.cs
+++ b/GingerCommon/Static/HttpUtils.cs
@@ -1,10 +1,5 @@
using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Net.Http.Headers;
using System.Net.Http;
-using System.Text;
-using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using System.Net;
diff --git a/GingerCommon/Static/JsonUtils.cs b/GingerCommon/Static/JsonUtils.cs
index d01f0fdeef..77c63a031d 100644
--- a/GingerCommon/Static/JsonUtils.cs
+++ b/GingerCommon/Static/JsonUtils.cs
@@ -1,9 +1,4 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
using System.Text.Json;
-using System.Threading.Tasks;
namespace GingerCommon.Static;
diff --git a/GingerCommon/Static/TypeUtils.cs b/GingerCommon/Static/TypeUtils.cs
index 8f75a8f858..7dc31ae9f2 100644
--- a/GingerCommon/Static/TypeUtils.cs
+++ b/GingerCommon/Static/TypeUtils.cs
@@ -1,9 +1,5 @@
using System;
-using System.Collections.Generic;
-using System.Linq;
using System.Runtime.CompilerServices;
-using System.Text;
-using System.Threading.Tasks;
namespace GingerCommon.Static;
diff --git a/WalletWasabi.Backend/Extensions.cs b/WalletWasabi.Backend/Extensions.cs
index ab7b1a6714..993ea1ebd5 100644
--- a/WalletWasabi.Backend/Extensions.cs
+++ b/WalletWasabi.Backend/Extensions.cs
@@ -1,4 +1,3 @@
-using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System.Linq;
diff --git a/WalletWasabi.Backend/Global.cs b/WalletWasabi.Backend/Global.cs
index a77f4ff83c..c257684fdb 100644
--- a/WalletWasabi.Backend/Global.cs
+++ b/WalletWasabi.Backend/Global.cs
@@ -1,7 +1,6 @@
using GingerCommon.Logging;
using Microsoft.Extensions.Logging;
using NBitcoin;
-using NBitcoin.Secp256k1;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
@@ -21,7 +20,6 @@
using WalletWasabi.WabiSabi;
using WalletWasabi.WabiSabi.Backend;
using WalletWasabi.WabiSabi.Backend.Banning;
-using WalletWasabi.WabiSabi.Backend.Rounds;
using WalletWasabi.WabiSabi.Backend.Rounds.CoinJoinStorage;
using WalletWasabi.WabiSabi.Backend.Statistics;
using WalletWasabi.WabiSabi.Recommendation;
diff --git a/WalletWasabi.Daemon/BuySell/BuySellClient.cs b/WalletWasabi.Daemon/BuySell/BuySellClient.cs
index 9229e5b11e..3164323629 100644
--- a/WalletWasabi.Daemon/BuySell/BuySellClient.cs
+++ b/WalletWasabi.Daemon/BuySell/BuySellClient.cs
@@ -1,4 +1,3 @@
-using System;
using System.Net.Http;
using System.Text;
using System.Text.Json;
diff --git a/WalletWasabi.Daemon/FeeRateProviders/FeeRateProvider.cs b/WalletWasabi.Daemon/FeeRateProviders/FeeRateProvider.cs
index 7ade825529..ce40d567d1 100644
--- a/WalletWasabi.Daemon/FeeRateProviders/FeeRateProvider.cs
+++ b/WalletWasabi.Daemon/FeeRateProviders/FeeRateProvider.cs
@@ -82,6 +82,7 @@ public void Initialize(RpcFeeRateProvider? rpcFeeRateProvider)
///
public FeeRateProvider(WasabiHttpClientFactory httpClientFactory, Network network) : this(httpClientFactory, FeeRateProviderSource.BlockstreamInfo, network)
{
+ Initialize(null);
}
public AllFeeEstimate GetAllFeeEstimate()
diff --git a/WalletWasabi.Daemon/PersistentConfig.cs b/WalletWasabi.Daemon/PersistentConfig.cs
index 1ab95b09e5..31167e6c23 100644
--- a/WalletWasabi.Daemon/PersistentConfig.cs
+++ b/WalletWasabi.Daemon/PersistentConfig.cs
@@ -252,6 +252,16 @@ static public bool Migrate(bool readFromWasabi, [NotNullWhen(true)] ref Persiste
};
}
+ // Previous imports from Wasabi could increase this to 100. We do want to check and change this, even if we are not readFromWasabi.
+ if (config.AbsoluteMinInputCount > Constants.DefaultAbsoluteMinInputCount)
+ {
+ hasChanged = true;
+ config = config with
+ {
+ AbsoluteMinInputCount = Constants.DefaultAbsoluteMinInputCount
+ };
+ }
+
if (readFromWasabi)
{
var torMode = Config.ObjectToTorMode(config.UseTor);
diff --git a/WalletWasabi.Fluent/AddWallet/ViewModels/AdvancedRecoveryOptionsViewModel.cs b/WalletWasabi.Fluent/AddWallet/ViewModels/AdvancedRecoveryOptionsViewModel.cs
index 01cecbd5da..77b4419b91 100644
--- a/WalletWasabi.Fluent/AddWallet/ViewModels/AdvancedRecoveryOptionsViewModel.cs
+++ b/WalletWasabi.Fluent/AddWallet/ViewModels/AdvancedRecoveryOptionsViewModel.cs
@@ -32,7 +32,7 @@ public AdvancedRecoveryOptionsViewModel(int minGapLimit)
private void ValidateMinGapLimit(IValidationErrors errors)
{
if (!int.TryParse(MinGapLimit, out var minGapLimit) ||
- minGapLimit is < KeyManager.AbsoluteMinGapLimit or > KeyManager.MaxGapLimit)
+ minGapLimit is < KeyManager.AbsoluteMinGapLimit or > KeyManager.MaxGapLimit)
{
errors.Add(
ErrorSeverity.Error,
diff --git a/WalletWasabi.Fluent/AddWallet/ViewModels/Create/ConfirmRecoveryWordsViewModel.cs b/WalletWasabi.Fluent/AddWallet/ViewModels/Create/ConfirmRecoveryWordsViewModel.cs
index aa69788d82..39e7ad88a8 100644
--- a/WalletWasabi.Fluent/AddWallet/ViewModels/Create/ConfirmRecoveryWordsViewModel.cs
+++ b/WalletWasabi.Fluent/AddWallet/ViewModels/Create/ConfirmRecoveryWordsViewModel.cs
@@ -55,9 +55,9 @@ protected override void OnNavigatedTo(bool isInHistory, CompositeDisposable disp
var nextCommandCanExecute =
confirmationWordsSourceList
- .Connect()
- .WhenValueChanged(x => x.IsConfirmed)
- .Select(_ => confirmationWordsSourceList.Items.All(x => x.IsConfirmed));
+ .Connect()
+ .WhenValueChanged(x => x.IsConfirmed)
+ .Select(_ => confirmationWordsSourceList.Items.All(x => x.IsConfirmed));
NextCommand = ReactiveCommand.CreateFromTask(OnNextAsync, nextCommandCanExecute);
diff --git a/WalletWasabi.Fluent/AddWallet/ViewModels/CreatePasswordDialogViewModel.cs b/WalletWasabi.Fluent/AddWallet/ViewModels/CreatePasswordDialogViewModel.cs
index 47d387cf38..e3acfe95a8 100644
--- a/WalletWasabi.Fluent/AddWallet/ViewModels/CreatePasswordDialogViewModel.cs
+++ b/WalletWasabi.Fluent/AddWallet/ViewModels/CreatePasswordDialogViewModel.cs
@@ -41,10 +41,10 @@ public CreatePasswordDialogViewModel(string title, string caption = "", bool ena
this.RaisePropertyChanged(nameof(ConfirmPassword));
return IsDialogOpen &&
- ((enableEmpty && string.IsNullOrEmpty(Password) &&
- string.IsNullOrEmpty(ConfirmPassword)) ||
- (!string.IsNullOrEmpty(Password) && !string.IsNullOrEmpty(ConfirmPassword) &&
- !Validations.Any));
+ ((enableEmpty && string.IsNullOrEmpty(Password) &&
+ string.IsNullOrEmpty(ConfirmPassword)) ||
+ (!string.IsNullOrEmpty(Password) && !string.IsNullOrEmpty(ConfirmPassword) &&
+ !Validations.Any));
})
.ObserveOn(RxApp.MainThreadScheduler);
diff --git a/WalletWasabi.Fluent/AddWallet/ViewModels/WelcomePageViewModel.cs b/WalletWasabi.Fluent/AddWallet/ViewModels/WelcomePageViewModel.cs
index 4c1e5f1f0d..30637f6882 100644
--- a/WalletWasabi.Fluent/AddWallet/ViewModels/WelcomePageViewModel.cs
+++ b/WalletWasabi.Fluent/AddWallet/ViewModels/WelcomePageViewModel.cs
@@ -35,12 +35,11 @@ public WelcomePageViewModel()
_selectedDisplayLanguage = Settings.SelectedDisplayLanguage;
this.WhenAnyValue(x => x.SelectedIndex)
- .Subscribe(
- x =>
- {
- NextLabel = x < NumberOfPages - 1 ? Resources.Continue : Resources.GetStarted;
- EnableNextKey = x < NumberOfPages - 1;
- });
+ .Subscribe(x =>
+ {
+ NextLabel = x < NumberOfPages - 1 ? Resources.Continue : Resources.GetStarted;
+ EnableNextKey = x < NumberOfPages - 1;
+ });
this.WhenAnyValue(x => x.IsActive)
.Skip(1)
diff --git a/WalletWasabi.Fluent/AddWallet/Views/AddWalletPageView.axaml b/WalletWasabi.Fluent/AddWallet/Views/AddWalletPageView.axaml
index 14db3a19cb..6078f6f3d7 100644
--- a/WalletWasabi.Fluent/AddWallet/Views/AddWalletPageView.axaml
+++ b/WalletWasabi.Fluent/AddWallet/Views/AddWalletPageView.axaml
@@ -16,16 +16,16 @@
EnableNext="False">
-
-
-
-
+
+
+
+
+ CancelContent="{x:Static lang:Resources.Cancel}"
+ EnableCancel="{Binding EnableCancel}"
+ EnableBack="{Binding EnableBack}"
+ EnableNext="True" NextContent="OK">
+ Caption="{Binding CurrentWord.Index, StringFormat='{x:Static lang:Resources.ConfirmRecoveryWordsViewModelClickRecovery}', FallbackValue=' '}"
+ EnableCancel="{Binding EnableCancel}"
+ EnableBack="{Binding EnableBack}"
+ EnableNext="True"
+ NextContent="{x:Static lang:Resources.Continue}"
+ EnableSkip="{Binding IsSkipEnabled}"
+ IsBusy="{Binding IsBusy}"
+ ScrollViewer.HorizontalScrollBarVisibility="Auto"
+ ScrollViewer.VerticalScrollBarVisibility="Disabled">
@@ -80,7 +80,8 @@
-
+
diff --git a/WalletWasabi.Fluent/AddWallet/Views/Create/RecoveryWordsView.axaml b/WalletWasabi.Fluent/AddWallet/Views/Create/RecoveryWordsView.axaml
index 0046acc08e..07672172b3 100644
--- a/WalletWasabi.Fluent/AddWallet/Views/Create/RecoveryWordsView.axaml
+++ b/WalletWasabi.Fluent/AddWallet/Views/Create/RecoveryWordsView.axaml
@@ -9,12 +9,12 @@
xmlns:create="clr-namespace:WalletWasabi.Fluent.AddWallet.ViewModels.Create"
x:Class="WalletWasabi.Fluent.AddWallet.Views.Create.RecoveryWordsView">
+ Caption="{x:Static lang:Resources.RecoveryWordsViewCaption}"
+ CancelContent="{x:Static lang:Resources.Cancel}"
+ EnableCancel="{Binding EnableCancel}"
+ EnableBack="{Binding EnableBack}"
+ EnableNext="True" NextContent="{x:Static lang:Resources.Continue}" FocusNext="True"
+ ScrollViewer.HorizontalScrollBarVisibility="Auto">
@@ -64,8 +64,8 @@
+ HorizontalAlignment="Center"
+ Grid.Row="4">
diff --git a/WalletWasabi.Fluent/AddWallet/Views/CreatePasswordDialogView.axaml b/WalletWasabi.Fluent/AddWallet/Views/CreatePasswordDialogView.axaml
index fe1a5f2b4b..d18a95d647 100644
--- a/WalletWasabi.Fluent/AddWallet/Views/CreatePasswordDialogView.axaml
+++ b/WalletWasabi.Fluent/AddWallet/Views/CreatePasswordDialogView.axaml
@@ -9,19 +9,25 @@
xmlns:viewModels="clr-namespace:WalletWasabi.Fluent.AddWallet.ViewModels"
x:Class="WalletWasabi.Fluent.AddWallet.Views.CreatePasswordDialogView">
+ Caption="{Binding Caption}"
+ CancelContent="{x:Static lang:Resources.Cancel}"
+ EnableCancel="{Binding EnableCancel}"
+ EnableBack="{Binding EnableBack}"
+ EnableNext="True" NextContent="{x:Static lang:Resources.Continue}"
+ Width="450">
-
+
-
+
diff --git a/WalletWasabi.Fluent/AddWallet/Views/RecoverWalletView.axaml b/WalletWasabi.Fluent/AddWallet/Views/RecoverWalletView.axaml
index fef5f62edb..61f4af71dd 100644
--- a/WalletWasabi.Fluent/AddWallet/Views/RecoverWalletView.axaml
+++ b/WalletWasabi.Fluent/AddWallet/Views/RecoverWalletView.axaml
@@ -9,12 +9,12 @@
xmlns:viewModels="clr-namespace:WalletWasabi.Fluent.AddWallet.ViewModels"
x:Class="WalletWasabi.Fluent.AddWallet.Views.RecoverWalletView">
+ Title="{Binding Title}"
+ CancelContent="{x:Static lang:Resources.Cancel}"
+ EnableCancel="{Binding EnableCancel}"
+ EnableBack="{Binding EnableBack}"
+ EnableNext="True" NextContent="{x:Static lang:Resources.Continue}"
+ IsBusy="{Binding IsBusy}">
+ ItemCountLimit="24"
+ SuggestionsAreCaseSensitive="False"
+ RestrictInputToSuggestions="True"
+ Items="{Binding Mnemonics}"
+ Suggestions="{Binding Suggestions}"
+ Watermark="{x:Static lang:Resources.TypeInYourRecoveryWords}"
+ AllowDuplication="True"
+ EnableCounter="True"
+ EnableDelete="False">
+
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/WalletWasabi.Fluent/CoinList/Views/CoinListView.axaml.cs b/WalletWasabi.Fluent/CoinList/Views/CoinListView.axaml.cs
index 425bee1010..20a1a07faf 100644
--- a/WalletWasabi.Fluent/CoinList/Views/CoinListView.axaml.cs
+++ b/WalletWasabi.Fluent/CoinList/Views/CoinListView.axaml.cs
@@ -2,6 +2,7 @@
using Avalonia.Markup.Xaml;
namespace WalletWasabi.Fluent.CoinList.Views;
+
public class CoinListView : UserControl
{
public CoinListView()
diff --git a/WalletWasabi.Fluent/CoinList/Views/Core/Cells/AnonymityScoreCellView.axaml b/WalletWasabi.Fluent/CoinList/Views/Core/Cells/AnonymityScoreCellView.axaml
index d74bc134ed..88b52447cf 100644
--- a/WalletWasabi.Fluent/CoinList/Views/Core/Cells/AnonymityScoreCellView.axaml
+++ b/WalletWasabi.Fluent/CoinList/Views/Core/Cells/AnonymityScoreCellView.axaml
@@ -8,7 +8,8 @@
x:DataType="viewModels:CoinListItem"
x:CompileBindings="True"
x:Class="WalletWasabi.Fluent.CoinList.Views.Core.Cells.AnonymityScoreCellView">
-
+
diff --git a/WalletWasabi.Fluent/CoinList/Views/Core/Cells/IndicatorsCellView.axaml b/WalletWasabi.Fluent/CoinList/Views/Core/Cells/IndicatorsCellView.axaml
index f8ae14adfa..37a6fb7ccb 100644
--- a/WalletWasabi.Fluent/CoinList/Views/Core/Cells/IndicatorsCellView.axaml
+++ b/WalletWasabi.Fluent/CoinList/Views/Core/Cells/IndicatorsCellView.axaml
@@ -31,8 +31,8 @@
-
-
+
+
-
-
+
+
diff --git a/WalletWasabi.Fluent/CoinList/Views/Core/Cells/SelectionCellView.axaml b/WalletWasabi.Fluent/CoinList/Views/Core/Cells/SelectionCellView.axaml
index e1f6944ac4..05aa08f02a 100644
--- a/WalletWasabi.Fluent/CoinList/Views/Core/Cells/SelectionCellView.axaml
+++ b/WalletWasabi.Fluent/CoinList/Views/Core/Cells/SelectionCellView.axaml
@@ -19,8 +19,8 @@
-
-
+
+
@@ -37,12 +37,14 @@
-
-
+
diff --git a/WalletWasabi.Fluent/CoinList/Views/SelectCoinsDialogView.axaml b/WalletWasabi.Fluent/CoinList/Views/SelectCoinsDialogView.axaml
index 50e51eb80e..b5f8128cfd 100644
--- a/WalletWasabi.Fluent/CoinList/Views/SelectCoinsDialogView.axaml
+++ b/WalletWasabi.Fluent/CoinList/Views/SelectCoinsDialogView.axaml
@@ -18,7 +18,8 @@
IsBusy="{Binding IsBusy}"
ScrollViewer.VerticalScrollBarVisibility="Disabled">
-
@@ -83,7 +87,8 @@
-
+
@@ -91,7 +96,8 @@
-
+
@@ -114,7 +120,8 @@
-
+
diff --git a/WalletWasabi.Fluent/Common/Views/Shell/WelcomeBg.axaml b/WalletWasabi.Fluent/Common/Views/Shell/WelcomeBg.axaml
index c5ac756ec6..32e96cbbee 100644
--- a/WalletWasabi.Fluent/Common/Views/Shell/WelcomeBg.axaml
+++ b/WalletWasabi.Fluent/Common/Views/Shell/WelcomeBg.axaml
@@ -238,4 +238,3 @@
-
diff --git a/WalletWasabi.Fluent/Common/Views/ShowErrorDialogView.axaml b/WalletWasabi.Fluent/Common/Views/ShowErrorDialogView.axaml
index 6b7f9a2c27..b28f93c976 100644
--- a/WalletWasabi.Fluent/Common/Views/ShowErrorDialogView.axaml
+++ b/WalletWasabi.Fluent/Common/Views/ShowErrorDialogView.axaml
@@ -8,11 +8,12 @@
x:CompileBindings="True"
x:Class="WalletWasabi.Fluent.Common.Views.ShowErrorDialogView">
+ Caption="{Binding Caption}"
+ EnableNext="True" NextContent="OK"
+ ScrollViewer.VerticalScrollBarVisibility="Disabled">
-
+
diff --git a/WalletWasabi.Fluent/Common/Views/SuccessAnimationView.axaml b/WalletWasabi.Fluent/Common/Views/SuccessAnimationView.axaml
index f96565d145..b7a8c30a7a 100644
--- a/WalletWasabi.Fluent/Common/Views/SuccessAnimationView.axaml
+++ b/WalletWasabi.Fluent/Common/Views/SuccessAnimationView.axaml
@@ -19,7 +19,7 @@
-
+
-
diff --git a/WalletWasabi.Fluent/Controls/AdorningContentControl.axaml b/WalletWasabi.Fluent/Controls/AdorningContentControl.axaml
index 547a1fcb4c..b55777f429 100644
--- a/WalletWasabi.Fluent/Controls/AdorningContentControl.axaml
+++ b/WalletWasabi.Fluent/Controls/AdorningContentControl.axaml
@@ -1,5 +1,5 @@
-
+
diff --git a/WalletWasabi.Fluent/Controls/AmountControl.axaml b/WalletWasabi.Fluent/Controls/AmountControl.axaml
index 3ae90545b9..47fa13587b 100644
--- a/WalletWasabi.Fluent/Controls/AmountControl.axaml
+++ b/WalletWasabi.Fluent/Controls/AmountControl.axaml
@@ -6,7 +6,8 @@
-
@@ -21,7 +22,8 @@
diff --git a/WalletWasabi.Fluent/Controls/ChatTextBox.cs b/WalletWasabi.Fluent/Controls/ChatTextBox.cs
index 52b95c2257..3e1471afc8 100644
--- a/WalletWasabi.Fluent/Controls/ChatTextBox.cs
+++ b/WalletWasabi.Fluent/Controls/ChatTextBox.cs
@@ -3,7 +3,7 @@
namespace WalletWasabi.Fluent.Controls;
-public class ChatTextBox: TextBox
+public class ChatTextBox : TextBox
{
protected override Type StyleKeyOverride => typeof(TextBox);
diff --git a/WalletWasabi.Fluent/Controls/ClipboardCopyButton.axaml b/WalletWasabi.Fluent/Controls/ClipboardCopyButton.axaml
index d13e1f0d80..56c87b1f3f 100644
--- a/WalletWasabi.Fluent/Controls/ClipboardCopyButton.axaml
+++ b/WalletWasabi.Fluent/Controls/ClipboardCopyButton.axaml
@@ -8,11 +8,13 @@
-
+
-
-
+
+
diff --git a/WalletWasabi.Fluent/Controls/ContentArea.axaml.cs b/WalletWasabi.Fluent/Controls/ContentArea.axaml.cs
index b138edecaa..59e816b974 100644
--- a/WalletWasabi.Fluent/Controls/ContentArea.axaml.cs
+++ b/WalletWasabi.Fluent/Controls/ContentArea.axaml.cs
@@ -1,7 +1,6 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Presenters;
-using Avalonia.Data;
using Avalonia.Media;
namespace WalletWasabi.Fluent.Controls;
diff --git a/WalletWasabi.Fluent/Controls/CurrencyEntryBox.axaml b/WalletWasabi.Fluent/Controls/CurrencyEntryBox.axaml
index 3dd82c0645..302c73f826 100644
--- a/WalletWasabi.Fluent/Controls/CurrencyEntryBox.axaml
+++ b/WalletWasabi.Fluent/Controls/CurrencyEntryBox.axaml
@@ -4,7 +4,7 @@
-
+
@@ -36,9 +36,13 @@
-
-
-
+
+
+
@@ -171,7 +175,7 @@
SelectionForegroundBrush="{TemplateBinding SelectionForegroundBrush}"
CaretBrush="{TemplateBinding CaretBrush}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
- VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
+ VerticalAlignment="{TemplateBinding VerticalContentAlignment}" />
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
@@ -8,8 +8,8 @@
-
-
+
+
@@ -17,8 +17,8 @@
+ Height="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=DummyRowsControl}, Path=RowHeight}"
+ Template="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=DummyRowsControl}, Path=RowTemplate}" />
diff --git a/WalletWasabi.Fluent/Controls/DummyRowsControl.axaml.cs b/WalletWasabi.Fluent/Controls/DummyRowsControl.axaml.cs
index 475f74fe70..58a338a7d3 100644
--- a/WalletWasabi.Fluent/Controls/DummyRowsControl.axaml.cs
+++ b/WalletWasabi.Fluent/Controls/DummyRowsControl.axaml.cs
@@ -44,5 +44,5 @@ public ControlTemplate RowTemplate
set => SetValue(RowTemplateProperty, value);
}
- private static IEnumerable GenerateItems(double rowHeight, Rect bounds) => Enumerable.Range(0, (int) Math.Ceiling(bounds.Height / rowHeight));
+ private static IEnumerable GenerateItems(double rowHeight, Rect bounds) => Enumerable.Range(0, (int)Math.Ceiling(bounds.Height / rowHeight));
}
diff --git a/WalletWasabi.Fluent/Controls/FadeOutTextControl.axaml b/WalletWasabi.Fluent/Controls/FadeOutTextControl.axaml
index 5598e96630..5133b293c7 100644
--- a/WalletWasabi.Fluent/Controls/FadeOutTextControl.axaml
+++ b/WalletWasabi.Fluent/Controls/FadeOutTextControl.axaml
@@ -22,10 +22,10 @@
FontSize="{TemplateBinding FontSize}"
Opacity="0" />
+ TextAlignment="{TemplateBinding HorizontalAlignment}"
+ FontSize="{TemplateBinding FontSize}"
+ Text="{TemplateBinding Text}"
+ TextTrimming="None" />
@@ -33,4 +33,3 @@
-
diff --git a/WalletWasabi.Fluent/Controls/LabelsItemsPresenter.axaml b/WalletWasabi.Fluent/Controls/LabelsItemsPresenter.axaml
index f05dcfe7e6..a2cbbe337d 100644
--- a/WalletWasabi.Fluent/Controls/LabelsItemsPresenter.axaml
+++ b/WalletWasabi.Fluent/Controls/LabelsItemsPresenter.axaml
@@ -33,7 +33,7 @@
HorizontalAlignment="{TemplateBinding HorizontalAlignment}"
VerticalAlignment="{TemplateBinding VerticalAlignment}">
+ ItemsPanel="{TemplateBinding ItemsPanel}" />
@@ -58,8 +58,9 @@
-
+
-
diff --git a/WalletWasabi.Fluent/Controls/NotificationCard.axaml b/WalletWasabi.Fluent/Controls/NotificationCard.axaml
index f639d69ad0..a414a36ba3 100644
--- a/WalletWasabi.Fluent/Controls/NotificationCard.axaml
+++ b/WalletWasabi.Fluent/Controls/NotificationCard.axaml
@@ -25,37 +25,37 @@
BorderThickness="2"
CornerRadius="{TemplateBinding CornerRadius}"
ClipToBounds="True">
-
+
-
-
+
+
-
-
-
+
+
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
-
-
-
-
-
+
+
+
+
+
diff --git a/WalletWasabi.Fluent/Controls/PreviewItem.axaml b/WalletWasabi.Fluent/Controls/PreviewItem.axaml
index 744e8829be..8d781ecb92 100644
--- a/WalletWasabi.Fluent/Controls/PreviewItem.axaml
+++ b/WalletWasabi.Fluent/Controls/PreviewItem.axaml
@@ -6,7 +6,7 @@
+ CopyableContent="Text to copy" />
@@ -19,17 +19,18 @@
Data="{TemplateBinding Icon}"
Margin="0 0 20 0"
Foreground="{DynamicResource SystemAccentCustomColorLight}"
- DockPanel.Dock="Left" IsVisible="{TemplateBinding Icon, Converter={x:Static ObjectConverters.IsNotNull}}" />
+ DockPanel.Dock="Left"
+ IsVisible="{TemplateBinding Icon, Converter={x:Static ObjectConverters.IsNotNull}}" />
+ IsVisible="{Binding !!$self.Text}" />
+ ToolTip.Tip="{x:Static lang:Resources.Copy}"
+ DockPanel.Dock="Right" Width="30"
+ Text="{Binding Path=CopyableContent, RelativeSource={RelativeSource TemplatedParent}, Converter={x:Static conv:StringConverters.AsString}}"
+ ShowCopyButton="{TemplateBinding IsCopyButtonVisible}" />
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ xmlns:conv="using:WalletWasabi.Fluent.Converters"
+ xmlns:lang="clr-namespace:WalletWasabi.Lang;assembly=WalletWasabi">
+ CopyableContent="Text to copy" />
@@ -16,13 +16,13 @@
+ ToolTip.Tip="{x:Static lang:Resources.Copy}"
+ DockPanel.Dock="{TemplateBinding CopyButtonPlacement}"
+ Width="30"
+ Text="{Binding Path=CopyableContent, RelativeSource={RelativeSource TemplatedParent}, Converter={x:Static conv:StringConverters.AsString}}"
+ ShowCopyButton="{TemplateBinding IsCopyButtonVisible}"
+ VerticalAlignment="Center"
+ HorizontalAlignment="Center" />
-
-
-
diff --git a/WalletWasabi.Fluent/Controls/PreviewMessageItem.axaml.cs b/WalletWasabi.Fluent/Controls/PreviewMessageItem.axaml.cs
index 430b171dbe..d1108ba4d6 100644
--- a/WalletWasabi.Fluent/Controls/PreviewMessageItem.axaml.cs
+++ b/WalletWasabi.Fluent/Controls/PreviewMessageItem.axaml.cs
@@ -117,9 +117,9 @@ protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
var isCopyButtonVisible =
button.CopyCommand
- .IsExecuting
- .CombineLatest(this.WhenAnyValue(x => x.IsPointerOver, x => x.CopyableContent, (a, b) => a && !string.IsNullOrWhiteSpace(b?.ToString())))
- .Select(x => x.First || x.Second);
+ .IsExecuting
+ .CombineLatest(this.WhenAnyValue(x => x.IsPointerOver, x => x.CopyableContent, (a, b) => a && !string.IsNullOrWhiteSpace(b?.ToString())))
+ .Select(x => x.First || x.Second);
Bind(IsCopyButtonVisibleProperty, isCopyButtonVisible);
}
diff --git a/WalletWasabi.Fluent/Controls/PrivacyBar.axaml b/WalletWasabi.Fluent/Controls/PrivacyBar.axaml
index df3aacfc68..9775d4f7d8 100644
--- a/WalletWasabi.Fluent/Controls/PrivacyBar.axaml
+++ b/WalletWasabi.Fluent/Controls/PrivacyBar.axaml
@@ -11,7 +11,7 @@
CornerRadius="{TemplateBinding CornerRadius}"
Padding="{TemplateBinding Padding}">
+ ItemsPanel="{TemplateBinding ItemsPanel}" />
diff --git a/WalletWasabi.Fluent/Controls/PrivacyContentControl.axaml b/WalletWasabi.Fluent/Controls/PrivacyContentControl.axaml
index d27e488fae..fec692c5bf 100644
--- a/WalletWasabi.Fluent/Controls/PrivacyContentControl.axaml
+++ b/WalletWasabi.Fluent/Controls/PrivacyContentControl.axaml
@@ -19,7 +19,8 @@
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
Background="Transparent" />
-
@@ -27,7 +28,7 @@
+ MaxPrivacyChars="{TemplateBinding MaxPrivacyChars}" />
@@ -56,14 +57,18 @@
diff --git a/WalletWasabi.Fluent/Controls/PrivacyContentControl.axaml.cs b/WalletWasabi.Fluent/Controls/PrivacyContentControl.axaml.cs
index 6a6b010399..5a2e6d6c28 100644
--- a/WalletWasabi.Fluent/Controls/PrivacyContentControl.axaml.cs
+++ b/WalletWasabi.Fluent/Controls/PrivacyContentControl.axaml.cs
@@ -4,7 +4,6 @@
using Avalonia.Controls;
using Avalonia.Interactivity;
using ReactiveUI;
-using WalletWasabi.Daemon;
using WalletWasabi.Fluent.Helpers;
using WalletWasabi.Fluent.Models.UI;
diff --git a/WalletWasabi.Fluent/Controls/ProgressRingArc.axaml.cs b/WalletWasabi.Fluent/Controls/ProgressRingArc.axaml.cs
index fe5dbe25e9..8f30b16248 100644
--- a/WalletWasabi.Fluent/Controls/ProgressRingArc.axaml.cs
+++ b/WalletWasabi.Fluent/Controls/ProgressRingArc.axaml.cs
@@ -177,10 +177,10 @@ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs e)
base.OnPropertyChanged(e);
if (e.Property == SegmentColorProperty ||
- e.Property == StrokeThicknessProperty ||
- e.Property == PercentageProperty ||
- e.Property == StrokeBorderThicknessProperty ||
- e.Property == PercentageProperty)
+ e.Property == StrokeThicknessProperty ||
+ e.Property == PercentageProperty ||
+ e.Property == StrokeBorderThicknessProperty ||
+ e.Property == PercentageProperty)
{
ShowArc = Percentage > 0;
ShowSegmentBorder = StrokeBorderThickness != 0;
@@ -221,7 +221,7 @@ private void RenderArc()
PathFigureStartPoint = startPoint;
if (Math.Abs(startPoint.X - Math.Round(endPoint.X)) < 0.01 &&
- Math.Abs(startPoint.Y - Math.Round(endPoint.Y)) < 0.01)
+ Math.Abs(startPoint.Y - Math.Round(endPoint.Y)) < 0.01)
{
endPoint -= new Point(0.01, 0);
}
diff --git a/WalletWasabi.Fluent/Controls/ScrollBar.axaml b/WalletWasabi.Fluent/Controls/ScrollBar.axaml
index 62a41b0d32..940ac825df 100644
--- a/WalletWasabi.Fluent/Controls/ScrollBar.axaml
+++ b/WalletWasabi.Fluent/Controls/ScrollBar.axaml
@@ -10,26 +10,30 @@
-
-
-
-
+
+
+
+
-
-
-
-
+
+
+
+
-
+
+ CornerRadius="{TemplateBinding CornerRadius}" />
@@ -65,13 +69,13 @@
-
+
+ Background="{DynamicResource ScrollBarButtonBackground}"
+ BorderBrush="{DynamicResource ScrollBarButtonBorderBrush}"
+ Content="{TemplateBinding Content}" />
@@ -81,27 +85,27 @@
@@ -120,7 +124,7 @@
-
+
-
+
IsCustomOpenProperty =
- AvaloniaProperty.Register(nameof(IsCustomOpen));
+ public class SearchBarPopup : Popup
+ {
+ public static readonly StyledProperty IsCustomOpenProperty =
+ AvaloniaProperty.Register(nameof(IsCustomOpen));
- public bool IsCustomOpen
- {
- get => GetValue(IsCustomOpenProperty);
- set => SetValue(IsCustomOpenProperty, value);
- }
+ public bool IsCustomOpen
+ {
+ get => GetValue(IsCustomOpenProperty);
+ set => SetValue(IsCustomOpenProperty, value);
+ }
- private const double AllowedMaxHeight = 512;
- private const double AllowedMaxWidth = 456;
+ private const double AllowedMaxHeight = 512;
+ private const double AllowedMaxWidth = 456;
- private Animation? _expandAnimation = new()
- {
- Easing = Easing.Parse("0.4,0,0.6,1"),
- Duration = TimeSpan.FromMilliseconds(200),
- Children =
- {
- new KeyFrame
- {
- Setters =
- {
- new Setter(MaxHeightProperty, 0.0),
- new Setter(MaxWidthProperty, 0.0),
- },
- Cue = new Cue(0)
- },
- new KeyFrame
- {
- Setters =
- {
- new Setter(MaxHeightProperty, AllowedMaxHeight * 0.75),
- new Setter(MaxWidthProperty, AllowedMaxWidth * 0.75),
- },
- Cue = new Cue(0.1)
- },
- new KeyFrame
- {
- Setters =
- {
- new Setter(MaxHeightProperty, AllowedMaxHeight),
- new Setter(MaxWidthProperty, AllowedMaxWidth),
- },
- Cue = new Cue(1)
- },
- }
- };
+ private Animation? _expandAnimation = new()
+ {
+ Easing = Easing.Parse("0.4,0,0.6,1"),
+ Duration = TimeSpan.FromMilliseconds(200),
+ Children =
+ {
+ new KeyFrame
+ {
+ Setters =
+ {
+ new Setter(MaxHeightProperty, 0.0),
+ new Setter(MaxWidthProperty, 0.0),
+ },
+ Cue = new Cue(0)
+ },
+ new KeyFrame
+ {
+ Setters =
+ {
+ new Setter(MaxHeightProperty, AllowedMaxHeight * 0.75),
+ new Setter(MaxWidthProperty, AllowedMaxWidth * 0.75),
+ },
+ Cue = new Cue(0.1)
+ },
+ new KeyFrame
+ {
+ Setters =
+ {
+ new Setter(MaxHeightProperty, AllowedMaxHeight),
+ new Setter(MaxWidthProperty, AllowedMaxWidth),
+ },
+ Cue = new Cue(1)
+ },
+ }
+ };
- private Animation? _collapseAnimation = new()
- {
- Easing = Easing.Parse("0.4,0,0.6,1"),
- Duration = TimeSpan.FromMilliseconds(200),
- Children =
- {
- new KeyFrame
- {
- Setters =
- {
- new Setter(MaxHeightProperty, AllowedMaxHeight),
- new Setter(MaxWidthProperty, AllowedMaxWidth),
- },
- Cue = new Cue(0)
- },
- new KeyFrame
- {
- Setters =
- {
- new Setter(MaxHeightProperty, AllowedMaxHeight * 0.25),
- new Setter(MaxWidthProperty, AllowedMaxWidth * 0.25),
- },
- Cue = new Cue(0.9)
- },
- new KeyFrame
- {
- Setters =
- {
- new Setter(MaxHeightProperty, 0.0),
- new Setter(MaxWidthProperty, 0.0),
- },
- Cue = new Cue(1)
- },
- }
- };
+ private Animation? _collapseAnimation = new()
+ {
+ Easing = Easing.Parse("0.4,0,0.6,1"),
+ Duration = TimeSpan.FromMilliseconds(200),
+ Children =
+ {
+ new KeyFrame
+ {
+ Setters =
+ {
+ new Setter(MaxHeightProperty, AllowedMaxHeight),
+ new Setter(MaxWidthProperty, AllowedMaxWidth),
+ },
+ Cue = new Cue(0)
+ },
+ new KeyFrame
+ {
+ Setters =
+ {
+ new Setter(MaxHeightProperty, AllowedMaxHeight * 0.25),
+ new Setter(MaxWidthProperty, AllowedMaxWidth * 0.25),
+ },
+ Cue = new Cue(0.9)
+ },
+ new KeyFrame
+ {
+ Setters =
+ {
+ new Setter(MaxHeightProperty, 0.0),
+ new Setter(MaxWidthProperty, 0.0),
+ },
+ Cue = new Cue(1)
+ },
+ }
+ };
- protected async override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
- {
- base.OnPropertyChanged(change);
+ protected async override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
+ {
+ base.OnPropertyChanged(change);
- if (change.Property == IsCustomOpenProperty && change.NewValue is bool isCustomOpen)
- {
- if (isCustomOpen)
- {
- IsOpen = true;
+ if (change.Property == IsCustomOpenProperty && change.NewValue is bool isCustomOpen)
+ {
+ if (isCustomOpen)
+ {
+ IsOpen = true;
- if (_expandAnimation != null)
- {
- await _expandAnimation.RunAsync(this);
- MaxHeight = AllowedMaxHeight;
- MaxWidth = AllowedMaxWidth;
- }
- }
- else
- {
- if (_collapseAnimation != null)
- {
- await _collapseAnimation.RunAsync(this);
- MaxHeight = 0;
- MaxWidth = 0;
- }
+ if (_expandAnimation != null)
+ {
+ await _expandAnimation.RunAsync(this);
+ MaxHeight = AllowedMaxHeight;
+ MaxWidth = AllowedMaxWidth;
+ }
+ }
+ else
+ {
+ if (_collapseAnimation != null)
+ {
+ await _collapseAnimation.RunAsync(this);
+ MaxHeight = 0;
+ MaxWidth = 0;
+ }
- IsOpen = false;
- }
- }
- }
- }
+ IsOpen = false;
+ }
+ }
+ }
+ }
}
diff --git a/WalletWasabi.Fluent/Controls/Sorting/SortControl.axaml b/WalletWasabi.Fluent/Controls/Sorting/SortControl.axaml
index 1a13c02006..c2907a110d 100644
--- a/WalletWasabi.Fluent/Controls/Sorting/SortControl.axaml
+++ b/WalletWasabi.Fluent/Controls/Sorting/SortControl.axaml
@@ -1,8 +1,8 @@
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ xmlns:behaviors="clr-namespace:WalletWasabi.Fluent.Behaviors"
+ xmlns:generic="clr-namespace:System.Collections.Generic;assembly=System.Collections"
+ xmlns:sorting="clr-namespace:WalletWasabi.Fluent.Controls.Sorting">
diff --git a/WalletWasabi.Fluent/Controls/Spectrum/SpectrumControl.cs b/WalletWasabi.Fluent/Controls/Spectrum/SpectrumControl.cs
index 457e2d9450..812a3cecee 100644
--- a/WalletWasabi.Fluent/Controls/Spectrum/SpectrumControl.cs
+++ b/WalletWasabi.Fluent/Controls/Spectrum/SpectrumControl.cs
@@ -81,62 +81,62 @@ public override void Render(DrawingContext context)
_state.Render(context);
}
#else
- protected override void OnLoaded(RoutedEventArgs routedEventArgs)
- {
- base.OnLoaded(routedEventArgs);
-
- var elemVisual = ElementComposition.GetElementVisual(this);
- var compositor = elemVisual?.Compositor;
- if (compositor is null)
- {
- return;
- }
-
- _customVisual = compositor.CreateCustomVisual(new DrawCompositionCustomVisualHandler());
- ElementComposition.SetElementChildVisual(this, _customVisual);
-
- LayoutUpdated += OnLayoutUpdated;
-
- _customVisual.Size = new Vector2((float)Bounds.Size.Width, (float)Bounds.Size.Height);
- _customVisual.SendHandlerMessage(new DrawPayload(HandlerCommand.Update, _spectrumDrawHandler));
-
- // TODO: Start();
- }
-
- protected override void OnUnloaded(RoutedEventArgs routedEventArgs)
- {
- base.OnUnloaded(routedEventArgs);
-
- LayoutUpdated -= OnLayoutUpdated;
-
- Stop();
- DisposeImpl();
- }
-
- private void OnLayoutUpdated(object? sender, EventArgs e)
- {
- if (_customVisual == null)
- {
- return;
- }
-
- _customVisual.Size = new Vector2((float)Bounds.Size.Width, (float)Bounds.Size.Height);
- _customVisual.SendHandlerMessage(new DrawPayload(HandlerCommand.Update, _spectrumDrawHandler, Bounds));
- }
-
- public void Start()
- {
- _customVisual?.SendHandlerMessage(new DrawPayload(HandlerCommand.Start, _spectrumDrawHandler, Bounds));
- }
-
- public void Stop()
- {
- _customVisual?.SendHandlerMessage(new DrawPayload(HandlerCommand.Stop));
- }
-
- private void DisposeImpl()
- {
- _customVisual?.SendHandlerMessage(new DrawPayload(HandlerCommand.Dispose));
- }
+ protected override void OnLoaded(RoutedEventArgs routedEventArgs)
+ {
+ base.OnLoaded(routedEventArgs);
+
+ var elemVisual = ElementComposition.GetElementVisual(this);
+ var compositor = elemVisual?.Compositor;
+ if (compositor is null)
+ {
+ return;
+ }
+
+ _customVisual = compositor.CreateCustomVisual(new DrawCompositionCustomVisualHandler());
+ ElementComposition.SetElementChildVisual(this, _customVisual);
+
+ LayoutUpdated += OnLayoutUpdated;
+
+ _customVisual.Size = new Vector2((float)Bounds.Size.Width, (float)Bounds.Size.Height);
+ _customVisual.SendHandlerMessage(new DrawPayload(HandlerCommand.Update, _spectrumDrawHandler));
+
+ // TODO: Start();
+ }
+
+ protected override void OnUnloaded(RoutedEventArgs routedEventArgs)
+ {
+ base.OnUnloaded(routedEventArgs);
+
+ LayoutUpdated -= OnLayoutUpdated;
+
+ Stop();
+ DisposeImpl();
+ }
+
+ private void OnLayoutUpdated(object? sender, EventArgs e)
+ {
+ if (_customVisual == null)
+ {
+ return;
+ }
+
+ _customVisual.Size = new Vector2((float)Bounds.Size.Width, (float)Bounds.Size.Height);
+ _customVisual.SendHandlerMessage(new DrawPayload(HandlerCommand.Update, _spectrumDrawHandler, Bounds));
+ }
+
+ public void Start()
+ {
+ _customVisual?.SendHandlerMessage(new DrawPayload(HandlerCommand.Start, _spectrumDrawHandler, Bounds));
+ }
+
+ public void Stop()
+ {
+ _customVisual?.SendHandlerMessage(new DrawPayload(HandlerCommand.Stop));
+ }
+
+ private void DisposeImpl()
+ {
+ _customVisual?.SendHandlerMessage(new DrawPayload(HandlerCommand.Dispose));
+ }
#endif
}
diff --git a/WalletWasabi.Fluent/Controls/Spectrum/SpectrumDrawHandler.cs b/WalletWasabi.Fluent/Controls/Spectrum/SpectrumDrawHandler.cs
index 5142aab767..a1e5fb1327 100644
--- a/WalletWasabi.Fluent/Controls/Spectrum/SpectrumDrawHandler.cs
+++ b/WalletWasabi.Fluent/Controls/Spectrum/SpectrumDrawHandler.cs
@@ -1,6 +1,5 @@
using System.Linq;
using Avalonia;
-using Avalonia.Media;
using Avalonia.Skia;
using Avalonia.Threading;
using SkiaSharp;
diff --git a/WalletWasabi.Fluent/Controls/Spectrum/SpectrumDrawOperation.cs b/WalletWasabi.Fluent/Controls/Spectrum/SpectrumDrawOperation.cs
index ac25fb0953..492c996389 100644
--- a/WalletWasabi.Fluent/Controls/Spectrum/SpectrumDrawOperation.cs
+++ b/WalletWasabi.Fluent/Controls/Spectrum/SpectrumDrawOperation.cs
@@ -34,6 +34,7 @@ void ICustomDrawOperation.Render(ImmediateDrawingContext context)
{
return;
}
+
_draw(skia, Bounds);
}
}
diff --git a/WalletWasabi.Fluent/Controls/SplitButton.axaml b/WalletWasabi.Fluent/Controls/SplitButton.axaml
index a1478a7073..e66a460666 100644
--- a/WalletWasabi.Fluent/Controls/SplitButton.axaml
+++ b/WalletWasabi.Fluent/Controls/SplitButton.axaml
@@ -1,15 +1,17 @@
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ xmlns:converters="using:Avalonia.Controls.Converters"
+ x:ClassModifier="internal">
24
20
1
32
-
-
+
+
@@ -133,7 +135,7 @@
Hello
-
+
diff --git a/WalletWasabi.Fluent/Controls/StatusItem.axaml b/WalletWasabi.Fluent/Controls/StatusItem.axaml
index 628458b37e..45992d9525 100644
--- a/WalletWasabi.Fluent/Controls/StatusItem.axaml
+++ b/WalletWasabi.Fluent/Controls/StatusItem.axaml
@@ -18,7 +18,8 @@
-
+
diff --git a/WalletWasabi.Fluent/Controls/SubActionButton.axaml b/WalletWasabi.Fluent/Controls/SubActionButton.axaml
index f2efe9f099..212ef59c62 100644
--- a/WalletWasabi.Fluent/Controls/SubActionButton.axaml
+++ b/WalletWasabi.Fluent/Controls/SubActionButton.axaml
@@ -66,7 +66,9 @@
-
+
diff --git a/WalletWasabi.Fluent/Controls/SuggestionItem.axaml b/WalletWasabi.Fluent/Controls/SuggestionItem.axaml
index b8c52cbb9d..b9b2c90160 100644
--- a/WalletWasabi.Fluent/Controls/SuggestionItem.axaml
+++ b/WalletWasabi.Fluent/Controls/SuggestionItem.axaml
@@ -29,14 +29,14 @@
+ ClipToBounds="{TemplateBinding ClipToBounds}"
+ BorderBrush="{TemplateBinding BorderBrush}"
+ BorderThickness="{TemplateBinding BorderThickness}"
+ ContentTemplate="{TemplateBinding ContentTemplate}"
+ Content="{TemplateBinding Content}"
+ Padding="{TemplateBinding Padding}"
+ VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
+ HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}" />
diff --git a/WalletWasabi.Fluent/Controls/TagControl.axaml b/WalletWasabi.Fluent/Controls/TagControl.axaml
index d65c07c111..154d4f6228 100644
--- a/WalletWasabi.Fluent/Controls/TagControl.axaml
+++ b/WalletWasabi.Fluent/Controls/TagControl.axaml
@@ -54,7 +54,8 @@
-
+
diff --git a/WalletWasabi.Fluent/Controls/TagsBox.axaml b/WalletWasabi.Fluent/Controls/TagsBox.axaml
index 723fbef825..cb6032d12a 100644
--- a/WalletWasabi.Fluent/Controls/TagsBox.axaml
+++ b/WalletWasabi.Fluent/Controls/TagsBox.axaml
@@ -1,8 +1,7 @@
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ xmlns:generic="clr-namespace:System.Collections.Generic;assembly=System.Collections"
+ xmlns:system="clr-namespace:System;assembly=System.Runtime">
@@ -16,7 +15,7 @@
-
+
@@ -40,71 +39,71 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -116,7 +115,8 @@
-
+
@@ -127,18 +127,18 @@
diff --git a/WalletWasabi.Fluent/Controls/TagsBox.axaml.cs b/WalletWasabi.Fluent/Controls/TagsBox.axaml.cs
index 798ecbb481..66038b25cf 100644
--- a/WalletWasabi.Fluent/Controls/TagsBox.axaml.cs
+++ b/WalletWasabi.Fluent/Controls/TagsBox.axaml.cs
@@ -13,7 +13,6 @@
using Avalonia.Metadata;
using Avalonia.Threading;
using Avalonia.VisualTree;
-using Microsoft.Net.Http.Headers;
using ReactiveUI;
using WalletWasabi.Fluent.Extensions;
using WalletWasabi.Fluent.Helpers;
@@ -229,9 +228,9 @@ private void Initialize()
}
_autoCompleteBox.InternalTextBox.WhenAnyValue(x => x.IsFocused)
- .Where(isFocused => isFocused == false)
- .Subscribe(_ => RequestAdd = true)
- .DisposeWith(_compositeDisposable);
+ .Where(isFocused => isFocused == false)
+ .Subscribe(_ => RequestAdd = true)
+ .DisposeWith(_compositeDisposable);
if (_autoCompleteBox.SuggestionListBox is not null)
{
diff --git a/WalletWasabi.Fluent/Controls/TagsBoxAutoCompleteBox.axaml b/WalletWasabi.Fluent/Controls/TagsBoxAutoCompleteBox.axaml
index 1259cb6279..d97527715a 100644
--- a/WalletWasabi.Fluent/Controls/TagsBoxAutoCompleteBox.axaml
+++ b/WalletWasabi.Fluent/Controls/TagsBoxAutoCompleteBox.axaml
@@ -1,6 +1,6 @@
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ xmlns:lang="clr-namespace:WalletWasabi.Lang;assembly=WalletWasabi">
@@ -10,80 +10,81 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
-
+
-
+
diff --git a/WalletWasabi.Fluent/Controls/TextBox.axaml b/WalletWasabi.Fluent/Controls/TextBox.axaml
index 03fa5fbb4a..2a6cca5be6 100644
--- a/WalletWasabi.Fluent/Controls/TextBox.axaml
+++ b/WalletWasabi.Fluent/Controls/TextBox.axaml
@@ -9,7 +9,7 @@
-
+
@@ -18,9 +18,12 @@
-
-
-
+
+
+
@@ -51,15 +54,17 @@
-
+
-
+
-
+
-
-
+
+
@@ -124,7 +129,8 @@
-
+
@@ -143,10 +149,10 @@
+ IsVisible="{Binding !$parent[ToggleButton].IsChecked}" />
+ IsVisible="{Binding $parent[ToggleButton].IsChecked}" />
@@ -155,50 +161,51 @@
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/WalletWasabi.Fluent/Controls/ThemeImage.cs b/WalletWasabi.Fluent/Controls/ThemeImage.cs
index ff77a89948..7b07138025 100644
--- a/WalletWasabi.Fluent/Controls/ThemeImage.cs
+++ b/WalletWasabi.Fluent/Controls/ThemeImage.cs
@@ -3,7 +3,7 @@
namespace WalletWasabi.Fluent.Controls;
-public class ThemeImage: Image
+public class ThemeImage : Image
{
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
{
@@ -12,11 +12,11 @@ protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
{
- ActualThemeVariantChanged -= OnThemeVariantChanged;
+ ActualThemeVariantChanged -= OnThemeVariantChanged;
}
private void OnThemeVariantChanged(object? sender, EventArgs e)
{
InvalidateVisual();
- }
+ }
}
diff --git a/WalletWasabi.Fluent/Controls/TileControl.axaml b/WalletWasabi.Fluent/Controls/TileControl.axaml
index c692b3b1e8..803826d193 100644
--- a/WalletWasabi.Fluent/Controls/TileControl.axaml
+++ b/WalletWasabi.Fluent/Controls/TileControl.axaml
@@ -22,7 +22,8 @@
-
+
diff --git a/WalletWasabi.Fluent/Controls/ToggleRadioButton.axaml b/WalletWasabi.Fluent/Controls/ToggleRadioButton.axaml
index 4a2180e810..8f3d558cd6 100644
--- a/WalletWasabi.Fluent/Controls/ToggleRadioButton.axaml
+++ b/WalletWasabi.Fluent/Controls/ToggleRadioButton.axaml
@@ -16,7 +16,8 @@
-
+
diff --git a/WalletWasabi.Fluent/Controls/TreeDataGrid.axaml b/WalletWasabi.Fluent/Controls/TreeDataGrid.axaml
index 6b28ebb35e..b72225ee51 100644
--- a/WalletWasabi.Fluent/Controls/TreeDataGrid.axaml
+++ b/WalletWasabi.Fluent/Controls/TreeDataGrid.axaml
@@ -33,7 +33,7 @@
-
+
-
+
@@ -173,7 +174,7 @@
Classes.isChild="{Binding IsChild}"
Classes.isLastChild="{Binding IsLastChild}"
IsHitTestVisible="False"
- Margin="{StaticResource TreeDataGridMarginFix}"/>
+ Margin="{StaticResource TreeDataGridMarginFix}" />
@@ -267,7 +268,8 @@
-
+
@@ -306,7 +308,8 @@
-
+
@@ -330,7 +333,8 @@
-
+
@@ -342,7 +346,7 @@
-
+
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
-
+
diff --git a/WalletWasabi.Fluent/Converters/MoneyConverters.cs b/WalletWasabi.Fluent/Converters/MoneyConverters.cs
index 86da760bb0..9b2ca0525b 100644
--- a/WalletWasabi.Fluent/Converters/MoneyConverters.cs
+++ b/WalletWasabi.Fluent/Converters/MoneyConverters.cs
@@ -32,5 +32,5 @@ public static class MoneyConverters
new FuncValueConverter(n => n?.ToFeeDisplayUnitRawString());
public static readonly IValueConverter PercentageDifferenceConverter =
- new FuncValueConverter(TextHelpers.FormatPercentageDiff );
+ new FuncValueConverter(TextHelpers.FormatPercentageDiff);
}
diff --git a/WalletWasabi.Fluent/Converters/TextToInlineCollectionConverter.cs b/WalletWasabi.Fluent/Converters/TextToInlineCollectionConverter.cs
index e2125225c1..d0daf4e4f0 100644
--- a/WalletWasabi.Fluent/Converters/TextToInlineCollectionConverter.cs
+++ b/WalletWasabi.Fluent/Converters/TextToInlineCollectionConverter.cs
@@ -11,83 +11,83 @@ namespace WalletWasabi.Fluent.Converters;
public class TextToInlineCollectionConverter
{
- public static readonly FuncValueConverter Instance = new(ToInlineCollection);
+ public static readonly FuncValueConverter Instance = new(ToInlineCollection);
- // This regular expression will handle detecting URLs, **bold** text, and markdown links [text](url).
- private static readonly Regex Regex = new(@"(\*\*(.*?)\*\*)|(\b(?:https?://|www\.)\S+\b)|(\[(.*?)\]\((http[s]?:\/\/.*?)\))", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline);
+ // This regular expression will handle detecting URLs, **bold** text, and markdown links [text](url).
+ private static readonly Regex Regex = new(@"(\*\*(.*?)\*\*)|(\b(?:https?://|www\.)\S+\b)|(\[(.*?)\]\((http[s]?:\/\/.*?)\))", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline);
- private static InlineCollection ToInlineCollection(string? str)
- {
- if (string.IsNullOrEmpty(str))
- {
- return new InlineCollection { new Run { Text = string.Empty } }; // Returns an empty Run if there's no text.
- }
+ private static InlineCollection ToInlineCollection(string? str)
+ {
+ if (string.IsNullOrEmpty(str))
+ {
+ return new InlineCollection { new Run { Text = string.Empty } }; // Returns an empty Run if there's no text.
+ }
- var inlines = new List();
- var lastIndex = 0;
+ var inlines = new List();
+ var lastIndex = 0;
- foreach (Match match in Regex.Matches(str))
- {
- // Add normal text before the found match.
- if (match.Index > lastIndex)
- {
- inlines.Add(new Run { Text = str[lastIndex..match.Index], BaselineAlignment = BaselineAlignment.Center });
- }
+ foreach (Match match in Regex.Matches(str))
+ {
+ // Add normal text before the found match.
+ if (match.Index > lastIndex)
+ {
+ inlines.Add(new Run { Text = str[lastIndex..match.Index], BaselineAlignment = BaselineAlignment.Center });
+ }
- // Check if the match is a URL, bold text, or a markdown link.
- if (match.Value.StartsWith("**") && match.Value.EndsWith("**"))
- {
- // Extract the text inside the **bold** tags.
- var boldText = match.Groups[2].Value; // Uses the captured group for the bold text.
- inlines.Add(new Run { Text = boldText, FontWeight = FontWeight.Bold });
- }
- else if (!string.IsNullOrEmpty(match.Groups[3].Value)) // It's a URL.
- {
- var url = match.Groups[3].Value;
- var hyperlink = new InlineUIContainer(
- new ContentControl
- {
- Content = new LinkViewModel()
- {
- IsClickable = true,
- Description = url,
- Link = url
- }
- });
+ // Check if the match is a URL, bold text, or a markdown link.
+ if (match.Value.StartsWith("**") && match.Value.EndsWith("**"))
+ {
+ // Extract the text inside the **bold** tags.
+ var boldText = match.Groups[2].Value; // Uses the captured group for the bold text.
+ inlines.Add(new Run { Text = boldText, FontWeight = FontWeight.Bold });
+ }
+ else if (!string.IsNullOrEmpty(match.Groups[3].Value)) // It's a URL.
+ {
+ var url = match.Groups[3].Value;
+ var hyperlink = new InlineUIContainer(
+ new ContentControl
+ {
+ Content = new LinkViewModel()
+ {
+ IsClickable = true,
+ Description = url,
+ Link = url
+ }
+ });
- hyperlink.BaselineAlignment = BaselineAlignment.Center;
- inlines.Add(hyperlink);
- }
- else if (!string.IsNullOrEmpty(match.Groups[5].Value) && !string.IsNullOrEmpty(match.Groups[6].Value)) // It's a markdown link.
- {
- var linkText = match.Groups[5].Value;
- var url = match.Groups[6].Value;
- var hyperlink = new InlineUIContainer(
- new ContentControl
- {
- Content = new LinkViewModel()
- {
- IsClickable = true,
- Description = linkText, // Uses the visible text of the markdown link.
- Link = url
- }
- });
+ hyperlink.BaselineAlignment = BaselineAlignment.Center;
+ inlines.Add(hyperlink);
+ }
+ else if (!string.IsNullOrEmpty(match.Groups[5].Value) && !string.IsNullOrEmpty(match.Groups[6].Value)) // It's a markdown link.
+ {
+ var linkText = match.Groups[5].Value;
+ var url = match.Groups[6].Value;
+ var hyperlink = new InlineUIContainer(
+ new ContentControl
+ {
+ Content = new LinkViewModel()
+ {
+ IsClickable = true,
+ Description = linkText, // Uses the visible text of the markdown link.
+ Link = url
+ }
+ });
- hyperlink.BaselineAlignment = BaselineAlignment.Center;
- inlines.Add(hyperlink);
- }
+ hyperlink.BaselineAlignment = BaselineAlignment.Center;
+ inlines.Add(hyperlink);
+ }
- lastIndex = match.Index + match.Length;
- }
+ lastIndex = match.Index + match.Length;
+ }
- // Add any remaining text after the last match.
- if (lastIndex < str.Length)
- {
- inlines.Add(new Run { Text = str[lastIndex..], BaselineAlignment = BaselineAlignment.Center });
- }
+ // Add any remaining text after the last match.
+ if (lastIndex < str.Length)
+ {
+ inlines.Add(new Run { Text = str[lastIndex..], BaselineAlignment = BaselineAlignment.Center });
+ }
- var inlineCollection = new InlineCollection();
- inlineCollection.AddRange(inlines);
- return inlineCollection;
- }
+ var inlineCollection = new InlineCollection();
+ inlineCollection.AddRange(inlines);
+ return inlineCollection;
+ }
}
diff --git a/WalletWasabi.Fluent/CrashReport/ViewModels/CrashReportWindowViewModel.cs b/WalletWasabi.Fluent/CrashReport/ViewModels/CrashReportWindowViewModel.cs
index f2991abe78..e9c3deba90 100644
--- a/WalletWasabi.Fluent/CrashReport/ViewModels/CrashReportWindowViewModel.cs
+++ b/WalletWasabi.Fluent/CrashReport/ViewModels/CrashReportWindowViewModel.cs
@@ -17,10 +17,7 @@ public CrashReportWindowViewModel(SerializableException serializedException)
OpenGitHubRepoCommand = ReactiveCommand.CreateFromTask(async () => await WebBrowserService.Instance.OpenUrlInPreferredBrowserAsync(Link));
- CopyTraceCommand = ReactiveCommand.CreateFromTask(async () =>
- {
- await ApplicationHelper.SetTextAsync(Trace);
- });
+ CopyTraceCommand = ReactiveCommand.CreateFromTask(async () => { await ApplicationHelper.SetTextAsync(Trace); });
}
public SerializableException SerializedException { get; }
diff --git a/WalletWasabi.Fluent/CrashReport/Views/CrashReportWindow.axaml b/WalletWasabi.Fluent/CrashReport/Views/CrashReportWindow.axaml
index 3caa7e4df0..8a5911474f 100644
--- a/WalletWasabi.Fluent/CrashReport/Views/CrashReportWindow.axaml
+++ b/WalletWasabi.Fluent/CrashReport/Views/CrashReportWindow.axaml
@@ -13,14 +13,14 @@
x:DataType="vm:CrashReportWindowViewModel">
+ EnableCancel="True"
+ NextContent="Exit"
+ CancelContent="Restart Ginger Wallet"
+ FocusNext="True"
+ Title="{Binding Title}"
+ Caption="{Binding Caption}"
+ ScrollViewer.HorizontalScrollBarVisibility="Disabled"
+ ScrollViewer.VerticalScrollBarVisibility="Disabled">
-
-
+
+
-
+
-
+
@@ -138,8 +141,9 @@
-
-
+
+
diff --git a/WalletWasabi.Fluent/HomeScreen/BuySell/Models/BuySellModel.cs b/WalletWasabi.Fluent/HomeScreen/BuySell/Models/BuySellModel.cs
index 0775533511..be821514ed 100644
--- a/WalletWasabi.Fluent/HomeScreen/BuySell/Models/BuySellModel.cs
+++ b/WalletWasabi.Fluent/HomeScreen/BuySell/Models/BuySellModel.cs
@@ -10,7 +10,6 @@
using WalletWasabi.Daemon.BuySell;
using WalletWasabi.Fluent.Extensions;
using WalletWasabi.Fluent.HomeScreen.BuySell.Extensions;
-using WalletWasabi.Fluent.Infrastructure;
using WalletWasabi.Logging;
using WalletWasabi.Wallets;
diff --git a/WalletWasabi.Fluent/HomeScreen/BuySell/ViewModels/BuyOffersViewModel.cs b/WalletWasabi.Fluent/HomeScreen/BuySell/ViewModels/BuyOffersViewModel.cs
index f579086a9a..49027693d6 100644
--- a/WalletWasabi.Fluent/HomeScreen/BuySell/ViewModels/BuyOffersViewModel.cs
+++ b/WalletWasabi.Fluent/HomeScreen/BuySell/ViewModels/BuyOffersViewModel.cs
@@ -15,7 +15,6 @@ public partial class BuyOffersViewModel : OffersViewModel
{
public BuyOffersViewModel(WalletModel wallet, IEnumerable offers) : base(wallet, offers)
{
-
}
protected override OfferViewModel CreateOfferViewModel(OfferModel model)
diff --git a/WalletWasabi.Fluent/HomeScreen/BuySell/ViewModels/BuyViewModel.cs b/WalletWasabi.Fluent/HomeScreen/BuySell/ViewModels/BuyViewModel.cs
index a49877a09d..bbc5a487c2 100644
--- a/WalletWasabi.Fluent/HomeScreen/BuySell/ViewModels/BuyViewModel.cs
+++ b/WalletWasabi.Fluent/HomeScreen/BuySell/ViewModels/BuyViewModel.cs
@@ -130,7 +130,7 @@ private void ValidateAmount(IValidationErrors errors)
if (decimalAmount > MaxAmount)
{
- errors.Add(ErrorSeverity.Error,Resources.AmountCannotExceed.SafeInject(MaxAmount.ToFormattedFiat(SelectedCurrency?.Ticker)));
+ errors.Add(ErrorSeverity.Error, Resources.AmountCannotExceed.SafeInject(MaxAmount.ToFormattedFiat(SelectedCurrency?.Ticker)));
}
else if (decimalAmount < MinAmount)
{
@@ -212,7 +212,6 @@ private async Task SetLimitsAsync()
MinAmount = min;
MaxAmount = max;
-
}
catch (Exception ex)
{
diff --git a/WalletWasabi.Fluent/HomeScreen/BuySell/ViewModels/OffersViewModel.cs b/WalletWasabi.Fluent/HomeScreen/BuySell/ViewModels/OffersViewModel.cs
index 4f17c62385..5fcdcf9e04 100644
--- a/WalletWasabi.Fluent/HomeScreen/BuySell/ViewModels/OffersViewModel.cs
+++ b/WalletWasabi.Fluent/HomeScreen/BuySell/ViewModels/OffersViewModel.cs
@@ -29,7 +29,7 @@ protected OffersViewModel(WalletModel wallet, IEnumerable offers)
Title = Resources.Offers;
_wallet = wallet;
- PaymentMethods = new []{_allText}.Concat(offers.Select(x => x.MethodName).Order()).Distinct();
+ PaymentMethods = new[] { _allText }.Concat(offers.Select(x => x.MethodName).Order()).Distinct();
_selectedPaymentMethod = UiContext.ApplicationSettings.BuySellConfiguration.BuyPaymentMethod ?? "";
if (string.IsNullOrEmpty(_selectedPaymentMethod) || !PaymentMethods.Contains(_selectedPaymentMethod))
diff --git a/WalletWasabi.Fluent/HomeScreen/BuySell/ViewModels/OrdersViewModel.cs b/WalletWasabi.Fluent/HomeScreen/BuySell/ViewModels/OrdersViewModel.cs
index fad2b9b0f8..a53a63aad1 100644
--- a/WalletWasabi.Fluent/HomeScreen/BuySell/ViewModels/OrdersViewModel.cs
+++ b/WalletWasabi.Fluent/HomeScreen/BuySell/ViewModels/OrdersViewModel.cs
@@ -63,23 +63,23 @@ protected override void OnNavigatedTo(bool isInHistory, CompositeDisposable disp
[
new SortableItem(Resources.Status)
{
- SortByAscendingCommand = ReactiveCommand.Create(() => ((ITreeDataGridSource) Source).SortBy(Source.Columns[0], ListSortDirection.Ascending)),
- SortByDescendingCommand = ReactiveCommand.Create(() => ((ITreeDataGridSource) Source).SortBy(Source.Columns[0], ListSortDirection.Descending))
+ SortByAscendingCommand = ReactiveCommand.Create(() => ((ITreeDataGridSource)Source).SortBy(Source.Columns[0], ListSortDirection.Ascending)),
+ SortByDescendingCommand = ReactiveCommand.Create(() => ((ITreeDataGridSource)Source).SortBy(Source.Columns[0], ListSortDirection.Descending))
},
new SortableItem(Resources.Date)
{
- SortByAscendingCommand = ReactiveCommand.Create(() => ((ITreeDataGridSource) Source).SortBy(Source.Columns[1], ListSortDirection.Ascending)),
- SortByDescendingCommand = ReactiveCommand.Create(() => ((ITreeDataGridSource) Source).SortBy(Source.Columns[1], ListSortDirection.Descending))
+ SortByAscendingCommand = ReactiveCommand.Create(() => ((ITreeDataGridSource)Source).SortBy(Source.Columns[1], ListSortDirection.Ascending)),
+ SortByDescendingCommand = ReactiveCommand.Create(() => ((ITreeDataGridSource)Source).SortBy(Source.Columns[1], ListSortDirection.Descending))
},
new SortableItem(Resources.Amount)
{
- SortByAscendingCommand = ReactiveCommand.Create(() => ((ITreeDataGridSource) Source).SortBy(Source.Columns[2], ListSortDirection.Ascending)),
- SortByDescendingCommand = ReactiveCommand.Create(() => ((ITreeDataGridSource) Source).SortBy(Source.Columns[2], ListSortDirection.Descending))
+ SortByAscendingCommand = ReactiveCommand.Create(() => ((ITreeDataGridSource)Source).SortBy(Source.Columns[2], ListSortDirection.Ascending)),
+ SortByDescendingCommand = ReactiveCommand.Create(() => ((ITreeDataGridSource)Source).SortBy(Source.Columns[2], ListSortDirection.Descending))
},
new SortableItem(Resources.Provider)
{
- SortByAscendingCommand = ReactiveCommand.Create(() => ((ITreeDataGridSource) Source).SortBy(Source.Columns[3], ListSortDirection.Ascending)),
- SortByDescendingCommand = ReactiveCommand.Create(() => ((ITreeDataGridSource) Source).SortBy(Source.Columns[3], ListSortDirection.Descending))
+ SortByAscendingCommand = ReactiveCommand.Create(() => ((ITreeDataGridSource)Source).SortBy(Source.Columns[3], ListSortDirection.Ascending)),
+ SortByDescendingCommand = ReactiveCommand.Create(() => ((ITreeDataGridSource)Source).SortBy(Source.Columns[3], ListSortDirection.Descending))
},
];
}
diff --git a/WalletWasabi.Fluent/HomeScreen/BuySell/Views/BuyView.axaml b/WalletWasabi.Fluent/HomeScreen/BuySell/Views/BuyView.axaml
index 75a3d3716d..94ac04e12b 100644
--- a/WalletWasabi.Fluent/HomeScreen/BuySell/Views/BuyView.axaml
+++ b/WalletWasabi.Fluent/HomeScreen/BuySell/Views/BuyView.axaml
@@ -29,7 +29,7 @@
Theme="{StaticResource OutlineButton}">
+ IsVisible="{Binding SelectedCountry.StateName, Converter={x:Static StringConverters.IsNotNullOrEmpty}}" />
diff --git a/WalletWasabi.Fluent/HomeScreen/BuySell/Views/Columns/OrderActionsColumnView.axaml b/WalletWasabi.Fluent/HomeScreen/BuySell/Views/Columns/OrderActionsColumnView.axaml
index c630b04e7b..78ad1da1db 100644
--- a/WalletWasabi.Fluent/HomeScreen/BuySell/Views/Columns/OrderActionsColumnView.axaml
+++ b/WalletWasabi.Fluent/HomeScreen/BuySell/Views/Columns/OrderActionsColumnView.axaml
@@ -8,7 +8,8 @@
x:DataType="viewModels:OrderViewModel"
x:CompileBindings="True"
x:Class="WalletWasabi.Fluent.HomeScreen.BuySell.Views.Columns.OrderActionsColumnView">
-
+
diff --git a/WalletWasabi.Fluent/HomeScreen/BuySell/Views/SellSuccessView.axaml b/WalletWasabi.Fluent/HomeScreen/BuySell/Views/SellSuccessView.axaml
index 71c0501fb5..b117dd9a03 100644
--- a/WalletWasabi.Fluent/HomeScreen/BuySell/Views/SellSuccessView.axaml
+++ b/WalletWasabi.Fluent/HomeScreen/BuySell/Views/SellSuccessView.axaml
@@ -34,11 +34,14 @@
-
+
-
+
-
+
@@ -47,7 +50,7 @@
FontWeight="Bold"
Text="{Binding Provider}"
Foreground="{DynamicResource SystemAccentCustomColorDark}"
- Opacity="0.8"/>
+ Opacity="0.8" />
@@ -55,4 +58,3 @@
-
diff --git a/WalletWasabi.Fluent/HomeScreen/BuySell/Views/SellSuccessView.axaml.cs b/WalletWasabi.Fluent/HomeScreen/BuySell/Views/SellSuccessView.axaml.cs
index f3ebba2faa..c1a45303b0 100644
--- a/WalletWasabi.Fluent/HomeScreen/BuySell/Views/SellSuccessView.axaml.cs
+++ b/WalletWasabi.Fluent/HomeScreen/BuySell/Views/SellSuccessView.axaml.cs
@@ -15,4 +15,3 @@ private void InitializeComponent()
AvaloniaXamlLoader.Load(this);
}
}
-
diff --git a/WalletWasabi.Fluent/HomeScreen/BuySell/Views/SellView.axaml b/WalletWasabi.Fluent/HomeScreen/BuySell/Views/SellView.axaml
index 1c62140d68..449aeceb20 100644
--- a/WalletWasabi.Fluent/HomeScreen/BuySell/Views/SellView.axaml
+++ b/WalletWasabi.Fluent/HomeScreen/BuySell/Views/SellView.axaml
@@ -23,7 +23,7 @@
Theme="{StaticResource OutlineButton}">
+ IsVisible="{Binding SelectedCountry.StateName, Converter={x:Static StringConverters.IsNotNullOrEmpty}}" />
@@ -55,7 +55,8 @@
+ Margin="0 8 0 0" VerticalAlignment="Top" Content="{x:Static lang:Resources.AmountWithColom}"
+ Target="amountTb" />
-
+
diff --git a/WalletWasabi.Fluent/HomeScreen/CoinjoinPlayer/View/CoinjoinPlayerControlView.axaml b/WalletWasabi.Fluent/HomeScreen/CoinjoinPlayer/View/CoinjoinPlayerControlView.axaml
index 6438e7cd5a..e0b8e5cd64 100644
--- a/WalletWasabi.Fluent/HomeScreen/CoinjoinPlayer/View/CoinjoinPlayerControlView.axaml
+++ b/WalletWasabi.Fluent/HomeScreen/CoinjoinPlayer/View/CoinjoinPlayerControlView.axaml
@@ -63,7 +63,8 @@
-
+
@@ -72,7 +73,8 @@
-
+
@@ -110,12 +112,14 @@
-
-
+
@@ -140,7 +141,8 @@
-
+
diff --git a/WalletWasabi.Fluent/Styles/Dialog.axaml b/WalletWasabi.Fluent/Styles/Dialog.axaml
index c38902ec13..0e289351e9 100644
--- a/WalletWasabi.Fluent/Styles/Dialog.axaml
+++ b/WalletWasabi.Fluent/Styles/Dialog.axaml
@@ -6,13 +6,13 @@
-
+
-
+
-
+
diff --git a/WalletWasabi.Fluent/Styles/Markdown.axaml b/WalletWasabi.Fluent/Styles/Markdown.axaml
index d3d54ffe61..6b1fae0223 100644
--- a/WalletWasabi.Fluent/Styles/Markdown.axaml
+++ b/WalletWasabi.Fluent/Styles/Markdown.axaml
@@ -63,38 +63,38 @@
diff --git a/WalletWasabi.Fluent/Styles/RadioButton.axaml b/WalletWasabi.Fluent/Styles/RadioButton.axaml
index 6e03b4142e..b5b26ed316 100644
--- a/WalletWasabi.Fluent/Styles/RadioButton.axaml
+++ b/WalletWasabi.Fluent/Styles/RadioButton.axaml
@@ -41,7 +41,8 @@
-
diff --git a/WalletWasabi.Fluent/Styles/ScrollBar.axaml b/WalletWasabi.Fluent/Styles/ScrollBar.axaml
index 84e54973ea..e426b7d153 100644
--- a/WalletWasabi.Fluent/Styles/ScrollBar.axaml
+++ b/WalletWasabi.Fluent/Styles/ScrollBar.axaml
@@ -3,7 +3,7 @@
-
+
diff --git a/WalletWasabi.Fluent/Styles/SettingsLayout.axaml b/WalletWasabi.Fluent/Styles/SettingsLayout.axaml
index 86d3c5c7e4..18cefd5ddd 100644
--- a/WalletWasabi.Fluent/Styles/SettingsLayout.axaml
+++ b/WalletWasabi.Fluent/Styles/SettingsLayout.axaml
@@ -57,7 +57,8 @@
-
diff --git a/WalletWasabi.Fluent/Styles/TagsBox.axaml b/WalletWasabi.Fluent/Styles/TagsBox.axaml
index 26761938b4..ad9e83e7c2 100644
--- a/WalletWasabi.Fluent/Styles/TagsBox.axaml
+++ b/WalletWasabi.Fluent/Styles/TagsBox.axaml
@@ -1,7 +1,8 @@
-
diff --git a/WalletWasabi.Fluent/Styles/Themes/Base.axaml b/WalletWasabi.Fluent/Styles/Themes/Base.axaml
index c0d755f96a..638e9880f5 100644
--- a/WalletWasabi.Fluent/Styles/Themes/Base.axaml
+++ b/WalletWasabi.Fluent/Styles/Themes/Base.axaml
@@ -114,8 +114,10 @@
0 4 10 0 #4A000000
-
-
+
+
diff --git a/WalletWasabi.Fluent/Styles/Themes/Dark.axaml b/WalletWasabi.Fluent/Styles/Themes/Dark.axaml
index 1d5ff18c75..edbd2f154a 100644
--- a/WalletWasabi.Fluent/Styles/Themes/Dark.axaml
+++ b/WalletWasabi.Fluent/Styles/Themes/Dark.axaml
@@ -89,7 +89,8 @@
#27757575
-
+
@@ -145,7 +146,8 @@
-
+
diff --git a/WalletWasabi.Fluent/Styles/Themes/Light.axaml b/WalletWasabi.Fluent/Styles/Themes/Light.axaml
index 5bf8c107ca..b07bb8c6ff 100644
--- a/WalletWasabi.Fluent/Styles/Themes/Light.axaml
+++ b/WalletWasabi.Fluent/Styles/Themes/Light.axaml
@@ -145,8 +145,10 @@
-
-
+
+
diff --git a/WalletWasabi.Fluent/Styles/ToggleSwitch.axaml b/WalletWasabi.Fluent/Styles/ToggleSwitch.axaml
index 91cdd10989..496623837c 100644
--- a/WalletWasabi.Fluent/Styles/ToggleSwitch.axaml
+++ b/WalletWasabi.Fluent/Styles/ToggleSwitch.axaml
@@ -5,21 +5,21 @@
diff --git a/WalletWasabi.Fluent/TransactionBroadcasting/Views/BroadcastTransactionView.axaml b/WalletWasabi.Fluent/TransactionBroadcasting/Views/BroadcastTransactionView.axaml
index f07aa9b00e..169a7666c1 100644
--- a/WalletWasabi.Fluent/TransactionBroadcasting/Views/BroadcastTransactionView.axaml
+++ b/WalletWasabi.Fluent/TransactionBroadcasting/Views/BroadcastTransactionView.axaml
@@ -5,34 +5,34 @@
x:CompileBindings="True"
x:Class="WalletWasabi.Fluent.TransactionBroadcasting.Views.BroadcastTransactionView">
+ EnableCancel="{Binding EnableCancel}"
+ EnableBack="{Binding EnableBack}"
+ EnableNext="True" NextContent="Broadcast"
+ IsBusy="{Binding IsBusy}">
+ Label="Transaction ID"
+ Content="{Binding TransactionId, FallbackValue=ab550cbedcacc9e4d1d6d37b1dc54eee0cax7b70c4e0beeac2d0915353b70ae0}" />
+ Label="Input amount"
+ Content="{Binding InputAmountString, FallbackValue=0.0001 1234 BTC}" />
+ Label="Output amount"
+ Content="{Binding OutputAmountString, FallbackValue=0.0001 1234 BTC}" />
+ Label="Input count"
+ Content="{Binding InputCount, FallbackValue=1}" />
+ Label="Output count"
+ Content="{Binding OutputCount, FallbackValue=1}" />
+ Label="Fee"
+ Content="{Binding FeeString, FallbackValue=0.0001 1234 BTC}" />
diff --git a/WalletWasabi.Fluent/TreeDataGrid/PlainTextElementFactory.cs b/WalletWasabi.Fluent/TreeDataGrid/PlainTextElementFactory.cs
index a96d8ce617..6fa6a6a453 100644
--- a/WalletWasabi.Fluent/TreeDataGrid/PlainTextElementFactory.cs
+++ b/WalletWasabi.Fluent/TreeDataGrid/PlainTextElementFactory.cs
@@ -7,15 +7,11 @@ internal class PlainTextElementFactory : TreeDataGridElementFactory
{
protected override Control CreateElement(object? data)
{
- return data is PlainTextCell ?
- new TreeDataGridPlainTextCell() :
- base.CreateElement(data);
+ return data is PlainTextCell ? new TreeDataGridPlainTextCell() : base.CreateElement(data);
}
protected override string GetDataRecycleKey(object? data)
{
- return data is PlainTextCell ?
- typeof(TreeDataGridPlainTextCell).FullName! :
- base.GetDataRecycleKey(data);
+ return data is PlainTextCell ? typeof(TreeDataGridPlainTextCell).FullName! : base.GetDataRecycleKey(data);
}
}
diff --git a/WalletWasabi.Fluent/TreeDataGrid/TreeDataGridPlainTextCell.cs b/WalletWasabi.Fluent/TreeDataGrid/TreeDataGridPlainTextCell.cs
index d5558f6eb9..9e015f93f9 100644
--- a/WalletWasabi.Fluent/TreeDataGrid/TreeDataGridPlainTextCell.cs
+++ b/WalletWasabi.Fluent/TreeDataGrid/TreeDataGridPlainTextCell.cs
@@ -47,6 +47,7 @@ public override void Render(DrawingContext context)
{
_formattedText.SetForegroundBrush(Foreground);
}
+
context.DrawText(_formattedText, new Point(0, r.Position.Y));
}
}
diff --git a/WalletWasabi.Fluent/TreeDataGrid/TreeDataGridPrivacyTextCell.cs b/WalletWasabi.Fluent/TreeDataGrid/TreeDataGridPrivacyTextCell.cs
index e11986bfdb..0532813006 100644
--- a/WalletWasabi.Fluent/TreeDataGrid/TreeDataGridPrivacyTextCell.cs
+++ b/WalletWasabi.Fluent/TreeDataGrid/TreeDataGridPrivacyTextCell.cs
@@ -89,7 +89,9 @@ public override void Render(DrawingContext context)
var formattedText = !_isContentVisible
? _privacyFormattedText
- : _haveText ? _formattedText : null;
+ : _haveText
+ ? _formattedText
+ : null;
if (formattedText is null)
{
diff --git a/WalletWasabi.Fluent/TwoFactor/Controls/TwoFactorEntryBox.axaml b/WalletWasabi.Fluent/TwoFactor/Controls/TwoFactorEntryBox.axaml
index 9961a0ae31..ac57570711 100644
--- a/WalletWasabi.Fluent/TwoFactor/Controls/TwoFactorEntryBox.axaml
+++ b/WalletWasabi.Fluent/TwoFactor/Controls/TwoFactorEntryBox.axaml
@@ -8,35 +8,35 @@
-
-
-
-
-
-
-
-
-
-
-
-
- 1
- 2
- 10,15
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+ 2
+ 10,15
+
+
+
+
+
+
+
+
diff --git a/WalletWasabi.Fluent/TwoFactor/Controls/TwoFactorEntryBox.axaml.cs b/WalletWasabi.Fluent/TwoFactor/Controls/TwoFactorEntryBox.axaml.cs
index 894bb38bc0..5afa2878fb 100644
--- a/WalletWasabi.Fluent/TwoFactor/Controls/TwoFactorEntryBox.axaml.cs
+++ b/WalletWasabi.Fluent/TwoFactor/Controls/TwoFactorEntryBox.axaml.cs
@@ -116,7 +116,7 @@ private void ItemsControlLoaded(object? sender, RoutedEventArgs e)
if (textBoxes[DigitCount / 2 - 1] is { } middleTb)
{
- middleTb.Margin = new Thickness(0, 0, 10 , 0);
+ middleTb.Margin = new Thickness(0, 0, 10, 0);
}
}
diff --git a/WalletWasabi.Fluent/TwoFactor/Views/TwoFactoryAuthenticationDialogView.axaml b/WalletWasabi.Fluent/TwoFactor/Views/TwoFactoryAuthenticationDialogView.axaml
index c91605253a..72847244fe 100644
--- a/WalletWasabi.Fluent/TwoFactor/Views/TwoFactoryAuthenticationDialogView.axaml
+++ b/WalletWasabi.Fluent/TwoFactor/Views/TwoFactoryAuthenticationDialogView.axaml
@@ -16,7 +16,8 @@
ScrollViewer.VerticalScrollBarVisibility="Disabled">
-
+
diff --git a/WalletWasabi.Fluent/TwoFactor/Views/VerifyTwoFactoryAuthenticationDialogView.axaml b/WalletWasabi.Fluent/TwoFactor/Views/VerifyTwoFactoryAuthenticationDialogView.axaml
index b071c9b24e..3aa739a767 100644
--- a/WalletWasabi.Fluent/TwoFactor/Views/VerifyTwoFactoryAuthenticationDialogView.axaml
+++ b/WalletWasabi.Fluent/TwoFactor/Views/VerifyTwoFactoryAuthenticationDialogView.axaml
@@ -16,7 +16,7 @@
NextContent="{x:Static lang:Resources.Verify}">
+ Text="{Binding TwoFactorToken, Mode=OneWayToSource}" />
diff --git a/WalletWasabi.Fluent/Validation/Validations.cs b/WalletWasabi.Fluent/Validation/Validations.cs
index 29598921bf..52cac8e7a1 100644
--- a/WalletWasabi.Fluent/Validation/Validations.cs
+++ b/WalletWasabi.Fluent/Validation/Validations.cs
@@ -88,7 +88,8 @@ public IEnumerable GetErrors(string? propertyName)
if (!string.IsNullOrWhiteSpace(propertyName))
{
return ErrorsByPropertyName.TryGetValue(propertyName, out ErrorDescriptors? value) && value.Any()
- ? value : ErrorDescriptors.Empty;
+ ? value
+ : ErrorDescriptors.Empty;
}
else
{
diff --git a/WalletWasabi.Fluent/ViewLocator.cs b/WalletWasabi.Fluent/ViewLocator.cs
index 7ab7d649b3..e41fb03348 100644
--- a/WalletWasabi.Fluent/ViewLocator.cs
+++ b/WalletWasabi.Fluent/ViewLocator.cs
@@ -13,12 +13,13 @@ public Control Build(object? data)
{
throw new ArgumentNullException($"Param: {data}");
}
-
+
var type = data.GetType();
if (s_views.TryGetValue(type, out var func))
{
return func.Invoke();
}
+
throw new Exception($"Unable to create view for type: {type}");
}
diff --git a/WalletWasabi.Fluent/WalletWasabi.Fluent.csproj b/WalletWasabi.Fluent/WalletWasabi.Fluent.csproj
index 0d168701fc..851bc23b40 100644
--- a/WalletWasabi.Fluent/WalletWasabi.Fluent.csproj
+++ b/WalletWasabi.Fluent/WalletWasabi.Fluent.csproj
@@ -12,28 +12,28 @@
true
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
-
+
diff --git a/WalletWasabi.Fluent/WalletWasabi.Fluent.csproj.DotSettings b/WalletWasabi.Fluent/WalletWasabi.Fluent.csproj.DotSettings
index 71a1e0fc85..0601543646 100644
--- a/WalletWasabi.Fluent/WalletWasabi.Fluent.csproj.DotSettings
+++ b/WalletWasabi.Fluent/WalletWasabi.Fluent.csproj.DotSettings
@@ -1,3 +1,3 @@
Yes
- Pessimistic
\ No newline at end of file
+ Pessimistic
\ No newline at end of file
diff --git a/WalletWasabi.Fluent/WebBrowserService.cs b/WalletWasabi.Fluent/WebBrowserService.cs
index 8a20dad683..38981bd304 100644
--- a/WalletWasabi.Fluent/WebBrowserService.cs
+++ b/WalletWasabi.Fluent/WebBrowserService.cs
@@ -16,14 +16,12 @@ public sealed class WebBrowserService
private BrowserType? PreferredBrowserType { get; set; } = null;
private WebBrowserService()
- { }
+ {
+ }
public static WebBrowserService Instance
{
- get
- {
- return LazyInstance.Value;
- }
+ get { return LazyInstance.Value; }
}
public void SetConfig(string filePathOrEnumValueThatIsInConfig)
@@ -124,8 +122,10 @@ private static bool TryOpenPreferredBrowser(string url, BrowserType preferredBro
Process.Start(iePath, url);
return true;
}
+
return true;
}
+
break;
case BrowserType.Safari:
@@ -134,6 +134,7 @@ private static bool TryOpenPreferredBrowser(string url, BrowserType preferredBro
OpenInBrowser(url, "open"); // Safari uses the 'open' command
return true;
}
+
break;
case BrowserType.Firefox:
@@ -142,8 +143,10 @@ private static bool TryOpenPreferredBrowser(string url, BrowserType preferredBro
OpenInBrowser(url, GetFirefoxPath());
return true;
}
+
break;
}
+
return false;
}
@@ -155,6 +158,7 @@ private static bool TryOpenTorBrowser(string url)
Process.Start(torBrowserPath, url);
return true;
}
+
return false;
}
@@ -166,6 +170,7 @@ private static bool TryOpenChrome(string url)
Process.Start(chromePath, url);
return true;
}
+
return false;
}
@@ -177,6 +182,7 @@ private static bool TryOpenBrave(string url)
Process.Start(bravePath, url);
return true;
}
+
return false;
}
@@ -188,6 +194,7 @@ private static bool TryOpenOpera(string url)
Process.Start(operaPath, url);
return true;
}
+
return false;
}
@@ -212,6 +219,7 @@ private static string GetTorBrowserPath()
{
return Path.Combine(Environment.GetEnvironmentVariable("HOME") ?? "", "tor-browser", "Browser", "firefox");
}
+
return string.Empty;
}
@@ -229,6 +237,7 @@ private static string GetChromePath()
{
return "/usr/bin/google-chrome";
}
+
return string.Empty;
}
@@ -246,6 +255,7 @@ private static string GetBravePath()
{
return "/usr/bin/brave-browser";
}
+
return string.Empty;
}
@@ -263,6 +273,7 @@ private static string GetOperaPath()
{
return "/usr/bin/opera";
}
+
return string.Empty;
}
@@ -276,6 +287,7 @@ private static string GetFirefoxPath()
{
return "/usr/bin/firefox";
}
+
return string.Empty;
}
@@ -287,26 +299,32 @@ public static List GetAvailableBrowsers()
{
availableBrowsers.Add(BrowserType.Tor);
}
+
if (!string.IsNullOrEmpty(GetChromePath()) && File.Exists(GetChromePath()))
{
availableBrowsers.Add(BrowserType.Chrome);
}
+
if (!string.IsNullOrEmpty(GetBravePath()) && File.Exists(GetBravePath()))
{
availableBrowsers.Add(BrowserType.Brave);
}
+
if (!string.IsNullOrEmpty(GetOperaPath()) && File.Exists(GetOperaPath()))
{
availableBrowsers.Add(BrowserType.Opera);
}
+
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && File.Exists(InternetExplorerDefaultPath))
{
availableBrowsers.Add(BrowserType.InternetExplorer);
}
+
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX) && File.Exists("/Applications/Safari.app"))
{
availableBrowsers.Add(BrowserType.Safari);
}
+
if (!string.IsNullOrEmpty(GetFirefoxPath()) && File.Exists(GetFirefoxPath()))
{
availableBrowsers.Add(BrowserType.Firefox);
@@ -323,8 +341,8 @@ private static bool IsLikelyTorPath(string? customBrowserPath)
}
if (customBrowserPath.Contains("firefox") &&
- customBrowserPath.Contains("tor", StringComparison.InvariantCultureIgnoreCase) &&
- customBrowserPath.Contains("browser", StringComparison.InvariantCultureIgnoreCase))
+ customBrowserPath.Contains("tor", StringComparison.InvariantCultureIgnoreCase) &&
+ customBrowserPath.Contains("browser", StringComparison.InvariantCultureIgnoreCase))
{
return true;
}
diff --git a/WalletWasabi.Packager/Program.cs b/WalletWasabi.Packager/Program.cs
index d8f70ab080..fd61f718be 100644
--- a/WalletWasabi.Packager/Program.cs
+++ b/WalletWasabi.Packager/Program.cs
@@ -8,7 +8,6 @@
using System.Text.Json;
using System.Threading.Tasks;
using WalletWasabi.Helpers;
-using WalletWasabi.Userfacing;
namespace WalletWasabi.Packager;
@@ -33,7 +32,6 @@ public static class Program
private static readonly string WasabiPrivateKeyFilePath = Path.Combine(GingerFolderPath, "Ginger.privkey");
private static readonly string WasabiPublicKeyFilePath = Path.Combine(GingerFolderPath, "Ginger.pubkey");
-
/// Only 64-bit platforms are supported for now.
///
private static string[] Targets = new[]
@@ -61,7 +59,6 @@ public static class Program
///
private static async Task Main(string[] args)
{
-
var argsProcessor = new ArgsProcessor(args);
// For now this is enough. If you run it on macOS you want to sign.
@@ -647,7 +644,6 @@ private static void ExecuteBashCommands(string[] commands)
return output;
}
-
private static bool TryStartProcessAndWaitForExit(string command, string workingDirectory, [NotNullWhen(true)] out string? result, string? writeToStandardInput = null, string? arguments = null, bool redirectStandardOutput = false)
{
result = null;
diff --git a/WalletWasabi.Tests/RegressionTests/BackendTests.cs b/WalletWasabi.Tests/RegressionTests/BackendTests.cs
index f162d7c7c8..a7df78877a 100644
--- a/WalletWasabi.Tests/RegressionTests/BackendTests.cs
+++ b/WalletWasabi.Tests/RegressionTests/BackendTests.cs
@@ -64,7 +64,7 @@ public async Task GetExchangeRatesAsync()
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
var exchangeRates = await response.Content.ReadAsJsonAsync>();
- Assert.Single(exchangeRates);
+ Assert.True(exchangeRates.Count > 0);
var rate = exchangeRates[0];
Assert.Equal("USD", rate.Ticker);
diff --git a/WalletWasabi.Tests/UnitTests/KeyManagementTests.cs b/WalletWasabi.Tests/UnitTests/KeyManagementTests.cs
index 207ad32c67..73a125beea 100644
--- a/WalletWasabi.Tests/UnitTests/KeyManagementTests.cs
+++ b/WalletWasabi.Tests/UnitTests/KeyManagementTests.cs
@@ -8,7 +8,6 @@
using WalletWasabi.Logging;
using WalletWasabi.Models;
using WalletWasabi.Tests.TestCommon;
-using WalletWasabi.Wallets;
using Xunit;
namespace WalletWasabi.Tests.UnitTests;
diff --git a/WalletWasabi.Tests/UnitTests/StandaloneTests/ConfigTests.cs b/WalletWasabi.Tests/UnitTests/StandaloneTests/ConfigTests.cs
index d8d9620a8d..5a9302bdc9 100644
--- a/WalletWasabi.Tests/UnitTests/StandaloneTests/ConfigTests.cs
+++ b/WalletWasabi.Tests/UnitTests/StandaloneTests/ConfigTests.cs
@@ -81,6 +81,7 @@ public static string GetWasabiConfigString(decimal coordinationFeeRate = 0.003m)
"ConfirmationTarget": 108,
"DoSSeverity": "0.10",
"DoSMinTimeForFailedToVerify": "31d 0h 0m 0s",
+ "DoSSimplifiedPunishment": "1d 0h 0m 0s",
"DoSMinTimeForCheating": "1d 0h 0m 0s",
"DoSPenaltyFactorForDisruptingConfirmation": 0.2,
"DoSPenaltyFactorForDisruptingSignalReadyToSign": 1.0,
diff --git a/WalletWasabi.Tests/UnitTests/StartWasabiOnSystemStartupTests.cs b/WalletWasabi.Tests/UnitTests/StartWasabiOnSystemStartupTests.cs
index cef57276f8..2f6756370b 100644
--- a/WalletWasabi.Tests/UnitTests/StartWasabiOnSystemStartupTests.cs
+++ b/WalletWasabi.Tests/UnitTests/StartWasabiOnSystemStartupTests.cs
@@ -2,7 +2,6 @@
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using WalletWasabi.Daemon;
-using WalletWasabi.Fluent;
using WalletWasabi.Fluent.Helpers;
using WalletWasabi.Helpers;
using WalletWasabi.Tests.Helpers;
diff --git a/WalletWasabi.Tests/UnitTests/TestWallet.cs b/WalletWasabi.Tests/UnitTests/TestWallet.cs
index 70d4460715..4a95a1b589 100644
--- a/WalletWasabi.Tests/UnitTests/TestWallet.cs
+++ b/WalletWasabi.Tests/UnitTests/TestWallet.cs
@@ -170,4 +170,9 @@ private ExtKey CreateNewKey(bool isInternal)
var path = isInternal ? "84'/0'/0'/1" : "84'/0'/0'/0";
return ExtKey.Derive(KeyPath.Parse($"{path}/{NextKeyIndex++}"));
}
+
+ public string SignMessage(string message, HdPubKey hdPubKey)
+ {
+ throw new NotImplementedException();
+ }
}
diff --git a/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/CoinVerifierTests.cs b/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/CoinVerifierTests.cs
index e717f4b0ce..cb7e929a7d 100644
--- a/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/CoinVerifierTests.cs
+++ b/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/CoinVerifierTests.cs
@@ -43,7 +43,7 @@ public override bool IsValid(HttpResponseMessage response)
public override ApiResponse ParseResponse(HttpStatusCode statusCode, string responseString, Coin coin, int coinBlockHeight, int currentBlockHeight)
{
bool ban = responseString == BanResponse;
- return new ApiResponse(ApiResponseInfo.OK, "mock", ban, ban, "");
+ return new ApiResponse(ApiResponseInfo.OK, "mock", ban, ban, "", TimeSpan.Zero);
}
}
diff --git a/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/OffenderSerializationTests.cs b/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/OffenderSerializationTests.cs
index 7c975b0ed1..e1d0a35b27 100644
--- a/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/OffenderSerializationTests.cs
+++ b/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/OffenderSerializationTests.cs
@@ -2,6 +2,7 @@
using NBitcoin;
using WalletWasabi.Tests.Helpers;
using WalletWasabi.WabiSabi.Backend.DoSPrevention;
+using WalletWasabi.WabiSabi.Models;
using Xunit;
namespace WalletWasabi.Tests.UnitTests.WabiSabi.Backend;
@@ -41,13 +42,13 @@ public void SerializationTest()
Assert.Equal(offender3xstr, Offender.FromStringLine(offender3xstr).ToStringLine());
// Fail to verify
- var offender4 = new Offender(outpoint, now, new FailedToVerify(roundId));
+ var offender4 = new Offender(outpoint, now, new FailedToVerify(roundId, TimeSpan.Zero, "local"));
var offender4str = offender4.ToStringLine();
Assert.Equal(offender4str, Offender.FromStringLine(offender4str).ToStringLine());
// Fail to verify
var ancestors = Enumerable.Range(0, 3).Select(_ => BitcoinFactory.CreateOutPoint()).ToArray();
- var offender5 = new Offender(outpoint, now, new Inherited(ancestors));
+ var offender5 = new Offender(outpoint, now, new Inherited(ancestors, [InputBannedReasonEnum.RoundDisruptionMethodDoubleSpent, InputBannedReasonEnum.Inherited]));
var offender5str = offender5.ToStringLine();
Assert.Equal(offender5str, Offender.FromStringLine(offender5str).ToStringLine());
diff --git a/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/PostRequests/RegisterInputFailureTests.cs b/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/PostRequests/RegisterInputFailureTests.cs
index 6f11b865cc..8b98e95084 100644
--- a/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/PostRequests/RegisterInputFailureTests.cs
+++ b/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/PostRequests/RegisterInputFailureTests.cs
@@ -154,7 +154,7 @@ public async Task InputLongBannedAsync()
var round = WabiSabiTestFactory.CreateRound(cfg);
Prison prison = WabiSabiTestFactory.CreatePrison();
- prison.FailedVerification(coin.Outpoint, round.Id);
+ prison.FailedVerification(coin.Outpoint, round.Id, TimeSpan.Zero, "");
using Arena arena = await ArenaTestFactory.From(cfg, rpc, prison).CreateAndStartAsync(round);
var ownershipProof = WabiSabiTestFactory.CreateOwnershipProof(key, round.Id);
diff --git a/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/PostRequests/RegisterOutputTests.cs b/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/PostRequests/RegisterOutputTests.cs
index 803d5eab56..43d1e32f25 100644
--- a/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/PostRequests/RegisterOutputTests.cs
+++ b/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/PostRequests/RegisterOutputTests.cs
@@ -7,7 +7,6 @@
using WalletWasabi.WabiSabi.Backend;
using WalletWasabi.WabiSabi.Backend.Models;
using WalletWasabi.WabiSabi.Backend.Rounds;
-using WalletWasabi.WabiSabi.Models.MultipartyTransaction;
using Xunit;
namespace WalletWasabi.Tests.UnitTests.WabiSabi.Backend.PostRequests;
diff --git a/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/PrisonTests.cs b/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/PrisonTests.cs
index 030ce39f8c..6a7679bdb5 100644
--- a/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/PrisonTests.cs
+++ b/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/PrisonTests.cs
@@ -5,6 +5,7 @@
using WalletWasabi.Tests.Helpers;
using WalletWasabi.WabiSabi.Backend.DoSPrevention;
using Xunit;
+using WalletWasabi.WabiSabi.Models;
namespace WalletWasabi.Tests.UnitTests.WabiSabi.Backend;
@@ -24,7 +25,7 @@ public async Task OffensesAreSavedAsync()
var roundId = BitcoinFactory.CreateUint256();
// Fail to verify
- prison.FailedVerification(outpoint, roundId);
+ prison.FailedVerification(outpoint, roundId, TimeSpan.Zero, "");
var offenderToSave = await reader.ReadAsync(ctsTimeout.Token);
var failedToVerify = Assert.IsType(offenderToSave.Offense);
Assert.Equal(outpoint, offenderToSave.OutPoint);
@@ -80,7 +81,7 @@ public async Task OffensesAreSavedAsync()
BitcoinFactory.CreateOutPoint()
};
- prison.InheritPunishment(outpoint, ancestors);
+ prison.InheritPunishment(outpoint, ancestors, []);
offenderToSave = await reader.ReadAsync(ctsTimeout.Token);
var inherited = Assert.IsType(offenderToSave.Offense);
Assert.Equal(outpoint, offenderToSave.OutPoint);
@@ -94,16 +95,16 @@ public async Task OffensesAreSavedAsync()
public void BanningTime()
{
var (prison, _) = WabiSabiTestFactory.CreateObservablePrison();
- var cfg = WabiSabiTestFactory.CreateWabiSabiConfig().GetDoSConfiguration();
+ var cfg = WabiSabiTestFactory.CreateWabiSabiConfig().GetDoSConfiguration() with { SimplifiedPunishment = TimeSpan.Zero };
var roundId = BitcoinFactory.CreateUint256();
// Failed to verify punishment is constant (not affected by number of attempts)
var ftvOutpoint = BitcoinFactory.CreateOutPoint();
- prison.FailedVerification(ftvOutpoint, roundId);
- prison.FailedVerification(ftvOutpoint, roundId);
- prison.FailedVerification(ftvOutpoint, roundId);
- var banningPeriod = prison.GetBanTimePeriod(ftvOutpoint, cfg);
+ prison.FailedVerification(ftvOutpoint, roundId, TimeSpan.Zero, "");
+ prison.FailedVerification(ftvOutpoint, roundId, TimeSpan.Zero, "");
+ prison.FailedVerification(ftvOutpoint, roundId, TimeSpan.Zero, "");
+ var banningPeriod = prison.GetBan(ftvOutpoint, cfg).BanningTime;
Assert.Equal(cfg.MinTimeForFailedToVerify, banningPeriod.Duration);
// Cheating punishment is constant (not affected by number of attempts)
@@ -111,7 +112,7 @@ public void BanningTime()
prison.CheatingDetected(chtOutpoint, roundId);
prison.CheatingDetected(chtOutpoint, roundId);
prison.CheatingDetected(chtOutpoint, roundId);
- banningPeriod = prison.GetBanTimePeriod(chtOutpoint, cfg);
+ banningPeriod = prison.GetBan(chtOutpoint, cfg).BanningTime;
Assert.Equal(cfg.MinTimeForCheating, banningPeriod.Duration);
// Failed to confirm is calculated and inversely proportional to the amount
@@ -120,13 +121,13 @@ public void BanningTime()
prison.FailedToConfirm(ftcOutpoint1, Money.Coins(0.5m), roundId);
prison.FailedToConfirm(ftcOutpoint2, Money.Coins(1.0m), roundId);
- var banningPeriod1 = prison.GetBanTimePeriod(ftcOutpoint1, cfg);
- var banningPeriod2 = prison.GetBanTimePeriod(ftcOutpoint2, cfg);
+ var banningPeriod1 = prison.GetBan(ftcOutpoint1, cfg).BanningTime;
+ var banningPeriod2 = prison.GetBan(ftcOutpoint2, cfg).BanningTime;
Assert.True(banningPeriod1.Duration == 2 * banningPeriod2.Duration);
// .... second attempt is punished harder
prison.FailedToConfirm(ftcOutpoint1, Money.Coins(0.5m), roundId);
- var banningPeriod1FailedToConfirmTwice = prison.GetBanTimePeriod(ftcOutpoint1, cfg);
+ var banningPeriod1FailedToConfirmTwice = prison.GetBan(ftcOutpoint1, cfg).BanningTime;
Assert.True(banningPeriod1FailedToConfirmTwice.Duration > banningPeriod1.Duration);
// .... the worst offense is applied
@@ -135,28 +136,28 @@ public void BanningTime()
prison.FailedToConfirm(ftcOutpoint3, Money.Coins(0.5m), roundId);
prison.FailedToSign(ftcOutpoint3, Money.Coins(0.5m), roundId);
- var banningPeriod3FailedToConfirmAndSign = prison.GetBanTimePeriod(ftcOutpoint3, cfg);
+ var banningPeriod3FailedToConfirmAndSign = prison.GetBan(ftcOutpoint3, cfg).BanningTime;
Assert.True(banningPeriod3FailedToConfirmAndSign.Duration > banningPeriod1FailedToConfirmTwice.Duration);
// Big amounts are not banned the first time
var ftcOutpointBig = BitcoinFactory.CreateOutPoint();
prison.FailedToConfirm(ftcOutpointBig, Money.Coins(2m), roundId);
- var banningPeriodBigCoin = prison.GetBanTimePeriod(ftcOutpointBig, cfg);
+ var banningPeriodBigCoin = prison.GetBan(ftcOutpointBig, cfg).BanningTime;
Assert.Equal(TimeSpan.Zero, banningPeriodBigCoin.Duration);
// ... however, second attempts could be punished
prison.FailedToConfirm(ftcOutpointBig, Money.Coins(2m), roundId);
- banningPeriodBigCoin = prison.GetBanTimePeriod(ftcOutpointBig, cfg);
+ banningPeriodBigCoin = prison.GetBan(ftcOutpointBig, cfg).BanningTime;
Assert.NotEqual(TimeSpan.Zero, banningPeriodBigCoin.Duration);
// ... except if they come from previous coinjoins
var ftcOutpointBigPaid = new OutPoint(uint256.One, 1); // tx 0000....0000001 is a coinjoin
prison.FailedToConfirm(ftcOutpointBigPaid, Money.Coins(2m), roundId);
- var banningPeriodBigPaidCoin = prison.GetBanTimePeriod(ftcOutpointBigPaid, cfg);
+ var banningPeriodBigPaidCoin = prison.GetBan(ftcOutpointBigPaid, cfg).BanningTime;
Assert.Equal(TimeSpan.Zero, banningPeriodBigPaidCoin.Duration); // it is not banned first time
prison.FailedToConfirm(ftcOutpointBigPaid, Money.Coins(2m), roundId);
- banningPeriodBigPaidCoin = prison.GetBanTimePeriod(ftcOutpointBigPaid, cfg);
+ banningPeriodBigPaidCoin = prison.GetBan(ftcOutpointBigPaid, cfg).BanningTime;
Assert.NotEqual(TimeSpan.Zero, banningPeriodBigPaidCoin.Duration); // it IS banned second time
// coins inherit the punishments from their ancestors
@@ -165,21 +166,28 @@ public void BanningTime()
var ftcInheritFromFailedToSign = BitcoinFactory.CreateOutPoint();
prison.FailedToSign(ftcFailedToSign, Money.Coins(0.005m), roundId);
prison.FailedToConfirm(ftcFailedToConfirm, Money.Coins(0.005m), roundId);
- prison.InheritPunishment(ftcInheritFromFailedToSign, new[] { ftcFailedToSign, ftcFailedToConfirm });
- var failToSignBanningTime = prison.GetBanTimePeriod(ftcFailedToSign, cfg);
- var inheritedBanningTime = prison.GetBanTimePeriod(ftcInheritFromFailedToSign, cfg);
+ prison.InheritPunishment(ftcInheritFromFailedToSign, [ftcFailedToSign, ftcFailedToConfirm], [InputBannedReasonEnum.RoundDisruptionMethodDidNotSign, InputBannedReasonEnum.RoundDisruptionMethodDidNotConfirm]);
+ var failToSignBanningTime = prison.GetBan(ftcFailedToSign, cfg).BanningTime;
+ var inheritedBanItem = prison.GetBan(ftcInheritFromFailedToSign, cfg);
+ var inheritedBanningTime = inheritedBanItem.BanningTime;
+ var inheritedReasons = inheritedBanItem.Reasons;
Assert.Equal(failToSignBanningTime.StartTime, inheritedBanningTime.StartTime); // because fail to sign is punished harder
Assert.Equal(failToSignBanningTime.Duration * 0.5, inheritedBanningTime.Duration); // after spending the punishment is reduced by half
+ InputBannedReasonEnum[] expected = [InputBannedReasonEnum.Inherited, InputBannedReasonEnum.RoundDisruptionMethodDidNotSign, InputBannedReasonEnum.RoundDisruptionMethodDidNotConfirm];
+
+ // Assert exact match ignoring order
+ Assert.Equal(expected.OrderBy(e => e), inheritedReasons.OrderBy(e => e));
+
// After spending a couple of times the coin is no longer banned
var outpointsToBan = Enumerable.Range(0, 10).Select(_ => BitcoinFactory.CreateOutPoint()).ToArray();
prison.FailedToSign(outpointsToBan[0], Money.Coins(0.005m), roundId);
var banningTimeFrames = outpointsToBan.Zip(outpointsToBan[1..], (a, b) => new { Destroyed = a, Created = b })
.Select(x =>
{
- prison.InheritPunishment(x.Created, new[] { x.Destroyed });
- return prison.GetBanTimePeriod(x.Created, cfg);
+ prison.InheritPunishment(x.Created, new[] { x.Destroyed }, []);
+ return prison.GetBan(x.Created, cfg).BanningTime;
}).ToArray();
// Every time it is banned, the duration is halfed
diff --git a/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/UtxoPrisonWardenTests.cs b/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/UtxoPrisonWardenTests.cs
index 07644fbe78..0cb02ec07e 100644
--- a/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/UtxoPrisonWardenTests.cs
+++ b/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/UtxoPrisonWardenTests.cs
@@ -1,5 +1,6 @@
using Moq;
using NBitcoin;
+using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using WalletWasabi.Tests.Helpers;
@@ -7,6 +8,7 @@
using WalletWasabi.WabiSabi;
using WalletWasabi.WabiSabi.Backend.DoSPrevention;
using WalletWasabi.WabiSabi.Backend.Rounds.CoinJoinStorage;
+using WalletWasabi.WabiSabi.Models;
using Xunit;
namespace WalletWasabi.Tests.UnitTests.WabiSabi.Backend;
@@ -44,11 +46,13 @@ public async Task PrisonSerializationAsync()
var i3 = BitcoinFactory.CreateOutPoint();
var i4 = BitcoinFactory.CreateOutPoint();
var i5 = BitcoinFactory.CreateOutPoint();
- w.Prison.FailedVerification(i1, uint256.One);
+ var i6 = BitcoinFactory.CreateOutPoint();
+ w.Prison.FailedVerification(i1, uint256.One, TimeSpan.Zero, "local"); // For local we will add a special reason.
w.Prison.FailedToConfirm(i2, Money.Coins(0.01m), uint256.One);
w.Prison.FailedToSign(i3, Money.Coins(0.1m), uint256.One);
w.Prison.DoubleSpent(i4, Money.Coins(0.1m), uint256.One);
w.Prison.CheatingDetected(i5, uint256.One);
+ w.Prison.InheritPunishment(i6, [i3], [InputBannedReasonEnum.RoundDisruptionMethodDidNotSignalReadyToSign]);
// Wait until serializes.
await w.StopAsync(CancellationToken.None);
@@ -66,6 +70,11 @@ public async Task PrisonSerializationAsync()
Assert.True(w2.Prison.IsBanned(i3, dosConfig, DateTimeOffset.UtcNow));
Assert.True(w2.Prison.IsBanned(i4, dosConfig, DateTimeOffset.UtcNow));
Assert.True(w2.Prison.IsBanned(i5, dosConfig, DateTimeOffset.UtcNow));
+ Assert.True(w2.Prison.IsBanned(i6, dosConfig, DateTimeOffset.UtcNow));
+
+ Assert.Equal(InputBannedReasonEnum.LocalCoinVerifier, w2.Prison.GetBan(i1, dosConfig).Reasons.First());
+
+ Assert.Equal([InputBannedReasonEnum.Inherited, InputBannedReasonEnum.RoundDisruptionMethodDidNotSignalReadyToSign], w2.Prison.GetBan(i6, dosConfig).Reasons.OrderBy(x => x));
await w2.StopAsync(CancellationToken.None);
}
diff --git a/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/WhitelistTests.cs b/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/WhitelistTests.cs
index d1d341eb43..b878e46435 100644
--- a/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/WhitelistTests.cs
+++ b/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/WhitelistTests.cs
@@ -1,7 +1,6 @@
using System.Linq;
using System.Threading.Tasks;
using WalletWasabi.Tests.Helpers;
-using WalletWasabi.WabiSabi.Backend;
using WalletWasabi.WabiSabi.Backend.Banning;
using Xunit;
diff --git a/WalletWasabi.Tests/UnitTests/WabiSabi/Client/KeyChainTests.cs b/WalletWasabi.Tests/UnitTests/WabiSabi/Client/KeyChainTests.cs
index 86656b7efa..ce9da98c0d 100644
--- a/WalletWasabi.Tests/UnitTests/WabiSabi/Client/KeyChainTests.cs
+++ b/WalletWasabi.Tests/UnitTests/WabiSabi/Client/KeyChainTests.cs
@@ -1,4 +1,5 @@
using NBitcoin;
+using NBitcoin.BIP322;
using System.Linq;
using WalletWasabi.Blockchain.Keys;
using WalletWasabi.Tests.Helpers;
@@ -28,4 +29,153 @@ public void SignTransactionTest()
var signedTx = keyChain.Sign(transaction, coin, transaction.PrecomputeTransactionData(new[] { coin }));
Assert.True(signedTx.HasWitness);
}
+
+ // --- Legacy (+8 header quirk) exact-match fixtures for index 0 and 1 ---
+ [Theory]
+ [InlineData(0, "helloworld", "H5bVLsJtPUeZiS+7N1GlMw6hTsLbUcLocs7qkeCzlxnmdYPBxk7NrgtYyd+NaNDvBW1ehOU69qSdohfZnFzI3X0=")]
+ [InlineData(1, "helloworld", "IGC8vofPdhcLLSlVR2CCf35R6VJzYQknFjF6FR5zuKfnLXVcxopHBGD+4BL9JimiRUTlLzBGpF47oB7IP5hQnjI=")]
+ public void SignMessage_LegacyPlus8_MultipleCases(int index, string message, string expectedBase64)
+ {
+ // Arrange wallet & helpers
+ var keyManager = KeyManager.Recover(
+ new Mnemonic("faculty plunge pilot slice amount salute artist raw amateur excite dial canyon"),
+ "",
+ Network.Main,
+ KeyManager.GetAccountKeyPath(Network.Main, ScriptPubKeyType.Segwit));
+
+ var destinationProvider = new InternalDestinationProvider(keyManager);
+ var keyChain = new KeyChain(keyManager, new Kitchen(""));
+ var net = keyManager.GetNetwork();
+
+ // Get HdPubKey at the requested external index
+ var dests = destinationProvider.GetNextDestinations(index + 1, false).ToArray();
+ var dest = dests.Last();
+ Assert.True(keyManager.TryGetKeyForScriptPubKey(dest.ScriptPubKey, out HdPubKey? hdPubKey));
+ Assert.NotNull(hdPubKey);
+
+ // Act: your KeyChain.SignMessage uses Legacy + header +8, lowR:false
+ var sig = keyChain.SignMessage(message, hdPubKey!);
+
+ // Exact-match with the fixture you supplied
+ Assert.Equal(expectedBase64, sig);
+
+ // Sanity: recover pubkey from legacy compact sig & compare
+ var hashLegacy = BIP322Signature.CreateMessageHash(message, legacy: true);
+ var raw = Convert.FromBase64String(sig);
+ Assert.Equal(65, raw.Length);
+ int recId = (raw[0] - 27) & 3;
+ var compact = new CompactSignature(recId, raw.AsSpan(1, 64).ToArray());
+ var recovered = PubKey.RecoverCompact(hashLegacy, compact);
+ Assert.Equal(hdPubKey!.PubKey, recovered);
+
+ // Legacy website-style verification (requires P2PKH)
+ var p2pkh = hdPubKey.PubKey.GetAddress(ScriptPubKeyType.Legacy, keyManager.GetNetwork());
+ Assert.True(VerifyLegacyCompact(p2pkh, message, expectedBase64));
+ }
+
+ private static bool VerifyLegacyCompact(BitcoinAddress p2pkh, string message, string base64)
+ {
+ // Must be a P2PKH address for legacy compact signatures
+ if (!p2pkh.ScriptPubKey.IsScriptType(ScriptType.P2PKH))
+ {
+ return false;
+ }
+
+ byte[] bytes;
+ try { bytes = Convert.FromBase64String(base64); }
+ catch { return false; }
+
+ if (bytes.Length != 65)
+ {
+ return false;
+ }
+
+ // Legacy message hash = doubleSHA256(varstr("Bitcoin Signed Message:\n") || varstr(message))
+ var hash = BIP322Signature.CreateMessageHash(message, legacy: true);
+
+ // Parse header & compact r||s (header may include the +8 quirk; we only use the low 2 bits)
+ int header = bytes[0];
+ int recId = (header - 27) & 3;
+ var compact = new CompactSignature(recId, bytes.AsSpan(1, 64).ToArray());
+
+ // Recover pubkey and compare to the P2PKH address
+ var recovered = PubKey.RecoverCompact(hash, compact);
+ var recoveredAddr = recovered.GetAddress(ScriptPubKeyType.Legacy, p2pkh.Network);
+ return recoveredAddr == p2pkh;
+ }
+
+ [Theory]
+ [InlineData(0, "helloworld")]
+ [InlineData(1, "verify taproot bip322")]
+ public void SignMessage_BIP322_Taproot_Simple_Verifies(int index, string message)
+ {
+ var keyManager = KeyManager.Recover(
+ new Mnemonic("faculty plunge pilot slice amount salute artist raw amateur excite dial canyon"),
+ "",
+ Network.Main,
+ KeyManager.GetAccountKeyPath(Network.Main, ScriptPubKeyType.TaprootBIP86));
+
+ var net = keyManager.GetNetwork();
+ var destinationProvider = new InternalDestinationProvider(keyManager);
+
+ // Ask for (index+1) external destinations with Taproot preferred, then take the last (our target index).
+ var dests = destinationProvider.GetNextDestinations(index + 1, preferTaproot: true).ToArray();
+ Assert.NotEmpty(dests);
+ var dest = dests.Last();
+
+ // Resolve HdPubKey for the selected destination.
+ Assert.True(keyManager.TryGetKeyForScriptPubKey(dest.ScriptPubKey, out HdPubKey? hdPubKey));
+ Assert.NotNull(hdPubKey);
+
+ // Assert we actually got a Taproot key (by derivation path type).
+ var scriptType = hdPubKey!.FullKeyPath.GetScriptTypeFromKeyPath();
+ Assert.Equal(ScriptPubKeyType.TaprootBIP86, scriptType);
+
+ // Optional: double-check via the address script type as well.
+ var addr = hdPubKey.GetAddress(net);
+ Assert.True(addr.ScriptPubKey.IsScriptType(ScriptType.Taproot), "Expected Taproot (P2TR) address.");
+
+ // Taproot uses BIP-322 Simple proof.
+ var sig322 = KeyManagerExtension.SignMessage(keyManager, "", message, hdPubKey, legacy: false);
+
+ var parsed = BIP322Signature.Parse(sig322, net);
+ Assert.True(addr.VerifyBIP322(message, parsed));
+
+ // Negative check: flip a byte → verification must fail.
+ var tampered = Convert.FromBase64String(sig322);
+ tampered[8] ^= 0x01;
+ Assert.False(addr.VerifyBIP322(message, BIP322Signature.Parse(Convert.ToBase64String(tampered), net)));
+ }
+
+ // --- Optional: BIP-322 verification for both indices (no fixed base64 needed) ---
+ [Theory]
+ [InlineData(0, "helloworld")]
+ [InlineData(1, "helloworld")]
+ public void SignMessage_BIP322_Verifies(int index, string message)
+ {
+ var keyManager = KeyManager.Recover(
+ new Mnemonic("faculty plunge pilot slice amount salute artist raw amateur excite dial canyon"),
+ "",
+ Network.Main,
+ KeyManager.GetAccountKeyPath(Network.Main, ScriptPubKeyType.Segwit));
+
+ var destinationProvider = new InternalDestinationProvider(keyManager);
+ var net = keyManager.GetNetwork();
+
+ var dests = destinationProvider.GetNextDestinations(index + 1, false).ToArray();
+ var dest = dests.Last();
+ Assert.True(keyManager.TryGetKeyForScriptPubKey(dest.ScriptPubKey, out HdPubKey? hdPubKey));
+ Assert.NotNull(hdPubKey);
+
+ // Use your helper directly to generate BIP-322 (legacy:false)
+ var sig322 = KeyManagerExtension.SignMessage(keyManager, "", message, hdPubKey!, legacy: false);
+ var addr = hdPubKey!.GetAddress(net);
+ var parsed = BIP322Signature.Parse(sig322, net);
+ Assert.True(addr.VerifyBIP322(message, parsed));
+
+ // Negative: tamper a byte -> must fail
+ var tampered = Convert.FromBase64String(sig322);
+ tampered[8] ^= 0x01;
+ Assert.False(addr.VerifyBIP322(message, BIP322Signature.Parse(Convert.ToBase64String(tampered), net)));
+ }
}
diff --git a/WalletWasabi.Tests/UnitTests/WabiSabi/Client/PaymentAwareOutputProviderTests.cs b/WalletWasabi.Tests/UnitTests/WabiSabi/Client/PaymentAwareOutputProviderTests.cs
index 293469b7da..9d0c7e1cb6 100644
--- a/WalletWasabi.Tests/UnitTests/WabiSabi/Client/PaymentAwareOutputProviderTests.cs
+++ b/WalletWasabi.Tests/UnitTests/WabiSabi/Client/PaymentAwareOutputProviderTests.cs
@@ -3,7 +3,6 @@
using System.Linq;
using WalletWasabi.Extensions;
using WalletWasabi.Tests.Helpers;
-using WalletWasabi.WabiSabi.Backend;
using WalletWasabi.WabiSabi.Client;
using WalletWasabi.WabiSabi.Client.Batching;
using WalletWasabi.WabiSabi.Recommendation;
diff --git a/WalletWasabi.Tests/UnitTests/WabiSabi/Integration/StuttererHttpClient.cs b/WalletWasabi.Tests/UnitTests/WabiSabi/Integration/StuttererHttpClient.cs
index fabb4a1882..383470788e 100644
--- a/WalletWasabi.Tests/UnitTests/WabiSabi/Integration/StuttererHttpClient.cs
+++ b/WalletWasabi.Tests/UnitTests/WabiSabi/Integration/StuttererHttpClient.cs
@@ -1,5 +1,3 @@
-using Microsoft.AspNetCore.Hosting;
-using NBitcoin;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
diff --git a/WalletWasabi.Tests/UnitTests/WabiSabi/Integration/WabiSabiHttpApiIntegrationTests.cs b/WalletWasabi.Tests/UnitTests/WabiSabi/Integration/WabiSabiHttpApiIntegrationTests.cs
index 641dae1f64..eec1d42c87 100644
--- a/WalletWasabi.Tests/UnitTests/WabiSabi/Integration/WabiSabiHttpApiIntegrationTests.cs
+++ b/WalletWasabi.Tests/UnitTests/WabiSabi/Integration/WabiSabiHttpApiIntegrationTests.cs
@@ -89,7 +89,7 @@ public async Task RegisterBannedCoinAsync()
services.AddScoped(s => rpc);
var prison = WabiSabiTestFactory.CreatePrison();
- prison.FailedVerification(bannedOutPoint, uint256.One);
+ prison.FailedVerification(bannedOutPoint, uint256.One, TimeSpan.Zero, "");
services.AddScoped(_ => prison);
})).CreateClient();
diff --git a/WalletWasabi.Tests/UnitTests/WabiSabi/Models/ConstructionStateTests.cs b/WalletWasabi.Tests/UnitTests/WabiSabi/Models/ConstructionStateTests.cs
index 5e67b3fa7f..f9fa3e0e63 100644
--- a/WalletWasabi.Tests/UnitTests/WabiSabi/Models/ConstructionStateTests.cs
+++ b/WalletWasabi.Tests/UnitTests/WabiSabi/Models/ConstructionStateTests.cs
@@ -2,7 +2,6 @@
using WalletWasabi.Extensions;
using WalletWasabi.Helpers;
using WalletWasabi.Tests.Helpers;
-using WalletWasabi.WabiSabi.Backend;
using WalletWasabi.WabiSabi.Backend.Rounds;
using WalletWasabi.WabiSabi.Models.MultipartyTransaction;
using Xunit;
diff --git a/WalletWasabi.Tests/UnitTests/Wallet/WalletSynchronizationTests.cs b/WalletWasabi.Tests/UnitTests/Wallet/WalletSynchronizationTests.cs
index f759aba78a..10d69363a9 100644
--- a/WalletWasabi.Tests/UnitTests/Wallet/WalletSynchronizationTests.cs
+++ b/WalletWasabi.Tests/UnitTests/Wallet/WalletSynchronizationTests.cs
@@ -2,7 +2,6 @@
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
-using WalletWasabi.Wallets;
using Xunit;
namespace WalletWasabi.Tests.UnitTests.Wallet;
diff --git a/WalletWasabi/Announcer/AnnouncementManager.cs b/WalletWasabi/Announcer/AnnouncementManager.cs
index ddf923f3c4..94cb90cb1d 100644
--- a/WalletWasabi/Announcer/AnnouncementManager.cs
+++ b/WalletWasabi/Announcer/AnnouncementManager.cs
@@ -4,12 +4,10 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
-using System.Net;
using System.Threading;
using System.Threading.Tasks;
using WalletWasabi.Bases;
using WalletWasabi.Extensions;
-using WalletWasabi.Interfaces;
using WalletWasabi.Logging;
using WalletWasabi.Models;
using WalletWasabi.WebClients.Wasabi;
diff --git a/WalletWasabi/Backend/Models/Responses/TwoFactorSetupResponse.cs b/WalletWasabi/Backend/Models/Responses/TwoFactorSetupResponse.cs
index a4b3ebe0a7..5a908a658b 100644
--- a/WalletWasabi/Backend/Models/Responses/TwoFactorSetupResponse.cs
+++ b/WalletWasabi/Backend/Models/Responses/TwoFactorSetupResponse.cs
@@ -1,5 +1,3 @@
-using Newtonsoft.Json;
-
namespace WalletWasabi.Backend.Models.Responses;
public class TwoFactorSetupResponse
diff --git a/WalletWasabi/Blockchain/Keys/HdPubKey.cs b/WalletWasabi/Blockchain/Keys/HdPubKey.cs
index 56f4208f23..dc4fcd5fc2 100644
--- a/WalletWasabi/Blockchain/Keys/HdPubKey.cs
+++ b/WalletWasabi/Blockchain/Keys/HdPubKey.cs
@@ -2,7 +2,6 @@
using Newtonsoft.Json;
using System.Collections.Generic;
using System.Linq;
-using System.Text.Json;
using System.Text.Json.Serialization;
using WalletWasabi.Bases;
using WalletWasabi.Blockchain.Analysis.Clustering;
diff --git a/WalletWasabi/Blockchain/TransactionOutputs/SmartCoin.cs b/WalletWasabi/Blockchain/TransactionOutputs/SmartCoin.cs
index a918691c54..2d99f273bd 100644
--- a/WalletWasabi/Blockchain/TransactionOutputs/SmartCoin.cs
+++ b/WalletWasabi/Blockchain/TransactionOutputs/SmartCoin.cs
@@ -6,6 +6,7 @@
using WalletWasabi.Blockchain.Transactions;
using WalletWasabi.Extensions;
using WalletWasabi.Models;
+using WalletWasabi.WabiSabi.Models;
namespace WalletWasabi.Blockchain.TransactionOutputs;
@@ -23,6 +24,7 @@ public class SmartCoin : NotifyPropertyChangedBase, IEquatable, IDest
private bool _confirmed;
private bool _isBanned;
+ private InputBannedReasonEnum[]? _reasons;
private bool _isExcludedFromCoinJoin;
private bool _isCoinJoinOutput;
@@ -101,6 +103,12 @@ public DateTimeOffset? BannedUntilUtc
}
}
+ public InputBannedReasonEnum[]? BanReasons
+ {
+ get => _reasons;
+ set => RaiseAndSetIfChanged(ref _reasons, value);
+ }
+
///
/// If the backend thinks it's spent, but Wasabi does not yet know.
///
@@ -153,6 +161,7 @@ public bool RefreshAndGetIsBanned()
IsBanned = false;
BannedUntilUtc = null;
+ BanReasons = null;
return false;
}
diff --git a/WalletWasabi/Extensions/KeyManagerExtension.cs b/WalletWasabi/Extensions/KeyManagerExtension.cs
new file mode 100644
index 0000000000..7588ed46eb
--- /dev/null
+++ b/WalletWasabi/Extensions/KeyManagerExtension.cs
@@ -0,0 +1,136 @@
+using NBitcoin;
+using NBitcoin.BIP322;
+using System.Linq;
+using System.Security;
+using WalletWasabi.Blockchain.Keys;
+using WalletWasabi.Extensions;
+using WalletWasabi.Helpers;
+
+public static class KeyManagerExtension
+{
+ ///
+ /// Controls the first byte ("header") of legacy compact signatures.
+ /// Standard=27..34 as per de-facto convention.
+ /// Plus8 allows reproducing historical/quirky signers that emitted header+8
+ /// (verifiers usually mask off the extra bit, so both parse identically).
+ ///
+ public enum LegacyHeaderStyle
+ {
+ Standard = 0,
+ Plus8 = 8
+ }
+
+ ///
+ /// Signs an arbitrary message with the HdPubKey's private key.
+ ///
+ /// Two modes:
+ /// - legacy=true → legacy "Bitcoin Signed Message" compact ECDSA (65 bytes, base64).
+ /// Works best with P2PKH verifiers. Header byte is adjustable to reproduce
+ /// historical outputs; r||s is produced by RFC6979 (deterministic k).
+ /// - legacy=false → BIP-322 (Simple/Full) proof depending on address type (SegWit/Taproot).
+ /// Safer & standard for bc1... addresses; bytes are library-specific,
+ /// so verify, don't compare for byte-equality.
+ ///
+ /// Notes / gotchas (short and on-point):
+ /// - Legacy preimage is double-SHA256(varstr("Bitcoin Signed Message:\n") || varstr(message)).
+ /// - Compact legacy signature layout: [header (1)] [r (32)] [s (32)].
+ /// header = 27 + recId (0..3) + (IsCompressed ? 4 : 0) + headerStyle (0 or 8).
+ /// Many verifiers only use (header-27)&3 and (header-27)&4, so Standard and Plus8 both verify.
+ /// - We *recompute* recId by trying 0..3 and checking pubkey recovery. This guarantees the header
+ /// decodes back to the exact HdPubKey regardless of library internals.
+ /// - forceLowR=false may be needed to reproduce old fixtures; low-R only affects DER/compact encoding
+ /// choice of r (same signature validity).
+ /// - Legacy verification on websites typically expects a P2PKH address; BIP-322 should be used for
+ /// SegWit/Taproot (bc1...) and is what modern wallets accept.
+ ///
+ public static string SignMessage(
+ this KeyManager keyManager,
+ string password,
+ string message,
+ HdPubKey hdPubKey,
+ bool legacy = true,
+ LegacyHeaderStyle headerStyle = LegacyHeaderStyle.Standard,
+ bool forceLowR = true)
+ {
+ Guard.NotNullOrEmptyOrWhitespace(message, nameof(message));
+ Guard.NotNull(nameof(hdPubKey), hdPubKey);
+
+ // Can't sign without secrets (watch-only / HW-only without software signing)
+ if (keyManager.IsWatchOnly)
+ {
+ throw new SecurityException("Wallet is watch-only/hardware-only; cannot sign.");
+ }
+
+ // Must know the derivation to locate the child secret
+ if (hdPubKey.FullKeyPath is null)
+ {
+ throw new InvalidOperationException("HdPubKey does not have a FullKeyPath.");
+ }
+
+ var network = keyManager.GetNetwork();
+ var spk = hdPubKey.GetAssumedScriptPubKey();
+
+ // Retrieve the child extended key for this script; throws if not found
+ var ek = keyManager.GetSecrets(password, spk).SingleOrDefault()
+ ?? throw new InvalidOperationException($"The signing key for '{spk}' was not found.");
+ var key = ek.GetBitcoinSecret(network, spk).PrivateKey;
+
+ if (legacy)
+ {
+ // LEGACY COMPACT (Bitcoin Signed Message)
+ // Preimage = varstr("Bitcoin Signed Message:\n") || varstr(message)
+ // Hash = double-SHA256(preimage)
+ var msgHash = BIP322Signature.CreateMessageHash(message, legacy: true);
+
+ // RFC6979 deterministic ECDSA; low-R toggle for fixture reproduction
+ var compact = key.SignCompact(msgHash, forceLowR: forceLowR);
+
+ // Determine recId (0..3) by recovery so our header definitely maps to this pubkey
+ int recId = -1;
+ for (int i = 0; i < 4; i++)
+ {
+ var candidate = new CompactSignature(i, compact.Signature);
+ if (PubKey.RecoverCompact(msgHash, candidate) == hdPubKey.PubKey)
+ {
+ recId = i;
+ break;
+ }
+ }
+ if (recId < 0)
+ {
+ throw new InvalidOperationException("Could not determine recovery id.");
+ }
+
+ // Header layout:
+ // 27 base + recId (0..3) + 4 if compressed + optional +8 for historical compatibility
+ bool compressed = hdPubKey.PubKey.IsCompressed; // HD keys are typically compressed
+ byte header = (byte)(27 + recId + (compressed ? 4 : 0) + (int)headerStyle);
+
+ // Assemble 65-byte payload: header + r||s (big-endian, fixed 32 bytes each)
+ var sig65 = new byte[65];
+ sig65[0] = header;
+ Buffer.BlockCopy(compact.Signature, 0, sig65, 1, 64);
+
+ // Most sites/APIs take base64 of these 65 bytes
+ return Convert.ToBase64String(sig65);
+ }
+ else
+ {
+ // BIP-322 (preferred for SegWit/Taproot). Produces either a Simple proof (witness stack)
+ // or a Full proof (serialized tx), both accepted by modern verifiers for the address type.
+ var addr = hdPubKey.GetAddress(network);
+
+ // Choose proof flavor: Simple for BIP84/86 single-key (P2WPKH/P2TR), Legacy for P2PKH
+ var sigType = hdPubKey.FullKeyPath.GetScriptTypeFromKeyPath() switch
+ {
+ ScriptPubKeyType.Segwit => SignatureType.Simple, // BIP84 P2WPKH
+ ScriptPubKeyType.TaprootBIP86 => SignatureType.Simple, // BIP86 P2TR single-key
+ _ => SignatureType.Legacy // P2PKH fallback
+ };
+
+ // NOTE: BIP-322 serialization can vary by implementation; do not byte-compare between libs.
+ // Always verify with addr.VerifyBIP322(message, parsedSignature).
+ return key.SignBIP322(addr, message, sigType).ToBase64();
+ }
+ }
+}
diff --git a/WalletWasabi/Filter/FilterChecker.cs b/WalletWasabi/Filter/FilterChecker.cs
index 57d52a6e44..ba86cbd3af 100644
--- a/WalletWasabi/Filter/FilterChecker.cs
+++ b/WalletWasabi/Filter/FilterChecker.cs
@@ -2,7 +2,6 @@
using System.Buffers.Binary;
using System.Collections.Generic;
using System.Numerics;
-using WalletWasabi.Backend.Models;
namespace WalletWasabi.Filter;
diff --git a/WalletWasabi/Hwi/HwiClient.cs b/WalletWasabi/Hwi/HwiClient.cs
index 4da9da53d1..a8e722a258 100644
--- a/WalletWasabi/Hwi/HwiClient.cs
+++ b/WalletWasabi/Hwi/HwiClient.cs
@@ -159,6 +159,22 @@ private async Task DisplayAddressImplAsync(HardwareWall
return address;
}
+ public async Task SignMessageAsync(HDFingerprint fingerprint, KeyPath keyPath, string message, CancellationToken cancel)
+ {
+ var keyPathString = keyPath.ToString(true, "h");
+
+ var response = await SendCommandAsync(
+ options: BuildOptions(deviceType: null, devicePath: null, fingerprint: fingerprint),
+ command: HwiCommands.SignMessage,
+ commandArguments: $"\"{message}\" {keyPathString}",
+ openConsole: false,
+ cancel).ConfigureAwait(false);
+
+ return HwiParser.ParseSignMessageResponse(response);
+ }
+
+
+
public async Task SignTxAsync(HardwareWalletModels deviceType, string? devicePath, PSBT psbt, CancellationToken cancel)
=> await SignTxImplAsync(deviceType, devicePath, null, psbt, cancel).ConfigureAwait(false);
diff --git a/WalletWasabi/Hwi/Parsers/HwiParser.cs b/WalletWasabi/Hwi/Parsers/HwiParser.cs
index a457698f4a..1e8f12d096 100644
--- a/WalletWasabi/Hwi/Parsers/HwiParser.cs
+++ b/WalletWasabi/Hwi/Parsers/HwiParser.cs
@@ -398,4 +398,20 @@ public static string ToHwiFriendlyString(this HardwareWalletModels me)
{
return me.ToString().ToLowerInvariant();
}
+
+ public static string ParseSignMessageResponse(string json)
+ {
+ if (JsonHelpers.TryParseJToken(json, out JToken? token))
+ {
+ string? signature = token["signature"]?.ToString()?.Trim()
+ ?? throw new ArgumentNullException("Signature is null.");
+
+ return signature;
+ }
+ else
+ {
+ throw new FormatException($"Could not parse signature: {json}.");
+ }
+ }
+
}
diff --git a/WalletWasabi/JsonConverters/DecimalSeparatorJsonConverter.cs b/WalletWasabi/JsonConverters/DecimalSeparatorJsonConverter.cs
index 12ea143d65..f7c4ec070a 100644
--- a/WalletWasabi/JsonConverters/DecimalSeparatorJsonConverter.cs
+++ b/WalletWasabi/JsonConverters/DecimalSeparatorJsonConverter.cs
@@ -1,5 +1,4 @@
-using System.Globalization;
-using System.Linq;
+using System.Linq;
using System.Text.Json;
using System.Text.Json.Serialization;
using WalletWasabi.Extensions;
diff --git a/WalletWasabi/JsonConverters/DisplayLanguageJsonConverter.cs b/WalletWasabi/JsonConverters/DisplayLanguageJsonConverter.cs
index 1089912e6b..da626fffb4 100644
--- a/WalletWasabi/JsonConverters/DisplayLanguageJsonConverter.cs
+++ b/WalletWasabi/JsonConverters/DisplayLanguageJsonConverter.cs
@@ -1,6 +1,5 @@
using System.Text.Json;
using System.Text.Json.Serialization;
-using WalletWasabi.Lang;
using WalletWasabi.Models;
namespace WalletWasabi.JsonConverters;
diff --git a/WalletWasabi/JsonConverters/GetOrderResponseItemJsonConverter.cs b/WalletWasabi/JsonConverters/GetOrderResponseItemJsonConverter.cs
index 1b6ca79883..c454fc9f73 100644
--- a/WalletWasabi/JsonConverters/GetOrderResponseItemJsonConverter.cs
+++ b/WalletWasabi/JsonConverters/GetOrderResponseItemJsonConverter.cs
@@ -1,6 +1,4 @@
using Newtonsoft.Json;
-using System;
-using WalletWasabi.BuySell;
using static WalletWasabi.BuySell.BuySellClientModels;
namespace WalletWasabi.JsonConverters
diff --git a/WalletWasabi/JsonConverters/GroupSeparatorJsonConverter.cs b/WalletWasabi/JsonConverters/GroupSeparatorJsonConverter.cs
index 0429c7a497..c8125f4f4e 100644
--- a/WalletWasabi/JsonConverters/GroupSeparatorJsonConverter.cs
+++ b/WalletWasabi/JsonConverters/GroupSeparatorJsonConverter.cs
@@ -1,5 +1,4 @@
-using System.Globalization;
-using System.Linq;
+using System.Linq;
using System.Text.Json;
using System.Text.Json.Serialization;
using WalletWasabi.Extensions;
diff --git a/WalletWasabi/Lang/LocalizationExtension.cs b/WalletWasabi/Lang/LocalizationExtension.cs
index fbe3bac544..3e7a73c2a2 100644
--- a/WalletWasabi/Lang/LocalizationExtension.cs
+++ b/WalletWasabi/Lang/LocalizationExtension.cs
@@ -2,7 +2,6 @@
using System.Globalization;
using System.Linq;
using System.Resources;
-using System.Text;
using WalletWasabi.Extensions;
using WalletWasabi.Lang.Models;
using WalletWasabi.Logging;
diff --git a/WalletWasabi/Lang/Models/DecimalSeparator.cs b/WalletWasabi/Lang/Models/DecimalSeparator.cs
index e7e777ce73..59f5d46ec0 100644
--- a/WalletWasabi/Lang/Models/DecimalSeparator.cs
+++ b/WalletWasabi/Lang/Models/DecimalSeparator.cs
@@ -1,4 +1,3 @@
-using System.ComponentModel;
using WalletWasabi.Models;
namespace WalletWasabi.Lang.Models;
diff --git a/WalletWasabi/Lang/Models/GroupSeparator.cs b/WalletWasabi/Lang/Models/GroupSeparator.cs
index 46762764cf..8549613e54 100644
--- a/WalletWasabi/Lang/Models/GroupSeparator.cs
+++ b/WalletWasabi/Lang/Models/GroupSeparator.cs
@@ -1,4 +1,3 @@
-using System.ComponentModel;
using WalletWasabi.Models;
namespace WalletWasabi.Lang.Models;
diff --git a/WalletWasabi/Lang/Resources.Designer.cs b/WalletWasabi/Lang/Resources.Designer.cs
index 3884483cfa..68a7b0b24c 100644
--- a/WalletWasabi/Lang/Resources.Designer.cs
+++ b/WalletWasabi/Lang/Resources.Designer.cs
@@ -1,6 +1,7 @@
//------------------------------------------------------------------------------
//
// This code was generated by a tool.
+// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
@@ -185,6 +186,15 @@ public static string AddressesAwaitingPayment {
}
}
+ ///
+ /// Looks up a localized string similar to Address:.
+ ///
+ public static string AddressWithColon {
+ get {
+ return ResourceManager.GetString("AddressWithColon", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Create, connect, import or recover.
///
@@ -608,6 +618,15 @@ public static string BackendNotConnected {
}
}
+ ///
+ /// Looks up a localized string similar to Temporary Server Issue.
+ ///
+ public static string BackendStabilitySafetyEnum {
+ get {
+ return ResourceManager.GetString("BackendStabilitySafetyEnum", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to The reported balance might not be accurate, and some other features may not be available because Ginger was unable to connect to the server. Retrying to connect..
///
@@ -959,6 +978,15 @@ public static string ChangeTransactionFeeOrConfirmationTime {
}
}
+ ///
+ /// Looks up a localized string similar to Protocol Violation.
+ ///
+ public static string CheatingEnum {
+ get {
+ return ResourceManager.GetString("CheatingEnum", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Check your device and finish the initialization..
///
@@ -1976,6 +2004,15 @@ public static string EnterPassphraseAuth {
}
}
+ ///
+ /// Looks up a localized string similar to Enter the message you wish to sign.
+ ///
+ public static string EnterTheMessageYouWishToSign {
+ get {
+ return ResourceManager.GetString("EnterTheMessageYouWishToSign", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Some entities can deanonymize this transaction because it spends coins with no privacy. Coinjoin more to have enough private coins for this transaction..
///
@@ -2201,6 +2238,15 @@ public static string FailedToGetLegalDocuments {
}
}
+ ///
+ /// Looks up a localized string similar to Potential Risk.
+ ///
+ public static string FailedToVerifyEnum {
+ get {
+ return ResourceManager.GetString("FailedToVerifyEnum", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to FAQ.
///
@@ -2651,6 +2697,15 @@ public static string Index {
}
}
+ ///
+ /// Looks up a localized string similar to (Inherited from another coin).
+ ///
+ public static string InheritedEnum {
+ get {
+ return ResourceManager.GetString("InheritedEnum", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Initialize your device first..
///
@@ -2714,6 +2769,15 @@ public static string InTheFieldBelow {
}
}
+ ///
+ /// Looks up a localized string similar to Invalid Bitcoin address..
+ ///
+ public static string InvalidBitcoinAddress {
+ get {
+ return ResourceManager.GetString("InvalidBitcoinAddress", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Address is not a valid BTC address..
///
@@ -2957,6 +3021,15 @@ public static string LocalBitcoinCoreKnotsVersion {
}
}
+ ///
+ /// Looks up a localized string similar to Temporarily Restriction.
+ ///
+ public static string LocalCoinVerifierEnum {
+ get {
+ return ResourceManager.GetString("LocalCoinVerifierEnum", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Mark all as read.
///
@@ -2975,6 +3048,15 @@ public static string Max {
}
}
+ ///
+ /// Looks up a localized string similar to Message:.
+ ///
+ public static string MessageWithColon {
+ get {
+ return ResourceManager.GetString("MessageWithColon", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to min.
///
@@ -3552,6 +3634,15 @@ public static string PasteFromClipboard {
}
}
+ ///
+ /// Looks up a localized string similar to Paste the address provided by the exchange.
+ ///
+ public static string PasteTheAddressProvidedByTheExchange {
+ get {
+ return ResourceManager.GetString("PasteTheAddressProvidedByTheExchange", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to You can track, manage, and view the details of your past orders..
///
@@ -3894,6 +3985,15 @@ public static string Rarely {
}
}
+ ///
+ /// Looks up a localized string similar to Reason: {0}.
+ ///
+ public static string Reason {
+ get {
+ return ResourceManager.GetString("Reason", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Receive in:.
///
@@ -4101,6 +4201,51 @@ public static string ReviewCoins {
}
}
+ ///
+ /// Looks up a localized string similar to Round Interruption.
+ ///
+ public static string RoundDisruption {
+ get {
+ return ResourceManager.GetString("RoundDisruption", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Round Interruption.
+ ///
+ public static string RoundDisruptionMethodDidNotConfirmEnum {
+ get {
+ return ResourceManager.GetString("RoundDisruptionMethodDidNotConfirmEnum", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Round Interruption.
+ ///
+ public static string RoundDisruptionMethodDidNotSignalReadyToSignEnum {
+ get {
+ return ResourceManager.GetString("RoundDisruptionMethodDidNotSignalReadyToSignEnum", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Round Interruption.
+ ///
+ public static string RoundDisruptionMethodDidNotSignEnum {
+ get {
+ return ResourceManager.GetString("RoundDisruptionMethodDidNotSignEnum", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Coin was spent during the round.
+ ///
+ public static string RoundDisruptionMethodDoubleSpentEnum {
+ get {
+ return ResourceManager.GetString("RoundDisruptionMethodDoubleSpentEnum", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Round ended, awaiting next round.
///
@@ -4578,6 +4723,42 @@ public static string ShowSensitiveData {
}
}
+ ///
+ /// Looks up a localized string similar to Signature.
+ ///
+ public static string Signature {
+ get {
+ return ResourceManager.GetString("Signature", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Sign Message.
+ ///
+ public static string SignMessage {
+ get {
+ return ResourceManager.GetString("SignMessage", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Sign a message to verify you own the address for the exchange.
+ ///
+ public static string SignMessageToVerifyYouOwnAddressForExchange {
+ get {
+ return ResourceManager.GetString("SignMessageToVerifyYouOwnAddressForExchange", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Sign with Hardware Wallet.
+ ///
+ public static string SignWithHardwareWallet {
+ get {
+ return ResourceManager.GetString("SignWithHardwareWallet", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Skip.
///
@@ -4812,6 +4993,15 @@ public static string TermsAndConditionsViewTermsAndConditions {
}
}
+ ///
+ /// Looks up a localized string similar to The address does not belong to this wallet..
+ ///
+ public static string TheAddressDoesNotBelongToThisWallet {
+ get {
+ return ResourceManager.GetString("TheAddressDoesNotBelongToThisWallet", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to the same amount.
///
@@ -5370,6 +5560,15 @@ public static string Unknown {
}
}
+ ///
+ /// Looks up a localized string similar to Unknown.
+ ///
+ public static string UnknownEnum {
+ get {
+ return ResourceManager.GetString("UnknownEnum", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Unknown error. Make sure the device is connected and isn't busy, then try again..
///
diff --git a/WalletWasabi/Lang/Resources.de.resx b/WalletWasabi/Lang/Resources.de.resx
index b2a77c6bb6..c675bc80a5 100644
--- a/WalletWasabi/Lang/Resources.de.resx
+++ b/WalletWasabi/Lang/Resources.de.resx
@@ -2134,4 +2134,70 @@
Einstellungen öffnen
+
+ Nachricht signieren
+
+
+ Ungültige Bitcoin-Adresse.
+
+
+ Die Adresse gehört nicht zu dieser Wallet.
+
+
+ Mit Hardware-Wallet signieren
+
+
+ Signieren Sie eine Nachricht, um zu bestätigen, dass Sie die Adresse für die Börse besitzen
+
+
+ Adresse:
+
+
+ Fügen Sie die von der Wechselstelle bereitgestellte Adresse ein
+
+
+ Nachricht:
+
+
+ Geben Sie die Nachricht ein, die Sie signieren möchten
+
+
+ Signatur
+
+
+ Grund: {0}
+
+
+ Unbekannt
+
+
+ Vorübergehendes Serverproblem
+
+
+ Protokollverletzung
+
+
+ Rundenunterbrechung
+
+
+ Potenzielles Risiko
+
+
+ (Von einer anderen Münze geerbt)
+
+
+ Vorübergehende Einschränkung
+
+
+ Rundenunterbrechung
+
+
+ Rundenunterbrechung
+
+
+ Münze wurde während der Runde ausgegeben
+
+
+ Rundenunterbrechung
+
diff --git a/WalletWasabi/Lang/Resources.es.resx b/WalletWasabi/Lang/Resources.es.resx
index 0ef6e53528..f1cdf6f6eb 100644
--- a/WalletWasabi/Lang/Resources.es.resx
+++ b/WalletWasabi/Lang/Resources.es.resx
@@ -2119,4 +2119,70 @@
Establecer como predeterminado
+
+ Firmar mensaje
+
+
+ Dirección de Bitcoin no válida.
+
+
+ La dirección no pertenece a esta cartera.
+
+
+ Firmar con cartera de hardware
+
+
+ Firma un mensaje para verificar que eres el propietario de la dirección para el intercambio
+
+
+ Dirección:
+
+
+ Pega la dirección proporcionada por el cambiador
+
+
+ Mensaje:
+
+
+ Introduce el mensaje que deseas firmar
+
+
+ Firma
+
+
+ Razón: {0}
+
+
+ Desconocido
+
+
+ Problema temporal del servidor
+
+
+ Violación de protocolo
+
+
+ Interrupción de la ronda
+
+
+ Riesgo potencial
+
+
+ (Heredado de otra moneda)
+
+
+ Restricción temporal
+
+
+ Interrupción de la ronda
+
+
+ La moneda se gastó durante la ronda
+
+
+ Interrupción de la ronda
+
+
+ Interrupción de la ronda
+
diff --git a/WalletWasabi/Lang/Resources.fr.resx b/WalletWasabi/Lang/Resources.fr.resx
index 10989918bc..76c6d62a19 100644
--- a/WalletWasabi/Lang/Resources.fr.resx
+++ b/WalletWasabi/Lang/Resources.fr.resx
@@ -2125,4 +2125,70 @@
Définir par défaut
+
+ Signer le message
+
+
+ Adresse Bitcoin invalide.
+
+
+ L'adresse n'appartient pas à ce portefeuille.
+
+
+ Signer avec un portefeuille matériel
+
+
+ Signez un message pour vérifier que vous possédez l'adresse pour l'échange
+
+
+ Adresse :
+
+
+ Collez l'adresse fournie par le service de change
+
+
+ Message :
+
+
+ Entrez le message que vous souhaitez signer
+
+
+ Signature
+
+
+ Raison : {0}
+
+
+ Inconnu
+
+
+ Problème temporaire du serveur
+
+
+ Violation du protocole
+
+
+ Interruption de la manche
+
+
+ Risque potentiel
+
+
+ (Hérité d'une autre pièce)
+
+
+ Restriction temporaire
+
+
+ Interruption de la manche
+
+
+ Manche interrompue
+
+
+ La pièce a été dépensée pendant la manche
+
+
+ Interruption de la manche
+
diff --git a/WalletWasabi/Lang/Resources.hu.resx b/WalletWasabi/Lang/Resources.hu.resx
index 90ed8364e8..9d7bdf2ea6 100644
--- a/WalletWasabi/Lang/Resources.hu.resx
+++ b/WalletWasabi/Lang/Resources.hu.resx
@@ -2119,4 +2119,70 @@
Beállítás alapértelmezettként
+
+ Üzenet aláírása
+
+
+ Érvénytelen Bitcoin-cím.
+
+
+ A cím nem ehhez a pénztárcához tartozik.
+
+
+ Aláírás hardveres pénztárcával
+
+
+ Írj alá egy üzenetet annak igazolására, hogy a váltó számára te birtoklod a címet
+
+
+ Cím:
+
+
+ Illeszd be a váltó által megadott címet
+
+
+ Üzenet:
+
+
+ Add meg az üzenetet, amelyet alá szeretnél írni
+
+
+ Aláírás
+
+
+ Ok: {0}
+
+
+ Ismeretlen
+
+
+ Átmeneti szerverhiba
+
+
+ Protokoll megsértése
+
+
+ Kör megszakítása
+
+
+ Lehetséges kockázat
+
+
+ (Egy másik érmétől örökölve)
+
+
+ Átmeneti korlátozás
+
+
+ Kör megszakítása
+
+
+ Kör megszakítása
+
+
+ Az érme elköltésre került a kör során
+
+
+ Kör megszakítása
+
diff --git a/WalletWasabi/Lang/Resources.it.resx b/WalletWasabi/Lang/Resources.it.resx
index 7a816fdf2f..9cf63dfb56 100644
--- a/WalletWasabi/Lang/Resources.it.resx
+++ b/WalletWasabi/Lang/Resources.it.resx
@@ -2098,4 +2098,70 @@
Imposta come predefinito
+
+ Firma messaggio
+
+
+ Indirizzo Bitcoin non valido.
+
+
+ L'indirizzo non appartiene a questo portafoglio.
+
+
+ Firma con portafoglio hardware
+
+
+ Firma un messaggio per verificare che possiedi l'indirizzo per lo scambio
+
+
+ Indirizzo:
+
+
+ Incolla l'indirizzo fornito dal servizio di cambio
+
+
+ Messaggio:
+
+
+ Inserisci il messaggio che desideri firmare
+
+
+ Firma
+
+
+ Motivo: {0}
+
+
+ Sconosciuto
+
+
+ Problema temporaneo del server
+
+
+ Violazione del protocollo
+
+
+ Interruzione del turno
+
+
+ Rischio potenziale
+
+
+ (Ereditato da un'altra moneta)
+
+
+ Restrizione temporanea
+
+
+ Interruzione del turno
+
+
+ Interruzione del turno
+
+
+ La moneta è stata spesa durante il turno
+
+
+ Interruzione del turno
+
diff --git a/WalletWasabi/Lang/Resources.pt.resx b/WalletWasabi/Lang/Resources.pt.resx
index b8d365f86a..aeec8de4c2 100644
--- a/WalletWasabi/Lang/Resources.pt.resx
+++ b/WalletWasabi/Lang/Resources.pt.resx
@@ -2119,4 +2119,70 @@
Definir como padrão
+
+ Assinar mensagem
+
+
+ Endereço de Bitcoin inválido.
+
+
+ O endereço não pertence a esta carteira.
+
+
+ Assinar com carteira de hardware
+
+
+ Assine uma mensagem para verificar que você possui o endereço para a troca
+
+
+ Endereço:
+
+
+ Cole o endereço fornecido pelo serviço de câmbio
+
+
+ Mensagem:
+
+
+ Insira a mensagem que deseja assinar
+
+
+ Assinatura
+
+
+ Motivo: {0}
+
+
+ Desconhecido
+
+
+ Problema temporário no servidor
+
+
+ Violação de protocolo
+
+
+ Interrupção da rodada
+
+
+ Risco potencial
+
+
+ (Herdado de outra moeda)
+
+
+ Restrição temporária
+
+
+ Interrupção da rodada
+
+
+ Interrupção da rodada
+
+
+ A moeda foi gasta durante a rodada
+
+
+ Interrupção da rodada
+
diff --git a/WalletWasabi/Lang/Resources.resx b/WalletWasabi/Lang/Resources.resx
index 9e65bf40e5..f7a7c2cb5c 100644
--- a/WalletWasabi/Lang/Resources.resx
+++ b/WalletWasabi/Lang/Resources.resx
@@ -2121,4 +2121,70 @@
Set as Default
+
+ Sign Message
+
+
+ Invalid Bitcoin address.
+
+
+ The address does not belong to this wallet.
+
+
+ Sign with Hardware Wallet
+
+
+ Sign a message to verify you own the address for the exchange
+
+
+ Address:
+
+
+ Paste the address provided by the exchange
+
+
+ Message:
+
+
+ Enter the message you wish to sign
+
+
+ Signature
+
+
+ Reason: {0}
+
+
+ Unknown
+
+
+ Temporary Server Issue
+
+
+ Protocol Violation
+
+
+ Round Interruption
+
+
+ Potential Risk
+
+
+ (Inherited from another coin)
+
+
+ Temporarily Restriction
+
+
+ Round Interruption
+
+
+ Round Interruption
+
+
+ Coin was spent during the round
+
+
+ Round Interruption
+
diff --git a/WalletWasabi/Lang/Resources.tr.resx b/WalletWasabi/Lang/Resources.tr.resx
index ad022cb557..7ebc26026e 100644
--- a/WalletWasabi/Lang/Resources.tr.resx
+++ b/WalletWasabi/Lang/Resources.tr.resx
@@ -2119,4 +2119,70 @@
Varsayılan olarak ayarla
+
+ Mesaj imzala
+
+
+ Geçersiz Bitcoin adresi.
+
+
+ Adres bu cüzdana ait değil.
+
+
+ Donanım cüzdanı ile imzala
+
+
+ Borsada adresin size ait olduğunu doğrulamak için bir mesaj imzalayın
+
+
+ Adres:
+
+
+ Döviz bozdurma hizmeti tarafından verilen adresi yapıştırın
+
+
+ Mesaj:
+
+
+ İmzalamak istediğiniz mesajı girin
+
+
+ İmza
+
+
+ Sebep: {0}
+
+
+ Bilinmiyor
+
+
+ Geçici sunucu sorunu
+
+
+ Protokol ihlali
+
+
+ Turun kesilmesi
+
+
+ Potansiyel risk
+
+
+ (Başka bir madenden devralındı)
+
+
+ Geçici kısıtlama
+
+
+ Turun kesilmesi
+
+
+ Turun kesilmesi
+
+
+ Tur sırasında madeni para harcandı
+
+
+ Turun kesilmesi
+
diff --git a/WalletWasabi/Lang/Resources.zh.resx b/WalletWasabi/Lang/Resources.zh.resx
index 735502222b..df8e2184c8 100644
--- a/WalletWasabi/Lang/Resources.zh.resx
+++ b/WalletWasabi/Lang/Resources.zh.resx
@@ -2134,4 +2134,70 @@
打开设置
+
+ 签署消息
+
+
+ 无效的比特币地址。
+
+
+ 该地址不属于此钱包。
+
+
+ 使用硬件钱包签名
+
+
+ 签署一条消息以验证您拥有用于交易所的地址
+
+
+ 地址:
+
+
+ 粘贴兑换服务提供的地址
+
+
+ 消息:
+
+
+ 输入您想要签名的消息
+
+
+ 签名
+
+
+ 原因:{0}
+
+
+ 未知
+
+
+ 临时服务器问题
+
+
+ 协议违规
+
+
+ 回合中断
+
+
+ 潜在风险
+
+
+ (继承自另一枚币)
+
+
+ 临时限制
+
+
+ 回合中断
+
+
+ 回合中断
+
+
+ 硬币在回合中已被花费
+
+
+ 回合中断
+
diff --git a/WalletWasabi/Models/TorMode.cs b/WalletWasabi/Models/TorMode.cs
index 0c9e2d1115..cf127f7d85 100644
--- a/WalletWasabi/Models/TorMode.cs
+++ b/WalletWasabi/Models/TorMode.cs
@@ -1,5 +1,3 @@
-using WalletWasabi.Lang;
-
namespace WalletWasabi.Models;
///
diff --git a/WalletWasabi/Nostr/CoordinatorNostrPublisher.cs b/WalletWasabi/Nostr/CoordinatorNostrPublisher.cs
index 6894c76afc..729c0db6ce 100644
--- a/WalletWasabi/Nostr/CoordinatorNostrPublisher.cs
+++ b/WalletWasabi/Nostr/CoordinatorNostrPublisher.cs
@@ -1,7 +1,6 @@
using System.Net;
using System.Threading;
using System.Threading.Tasks;
-using NBitcoin;
using NBitcoin.Secp256k1;
using NNostr.Client;
using WalletWasabi.Bases;
diff --git a/WalletWasabi/Nostr/NostrExtensions.cs b/WalletWasabi/Nostr/NostrExtensions.cs
index 7fd57f0368..8913f2df00 100644
--- a/WalletWasabi/Nostr/NostrExtensions.cs
+++ b/WalletWasabi/Nostr/NostrExtensions.cs
@@ -6,7 +6,6 @@
using NBitcoin;
using NBitcoin.Secp256k1;
using NNostr.Client;
-using WalletWasabi.Exceptions;
using WalletWasabi.Helpers;
namespace WalletWasabi.Nostr;
diff --git a/WalletWasabi/Tor/TorProcessManager.cs b/WalletWasabi/Tor/TorProcessManager.cs
index e0afa85a07..c4b9897ed8 100644
--- a/WalletWasabi/Tor/TorProcessManager.cs
+++ b/WalletWasabi/Tor/TorProcessManager.cs
@@ -1,4 +1,3 @@
-using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
diff --git a/WalletWasabi/Userfacing/CurrencyInput.cs b/WalletWasabi/Userfacing/CurrencyInput.cs
index 253128b198..987464d1ac 100644
--- a/WalletWasabi/Userfacing/CurrencyInput.cs
+++ b/WalletWasabi/Userfacing/CurrencyInput.cs
@@ -1,6 +1,5 @@
using System.Diagnostics.CodeAnalysis;
using System.Linq;
-using System.Text;
using System.Text.RegularExpressions;
namespace WalletWasabi.Userfacing;
diff --git a/WalletWasabi/WabiSabi/Backend/Banning/ApiResponse.cs b/WalletWasabi/WabiSabi/Backend/Banning/ApiResponse.cs
index be012cc983..fa583b48e3 100644
--- a/WalletWasabi/WabiSabi/Backend/Banning/ApiResponse.cs
+++ b/WalletWasabi/WabiSabi/Backend/Banning/ApiResponse.cs
@@ -1,3 +1,3 @@
namespace WalletWasabi.WabiSabi.Backend.Banning;
-public record ApiResponse(ApiResponseInfo Info, string Provider, bool ShouldBan, bool ShouldRemove, string Details);
+public record ApiResponse(ApiResponseInfo Info, string Provider, bool ShouldBan, bool ShouldRemove, string Details, TimeSpan RecommendedBanTime);
diff --git a/WalletWasabi/WabiSabi/Backend/Banning/CoinVerifier.cs b/WalletWasabi/WabiSabi/Backend/Banning/CoinVerifier.cs
index aba8d0b630..17f0f9cc9c 100644
--- a/WalletWasabi/WabiSabi/Backend/Banning/CoinVerifier.cs
+++ b/WalletWasabi/WabiSabi/Backend/Banning/CoinVerifier.cs
@@ -32,7 +32,21 @@ internal CoinVerifier(CoinJoinIdStore coinJoinIdStore, CoinVerifierApiClient api
VerifierAuditArchiver = auditArchiver ?? new("test/directory/path");
}
- public event EventHandler? CoinBlacklisted;
+ public class CoinBlacklistedEventArgs : EventArgs
+ {
+ public Coin Coin { get; }
+ public TimeSpan RecommendedBanTime { get; }
+ public string Provider { get; }
+
+ public CoinBlacklistedEventArgs(Coin coin, TimeSpan recommendedBanTime, string provider)
+ {
+ Coin = coin;
+ RecommendedBanTime = recommendedBanTime;
+ Provider = provider;
+ }
+ }
+
+ public event EventHandler? CoinBlacklisted;
// This should be much bigger than the possible input-reg period.
private TimeSpan AbsoluteScheduleSanityTimeout { get; } = TimeSpan.FromDays(2);
@@ -231,7 +245,7 @@ public bool TryScheduleVerification(Coin coin, TimeSpan delayedStart, int confir
// We got a definitive answer.
if (apiResponseItem.ShouldBan)
{
- CoinBlacklisted?.SafeInvoke(this, coin);
+ CoinBlacklisted?.SafeInvoke(this, new CoinBlacklistedEventArgs(coin, apiResponseItem.RecommendedBanTime, apiResponseItem.Provider));
}
else if (!apiResponseItem.ShouldRemove)
{
diff --git a/WalletWasabi/WabiSabi/Backend/Banning/CoinVerifierApiClient.cs b/WalletWasabi/WabiSabi/Backend/Banning/CoinVerifierApiClient.cs
index d7ea919a3f..eaf5df6e85 100644
--- a/WalletWasabi/WabiSabi/Backend/Banning/CoinVerifierApiClient.cs
+++ b/WalletWasabi/WabiSabi/Backend/Banning/CoinVerifierApiClient.cs
@@ -32,7 +32,7 @@ public async Task SendRequestAsync(Coin coin, int coinBlockHeight,
return response;
}
}
- response ??= new(ApiResponseInfo.OK, "none", true, true, "No providers");
+ response ??= new(ApiResponseInfo.OK, "none", true, true, "No providers", TimeSpan.FromHours(1));
return response;
}
diff --git a/WalletWasabi/WabiSabi/Backend/DoSPrevention/DoSConfiguration.cs b/WalletWasabi/WabiSabi/Backend/DoSPrevention/DoSConfiguration.cs
index e3653e30f2..129ec3f045 100644
--- a/WalletWasabi/WabiSabi/Backend/DoSPrevention/DoSConfiguration.cs
+++ b/WalletWasabi/WabiSabi/Backend/DoSPrevention/DoSConfiguration.cs
@@ -3,6 +3,7 @@ namespace WalletWasabi.WabiSabi.Backend.DoSPrevention;
public record DoSConfiguration(
decimal SeverityInBitcoinsPerHour,
TimeSpan MinTimeForFailedToVerify,
+ TimeSpan SimplifiedPunishment,
TimeSpan MinTimeForCheating,
TimeSpan MinTimeInPrison,
decimal PenaltyFactorForDisruptingConfirmation,
diff --git a/WalletWasabi/WabiSabi/Backend/DoSPrevention/Offender.cs b/WalletWasabi/WabiSabi/Backend/DoSPrevention/Offender.cs
index 0c20592d70..97ac85935c 100644
--- a/WalletWasabi/WabiSabi/Backend/DoSPrevention/Offender.cs
+++ b/WalletWasabi/WabiSabi/Backend/DoSPrevention/Offender.cs
@@ -1,7 +1,9 @@
+using NBitcoin;
using System.Collections.Generic;
+using System.Globalization;
using System.Linq;
-using NBitcoin;
using WalletWasabi.Extensions;
+using WalletWasabi.WabiSabi.Models;
namespace WalletWasabi.WabiSabi.Backend.DoSPrevention;
@@ -18,12 +20,12 @@ public abstract record Offense();
public record RoundDisruption(IEnumerable DisruptedRoundIds, Money Value, RoundDisruptionMethod Method) : Offense
{
- public RoundDisruption(uint256 disruptedRoundId, Money value, RoundDisruptionMethod method)
- : this(disruptedRoundId.Singleton(), value, method) {}
+ public RoundDisruption(uint256 disruptedRoundId, Money value, RoundDisruptionMethod method)
+ : this(disruptedRoundId.Singleton(), value, method) { }
}
public record BackendStabilitySafety(uint256 RoundId) : Offense;
-public record FailedToVerify(uint256 VerifiedInRoundId) : Offense;
-public record Inherited(OutPoint[] Ancestors) : Offense;
+public record FailedToVerify(uint256 VerifiedInRoundId, TimeSpan RecommendedBanTime, string Provider) : Offense;
+public record Inherited(OutPoint[] Ancestors, InputBannedReasonEnum[] InputBannedReasonEnums) : Offense;
public record Cheating(uint256 RoundId) : Offense;
public record Offender(OutPoint OutPoint, DateTimeOffset StartedTime, Offense Offense)
@@ -54,25 +56,36 @@ IEnumerable SerializedElements()
yield return disruptedRoundId.ToString();
}
break;
+
case BackendStabilitySafety backendStabilitySafety:
yield return nameof(BackendStabilitySafety);
yield return backendStabilitySafety.RoundId.ToString();
break;
+
case FailedToVerify fv:
yield return nameof(FailedToVerify);
yield return fv.VerifiedInRoundId.ToString();
+ yield return fv.RecommendedBanTime.ToString();
+ yield return fv.Provider;
break;
+
case Inherited inherited:
yield return nameof(Inherited);
foreach (var ancestor in inherited.Ancestors)
{
yield return ancestor.ToString();
}
+ foreach (var reason in inherited.InputBannedReasonEnums)
+ {
+ yield return reason.ToString();
+ }
break;
+
case Cheating cheating:
yield return nameof(Cheating);
yield return cheating.RoundId.ToString();
break;
+
default:
throw new NotImplementedException("Cannot serialize an unknown offense type.");
}
@@ -84,7 +97,6 @@ IEnumerable SerializedElements()
public static Offender FromStringLine(string str)
{
var parts = str.Split(Separator);
-
var startedTime = DateTimeOffset.FromUnixTimeSeconds(long.Parse(parts[0]));
var outpoint = OutPoint.Parse(parts[1]);
@@ -106,20 +118,103 @@ public static Offender FromStringLine(string str)
nameof(BackendStabilitySafety) =>
new BackendStabilitySafety(uint256.Parse(parts[3])),
nameof(FailedToVerify) =>
- new FailedToVerify(uint256.Parse(parts[3])),
+ new FailedToVerify(
+ VerifiedInRoundId: uint256.Parse(parts[3]),
+ RecommendedBanTime: parts.Length > 4 ? TimeSpan.Parse(parts[4], CultureInfo.InvariantCulture) : TimeSpan.Zero,
+ Provider: parts.Length > 5 ? parts[5] : ""),
nameof(Inherited) =>
ParseInheritedOffense(),
nameof(Cheating) =>
new Cheating(uint256.Parse(parts[3])),
- _ => throw new NotImplementedException("Cannot deserialize an unknown offense type.")
+ _ => throw new NotImplementedException("Cannot deserialize an unknown offense type.")
};
return new Offender(outpoint, startedTime, offense);
Offense ParseInheritedOffense()
{
- var ancestors = parts.Skip(3).Select(OutPoint.Parse).ToArray();
- return new Inherited(ancestors);
+ List ancestors = [];
+ List reasons = [];
+
+ foreach (var ancestorOrReason in parts.Skip(3))
+ {
+ if (OutPoint.TryParse(ancestorOrReason, out var outPoint))
+ {
+ ancestors.Add(outPoint!);
+ }
+ else if (Enum.TryParse(ancestorOrReason, true, out var reason))
+ {
+ reasons.Add(reason);
+ }
+ }
+
+ return new Inherited(ancestors.ToArray(), reasons.ToArray());
}
}
+
+ public InputBannedReasonEnum[] GetReasonEnums()
+ {
+ return GetReasonEnums(this);
+ }
+
+ private static InputBannedReasonEnum[] GetReasonEnums(Offender offender)
+ {
+ List reasonEnums = [];
+
+ switch (offender.Offense)
+ {
+ case RoundDisruption rd:
+ switch (rd.Method)
+ {
+ case RoundDisruptionMethod.DidNotConfirm:
+ reasonEnums.Add(InputBannedReasonEnum.RoundDisruptionMethodDidNotConfirm);
+ break;
+
+ case RoundDisruptionMethod.DidNotSignalReadyToSign:
+ reasonEnums.Add(InputBannedReasonEnum.RoundDisruptionMethodDidNotSignalReadyToSign);
+ break;
+
+ case RoundDisruptionMethod.DidNotSign:
+ reasonEnums.Add(InputBannedReasonEnum.RoundDisruptionMethodDidNotSign);
+ break;
+
+ case RoundDisruptionMethod.DoubleSpent:
+ reasonEnums.Add(InputBannedReasonEnum.RoundDisruptionMethodDoubleSpent);
+ break;
+
+ default:
+ throw new NotImplementedException("Unknown round disruption method.");
+ }
+ break;
+
+ case BackendStabilitySafety:
+ reasonEnums.Add(InputBannedReasonEnum.BackendStabilitySafety);
+ break;
+
+ case FailedToVerify ftv:
+ if (string.Equals(ftv.Provider, "local", StringComparison.InvariantCultureIgnoreCase))
+ {
+ reasonEnums.Add(InputBannedReasonEnum.LocalCoinVerifier);
+ }
+ else
+ {
+ reasonEnums.Add(InputBannedReasonEnum.FailedToVerify);
+ }
+ break;
+
+ case Inherited inherited:
+ reasonEnums.Add(InputBannedReasonEnum.Inherited);
+ reasonEnums.AddRange(inherited.InputBannedReasonEnums);
+ break;
+
+ case Cheating:
+ reasonEnums.Add(InputBannedReasonEnum.Cheating);
+ break;
+
+ default:
+ throw new NotImplementedException("Unknown offense type.");
+ }
+
+ return reasonEnums.Distinct().ToArray();
+ }
}
diff --git a/WalletWasabi/WabiSabi/Backend/DoSPrevention/Prison.cs b/WalletWasabi/WabiSabi/Backend/DoSPrevention/Prison.cs
index a0b02e6e0d..1b4cff3b9e 100644
--- a/WalletWasabi/WabiSabi/Backend/DoSPrevention/Prison.cs
+++ b/WalletWasabi/WabiSabi/Backend/DoSPrevention/Prison.cs
@@ -5,6 +5,7 @@
using WalletWasabi.Logging;
using WalletWasabi.WabiSabi.Backend.Rounds;
using WalletWasabi.WabiSabi.Backend.Rounds.CoinJoinStorage;
+using WalletWasabi.WabiSabi.Models;
namespace WalletWasabi.WabiSabi.Backend.DoSPrevention;
@@ -20,7 +21,7 @@ public Prison(ICoinJoinIdStore coinJoinIdStore, IEnumerable offenders,
private ICoinJoinIdStore CoinJoinIdStore { get; }
private ChannelWriter NotificationChannelWriter { get; }
private Dictionary> OffendersByTxId { get; }
- private Dictionary BanningTimeCache { get; } = new();
+ private Dictionary BanningTimeCache { get; } = new();
/// Lock object to guard and
private object Lock { get; } = new();
@@ -34,8 +35,8 @@ public void FailedToConfirm(OutPoint outPoint, Money value, uint256 roundId) =>
public void FailedToSign(OutPoint outPoint, Money value, uint256 roundId) =>
Punish(new Offender(outPoint, DateTimeOffset.UtcNow, new RoundDisruption(roundId, value, RoundDisruptionMethod.DidNotSign)));
- public void FailedVerification(OutPoint outPoint, uint256 roundId) =>
- Punish(new Offender(outPoint, DateTimeOffset.UtcNow, new FailedToVerify(roundId)));
+ public void FailedVerification(OutPoint outPoint, uint256 roundId, TimeSpan recommendedBanTime, string provider) =>
+ Punish(new Offender(outPoint, DateTimeOffset.UtcNow, new FailedToVerify(roundId, recommendedBanTime, provider)));
public void CheatingDetected(OutPoint outPoint, uint256 roundId) =>
Punish(new Offender(outPoint, DateTimeOffset.UtcNow, new Cheating(roundId)));
@@ -46,16 +47,16 @@ public void DoubleSpent(OutPoint outPoint, Money value, uint256 roundId) =>
public void DoubleSpent(OutPoint outPoint, Money value, IEnumerable roundIds) =>
Punish(new Offender(outPoint, DateTimeOffset.UtcNow, new RoundDisruption(roundIds, value, RoundDisruptionMethod.DoubleSpent)));
- public void InheritPunishment(OutPoint outpoint, OutPoint[] ancestors) =>
- Punish(new Offender(outpoint, DateTimeOffset.UtcNow, new Inherited(ancestors)));
+ public void InheritPunishment(OutPoint outpoint, OutPoint[] outPoints, InputBannedReasonEnum[] inputBannedReasonEnum) =>
+ Punish(new Offender(outpoint, DateTimeOffset.UtcNow, new Inherited(outPoints, inputBannedReasonEnum)));
public void FailedToSignalReadyToSign(OutPoint outPoint, Money value, uint256 roundId) =>
Punish(new Offender(outPoint, DateTimeOffset.UtcNow, new RoundDisruption(roundId, value, RoundDisruptionMethod.DidNotSignalReadyToSign)));
public bool IsBanned(OutPoint outpoint, DoSConfiguration configuration, DateTimeOffset when) =>
- GetBanTimePeriod(outpoint, configuration).Includes(when);
+ GetBan(outpoint, configuration).BanningTime.Includes(when);
- public TimeFrame GetBanTimePeriod(OutPoint outpoint, DoSConfiguration configuration)
+ public BanItem GetBan(OutPoint outpoint, DoSConfiguration configuration)
{
TimeFrame EffectiveMinTimeFrame(TimeFrame banningPeriod) =>
banningPeriod.Duration < configuration.MinTimeInPrison
@@ -64,6 +65,11 @@ TimeFrame EffectiveMinTimeFrame(TimeFrame banningPeriod) =>
TimeSpan CalculatePunishment(Offender offender, RoundDisruption disruption)
{
+ if (configuration.SimplifiedPunishment > TimeSpan.Zero)
+ {
+ return configuration.SimplifiedPunishment;
+ }
+
var basePunishmentInHours = configuration.SeverityInBitcoinsPerHour / disruption.Value.ToDecimal(MoneyUnit.BTC);
IReadOnlyList offenderHistory;
@@ -102,7 +108,7 @@ TimeSpan CalculatePunishment(Offender offender, RoundDisruption disruption)
TimeFrame CalculatePunishmentInheritance(OutPoint[] ancestors)
{
var banningTimeFrame = ancestors
- .Select(a => (Ancestor: a, BanningTime: GetBanTimePeriod(a, configuration)))
+ .Select(a => (Ancestor: a, BanningTime: GetBan(a, configuration).BanningTime))
.MaxBy(x => x.BanningTime.EndTime)
.BanningTime;
return new TimeFrame(banningTimeFrame.StartTime, banningTimeFrame.Duration / 2);
@@ -125,18 +131,20 @@ TimeFrame CalculatePunishmentInheritance(OutPoint[] ancestors)
{
null => TimeFrame.Zero,
{ Offense: BackendStabilitySafety } => new TimeFrame(offender.StartedTime, configuration.MinTimeInPrison + TimeSpan.FromHours(new Random().Next(0, 4))),
- { Offense: FailedToVerify } => new TimeFrame(offender.StartedTime, configuration.MinTimeForFailedToVerify),
+ { Offense: FailedToVerify offense } => new TimeFrame(offender.StartedTime, offense.RecommendedBanTime > TimeSpan.Zero ? offense.RecommendedBanTime : configuration.MinTimeForFailedToVerify),
{ Offense: Cheating } => new TimeFrame(offender.StartedTime, configuration.MinTimeForCheating),
{ Offense: RoundDisruption offense } => new TimeFrame(offender.StartedTime, CalculatePunishment(offender, offense)),
{ Offense: Inherited { Ancestors: { } ancestors } } => CalculatePunishmentInheritance(ancestors),
_ => throw new NotSupportedException("Unknown offense type.")
});
+ var result = new BanItem(banningTime, offender?.GetReasonEnums() ?? []);
+
lock (Lock)
{
- BanningTimeCache[outpoint] = banningTime;
+ BanningTimeCache[outpoint] = result;
}
- return banningTime;
+ return result;
}
private void Punish(Offender offender)
@@ -165,3 +173,11 @@ public int GetCount()
return OffendersByTxId.Count;
}
}
+
+public record BanItem(TimeFrame BanningTime, InputBannedReasonEnum[] Reasons)
+{
+ public override string ToString()
+ {
+ return $"BannItem {{ BanningTime duration = {BanningTime.Duration}, Reasons = '{string.Join(",", Reasons.Select(r => r.ToString()))}' }}";
+ }
+}
diff --git a/WalletWasabi/WabiSabi/Backend/Models/InputBannedExceptionData.cs b/WalletWasabi/WabiSabi/Backend/Models/InputBannedExceptionData.cs
index 236edc6709..5981d202b4 100644
--- a/WalletWasabi/WabiSabi/Backend/Models/InputBannedExceptionData.cs
+++ b/WalletWasabi/WabiSabi/Backend/Models/InputBannedExceptionData.cs
@@ -1,3 +1,7 @@
+using WalletWasabi.WabiSabi.Models;
+
namespace WalletWasabi.WabiSabi.Backend.Models;
-public record InputBannedExceptionData(DateTimeOffset BannedUntil) : ExceptionData;
+public record InputBannedExceptionData(
+ DateTimeOffset BannedUntil,
+ InputBannedReasonEnum[]? InputBannedReasonEnums = null) : ExceptionData;
diff --git a/WalletWasabi/WabiSabi/Backend/Rounds/Arena.Partial.cs b/WalletWasabi/WabiSabi/Backend/Rounds/Arena.Partial.cs
index 7f00c254ba..fb8bb23124 100644
--- a/WalletWasabi/WabiSabi/Backend/Rounds/Arena.Partial.cs
+++ b/WalletWasabi/WabiSabi/Backend/Rounds/Arena.Partial.cs
@@ -422,11 +422,12 @@ public async Task GetRecommendationAsync(RoundRecom
private void CheckCoinIsNotBanned(OutPoint input, Round round)
{
- var banningTime = Prison.GetBanTimePeriod(input, Config.GetDoSConfiguration());
- if (banningTime.Includes(DateTimeOffset.UtcNow))
+ var bannItem = Prison.GetBan(input, Config.GetDoSConfiguration());
+
+ if (bannItem.BanningTime.Includes(DateTimeOffset.UtcNow))
{
- round.LogInfo($"{input} rejected. Banned until {banningTime.EndTime}");
- throw new WabiSabiProtocolException(WabiSabiProtocolErrorCode.InputBanned, exceptionData: new InputBannedExceptionData(banningTime.EndTime));
+ round.LogInfo($"{input} rejected. {bannItem}");
+ throw new WabiSabiProtocolException(WabiSabiProtocolErrorCode.InputBanned, exceptionData: new InputBannedExceptionData(bannItem.BanningTime.EndTime, bannItem.Reasons));
}
}
diff --git a/WalletWasabi/WabiSabi/Backend/Rounds/Arena.cs b/WalletWasabi/WabiSabi/Backend/Rounds/Arena.cs
index 2f85d0313f..689392f66d 100644
--- a/WalletWasabi/WabiSabi/Backend/Rounds/Arena.cs
+++ b/WalletWasabi/WabiSabi/Backend/Rounds/Arena.cs
@@ -758,14 +758,14 @@ private Script GetCoordinatorScriptPreventReuse(Round round)
return coordinatorScriptPubKey;
}
- private void CoinVerifier_CoinBlacklisted(object? _, Coin coin)
+ private void CoinVerifier_CoinBlacklisted(object? _, CoinVerifier.CoinBlacklistedEventArgs args)
{
// For logging reason Prison needs the roundId.
- var roundState = RoundStates.FirstOrDefault(rs => rs.CoinjoinState.Inputs.Any(input => input.Outpoint == coin.Outpoint));
+ var roundState = RoundStates.FirstOrDefault(rs => rs.CoinjoinState.Inputs.Any(input => input.Outpoint == args.Coin.Outpoint));
// Could be a coin from WW1.
var roundId = roundState?.Id ?? uint256.Zero;
- Prison.FailedVerification(coin.Outpoint, roundId);
+ Prison.FailedVerification(args.Coin.Outpoint, roundId, args.RecommendedBanTime, args.Provider);
}
private void AddRound(Round round)
diff --git a/WalletWasabi/WabiSabi/Backend/Rounds/RoundParameters.cs b/WalletWasabi/WabiSabi/Backend/Rounds/RoundParameters.cs
index 2b8172d1fa..c3d7d02641 100644
--- a/WalletWasabi/WabiSabi/Backend/Rounds/RoundParameters.cs
+++ b/WalletWasabi/WabiSabi/Backend/Rounds/RoundParameters.cs
@@ -1,7 +1,6 @@
using System.Collections.Immutable;
using NBitcoin;
using NBitcoin.Policy;
-using WalletWasabi.Helpers;
using WalletWasabi.WabiSabi.Models;
using WalletWasabi.WabiSabi.Models.MultipartyTransaction;
diff --git a/WalletWasabi/WabiSabi/Backend/WabiSabiConfig.cs b/WalletWasabi/WabiSabi/Backend/WabiSabiConfig.cs
index 168ab4b58d..aaa110fd73 100644
--- a/WalletWasabi/WabiSabi/Backend/WabiSabiConfig.cs
+++ b/WalletWasabi/WabiSabi/Backend/WabiSabiConfig.cs
@@ -34,6 +34,10 @@ public WabiSabiConfig() : base()
[JsonProperty(PropertyName = "DoSMinTimeForFailedToVerify", DefaultValueHandling = DefaultValueHandling.Populate)]
public TimeSpan DoSMinTimeForFailedToVerify { get; set; } = TimeSpan.FromDays(31);
+ [DefaultValueTimeSpan("1d 0h 0m 0s")]
+ [JsonProperty(PropertyName = "DoSSimplifiedPunishment", DefaultValueHandling = DefaultValueHandling.Populate)]
+ public TimeSpan DoSSimplifiedPunishment { get; set; } = TimeSpan.FromDays(1);
+
[DefaultValueTimeSpan("1d 0h 0m 0s")]
[JsonProperty(PropertyName = "DoSMinTimeForCheating", DefaultValueHandling = DefaultValueHandling.Populate)]
public TimeSpan DoSMinTimeForCheating { get; set; } = TimeSpan.FromDays(1);
@@ -249,6 +253,7 @@ public DoSConfiguration GetDoSConfiguration() =>
new(
SeverityInBitcoinsPerHour: DoSSeverity.ToDecimal(MoneyUnit.BTC),
MinTimeForFailedToVerify: DoSMinTimeForFailedToVerify,
+ SimplifiedPunishment: DoSSimplifiedPunishment,
MinTimeForCheating: DoSMinTimeForCheating,
PenaltyFactorForDisruptingConfirmation: (decimal)DoSPenaltyFactorForDisruptingConfirmation,
PenaltyFactorForDisruptingSignalReadyToSign: (decimal)DoSPenaltyFactorForDisruptingSignalReadyToSign,
diff --git a/WalletWasabi/WabiSabi/Client/Banning/CoinPrison.cs b/WalletWasabi/WabiSabi/Client/Banning/CoinPrison.cs
index dd9af9ccb8..ae5ee16a3f 100644
--- a/WalletWasabi/WabiSabi/Client/Banning/CoinPrison.cs
+++ b/WalletWasabi/WabiSabi/Client/Banning/CoinPrison.cs
@@ -6,14 +6,15 @@
using WalletWasabi.Blockchain.TransactionOutputs;
using WalletWasabi.Helpers;
using WalletWasabi.Logging;
+using WalletWasabi.WabiSabi.Models;
using WalletWasabi.Wallets;
namespace WalletWasabi.WabiSabi.Client.Banning;
public class CoinPrison : IDisposable
{
- // Coins with banning time longer than this will be reduced to a random value between 2 and 4 days.
- private static readonly int MaxDaysToTrustLocalPrison = 4;
+ // Coins with banning time longer than this will be reduced to a random value between 1 and 2 days.
+ private static readonly int MaxDaysToTrustLocalPrison = 2;
public CoinPrison(string filePath)
{
@@ -24,16 +25,16 @@ public CoinPrison(string filePath)
private string FilePath { get; }
private object Lock { get; set; } = new();
- public bool TryGetOrRemoveBannedCoin(SmartCoin coin, [NotNullWhen(true)] out DateTimeOffset? bannedUntil)
+ public bool TryGetOrRemoveBannedCoin(SmartCoin coin, [NotNullWhen(true)] out PrisonedCoinRecord? prisonedCoin)
{
lock (Lock)
{
- bannedUntil = null;
- if (BannedCoins.SingleOrDefault(record => record.Outpoint == coin.Outpoint) is { } record)
+ prisonedCoin = null;
+ if (BannedCoins.SingleOrDefault(x => x.Outpoint == coin.Outpoint) is { } record)
{
if (DateTimeOffset.UtcNow < record.BannedUntil)
{
- bannedUntil = record.BannedUntil;
+ prisonedCoin = record;
return true;
}
RemoveBannedCoinNoLock(coin);
@@ -42,7 +43,7 @@ public bool TryGetOrRemoveBannedCoin(SmartCoin coin, [NotNullWhen(true)] out Dat
}
}
- public void Ban(SmartCoin coin, DateTimeOffset until)
+ public void Ban(SmartCoin coin, DateTimeOffset until, InputBannedReasonEnum[] reasons)
{
lock (Lock)
{
@@ -52,8 +53,9 @@ public void Ban(SmartCoin coin, DateTimeOffset until)
}
until = ReduceBanningTimeIfNeeded(until);
- BannedCoins.Add(new(coin.Outpoint, until));
+ BannedCoins.Add(new(coin.Outpoint, until, reasons));
coin.BannedUntilUtc = until;
+ coin.BanReasons = reasons;
ToFile();
}
}
@@ -86,7 +88,7 @@ private void ToFile()
///
/// Reduces local banning time, which we save to disk, if it's longer than the .
/// This is to avoid saving absurd long banning times like 1-2 years.
- /// With this, the coin will retry to participate in a CJ in every 2-4 days and see if the coin is still banned or not according to the backend.
+ /// With this, the coin will retry to participate in a CJ in every 1-2 days and see if the coin is still banned or not according to the backend.
/// Random values are used for the new banning period so we don't leak information to the coordinator when the coins get released from the local prison.
///
/// Banning time according to the backend.
@@ -144,9 +146,10 @@ public void UpdateWallet(Wallet wallet)
{
foreach (var coin in wallet.Coins)
{
- if (TryGetOrRemoveBannedCoin(coin, out var bannedUntil))
+ if (TryGetOrRemoveBannedCoin(coin, out var prisonedCoin))
{
- coin.BannedUntilUtc = bannedUntil;
+ coin.BannedUntilUtc = prisonedCoin.BannedUntil;
+ coin.BanReasons = prisonedCoin.Reasons;
}
}
}
diff --git a/WalletWasabi/WabiSabi/Client/Banning/PrisonedCoinRecord.cs b/WalletWasabi/WabiSabi/Client/Banning/PrisonedCoinRecord.cs
index 0bd5402475..7207c4d5cc 100644
--- a/WalletWasabi/WabiSabi/Client/Banning/PrisonedCoinRecord.cs
+++ b/WalletWasabi/WabiSabi/Client/Banning/PrisonedCoinRecord.cs
@@ -1,15 +1,17 @@
using NBitcoin;
using Newtonsoft.Json;
using WalletWasabi.JsonConverters;
+using WalletWasabi.WabiSabi.Models;
namespace WalletWasabi.WabiSabi.Client.Banning;
public record PrisonedCoinRecord
{
- public PrisonedCoinRecord(OutPoint outpoint, DateTimeOffset bannedUntil)
+ public PrisonedCoinRecord(OutPoint outpoint, DateTimeOffset bannedUntil, InputBannedReasonEnum[] reasons)
{
Outpoint = outpoint;
BannedUntil = bannedUntil;
+ Reasons = reasons;
}
[JsonProperty]
@@ -17,4 +19,5 @@ public PrisonedCoinRecord(OutPoint outpoint, DateTimeOffset bannedUntil)
public OutPoint Outpoint { get; set; }
public DateTimeOffset BannedUntil { get; set; }
+ public InputBannedReasonEnum[] Reasons { get; set; }
}
diff --git a/WalletWasabi/WabiSabi/Client/CoinJoin/Client/CoinJoinClient.cs b/WalletWasabi/WabiSabi/Client/CoinJoin/Client/CoinJoinClient.cs
index e73c314717..5d63d317ee 100644
--- a/WalletWasabi/WabiSabi/Client/CoinJoin/Client/CoinJoinClient.cs
+++ b/WalletWasabi/WabiSabi/Client/CoinJoin/Client/CoinJoinClient.cs
@@ -528,7 +528,8 @@ public async Task StartRoundAsync(IEnumerable smartCo
Logger.LogError($"{nameof(InputBannedExceptionData)} is missing.");
}
var bannedUntil = inputBannedExData?.BannedUntil ?? DateTimeOffset.UtcNow + TimeSpan.FromDays(1);
- CoinJoinClientProgress.SafeInvoke(this, new CoinBanned(coin, bannedUntil));
+
+ CoinJoinClientProgress.SafeInvoke(this, new CoinBanned(coin, bannedUntil, inputBannedExData?.InputBannedReasonEnums ?? []));
roundState.LogInfo($"{coin.Coin.Outpoint} is banned until {bannedUntil}.");
break;
diff --git a/WalletWasabi/WabiSabi/Client/CoinJoin/Client/CoinJoinProgressEvents/CoinBanned.cs b/WalletWasabi/WabiSabi/Client/CoinJoin/Client/CoinJoinProgressEvents/CoinBanned.cs
index bc424c52cd..ea6fede33f 100644
--- a/WalletWasabi/WabiSabi/Client/CoinJoin/Client/CoinJoinProgressEvents/CoinBanned.cs
+++ b/WalletWasabi/WabiSabi/Client/CoinJoin/Client/CoinJoinProgressEvents/CoinBanned.cs
@@ -1,15 +1,18 @@
using WalletWasabi.Blockchain.TransactionOutputs;
+using WalletWasabi.WabiSabi.Models;
namespace WalletWasabi.WabiSabi.Client.CoinJoinProgressEvents;
public class CoinBanned : CoinJoinProgressEventArgs
{
- public CoinBanned(SmartCoin coin, DateTimeOffset banUntilUtc)
+ public CoinBanned(SmartCoin coin, DateTimeOffset banUntilUtc, InputBannedReasonEnum[] reasons)
{
Coin = coin;
BanUntilUtc = banUntilUtc;
+ Reasons = reasons;
}
public SmartCoin Coin { get; }
public DateTimeOffset BanUntilUtc { get; }
+ public InputBannedReasonEnum[] Reasons { get; }
}
diff --git a/WalletWasabi/WabiSabi/Client/CoinJoin/Client/CoinSelectionStatistics.cs b/WalletWasabi/WabiSabi/Client/CoinJoin/Client/CoinSelectionStatistics.cs
index c83c7e8210..f8bf12594d 100644
--- a/WalletWasabi/WabiSabi/Client/CoinJoin/Client/CoinSelectionStatistics.cs
+++ b/WalletWasabi/WabiSabi/Client/CoinJoin/Client/CoinSelectionStatistics.cs
@@ -1,9 +1,5 @@
using NBitcoin;
using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using System.Transactions;
namespace WalletWasabi.WabiSabi.Client.CoinJoin.Client;
diff --git a/WalletWasabi/WabiSabi/Client/CoinJoin/Client/DependencyGraphTaskScheduler.cs b/WalletWasabi/WabiSabi/Client/CoinJoin/Client/DependencyGraphTaskScheduler.cs
index d4207ca595..b9b8e61cc2 100644
--- a/WalletWasabi/WabiSabi/Client/CoinJoin/Client/DependencyGraphTaskScheduler.cs
+++ b/WalletWasabi/WabiSabi/Client/CoinJoin/Client/DependencyGraphTaskScheduler.cs
@@ -8,7 +8,6 @@
using WalletWasabi.Crypto;
using WalletWasabi.Helpers;
using WalletWasabi.Logging;
-using WalletWasabi.Userfacing.Bip21;
using WalletWasabi.WabiSabi.Backend.Models;
using WalletWasabi.WabiSabi.Client.CredentialDependencies;
diff --git a/WalletWasabi/WabiSabi/Client/CoinJoin/Manager/CoinJoinManager.cs b/WalletWasabi/WabiSabi/Client/CoinJoin/Manager/CoinJoinManager.cs
index 56595772e0..de89474e08 100644
--- a/WalletWasabi/WabiSabi/Client/CoinJoin/Manager/CoinJoinManager.cs
+++ b/WalletWasabi/WabiSabi/Client/CoinJoin/Manager/CoinJoinManager.cs
@@ -572,7 +572,7 @@ private async Task HandleCoinJoinFinalizationAsync(CoinJoinTracker finishedCoinJ
{
foreach (var info in finishedCoinJoin.BannedCoins)
{
- CoinPrison.Ban(info.Coin, info.BanUntilUtc);
+ CoinPrison.Ban(info.Coin, info.BanUntilUtc, info.Reasons);
}
}
diff --git a/WalletWasabi/WabiSabi/Client/IKeyChain.cs b/WalletWasabi/WabiSabi/Client/IKeyChain.cs
index ef3b064785..1fc1a4c31f 100644
--- a/WalletWasabi/WabiSabi/Client/IKeyChain.cs
+++ b/WalletWasabi/WabiSabi/Client/IKeyChain.cs
@@ -1,4 +1,3 @@
-using System.Collections.Generic;
using NBitcoin;
using WalletWasabi.Blockchain.Keys;
using WalletWasabi.Crypto;
@@ -10,4 +9,6 @@ public interface IKeyChain
OwnershipProof GetOwnershipProof(IDestination destination, CoinJoinInputCommitmentData committedData);
Transaction Sign(Transaction transaction, Coin coin, PrecomputedTransactionData precomputeTransactionData);
+
+ public string SignMessage(string message, HdPubKey hdPubKey);
}
diff --git a/WalletWasabi/WabiSabi/Client/KeyChain.cs b/WalletWasabi/WabiSabi/Client/KeyChain.cs
index 327505e3bf..f8d923fd23 100644
--- a/WalletWasabi/WabiSabi/Client/KeyChain.cs
+++ b/WalletWasabi/WabiSabi/Client/KeyChain.cs
@@ -61,4 +61,9 @@ public Transaction Sign(Transaction transaction, Coin coin, PrecomputedTransacti
return transaction;
}
+
+ public string SignMessage(string message, HdPubKey hdPubKey)
+ {
+ return KeyManager.SignMessage(Kitchen.SaltSoup(), message, hdPubKey, true, KeyManagerExtension.LegacyHeaderStyle.Standard, false);
+ }
}
diff --git a/WalletWasabi/WabiSabi/Client/RoundStateAwaiters/RoundStateHolder.cs b/WalletWasabi/WabiSabi/Client/RoundStateAwaiters/RoundStateHolder.cs
index 00d64df23a..843a388b31 100644
--- a/WalletWasabi/WabiSabi/Client/RoundStateAwaiters/RoundStateHolder.cs
+++ b/WalletWasabi/WabiSabi/Client/RoundStateAwaiters/RoundStateHolder.cs
@@ -7,8 +7,6 @@
using WalletWasabi.WabiSabi.Models.MultipartyTransaction;
using WalletWasabi.WabiSabi.Models.Serialization;
using WalletWasabi.WabiSabi.Backend.Rounds;
-using NBitcoin;
-using WalletWasabi.WabiSabi.Crypto;
namespace WalletWasabi.WabiSabi.Client.RoundStateAwaiters;
diff --git a/WalletWasabi/WabiSabi/Models/InputBannedReasonEnum.cs b/WalletWasabi/WabiSabi/Models/InputBannedReasonEnum.cs
new file mode 100644
index 0000000000..fcd4dfe8c9
--- /dev/null
+++ b/WalletWasabi/WabiSabi/Models/InputBannedReasonEnum.cs
@@ -0,0 +1,36 @@
+using WalletWasabi.Models;
+
+namespace WalletWasabi.WabiSabi.Models;
+
+public enum InputBannedReasonEnum
+{
+ [FriendlyName(isLocalized: true)]
+ Unknown,
+
+ [FriendlyName(isLocalized: true)]
+ BackendStabilitySafety,
+
+ [FriendlyName(isLocalized: true)]
+ FailedToVerify,
+
+ [FriendlyName(isLocalized: true)]
+ Cheating,
+
+ [FriendlyName(isLocalized: true)]
+ Inherited,
+
+ [FriendlyName(isLocalized: true)]
+ RoundDisruptionMethodDidNotConfirm,
+
+ [FriendlyName(isLocalized: true)]
+ RoundDisruptionMethodDidNotSign,
+
+ [FriendlyName(isLocalized: true)]
+ RoundDisruptionMethodDoubleSpent,
+
+ [FriendlyName(isLocalized: true)]
+ RoundDisruptionMethodDidNotSignalReadyToSign,
+
+ [FriendlyName(isLocalized: true)]
+ LocalCoinVerifier
+}
diff --git a/WalletWasabi/WabiSabi/Models/ReadyToSignRequestRequest.cs b/WalletWasabi/WabiSabi/Models/ReadyToSignRequestRequest.cs
index 7b485ddc2d..fe2bdb0c47 100644
--- a/WalletWasabi/WabiSabi/Models/ReadyToSignRequestRequest.cs
+++ b/WalletWasabi/WabiSabi/Models/ReadyToSignRequestRequest.cs
@@ -1,4 +1,3 @@
-using System.Linq;
using NBitcoin;
using Newtonsoft.Json;
diff --git a/WalletWasabi/WabiSabi/Models/RoundState.cs b/WalletWasabi/WabiSabi/Models/RoundState.cs
index f0490a5770..537638597b 100644
--- a/WalletWasabi/WabiSabi/Models/RoundState.cs
+++ b/WalletWasabi/WabiSabi/Models/RoundState.cs
@@ -1,7 +1,6 @@
using NBitcoin;
using WabiSabi.Crypto;
using WabiSabi.Crypto.Randomness;
-using WalletWasabi.Helpers;
using WalletWasabi.WabiSabi.Backend.Rounds;
using WalletWasabi.WabiSabi.Models.MultipartyTransaction;
using CredentialIssuerParameters = WabiSabi.Crypto.CredentialIssuerParameters;
diff --git a/WalletWasabi/WabiSabi/WabiSabiCoordinator.cs b/WalletWasabi/WabiSabi/WabiSabiCoordinator.cs
index ad52dd19b7..7393044906 100644
--- a/WalletWasabi/WabiSabi/WabiSabiCoordinator.cs
+++ b/WalletWasabi/WabiSabi/WabiSabiCoordinator.cs
@@ -119,18 +119,25 @@ public void BanDescendant(object? sender, Block block)
{
var now = DateTimeOffset.UtcNow;
- bool IsInputBanned(TxIn input) => Warden.Prison.IsBanned(input.PrevOut, Config.GetDoSConfiguration(), now);
- OutPoint[] BannedInputs(Transaction tx) => tx.Inputs.Where(IsInputBanned).Select(x => x.PrevOut).ToArray();
+ bool IsInputBanned(BanItem banItem) => banItem.BanningTime.Includes(now);
+ BanItem GetBan(TxIn input) => Warden.Prison.GetBan(input.PrevOut, Config.GetDoSConfiguration());
+
+ BanItem[] BannedInputs(Transaction tx) =>
+ tx.Inputs
+ .Select(GetBan)
+ .Where(IsInputBanned)
+ .Where(b => b.Reasons.Contains(Models.InputBannedReasonEnum.RoundDisruptionMethodDoubleSpent))
+ .ToArray();
var outpointsToBan = block.Transactions
.Where(tx => !DescendantCoinJoinCheck(tx)) // We don't ban coinjoin outputs
.Select(tx => (Tx: tx, BannedInputs: BannedInputs(tx)))
.Where(x => x.BannedInputs.Length != 0)
- .SelectMany(x => x.Tx.Outputs.Select((_, i) => (new OutPoint(x.Tx, i), x.BannedInputs)));
+ .SelectMany(x => x.Tx.Outputs.Select((_, i) => (new OutPoint(x.Tx, i), x.Tx.Inputs.Select(tx => tx.PrevOut).ToArray(), x.BannedInputs)));
- foreach (var (outpoint, ancestors) in outpointsToBan)
+ foreach (var (outpoint, ancestors, banItems) in outpointsToBan)
{
- Warden.Prison.InheritPunishment(outpoint, ancestors);
+ Warden.Prison.InheritPunishment(outpoint, ancestors, banItems.SelectMany(bi => bi.Reasons).ToArray());
}
}
diff --git a/WalletWasabi/Wallets/P2PNodesManager.cs b/WalletWasabi/Wallets/P2PNodesManager.cs
index 4f0723144d..497b390cb9 100644
--- a/WalletWasabi/Wallets/P2PNodesManager.cs
+++ b/WalletWasabi/Wallets/P2PNodesManager.cs
@@ -1,7 +1,7 @@
-using System.Threading;
-using System.Threading.Tasks;
using NBitcoin;
using NBitcoin.Protocol;
+using System.Threading;
+using System.Threading.Tasks;
using WabiSabi.Crypto.Randomness;
using WalletWasabi.Extensions;
using WalletWasabi.Helpers;
@@ -26,18 +26,30 @@ public P2PNodesManager(Network network, NodesGroup nodes, bool isTorEnabled)
public async Task GetNodeAsync(CancellationToken cancellationToken)
{
- while (Nodes.ConnectedNodes.Count == 0)
+ do
{
- await Task.Delay(100, cancellationToken).ConfigureAwait(false);
- }
+ if (Nodes.ConnectedNodes.Count > 0)
+ {
+ var node = Nodes.ConnectedNodes.RandomElement(SecureRandom.Instance);
- // Select a random node we are connected to.
- return Nodes.ConnectedNodes.RandomElement(SecureRandom.Instance);
+ if (node is not null && node.IsConnected)
+ {
+ return node;
+ }
+
+ Logger.LogTrace($"Selected node is null or disconnected.");
+ }
+
+ await Task.Delay(TimeSpan.FromMilliseconds(50), cancellationToken).ConfigureAwait(false);
+
+ cancellationToken.ThrowIfCancellationRequested();
+ }
+ while (true);
}
public void DisconnectNodeIfEnoughPeers(Node node, string reason)
{
- if (Nodes.ConnectedNodes.Count > 3)
+ if (Nodes.ConnectedNodes.Count > 5)
{
DisconnectNode(node, reason);
}
diff --git a/WalletWasabi/Wallets/WalletFilterProcessor.cs b/WalletWasabi/Wallets/WalletFilterProcessor.cs
index 3619e6fea4..2db9f021ce 100644
--- a/WalletWasabi/Wallets/WalletFilterProcessor.cs
+++ b/WalletWasabi/Wallets/WalletFilterProcessor.cs
@@ -9,7 +9,6 @@
using WalletWasabi.Blockchain.Keys;
using WalletWasabi.Blockchain.TransactionProcessing;
using WalletWasabi.Blockchain.Transactions;
-using WalletWasabi.Extensions;
using WalletWasabi.Filter;
using WalletWasabi.Logging;
using WalletWasabi.Models;
diff --git a/WalletWasabi/Wallets/WalletManager.cs b/WalletWasabi/Wallets/WalletManager.cs
index ac0471f98c..a4551cd97a 100644
--- a/WalletWasabi/Wallets/WalletManager.cs
+++ b/WalletWasabi/Wallets/WalletManager.cs
@@ -4,7 +4,6 @@
using System.Globalization;
using System.IO;
using System.Linq;
-using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;
using WalletWasabi.Blockchain.Keys;
@@ -404,9 +403,12 @@ public async Task RemoveAndStopAllAsync(CancellationToken cancel)
if (wallet.State >= WalletState.Initialized)
{
var keyManager = wallet.KeyManager;
- string backupWalletFilePath = WalletDirectories.GetWalletFilePaths(Path.GetFileName(keyManager.FilePath)!).walletBackupFilePath;
- keyManager.ToFile(backupWalletFilePath);
- Logger.LogInfo($"{nameof(wallet.KeyManager)} backup saved to `{backupWalletFilePath}`.");
+ if (keyManager.FilePath is not null)
+ {
+ string backupWalletFilePath = WalletDirectories.GetWalletFilePaths(Path.GetFileName(keyManager.FilePath)!).walletBackupFilePath;
+ keyManager.ToFile(backupWalletFilePath);
+ Logger.LogInfo($"{nameof(wallet.KeyManager)} backup saved to `{backupWalletFilePath}`.");
+ }
await wallet.StopAsync(cancel).ConfigureAwait(false);
Logger.LogInfo($"'{wallet.WalletName}' wallet is stopped.");
}
diff --git a/WalletWasabi/WebClients/Bitstamp/BitstampExchangeRateProvider.cs b/WalletWasabi/WebClients/Bitstamp/BitstampExchangeRateProvider.cs
index 7e6de2b410..78c5921028 100644
--- a/WalletWasabi/WebClients/Bitstamp/BitstampExchangeRateProvider.cs
+++ b/WalletWasabi/WebClients/Bitstamp/BitstampExchangeRateProvider.cs
@@ -3,7 +3,6 @@
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
-using GingerCommon.Logging;
using WalletWasabi.Backend.Models;
using WalletWasabi.Interfaces;
using WalletWasabi.Tor.Http.Extensions;
diff --git a/WalletWasabi/WebClients/CoinGecko/CoinGeckoExchangeRateProvider.cs b/WalletWasabi/WebClients/CoinGecko/CoinGeckoExchangeRateProvider.cs
index 21c4bd0816..19e82ac9e2 100644
--- a/WalletWasabi/WebClients/CoinGecko/CoinGeckoExchangeRateProvider.cs
+++ b/WalletWasabi/WebClients/CoinGecko/CoinGeckoExchangeRateProvider.cs
@@ -8,7 +8,6 @@
using WalletWasabi.Backend.Models;
using WalletWasabi.Helpers;
using WalletWasabi.Interfaces;
-using WalletWasabi.Logging;
using WalletWasabi.Tor.Http.Extensions;
namespace WalletWasabi.WebClients.CoinGecko;
diff --git a/WalletWasabi/WebClients/Coingate/CoingateExchangeRateProvider.cs b/WalletWasabi/WebClients/Coingate/CoingateExchangeRateProvider.cs
index ca1e21bcb8..af408f4c16 100644
--- a/WalletWasabi/WebClients/Coingate/CoingateExchangeRateProvider.cs
+++ b/WalletWasabi/WebClients/Coingate/CoingateExchangeRateProvider.cs
@@ -2,7 +2,6 @@
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
-using GingerCommon.Logging;
using WalletWasabi.Backend.Models;
using WalletWasabi.Interfaces;
diff --git a/WalletWasabi/WebClients/Gemini/GeminiExchangeRateProvider.cs b/WalletWasabi/WebClients/Gemini/GeminiExchangeRateProvider.cs
index 04cb3d1d35..4b3601fba4 100644
--- a/WalletWasabi/WebClients/Gemini/GeminiExchangeRateProvider.cs
+++ b/WalletWasabi/WebClients/Gemini/GeminiExchangeRateProvider.cs
@@ -3,7 +3,6 @@
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
-using GingerCommon.Logging;
using WalletWasabi.Backend.Models;
using WalletWasabi.Interfaces;
using WalletWasabi.Tor.Http.Extensions;