Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/master'
Browse files Browse the repository at this point in the history
  • Loading branch information
RolandUI committed May 25, 2024
2 parents ace7744 + 13b5b0b commit b562db4
Show file tree
Hide file tree
Showing 19 changed files with 153 additions and 259 deletions.
51 changes: 40 additions & 11 deletions WalletWasabi.Backend/Controllers/BlockchainController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,7 @@ public async Task<IActionResult> GetTransactionsAsync([FromQuery, Required] IEnu
/// <summary>
/// Fetches transactions from cache if possible and missing transactions are fetched using RPC.
/// </summary>
/// <exception cref="AggregateException">If RPC client succeeds in getting some transactions but not all.</exception>
private async Task<Transaction[]> FetchTransactionsAsync(uint256[] txIds, CancellationToken cancellationToken)
{
int requestCount = txIds.Length;
Expand Down Expand Up @@ -204,12 +205,20 @@ private async Task<Transaction[]> FetchTransactionsAsync(uint256[] txIds, Cancel
{
// Ask to get missing transactions over RPC.
IEnumerable<Transaction> txs = await RpcClient.GetRawTransactionsAsync(txIdsRetrieve.Keys, cancellationToken).ConfigureAwait(false);

Dictionary<uint256, Transaction> rpcBatch = txs.ToDictionary(x => x.GetHash(), x => x);

foreach (KeyValuePair<uint256, Transaction> kvp in rpcBatch)
{
txIdsRetrieve[kvp.Key].TrySetResult(kvp.Value);
}

// RPC client does not throw if a transaction is missing, so we need to account for this case.
if (rpcBatch.Count < txIdsRetrieve.Count)
{
IReadOnlyList<Exception> exceptions = MarkNotFinishedTasksAsFailed(txIdsRetrieve);
throw new AggregateException(exceptions);
}
}

Transaction[] result = new Transaction[requestCount];
Expand All @@ -227,18 +236,30 @@ private async Task<Transaction[]> FetchTransactionsAsync(uint256[] txIds, Cancel
{
if (txIdsRetrieve.Count > 0)
{
// It's necessary to always set a result to the task completion sources. Otherwise, cache can get corrupted.
Exception ex = new InvalidOperationException("Failed to get the transaction.");
foreach ((uint256 txid, TaskCompletionSource<Transaction> tcs) in txIdsRetrieve)
MarkNotFinishedTasksAsFailed(txIdsRetrieve);
}
}

IReadOnlyList<Exception> MarkNotFinishedTasksAsFailed(Dictionary<uint256, TaskCompletionSource<Transaction>> txIdsRetrieve)
{
List<Exception>? exceptions = null;

// It's necessary to always set a result to the task completion sources. Otherwise, cache can get corrupted.
foreach ((uint256 txid, TaskCompletionSource<Transaction> tcs) in txIdsRetrieve)
{
if (!tcs.Task.IsCompleted)
{
if (!tcs.Task.IsCompleted)
{
// Prefer new cache requests to try again rather than getting the exception. The window is small though.
Cache.Remove(txid);
tcs.SetException(ex);
}
exceptions ??= new();

// Prefer new cache requests to try again rather than getting the exception. The window is small though.
Exception e = new InvalidOperationException($"Failed to get the transaction '{txid}'.");
exceptions.Add(e);
Cache.Remove($"{nameof(GetTransactionsAsync)}#{txid}");
tcs.SetException(e);
}
}

return exceptions ?? [];
}
}

Expand Down Expand Up @@ -489,11 +510,19 @@ private async Task<Dictionary<uint256, UnconfirmedTransactionChainItem>> BuildUn

private async Task<UnconfirmedTransactionChainItem> ComputeUnconfirmedTransactionChainItemAsync(uint256 currentTxId, IEnumerable<uint256> mempoolHashes, CancellationToken cancellationToken)
{
var currentTx = (await FetchTransactionsAsync([currentTxId], cancellationToken).ConfigureAwait(false)).FirstOrDefault() ?? throw new InvalidOperationException("Tx not found");
var currentTx = (await FetchTransactionsAsync([currentTxId], cancellationToken).ConfigureAwait(false)).First();

var txsToFetch = currentTx.Inputs.Select(input => input.PrevOut.Hash).Distinct().ToArray();

var parentTxs = await FetchTransactionsAsync(txsToFetch, cancellationToken).ConfigureAwait(false);
Transaction[] parentTxs;
try
{
parentTxs = await FetchTransactionsAsync(txsToFetch, cancellationToken).ConfigureAwait(false);
}
catch(AggregateException ex)
{
throw new InvalidOperationException($"Some transactions part of the chain were not found: {ex}");
}

// Get unconfirmed parents and children
var unconfirmedParents = parentTxs.Where(x => mempoolHashes.Contains(x.GetHash())).ToHashSet();
Expand Down
30 changes: 19 additions & 11 deletions WalletWasabi.Daemon/Config.cs
Original file line number Diff line number Diff line change
Expand Up @@ -384,8 +384,26 @@ private static TorModeValue GetTorModeValue(string key, object value, string[] c
{
TorMode computedValue;

computedValue = ObjectToTorMode(value);

if (GetOverrideValue(key, cliArgs, out string? overrideValue, out ValueSource? valueSource))
{
if (!Enum.TryParse(overrideValue, out TorMode parsedOverrideValue))
{
throw new ArgumentException($"Could not convert overridden value '{overrideValue}' to a valid {nameof(TorMode)} value.");
}

return new TorModeValue(computedValue, parsedOverrideValue, valueSource.Value);
}

return new TorModeValue(computedValue, computedValue, ValueSource.Disk);
}

public static TorMode ObjectToTorMode(object value)
{
string? stringValue = value.ToString();

TorMode computedValue;
if (stringValue is null)
{
throw new ArgumentException($"Could not convert '{value}' to a string value.");
Expand All @@ -407,17 +425,7 @@ private static TorModeValue GetTorModeValue(string key, object value, string[] c
throw new ArgumentException($"Could not convert '{value}' to a valid {nameof(TorMode)} value.");
}

if (GetOverrideValue(key, cliArgs, out string? overrideValue, out ValueSource? valueSource))
{
if (!Enum.TryParse(overrideValue, out TorMode parsedOverrideValue))
{
throw new ArgumentException($"Could not convert overridden value '{overrideValue}' to a valid {nameof(TorMode)} value.");
}

return new TorModeValue(computedValue, parsedOverrideValue, valueSource.Value);
}

return new TorModeValue(computedValue, computedValue, ValueSource.Disk);
return computedValue;
}

private static bool GetOverrideValue(string key, string[] cliArgs, [NotNullWhen(true)] out string? overrideValue, [NotNullWhen(true)] out ValueSource? valueSource)
Expand Down
2 changes: 1 addition & 1 deletion WalletWasabi.Daemon/PersistentConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ public record PersistentConfig : IConfigNg

public bool DeepEquals(PersistentConfig other)
{
bool useTorIsEqual = UseTor.ToString() == other.UseTor.ToString();
bool useTorIsEqual = Config.ObjectToTorMode(UseTor) == Config.ObjectToTorMode(other.UseTor);

return
Network == other.Network &&
Expand Down
2 changes: 1 addition & 1 deletion WalletWasabi.Documentation/WasabiCompatibility.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,4 @@ Wasabi dependencies are:
## What about Tails and Whonix?

Tails and Whonix are privacy-oriented OSs, so it makes sense to use them with Wasabi Wallet. At the moment, Wasabi is working properly on these platforms, but our dependencies do not officially support them, so we cannot make promises regarding future stability.
To make Wasabi work on these OSs, it should be started with the following start up parameters: `--UseTor=EnabledOnlyRunning --TorSocksPort=9050`.
To make Wasabi work on these OSs, it should be started with the following start up parameter: `--UseTor=EnabledOnlyRunning`.
5 changes: 0 additions & 5 deletions WalletWasabi.Fluent/Controls/NavBarItem.axaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,4 @@ private void UpdateIndicatorOrientationPseudoClasses(Orientation orientation)
PseudoClasses.Set(":horizontal", orientation == Orientation.Horizontal);
PseudoClasses.Set(":vertical", orientation == Orientation.Vertical);
}

private void UpdatePseudoClass(string pseudoClass, bool value)
{
PseudoClasses.Set(pseudoClass, value);
}
}
3 changes: 0 additions & 3 deletions WalletWasabi.Fluent/Controls/Spectrum/SpectrumDrawHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,7 @@ public class SpectrumDrawHandler : IDrawHandler
private const int NumBins = 64;
private const double TextureHeight = 32;
private const double TextureWidth = 32;
private const double Fps = 15.0;

private readonly SpectrumDataSource[] _sources;
private readonly DispatcherTimer _invalidationTimer;
private readonly SpectrumControl _control;
private SKColor _pathColor;
private SKSurface? _surface;
Expand Down
5 changes: 3 additions & 2 deletions WalletWasabi.Fluent/Helpers/NotificationHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ private static bool TryGetNotificationInputs(ProcessedResult result, decimal fia
bool isConfirmedReceive = result.NewlyConfirmedReceivedCoins.Count != 0;
bool isConfirmedSpent = result.NewlyConfirmedReceivedCoins.Count != 0;
Money miningFee = result.Transaction.Transaction.GetFee(result.SpentCoins.Select(x => (ICoin)x.Coin).ToArray()) ?? Money.Zero;
bool isAccelerator = result.Transaction.IsCPFP;

if (isReceived || isSpent)
{
Expand All @@ -78,7 +79,7 @@ private static bool TryGetNotificationInputs(ProcessedResult result, decimal fia
}
else if (isSpent && receiveSpentDiff == miningFee)
{
message = $"Self transfer";
message = isAccelerator ? "Accelerator transaction" : $"Self transfer";
}
else if (incoming > Money.Zero)
{
Expand All @@ -102,7 +103,7 @@ private static bool TryGetNotificationInputs(ProcessedResult result, decimal fia

if (isConfirmedSpent && receiveSpentDiff == miningFee)
{
message = $"Self transfer confirmed";
message = isAccelerator ? "Accelerator transaction confirmed" : $"Self transfer confirmed";
}
else if (incoming > Money.Zero)
{
Expand Down
2 changes: 1 addition & 1 deletion WalletWasabi.Fluent/Models/UI/ApplicationSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ public ApplicationSettings(string persistentConfigFilePath, PersistentConfig per
: FeeDisplayUnit.Satoshis;
_runOnSystemStartup = _uiConfig.RunOnSystemStartup;
_hideOnClose = _uiConfig.HideOnClose;
_useTor = config.UseTor;
_useTor = Config.ObjectToTorMode(_startupConfig.UseTor);
_terminateTorOnExit = _startupConfig.TerminateTorOnExit;
_downloadNewVersion = _startupConfig.DownloadNewVersion;

Expand Down
5 changes: 4 additions & 1 deletion WalletWasabi.Fluent/Styles/SettingsLayout.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
</Style>

<Style Selector="StackPanel.settingsLayout > DockPanel > Button">
<Setter Property="Margin" Value="0 0 -12 0" />
<Setter Property="HorizontalAlignment" Value="Right" />
</Style>

Expand All @@ -30,4 +29,8 @@
<Setter Property="Margin" Value="0 8 0 0" />
<Setter Property="HorizontalAlignment" Value="Right" />
</Style>

<Style Selector="StackPanel.settingsLayout > StackPanel">
<Setter Property="Spacing" Value="6" />
</Style>
</Styles>
13 changes: 0 additions & 13 deletions WalletWasabi.Fluent/ViewModels/Wallets/Coins/CoinListViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -142,11 +142,6 @@ public void Dispose()
_disposables.Dispose();
}

private static ReadOnlyCollection<ICoinModel> GetSelectedCoins(IReadOnlyCollection<CoinViewModel> list)
{
return new ReadOnlyCollection<ICoinModel>(list.Where(item => item.IsSelected == true).Select(x => x.Coin).ToList());
}

private static void UpdateSelection(IEnumerable<CoinViewModel> coinItems, IList<ICoinModel> selectedCoins)
{
var selectedSmartCoins = selectedCoins.GetSmartCoins().ToList();
Expand Down Expand Up @@ -188,12 +183,4 @@ private void RestoreExpandedRows(IEnumerable<LabelsArray> oldItemsLabels)
item.IsExpanded = true;
}
}

private void ExpandSelectedItems()
{
foreach (var item in _itemsCollection.Where(x => x.IsSelected is not false))
{
item.IsExpanded = true;
}
}
}
21 changes: 10 additions & 11 deletions WalletWasabi.Fluent/Views/Settings/BitcoinTabSettingsView.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,15 @@
x:CompileBindings="True">
<StackPanel Classes="settingsLayout">

<StackPanel Spacing="10">
<StackPanel>
<TextBlock Text="Network" />
<ComboBox HorizontalAlignment="Stretch"
ItemsSource="{Binding Networks}"
SelectedItem="{Binding Settings.Network}" />
</StackPanel>

<DockPanel>
<TextBlock VerticalAlignment="Center" Text="(EXPERIMENTAL) Run Bitcoin Knots on startup" />
<TextBlock Text="(EXPERIMENTAL) Run Bitcoin Knots on startup" />
<ToggleSwitch IsChecked="{Binding Settings.StartLocalBitcoinCoreOnStartup}" />
</DockPanel>

Expand All @@ -27,27 +27,26 @@
</DockPanel>

<DockPanel IsVisible="{Binding Settings.StartLocalBitcoinCoreOnStartup}">
<TextBlock VerticalAlignment="Center" Text="Local Bitcoin Core/Knots version" />
<TextBlock Text="Local Bitcoin Core/Knots version" />
<Label Content="{Binding BitcoinCoreVersion}" />
</DockPanel>

<StackPanel Spacing="10" IsVisible="{Binding Settings.StartLocalBitcoinCoreOnStartup}">
<StackPanel IsVisible="{Binding Settings.StartLocalBitcoinCoreOnStartup}">
<TextBlock Text="Bitcoin Core/Knots Data Folder" />
<TextBox Text="{Binding Settings.LocalBitcoinCoreDataDir}" />
</StackPanel>

<DockPanel IsVisible="{Binding !Settings.StartLocalBitcoinCoreOnStartup}"
ToolTip.Tip="Wasabi will download blocks from a full node you control.">
<StackPanel IsVisible="{Binding !Settings.StartLocalBitcoinCoreOnStartup}"
ToolTip.Tip="Wasabi will download blocks from a full node you control.">
<TextBlock Text="Bitcoin P2P Endpoint" />
<PrivacyContentControl PrivacyReplacementMode="Text" VerticalAlignment="Bottom" UseOpacity="True">
<PrivacyContentControl PrivacyReplacementMode="Text" UseOpacity="True">
<TextBox Text="{Binding BitcoinP2PEndPoint}" />
</PrivacyContentControl>
</DockPanel>
</StackPanel>

<StackPanel Spacing="10"
ToolTip.Tip="Coins received from others to already used addresses won't appear below this amount. To prevent potential dust attacks.">
<StackPanel ToolTip.Tip="Coins received from others to already used addresses won't appear below this amount. To prevent potential dust attacks.">
<TextBlock Text="Dust Threshold" />
<CurrencyEntryBox Text="{Binding DustThreshold}" CurrencyCode="BTC" />
<CurrencyEntryBox Classes="standalone" Text="{Binding DustThreshold}" CurrencyCode="BTC" />
</StackPanel>
</StackPanel>
</UserControl>
Original file line number Diff line number Diff line change
Expand Up @@ -44,18 +44,17 @@
</DockPanel>
-->

<DockPanel>
<StackPanel>
<TextBlock Text="Network anonymization (Tor)" />
<ComboBox HorizontalAlignment="Stretch" ItemsSource="{Binding TorModes}" SelectedItem="{Binding Settings.UseTor}" Width="250">
<ComboBox HorizontalAlignment="Stretch" ItemsSource="{Binding TorModes}" SelectedItem="{Binding Settings.UseTor}">
<ComboBox.ItemTemplate>
<DataTemplate x:DataType="basemodels:TorMode">
<TextBlock Text="{Binding Converter={x:Static conv:EnumConverters.ToFriendlyName}}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</DockPanel>
</StackPanel>

<!--<DockPanel IsVisible="{Binding Settings.UseTor}">-->
<DockPanel IsVisible="{Binding Settings.UseTor, ConverterParameter={x:Static basemodels:TorMode.Enabled}, Converter={x:Static conv:EnumToBoolConverter.Instance}}">
<TextBlock Text="Terminate Tor when Wasabi shuts down" />
<ToggleSwitch IsChecked="{Binding Settings.TerminateTorOnExit}" />
Expand All @@ -66,7 +65,7 @@
<ToggleSwitch IsChecked="{Binding Settings.DownloadNewVersion}" />
</DockPanel>

<StackPanel Spacing="10">
<StackPanel>
<TextBlock Text="Fee display unit" />
<ComboBox HorizontalAlignment="Stretch" ItemsSource="{Binding FeeDisplayUnits}" SelectedItem="{Binding Settings.SelectedFeeDisplayUnit}">
<ComboBox.ItemTemplate>
Expand Down
Loading

0 comments on commit b562db4

Please sign in to comment.