Skip to content

Commit

Permalink
Merge branch 'master' into SendManualControl2
Browse files Browse the repository at this point in the history
  • Loading branch information
ichthus1604 committed Jun 11, 2024
2 parents eda5d71 + bf2f338 commit 4ee4f48
Show file tree
Hide file tree
Showing 30 changed files with 482 additions and 82 deletions.
4 changes: 2 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ this.WhenAnyValue(...)

## Subscribe triggered once on initialization

When you subscribe with the usage of `.WhenAnyValue()` right after the creation one call of Subcription will be triggered. This is by design and most of the cases it is fine. Still you can supress this behaviour by adding `Skip(1)`.
When you subscribe with the usage of `.WhenAnyValue()` right after the creation one call of Subscription will be triggered. This is by design and most of the cases it is fine. Still you can suppress this behaviour by adding `Skip(1)`.

```cs
this.WhenAnyValue(x => x.PreferPsbtWorkflow)
Expand Down Expand Up @@ -345,7 +345,7 @@ In order to minimize the amount of boilerplate required for such initialization,
- A new constructor is generated for that ViewModel, including all parameters of any existing constructor plus the UiContext.
- This generated constructor initializes the `UiContext` *after* running the code of the manually written constructor (if any).
- A Roslyn Analyzer inspects any manually written constructors in the ViewModel to prevent references to `UiContext` in the constructor body, before the above mentioned initialization can take place, resulting in `NullReferenceException`s.
- The Analyzer demands the manually written constructor to be declared `private`, so that external instatiation of the ViewModel is done by calling the source-generated constructor.
- The Analyzer demands the manually written constructor to be declared `private`, so that external instantiation of the ViewModel is done by calling the source-generated constructor.

❌ Writing code that directly references `UiContext` in a ViewModel's constructor body will result in a compile-time error.

Expand Down
24 changes: 24 additions & 0 deletions WalletWasabi.Daemon/Config.cs
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,12 @@ [ nameof(EnableGpu)] = (
[ nameof(CoordinatorIdentifier)] = (
"-",
GetStringValue("CoordinatorIdentifier", PersistentConfig.CoordinatorIdentifier, cliArgs)),
[ nameof(MaxCoordinationFeeRate)] = (
"Max coordination fee rate the client is willing to accept to participate into a round",
GetDecimalValue("MaxCoordinationFeeRate", PersistentConfig.MaxCoordinationFeeRate, cliArgs)),
[ nameof(MaxCoinjoinMiningFeeRate)] = (
"Max mining fee rate in s/vb the client is willing to pay to participate into a round",
GetDecimalValue("MaxCoinjoinMiningFeeRate", PersistentConfig.MaxCoinJoinMiningFeeRate, cliArgs)),
};

// Check if any config value is overridden (either by an environment value, or by a CLI argument).
Expand Down Expand Up @@ -184,6 +190,8 @@ [ nameof(CoordinatorIdentifier)] = (

public bool EnableGpu => GetEffectiveValue<BoolValue, bool>(nameof(EnableGpu));
public string CoordinatorIdentifier => GetEffectiveValue<StringValue, string>(nameof(CoordinatorIdentifier));
public decimal MaxCoordinationFeeRate => GetEffectiveValue<DecimalValue, decimal>(nameof(MaxCoordinationFeeRate));
public decimal MaxCoinjoinMiningFeeRate => GetEffectiveValue<DecimalValue, decimal>(nameof(MaxCoinjoinMiningFeeRate));
public ServiceConfiguration ServiceConfiguration { get; }

public static string DataDir { get; } = GetStringValue(
Expand Down Expand Up @@ -310,6 +318,21 @@ private BoolValue GetBoolValue(string key, bool value, string[] cliArgs)
return new BoolValue(value, value, ValueSource.Disk);
}

private DecimalValue GetDecimalValue(string key, decimal value, string[] cliArgs)
{
if (GetOverrideValue(key, cliArgs, out string? overrideValue, out ValueSource? valueSource))
{
if (!int.TryParse(overrideValue, out int argsLongValue))
{
throw new ArgumentException("must be a decimal number.", key);
}

return new DecimalValue(value, argsLongValue, valueSource.Value);
}

return new DecimalValue(value, value, ValueSource.Disk);
}

private IntValue GetLongValue(string key, int value, string[] cliArgs)
{
if (GetOverrideValue(key, cliArgs, out string? overrideValue, out ValueSource? valueSource))
Expand Down Expand Up @@ -511,6 +534,7 @@ private interface ITypedValue<T> : IValue

private record BoolValue(bool Value, bool EffectiveValue, ValueSource ValueSource) : ITypedValue<bool>;
private record IntValue(int Value, int EffectiveValue, ValueSource ValueSource) : ITypedValue<int>;
private record DecimalValue(decimal Value, decimal EffectiveValue, ValueSource ValueSource) : ITypedValue<decimal>;
private record StringValue(string Value, string EffectiveValue, ValueSource ValueSource) : ITypedValue<string>;
private record NullableStringValue(string? Value, string? EffectiveValue, ValueSource ValueSource) : ITypedValue<string?>;
private record StringArrayValue(string[] Value, string[] EffectiveValue, ValueSource ValueSource) : ITypedValue<string[]>;
Expand Down
4 changes: 3 additions & 1 deletion WalletWasabi.Daemon/Global.cs
Original file line number Diff line number Diff line change
Expand Up @@ -399,7 +399,9 @@ private void RegisterCoinJoinComponents()
{
Tor.Http.IHttpClient roundStateUpdaterHttpClient = CoordinatorHttpClientFactory.NewHttpClient(Mode.SingleCircuitPerLifetime, RoundStateUpdaterCircuit);
HostedServices.Register<RoundStateUpdater>(() => new RoundStateUpdater(TimeSpan.FromSeconds(10), new WabiSabiHttpApiClient(roundStateUpdaterHttpClient)), "Round info updater");
HostedServices.Register<CoinJoinManager>(() => new CoinJoinManager(WalletManager, HostedServices.Get<RoundStateUpdater>(), CoordinatorHttpClientFactory, HostedServices.Get<WasabiSynchronizer>(), Config.CoordinatorIdentifier, CoinPrison), "CoinJoin Manager");

var coinJoinConfiguration = new CoinJoinConfiguration(Config.CoordinatorIdentifier, Config.MaxCoordinationFeeRate, Config.MaxCoinjoinMiningFeeRate);
HostedServices.Register<CoinJoinManager>(() => new CoinJoinManager(WalletManager, HostedServices.Get<RoundStateUpdater>(), CoordinatorHttpClientFactory, HostedServices.Get<WasabiSynchronizer>(), coinJoinConfiguration, CoinPrison), "CoinJoin Manager");
}

private void WalletManager_WalletStateChanged(object? sender, WalletState e)
Expand Down
31 changes: 29 additions & 2 deletions WalletWasabi.Daemon/PersistentConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ namespace WalletWasabi.Daemon;

public record PersistentConfig : IConfigNg
{
public const int DefaultJsonRpcServerPort = 37128;
public static readonly Money DefaultDustThreshold = Money.Coins(Constants.DefaultDustThreshold);

[JsonPropertyName("Network")]
Expand Down Expand Up @@ -118,6 +117,12 @@ public record PersistentConfig : IConfigNg
[JsonPropertyName("CoordinatorIdentifier")]
public string CoordinatorIdentifier { get; init; } = "CoinJoinCoordinatorIdentifier";

[JsonPropertyName("MaxCoordinationFeeRate")]
public decimal MaxCoordinationFeeRate { get; init; } = Constants.DefaultMaxCoordinationFeeRate;

[JsonPropertyName("MaxCoinJoinMiningFeeRate")]
public decimal MaxCoinJoinMiningFeeRate { get; init; } = Constants.DefaultMaxCoinJoinMiningFeeRate;

public bool DeepEquals(PersistentConfig other)
{
bool useTorIsEqual = Config.ObjectToTorMode(UseTor) == Config.ObjectToTorMode(other.UseTor);
Expand Down Expand Up @@ -145,7 +150,9 @@ public bool DeepEquals(PersistentConfig other)
JsonRpcServerPrefixes.SequenceEqual(other.JsonRpcServerPrefixes) &&
DustThreshold == other.DustThreshold &&
EnableGpu == other.EnableGpu &&
CoordinatorIdentifier == other.CoordinatorIdentifier;
CoordinatorIdentifier == other.CoordinatorIdentifier &&
MaxCoordinationFeeRate == other.MaxCoordinationFeeRate &&
MaxCoinJoinMiningFeeRate == other.MaxCoinJoinMiningFeeRate;
}

public EndPoint GetBitcoinP2pEndPoint()
Expand Down Expand Up @@ -185,6 +192,26 @@ public string GetCoordinatorUri()
throw new NotSupportedNetworkException(Network);
}

public string GetBackendUri()
{
if (Network == Network.Main)
{
return MainNetBackendUri;
}

if (Network == Network.TestNet)
{
return TestNetBackendUri;
}

if (Network == Network.RegTest)
{
return RegTestBackendUri;
}

throw new NotSupportedNetworkException(Network);
}

public bool MigrateOldDefaultBackendUris([NotNullWhen(true)] out PersistentConfig? newConfig)
{
bool hasChanged = false;
Expand Down
29 changes: 29 additions & 0 deletions WalletWasabi.Fluent/Behaviors/WhitespaceInputRemovalBehavior.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using System.Reactive.Disposables;
using System.Reactive.Linq;
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Interactivity;
using Avalonia.Xaml.Interactions.Custom;
using WalletWasabi.Extensions;
using WalletWasabi.Fluent.Extensions;

namespace WalletWasabi.Fluent.Behaviors;

public class WhitespaceInputRemovalBehavior : DisposingBehavior<TextBox>
{
protected override void OnAttached(CompositeDisposable disposables)
{
if (AssociatedObject != null)
{
AssociatedObject.OnEvent(InputElement.TextInputEvent, RoutingStrategies.Tunnel)
.Select(x => x.EventArgs)
.Do(x => Filter(x, x.Text))
.Subscribe();
}
}

private void Filter(TextInputEventArgs textInputEventArgs, string? objText)
{
textInputEventArgs.Text = objText?.WithoutWhitespace();
}
}
43 changes: 43 additions & 0 deletions WalletWasabi.Fluent/Behaviors/WhitespacePasteRemovalBehavior.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
using System.Reactive.Disposables;
using System.Reactive.Linq;
using Avalonia.Controls;
using Avalonia.Interactivity;
using Avalonia.Xaml.Interactions.Custom;
using ReactiveUI;
using WalletWasabi.Extensions;
using WalletWasabi.Fluent.Helpers;

namespace WalletWasabi.Fluent.Behaviors;

public class WhitespacePasteRemovalBehavior : DisposingBehavior<TextBox>
{
protected override void OnAttached(CompositeDisposable disposables)
{
var tb = AssociatedObject;

if (tb != null)
{
var pasteEvents = Observable.FromEventPattern<EventHandler<RoutedEventArgs>, RoutedEventArgs>(
eh => tb.PastingFromClipboard += eh, eh => tb.PastingFromClipboard -= eh);

pasteEvents
.Do(args => args.EventArgs.Handled = true) // Always mark the attempt to paste as handled, so we'll always use the customized paste.
.Select(_ => Observable.FromAsync(ApplicationHelper.GetTextAsync, scheduler: RxApp.MainThreadScheduler)) // Executes get text asynchronously using the UI thread
.Concat() // Concatenates the results of the requests into a single observable
.Do(clipboardText => Paste(clipboardText, tb)) // Pastes the text
.Subscribe()
.DisposeWith(disposables);
}
}

private static void Paste(string text, TextBox tb)
{
var pasted = text.WithoutWhitespace();
var start = Math.Min(tb.SelectionStart, tb.SelectionEnd);
var end = Math.Max(tb.SelectionStart, tb.SelectionEnd);

var current = tb.Text ?? "";
tb.Text = current[..start] + pasted + current[end..];
tb.CaretIndex = start + pasted.Length;
}
}
1 change: 1 addition & 0 deletions WalletWasabi.Fluent/Controls/CurrencyEntryBox.axaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,7 @@ private void DoPasteCheck(KeyEventArgs e)
if (keymap is { } && Match(keymap.Paste))
{
ModifiedPasteAsync();
e.Handled = true;
}
else
{
Expand Down
37 changes: 33 additions & 4 deletions WalletWasabi.Fluent/Models/UI/ApplicationSettings.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System.Globalization;
using Avalonia.Controls;
using NBitcoin;
using ReactiveUI;
Expand Down Expand Up @@ -33,6 +34,7 @@ public partial class ApplicationSettings : ReactiveObject

// Advanced
[AutoNotify] private bool _enableGpu;
[AutoNotify] private string _backendUri;

// Bitcoin
[AutoNotify] private Network _network;
Expand All @@ -42,6 +44,8 @@ public partial class ApplicationSettings : ReactiveObject
[AutoNotify] private bool _stopLocalBitcoinCoreOnShutdown;
[AutoNotify] private string _bitcoinP2PEndPoint;
[AutoNotify] private string _coordinatorUri;
[AutoNotify] private string _maxCoordinationFeeRate;
[AutoNotify] private string _maxCoinJoinMiningFeeRate;
[AutoNotify] private string _dustThreshold;

// General
Expand Down Expand Up @@ -77,6 +81,7 @@ public ApplicationSettings(string persistentConfigFilePath, PersistentConfig per

// Advanced
_enableGpu = _startupConfig.EnableGpu;
_backendUri = _startupConfig.GetBackendUri();

// Bitcoin
_network = config.Network;
Expand All @@ -85,6 +90,8 @@ public ApplicationSettings(string persistentConfigFilePath, PersistentConfig per
_stopLocalBitcoinCoreOnShutdown = _startupConfig.StopLocalBitcoinCoreOnShutdown;
_bitcoinP2PEndPoint = _startupConfig.GetBitcoinP2pEndPoint().ToString(defaultPort: -1);
_coordinatorUri = _startupConfig.GetCoordinatorUri();
_maxCoordinationFeeRate = _startupConfig.MaxCoordinationFeeRate.ToString(CultureInfo.InvariantCulture);
_maxCoinJoinMiningFeeRate = _startupConfig.MaxCoinJoinMiningFeeRate.ToString(CultureInfo.InvariantCulture);
_dustThreshold = _startupConfig.DustThreshold.ToString();

// General
Expand All @@ -110,7 +117,8 @@ public ApplicationSettings(string persistentConfigFilePath, PersistentConfig per
_windowState = (WindowState)Enum.Parse(typeof(WindowState), _uiConfig.WindowState);

// Save on change
this.WhenAnyValue(

var configChangeTrigger1 = this.WhenAnyValue(
x => x.EnableGpu,
x => x.Network,
x => x.StartLocalBitcoinCoreOnStartup,
Expand All @@ -129,6 +137,17 @@ public ApplicationSettings(string persistentConfigFilePath, PersistentConfig per
.Do(_ => Save())
.Subscribe();

// Save on change - continuation. WhenAnyValue cannot have more than 12 arguments.
this.WhenAnyValue(x =>
x.MaxCoordinationFeeRate,
x => x.MaxCoinJoinMiningFeeRate,
(_, _) => Unit.Default)
.Skip(1)
.ObserveOn(RxApp.MainThreadScheduler)
.Throttle(TimeSpan.FromMilliseconds(ThrottleTime))
.Do(_ => Save())
.Subscribe();

// Save UiConfig on change
this.WhenAnyValue(
x => x.DarkModeEnabled,
Expand Down Expand Up @@ -232,14 +251,17 @@ private PersistentConfig ApplyChanges(PersistentConfig config)
if (Network == Network.Main)
{
result = result with { MainNetCoordinatorUri = CoordinatorUri };
result = result with { MainNetBackendUri = BackendUri };
}
else if (Network == Network.TestNet)
{
result = result with { TestNetCoordinatorUri = CoordinatorUri };
result = result with { TestNetBackendUri = BackendUri };
}
else if (Network == Network.RegTest)
{
result = result with { RegTestCoordinatorUri = CoordinatorUri };
result = result with { RegTestBackendUri = BackendUri };
}
else
{
Expand All @@ -251,9 +273,15 @@ private PersistentConfig ApplyChanges(PersistentConfig config)
StartLocalBitcoinCoreOnStartup = StartLocalBitcoinCoreOnStartup,
StopLocalBitcoinCoreOnShutdown = StopLocalBitcoinCoreOnShutdown,
LocalBitcoinCoreDataDir = Guard.Correct(LocalBitcoinCoreDataDir),
DustThreshold = decimal.TryParse(DustThreshold, out var threshold)
? Money.Coins(threshold)
: PersistentConfig.DefaultDustThreshold,
DustThreshold = decimal.TryParse(DustThreshold, out var threshold) ?
Money.Coins(threshold) :
PersistentConfig.DefaultDustThreshold,
MaxCoordinationFeeRate = decimal.TryParse(MaxCoordinationFeeRate, out var maxCoordinationFeeRate) ?
maxCoordinationFeeRate :
Constants.DefaultMaxCoordinationFeeRate,
MaxCoinJoinMiningFeeRate = decimal.TryParse(MaxCoinJoinMiningFeeRate, out var maxCoinjoinMiningFeeRate) ?
maxCoinjoinMiningFeeRate :
Constants.DefaultMaxCoinJoinMiningFeeRate
};
}
else
Expand All @@ -265,6 +293,7 @@ private PersistentConfig ApplyChanges(PersistentConfig config)

BitcoinP2PEndPoint = result.GetBitcoinP2pEndPoint().ToString(defaultPort: -1);
CoordinatorUri = result.GetCoordinatorUri();
BackendUri = result.GetBackendUri();
}

// General
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public ZkSnacksCoordinatorAnnouncementViewModel(UiContext uiContext)
if (UiContext.MainViewModel is { } mainViewModel)
{
await mainViewModel.SettingsPage.ActivateBitcoinTabWithFocusOnCoordinatorUri();
await mainViewModel.SettingsPage.ActivateCoordinatorTab();
}
});
}
Expand Down
9 changes: 8 additions & 1 deletion WalletWasabi.Fluent/ViewModels/MainViewModelExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,19 @@ public static void RegisterAllViewModels(this MainViewModel mainViewModel, UiCon
return mainViewModel.SettingsPage;
});

AdvancedSettingsTabViewModel.RegisterLazy(() =>
CoordinatorTabSettingsViewModel.RegisterLazy(() =>
{
mainViewModel.SettingsPage.SelectedTab = 2;
return mainViewModel.SettingsPage;
});


AdvancedSettingsTabViewModel.RegisterLazy(() =>
{
mainViewModel.SettingsPage.SelectedTab = 3;
return mainViewModel.SettingsPage;
});

AboutViewModel.RegisterLazy(() => new AboutViewModel(uiContext));
BroadcasterViewModel.RegisterLazy(() => new BroadcasterViewModel(uiContext));
UserSupportViewModel.RegisterLazy(() => new UserSupportViewModel(uiContext));
Expand Down
Loading

0 comments on commit 4ee4f48

Please sign in to comment.