Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BIP79 Support #3256

Closed
wants to merge 10 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions WalletWasabi.Gui/Controls/WalletExplorer/SendControlView.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
<DockPanel LastChildFill="True" Margin="20">
<StackPanel DockPanel.Dock="Bottom" Margin="0 10" Spacing="10" HorizontalAlignment="Left">
<TextBlock>Select coins you want to spend.</TextBlock>
<controls:ExtendedTextBox Classes="monospaceFont" Text="{Binding Address}" Watermark="Address" UseFloatingWatermark="True">
<controls:ExtendedTextBox IsVisible="{Binding !Bip79UrlIsVisible}" Classes="monospaceFont" Text="{Binding Address}" Watermark="Destination" UseFloatingWatermark="True">
<i:Interaction.Behaviors>
<behaviors:PasteAddressOnClickBehavior Command="{Binding OnAddressPasteCommand}" />
</i:Interaction.Behaviors>
Expand All @@ -42,8 +42,9 @@
</i:Interaction.Behaviors>
</controls:ExtendedTextBox>

<suggestions:SuggestLabelView DataContext="{Binding LabelSuggestion}" />
<controls:ExtendedTextBox IsVisible="{Binding Bip79UrlIsVisible}" Classes="monospaceFont" Text="{Binding Bip79Url}" Watermark="PayJoin EndPoint" UseFloatingWatermark="True" />

<suggestions:SuggestLabelView DataContext="{Binding LabelSuggestion}" />
<StackPanel Orientation="Horizontal" Spacing="10">
<Button Content="{Binding IsMax, Converter={StaticResource BooleanStringConverter}, ConverterParameter=Clear:Max}" Command="{Binding MaxCommand}" VerticalAlignment="Top" Height="40" />
<controls:ExtendedTextBox
Expand Down
38 changes: 37 additions & 1 deletion WalletWasabi.Gui/Controls/WalletExplorer/SendControlViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ public abstract class SendControlViewModel : WasabiDocumentTabViewModel
private bool _isHardwareBusy;
private bool _isCustomFee;
private bool _isCustomChangeAddress;
private string _bip79;
private ObservableAsPropertyHelper<bool> _bip79UrlIsVisible;

private const string WaitingForHardwareWalletButtonTextString = "Waiting for Hardware Wallet...";

Expand Down Expand Up @@ -230,6 +232,10 @@ protected SendControlViewModel(Wallet wallet, string title)
.ObserveOn(RxApp.MainThreadScheduler)
.Subscribe(_ => this.RaisePropertyChanged(nameof(Address)));

_bip79UrlIsVisible = this.WhenAnyValue(x => x.Bip79Url)
.Select(x => !string.IsNullOrWhiteSpace(x))
.ToProperty(this, x => x.Bip79UrlIsVisible, scheduler: RxApp.MainThreadScheduler);

FeeRateCommand = ReactiveCommand.Create(ChangeFeeRateDisplay, outputScheduler: RxApp.MainThreadScheduler);

OnAddressPasteCommand = ReactiveCommand.Create((BitcoinUrlBuilder url) =>
Expand All @@ -244,6 +250,15 @@ protected SendControlViewModel(Wallet wallet, string title)
{
AmountText = url.Amount.ToString(false, true);
}

if (url.UnknowParameters.ContainsKey("bpu"))
{
Bip79Url = url.UnknowParameters["bpu"];
}
else
{
Bip79Url = "";
}
});

BuildTransactionCommand = ReactiveCommand.CreateFromTask(async () =>
Expand Down Expand Up @@ -380,7 +395,7 @@ protected SendControlViewModel(Wallet wallet, string title)
}
}

BuildTransactionResult result = await Task.Run(() => Wallet.BuildTransaction(Password, intent, feeStrategy, allowUnconfirmed: true, allowedInputs: selectedCoinReferences));
BuildTransactionResult result = await Task.Run(() => Wallet.BuildTransaction(Password, intent, feeStrategy, allowUnconfirmed: true, allowedInputs: selectedCoinReferences, Bip79Url));

await DoAfterBuildTransaction(result);
}
Expand Down Expand Up @@ -787,6 +802,7 @@ public decimal FeePercentage
}

public decimal UsdExchangeRate => _usdExchangeRate?.Value ?? 0m;
public bool Bip79UrlIsVisible => _bip79UrlIsVisible?.Value ?? false;

public Money AllSelectedAmount
{
Expand Down Expand Up @@ -830,6 +846,19 @@ public ErrorDescriptors ValidateActiveAddress()
return new ErrorDescriptors(new ErrorDescriptor(ErrorSeverity.Error, "Invalid address."));
}

