mirror of
https://github.com/aljazceru/btcpayserver.git
synced 2025-12-18 06:24:24 +01:00
Give more information to the user if payjoin fails
This commit is contained in:
@@ -8,6 +8,7 @@ using System.Threading.Tasks;
|
|||||||
using BTCPayServer.ModelBinders;
|
using BTCPayServer.ModelBinders;
|
||||||
using BTCPayServer.Models;
|
using BTCPayServer.Models;
|
||||||
using BTCPayServer.Models.WalletViewModels;
|
using BTCPayServer.Models.WalletViewModels;
|
||||||
|
using BTCPayServer.Services;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using NBitcoin;
|
using NBitcoin;
|
||||||
using NBXplorer;
|
using NBXplorer;
|
||||||
@@ -144,25 +145,15 @@ namespace BTCPayServer.Controllers
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<PSBT> TryGetPayjoinProposedTX(string bpu, PSBT psbt, DerivationSchemeSettings derivationSchemeSettings, BTCPayNetwork btcPayNetwork, CancellationToken cancellationToken)
|
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))
|
|
||||||
{
|
{
|
||||||
|
if (string.IsNullOrEmpty(bpu) || !Uri.TryCreate(bpu, UriKind.Absolute, out var endpoint))
|
||||||
|
throw new InvalidOperationException("No payjoin url available");
|
||||||
var cloned = psbt.Clone();
|
var cloned = psbt.Clone();
|
||||||
cloned = cloned.Finalize();
|
cloned = cloned.Finalize();
|
||||||
await _broadcaster.Schedule(DateTimeOffset.UtcNow + TimeSpan.FromMinutes(1.0), cloned.ExtractTransaction(), btcPayNetwork);
|
await _broadcaster.Schedule(DateTimeOffset.UtcNow + TimeSpan.FromMinutes(1.0), cloned.ExtractTransaction(), btcPayNetwork);
|
||||||
try
|
|
||||||
{
|
|
||||||
return await _payjoinClient.RequestPayjoin(endpoint, derivationSchemeSettings, cloned, cancellationToken);
|
return await _payjoinClient.RequestPayjoin(endpoint, derivationSchemeSettings, cloned, cancellationToken);
|
||||||
}
|
}
|
||||||
catch (Exception)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
[Route("{walletId}/psbt/ready")]
|
[Route("{walletId}/psbt/ready")]
|
||||||
@@ -321,22 +312,11 @@ namespace BTCPayServer.Controllers
|
|||||||
switch (command)
|
switch (command)
|
||||||
{
|
{
|
||||||
case "payjoin":
|
case "payjoin":
|
||||||
var proposedPayjoin =await
|
string error = null;
|
||||||
TryGetPayjoinProposedTX(vm.PayJoinEndpointUrl, psbt, derivationSchemeSettings, network, cancellationToken);
|
try
|
||||||
if (proposedPayjoin == null)
|
|
||||||
{
|
{
|
||||||
//we possibly exposed the tx to the receiver, so we need to broadcast straight away
|
var proposedPayjoin = await GetPayjoinProposedTX(vm.PayJoinEndpointUrl, psbt,
|
||||||
TempData.SetStatusMessageModel(new StatusMessageModel()
|
derivationSchemeSettings, network, cancellationToken);
|
||||||
{
|
|
||||||
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
|
|
||||||
{
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var extKey = ExtKey.Parse(vm.SigningKey, network.NBitcoinNetwork);
|
var extKey = ExtKey.Parse(vm.SigningKey, network.NBitcoinNetwork);
|
||||||
@@ -345,20 +325,53 @@ namespace BTCPayServer.Controllers
|
|||||||
RootedKeyPath.Parse(vm.SigningKeyPath));
|
RootedKeyPath.Parse(vm.SigningKeyPath));
|
||||||
vm.PSBT = proposedPayjoin.ToBase64();
|
vm.PSBT = proposedPayjoin.ToBase64();
|
||||||
vm.OriginalPSBT = psbt.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");
|
return await WalletPSBTReady(walletId, vm, "broadcast");
|
||||||
}
|
}
|
||||||
catch (Exception)
|
catch (Exception)
|
||||||
{
|
{
|
||||||
TempData.SetStatusMessageModel(new StatusMessageModel()
|
TempData.SetStatusMessageModel(new StatusMessageModel()
|
||||||
{
|
{
|
||||||
Severity = StatusMessageModel.StatusSeverity.Info,
|
Severity = StatusMessageModel.StatusSeverity.Warning,
|
||||||
AllowDismiss = false,
|
AllowDismiss = false,
|
||||||
Html =
|
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);
|
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):
|
case "broadcast" when !psbt.IsAllFinalized() && !psbt.TryFinalize(out var errors):
|
||||||
vm.SetErrors(errors);
|
vm.SetErrors(errors);
|
||||||
return View(nameof(WalletPSBTReady),vm);
|
return View(nameof(WalletPSBTReady),vm);
|
||||||
@@ -376,7 +389,7 @@ namespace BTCPayServer.Controllers
|
|||||||
{
|
{
|
||||||
Severity = StatusMessageModel.StatusSeverity.Warning,
|
Severity = StatusMessageModel.StatusSeverity.Warning,
|
||||||
AllowDismiss = false,
|
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.PSBT = vm.OriginalPSBT;
|
||||||
vm.OriginalPSBT = null;
|
vm.OriginalPSBT = null;
|
||||||
@@ -392,6 +405,11 @@ namespace BTCPayServer.Controllers
|
|||||||
vm.GlobalError = "Error while broadcasting: " + ex.Message;
|
vm.GlobalError = "Error while broadcasting: " + ex.Message;
|
||||||
return View(nameof(WalletPSBTReady),vm);
|
return View(nameof(WalletPSBTReady),vm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!TempData.HasStatusMessage())
|
||||||
|
{
|
||||||
|
TempData[WellKnownTempData.SuccessMessage] = $"Transaction broadcasted successfully ({transaction.GetHash()})";
|
||||||
|
}
|
||||||
return RedirectToWalletTransaction(walletId, transaction);
|
return RedirectToWalletTransaction(walletId, transaction);
|
||||||
}
|
}
|
||||||
case "analyze-psbt":
|
case "analyze-psbt":
|
||||||
|
|||||||
@@ -869,17 +869,6 @@ namespace BTCPayServer.Controllers
|
|||||||
var wallet = _walletProvider.GetWallet(network);
|
var wallet = _walletProvider.GetWallet(network);
|
||||||
var derivationSettings = GetDerivationSchemeSettings(walletId);
|
var derivationSettings = GetDerivationSchemeSettings(walletId);
|
||||||
wallet.InvalidateCache(derivationSettings.AccountDerivation);
|
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() });
|
return RedirectToAction(nameof(WalletTransactions), new { walletId = walletId.ToString() });
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user