diff --git a/BTCPayServer/Controllers/UIWalletsController.PSBT.cs b/BTCPayServer/Controllers/UIWalletsController.PSBT.cs index 79999c7e2..d5f3fb759 100644 --- a/BTCPayServer/Controllers/UIWalletsController.PSBT.cs +++ b/BTCPayServer/Controllers/UIWalletsController.PSBT.cs @@ -86,6 +86,7 @@ namespace BTCPayServer.Controllers BackUrl = vm.BackUrl }); } + switch (command) { case "vault": @@ -170,6 +171,7 @@ namespace BTCPayServer.Controllers { return View("WalletPSBT", vm); } + switch (command) { case "createpending": @@ -373,14 +375,25 @@ namespace BTCPayServer.Controllers vm.FeeRate = feeRate.ToString(); } - var sanityErrors = psbtObject.CheckSanity(); - if (sanityErrors.Count != 0) + if (!psbtObject.IsAllFinalized()) { - vm.SetErrors(sanityErrors); - } - else if (!psbtObject.IsAllFinalized() && !psbtObject.TryFinalize(out var errors)) - { - vm.SetErrors(errors); + var sanityErrors = new List(); + foreach (var input in psbtObject.Inputs) + { + if (input.IsFinalized()) + 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) diff --git a/BTCPayServer/Controllers/UIWalletsController.cs b/BTCPayServer/Controllers/UIWalletsController.cs index e2a353732..68c61bdb1 100644 --- a/BTCPayServer/Controllers/UIWalletsController.cs +++ b/BTCPayServer/Controllers/UIWalletsController.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Net.Mime; +using System.Text; using System.Threading; using System.Threading.Tasks; using BTCPayServer.Abstractions.Constants; @@ -1289,6 +1290,9 @@ namespace BTCPayServer.Controllers ChangeAddress = psbtResponse.ChangeAddress?.ToString(), PSBT = psbt.ToHex() }; + + if (!psbt.IsReadyToSign() && command == "sign") + command = "analyze-psbt"; switch (command) { case "createpending": @@ -1512,9 +1516,9 @@ namespace BTCPayServer.Controllers 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) @@ -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, FiatRate? rate) => new(