public ErrorDescriptors ValidateBip79Url()
{
if (string.IsNullOrWhiteSpace(Bip79Url))
{
return ErrorDescriptors.Empty;
}
if (Uri.TryCreate(Bip79Url, UriKind.Absolute, out _))
{
return ErrorDescriptors.Empty;
}
return new ErrorDescriptors(new ErrorDescriptor(ErrorSeverity.Error, "Invalid url."));
}

public ErrorDescriptors ValidateCustomChangeAddress()
{
if (string.IsNullOrWhiteSpace(CustomChangeAddress))
Expand Down Expand Up @@ -869,6 +898,13 @@ public string CustomChangeAddress
set => this.RaiseAndSetIfChanged(ref _customChangeAddress, value?.Trim());
}

[ValidateMethod(nameof(ValidateBip79Url))]
public string Bip79Url
{
get => _bip79;
set => this.RaiseAndSetIfChanged(ref _bip79, value);
}

public string LabelToolTip
{
get => _labelToolTip;
Expand Down
44 changes: 43 additions & 1 deletion WalletWasabi/Blockchain/Transactions/TransactionFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Text;
using WalletWasabi.Blockchain.Analysis.Clustering;
using WalletWasabi.Blockchain.Keys;
using WalletWasabi.Blockchain.TransactionBuilding;
Expand All @@ -11,6 +13,7 @@
using WalletWasabi.Helpers;
using WalletWasabi.Logging;
using WalletWasabi.Models;
using WalletWasabi.TorSocks5;

namespace WalletWasabi.Blockchain.Transactions
{
Expand Down Expand Up @@ -48,7 +51,9 @@ public TransactionFactory(Network network, KeyManager keyManager, ICoinsView coi
PaymentIntent payments,
Func<FeeRate> feeRateFetcher,
IEnumerable<OutPoint> allowedInputs = null,
Func<LockTime> lockTimeSelector = null)
Func<LockTime> lockTimeSelector = null,
ITorHttpClient httpClient = null,
string uri = null)
{
payments = Guard.NotNull(nameof(payments), payments);
lockTimeSelector ??= () => LockTime.Zero;
Expand Down Expand Up @@ -222,6 +227,43 @@ public TransactionFactory(Network network, KeyManager keyManager, ICoinsView coi
builder.SetLockTime(lockTimeSelector());
builder.SignPSBT(psbt);
psbt.Finalize();
if (uri is { } && httpClient is { })
{
using var httpRequestMessage = new HttpRequestMessage(HttpMethod.Post, uri)
{
Content = new StringContent(psbt.ToBase64(), Encoding.UTF8, "text/plain")
};
using var response = httpClient.SendAsync(httpRequestMessage).Result;
if (response.IsSuccessStatusCode && PSBT.TryParse(response.Content.ReadAsStringAsync().Result, Network, out var payjoinPSBT))
{
bool valid = false;
var existingInputs = psbt.Inputs.Select(input => input.PrevOut).ToList();
foreach (var input in payjoinPSBT.Inputs)
{
var existingInput = existingInputs.SingleOrDefault(point => point == input.PrevOut);
if (existingInput is { })
{
existingInputs.Remove(existingInput);
continue;
}

if (!input.TryFinalizeInput(out _))
{
valid = false;
break;
}
// a new signed input was provided
valid = true;
}

if (valid && !existingInputs.Any())
{
builder.SignPSBT(payjoinPSBT);
payjoinPSBT.Finalize();
psbt = payjoinPSBT;
}
}
}
tx = psbt.ExtractTransaction();

var checkResults = builder.Check(tx).ToList();
Expand Down
7 changes: 5 additions & 2 deletions WalletWasabi/Wallets/Wallet.cs
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,8 @@ public async Task<int> CountBlocksAsync()
PaymentIntent payments,
FeeStrategy feeStrategy,
bool allowUnconfirmed = false,
IEnumerable<OutPoint> allowedInputs = null)
IEnumerable<OutPoint> allowedInputs = null,
string bip79 = null)
{
var builder = new TransactionFactory(Network, KeyManager, Coins, password, allowUnconfirmed);
return builder.BuildTransaction(
Expand All @@ -310,7 +311,9 @@ public async Task<int> CountBlocksAsync()
}
},
allowedInputs,
SelectLockTimeForTransaction);
SelectLockTimeForTransaction,
Synchronizer.WasabiClient.TorClient,
bip79);
}

public void RenameLabel(SmartCoin coin, SmartLabel newLabel)
Expand Down