Give more information to the user if payjoin fails

This commit is contained in:
nicolas.dorier
2020-04-06 13:19:51 +09:00
parent 9af7edf8b8
commit d939baac84
2 changed files with 53 additions and 46 deletions

View File

@@ -8,6 +8,7 @@ using System.Threading.Tasks;
using BTCPayServer.ModelBinders;
using BTCPayServer.Models;
using BTCPayServer.Models.WalletViewModels;
using BTCPayServer.Services;
using Microsoft.AspNetCore.Mvc;
using NBitcoin;
using NBXplorer;
@@ -144,25 +145,15 @@ namespace BTCPayServer.Controllers
}
}
private async Task<PSBT> TryGetPayjoinProposedTX(string bpu, PSBT psbt, DerivationSchemeSettings derivationSchemeSettings, BTCPayNetwork btcPayNetwork, CancellationToken cancellationToken)
{
if (!string.IsNullOrEmpty(bpu) && Uri.TryCreate(bpu, UriKind.Absolute, out var endpoint))
private async Task<PSBT> GetPayjoinProposedTX(string bpu, PSBT psbt, DerivationSchemeSettings derivationSchemeSettings, BTCPayNetwork btcPayNetwork, CancellationToken cancellationToken)
{
if (string.IsNullOrEmpty(bpu) || !Uri.TryCreate(bpu, UriKind.Absolute, out var endpoint))
throw new InvalidOperationException("No payjoin url available");
var cloned = psbt.Clone();
cloned = cloned.Finalize();
await _broadcaster.Schedule(DateTimeOffset.UtcNow + TimeSpan.FromMinutes(1.0), cloned.ExtractTransaction(), btcPayNetwork);
try
{
return await _payjoinClient.RequestPayjoin(endpoint, derivationSchemeSettings, cloned, cancellationToken);
}
catch (Exception)
{
return null;
}
}
return null;
}
[HttpGet]
[Route("{walletId}/psbt/ready")]
@@ -321,22 +312,11 @@ namespace BTCPayServer.Controllers
switch (command)
{
case "payjoin":
var proposedPayjoin =await
TryGetPayjoinProposedTX(vm.PayJoinEndpointUrl, psbt, derivationSchemeSettings, network, cancellationToken);
if (proposedPayjoin == null)
string error = null;
try
{
//we possibly exposed the tx to the receiver, so we need to broadcast straight away
TempData.SetStatusMessageModel(new StatusMessageModel()
{
Severity = StatusMessageModel.StatusSeverity.Warning,
AllowDismiss = false,
Html = $"The payjoin transaction could not be created. The original transaction was broadcast instead."
});
return await WalletPSBTReady(walletId, vm, "broadcast");
}
else
{
var proposedPayjoin = await GetPayjoinProposedTX(vm.PayJoinEndpointUrl, psbt,
derivationSchemeSettings, network, cancellationToken);
try
{
var extKey = ExtKey.Parse(vm.SigningKey, network.NBitcoinNetwork);
@@ -345,20 +325,53 @@ namespace BTCPayServer.Controllers
RootedKeyPath.Parse(vm.SigningKeyPath));
vm.PSBT = proposedPayjoin.ToBase64();
vm.OriginalPSBT = psbt.ToBase64();
proposedPayjoin.Finalize();
TempData.SetStatusMessageModel(new StatusMessageModel()
{
Severity = StatusMessageModel.StatusSeverity.Success,
AllowDismiss = false,
Html = $"The payjoin transaction has been successfully broadcasted ({proposedPayjoin.ExtractTransaction().GetHash()})"
});
return await WalletPSBTReady(walletId, vm, "broadcast");
}
catch (Exception)
{
TempData.SetStatusMessageModel(new StatusMessageModel()
{
Severity = StatusMessageModel.StatusSeverity.Info,
Severity = StatusMessageModel.StatusSeverity.Warning,
AllowDismiss = false,
Html =
$"This transaction has been coordinated between the receiver and you to create a <a href='https://en.bitcoin.it/wiki/PayJoin' target='_blank'>payjoin transaction</a> by adding inputs from the receiver. The amount being sent may appear higher but is in fact the same"
$"This transaction has been coordinated between the receiver and you to create a <a href='https://en.bitcoin.it/wiki/PayJoin' target='_blank'>payjoin transaction</a> by adding inputs from the receiver.<br/>" +
$"The amount being sent may appear higher but is in fact almost same.<br/><br/>" +
$"If you cancel refuse to sign this transaction, the payment will proceed without payjoin"
});
return ViewVault(walletId, proposedPayjoin, vm.PayJoinEndpointUrl, psbt);
}
}
catch (PayjoinReceiverException ex)
{
error = $"The payjoin receiver could not complete the payjoin: {ex.Message}";
}
catch (PayjoinSenderException ex)
{
error = $"We rejected the receiver's payjoin proposal: {ex.Message}";
}
catch (Exception ex)
{
error = $"Unexpected payjoin error: {ex.Message}";
}
//we possibly exposed the tx to the receiver, so we need to broadcast straight away
psbt.Finalize();
TempData.SetStatusMessageModel(new StatusMessageModel()
{
Severity = StatusMessageModel.StatusSeverity.Warning,
AllowDismiss = false,
Html = $"The payjoin transaction could not be created.<br/>" +
$"The original transaction was broadcasted instead. ({psbt.ExtractTransaction().GetHash()})<br/><br/>" +
$"{error}"
});
return await WalletPSBTReady(walletId, vm, "broadcast");
case "broadcast" when !psbt.IsAllFinalized() && !psbt.TryFinalize(out var errors):
vm.SetErrors(errors);
return View(nameof(WalletPSBTReady),vm);
@@ -376,7 +389,7 @@ namespace BTCPayServer.Controllers
{
Severity = StatusMessageModel.StatusSeverity.Warning,
AllowDismiss = false,
Html = $"The payjoin transaction could not be broadcast.<br/>({broadcastResult.RPCCode} {broadcastResult.RPCCodeMessage} {broadcastResult.RPCMessage}).<br/>The transaction has been reverted back to its original format and has been broadcast."
Html = $"The payjoin transaction could not be broadcasted.<br/>({broadcastResult.RPCCode} {broadcastResult.RPCCodeMessage} {broadcastResult.RPCMessage}).<br/>The transaction has been reverted back to its original format and has been broadcast."
});
vm.PSBT = vm.OriginalPSBT;
vm.OriginalPSBT = null;
@@ -392,6 +405,11 @@ namespace BTCPayServer.Controllers
vm.GlobalError = "Error while broadcasting: " + ex.Message;
return View(nameof(WalletPSBTReady),vm);
}
if (!TempData.HasStatusMessage())
{
TempData[WellKnownTempData.SuccessMessage] = $"Transaction broadcasted successfully ({transaction.GetHash()})";
}
return RedirectToWalletTransaction(walletId, transaction);
}
case "analyze-psbt":

View File

@@ -869,17 +869,6 @@ namespace BTCPayServer.Controllers
var wallet = _walletProvider.GetWallet(network);
var derivationSettings = GetDerivationSchemeSettings(walletId);
wallet.InvalidateCache(derivationSettings.AccountDerivation);
if (TempData.GetStatusMessageModel() == null)
{
TempData[WellKnownTempData.SuccessMessage] =
$"Transaction broadcasted successfully ({transaction.GetHash()})";
}
else
{
var statusMessageModel = TempData.GetStatusMessageModel();
statusMessageModel.Message += $" ({transaction.GetHash()})";
TempData.SetStatusMessageModel(statusMessageModel);
}
}
return RedirectToAction(nameof(WalletTransactions), new { walletId = walletId.ToString() });
}