diff --git a/Directory.Packages.props b/Directory.Packages.props index dfeb759df2..b2b3e09a97 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -6,6 +6,7 @@ + diff --git a/GingerCommon/Crypto/Random/DeterministicRandom.cs b/GingerCommon/Crypto/Random/DeterministicRandom.cs index e8419789e3..795952e8d6 100644 --- a/GingerCommon/Crypto/Random/DeterministicRandom.cs +++ b/GingerCommon/Crypto/Random/DeterministicRandom.cs @@ -42,6 +42,14 @@ public DeterministicRandom(byte[] seed) _s3 = BinaryPrimitives.ReadUInt64LittleEndian(new ReadOnlySpan(seed, 24, 8)); } + public DeterministicRandom(ReadOnlySpan seed) + { + _s0 = BinaryPrimitives.ReadUInt64LittleEndian(seed); + _s1 = BinaryPrimitives.ReadUInt64LittleEndian(seed[8..]); + _s2 = BinaryPrimitives.ReadUInt64LittleEndian(seed[16..]); + _s3 = BinaryPrimitives.ReadUInt64LittleEndian(seed[24..]); + } + public override void GetBytes(Span buffer) { int idx = 0, sizeFull = buffer.Length, size8 = sizeFull & ~7; diff --git a/GingerCommon/Crypto/Random/GingerRandom.cs b/GingerCommon/Crypto/Random/GingerRandom.cs index 95bbc2eeda..205d912a88 100644 --- a/GingerCommon/Crypto/Random/GingerRandom.cs +++ b/GingerCommon/Crypto/Random/GingerRandom.cs @@ -50,4 +50,19 @@ public virtual long GetInt64(long fromInclusive, long toExclusive) return fromInclusive + (long)high; } + + public string GetString(int len, string chars) + { + if (string.IsNullOrEmpty(chars)) + { + throw new ArgumentException($"{nameof(chars)} is empty"); + } + + Span rndChars = len <= 256 ? stackalloc char[len] : new char[len]; + for (int idx = 0; idx < len; idx++) + { + rndChars[idx] = chars[GetInt(0, chars.Length)]; + } + return new string(rndChars); + } } diff --git a/GingerCommon/Crypto/Random/RandomExtensions.cs b/GingerCommon/Crypto/Random/RandomExtensions.cs index e670106ae6..22f5f4b893 100644 --- a/GingerCommon/Crypto/Random/RandomExtensions.cs +++ b/GingerCommon/Crypto/Random/RandomExtensions.cs @@ -1,4 +1,7 @@ +using System; using System.Collections.Generic; +using System.Security.Cryptography; +using System.Text; namespace GingerCommon.Crypto.Random; @@ -17,4 +20,14 @@ public static IList Shuffle(this IList list, GingerRandom random) } return list; } + + public static void GenerateSeed(Span seed, ReadOnlySpan seedArray) + { + SHA256.HashData(seedArray, seed); + } + + public static void GenerateSeed(Span seed, string seedString) + { + SHA256.HashData(Encoding.UTF8.GetBytes(seedString), seed); + } } diff --git a/GingerCommon/Static/JsonUtils.cs b/GingerCommon/Static/JsonUtils.cs index 77c63a031d..97be0da731 100644 --- a/GingerCommon/Static/JsonUtils.cs +++ b/GingerCommon/Static/JsonUtils.cs @@ -1,3 +1,5 @@ +using GingerCommon.Logging; +using System; using System.Text.Json; namespace GingerCommon.Static; @@ -5,4 +7,35 @@ namespace GingerCommon.Static; public static class JsonUtils { public static readonly JsonSerializerOptions OptionCaseInsensitive = new() { PropertyNameCaseInsensitive = true }; + + public static string Serialize(TRequest obj) + { + return Serialize(obj, OptionCaseInsensitive); + } + + public static string Serialize(TRequest obj, JsonSerializerOptions options) + { + try + { + return JsonSerializer.Serialize(obj, options); + } + catch + { + Logger.LogDebug($"Failed to serialize {typeof(TRequest)} from obj '{obj}'"); + throw; + } + } + + public static TResponse Deserialize(string jsonString) + { + try + { + return JsonSerializer.Deserialize(jsonString, OptionCaseInsensitive) ?? throw new InvalidOperationException("Deserialization error"); + } + catch + { + Logger.LogDebug($"Failed to deserialize {typeof(TResponse)} from json '{jsonString}'"); + throw; + } + } } diff --git a/WalletWasabi.Backend/Startup.cs b/WalletWasabi.Backend/Startup.cs index 1bb4d0f336..3d82a8a283 100644 --- a/WalletWasabi.Backend/Startup.cs +++ b/WalletWasabi.Backend/Startup.cs @@ -152,7 +152,7 @@ public virtual void AddExtraServices(IServiceCollection services, string dataDir } [SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "This method gets called by the runtime. Use this method to configure the HTTP request pipeline")] - public void Configure(IApplicationBuilder app, IWebHostEnvironment env, Global global) + public virtual void Configure(IApplicationBuilder app, IWebHostEnvironment env, Global global) { // Enable middleware to serve generated Swagger as a JSON endpoint. app.UseSwagger(); diff --git a/WalletWasabi.Backend/WalletWasabi.Backend.csproj b/WalletWasabi.Backend/WalletWasabi.Backend.csproj index fd9cbf04f8..1fe54abc5c 100644 --- a/WalletWasabi.Backend/WalletWasabi.Backend.csproj +++ b/WalletWasabi.Backend/WalletWasabi.Backend.csproj @@ -3,11 +3,11 @@ true 1701;1702;1705;1591;1573;CA1031;CA1822 - WalletWasabiApi + GingerWalletApi MIT - walletwasabi, wasabiwallet, wasabi, wallet, bitcoin, nbitcoin, tor, zerolink, wabisabi, coinjoin, fungibility, privacy, anonymity + gingerwallet, ginger, wallet, bitcoin, nbitcoin, tor, zerolink, wabisabi, coinjoin, fungibility, privacy, anonymity Git - https://github.com/zkSNACKs/WalletWasabi/ + https://github.com/GingerPrivacy/GingerBackend/ $(MSBuildProjectDirectory)\=WalletWasabi.Backend Linux false @@ -34,6 +34,7 @@ + diff --git a/WalletWasabi.Daemon/Global.cs b/WalletWasabi.Daemon/Global.cs index 52bd191856..3ee3942245 100644 --- a/WalletWasabi.Daemon/Global.cs +++ b/WalletWasabi.Daemon/Global.cs @@ -37,6 +37,7 @@ using WalletWasabi.Models; using WalletWasabi.Daemon.BuySell; using WalletWasabi.Daemon.FeeRateProviders; +using WalletWasabi.SecretHunt; namespace WalletWasabi.Daemon; @@ -91,7 +92,7 @@ public Global(string dataDir, string configFilePath, Config config, UiConfig uiC UpdateManager = new(DataDir, Config.DownloadNewVersion, HttpClientFactory.NewHttpClient(Mode.DefaultCircuit, maximumRedirects: 10), updateChecker); TorStatusChecker = new TorStatusChecker(TimeSpan.FromHours(6), HttpClientFactory.NewHttpClient(Mode.DefaultCircuit)); - RoundStateUpdaterCircuit = new PersonCircuit(); + RoundStateUpdaterCircuit = new(); Cache = new MemoryCache(new MemoryCacheOptions { @@ -189,6 +190,7 @@ public Global(string dataDir, string configFilePath, Config config, UiConfig uiC public Uri? OnionServiceUri { get; private set; } private PersonCircuit RoundStateUpdaterCircuit { get; } + private AllTransactionStore AllTransactionStore { get; } private IndexStore IndexStore { get; } @@ -405,6 +407,8 @@ private void RegisterCoinJoinComponents() Tor.Http.IHttpClient roundStateUpdaterHttpClient = CoordinatorHttpClientFactory.NewHttpClient(Mode.SingleCircuitPerLifetime, RoundStateUpdaterCircuit); HostedServices.Register(() => new RoundStateUpdater(TimeSpan.FromSeconds(0.1), allowedCoordinationIdentifiers, new WabiSabiHttpApiClient(roundStateUpdaterHttpClient)), "Round info updater"); + HostedServices.Register(() => new SecretHuntUpdater(WalletManager, TimeSpan.FromSeconds(0.1), CoordinatorHttpClientFactory, HostedServices.Get()), "Secret Hunt updater"); + var coinJoinConfiguration = new CoinJoinConfiguration(Config.CoordinatorIdentifier, Config.MaxCoordinationFeeRate, Config.MaxCoinjoinMiningFeeRate, Config.AbsoluteMinInputCount, AllowSoloCoinjoining: false); HostedServices.Register(() => new CoinJoinManager(WalletManager, HostedServices.Get(), CoordinatorHttpClientFactory, HostedServices.Get(), coinJoinConfiguration, CoinPrison), "CoinJoin Manager"); } diff --git a/WalletWasabi.Fluent/Extensions/DateTimeExtensions.cs b/WalletWasabi.Fluent/Extensions/DateTimeExtensions.cs index ada0bed4fb..3db9f3b1c1 100644 --- a/WalletWasabi.Fluent/Extensions/DateTimeExtensions.cs +++ b/WalletWasabi.Fluent/Extensions/DateTimeExtensions.cs @@ -9,6 +9,11 @@ public static string ToUserFacingString(this DateTime value, bool withTime = tru return value.ToString(withTime ? "HH:mm MMMM d, yyyy" : "MMMM d, yyyy", Resources.Culture); } + public static string ToUserFacingStringFixLength(this DateTime value, bool withTime = true) + { + return value.ToString(withTime ? "HH:mm MMM dd, yyyy" : "MMM dd, yyyy", Resources.Culture); + } + public static string ToUserFacingFriendlyString(this DateTime value) { if (value.Date == DateTime.Today) diff --git a/WalletWasabi.Fluent/Extensions/DateTimeOffsetExtensions.cs b/WalletWasabi.Fluent/Extensions/DateTimeOffsetExtensions.cs index ebc5d29884..87c488444d 100644 --- a/WalletWasabi.Fluent/Extensions/DateTimeOffsetExtensions.cs +++ b/WalletWasabi.Fluent/Extensions/DateTimeOffsetExtensions.cs @@ -6,6 +6,8 @@ public static class DateTimeOffsetExtensions { public static string ToUserFacingString(this DateTimeOffset value, bool withTime = true) => value.DateTime.ToUserFacingString(withTime); + public static string ToUserFacingStringFixLength(this DateTimeOffset value, bool withTime = true) => value.DateTime.ToUserFacingStringFixLength(withTime); + public static string ToUserFacingFriendlyString(this DateTimeOffset value) => value.DateTime.ToUserFacingFriendlyString(); public static string ToOnlyTimeString(this DateTimeOffset value) => value.ToString("HH:mm", CultureInfo.InvariantCulture); diff --git a/WalletWasabi.Fluent/HomeScreen/Wallets/ViewModels/WalletViewModel.cs b/WalletWasabi.Fluent/HomeScreen/Wallets/ViewModels/WalletViewModel.cs index d4cdf045e6..b4f5fb5dcc 100644 --- a/WalletWasabi.Fluent/HomeScreen/Wallets/ViewModels/WalletViewModel.cs +++ b/WalletWasabi.Fluent/HomeScreen/Wallets/ViewModels/WalletViewModel.cs @@ -131,6 +131,8 @@ public WalletViewModel(WalletModel walletModel, Wallet wallet) SignMessageCommand = ReactiveCommand.Create(() => UiContext.Navigate().To().SignMessage(WalletModel)); + SecretHuntCommand = ReactiveCommand.Create(() => UiContext.Navigate().To().SecretHunt(WalletModel)); + CoinjoinPlayerViewModel = new CoinjoinPlayerViewModel(WalletModel, Settings); Tiles = GetTiles().ToList(); @@ -179,6 +181,8 @@ public WalletViewModel(WalletModel walletModel, Wallet wallet) public ICommand SignMessageCommand { get; private set; } + public ICommand SecretHuntCommand { get; private set; } + public ICommand WalletSettingsCommand { get; private set; } public ICommand WalletStatsCommand { get; private set; } @@ -278,7 +282,8 @@ private ISearchItem CreateSendItem() { SendCommand.ExecuteIfCan(); return Task.CompletedTask; - }, Resources.Wallet, Resources.AboutViewModelKeywords.ToKeywords()) { Icon = "wallet_action_send", IsDefault = true, Priority = 1 }; + }, Resources.Wallet, Resources.AboutViewModelKeywords.ToKeywords()) + { Icon = "wallet_action_send", IsDefault = true, Priority = 1 }; } private IEnumerable GetTiles() diff --git a/WalletWasabi.Fluent/HomeScreen/Wallets/Views/WalletView.axaml b/WalletWasabi.Fluent/HomeScreen/Wallets/Views/WalletView.axaml index 9162eec3a0..6b1dfa58b0 100644 --- a/WalletWasabi.Fluent/HomeScreen/Wallets/Views/WalletView.axaml +++ b/WalletWasabi.Fluent/HomeScreen/Wallets/Views/WalletView.axaml @@ -52,6 +52,15 @@ + + + + + + + + @@ -94,7 +103,6 @@ Width="11" Height="11" Stroke="{DynamicResource RegionBrush}" StrokeThickness="2" Fill="{DynamicResource WarningMessageForeground}" /> - @@ -121,7 +129,6 @@ Width="11" Height="11" Stroke="{DynamicResource RegionBrush}" StrokeThickness="2" Fill="{DynamicResource WarningMessageForeground}" /> - @@ -183,8 +190,6 @@ - - diff --git a/WalletWasabi.Fluent/Models/Wallets/WalletModel.cs b/WalletWasabi.Fluent/Models/Wallets/WalletModel.cs index f51bbe5e53..81d56f8e13 100644 --- a/WalletWasabi.Fluent/Models/Wallets/WalletModel.cs +++ b/WalletWasabi.Fluent/Models/Wallets/WalletModel.cs @@ -8,6 +8,7 @@ using WalletWasabi.Fluent.HomeScreen.BuySell.Models; using WalletWasabi.Fluent.HomeScreen.Labels.Models; using WalletWasabi.Fluent.Models.Transactions; +using WalletWasabi.SecretHunt; using WalletWasabi.Wallets; namespace WalletWasabi.Fluent.Models.Wallets; @@ -146,6 +147,8 @@ public PrivacySuggestionsModel GetPrivacySuggestionsModel(SendFlowModel sendFlow return new PrivacySuggestionsModel(sendFlow); } + public List GetSecretHuntResults() => Wallet.KeyManager.GetSecretHuntEventResultModelList(); + public string SignMessage(string messageToSign, HdPubKey hdPubKey) { return Wallet.KeyChain.SignMessage(messageToSign, hdPubKey); diff --git a/WalletWasabi.Fluent/Models/Wallets/WalletSettingsModel.cs b/WalletWasabi.Fluent/Models/Wallets/WalletSettingsModel.cs index 694dc83b01..7e80921df4 100644 --- a/WalletWasabi.Fluent/Models/Wallets/WalletSettingsModel.cs +++ b/WalletWasabi.Fluent/Models/Wallets/WalletSettingsModel.cs @@ -36,6 +36,7 @@ public partial class WalletSettingsModel : ReactiveObject [AutoNotify] private double _valueLossRateNormal; [AutoNotify] private double _targetCoinCountPerBucket; [AutoNotify] private bool _useOldCoinSelectorAsFallback; + [AutoNotify] private bool _enableSecretHunt; public WalletSettingsModel(KeyManager keyManager, bool isNewWallet = false, bool isCoinJoinPaused = false) { @@ -45,6 +46,7 @@ public WalletSettingsModel(KeyManager keyManager, bool isNewWallet = false, bool _isDirty = isNewWallet; IsCoinJoinPaused = isCoinJoinPaused; + _enableSecretHunt = _keyManager.EnableSecretHunt; _autoCoinjoin = _keyManager.AutoCoinJoin; _isCoinjoinProfileSelected = _keyManager.IsCoinjoinProfileSelected; _preferPsbtWorkflow = _keyManager.PreferPsbtWorkflow; @@ -73,6 +75,7 @@ public WalletSettingsModel(KeyManager keyManager, bool isNewWallet = false, bool WalletType = WalletHelpers.GetType(_keyManager); this.WhenAnyValue( + x => x.EnableSecretHunt, x => x.AutoCoinjoin, x => x.IsCoinjoinProfileSelected, x => x.PreferPsbtWorkflow, @@ -81,7 +84,7 @@ public WalletSettingsModel(KeyManager keyManager, bool isNewWallet = false, bool x => x.RedCoinIsolation, x => x.FeeRateMedianTimeFrameHours, x => x.IsRecovering, - (_, _, _, _, _, _, _, _) => Unit.Default) + (_, _, _, _, _, _, _, _, _) => Unit.Default) .Skip(1) .Do(_ => SetValues()) .Subscribe(); @@ -132,6 +135,7 @@ public WalletId Save() private void SetValues() { + _keyManager.EnableSecretHunt = EnableSecretHunt; _keyManager.AutoCoinJoin = AutoCoinjoin; _keyManager.IsCoinjoinProfileSelected = IsCoinjoinProfileSelected; _keyManager.PreferPsbtWorkflow = PreferPsbtWorkflow; diff --git a/WalletWasabi.Fluent/SecretHunt/ViewModels/SecretHuntItemViewModel.cs b/WalletWasabi.Fluent/SecretHunt/ViewModels/SecretHuntItemViewModel.cs new file mode 100644 index 0000000000..ffc9694c4a --- /dev/null +++ b/WalletWasabi.Fluent/SecretHunt/ViewModels/SecretHuntItemViewModel.cs @@ -0,0 +1,74 @@ +using Avalonia.Controls; +using System.Collections.ObjectModel; +using WalletWasabi.Fluent.Extensions; +using WalletWasabi.SecretHunt; + +namespace WalletWasabi.Fluent.SecretHunt.ViewModels; + +// Using multiple ViewModels in a tree is a pain in the neck in Avalonia, so we don't use it +public class SecretHuntItemViewModel +{ + public SecretHuntItemViewModel(SecretHuntEventResultModel model, int idx) + { + if (idx >= -1) + { + ExtraSecret = idx == -1; + Description = idx == -1 ? (model.ExtraSecret ?? "") : model.Secrets[idx]; + Icon = ExtraSecret ? "double_shield_regular" : "private_key_regular"; + ToolTip = ExtraSecret ? Lang.Resources.SecretHuntExtraSecret : Lang.Resources.SecretHuntSecret; + return; + } + + IsEventItem = true; + Id = model.Id; + StartDate = model.StartDate; + EndDate = model.EndDate; + Description = model.Description; + + if (model.ExtraSecret is not null) + { + Secrets.Add(new(model, -1)); + } + for (int secretIdx = 0, len = model.Secrets.Count; secretIdx < len; secretIdx++) + { + Secrets.Add(new(model, secretIdx)); + } + + UpdateStatus(); + } + + public void UpdateStatus() + { + if (IsEventItem) + { + bool extraSecret = Secrets.Count > 0 && Secrets[0].ExtraSecret; + Icon = extraSecret ? "checkmark_circle_filled" : "book_question_mark_regular"; + ToolTip = extraSecret ? Lang.Resources.SecretHuntEventSolved : Lang.Resources.SecretHuntEventUnsolved; + } + } + + private static GridLength GridEmpty = new(0); + private static GridLength GridDate = new(100); + + public GridLength GridDateLength => IsEventItem ? GridDate : GridEmpty; + + public string StartDateString => StartDate.ToUserFacingStringFixLength(false); + public string StartDateToolTipString => StartDate.ToUserFacingString(); + public string EndDateString => EndDate.ToUserFacingStringFixLength(false); + public string EndDateToolTipString => EndDate.ToUserFacingString(); + + public bool ExtraSecret { get; set; } = false; + public bool IsEventItem { get; set; } = false; + + public string Icon { get; set; } = ""; + public string ToolTip { get; set; } = ""; + + // Event data + public string Id { get; set; } = ""; + + public DateTimeOffset StartDate { get; set; } = DateTimeOffset.UnixEpoch; + public DateTimeOffset EndDate { get; set; } = DateTimeOffset.UnixEpoch; + public string Description { get; set; } = ""; + + public ObservableCollection Secrets { get; set; } = []; +} diff --git a/WalletWasabi.Fluent/SecretHunt/ViewModels/SecretHuntViewModel.cs b/WalletWasabi.Fluent/SecretHunt/ViewModels/SecretHuntViewModel.cs new file mode 100644 index 0000000000..9eb3a07002 --- /dev/null +++ b/WalletWasabi.Fluent/SecretHunt/ViewModels/SecretHuntViewModel.cs @@ -0,0 +1,100 @@ +using System.Collections.ObjectModel; +using System.Reactive.Disposables; +using WalletWasabi.Fluent.Models; +using WalletWasabi.Fluent.Models.Wallets; +using WalletWasabi.Fluent.Navigation.ViewModels; +using ReactiveUI; +using System.Reactive.Linq; + +namespace WalletWasabi.Fluent.SecretHunt.ViewModels; + +[NavigationMetaData( + IconName = "nav_wallet_24_regular", + Order = 0, + Category = SearchCategory.Wallet, + NavBarPosition = NavBarPosition.None, + NavigationTarget = NavigationTarget.DialogScreen, + Searchable = false, + IsLocalized = true)] +public partial class SecretHuntViewModel : RoutableViewModel +{ + private readonly CompositeDisposable _disposables = new(); + + private readonly WalletModel _wallet; + + [AutoNotify] private bool _enableSecretHunt; + + public SecretHuntViewModel(WalletModel wallet) + { + SetupCancel(enableCancel: false, enableCancelOnEscape: true, enableCancelOnPressed: true); + NextCommand = CancelCommand; + + _wallet = wallet; + _enableSecretHunt = _wallet.Wallet.KeyManager.EnableSecretHunt; + + this.WhenAnyValue(x => x.EnableSecretHunt) + .Skip(1) + .ObserveOn(RxApp.TaskpoolScheduler) + .Subscribe(x => + { + _wallet.Settings.EnableSecretHunt = x; + _wallet.Settings.Save(); + UpdateTree(); + }); + + TreeDataSource = []; + UpdateTree(); + } + + public void SetValues() + { + _wallet.Wallet.KeyManager.EnableSecretHunt = _enableSecretHunt; + } + + public void UpdateTree() + { + if (!_enableSecretHunt) + { + TreeDataSource.Clear(); + return; + } + + var modelList = _wallet.GetSecretHuntResults(); + for (int idx = 0, len = modelList.Count; idx < len; idx++) + { + var model = modelList[idx]; + SecretHuntItemViewModel? eventItem = null; + if (TreeDataSource.Count <= idx || TreeDataSource[idx].Id != model.Id) + { + eventItem = new(model, -2); + TreeDataSource.Insert(idx, eventItem); + continue; + } + eventItem = TreeDataSource[idx]; + int secretOfs = 0; + if (model.ExtraSecret is not null) + { + if (eventItem.Secrets.Count == 0 || !eventItem.Secrets[0].ExtraSecret) + { + eventItem.Secrets.Insert(0, new(model, -1)); + } + secretOfs = 1; + } + for (int secretIdx = 0, secretLen = model.Secrets.Count; secretIdx < secretLen; secretIdx++) + { + if (eventItem.Secrets.Count <= secretIdx + secretOfs || eventItem.Secrets[secretIdx + secretOfs].Description != model.Secrets[secretIdx]) + { + eventItem.Secrets.Insert(secretIdx + secretOfs, new(model, secretIdx)); + } + } + eventItem.UpdateStatus(); + } + } + + public ObservableCollection TreeDataSource { get; } + + public void Dispose() + { + _disposables.Dispose(); + } +} diff --git a/WalletWasabi.Fluent/SecretHunt/Views/SecretHuntView.axaml b/WalletWasabi.Fluent/SecretHunt/Views/SecretHuntView.axaml new file mode 100644 index 0000000000..b4e9a2575f --- /dev/null +++ b/WalletWasabi.Fluent/SecretHunt/Views/SecretHuntView.axaml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/WalletWasabi.Fluent/SecretHunt/Views/SecretHuntView.axaml.cs b/WalletWasabi.Fluent/SecretHunt/Views/SecretHuntView.axaml.cs new file mode 100644 index 0000000000..a4c71511d6 --- /dev/null +++ b/WalletWasabi.Fluent/SecretHunt/Views/SecretHuntView.axaml.cs @@ -0,0 +1,17 @@ +using Avalonia.Controls; +using Avalonia.Markup.Xaml; + +namespace WalletWasabi.Fluent.SecretHunt.Views; + +public class SecretHuntView : UserControl +{ + public SecretHuntView() + { + InitializeComponent(); + } + + private void InitializeComponent() + { + AvaloniaXamlLoader.Load(this); + } +} diff --git a/WalletWasabi.Tests/Helpers/ArenaTestFactory.cs b/WalletWasabi.Tests/Helpers/ArenaTestFactory.cs index bea9a1d3d6..0de11fd3c1 100644 --- a/WalletWasabi.Tests/Helpers/ArenaTestFactory.cs +++ b/WalletWasabi.Tests/Helpers/ArenaTestFactory.cs @@ -1,3 +1,4 @@ +using GingerCommon.Crypto.Random; using NBitcoin; using System.Threading; using System.Threading.Tasks; @@ -27,12 +28,12 @@ public class ArenaTestFactory public ICoinJoinIdStore? CoinJoinIdStore { get; set; } /// Rounds to initialize with. - public Arena Create(params Round[] rounds) + public Arena Create(GingerRandom rnd, params Round[] rounds) { TimeSpan period = Period ?? TimeSpan.FromHours(1); Prison prison = Prison ?? WabiSabiTestFactory.CreatePrison(); WabiSabiConfig config = Config ?? WabiSabiBackendFactory.Instance.CreateWabiSabiConfig(); - IRPCClient rpc = Rpc ?? WabiSabiTestFactory.CreatePreconfiguredRpcClient(); + IRPCClient rpc = Rpc ?? WabiSabiTestFactory.CreatePreconfiguredRpcClient(rnd); Network network = Network ?? Network.Main; ICoinJoinIdStore coinJoinIdStore = CoinJoinIdStore ?? new CoinJoinIdStore(); RoundParameterFactory roundParameterFactory = RoundParameterFactory ?? CreateRoundParameterFactory(config, network); @@ -47,16 +48,15 @@ public Arena Create(params Round[] rounds) return arena; } - public Task CreateAndStartAsync(params Round[] rounds) - => CreateAndStartAsync(rounds, CancellationToken.None); + public Task CreateAndStartAsync(GingerRandom rnd, params Round[] rounds) => CreateAndStartAsync(rnd, rounds, CancellationToken.None); - public async Task CreateAndStartAsync(Round[] rounds, CancellationToken cancellationToken = default) + public async Task CreateAndStartAsync(GingerRandom rnd, Round[] rounds, CancellationToken cancellationToken = default) { Arena? toDispose = null; try { - toDispose = Create(rounds); + toDispose = Create(rnd, rounds); Arena arena = toDispose; await arena.StartAsync(cancellationToken).ConfigureAwait(false); toDispose = null; diff --git a/WalletWasabi.Tests/Helpers/BitcoinFactory.cs b/WalletWasabi.Tests/Helpers/BitcoinFactory.cs index fce5d26b1b..b1361e461f 100644 --- a/WalletWasabi.Tests/Helpers/BitcoinFactory.cs +++ b/WalletWasabi.Tests/Helpers/BitcoinFactory.cs @@ -11,24 +11,25 @@ using WalletWasabi.Models; using WalletWasabi.Tests.UnitTests; using WalletWasabi.Blockchain.Analysis; +using GingerCommon.Crypto.Random; namespace WalletWasabi.Tests.Helpers; public static class BitcoinFactory { - public static SmartTransaction CreateSmartTransaction(int othersInputCount = 1, int othersOutputCount = 1, int ownInputCount = 0, int ownOutputCount = 0, bool orderByAmount = false) - => CreateSmartTransaction(othersInputCount, Enumerable.Repeat(Money.Coins(1m), othersOutputCount), Enumerable.Repeat((Money.Coins(1.1m), 1), ownInputCount), Enumerable.Repeat((Money.Coins(1m), 1), ownOutputCount), orderByAmount); + public static SmartTransaction CreateSmartTransaction(GingerRandom rnd, int othersInputCount = 1, int othersOutputCount = 1, int ownInputCount = 0, int ownOutputCount = 0, bool orderByAmount = false) + => CreateSmartTransaction(rnd, othersInputCount, Enumerable.Repeat(Money.Coins(1m), othersOutputCount), Enumerable.Repeat((Money.Coins(1.1m), 1), ownInputCount), Enumerable.Repeat((Money.Coins(1m), 1), ownOutputCount), orderByAmount); - public static SmartTransaction CreateSmartTransaction(int othersInputCount, IEnumerable othersOutputs, IEnumerable<(Money value, int anonset)> ownInputs, IEnumerable<(Money value, int anonset)> ownOutputs, bool orderByAmount = false) + public static SmartTransaction CreateSmartTransaction(GingerRandom rnd, int othersInputCount, IEnumerable othersOutputs, IEnumerable<(Money value, int anonset)> ownInputs, IEnumerable<(Money value, int anonset)> ownOutputs, bool orderByAmount = false) { var km = ServiceFactory.CreateKeyManager(); - return CreateSmartTransaction(othersInputCount, othersOutputs, ownInputs.Select(x => (x.value, x.anonset, CreateHdPubKey(km))), ownOutputs.Select(x => (x.value, x.anonset, CreateHdPubKey(km))), orderByAmount); + return CreateSmartTransaction(rnd, othersInputCount, othersOutputs, ownInputs.Select(x => (x.value, x.anonset, CreateHdPubKey(km))), ownOutputs.Select(x => (x.value, x.anonset, CreateHdPubKey(km))), orderByAmount); } - public static SmartTransaction CreateSmartTransaction(int othersInputCount, IEnumerable othersOutputs, IEnumerable<(Money value, int anonset, HdPubKey hdpk)> ownInputs, IEnumerable<(Money value, int anonset, HdPubKey hdpk)> ownOutputs, bool orderByAmount = false) - => CreateSmartTransaction(othersInputCount, othersOutputs.Select(x => new TxOut(x, new Key())), ownInputs, ownOutputs, orderByAmount); + public static SmartTransaction CreateSmartTransaction(GingerRandom rnd, int othersInputCount, IEnumerable othersOutputs, IEnumerable<(Money value, int anonset, HdPubKey hdpk)> ownInputs, IEnumerable<(Money value, int anonset, HdPubKey hdpk)> ownOutputs, bool orderByAmount = false) + => CreateSmartTransaction(rnd, othersInputCount, othersOutputs.Select(x => new TxOut(x, new Key())), ownInputs, ownOutputs, orderByAmount); - public static SmartTransaction CreateSmartTransaction(int othersInputCount, IEnumerable othersOutputs, IEnumerable<(Money value, int anonset, HdPubKey hdpk)> ownInputs, IEnumerable<(Money value, int anonset, HdPubKey hdpk)> ownOutputs, bool orderByAmount) + public static SmartTransaction CreateSmartTransaction(GingerRandom rnd, int othersInputCount, IEnumerable othersOutputs, IEnumerable<(Money value, int anonset, HdPubKey hdpk)> ownInputs, IEnumerable<(Money value, int anonset, HdPubKey hdpk)> ownOutputs, bool orderByAmount) { var tx = Transaction.Create(Network.Main); var walletInputs = new HashSet(); @@ -45,7 +46,7 @@ public static SmartTransaction CreateSmartTransaction(int othersInputCount, IEnu { if (input.value > othersOutputSum) { - AddInput(tx, walletInputs, input.value, input.anonset, input.hdpk); + AddInput(rnd, tx, walletInputs, input.value, input.anonset, input.hdpk); } else { @@ -58,12 +59,12 @@ public static SmartTransaction CreateSmartTransaction(int othersInputCount, IEnu for (int i = 0; i < othersInputCount; i++) { - tx.Inputs.Add(CreateOutPoint()); + tx.Inputs.Add(CreateOutPoint(rnd)); } foreach (var (value, anonset, hdpk) in remainingOwnInputs) { - AddInput(tx, walletInputs, value, anonset, hdpk); + AddInput(rnd, tx, walletInputs, value, anonset, hdpk); } var outputs = new List<(TxOut txout, (int anonset, HdPubKey hdpk)? own)>(); @@ -111,9 +112,9 @@ public static SmartTransaction CreateSmartTransaction(int othersInputCount, IEnu return stx; } - private static void AddInput(Transaction tx, HashSet walletInputs, Money value, int anonset, HdPubKey hdpk) + private static void AddInput(GingerRandom rnd, Transaction tx, HashSet walletInputs, Money value, int anonset, HdPubKey hdpk) { - var sc = CreateSmartCoin(hdpk, value, anonymitySet: anonset); + var sc = CreateSmartCoin(rnd, hdpk, value, anonymitySet: anonset); tx.Inputs.Add(sc.Outpoint); walletInputs.Add(sc); } @@ -124,18 +125,18 @@ public static HdPubKey CreateHdPubKey(KeyManager km) public static HdPubKey CreateHdPubKey(KeyManager km, bool isInternal) => km.GenerateNewKey(LabelsArray.Empty, KeyState.Clean, isInternal); - public static SmartCoin CreateSmartCoin(HdPubKey pubKey, decimal amountBtc, bool confirmed = true, int anonymitySet = 1) - => CreateSmartCoin(pubKey, Money.Coins(amountBtc), confirmed, anonymitySet); + public static SmartCoin CreateSmartCoin(GingerRandom rnd, HdPubKey pubKey, decimal amountBtc, bool confirmed = true, int anonymitySet = 1) + => CreateSmartCoin(rnd, pubKey, Money.Coins(amountBtc), confirmed, anonymitySet); - public static SmartCoin CreateSmartCoin(HdPubKey pubKey, Money amount, bool confirmed = true, int anonymitySet = 1) - => CreateSmartCoin(Transaction.Create(Network.Main), pubKey, amount, confirmed, anonymitySet); + public static SmartCoin CreateSmartCoin(GingerRandom rnd, HdPubKey pubKey, Money amount, bool confirmed = true, int anonymitySet = 1) + => CreateSmartCoin(rnd, Transaction.Create(Network.Main), pubKey, amount, confirmed, anonymitySet); - public static SmartCoin CreateSmartCoin(Transaction tx, HdPubKey pubKey, Money amount, bool confirmed = true, int anonymitySet = 1) + public static SmartCoin CreateSmartCoin(GingerRandom rnd, Transaction tx, HdPubKey pubKey, Money amount, bool confirmed = true, int anonymitySet = 1) { var height = confirmed ? new Height(CryptoHelpers.RandomInt(0, 200)) : Height.Mempool; pubKey.SetKeyState(KeyState.Used); tx.Outputs.Add(new TxOut(amount, pubKey.GetAssumedScriptPubKey())); - tx.Inputs.Add(CreateOutPoint()); + tx.Inputs.Add(CreateOutPoint(rnd)); var stx = new SmartTransaction(tx, height); pubKey.SetAnonymitySet(anonymitySet, stx.GetHash()); var sc = new SmartCoin(stx, (uint)tx.Outputs.Count - 1, pubKey); @@ -143,14 +144,12 @@ public static SmartCoin CreateSmartCoin(Transaction tx, HdPubKey pubKey, Money a return sc; } - public static OutPoint CreateOutPoint() - => new(CreateUint256(), (uint)CryptoHelpers.RandomInt(0, 100)); + public static OutPoint CreateOutPoint(GingerRandom rnd) => new(CreateUint256(rnd), (uint)rnd.GetInt(0, 100)); - public static uint256 CreateUint256() + public static uint256 CreateUint256(GingerRandom rnd) { - var rand = new UnsecureRandom(); - var bytes = new byte[32]; - rand.GetBytes(bytes); + Span bytes = stackalloc byte[32]; + rnd.GetBytes(bytes); return new uint256(bytes); } @@ -202,7 +201,7 @@ public static BitcoinAddress CreateBitcoinAddress(Network network, Key? key = nu return CreateScript(key).GetDestinationAddress(network)!; } - public static Transaction CreateTransaction() => CreateSmartTransaction(1, 0, 0, 1).Transaction; + public static Transaction CreateTransaction(GingerRandom rnd) => CreateSmartTransaction(rnd, 1, 0, 0, 1).Transaction; public static MemoryCache CreateMemoryCache() => new(new MemoryCacheOptions { diff --git a/WalletWasabi.Tests/Helpers/ServiceFactory.cs b/WalletWasabi.Tests/Helpers/ServiceFactory.cs index ea536f4b0a..737e2b124a 100644 --- a/WalletWasabi.Tests/Helpers/ServiceFactory.cs +++ b/WalletWasabi.Tests/Helpers/ServiceFactory.cs @@ -1,3 +1,4 @@ +using GingerCommon.Crypto.Random; using NBitcoin; using System.Collections.Generic; using System.Linq; @@ -9,21 +10,17 @@ namespace WalletWasabi.Tests.Helpers; public static class ServiceFactory { - public static TransactionFactory CreateTransactionFactory( - IEnumerable<(string Label, int KeyIndex, decimal Amount, bool Confirmed, int AnonymitySet)> coins, - bool watchOnly = false) + public static TransactionFactory CreateTransactionFactory(GingerRandom rnd, IEnumerable<(string Label, int KeyIndex, decimal Amount, bool Confirmed, int AnonymitySet)> coins, bool watchOnly = false) { string password = "foo"; KeyManager keyManager = watchOnly ? CreateWatchOnlyKeyManager() : CreateKeyManager(password); - SmartCoin[] sCoins = CreateCoins(keyManager, coins); + SmartCoin[] sCoins = CreateCoins(rnd, keyManager, coins); var coinsView = new CoinsView(sCoins); var mockTransactionStore = new AllTransactionStore(".", Network.Main); return new TransactionFactory(Network.Main, keyManager, coinsView, mockTransactionStore, password); } - public static SmartCoin[] CreateCoins( - KeyManager keyManager, - IEnumerable<(string Label, int KeyIndex, decimal Amount, bool Confirmed, int AnonymitySet)> coins) + public static SmartCoin[] CreateCoins(GingerRandom rnd, KeyManager keyManager, IEnumerable<(string Label, int KeyIndex, decimal Amount, bool Confirmed, int AnonymitySet)> coins) { var coinArray = coins.ToArray(); @@ -43,7 +40,7 @@ public static SmartCoin[] CreateCoins( k.SetAnonymitySet(c.AnonymitySet); } - var sCoins = coins.Select(x => BitcoinFactory.CreateSmartCoin(keys[x.KeyIndex], x.Amount, x.Confirmed, x.AnonymitySet)).ToArray(); + var sCoins = coins.Select(x => BitcoinFactory.CreateSmartCoin(rnd, keys[x.KeyIndex], x.Amount, x.Confirmed, x.AnonymitySet)).ToArray(); foreach (var coin in sCoins) { foreach (var sameLabelCoin in sCoins.Where(c => !c.HdPubKey.Labels.IsEmpty && c.HdPubKey.Labels == coin.HdPubKey.Labels)) diff --git a/WalletWasabi.Tests/Helpers/WabiSabiTestFactory.cs b/WalletWasabi.Tests/Helpers/WabiSabiTestFactory.cs index 078ea1ac2c..8c5b27f1c0 100644 --- a/WalletWasabi.Tests/Helpers/WabiSabiTestFactory.cs +++ b/WalletWasabi.Tests/Helpers/WabiSabiTestFactory.cs @@ -30,6 +30,7 @@ using WabiSabi.Crypto.Randomness; using System.Runtime.CompilerServices; using WalletWasabi.Tests.TestCommon; +using GingerCommon.Crypto.Random; namespace WalletWasabi.Tests.Helpers; @@ -51,23 +52,23 @@ public static Coin CreateCoin(Key? key = null, Money? amount = null, ScriptPubKe new TxOut(amount, key.PubKey.GetScriptPubKey(scriptPubKeyType))); } - public static Tuple CreateCoinWithOwnershipProof(Key? key = null, Money? amount = null, uint256? roundId = null, ScriptPubKeyType scriptPubKeyType = ScriptPubKeyType.Segwit) + public static Tuple CreateCoinWithOwnershipProof(GingerRandom rnd, Key? key = null, Money? amount = null, uint256? roundId = null, ScriptPubKeyType scriptPubKeyType = ScriptPubKeyType.Segwit) { key ??= new(); var coin = CreateCoin(key, amount, scriptPubKeyType); roundId ??= uint256.One; - var ownershipProof = CreateOwnershipProof(key, roundId); + var ownershipProof = CreateOwnershipProof(rnd, key, roundId); return new Tuple(coin, ownershipProof); } public static CoinJoinInputCommitmentData CreateCommitmentData(uint256? roundId = null) => new(CoordinatorIdentifier, roundId ?? uint256.One); - public static OwnershipProof CreateOwnershipProof(Key key, uint256? roundHash = null, ScriptPubKeyType scriptPubKeyType = ScriptPubKeyType.Segwit) + public static OwnershipProof CreateOwnershipProof(GingerRandom rnd, Key key, uint256? roundHash = null, ScriptPubKeyType scriptPubKeyType = ScriptPubKeyType.Segwit) => OwnershipProof.GenerateCoinJoinInputProof( key, GetOwnershipIdentifier(key.PubKey.GetScriptPubKey(scriptPubKeyType)), - new CoinJoinInputCommitmentData(CoordinatorIdentifier, roundHash ?? BitcoinFactory.CreateUint256()), + new CoinJoinInputCommitmentData(CoordinatorIdentifier, roundHash ?? BitcoinFactory.CreateUint256(rnd)), scriptPubKeyType); public static OwnershipIdentifier GetOwnershipIdentifier(Script scriptPubKey) @@ -93,7 +94,7 @@ public static Round CreateRound(WabiSabiConfig cfg, ulong seed = 0, [CallerFileP Constants.P2wpkhInputVirtualSize + Constants.P2wpkhOutputVirtualSize // enough vsize for one input and one output }, seed, callerFilePath, callerMemberName); - public static MockRpcClient CreatePreconfiguredRpcClient(params Coin[] coins) + public static MockRpcClient CreatePreconfiguredRpcClient(GingerRandom rnd, params Coin[] coins) { using Key key = new(); var mockRpc = new MockRpcClient(); @@ -119,7 +120,7 @@ public static MockRpcClient CreatePreconfiguredRpcClient(params Coin[] coins) }; }; mockRpc.OnGetRawTransactionAsync = (_, _) => - Task.FromResult(BitcoinFactory.CreateTransaction()); + Task.FromResult(BitcoinFactory.CreateTransaction(rnd)); mockRpc.OnEstimateSmartFeeAsync = (_, _) => Task.FromResult(new EstimateSmartFeeResponse @@ -140,22 +141,22 @@ public static MockRpcClient CreatePreconfiguredRpcClient(params Coin[] coins) public static Alice CreateAlice(Coin coin, OwnershipProof ownershipProof, Round round) => new(coin, ownershipProof, round, Guid.NewGuid(), false) { Deadline = DateTimeOffset.UtcNow + TimeSpan.FromHours(1) }; - public static Alice CreateAlice(Key key, Money amount, Round round, ScriptPubKeyType scriptPubKeyType = ScriptPubKeyType.Segwit) - => CreateAlice(CreateCoin(key, amount, scriptPubKeyType), CreateOwnershipProof(key, round.Id, scriptPubKeyType), round); + public static Alice CreateAlice(GingerRandom rnd, Key key, Money amount, Round round, ScriptPubKeyType scriptPubKeyType = ScriptPubKeyType.Segwit) + => CreateAlice(CreateCoin(key, amount, scriptPubKeyType), CreateOwnershipProof(rnd, key, round.Id, scriptPubKeyType), round); - public static Alice CreateAlice(Money amount, Round round) + public static Alice CreateAlice(GingerRandom rnd, Money amount, Round round) { using var key = new Key(); - return CreateAlice(key, amount, round); + return CreateAlice(rnd, key, amount, round); } - public static Alice CreateAlice(Key key, Round round, ScriptPubKeyType scriptPubKeyType = ScriptPubKeyType.Segwit) - => CreateAlice(key, Money.Coins(1), round, scriptPubKeyType); + public static Alice CreateAlice(GingerRandom rnd, Key key, Round round, ScriptPubKeyType scriptPubKeyType = ScriptPubKeyType.Segwit) + => CreateAlice(rnd, key, Money.Coins(1), round, scriptPubKeyType); - public static Alice CreateAlice(Round round) + public static Alice CreateAlice(GingerRandom rnd, Round round) { using var key = new Key(); - return CreateAlice(key, Money.Coins(1), round); + return CreateAlice(rnd, key, Money.Coins(1), round); } public static ArenaClient CreateArenaClient(Arena arena, Round? round = null) @@ -168,7 +169,7 @@ public static ArenaClient CreateArenaClient(Arena arena, Round? round = null) arena); } - public static InputRegistrationRequest CreateInputRegistrationRequest(Round round, Key? key = null, OutPoint? prevout = null) + public static InputRegistrationRequest CreateInputRegistrationRequest(GingerRandom rnd, Round round, Key? key = null, OutPoint? prevout = null) { (var amClient, var vsClient, _, _, _, _) = CreateWabiSabiClientsAndIssuers(round); var (zeroAmountCredentialRequest, _) = amClient.CreateRequestForZeroAmount(); @@ -177,8 +178,8 @@ public static InputRegistrationRequest CreateInputRegistrationRequest(Round roun using var newkey = new Key(); return new( round.Id, - prevout ?? BitcoinFactory.CreateOutPoint(), - CreateOwnershipProof(key ?? newkey, round.Id), + prevout ?? BitcoinFactory.CreateOutPoint(rnd), + CreateOwnershipProof(rnd, key ?? newkey, round.Id), zeroAmountCredentialRequest, zeroVsizeCredentialRequest); } @@ -221,11 +222,11 @@ private static (IEnumerable, IEnumerable) EnsureZeroCred vsClient.HandleResponse(vsCredResp, vsVal)); } - public static (Alice alice, RealCredentialsRequest amountRequest, RealCredentialsRequest vsizeRequest) CreateRealCredentialRequests(Round round, Money? amount = null, long? vsize = null) + public static (Alice alice, RealCredentialsRequest amountRequest, RealCredentialsRequest vsizeRequest) CreateRealCredentialRequests(GingerRandom rnd, Round round, Money? amount = null, long? vsize = null) { var (amClient, vsClient, _, _, amZeroCredentials, vsZeroCredentials) = CreateWabiSabiClientsAndIssuers(round); - var alice = round.Alices.FirstOrDefault() ?? CreateAlice(round); + var alice = round.Alices.FirstOrDefault() ?? CreateAlice(rnd, round); var (realAmountCredentialRequest, _) = amClient.CreateRequest( new[] { amount?.Satoshi ?? alice.CalculateRemainingAmountCredentials(round.Parameters.MiningFeeRate, round.Parameters.CoordinationFeeRate).Satoshi }, amZeroCredentials, @@ -238,9 +239,9 @@ public static (Alice alice, RealCredentialsRequest amountRequest, RealCredential return (alice, realAmountCredentialRequest, realVsizeCredentialRequest); } - public static ConnectionConfirmationRequest CreateConnectionConfirmationRequest(Round round) + public static ConnectionConfirmationRequest CreateConnectionConfirmationRequest(GingerRandom rnd, Round round) { - var (alice, realAmountCredentialRequest, realVsizeCredentialRequest) = CreateRealCredentialRequests(round); + var (alice, realAmountCredentialRequest, realVsizeCredentialRequest) = CreateRealCredentialRequests(rnd, round); var (amClient, vsClient, _, _, _, _) = CreateWabiSabiClientsAndIssuers(round); var (zeroAmountCredentialRequest, _) = amClient.CreateRequestForZeroAmount(); @@ -255,11 +256,11 @@ public static ConnectionConfirmationRequest CreateConnectionConfirmationRequest( realVsizeCredentialRequest); } - public static OutputRegistrationRequest CreateOutputRegistrationRequest(Round round, Script? script = null, int? vsize = null) + public static OutputRegistrationRequest CreateOutputRegistrationRequest(GingerRandom rnd, Round round, Script? script = null, int? vsize = null) { var (amClient, vsClient, amIssuer, vsIssuer, amZeroCredentials, vsZeroCredentials) = CreateWabiSabiClientsAndIssuers(round); - var alice = round.Alices.FirstOrDefault() ?? CreateAlice(round); + var alice = round.Alices.FirstOrDefault() ?? CreateAlice(rnd, round); var (amCredentialRequest, amValid) = amClient.CreateRequest( new[] { alice.CalculateRemainingAmountCredentials(round.Parameters.MiningFeeRate, round.Parameters.CoordinationFeeRate).Satoshi }, amZeroCredentials, // FIXME doesn't make much sense @@ -310,13 +311,13 @@ public static BlameRound CreateBlameRound(Round round, WabiSabiConfig cfg) InsecureRandom.Instance); } - public static (IKeyChain, SmartCoin, SmartCoin) CreateCoinKeyPairs(KeyManager? keyManager = null) + public static (IKeyChain, SmartCoin, SmartCoin) CreateCoinKeyPairs(GingerRandom rnd, KeyManager? keyManager = null) { var km = keyManager ?? ServiceFactory.CreateKeyManager(""); var keyChain = new KeyChain(km, new Kitchen("")); - var smartCoin1 = BitcoinFactory.CreateSmartCoin(BitcoinFactory.CreateHdPubKey(km), Money.Coins(1m)); - var smartCoin2 = BitcoinFactory.CreateSmartCoin(BitcoinFactory.CreateHdPubKey(km), Money.Coins(2m)); + var smartCoin1 = BitcoinFactory.CreateSmartCoin(rnd, BitcoinFactory.CreateHdPubKey(km), Money.Coins(1m)); + var smartCoin2 = BitcoinFactory.CreateSmartCoin(rnd, BitcoinFactory.CreateHdPubKey(km), Money.Coins(2m)); return (keyChain, smartCoin1, smartCoin2); } diff --git a/WalletWasabi.Tests/IntegrationTests/TorTests.cs b/WalletWasabi.Tests/IntegrationTests/TorTests.cs index dd53f12cdc..273d62d67f 100644 --- a/WalletWasabi.Tests/IntegrationTests/TorTests.cs +++ b/WalletWasabi.Tests/IntegrationTests/TorTests.cs @@ -136,7 +136,7 @@ public async Task CanDoBasicPostHttpsRequestAsync() Assert.NotNull(headersNode); Assert.Equal("58", GetJsonNode(headersNode, "content-length").GetValue()); - Assert.Equal("gzip", GetJsonNode(headersNode, "accept-encoding").GetValue()); + Assert.Equal("gzip, br", GetJsonNode(headersNode, "accept-encoding").GetValue()); Assert.Equal("text/plain; charset=utf-8", GetJsonNode(headersNode, "content-type").GetValue()); } @@ -238,7 +238,7 @@ public async Task CanDoHttpsAsync() Assert.Equal("https", GetJsonNode(headersNode, "x-forwarded-proto").GetValue()); Assert.Equal("443", GetJsonNode(headersNode, "x-forwarded-port").GetValue()); - Assert.Equal("gzip", GetJsonNode(headersNode, "accept-encoding").GetValue()); + Assert.Equal("gzip, br", GetJsonNode(headersNode, "accept-encoding").GetValue()); } Assert.Equal("https://postman-echo.com/get?foo1=bar1&foo2=bar2", GetJsonNode(responseNode, "url").GetValue()); diff --git a/WalletWasabi.Tests/TestCommon/TestRandom.cs b/WalletWasabi.Tests/TestCommon/TestRandom.cs index ba3ef12dd9..bc93b5b982 100644 --- a/WalletWasabi.Tests/TestCommon/TestRandom.cs +++ b/WalletWasabi.Tests/TestCommon/TestRandom.cs @@ -1,8 +1,6 @@ using GingerCommon.Crypto.Random; using System.Globalization; using System.Runtime.CompilerServices; -using System.Security.Cryptography; -using System.Text; using WabiSabi.Crypto.Randomness; using WalletWasabi.Crypto.Randomness; @@ -17,7 +15,9 @@ public static GingerRandom Get(ulong seed = 0, [CallerFilePath] string callerFil // Deterministic seed var localFilePath = callerFilePath[callerFilePath.LastIndexOf("WalletWasabi")..].Replace("\\", "/"); var str = string.Join(",", localFilePath, callerMemberName, seed.ToString(CultureInfo.InvariantCulture)); - return new DeterministicRandom(SHA256.HashData(Encoding.UTF8.GetBytes(str))); + Span seedArray = stackalloc byte[32]; + RandomExtensions.GenerateSeed(seedArray, str); + return new DeterministicRandom(seedArray); } return new DeterministicRandom(seed); } diff --git a/WalletWasabi.Tests/UnitTests/BitcoinCore/MempoolMirrorTests.cs b/WalletWasabi.Tests/UnitTests/BitcoinCore/MempoolMirrorTests.cs index d90f5c6367..22fa81adc7 100644 --- a/WalletWasabi.Tests/UnitTests/BitcoinCore/MempoolMirrorTests.cs +++ b/WalletWasabi.Tests/UnitTests/BitcoinCore/MempoolMirrorTests.cs @@ -50,7 +50,7 @@ public async Task CanCopyMempoolFromRpcAsync() finally { await services.StopAllAsync(); - await coreNode.TryStopAsync(); + await coreNode.TryStopAsync(true, 2); } } @@ -88,7 +88,7 @@ public async Task CanHandleArrivingTxAsync() finally { await services.StopAllAsync(); - await coreNode.TryStopAsync(); + await coreNode.TryStopAsync(true, 2); } } @@ -141,7 +141,7 @@ public async Task CanHandleTheSameTxSentManyTimesAsync() finally { await services.StopAllAsync(); - await coreNode.TryStopAsync(); + await coreNode.TryStopAsync(true, 2); } } @@ -187,7 +187,7 @@ public async Task CanHandleManyTxsAsync() finally { await services.StopAllAsync(); - await coreNode.TryStopAsync(); + await coreNode.TryStopAsync(true, 2); } } @@ -239,7 +239,7 @@ public async Task CanHandleConfirmationAsync() finally { await services.StopAllAsync(); - await coreNode.TryStopAsync(); + await coreNode.TryStopAsync(true, 2); } } } diff --git a/WalletWasabi.Tests/UnitTests/BitcoinCore/NodeBuildingTests.cs b/WalletWasabi.Tests/UnitTests/BitcoinCore/NodeBuildingTests.cs index 7061e70d6c..d9c039fce9 100644 --- a/WalletWasabi.Tests/UnitTests/BitcoinCore/NodeBuildingTests.cs +++ b/WalletWasabi.Tests/UnitTests/BitcoinCore/NodeBuildingTests.cs @@ -14,7 +14,7 @@ public class NodeBuildingTests public async Task CanBuildCoreNodeAsync() { var coreNode = await TestNodeBuilder.CreateAsync(); - await coreNode.TryStopAsync(); + await coreNode.TryStopAsync(true, 2); } [Fact] @@ -31,7 +31,7 @@ public async Task NodesDifferAsync() } finally { - await Task.WhenAll(node1.TryStopAsync(), node2.TryStopAsync()); + await Task.WhenAll(node1.TryStopAsync(true, 2), node2.TryStopAsync(true, 2)); } } @@ -46,7 +46,7 @@ public async Task RpcWorksAsync() } finally { - await coreNode.TryStopAsync(); + await coreNode.TryStopAsync(true, 2); } } @@ -64,7 +64,7 @@ public async Task P2pWorksAsync() finally { node.Disconnect(); - await coreNode.TryStopAsync(); + await coreNode.TryStopAsync(true, 2); } } } diff --git a/WalletWasabi.Tests/UnitTests/BitcoinCore/P2pBasedTests.cs b/WalletWasabi.Tests/UnitTests/BitcoinCore/P2pBasedTests.cs index 82a8c07e45..f7a760d070 100644 --- a/WalletWasabi.Tests/UnitTests/BitcoinCore/P2pBasedTests.cs +++ b/WalletWasabi.Tests/UnitTests/BitcoinCore/P2pBasedTests.cs @@ -86,7 +86,7 @@ public async Task MempoolNotifiesAsync() finally { node.Disconnect(); - await coreNode.TryStopAsync(); + await coreNode.TryStopAsync(true, 2); } } @@ -133,7 +133,7 @@ public async Task TrustedNotifierNotifiesTxAsync() } finally { - await coreNode.TryStopAsync(); + await coreNode.TryStopAsync(true, 2); } } @@ -209,7 +209,7 @@ public async Task BlockNotifierTestsAsync() finally { await services.StopAllAsync(); - await coreNode.TryStopAsync(); + await coreNode.TryStopAsync(true, 2); } } } diff --git a/WalletWasabi.Tests/UnitTests/BitcoinCore/RpcBasedTests.cs b/WalletWasabi.Tests/UnitTests/BitcoinCore/RpcBasedTests.cs index f3defadc67..c7b0b53c43 100644 --- a/WalletWasabi.Tests/UnitTests/BitcoinCore/RpcBasedTests.cs +++ b/WalletWasabi.Tests/UnitTests/BitcoinCore/RpcBasedTests.cs @@ -8,6 +8,7 @@ using WalletWasabi.BitcoinCore.Rpc.Models; using WalletWasabi.Extensions; using WalletWasabi.Tests.Helpers; +using WalletWasabi.Tests.TestCommon; using Xunit; namespace WalletWasabi.Tests.UnitTests.BitcoinCore; @@ -165,7 +166,7 @@ public async Task AllFeeEstimateAsync() } finally { - await coreNode.TryStopAsync(); + await coreNode.TryStopAsync(true, 2); } } @@ -181,7 +182,7 @@ public async Task FeeEstimationCanCancelAsync() } finally { - await coreNode.TryStopAsync(); + await coreNode.TryStopAsync(true, 2); } } @@ -223,7 +224,7 @@ public async Task CantDoubleSpendAsync() } finally { - await coreNode.TryStopAsync(); + await coreNode.TryStopAsync(true, 2); } } @@ -239,7 +240,7 @@ public async Task VerboseBlockInfoAsync() } finally { - await coreNode.TryStopAsync(); + await coreNode.TryStopAsync(true, 2); } } @@ -275,8 +276,9 @@ public async Task GetRawTransactionsAsync() var coreNode = await TestNodeBuilder.CreateAsync(); try { + var rnd = TestRandom.Get(); var rpc = coreNode.RpcClient; - var txs = await rpc.GetRawTransactionsAsync(new[] { BitcoinFactory.CreateUint256(), BitcoinFactory.CreateUint256() }, CancellationToken.None); + var txs = await rpc.GetRawTransactionsAsync(new[] { BitcoinFactory.CreateUint256(rnd), BitcoinFactory.CreateUint256(rnd) }, CancellationToken.None); Assert.Empty(txs); await rpc.CreateWalletAsync("wallet"); @@ -308,7 +310,7 @@ public async Task GetRawTransactionsAsync() } finally { - await coreNode.TryStopAsync(); + await coreNode.TryStopAsync(true, 2); } } } diff --git a/WalletWasabi.Tests/UnitTests/Blockchain/TransactionBuilding/ChangelessTransactionCoinSelectorTests.cs b/WalletWasabi.Tests/UnitTests/Blockchain/TransactionBuilding/ChangelessTransactionCoinSelectorTests.cs index 658e2359d4..e668da4c7b 100644 --- a/WalletWasabi.Tests/UnitTests/Blockchain/TransactionBuilding/ChangelessTransactionCoinSelectorTests.cs +++ b/WalletWasabi.Tests/UnitTests/Blockchain/TransactionBuilding/ChangelessTransactionCoinSelectorTests.cs @@ -1,3 +1,4 @@ +using GingerCommon.Crypto.Random; using NBitcoin; using System.Collections.Generic; using System.Linq; @@ -8,6 +9,7 @@ using WalletWasabi.Blockchain.TransactionOutputs; using WalletWasabi.Extensions; using WalletWasabi.Tests.Helpers; +using WalletWasabi.Tests.TestCommon; using Xunit; namespace WalletWasabi.Tests.UnitTests.Blockchain.TransactionBuilding; @@ -26,7 +28,7 @@ public void GoodSuggestion() using CancellationTokenSource testDeadlineCts = new(TimeSpan.FromMinutes(1)); KeyManager km = ServiceFactory.CreateKeyManager(); - IEnumerable coins = GenerateDummySmartCoins(km, 6_025, 6_561, 8_192, 13_122, 50_000, 100_000, 196_939, 524_288); + IEnumerable coins = GenerateDummySmartCoins(TestRandom.Get(), km, 6_025, 6_561, 8_192, 13_122, 50_000, 100_000, 196_939, 524_288); var coinsByScript = coins .GroupBy(coin => coin.ScriptPubKey) @@ -56,7 +58,7 @@ public void Good_LesserSuggestion() using CancellationTokenSource testDeadlineCts = new(TimeSpan.FromMinutes(1)); KeyManager km = ServiceFactory.CreateKeyManager(); - List coins = GenerateDummySmartCoins(km, 6_025, 6_561, 8_192, 13_122, 50_000, 100_000, 196_939, 524_288); + List coins = GenerateDummySmartCoins(TestRandom.Get(), km, 6_025, 6_561, 8_192, 13_122, 50_000, 100_000, 196_939, 524_288); var coinsByScript = coins .GroupBy(coin => coin.ScriptPubKey) @@ -88,7 +90,7 @@ public void TooExpensiveSolution() using CancellationTokenSource testDeadlineCts = new(TimeSpan.FromMinutes(1)); KeyManager km = ServiceFactory.CreateKeyManager(); - List coins = GenerateDummySmartCoins(km, 150_000); + List coins = GenerateDummySmartCoins(TestRandom.Get(), km, 150_000); var coinsByScript = coins .GroupBy(coin => coin.ScriptPubKey) @@ -114,6 +116,7 @@ public void TooExpensiveSolution() [Fact] public async void BnBRespectScriptPubKeyPrivacyRuleAsync() { + var rnd = TestRandom.Get(); using CancellationTokenSource testDeadlineCts = new(TimeSpan.FromMinutes(5)); KeyManager km = ServiceFactory.CreateKeyManager(); @@ -124,15 +127,15 @@ public async void BnBRespectScriptPubKeyPrivacyRuleAsync() List availableCoins = new() { // Group #1. - BitcoinFactory.CreateSmartCoin(constantHdPubKey, Money.Satoshis(10000)), - BitcoinFactory.CreateSmartCoin(constantHdPubKey, Money.Satoshis(10000)), - BitcoinFactory.CreateSmartCoin(constantHdPubKey, Money.Satoshis(10000)), + BitcoinFactory.CreateSmartCoin(rnd, constantHdPubKey, Money.Satoshis(10000)), + BitcoinFactory.CreateSmartCoin(rnd, constantHdPubKey, Money.Satoshis(10000)), + BitcoinFactory.CreateSmartCoin(rnd, constantHdPubKey, Money.Satoshis(10000)), // Group #2. - BitcoinFactory.CreateSmartCoin(constantHdPubKey2, Money.Satoshis(10000)), - BitcoinFactory.CreateSmartCoin(constantHdPubKey2, Money.Satoshis(10000)), - BitcoinFactory.CreateSmartCoin(constantHdPubKey2, Money.Satoshis(10000)), - BitcoinFactory.CreateSmartCoin(constantHdPubKey2, Money.Satoshis(5000)), + BitcoinFactory.CreateSmartCoin(rnd, constantHdPubKey2, Money.Satoshis(10000)), + BitcoinFactory.CreateSmartCoin(rnd, constantHdPubKey2, Money.Satoshis(10000)), + BitcoinFactory.CreateSmartCoin(rnd, constantHdPubKey2, Money.Satoshis(10000)), + BitcoinFactory.CreateSmartCoin(rnd, constantHdPubKey2, Money.Satoshis(5000)), }; // First test case where we show that we are not mixing the script pub keys and we spend the address reused coins together. @@ -192,13 +195,13 @@ public async void BnBRespectScriptPubKeyPrivacyRuleAsync() } /// These smart coins are from an invalid transaction but we are interested only in smart coins' amounts. - private List GenerateDummySmartCoins(KeyManager km, params long[] values) + private List GenerateDummySmartCoins(GingerRandom rnd, KeyManager km, params long[] values) { List result = new(capacity: values.Length); for (uint i = 0; i < values.Length; i++) { - result.Add(BitcoinFactory.CreateSmartCoin(BitcoinFactory.CreateHdPubKey(km), Money.Satoshis(values[i]))); + result.Add(BitcoinFactory.CreateSmartCoin(rnd, BitcoinFactory.CreateHdPubKey(km), Money.Satoshis(values[i]))); } return result.OrderByDescending(x => x.Amount.Satoshi).ToList(); diff --git a/WalletWasabi.Tests/UnitTests/BlockchainAnalysis/CoinJoinAnonscoreTests.cs b/WalletWasabi.Tests/UnitTests/BlockchainAnalysis/CoinJoinAnonscoreTests.cs index a3fcd5a90d..fbbb7a29e5 100644 --- a/WalletWasabi.Tests/UnitTests/BlockchainAnalysis/CoinJoinAnonscoreTests.cs +++ b/WalletWasabi.Tests/UnitTests/BlockchainAnalysis/CoinJoinAnonscoreTests.cs @@ -15,7 +15,7 @@ public class CoinJoinAnonScoreTests public void BasicCalculation() { var analyzer = new BlockchainAnalyzer(); - var tx = BitcoinFactory.CreateSmartTransaction(9, Enumerable.Repeat(Money.Coins(1m), 9), new[] { (Money.Coins(1.1m), 1) }, new[] { (Money.Coins(1m), HdPubKey.DefaultHighAnonymitySet) }); + var tx = BitcoinFactory.CreateSmartTransaction(TestRandom.Get(), 9, Enumerable.Repeat(Money.Coins(1m), 9), new[] { (Money.Coins(1.1m), 1) }, new[] { (Money.Coins(1m), HdPubKey.DefaultHighAnonymitySet) }); analyzer.Analyze(tx); @@ -29,7 +29,7 @@ public void BasicCalculation() public void DoubleProcessing() { var analyzer = new BlockchainAnalyzer(); - var tx = BitcoinFactory.CreateSmartTransaction(9, Enumerable.Repeat(Money.Coins(1m), 9), new[] { (Money.Coins(1.1m), 1) }, new[] { (Money.Coins(1m), HdPubKey.DefaultHighAnonymitySet) }); + var tx = BitcoinFactory.CreateSmartTransaction(TestRandom.Get(), 9, Enumerable.Repeat(Money.Coins(1m), 9), new[] { (Money.Coins(1.1m), 1) }, new[] { (Money.Coins(1m), HdPubKey.DefaultHighAnonymitySet) }); analyzer.Analyze(tx); analyzer.Analyze(tx); Assert.Equal(1, tx.WalletInputs.First().HdPubKey.AnonymitySet); @@ -42,7 +42,7 @@ public void DoubleProcessing() public void OtherWalletChangesThings() { var analyzer = new BlockchainAnalyzer(); - var tx = BitcoinFactory.CreateSmartTransaction(9, Enumerable.Repeat(Money.Coins(1m), 8), new[] { (Money.Coins(1.1m), 1) }, new[] { (Money.Coins(1m), HdPubKey.DefaultHighAnonymitySet), (Money.Coins(1m), HdPubKey.DefaultHighAnonymitySet) }); + var tx = BitcoinFactory.CreateSmartTransaction(TestRandom.Get(), 9, Enumerable.Repeat(Money.Coins(1m), 8), new[] { (Money.Coins(1.1m), 1) }, new[] { (Money.Coins(1m), HdPubKey.DefaultHighAnonymitySet), (Money.Coins(1m), HdPubKey.DefaultHighAnonymitySet) }); var sc = tx.WalletOutputs.First(); Assert.True(tx.TryRemoveWalletOutput(sc)); analyzer.Analyze(tx); @@ -59,7 +59,7 @@ public void OtherWalletChangesThings() public void Inheritance() { var analyzer = new BlockchainAnalyzer(); - var tx = BitcoinFactory.CreateSmartTransaction(9, Enumerable.Repeat(Money.Coins(1m), 9), new[] { (Money.Coins(1.1m), 100) }, new[] { (Money.Coins(1m), HdPubKey.DefaultHighAnonymitySet) }); + var tx = BitcoinFactory.CreateSmartTransaction(TestRandom.Get(), 9, Enumerable.Repeat(Money.Coins(1m), 9), new[] { (Money.Coins(1.1m), 100) }, new[] { (Money.Coins(1m), HdPubKey.DefaultHighAnonymitySet) }); analyzer.Analyze(tx); @@ -74,7 +74,7 @@ public void Inheritance() public void ChangeOutput() { var analyzer = new BlockchainAnalyzer(); - var tx = BitcoinFactory.CreateSmartTransaction(9, Enumerable.Repeat(Money.Coins(1m), 9), new[] { (Money.Coins(6.2m), 1) }, new[] { (Money.Coins(1m), HdPubKey.DefaultHighAnonymitySet), (Money.Coins(5m), HdPubKey.DefaultHighAnonymitySet) }); + var tx = BitcoinFactory.CreateSmartTransaction(TestRandom.Get(), 9, Enumerable.Repeat(Money.Coins(1m), 9), new[] { (Money.Coins(6.2m), 1) }, new[] { (Money.Coins(1m), HdPubKey.DefaultHighAnonymitySet), (Money.Coins(5m), HdPubKey.DefaultHighAnonymitySet) }); analyzer.Analyze(tx); @@ -90,7 +90,7 @@ public void ChangeOutput() public void ChangeOutputConservativeConsolidation() { var analyzer = new BlockchainAnalyzer(); - var tx = BitcoinFactory.CreateSmartTransaction(9, Enumerable.Repeat(Money.Coins(1m), 9), new[] { (Money.Coins(3.1m), 1), (Money.Coins(3.1m), 100) }, new[] { (Money.Coins(1m), HdPubKey.DefaultHighAnonymitySet), (Money.Coins(5m), HdPubKey.DefaultHighAnonymitySet) }); + var tx = BitcoinFactory.CreateSmartTransaction(TestRandom.Get(), 9, Enumerable.Repeat(Money.Coins(1m), 9), new[] { (Money.Coins(3.1m), 1), (Money.Coins(3.1m), 100) }, new[] { (Money.Coins(1m), HdPubKey.DefaultHighAnonymitySet), (Money.Coins(5m), HdPubKey.DefaultHighAnonymitySet) }); analyzer.Analyze(tx); @@ -106,7 +106,7 @@ public void ChangeOutputConservativeConsolidation() public void ChangeOutputInheritance() { var analyzer = new BlockchainAnalyzer(); - var tx = BitcoinFactory.CreateSmartTransaction(9, Enumerable.Repeat(Money.Coins(1m), 9), new[] { (Money.Coins(6.2m), 100) }, new[] { (Money.Coins(1m), HdPubKey.DefaultHighAnonymitySet), (Money.Coins(5m), HdPubKey.DefaultHighAnonymitySet) }); + var tx = BitcoinFactory.CreateSmartTransaction(TestRandom.Get(), 9, Enumerable.Repeat(Money.Coins(1m), 9), new[] { (Money.Coins(6.2m), 100) }, new[] { (Money.Coins(1m), HdPubKey.DefaultHighAnonymitySet), (Money.Coins(5m), HdPubKey.DefaultHighAnonymitySet) }); analyzer.Analyze(tx); @@ -125,6 +125,7 @@ public void MultiDenomination() var analyzer = new BlockchainAnalyzer(); var othersOutputs = new[] { 1, 1, 1, 2, 2 }; var tx = BitcoinFactory.CreateSmartTransaction( + TestRandom.Get(), 9, othersOutputs.Select(x => Money.Coins(x)), new[] { (Money.Coins(3.2m), 1) }, @@ -147,6 +148,7 @@ public void MultiDenominationInheritance() var analyzer = new BlockchainAnalyzer(); var othersOutputs = new[] { 1, 1, 1, 2, 2 }; var tx = BitcoinFactory.CreateSmartTransaction( + TestRandom.Get(), 9, othersOutputs.Select(x => Money.Coins(x)), new[] { (Money.Coins(3.2m), 100) }, @@ -170,6 +172,7 @@ public void SelfAnonsetSanityCheck() var othersOutputs = new[] { 1, 1, 1 }; var ownOutputs = new[] { 1, 1 }; var tx = BitcoinFactory.CreateSmartTransaction( + TestRandom.Get(), 9, othersOutputs.Select(x => Money.Coins(x)), new[] { (Money.Coins(3.2m), 1) }, @@ -188,6 +191,7 @@ public void SelfAnonsetSanityCheck2() var othersOutputs = new[] { 1 }; var ownOutputs = new[] { 1, 1, 1, 1 }; var tx = BitcoinFactory.CreateSmartTransaction( + TestRandom.Get(), 1, othersOutputs.Select(x => Money.Coins(x)), new[] { (Money.Coins(4.2m), 4) }, @@ -208,7 +212,7 @@ public void InputSanityCheck() { // Anonset can never be larger than the number of inputs. var analyzer = new BlockchainAnalyzer(); - var tx = BitcoinFactory.CreateSmartTransaction(2, Enumerable.Repeat(Money.Coins(1m), 9), new[] { (Money.Coins(1.1m), 1) }, new[] { (Money.Coins(1m), HdPubKey.DefaultHighAnonymitySet) }); + var tx = BitcoinFactory.CreateSmartTransaction(TestRandom.Get(), 2, Enumerable.Repeat(Money.Coins(1m), 9), new[] { (Money.Coins(1.1m), 1) }, new[] { (Money.Coins(1m), HdPubKey.DefaultHighAnonymitySet) }); analyzer.Analyze(tx); @@ -224,6 +228,7 @@ public void SelfAnonsetSanityBeforeInputSanityCheck() var othersOutputs = new[] { 1, 1, 1 }; var ownOutputs = new[] { 1, 1 }; var tx = BitcoinFactory.CreateSmartTransaction( + TestRandom.Get(), 1, othersOutputs.Select(x => Money.Coins(x)), new[] { (Money.Coins(3.2m), 1) }, @@ -245,6 +250,7 @@ public void InputMergePunishmentNoInheritance() // Input merging results in worse inherited anonset, but does not punish gains from output indistinguishability. var analyzer = new BlockchainAnalyzer(); var tx = BitcoinFactory.CreateSmartTransaction( + TestRandom.Get(), 9, Enumerable.Repeat(Money.Coins(1m), 9), new[] { (Money.Coins(1.1m), 1), (Money.Coins(1.2m), 1), (Money.Coins(1.3m), 1), (Money.Coins(1.4m), 1) }, @@ -268,6 +274,7 @@ public void InputMergeNonStandardChange() var othersOutputCount = 9; var tx = BitcoinFactory.CreateSmartTransaction( + TestRandom.Get(), 50, Enumerable.Repeat(Money.Coins(1m), othersOutputCount), ownInputs, @@ -295,6 +302,7 @@ public void InputMergeSmallUniqueDenom() var othersOutputCount = 9; var tx = BitcoinFactory.CreateSmartTransaction( + TestRandom.Get(), 50, Enumerable.Repeat(Money.Coins(1m), othersOutputCount), ownInputs, diff --git a/WalletWasabi.Tests/UnitTests/BlockchainAnalysis/NormalSpendAnonScoreTests.cs b/WalletWasabi.Tests/UnitTests/BlockchainAnalysis/NormalSpendAnonScoreTests.cs index 55197981ea..0bd1f33227 100644 --- a/WalletWasabi.Tests/UnitTests/BlockchainAnalysis/NormalSpendAnonScoreTests.cs +++ b/WalletWasabi.Tests/UnitTests/BlockchainAnalysis/NormalSpendAnonScoreTests.cs @@ -1,5 +1,6 @@ using WalletWasabi.Blockchain.Analysis; using WalletWasabi.Tests.Helpers; +using WalletWasabi.Tests.TestCommon; using Xunit; namespace WalletWasabi.Tests.UnitTests.BlockchainAnalysis; @@ -13,7 +14,7 @@ public class NormalSpendAnonScoreTests public void OneOwnInOneOut() { var analyzer = new BlockchainAnalyzer(); - var tx = BitcoinFactory.CreateSmartTransaction(0, 1, 1, 0); + var tx = BitcoinFactory.CreateSmartTransaction(TestRandom.Get(), 0, 1, 1, 0); var coin = Assert.Single(tx.WalletInputs); var key = coin.HdPubKey; key.SetAnonymitySet(3, tx.GetHash()); @@ -30,7 +31,7 @@ public void OneOwnInOneOut() public void ManyOwnInOneOut() { var analyzer = new BlockchainAnalyzer(); - var tx = BitcoinFactory.CreateSmartTransaction(0, 1, 3, 0); + var tx = BitcoinFactory.CreateSmartTransaction(TestRandom.Get(), 0, 1, 3, 0); foreach (var coin in tx.WalletInputs) { @@ -49,7 +50,7 @@ public void ManyOwnInOneOut() public void OneOwnInManyOut() { var analyzer = new BlockchainAnalyzer(); - var tx = BitcoinFactory.CreateSmartTransaction(0, 3, 1, 0); + var tx = BitcoinFactory.CreateSmartTransaction(TestRandom.Get(), 0, 3, 1, 0); var coin = Assert.Single(tx.WalletInputs); var key = coin.HdPubKey; key.SetAnonymitySet(3, tx.GetHash()); @@ -66,7 +67,7 @@ public void OneOwnInManyOut() public void OneOwnInOneOutOneOwnOut() { var analyzer = new BlockchainAnalyzer(); - var tx = BitcoinFactory.CreateSmartTransaction(0, 1, 1, 1); + var tx = BitcoinFactory.CreateSmartTransaction(TestRandom.Get(), 0, 1, 1, 1); foreach (var coin in tx.WalletInputs) { @@ -86,7 +87,7 @@ public void OneOwnInOneOutOneOwnOut() public void OneOwnInManyOutManyOwnOut() { var analyzer = new BlockchainAnalyzer(); - var tx = BitcoinFactory.CreateSmartTransaction(0, 3, 1, 3); + var tx = BitcoinFactory.CreateSmartTransaction(TestRandom.Get(), 0, 3, 1, 3); foreach (var coin in tx.WalletInputs) { @@ -105,7 +106,7 @@ public void OneOwnInManyOutManyOwnOut() public void ManyOwnInOneOutOneOwnOut() { var analyzer = new BlockchainAnalyzer(); - var tx = BitcoinFactory.CreateSmartTransaction(0, 1, 3, 1); + var tx = BitcoinFactory.CreateSmartTransaction(TestRandom.Get(), 0, 1, 3, 1); foreach (var coin in tx.WalletInputs) { @@ -124,7 +125,7 @@ public void ManyOwnInOneOutOneOwnOut() public void ManyOwnInManyOutManyOwnOut() { var analyzer = new BlockchainAnalyzer(); - var tx = BitcoinFactory.CreateSmartTransaction(0, 3, 3, 3); + var tx = BitcoinFactory.CreateSmartTransaction(TestRandom.Get(), 0, 3, 3, 3); foreach (var coin in tx.WalletInputs) { diff --git a/WalletWasabi.Tests/UnitTests/BlockchainAnalysis/PubKeyReuseAnonScoreTests.cs b/WalletWasabi.Tests/UnitTests/BlockchainAnalysis/PubKeyReuseAnonScoreTests.cs index e0a4127157..0fc7cdbc14 100644 --- a/WalletWasabi.Tests/UnitTests/BlockchainAnalysis/PubKeyReuseAnonScoreTests.cs +++ b/WalletWasabi.Tests/UnitTests/BlockchainAnalysis/PubKeyReuseAnonScoreTests.cs @@ -3,6 +3,7 @@ using WalletWasabi.Blockchain.Analysis; using WalletWasabi.Blockchain.Keys; using WalletWasabi.Tests.Helpers; +using WalletWasabi.Tests.TestCommon; using Xunit; namespace WalletWasabi.Tests.UnitTests.BlockchainAnalysis; @@ -17,6 +18,7 @@ public void AddressReusePunishment() var km = ServiceFactory.CreateKeyManager(); var reuse = BitcoinFactory.CreateHdPubKey(km); var tx = BitcoinFactory.CreateSmartTransaction( + TestRandom.Get(), othersInputCount: 9, Enumerable.Repeat(Money.Coins(1m), 9), new[] { (Money.Coins(1.1m), 100, BitcoinFactory.CreateHdPubKey(km)) }, @@ -40,6 +42,7 @@ public void AddressReusePunishmentProcessTwice() var km = ServiceFactory.CreateKeyManager(); var reuse = BitcoinFactory.CreateHdPubKey(km); var tx = BitcoinFactory.CreateSmartTransaction( + TestRandom.Get(), othersInputCount: 9, Enumerable.Repeat(Money.Coins(1m), 9), new[] { (Money.Coins(1.1m), 100, BitcoinFactory.CreateHdPubKey(km)) }, @@ -68,6 +71,7 @@ public void SelfSpendReuse() var km = ServiceFactory.CreateKeyManager(); var reuse = BitcoinFactory.CreateHdPubKey(km); var tx = BitcoinFactory.CreateSmartTransaction( + TestRandom.Get(), othersInputCount: 0, Enumerable.Empty(), new[] { (Money.Coins(1.1m), 100, BitcoinFactory.CreateHdPubKey(km)) }, @@ -92,6 +96,7 @@ public void AddressReuseIrrelevantInNormalSpend() var km = ServiceFactory.CreateKeyManager(); var key = BitcoinFactory.CreateHdPubKey(km); var tx = BitcoinFactory.CreateSmartTransaction( + TestRandom.Get(), othersInputCount: 0, Enumerable.Repeat(Money.Coins(1m), 9), new[] { (Money.Coins(1.1m), 100, key), (Money.Coins(1.2m), 100, key), (Money.Coins(1.3m), 100, key), (Money.Coins(1.4m), 100, key) }, @@ -111,6 +116,7 @@ public void InputSideAddressReuseHaveNoConsolidationPunishmentInSelfSpend() var km = ServiceFactory.CreateKeyManager(); var key = BitcoinFactory.CreateHdPubKey(km); var tx = BitcoinFactory.CreateSmartTransaction( + TestRandom.Get(), othersInputCount: 0, Enumerable.Empty(), new[] { (Money.Coins(1.1m), 100, key), (Money.Coins(1.2m), 100, key), (Money.Coins(1.3m), 100, key), (Money.Coins(1.4m), 100, key) }, @@ -129,6 +135,7 @@ public void InputSideAddressReuseHaveNoConsolidationPunishmentInCoinJoin() var km = ServiceFactory.CreateKeyManager(); var key = BitcoinFactory.CreateHdPubKey(km); var tx = BitcoinFactory.CreateSmartTransaction( + TestRandom.Get(), othersInputCount: 9, Enumerable.Repeat(Money.Coins(1m), 9), new[] { (Money.Coins(1.1m), 100, key), (Money.Coins(1.2m), 100, key), (Money.Coins(1.3m), 100, key), (Money.Coins(1.4m), 100, key) }, @@ -147,6 +154,7 @@ public void InputOutputSideAddress() var analyser = new BlockchainAnalyzer(); var key = BitcoinFactory.CreateHdPubKey(ServiceFactory.CreateKeyManager()); var tx = BitcoinFactory.CreateSmartTransaction( + TestRandom.Get(), othersInputCount: 9, Enumerable.Repeat(Money.Coins(1m), 9), new[] { (Money.Coins(1.1m), 100, key) }, @@ -165,6 +173,7 @@ public void InputOutputSidePreviouslyUsedAddress() var analyser = new BlockchainAnalyzer(); var reuse = BitcoinFactory.CreateHdPubKey(ServiceFactory.CreateKeyManager()); var tx = BitcoinFactory.CreateSmartTransaction( + TestRandom.Get(), othersInputCount: 9, Enumerable.Repeat(Money.Coins(1m), 9), new[] { (Money.Coins(1.1m), 100, BitcoinFactory.CreateHdPubKey(ServiceFactory.CreateKeyManager())) }, @@ -185,6 +194,7 @@ public void OutputSideAddressReusePunished() var km = ServiceFactory.CreateKeyManager(); var key = BitcoinFactory.CreateHdPubKey(km); var tx = BitcoinFactory.CreateSmartTransaction( + TestRandom.Get(), othersInputCount: 9, Enumerable.Repeat(Money.Coins(1m), 9).Concat(Enumerable.Repeat(Money.Coins(2m), 7)), new[] { (Money.Coins(1.1m), 100, BitcoinFactory.CreateHdPubKey(km)) }, @@ -206,6 +216,7 @@ public void OutputSideAddressReuseDoesntPunishedMoreThanInheritance() var km = ServiceFactory.CreateKeyManager(); var key = BitcoinFactory.CreateHdPubKey(km); var tx = BitcoinFactory.CreateSmartTransaction( + TestRandom.Get(), othersInputCount: 9, Enumerable.Repeat(Money.Coins(1m), 9).Concat(Enumerable.Repeat(Money.Coins(2m), 8)).Concat(Enumerable.Repeat(Money.Coins(3m), 7)).Concat(Enumerable.Repeat(Money.Coins(4m), 6)).Concat(Enumerable.Repeat(Money.Coins(5m), 5)).Concat(Enumerable.Repeat(Money.Coins(6m), 4)), new[] { (Money.Coins(1.1m), 100, BitcoinFactory.CreateHdPubKey(km)) }, @@ -230,6 +241,7 @@ public void OutputSideAddressReuseBySomeoneElse() using var destination = new Key(); var reusedTxOut = new TxOut(equalOutputAmount, destination); var tx = BitcoinFactory.CreateSmartTransaction( + TestRandom.Get(), othersInputCount: 9, Enumerable.Range(0, 7).Select(_ => new TxOut(equalOutputAmount, new Key())).Concat([reusedTxOut, reusedTxOut]), [(Money.Coins(1.1m), 1, BitcoinFactory.CreateHdPubKey(km))], @@ -248,7 +260,7 @@ public void OutputSideAddressReuseBySomeoneElse() public void CoinJoinSend() { var analyser = new BlockchainAnalyzer(); - var tx = BitcoinFactory.CreateSmartTransaction(2, 2, 40, 0); + var tx = BitcoinFactory.CreateSmartTransaction(TestRandom.Get(), 2, 2, 40, 0); // Make sure that Analyze won't throw in case of no own outputs. analyser.Analyze(tx); diff --git a/WalletWasabi.Tests/UnitTests/BlockchainAnalysis/ReceiveAnonScoreTests.cs b/WalletWasabi.Tests/UnitTests/BlockchainAnalysis/ReceiveAnonScoreTests.cs index 4315137518..084b7baae5 100644 --- a/WalletWasabi.Tests/UnitTests/BlockchainAnalysis/ReceiveAnonScoreTests.cs +++ b/WalletWasabi.Tests/UnitTests/BlockchainAnalysis/ReceiveAnonScoreTests.cs @@ -3,6 +3,7 @@ using WalletWasabi.Blockchain.Analysis; using WalletWasabi.Blockchain.Keys; using WalletWasabi.Tests.Helpers; +using WalletWasabi.Tests.TestCommon; using Xunit; namespace WalletWasabi.Tests.UnitTests.BlockchainAnalysis; @@ -16,7 +17,7 @@ public class ReceiveAnonScoreTests public void NormalReceive() { var analyser = new BlockchainAnalyzer(); - var tx = BitcoinFactory.CreateSmartTransaction(1, 1, 0, 1); + var tx = BitcoinFactory.CreateSmartTransaction(TestRandom.Get(), 1, 1, 0, 1); analyser.Analyze(tx); @@ -28,7 +29,7 @@ public void NormalReceive() public void WholeCoinReceive() { var analyser = new BlockchainAnalyzer(); - var tx = BitcoinFactory.CreateSmartTransaction(1, 0, 0, 1); + var tx = BitcoinFactory.CreateSmartTransaction(TestRandom.Get(), 1, 0, 0, 1); analyser.Analyze(tx); @@ -40,7 +41,7 @@ public void WholeCoinReceive() public void CoinjoinReceive() { var analyser = new BlockchainAnalyzer(); - var tx = BitcoinFactory.CreateSmartTransaction(10, Enumerable.Repeat(Money.Coins(1m), 9), Enumerable.Empty<(Money, int)>(), new[] { (Money.Coins(1m), HdPubKey.DefaultHighAnonymitySet) }); + var tx = BitcoinFactory.CreateSmartTransaction(TestRandom.Get(), 10, Enumerable.Repeat(Money.Coins(1m), 9), Enumerable.Empty<(Money, int)>(), new[] { (Money.Coins(1m), HdPubKey.DefaultHighAnonymitySet) }); analyser.Analyze(tx); diff --git a/WalletWasabi.Tests/UnitTests/BlockchainAnalysis/SelfSpendAnonScoreTests.cs b/WalletWasabi.Tests/UnitTests/BlockchainAnalysis/SelfSpendAnonScoreTests.cs index 372e4ffd9b..7367e1e379 100644 --- a/WalletWasabi.Tests/UnitTests/BlockchainAnalysis/SelfSpendAnonScoreTests.cs +++ b/WalletWasabi.Tests/UnitTests/BlockchainAnalysis/SelfSpendAnonScoreTests.cs @@ -2,6 +2,7 @@ using System.Linq; using WalletWasabi.Blockchain.Analysis; using WalletWasabi.Tests.Helpers; +using WalletWasabi.Tests.TestCommon; using Xunit; namespace WalletWasabi.Tests.UnitTests.BlockchainAnalysis; @@ -15,7 +16,7 @@ public class SelfSpendAnonScoreTests public void OneOwnInOneOwnOut() { var analyser = new BlockchainAnalyzer(); - var tx = BitcoinFactory.CreateSmartTransaction(0, 0, 1, 1); + var tx = BitcoinFactory.CreateSmartTransaction(TestRandom.Get(), 0, 0, 1, 1); var coin = Assert.Single(tx.WalletInputs); var key = coin.HdPubKey; key.SetAnonymitySet(3, tx.GetHash()); @@ -33,7 +34,7 @@ public void OneOwnInOneOwnOut() public void OneOwnInManyOwnOut() { var analyser = new BlockchainAnalyzer(); - var tx = BitcoinFactory.CreateSmartTransaction(0, 0, 1, 3); + var tx = BitcoinFactory.CreateSmartTransaction(TestRandom.Get(), 0, 0, 1, 3); var coin = Assert.Single(tx.WalletInputs); var key = coin.HdPubKey; key.SetAnonymitySet(3, tx.GetHash()); @@ -52,7 +53,7 @@ public void OneOwnInManyOwnOut() public void ManyOwnInOneOwnOut() { var analyser = new BlockchainAnalyzer(); - var tx = BitcoinFactory.CreateSmartTransaction(0, 0, 3, 1); + var tx = BitcoinFactory.CreateSmartTransaction(TestRandom.Get(), 0, 0, 3, 1); var smallestAnonset = 3; tx.WalletInputs.First().HdPubKey.SetAnonymitySet(smallestAnonset, uint256.One); @@ -72,7 +73,7 @@ public void ManyOwnInOneOwnOut() public void ManyOwnInManyOwnOut() { var analyser = new BlockchainAnalyzer(); - var tx = BitcoinFactory.CreateSmartTransaction(0, 0, 3, 3); + var tx = BitcoinFactory.CreateSmartTransaction(TestRandom.Get(), 0, 0, 3, 3); var smallestAnonset = 3; tx.WalletInputs.First().HdPubKey.SetAnonymitySet(smallestAnonset, uint256.One); diff --git a/WalletWasabi.Tests/UnitTests/BlockchainAnalysis/WalletIrrelevantAnonScoreTests.cs b/WalletWasabi.Tests/UnitTests/BlockchainAnalysis/WalletIrrelevantAnonScoreTests.cs index 01f3cbecde..e86159290f 100644 --- a/WalletWasabi.Tests/UnitTests/BlockchainAnalysis/WalletIrrelevantAnonScoreTests.cs +++ b/WalletWasabi.Tests/UnitTests/BlockchainAnalysis/WalletIrrelevantAnonScoreTests.cs @@ -1,5 +1,6 @@ using WalletWasabi.Blockchain.Analysis; using WalletWasabi.Tests.Helpers; +using WalletWasabi.Tests.TestCommon; using Xunit; namespace WalletWasabi.Tests.UnitTests.BlockchainAnalysis; @@ -13,7 +14,7 @@ public class WalletIrrelevantAnonScoreTests public void OneInOneOut() { var analyser = new BlockchainAnalyzer(); - var tx = BitcoinFactory.CreateSmartTransaction(1, 1, 0, 0); + var tx = BitcoinFactory.CreateSmartTransaction(TestRandom.Get(), 1, 1, 0, 0); analyser.Analyze(tx); @@ -25,7 +26,7 @@ public void OneInOneOut() public void ManyInManyOut() { var analyser = new BlockchainAnalyzer(); - var tx = BitcoinFactory.CreateSmartTransaction(3, 3, 0, 0); + var tx = BitcoinFactory.CreateSmartTransaction(TestRandom.Get(), 3, 3, 0, 0); analyser.Analyze(tx); diff --git a/WalletWasabi.Tests/UnitTests/InvalidTxExceptionTests.cs b/WalletWasabi.Tests/UnitTests/InvalidTxExceptionTests.cs index 8f97eccdb5..799bb23dd8 100644 --- a/WalletWasabi.Tests/UnitTests/InvalidTxExceptionTests.cs +++ b/WalletWasabi.Tests/UnitTests/InvalidTxExceptionTests.cs @@ -1,9 +1,10 @@ -using System.Linq; using NBitcoin; using NBitcoin.Policy; +using System.Linq; using WalletWasabi.Blockchain.Keys; using WalletWasabi.Exceptions; using WalletWasabi.Tests.Helpers; +using WalletWasabi.Tests.TestCommon; using Xunit; namespace WalletWasabi.Tests.UnitTests; @@ -14,6 +15,7 @@ public class InvalidTxExceptionTests public void ExceptionMessageContainsUsefulInformation() { var crazyInvalidTx = BitcoinFactory.CreateSmartTransaction( + TestRandom.Get(), 9, Enumerable.Repeat(Money.Coins(1m), 9), new[] { (Money.Coins(1.1m), 1) }, diff --git a/WalletWasabi.Tests/UnitTests/KeyManagementTests.cs b/WalletWasabi.Tests/UnitTests/KeyManagementTests.cs index 73a125beea..ae0d166d0f 100644 --- a/WalletWasabi.Tests/UnitTests/KeyManagementTests.cs +++ b/WalletWasabi.Tests/UnitTests/KeyManagementTests.cs @@ -2,9 +2,11 @@ using System.IO; using System.Linq; using System.Security; +using WabiSabi.Crypto.Randomness; using WalletWasabi.Blockchain.Analysis.Clustering; using WalletWasabi.Blockchain.Keys; using WalletWasabi.Crypto.Randomness; +using WalletWasabi.Helpers; using WalletWasabi.Logging; using WalletWasabi.Models; using WalletWasabi.Tests.TestCommon; @@ -125,12 +127,12 @@ public void CanSerialize() manager.ToFile(); // assert it does not throw - void Generate500keys(ScriptPubKeyType scriptPubKeyType) + void Generate500keys(ScriptPubKeyType scriptPubKeyType, WasabiRandom rnd) { for (int i = 0; i < 500; i++) { var isInternal = Random.Shared.Next(2) == 0; - var label = RandomString.AlphaNumeric(21); + var label = rnd.GetString(21, Constants.AlphaNumericCharacters); var keyState = (KeyState)Random.Shared.Next(3); manager.GenerateNewKey(label, keyState, isInternal, scriptPubKeyType); } @@ -138,8 +140,9 @@ void Generate500keys(ScriptPubKeyType scriptPubKeyType) manager.ToFile(); } - Generate500keys(ScriptPubKeyType.Segwit); - Generate500keys(ScriptPubKeyType.TaprootBIP86); + var rnd = TestRandom.Wasabi(); + Generate500keys(ScriptPubKeyType.Segwit, rnd); + Generate500keys(ScriptPubKeyType.TaprootBIP86, rnd); Assert.True(File.Exists(filePath)); @@ -173,10 +176,11 @@ public void CanGenerateKeys() var k1 = manager.GenerateNewKey(LabelsArray.Empty, KeyState.Clean, true); Assert.Equal(LabelsArray.Empty, k1.Labels); + var rnd = TestRandom.Wasabi(); for (int i = 0; i < 1000; i++) { var isInternal = Random.Shared.Next(2) == 0; - var label = RandomString.AlphaNumeric(21); + var label = rnd.GetString(21, Constants.AlphaNumericCharacters); var keyState = (KeyState)Random.Shared.Next(3); var generatedKey = manager.GenerateNewKey(label, keyState, isInternal); diff --git a/WalletWasabi.Tests/UnitTests/SafeIoManagerTests.cs b/WalletWasabi.Tests/UnitTests/SafeIoManagerTests.cs index 2b8a7a23ef..603d70193d 100644 --- a/WalletWasabi.Tests/UnitTests/SafeIoManagerTests.cs +++ b/WalletWasabi.Tests/UnitTests/SafeIoManagerTests.cs @@ -1,3 +1,4 @@ +using GingerCommon.Crypto.Random; using Nito.AsyncEx; using System.Collections.Generic; using System.IO; @@ -5,7 +6,6 @@ using System.Text; using System.Threading; using System.Threading.Tasks; -using WalletWasabi.Crypto.Randomness; using WalletWasabi.Helpers; using WalletWasabi.Io; using WalletWasabi.Tests.TestCommon; @@ -23,11 +23,11 @@ public async Task IoManagerTestsAsync() { var file = Path.Combine(TestDirectory.Get(), "file1.dat"); + var rnd = TestRandom.Wasabi(); List lines = new(); for (int i = 0; i < 1000; i++) { - string line = RandomString.AlphaNumeric(100); - + string line = rnd.GetString(100, Constants.AlphaNumericCharacters); lines.Add(line); } @@ -52,10 +52,7 @@ static bool IsStringArraysEqual(string[] lines1, string[] lines2) for (int i = 0; i < lines1.Length; i++) { - string line = lines2[i]; - var readLine = lines1[i]; - - if (!line.Equals(readLine)) + if (lines1[i] != lines2[i]) { return false; } @@ -105,11 +102,7 @@ static bool IsStringArraysEqual(string[] lines1, string[] lines2) // TryReplace test. var dummyFilePath = $"{ioman1.FilePath}dummy"; - var dummyContent = new string[] - { - "banana", - "peach" - }; + string[] dummyContent = ["banana", "peach"]; await File.WriteAllLinesAsync(dummyFilePath, dummyContent); await ioman1.WriteAllLinesAsync(lines); @@ -138,22 +131,22 @@ public async Task IoTestsAsync() IoHelpers.EnsureContainingDirectoryExists(ioman.FilePath); File.Create(ioman.FilePath).Dispose(); - static string RandomString() + static string RandomString(GingerRandom rnd) { StringBuilder builder = new(); char ch; - for (int i = 0; i < Random.Shared.Next(10, 100); i++) + for (int i = 0, len = rnd.GetInt(10, 100); i < len; i++) { - ch = Convert.ToChar(Convert.ToInt32(Math.Floor((26 * Random.Shared.NextDouble()) + 65))); + ch = Convert.ToChar(rnd.GetInt(65, 65 + 26)); builder.Append(ch); } return builder.ToString(); } var list = new List(); - async Task WriteNextLineAsync() + async Task WriteNextLineAsync(GingerRandom rnd) { - var next = RandomString(); + var next = RandomString(rnd); lock (list) { list.Add(next); @@ -171,6 +164,7 @@ async Task WriteNextLineAsync() var t1 = new Thread(() => { + var rnd = TestRandom.Get(1); for (var i = 0; i < Iterations; i++) { /* We have to block the Thread. @@ -178,21 +172,23 @@ async Task WriteNextLineAsync() * which is not true because the WriteNextLineAsync() is not yet finished. The reason is that await will return execution * the to the calling thread it is detected as the thread is done. t1 and t2 and t3 will still run in parallel! */ - WriteNextLineAsync().Wait(); + WriteNextLineAsync(rnd).Wait(); } }); var t2 = new Thread(() => { + var rnd = TestRandom.Get(2); for (var i = 0; i < Iterations; i++) { - WriteNextLineAsync().Wait(); + WriteNextLineAsync(rnd).Wait(); } }); var t3 = new Thread(() => { + var rnd = TestRandom.Get(3); for (var i = 0; i < Iterations; i++) { - WriteNextLineAsync().Wait(); + WriteNextLineAsync(rnd).Wait(); } }); diff --git a/WalletWasabi.Tests/UnitTests/SmartCoinTests.cs b/WalletWasabi.Tests/UnitTests/SmartCoinTests.cs index 772b45b278..45a8260f66 100644 --- a/WalletWasabi.Tests/UnitTests/SmartCoinTests.cs +++ b/WalletWasabi.Tests/UnitTests/SmartCoinTests.cs @@ -6,6 +6,7 @@ using WalletWasabi.Blockchain.Transactions; using WalletWasabi.Models; using WalletWasabi.Tests.Helpers; +using WalletWasabi.Tests.TestCommon; using Xunit; namespace WalletWasabi.Tests.UnitTests; @@ -41,6 +42,7 @@ public void SmartCoinEquality() [Fact] public void IsSufficientlyDistancedFromExternalKeys() { + var rnd = TestRandom.Get(); // 1. External, no inputs ours: var tx = Transaction.Parse("0100000000010176f521a178a4b394b647169a89b29bb0d95b6bce192fd686d533eb4ea98464a20000000000ffffffff02ed212d020000000016001442da650f25abde0fef57badd745df7346e3e7d46fbda6400000000001976a914bd2e4029ce7d6eca7d2c3779e8eac36c952afee488ac02483045022100ea43ccf95e1ac4e8b305c53761da7139dbf6ff164e137a6ce9c09e15f316c22902203957818bc505bbafc181052943d7ab1f3ae82c094bf749813e8f59108c6c268a012102e59e61f20c7789aa73faf5a92dc8c0424e538c635c55d64326d95059f0f8284200000000", Network.TestNet); var index = 0U; @@ -65,7 +67,7 @@ public void IsSufficientlyDistancedFromExternalKeys() hdpk = km.GenerateNewKey(LabelsArray.Empty, KeyState.Clean, isInternal: false); tx.Outputs[0].ScriptPubKey = hdpk.P2wpkhScript; var inHdpk = km.GenerateNewKey(LabelsArray.Empty, KeyState.Clean, isInternal: false); - var inCoin = BitcoinFactory.CreateSmartCoin(inHdpk, 1m); + var inCoin = BitcoinFactory.CreateSmartCoin(rnd, inHdpk, 1m); tx.Inputs.Add(inCoin.Outpoint); stx = new SmartTransaction(tx, height); stx.TryAddWalletInput(inCoin); @@ -86,8 +88,8 @@ public void IsSufficientlyDistancedFromExternalKeys() hdpk = km.GenerateNewKey(LabelsArray.Empty, KeyState.Clean, isInternal: false); tx.Outputs[0].ScriptPubKey = hdpk.P2wpkhScript; inHdpk = km.GenerateNewKey(LabelsArray.Empty, KeyState.Clean, isInternal: true); - inCoin = BitcoinFactory.CreateSmartCoin(inHdpk, 1m); - inCoin.Transaction.TryAddWalletInput(BitcoinFactory.CreateSmartCoin(km.GenerateNewKey(LabelsArray.Empty, KeyState.Clean, isInternal: false), 1m)); + inCoin = BitcoinFactory.CreateSmartCoin(rnd, inHdpk, 1m); + inCoin.Transaction.TryAddWalletInput(BitcoinFactory.CreateSmartCoin(rnd, km.GenerateNewKey(LabelsArray.Empty, KeyState.Clean, isInternal: false), 1m)); tx.Inputs[1] = new TxIn(inCoin.Outpoint); stx = new SmartTransaction(tx, height); stx.TryAddWalletInput(inCoin); @@ -108,7 +110,7 @@ public void IsSufficientlyDistancedFromExternalKeys() hdpk = km.GenerateNewKey(LabelsArray.Empty, KeyState.Clean, isInternal: false); tx.Outputs[0].ScriptPubKey = hdpk.P2wpkhScript; var inHdpk2 = km.GenerateNewKey(LabelsArray.Empty, KeyState.Clean, isInternal: true); - var inCoin2 = BitcoinFactory.CreateSmartCoin(inHdpk2, 1m); + var inCoin2 = BitcoinFactory.CreateSmartCoin(rnd, inHdpk2, 1m); tx.Inputs.Add(inCoin2.Outpoint); stx = new SmartTransaction(tx, height); stx.TryAddWalletInput(inCoin); diff --git a/WalletWasabi.Tests/UnitTests/Tor/Socks5/Pool/TorHttpPoolTests.cs b/WalletWasabi.Tests/UnitTests/Tor/Socks5/Pool/TorHttpPoolTests.cs index 599f1f058c..e82840646b 100644 --- a/WalletWasabi.Tests/UnitTests/Tor/Socks5/Pool/TorHttpPoolTests.cs +++ b/WalletWasabi.Tests/UnitTests/Tor/Socks5/Pool/TorHttpPoolTests.cs @@ -159,7 +159,7 @@ public async Task RequestAndReplyAsync() string[] expectedResponse = new[] { "GET / HTTP/1.1", - "Accept-Encoding:gzip", + "Accept-Encoding:br", "Host:somesite.com", "" }; @@ -249,7 +249,7 @@ public async Task RedirectSupportAsync(int maximumRedirects) string[] expectedServerResponse1 = new[] { "GET /redirect/123456 HTTP/1.1", - "Accept-Encoding:gzip", + "Accept-Encoding:br", "Host:api.github.com", "" }; @@ -297,7 +297,7 @@ Resource found. string[] expectedServerResponse2 = new[] { "GET /github-production-release-asset-2e65be/55341469/84261958-b5b5-45dc-a1fe-3bd96253e120?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAIWNJYAX4CSVEH53A%2F20221211%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20221211T094726Z&X-Amz-Expires=300&X-Amz-Signature=bee3014421243a64ae1d2ffc7ca5e3693cbbd49b08b386df7bd7569494d04a7f&X-Amz-SignedHeaders=host&actor_id=0&key_id=0&repo_id=55341469&response-content-disposition=attachment%3B%20filename%3DWasabi-2.0.1.4.msi&response-content-type=application%2Foctet-stream HTTP/1.1", - "Accept-Encoding:gzip", + "Accept-Encoding:br", "Host:objects.githubusercontent.com", "" }; diff --git a/WalletWasabi.Tests/UnitTests/Transactions/AllTransactionStoreTests.cs b/WalletWasabi.Tests/UnitTests/Transactions/AllTransactionStoreTests.cs index a6a5d2e1b8..c9f3c58142 100644 --- a/WalletWasabi.Tests/UnitTests/Transactions/AllTransactionStoreTests.cs +++ b/WalletWasabi.Tests/UnitTests/Transactions/AllTransactionStoreTests.cs @@ -7,6 +7,7 @@ using WalletWasabi.Models; using WalletWasabi.Stores; using WalletWasabi.Tests.Helpers; +using WalletWasabi.Tests.TestCommon; using Xunit; namespace WalletWasabi.Tests.UnitTests.Transactions; @@ -67,7 +68,7 @@ public async Task CanInitializeEmptyAsync(Network network) Assert.Empty(txStore.ConfirmedStore.GetTransactions()); Assert.Empty(txStore.ConfirmedStore.GetTransactionHashes()); - uint256 txHash = BitcoinFactory.CreateSmartTransaction().GetHash(); + uint256 txHash = BitcoinFactory.CreateSmartTransaction(TestRandom.Get()).GetHash(); Assert.False(txStore.Contains(txHash)); Assert.True(txStore.IsEmpty()); Assert.False(txStore.TryGetTransaction(txHash, out _)); @@ -102,7 +103,7 @@ public async Task CanInitializeAsync() Assert.Equal(3, txStore.ConfirmedStore.GetTransactionHashes().Count); Assert.False(txStore.IsEmpty()); - uint256 doesntContainTxHash = BitcoinFactory.CreateSmartTransaction().GetHash(); + uint256 doesntContainTxHash = BitcoinFactory.CreateSmartTransaction(TestRandom.Get()).GetHash(); Assert.False(txStore.Contains(doesntContainTxHash)); Assert.False(txStore.TryGetTransaction(doesntContainTxHash, out _)); @@ -200,10 +201,11 @@ public async Task CorrectOrderAsync() [MemberData(nameof(GetDifferentNetworkValues))] public async Task DoesntUpdateAsync(Network network) { + var rnd = TestRandom.Get(); await using AllTransactionStore txStore = new(SqliteStorageHelper.InMemoryDatabase, network); await txStore.InitializeAsync(); - var tx = BitcoinFactory.CreateSmartTransaction(); + var tx = BitcoinFactory.CreateSmartTransaction(rnd); Assert.False(txStore.TryUpdate(tx)); // Assert TryUpdate didn't modify anything. @@ -214,7 +216,7 @@ public async Task DoesntUpdateAsync(Network network) Assert.Empty(txStore.ConfirmedStore.GetTransactions()); Assert.Empty(txStore.ConfirmedStore.GetTransactionHashes()); - uint256 txid = BitcoinFactory.CreateSmartTransaction().GetHash(); + uint256 txid = BitcoinFactory.CreateSmartTransaction(rnd).GetHash(); Assert.False(txStore.Contains(txid)); Assert.True(txStore.IsEmpty()); Assert.False(txStore.TryGetTransaction(txid, out _)); diff --git a/WalletWasabi.Tests/UnitTests/Transactions/PayjoinTests.cs b/WalletWasabi.Tests/UnitTests/Transactions/PayjoinTests.cs index 58cf6a2970..4f81b28d29 100644 --- a/WalletWasabi.Tests/UnitTests/Transactions/PayjoinTests.cs +++ b/WalletWasabi.Tests/UnitTests/Transactions/PayjoinTests.cs @@ -1,16 +1,17 @@ using NBitcoin; -using System.Threading.Tasks; using System.Linq; -using WalletWasabi.Blockchain.Keys; -using WalletWasabi.Blockchain.TransactionBuilding; -using WalletWasabi.WebClients.PayJoin; -using Xunit; -using System.Net.Http; using System.Net; +using System.Net.Http; +using System.Net.Mime; using System.Text; +using System.Threading.Tasks; +using WalletWasabi.Blockchain.Keys; +using WalletWasabi.Blockchain.TransactionBuilding; using WalletWasabi.Tests.Helpers; +using WalletWasabi.Tests.TestCommon; using WalletWasabi.Tor.Http; -using System.Net.Mime; +using WalletWasabi.WebClients.PayJoin; +using Xunit; namespace WalletWasabi.Tests.UnitTests.Transactions; @@ -68,6 +69,7 @@ public void LazyPayjoinServerTest() var payjoinClient = NewPayjoinClient(mockHttpClient); var transactionFactory = ServiceFactory.CreateTransactionFactory( + TestRandom.Get(), new[] { ("Pablo", 0, 0.1m, confirmed: true, anonymitySet: 1) @@ -94,6 +96,7 @@ public void LazyPayjoinServerTest() [Fact] public void HonestPayjoinServerTest() { + var rnd = TestRandom.Get(); var amountToPay = Money.Coins(0.001m); // This tests the scenario where the payjoin server behaves as expected. @@ -124,6 +127,7 @@ public void HonestPayjoinServerTest() var payjoinClient = NewPayjoinClient(mockHttpClient); var transactionFactory = ServiceFactory.CreateTransactionFactory( + rnd, new[] { ("Pablo", 0, 0.1m, confirmed: true, anonymitySet: 1) @@ -149,6 +153,7 @@ public void HonestPayjoinServerTest() Assert.Equal(0.09899718m, innerOutput.Amount.ToUnit(MoneyUnit.BTC)); transactionFactory = ServiceFactory.CreateTransactionFactory( + rnd, new[] { ("Pablo", 0, 0.1m, confirmed: true, anonymitySet: 1) @@ -194,7 +199,7 @@ public void DishonestPayjoinServerTest() return psbt; }); - var transactionFactory = ServiceFactory.CreateTransactionFactory(walletCoins); + var transactionFactory = ServiceFactory.CreateTransactionFactory(TestRandom.Get(), walletCoins); var txParameters = CreateBuilder() .SetPayment(payment) @@ -248,7 +253,7 @@ public void BadImplementedPayjoinServerTest() return psbt; }); - var transactionFactory = ServiceFactory.CreateTransactionFactory(walletCoins); + var transactionFactory = ServiceFactory.CreateTransactionFactory(TestRandom.Get(), walletCoins); var txParameters = CreateBuilder() .SetPayment(payment) .SetAllowedInputs(transactionFactory.Coins.Select(x => x.Outpoint)) @@ -397,6 +402,7 @@ public void BadImplementedPayjoinServerTest() [Fact] public void MinersLoverPayjoinServerTest() { + var rnd = TestRandom.Get(); // The server wants to make us sign a transaction that pays too much fee var walletCoins = new[] { ("Pablo", 0, 0.1m, confirmed: true, anonymitySet: 1) }; var amountToPay = Money.Coins(0.001m); @@ -414,7 +420,7 @@ public void MinersLoverPayjoinServerTest() return PSBT.FromTransaction(globalTx, Network.Main); }); - var transactionFactory = ServiceFactory.CreateTransactionFactory(walletCoins); + var transactionFactory = ServiceFactory.CreateTransactionFactory(rnd, walletCoins); var txParameters = CreateBuilder() .SetPayment(payment) .SetAllowedInputs(transactionFactory.Coins.Select(x => x.Outpoint)) @@ -436,7 +442,7 @@ public void BrokenPayjoinServerTest() mockHttpClient.OnSendAsync = req => PayjoinServerErrorAsync(HttpStatusCode.InternalServerError, "-2345", "Internal Server Error"); - var transactionFactory = ServiceFactory.CreateTransactionFactory(walletCoins); + var transactionFactory = ServiceFactory.CreateTransactionFactory(TestRandom.Get(), walletCoins); var txParameters = CreateBuilder() .SetPayment(payment) .SetAllowedInputs(transactionFactory.Coins.Select(x => x.Outpoint)) diff --git a/WalletWasabi.Tests/UnitTests/Transactions/SmartTransactionTests.cs b/WalletWasabi.Tests/UnitTests/Transactions/SmartTransactionTests.cs index 6c654ab87c..ae852cc31e 100644 --- a/WalletWasabi.Tests/UnitTests/Transactions/SmartTransactionTests.cs +++ b/WalletWasabi.Tests/UnitTests/Transactions/SmartTransactionTests.cs @@ -6,6 +6,7 @@ using WalletWasabi.Helpers; using WalletWasabi.Models; using WalletWasabi.Tests.Helpers; +using WalletWasabi.Tests.TestCommon; using Xunit; namespace WalletWasabi.Tests.UnitTests.Transactions; @@ -146,7 +147,7 @@ public void SmartTransactionLineDeserialization() [Fact] public void SmartTransactionVirtualMembersEquals() { - SmartTransaction st = BitcoinFactory.CreateSmartTransaction(0, 1, 1, 1); + SmartTransaction st = BitcoinFactory.CreateSmartTransaction(TestRandom.Get(), 0, 1, 1, 1); var originalSentBytes = st.WalletInputs.First().HdPubKey.PubKey.ToBytes(); var virtualSentBytes = st.WalletVirtualInputs.First().Id; @@ -205,6 +206,7 @@ public void SmartTransactionVirtualForeignOutputMerge() [Fact] public void SmartTransactionVirtualWalletInputMerge() { + var rnd = TestRandom.Get(); var km = ServiceFactory.CreateKeyManager(""); var network = km.GetNetwork(); HdPubKey hdPubKey = BitcoinFactory.CreateHdPubKey(km); @@ -213,8 +215,8 @@ public void SmartTransactionVirtualWalletInputMerge() SmartTransaction st1 = new(t, 0); - var sc = BitcoinFactory.CreateSmartCoin(hdPubKey, Money.Coins(1)); - var sc2 = BitcoinFactory.CreateSmartCoin(hdPubKey, Money.Coins(2)); + var sc = BitcoinFactory.CreateSmartCoin(rnd, hdPubKey, Money.Coins(1)); + var sc2 = BitcoinFactory.CreateSmartCoin(rnd, hdPubKey, Money.Coins(2)); st1.TryAddWalletInput(sc); st1.TryAddWalletInput(sc2); @@ -226,14 +228,15 @@ public void SmartTransactionVirtualWalletInputMerge() [Fact] public void SmartTransactionVirtualWalletOutputMerge() { + var rnd = TestRandom.Get(); var km = ServiceFactory.CreateKeyManager(""); var network = km.GetNetwork(); HdPubKey hdPubKey = BitcoinFactory.CreateHdPubKey(km); Transaction t = Transaction.Create(network); - var sc = BitcoinFactory.CreateSmartCoin(t, hdPubKey, Money.Coins(1)); - var sc2 = BitcoinFactory.CreateSmartCoin(t, hdPubKey, Money.Coins(2)); + var sc = BitcoinFactory.CreateSmartCoin(rnd, t, hdPubKey, Money.Coins(1)); + var sc2 = BitcoinFactory.CreateSmartCoin(rnd, t, hdPubKey, Money.Coins(2)); Assert.NotEqual(sc, sc2); var st1 = sc.Transaction; diff --git a/WalletWasabi.Tests/UnitTests/Transactions/TransactionFactoryTests.cs b/WalletWasabi.Tests/UnitTests/Transactions/TransactionFactoryTests.cs index 35da68a4c7..d3f2bce285 100644 --- a/WalletWasabi.Tests/UnitTests/Transactions/TransactionFactoryTests.cs +++ b/WalletWasabi.Tests/UnitTests/Transactions/TransactionFactoryTests.cs @@ -8,6 +8,7 @@ using WalletWasabi.Blockchain.Transactions; using WalletWasabi.Exceptions; using WalletWasabi.Tests.Helpers; +using WalletWasabi.Tests.TestCommon; using WalletWasabi.Wallets; using Xunit; @@ -19,6 +20,7 @@ public class TransactionFactoryTests public void InsufficientBalance() { var transactionFactory = ServiceFactory.CreateTransactionFactory( + TestRandom.Get(), new[] { ("Martin", 0, 0.01m, confirmed: true, anonymitySet: 1), @@ -41,6 +43,7 @@ public void InsufficientBalance() public void TooMuchFeePaid() { var transactionFactory = ServiceFactory.CreateTransactionFactory( + TestRandom.Get(), new[] { ("Pablo", 0, 0.0001m, confirmed: true, anonymitySet: 1) @@ -69,6 +72,7 @@ public void TooMuchFeePaid() public void SelectMostPrivateIndependentlyOfCluster() { var transactionFactory = ServiceFactory.CreateTransactionFactory( + TestRandom.Get(), new[] { ("", 0, 0.08m, confirmed: true, anonymitySet: 50), @@ -101,6 +105,7 @@ public void SelectMostPrivateIndependentlyOfCluster() public void SelectMostPrivateCoin() { var transactionFactory = ServiceFactory.CreateTransactionFactory( + TestRandom.Get(), new[] { ("Maria", 0, 0.08m, confirmed: true, anonymitySet: 50), @@ -132,6 +137,7 @@ public void SelectMostPrivateCoin() public void SelectMostPrivateCoins() { var transactionFactory = ServiceFactory.CreateTransactionFactory( + TestRandom.Get(), new[] { ("Pablo", 0, 0.01m, confirmed: true, anonymitySet: 1), @@ -165,6 +171,7 @@ public void SelectMostPrivateCoins() public void SelectSameScriptPubKeyCoins() { var transactionFactory = ServiceFactory.CreateTransactionFactory( + TestRandom.Get(), new[] { ("Pablo", 0, 0.01m, confirmed: false, anonymitySet: 10), @@ -198,22 +205,23 @@ public void SelectSameScriptPubKeyCoins() [Fact] public async Task SelectSameClusterCoinsAsync() { + var rnd = TestRandom.Get(); var password = "foo"; var keyManager = ServiceFactory.CreateKeyManager(password); HdPubKey NewKey(string label) => keyManager.GenerateNewKey(label, KeyState.Used, true); var sCoins = new[] { - BitcoinFactory.CreateSmartCoin(NewKey("Pablo"), 0.9m), - BitcoinFactory.CreateSmartCoin(NewKey("Daniel"), 0.9m), - BitcoinFactory.CreateSmartCoin(NewKey("Adolf"), 0.9m), - BitcoinFactory.CreateSmartCoin(NewKey("Maria"), 0.9m), - BitcoinFactory.CreateSmartCoin(NewKey("Ding"), 0.9m), - BitcoinFactory.CreateSmartCoin(NewKey("Joseph"), 0.9m), - BitcoinFactory.CreateSmartCoin(NewKey("Eve"), 0.9m), - BitcoinFactory.CreateSmartCoin(NewKey("Julio"), 0.9m), - BitcoinFactory.CreateSmartCoin(NewKey("Donald, Jean, Lee, Jack"), 0.9m), - BitcoinFactory.CreateSmartCoin(NewKey("Satoshi"), 0.9m) + BitcoinFactory.CreateSmartCoin(rnd, NewKey("Pablo"), 0.9m), + BitcoinFactory.CreateSmartCoin(rnd, NewKey("Daniel"), 0.9m), + BitcoinFactory.CreateSmartCoin(rnd, NewKey("Adolf"), 0.9m), + BitcoinFactory.CreateSmartCoin(rnd, NewKey("Maria"), 0.9m), + BitcoinFactory.CreateSmartCoin(rnd, NewKey("Ding"), 0.9m), + BitcoinFactory.CreateSmartCoin(rnd, NewKey("Joseph"), 0.9m), + BitcoinFactory.CreateSmartCoin(rnd, NewKey("Eve"), 0.9m), + BitcoinFactory.CreateSmartCoin(rnd, NewKey("Julio"), 0.9m), + BitcoinFactory.CreateSmartCoin(rnd, NewKey("Donald, Jean, Lee, Jack"), 0.9m), + BitcoinFactory.CreateSmartCoin(rnd, NewKey("Satoshi"), 0.9m) }; var coinsByLabel = sCoins.ToDictionary(x => x.HdPubKey.Labels); @@ -288,6 +296,7 @@ public async Task SelectSameClusterCoinsAsync() public void CustomChangeScript() { var transactionFactory = ServiceFactory.CreateTransactionFactory( + TestRandom.Get(), new[] { ("Maria", 0, 1m, confirmed: true, anonymitySet: 100) @@ -320,6 +329,7 @@ public void CustomChangeScript() public void SubtractFeeFromSpecificOutput() { var transactionFactory = ServiceFactory.CreateTransactionFactory( + TestRandom.Get(), new[] { ("Maria", 0, 1m, confirmed: true, anonymitySet: 100) @@ -356,6 +366,7 @@ public void SubtractFeeFromSpecificOutput() public void SubtractFeeFromTooSmallOutput() { var transactionFactory = ServiceFactory.CreateTransactionFactory( + TestRandom.Get(), new[] { ("Maria", 0, 1m, confirmed: true, anonymitySet: 100) @@ -381,6 +392,7 @@ public void SubtractFeeFromTooSmallOutput() public void MultiplePaymentsToSameAddress() { var transactionFactory = ServiceFactory.CreateTransactionFactory( + TestRandom.Get(), new[] { ("Maria", 0, 1m, confirmed: true, anonymitySet: 100) @@ -416,6 +428,7 @@ public void MultiplePaymentsToSameAddress() public void SendAbsolutelyAllCoins() { var transactionFactory = ServiceFactory.CreateTransactionFactory( + TestRandom.Get(), new[] { ("Maria", 0, 0.5m, confirmed: false, anonymitySet: 1), @@ -444,6 +457,7 @@ public void SendAbsolutelyAllCoins() public void SpendOnlyAllowedCoins() { var transactionFactory = ServiceFactory.CreateTransactionFactory( + TestRandom.Get(), new[] { ("Pablo", 0, 0.01m, confirmed: false, anonymitySet: 50), @@ -478,6 +492,7 @@ public void SpendOnlyAllowedCoins() public void SpendWholeAllowedCoins() { var transactionFactory = ServiceFactory.CreateTransactionFactory( + TestRandom.Get(), new[] { ("Pablo", 0, 0.01m, confirmed: false, anonymitySet: 50), @@ -520,6 +535,7 @@ public void SpendWholeAllowedCoins() public void InsufficientAllowedCoins() { var transactionFactory = ServiceFactory.CreateTransactionFactory( + TestRandom.Get(), new[] { ("Pablo", 0, 0.01m, confirmed: true, anonymitySet: 1), @@ -550,6 +566,7 @@ public void InsufficientAllowedCoins() public void SpendWholeCoinsEvenWhenNotAllowed() { var transactionFactory = ServiceFactory.CreateTransactionFactory( + TestRandom.Get(), new[] { ("Pablo", 0, 0.01m, confirmed: false, anonymitySet: 50), @@ -590,6 +607,7 @@ public void SpendWholeCoinsEvenWhenNotAllowed() public void DoNotSignWatchOnly() { var transactionFactory = ServiceFactory.CreateTransactionFactory( + TestRandom.Get(), new[] { ("Pablo", 0, 1m, confirmed: true, anonymitySet: 1) @@ -614,7 +632,7 @@ public void TooManyInputCoins() Money paymentAmount = Money.Coins(0.29943925m); using Key key = new(); - TransactionFactory transactionFactory = ServiceFactory.CreateTransactionFactory(DemoCoinSets.LotOfCoins); + TransactionFactory transactionFactory = ServiceFactory.CreateTransactionFactory(TestRandom.Get(), DemoCoinSets.LotOfCoins); PaymentIntent payment = new(key, MoneyRequest.Create(paymentAmount)); Assert.Equal(ChangeStrategy.Auto, payment.ChangeStrategy); @@ -718,7 +736,7 @@ public void TransactionSizeExceptionTest() ("", 81, 0.00006292m, true, 1) }; - TransactionFactory transactionFactory = ServiceFactory.CreateTransactionFactory(coins); + TransactionFactory transactionFactory = ServiceFactory.CreateTransactionFactory(TestRandom.Get(), coins); PaymentIntent payment = new(key, MoneyRequest.Create(paymentAmount)); Assert.Equal(ChangeStrategy.Auto, payment.ChangeStrategy); @@ -805,6 +823,7 @@ public void HowFeeRateAffectsFeeAndNumberOfOutputs() static BuildTransactionResult ComputeTxResult(decimal feeRate) { TransactionFactory transactionFactory = ServiceFactory.CreateTransactionFactory( + TestRandom.Get(), new[] { (Label: "Pablo", KeyIndex: 0, Amount: 0.00011409m, Confirmed: true, AnonymitySet: 1) diff --git a/WalletWasabi.Tests/UnitTests/UserInterfaceTest/LabelTestExtensions.cs b/WalletWasabi.Tests/UnitTests/UserInterfaceTest/LabelTestExtensions.cs index b135c944d4..5946606836 100644 --- a/WalletWasabi.Tests/UnitTests/UserInterfaceTest/LabelTestExtensions.cs +++ b/WalletWasabi.Tests/UnitTests/UserInterfaceTest/LabelTestExtensions.cs @@ -1,3 +1,4 @@ +using GingerCommon.Crypto.Random; using System.Collections.Generic; using System.Linq; using WalletWasabi.Blockchain.Analysis.Clustering; @@ -18,10 +19,10 @@ public static LabelViewModel GetLabel(this LabelSelectionViewModel selection, st return selection.AllLabelsViewModel.Single(x => x.Value == label); } - public static void AddPocket(this List pockets, decimal amount, out Pocket pocket, params string[] labels) + public static void AddPocket(this List pockets, GingerRandom rnd, decimal amount, out Pocket pocket, params string[] labels) { var labelsArray = new LabelsArray(labels); - var coinsView = new CoinsView(new[] { BitcoinFactory.CreateSmartCoin(NewKey(labelsArray), amount) }); + var coinsView = new CoinsView(new[] { BitcoinFactory.CreateSmartCoin(rnd, NewKey(labelsArray), amount) }); pocket = new Pocket((labelsArray, coinsView)); pockets.Add(pocket); } @@ -35,19 +36,19 @@ public static HdPubKey NewKey(string label = "", int anonymitySet = 1) return key; } - public static SmartCoin CreateCoin(decimal amount, string label = "", int anonymitySet = 1) + public static SmartCoin CreateCoin(GingerRandom rnd, decimal amount, string label = "", int anonymitySet = 1) { - var coin = BitcoinFactory.CreateSmartCoin(NewKey(label: label, anonymitySet: anonymitySet), amount); + var coin = BitcoinFactory.CreateSmartCoin(rnd, NewKey(label: label, anonymitySet: anonymitySet), amount); coin.HdPubKey.SetAnonymitySet(anonymitySet); return coin; } - public static Pocket CreateSingleCoinPocket(decimal amount, string label = "", int anonSet = 0) + public static Pocket CreateSingleCoinPocket(GingerRandom rnd, decimal amount, string label = "", int anonSet = 0) { var coins = new[] { - CreateCoin(amount, label, anonSet) + CreateCoin(rnd, amount, label, anonSet) }; var coinsView = new CoinsView(coins.ToArray()); diff --git a/WalletWasabi.Tests/UnitTests/UserInterfaceTest/PocketSelectionTests.cs b/WalletWasabi.Tests/UnitTests/UserInterfaceTest/PocketSelectionTests.cs index 0a1492114d..5fa5132bc1 100644 --- a/WalletWasabi.Tests/UnitTests/UserInterfaceTest/PocketSelectionTests.cs +++ b/WalletWasabi.Tests/UnitTests/UserInterfaceTest/PocketSelectionTests.cs @@ -1,7 +1,7 @@ +using NBitcoin; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; -using NBitcoin; using WalletWasabi.Blockchain.Analysis.Clustering; using WalletWasabi.Blockchain.Keys; using WalletWasabi.Blockchain.TransactionOutputs; @@ -10,6 +10,7 @@ using WalletWasabi.Fluent.HomeScreen.Send.ViewModels; using WalletWasabi.Fluent.Models; using WalletWasabi.Tests.Helpers; +using WalletWasabi.Tests.TestCommon; using Xunit; namespace WalletWasabi.Tests.UnitTests.UserInterfaceTest; @@ -38,13 +39,14 @@ private LabelSelectionViewModel CreateLabelSelectionViewModel(Money amount, Labe [Fact] public async Task WhiteListHighlightsAllLabelsInOtherPocketsThatContainTargetLabelAsync() { + var rnd = TestRandom.Get(); var selection = CreateLabelSelectionViewModel(Money.Parse("1.0"), LabelsArray.Empty); var pockets = new List(); - pockets.AddPocket(1.0M, out _, "Target", "Dan", "Roland"); - pockets.AddPocket(1.0M, out _, "Target"); - pockets.AddPocket(1.0M, out _, "David", "Adam", "Lucas"); - pockets.AddPocket(1.0M, out _, "Jumar"); + pockets.AddPocket(rnd, 1.0M, out _, "Target", "Dan", "Roland"); + pockets.AddPocket(rnd, 1.0M, out _, "Target"); + pockets.AddPocket(rnd, 1.0M, out _, "David", "Adam", "Lucas"); + pockets.AddPocket(rnd, 1.0M, out _, "Jumar"); await selection.ResetAsync(pockets.ToArray()); @@ -63,13 +65,14 @@ public async Task WhiteListHighlightsAllLabelsInOtherPocketsThatContainTargetLab [Fact] public async Task AllWhitelistPocketsAreOutputAsync() { + var rnd = TestRandom.Get(); var selection = CreateLabelSelectionViewModel(Money.Parse("1.0"), LabelsArray.Empty); var pockets = new List(); - pockets.AddPocket(1.0M, out var pocket1, "Target", "Dan", "Roland"); - pockets.AddPocket(1.0M, out var pocket2, "Target"); - pockets.AddPocket(1.0M, out var pocket3, "David", "Adam", "Lucas"); - pockets.AddPocket(1.0M, out var pocket4, "Jumar"); + pockets.AddPocket(rnd, 1.0M, out var pocket1, "Target", "Dan", "Roland"); + pockets.AddPocket(rnd, 1.0M, out var pocket2, "Target"); + pockets.AddPocket(rnd, 1.0M, out var pocket3, "David", "Adam", "Lucas"); + pockets.AddPocket(rnd, 1.0M, out var pocket4, "Jumar"); await selection.ResetAsync(pockets.ToArray()); @@ -84,11 +87,12 @@ public async Task AllWhitelistPocketsAreOutputAsync() [Fact] public async Task WhiteListHighlightsAllLabelsInOtherPocketsThatContainTargetLabelExceptThoseAvailableInOtherPocketsAsync() { + var rnd = TestRandom.Get(); var selection = CreateLabelSelectionViewModel(Money.Parse("1.0"), LabelsArray.Empty); var pockets = new List(); - pockets.AddPocket(1.0M, out _, "Target", "Dan", "Roland"); - pockets.AddPocket(1.0M, out _, "Dan"); + pockets.AddPocket(rnd, 1.0M, out _, "Target", "Dan", "Roland"); + pockets.AddPocket(rnd, 1.0M, out _, "Dan"); await selection.ResetAsync(pockets.ToArray()); @@ -102,11 +106,12 @@ public async Task WhiteListHighlightsAllLabelsInOtherPocketsThatContainTargetLab [Fact] public async Task WhiteListHighlightsAllLabelsInOtherPocketsThatContainTargetLabelAndTheOtherLabelsInThosePocketsAsync() { + var rnd = TestRandom.Get(); var selection = CreateLabelSelectionViewModel(Money.Parse("1.0"), LabelsArray.Empty); var pockets = new List(); - pockets.AddPocket(1.0M, out _, "Target", "Dan", "Roland"); - pockets.AddPocket(1.0M, out _, "Dan"); + pockets.AddPocket(rnd, 1.0M, out _, "Target", "Dan", "Roland"); + pockets.AddPocket(rnd, 1.0M, out _, "Dan"); await selection.ResetAsync(pockets.ToArray()); @@ -120,11 +125,12 @@ public async Task WhiteListHighlightsAllLabelsInOtherPocketsThatContainTargetLab [Fact] public async Task WhiteListSwapsAllLabelsInOtherPocketsThatContainTargetLabelAndTheOtherLabelsInThosePocketsAsync() { + var rnd = TestRandom.Get(); var selection = CreateLabelSelectionViewModel(Money.Parse("1.0"), LabelsArray.Empty); var pockets = new List(); - pockets.AddPocket(1.0M, out _, "Target", "Dan", "Roland"); - pockets.AddPocket(1.0M, out _, "Dan"); + pockets.AddPocket(rnd, 1.0M, out _, "Target", "Dan", "Roland"); + pockets.AddPocket(rnd, 1.0M, out _, "Dan"); await selection.ResetAsync(pockets.ToArray()); @@ -138,11 +144,12 @@ public async Task WhiteListSwapsAllLabelsInOtherPocketsThatContainTargetLabelAnd [Fact] public async Task OutPutMatchesWhiteListScenario1Async() { + var rnd = TestRandom.Get(); var selection = CreateLabelSelectionViewModel(Money.Parse("1.0"), LabelsArray.Empty); var pockets = new List(); - pockets.AddPocket(1.0M, out var pocket1, "Target", "Dan", "Roland"); - pockets.AddPocket(1.0M, out var pocket2, "Dan"); + pockets.AddPocket(rnd, 1.0M, out var pocket1, "Target", "Dan", "Roland"); + pockets.AddPocket(rnd, 1.0M, out var pocket2, "Dan"); await selection.ResetAsync(pockets.ToArray()); @@ -157,11 +164,12 @@ public async Task OutPutMatchesWhiteListScenario1Async() [Fact] public async Task BlackListHighlightsLabelsExclusivelyThatExistInMultiplePocketsAsync() { + var rnd = TestRandom.Get(); var selection = CreateLabelSelectionViewModel(Money.Parse("1.0"), LabelsArray.Empty); var pockets = new List(); - pockets.AddPocket(1.0M, out var pocket1, "Target", "Dan", "Roland"); - pockets.AddPocket(1.0M, out var pocket2, "Dan"); + pockets.AddPocket(rnd, 1.0M, out var pocket1, "Target", "Dan", "Roland"); + pockets.AddPocket(rnd, 1.0M, out var pocket2, "Dan"); await selection.ResetAsync(pockets.ToArray()); @@ -186,12 +194,13 @@ public async Task BlackListHighlightsLabelsExclusivelyThatExistInMultiplePockets [Fact] public async Task BlackListHighlightsDealWithMultipleOverlapsCorrectlyAsync() { + var rnd = TestRandom.Get(); var selection = CreateLabelSelectionViewModel(Money.Parse("1.0"), LabelsArray.Empty); var pockets = new List(); - pockets.AddPocket(1.0M, out var pocket1, "Target", "Dan"); - pockets.AddPocket(1.0M, out var pocket2, "Target"); - pockets.AddPocket(1.0M, out var pocket3, "Target", "Roland"); + pockets.AddPocket(rnd, 1.0M, out var pocket1, "Target", "Dan"); + pockets.AddPocket(rnd, 1.0M, out var pocket2, "Target"); + pockets.AddPocket(rnd, 1.0M, out var pocket3, "Target", "Roland"); await selection.ResetAsync(pockets.ToArray()); @@ -217,12 +226,13 @@ public async Task BlackListHighlightsDealWithMultipleOverlapsCorrectlyAsync() [Fact] public async Task WhiteListHighlightsDealWithMultipleOverlapsCorrectlyAsync() { + var rnd = TestRandom.Get(); var selection = CreateLabelSelectionViewModel(Money.Parse("1.0"), LabelsArray.Empty); var pockets = new List(); - pockets.AddPocket(1.0M, out _, "Target", "Dan"); - pockets.AddPocket(1.0M, out _, "Target"); - pockets.AddPocket(1.0M, out _, "Target", "Roland"); + pockets.AddPocket(rnd, 1.0M, out _, "Target", "Dan"); + pockets.AddPocket(rnd, 1.0M, out _, "Target"); + pockets.AddPocket(rnd, 1.0M, out _, "Target", "Roland"); await selection.ResetAsync(pockets.ToArray()); @@ -236,12 +246,13 @@ public async Task WhiteListHighlightsDealWithMultipleOverlapsCorrectlyAsync() [Fact] public async Task WhiteListSwapsGroupedLabelsInOtherPocketsThatContainTargetLabelExceptThoseAvailableInOtherPocketsAsync() { + var rnd = TestRandom.Get(); var selection = CreateLabelSelectionViewModel(Money.Parse("1.0"), LabelsArray.Empty); var pockets = new List(); - pockets.AddPocket(1.0M, out var pocket1, "Target", "Dan"); - pockets.AddPocket(1.0M, out var pocket2, "Target"); - pockets.AddPocket(1.0M, out var pocket3, "Target", "Roland"); + pockets.AddPocket(rnd, 1.0M, out var pocket1, "Target", "Dan"); + pockets.AddPocket(rnd, 1.0M, out var pocket2, "Target"); + pockets.AddPocket(rnd, 1.0M, out var pocket3, "Target", "Roland"); await selection.ResetAsync(pockets.ToArray()); @@ -261,13 +272,14 @@ public async Task WhiteListSwapsGroupedLabelsInOtherPocketsThatContainTargetLabe [Fact] public async Task MovesFromWhiteListHighlightsAllLabelsInOtherPocketsThatContainTargetLabelAsync() { + var rnd = TestRandom.Get(); var selection = CreateLabelSelectionViewModel(Money.Parse("1.0"), LabelsArray.Empty); var pockets = new List(); - pockets.AddPocket(1.0M, out _, "Target", "Dan", "Roland"); - pockets.AddPocket(1.0M, out _, "Target"); - pockets.AddPocket(1.0M, out _, "David", "Adam", "Lucas"); - pockets.AddPocket(1.0M, out _, "Jumar"); + pockets.AddPocket(rnd, 1.0M, out _, "Target", "Dan", "Roland"); + pockets.AddPocket(rnd, 1.0M, out _, "Target"); + pockets.AddPocket(rnd, 1.0M, out _, "David", "Adam", "Lucas"); + pockets.AddPocket(rnd, 1.0M, out _, "Jumar"); await selection.ResetAsync(pockets.ToArray()); @@ -295,12 +307,13 @@ public async Task MovesFromWhiteListHighlightsAllLabelsInOtherPocketsThatContain [Fact] public async Task PrivateAndSemiPrivatePocketsAreHiddenAsync() { + var rnd = TestRandom.Get(); var selection = CreateLabelSelectionViewModel(Money.Parse("1.0"), LabelsArray.Empty); var pockets = new List(); - pockets.AddPocket(1.0M, out var pocket1, CoinPocketHelper.PrivateFundsText); - pockets.AddPocket(2.0M, out var pocket2, "Dan"); - pockets.AddPocket(1.0M, out var pocket3, CoinPocketHelper.SemiPrivateFundsText); + pockets.AddPocket(rnd, 1.0M, out var pocket1, CoinPocketHelper.PrivateFundsText); + pockets.AddPocket(rnd, 2.0M, out var pocket2, "Dan"); + pockets.AddPocket(rnd, 1.0M, out var pocket3, CoinPocketHelper.SemiPrivateFundsText); await selection.ResetAsync(pockets.ToArray()); @@ -314,12 +327,13 @@ public async Task PrivateAndSemiPrivatePocketsAreHiddenAsync() [Fact] public async Task UseOnlyPrivateFundsAsync() { + var rnd = TestRandom.Get(); var selection = CreateLabelSelectionViewModel(Money.Parse("1.0"), LabelsArray.Empty); var pockets = new List(); - pockets.AddPocket(1.1M, out var pocket1, CoinPocketHelper.PrivateFundsText); - pockets.AddPocket(2.0M, out var pocket2, "Dan"); - pockets.AddPocket(1.0M, out var pocket3, CoinPocketHelper.SemiPrivateFundsText); + pockets.AddPocket(rnd, 1.1M, out var pocket1, CoinPocketHelper.PrivateFundsText); + pockets.AddPocket(rnd, 2.0M, out var pocket2, "Dan"); + pockets.AddPocket(rnd, 1.0M, out var pocket3, CoinPocketHelper.SemiPrivateFundsText); await selection.ResetAsync(pockets.ToArray()); @@ -337,12 +351,13 @@ public async Task UseOnlyPrivateFundsAsync() [Fact] public async Task UsePrivateAndSemiPrivateFundsAsync() { + var rnd = TestRandom.Get(); var selection = CreateLabelSelectionViewModel(Money.Parse("1.5"), LabelsArray.Empty); var pockets = new List(); - pockets.AddPocket(0.9M, out var pocket1, CoinPocketHelper.PrivateFundsText); - pockets.AddPocket(2.0M, out var pocket2, "Dan"); - pockets.AddPocket(1.0M, out var pocket3, CoinPocketHelper.SemiPrivateFundsText); + pockets.AddPocket(rnd, 0.9M, out var pocket1, CoinPocketHelper.PrivateFundsText); + pockets.AddPocket(rnd, 2.0M, out var pocket2, "Dan"); + pockets.AddPocket(rnd, 1.0M, out var pocket3, CoinPocketHelper.SemiPrivateFundsText); await selection.ResetAsync(pockets.ToArray()); @@ -360,12 +375,13 @@ public async Task UsePrivateAndSemiPrivateFundsAsync() [Fact] public async Task DoNotUsePrivateFundsAsync() { + var rnd = TestRandom.Get(); var selection = CreateLabelSelectionViewModel(Money.Parse("1.0"), "Dan"); var pockets = new List(); - pockets.AddPocket(1.0M, out var pocket1, CoinPocketHelper.PrivateFundsText); - pockets.AddPocket(2.0M, out var pocket2, "Dan"); - pockets.AddPocket(1.0M, out var pocket3, CoinPocketHelper.SemiPrivateFundsText); + pockets.AddPocket(rnd, 1.0M, out var pocket1, CoinPocketHelper.PrivateFundsText); + pockets.AddPocket(rnd, 2.0M, out var pocket2, "Dan"); + pockets.AddPocket(rnd, 1.0M, out var pocket3, CoinPocketHelper.SemiPrivateFundsText); await selection.ResetAsync(pockets.ToArray()); @@ -381,12 +397,13 @@ public async Task DoNotUsePrivateFundsAsync() [Fact] public async Task IncludePrivateFundsAsync() { + var rnd = TestRandom.Get(); var selection = CreateLabelSelectionViewModel(Money.Parse("2.5"), LabelsArray.Empty); var pockets = new List(); - pockets.AddPocket(1.0M, out var pocket1, CoinPocketHelper.PrivateFundsText); - pockets.AddPocket(2.0M, out var pocket2, "Dan"); - pockets.AddPocket(1.0M, out var pocket3, CoinPocketHelper.SemiPrivateFundsText); + pockets.AddPocket(rnd, 1.0M, out var pocket1, CoinPocketHelper.PrivateFundsText); + pockets.AddPocket(rnd, 2.0M, out var pocket2, "Dan"); + pockets.AddPocket(rnd, 1.0M, out var pocket3, CoinPocketHelper.SemiPrivateFundsText); await selection.ResetAsync(pockets.ToArray()); @@ -401,12 +418,13 @@ public async Task IncludePrivateFundsAsync() [Fact] public async Task IncludePrivateAndSemiPrivateFundsAsync() { + var rnd = TestRandom.Get(); var selection = CreateLabelSelectionViewModel(Money.Parse("2.5"), LabelsArray.Empty); var pockets = new List(); - pockets.AddPocket(0.6M, out var pocket1, CoinPocketHelper.PrivateFundsText); - pockets.AddPocket(1.0M, out var pocket2, "Dan"); - pockets.AddPocket(1.0M, out var pocket3, CoinPocketHelper.SemiPrivateFundsText); + pockets.AddPocket(rnd, 0.6M, out var pocket1, CoinPocketHelper.PrivateFundsText); + pockets.AddPocket(rnd, 1.0M, out var pocket2, "Dan"); + pockets.AddPocket(rnd, 1.0M, out var pocket3, CoinPocketHelper.SemiPrivateFundsText); await selection.ResetAsync(pockets.ToArray()); @@ -421,15 +439,16 @@ public async Task IncludePrivateAndSemiPrivateFundsAsync() [Fact] public async Task StillIncludePrivateFundsAfterSwapAsync() { + var rnd = TestRandom.Get(); var selection = CreateLabelSelectionViewModel(Money.Parse("1.0"), LabelsArray.Empty); var pockets = new List(); - var privateCoin = LabelTestExtensions.CreateCoin(0.8m, "", 999); + var privateCoin = LabelTestExtensions.CreateCoin(rnd, 0.8m, "", 999); var privatePocket = new Pocket((CoinPocketHelper.PrivateFundsText, new CoinsView(new[] { privateCoin }))); pockets.Add(privatePocket); - pockets.AddPocket(0.3M, out var pocket2, "Dan"); - pockets.AddPocket(0.1M, out var pocket3, "Lucas"); + pockets.AddPocket(rnd, 0.3M, out var pocket2, "Dan"); + pockets.AddPocket(rnd, 0.1M, out var pocket3, "Lucas"); await selection.ResetAsync(pockets.ToArray()); @@ -454,11 +473,12 @@ public async Task StillIncludePrivateFundsAfterSwapAsync() [Fact] public async Task NotEnoughSelectedAsync() { + var rnd = TestRandom.Get(); var selection = CreateLabelSelectionViewModel(Money.Parse("1.0"), LabelsArray.Empty); var pockets = new List(); - pockets.AddPocket(1.0M, out _, "David"); - pockets.AddPocket(2.0M, out _, "Dan"); + pockets.AddPocket(rnd, 1.0M, out _, "David"); + pockets.AddPocket(rnd, 2.0M, out _, "Dan"); await selection.ResetAsync(pockets.ToArray()); @@ -471,13 +491,14 @@ public async Task NotEnoughSelectedAsync() [Fact] public async Task AutoSelectOnlyPrivatePocketAsync() { + var rnd = TestRandom.Get(); var selection = CreateLabelSelectionViewModel(Money.Parse("0.7"), "Dan"); var pockets = new List(); - pockets.AddPocket(0.8M, out var pocket1, CoinPocketHelper.PrivateFundsText); - pockets.AddPocket(0.6M, out var pocket2, "Dan"); - pockets.AddPocket(0.7M, out var pocket3, CoinPocketHelper.UnlabelledFundsText); - pockets.AddPocket(0.7M, out var pocket4, CoinPocketHelper.SemiPrivateFundsText); + pockets.AddPocket(rnd, 0.8M, out var pocket1, CoinPocketHelper.PrivateFundsText); + pockets.AddPocket(rnd, 0.6M, out var pocket2, "Dan"); + pockets.AddPocket(rnd, 0.7M, out var pocket3, CoinPocketHelper.UnlabelledFundsText); + pockets.AddPocket(rnd, 0.7M, out var pocket4, CoinPocketHelper.SemiPrivateFundsText); await selection.ResetAsync(pockets.ToArray()); @@ -491,13 +512,14 @@ public async Task AutoSelectOnlyPrivatePocketAsync() [Fact] public async Task AutoSelectPrivateAndSemiPrivatePocketsAsync() { + var rnd = TestRandom.Get(); var selection = CreateLabelSelectionViewModel(Money.Parse("0.7"), "Dan"); var pockets = new List(); - pockets.AddPocket(0.4M, out var pocket1, CoinPocketHelper.PrivateFundsText); - pockets.AddPocket(0.6M, out var pocket2, "Dan"); - pockets.AddPocket(0.7M, out var pocket3, CoinPocketHelper.UnlabelledFundsText); - pockets.AddPocket(0.4M, out var pocket4, CoinPocketHelper.SemiPrivateFundsText); + pockets.AddPocket(rnd, 0.4M, out var pocket1, CoinPocketHelper.PrivateFundsText); + pockets.AddPocket(rnd, 0.6M, out var pocket2, "Dan"); + pockets.AddPocket(rnd, 0.7M, out var pocket3, CoinPocketHelper.UnlabelledFundsText); + pockets.AddPocket(rnd, 0.4M, out var pocket4, CoinPocketHelper.SemiPrivateFundsText); await selection.ResetAsync(pockets.ToArray()); @@ -511,13 +533,14 @@ public async Task AutoSelectPrivateAndSemiPrivatePocketsAsync() [Fact] public async Task AutoSelectPrivateAndSemiPrivateAndKnownPocketsAsync() { + var rnd = TestRandom.Get(); var selection = CreateLabelSelectionViewModel(Money.Parse("1.0"), "Dan"); var pockets = new List(); - pockets.AddPocket(0.3M, out var pocket1, CoinPocketHelper.PrivateFundsText); - pockets.AddPocket(0.5M, out var pocket2, "Dan"); - pockets.AddPocket(0.4M, out var pocket3, CoinPocketHelper.UnlabelledFundsText); - pockets.AddPocket(0.3M, out var pocket4, CoinPocketHelper.SemiPrivateFundsText); + pockets.AddPocket(rnd, 0.3M, out var pocket1, CoinPocketHelper.PrivateFundsText); + pockets.AddPocket(rnd, 0.5M, out var pocket2, "Dan"); + pockets.AddPocket(rnd, 0.4M, out var pocket3, CoinPocketHelper.UnlabelledFundsText); + pockets.AddPocket(rnd, 0.3M, out var pocket4, CoinPocketHelper.SemiPrivateFundsText); await selection.ResetAsync(pockets.ToArray()); @@ -531,13 +554,14 @@ public async Task AutoSelectPrivateAndSemiPrivateAndKnownPocketsAsync() [Fact] public async Task AutoSelectPrivateAndSemiPrivateAndUnknownPocketsAsync() { + var rnd = TestRandom.Get(); var selection = CreateLabelSelectionViewModel(Money.Parse("1.0"), "Dan"); var pockets = new List(); - pockets.AddPocket(0.3M, out var pocket1, CoinPocketHelper.PrivateFundsText); - pockets.AddPocket(0.1M, out var pocket2, "Dan"); - pockets.AddPocket(0.5M, out var pocket3, CoinPocketHelper.UnlabelledFundsText); - pockets.AddPocket(0.4M, out var pocket4, CoinPocketHelper.SemiPrivateFundsText); + pockets.AddPocket(rnd, 0.3M, out var pocket1, CoinPocketHelper.PrivateFundsText); + pockets.AddPocket(rnd, 0.1M, out var pocket2, "Dan"); + pockets.AddPocket(rnd, 0.5M, out var pocket3, CoinPocketHelper.UnlabelledFundsText); + pockets.AddPocket(rnd, 0.4M, out var pocket4, CoinPocketHelper.SemiPrivateFundsText); await selection.ResetAsync(pockets.ToArray()); @@ -551,13 +575,14 @@ public async Task AutoSelectPrivateAndSemiPrivateAndUnknownPocketsAsync() [Fact] public async Task AutoSelectAllPocketsAsync() { + var rnd = TestRandom.Get(); var selection = CreateLabelSelectionViewModel(Money.Parse("2.0"), "Dan"); var pockets = new List(); - pockets.AddPocket(0.8M, out var pocket1, CoinPocketHelper.PrivateFundsText); - pockets.AddPocket(0.3M, out var pocket2, "Dan"); - pockets.AddPocket(0.5M, out var pocket3, CoinPocketHelper.UnlabelledFundsText); - pockets.AddPocket(0.5M, out var pocket4, CoinPocketHelper.SemiPrivateFundsText); + pockets.AddPocket(rnd, 0.8M, out var pocket1, CoinPocketHelper.PrivateFundsText); + pockets.AddPocket(rnd, 0.3M, out var pocket2, "Dan"); + pockets.AddPocket(rnd, 0.5M, out var pocket3, CoinPocketHelper.UnlabelledFundsText); + pockets.AddPocket(rnd, 0.5M, out var pocket4, CoinPocketHelper.SemiPrivateFundsText); await selection.ResetAsync(pockets.ToArray()); @@ -571,17 +596,18 @@ public async Task AutoSelectAllPocketsAsync() [Fact] public async Task AutoSelectOnlyKnownByRecipientPocketsAsync() { + var rnd = TestRandom.Get(); var selection = CreateLabelSelectionViewModel(Money.Parse("1.0"), "David, Lucas"); var pockets = new List(); - pockets.AddPocket(1.0M, out var pocket1, CoinPocketHelper.PrivateFundsText); - pockets.AddPocket(1.1M, out var pocket2, "Dan"); - pockets.AddPocket(0.5M, out var pocket3, CoinPocketHelper.UnlabelledFundsText); - pockets.AddPocket(1.1M, out var pocket4, "David", "Lucas"); - pockets.AddPocket(1.1M, out var pocket5, "David"); - pockets.AddPocket(1.1M, out var pocket6, "Lucas"); - pockets.AddPocket(1.1M, out var pocket7, "David", "Lucas", "Dan"); - pockets.AddPocket(1.0M, out var pocket8, CoinPocketHelper.SemiPrivateFundsText); + pockets.AddPocket(rnd, 1.0M, out var pocket1, CoinPocketHelper.PrivateFundsText); + pockets.AddPocket(rnd, 1.1M, out var pocket2, "Dan"); + pockets.AddPocket(rnd, 0.5M, out var pocket3, CoinPocketHelper.UnlabelledFundsText); + pockets.AddPocket(rnd, 1.1M, out var pocket4, "David", "Lucas"); + pockets.AddPocket(rnd, 1.1M, out var pocket5, "David"); + pockets.AddPocket(rnd, 1.1M, out var pocket6, "Lucas"); + pockets.AddPocket(rnd, 1.1M, out var pocket7, "David", "Lucas", "Dan"); + pockets.AddPocket(rnd, 1.0M, out var pocket8, CoinPocketHelper.SemiPrivateFundsText); await selection.ResetAsync(pockets.ToArray()); @@ -599,13 +625,14 @@ public async Task AutoSelectOnlyKnownByRecipientPocketsAsync() [Fact] public async Task AutoSelectOnlyKnownByRecipientPocketsCaseInsensitiveAsync() { + var rnd = TestRandom.Get(); var selection = CreateLabelSelectionViewModel(Money.Parse("1.0"), "dAN"); var pockets = new List(); - pockets.AddPocket(2.8M, out var pocket1, CoinPocketHelper.PrivateFundsText); - pockets.AddPocket(1.1M, out var pocket2, "Dan"); - pockets.AddPocket(1.1M, out var pocket3, "Lucas", "Dan"); - pockets.AddPocket(1.1M, out var pocket4, "dan"); + pockets.AddPocket(rnd, 2.8M, out var pocket1, CoinPocketHelper.PrivateFundsText); + pockets.AddPocket(rnd, 1.1M, out var pocket2, "Dan"); + pockets.AddPocket(rnd, 1.1M, out var pocket3, "Lucas", "Dan"); + pockets.AddPocket(rnd, 1.1M, out var pocket4, "dan"); await selection.ResetAsync(pockets.ToArray()); @@ -619,14 +646,15 @@ public async Task AutoSelectOnlyKnownByRecipientPocketsCaseInsensitiveAsync() [Fact] public async Task AutoSelectKnownByRecipientPocketsAsync() { + var rnd = TestRandom.Get(); var selection = CreateLabelSelectionViewModel(Money.Parse("1.9"), "David"); var pockets = new List(); - pockets.AddPocket(0.8M, out var pocket1, CoinPocketHelper.PrivateFundsText); - pockets.AddPocket(1.1M, out var pocket2, "Dan"); - pockets.AddPocket(1.5M, out var pocket3, CoinPocketHelper.UnlabelledFundsText); - pockets.AddPocket(1.1M, out var pocket4, "David", "Lucas"); - pockets.AddPocket(0.1M, out var pocket5, CoinPocketHelper.SemiPrivateFundsText); + pockets.AddPocket(rnd, 0.8M, out var pocket1, CoinPocketHelper.PrivateFundsText); + pockets.AddPocket(rnd, 1.1M, out var pocket2, "Dan"); + pockets.AddPocket(rnd, 1.5M, out var pocket3, CoinPocketHelper.UnlabelledFundsText); + pockets.AddPocket(rnd, 1.1M, out var pocket4, "David", "Lucas"); + pockets.AddPocket(rnd, 0.1M, out var pocket5, CoinPocketHelper.SemiPrivateFundsText); await selection.ResetAsync(pockets.ToArray()); @@ -641,18 +669,19 @@ public async Task AutoSelectKnownByRecipientPocketsAsync() [Fact] public async Task AutoSelectKnownByMultipleRecipientPocketsAsync() { + var rnd = TestRandom.Get(); var selection = CreateLabelSelectionViewModel(Money.Parse("1.9"), "David, Lucas"); var pockets = new List(); - pockets.AddPocket(0.8M, out var pocket1, CoinPocketHelper.PrivateFundsText); - pockets.AddPocket(1.1M, out var pocket2, "Dan"); - pockets.AddPocket(0.5M, out var pocket3, CoinPocketHelper.UnlabelledFundsText); - pockets.AddPocket(1.1M, out var pocket4, "David", "Lucas", "Dan"); - pockets.AddPocket(1.1M, out var pocket5, "David"); - pockets.AddPocket(1.1M, out var pocket6, "Lucas"); - pockets.AddPocket(1.1M, out var pocket7, "David", "Lucas", "Dan", "Roland"); - pockets.AddPocket(1.1M, out var pocket8, "David", "Dan"); - pockets.AddPocket(0.1M, out var pocket9, CoinPocketHelper.SemiPrivateFundsText); + pockets.AddPocket(rnd, 0.8M, out var pocket1, CoinPocketHelper.PrivateFundsText); + pockets.AddPocket(rnd, 1.1M, out var pocket2, "Dan"); + pockets.AddPocket(rnd, 0.5M, out var pocket3, CoinPocketHelper.UnlabelledFundsText); + pockets.AddPocket(rnd, 1.1M, out var pocket4, "David", "Lucas", "Dan"); + pockets.AddPocket(rnd, 1.1M, out var pocket5, "David"); + pockets.AddPocket(rnd, 1.1M, out var pocket6, "Lucas"); + pockets.AddPocket(rnd, 1.1M, out var pocket7, "David", "Lucas", "Dan", "Roland"); + pockets.AddPocket(rnd, 1.1M, out var pocket8, "David", "Dan"); + pockets.AddPocket(rnd, 0.1M, out var pocket9, CoinPocketHelper.SemiPrivateFundsText); await selection.ResetAsync(pockets.ToArray()); @@ -671,18 +700,19 @@ public async Task AutoSelectKnownByMultipleRecipientPocketsAsync() [Fact] public async Task AutoSelectMultipleKnownByMultipleRecipientPocketsAsync() { + var rnd = TestRandom.Get(); var selection = CreateLabelSelectionViewModel(Money.Parse("1.0"), "David, Lucas"); var pockets = new List(); - pockets.AddPocket(0.2M, out var pocket1, CoinPocketHelper.PrivateFundsText); - pockets.AddPocket(1.1M, out var pocket2, "Dan"); - pockets.AddPocket(0.5M, out var pocket3, CoinPocketHelper.UnlabelledFundsText); - pockets.AddPocket(0.4M, out var pocket4, "David", "Lucas", "Dan"); - pockets.AddPocket(0.6M, out var pocket5, "David"); - pockets.AddPocket(0.5M, out var pocket6, "Lucas"); - pockets.AddPocket(0.5M, out var pocket7, "David", "Lucas", "Dan", "Roland"); - pockets.AddPocket(0.5M, out var pocket8, "David", "Dan"); - pockets.AddPocket(0.1M, out var pocket9, CoinPocketHelper.SemiPrivateFundsText); + pockets.AddPocket(rnd, 0.2M, out var pocket1, CoinPocketHelper.PrivateFundsText); + pockets.AddPocket(rnd, 1.1M, out var pocket2, "Dan"); + pockets.AddPocket(rnd, 0.5M, out var pocket3, CoinPocketHelper.UnlabelledFundsText); + pockets.AddPocket(rnd, 0.4M, out var pocket4, "David", "Lucas", "Dan"); + pockets.AddPocket(rnd, 0.6M, out var pocket5, "David"); + pockets.AddPocket(rnd, 0.5M, out var pocket6, "Lucas"); + pockets.AddPocket(rnd, 0.5M, out var pocket7, "David", "Lucas", "Dan", "Roland"); + pockets.AddPocket(rnd, 0.5M, out var pocket8, "David", "Dan"); + pockets.AddPocket(rnd, 0.1M, out var pocket9, CoinPocketHelper.SemiPrivateFundsText); await selection.ResetAsync(pockets.ToArray()); @@ -701,16 +731,17 @@ public async Task AutoSelectMultipleKnownByMultipleRecipientPocketsAsync() [Fact] public async Task AutoSelectRequiredKnownByRecipientPocketsAsync() { + var rnd = TestRandom.Get(); var selection = CreateLabelSelectionViewModel(Money.Parse("1.3"), "David"); var pockets = new List(); - pockets.AddPocket(0.2M, out var pocket1, CoinPocketHelper.PrivateFundsText); - pockets.AddPocket(1.1M, out var pocket2, "Dan"); - pockets.AddPocket(1.5M, out var pocket3, CoinPocketHelper.UnlabelledFundsText); - pockets.AddPocket(0.5M, out var pocket4, "David"); - pockets.AddPocket(0.4M, out var pocket5, "David", "Max"); - pockets.AddPocket(0.6M, out var pocket6, "David", "Lucas", "Dan"); - pockets.AddPocket(0.1M, out var pocket7, CoinPocketHelper.SemiPrivateFundsText); + pockets.AddPocket(rnd, 0.2M, out var pocket1, CoinPocketHelper.PrivateFundsText); + pockets.AddPocket(rnd, 1.1M, out var pocket2, "Dan"); + pockets.AddPocket(rnd, 1.5M, out var pocket3, CoinPocketHelper.UnlabelledFundsText); + pockets.AddPocket(rnd, 0.5M, out var pocket4, "David"); + pockets.AddPocket(rnd, 0.4M, out var pocket5, "David", "Max"); + pockets.AddPocket(rnd, 0.6M, out var pocket6, "David", "Lucas", "Dan"); + pockets.AddPocket(rnd, 0.1M, out var pocket7, CoinPocketHelper.SemiPrivateFundsText); await selection.ResetAsync(pockets.ToArray()); @@ -727,11 +758,12 @@ public async Task AutoSelectRequiredKnownByRecipientPocketsAsync() [Fact] public async Task NotEnoughSelectedWhenSameLabelFoundInSeveralPocketsAsync() { + var rnd = TestRandom.Get(); var selection = CreateLabelSelectionViewModel(Money.Parse("1.0"), LabelsArray.Empty); var pockets = new List(); - pockets.AddPocket(0.4M, out _, "Dan"); - pockets.AddPocket(2.0M, out _, "Dan", "David"); + pockets.AddPocket(rnd, 0.4M, out _, "Dan"); + pockets.AddPocket(rnd, 2.0M, out _, "Dan", "David"); await selection.ResetAsync(pockets.ToArray()); @@ -747,11 +779,12 @@ public async Task NotEnoughSelectedWhenSameLabelFoundInSeveralPocketsAsync() [Fact] public async Task SetUsedLabelIgnoreCaseAsync() { + var rnd = TestRandom.Get(); var selection = CreateLabelSelectionViewModel(Money.Parse("1.0"), "Dan"); var pockets = new List(); - pockets.AddPocket(1.2M, out var pocket1, "Dan"); - pockets.AddPocket(1.2M, out var pocket2, "Lucas"); + pockets.AddPocket(rnd, 1.2M, out var pocket1, "Dan"); + pockets.AddPocket(rnd, 1.2M, out var pocket2, "Lucas"); await selection.ResetAsync(pockets.ToArray()); @@ -760,7 +793,7 @@ public async Task SetUsedLabelIgnoreCaseAsync() Assert.DoesNotContain(pocket2, output); var hdpk = LabelTestExtensions.NewKey("dan"); - var usedCoin = BitcoinFactory.CreateSmartCoin(hdpk, 1.0M); + var usedCoin = BitcoinFactory.CreateSmartCoin(TestRandom.Get(), hdpk, 1.0M); await selection.SetUsedLabelAsync(new[] { usedCoin }, privateThreshold: 10); Assert.Contains(selection.GetLabel("Lucas"), selection.LabelsBlackList); @@ -770,15 +803,16 @@ public async Task SetUsedLabelIgnoreCaseAsync() [Fact] public async Task SetUsedLabelIncludePrivateFundsAsync() { + var rnd = TestRandom.Get(); var selection = CreateLabelSelectionViewModel(Money.Parse("1.5"), "Dan"); var pockets = new List(); - pockets.AddPocket(1.0M, out _, "Dan"); + pockets.AddPocket(rnd, 1.0M, out _, "Dan"); var privateCoins = new[] { - BitcoinFactory.CreateSmartCoin(LabelTestExtensions.NewKey(anonymitySet: 999), 0.5m), - BitcoinFactory.CreateSmartCoin(LabelTestExtensions.NewKey(anonymitySet: 999), 0.5m), + BitcoinFactory.CreateSmartCoin(rnd, LabelTestExtensions.NewKey(anonymitySet: 999), 0.5m), + BitcoinFactory.CreateSmartCoin(rnd, LabelTestExtensions.NewKey(anonymitySet: 999), 0.5m), }; var coinsView = new CoinsView(privateCoins.ToArray()); var pocket = new Pocket((LabelsArray.Empty, coinsView)); @@ -796,15 +830,16 @@ public async Task SetUsedLabelIncludePrivateFundsAsync() [Fact] public async Task SetUsedLabelIncludeSemiPrivateFundsAsync() { + var rnd = TestRandom.Get(); var selection = CreateLabelSelectionViewModel(Money.Parse("1.5"), "Dan"); var pockets = new List(); - pockets.AddPocket(1.0M, out _, "Dan"); + pockets.AddPocket(rnd, 1.0M, out _, "Dan"); var semiPrivateCoins = new[] { - BitcoinFactory.CreateSmartCoin(LabelTestExtensions.NewKey(anonymitySet: 5), 0.5m), - BitcoinFactory.CreateSmartCoin(LabelTestExtensions.NewKey(anonymitySet: 5), 0.5m), + BitcoinFactory.CreateSmartCoin(rnd, LabelTestExtensions.NewKey(anonymitySet: 5), 0.5m), + BitcoinFactory.CreateSmartCoin(rnd, LabelTestExtensions.NewKey(anonymitySet: 5), 0.5m), }; var coinsView = new CoinsView(semiPrivateCoins.ToArray()); var pocket = new Pocket((LabelsArray.Empty, coinsView)); @@ -822,15 +857,16 @@ public async Task SetUsedLabelIncludeSemiPrivateFundsAsync() [Fact] public async Task SetUsedLabelIncludePrivateAndSemiPrivateFundsAsync() { + var rnd = TestRandom.Get(); var selection = CreateLabelSelectionViewModel(Money.Parse("2.5"), "Dan"); var pockets = new List(); - pockets.AddPocket(1.0M, out _, "Dan"); + pockets.AddPocket(rnd, 1.0M, out _, "Dan"); var privateCoins = new[] { - BitcoinFactory.CreateSmartCoin(LabelTestExtensions.NewKey(anonymitySet: 999), 0.5m), - BitcoinFactory.CreateSmartCoin(LabelTestExtensions.NewKey(anonymitySet: 999), 0.5m), + BitcoinFactory.CreateSmartCoin(rnd, LabelTestExtensions.NewKey(anonymitySet: 999), 0.5m), + BitcoinFactory.CreateSmartCoin(rnd, LabelTestExtensions.NewKey(anonymitySet: 999), 0.5m), }; var privateCoinsView = new CoinsView(privateCoins.ToArray()); var privatePocket = new Pocket((LabelsArray.Empty, privateCoinsView)); @@ -838,8 +874,8 @@ public async Task SetUsedLabelIncludePrivateAndSemiPrivateFundsAsync() var semiPrivateCoins = new[] { - BitcoinFactory.CreateSmartCoin(LabelTestExtensions.NewKey(anonymitySet: 5), 0.5m), - BitcoinFactory.CreateSmartCoin(LabelTestExtensions.NewKey(anonymitySet: 5), 0.5m), + BitcoinFactory.CreateSmartCoin(rnd, LabelTestExtensions.NewKey(anonymitySet: 5), 0.5m), + BitcoinFactory.CreateSmartCoin(rnd, LabelTestExtensions.NewKey(anonymitySet: 5), 0.5m), }; var semiPrivateCoinsView = new CoinsView(semiPrivateCoins.ToArray()); var semiPrivatePocket = new Pocket((LabelsArray.Empty, semiPrivateCoinsView)); @@ -857,14 +893,15 @@ public async Task SetUsedLabelIncludePrivateAndSemiPrivateFundsAsync() [Fact] public async Task IsPocketEnoughWithoutCoinjoiningCoinsAsync() { + var rnd = TestRandom.Get(); var selection = CreateLabelSelectionViewModel(Money.Parse("0.5"), "Daniel"); var pockets = new List(); var privateCoins = new[] { - BitcoinFactory.CreateSmartCoin(LabelTestExtensions.NewKey(anonymitySet: 999), 1m), - BitcoinFactory.CreateSmartCoin(LabelTestExtensions.NewKey(anonymitySet: 999), 1m), - BitcoinFactory.CreateSmartCoin(LabelTestExtensions.NewKey(anonymitySet: 999), 1m) + BitcoinFactory.CreateSmartCoin(rnd, LabelTestExtensions.NewKey(anonymitySet: 999), 1m), + BitcoinFactory.CreateSmartCoin(rnd, LabelTestExtensions.NewKey(anonymitySet: 999), 1m), + BitcoinFactory.CreateSmartCoin(rnd, LabelTestExtensions.NewKey(anonymitySet: 999), 1m) }; var privateCoinsView = new CoinsView(privateCoins.ToArray()); var privatePocket = new Pocket((LabelsArray.Empty, privateCoinsView)); @@ -889,14 +926,15 @@ public async Task IsPocketEnoughWithoutCoinjoiningCoinsAsync() [Fact] public async Task UseCoinjoiningCoinsIfNecessaryAsync() { + var rnd = TestRandom.Get(); var selection = CreateLabelSelectionViewModel(Money.Parse("2.9"), "Daniel"); var pockets = new List(); var privateCoins = new[] { - BitcoinFactory.CreateSmartCoin(LabelTestExtensions.NewKey(anonymitySet: 999), 1m), - BitcoinFactory.CreateSmartCoin(LabelTestExtensions.NewKey(anonymitySet: 999), 1m), - BitcoinFactory.CreateSmartCoin(LabelTestExtensions.NewKey(anonymitySet: 999), 1m) + BitcoinFactory.CreateSmartCoin(rnd, LabelTestExtensions.NewKey(anonymitySet: 999), 1m), + BitcoinFactory.CreateSmartCoin(rnd, LabelTestExtensions.NewKey(anonymitySet: 999), 1m), + BitcoinFactory.CreateSmartCoin(rnd, LabelTestExtensions.NewKey(anonymitySet: 999), 1m) }; var privateCoinsView = new CoinsView(privateCoins.ToArray()); @@ -921,13 +959,14 @@ public async Task UseCoinjoiningCoinsIfNecessaryAsync() [Fact] public async Task IsOtherSelectionPossibleCasesAsync() { + var rnd = TestRandom.Get(); var pockets = new List(); - var privatePocket = LabelTestExtensions.CreateSingleCoinPocket(1.0m, CoinPocketHelper.PrivateFundsText, anonSet: 999); - var semiPrivatePocket = LabelTestExtensions.CreateSingleCoinPocket(1.0m, CoinPocketHelper.SemiPrivateFundsText, anonSet: 2); + var privatePocket = LabelTestExtensions.CreateSingleCoinPocket(rnd, 1.0m, CoinPocketHelper.PrivateFundsText, anonSet: 999); + var semiPrivatePocket = LabelTestExtensions.CreateSingleCoinPocket(rnd, 1.0m, CoinPocketHelper.SemiPrivateFundsText, anonSet: 2); - pockets.Add(LabelTestExtensions.CreateSingleCoinPocket(1.0m, "Dan")); - pockets.Add(LabelTestExtensions.CreateSingleCoinPocket(1.0m, "Dan, Lucas")); + pockets.Add(LabelTestExtensions.CreateSingleCoinPocket(rnd, 1.0m, "Dan")); + pockets.Add(LabelTestExtensions.CreateSingleCoinPocket(rnd, 1.0m, "Dan, Lucas")); // Other pocket can be used case. var recipient = "Lucas"; diff --git a/WalletWasabi.Tests/UnitTests/UserInterfaceTest/PocketTests.cs b/WalletWasabi.Tests/UnitTests/UserInterfaceTest/PocketTests.cs index 32d7188075..30fc9bc0c9 100644 --- a/WalletWasabi.Tests/UnitTests/UserInterfaceTest/PocketTests.cs +++ b/WalletWasabi.Tests/UnitTests/UserInterfaceTest/PocketTests.cs @@ -1,5 +1,6 @@ using System.Linq; using WalletWasabi.Fluent.Models; +using WalletWasabi.Tests.TestCommon; using Xunit; namespace WalletWasabi.Tests.UnitTests.UserInterfaceTest; @@ -9,11 +10,12 @@ public class PocketTests [Fact] public void MergeTests() { + var rnd = TestRandom.Get(); var emptyPocketArray = Array.Empty(); var emptyPocket = Pocket.Empty; - var pocket1 = LabelTestExtensions.CreateSingleCoinPocket(0.5m, "Pocket 1"); - var pocket2 = LabelTestExtensions.CreateSingleCoinPocket(0.2m, "Pocket 2"); - var pocket3 = LabelTestExtensions.CreateSingleCoinPocket(0.4m, "Pocket 3"); + var pocket1 = LabelTestExtensions.CreateSingleCoinPocket(rnd, 0.5m, "Pocket 1"); + var pocket2 = LabelTestExtensions.CreateSingleCoinPocket(rnd, 0.2m, "Pocket 2"); + var pocket3 = LabelTestExtensions.CreateSingleCoinPocket(rnd, 0.4m, "Pocket 3"); var pocketArray = new[] { pocket1, pocket2 }; // Two not empty pocket diff --git a/WalletWasabi.Tests/UnitTests/Userfacing/PasswordTests.cs b/WalletWasabi.Tests/UnitTests/Userfacing/PasswordTests.cs index 5e65e2ea4e..4ebfe5201b 100644 --- a/WalletWasabi.Tests/UnitTests/Userfacing/PasswordTests.cs +++ b/WalletWasabi.Tests/UnitTests/Userfacing/PasswordTests.cs @@ -7,6 +7,7 @@ using WalletWasabi.Crypto.Randomness; using WalletWasabi.Userfacing; using NBitcoin; +using WalletWasabi.Tests.TestCommon; namespace WalletWasabi.Tests.UnitTests.Userfacing; @@ -52,19 +53,21 @@ public void FormattingTest() // Creating a wallet with buggy password. var keyManager = KeyManager.CreateNew(out _, Guard.Correct(buggy), Network.Main); // Every wallet was created with Guard.Correct before. + var rnd = TestRandom.Wasabi(); + Logger.TurnOff(); // Password will be trimmed inside. PasswordHelper.GetMasterExtKey(keyManager, original, out _); // This should not throw format exception but pw is not correct. - Assert.Throws(() => PasswordHelper.GetMasterExtKey(keyManager, RandomString.AlphaNumeric(PasswordHelper.MaxPasswordLength), out _)); + Assert.Throws(() => PasswordHelper.GetMasterExtKey(keyManager, rnd.GetString(PasswordHelper.MaxPasswordLength, Constants.AlphaNumericCharacters), out _)); // Password should be formatted, before entering here. - Assert.Throws(() => PasswordHelper.GetMasterExtKey(keyManager, RandomString.AlphaNumeric(PasswordHelper.MaxPasswordLength + 1), out _)); + Assert.Throws(() => PasswordHelper.GetMasterExtKey(keyManager, rnd.GetString(PasswordHelper.MaxPasswordLength + 1, Constants.AlphaNumericCharacters), out _)); // Too long password with extra spaces. - var badPassword = $" {RandomString.AlphaNumeric(PasswordHelper.MaxPasswordLength + 1)} "; + var badPassword = $" {rnd.GetString(PasswordHelper.MaxPasswordLength + 1, Constants.AlphaNumericCharacters)} "; // Password should be formatted, before entering here. Assert.Throws(() => PasswordHelper.GetMasterExtKey(keyManager, badPassword, out _)); diff --git a/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/AliceTimeoutTests.cs b/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/AliceTimeoutTests.cs index 649f1ae249..6946bda003 100644 --- a/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/AliceTimeoutTests.cs +++ b/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/AliceTimeoutTests.cs @@ -1,6 +1,7 @@ using System.Threading; using System.Threading.Tasks; using WalletWasabi.Tests.Helpers; +using WalletWasabi.Tests.TestCommon; using WalletWasabi.WabiSabi.Backend; using WalletWasabi.WabiSabi.Backend.Models; using WalletWasabi.WabiSabi.Backend.Rounds; @@ -18,6 +19,7 @@ public class AliceTimeoutTests [Fact] public async Task AliceRegistrationTimesOutAsync() { + var rnd = TestRandom.Get(); using CancellationTokenSource testDeadlineCts = new(TimeSpan.FromMinutes(5)); // Sanity timeout for the unit test. using CancellationTokenSource silentLeave = new(); var silentLeaveToken = silentLeave.Token; @@ -27,10 +29,10 @@ public async Task AliceRegistrationTimesOutAsync() var round = WabiSabiTestFactory.CreateRound(cfg); var km = ServiceFactory.CreateKeyManager(password: ""); var key = BitcoinFactory.CreateHdPubKey(km); - var smartCoin = BitcoinFactory.CreateSmartCoin(key, 10m); - var rpc = WabiSabiTestFactory.CreatePreconfiguredRpcClient(smartCoin.Coin); + var smartCoin = BitcoinFactory.CreateSmartCoin(rnd, key, 10m); + var rpc = WabiSabiTestFactory.CreatePreconfiguredRpcClient(rnd, smartCoin.Coin); - using Arena arena = await ArenaTestFactory.From(cfg).With(rpc).CreateAndStartAsync(round); + using Arena arena = await ArenaTestFactory.From(cfg).With(rpc).CreateAndStartAsync(rnd, round); var arenaClient = WabiSabiTestFactory.CreateArenaClient(arena); using RoundStateUpdater roundStateUpdater = new(TimeSpan.FromSeconds(2), ["CoinJoinCoordinatorIdentifier"], arena); @@ -72,16 +74,17 @@ public async Task AliceRegistrationTimesOutAsync() [Fact] public async Task AliceDoesntTimeoutInConnectionConfirmationAsync() { + var rnd = TestRandom.Get(); // Alice does not time out when it's not input registration anymore, // even though the deadline is reached. WabiSabiConfig cfg = WabiSabiTestFactory.CreateDefaultWabiSabiConfig(); var round = WabiSabiTestFactory.CreateRound(cfg); round.SetPhase(Phase.ConnectionConfirmation); - var alice = WabiSabiTestFactory.CreateAlice(round); + var alice = WabiSabiTestFactory.CreateAlice(rnd, round); round.Alices.Add(alice); - using Arena arena = await ArenaTestFactory.From(cfg).CreateAndStartAsync(round); + using Arena arena = await ArenaTestFactory.From(cfg).CreateAndStartAsync(rnd, round); - var req = WabiSabiTestFactory.CreateConnectionConfirmationRequest(round); + var req = WabiSabiTestFactory.CreateConnectionConfirmationRequest(rnd, round); Assert.Single(round.Alices); DateTimeOffset preDeadline = DateTimeOffset.UtcNow - TimeSpan.FromMilliseconds(1); @@ -98,15 +101,16 @@ public async Task AliceDoesntTimeoutIfInputRegistrationTimedoutAsync() { // Alice does not time out if input registration timed out, // even though the deadline is reached and still in input reg. + var rnd = TestRandom.Get(); WabiSabiConfig cfg = WabiSabiTestFactory.CreateDefaultWabiSabiConfig(); cfg.StandardInputRegistrationTimeout = TimeSpan.Zero; var round = WabiSabiTestFactory.CreateRound(cfg); - var alice = WabiSabiTestFactory.CreateAlice(round); + var alice = WabiSabiTestFactory.CreateAlice(rnd, round); round.Alices.Add(alice); - using Arena arena = await ArenaTestFactory.From(cfg).CreateAndStartAsync(round); + using Arena arena = await ArenaTestFactory.From(cfg).CreateAndStartAsync(rnd, round); - var req = WabiSabiTestFactory.CreateConnectionConfirmationRequest(round); + var req = WabiSabiTestFactory.CreateConnectionConfirmationRequest(rnd, round); Assert.Single(round.Alices); DateTimeOffset preDeadline = DateTimeOffset.UtcNow - TimeSpan.FromMilliseconds(1); @@ -123,17 +127,18 @@ public async Task AliceDoesntTimeoutIfMaxInputCountReachedAsync() { // Alice does not time out if input reg is full with alices, // even though the deadline is reached and still in input reg. + var rnd = TestRandom.Get(); WabiSabiConfig cfg = WabiSabiTestFactory.CreateDefaultWabiSabiConfig(); cfg.MaxInputCountByRound = 3; var round = WabiSabiTestFactory.CreateRound(cfg); - var alice = WabiSabiTestFactory.CreateAlice(round); + var alice = WabiSabiTestFactory.CreateAlice(rnd, round); round.Alices.Add(alice); - round.Alices.Add(WabiSabiTestFactory.CreateAlice(round)); - round.Alices.Add(WabiSabiTestFactory.CreateAlice(round)); - using Arena arena = await ArenaTestFactory.From(cfg).CreateAndStartAsync(round); + round.Alices.Add(WabiSabiTestFactory.CreateAlice(rnd, round)); + round.Alices.Add(WabiSabiTestFactory.CreateAlice(rnd, round)); + using Arena arena = await ArenaTestFactory.From(cfg).CreateAndStartAsync(rnd, round); - var req = WabiSabiTestFactory.CreateConnectionConfirmationRequest(round); + var req = WabiSabiTestFactory.CreateConnectionConfirmationRequest(rnd, round); Assert.Equal(3, round.Alices.Count); DateTimeOffset preDeadline = DateTimeOffset.UtcNow - TimeSpan.FromMilliseconds(1); @@ -149,13 +154,14 @@ public async Task AliceDoesntTimeoutIfMaxInputCountReachedAsync() public async Task AliceDeadlineUpdatedAsync() { // Alice's deadline is updated by connection confirmation. + var rnd = TestRandom.Get(); WabiSabiConfig cfg = WabiSabiTestFactory.CreateDefaultWabiSabiConfig(); var round = WabiSabiTestFactory.CreateRound(cfg); - var alice = WabiSabiTestFactory.CreateAlice(round); + var alice = WabiSabiTestFactory.CreateAlice(rnd, round); round.Alices.Add(alice); - using Arena arena = await ArenaTestFactory.From(cfg).CreateAndStartAsync(round); + using Arena arena = await ArenaTestFactory.From(cfg).CreateAndStartAsync(rnd, round); - var req = WabiSabiTestFactory.CreateConnectionConfirmationRequest(round); + var req = WabiSabiTestFactory.CreateConnectionConfirmationRequest(rnd, round); Assert.Single(round.Alices); DateTimeOffset preDeadline = DateTimeOffset.UtcNow - TimeSpan.FromMilliseconds(1); diff --git a/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/CoinJoinMempoolManagerTests.cs b/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/CoinJoinMempoolManagerTests.cs index 0139c99f19..e1eddd24d2 100644 --- a/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/CoinJoinMempoolManagerTests.cs +++ b/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/CoinJoinMempoolManagerTests.cs @@ -66,7 +66,7 @@ public async Task CoinJoinVisibleTestAsync() finally { await services.StopAllAsync(); - await coreNode.TryStopAsync(); + await coreNode.TryStopAsync(true, 2); } } } diff --git a/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/CoordinatorTests.cs b/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/CoordinatorTests.cs index f5b91e46a5..3e5a835cda 100644 --- a/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/CoordinatorTests.cs +++ b/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/CoordinatorTests.cs @@ -92,7 +92,7 @@ public void BanDoubleSpendersTest() { var round = WabiSabiTestFactory.CreateRound(cfg); using Key key = new(); - Alice alice = WabiSabiTestFactory.CreateAlice(key: key, round: round); + Alice alice = WabiSabiTestFactory.CreateAlice(TestRandom.Get(), key: key, round: round); // Register our coin.. round.CoinjoinState = round.AddInput(alice.Coin, alice.OwnershipProof, WabiSabiTestFactory.CreateCommitmentData(round.Id)); @@ -136,7 +136,7 @@ private static Transaction CreateTransaction(Money amount, OutPoint? outPoint = var tx = Network.RegTest.CreateTransaction(); tx.Version = 1; tx.LockTime = LockTime.Zero; - tx.Inputs.Add(outPoint ?? BitcoinFactory.CreateOutPoint(), new Script(OpcodeType.OP_0, OpcodeType.OP_0), sequence: Sequence.Final); + tx.Inputs.Add(outPoint ?? BitcoinFactory.CreateOutPoint(TestRandom.Get()), new Script(OpcodeType.OP_0, OpcodeType.OP_0), sequence: Sequence.Final); using Key key = new(); tx.Outputs.Add(amount, key.GetScriptPubKey(ScriptPubKeyType.Segwit)); return tx; diff --git a/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/OffenderSerializationTests.cs b/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/OffenderSerializationTests.cs index e1d0a35b27..2fe5a948df 100644 --- a/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/OffenderSerializationTests.cs +++ b/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/OffenderSerializationTests.cs @@ -1,6 +1,7 @@ -using System.Linq; using NBitcoin; +using System.Linq; using WalletWasabi.Tests.Helpers; +using WalletWasabi.Tests.TestCommon; using WalletWasabi.WabiSabi.Backend.DoSPrevention; using WalletWasabi.WabiSabi.Models; using Xunit; @@ -12,9 +13,10 @@ public class OffenderSerializationTests [Fact] public void SerializationTest() { - var outpoint = BitcoinFactory.CreateOutPoint(); + var rnd = TestRandom.Get(); + var outpoint = BitcoinFactory.CreateOutPoint(rnd); var now = DateTimeOffset.UtcNow; - var roundId = BitcoinFactory.CreateUint256(); + var roundId = BitcoinFactory.CreateUint256(rnd); // Cheating var offender0 = new Offender(outpoint, now, new Cheating(roundId)); @@ -47,7 +49,7 @@ public void SerializationTest() Assert.Equal(offender4str, Offender.FromStringLine(offender4str).ToStringLine()); // Fail to verify - var ancestors = Enumerable.Range(0, 3).Select(_ => BitcoinFactory.CreateOutPoint()).ToArray(); + var ancestors = Enumerable.Range(0, 3).Select(_ => BitcoinFactory.CreateOutPoint(rnd)).ToArray(); 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/PhaseStepping/StepConnectionConfirmationTests.cs b/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/PhaseStepping/StepConnectionConfirmationTests.cs index 8de4920be7..21b36115ce 100644 --- a/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/PhaseStepping/StepConnectionConfirmationTests.cs +++ b/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/PhaseStepping/StepConnectionConfirmationTests.cs @@ -1,6 +1,7 @@ using System.Threading; using System.Threading.Tasks; using WalletWasabi.Tests.Helpers; +using WalletWasabi.Tests.TestCommon; using WalletWasabi.Tests.UnitTests.WabiSabi.Backend.Rounds.Utils; using WalletWasabi.WabiSabi.Backend; using WalletWasabi.WabiSabi.Backend.DoSPrevention; @@ -14,15 +15,16 @@ public class StepConnectionConfirmationTests [Fact] public async Task AllConfirmedStepsAsync() { + var rnd = TestRandom.Get(); WabiSabiConfig cfg = WabiSabiTestFactory.CreateDefaultWabiSabiConfig(); cfg.MaxInputCountByRound = 4; cfg.MinInputCountByRoundMultiplier = 0.5; var round = WabiSabiTestFactory.CreateRound(cfg); - var a1 = WabiSabiTestFactory.CreateAlice(round); - var a2 = WabiSabiTestFactory.CreateAlice(round); - var a3 = WabiSabiTestFactory.CreateAlice(round); - var a4 = WabiSabiTestFactory.CreateAlice(round); + var a1 = WabiSabiTestFactory.CreateAlice(rnd, round); + var a2 = WabiSabiTestFactory.CreateAlice(rnd, round); + var a3 = WabiSabiTestFactory.CreateAlice(rnd, round); + var a4 = WabiSabiTestFactory.CreateAlice(rnd, round); a1.ConfirmedConnection = true; a2.ConfirmedConnection = true; a3.ConfirmedConnection = true; @@ -32,7 +34,7 @@ public async Task AllConfirmedStepsAsync() round.Alices.Add(a3); round.Alices.Add(a4); round.SetPhase(Phase.ConnectionConfirmation); - using Arena arena = await ArenaTestFactory.From(cfg).CreateAndStartAsync(round); + using Arena arena = await ArenaTestFactory.From(cfg).CreateAndStartAsync(rnd, round); await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); Assert.Equal(Phase.OutputRegistration, round.Phase); @@ -43,15 +45,16 @@ public async Task AllConfirmedStepsAsync() [Fact] public async Task NotAllConfirmedStaysAsync() { + var rnd = TestRandom.Get(); WabiSabiConfig cfg = WabiSabiTestFactory.CreateDefaultWabiSabiConfig(); cfg.MaxInputCountByRound = 4; cfg.MinInputCountByRoundMultiplier = 0.5; var round = WabiSabiTestFactory.CreateRound(cfg); - var a1 = WabiSabiTestFactory.CreateAlice(round); - var a2 = WabiSabiTestFactory.CreateAlice(round); - var a3 = WabiSabiTestFactory.CreateAlice(round); - var a4 = WabiSabiTestFactory.CreateAlice(round); + var a1 = WabiSabiTestFactory.CreateAlice(rnd, round); + var a2 = WabiSabiTestFactory.CreateAlice(rnd, round); + var a3 = WabiSabiTestFactory.CreateAlice(rnd, round); + var a4 = WabiSabiTestFactory.CreateAlice(rnd, round); a1.ConfirmedConnection = true; a2.ConfirmedConnection = true; a3.ConfirmedConnection = true; @@ -63,7 +66,7 @@ public async Task NotAllConfirmedStaysAsync() round.SetPhase(Phase.ConnectionConfirmation); Prison prison = WabiSabiTestFactory.CreatePrison(); - using Arena arena = await ArenaTestFactory.From(cfg, prison).CreateAndStartAsync(round); + using Arena arena = await ArenaTestFactory.From(cfg, prison).CreateAndStartAsync(rnd, round); await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); Assert.Equal(Phase.ConnectionConfirmation, round.Phase); @@ -75,15 +78,16 @@ public async Task NotAllConfirmedStaysAsync() [Fact] public async Task EnoughConfirmedTimedoutStepsAsync() { + var rnd = TestRandom.Get(); WabiSabiConfig cfg = WabiSabiTestFactory.CreateWabiSabiConfig(); cfg.MaxInputCountByRound = 4; cfg.ConnectionConfirmationTimeout = TimeSpan.Zero; var round = WabiSabiTestFactory.CreateRound(cfg); - var a1 = WabiSabiTestFactory.CreateAlice(round); - var a2 = WabiSabiTestFactory.CreateAlice(round); - var a3 = WabiSabiTestFactory.CreateAlice(round); - var a4 = WabiSabiTestFactory.CreateAlice(round); + var a1 = WabiSabiTestFactory.CreateAlice(rnd, round); + var a2 = WabiSabiTestFactory.CreateAlice(rnd, round); + var a3 = WabiSabiTestFactory.CreateAlice(rnd, round); + var a4 = WabiSabiTestFactory.CreateAlice(rnd, round); a1.ConfirmedConnection = true; a2.ConfirmedConnection = true; a3.ConfirmedConnection = false; @@ -95,7 +99,7 @@ public async Task EnoughConfirmedTimedoutStepsAsync() round.SetPhase(Phase.ConnectionConfirmation); Prison prison = WabiSabiTestFactory.CreatePrison(); - using Arena arena = await ArenaTestFactory.From(cfg, prison).CreateAndStartAsync(round); + using Arena arena = await ArenaTestFactory.From(cfg, prison).CreateAndStartAsync(rnd, round); await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); @@ -110,16 +114,17 @@ public async Task EnoughConfirmedTimedoutStepsAsync() [Fact] public async Task NotEnoughConfirmedTimedoutDestroysAsync() { + var rnd = TestRandom.Get(); WabiSabiConfig cfg = WabiSabiTestFactory.CreateWabiSabiConfig(); cfg.MaxInputCountByRound = 4; cfg.ConnectionConfirmationTimeout = TimeSpan.Zero; cfg.MinInputCountByRoundMultiplier = 0.9; var round = WabiSabiTestFactory.CreateRound(cfg); - var a1 = WabiSabiTestFactory.CreateAlice(round); - var a2 = WabiSabiTestFactory.CreateAlice(round); - var a3 = WabiSabiTestFactory.CreateAlice(round); - var a4 = WabiSabiTestFactory.CreateAlice(round); + var a1 = WabiSabiTestFactory.CreateAlice(rnd, round); + var a2 = WabiSabiTestFactory.CreateAlice(rnd, round); + var a3 = WabiSabiTestFactory.CreateAlice(rnd, round); + var a4 = WabiSabiTestFactory.CreateAlice(rnd, round); a1.ConfirmedConnection = true; a2.ConfirmedConnection = false; a3.ConfirmedConnection = false; @@ -131,7 +136,7 @@ public async Task NotEnoughConfirmedTimedoutDestroysAsync() round.SetPhase(Phase.ConnectionConfirmation); Prison prison = WabiSabiTestFactory.CreatePrison(); - using Arena arena = await ArenaTestFactory.From(cfg, prison).CreateAndStartAsync(round); + using Arena arena = await ArenaTestFactory.From(cfg, prison).CreateAndStartAsync(rnd, round); await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); Assert.DoesNotContain(round, arena.GetActiveRounds()); diff --git a/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/PhaseStepping/StepInputRegistrationTests.cs b/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/PhaseStepping/StepInputRegistrationTests.cs index 5833de6f8d..fa64927087 100644 --- a/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/PhaseStepping/StepInputRegistrationTests.cs +++ b/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/PhaseStepping/StepInputRegistrationTests.cs @@ -1,6 +1,7 @@ using System.Threading; using System.Threading.Tasks; using WalletWasabi.Tests.Helpers; +using WalletWasabi.Tests.TestCommon; using WalletWasabi.Tests.UnitTests.WabiSabi.Backend.Rounds.Utils; using WalletWasabi.WabiSabi.Backend; using WalletWasabi.WabiSabi.Backend.Rounds; @@ -13,21 +14,22 @@ public class StepInputRegistrationTests [Fact] public async Task RoundFullAsync() { + var rnd = TestRandom.Get(); WabiSabiConfig cfg = WabiSabiTestFactory.CreateDefaultWabiSabiConfig(); cfg.MaxInputCountByRound = 3; var round = WabiSabiTestFactory.CreateRound(cfg); - using Arena arena = await ArenaTestFactory.From(cfg).CreateAndStartAsync(round); + using Arena arena = await ArenaTestFactory.From(cfg).CreateAndStartAsync(rnd, round); - round.Alices.Add(WabiSabiTestFactory.CreateAlice(round)); + round.Alices.Add(WabiSabiTestFactory.CreateAlice(rnd, round)); await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); Assert.Equal(Phase.InputRegistration, round.Phase); - round.Alices.Add(WabiSabiTestFactory.CreateAlice(round)); + round.Alices.Add(WabiSabiTestFactory.CreateAlice(rnd, round)); await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); Assert.Equal(Phase.InputRegistration, round.Phase); - round.Alices.Add(WabiSabiTestFactory.CreateAlice(round)); + round.Alices.Add(WabiSabiTestFactory.CreateAlice(rnd, round)); await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); Assert.Equal(Phase.ConnectionConfirmation, round.Phase); @@ -37,13 +39,14 @@ public async Task RoundFullAsync() [Fact] public async Task DetectSpentTxoBeforeSteppingIntoConnectionConfirmationAsync() { + var rnd = TestRandom.Get(); WabiSabiConfig cfg = WabiSabiTestFactory.CreateDefaultWabiSabiConfig(); cfg.MaxInputCountByRound = 3; var round = WabiSabiTestFactory.CreateRound(cfg); - var offendingAlice = WabiSabiTestFactory.CreateAlice(round); // this Alice spent the coin after registration + var offendingAlice = WabiSabiTestFactory.CreateAlice(rnd, round); // this Alice spent the coin after registration - var mockRpc = WabiSabiTestFactory.CreatePreconfiguredRpcClient(); + var mockRpc = WabiSabiTestFactory.CreatePreconfiguredRpcClient(rnd); var defaultBehavior = mockRpc.OnGetTxOutAsync; mockRpc.OnGetTxOutAsync = (txId, n, b) => { @@ -56,16 +59,16 @@ public async Task DetectSpentTxoBeforeSteppingIntoConnectionConfirmationAsync() return defaultBehavior?.Invoke(txId, n, b); }; - using Arena arena = await ArenaTestFactory.From(cfg).With(mockRpc).CreateAndStartAsync(round); + using Arena arena = await ArenaTestFactory.From(cfg).With(mockRpc).CreateAndStartAsync(rnd, round); - round.Alices.Add(WabiSabiTestFactory.CreateAlice(round)); + round.Alices.Add(WabiSabiTestFactory.CreateAlice(rnd, round)); round.Alices.Add(offendingAlice); - round.Alices.Add(WabiSabiTestFactory.CreateAlice(round)); + round.Alices.Add(WabiSabiTestFactory.CreateAlice(rnd, round)); await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); Assert.Equal(Phase.InputRegistration, round.Phase); Assert.Equal(2, round.Alices.Count); // the offending alice was removed - round.Alices.Add(WabiSabiTestFactory.CreateAlice(round)); + round.Alices.Add(WabiSabiTestFactory.CreateAlice(rnd, round)); await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); Assert.Equal(Phase.ConnectionConfirmation, round.Phase); Assert.Equal(3, round.Alices.Count); @@ -76,20 +79,21 @@ public async Task DetectSpentTxoBeforeSteppingIntoConnectionConfirmationAsync() [Fact] public async Task BlameRoundFullAsync() { + var rnd = TestRandom.Get(); WabiSabiConfig cfg = WabiSabiTestFactory.CreateDefaultWabiSabiConfig(); cfg.MaxInputCountByRound = 4; cfg.MinInputCountByRoundMultiplier = 0.5; var round = WabiSabiTestFactory.CreateRound(cfg); - var alice1 = WabiSabiTestFactory.CreateAlice(round); - var alice2 = WabiSabiTestFactory.CreateAlice(round); - var alice3 = WabiSabiTestFactory.CreateAlice(round); + var alice1 = WabiSabiTestFactory.CreateAlice(rnd, round); + var alice2 = WabiSabiTestFactory.CreateAlice(rnd, round); + var alice3 = WabiSabiTestFactory.CreateAlice(rnd, round); round.Alices.Add(alice1); round.Alices.Add(alice2); round.Alices.Add(alice3); var blameRound = WabiSabiTestFactory.CreateBlameRound(round, cfg); - using Arena arena = await ArenaTestFactory.From(cfg).CreateAndStartAsync(blameRound); + using Arena arena = await ArenaTestFactory.From(cfg).CreateAndStartAsync(rnd, blameRound); blameRound.Alices.Add(alice1); await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); @@ -109,16 +113,17 @@ public async Task BlameRoundFullAsync() [Fact] public async Task InputRegistrationTimedoutWithSufficientInputsAsync() { + var rnd = TestRandom.Get(); WabiSabiConfig cfg = WabiSabiTestFactory.CreateDefaultWabiSabiConfig(); cfg.StandardInputRegistrationTimeout = TimeSpan.Zero; cfg.MaxInputCountByRound = 4; cfg.MinInputCountByRoundMultiplier = 0.5; var round = WabiSabiTestFactory.CreateRound(cfg); - round.Alices.Add(WabiSabiTestFactory.CreateAlice(round)); - round.Alices.Add(WabiSabiTestFactory.CreateAlice(round)); + round.Alices.Add(WabiSabiTestFactory.CreateAlice(rnd, round)); + round.Alices.Add(WabiSabiTestFactory.CreateAlice(rnd, round)); - using Arena arena = await ArenaTestFactory.From(cfg).CreateAndStartAsync(round); + using Arena arena = await ArenaTestFactory.From(cfg).CreateAndStartAsync(rnd, round); await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); Assert.Equal(Phase.ConnectionConfirmation, round.Phase); @@ -128,6 +133,7 @@ public async Task InputRegistrationTimedoutWithSufficientInputsAsync() [Fact] public async Task BlameRoundTimedoutWithSufficientInputsAsync() { + var rnd = TestRandom.Get(); WabiSabiConfig cfg = WabiSabiTestFactory.CreateDefaultWabiSabiConfig(); cfg.BlameInputRegistrationTimeout = TimeSpan.Zero; cfg.StandardInputRegistrationTimeout = TimeSpan.FromHours(1); // Test that this is disregarded. @@ -135,9 +141,9 @@ public async Task BlameRoundTimedoutWithSufficientInputsAsync() cfg.MinInputCountByRoundMultiplier = 0.5; var round = WabiSabiTestFactory.CreateRound(cfg); - var alice1 = WabiSabiTestFactory.CreateAlice(round); - var alice2 = WabiSabiTestFactory.CreateAlice(round); - var alice3 = WabiSabiTestFactory.CreateAlice(round); + var alice1 = WabiSabiTestFactory.CreateAlice(rnd, round); + var alice2 = WabiSabiTestFactory.CreateAlice(rnd, round); + var alice3 = WabiSabiTestFactory.CreateAlice(rnd, round); round.Alices.Add(alice1); round.Alices.Add(alice2); round.Alices.Add(alice3); @@ -145,7 +151,7 @@ public async Task BlameRoundTimedoutWithSufficientInputsAsync() blameRound.Alices.Add(alice1); blameRound.Alices.Add(alice2); - using Arena arena = await ArenaTestFactory.From(cfg).CreateAndStartAsync(blameRound); + using Arena arena = await ArenaTestFactory.From(cfg).CreateAndStartAsync(rnd, blameRound); await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); Assert.Equal(Phase.ConnectionConfirmation, blameRound.Phase); @@ -155,15 +161,16 @@ public async Task BlameRoundTimedoutWithSufficientInputsAsync() [Fact] public async Task InputRegistrationTimedoutWithoutSufficientInputsAsync() { + var rnd = TestRandom.Get(); WabiSabiConfig cfg = WabiSabiTestFactory.CreateDefaultWabiSabiConfig(); cfg.StandardInputRegistrationTimeout = TimeSpan.Zero; cfg.MaxInputCountByRound = 4; cfg.MinInputCountByRoundMultiplier = 0.5; var round = WabiSabiTestFactory.CreateRound(cfg); - using Arena arena = await ArenaTestFactory.From(cfg).CreateAndStartAsync(round); + using Arena arena = await ArenaTestFactory.From(cfg).CreateAndStartAsync(rnd, round); - round.Alices.Add(WabiSabiTestFactory.CreateAlice(round)); + round.Alices.Add(WabiSabiTestFactory.CreateAlice(rnd, round)); await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); Assert.Equal(Phase.Ended, round.Phase); Assert.DoesNotContain(round, arena.GetActiveRounds()); @@ -177,6 +184,7 @@ public async Task BlameRoundTimedoutWithoutSufficientInputsAsync() // This test also tests that the min input count multiplier is applied // against the max input count by round number and not against the // number of inputs awaited by the blame round itself. + var rnd = TestRandom.Get(); WabiSabiConfig cfg = WabiSabiTestFactory.CreateDefaultWabiSabiConfig(); cfg.BlameInputRegistrationTimeout = TimeSpan.Zero; cfg.StandardInputRegistrationTimeout = TimeSpan.FromHours(1); // Test that this is disregarded. @@ -185,11 +193,11 @@ public async Task BlameRoundTimedoutWithoutSufficientInputsAsync() cfg.MinInputCountByBlameRoundMultiplier = 0.4; var round = WabiSabiTestFactory.CreateRound(cfg); - var alice1 = WabiSabiTestFactory.CreateAlice(round); - var alice2 = WabiSabiTestFactory.CreateAlice(round); - var alice3 = WabiSabiTestFactory.CreateAlice(round); - var alice4 = WabiSabiTestFactory.CreateAlice(round); - var alice5 = WabiSabiTestFactory.CreateAlice(round); + var alice1 = WabiSabiTestFactory.CreateAlice(rnd, round); + var alice2 = WabiSabiTestFactory.CreateAlice(rnd, round); + var alice3 = WabiSabiTestFactory.CreateAlice(rnd, round); + var alice4 = WabiSabiTestFactory.CreateAlice(rnd, round); + var alice5 = WabiSabiTestFactory.CreateAlice(rnd, round); round.Alices.Add(alice1); round.Alices.Add(alice2); round.Alices.Add(alice3); @@ -200,7 +208,7 @@ public async Task BlameRoundTimedoutWithoutSufficientInputsAsync() blameRound.Alices.Add(alice2); blameRound.Alices.Add(alice3); - using Arena arena = await ArenaTestFactory.From(cfg).CreateAndStartAsync(blameRound); + using Arena arena = await ArenaTestFactory.From(cfg).CreateAndStartAsync(rnd, blameRound); await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); Assert.Equal(Phase.Ended, blameRound.Phase); Assert.DoesNotContain(blameRound, arena.GetActiveRounds()); @@ -211,16 +219,17 @@ public async Task BlameRoundTimedoutWithoutSufficientInputsAsync() [Fact] public async Task InputRegistrationTimeoutCanBeModifiedRuntimeAsync() { + var rnd = TestRandom.Get(); WabiSabiConfig cfg = WabiSabiTestFactory.CreateDefaultWabiSabiConfig(); cfg.StandardInputRegistrationTimeout = TimeSpan.FromHours(1); cfg.MaxInputCountByRound = 4; cfg.MinInputCountByRoundMultiplier = 0.5; var round = WabiSabiTestFactory.CreateRound(cfg); - using Arena arena = await ArenaTestFactory.From(cfg).CreateAndStartAsync(round); + using Arena arena = await ArenaTestFactory.From(cfg).CreateAndStartAsync(rnd, round); - round.Alices.Add(WabiSabiTestFactory.CreateAlice(round)); - round.Alices.Add(WabiSabiTestFactory.CreateAlice(round)); + round.Alices.Add(WabiSabiTestFactory.CreateAlice(rnd, round)); + round.Alices.Add(WabiSabiTestFactory.CreateAlice(rnd, round)); await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); Assert.Equal(Phase.InputRegistration, round.Phase); diff --git a/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/PhaseStepping/StepOutputRegistrationTests.cs b/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/PhaseStepping/StepOutputRegistrationTests.cs index a90a905da6..45e4fc9d60 100644 --- a/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/PhaseStepping/StepOutputRegistrationTests.cs +++ b/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/PhaseStepping/StepOutputRegistrationTests.cs @@ -5,6 +5,7 @@ using WalletWasabi.Blockchain.TransactionOutputs; using WalletWasabi.Helpers; using WalletWasabi.Tests.Helpers; +using WalletWasabi.Tests.TestCommon; using WalletWasabi.WabiSabi; using WalletWasabi.WabiSabi.Backend; using WalletWasabi.WabiSabi.Backend.Models; @@ -25,6 +26,7 @@ public class StepOutputRegistrationTests [Fact] public async Task AllBobsRegisteredAsync() { + var rnd = TestRandom.Get(); using CancellationTokenSource cancellationTokenSource = new(TestTimeout); var token = cancellationTokenSource.Token; @@ -32,10 +34,10 @@ public async Task AllBobsRegisteredAsync() cfg.MaxInputCountByRound = 2; cfg.MinInputCountByRoundMultiplier = 0.5; - var (keyChain, coin1, coin2) = WabiSabiTestFactory.CreateCoinKeyPairs(); + var (keyChain, coin1, coin2) = WabiSabiTestFactory.CreateCoinKeyPairs(rnd); - var mockRpc = WabiSabiTestFactory.CreatePreconfiguredRpcClient(coin1.Coin, coin2.Coin); - using Arena arena = await ArenaTestFactory.From(cfg).With(mockRpc).CreateAndStartAsync(); + var mockRpc = WabiSabiTestFactory.CreatePreconfiguredRpcClient(rnd, coin1.Coin, coin2.Coin); + using Arena arena = await ArenaTestFactory.From(cfg).With(mockRpc).CreateAndStartAsync(rnd); var (round, arenaClient, alices) = await CreateRoundWithTwoConfirmedConnectionsAsync(arena, keyChain, coin1, coin2); var (amountCredentials1, vsizeCredentials1) = (alices[0].IssuedAmountCredentials, alices[0].IssuedVsizeCredentials); var (amountCredentials2, vsizeCredentials2) = (alices[1].IssuedAmountCredentials, alices[1].IssuedVsizeCredentials); @@ -73,6 +75,7 @@ await bobClient.RegisterOutputAsync( [Fact] public async Task SomeBobsRegisteredTimeoutAsync() { + var rnd = TestRandom.Get(); using CancellationTokenSource cancellationTokenSource = new(TestTimeout); var token = cancellationTokenSource.Token; @@ -82,10 +85,10 @@ public async Task SomeBobsRegisteredTimeoutAsync() cfg.OutputRegistrationTimeout = TimeSpan.Zero; cfg.CoordinationFeeRate = CoordinationFeeRate.Zero; - var (keyChain, coin1, coin2) = WabiSabiTestFactory.CreateCoinKeyPairs(); + var (keyChain, coin1, coin2) = WabiSabiTestFactory.CreateCoinKeyPairs(rnd); - var mockRpc = WabiSabiTestFactory.CreatePreconfiguredRpcClient(coin1.Coin, coin2.Coin); - using Arena arena = await ArenaTestFactory.From(cfg).With(mockRpc).CreateAndStartAsync(); + var mockRpc = WabiSabiTestFactory.CreatePreconfiguredRpcClient(rnd, coin1.Coin, coin2.Coin); + using Arena arena = await ArenaTestFactory.From(cfg).With(mockRpc).CreateAndStartAsync(rnd); var (round, arenaClient, alices) = await CreateRoundWithTwoConfirmedConnectionsAsync(arena, keyChain, coin1, coin2); var (amountCredentials1, vsizeCredentials1) = (alices[0].IssuedAmountCredentials, alices[0].IssuedVsizeCredentials); var (amountCredentials2, vsizeCredentials2) = (alices[1].IssuedAmountCredentials, alices[1].IssuedVsizeCredentials); @@ -117,16 +120,17 @@ public async Task DiffTooSmallToBlameAsync() using CancellationTokenSource cancellationTokenSource = new(TestTimeout); var token = cancellationTokenSource.Token; + var rnd = TestRandom.Get(); WabiSabiConfig cfg = WabiSabiTestFactory.CreateDefaultWabiSabiConfig(); cfg.MaxInputCountByRound = 2; cfg.MinInputCountByRoundMultiplier = 0.5; cfg.OutputRegistrationTimeout = TimeSpan.Zero; cfg.CoordinationFeeRate = CoordinationFeeRate.Zero; - var (keyChain, coin1, coin2) = WabiSabiTestFactory.CreateCoinKeyPairs(); + var (keyChain, coin1, coin2) = WabiSabiTestFactory.CreateCoinKeyPairs(rnd); - var mockRpc = WabiSabiTestFactory.CreatePreconfiguredRpcClient(coin1.Coin, coin2.Coin); - using Arena arena = await ArenaTestFactory.From(cfg).With(mockRpc).CreateAndStartAsync(); + var mockRpc = WabiSabiTestFactory.CreatePreconfiguredRpcClient(rnd, coin1.Coin, coin2.Coin); + using Arena arena = await ArenaTestFactory.From(cfg).With(mockRpc).CreateAndStartAsync(rnd); var (round, arenaClient, alices) = await CreateRoundWithTwoConfirmedConnectionsAsync(arena, keyChain, coin1, coin2); var (amountCredentials1, vsizeCredentials1) = (alices[0].IssuedAmountCredentials, alices[0].IssuedVsizeCredentials); var (amountCredentials2, vsizeCredentials2) = (alices[1].IssuedAmountCredentials, alices[1].IssuedVsizeCredentials); @@ -153,7 +157,7 @@ await bobClient.RegisterOutputAsync( // the remaining amount after deducting the fees needs to be less // than the minimum. var txParameters = round.Parameters; - var extraAlice = WabiSabiTestFactory.CreateAlice(round.Parameters.MiningFeeRate.GetFee(Constants.P2wpkhInputVirtualSize) + txParameters.AllowedOutputAmounts.Min - new Money(1L), round); + var extraAlice = WabiSabiTestFactory.CreateAlice(rnd, round.Parameters.MiningFeeRate.GetFee(Constants.P2wpkhInputVirtualSize) + txParameters.AllowedOutputAmounts.Min - new Money(1L), round); extraAlice.ReadyToSign = true; round.Alices.Add(extraAlice); round.CoinjoinState = round.Assert().AddInput(extraAlice.Coin, extraAlice.OwnershipProof, WabiSabiTestFactory.CreateCommitmentData(round.Id)); @@ -174,14 +178,15 @@ public async Task DoesntSwitchImmaturelyAsync() using CancellationTokenSource cancellationTokenSource = new(TestTimeout); var token = cancellationTokenSource.Token; + var rnd = TestRandom.Get(); WabiSabiConfig cfg = WabiSabiTestFactory.CreateDefaultWabiSabiConfig(); cfg.MaxInputCountByRound = 2; cfg.MinInputCountByRoundMultiplier = 0.5; - var (keyChain, coin1, coin2) = WabiSabiTestFactory.CreateCoinKeyPairs(); + var (keyChain, coin1, coin2) = WabiSabiTestFactory.CreateCoinKeyPairs(rnd); - var mockRpc = WabiSabiTestFactory.CreatePreconfiguredRpcClient(coin1.Coin, coin2.Coin); - using Arena arena = await ArenaTestFactory.From(cfg).With(mockRpc).CreateAndStartAsync(); + var mockRpc = WabiSabiTestFactory.CreatePreconfiguredRpcClient(rnd, coin1.Coin, coin2.Coin); + using Arena arena = await ArenaTestFactory.From(cfg).With(mockRpc).CreateAndStartAsync(rnd); var (round, arenaClient, alices) = await CreateRoundWithTwoConfirmedConnectionsAsync(arena, keyChain, coin1, coin2); var (amountCredentials1, vsizeCredentials1) = (alices[0].IssuedAmountCredentials, alices[0].IssuedVsizeCredentials); var (amountCredentials2, vsizeCredentials2) = (alices[1].IssuedAmountCredentials, alices[1].IssuedVsizeCredentials); @@ -253,6 +258,7 @@ public async Task SomeBobsReusingAddressAsync() using CancellationTokenSource silentLeave = new(); var silentLeaveToken = silentLeave.Token; + var rnd = TestRandom.Get(); WabiSabiConfig cfg = WabiSabiTestFactory.CreateDefaultWabiSabiConfig(); cfg.MaxInputCountByRound = 2; cfg.MinInputCountByRoundMultiplier = 0.5; @@ -261,11 +267,11 @@ public async Task SomeBobsReusingAddressAsync() var keyManager1 = ServiceFactory.CreateKeyManager(""); var keyManager2 = ServiceFactory.CreateKeyManager(""); - var (keyChain1, coin1a, coin1b) = WabiSabiTestFactory.CreateCoinKeyPairs(keyManager1); - var (keyChain2, coin2a, coin2b) = WabiSabiTestFactory.CreateCoinKeyPairs(keyManager2); + var (keyChain1, coin1a, coin1b) = WabiSabiTestFactory.CreateCoinKeyPairs(rnd, keyManager1); + var (keyChain2, coin2a, coin2b) = WabiSabiTestFactory.CreateCoinKeyPairs(rnd, keyManager2); - var mockRpc = WabiSabiTestFactory.CreatePreconfiguredRpcClient(coin1a.Coin, coin1b.Coin, coin2a.Coin, coin2b.Coin); - using Arena arena = await ArenaTestFactory.From(cfg).With(mockRpc).CreateAndStartAsync(); + var mockRpc = WabiSabiTestFactory.CreatePreconfiguredRpcClient(rnd, coin1a.Coin, coin1b.Coin, coin2a.Coin, coin2b.Coin); + using Arena arena = await ArenaTestFactory.From(cfg).With(mockRpc).CreateAndStartAsync(rnd); // Get the round. await arena.TriggerAndWaitRoundAsync(token); @@ -400,15 +406,16 @@ public async Task AliceIsNotReadyAsync() using CancellationTokenSource cancellationTokenSource = new(TestTimeout); var token = cancellationTokenSource.Token; + var rnd = TestRandom.Get(); WabiSabiConfig cfg = WabiSabiTestFactory.CreateDefaultWabiSabiConfig(); cfg.MaxInputCountByRound = 2; cfg.MinInputCountByRoundMultiplier = 0.5; cfg.OutputRegistrationTimeout = TimeSpan.Zero; - var (keyChain, coin1, coin2) = WabiSabiTestFactory.CreateCoinKeyPairs(); + var (keyChain, coin1, coin2) = WabiSabiTestFactory.CreateCoinKeyPairs(rnd); - var mockRpc = WabiSabiTestFactory.CreatePreconfiguredRpcClient(coin1.Coin, coin2.Coin); - using Arena arena = await ArenaTestFactory.From(cfg).With(mockRpc).CreateAndStartAsync(); + var mockRpc = WabiSabiTestFactory.CreatePreconfiguredRpcClient(rnd, coin1.Coin, coin2.Coin); + using Arena arena = await ArenaTestFactory.From(cfg).With(mockRpc).CreateAndStartAsync(rnd); var (round, arenaClient, alices) = await CreateRoundWithTwoConfirmedConnectionsAsync(arena, keyChain, coin1, coin2); await alices[0].ReadyToSignAsync(token); diff --git a/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/PhaseStepping/StepTransactionSigningTests.cs b/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/PhaseStepping/StepTransactionSigningTests.cs index 0b0b984b30..393bef8753 100644 --- a/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/PhaseStepping/StepTransactionSigningTests.cs +++ b/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/PhaseStepping/StepTransactionSigningTests.cs @@ -5,6 +5,7 @@ using System.Threading.Tasks; using WalletWasabi.Blockchain.TransactionOutputs; using WalletWasabi.Tests.Helpers; +using WalletWasabi.Tests.TestCommon; using WalletWasabi.Tests.UnitTests.WabiSabi.Backend.Rounds.Utils; using WalletWasabi.WabiSabi; using WalletWasabi.WabiSabi.Backend; @@ -29,16 +30,17 @@ public async Task EveryoneSignedAsync() using CancellationTokenSource cancellationTokenSource = new(TestTimeout); var token = cancellationTokenSource.Token; + var rnd = TestRandom.Get(); WabiSabiConfig cfg = WabiSabiTestFactory.CreateDefaultWabiSabiConfig(); cfg.MaxInputCountByRound = 2; cfg.MinInputCountByRoundMultiplier = 0.5; cfg.MaxSuggestedAmountBase = Money.Satoshis(ProtocolConstants.MaxAmountCredentialValue); cfg.CreateNewRoundBeforeInputRegEnd = TimeSpan.Zero; - var (keyChain, coin1, coin2) = WabiSabiTestFactory.CreateCoinKeyPairs(); + var (keyChain, coin1, coin2) = WabiSabiTestFactory.CreateCoinKeyPairs(rnd); - var mockRpc = WabiSabiTestFactory.CreatePreconfiguredRpcClient(coin1.Coin, coin2.Coin); - using Arena arena = await ArenaTestFactory.From(cfg).With(mockRpc).CreateAndStartAsync(); + var mockRpc = WabiSabiTestFactory.CreatePreconfiguredRpcClient(rnd, coin1.Coin, coin2.Coin); + using Arena arena = await ArenaTestFactory.From(cfg).With(mockRpc).CreateAndStartAsync(rnd); var (round, aliceClient1, aliceClient2) = await CreateRoundWithOutputsReadyToSignAsync(arena, keyChain, coin1, coin2); await arena.TriggerAndWaitRoundAsync(token); @@ -61,15 +63,16 @@ public async Task TransactionBroadcastErrorsAsync() using CancellationTokenSource cancellationTokenSource = new(TestTimeout); var token = cancellationTokenSource.Token; + var rnd = TestRandom.Get(); WabiSabiConfig cfg = WabiSabiTestFactory.CreateWabiSabiConfig(); - var (keyChain, coin1, coin2) = WabiSabiTestFactory.CreateCoinKeyPairs(); + var (keyChain, coin1, coin2) = WabiSabiTestFactory.CreateCoinKeyPairs(rnd); - var mockRpc = WabiSabiTestFactory.CreatePreconfiguredRpcClient(coin1.Coin, coin2.Coin); + var mockRpc = WabiSabiTestFactory.CreatePreconfiguredRpcClient(rnd, coin1.Coin, coin2.Coin); mockRpc.OnSendRawTransactionAsync = (_) => throw new RPCException(RPCErrorCode.RPC_TRANSACTION_REJECTED, "", null); Prison prison = WabiSabiTestFactory.CreatePrison(); - using Arena arena = await ArenaTestFactory.From(cfg, mockRpc, prison).CreateAndStartAsync(); + using Arena arena = await ArenaTestFactory.From(cfg, mockRpc, prison).CreateAndStartAsync(rnd); var (round, aliceClient1, aliceClient2) = await CreateRoundWithOutputsReadyToSignAsync(arena, keyChain, coin1, coin2); await arena.TriggerAndWaitRoundAsync(token); @@ -98,15 +101,16 @@ public async Task AlicesSpentAsync() using CancellationTokenSource cancellationTokenSource = new(TestTimeout); var token = cancellationTokenSource.Token; + var rnd = TestRandom.Get(); WabiSabiConfig cfg = WabiSabiTestFactory.CreateWabiSabiConfig(); - var (keyChain, coin1, coin2) = WabiSabiTestFactory.CreateCoinKeyPairs(); + var (keyChain, coin1, coin2) = WabiSabiTestFactory.CreateCoinKeyPairs(rnd); - var mockRpc = WabiSabiTestFactory.CreatePreconfiguredRpcClient(coin1.Coin, coin2.Coin); + var mockRpc = WabiSabiTestFactory.CreatePreconfiguredRpcClient(rnd, coin1.Coin, coin2.Coin); mockRpc.OnSendRawTransactionAsync = (_) => throw new RPCException(RPCErrorCode.RPC_TRANSACTION_REJECTED, "", null); Prison prison = WabiSabiTestFactory.CreatePrison(); - using Arena arena = await ArenaTestFactory.From(cfg, mockRpc, prison).CreateAndStartAsync(); + using Arena arena = await ArenaTestFactory.From(cfg, mockRpc, prison).CreateAndStartAsync(rnd); var (round, aliceClient1, aliceClient2) = await CreateRoundWithOutputsReadyToSignAsync(arena, keyChain, coin1, coin2); @@ -139,18 +143,19 @@ public async Task TimeoutInsufficientPeersAsync() using CancellationTokenSource cancellationTokenSource = new(TestTimeout); var token = cancellationTokenSource.Token; + var rnd = TestRandom.Get(); WabiSabiConfig cfg = WabiSabiTestFactory.CreateWabiSabiConfig(); cfg.MinInputCountByRoundMultiplier = 1; cfg.MinInputCountByBlameRoundMultiplier = 1; cfg.TransactionSigningTimeout = TimeSpan.Zero; - var (keyChain, coin1, coin2) = WabiSabiTestFactory.CreateCoinKeyPairs(); + var (keyChain, coin1, coin2) = WabiSabiTestFactory.CreateCoinKeyPairs(rnd); - var mockRpc = WabiSabiTestFactory.CreatePreconfiguredRpcClient(coin1.Coin, coin2.Coin); + var mockRpc = WabiSabiTestFactory.CreatePreconfiguredRpcClient(rnd, coin1.Coin, coin2.Coin); mockRpc.OnSendRawTransactionAsync = (_) => throw new RPCException(RPCErrorCode.RPC_TRANSACTION_REJECTED, "", null); Prison prison = WabiSabiTestFactory.CreatePrison(); - using Arena arena = await ArenaTestFactory.From(cfg, mockRpc, prison).CreateAndStartAsync(); + using Arena arena = await ArenaTestFactory.From(cfg, mockRpc, prison).CreateAndStartAsync(rnd); var (round, aliceClient1, aliceClient2) = await CreateRoundWithOutputsReadyToSignAsync(arena, keyChain, coin1, coin2); await arena.TriggerAndWaitRoundAsync(token); @@ -175,23 +180,24 @@ public async Task TimeoutSufficientPeersAsync() using CancellationTokenSource cancellationTokenSource = new(TestTimeout); var token = cancellationTokenSource.Token; + var rnd = TestRandom.Get(); WabiSabiConfig cfg = WabiSabiTestFactory.CreateWabiSabiConfig(); cfg.MinInputCountByRoundMultiplier = 1; cfg.TransactionSigningTimeout = TimeSpan.Zero; cfg.FailFastOutputRegistrationTimeout = TimeSpan.Zero; - var (keyChain, coin1, coin2) = WabiSabiTestFactory.CreateCoinKeyPairs(); + var (keyChain, coin1, coin2) = WabiSabiTestFactory.CreateCoinKeyPairs(rnd); - var mockRpc = WabiSabiTestFactory.CreatePreconfiguredRpcClient(coin1.Coin, coin2.Coin); + var mockRpc = WabiSabiTestFactory.CreatePreconfiguredRpcClient(rnd, coin1.Coin, coin2.Coin); mockRpc.OnSendRawTransactionAsync = (_) => throw new RPCException(RPCErrorCode.RPC_TRANSACTION_REJECTED, "", null); Prison prison = WabiSabiTestFactory.CreatePrison(); - using Arena arena = await ArenaTestFactory.From(cfg, mockRpc, prison).CreateAndStartAsync(); + using Arena arena = await ArenaTestFactory.From(cfg, mockRpc, prison).CreateAndStartAsync(rnd); var (round, aliceClient1, aliceClient2) = await CreateRoundWithOutputsReadyToSignAsync(arena, keyChain, coin1, coin2); // Make sure not all alices signed. - var alice3 = WabiSabiTestFactory.CreateAlice(round); + var alice3 = WabiSabiTestFactory.CreateAlice(rnd, round); alice3.ConfirmedConnection = true; alice3.ReadyToSign = true; round.Alices.Add(alice3); @@ -227,20 +233,21 @@ public async Task AliceWasNotReadyAsync() using CancellationTokenSource cancellationTokenSource = new(TestTimeout); var token = cancellationTokenSource.Token; + var rnd = TestRandom.Get(); WabiSabiConfig cfg = WabiSabiTestFactory.CreateWabiSabiConfig(); cfg.TransactionSigningTimeout = TimeSpan.Zero; cfg.OutputRegistrationTimeout = TimeSpan.Zero; cfg.FailFastTransactionSigningTimeout = TimeSpan.Zero; - var (keyChain, coin1, coin2) = WabiSabiTestFactory.CreateCoinKeyPairs(); + var (keyChain, coin1, coin2) = WabiSabiTestFactory.CreateCoinKeyPairs(rnd); - var mockRpc = WabiSabiTestFactory.CreatePreconfiguredRpcClient(coin1.Coin, coin2.Coin); + var mockRpc = WabiSabiTestFactory.CreatePreconfiguredRpcClient(rnd, coin1.Coin, coin2.Coin); mockRpc.OnSendRawTransactionAsync = (tx) => throw new RPCException(RPCErrorCode.RPC_TRANSACTION_REJECTED, "", null); Prison prison = WabiSabiTestFactory.CreatePrison(); - using Arena arena = await ArenaTestFactory.From(cfg, mockRpc, prison).CreateAndStartAsync(); + using Arena arena = await ArenaTestFactory.From(cfg, mockRpc, prison).CreateAndStartAsync(rnd); var (round, aliceClient1, aliceClient2) = await CreateRoundWithOutputsReadyToSignAsync(arena, keyChain, coin1, coin2); var badOutpoint = aliceClient1.SmartCoin.Coin.Outpoint; diff --git a/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/PostRequests/ConfirmConnectionTests.cs b/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/PostRequests/ConfirmConnectionTests.cs index 88b0939dab..5e5a400e0a 100644 --- a/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/PostRequests/ConfirmConnectionTests.cs +++ b/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/PostRequests/ConfirmConnectionTests.cs @@ -4,6 +4,7 @@ using WabiSabi.CredentialRequesting; using WabiSabi.Crypto; using WalletWasabi.Tests.Helpers; +using WalletWasabi.Tests.TestCommon; using WalletWasabi.WabiSabi.Backend; using WalletWasabi.WabiSabi.Backend.Models; using WalletWasabi.WabiSabi.Backend.Rounds; @@ -17,14 +18,15 @@ public class ConfirmConnectionTests [Fact] public async Task SuccessInInputRegistrationPhaseAsync() { + var rnd = TestRandom.Get(); WabiSabiConfig cfg = WabiSabiTestFactory.CreateDefaultWabiSabiConfig(); var round = WabiSabiTestFactory.CreateRound(cfg); - var alice = WabiSabiTestFactory.CreateAlice(round); + var alice = WabiSabiTestFactory.CreateAlice(rnd, round); var preDeadline = alice.Deadline; round.Alices.Add(alice); - using Arena arena = await ArenaTestFactory.From(cfg).CreateAndStartAsync(round); + using Arena arena = await ArenaTestFactory.From(cfg).CreateAndStartAsync(rnd, round); - var req = WabiSabiTestFactory.CreateConnectionConfirmationRequest(round); + var req = WabiSabiTestFactory.CreateConnectionConfirmationRequest(rnd, round); var minAliceDeadline = DateTimeOffset.UtcNow + (cfg.ConnectionConfirmationTimeout * 0.9); var resp = await arena.ConfirmConnectionAsync(req, CancellationToken.None); @@ -43,16 +45,17 @@ public async Task SuccessInInputRegistrationPhaseAsync() [Fact] public async Task SuccessInConnectionConfirmationPhaseAsync() { + var rnd = TestRandom.Get(); WabiSabiConfig cfg = WabiSabiTestFactory.CreateDefaultWabiSabiConfig(); var round = WabiSabiTestFactory.CreateRound(cfg); round.SetPhase(Phase.ConnectionConfirmation); - var alice = WabiSabiTestFactory.CreateAlice(round); + var alice = WabiSabiTestFactory.CreateAlice(rnd, round); var preDeadline = alice.Deadline; round.Alices.Add(alice); - using Arena arena = await ArenaTestFactory.From(cfg).CreateAndStartAsync(round); + using Arena arena = await ArenaTestFactory.From(cfg).CreateAndStartAsync(rnd, round); - var req = WabiSabiTestFactory.CreateConnectionConfirmationRequest(round); + var req = WabiSabiTestFactory.CreateConnectionConfirmationRequest(rnd, round); var resp = await arena.ConfirmConnectionAsync(req, CancellationToken.None); Assert.NotNull(resp); @@ -69,10 +72,11 @@ public async Task SuccessInConnectionConfirmationPhaseAsync() [Fact] public async Task RoundNotFoundAsync() { + var rnd = TestRandom.Get(); var cfg = WabiSabiTestFactory.CreateDefaultWabiSabiConfig(); var nonExistingRound = WabiSabiTestFactory.CreateRound(cfg); - using Arena arena = await ArenaTestFactory.Default.CreateAndStartAsync(); - var req = WabiSabiTestFactory.CreateConnectionConfirmationRequest(nonExistingRound); + using Arena arena = await ArenaTestFactory.Default.CreateAndStartAsync(rnd); + var req = WabiSabiTestFactory.CreateConnectionConfirmationRequest(rnd, nonExistingRound); var ex = await Assert.ThrowsAsync( async () => await arena.ConfirmConnectionAsync(req, CancellationToken.None)); @@ -84,15 +88,16 @@ public async Task RoundNotFoundAsync() [Fact] public async Task WrongPhaseAsync() { + var rnd = TestRandom.Get(); WabiSabiConfig cfg = WabiSabiTestFactory.CreateDefaultWabiSabiConfig(); Round round = WabiSabiTestFactory.CreateRound(cfg); - using Arena arena = await ArenaTestFactory.From(cfg).CreateAndStartAsync(round); - var alice = WabiSabiTestFactory.CreateAlice(round); + using Arena arena = await ArenaTestFactory.From(cfg).CreateAndStartAsync(rnd, round); + var alice = WabiSabiTestFactory.CreateAlice(rnd, round); var preDeadline = alice.Deadline; round.Alices.Add(alice); await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); - var req = WabiSabiTestFactory.CreateConnectionConfirmationRequest(round); + var req = WabiSabiTestFactory.CreateConnectionConfirmationRequest(rnd, round); foreach (Phase phase in Enum.GetValues(typeof(Phase))) { if (phase != Phase.InputRegistration && phase != Phase.ConnectionConfirmation) @@ -113,11 +118,12 @@ public async Task WrongPhaseAsync() [Fact] public async Task AliceNotFoundAsync() { + var rnd = TestRandom.Get(); WabiSabiConfig cfg = WabiSabiTestFactory.CreateDefaultWabiSabiConfig(); var round = WabiSabiTestFactory.CreateRound(cfg); - using Arena arena = await ArenaTestFactory.From(cfg).CreateAndStartAsync(round); + using Arena arena = await ArenaTestFactory.From(cfg).CreateAndStartAsync(rnd, round); - var req = WabiSabiTestFactory.CreateConnectionConfirmationRequest(round); + var req = WabiSabiTestFactory.CreateConnectionConfirmationRequest(rnd, round); var ex = await Assert.ThrowsAsync(async () => await arena.ConfirmConnectionAsync(req, CancellationToken.None)); Assert.Equal(WabiSabiProtocolErrorCode.AliceNotFound, ex.ErrorCode); @@ -127,16 +133,17 @@ public async Task AliceNotFoundAsync() [Fact] public async Task IncorrectRequestedVsizeCredentialsAsync() { + var rnd = TestRandom.Get(); WabiSabiConfig cfg = WabiSabiTestFactory.CreateDefaultWabiSabiConfig(); var round = WabiSabiTestFactory.CreateRound(cfg); round.SetPhase(Phase.ConnectionConfirmation); - var alice = WabiSabiTestFactory.CreateAlice(round); + var alice = WabiSabiTestFactory.CreateAlice(rnd, round); round.Alices.Add(alice); - using Arena arena = await ArenaTestFactory.From(cfg).CreateAndStartAsync(round); + using Arena arena = await ArenaTestFactory.From(cfg).CreateAndStartAsync(rnd, round); Assert.Contains(alice, round.Alices); - var incorrectVsizeCredentials = WabiSabiTestFactory.CreateRealCredentialRequests(round, null, 234).vsizeRequest; - var req = WabiSabiTestFactory.CreateConnectionConfirmationRequest(round) with { RealVsizeCredentialRequests = incorrectVsizeCredentials }; + var incorrectVsizeCredentials = WabiSabiTestFactory.CreateRealCredentialRequests(rnd, round, null, 234).vsizeRequest; + var req = WabiSabiTestFactory.CreateConnectionConfirmationRequest(rnd, round) with { RealVsizeCredentialRequests = incorrectVsizeCredentials }; var ex = await Assert.ThrowsAsync(async () => await arena.ConfirmConnectionAsync(req, CancellationToken.None)); Assert.Equal(WabiSabiProtocolErrorCode.IncorrectRequestedVsizeCredentials, ex.ErrorCode); @@ -148,15 +155,16 @@ public async Task IncorrectRequestedVsizeCredentialsAsync() [Fact] public async Task IncorrectRequestedAmountCredentialsAsync() { + var rnd = TestRandom.Get(); WabiSabiConfig cfg = WabiSabiTestFactory.CreateDefaultWabiSabiConfig(); var round = WabiSabiTestFactory.CreateRound(cfg); round.SetPhase(Phase.ConnectionConfirmation); - var alice = WabiSabiTestFactory.CreateAlice(round); + var alice = WabiSabiTestFactory.CreateAlice(rnd, round); round.Alices.Add(alice); - using Arena arena = await ArenaTestFactory.From(cfg).CreateAndStartAsync(round); + using Arena arena = await ArenaTestFactory.From(cfg).CreateAndStartAsync(rnd, round); - var incorrectAmountCredentials = WabiSabiTestFactory.CreateRealCredentialRequests(round, Money.Coins(3), null).amountRequest; - var req = WabiSabiTestFactory.CreateConnectionConfirmationRequest(round) with { RealAmountCredentialRequests = incorrectAmountCredentials }; + var incorrectAmountCredentials = WabiSabiTestFactory.CreateRealCredentialRequests(rnd, round, Money.Coins(3), null).amountRequest; + var req = WabiSabiTestFactory.CreateConnectionConfirmationRequest(rnd, round) with { RealAmountCredentialRequests = incorrectAmountCredentials }; var ex = await Assert.ThrowsAsync(async () => await arena.ConfirmConnectionAsync(req, CancellationToken.None)); Assert.Equal(WabiSabiProtocolErrorCode.IncorrectRequestedAmountCredentials, ex.ErrorCode); @@ -185,14 +193,15 @@ private async Task InvalidRequestedCredentialsAsync( Func credentialIssuerSelector, Func credentialsRequestSelector) { + var rnd = TestRandom.Get(); WabiSabiConfig cfg = WabiSabiTestFactory.CreateDefaultWabiSabiConfig(); var round = WabiSabiTestFactory.CreateRound(cfg); round.SetPhase(Phase.ConnectionConfirmation); - var alice = WabiSabiTestFactory.CreateAlice(round); + var alice = WabiSabiTestFactory.CreateAlice(rnd, round); round.Alices.Add(alice); - using Arena arena = await ArenaTestFactory.From(cfg).CreateAndStartAsync(round); + using Arena arena = await ArenaTestFactory.From(cfg).CreateAndStartAsync(rnd, round); - var req = WabiSabiTestFactory.CreateConnectionConfirmationRequest(round); + var req = WabiSabiTestFactory.CreateConnectionConfirmationRequest(rnd, round); var (issuer, issuer2) = credentialIssuerSelector(round); var credentialsRequest = credentialsRequestSelector(req); diff --git a/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/PostRequests/CredentialReissuanceTests.cs b/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/PostRequests/CredentialReissuanceTests.cs index 5bcb0eecfc..32d4ac21b5 100644 --- a/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/PostRequests/CredentialReissuanceTests.cs +++ b/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/PostRequests/CredentialReissuanceTests.cs @@ -2,6 +2,7 @@ using System.Threading; using System.Threading.Tasks; using WalletWasabi.Tests.Helpers; +using WalletWasabi.Tests.TestCommon; using WalletWasabi.WabiSabi.Backend; using WalletWasabi.WabiSabi.Backend.Models; using WalletWasabi.WabiSabi.Backend.Rounds; @@ -15,12 +16,13 @@ public class CredentialReissuanceTest [Fact] public async Task ReissueExactDeltaAmountAsync() { + var rnd = TestRandom.Get(); WabiSabiConfig cfg = WabiSabiTestFactory.CreateDefaultWabiSabiConfig(); var round = WabiSabiTestFactory.CreateRound(cfg); round.SetPhase(Phase.OutputRegistration); - var alice = WabiSabiTestFactory.CreateAlice(round); + var alice = WabiSabiTestFactory.CreateAlice(rnd, round); round.Alices.Add(alice); - using Arena arena = await ArenaTestFactory.From(cfg).CreateAndStartAsync(round); + using Arena arena = await ArenaTestFactory.From(cfg).CreateAndStartAsync(rnd, round); // Step 1. Create credentials var (amClient, vsClient, amIssuer, vsIssuer, amZeroCredentials, vsZeroCredentials) = WabiSabiTestFactory.CreateWabiSabiClientsAndIssuers(round); diff --git a/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/PostRequests/ReadyToSignRequestRequestTests.cs b/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/PostRequests/ReadyToSignRequestRequestTests.cs index 0aef2e66e3..5666b70a9f 100644 --- a/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/PostRequests/ReadyToSignRequestRequestTests.cs +++ b/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/PostRequests/ReadyToSignRequestRequestTests.cs @@ -1,5 +1,6 @@ using NBitcoin; using WalletWasabi.Tests.Helpers; +using WalletWasabi.Tests.TestCommon; using WalletWasabi.WabiSabi.Models; using Xunit; @@ -13,7 +14,8 @@ public class ReadyToSignRequestRequestTests [Fact] public void EqualityTest() { - uint256 roundId = BitcoinFactory.CreateUint256(); + var rnd = TestRandom.Get(); + uint256 roundId = BitcoinFactory.CreateUint256(rnd); Guid guid = Guid.NewGuid(); // Request #1. @@ -25,7 +27,7 @@ public void EqualityTest() Assert.Equal(request1, request2); // Request #3. - ReadyToSignRequestRequest request3 = new(roundId: BitcoinFactory.CreateUint256(), aliceId: guid); + ReadyToSignRequestRequest request3 = new(roundId: BitcoinFactory.CreateUint256(rnd), aliceId: guid); Assert.NotEqual(request1, request3); } diff --git a/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/PostRequests/RegisterInputFailureTests.cs b/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/PostRequests/RegisterInputFailureTests.cs index 8b98e95084..60e87ee771 100644 --- a/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/PostRequests/RegisterInputFailureTests.cs +++ b/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/PostRequests/RegisterInputFailureTests.cs @@ -1,9 +1,11 @@ +using GingerCommon.Crypto.Random; using NBitcoin; using System.Linq; using System.Threading; using System.Threading.Tasks; using WalletWasabi.Crypto; using WalletWasabi.Tests.Helpers; +using WalletWasabi.Tests.TestCommon; using WalletWasabi.WabiSabi.Backend; using WalletWasabi.WabiSabi.Backend.DoSPrevention; using WalletWasabi.WabiSabi.Backend.Models; @@ -24,14 +26,15 @@ private static async Task RegisterAndAssertWrongPhaseAsync(InputRegistrationRequ [Fact] public async Task RoundNotFoundAsync() { + var rnd = TestRandom.Get(); using Key key = new(); - using Arena arena = await ArenaTestFactory.Default.CreateAndStartAsync(); + using Arena arena = await ArenaTestFactory.Default.CreateAndStartAsync(rnd); await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); - var ownershipProof = WabiSabiTestFactory.CreateOwnershipProof(key); + var ownershipProof = WabiSabiTestFactory.CreateOwnershipProof(rnd, key); var arenaClient = WabiSabiTestFactory.CreateArenaClient(arena); var ex = await Assert.ThrowsAsync( - async () => await arenaClient.RegisterInputAsync(uint256.Zero, BitcoinFactory.CreateOutPoint(), ownershipProof, CancellationToken.None)); + async () => await arenaClient.RegisterInputAsync(uint256.Zero, BitcoinFactory.CreateOutPoint(rnd), ownershipProof, CancellationToken.None)); Assert.Equal(WabiSabiProtocolErrorCode.RoundNotFound, ex.ErrorCode); await arena.StopAsync(CancellationToken.None); @@ -40,14 +43,15 @@ public async Task RoundNotFoundAsync() [Fact] public async Task WrongPhaseAsync() { + var rnd = TestRandom.Get(); WabiSabiConfig cfg = WabiSabiTestFactory.CreateDefaultWabiSabiConfig(); using Key key = new(); var coin = WabiSabiTestFactory.CreateCoin(key); - var rpc = WabiSabiTestFactory.CreatePreconfiguredRpcClient(coin); - using Arena arena = await ArenaTestFactory.From(cfg).With(rpc).CreateAndStartAsync(); + var rpc = WabiSabiTestFactory.CreatePreconfiguredRpcClient(rnd, coin); + using Arena arena = await ArenaTestFactory.From(cfg).With(rpc).CreateAndStartAsync(rnd); await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); var round = arena.Rounds.First(); - var req = WabiSabiTestFactory.CreateInputRegistrationRequest(round, key: key, coin.Outpoint); + var req = WabiSabiTestFactory.CreateInputRegistrationRequest(rnd, round, key: key, coin.Outpoint); foreach (Phase phase in Enum.GetValues(typeof(Phase))) { @@ -64,21 +68,22 @@ public async Task WrongPhaseAsync() [Fact] public async Task InputRegistrationFullAsync() { + var rnd = TestRandom.Get(); WabiSabiConfig cfg = WabiSabiTestFactory.CreateDefaultWabiSabiConfig(); cfg.MaxInputCountByRound = 3; var round = WabiSabiTestFactory.CreateRound(cfg); using Key key = new(); var coin = WabiSabiTestFactory.CreateCoin(key); - var ownershipProof = WabiSabiTestFactory.CreateOwnershipProof(key, round.Id); - var rpc = WabiSabiTestFactory.CreatePreconfiguredRpcClient(coin); + var ownershipProof = WabiSabiTestFactory.CreateOwnershipProof(rnd, key, round.Id); + var rpc = WabiSabiTestFactory.CreatePreconfiguredRpcClient(rnd, coin); - using Arena arena = await ArenaTestFactory.From(cfg).With(rpc).CreateAndStartAsync(round); + using Arena arena = await ArenaTestFactory.From(cfg).With(rpc).CreateAndStartAsync(rnd, round); await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); - round.Alices.Add(WabiSabiTestFactory.CreateAlice(round)); - round.Alices.Add(WabiSabiTestFactory.CreateAlice(round)); - round.Alices.Add(WabiSabiTestFactory.CreateAlice(round)); + round.Alices.Add(WabiSabiTestFactory.CreateAlice(rnd, round)); + round.Alices.Add(WabiSabiTestFactory.CreateAlice(rnd, round)); + round.Alices.Add(WabiSabiTestFactory.CreateAlice(rnd, round)); var arenaClient = WabiSabiTestFactory.CreateArenaClient(arena); var ex = await Assert.ThrowsAsync( @@ -92,16 +97,17 @@ public async Task InputRegistrationFullAsync() [Fact] public async Task InputRegistrationTimedOutAsync() { + var rnd = TestRandom.Get(); WabiSabiConfig cfg = WabiSabiTestFactory.CreateDefaultWabiSabiConfig(); cfg.StandardInputRegistrationTimeout = TimeSpan.Zero; var round = WabiSabiTestFactory.CreateRound(cfg); using Key key = new(); var coin = WabiSabiTestFactory.CreateCoin(key); - var rpc = WabiSabiTestFactory.CreatePreconfiguredRpcClient(coin); - var ownershipProof = WabiSabiTestFactory.CreateOwnershipProof(key, round.Id); + var rpc = WabiSabiTestFactory.CreatePreconfiguredRpcClient(rnd, coin); + var ownershipProof = WabiSabiTestFactory.CreateOwnershipProof(rnd, key, round.Id); - using Arena arena = await ArenaTestFactory.From(cfg).With(rpc).CreateAndStartAsync(); + using Arena arena = await ArenaTestFactory.From(cfg).With(rpc).CreateAndStartAsync(rnd); await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); @@ -122,15 +128,16 @@ public async Task InputBannedAsync() using Key key = new(); var coin = WabiSabiTestFactory.CreateCoin(key); + var rnd = TestRandom.Get(); WabiSabiConfig cfg = WabiSabiTestFactory.CreateWabiSabiConfig(); var round = WabiSabiTestFactory.CreateRound(cfg); Prison prison = WabiSabiTestFactory.CreatePrison(); - var rpc = WabiSabiTestFactory.CreatePreconfiguredRpcClient(coin); - using Arena arena = await ArenaTestFactory.From(cfg, rpc, prison).CreateAndStartAsync(round); + var rpc = WabiSabiTestFactory.CreatePreconfiguredRpcClient(rnd, coin); + using Arena arena = await ArenaTestFactory.From(cfg, rpc, prison).CreateAndStartAsync(rnd, round); prison.FailedToSign(coin.Outpoint, Money.Coins(1m), round.Id); - var ownershipProof = WabiSabiTestFactory.CreateOwnershipProof(key, round.Id); + var ownershipProof = WabiSabiTestFactory.CreateOwnershipProof(rnd, key, round.Id); var arenaClient = WabiSabiTestFactory.CreateArenaClient(arena); @@ -146,18 +153,19 @@ public async Task InputBannedAsync() [Fact] public async Task InputLongBannedAsync() { + var rnd = TestRandom.Get(); using Key key = new(); var coin = WabiSabiTestFactory.CreateCoin(key); - var rpc = WabiSabiTestFactory.CreatePreconfiguredRpcClient(coin); + var rpc = WabiSabiTestFactory.CreatePreconfiguredRpcClient(rnd, coin); WabiSabiConfig cfg = WabiSabiTestFactory.CreateDefaultWabiSabiConfig(); var round = WabiSabiTestFactory.CreateRound(cfg); Prison prison = WabiSabiTestFactory.CreatePrison(); prison.FailedVerification(coin.Outpoint, round.Id, TimeSpan.Zero, ""); - using Arena arena = await ArenaTestFactory.From(cfg, rpc, prison).CreateAndStartAsync(round); + using Arena arena = await ArenaTestFactory.From(cfg, rpc, prison).CreateAndStartAsync(rnd, round); - var ownershipProof = WabiSabiTestFactory.CreateOwnershipProof(key, round.Id); + var ownershipProof = WabiSabiTestFactory.CreateOwnershipProof(rnd, key, round.Id); var arenaClient = WabiSabiTestFactory.CreateArenaClient(arena); @@ -173,20 +181,21 @@ public async Task InputLongBannedAsync() [Fact] public async Task InputCantBeNotedAsync() { + var rnd = TestRandom.Get(); using Key key = new(); var coin = WabiSabiTestFactory.CreateCoin(key); - var rpc = WabiSabiTestFactory.CreatePreconfiguredRpcClient(coin); + var rpc = WabiSabiTestFactory.CreatePreconfiguredRpcClient(rnd, coin); WabiSabiConfig cfg = WabiSabiTestFactory.CreateWabiSabiConfig(); cfg.AllowNotedInputRegistration = false; var round = WabiSabiTestFactory.CreateRound(cfg); - var ownershipProof = WabiSabiTestFactory.CreateOwnershipProof(key, round.Id); + var ownershipProof = WabiSabiTestFactory.CreateOwnershipProof(rnd, key, round.Id); Prison prison = WabiSabiTestFactory.CreatePrison(); prison.FailedToConfirm(coin.Outpoint, Money.Coins(1m), round.Id); - using Arena arena = await ArenaTestFactory.From(cfg, rpc, prison).CreateAndStartAsync(round); + using Arena arena = await ArenaTestFactory.From(cfg, rpc, prison).CreateAndStartAsync(rnd, round); var arenaClient = WabiSabiTestFactory.CreateArenaClient(arena); @@ -200,19 +209,20 @@ public async Task InputCantBeNotedAsync() [Fact] public async Task InputSpentAsync() { + var rnd = TestRandom.Get(); using Key key = new(); WabiSabiConfig cfg = WabiSabiTestFactory.CreateDefaultWabiSabiConfig(); var round = WabiSabiTestFactory.CreateRound(cfg); - var ownershipProof = WabiSabiTestFactory.CreateOwnershipProof(key, round.Id); + var ownershipProof = WabiSabiTestFactory.CreateOwnershipProof(rnd, key, round.Id); var mockRpc = new MockRpcClient(); mockRpc.OnGetTxOutAsync = (_, _, _) => null; - using Arena arena = await ArenaTestFactory.From(cfg).With(mockRpc).CreateAndStartAsync(round); + using Arena arena = await ArenaTestFactory.From(cfg).With(mockRpc).CreateAndStartAsync(rnd, round); var arenaClient = WabiSabiTestFactory.CreateArenaClient(arena); var ex = await Assert.ThrowsAsync( - async () => await arenaClient.RegisterInputAsync(round.Id, BitcoinFactory.CreateOutPoint(), ownershipProof, CancellationToken.None)); + async () => await arenaClient.RegisterInputAsync(round.Id, BitcoinFactory.CreateOutPoint(rnd), ownershipProof, CancellationToken.None)); Assert.Equal(WabiSabiProtocolErrorCode.InputSpent, ex.ErrorCode); await arena.StopAsync(CancellationToken.None); @@ -221,20 +231,21 @@ public async Task InputSpentAsync() [Fact] public async Task InputUnconfirmedAsync() { + var rnd = TestRandom.Get(); using Key key = new(); WabiSabiConfig cfg = WabiSabiTestFactory.CreateDefaultWabiSabiConfig(); var round = WabiSabiTestFactory.CreateRound(cfg); - var ownershipProof = WabiSabiTestFactory.CreateOwnershipProof(key, round.Id); + var ownershipProof = WabiSabiTestFactory.CreateOwnershipProof(rnd, key, round.Id); var mockRpc = new MockRpcClient(); mockRpc.OnGetTxOutAsync = (_, _, _) => new NBitcoin.RPC.GetTxOutResponse { Confirmations = 0 }; - using Arena arena = await ArenaTestFactory.From(cfg).With(mockRpc).CreateAndStartAsync(round); + using Arena arena = await ArenaTestFactory.From(cfg).With(mockRpc).CreateAndStartAsync(rnd, round); var arenaClient = WabiSabiTestFactory.CreateArenaClient(arena); var ex = await Assert.ThrowsAsync( - async () => await arenaClient.RegisterInputAsync(round.Id, BitcoinFactory.CreateOutPoint(), ownershipProof, CancellationToken.None)); + async () => await arenaClient.RegisterInputAsync(round.Id, BitcoinFactory.CreateOutPoint(rnd), ownershipProof, CancellationToken.None)); Assert.Equal(WabiSabiProtocolErrorCode.InputUnconfirmed, ex.ErrorCode); await arena.StopAsync(CancellationToken.None); @@ -243,12 +254,13 @@ public async Task InputUnconfirmedAsync() [Fact] public async Task InputImmatureAsync() { + var rnd = TestRandom.Get(); using Key key = new(); WabiSabiConfig cfg = WabiSabiTestFactory.CreateDefaultWabiSabiConfig(); var round = WabiSabiTestFactory.CreateRound(cfg); - var ownershipProof = WabiSabiTestFactory.CreateOwnershipProof(key, round.Id); + var ownershipProof = WabiSabiTestFactory.CreateOwnershipProof(rnd, key, round.Id); - var rpc = WabiSabiTestFactory.CreatePreconfiguredRpcClient(); + var rpc = WabiSabiTestFactory.CreatePreconfiguredRpcClient(rnd); var callCounter = 1; rpc.OnGetTxOutAsync = (_, _, _) => { @@ -256,14 +268,14 @@ public async Task InputImmatureAsync() callCounter++; return ret; }; - using Arena arena = await ArenaTestFactory.From(cfg).With(rpc).CreateAndStartAsync(round); + using Arena arena = await ArenaTestFactory.From(cfg).With(rpc).CreateAndStartAsync(rnd, round); var arenaClient = WabiSabiTestFactory.CreateArenaClient(arena); - var req = WabiSabiTestFactory.CreateInputRegistrationRequest(round: round); + var req = WabiSabiTestFactory.CreateInputRegistrationRequest(rnd, round: round); foreach (var i in Enumerable.Range(1, 100)) { var ex = await Assert.ThrowsAsync( - async () => await arenaClient.RegisterInputAsync(round.Id, BitcoinFactory.CreateOutPoint(), ownershipProof, CancellationToken.None)); + async () => await arenaClient.RegisterInputAsync(round.Id, BitcoinFactory.CreateOutPoint(rnd), ownershipProof, CancellationToken.None)); Assert.Equal(WabiSabiProtocolErrorCode.InputImmature, ex.ErrorCode); } @@ -274,6 +286,7 @@ public async Task InputImmatureAsync() [Fact] public async Task TaprootNotAllowedAsync() { + var rnd = TestRandom.Get(); WabiSabiConfig cfg = WabiSabiTestFactory.CreateDefaultWabiSabiConfig(); cfg.AllowP2trInputs = false; @@ -281,12 +294,12 @@ public async Task TaprootNotAllowedAsync() using Key key = new(); var coin = WabiSabiTestFactory.CreateCoin(key, scriptPubKeyType: ScriptPubKeyType.TaprootBIP86); - var rpc = WabiSabiTestFactory.CreatePreconfiguredRpcClient(coin); - using Arena arena = await ArenaTestFactory.From(cfg).With(rpc).CreateAndStartAsync(round); + var rpc = WabiSabiTestFactory.CreatePreconfiguredRpcClient(rnd, coin); + using Arena arena = await ArenaTestFactory.From(cfg).With(rpc).CreateAndStartAsync(rnd, round); var minAliceDeadline = DateTimeOffset.UtcNow + (cfg.ConnectionConfirmationTimeout * 0.9); var arenaClient = WabiSabiTestFactory.CreateArenaClient(arena); - var ownershipProof = WabiSabiTestFactory.CreateOwnershipProof(key, round.Id, scriptPubKeyType: ScriptPubKeyType.TaprootBIP86); + var ownershipProof = WabiSabiTestFactory.CreateOwnershipProof(rnd, key, round.Id, scriptPubKeyType: ScriptPubKeyType.TaprootBIP86); var ex = await Assert.ThrowsAsync( async () => await arenaClient.RegisterInputAsync(round.Id, coin.Outpoint, ownershipProof, CancellationToken.None)); @@ -298,36 +311,40 @@ public async Task TaprootNotAllowedAsync() [Fact] public async Task WrongScriptPubKeyInOwnershipProofAsync() { - await TestOwnershipProofAsync((key, roundId) => WabiSabiTestFactory.CreateOwnershipProof(new Key(), roundId)); + var rnd = TestRandom.Get(); + await TestOwnershipProofAsync(rnd, (key, roundId) => WabiSabiTestFactory.CreateOwnershipProof(rnd, new Key(), roundId)); } [Fact] public async Task WrongRoundIdInOwnershipProofAsync() { - await TestOwnershipProofAsync((key, roundId) => WabiSabiTestFactory.CreateOwnershipProof(key, uint256.One)); + var rnd = TestRandom.Get(); + await TestOwnershipProofAsync(rnd, (key, roundId) => WabiSabiTestFactory.CreateOwnershipProof(rnd, key, uint256.One)); } [Fact] public async Task WrongCoordinatorIdentifierInOwnershipProofAsync() { - await TestOwnershipProofAsync((key, roundId) => OwnershipProof.GenerateCoinJoinInputProof(key, new OwnershipIdentifier(key, key.PubKey.GetScriptPubKey(ScriptPubKeyType.Segwit)), new CoinJoinInputCommitmentData("test", roundId), ScriptPubKeyType.Segwit)); + var rnd = TestRandom.Get(); + await TestOwnershipProofAsync(rnd, (key, roundId) => OwnershipProof.GenerateCoinJoinInputProof(key, new OwnershipIdentifier(key, key.PubKey.GetScriptPubKey(ScriptPubKeyType.Segwit)), new CoinJoinInputCommitmentData("test", roundId), ScriptPubKeyType.Segwit)); } [Fact] public async Task NotEnoughFundsAsync() { + var rnd = TestRandom.Get(); using Key key = new(); var txOut = new TxOut(Money.Coins(1.0m), key.GetScriptPubKey(ScriptPubKeyType.Segwit)); - var outpoint = BitcoinFactory.CreateOutPoint(); - var rpc = WabiSabiTestFactory.CreatePreconfiguredRpcClient(new Coin(outpoint, txOut)); + var outpoint = BitcoinFactory.CreateOutPoint(rnd); + var rpc = WabiSabiTestFactory.CreatePreconfiguredRpcClient(rnd, new Coin(outpoint, txOut)); WabiSabiConfig cfg = WabiSabiTestFactory.CreateDefaultWabiSabiConfig(); cfg.MinRegistrableAmount = Money.Coins(2); var round = WabiSabiTestFactory.CreateRound(cfg); - var ownershipProof = WabiSabiTestFactory.CreateOwnershipProof(key, round.Id); + var ownershipProof = WabiSabiTestFactory.CreateOwnershipProof(rnd, key, round.Id); - using Arena arena = await ArenaTestFactory.From(cfg).With(rpc).CreateAndStartAsync(round); + using Arena arena = await ArenaTestFactory.From(cfg).With(rpc).CreateAndStartAsync(rnd, round); var arenaClient = WabiSabiTestFactory.CreateArenaClient(arena); var ex = await Assert.ThrowsAsync( @@ -340,16 +357,17 @@ public async Task NotEnoughFundsAsync() [Fact] public async Task TooMuchFundsAsync() { + var rnd = TestRandom.Get(); using Key key = new(); var coin = WabiSabiTestFactory.CreateCoin(key); - var rpc = WabiSabiTestFactory.CreatePreconfiguredRpcClient(coin); + var rpc = WabiSabiTestFactory.CreatePreconfiguredRpcClient(rnd, coin); WabiSabiConfig cfg = WabiSabiTestFactory.CreateDefaultWabiSabiConfig(); cfg.MaxRegistrableAmount = Money.Coins(0.9m); var round = WabiSabiTestFactory.CreateRound(cfg); - var ownershipProof = WabiSabiTestFactory.CreateOwnershipProof(key, round.Id); + var ownershipProof = WabiSabiTestFactory.CreateOwnershipProof(rnd, key, round.Id); - using Arena arena = await ArenaTestFactory.From(cfg).With(rpc).CreateAndStartAsync(round); + using Arena arena = await ArenaTestFactory.From(cfg).With(rpc).CreateAndStartAsync(rnd, round); var arenaClient = WabiSabiTestFactory.CreateArenaClient(arena); var ex = await Assert.ThrowsAsync( @@ -365,17 +383,18 @@ public async Task TooMuchVsizeAsync() // Configures a round that allows so many inputs (Alices) that // the virtual size each of they have available is not enough // to register anything. + var rnd = TestRandom.Get(); WabiSabiConfig cfg = WabiSabiTestFactory.CreateDefaultWabiSabiConfig(); cfg.MaxInputCountByRound = 100_000; using Key key = new(); var coin = WabiSabiTestFactory.CreateCoin(key); - var rpc = WabiSabiTestFactory.CreatePreconfiguredRpcClient(coin); + var rpc = WabiSabiTestFactory.CreatePreconfiguredRpcClient(rnd, coin); RoundParameterFactory roundParameterFactory = WabiSabiTestFactory.CreateRoundParametersFactory(cfg, rpc.Network, maxVsizeAllocationPerAlice: 0); Round round = WabiSabiTestFactory.CreateRound(roundParameterFactory.CreateRoundParameter(new FeeRate(10m), Money.Zero)); - using Arena arena = await ArenaTestFactory.From(cfg).With(rpc).With(roundParameterFactory).CreateAndStartAsync(round); - var ownershipProof = WabiSabiTestFactory.CreateOwnershipProof(key, round.Id); + using Arena arena = await ArenaTestFactory.From(cfg).With(rpc).With(roundParameterFactory).CreateAndStartAsync(rnd, round); + var ownershipProof = WabiSabiTestFactory.CreateOwnershipProof(rnd, key, round.Id); var arenaClient = WabiSabiTestFactory.CreateArenaClient(arena); var ex = await Assert.ThrowsAsync( @@ -388,18 +407,19 @@ public async Task TooMuchVsizeAsync() [Fact] public async Task AliceAlreadyRegisteredIntraRoundAsync() { + var rnd = TestRandom.Get(); using Key key = new(); var coin = WabiSabiTestFactory.CreateCoin(key); WabiSabiConfig cfg = WabiSabiTestFactory.CreateDefaultWabiSabiConfig(); var round = WabiSabiTestFactory.CreateRound(cfg); - var ownershipProof = WabiSabiTestFactory.CreateOwnershipProof(key, round.Id); + var ownershipProof = WabiSabiTestFactory.CreateOwnershipProof(rnd, key, round.Id); // Make sure an Alice have already been registered with the same input. - var anotherAlice = WabiSabiTestFactory.CreateAlice(coin, WabiSabiTestFactory.CreateOwnershipProof(key), round); + var anotherAlice = WabiSabiTestFactory.CreateAlice(coin, WabiSabiTestFactory.CreateOwnershipProof(rnd, key), round); round.Alices.Add(anotherAlice); round.SetPhase(Phase.ConnectionConfirmation); - var rpc = WabiSabiTestFactory.CreatePreconfiguredRpcClient(coin); - using Arena arena = await ArenaTestFactory.From(cfg).With(rpc).CreateAndStartAsync(round); + var rpc = WabiSabiTestFactory.CreatePreconfiguredRpcClient(rnd, coin); + using Arena arena = await ArenaTestFactory.From(cfg).With(rpc).CreateAndStartAsync(rnd, round); var arenaClient = WabiSabiTestFactory.CreateArenaClient(arena); var ex = await Assert.ThrowsAsync( @@ -412,20 +432,21 @@ public async Task AliceAlreadyRegisteredIntraRoundAsync() [Fact] public async Task AliceAlreadyRegisteredCrossRoundAsync() { + var rnd = TestRandom.Get(); using Key key = new(); var coin = WabiSabiTestFactory.CreateCoin(key); WabiSabiConfig cfg = WabiSabiTestFactory.CreateDefaultWabiSabiConfig(); var round = WabiSabiTestFactory.CreateRound(cfg); var anotherRound = WabiSabiTestFactory.CreateRound(cfg); - var ownershipProof = WabiSabiTestFactory.CreateOwnershipProof(key, round.Id); + var ownershipProof = WabiSabiTestFactory.CreateOwnershipProof(rnd, key, round.Id); // Make sure an Alice have already been registered with the same input. - var preAlice = WabiSabiTestFactory.CreateAlice(coin, WabiSabiTestFactory.CreateOwnershipProof(key), round); + var preAlice = WabiSabiTestFactory.CreateAlice(coin, WabiSabiTestFactory.CreateOwnershipProof(rnd, key), round); anotherRound.Alices.Add(preAlice); anotherRound.SetPhase(Phase.ConnectionConfirmation); - var rpc = WabiSabiTestFactory.CreatePreconfiguredRpcClient(coin); - using Arena arena = await ArenaTestFactory.From(cfg).With(rpc).CreateAndStartAsync(round, anotherRound); + var rpc = WabiSabiTestFactory.CreatePreconfiguredRpcClient(rnd, coin); + using Arena arena = await ArenaTestFactory.From(cfg).With(rpc).CreateAndStartAsync(rnd, round, anotherRound); var arenaClient = WabiSabiTestFactory.CreateArenaClient(arena); var ex = await Assert.ThrowsAsync( @@ -435,14 +456,14 @@ public async Task AliceAlreadyRegisteredCrossRoundAsync() await arena.StopAsync(CancellationToken.None); } - private async Task TestOwnershipProofAsync(Func ownershipProofFunc) + private async Task TestOwnershipProofAsync(GingerRandom rnd, Func ownershipProofFunc) { using Key key = new(); var coin = WabiSabiTestFactory.CreateCoin(key); WabiSabiConfig cfg = WabiSabiTestFactory.CreateDefaultWabiSabiConfig(); var round = WabiSabiTestFactory.CreateRound(cfg); - var rpc = WabiSabiTestFactory.CreatePreconfiguredRpcClient(coin); - using Arena arena = await ArenaTestFactory.From(cfg).With(rpc).CreateAndStartAsync(round); + var rpc = WabiSabiTestFactory.CreatePreconfiguredRpcClient(rnd, coin); + using Arena arena = await ArenaTestFactory.From(cfg).With(rpc).CreateAndStartAsync(rnd, round); var arenaClient = WabiSabiTestFactory.CreateArenaClient(arena); OwnershipProof ownershipProof = ownershipProofFunc(key, round.Id); diff --git a/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/PostRequests/RegisterInputSuccessTests.cs b/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/PostRequests/RegisterInputSuccessTests.cs index b99d25932b..0d1b7834aa 100644 --- a/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/PostRequests/RegisterInputSuccessTests.cs +++ b/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/PostRequests/RegisterInputSuccessTests.cs @@ -29,17 +29,18 @@ private static void AssertSingleAliceSuccessfullyRegistered(Round round, DateTim [Fact] public async Task SuccessAsync() { + var rnd = TestRandom.Get(); WabiSabiConfig cfg = WabiSabiTestFactory.CreateDefaultWabiSabiConfig(); var round = WabiSabiTestFactory.CreateRound(cfg); using Key key = new(); var coin = WabiSabiTestFactory.CreateCoin(key); - var rpc = WabiSabiTestFactory.CreatePreconfiguredRpcClient(coin); - using Arena arena = await ArenaTestFactory.From(cfg).With(rpc).CreateAndStartAsync(round); + var rpc = WabiSabiTestFactory.CreatePreconfiguredRpcClient(rnd, coin); + using Arena arena = await ArenaTestFactory.From(cfg).With(rpc).CreateAndStartAsync(rnd, round); var minAliceDeadline = DateTimeOffset.UtcNow + (cfg.ConnectionConfirmationTimeout * 0.9); var arenaClient = WabiSabiTestFactory.CreateArenaClient(arena); - var ownershipProof = WabiSabiTestFactory.CreateOwnershipProof(key, round.Id); + var ownershipProof = WabiSabiTestFactory.CreateOwnershipProof(rnd, key, round.Id); var (resp, _) = await arenaClient.RegisterInputAsync(round.Id, coin.Outpoint, ownershipProof, CancellationToken.None); AssertSingleAliceSuccessfullyRegistered(round, minAliceDeadline, resp); @@ -50,14 +51,15 @@ public async Task SuccessAsync() [Fact] public async Task SuccessCustomCoordinatorIdentifierAsync() { + var rnd = TestRandom.Get(); WabiSabiConfig cfg = WabiSabiTestFactory.CreateDefaultWabiSabiConfig(); cfg.CoordinatorIdentifier = "test"; var round = WabiSabiTestFactory.CreateRound(cfg, 1); using Key key = new(); var coin = WabiSabiTestFactory.CreateCoin(key); - var rpc = WabiSabiTestFactory.CreatePreconfiguredRpcClient(coin); - using Arena arena = await ArenaTestFactory.From(cfg).With(rpc).CreateAndStartAsync(round); + var rpc = WabiSabiTestFactory.CreatePreconfiguredRpcClient(rnd, coin); + using Arena arena = await ArenaTestFactory.From(cfg).With(rpc).CreateAndStartAsync(rnd, round); var minAliceDeadline = DateTimeOffset.UtcNow + (cfg.ConnectionConfirmationTimeout * 0.9); @@ -78,19 +80,20 @@ public async Task SuccessCustomCoordinatorIdentifierAsync() [Fact] public async Task SuccessFromPreviousCoinJoinAsync() { + var rnd = TestRandom.Get(); WabiSabiConfig cfg = WabiSabiTestFactory.CreateDefaultWabiSabiConfig(); var round = WabiSabiTestFactory.CreateRound(cfg); using Key key = new(); var coin = WabiSabiTestFactory.CreateCoin(key); - var rpc = WabiSabiTestFactory.CreatePreconfiguredRpcClient(coin); + var rpc = WabiSabiTestFactory.CreatePreconfiguredRpcClient(rnd, coin); var coinJoinIdStore = new CoinJoinIdStore(); coinJoinIdStore.TryAdd(coin.Outpoint.Hash); - using Arena arena = await ArenaTestFactory.From(cfg).With(rpc).With(coinJoinIdStore).CreateAndStartAsync(round); + using Arena arena = await ArenaTestFactory.From(cfg).With(rpc).With(coinJoinIdStore).CreateAndStartAsync(rnd, round); var minAliceDeadline = DateTimeOffset.UtcNow + (cfg.ConnectionConfirmationTimeout * 0.9); var arenaClient = WabiSabiTestFactory.CreateArenaClient(arena); - var ownershipProof = WabiSabiTestFactory.CreateOwnershipProof(key, round.Id); + var ownershipProof = WabiSabiTestFactory.CreateOwnershipProof(rnd, key, round.Id); var (resp, _) = await arenaClient.RegisterInputAsync(round.Id, coin.Outpoint, ownershipProof, CancellationToken.None); AssertSingleAliceSuccessfullyRegistered(round, minAliceDeadline, resp); @@ -104,19 +107,20 @@ public async Task SuccessFromPreviousCoinJoinAsync() [Fact] public async Task SuccessWithAliceUpdateIntraRoundAsync() { + var rnd = TestRandom.Get(); WabiSabiConfig cfg = WabiSabiTestFactory.CreateDefaultWabiSabiConfig(); var round = WabiSabiTestFactory.CreateRound(cfg); using Key key = new(); - var ownershipProof = WabiSabiTestFactory.CreateOwnershipProof(key, round.Id); + var ownershipProof = WabiSabiTestFactory.CreateOwnershipProof(rnd, key, round.Id); var coin = WabiSabiTestFactory.CreateCoin(key); // Make sure an Alice have already been registered with the same input. - var preAlice = WabiSabiTestFactory.CreateAlice(coin, WabiSabiTestFactory.CreateOwnershipProof(key), round); + var preAlice = WabiSabiTestFactory.CreateAlice(coin, WabiSabiTestFactory.CreateOwnershipProof(rnd, key), round); round.Alices.Add(preAlice); - var rpc = WabiSabiTestFactory.CreatePreconfiguredRpcClient(coin); - using Arena arena = await ArenaTestFactory.From(cfg).With(rpc).CreateAndStartAsync(round); + var rpc = WabiSabiTestFactory.CreatePreconfiguredRpcClient(rnd, coin); + using Arena arena = await ArenaTestFactory.From(cfg).With(rpc).CreateAndStartAsync(rnd, round); var arenaClient = WabiSabiTestFactory.CreateArenaClient(arena); var ex = await Assert.ThrowsAsync(async () => await arenaClient.RegisterInputAsync(round.Id, coin.Outpoint, ownershipProof, CancellationToken.None).ConfigureAwait(false)); @@ -128,6 +132,7 @@ public async Task SuccessWithAliceUpdateIntraRoundAsync() [Fact] public async Task TaprootSuccessAsync() { + var rnd = TestRandom.Get(); WabiSabiConfig cfg = WabiSabiTestFactory.CreateDefaultWabiSabiConfig(); cfg.AllowP2trInputs = true; @@ -135,12 +140,12 @@ public async Task TaprootSuccessAsync() using Key key = new(); var coin = WabiSabiTestFactory.CreateCoin(key, scriptPubKeyType: ScriptPubKeyType.TaprootBIP86); - var rpc = WabiSabiTestFactory.CreatePreconfiguredRpcClient(coin); - using Arena arena = await ArenaTestFactory.From(cfg).With(rpc).CreateAndStartAsync(round); + var rpc = WabiSabiTestFactory.CreatePreconfiguredRpcClient(rnd, coin); + using Arena arena = await ArenaTestFactory.From(cfg).With(rpc).CreateAndStartAsync(rnd, round); var minAliceDeadline = DateTimeOffset.UtcNow + (cfg.ConnectionConfirmationTimeout * 0.9); var arenaClient = WabiSabiTestFactory.CreateArenaClient(arena); - var ownershipProof = WabiSabiTestFactory.CreateOwnershipProof(key, round.Id, ScriptPubKeyType.TaprootBIP86); + var ownershipProof = WabiSabiTestFactory.CreateOwnershipProof(rnd, key, round.Id, ScriptPubKeyType.TaprootBIP86); var (resp, _) = await arenaClient.RegisterInputAsync(round.Id, coin.Outpoint, ownershipProof, CancellationToken.None); AssertSingleAliceSuccessfullyRegistered(round, minAliceDeadline, resp); diff --git a/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/PostRequests/RegisterInputToBlameRoundTests.cs b/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/PostRequests/RegisterInputToBlameRoundTests.cs index 2c620c0bfd..f32ad2ad76 100644 --- a/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/PostRequests/RegisterInputToBlameRoundTests.cs +++ b/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/PostRequests/RegisterInputToBlameRoundTests.cs @@ -2,6 +2,7 @@ using System.Threading; using System.Threading.Tasks; using WalletWasabi.Tests.Helpers; +using WalletWasabi.Tests.TestCommon; using WalletWasabi.WabiSabi.Backend; using WalletWasabi.WabiSabi.Backend.DoSPrevention; using WalletWasabi.WabiSabi.Backend.Models; @@ -15,17 +16,18 @@ public class RegisterInputToBlameRoundTests [Fact] public async Task InputNotWhitelistedAsync() { + var rnd = TestRandom.Get(); WabiSabiConfig cfg = WabiSabiTestFactory.CreateDefaultWabiSabiConfig(); using Key key = new(); var coin = WabiSabiTestFactory.CreateCoin(key); - var mockRpc = WabiSabiTestFactory.CreatePreconfiguredRpcClient(coin); + var mockRpc = WabiSabiTestFactory.CreatePreconfiguredRpcClient(rnd, coin); var round = WabiSabiTestFactory.CreateRound(cfg); - round.Alices.Add(WabiSabiTestFactory.CreateAlice(round)); + round.Alices.Add(WabiSabiTestFactory.CreateAlice(rnd, round)); Round blameRound = WabiSabiTestFactory.CreateBlameRound(round, cfg); - using Arena arena = await ArenaTestFactory.From(cfg).With(mockRpc).CreateAndStartAsync(round, blameRound); + using Arena arena = await ArenaTestFactory.From(cfg).With(mockRpc).CreateAndStartAsync(rnd, round, blameRound); - var req = WabiSabiTestFactory.CreateInputRegistrationRequest(round: blameRound, key, coin.Outpoint); + var req = WabiSabiTestFactory.CreateInputRegistrationRequest(rnd, round: blameRound, key, coin.Outpoint); var ex = await Assert.ThrowsAsync(async () => await arena.RegisterInputAsync(req, CancellationToken.None)); Assert.Equal(WabiSabiProtocolErrorCode.InputNotWhitelisted, ex.ErrorCode); @@ -35,14 +37,15 @@ public async Task InputNotWhitelistedAsync() [Fact] public async Task InputWhitelistedAsync() { + var rnd = TestRandom.Get(); WabiSabiConfig cfg = WabiSabiTestFactory.CreateDefaultWabiSabiConfig(); var round = WabiSabiTestFactory.CreateRound(cfg); - var alice = WabiSabiTestFactory.CreateAlice(round); + var alice = WabiSabiTestFactory.CreateAlice(rnd, round); round.Alices.Add(alice); Round blameRound = WabiSabiTestFactory.CreateBlameRound(round, cfg); - using Arena arena = await ArenaTestFactory.From(cfg).CreateAndStartAsync(round, blameRound); + using Arena arena = await ArenaTestFactory.From(cfg).CreateAndStartAsync(rnd, round, blameRound); - var req = WabiSabiTestFactory.CreateInputRegistrationRequest(prevout: alice.Coin.Outpoint, round: blameRound); + var req = WabiSabiTestFactory.CreateInputRegistrationRequest(rnd, prevout: alice.Coin.Outpoint, round: blameRound); var ex = await Assert.ThrowsAnyAsync(async () => await arena.RegisterInputAsync(req, CancellationToken.None)); if (ex is WabiSabiProtocolException wspex) @@ -56,23 +59,24 @@ public async Task InputWhitelistedAsync() [Fact] public async Task InputWhitelistedButBannedAsync() { + var rnd = TestRandom.Get(); WabiSabiConfig cfg = WabiSabiTestFactory.CreateWabiSabiConfig(); var round = WabiSabiTestFactory.CreateRound(cfg); using Key key = new(); - var alice = WabiSabiTestFactory.CreateAlice(key, round); + var alice = WabiSabiTestFactory.CreateAlice(rnd, key, round); var bannedCoin = alice.Coin.Outpoint; round.Alices.Add(alice); Round blameRound = WabiSabiTestFactory.CreateBlameRound(round, cfg); - var mockRpc = WabiSabiTestFactory.CreatePreconfiguredRpcClient(alice.Coin); + var mockRpc = WabiSabiTestFactory.CreatePreconfiguredRpcClient(rnd, alice.Coin); Prison prison = WabiSabiTestFactory.CreatePrison(); - using Arena arena = await ArenaTestFactory.From(cfg, mockRpc, prison).CreateAndStartAsync(round, blameRound); + using Arena arena = await ArenaTestFactory.From(cfg, mockRpc, prison).CreateAndStartAsync(rnd, round, blameRound); prison.FailedToConfirm(bannedCoin, alice.Coin.Amount, round.Id); - var req = WabiSabiTestFactory.CreateInputRegistrationRequest(key: key, round: blameRound, prevout: bannedCoin); + var req = WabiSabiTestFactory.CreateInputRegistrationRequest(rnd, key: key, round: blameRound, prevout: bannedCoin); var ex = await Assert.ThrowsAsync(async () => await arena.RegisterInputAsync(req, CancellationToken.None)); Assert.Equal(WabiSabiProtocolErrorCode.InputBanned, ex.ErrorCode); diff --git a/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/PostRequests/RegisterOutputTests.cs b/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/PostRequests/RegisterOutputTests.cs index 43d1e32f25..343ab13388 100644 --- a/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/PostRequests/RegisterOutputTests.cs +++ b/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/PostRequests/RegisterOutputTests.cs @@ -4,6 +4,7 @@ using WalletWasabi.Extensions; using WalletWasabi.Helpers; using WalletWasabi.Tests.Helpers; +using WalletWasabi.Tests.TestCommon; using WalletWasabi.WabiSabi.Backend; using WalletWasabi.WabiSabi.Backend.Models; using WalletWasabi.WabiSabi.Backend.Rounds; @@ -16,13 +17,14 @@ public class RegisterOutputTests [Fact] public async Task SuccessAsync() { + var rnd = TestRandom.Get(); WabiSabiConfig cfg = WabiSabiTestFactory.CreateDefaultWabiSabiConfig(); var round = WabiSabiTestFactory.CreateRound(cfg); round.SetPhase(Phase.OutputRegistration); - round.Alices.Add(WabiSabiTestFactory.CreateAlice(round)); - using Arena arena = await ArenaTestFactory.From(cfg).CreateAndStartAsync(round); + round.Alices.Add(WabiSabiTestFactory.CreateAlice(rnd, round)); + using Arena arena = await ArenaTestFactory.From(cfg).CreateAndStartAsync(rnd, round); - var req = WabiSabiTestFactory.CreateOutputRegistrationRequest(round); + var req = WabiSabiTestFactory.CreateOutputRegistrationRequest(rnd, round); await arena.RegisterOutputAsync(req, CancellationToken.None); Assert.NotEmpty(round.Bobs); @@ -32,26 +34,27 @@ public async Task SuccessAsync() [Fact] public async Task LegacyOutputsSuccessAsync() { + var rnd = TestRandom.Get(); WabiSabiConfig cfg = WabiSabiTestFactory.CreateDefaultWabiSabiConfig(); cfg.AllowP2pkhOutputs = true; cfg.AllowP2shOutputs = true; var round = WabiSabiTestFactory.CreateRound(cfg); round.SetPhase(Phase.OutputRegistration); - round.Alices.Add(WabiSabiTestFactory.CreateAlice(round)); - using Arena arena = await ArenaTestFactory.From(cfg).CreateAndStartAsync(round); + round.Alices.Add(WabiSabiTestFactory.CreateAlice(rnd, round)); + using Arena arena = await ArenaTestFactory.From(cfg).CreateAndStartAsync(rnd, round); // p2pkh using Key privKey0 = new(); var pkhScript = privKey0.PubKey.GetScriptPubKey(ScriptPubKeyType.Legacy); - var req0 = WabiSabiTestFactory.CreateOutputRegistrationRequest(round, pkhScript, pkhScript.EstimateOutputVsize()); + var req0 = WabiSabiTestFactory.CreateOutputRegistrationRequest(rnd, round, pkhScript, pkhScript.EstimateOutputVsize()); await arena.RegisterOutputAsync(req0, CancellationToken.None); Assert.Single(round.Bobs); // p2sh using Key privKey1 = new(); var shScript = privKey1.PubKey.ScriptPubKey.Hash.ScriptPubKey; - var req1 = WabiSabiTestFactory.CreateOutputRegistrationRequest(round, shScript, shScript.EstimateOutputVsize()); + var req1 = WabiSabiTestFactory.CreateOutputRegistrationRequest(rnd, round, shScript, shScript.EstimateOutputVsize()); await arena.RegisterOutputAsync(req1, CancellationToken.None); Assert.Equal(2, round.Bobs.Count); await arena.StopAsync(CancellationToken.None); @@ -60,16 +63,17 @@ public async Task LegacyOutputsSuccessAsync() [Fact] public async Task TaprootSuccessAsync() { + var rnd = TestRandom.Get(); WabiSabiConfig cfg = WabiSabiTestFactory.CreateDefaultWabiSabiConfig(); cfg.AllowP2trOutputs = true; var round = WabiSabiTestFactory.CreateRound(cfg); round.SetPhase(Phase.OutputRegistration); - round.Alices.Add(WabiSabiTestFactory.CreateAlice(round)); - using Arena arena = await ArenaTestFactory.From(cfg).CreateAndStartAsync(round); + round.Alices.Add(WabiSabiTestFactory.CreateAlice(rnd, round)); + using Arena arena = await ArenaTestFactory.From(cfg).CreateAndStartAsync(rnd, round); using Key privKey = new(); - var req = WabiSabiTestFactory.CreateOutputRegistrationRequest(round, privKey.PubKey.GetScriptPubKey(ScriptPubKeyType.TaprootBIP86), Constants.P2trOutputVirtualSize); + var req = WabiSabiTestFactory.CreateOutputRegistrationRequest(rnd, round, privKey.PubKey.GetScriptPubKey(ScriptPubKeyType.TaprootBIP86), Constants.P2trOutputVirtualSize); await arena.RegisterOutputAsync(req, CancellationToken.None); Assert.NotEmpty(round.Bobs); @@ -79,16 +83,17 @@ public async Task TaprootSuccessAsync() [Fact] public async Task TaprootNotAllowedAsync() { + var rnd = TestRandom.Get(); WabiSabiConfig cfg = WabiSabiTestFactory.CreateDefaultWabiSabiConfig(); cfg.AllowP2trOutputs = false; var round = WabiSabiTestFactory.CreateRound(cfg); round.SetPhase(Phase.OutputRegistration); - round.Alices.Add(WabiSabiTestFactory.CreateAlice(round)); - using Arena arena = await ArenaTestFactory.From(cfg).CreateAndStartAsync(round); + round.Alices.Add(WabiSabiTestFactory.CreateAlice(rnd, round)); + using Arena arena = await ArenaTestFactory.From(cfg).CreateAndStartAsync(rnd, round); using Key privKey = new(); - var req = WabiSabiTestFactory.CreateOutputRegistrationRequest(round, privKey.PubKey.GetScriptPubKey(ScriptPubKeyType.TaprootBIP86), Constants.P2trOutputVirtualSize); + var req = WabiSabiTestFactory.CreateOutputRegistrationRequest(rnd, round, privKey.PubKey.GetScriptPubKey(ScriptPubKeyType.TaprootBIP86), Constants.P2trOutputVirtualSize); var ex = await Assert.ThrowsAsync(async () => await arena.RegisterOutputAsync(req, CancellationToken.None)); Assert.Equal(WabiSabiProtocolErrorCode.ScriptNotAllowed, ex.ErrorCode); @@ -98,10 +103,11 @@ public async Task TaprootNotAllowedAsync() [Fact] public async Task RoundNotFoundAsync() { + var rnd = TestRandom.Get(); var cfg = WabiSabiTestFactory.CreateDefaultWabiSabiConfig(); var nonExistingRound = WabiSabiTestFactory.CreateRound(cfg); - using Arena arena = await ArenaTestFactory.Default.CreateAndStartAsync(); - var req = WabiSabiTestFactory.CreateOutputRegistrationRequest(nonExistingRound); + using Arena arena = await ArenaTestFactory.Default.CreateAndStartAsync(rnd); + var req = WabiSabiTestFactory.CreateOutputRegistrationRequest(rnd, nonExistingRound); var ex = await Assert.ThrowsAsync(async () => await arena.RegisterOutputAsync(req, CancellationToken.None)); Assert.Equal(WabiSabiProtocolErrorCode.RoundNotFound, ex.ErrorCode); @@ -111,6 +117,7 @@ public async Task RoundNotFoundAsync() [Fact] public async Task ScriptNotAllowedAsync() { + var rnd = TestRandom.Get(); using Key key = new(); var outputScript = key.PubKey.GetAddress(ScriptPubKeyType.Legacy, Network.Main).ScriptPubKey; @@ -120,12 +127,12 @@ public async Task ScriptNotAllowedAsync() { MaxVsizeAllocationPerAlice = Constants.P2wpkhInputVirtualSize + outputScript.EstimateOutputVsize() }; var round = WabiSabiTestFactory.CreateRound(parameters); - using Arena arena = await ArenaTestFactory.From(cfg).CreateAndStartAsync(round); + using Arena arena = await ArenaTestFactory.From(cfg).CreateAndStartAsync(rnd, round); round.SetPhase(Phase.OutputRegistration); - round.Alices.Add(WabiSabiTestFactory.CreateAlice(Money.Coins(1), round)); + round.Alices.Add(WabiSabiTestFactory.CreateAlice(rnd, Money.Coins(1), round)); - var req = WabiSabiTestFactory.CreateOutputRegistrationRequest(round, outputScript); + var req = WabiSabiTestFactory.CreateOutputRegistrationRequest(rnd, round, outputScript); var ex = await Assert.ThrowsAsync(async () => await arena.RegisterOutputAsync(req, CancellationToken.None)); Assert.Equal(WabiSabiProtocolErrorCode.ScriptNotAllowed, ex.ErrorCode); @@ -135,18 +142,19 @@ public async Task ScriptNotAllowedAsync() [Fact] public async Task NonStandardOutputAsync() { + var rnd = TestRandom.Get(); var sha256Bounty = Script.FromHex("aa20000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f87"); WabiSabiConfig cfg = WabiSabiTestFactory.CreateDefaultWabiSabiConfig(); RoundParameters parameters = WabiSabiTestFactory.CreateRoundParameters(cfg) with { MaxVsizeAllocationPerAlice = Constants.P2wpkhInputVirtualSize + sha256Bounty.EstimateOutputVsize() }; var round = WabiSabiTestFactory.CreateRound(parameters); - using Arena arena = await ArenaTestFactory.From(cfg).CreateAndStartAsync(round); + using Arena arena = await ArenaTestFactory.From(cfg).CreateAndStartAsync(rnd, round); round.SetPhase(Phase.OutputRegistration); - round.Alices.Add(WabiSabiTestFactory.CreateAlice(Money.Coins(1), round)); + round.Alices.Add(WabiSabiTestFactory.CreateAlice(rnd, Money.Coins(1), round)); - var req = WabiSabiTestFactory.CreateOutputRegistrationRequest(round, sha256Bounty); + var req = WabiSabiTestFactory.CreateOutputRegistrationRequest(rnd, round, sha256Bounty); var ex = await Assert.ThrowsAsync(async () => await arena.RegisterOutputAsync(req, CancellationToken.None)); // The following assertion requires standardness to be checked before allowed script types @@ -158,15 +166,16 @@ public async Task NonStandardOutputAsync() [Fact] public async Task NotEnoughFundsAsync() { + var rnd = TestRandom.Get(); WabiSabiConfig cfg = WabiSabiTestFactory.CreateDefaultWabiSabiConfig(); cfg.MinRegistrableAmount = Money.Coins(2); var round = WabiSabiTestFactory.CreateRound(cfg); round.SetPhase(Phase.OutputRegistration); - round.Alices.Add(WabiSabiTestFactory.CreateAlice(Money.Coins(1), round)); - using Arena arena = await ArenaTestFactory.From(cfg).CreateAndStartAsync(round); + round.Alices.Add(WabiSabiTestFactory.CreateAlice(rnd, Money.Coins(1), round)); + using Arena arena = await ArenaTestFactory.From(cfg).CreateAndStartAsync(rnd, round); - var req = WabiSabiTestFactory.CreateOutputRegistrationRequest(round); + var req = WabiSabiTestFactory.CreateOutputRegistrationRequest(rnd, round); var ex = await Assert.ThrowsAsync(async () => await arena.RegisterOutputAsync(req, CancellationToken.None)); Assert.Equal(WabiSabiProtocolErrorCode.NotEnoughFunds, ex.ErrorCode); @@ -177,15 +186,16 @@ public async Task NotEnoughFundsAsync() [Fact] public async Task TooMuchFundsAsync() { + var rnd = TestRandom.Get(); WabiSabiConfig cfg = WabiSabiTestFactory.CreateDefaultWabiSabiConfig(); cfg.MaxRegistrableAmount = Money.Coins(1.993m); // TODO migrate to MultipartyTransactionParameters var round = WabiSabiTestFactory.CreateRound(cfg); round.SetPhase(Phase.OutputRegistration); - round.Alices.Add(WabiSabiTestFactory.CreateAlice(Money.Coins(2), round)); - using Arena arena = await ArenaTestFactory.From(cfg).CreateAndStartAsync(round); + round.Alices.Add(WabiSabiTestFactory.CreateAlice(rnd, Money.Coins(2), round)); + using Arena arena = await ArenaTestFactory.From(cfg).CreateAndStartAsync(rnd, round); - var req = WabiSabiTestFactory.CreateOutputRegistrationRequest(round); + var req = WabiSabiTestFactory.CreateOutputRegistrationRequest(rnd, round); var ex = await Assert.ThrowsAsync(async () => await arena.RegisterOutputAsync(req, CancellationToken.None)); Assert.Equal(WabiSabiProtocolErrorCode.TooMuchFunds, ex.ErrorCode); @@ -196,13 +206,14 @@ public async Task TooMuchFundsAsync() [Fact] public async Task IncorrectRequestedVsizeCredentialsAsync() { + var rnd = TestRandom.Get(); WabiSabiConfig cfg = WabiSabiTestFactory.CreateDefaultWabiSabiConfig(); var round = WabiSabiTestFactory.CreateRound(cfg); round.SetPhase(Phase.OutputRegistration); - round.Alices.Add(WabiSabiTestFactory.CreateAlice(round)); - using Arena arena = await ArenaTestFactory.From(cfg).CreateAndStartAsync(round); + round.Alices.Add(WabiSabiTestFactory.CreateAlice(rnd, round)); + using Arena arena = await ArenaTestFactory.From(cfg).CreateAndStartAsync(rnd, round); - var req = WabiSabiTestFactory.CreateOutputRegistrationRequest(round, vsize: 30); + var req = WabiSabiTestFactory.CreateOutputRegistrationRequest(rnd, round, vsize: 30); var ex = await Assert.ThrowsAsync(async () => await arena.RegisterOutputAsync(req, CancellationToken.None)); Assert.Equal(WabiSabiProtocolErrorCode.IncorrectRequestedVsizeCredentials, ex.ErrorCode); @@ -213,21 +224,22 @@ public async Task IncorrectRequestedVsizeCredentialsAsync() [Fact] public async Task WrongPhaseAsync() { + var rnd = TestRandom.Get(); WabiSabiConfig cfg = WabiSabiTestFactory.CreateDefaultWabiSabiConfig(); Round round = WabiSabiTestFactory.CreateRound(cfg); - using Arena arena = await ArenaTestFactory.From(cfg).CreateAndStartAsync(round); + using Arena arena = await ArenaTestFactory.From(cfg).CreateAndStartAsync(rnd, round); await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); // Refresh the Arena States because of vsize manipulation. await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); - round.Alices.Add(WabiSabiTestFactory.CreateAlice(round)); + round.Alices.Add(WabiSabiTestFactory.CreateAlice(rnd, round)); foreach (Phase phase in Enum.GetValues(typeof(Phase))) { if (phase != Phase.OutputRegistration) { - var req = WabiSabiTestFactory.CreateOutputRegistrationRequest(round); + var req = WabiSabiTestFactory.CreateOutputRegistrationRequest(rnd, round); round.SetPhase(phase); var ex = await Assert.ThrowsAsync(async () => await arena.RegisterOutputAsync(req, CancellationToken.None)); Assert.Equal(WabiSabiProtocolErrorCode.WrongPhase, ex.ErrorCode); diff --git a/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/PostRequests/RemoveInputTests.cs b/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/PostRequests/RemoveInputTests.cs index b93cf8305b..3cc55fef19 100644 --- a/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/PostRequests/RemoveInputTests.cs +++ b/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/PostRequests/RemoveInputTests.cs @@ -1,8 +1,9 @@ +using NBitcoin; using System.Linq; using System.Threading; using System.Threading.Tasks; -using NBitcoin; using WalletWasabi.Tests.Helpers; +using WalletWasabi.Tests.TestCommon; using WalletWasabi.WabiSabi.Backend; using WalletWasabi.WabiSabi.Backend.Models; using WalletWasabi.WabiSabi.Backend.Rounds; @@ -16,14 +17,15 @@ public class RemoveInputTests [Fact] public async Task SuccessAsync() { + var rnd = TestRandom.Get(); WabiSabiConfig cfg = WabiSabiTestFactory.CreateDefaultWabiSabiConfig(); var round = WabiSabiTestFactory.CreateRound(cfg); var initialRemaining = round.RemainingInputVsizeAllocation; - var alice = WabiSabiTestFactory.CreateAlice(round); + var alice = WabiSabiTestFactory.CreateAlice(rnd, round); round.Alices.Add(alice); Assert.True(round.RemainingInputVsizeAllocation < initialRemaining); - using Arena arena = await ArenaTestFactory.From(cfg).CreateAndStartAsync(round); + using Arena arena = await ArenaTestFactory.From(cfg).CreateAndStartAsync(rnd, round); // There's no such alice yet, so success. var req = new InputsRemovalRequest(round.Id, Guid.NewGuid()); @@ -43,7 +45,7 @@ public async Task SuccessAsync() [Fact] public async Task RoundNotFoundAsync() { - using Arena arena = await ArenaTestFactory.Default.CreateAndStartAsync(); + using Arena arena = await ArenaTestFactory.Default.CreateAndStartAsync(TestRandom.Get()); var req = new InputsRemovalRequest(uint256.Zero, Guid.NewGuid()); var ex = await Assert.ThrowsAsync(async () => await arena.RemoveInputAsync(req, CancellationToken.None)); @@ -56,7 +58,7 @@ public async Task RoundNotFoundAsync() public async Task WrongPhaseAsync() { WabiSabiConfig cfg = WabiSabiTestFactory.CreateDefaultWabiSabiConfig(); - using Arena arena = await ArenaTestFactory.From(cfg).CreateAndStartAsync(); + using Arena arena = await ArenaTestFactory.From(cfg).CreateAndStartAsync(TestRandom.Get()); await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); var round = arena.Rounds.First(); diff --git a/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/PostRequests/SignTransactionTests.cs b/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/PostRequests/SignTransactionTests.cs index d19a53c38b..c5c80db80e 100644 --- a/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/PostRequests/SignTransactionTests.cs +++ b/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/PostRequests/SignTransactionTests.cs @@ -4,6 +4,7 @@ using System.Threading.Tasks; using WalletWasabi.Extensions; using WalletWasabi.Tests.Helpers; +using WalletWasabi.Tests.TestCommon; using WalletWasabi.WabiSabi.Backend; using WalletWasabi.WabiSabi.Backend.Models; using WalletWasabi.WabiSabi.Backend.Rounds; @@ -18,14 +19,15 @@ public class SignTransactionTests [Fact] public async Task SuccessAsync() { + var rnd = TestRandom.Get(); WabiSabiConfig cfg = WabiSabiTestFactory.CreateDefaultWabiSabiConfig(); var round = WabiSabiTestFactory.CreateRound(cfg); using Key key = new(); - Alice alice = WabiSabiTestFactory.CreateAlice(key: key, round: round); + Alice alice = WabiSabiTestFactory.CreateAlice(rnd, key: key, round: round); round.Alices.Add(alice); round.CoinjoinState = round.AddInput(alice.Coin, alice.OwnershipProof, WabiSabiTestFactory.CreateCommitmentData(round.Id)).Finalize(); round.SetPhase(Phase.TransactionSigning); - using Arena arena = await ArenaTestFactory.From(cfg).CreateAndStartAsync(round); + using Arena arena = await ArenaTestFactory.From(cfg).CreateAndStartAsync(rnd, round); var aliceSignedCoinJoin = round.Assert().CreateUnsignedTransaction(); aliceSignedCoinJoin.Sign(key.GetBitcoinSecret(Network.Main), alice.Coin); @@ -39,17 +41,18 @@ public async Task SuccessAsync() [Fact] public async Task TaprootSuccessAsync() { + var rnd = TestRandom.Get(); WabiSabiConfig cfg = WabiSabiTestFactory.CreateDefaultWabiSabiConfig(); cfg.AllowP2trInputs = true; cfg.AllowP2trOutputs = true; var round = WabiSabiTestFactory.CreateRound(cfg); using Key key = new(); - Alice alice = WabiSabiTestFactory.CreateAlice(key: key, round: round, scriptPubKeyType: ScriptPubKeyType.TaprootBIP86); + Alice alice = WabiSabiTestFactory.CreateAlice(rnd, key: key, round: round, scriptPubKeyType: ScriptPubKeyType.TaprootBIP86); round.Alices.Add(alice); round.CoinjoinState = round.AddInput(alice.Coin, alice.OwnershipProof, WabiSabiTestFactory.CreateCommitmentData(round.Id)).Finalize(); round.SetPhase(Phase.TransactionSigning); - using Arena arena = await ArenaTestFactory.From(cfg).CreateAndStartAsync(round); + using Arena arena = await ArenaTestFactory.From(cfg).CreateAndStartAsync(rnd, round); var aliceSignedCoinJoin = round.Assert().CreateUnsignedTransaction(); aliceSignedCoinJoin.Sign(key.GetBitcoinSecret(Network.Main), alice.Coin); @@ -63,7 +66,7 @@ public async Task TaprootSuccessAsync() [Fact] public async Task RoundNotFoundAsync() { - using Arena arena = await ArenaTestFactory.Default.CreateAndStartAsync(); + using Arena arena = await ArenaTestFactory.Default.CreateAndStartAsync(TestRandom.Get()); var req = new TransactionSignaturesRequest(uint256.Zero, 0, WitScript.Empty); var ex = await Assert.ThrowsAsync(async () => await arena.SignTransactionAsync(req, CancellationToken.None)); Assert.Equal(WabiSabiProtocolErrorCode.RoundNotFound, ex.ErrorCode); @@ -74,7 +77,7 @@ public async Task RoundNotFoundAsync() public async Task WrongPhaseAsync() { WabiSabiConfig cfg = WabiSabiTestFactory.CreateDefaultWabiSabiConfig(); - using Arena arena = await ArenaTestFactory.From(cfg).CreateAndStartAsync(); + using Arena arena = await ArenaTestFactory.From(cfg).CreateAndStartAsync(TestRandom.Get()); await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); var round = arena.Rounds.First(); diff --git a/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/PrisonTests.cs b/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/PrisonTests.cs index 6a7679bdb5..bb1dc715d8 100644 --- a/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/PrisonTests.cs +++ b/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/PrisonTests.cs @@ -1,11 +1,12 @@ -using System.Linq; using NBitcoin; +using System.Linq; using System.Threading; using System.Threading.Tasks; using WalletWasabi.Tests.Helpers; +using WalletWasabi.Tests.TestCommon; using WalletWasabi.WabiSabi.Backend.DoSPrevention; -using Xunit; using WalletWasabi.WabiSabi.Models; +using Xunit; namespace WalletWasabi.Tests.UnitTests.WabiSabi.Backend; @@ -19,10 +20,11 @@ public async Task OffensesAreSavedAsync() { using CancellationTokenSource ctsTimeout = new(TimeSpan.FromMinutes(1)); + var rnd = TestRandom.Get(); var (prison, reader) = WabiSabiTestFactory.CreateObservablePrison(); - var outpoint = BitcoinFactory.CreateOutPoint(); - var roundId = BitcoinFactory.CreateUint256(); + var outpoint = BitcoinFactory.CreateOutPoint(rnd); + var roundId = BitcoinFactory.CreateUint256(rnd); // Fail to verify prison.FailedVerification(outpoint, roundId, TimeSpan.Zero, ""); @@ -77,8 +79,8 @@ public async Task OffensesAreSavedAsync() // Inherited var ancestors = new[] { - BitcoinFactory.CreateOutPoint(), - BitcoinFactory.CreateOutPoint() + BitcoinFactory.CreateOutPoint(rnd), + BitcoinFactory.CreateOutPoint(rnd) }; prison.InheritPunishment(outpoint, ancestors, []); @@ -94,13 +96,14 @@ public async Task OffensesAreSavedAsync() [Fact] public void BanningTime() { + var rnd = TestRandom.Get(); var (prison, _) = WabiSabiTestFactory.CreateObservablePrison(); var cfg = WabiSabiTestFactory.CreateWabiSabiConfig().GetDoSConfiguration() with { SimplifiedPunishment = TimeSpan.Zero }; - var roundId = BitcoinFactory.CreateUint256(); + var roundId = BitcoinFactory.CreateUint256(rnd); // Failed to verify punishment is constant (not affected by number of attempts) - var ftvOutpoint = BitcoinFactory.CreateOutPoint(); + var ftvOutpoint = BitcoinFactory.CreateOutPoint(rnd); prison.FailedVerification(ftvOutpoint, roundId, TimeSpan.Zero, ""); prison.FailedVerification(ftvOutpoint, roundId, TimeSpan.Zero, ""); prison.FailedVerification(ftvOutpoint, roundId, TimeSpan.Zero, ""); @@ -108,7 +111,7 @@ public void BanningTime() Assert.Equal(cfg.MinTimeForFailedToVerify, banningPeriod.Duration); // Cheating punishment is constant (not affected by number of attempts) - var chtOutpoint = BitcoinFactory.CreateOutPoint(); + var chtOutpoint = BitcoinFactory.CreateOutPoint(rnd); prison.CheatingDetected(chtOutpoint, roundId); prison.CheatingDetected(chtOutpoint, roundId); prison.CheatingDetected(chtOutpoint, roundId); @@ -116,8 +119,8 @@ public void BanningTime() Assert.Equal(cfg.MinTimeForCheating, banningPeriod.Duration); // Failed to confirm is calculated and inversely proportional to the amount - var ftcOutpoint1 = BitcoinFactory.CreateOutPoint(); - var ftcOutpoint2 = BitcoinFactory.CreateOutPoint(); + var ftcOutpoint1 = BitcoinFactory.CreateOutPoint(rnd); + var ftcOutpoint2 = BitcoinFactory.CreateOutPoint(rnd); prison.FailedToConfirm(ftcOutpoint1, Money.Coins(0.5m), roundId); prison.FailedToConfirm(ftcOutpoint2, Money.Coins(1.0m), roundId); @@ -132,7 +135,7 @@ public void BanningTime() // .... the worst offense is applied // Note: this case is compared against ftcOutpoint1 which failed to confirm twice - var ftcOutpoint3 = BitcoinFactory.CreateOutPoint(); + var ftcOutpoint3 = BitcoinFactory.CreateOutPoint(rnd); prison.FailedToConfirm(ftcOutpoint3, Money.Coins(0.5m), roundId); prison.FailedToSign(ftcOutpoint3, Money.Coins(0.5m), roundId); @@ -140,7 +143,7 @@ public void BanningTime() Assert.True(banningPeriod3FailedToConfirmAndSign.Duration > banningPeriod1FailedToConfirmTwice.Duration); // Big amounts are not banned the first time - var ftcOutpointBig = BitcoinFactory.CreateOutPoint(); + var ftcOutpointBig = BitcoinFactory.CreateOutPoint(rnd); prison.FailedToConfirm(ftcOutpointBig, Money.Coins(2m), roundId); var banningPeriodBigCoin = prison.GetBan(ftcOutpointBig, cfg).BanningTime; Assert.Equal(TimeSpan.Zero, banningPeriodBigCoin.Duration); @@ -161,9 +164,9 @@ public void BanningTime() Assert.NotEqual(TimeSpan.Zero, banningPeriodBigPaidCoin.Duration); // it IS banned second time // coins inherit the punishments from their ancestors - var ftcFailedToSign = BitcoinFactory.CreateOutPoint(); - var ftcFailedToConfirm = BitcoinFactory.CreateOutPoint(); - var ftcInheritFromFailedToSign = BitcoinFactory.CreateOutPoint(); + var ftcFailedToSign = BitcoinFactory.CreateOutPoint(rnd); + var ftcFailedToConfirm = BitcoinFactory.CreateOutPoint(rnd); + var ftcInheritFromFailedToSign = BitcoinFactory.CreateOutPoint(rnd); prison.FailedToSign(ftcFailedToSign, Money.Coins(0.005m), roundId); prison.FailedToConfirm(ftcFailedToConfirm, Money.Coins(0.005m), roundId); prison.InheritPunishment(ftcInheritFromFailedToSign, [ftcFailedToSign, ftcFailedToConfirm], [InputBannedReasonEnum.RoundDisruptionMethodDidNotSign, InputBannedReasonEnum.RoundDisruptionMethodDidNotConfirm]); @@ -181,7 +184,7 @@ public void BanningTime() 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(); + var outpointsToBan = Enumerable.Range(0, 10).Select(_ => BitcoinFactory.CreateOutPoint(rnd)).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 => diff --git a/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/RoundCreationTests.cs b/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/RoundCreationTests.cs index 1b7a4e66f7..2bb85a7231 100644 --- a/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/RoundCreationTests.cs +++ b/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/RoundCreationTests.cs @@ -1,8 +1,10 @@ +using GingerCommon.Crypto.Random; using System.Linq; using System.Threading; using System.Threading.Tasks; using WalletWasabi.BitcoinCore.Rpc; using WalletWasabi.Tests.Helpers; +using WalletWasabi.Tests.TestCommon; using WalletWasabi.WabiSabi.Backend; using WalletWasabi.WabiSabi.Backend.Rounds; using Xunit; @@ -11,11 +13,11 @@ namespace WalletWasabi.Tests.UnitTests.WabiSabi.Backend; public class RoundCreationTests { - private static Arena CreateArena(WabiSabiConfig cfg, IRPCClient rpc) + private static Arena CreateArena(GingerRandom rnd, WabiSabiConfig cfg, IRPCClient rpc) { var arenaBuilder = ArenaTestFactory.From(cfg).With(rpc); arenaBuilder.Period = TimeSpan.FromSeconds(1); - return arenaBuilder.Create(); + return arenaBuilder.Create(rnd); } [Fact] @@ -24,7 +26,7 @@ public async Task InitializesRoundAsync() WabiSabiConfig cfg = WabiSabiTestFactory.CreateDefaultWabiSabiConfig(); var mockRpc = BitcoinFactory.GetMockMinimalRpc(); - using Arena arena = CreateArena(cfg, mockRpc); + using Arena arena = CreateArena(TestRandom.Get(), cfg, mockRpc); Assert.Empty(arena.Rounds); await arena.StartAsync(CancellationToken.None); await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); @@ -39,7 +41,7 @@ public async Task CreatesRoundIfNoneInputRegistrationAsync() WabiSabiConfig cfg = WabiSabiTestFactory.CreateDefaultWabiSabiConfig(); var mockRpc = BitcoinFactory.GetMockMinimalRpc(); - using Arena arena = CreateArena(cfg, mockRpc); + using Arena arena = CreateArena(TestRandom.Get(), cfg, mockRpc); Assert.Empty(arena.Rounds); await arena.StartAsync(CancellationToken.None); await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); @@ -55,17 +57,18 @@ public async Task CreatesRoundIfNoneInputRegistrationAsync() [Fact] public async Task CreatesRoundIfInBlameInputRegistrationAsync() { + var rnd = TestRandom.Get(); WabiSabiConfig cfg = WabiSabiTestFactory.CreateDefaultWabiSabiConfig(); var mockRpc = BitcoinFactory.GetMockMinimalRpc(); - using Arena arena = CreateArena(cfg, mockRpc); + using Arena arena = CreateArena(rnd, cfg, mockRpc); Assert.Empty(arena.Rounds); await arena.StartAsync(CancellationToken.None); await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); var round = Assert.Single(arena.Rounds); round.SetPhase(Phase.ConnectionConfirmation); - round.Alices.Add(WabiSabiTestFactory.CreateAlice(round)); + round.Alices.Add(WabiSabiTestFactory.CreateAlice(rnd, round)); Round blameRound = WabiSabiTestFactory.CreateBlameRound(round, cfg); Assert.Equal(Phase.InputRegistration, blameRound.Phase); arena.Rounds.Add(blameRound); diff --git a/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/Rounds/MultipartyTransactionStateTests.cs b/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/Rounds/MultipartyTransactionStateTests.cs index 8b0efc1933..9af5acc99d 100644 --- a/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/Rounds/MultipartyTransactionStateTests.cs +++ b/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/Rounds/MultipartyTransactionStateTests.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using WalletWasabi.Crypto.Randomness; using WalletWasabi.Tests.Helpers; +using WalletWasabi.Tests.TestCommon; using WalletWasabi.WabiSabi; using WalletWasabi.WabiSabi.Backend; using WalletWasabi.WabiSabi.Backend.Rounds; @@ -15,14 +16,15 @@ public class MultipartyTransactionStateTests [Fact] public void CanGetDifferentialStateTest() { + var rnd = TestRandom.Get(); var cfg = WabiSabiTestFactory.CreateDefaultWabiSabiConfig(); var round = WabiSabiTestFactory.CreateRound(cfg); var commitmentData = WabiSabiTestFactory.CreateCommitmentData(round.Id); - (var coin1, var ownershipProof1) = WabiSabiTestFactory.CreateCoinWithOwnershipProof(roundId: round.Id); - (var coin2, var ownershipProof2) = WabiSabiTestFactory.CreateCoinWithOwnershipProof(roundId: round.Id); - (var coin3, var ownershipProof3) = WabiSabiTestFactory.CreateCoinWithOwnershipProof(roundId: round.Id); + (var coin1, var ownershipProof1) = WabiSabiTestFactory.CreateCoinWithOwnershipProof(rnd, roundId: round.Id); + (var coin2, var ownershipProof2) = WabiSabiTestFactory.CreateCoinWithOwnershipProof(rnd, roundId: round.Id); + (var coin3, var ownershipProof3) = WabiSabiTestFactory.CreateCoinWithOwnershipProof(rnd, roundId: round.Id); // Three events / three states var state0 = round.Assert(); diff --git a/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/Rounds/Utils/CoinJoinTransactionArchiverTests.cs b/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/Rounds/Utils/CoinJoinTransactionArchiverTests.cs index 8f6ea6ed8f..650c490045 100644 --- a/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/Rounds/Utils/CoinJoinTransactionArchiverTests.cs +++ b/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/Rounds/Utils/CoinJoinTransactionArchiverTests.cs @@ -6,6 +6,7 @@ using WalletWasabi.Blockchain.TransactionBuilding; using WalletWasabi.Blockchain.Transactions; using WalletWasabi.Tests.Helpers; +using WalletWasabi.Tests.TestCommon; using WalletWasabi.WabiSabi.Backend.Rounds.CoinJoinStorage; using Xunit; using static WalletWasabi.WabiSabi.Backend.Rounds.CoinJoinStorage.CoinJoinTransactionArchiver; @@ -24,11 +25,11 @@ public async Task StoreTransactionAsync() CoinJoinTransactionArchiver archiver = new(tempFolder); var coins = new[] -{ + { ("", 0, 1.00118098m, true, 1) }; - TransactionFactory transactionFactory = ServiceFactory.CreateTransactionFactory(coins); + TransactionFactory transactionFactory = ServiceFactory.CreateTransactionFactory(TestRandom.Get(), coins); using Key key = new(); var payment = new PaymentIntent(new[] { diff --git a/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/UtxoPrisonWardenTests.cs b/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/UtxoPrisonWardenTests.cs index 0cb02ec07e..b1b8baa982 100644 --- a/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/UtxoPrisonWardenTests.cs +++ b/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/UtxoPrisonWardenTests.cs @@ -31,51 +31,57 @@ public async Task CanStartAndStopAsync() [Fact] public async Task PrisonSerializationAsync() { + var rnd = TestRandom.Get(); var workDir = TestDirectory.Get(); + var i1 = BitcoinFactory.CreateOutPoint(rnd); + var i2 = BitcoinFactory.CreateOutPoint(rnd); + var i3 = BitcoinFactory.CreateOutPoint(rnd); + var i4 = BitcoinFactory.CreateOutPoint(rnd); + var i5 = BitcoinFactory.CreateOutPoint(rnd); + var i6 = BitcoinFactory.CreateOutPoint(rnd); + // Create prison. - CoordinatorParameters coordinatorParameters = new(workDir); - using var w = new Warden( - coordinatorParameters.PrisonFilePath, - WabiSabiTestFactory.CreateCoinJoinIdStore(), - coordinatorParameters.RuntimeCoordinatorConfig); - await w.StartAsync(CancellationToken.None); - var now = DateTimeOffset.FromUnixTimeSeconds(DateTimeOffset.UtcNow.ToUnixTimeSeconds()); - var i1 = BitcoinFactory.CreateOutPoint(); - var i2 = BitcoinFactory.CreateOutPoint(); - var i3 = BitcoinFactory.CreateOutPoint(); - var i4 = BitcoinFactory.CreateOutPoint(); - var i5 = BitcoinFactory.CreateOutPoint(); - 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]); + { + CoordinatorParameters coordinatorParameters = new(workDir); + using var w = new Warden( + coordinatorParameters.PrisonFilePath, + WabiSabiTestFactory.CreateCoinJoinIdStore(), + coordinatorParameters.RuntimeCoordinatorConfig); + await w.StartAsync(CancellationToken.None); + var now = DateTimeOffset.FromUnixTimeSeconds(DateTimeOffset.UtcNow.ToUnixTimeSeconds()); + 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); + // Wait until serializes. + await w.StopAsync(CancellationToken.None); + } // See if prev UTXOs are loaded. - CoordinatorParameters coordinatorParameters2 = new(workDir); - var coinjoinIdStoreMock = new Mock(); - coinjoinIdStoreMock.Setup(x => x.Contains(It.IsAny())).Returns(true); - using var w2 = new Warden(coordinatorParameters2.PrisonFilePath, coinjoinIdStoreMock.Object, coordinatorParameters2.RuntimeCoordinatorConfig); - await w2.StartAsync(CancellationToken.None); + { + CoordinatorParameters coordinatorParameters2 = new(workDir); + var coinjoinIdStoreMock = new Mock(); + coinjoinIdStoreMock.Setup(x => x.Contains(It.IsAny())).Returns(true); + using var w2 = new Warden(coordinatorParameters2.PrisonFilePath, coinjoinIdStoreMock.Object, coordinatorParameters2.RuntimeCoordinatorConfig); + await w2.StartAsync(CancellationToken.None); - var dosConfig = coordinatorParameters2.RuntimeCoordinatorConfig.GetDoSConfiguration(); - Assert.True(w2.Prison.IsBanned(i1, dosConfig, DateTimeOffset.UtcNow)); - Assert.True(w2.Prison.IsBanned(i2, dosConfig, DateTimeOffset.UtcNow)); - 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)); + var dosConfig = coordinatorParameters2.RuntimeCoordinatorConfig.GetDoSConfiguration(); + Assert.True(w2.Prison.IsBanned(i1, dosConfig, DateTimeOffset.UtcNow)); + Assert.True(w2.Prison.IsBanned(i2, dosConfig, DateTimeOffset.UtcNow)); + 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.LocalCoinVerifier, w2.Prison.GetBan(i1, dosConfig).Reasons.First()); - Assert.Equal([InputBannedReasonEnum.Inherited, InputBannedReasonEnum.RoundDisruptionMethodDidNotSignalReadyToSign], w2.Prison.GetBan(i6, dosConfig).Reasons.OrderBy(x => x)); + Assert.Equal([InputBannedReasonEnum.Inherited, InputBannedReasonEnum.RoundDisruptionMethodDidNotSignalReadyToSign], w2.Prison.GetBan(i6, dosConfig).Reasons.OrderBy(x => x)); - await w2.StopAsync(CancellationToken.None); + await w2.StopAsync(CancellationToken.None); + } } } diff --git a/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/WhitelistTests.cs b/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/WhitelistTests.cs index b878e46435..d4f6e1e2d1 100644 --- a/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/WhitelistTests.cs +++ b/WalletWasabi.Tests/UnitTests/WabiSabi/Backend/WhitelistTests.cs @@ -1,6 +1,7 @@ using System.Linq; using System.Threading.Tasks; using WalletWasabi.Tests.Helpers; +using WalletWasabi.Tests.TestCommon; using WalletWasabi.WabiSabi.Backend.Banning; using Xunit; @@ -11,18 +12,19 @@ public class WhitelistTests [Fact] public async Task WhitelistChangeTrafficAsync() { + var rnd = TestRandom.Get(); var cfg = WabiSabiTestFactory.CreateDefaultWabiSabiConfig(); cfg.ReleaseFromWhitelistAfter = TimeSpan.FromSeconds(1); Whitelist whitelist = new(Enumerable.Empty(), string.Empty, cfg); var currentChangeId = whitelist.ChangeId; - var outpoint = BitcoinFactory.CreateOutPoint(); + var outpoint = BitcoinFactory.CreateOutPoint(rnd); whitelist.Add(outpoint); Assert.NotEqual(currentChangeId, whitelist.ChangeId); currentChangeId = whitelist.ChangeId; - var outpoint2 = BitcoinFactory.CreateOutPoint(); + var outpoint2 = BitcoinFactory.CreateOutPoint(rnd); whitelist.Add(outpoint2); Assert.NotEqual(currentChangeId, whitelist.ChangeId); currentChangeId = whitelist.ChangeId; @@ -30,7 +32,7 @@ public async Task WhitelistChangeTrafficAsync() Assert.True(whitelist.TryRelease(outpoint)); Assert.NotEqual(currentChangeId, whitelist.ChangeId); - var outpoint3 = BitcoinFactory.CreateOutPoint(); + var outpoint3 = BitcoinFactory.CreateOutPoint(rnd); whitelist.Add(outpoint3); Assert.NotEqual(currentChangeId, whitelist.ChangeId); diff --git a/WalletWasabi.Tests/UnitTests/WabiSabi/Client/AmountDecomposerTests.cs b/WalletWasabi.Tests/UnitTests/WabiSabi/Client/AmountDecomposerTests.cs index ba5ecc7bd9..5e729ac912 100644 --- a/WalletWasabi.Tests/UnitTests/WabiSabi/Client/AmountDecomposerTests.cs +++ b/WalletWasabi.Tests/UnitTests/WabiSabi/Client/AmountDecomposerTests.cs @@ -48,8 +48,8 @@ public void DecompositionsInvariantTest(decimal feeRateDecimal, long minOutputAm var availableVsize = maxAvailableOutputs * outputVirtualSize; var feeRate = new FeeRate(feeRateDecimal); var feePerOutput = feeRate.GetFee(outputVirtualSize); - var registeredCoinEffectiveValues = GenerateRandomCoins(random).Take(3).Select(c => c.EffectiveValue(feeRate, CoordinationFeeRate.Zero)).ToList(); - var theirCoinEffectiveValues = GenerateRandomCoins(random).Take(30).Select(c => c.EffectiveValue(feeRate, CoordinationFeeRate.Zero)).ToList(); + var registeredCoinEffectiveValues = GenerateRandomCoins(random, 3).Select(c => c.EffectiveValue(feeRate, CoordinationFeeRate.Zero)).ToList(); + var theirCoinEffectiveValues = GenerateRandomCoins(random, 30).Select(c => c.EffectiveValue(feeRate, CoordinationFeeRate.Zero)).ToList(); var allowedOutputAmountRange = new MoneyRange(Money.Satoshis(minOutputAmount), Money.Satoshis(ProtocolConstants.MaxAmountCredentialValue)); var allowedOutputTypes = isTaprootEnabled ? new List() { ScriptType.Taproot, ScriptType.P2WPKH } : new List() { ScriptType.P2WPKH }; @@ -84,20 +84,22 @@ public void DecompositionsInvariantTest(decimal feeRateDecimal, long minOutputAm } } - private static IEnumerable GenerateRandomCoins(GingerRandom random) + private static List GenerateRandomCoins(GingerRandom random, int num) { using var key = new Key(); var script = key.GetScriptPubKey(ScriptPubKeyType.Segwit); - while (true) + List result = []; + for (; num > 0; --num) { var amount = random.GetInt64(100_000, ProtocolConstants.MaxAmountCredentialValue); - yield return CreateCoin(script, amount); + result.Add(CreateCoin(random, script, amount)); } + return result; } - private static Coin CreateCoin(Script scriptPubKey, long amount) + private static Coin CreateCoin(GingerRandom rnd, Script scriptPubKey, long amount) { - var prevOut = BitcoinFactory.CreateOutPoint(); + var prevOut = BitcoinFactory.CreateOutPoint(rnd); var txOut = new TxOut(Money.Satoshis(amount), scriptPubKey); return new Coin(prevOut, txOut); } diff --git a/WalletWasabi.Tests/UnitTests/WabiSabi/Client/ArenaClientTests.cs b/WalletWasabi.Tests/UnitTests/WabiSabi/Client/ArenaClientTests.cs index 71a490c4d9..74f28449db 100644 --- a/WalletWasabi.Tests/UnitTests/WabiSabi/Client/ArenaClientTests.cs +++ b/WalletWasabi.Tests/UnitTests/WabiSabi/Client/ArenaClientTests.cs @@ -48,15 +48,16 @@ public async Task FullP2trCoinjoinTestAsync() [Fact] public async Task RemoveInputAsyncTestAsync() { + var rnd = TestRandom.Get(); var config = WabiSabiTestFactory.CreateDefaultWabiSabiConfig(); var round = WabiSabiTestFactory.CreateRound(config); round.SetPhase(Phase.ConnectionConfirmation); - var fundingTx = BitcoinFactory.CreateSmartTransaction(ownOutputCount: 1); + var fundingTx = BitcoinFactory.CreateSmartTransaction(rnd, ownOutputCount: 1); var coin = fundingTx.WalletOutputs.First().Coin; var alice = new Alice(coin, new OwnershipProof(), round, Guid.NewGuid(), false); round.Alices.Add(alice); - using Arena arena = await ArenaTestFactory.From(config).CreateAndStartAsync(round); + using Arena arena = await ArenaTestFactory.From(config).CreateAndStartAsync(rnd, round); using var memoryCache = new MemoryCache(new MemoryCacheOptions()); var idempotencyRequestCache = new IdempotencyRequestCache(memoryCache); @@ -75,6 +76,7 @@ public async Task RemoveInputAsyncTestAsync() [Fact] public async Task SignTransactionAsync() { + var rnd = TestRandom.Get(); WabiSabiConfig config = WabiSabiTestFactory.CreateDefaultWabiSabiConfig(); Round round = WabiSabiTestFactory.CreateRound(config); var password = "satoshi"; @@ -85,7 +87,7 @@ public async Task SignTransactionAsync() var coins = destinationProvider.GetNextDestinations(2, false) .Select(destination => ( - Coin: new Coin(BitcoinFactory.CreateOutPoint(), new TxOut(Money.Coins(1.0m), destination)), + Coin: new Coin(BitcoinFactory.CreateOutPoint(rnd), new TxOut(Money.Coins(1.0m), destination)), OwnershipProof: keyChain.GetOwnershipProof(destination, WabiSabiTestFactory.CreateCommitmentData(round.Id)))) .ToArray(); @@ -95,7 +97,7 @@ public async Task SignTransactionAsync() Alice alice2 = WabiSabiTestFactory.CreateAlice(coins[1].Coin, coins[1].OwnershipProof, round: round); round.Alices.Add(alice2); - using Arena arena = await ArenaTestFactory.From(config).CreateAndStartAsync(round); + using Arena arena = await ArenaTestFactory.From(config).CreateAndStartAsync(rnd, round); using var memoryCache = new MemoryCache(new MemoryCacheOptions()); var idempotencyRequestCache = new IdempotencyRequestCache(memoryCache); @@ -150,6 +152,7 @@ await Assert.ThrowsAsync(async () => private async Task TestFullCoinjoinAsync(ScriptPubKeyType scriptPubKeyType, int inputVirtualSize) { + var rnd = TestRandom.Get(); var config = WabiSabiTestFactory.CreateDefaultWabiSabiConfig(); config.MaxInputCountByRound = 1; config.AllowP2trInputs = true; @@ -157,8 +160,8 @@ private async Task TestFullCoinjoinAsync(ScriptPubKeyType scriptPubKeyType, int var round = WabiSabiTestFactory.CreateRound(WabiSabiTestFactory.CreateRoundParameters(config)); using var key = new Key(); - var outpoint = BitcoinFactory.CreateOutPoint(); - var mockRpc = WabiSabiTestFactory.CreatePreconfiguredRpcClient(); + var outpoint = BitcoinFactory.CreateOutPoint(rnd); + var mockRpc = WabiSabiTestFactory.CreatePreconfiguredRpcClient(rnd); mockRpc.OnGetTxOutAsync = (_, _, _) => new GetTxOutResponse { @@ -178,10 +181,10 @@ private async Task TestFullCoinjoinAsync(ScriptPubKeyType scriptPubKeyType, int MinRelayTxFee = 1 }); mockRpc.OnGetRawTransactionAsync = (_, _) => - Task.FromResult(BitcoinFactory.CreateTransaction()); + Task.FromResult(BitcoinFactory.CreateTransaction(rnd)); - using Arena arena = await ArenaTestFactory.From(config).With(mockRpc).CreateAndStartAsync(round); - await arena.TriggerAndWaitRoundAsync(TimeSpan.FromMinutes(1)); + using Arena arena = await ArenaTestFactory.From(config).With(mockRpc).CreateAndStartAsync(rnd, round); + await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(5)); using var memoryCache = new MemoryCache(new MemoryCacheOptions()); var idempotencyRequestCache = new IdempotencyRequestCache(memoryCache); @@ -196,7 +199,7 @@ private async Task TestFullCoinjoinAsync(ScriptPubKeyType scriptPubKeyType, int roundState.CreateVsizeCredentialClient(TestRandom.Wasabi(2)), config.CoordinatorIdentifier, wabiSabiApi); - var ownershipProof = WabiSabiTestFactory.CreateOwnershipProof(key, round.Id, scriptPubKeyType); + var ownershipProof = WabiSabiTestFactory.CreateOwnershipProof(rnd, key, round.Id, scriptPubKeyType); var (inputRegistrationResponse, _) = await aliceArenaClient.RegisterInputAsync(round.Id, outpoint, ownershipProof, CancellationToken.None); var aliceId = inputRegistrationResponse.Value; @@ -225,7 +228,7 @@ private async Task TestFullCoinjoinAsync(ScriptPubKeyType scriptPubKeyType, int inputRegistrationResponse.IssuedVsizeCredentials, CancellationToken.None); - await arena.TriggerAndWaitRoundAsync(TimeSpan.FromMinutes(1)); + await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(5)); Assert.Equal(Phase.ConnectionConfirmation, round.Phase); // Phase: Connection Confirmation @@ -267,23 +270,13 @@ private async Task TestFullCoinjoinAsync(ScriptPubKeyType scriptPubKeyType, int Credential zeroVsizeCred1 = reissuanceResponse.IssuedVsizeCredentials.ElementAt(2); Credential zeroVsizeCred2 = reissuanceResponse.IssuedVsizeCredentials.ElementAt(3); - await bobArenaClient.RegisterOutputAsync( - round.Id, - destinationKey1.PubKey.GetScriptPubKey(scriptPubKeyType), - new[] { amountCred1, zeroAmountCred1 }, - new[] { vsizeCred1, zeroVsizeCred1 }, - CancellationToken.None); + await bobArenaClient.RegisterOutputAsync(round.Id, destinationKey1.PubKey.GetScriptPubKey(scriptPubKeyType), [amountCred1, zeroAmountCred1], [vsizeCred1, zeroVsizeCred1], CancellationToken.None); - await bobArenaClient.RegisterOutputAsync( - round.Id, - destinationKey2.PubKey.GetScriptPubKey(scriptPubKeyType), - new[] { amountCred2, zeroAmountCred2 }, - new[] { vsizeCred2, zeroVsizeCred2 }, - CancellationToken.None); + await bobArenaClient.RegisterOutputAsync(round.Id, destinationKey2.PubKey.GetScriptPubKey(scriptPubKeyType), [amountCred2, zeroAmountCred2], [vsizeCred2, zeroVsizeCred2], CancellationToken.None); await aliceArenaClient.ReadyToSignAsync(round.Id, aliceId, CancellationToken.None); - await arena.TriggerAndWaitRoundAsync(TimeSpan.FromMinutes(1)); + await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(5)); Assert.Equal(Phase.TransactionSigning, round.Phase); var tx = round.Assert().CreateTransaction(); diff --git a/WalletWasabi.Tests/UnitTests/WabiSabi/Client/BobClientTests.cs b/WalletWasabi.Tests/UnitTests/WabiSabi/Client/BobClientTests.cs index d57c23d69f..5ef030a9a1 100644 --- a/WalletWasabi.Tests/UnitTests/WabiSabi/Client/BobClientTests.cs +++ b/WalletWasabi.Tests/UnitTests/WabiSabi/Client/BobClientTests.cs @@ -30,6 +30,7 @@ public class BobClientTests [Fact] public async Task RegisterOutputTestAsync() { + var rnd = TestRandom.Get(); using CancellationTokenSource cancellationTokenSource = new(TestTimeout); var token = cancellationTokenSource.Token; using CancellationTokenSource silentLeave = new(); @@ -40,10 +41,10 @@ public async Task RegisterOutputTestAsync() var round = WabiSabiTestFactory.CreateRound(config); var km = ServiceFactory.CreateKeyManager(""); var key = BitcoinFactory.CreateHdPubKey(km); - SmartCoin coin1 = BitcoinFactory.CreateSmartCoin(key, Money.Coins(2m)); + SmartCoin coin1 = BitcoinFactory.CreateSmartCoin(rnd, key, Money.Coins(2m)); - var mockRpc = WabiSabiTestFactory.CreatePreconfiguredRpcClient(coin1.Coin); - using Arena arena = await ArenaTestFactory.From(config).With(mockRpc).CreateAndStartAsync(round); + var mockRpc = WabiSabiTestFactory.CreatePreconfiguredRpcClient(rnd, coin1.Coin); + using Arena arena = await ArenaTestFactory.From(config).With(mockRpc).CreateAndStartAsync(rnd, round); await arena.TriggerAndWaitRoundAsync(token); using var memoryCache = new MemoryCache(new MemoryCacheOptions()); diff --git a/WalletWasabi.Tests/UnitTests/WabiSabi/Client/CoinJoinCoinSelectionTests.cs b/WalletWasabi.Tests/UnitTests/WabiSabi/Client/CoinJoinCoinSelectionTests.cs index 133e113d82..7f7f6cf232 100644 --- a/WalletWasabi.Tests/UnitTests/WabiSabi/Client/CoinJoinCoinSelectionTests.cs +++ b/WalletWasabi.Tests/UnitTests/WabiSabi/Client/CoinJoinCoinSelectionTests.cs @@ -1,12 +1,13 @@ -using System.Linq; using Moq; using NBitcoin; +using System.Linq; using WabiSabi.Crypto.Randomness; using WalletWasabi.Blockchain.Analysis; using WalletWasabi.Blockchain.Keys; using WalletWasabi.Blockchain.TransactionOutputs; using WalletWasabi.Helpers; using WalletWasabi.Tests.Helpers; +using WalletWasabi.Tests.TestCommon; using WalletWasabi.WabiSabi.Backend.Rounds; using WalletWasabi.WabiSabi.Client; using WalletWasabi.WabiSabi.Client.CoinJoin.Client; @@ -50,18 +51,19 @@ public async void SelectNothingFromEmptySetOfCoinsAsync() [Fact] public async void SelectNothingFromFullyPrivateSetOfCoinsAsync() { + var rnd = TestRandom.Get(); const int AnonymitySet = 10; var km = KeyManager.CreateNew(out _, "", Network.Main); var coinsToSelectFrom = Enumerable .Range(0, 10) - .Select(i => BitcoinFactory.CreateSmartCoin(BitcoinFactory.CreateHdPubKey(km, isInternal: true), Money.Coins(1m), anonymitySet: AnonymitySet + 1)) + .Select(i => BitcoinFactory.CreateSmartCoin(rnd, BitcoinFactory.CreateHdPubKey(km, isInternal: true), Money.Coins(1m), anonymitySet: AnonymitySet + 1)) .ToList(); // We gotta make sure the distance from external keys is sufficient. foreach (var sc in coinsToSelectFrom) { - var sci = BitcoinFactory.CreateSmartCoin(BitcoinFactory.CreateHdPubKey(km, isInternal: true), Money.Coins(1m), anonymitySet: AnonymitySet + 1); - sci.Transaction.TryAddWalletInput(BitcoinFactory.CreateSmartCoin(BitcoinFactory.CreateHdPubKey(km, isInternal: true), Money.Coins(1m), anonymitySet: AnonymitySet + 1)); + var sci = BitcoinFactory.CreateSmartCoin(rnd, BitcoinFactory.CreateHdPubKey(km, isInternal: true), Money.Coins(1m), anonymitySet: AnonymitySet + 1); + sci.Transaction.TryAddWalletInput(BitcoinFactory.CreateSmartCoin(rnd, BitcoinFactory.CreateHdPubKey(km, isInternal: true), Money.Coins(1m), anonymitySet: AnonymitySet + 1)); sc.Transaction.TryAddWalletInput(sci); } foreach (var sc in coinsToSelectFrom) @@ -87,12 +89,13 @@ public async void SelectNothingFromFullyPrivateSetOfCoinsAsync() [Fact] public async void SelectSomethingFromPrivateButExternalSetOfCoins1Async() { + var rnd = TestRandom.Get(); // Although all coins have reached the desired anonymity set, they are not sufficiently distanced from external keys, because they are external keys. const int AnonymitySet = 10; var km = KeyManager.CreateNew(out _, "", Network.Main); var coinsToSelectFrom = Enumerable .Range(0, 10) - .Select(i => BitcoinFactory.CreateSmartCoin(BitcoinFactory.CreateHdPubKey(km, isInternal: false), Money.Coins(1m), anonymitySet: AnonymitySet + 1)) + .Select(i => BitcoinFactory.CreateSmartCoin(rnd, BitcoinFactory.CreateHdPubKey(km, isInternal: false), Money.Coins(1m), anonymitySet: AnonymitySet + 1)) .ToList(); CoinJoinCoinSelectorRandomnessGenerator generator = CreateSelectorGenerator(inputTarget: 5); @@ -108,12 +111,13 @@ public async void SelectSomethingFromPrivateButExternalSetOfCoins1Async() [Fact] public async void SelectSomethingFromPrivateButNotDistancedSetOfCoins2Async() { + var rnd = TestRandom.Get(); // Although all coins have reached the desired anonymity set, they are not sufficiently distanced from external keys. const int AnonymitySet = 10; var km = KeyManager.CreateNew(out _, "", Network.Main); var coinsToSelectFrom = Enumerable .Range(0, 10) - .Select(i => BitcoinFactory.CreateSmartCoin(BitcoinFactory.CreateHdPubKey(km, isInternal: true), Money.Coins(1m), anonymitySet: AnonymitySet + 1)) + .Select(i => BitcoinFactory.CreateSmartCoin(rnd, BitcoinFactory.CreateHdPubKey(km, isInternal: true), Money.Coins(1m), anonymitySet: AnonymitySet + 1)) .ToList(); CoinJoinCoinSelectorRandomnessGenerator generator = CreateSelectorGenerator(inputTarget: 5); @@ -129,18 +133,19 @@ public async void SelectSomethingFromPrivateButNotDistancedSetOfCoins2Async() [Fact] public async void SelectSomethingFromPrivateButExternalSetOfCoins3Async() { + var rnd = TestRandom.Get(); // Although all coins have reached the desired anonymity set, they are not sufficiently distanced from external keys. const int AnonymitySet = 10; var km = KeyManager.CreateNew(out _, "", Network.Main); var coinsToSelectFrom = Enumerable .Range(0, 10) - .Select(i => BitcoinFactory.CreateSmartCoin(BitcoinFactory.CreateHdPubKey(km, isInternal: true), Money.Coins(1m), anonymitySet: AnonymitySet + 1)) + .Select(i => BitcoinFactory.CreateSmartCoin(rnd, BitcoinFactory.CreateHdPubKey(km, isInternal: true), Money.Coins(1m), anonymitySet: AnonymitySet + 1)) .ToList(); // We gotta make sure the distance from external keys is sufficient. foreach (var sc in coinsToSelectFrom) { - sc.Transaction.TryAddWalletInput(BitcoinFactory.CreateSmartCoin(BitcoinFactory.CreateHdPubKey(km, isInternal: false), Money.Coins(1m), anonymitySet: AnonymitySet + 1)); + sc.Transaction.TryAddWalletInput(BitcoinFactory.CreateSmartCoin(rnd, BitcoinFactory.CreateHdPubKey(km, isInternal: false), Money.Coins(1m), anonymitySet: AnonymitySet + 1)); } foreach (var sc in coinsToSelectFrom) { @@ -161,7 +166,7 @@ public async void SelectSomethingFromPrivateButExternalSetOfCoins3Async() public async void SelectNothingFromTooSmallCoinAsync() { var km = KeyManager.CreateNew(out _, "", Network.Main); - var coinsToSelectFrom = new[] { BitcoinFactory.CreateSmartCoin(BitcoinFactory.CreateHdPubKey(km), Money.Coins(0.00017423m), anonymitySet: 1) }; + var coinsToSelectFrom = new[] { BitcoinFactory.CreateSmartCoin(TestRandom.Get(), BitcoinFactory.CreateHdPubKey(km), Money.Coins(0.00017423m), anonymitySet: 1) }; var roundParams = CreateRoundParameters(); CoinJoinCoinSelectorRandomnessGenerator generator = CreateSelectorGenerator(inputTarget: 5); @@ -181,11 +186,12 @@ public async void SelectNothingFromTooSmallCoinAsync() [Fact] public async void SelectNothingFromTooSmallSetOfCoinsAsync() { + var rnd = TestRandom.Get(); var km = KeyManager.CreateNew(out _, "", Network.Main); var coinsToSelectFrom = new[] { - BitcoinFactory.CreateSmartCoin(BitcoinFactory.CreateHdPubKey(km), Money.Coins(0.00008711m + 0.00006900m), anonymitySet: 1), - BitcoinFactory.CreateSmartCoin(BitcoinFactory.CreateHdPubKey(km), Money.Coins(0.00008710m + 0.00006900m), anonymitySet: 1) + BitcoinFactory.CreateSmartCoin(rnd, BitcoinFactory.CreateHdPubKey(km), Money.Coins(0.00008711m + 0.00006900m), anonymitySet: 1), + BitcoinFactory.CreateSmartCoin(rnd, BitcoinFactory.CreateHdPubKey(km), Money.Coins(0.00008710m + 0.00006900m), anonymitySet: 1) }; var roundParams = CreateRoundParameters(); @@ -206,11 +212,12 @@ public async void SelectNothingFromTooSmallSetOfCoinsAsync() [Fact] public async void SelectSomethingFromJustEnoughSetOfCoinsAsync() { + var rnd = TestRandom.Get(); var km = KeyManager.CreateNew(out _, "", Network.Main); var coinsToSelectFrom = new[] { - BitcoinFactory.CreateSmartCoin(BitcoinFactory.CreateHdPubKey(km), Money.Coins(0.00008711m + 0.00006900m), anonymitySet: 1), - BitcoinFactory.CreateSmartCoin(BitcoinFactory.CreateHdPubKey(km), Money.Coins(0.00008711m + 0.00006900m), anonymitySet: 1) + BitcoinFactory.CreateSmartCoin(rnd, BitcoinFactory.CreateHdPubKey(km), Money.Coins(0.00008711m + 0.00006900m), anonymitySet: 1), + BitcoinFactory.CreateSmartCoin(rnd, BitcoinFactory.CreateHdPubKey(km), Money.Coins(0.00008711m + 0.00006900m), anonymitySet: 1) }; var roundParams = CreateRoundParameters(); @@ -231,12 +238,13 @@ public async void SelectSomethingFromJustEnoughSetOfCoinsAsync() [Fact] public async void SelectNonPrivateCoinFromOneNonPrivateCoinInBigSetOfCoinsConsolidationModeAsync() { + var rnd = TestRandom.Get(); const int AnonymitySet = 10; var km = KeyManager.CreateNew(out _, "", Network.Main); - SmartCoin smallerAnonCoin = BitcoinFactory.CreateSmartCoin(BitcoinFactory.CreateHdPubKey(km), Money.Coins(1m), anonymitySet: AnonymitySet - 1); + SmartCoin smallerAnonCoin = BitcoinFactory.CreateSmartCoin(rnd, BitcoinFactory.CreateHdPubKey(km), Money.Coins(1m), anonymitySet: AnonymitySet - 1); var coinsToSelectFrom = Enumerable .Range(0, 10) - .Select(i => BitcoinFactory.CreateSmartCoin(BitcoinFactory.CreateHdPubKey(km), Money.Coins(1m), anonymitySet: AnonymitySet + 1)) + .Select(i => BitcoinFactory.CreateSmartCoin(rnd, BitcoinFactory.CreateHdPubKey(km), Money.Coins(1m), anonymitySet: AnonymitySet + 1)) .Prepend(smallerAnonCoin) .ToList(); @@ -258,11 +266,12 @@ public async void SelectNonPrivateCoinFromOneNonPrivateCoinInBigSetOfCoinsConsol [Fact] public async void SelectNonPrivateCoinFromOneCoinSetOfCoinsAsync() { + var rnd = TestRandom.Get(); const int AnonymitySet = 10; var km = KeyManager.CreateNew(out _, "", Network.Main); var coinsToSelectFrom = Enumerable .Empty() - .Prepend(BitcoinFactory.CreateSmartCoin(BitcoinFactory.CreateHdPubKey(km), Money.Coins(1m), anonymitySet: AnonymitySet - 1)) + .Prepend(BitcoinFactory.CreateSmartCoin(rnd, BitcoinFactory.CreateHdPubKey(km), Money.Coins(1m), anonymitySet: AnonymitySet - 1)) .ToList(); CoinJoinCoinSelectorRandomnessGenerator generator = CreateSelectorGenerator(inputTarget: 10); @@ -283,12 +292,13 @@ public async void SelectNonPrivateCoinFromOneCoinSetOfCoinsAsync() [Fact] public async void SelectMoreNonPrivateCoinFromTwoCoinsSetOfCoinsAsync() { + var rnd = TestRandom.Get(); const int AnonymitySet = 10; var km = KeyManager.CreateNew(out _, "", Network.Main); var coinsToSelectFrom = Enumerable .Empty() - .Prepend(BitcoinFactory.CreateSmartCoin(BitcoinFactory.CreateHdPubKey(km), Money.Coins(1m), anonymitySet: AnonymitySet - 1)) - .Prepend(BitcoinFactory.CreateSmartCoin(BitcoinFactory.CreateHdPubKey(km), Money.Coins(1m), anonymitySet: AnonymitySet - 1)) + .Prepend(BitcoinFactory.CreateSmartCoin(rnd, BitcoinFactory.CreateHdPubKey(km), Money.Coins(1m), anonymitySet: AnonymitySet - 1)) + .Prepend(BitcoinFactory.CreateSmartCoin(rnd, BitcoinFactory.CreateHdPubKey(km), Money.Coins(1m), anonymitySet: AnonymitySet - 1)) .ToList(); CoinJoinCoinSelectorRandomnessGenerator generator = CreateSelectorGenerator(inputTarget: 10, sameTxAllowance: 0); @@ -308,12 +318,13 @@ public async void SelectMoreNonPrivateCoinFromTwoCoinsSetOfCoinsAsync() [Fact] public async void SelectTwoNonPrivateCoinsFromTwoCoinsSetOfCoinsConsolidationModeAsync() { + var rnd = TestRandom.Get(); const int AnonymitySet = 10; var km = KeyManager.CreateNew(out _, "", Network.Main); var coinsToSelectFrom = Enumerable .Empty() - .Prepend(BitcoinFactory.CreateSmartCoin(BitcoinFactory.CreateHdPubKey(km), Money.Coins(1m), anonymitySet: AnonymitySet - 1)) - .Prepend(BitcoinFactory.CreateSmartCoin(BitcoinFactory.CreateHdPubKey(km), Money.Coins(1m), anonymitySet: AnonymitySet - 1)) + .Prepend(BitcoinFactory.CreateSmartCoin(rnd, BitcoinFactory.CreateHdPubKey(km), Money.Coins(1m), anonymitySet: AnonymitySet - 1)) + .Prepend(BitcoinFactory.CreateSmartCoin(rnd, BitcoinFactory.CreateHdPubKey(km), Money.Coins(1m), anonymitySet: AnonymitySet - 1)) .ToList(); CoinJoinCoinSelectorRandomnessGenerator generator = CreateSelectorGenerator(inputTarget: 10); @@ -333,21 +344,22 @@ public async void SelectTwoNonPrivateCoinsFromTwoCoinsSetOfCoinsConsolidationMod [Fact] public async void SelectNothingFromFullyPrivateAndBelowMinAllowedSetOfCoinsAsync() { + var rnd = TestRandom.Get(); const int AnonymitySet = 10; var km = KeyManager.CreateNew(out _, "", Network.Main); var coinsToSelectFrom = Enumerable .Range(0, 10) - .Select(i => BitcoinFactory.CreateSmartCoin(BitcoinFactory.CreateHdPubKey(km, isInternal: true), Money.Coins(1m), anonymitySet: AnonymitySet + 1)) + .Select(i => BitcoinFactory.CreateSmartCoin(rnd, BitcoinFactory.CreateHdPubKey(km, isInternal: true), Money.Coins(1m), anonymitySet: AnonymitySet + 1)) .ToList(); var utxoSelectionParameter = CreateUtxoSelectionParameters(); - coinsToSelectFrom.Add(BitcoinFactory.CreateSmartCoin(BitcoinFactory.CreateHdPubKey(km, isInternal: true), utxoSelectionParameter.AllowedInputAmounts.Min - Money.Satoshis(1), anonymitySet: AnonymitySet - 1)); + coinsToSelectFrom.Add(BitcoinFactory.CreateSmartCoin(rnd, BitcoinFactory.CreateHdPubKey(km, isInternal: true), utxoSelectionParameter.AllowedInputAmounts.Min - Money.Satoshis(1), anonymitySet: AnonymitySet - 1)); // We gotta make sure the distance from external keys is sufficient. foreach (var sc in coinsToSelectFrom) { - var sci = BitcoinFactory.CreateSmartCoin(BitcoinFactory.CreateHdPubKey(km, isInternal: true), Money.Coins(1m), anonymitySet: AnonymitySet + 1); - sci.Transaction.TryAddWalletInput(BitcoinFactory.CreateSmartCoin(BitcoinFactory.CreateHdPubKey(km, isInternal: true), Money.Coins(1m), anonymitySet: AnonymitySet + 1)); + var sci = BitcoinFactory.CreateSmartCoin(rnd, BitcoinFactory.CreateHdPubKey(km, isInternal: true), Money.Coins(1m), anonymitySet: AnonymitySet + 1); + sci.Transaction.TryAddWalletInput(BitcoinFactory.CreateSmartCoin(rnd, BitcoinFactory.CreateHdPubKey(km, isInternal: true), Money.Coins(1m), anonymitySet: AnonymitySet + 1)); sc.Transaction.TryAddWalletInput(sci); } foreach (var sc in coinsToSelectFrom) diff --git a/WalletWasabi.Tests/UnitTests/WabiSabi/Client/KeyChainTests.cs b/WalletWasabi.Tests/UnitTests/WabiSabi/Client/KeyChainTests.cs index ce9da98c0d..ee0d0059e6 100644 --- a/WalletWasabi.Tests/UnitTests/WabiSabi/Client/KeyChainTests.cs +++ b/WalletWasabi.Tests/UnitTests/WabiSabi/Client/KeyChainTests.cs @@ -3,6 +3,7 @@ using System.Linq; using WalletWasabi.Blockchain.Keys; using WalletWasabi.Tests.Helpers; +using WalletWasabi.Tests.TestCommon; using WalletWasabi.WabiSabi.Client; using WalletWasabi.Wallets; using Xunit; @@ -19,7 +20,7 @@ public void SignTransactionTest() var keyChain = new KeyChain(keyManager, new Kitchen("")); var coinDestination = destinationProvider.GetNextDestinations(1, false).First(); - var coin = new Coin(BitcoinFactory.CreateOutPoint(), new TxOut(Money.Coins(1.0m), coinDestination)); + var coin = new Coin(BitcoinFactory.CreateOutPoint(TestRandom.Get()), new TxOut(Money.Coins(1.0m), coinDestination)); var transaction = Transaction.Create(Network.Main); // the transaction doesn't contain the input that we request to be signed. diff --git a/WalletWasabi.Tests/UnitTests/WabiSabi/Crypto/OwnershipProofTests.cs b/WalletWasabi.Tests/UnitTests/WabiSabi/Crypto/OwnershipProofTests.cs index 4386790068..ac5ee1d94a 100644 --- a/WalletWasabi.Tests/UnitTests/WabiSabi/Crypto/OwnershipProofTests.cs +++ b/WalletWasabi.Tests/UnitTests/WabiSabi/Crypto/OwnershipProofTests.cs @@ -1,6 +1,7 @@ using NBitcoin; using WalletWasabi.Crypto; using WalletWasabi.Tests.Helpers; +using WalletWasabi.Tests.TestCommon; using Xunit; namespace WalletWasabi.Tests.UnitTests.WabiSabi.Crypto; @@ -13,8 +14,9 @@ public class OwnershipProofTests [Fact] public void EqualityTest() { + var rnd = TestRandom.Get(); using Key key = new(); - uint256 roundHash = BitcoinFactory.CreateUint256(); + uint256 roundHash = BitcoinFactory.CreateUint256(rnd); // Request #1. OwnershipProof request1 = CreateOwnershipProof(key, roundHash); @@ -30,7 +32,7 @@ public void EqualityTest() Assert.NotEqual(request1, request3); // Request #4. - OwnershipProof request4 = CreateOwnershipProof(key, roundHash: BitcoinFactory.CreateUint256()); // Round hash intentionally changed. + OwnershipProof request4 = CreateOwnershipProof(key, roundHash: BitcoinFactory.CreateUint256(rnd)); // Round hash intentionally changed. Assert.NotEqual(request1, request4); } diff --git a/WalletWasabi.Tests/UnitTests/WabiSabi/Integration/Startup.cs b/WalletWasabi.Tests/UnitTests/WabiSabi/Integration/Startup.cs index de951b5000..21be1af69a 100644 --- a/WalletWasabi.Tests/UnitTests/WabiSabi/Integration/Startup.cs +++ b/WalletWasabi.Tests/UnitTests/WabiSabi/Integration/Startup.cs @@ -20,7 +20,6 @@ public Startup(IConfiguration configuration) public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { - app.UseStaticFiles(); app.UseRouting(); app.UseEndpoints(endpoints => endpoints.MapControllers()); } diff --git a/WalletWasabi.Tests/UnitTests/WabiSabi/Integration/WabiSabiHttpApiIntegrationTests.cs b/WalletWasabi.Tests/UnitTests/WabiSabi/Integration/WabiSabiHttpApiIntegrationTests.cs index eec1d42c87..f4eb5415c8 100644 --- a/WalletWasabi.Tests/UnitTests/WabiSabi/Integration/WabiSabiHttpApiIntegrationTests.cs +++ b/WalletWasabi.Tests/UnitTests/WabiSabi/Integration/WabiSabiHttpApiIntegrationTests.cs @@ -1,6 +1,7 @@ -using System.Collections.Immutable; +using GingerCommon.Crypto.Random; using Microsoft.Extensions.DependencyInjection; using NBitcoin; +using System.Collections.Immutable; using System.IO; using System.Linq; using System.Net.Http; @@ -8,7 +9,9 @@ using System.Threading.Tasks; using WalletWasabi.BitcoinCore.Rpc; using WalletWasabi.Blockchain.Keys; +using WalletWasabi.Blockchain.TransactionOutputs; using WalletWasabi.Tests.Helpers; +using WalletWasabi.Tests.TestCommon; using WalletWasabi.Tor.Http; using WalletWasabi.Tor.Socks5.Pool.Circuits; using WalletWasabi.WabiSabi; @@ -18,14 +21,13 @@ using WalletWasabi.WabiSabi.Backend.Rounds.CoinJoinStorage; using WalletWasabi.WabiSabi.Backend.Statistics; using WalletWasabi.WabiSabi.Client; +using WalletWasabi.WabiSabi.Client.CoinJoin.Client; using WalletWasabi.WabiSabi.Client.CoinJoinProgressEvents; using WalletWasabi.WabiSabi.Client.RoundStateAwaiters; using WalletWasabi.WabiSabi.Models; using WalletWasabi.WabiSabi.Models.MultipartyTransaction; using Xunit; using Xunit.Abstractions; -using WalletWasabi.Blockchain.TransactionOutputs; -using WalletWasabi.WabiSabi.Client.CoinJoin.Client; namespace WalletWasabi.Tests.UnitTests.WabiSabi.Integration; @@ -55,7 +57,7 @@ public async Task RegisterSpentOrInNonExistentCoinAsync() // means that the output is spent or simply doesn't even exist. var nonExistingOutPoint = new OutPoint(); using var signingKey = new Key(); - var ownershipProof = WabiSabiTestFactory.CreateOwnershipProof(signingKey, round.Id); + var ownershipProof = WabiSabiTestFactory.CreateOwnershipProof(TestRandom.Get(), signingKey, round.Id); var ex = await Assert.ThrowsAsync(async () => await apiClient.RegisterInputAsync(round.Id, nonExistingOutPoint, ownershipProof, CancellationToken.None)); @@ -99,7 +101,7 @@ public async Task RegisterBannedCoinAsync() // If an output is not in the utxo dataset then it is not unspent, this // means that the output is spent or simply doesn't even exist. - var ownershipProof = WabiSabiTestFactory.CreateOwnershipProof(signingKey, round.Id); + var ownershipProof = WabiSabiTestFactory.CreateOwnershipProof(TestRandom.Get(), signingKey, round.Id); var ex = await Assert.ThrowsAsync(async () => await apiClient.RegisterInputAsync(round.Id, bannedOutPoint, ownershipProof, timeoutCts.Token)); @@ -122,7 +124,7 @@ public async Task SoloCoinJoinTestAsync(long[] amounts) _output.WriteLine("Creating key manager..."); KeyManager keyManager = KeyManager.CreateNew(out _, password: "", Network.Main); - var coins = GenerateSmartCoins(keyManager, amounts, inputCount); + var coins = GenerateSmartCoins(TestRandom.Get(), keyManager, amounts, inputCount); _output.WriteLine("Coins were created successfully"); @@ -193,7 +195,7 @@ public async Task ErrorWhileRegisterOutputsCoinJoinTestAsync() _output.WriteLine("Creating key manager..."); KeyManager keyManager = KeyManager.CreateNew(out var _, password: "", Network.Main); - var coins = GenerateSmartCoins(keyManager, amounts, inputCount); + var coins = GenerateSmartCoins(TestRandom.Get(), keyManager, amounts, inputCount); _output.WriteLine("Coins were created successfully"); @@ -274,6 +276,7 @@ void HandleCoinJoinProgress(object? sender, CoinJoinProgressEventArgs coinJoinPr [InlineData(new long[] { 20_000_000, 40_000_000, 60_000_000, 80_000_000 })] public async Task CoinJoinWithBlameRoundTestAsync(long[] amounts) { + var rnd = TestRandom.Get(); int inputCount = amounts.Length; // At the end of the test a coinjoin transaction has to be created and broadcasted. @@ -286,8 +289,8 @@ public async Task CoinJoinWithBlameRoundTestAsync(long[] amounts) KeyManager keyManager1 = KeyManager.CreateNew(out var _, password: "", Network.Main); KeyManager keyManager2 = KeyManager.CreateNew(out var _, password: "", Network.Main); - var coins = GenerateSmartCoins(keyManager1, amounts, inputCount); - var badCoins = GenerateSmartCoins(keyManager2, amounts, inputCount); + var coins = GenerateSmartCoins(rnd, keyManager1, amounts, inputCount); + var badCoins = GenerateSmartCoins(rnd, keyManager2, amounts, inputCount); var httpClient = _apiApplicationFactory.WithWebHostBuilder(builder => builder.AddMockRpcClient( @@ -534,16 +537,17 @@ public async Task MultiClientsCoinJoinTestAsync( } finally { - await node.TryStopAsync(); + await node.TryStopAsync(true, 2); } } [Fact] public async Task RegisterCoinIdempotencyAsync() { + var rnd = TestRandom.Get(); using Key signingKey = new(); Coin coinToRegister = new( - fromOutpoint: BitcoinFactory.CreateOutPoint(), + fromOutpoint: BitcoinFactory.CreateOutPoint(rnd), fromTxOut: new TxOut(Money.Coins(1), signingKey.PubKey.GetScriptPubKey(ScriptPubKeyType.Segwit))); using HttpClient httpClient = _apiApplicationFactory.WithWebHostBuilder(builder => @@ -570,17 +574,17 @@ public async Task RegisterCoinIdempotencyAsync() RoundState[] rounds = (await apiClient.GetStatusAsync(RoundStateRequest.Empty, CancellationToken.None)).RoundStates; RoundState round = rounds.First(x => x.CoinjoinState is ConstructionState); - var ownershipProof = WabiSabiTestFactory.CreateOwnershipProof(signingKey, round.Id); + var ownershipProof = WabiSabiTestFactory.CreateOwnershipProof(rnd, signingKey, round.Id); var (response, _) = await apiClient.RegisterInputAsync(round.Id, coinToRegister.Outpoint, ownershipProof, CancellationToken.None); Assert.NotEqual(Guid.Empty, response.Value); } - private SmartCoin[] GenerateSmartCoins(KeyManager keyManager, long[] amounts, int inputCount) + private SmartCoin[] GenerateSmartCoins(GingerRandom rnd, KeyManager keyManager, long[] amounts, int inputCount) { return keyManager.GetKeys() .Take(inputCount) - .Select((x, i) => BitcoinFactory.CreateSmartCoin(x, Money.Satoshis(amounts[i]))) + .Select((x, i) => BitcoinFactory.CreateSmartCoin(rnd, x, Money.Satoshis(amounts[i]))) .ToArray(); } diff --git a/WalletWasabi.Tests/UnitTests/WabiSabi/Models/ConnectionConfirmationRequestTests.cs b/WalletWasabi.Tests/UnitTests/WabiSabi/Models/ConnectionConfirmationRequestTests.cs index cd2aee357e..6a3a539798 100644 --- a/WalletWasabi.Tests/UnitTests/WabiSabi/Models/ConnectionConfirmationRequestTests.cs +++ b/WalletWasabi.Tests/UnitTests/WabiSabi/Models/ConnectionConfirmationRequestTests.cs @@ -7,6 +7,7 @@ using WabiSabi.Crypto.Groups; using WabiSabi.Crypto.ZeroKnowledge; using WalletWasabi.Tests.Helpers; +using WalletWasabi.Tests.TestCommon; using WalletWasabi.WabiSabi.Models; using Xunit; @@ -21,7 +22,8 @@ public class ConnectionConfirmationRequestTests [Fact] public void EqualityTest() { - uint256 roundId = BitcoinFactory.CreateUint256(); + var rnd = TestRandom.Get(); + uint256 roundId = BitcoinFactory.CreateUint256(rnd); Guid guid = Guid.NewGuid(); // Request #1. @@ -48,7 +50,7 @@ public void EqualityTest() // Request #3. ConnectionConfirmationRequest request3 = new( - RoundId: BitcoinFactory.CreateUint256(), // Intentionally changed. + RoundId: BitcoinFactory.CreateUint256(rnd), // Intentionally changed. AliceId: guid, ZeroAmountCredentialRequests: NewZeroCredentialsRequest(modifier: 1), RealAmountCredentialRequests: NewRealCredentialsRequest(modifier: 1), diff --git a/WalletWasabi.Tests/UnitTests/WabiSabi/Models/ConstructionStateTests.cs b/WalletWasabi.Tests/UnitTests/WabiSabi/Models/ConstructionStateTests.cs index f9fa3e0e63..de7d646577 100644 --- a/WalletWasabi.Tests/UnitTests/WabiSabi/Models/ConstructionStateTests.cs +++ b/WalletWasabi.Tests/UnitTests/WabiSabi/Models/ConstructionStateTests.cs @@ -2,6 +2,7 @@ using WalletWasabi.Extensions; using WalletWasabi.Helpers; using WalletWasabi.Tests.Helpers; +using WalletWasabi.Tests.TestCommon; using WalletWasabi.WabiSabi.Backend.Rounds; using WalletWasabi.WabiSabi.Models.MultipartyTransaction; using Xunit; @@ -26,6 +27,7 @@ public void ConstructionStateFeeRateCalculation() var state = round.Assert(); var (coin, ownershipProof) = WabiSabiTestFactory.CreateCoinWithOwnershipProof( + TestRandom.Get(), amount: roundParameters.AllowedInputAmounts.Min + miningFeeRate.GetFee(Constants.P2wpkhInputVirtualSize + Constants.P2wpkhOutputVirtualSize), roundId: round.Id); state = state.AddInput(coin, ownershipProof, WabiSabiTestFactory.CreateCommitmentData(round.Id)); diff --git a/WalletWasabi.Tests/UnitTests/WabiSabi/Models/InputRegistrationRequestTests.cs b/WalletWasabi.Tests/UnitTests/WabiSabi/Models/InputRegistrationRequestTests.cs index c80e70b9d5..eb7b0a8009 100644 --- a/WalletWasabi.Tests/UnitTests/WabiSabi/Models/InputRegistrationRequestTests.cs +++ b/WalletWasabi.Tests/UnitTests/WabiSabi/Models/InputRegistrationRequestTests.cs @@ -8,6 +8,7 @@ using WabiSabi.Crypto.ZeroKnowledge; using WalletWasabi.Crypto; using WalletWasabi.Tests.Helpers; +using WalletWasabi.Tests.TestCommon; using WalletWasabi.WabiSabi.Models; using Xunit; @@ -21,9 +22,10 @@ public class InputRegistrationRequestTests [Fact] public void EqualityTest() { - uint256 roundId = BitcoinFactory.CreateUint256(); - uint256 roundHash = BitcoinFactory.CreateUint256(); - OutPoint outPoint = BitcoinFactory.CreateOutPoint(); + var rnd = TestRandom.Get(); + uint256 roundId = BitcoinFactory.CreateUint256(rnd); + uint256 roundHash = BitcoinFactory.CreateUint256(rnd); + OutPoint outPoint = BitcoinFactory.CreateOutPoint(rnd); using Key key = new(); @@ -49,7 +51,7 @@ public void EqualityTest() // Request #3. InputRegistrationRequest request3 = new( - RoundId: BitcoinFactory.CreateUint256(), // Intentionally changed. + RoundId: BitcoinFactory.CreateUint256(rnd), // Intentionally changed. Input: outPoint, OwnershipProof: CreateOwnershipProof(key, roundHash), ZeroAmountCredentialRequests: GetZeroCredentialsRequest(), @@ -61,7 +63,7 @@ public void EqualityTest() // Request #4. InputRegistrationRequest request4 = new( RoundId: roundId, - Input: BitcoinFactory.CreateOutPoint(), // Intentionally changed. + Input: BitcoinFactory.CreateOutPoint(rnd), // Intentionally changed. OwnershipProof: CreateOwnershipProof(key, roundHash), ZeroAmountCredentialRequests: GetZeroCredentialsRequest(), ZeroVsizeCredentialRequests: GetZeroCredentialsRequest() @@ -73,7 +75,7 @@ public void EqualityTest() InputRegistrationRequest request5 = new( RoundId: roundId, Input: outPoint, - OwnershipProof: CreateOwnershipProof(key, roundHash: BitcoinFactory.CreateUint256()), // Intentionally changed. + OwnershipProof: CreateOwnershipProof(key, roundHash: BitcoinFactory.CreateUint256(rnd)), // Intentionally changed. ZeroAmountCredentialRequests: GetZeroCredentialsRequest(), ZeroVsizeCredentialRequests: GetZeroCredentialsRequest() ); diff --git a/WalletWasabi.Tests/UnitTests/WabiSabi/Models/InputsRemovalRequestTests.cs b/WalletWasabi.Tests/UnitTests/WabiSabi/Models/InputsRemovalRequestTests.cs index 0940c214b7..cabec7b852 100644 --- a/WalletWasabi.Tests/UnitTests/WabiSabi/Models/InputsRemovalRequestTests.cs +++ b/WalletWasabi.Tests/UnitTests/WabiSabi/Models/InputsRemovalRequestTests.cs @@ -1,5 +1,6 @@ using NBitcoin; using WalletWasabi.Tests.Helpers; +using WalletWasabi.Tests.TestCommon; using WalletWasabi.WabiSabi.Models; using Xunit; @@ -13,7 +14,8 @@ public class InputsRemovalRequestTestsTests [Fact] public void EqualityTest() { - uint256 roundId = BitcoinFactory.CreateUint256(); + var rnd = TestRandom.Get(); + uint256 roundId = BitcoinFactory.CreateUint256(rnd); Guid guid = Guid.NewGuid(); // Request #1. @@ -25,7 +27,7 @@ public void EqualityTest() Assert.Equal(request1, request2); // Request #3. - InputsRemovalRequest request3 = new(RoundId: BitcoinFactory.CreateUint256(), AliceId: guid); + InputsRemovalRequest request3 = new(RoundId: BitcoinFactory.CreateUint256(rnd), AliceId: guid); Assert.NotEqual(request1, request3); } diff --git a/WalletWasabi.Tests/UnitTests/WabiSabi/Models/MultipartyTransactionTests.cs b/WalletWasabi.Tests/UnitTests/WabiSabi/Models/MultipartyTransactionTests.cs index f4479dcf23..4b074d1495 100644 --- a/WalletWasabi.Tests/UnitTests/WabiSabi/Models/MultipartyTransactionTests.cs +++ b/WalletWasabi.Tests/UnitTests/WabiSabi/Models/MultipartyTransactionTests.cs @@ -5,6 +5,7 @@ using WalletWasabi.Extensions; using WalletWasabi.Helpers; using WalletWasabi.Tests.Helpers; +using WalletWasabi.Tests.TestCommon; using WalletWasabi.WabiSabi; using WalletWasabi.WabiSabi.Backend; using WalletWasabi.WabiSabi.Backend.Models; @@ -38,11 +39,12 @@ private static RoundParameters CreateDefaultParameters() [Fact] public void TwoPartiesNoFees() { + var rnd = TestRandom.Get(); using Key key1 = new(); using Key key2 = new(); - (var alice1Coin, var alice1OwnershipProof) = WabiSabiTestFactory.CreateCoinWithOwnershipProof(key1); - (var alice2Coin, var alice2OwnershipProof) = WabiSabiTestFactory.CreateCoinWithOwnershipProof(key2); + (var alice1Coin, var alice1OwnershipProof) = WabiSabiTestFactory.CreateCoinWithOwnershipProof(rnd, key1); + (var alice2Coin, var alice2OwnershipProof) = WabiSabiTestFactory.CreateCoinWithOwnershipProof(rnd, key2); var state = new ConstructionState(DefaultParameters); @@ -117,7 +119,7 @@ public void TwoPartiesNoFees() [Fact] public void AddWithOptimize() { - (var coin, var ownershipProof) = WabiSabiTestFactory.CreateCoinWithOwnershipProof(); + (var coin, var ownershipProof) = WabiSabiTestFactory.CreateCoinWithOwnershipProof(TestRandom.Get()); var state = new ConstructionState(DefaultParameters).AddInput(coin, ownershipProof, CommitmentData); @@ -135,11 +137,12 @@ public void AddWithOptimize() [Fact] public void WitnessValidation() { + var rnd = TestRandom.Get(); using Key key1 = new(); using Key key2 = new(); - (var alice1Coin, var alice1OwnershipProof) = WabiSabiTestFactory.CreateCoinWithOwnershipProof(key1); - (var alice2Coin, var alice2OwnershipProof) = WabiSabiTestFactory.CreateCoinWithOwnershipProof(key2); + (var alice1Coin, var alice1OwnershipProof) = WabiSabiTestFactory.CreateCoinWithOwnershipProof(rnd, key1); + (var alice2Coin, var alice2OwnershipProof) = WabiSabiTestFactory.CreateCoinWithOwnershipProof(rnd, key2); var state = new ConstructionState(DefaultParameters).AddInput(alice1Coin, alice1OwnershipProof, CommitmentData).AddInput(alice2Coin, alice2OwnershipProof, CommitmentData); @@ -187,11 +190,12 @@ public void WitnessValidation() [Fact] public void PublishWitnessesTest() { + var rnd = TestRandom.Get(); using Key key1 = new(); using Key key2 = new(); - (var alice1Coin, var alice1OwnershipProof) = WabiSabiTestFactory.CreateCoinWithOwnershipProof(key1); - (var alice2Coin, var alice2OwnershipProof) = WabiSabiTestFactory.CreateCoinWithOwnershipProof(key2); + (var alice1Coin, var alice1OwnershipProof) = WabiSabiTestFactory.CreateCoinWithOwnershipProof(rnd, key1); + (var alice2Coin, var alice2OwnershipProof) = WabiSabiTestFactory.CreateCoinWithOwnershipProof(rnd, key2); var bob1 = new TxOut(Money.Coins(1), alice1Coin.ScriptPubKey); var bob2 = new TxOut(Money.Coins(1), alice2Coin.ScriptPubKey); @@ -249,13 +253,14 @@ public void PublishWitnessesTest() [Fact] public void FeeRateValidation() { + var rnd = TestRandom.Get(); var feeRate = new FeeRate(new Money(1000L)); using Key key1 = new(); using Key key2 = new(); - (var alice1Coin, var alice1OwnershipProof) = WabiSabiTestFactory.CreateCoinWithOwnershipProof(key1); - (var alice2Coin, var alice2OwnershipProof) = WabiSabiTestFactory.CreateCoinWithOwnershipProof(key2); + (var alice1Coin, var alice1OwnershipProof) = WabiSabiTestFactory.CreateCoinWithOwnershipProof(rnd, key1); + (var alice2Coin, var alice2OwnershipProof) = WabiSabiTestFactory.CreateCoinWithOwnershipProof(rnd, key2); var state = new ConstructionState(DefaultParameters with { MiningFeeRate = feeRate }) .AddInput(alice1Coin, alice1OwnershipProof, CommitmentData) @@ -310,7 +315,7 @@ public void FeeRateValidation() [Fact] public void NoDuplicateInputs() { - (var coin, var ownershipProof) = WabiSabiTestFactory.CreateCoinWithOwnershipProof(); + (var coin, var ownershipProof) = WabiSabiTestFactory.CreateCoinWithOwnershipProof(TestRandom.Get()); var state = new ConstructionState(DefaultParameters).AddInput(coin, ownershipProof, CommitmentData); ThrowsProtocolException(WabiSabiProtocolErrorCode.NonUniqueInputs, () => state.AddInput(coin, ownershipProof, CommitmentData)); Assert.Single(state.Inputs); @@ -321,15 +326,15 @@ public void NoDuplicateInputs() [Fact] public void OnlyAllowedInputTypes() { - var legacyOnly = new ConstructionState(DefaultParameters with { AllowedInputTypes = ImmutableSortedSet.Create(ScriptType.P2PKH) }); - (var coin, var ownershipProof) = WabiSabiTestFactory.CreateCoinWithOwnershipProof(); + var legacyOnly = new ConstructionState(DefaultParameters with { AllowedInputTypes = [ScriptType.P2PKH] }); + (var coin, var ownershipProof) = WabiSabiTestFactory.CreateCoinWithOwnershipProof(TestRandom.Get()); ThrowsProtocolException(WabiSabiProtocolErrorCode.ScriptNotAllowed, () => legacyOnly.AddInput(coin, ownershipProof, CommitmentData)); } [Fact] public void InputAmountRanges() { - (var coin, var ownershipProof) = WabiSabiTestFactory.CreateCoinWithOwnershipProof(); + (var coin, var ownershipProof) = WabiSabiTestFactory.CreateCoinWithOwnershipProof(TestRandom.Get()); var exact = new ConstructionState(DefaultParameters with { AllowedInputAmounts = new MoneyRange(coin.Amount, coin.Amount) }); var above = new ConstructionState(DefaultParameters with { AllowedInputAmounts = new MoneyRange(2 * coin.Amount, 3 * coin.Amount) }); @@ -345,8 +350,9 @@ public void InputAmountRanges() [Fact] public void UneconomicalInputs() { - (var alice1Coin, var alice1OwnershipProof) = WabiSabiTestFactory.CreateCoinWithOwnershipProof(amount: new Money(1000L)); - (var alice2Coin, var alice2OwnershipProof) = WabiSabiTestFactory.CreateCoinWithOwnershipProof(amount: new Money(2000L)); + var rnd = TestRandom.Get(); + (var alice1Coin, var alice1OwnershipProof) = WabiSabiTestFactory.CreateCoinWithOwnershipProof(rnd, amount: new Money(1000L)); + (var alice2Coin, var alice2OwnershipProof) = WabiSabiTestFactory.CreateCoinWithOwnershipProof(rnd, amount: new Money(2000L)); // requires 1k sats per input in sat/vKB var inputVsize = alice1Coin.ScriptPubKey.EstimateInputVsize(); @@ -416,7 +422,7 @@ public void NoDustOutputs() [InlineData(100, 105, "0")] public void FeeTests(int inputCount, int outputCount, string feeRateString) { - Random random = new(12345); + var rnd = TestRandom.Get(); FeeRate feeRate = new(satoshiPerByte: decimal.Parse(feeRateString)); CoordinationFeeRate coordinatorFeeRate = new(0m, Money.Zero); @@ -433,7 +439,7 @@ public void FeeTests(int inputCount, int outputCount, string feeRateString) for (int i = 0; i < inputCount; i++) { using Key key = new(); - (var aliceCoin, var aliceOwnershipProof) = WabiSabiTestFactory.CreateCoinWithOwnershipProof(key); + (var aliceCoin, var aliceOwnershipProof) = WabiSabiTestFactory.CreateCoinWithOwnershipProof(rnd, key); coinjoin = coinjoin.AddInput(aliceCoin, aliceOwnershipProof, CommitmentData); } @@ -448,7 +454,7 @@ public void FeeTests(int inputCount, int outputCount, string feeRateString) { var min = (int)(outputCoinNominal.Satoshi * 0.7); var max = (int)(outputCoinNominal.Satoshi * 1.3); - var amount = Money.Satoshis(random.Next(min, max)); + var amount = Money.Satoshis(rnd.GetInt(min, max)); var p2wpkh = BitcoinFactory.CreateScript(); coinjoin = coinjoin.AddOutput(new TxOut(amount, p2wpkh)); } @@ -459,7 +465,7 @@ public void FeeTests(int inputCount, int outputCount, string feeRateString) // Make sure the highest fee rate is low, so coordinator script will be added. WabiSabiConfig cfg2 = WabiSabiTestFactory.CreateDefaultWabiSabiConfig(); - using Arena arena = ArenaTestFactory.From(cfg2).Create([round]); + using Arena arena = ArenaTestFactory.From(cfg2).Create(rnd, [round]); var coinjoinWithCoordinatorScript = arena.AddCoordinationFee(round, coinjoin, coordinatorScript); coinjoinWithCoordinatorScript.Finalize(); diff --git a/WalletWasabi.Tests/UnitTests/WabiSabi/Models/OutputRegistrationRequestTests.cs b/WalletWasabi.Tests/UnitTests/WabiSabi/Models/OutputRegistrationRequestTests.cs index 21f23c8e5a..8f293e8e7e 100644 --- a/WalletWasabi.Tests/UnitTests/WabiSabi/Models/OutputRegistrationRequestTests.cs +++ b/WalletWasabi.Tests/UnitTests/WabiSabi/Models/OutputRegistrationRequestTests.cs @@ -6,6 +6,7 @@ using WabiSabi.Crypto.Groups; using WabiSabi.Crypto.ZeroKnowledge; using WalletWasabi.Tests.Helpers; +using WalletWasabi.Tests.TestCommon; using WalletWasabi.WabiSabi.Models; using Xunit; @@ -19,7 +20,8 @@ public class OutputRegistrationRequestTests [Fact] public void EqualityTest() { - uint256 roundId = BitcoinFactory.CreateUint256(); + var rnd = TestRandom.Get(); + uint256 roundId = BitcoinFactory.CreateUint256(rnd); Script script = Script.FromHex( "0200000000010111b6e0460bb810b05744f8d38262f95fbab02b168b070598a6f31fad438fced40" + "00000001716001427c106013c0042da165c082b3870c31fb3ab4683feffffff0200ca9a3b000000" + @@ -49,7 +51,7 @@ public void EqualityTest() // Request #3. OutputRegistrationRequest request3 = new( - RoundId: BitcoinFactory.CreateUint256(), // Intentionally changed. + RoundId: BitcoinFactory.CreateUint256(rnd), // Intentionally changed. Script: script, AmountCredentialRequests: NewRealCredentialsRequest(modifier: 1), VsizeCredentialRequests: NewRealCredentialsRequest(modifier: 2) diff --git a/WalletWasabi.Tests/UnitTests/WabiSabi/Models/SerializationTests.cs b/WalletWasabi.Tests/UnitTests/WabiSabi/Models/SerializationTests.cs index fecf862876..d539195fcf 100644 --- a/WalletWasabi.Tests/UnitTests/WabiSabi/Models/SerializationTests.cs +++ b/WalletWasabi.Tests/UnitTests/WabiSabi/Models/SerializationTests.cs @@ -28,9 +28,10 @@ public class SerializationTests [Fact] public void InputRegistrationRequestMessageSerialization() { + var rnd = TestRandom.Get(); var message = new InputRegistrationRequest( - BitcoinFactory.CreateUint256(), - BitcoinFactory.CreateOutPoint(), + BitcoinFactory.CreateUint256(rnd), + BitcoinFactory.CreateOutPoint(rnd), new OwnershipProof(), CreateZeroCredentialsRequest(), CreateZeroCredentialsRequest()); @@ -54,7 +55,7 @@ public void InputRegistrationResponseMessageSerialization() public void ConnectionConfirmationRequestMessageSerialization() { var message = new ConnectionConfirmationRequest( - BitcoinFactory.CreateUint256(), + BitcoinFactory.CreateUint256(TestRandom.Get()), Guid.NewGuid(), CreateZeroCredentialsRequest(), CreateRealCredentialsRequest(), @@ -80,7 +81,7 @@ public void ConnectionConfirmationResponseMessageSerialization() public void OutputRegistrationRequestMessageSerialization() { var message = new OutputRegistrationRequest( - BitcoinFactory.CreateUint256(), + BitcoinFactory.CreateUint256(TestRandom.Get()), BitcoinFactory.CreateScript(), CreateRealCredentialsRequest(), CreateRealCredentialsRequest()); @@ -92,7 +93,7 @@ public void OutputRegistrationRequestMessageSerialization() public void ReissueCredentialRequestMessageSerialization() { var message = new ReissueCredentialRequest( - BitcoinFactory.CreateUint256(), + BitcoinFactory.CreateUint256(TestRandom.Get()), CreateRealCredentialsRequest(), CreateRealCredentialsRequest(), CreateZeroCredentialsRequest(), @@ -117,7 +118,7 @@ public void ReissueCredentialResponseMessageSerialization() public void InpuRemovalRequestMessageSerialization() { var message = new InputsRemovalRequest( - BitcoinFactory.CreateUint256(), + BitcoinFactory.CreateUint256(TestRandom.Get()), Guid.NewGuid()); AssertSerialization(message); @@ -128,7 +129,7 @@ public void TransactionSignatureRequestMessageSerialization() { using var key1 = new Key(); using var key2 = new Key(); - var message = new TransactionSignaturesRequest(BitcoinFactory.CreateUint256(), 1, new WitScript(Op.GetPushOp(key1.PubKey.ToBytes()))); + var message = new TransactionSignaturesRequest(BitcoinFactory.CreateUint256(TestRandom.Get()), 1, new WitScript(Op.GetPushOp(key1.PubKey.ToBytes()))); AssertSerialization(message); } @@ -140,7 +141,7 @@ public void RoundStateMessageSerialization() AssertSerialization(RoundState.FromRound(round)); var state = round.Assert(); - (var coin, var ownershipProof) = WabiSabiTestFactory.CreateCoinWithOwnershipProof(roundId: round.Id); + (var coin, var ownershipProof) = WabiSabiTestFactory.CreateCoinWithOwnershipProof(TestRandom.Get(), roundId: round.Id); state = state.AddInput(coin, ownershipProof, WabiSabiTestFactory.CreateCommitmentData(round.Id)); round.CoinjoinState = new SigningState(state.Parameters, state.Events); AssertSerialization(RoundState.FromRound(round)); diff --git a/WalletWasabi.Tests/UnitTests/WabiSabi/Models/TransactionSignaturesRequestTests.cs b/WalletWasabi.Tests/UnitTests/WabiSabi/Models/TransactionSignaturesRequestTests.cs index 8f978121df..a74de2b965 100644 --- a/WalletWasabi.Tests/UnitTests/WabiSabi/Models/TransactionSignaturesRequestTests.cs +++ b/WalletWasabi.Tests/UnitTests/WabiSabi/Models/TransactionSignaturesRequestTests.cs @@ -1,5 +1,6 @@ using NBitcoin; using WalletWasabi.Tests.Helpers; +using WalletWasabi.Tests.TestCommon; using WalletWasabi.WabiSabi.Models; using Xunit; @@ -13,7 +14,8 @@ public class TransactionSignaturesRequestTests [Fact] public void EqualityTest() { - uint256 roundId = BitcoinFactory.CreateUint256(); + var rnd = TestRandom.Get(); + uint256 roundId = BitcoinFactory.CreateUint256(rnd); using Key key1 = new(); using Key key2 = new(); @@ -34,7 +36,7 @@ public void EqualityTest() // Request #3. TransactionSignaturesRequest request3 = new( - RoundId: BitcoinFactory.CreateUint256(), // Intentionally changed. + RoundId: BitcoinFactory.CreateUint256(rnd), // Intentionally changed. InputIndex: 1, Witness: new WitScript(Op.GetPushOp(key1.PubKey.ToBytes()))); diff --git a/WalletWasabi.Tests/UnitTests/Wallet/SmartCoinSelectorTests.cs b/WalletWasabi.Tests/UnitTests/Wallet/SmartCoinSelectorTests.cs index ce60e3f06f..9c0191004f 100644 --- a/WalletWasabi.Tests/UnitTests/Wallet/SmartCoinSelectorTests.cs +++ b/WalletWasabi.Tests/UnitTests/Wallet/SmartCoinSelectorTests.cs @@ -1,12 +1,14 @@ -using System.Collections.Generic; -using System.Linq; using DynamicData; +using GingerCommon.Crypto.Random; using NBitcoin; +using System.Collections.Generic; +using System.Linq; using WalletWasabi.Blockchain.Analysis.Clustering; using WalletWasabi.Blockchain.Keys; using WalletWasabi.Blockchain.TransactionBuilding; using WalletWasabi.Blockchain.TransactionOutputs; using WalletWasabi.Tests.Helpers; +using WalletWasabi.Tests.TestCommon; using Xunit; namespace WalletWasabi.Tests.UnitTests.Wallet; @@ -29,7 +31,7 @@ public SmartCoinSelectorTests() public void SelectsOnlyOneCoinWhenPossible() { decimal target = 0.3m; - List availableCoins = GenerateSmartCoins(Enumerable.Range(0, 9).Select(i => ("Juan", 0.1m * (i + 1)))); + List availableCoins = GenerateSmartCoins(TestRandom.Get(), Enumerable.Range(0, 9).Select(i => ("Juan", 0.1m * (i + 1)))); SmartCoinSelector selector = new(availableCoins); IEnumerable coinsToSpend = selector.Select(suggestion: EmptySuggestion, Money.Coins(target)); @@ -42,7 +44,7 @@ public void SelectsOnlyOneCoinWhenPossible() public void DontSelectUnnecessaryInputs() { Money target = Money.Coins(4m); - List availableCoins = GenerateSmartCoins(Enumerable.Range(0, 10).Select(i => ("Juan", 0.1m * (i + 1)))); + List availableCoins = GenerateSmartCoins(TestRandom.Get(), Enumerable.Range(0, 10).Select(i => ("Juan", 0.1m * (i + 1)))); SmartCoinSelector selector = new(availableCoins); List coinsToSpend = selector.Select(suggestion: EmptySuggestion, target).Cast().ToList(); @@ -55,7 +57,7 @@ public void DontSelectUnnecessaryInputs() public void PreferSameClusterOverExactAmount() { Money target = Money.Coins(0.3m); - List availableCoins = GenerateSmartCoins(("Besos", 0.2m), ("Besos", 0.2m), ("Juan", 0.1m), ("Juan", 0.1m)); + List availableCoins = GenerateSmartCoins(TestRandom.Get(), ("Besos", 0.2m), ("Besos", 0.2m), ("Juan", 0.1m), ("Juan", 0.1m)); SmartCoinSelector selector = new(availableCoins); List coinsToSpend = selector.Select(suggestion: EmptySuggestion, target).Cast().ToList(); @@ -68,7 +70,7 @@ public void PreferSameClusterOverExactAmount() public void PreferExactAmountWhenClustersAreDifferent() { Money target = Money.Coins(0.3m); - List availableCoins = GenerateSmartCoins(("Besos", 0.2m), ("Juan", 0.1m), ("Adam", 0.2m), ("Eve", 0.1m)); + List availableCoins = GenerateSmartCoins(TestRandom.Get(), ("Besos", 0.2m), ("Juan", 0.1m), ("Adam", 0.2m), ("Eve", 0.1m)); SmartCoinSelector selector = new(availableCoins); List coinsToSpend = selector.Select(suggestion: EmptySuggestion, target).Cast().ToList(); @@ -81,7 +83,7 @@ public void PreferExactAmountWhenClustersAreDifferent() public void DontUseTheWholeClusterIfNotNecessary() { Money target = Money.Coins(0.3m); - List availableCoins = GenerateDuplicateSmartCoins(("Juan", 0.1m), count: 10); + List availableCoins = GenerateDuplicateSmartCoins(TestRandom.Get(), ("Juan", 0.1m), count: 10); SmartCoinSelector selector = new(availableCoins); List coinsToSpend = selector.Select(suggestion: EmptySuggestion, target).Cast().ToList(); @@ -93,9 +95,10 @@ public void DontUseTheWholeClusterIfNotNecessary() [Fact] public void PreferLessCoinsOnSameAmount() { + var rnd = TestRandom.Get(); Money target = Money.Coins(1m); - List availableCoins = GenerateDuplicateSmartCoins(("Juan", 0.1m), count: 11); - availableCoins.Add(GenerateDuplicateSmartCoins(("Beto", 0.2m), count: 5)); + List availableCoins = GenerateDuplicateSmartCoins(rnd, ("Juan", 0.1m), count: 11); + availableCoins.Add(GenerateDuplicateSmartCoins(rnd, ("Beto", 0.2m), count: 5)); SmartCoinSelector selector = new(availableCoins); List coinsToSpend = selector.Select(suggestion: EmptySuggestion, target).Cast().ToList(); @@ -107,9 +110,10 @@ public void PreferLessCoinsOnSameAmount() [Fact] public void PreferLessCoinsOverExactAmount() { + var rnd = TestRandom.Get(); Money target = Money.Coins(0.41m); - var smartCoins = GenerateSmartCoins(Enumerable.Range(0, 10).Select(i => ("Juan", 0.1m * (i + 1)))); - smartCoins.Add(BitcoinFactory.CreateSmartCoin(smartCoins[0].HdPubKey, 0.11m)); + var smartCoins = GenerateSmartCoins(rnd, Enumerable.Range(0, 10).Select(i => ("Juan", 0.1m * (i + 1)))); + smartCoins.Add(BitcoinFactory.CreateSmartCoin(rnd, smartCoins[0].HdPubKey, 0.11m)); var someCoins = smartCoins.Select(x => x.Coin); var selector = new SmartCoinSelector(smartCoins); @@ -122,9 +126,10 @@ public void PreferLessCoinsOverExactAmount() [Fact] public void PreferSameScript() { + var rnd = TestRandom.Get(); Money target = Money.Coins(0.31m); - var smartCoins = GenerateSmartCoins(Enumerable.Repeat(("Juan", 0.2m), 12)).ToList(); - smartCoins.Add(BitcoinFactory.CreateSmartCoin(smartCoins[0].HdPubKey, 0.11m)); + var smartCoins = GenerateSmartCoins(rnd, Enumerable.Repeat(("Juan", 0.2m), 12)).ToList(); + smartCoins.Add(BitcoinFactory.CreateSmartCoin(rnd, smartCoins[0].HdPubKey, 0.11m)); var selector = new SmartCoinSelector(smartCoins); var coinsToSpend = selector.Select(suggestion: EmptySuggestion, target).Cast().ToList(); @@ -137,9 +142,10 @@ public void PreferSameScript() [Fact] public void PreferMorePrivateClusterScript() { + var rnd = TestRandom.Get(); Money target = Money.Coins(0.3m); - var coinsKnownByJuan = GenerateSmartCoins(Enumerable.Repeat(("Juan", 0.2m), 5)); - var coinsKnownByBeto = GenerateSmartCoins(Enumerable.Repeat(("Beto", 0.2m), 2)); + var coinsKnownByJuan = GenerateSmartCoins(rnd, Enumerable.Repeat(("Juan", 0.2m), 5)); + var coinsKnownByBeto = GenerateSmartCoins(rnd, Enumerable.Repeat(("Beto", 0.2m), 2)); var selector = new SmartCoinSelector(coinsKnownByJuan.Concat(coinsKnownByBeto).ToList()); var coinsToSpend = selector.Select(suggestion: EmptySuggestion, target).Cast().ToList(); @@ -148,13 +154,13 @@ public void PreferMorePrivateClusterScript() Assert.Equal(0.4m, coinsToSpend.Sum(x => x.Amount.ToUnit(MoneyUnit.BTC))); } - private List GenerateDuplicateSmartCoins((string Cluster, decimal amount) coin, int count) - => GenerateSmartCoins(Enumerable.Range(start: 0, count).Select(x => coin)); + private List GenerateDuplicateSmartCoins(GingerRandom rnd, (string Cluster, decimal amount) coin, int count) + => GenerateSmartCoins(rnd, Enumerable.Range(start: 0, count).Select(x => coin)); - private List GenerateSmartCoins(params (string Cluster, decimal amount)[] coins) - => GenerateSmartCoins((IEnumerable<(string Cluster, decimal amount)>)coins); + private List GenerateSmartCoins(GingerRandom rnd, params (string Cluster, decimal amount)[] coins) + => GenerateSmartCoins(rnd, (IEnumerable<(string Cluster, decimal amount)>)coins); - private List GenerateSmartCoins(IEnumerable<(string Cluster, decimal amount)> coins) + private List GenerateSmartCoins(GingerRandom rnd, IEnumerable<(string Cluster, decimal amount)> coins) { Dictionary> generatedKeyGroup = new(); @@ -189,7 +195,7 @@ private List GenerateSmartCoins(IEnumerable<(string Cluster, decimal return coinPairClusters.Select(x => x.coinPair) .SelectMany(x => - x.Select(y => BitcoinFactory.CreateSmartCoin(y.key, y.amount))) + x.Select(y => BitcoinFactory.CreateSmartCoin(rnd, y.key, y.amount))) .ToList(); // Generate the final SmartCoins. } } diff --git a/WalletWasabi.Tests/UnitTests/Wallet/WalletJsonTest.cs b/WalletWasabi.Tests/UnitTests/Wallet/WalletJsonTest.cs index fe61e09a15..809d436316 100644 --- a/WalletWasabi.Tests/UnitTests/Wallet/WalletJsonTest.cs +++ b/WalletWasabi.Tests/UnitTests/Wallet/WalletJsonTest.cs @@ -331,7 +331,11 @@ public void WalletJsonSaveTest() "Label": "Alice, Bob", "KeyState": 0 } - ] + ], + "SecretHuntResults": { + "Enabled": true, + "Results": [] + } } """; } diff --git a/WalletWasabi.Tests/XunitConfiguration/RegTestFixture.cs b/WalletWasabi.Tests/XunitConfiguration/RegTestFixture.cs index 165facdae0..5cde58ca68 100644 --- a/WalletWasabi.Tests/XunitConfiguration/RegTestFixture.cs +++ b/WalletWasabi.Tests/XunitConfiguration/RegTestFixture.cs @@ -61,7 +61,6 @@ public RegTestFixture() .ConfigureWebHostDefaults(webBuilder => webBuilder .UseStartup() .UseConfiguration(conf) - .UseWebRoot("../../../../WalletWasabi.Backend/wwwroot") .UseUrls(BackendEndPoint)) .Build(); @@ -109,7 +108,7 @@ protected virtual void Dispose(bool disposing) { BackendHost.StopAsync().GetAwaiter().GetResult(); BackendHost.Dispose(); - BackendRegTestNode.TryStopAsync().GetAwaiter().GetResult(); + BackendRegTestNode.TryStopAsync(true, 2).GetAwaiter().GetResult(); HttpClient.Dispose(); } diff --git a/WalletWasabi/Bases/ConfigBase.cs b/WalletWasabi/Bases/ConfigBase.cs index 4d6ee9dfca..0c7eb89ebb 100644 --- a/WalletWasabi/Bases/ConfigBase.cs +++ b/WalletWasabi/Bases/ConfigBase.cs @@ -21,6 +21,7 @@ protected ConfigBase() protected object FileLock { get; } = new(); /// + [JsonIgnore] public string FilePath { get; private set; } = ""; /// diff --git a/WalletWasabi/BitcoinCore/CoreNode.cs b/WalletWasabi/BitcoinCore/CoreNode.cs index e3282ff383..dee7f15838 100644 --- a/WalletWasabi/BitcoinCore/CoreNode.cs +++ b/WalletWasabi/BitcoinCore/CoreNode.cs @@ -285,7 +285,8 @@ public async Task DisposeAsync() } /// Only stop if this node owns the process. - public async Task TryStopAsync(bool onlyOwned = true) + /// /// Always kill the owned process if wait time is >=0 and ellapsed. + public async Task TryStopAsync(bool onlyOwned = true, int waitBeforeKillInSec = -1) { await DisposeAsync().ConfigureAwait(false); @@ -303,8 +304,10 @@ public async Task TryStopAsync(bool onlyOwned = true) { try { - await bridge.StopAsync(onlyOwned).ConfigureAwait(false); - Logger.LogInfo("Stopped."); + DateTimeOffset now = DateTimeOffset.UtcNow; + await bridge.StopAsync(onlyOwned, waitBeforeKillInSec).ConfigureAwait(false); + TimeSpan ellapsed = DateTimeOffset.UtcNow - now; + Logger.LogInfo($"Stopped in {ellapsed.TotalSeconds:F2} sec."); return true; } catch (Exception ex) diff --git a/WalletWasabi/BitcoinCore/Processes/BitcoindRpcProcessBridge.cs b/WalletWasabi/BitcoinCore/Processes/BitcoindRpcProcessBridge.cs index c3378244df..6409f9e812 100644 --- a/WalletWasabi/BitcoinCore/Processes/BitcoindRpcProcessBridge.cs +++ b/WalletWasabi/BitcoinCore/Processes/BitcoindRpcProcessBridge.cs @@ -121,7 +121,7 @@ public async Task StartAsync(CancellationToken cancel) /// /// If there is not PID file, no process is stopped. /// Only stop if this node owns the process. - public async Task StopAsync(bool onlyOwned) + public async Task StopAsync(bool onlyOwned, int waitBeforeKillInSec = -1) { Logger.LogDebug($"> {nameof(onlyOwned)}={onlyOwned}"); @@ -134,7 +134,7 @@ public async Task StopAsync(bool onlyOwned) // "process" variable is guaranteed to be non-null at this point. ProcessAsync process = Process; - using var cts = new CancellationTokenSource(_reasonableCoreShutdownTimeout); + using var cts = new CancellationTokenSource(waitBeforeKillInSec >= 0 ? TimeSpan.FromSeconds(waitBeforeKillInSec) : _reasonableCoreShutdownTimeout); int? pid = await PidFile.TryReadAsync().ConfigureAwait(false); // If the cached PID is PID, then we own the process. @@ -142,10 +142,9 @@ public async Task StopAsync(bool onlyOwned) { Logger.LogDebug($"User is responsible for the daemon process with PID {pid}. Stop it."); + bool isKilled = false; try { - bool isKilled = false; - try { // Stop Bitcoin daemon using RPC "stop" command. @@ -166,6 +165,18 @@ public async Task StopAsync(bool onlyOwned) await process.WaitForExitAsync(cts.Token).ConfigureAwait(false); } } + catch (TaskCanceledException) + { + if (!isKilled && waitBeforeKillInSec >= 0) + { + Logger.LogWarning("Wait time elapsed, killing the process."); + process.Kill(); + } + else + { + throw; + } + } finally { Logger.LogDebug($"Wait until the process is stopped."); diff --git a/WalletWasabi/Blockchain/BlockFilters/IndexBuilderService.cs b/WalletWasabi/Blockchain/BlockFilters/IndexBuilderService.cs index fd11fef2e6..7315ffc646 100644 --- a/WalletWasabi/Blockchain/BlockFilters/IndexBuilderService.cs +++ b/WalletWasabi/Blockchain/BlockFilters/IndexBuilderService.cs @@ -57,15 +57,27 @@ public IndexBuilderService(IndexType indexType, IRPCClient rpc, BlockNotifier bl } else { - ImmutableList.Builder builder = ImmutableList.CreateBuilder(); - + List newList = new(1000_000); foreach (var line in File.ReadAllLines(IndexFilePath)) { var filter = FilterModel.FromLine(line); - builder.Add(filter); + newList.Add(filter); } - Index = builder.ToImmutableList(); + _indexListLock.EnterWriteLock(); + try + { + _blockIndex.Clear(); + for (int idx = 0, len = newList.Count; idx < len; idx++) + { + _blockIndex.Add(newList[idx].Header.BlockHash, idx); + } + _indexList = newList; + } + finally + { + _indexListLock.ExitWriteLock(); + } } } @@ -77,10 +89,10 @@ public IndexBuilderService(IndexType indexType, IRPCClient rpc, BlockNotifier bl private IRPCClient RpcClient { get; } private BlockNotifier BlockNotifier { get; } private string IndexFilePath { get; } - private ImmutableList Index { get; set; } = ImmutableList.Empty; - /// Guards . - private object IndexLock { get; } = new(); + private ReaderWriterLockSlim _indexListLock = new(); + private List _indexList = []; + private Dictionary _blockIndex = []; private uint StartingHeight { get; } public bool IsRunning => Interlocked.Read(ref _serviceStatus) == Running; @@ -111,10 +123,12 @@ public void Synchronize() return; } - Interlocked.Increment(ref _workerCount); - while (Interlocked.Read(ref _workerCount) != 1) + if (Interlocked.Increment(ref _workerCount) != 1) { - await Task.Delay(100).ConfigureAwait(false); + while (Interlocked.Read(ref _workerCount) != 1) + { + await Task.Delay(100).ConfigureAwait(false); + } } if (IsStopping) @@ -132,14 +146,16 @@ public void Synchronize() { SyncInfo syncInfo = await GetSyncInfoAsync().ConfigureAwait(false); - FilterModel? lastIndexFilter = null; + FilterModel? lastIndexFilter; - lock (IndexLock) + _indexListLock.EnterReadLock(); + try { - if (Index.Count != 0) - { - lastIndexFilter = Index[^1]; - } + lastIndexFilter = _indexList.Count > 0 ? _indexList[^1] : null; + } + finally + { + _indexListLock.ExitReadLock(); } uint currentHeight; @@ -208,9 +224,15 @@ public void Synchronize() await File.AppendAllLinesAsync(IndexFilePath, new[] { filterModel.ToLine() }).ConfigureAwait(false); - lock (IndexLock) + _indexListLock.EnterWriteLock(); + try { - Index = Index.Add(filterModel); + _blockIndex.Add(filterModel.Header.BlockHash, _indexList.Count); + _indexList.Add(filterModel); + } + finally + { + _indexListLock.ExitWriteLock(); } // If not close to the tip, just log debug. @@ -299,10 +321,16 @@ private async Task ReorgOneAsync() // 1. Rollback index. uint256 blockHash; - lock (IndexLock) + _indexListLock.EnterWriteLock(); + try + { + blockHash = _indexList[^1].Header.BlockHash; + _blockIndex.Remove(blockHash); + _indexList.RemoveAt(_indexList.Count - 1); + } + finally { - blockHash = Index[^1].Header.BlockHash; - Index = Index.RemoveAt(Index.Count - 1); + _indexListLock.ExitWriteLock(); } Logger.LogInfo($"REORG invalid block: {blockHash}"); @@ -334,50 +362,41 @@ private void BlockNotifier_OnBlock(object? sender, Block e) public (Height bestHeight, IEnumerable filters) GetFilterLinesExcluding(uint256 bestKnownBlockHash, int count, out bool found) { - found = false; // Only build the filter list from when the known hash is found. - var filters = new List(); - - ImmutableList currentIndex; - - lock (IndexLock) - { - currentIndex = Index; - } - - foreach (var filter in currentIndex) + Height bestHeight; + List? filters = null; { - if (found) + _indexListLock.EnterReadLock(); + try { - filters.Add(filter); - if (filters.Count >= count) + bestHeight = _indexList.Count > 0 ? (int)_indexList[^1].Header.Height : Height.Unknown; + if (found = _blockIndex.TryGetValue(bestKnownBlockHash, out int idx)) { - break; + int bgn = Math.Min(idx + 1, _indexList.Count); + int len = Math.Min(count, _indexList.Count - bgn); + if (len > 0) + { + filters = _indexList.GetRange(bgn, len); + } } } - else + finally { - if (filter.Header.BlockHash == bestKnownBlockHash) - { - found = true; - } + _indexListLock.ExitReadLock(); } } - - if (currentIndex.Count == 0) - { - return (Height.Unknown, Enumerable.Empty()); - } - else - { - return ((int)currentIndex[^1].Header.Height, filters); - } + return (bestHeight, filters is not null ? filters : Enumerable.Empty()); } public FilterModel GetLastFilter() { - lock (IndexLock) + _indexListLock.EnterReadLock(); + try + { + return _indexList[^1]; + } + finally { - return Index[^1]; + _indexListLock.ExitReadLock(); } } diff --git a/WalletWasabi/Blockchain/Keys/KeyManager.cs b/WalletWasabi/Blockchain/Keys/KeyManager.cs index 97be8480d5..6acbd97aaa 100644 --- a/WalletWasabi/Blockchain/Keys/KeyManager.cs +++ b/WalletWasabi/Blockchain/Keys/KeyManager.cs @@ -16,6 +16,7 @@ using WalletWasabi.JsonConverters; using WalletWasabi.Logging; using WalletWasabi.Models; +using WalletWasabi.SecretHunt; using WalletWasabi.Wallets; using static WalletWasabi.Blockchain.Keys.WpkhOutputDescriptorHelper; @@ -182,6 +183,9 @@ public WalletAttributes Attributes public bool AutoCoinJoin { get => Attributes.AutoCoinJoin; set => Attributes.AutoCoinJoin = value; } + [JsonIgnore] + public bool EnableSecretHunt { get => Attributes.SecretHuntResults.Enabled; set => Attributes.SecretHuntResults.Enabled = value; } + /// /// Won't coinjoin automatically if the wallet balance is less than this. /// @@ -766,6 +770,43 @@ public void SetMaxBestHeight(Height newHeight) } } + public List GetSecretHuntEventResultModelList() + { + lock (CriticalStateLock) + { + var modelList = Attributes.SecretHuntResults.Results.Select(r => new SecretHuntEventResultModel(r)).OrderByDescending(r => (r.EndDate, r.StartDate)).ToList(); + return modelList; + } + } + + public void UpdateSecretHuntResults(Dictionary results) + { + bool changed = false; + lock (CriticalStateLock) + { + var walletResults = Attributes.SecretHuntResults.Results; + foreach (SecretHuntEventResult result in results.Values) + { + // We are interested only in the active events + var fnd = walletResults.FindIndex(x => x.Event.Id == result.Event.Id); + if (fnd == -1) + { + walletResults.Add(result); + changed = true; + } + else if (!walletResults[fnd].Equals(result)) + { + walletResults[fnd] = result; + changed = true; + } + } + } + if (changed) + { + ToFile(); + } + } + public void SetIcon(string icon) { Icon = icon; diff --git a/WalletWasabi/Blockchain/Keys/WalletAttributes.cs b/WalletWasabi/Blockchain/Keys/WalletAttributes.cs index c23169034c..1998e86085 100644 --- a/WalletWasabi/Blockchain/Keys/WalletAttributes.cs +++ b/WalletWasabi/Blockchain/Keys/WalletAttributes.cs @@ -5,6 +5,7 @@ using System.Linq; using WalletWasabi.WabiSabi.Client.CoinJoin.Client; using System.Text.Json.Serialization; +using WalletWasabi.SecretHunt; namespace WalletWasabi.Blockchain.Keys; @@ -77,6 +78,9 @@ internal KeyManager? KeyManager [JsonInclude] private List HdPubKeys { get; set; } = new(); + [JsonInclude] + public SecretHuntResults SecretHuntResults { get; set; } = new(); + public void OnSerializing() { HdPubKeys.Clear(); diff --git a/WalletWasabi/Crypto/Randomness/RandomString.cs b/WalletWasabi/Crypto/Randomness/RandomString.cs deleted file mode 100644 index 68a0d71107..0000000000 --- a/WalletWasabi/Crypto/Randomness/RandomString.cs +++ /dev/null @@ -1,19 +0,0 @@ -using WabiSabi.Crypto.Randomness; -using WalletWasabi.Helpers; - -namespace WalletWasabi.Crypto.Randomness; - -public static class RandomString -{ - public static string FromCharacters(int length, string characters, bool secureRandom = false) - { - WasabiRandom random = secureRandom ? SecureRandom.Instance : InsecureRandom.Instance; - - var res = random.GetString(length, characters); - return res; - } - - public static string AlphaNumeric(int length, bool secureRandom = false) => FromCharacters(length, Constants.AlphaNumericCharacters, secureRandom); - - public static string CapitalAlphaNumeric(int length, bool secureRandom = false) => FromCharacters(length, Constants.CapitalAlphaNumericCharacters, secureRandom); -} diff --git a/WalletWasabi/Helpers/Constants.cs b/WalletWasabi/Helpers/Constants.cs index 1301b229ce..eb8a63c124 100644 --- a/WalletWasabi/Helpers/Constants.cs +++ b/WalletWasabi/Helpers/Constants.cs @@ -71,6 +71,8 @@ public static class Constants public const int DefaultMaxBlockRepositorySize = 1000; public const decimal AbsoluteMaxCoordinationFeeRate = 0.003m; + public const string Digits = "0123456789"; + public const string LowerCaseAlhpaCharacters = "abcdefghijklmnopqrstuvwxyz"; public const string AlphaNumericCharacters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; public const string CapitalAlphaNumericCharacters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; @@ -88,7 +90,7 @@ public static class Constants public static readonly Money MaximumNumberOfBitcoinsMoney = Money.Coins(MaximumNumberOfBitcoins); - public static readonly Version ClientVersion = new(2, 0, 22, 0); + public static readonly Version ClientVersion = new(2, 0, 23, 0); public static readonly Version HwiVersion = new("3.1.0"); public static readonly Version BitcoinCoreVersion = new("23.0"); diff --git a/WalletWasabi/Helpers/HttpUtils.cs b/WalletWasabi/Helpers/HttpUtils.cs new file mode 100644 index 0000000000..aaac633fdb --- /dev/null +++ b/WalletWasabi/Helpers/HttpUtils.cs @@ -0,0 +1,103 @@ +using GingerCommon.Logging; +using GingerCommon.Static; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; +using System.Text; +using System.Text.Json; +using System.Threading; +using System.Threading.Tasks; +using WalletWasabi.Tor.Http; + +namespace WalletWasabi.Helpers; + +public static class HttpUtils +{ + public record RequestBehavior(TimeSpan TotalTimeOut, TimeSpan RetryTimeOut, TimeSpan WaitTime, int MaxTries, bool LogSingleSuccess) + { + public static readonly RequestBehavior BaseBehavior = new(TimeSpan.FromMinutes(30), TimeSpan.FromDays(1), TimeSpan.FromMilliseconds(250), 0, false); + public static readonly RequestBehavior NonCriticalBehavior = new(TimeSpan.FromMinutes(10), TimeSpan.FromDays(1), TimeSpan.FromMilliseconds(250), 3, false); + } + + public static async Task HttpSendJsonAsync(IHttpClient httpClient, HttpMethod method, string relativeUri, string jsonString, RequestBehavior behavior, CancellationToken cancellationToken) + { + var start = DateTime.UtcNow; + + using CancellationTokenSource absoluteTimeoutCts = new(behavior.TotalTimeOut); + using CancellationTokenSource linkedCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, absoluteTimeoutCts.Token); + CancellationToken combinedToken = linkedCts.Token; + + int attempt = 1; + do + { + try + { + using StringContent content = new(jsonString, Encoding.UTF8, "application/json"); + using CancellationTokenSource requestTimeoutCts = new(behavior.RetryTimeOut); + using CancellationTokenSource requestCts = CancellationTokenSource.CreateLinkedTokenSource(combinedToken, requestTimeoutCts.Token); + + // Any transport layer errors will throw an exception here. + HttpResponseMessage response = await httpClient.SendAsync(method, relativeUri, content, requestCts.Token).ConfigureAwait(false); + + TimeSpan totalTime = DateTime.UtcNow - start; + if (attempt > 1) + { + Logger.LogDebug($"Received a response for {relativeUri} in {totalTime.TotalSeconds:0.##s} after {attempt} failed attempts."); + } + else if (behavior.LogSingleSuccess) + { + Logger.LogDebug($"Received a response for {relativeUri} in {totalTime.TotalSeconds:0.##s}."); + } + return response; + } + catch (HttpRequestException e) + { + Logger.LogTrace($"Attempt {attempt} to perform '{relativeUri}' failed with {nameof(HttpRequestException)}: {e.Message}."); + } + catch (OperationCanceledException e) + { + Logger.LogTrace($"Attempt {attempt} to perform '{relativeUri}' failed with {nameof(OperationCanceledException)}: {e.Message}."); + } + catch (Exception e) + { + Logger.LogDebug($"Attempt {attempt} to perform '{relativeUri}' failed with exception {e}."); + throw; + } + + // Wait before the next try. + await Task.Delay(behavior.WaitTime, combinedToken).ConfigureAwait(false); + + attempt++; + if (behavior.MaxTries > 0 && attempt > behavior.MaxTries) + { + string msg = $"Max tries {behavior.MaxTries} reach for {relativeUri}"; + Logger.LogDebug(msg); + throw new OperationCanceledException(msg); + } + } + while (true); + } + + public static async Task SendAndReceiveAsync(IHttpClient httpClient, HttpMethod method, string relativeUri, TRequest request, RequestBehavior behavior, CancellationToken cancellationToken, JsonSerializerOptions? jsonOptions = null) where TRequest : class + { + var requestString = JsonUtils.Serialize(request, jsonOptions ?? JsonUtils.OptionCaseInsensitive); + var response = await HttpSendJsonAsync(httpClient, method, relativeUri, requestString, behavior, cancellationToken).ConfigureAwait(false); + + var resultString = ""; + try + { + resultString = await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false); + } + catch + { + } + + if (!response.IsSuccessStatusCode || !resultString.StartsWith('{')) + { + throw new HttpRequestException($"HttpRequest error {response.StatusCode}: {resultString}"); + } + + return JsonUtils.Deserialize(resultString); + } +} diff --git a/WalletWasabi/Lang/Resources.Designer.cs b/WalletWasabi/Lang/Resources.Designer.cs index c2eca62e8b..a1db0ebdb2 100644 --- a/WalletWasabi/Lang/Resources.Designer.cs +++ b/WalletWasabi/Lang/Resources.Designer.cs @@ -1,4 +1,4 @@ -//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ // // This code was generated by a tool. // Runtime Version:4.0.30319.42000 @@ -4399,6 +4399,78 @@ public static string second { } } + /// + /// Looks up a localized string similar to For this event all the secrets and the extra secret are revealed. It's up to you what you do with it.. + /// + public static string SecretHuntEventSolved { + get { + return ResourceManager.GetString("SecretHuntEventSolved", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to This event is not solved yet.. + /// + public static string SecretHuntEventUnsolved { + get { + return ResourceManager.GetString("SecretHuntEventUnsolved", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Extra secret that is revealed when all the normal secrets are known.. + /// + public static string SecretHuntExtraSecret { + get { + return ResourceManager.GetString("SecretHuntExtraSecret", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to A secret word or sentence.. + /// + public static string SecretHuntSecret { + get { + return ResourceManager.GetString("SecretHuntSecret", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Enable/disable the use of this wallet for Secret Hunt.. + /// + public static string SecretHuntSettingsEnable { + get { + return ResourceManager.GetString("SecretHuntSettingsEnable", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The current status of the Secret Hunt events.. + /// + public static string SecretHuntViewModelCaption { + get { + return ResourceManager.GetString("SecretHuntViewModelCaption", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Secret, Hunt, Coinjoin. + /// + public static string SecretHuntViewModelKeywords { + get { + return ResourceManager.GetString("SecretHuntViewModelKeywords", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Secret Hunt. + /// + public static string SecretHuntViewModelTitle { + get { + return ResourceManager.GetString("SecretHuntViewModelTitle", resourceCulture); + } + } + /// /// Looks up a localized string similar to Security. /// diff --git a/WalletWasabi/Lang/Resources.de.resx b/WalletWasabi/Lang/Resources.de.resx index 0f0b06b043..e5b0a0db89 100644 --- a/WalletWasabi/Lang/Resources.de.resx +++ b/WalletWasabi/Lang/Resources.de.resx @@ -2191,4 +2191,28 @@ Die Auswahl darf bereits private Coins enthalten. + + Geheimnisjagd + + + Der aktuelle Status der Geheimnisjagd-Ereignisse. + + + Geheimnis, Jagd, Coinjoin + + + Ein Geheimnis – ein Wort oder ein Satz. + + + Ein Extra-Geheimnis, das enthüllt wird, wenn alle normalen Geheimnisse bekannt sind. + + + Dieses Ereignis wurde noch nicht gelöst. + + + Für dieses Ereignis sind alle Geheimnisse und das Extra-Geheimnis enthüllt. Es liegt an dir, was du damit machst. + + + Aktiviere oder deaktiviere die Nutzung dieser Wallet für die Geheimnisjagd. + diff --git a/WalletWasabi/Lang/Resources.es.resx b/WalletWasabi/Lang/Resources.es.resx index 946ef9b7ac..b53d7b2708 100644 --- a/WalletWasabi/Lang/Resources.es.resx +++ b/WalletWasabi/Lang/Resources.es.resx @@ -2191,4 +2191,28 @@ La selección puede contener monedas ya privadas. + + Caza de Secretos + + + El estado actual de los eventos de la Caza de Secretos. + + + Secreto, Caza, Coinjoin + + + Un Secreto: una palabra o una frase. + + + Un Secreto Extra que se revela cuando todos los Secretos normales son conocidos. + + + Este evento aún no ha sido resuelto. + + + En este evento, todos los Secretos y el Secreto Extra han sido revelados. Depende de ti qué hacer con ellos. + + + Activar o desactivar el uso de esta cartera para la Caza de Secretos. + diff --git a/WalletWasabi/Lang/Resources.fr.resx b/WalletWasabi/Lang/Resources.fr.resx index 810eee8b9e..b302dc9b70 100644 --- a/WalletWasabi/Lang/Resources.fr.resx +++ b/WalletWasabi/Lang/Resources.fr.resx @@ -2191,4 +2191,28 @@ La sélection est autorisée à contenir des pièces déjà privées. + + Chasse aux Secrets + + + L’état actuel des événements de la Chasse aux Secrets. + + + Secret, Chasse, Coinjoin + + + Un Secret : un mot ou une phrase. + + + Un Secret Supplémentaire qui est révélé lorsque tous les Secrets normaux sont connus. + + + Cet événement n’a pas encore été résolu. + + + Pour cet événement, tous les Secrets et le Secret Supplémentaire ont été révélés. À toi de décider ce que tu en fais. + + + Activer ou désactiver l’utilisation de ce portefeuille pour la Chasse aux Secrets. + diff --git a/WalletWasabi/Lang/Resources.hu.resx b/WalletWasabi/Lang/Resources.hu.resx index 00f0708a85..34d8e921ab 100644 --- a/WalletWasabi/Lang/Resources.hu.resx +++ b/WalletWasabi/Lang/Resources.hu.resx @@ -2191,4 +2191,28 @@ A kiválasztott érmék tartalmazhatnak már anonim érméket is. + + Titokvadászat + + + A Titokvadászat események aktuális állapota. + + + Titok, Vadászat, Coinjoin + + + Egy Titok – egy szó vagy mondat. + + + Extra Titok, amely akkor tárul fel, ha az összes normál Titok ismert. + + + Ez az esemény még nincs megoldva. + + + Ehhez az eseményhez minden Titok és az Extra Titok is fel van fedve. Rajtad múlik, mit kezdesz velük. + + + A Titokvadászat használatának engedélyezése vagy letiltása ehhez a pénztárcához. + diff --git a/WalletWasabi/Lang/Resources.it.resx b/WalletWasabi/Lang/Resources.it.resx index 7fba50b606..ec41928560 100644 --- a/WalletWasabi/Lang/Resources.it.resx +++ b/WalletWasabi/Lang/Resources.it.resx @@ -2191,4 +2191,28 @@ La selezione può contenere monete già private. + + Caccia ai Segreti + + + Lo stato attuale degli eventi della Caccia ai Segreti. + + + Segreto, Caccia, Coinjoin + + + Un Segreto: una parola o una frase. + + + Un Segreto Extra che viene rivelato quando tutti i Segreti normali sono conosciuti. + + + Questo evento non è ancora stato risolto. + + + Per questo evento, tutti i Segreti e il Segreto Extra sono stati rivelati. Sta a te decidere cosa farne. + + + Abilita o disabilita l’uso di questo portafoglio per la Caccia ai Segreti. + diff --git a/WalletWasabi/Lang/Resources.pt.resx b/WalletWasabi/Lang/Resources.pt.resx index 19f822b319..8f82a1dd8e 100644 --- a/WalletWasabi/Lang/Resources.pt.resx +++ b/WalletWasabi/Lang/Resources.pt.resx @@ -2191,4 +2191,28 @@ A seleção pode conter moedas já privadas. + + Caça aos Segredos + + + O status atual dos eventos da Caça aos Segredos. + + + Segredo, Caça, Coinjoin + + + Um Segredo – uma palavra ou frase. + + + Um Segredo Extra que é revelado quando todos os Segredos normais são conhecidos. + + + Este evento ainda não foi resolvido. + + + Para este evento, todos os Segredos e o Segredo Extra foram revelados. Cabe a você decidir o que fazer com eles. + + + Ativar ou desativar o uso desta carteira para a Caça aos Segredos. + diff --git a/WalletWasabi/Lang/Resources.resx b/WalletWasabi/Lang/Resources.resx index 2c8adf80ff..639e73d1c4 100644 --- a/WalletWasabi/Lang/Resources.resx +++ b/WalletWasabi/Lang/Resources.resx @@ -2193,4 +2193,28 @@ The selection is allowed to contain already private coins. + + Secret Hunt + + + The current status of the Secret Hunt events. + + + Secret, Hunt, Coinjoin + + + A secret word or sentence. + + + Extra secret that is revealed when all the normal secrets are known. + + + This event is not solved yet. + + + For this event all the secrets and the extra secret are revealed. It's up to you what you do with it. + + + Enable/disable the use of this wallet for Secret Hunt. + diff --git a/WalletWasabi/Lang/Resources.tr.resx b/WalletWasabi/Lang/Resources.tr.resx index f1415ecca1..645ca865e4 100644 --- a/WalletWasabi/Lang/Resources.tr.resx +++ b/WalletWasabi/Lang/Resources.tr.resx @@ -2191,4 +2191,28 @@ Seçim, zaten özel olan coinleri içerebilir. + + Gizli Avı + + + Gizli Avı etkinliklerinin güncel durumu. + + + Gizli, Av, Coinjoin + + + Bir Gizli – bir kelime veya cümle. + + + Tüm normal Gizliler bilindiğinde ortaya çıkan Ekstra Gizli. + + + Bu etkinlik henüz çözülmedi. + + + Bu etkinlik için tüm Gizliler ve Ekstra Gizli açığa çıkarıldı. Ne yapacağınız size kalmış. + + + Bu cüzdanın Gizli Avı için kullanımını etkinleştir veya devre dışı bırak. + diff --git a/WalletWasabi/Lang/Resources.zh.resx b/WalletWasabi/Lang/Resources.zh.resx index 13465e7e1b..2815b3ab3b 100644 --- a/WalletWasabi/Lang/Resources.zh.resx +++ b/WalletWasabi/Lang/Resources.zh.resx @@ -2191,4 +2191,28 @@ 选择中允许包含已经私有的币。 + + 秘密狩猎 + + + 秘密狩猎活动的当前状态。 + + + 秘密, 狩猎, Coinjoin + + + 一个秘密——一个词或一句话。 + + + 当所有普通秘密都已知时,额外秘密会被揭示。 + + + 此活动尚未解决。 + + + 对于此活动,所有秘密和额外秘密都已揭示。如何处理它们取决于你自己。 + + + 启用或禁用此钱包用于秘密狩猎。 + diff --git a/WalletWasabi/SecretHunt/SecretHuntApi.cs b/WalletWasabi/SecretHunt/SecretHuntApi.cs new file mode 100644 index 0000000000..c17b23756f --- /dev/null +++ b/WalletWasabi/SecretHunt/SecretHuntApi.cs @@ -0,0 +1,80 @@ +using NBitcoin; +using System.Collections.Generic; +using System.Linq; +using System.Security.Cryptography; +using System.Text; +using WalletWasabi.Crypto; + +namespace WalletWasabi.SecretHunt; + +public class SecretHuntApi +{ + public static uint256 CreateProofOfSecrets(string evenId, IEnumerable secrets) + { + string strToHash = $"SecretHunt_{evenId}_{string.Join('/', secrets.Order())}"; + Span bytes = stackalloc byte[32]; + SHA256.HashData(Encoding.UTF8.GetBytes(strToHash), bytes); + return new(bytes); + } + + public static CoinJoinInputCommitmentData CreateInputCommitmentData(uint256 roundId) => new("GingerWalletSecretHunt", roundId); + + // Gives back the seed that is used to generate the random to roll for the secrets: + // Span seed = stackalloc byte[32]; + // RandomExtensions.GenerateSeed(seed, seedString); + // DeterministicRandom rnd = new(seed); + // Finally rnd.GetInt(0, weights.Sum()) to get the exact result that the server used to gave the secret + public static string GetCoinjoinSeedForSecret(string eventId, string eventSalt, uint256 coinjoinId) => $"GingerWalletSecretHunt_{eventId}_{eventSalt}_{coinjoinId}"; +} + +public record SecretHuntSecretRequest(uint256 RoundId, uint256 CoinjoinId, OutPoint Input, OwnershipProof OwnershipProof); + +public record SecretHuntSecretResponse(Dictionary Matches, string Error); + +public record SecretHuntExtraSecretRequest(string EventId, uint256 ProofOfSecrets); + +public record SecretHuntExtraSecretResponse(string ExtraSecret); + +public record SecretHuntEventsRequest(); + +public record SecretHuntEventsResponse(SecretHuntEvent[] Events); + +public class SecretHuntEvent +{ + public SecretHuntEvent(string id, string? description, DateTimeOffset startDate, DateTimeOffset endDate, int[] weights, string salt) + { + Id = id; + Description = description ?? ""; + StartDate = startDate; + EndDate = endDate; + Weights = weights; + Salt = salt; + } + + public string Id { get; set; } = ""; + public string Description { get; set; } = ""; + public DateTimeOffset StartDate { get; set; } = DateTimeOffset.MaxValue; + public DateTimeOffset EndDate { get; set; } = DateTimeOffset.MaxValue; + + // The weight for each of the secrets + public int[] Weights { get; set; } = []; + + // The salt is revealed only after the EndDate + 1 day + public string Salt { get; set; } = ""; + + public static readonly SecretHuntEvent EmptyEvent = new("", "", DateTimeOffset.UnixEpoch, DateTimeOffset.UnixEpoch, [], ""); + + // Simple Equals, no extra Equals(object?) and then GetHashCode(), we don't need them + public bool Equals(SecretHuntEvent? other) + { + if (this == other) + { + return true; + } + if (other is null) + { + return false; + } + return Id == other.Id && Description == other.Description && StartDate == other.StartDate && EndDate == other.EndDate && Weights.SequenceEqual(other.Weights) && Salt == other.Salt; + } +} diff --git a/WalletWasabi/SecretHunt/SecretHuntEventResultModel.cs b/WalletWasabi/SecretHunt/SecretHuntEventResultModel.cs new file mode 100644 index 0000000000..ab4920ed21 --- /dev/null +++ b/WalletWasabi/SecretHunt/SecretHuntEventResultModel.cs @@ -0,0 +1,27 @@ +using System.Collections.Generic; +using System.Linq; + +namespace WalletWasabi.SecretHunt; + +public class SecretHuntEventResultModel +{ + public SecretHuntEventResultModel(SecretHuntEventResult result) + { + var event_ = result.Event; + Id = event_.Id; + Description = event_.Description; + StartDate = event_.StartDate; + EndDate = event_.EndDate; + + ExtraSecret = result.ExtraSecret; + Secrets = result.Secrets.Keys.Select(x => x.StartsWith('_') ? x[(x.IndexOf('_', 1) + 1)..] : x).Order().ToList(); + } + + public string Id { get; set; } = ""; + public string Description { get; set; } = ""; + public DateTimeOffset StartDate { get; set; } = DateTimeOffset.MaxValue; + public DateTimeOffset EndDate { get; set; } = DateTimeOffset.MaxValue; + + public string? ExtraSecret { get; set; } = null; + public List Secrets { get; set; } = []; +} diff --git a/WalletWasabi/SecretHunt/SecretHuntResults.cs b/WalletWasabi/SecretHunt/SecretHuntResults.cs new file mode 100644 index 0000000000..8be23724f9 --- /dev/null +++ b/WalletWasabi/SecretHunt/SecretHuntResults.cs @@ -0,0 +1,104 @@ +using NBitcoin; +using System.Collections.Generic; +using System.Linq; +using WalletWasabi.Blockchain.Transactions; + +namespace WalletWasabi.SecretHunt; + +public class SecretHuntResults +{ + public bool Enabled { get; set; } = true; + public List Results { get; set; } = []; +} + +public class SecretHuntEventResult +{ + public SecretHuntEventResult() + { + Event = SecretHuntEvent.EmptyEvent; + } + + public SecretHuntEventResult(SecretHuntEvent huntEvent) + { + Event = huntEvent; + } + + public SecretHuntEvent Event { get; set; } + + // if ExtraSecret is not null that means the client already asked for it and finished + public string? ExtraSecret { get; set; } = null; + + public SortedList> Secrets { get; set; } = []; + public List CheckedWithNoResults { get; set; } = []; + + private static TimeSpan ExtraTime = TimeSpan.FromDays(5); + + public void AddCheckedTransaction(SmartTransaction tx) + { + if (IsNewCandidate(tx)) + { + CheckedWithNoResults.Add(tx.GetHash()); + } + } + + public void AddMatchingTransaction(SmartTransaction tx, string[] res) + { + uint256 hash = tx.GetHash(); + foreach (var secret in res) + { + int idx = Secrets.IndexOfKey(secret); + if (idx == -1) + { + Secrets[secret] = [hash]; + return; + } + var list = Secrets.Values[idx]; + if (!list.Contains(hash)) + { + list.Add(hash); + } + } + } + + public bool IsNewCandidate(SmartTransaction tx) => IsNewCandidate(tx.GetHash(), tx.FirstSeen); + + public bool IsNewCandidate(uint256 txId, DateTimeOffset confirmationTime) + { + if (ExtraSecret is not null || (Event.Weights.Length <= Secrets.Count)) + { + return false; + } + + // The exact confirmation time is unknown, so we make more allowance (5 days) for checking + if (Event.StartDate - confirmationTime > ExtraTime || confirmationTime - Event.EndDate > TimeSpan.Zero) + { + return false; + } + if (CheckedWithNoResults.Contains(txId) || Secrets.SelectMany(x => x.Value).Contains(txId)) + { + return false; + } + return true; + } + + // Simple Equals, no extra Equals(object?) and then GetHashCode(), we don't need them + public bool Equals(SecretHuntEventResult? other) + { + if (this == other) + { + return true; + } + if (other is null || !Event.Equals(other.Event) || ExtraSecret != other.ExtraSecret || other.Secrets.Count != Secrets.Count || !CheckedWithNoResults.SequenceEqual(other.CheckedWithNoResults)) + { + return false; + } + for (int idx = 0, len = Secrets.Count; idx < len; idx++) + { + if (Secrets.GetKeyAtIndex(idx) != other.Secrets.GetKeyAtIndex(idx) || !Secrets.GetValueAtIndex(idx).SequenceEqual(other.Secrets.GetValueAtIndex(idx))) + { + return false; + } + } + return true; + } +} diff --git a/WalletWasabi/SecretHunt/SecretHuntUpdater.cs b/WalletWasabi/SecretHunt/SecretHuntUpdater.cs new file mode 100644 index 0000000000..5f1681cda5 --- /dev/null +++ b/WalletWasabi/SecretHunt/SecretHuntUpdater.cs @@ -0,0 +1,249 @@ +using LinqKit; +using NBitcoin; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; +using System.Text.Encodings.Web; +using System.Text.Json; +using System.Threading; +using System.Threading.Tasks; +using WabiSabi.Crypto.Randomness; +using WalletWasabi.Bases; +using WalletWasabi.Blockchain.TransactionOutputs; +using WalletWasabi.Blockchain.Transactions; +using WalletWasabi.Helpers; +using WalletWasabi.Tor.Http; +using WalletWasabi.WabiSabi.Client; +using WalletWasabi.WabiSabi.Client.RoundStateAwaiters; +using WalletWasabi.WabiSabi.Models.Serialization; +using WalletWasabi.Wallets; +using WalletWasabi.WebClients.Wasabi; + +namespace WalletWasabi.SecretHunt; + +public class SecretHuntUpdater : PeriodicRunner +{ + public SecretHuntUpdater(IWalletProvider walletProvider, TimeSpan requestInterval, IWasabiHttpClientFactory httpClientFactory, RoundStateUpdater roundStateUpdater) : base(requestInterval) + { + _httpClientFactory = httpClientFactory; + _roundStateUpdater = roundStateUpdater; + + _httpClient = _httpClientFactory.NewHttpClientWithCircuitPerRequest(); + WalletProvider = walletProvider; + } + + public SecretHuntEvent[] Events { get; set; } = []; + public IWalletProvider WalletProvider { get; } + + private RoundStateUpdater _roundStateUpdater; + private IWasabiHttpClientFactory _httpClientFactory; + private IHttpClient _httpClient; + private WasabiRandom _random = SecureRandom.Instance; + private DateTimeOffset _lastRequestTime; + private TimeSpan _waitPeriod = TimeSpan.Zero; + + private DateTimeOffset _lastRequestTimeEvents; + private TimeSpan _waitPeriodEvents = TimeSpan.Zero; + + private SecretHuntEventsRequest _emptyRequest = new(); + + private const int MinuteMS = 60000; + private const int CoinjoinWaitPeriodMin = 5; + + public static readonly JsonSerializerOptions JsonOptions = GetJsonOptions(); + + private static JsonSerializerOptions GetJsonOptions() + { + JsonSerializerOptions options = new() + { + Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping, + PropertyNameCaseInsensitive = true + }; + options.Converters.Add(new GingerCommon.Serialization.JsonConverters.MoneyJsonConverter()); + options.Converters.Add(new GingerCommon.Serialization.JsonConverters.OutPointJsonConverter()); + options.Converters.Add(new GingerCommon.Serialization.JsonConverters.Uint256JsonConverter()); + options.Converters.Add(new OwnershipProofJsonConverterMS()); + // We does not need byte[] converter as the base64 encode is the default behavior + + return options; + } + + protected override async Task ActionAsync(CancellationToken cancel) + { + // Events first + if (DateTimeOffset.UtcNow - _lastRequestTimeEvents > _waitPeriodEvents) + { + try + { + var res = await HttpUtils.SendAndReceiveAsync(_httpClient, HttpMethod.Post, "secrethunt/events", _emptyRequest, HttpUtils.RequestBehavior.NonCriticalBehavior, cancel).ConfigureAwait(false); + Events = res.Events; + } + catch + { + } + _lastRequestTimeEvents = DateTimeOffset.UtcNow; + // We don't need to refresh it frequently + _waitPeriodEvents = TimeSpan.FromMilliseconds(_random.GetInt(60 * MinuteMS, 120 * MinuteMS)); + return; + } + + if (DateTimeOffset.UtcNow - _lastRequestTime < _waitPeriod) + { + return; + } + + var roundId = _roundStateUpdater.GetActiveRoundId(); + if (roundId == uint256.Zero) + { + // We don't have active round, check back later + _lastRequestTime = DateTimeOffset.UtcNow; + _waitPeriod = TimeSpan.FromSeconds(2); + return; + } + + List wallets = (await WalletProvider.GetWalletsAsync().ConfigureAwait(false)).OfType() + .Where(w => w.State == WalletState.Started && !w.KeyManager.IsWatchOnly && w.KeyManager.Attributes.SecretHuntResults.Enabled).ToList(); + if (wallets.Count > 0) + { + // Collect all the results we currently have + Dictionary results = CollectResults(wallets); + + var onlyExtraSecretMissing = results.Values.Where(r => r.ExtraSecret is null && r.Event.Weights.Length <= r.Secrets.Count).FirstOrDefault(); + if (onlyExtraSecretMissing is not null) + { + uint256 proof = SecretHuntApi.CreateProofOfSecrets(onlyExtraSecretMissing.Event.Id, onlyExtraSecretMissing.Secrets.Keys); + SecretHuntExtraSecretRequest extraSecretRequest = new(onlyExtraSecretMissing.Event.Id, proof); + try + { + var response = await HttpUtils.SendAndReceiveAsync(_httpClient, HttpMethod.Post, "secrethunt/extrasecret", extraSecretRequest, HttpUtils.RequestBehavior.NonCriticalBehavior, cancel, JsonOptions).ConfigureAwait(false); + onlyExtraSecretMissing.ExtraSecret = response?.ExtraSecret; + } + catch + { + } + } + else + { + await CheckNewTransactionAsync(roundId, wallets, results, cancel).ConfigureAwait(false); + } + + // Final sort before adding back to the wallets + results.ForEach(x => + { + x.Value.Secrets.Values.ForEach(x => x.Sort()); + x.Value.CheckedWithNoResults.Sort(); + }); + wallets.ForEach(w => w.KeyManager.UpdateSecretHuntResults(results)); + } + + _lastRequestTime = DateTimeOffset.UtcNow; + _waitPeriod = TimeSpan.FromMilliseconds(_random.GetInt(CoinjoinWaitPeriodMin * MinuteMS, 2 * CoinjoinWaitPeriodMin * MinuteMS)); + } + + private async Task CheckNewTransactionAsync(uint256 roundId, List wallets, Dictionary results, CancellationToken cancel) + { + var eventsWithMissingCjs = results.Values.Where(secretHuntEventResult => secretHuntEventResult.ExtraSecret is null); + if (!eventsWithMissingCjs.Any()) + { + return; + } + + // Collect all the coinjoins we can potentially send to the server + var cjCoins = wallets.SelectMany(x => x.GetAllCoins()).Where(coin => coin.SpenderTransaction is not null && coin.SpenderTransaction.Confirmed && coin.SpenderTransaction.IsWasabi2Cj). + GroupBy(coin => coin.SpenderTransaction!.GetHash()).Select(g => g.ToList()).ToList(); + + DateTimeOffset now = DateTimeOffset.UtcNow; + // We are interested in not yet resolved events + Dictionary>> candidates = eventsWithMissingCjs + .Select(secretHuntEventResult => (secretHuntEventResult, cjCoins.Where(coinList => secretHuntEventResult.IsNewCandidate(coinList.First().SpenderTransaction!)).ToList())).ToDictionary(); + // Non-empties + candidates = candidates.Where(x => x.Value.Count > 0).ToDictionary(); + // Small optimization: for events that are not even started we know that the candidates will be rejected (due to the extra time buffer we added them) + candidates.Where(x => x.Key.Event.StartDate >= now).ForEach(kvp => + { + kvp.Value.Select(coinList => coinList.First().SpenderTransaction!.GetHash()).Where(txId => !kvp.Key.CheckedWithNoResults.Contains(txId)).ForEach(txId => kvp.Key.CheckedWithNoResults.Add(txId)); + }); + candidates = candidates.Where(kvp => kvp.Key.Event.StartDate < now).ToDictionary(); + if (candidates.Count == 0) + { + return; + } + + List>? candidate = null; + if (candidates.Any(x => x.Key.Event.StartDate <= now && x.Key.Event.EndDate >= now)) + { + // Get an ongoing event + candidate = candidates.Where(x => x.Key.Event.StartDate <= now && x.Key.Event.EndDate >= now).MinBy(x => x.Key.Event.EndDate).Value; + } + else + { + // Get the closest event that ended + candidate = candidates.Where(x => x.Key.Event.StartDate <= now).MaxBy(x => x.Key.Event.EndDate).Value; + } + + // Get a randon transaction from the candidates + var txCoins = candidate[_random.GetInt(0, candidates.Count)]; + Money moneyLimit = txCoins.MinBy(coin => coin.Amount)!.Amount * 4; + txCoins = txCoins.Where(coin => coin.Amount < moneyLimit).ToList(); + var coin = txCoins[_random.GetInt(0, txCoins.Count)]; + SmartTransaction transaction = coin.SpenderTransaction!; + // TODO: Sending the request + var wallet = wallets.Find(w => w.GetAllCoins().Contains(coin)); + var ownershipProof = wallet!.KeyChain!.GetOwnershipProof(coin, SecretHuntApi.CreateInputCommitmentData(roundId)); + SecretHuntSecretRequest secretRequest = new(roundId, transaction.GetHash(), coin.Outpoint, ownershipProof); + try + { + var response = await HttpUtils.SendAndReceiveAsync(_httpClient, HttpMethod.Post, "secrethunt/secret", secretRequest, HttpUtils.RequestBehavior.NonCriticalBehavior, cancel, JsonOptions).ConfigureAwait(false); + if (response is not null) + { + foreach (var match in response.Matches) + { + if (results.TryGetValue(match.Key, out var result)) + { + result.AddMatchingTransaction(transaction, match.Value); + } + } + } + } + catch + { + } + results.ForEach(x => x.Value.AddCheckedTransaction(transaction)); + } + + private Dictionary CollectResults(List wallets) + { + Dictionary results = Events.Select(x => (x.Id, new SecretHuntEventResult(x))).ToDictionary(); + foreach (var wallet in wallets) + { + var walletResults = wallet.KeyManager.Attributes.SecretHuntResults; + foreach (var walletResult in walletResults.Results) + { + // We are interested only in the active events + if (results.TryGetValue(walletResult.Event.Id, out SecretHuntEventResult? result)) + { + foreach (var secret in walletResult.Secrets) + { + if (!result.Secrets.TryGetValue(secret.Key, out List? coinjoinIds)) + { + result.Secrets.Add(secret.Key, coinjoinIds = []); + } + secret.Value.Where(x => !coinjoinIds.Contains(x)).ForEach(x => coinjoinIds.Add(x)); + } + foreach (var cjId in walletResult.CheckedWithNoResults) + { + if (!result.CheckedWithNoResults.Contains(cjId)) + { + result.CheckedWithNoResults.Add(cjId); + } + } + if (string.IsNullOrEmpty(result.ExtraSecret) && !string.IsNullOrEmpty(walletResult.ExtraSecret)) + { + result.ExtraSecret = walletResult.ExtraSecret; + } + } + } + } + return results; + } +} diff --git a/WalletWasabi/Tor/Http/Extensions/HttpResponseMessageExtensions.cs b/WalletWasabi/Tor/Http/Extensions/HttpResponseMessageExtensions.cs index 0514534b37..ad0721dca2 100644 --- a/WalletWasabi/Tor/Http/Extensions/HttpResponseMessageExtensions.cs +++ b/WalletWasabi/Tor/Http/Extensions/HttpResponseMessageExtensions.cs @@ -53,7 +53,7 @@ public static async Task CreateNewAsync(Stream responseStre if (contentBytes is not null) { - contentBytes = HttpMessageHelper.DecompressGzipContentIfRequired(headerStruct.ContentHeaders, contentBytes); + contentBytes = HttpMessageHelper.DecompressContentIfRequired(headerStruct.ContentHeaders, contentBytes); response.Content = new ByteArrayContent(contentBytes); HttpMessageHelper.CopyHeaders(headerStruct.ContentHeaders, response.Content.Headers); } diff --git a/WalletWasabi/Tor/Http/Helpers/HttpMessageHelper.cs b/WalletWasabi/Tor/Http/Helpers/HttpMessageHelper.cs index 87ad52653c..e2d98160fd 100644 --- a/WalletWasabi/Tor/Http/Helpers/HttpMessageHelper.cs +++ b/WalletWasabi/Tor/Http/Helpers/HttpMessageHelper.cs @@ -133,7 +133,7 @@ private static async Task ReadCRLFLineAsync(Stream stream, Encoding enco : throw new FormatException("There's no CRLF."); } - public static byte[] DecompressGzipContentIfRequired(HttpContentHeaders contentHeaders, byte[] contentBytes) + public static byte[] DecompressContentIfRequired(HttpContentHeaders contentHeaders, byte[] contentBytes) { if (contentBytes.Length == 0) { @@ -142,25 +142,35 @@ public static byte[] DecompressGzipContentIfRequired(HttpContentHeaders contentH if (contentHeaders.ContentEncoding.Contains("gzip")) { - using (var src = new MemoryStream(contentBytes)) - using (var unzipStream = new GZipStream(src, CompressionMode.Decompress)) - using (var targetStream = new MemoryStream()) - { - unzipStream.CopyTo(targetStream); - contentBytes = targetStream.ToArray(); - } + using var src = new MemoryStream(contentBytes); + using var unzipStream = new GZipStream(src, CompressionMode.Decompress); + using var targetStream = new MemoryStream(); + + unzipStream.CopyTo(targetStream); + contentBytes = targetStream.ToArray(); // Content-Length is removed, since it no longer applies to the decompressed content. contentHeaders.ContentLength = null; - contentHeaders.ContentEncoding.Remove("gzip"); + } + else if (contentHeaders.ContentEncoding.Contains("br")) + { + using var src = new MemoryStream(contentBytes); + using var unzipStream = new BrotliStream(src, CompressionMode.Decompress); + using var targetStream = new MemoryStream(); - if (contentHeaders.ContentEncoding.Count == 0) - { - contentHeaders.Remove("Content-Encoding"); - } + unzipStream.CopyTo(targetStream); + contentBytes = targetStream.ToArray(); + + // Content-Length is removed, since it no longer applies to the decompressed content. + contentHeaders.ContentLength = null; + contentHeaders.ContentEncoding.Remove("br"); } + if (contentHeaders.ContentEncoding.Count == 0) + { + contentHeaders.Remove("Content-Encoding"); + } return contentBytes; } diff --git a/WalletWasabi/Tor/Socks5/Pool/Circuits/DefaultCircuit.cs b/WalletWasabi/Tor/Socks5/Pool/Circuits/DefaultCircuit.cs index c0210015c5..f8edab63d3 100644 --- a/WalletWasabi/Tor/Socks5/Pool/Circuits/DefaultCircuit.cs +++ b/WalletWasabi/Tor/Socks5/Pool/Circuits/DefaultCircuit.cs @@ -1,5 +1,6 @@ using System.Threading; using WalletWasabi.Crypto.Randomness; +using WalletWasabi.Helpers; namespace WalletWasabi.Tor.Socks5.Pool.Circuits; @@ -12,7 +13,7 @@ public class DefaultCircuit : INamedCircuit { public static readonly DefaultCircuit Instance = new(); - private static string RandomName = RandomString.CapitalAlphaNumeric(21, secureRandom: true); + private static string RandomName = SecureRandom.Instance.GetString(21, Constants.CapitalAlphaNumericCharacters); private long _isolationId = 0; diff --git a/WalletWasabi/Tor/Socks5/Pool/Circuits/OneOffCircuit.cs b/WalletWasabi/Tor/Socks5/Pool/Circuits/OneOffCircuit.cs index 8e30318d82..b322316ed6 100644 --- a/WalletWasabi/Tor/Socks5/Pool/Circuits/OneOffCircuit.cs +++ b/WalletWasabi/Tor/Socks5/Pool/Circuits/OneOffCircuit.cs @@ -1,4 +1,5 @@ using WalletWasabi.Crypto.Randomness; +using WalletWasabi.Helpers; namespace WalletWasabi.Tor.Socks5.Pool.Circuits; @@ -12,7 +13,7 @@ public class OneOffCircuit : INamedCircuit, IDisposable public OneOffCircuit(string? purpose = null) { - Name = RandomString.CapitalAlphaNumeric(21, secureRandom: true); + Name = SecureRandom.Instance.GetString(21, Constants.CapitalAlphaNumericCharacters); _isActive = true; Purpose = purpose; } diff --git a/WalletWasabi/Tor/Socks5/Pool/Circuits/PersonCircuit.cs b/WalletWasabi/Tor/Socks5/Pool/Circuits/PersonCircuit.cs index 267cf2517c..c5a01f3c10 100644 --- a/WalletWasabi/Tor/Socks5/Pool/Circuits/PersonCircuit.cs +++ b/WalletWasabi/Tor/Socks5/Pool/Circuits/PersonCircuit.cs @@ -1,5 +1,6 @@ using System.Threading; using WalletWasabi.Crypto.Randomness; +using WalletWasabi.Helpers; namespace WalletWasabi.Tor.Socks5.Pool.Circuits; @@ -16,7 +17,7 @@ public class PersonCircuit : INamedCircuit, IDisposable public PersonCircuit(string? purpose = null) { - Name = RandomString.CapitalAlphaNumeric(21, secureRandom: true); + Name = SecureRandom.Instance.GetString(21, Constants.CapitalAlphaNumericCharacters); _isActive = true; Purpose = purpose; } diff --git a/WalletWasabi/Tor/Socks5/Pool/TorHttpPool.cs b/WalletWasabi/Tor/Socks5/Pool/TorHttpPool.cs index 66067530a2..8e8124e5d0 100644 --- a/WalletWasabi/Tor/Socks5/Pool/TorHttpPool.cs +++ b/WalletWasabi/Tor/Socks5/Pool/TorHttpPool.cs @@ -35,6 +35,7 @@ public class TorHttpPool : IAsyncDisposable public const int MaxConnectionsPerHost = 1000; private static readonly StringWithQualityHeaderValue GzipEncoding = new("gzip"); + private static readonly StringWithQualityHeaderValue BrotliEncoding = new("br"); private static readonly UnboundedChannelOptions Options = new() { @@ -490,9 +491,14 @@ internal virtual async Task SendCoreAsync(TorTcpConnection request.Version = HttpProtocol.HTTP11.Version; // Do not re-add the header if it is already present. + /* if (!request.Headers.AcceptEncoding.Contains(GzipEncoding)) { request.Headers.AcceptEncoding.Add(GzipEncoding); + }*/ + if (!request.Headers.AcceptEncoding.Contains(BrotliEncoding)) + { + request.Headers.AcceptEncoding.Add(BrotliEncoding); } string requestString = await request.ToHttpStringAsync(requestUriOverride, token).ConfigureAwait(false); diff --git a/WalletWasabi/WabiSabi/Client/RoundStateAwaiters/RoundStateUpdater.cs b/WalletWasabi/WabiSabi/Client/RoundStateAwaiters/RoundStateUpdater.cs index 28260e50d5..93d6ce730e 100644 --- a/WalletWasabi/WabiSabi/Client/RoundStateAwaiters/RoundStateUpdater.cs +++ b/WalletWasabi/WabiSabi/Client/RoundStateAwaiters/RoundStateUpdater.cs @@ -55,6 +55,17 @@ public async Task ForceTriggerAndWaitRoundAsync(TimeSpan timeout) await TriggerAndWaitRoundAsync(timeout).ConfigureAwait(false); } + public uint256 GetActiveRoundId() + { + var actives = RoundStates.Values.Where(x => x.RoundState.Phase != Phase.Ended && !x.RoundState.IsBlame).ToList(); + if (actives.Count == 0) + { + return uint256.Zero; + } + var latest = actives.MaxBy(rs => rs.RoundState.InputRegistrationEnd); + return latest?.RoundState.Id ?? uint256.Zero; + } + protected override async Task ActionAsync(CancellationToken cancellationToken) { if (DateTimeOffset.UtcNow - _lastRequestTime < _waitPeriod && _verifyRoundState) diff --git a/WalletWasabi/WabiSabi/Models/Serialization/OwnershipProofJsonConverterMS.cs b/WalletWasabi/WabiSabi/Models/Serialization/OwnershipProofJsonConverterMS.cs new file mode 100644 index 0000000000..5f692617af --- /dev/null +++ b/WalletWasabi/WabiSabi/Models/Serialization/OwnershipProofJsonConverterMS.cs @@ -0,0 +1,20 @@ +using NBitcoin; +using System.Text.Json; +using System.Text.Json.Serialization; +using WalletWasabi.Crypto; + +namespace WalletWasabi.WabiSabi.Models.Serialization; + +internal class OwnershipProofJsonConverterMS : JsonConverter +{ + public override OwnershipProof? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + string? serialized = reader.GetString(); + return serialized is not null ? OwnershipProof.FromBytes(Convert.FromHexString(serialized)) : null; + } + + public override void Write(Utf8JsonWriter writer, OwnershipProof? value, JsonSerializerOptions options) + { + writer.WriteStringValue(Convert.ToHexString(value.ToBytes())); + } +} diff --git a/WalletWasabi/Wallets/Kitchen.cs b/WalletWasabi/Wallets/Kitchen.cs index ba9603d06a..cc2b2fd250 100644 --- a/WalletWasabi/Wallets/Kitchen.cs +++ b/WalletWasabi/Wallets/Kitchen.cs @@ -1,6 +1,7 @@ using System.Diagnostics.CodeAnalysis; using WalletWasabi.Crypto; using WalletWasabi.Crypto.Randomness; +using WalletWasabi.Helpers; namespace WalletWasabi.Wallets; @@ -45,7 +46,7 @@ public void Cook(string ingredients) { ingredients ??= ""; - Salt = RandomString.AlphaNumeric(21, secureRandom: true); + Salt = SecureRandom.Instance.GetString(21, Constants.AlphaNumericCharacters); Soup = StringCipher.Encrypt(ingredients, Salt); } } diff --git a/deps.nix b/deps.nix index 73640be8f7..996fe0ffa6 100644 --- a/deps.nix +++ b/deps.nix @@ -3,8 +3,12 @@ { fetchNuGet }: [ (fetchNuGet { pname = "LinqKit.Core"; version = "1.2.5"; sha256 = "15imfl77sfii5nz8i6pi3h5izhxyp2dihx13g2fzqnky1fj12gnk"; }) + (fetchNuGet { pname = "Microsoft.AspNetCore.Http.Abstractions"; version = "2.3.0"; sha256 = "1yd5k7fj47dh4nhgqqc0m0l3xbbjrgmbpnad8sg1jws89v70bc1n"; }) + (fetchNuGet { pname = "Microsoft.AspNetCore.Http.Extensions"; version = "2.3.0"; sha256 = "04n2hqs9v5abhcvjh4l0gj96dykwcm065nz18wwhzrqakh5p1rdh"; }) + (fetchNuGet { pname = "Microsoft.AspNetCore.Http.Features"; version = "2.3.0"; sha256 = "172hm32049yp9aqdy94hljnpbcc1m0s02sbss6nvq34wfi5lahs2"; }) (fetchNuGet { pname = "Microsoft.AspNetCore.JsonPatch"; version = "8.0.19"; sha256 = "1866r07yjr6zrjyvd8a1pvczmifavvvm1hrh6i37fa8jafywjj0g"; }) (fetchNuGet { pname = "Microsoft.AspNetCore.Mvc.NewtonsoftJson"; version = "8.0.19"; sha256 = "0p0qf5qw6qib76ky3cpi3gcz15nynd17ys0alw3sxmlika7c9p73"; }) + (fetchNuGet { pname = "Microsoft.AspNetCore.ResponseCompression"; version = "2.3.0"; sha256 = "0h528cmfwclkhhn4dy50a2kjqlalg297b0nkw9jvsfdl1zwdrcv3"; }) (fetchNuGet { pname = "Microsoft.AspNetCore.WebUtilities"; version = "8.0.19"; sha256 = "1dgbkd97chi7jgmbj24j9qf44b58q181nhidyxi3nwd5hlj6358d"; }) (fetchNuGet { pname = "Microsoft.Bcl.AsyncInterfaces"; version = "6.0.0"; sha256 = "15gqy2m14fdlvy1g59207h5kisznm355kbw010gy19vh47z8gpz3"; }) (fetchNuGet { pname = "Microsoft.Build"; version = "15.3.409"; sha256 = "0vzq6csp2yys9s96c7i37bjml439rdi47g8f5rzqdr7xf5a1jk81"; }) @@ -24,6 +28,7 @@ (fetchNuGet { pname = "Microsoft.Extensions.Configuration.Binder"; version = "8.0.0"; sha256 = "1m0gawiz8f5hc3li9vd5psddlygwgkiw13d7div87kmkf4idza8r"; }) (fetchNuGet { pname = "Microsoft.Extensions.Configuration.Binder"; version = "8.0.2"; sha256 = "08gdj4ljvcax1lq9prmrasm3kdsw385ayah1fhd8dpq2x9b78q38"; }) (fetchNuGet { pname = "Microsoft.Extensions.DependencyInjection"; version = "8.0.1"; sha256 = "0dc5nwq2h9pskbq2cyc8cj6gwqi4b032pakw9ylg33dycj6k9n1v"; }) + (fetchNuGet { pname = "Microsoft.Extensions.DependencyInjection.Abstractions"; version = "8.0.0"; sha256 = "1zw0bpp5742jzx03wvqc8csnvsbgdqi0ls9jfc5i2vd3cl8b74pg"; }) (fetchNuGet { pname = "Microsoft.Extensions.DependencyInjection.Abstractions"; version = "8.0.2"; sha256 = "01i66wklw882p6xgpm3pfw8maw977y0pxfzhakd10pr4008xzwji"; }) (fetchNuGet { pname = "Microsoft.Extensions.Diagnostics"; version = "8.0.1"; sha256 = "1dw8wjsjmxlrvv62af18yc5x2kp9sfkzbpx7qycci54m4qs8gdha"; }) (fetchNuGet { pname = "Microsoft.Extensions.Diagnostics.Abstractions"; version = "8.0.1"; sha256 = "1ga0fna5kzv0z1pmxi7f83k71kma2cxndqc6ymc93a1w21gdb43p"; }) @@ -43,6 +48,7 @@ (fetchNuGet { pname = "Microsoft.Extensions.Options.ConfigurationExtensions"; version = "8.0.0"; sha256 = "04nm8v5a3zp0ill7hjnwnja3s2676b4wffdri8hdk2341p7mp403"; }) (fetchNuGet { pname = "Microsoft.Extensions.Primitives"; version = "2.0.0"; sha256 = "1xppr5jbny04slyjgngxjdm0maxdh47vq481ps944d7jrfs0p3mb"; }) (fetchNuGet { pname = "Microsoft.Extensions.Primitives"; version = "8.0.0"; sha256 = "0aldaz5aapngchgdr7dax9jw5wy7k7hmjgjpfgfv1wfif27jlkqm"; }) + (fetchNuGet { pname = "Microsoft.Net.Http.Headers"; version = "2.3.0"; sha256 = "189sxglmvdkhg7yi1v9xrkqn02n412fq5pn9k1dlbkck2b5cx3ax"; }) (fetchNuGet { pname = "Microsoft.Net.Http.Headers"; version = "8.0.19"; sha256 = "0grz1xapl7z032qr8fc25i61fa7zqwffx0gwm5r4q8pac3q9nc25"; }) (fetchNuGet { pname = "Microsoft.NETCore.App"; version = "2.0.5"; sha256 = "0qb7k624w7l0zhapdp519ymqg84a67r8zyd8cpj42hywsgb0dqv6"; }) (fetchNuGet { pname = "Microsoft.NETCore.DotNetAppHost"; version = "2.0.5"; sha256 = "00bsxdg9c8msjxyffvfi8siqk8v2m7ca8fqy1npv7b2pzg3byjws"; }) @@ -84,6 +90,7 @@ (fetchNuGet { pname = "Swashbuckle.AspNetCore.SwaggerUI"; version = "6.5.0"; sha256 = "17hx7kc187higm0gk67dndng3n7932sn3fwyj48l45cvyr3025h7"; }) (fetchNuGet { pname = "System.AppContext"; version = "4.1.0"; sha256 = "0fv3cma1jp4vgj7a8hqc9n7hr1f1kjp541s6z0q1r6nazb4iz9mz"; }) (fetchNuGet { pname = "System.Buffers"; version = "4.0.0"; sha256 = "13s659bcmg9nwb6z78971z1lr6bmh2wghxi1ayqyzl4jijd351gr"; }) + (fetchNuGet { pname = "System.Buffers"; version = "4.6.0"; sha256 = "15zw58cparmgsmhsljx8w90kym082fxn0fbb0rgqks3ma202ar3k"; }) (fetchNuGet { pname = "System.Collections"; version = "4.0.11"; sha256 = "1ga40f5lrwldiyw6vy67d0sg7jd7ww6kgwbksm19wrvq9hr0bsm6"; }) (fetchNuGet { pname = "System.Collections"; version = "4.3.0"; sha256 = "19r4y64dqyrq6k4706dnyhhw7fs24kpp3awak7whzss39dakpxk9"; }) (fetchNuGet { pname = "System.Collections.Concurrent"; version = "4.0.12"; sha256 = "07y08kvrzpak873pmyxs129g1ch8l27zmg51pcyj2jvq03n0r0fc"; }) @@ -185,6 +192,7 @@ (fetchNuGet { pname = "System.Text.Encoding.CodePages"; version = "4.0.1"; sha256 = "00wpm3b9y0k996rm9whxprngm8l500ajmzgy2ip9pgwk0icp06y3"; }) (fetchNuGet { pname = "System.Text.Encoding.Extensions"; version = "4.0.11"; sha256 = "08nsfrpiwsg9x5ml4xyl3zyvjfdi4mvbqf93kjdh11j4fwkznizs"; }) (fetchNuGet { pname = "System.Text.Encoding.Extensions"; version = "4.3.0"; sha256 = "11q1y8hh5hrp5a3kw25cb6l00v5l5dvirkz8jr3sq00h1xgcgrxy"; }) + (fetchNuGet { pname = "System.Text.Encodings.Web"; version = "8.0.0"; sha256 = "1wbypkx0m8dgpsaqgyywz4z760xblnwalb241d5qv9kx8m128i11"; }) (fetchNuGet { pname = "System.Text.Json"; version = "8.0.6"; sha256 = "1sxmdc40jss7xjr1hwban81868nyffcc0mp05s9xyfyhg0bxcgd8"; }) (fetchNuGet { pname = "System.Text.RegularExpressions"; version = "4.1.0"; sha256 = "1mw7vfkkyd04yn2fbhm38msk7dz2xwvib14ygjsb8dq2lcvr18y7"; }) (fetchNuGet { pname = "System.Text.RegularExpressions"; version = "4.3.0"; sha256 = "1bgq51k7fwld0njylfn7qc5fmwrk2137gdq7djqdsw347paa9c2l"; })