Better error message on invalid PSBT in Sign with Seed (#6920)

* Better error message on invalid PSBT in Sign with Seed

* Improve error reporting if a PSBT built by SendWallet is not signable
This commit is contained in:
Nicolas Dorier
2025-09-18 10:49:54 +09:00
committed by GitHub
parent 790616d2a6
commit fa29176a9c
2 changed files with 47 additions and 9 deletions

View File

@@ -86,6 +86,7 @@ namespace BTCPayServer.Controllers
BackUrl = vm.BackUrl BackUrl = vm.BackUrl
}); });
} }
switch (command) switch (command)
{ {
case "vault": case "vault":
@@ -170,6 +171,7 @@ namespace BTCPayServer.Controllers
{ {
return View("WalletPSBT", vm); return View("WalletPSBT", vm);
} }
switch (command) switch (command)
{ {
case "createpending": case "createpending":
@@ -373,14 +375,25 @@ namespace BTCPayServer.Controllers
vm.FeeRate = feeRate.ToString(); vm.FeeRate = feeRate.ToString();
} }
var sanityErrors = psbtObject.CheckSanity(); if (!psbtObject.IsAllFinalized())
if (sanityErrors.Count != 0)
{ {
vm.SetErrors(sanityErrors); var sanityErrors = new List<PSBTError>();
} foreach (var input in psbtObject.Inputs)
else if (!psbtObject.IsAllFinalized() && !psbtObject.TryFinalize(out var errors)) {
{ if (input.IsFinalized())
vm.SetErrors(errors); continue;
if (input.GetSignableCoin(out var missingCoin) is null)
{
sanityErrors.Add(new PSBTError(input.Index, missingCoin));
}
else if (!input.TryFinalizeInput(out var err))
{
sanityErrors.Add(err[0]);
}
}
if (sanityErrors.Count > 0)
vm.SetErrors(sanityErrors);
} }
var combinedTypeIds = inputToObjects.Values.SelectMany(ids => ids).Concat(outputToObjects.Values) var combinedTypeIds = inputToObjects.Values.SelectMany(ids => ids).Concat(outputToObjects.Values)

View File

@@ -4,6 +4,7 @@ using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.Linq; using System.Linq;
using System.Net.Mime; using System.Net.Mime;
using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using BTCPayServer.Abstractions.Constants; using BTCPayServer.Abstractions.Constants;
@@ -1289,6 +1290,9 @@ namespace BTCPayServer.Controllers
ChangeAddress = psbtResponse.ChangeAddress?.ToString(), ChangeAddress = psbtResponse.ChangeAddress?.ToString(),
PSBT = psbt.ToHex() PSBT = psbt.ToHex()
}; };
if (!psbt.IsReadyToSign() && command == "sign")
command = "analyze-psbt";
switch (command) switch (command)
{ {
case "createpending": case "createpending":
@@ -1512,9 +1516,9 @@ namespace BTCPayServer.Controllers
var psbt = PSBT.Parse(viewModel.SigningContext.PSBT, network.NBitcoinNetwork); var psbt = PSBT.Parse(viewModel.SigningContext.PSBT, network.NBitcoinNetwork);
if (!psbt.IsReadyToSign()) if (!psbt.IsReadyToSign(out var errors))
{ {
ModelState.AddModelError(nameof(viewModel.SigningContext.PSBT), "PSBT is not ready to be signed"); ModelState.AddModelError(nameof(viewModel.SigningContext.PSBT), BuildErrorMessage(errors));
} }
if (!ModelState.IsValid) if (!ModelState.IsValid)
@@ -1580,6 +1584,27 @@ namespace BTCPayServer.Controllers
}); });
} }
private static string BuildErrorMessage(PSBTError[] errors)
{
StringBuilder errorMessage = new();
errorMessage.Append("PSBT is not ready to be signed.");
if (errors.Length == 1)
{
errorMessage.Append($" ({errors[0]})");
}
else
{
errorMessage.AppendLine();
foreach (var error in errors.Take(5))
{
errorMessage.AppendLine(error.ToString());
}
}
if (errors.Length > 5)
errorMessage.Append($"{errors.Length - 5} more errors...");
return errorMessage.ToString();
}
private WalletPSBTReadyViewModel.StringAmounts ValueToString(Money v, BTCPayNetworkBase network, private WalletPSBTReadyViewModel.StringAmounts ValueToString(Money v, BTCPayNetworkBase network,
FiatRate? rate) => FiatRate? rate) =>
new( new(