diff --git a/BTCPayServer.Tests/PSBTTests.cs b/BTCPayServer.Tests/PSBTTests.cs index 4c1a37e43..0b6588d76 100644 --- a/BTCPayServer.Tests/PSBTTests.cs +++ b/BTCPayServer.Tests/PSBTTests.cs @@ -68,7 +68,6 @@ namespace BTCPayServer.Tests var vmLedger = await walletController.WalletSend(walletId, sendModel, command: "ledger").AssertViewModelAsync(); PSBT.Parse(vmLedger.PSBT, user.SupportedNetwork.NBitcoinNetwork); BitcoinAddress.Create(vmLedger.HintChange, user.SupportedNetwork.NBitcoinNetwork); - Assert.NotNull(vmLedger.SuccessPath); Assert.NotNull(vmLedger.WebsocketPath); var redirectedPSBT = (string)Assert.IsType(await walletController.WalletSend(walletId, sendModel, command: "analyze-psbt")).RouteValues["psbt"]; diff --git a/BTCPayServer/Controllers/WalletsController.PSBT.cs b/BTCPayServer/Controllers/WalletsController.PSBT.cs index 2ce808bef..faf9fd54c 100644 --- a/BTCPayServer/Controllers/WalletsController.PSBT.cs +++ b/BTCPayServer/Controllers/WalletsController.PSBT.cs @@ -69,6 +69,8 @@ namespace BTCPayServer.Controllers WalletId walletId, WalletPSBTViewModel vm, string command = null) { + if (command == null) + return await WalletPSBT(walletId, vm); var network = NetworkProvider.GetNetwork(walletId.CryptoCode); var psbt = await vm.GetPSBT(network.NBitcoinNetwork); if (psbt == null) @@ -78,7 +80,7 @@ namespace BTCPayServer.Controllers } switch (command) { - case null: + case "decode": vm.Decoded = psbt.ToString(); ModelState.Remove(nameof(vm.PSBT)); ModelState.Remove(nameof(vm.FileName)); @@ -97,7 +99,7 @@ namespace BTCPayServer.Controllers return View(vm); } TempData[WellKnownTempData.SuccessMessage] = "PSBT updated!"; - return RedirectToAction(nameof(WalletPSBT), new { walletId = walletId, psbt = psbt.ToBase64(), FileName = vm.FileName }); + return RedirectToWalletPSBT(walletId, psbt, vm.FileName); case "seed": return SignWithSeed(walletId, psbt.ToBase64()); case "broadcast": @@ -258,6 +260,8 @@ namespace BTCPayServer.Controllers [ModelBinder(typeof(WalletIdModelBinder))] WalletId walletId, WalletPSBTReadyViewModel vm, string command = null) { + if (command == null) + return await WalletPSBTReady(walletId, vm.PSBT, vm.SigningKey, vm.SigningKeyPath); PSBT psbt = null; var network = NetworkProvider.GetNetwork(walletId.CryptoCode); try @@ -299,7 +303,7 @@ namespace BTCPayServer.Controllers } else if (command == "analyze-psbt") { - return RedirectToAction(nameof(WalletPSBT), new { walletId = walletId, psbt = psbt.ToBase64() }); + return RedirectToWalletPSBT(walletId, psbt); } else { @@ -308,27 +312,6 @@ namespace BTCPayServer.Controllers } } - private IActionResult ViewPSBT(PSBT psbt, IEnumerable errors = null) - { - return ViewPSBT(psbt, null, errors?.Select(e => e.ToString()).ToList()); - } - private IActionResult ViewPSBT(PSBT psbt, IEnumerable errors = null) - { - return ViewPSBT(psbt, null, errors); - } - private IActionResult ViewPSBT(PSBT psbt, string fileName, IEnumerable errors = null) - { - ModelState.Remove(nameof(WalletPSBTViewModel.PSBT)); - ModelState.Remove(nameof(WalletPSBTViewModel.FileName)); - return View(nameof(WalletPSBT), new WalletPSBTViewModel() - { - Decoded = psbt.ToString(), - FileName = fileName ?? string.Empty, - PSBT = psbt.ToBase64(), - Errors = errors?.ToList() - }); - } - private IActionResult FilePSBT(PSBT psbt, string fileName) { return File(psbt.ToBytes(), "application/octet-stream", fileName); @@ -354,7 +337,7 @@ namespace BTCPayServer.Controllers } sourcePSBT = sourcePSBT.Combine(psbt); TempData[WellKnownTempData.SuccessMessage] = "PSBT Successfully combined!"; - return ViewPSBT(sourcePSBT); + return RedirectToWalletPSBT(walletId, sourcePSBT); } } } diff --git a/BTCPayServer/Controllers/WalletsController.cs b/BTCPayServer/Controllers/WalletsController.cs index 803b62e56..e8565db1a 100644 --- a/BTCPayServer/Controllers/WalletsController.cs +++ b/BTCPayServer/Controllers/WalletsController.cs @@ -456,21 +456,59 @@ namespace BTCPayServer.Controllers case "analyze-psbt": var name = $"Send-{string.Join('_', vm.Outputs.Select(output => $"{output.Amount}->{output.DestinationAddress}{(output.SubtractFeesFromOutput ? "-Fees" : string.Empty)}"))}.psbt"; - return RedirectToAction(nameof(WalletPSBT), new { walletId = walletId, psbt = psbt.PSBT.ToBase64(), FileName = name }); + return RedirectToWalletPSBT(walletId, psbt.PSBT, name); default: return View(vm); } } + private IActionResult RedirectToWalletPSBT(WalletId walletId, PSBT psbt, string fileName = null) + { + var vm = new PostRedictViewModel() + { + AspController = "Wallets", + AspAction = nameof(WalletPSBT), + Parameters = + { + new KeyValuePair("psbt", psbt.ToBase64()) + } + }; + if (!string.IsNullOrEmpty(fileName)) + vm.Parameters.Add(new KeyValuePair("fileName", fileName)); + return View("PostRedirect", vm); + } + + void SetAmbientPSBT(PSBT psbt) + { + if (psbt != null) + TempData["AmbientPSBT"] = psbt.ToBase64(); + else + TempData.Remove("AmbientPSBT"); + } + PSBT GetAmbientPSBT(Network network, bool peek) + { + if (network == null) + throw new ArgumentNullException(nameof(network)); + if ((peek ? TempData.Peek("AmbientPSBT") : TempData["AmbientPSBT"]) is string str) + { + try + { + return PSBT.Parse(str, network); + } + catch { } + } + return null; + } + private ViewResult ViewWalletSendLedger(PSBT psbt, BitcoinAddress hintChange = null) { + SetAmbientPSBT(psbt); return View("WalletSendLedger", new WalletSendLedgerModel() { PSBT = psbt.ToBase64(), HintChange = hintChange?.ToString(), - WebsocketPath = this.Url.Action(nameof(LedgerConnection)), - SuccessPath = this.Url.Action(nameof(WalletPSBTReady)) + WebsocketPath = this.Url.Action(nameof(LedgerConnection)) }); } @@ -702,7 +740,6 @@ namespace BTCPayServer.Controllers // getxpub int account = 0, // sendtoaddress - string psbt = null, string hintChange = null ) { @@ -712,6 +749,7 @@ namespace BTCPayServer.Controllers var network = NetworkProvider.GetNetwork(walletId.CryptoCode); if (network == null) throw new FormatException("Invalid value for crypto code"); + PSBT psbt = GetAmbientPSBT(network.NBitcoinNetwork, true); var derivationSettings = GetDerivationSchemeSettings(walletId); var webSocket = await HttpContext.WebSockets.AcceptWebSocketAsync(); @@ -721,7 +759,6 @@ namespace BTCPayServer.Controllers { normalOperationTimeout.CancelAfter(TimeSpan.FromMinutes(30)); var hw = new LedgerHardwareWalletService(webSocket); - var model = new WalletSendLedgerModel(); object result = null; try { @@ -775,18 +812,12 @@ namespace BTCPayServer.Controllers await Repository.UpdateStore(storeData); } - var psbtResponse = new CreatePSBTResponse() - { - PSBT = PSBT.Parse(psbt, network.NBitcoinNetwork), - ChangeAddress = string.IsNullOrEmpty(hintChange) ? null : BitcoinAddress.Create(hintChange, network.NBitcoinNetwork) - }; - - - derivationSettings.RebaseKeyPaths(psbtResponse.PSBT); - + derivationSettings.RebaseKeyPaths(psbt); + var changeAddress = string.IsNullOrEmpty(hintChange) ? null : BitcoinAddress.Create(hintChange, network.NBitcoinNetwork); signTimeout.CancelAfter(TimeSpan.FromMinutes(5)); - psbtResponse.PSBT = await hw.SignTransactionAsync(psbtResponse.PSBT, accountKey.GetRootedKeyPath(), accountKey.AccountKey, psbtResponse.ChangeAddress?.ScriptPubKey, signTimeout.Token); - result = new SendToAddressResult() { PSBT = psbtResponse.PSBT.ToBase64() }; + psbt = await hw.SignTransactionAsync(psbt, accountKey.GetRootedKeyPath(), accountKey.AccountKey, changeAddress?.ScriptPubKey, signTimeout.Token); + SetAmbientPSBT(null); + result = new SendToAddressResult() { PSBT = psbt.ToBase64() }; } } catch (OperationCanceledException) diff --git a/BTCPayServer/Models/PostRedictViewModel.cs b/BTCPayServer/Models/PostRedictViewModel.cs new file mode 100644 index 000000000..0895f1199 --- /dev/null +++ b/BTCPayServer/Models/PostRedictViewModel.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace BTCPayServer.Models +{ + public class PostRedictViewModel + { + public string AspAction { get; set; } + public string AspController { get; set; } + public List> Parameters { get; set; } = new List>(); + } +} diff --git a/BTCPayServer/Models/WalletViewModels/WalletSendLedgerModel.cs b/BTCPayServer/Models/WalletViewModels/WalletSendLedgerModel.cs index 2709fd591..a5496a37d 100644 --- a/BTCPayServer/Models/WalletViewModels/WalletSendLedgerModel.cs +++ b/BTCPayServer/Models/WalletViewModels/WalletSendLedgerModel.cs @@ -10,6 +10,5 @@ namespace BTCPayServer.Models.WalletViewModels public string WebsocketPath { get; set; } public string PSBT { get; set; } public string HintChange { get; set; } - public string SuccessPath { get; set; } } } diff --git a/BTCPayServer/Views/Shared/PostRedirect.cshtml b/BTCPayServer/Views/Shared/PostRedirect.cshtml new file mode 100644 index 000000000..3492aec88 --- /dev/null +++ b/BTCPayServer/Views/Shared/PostRedirect.cshtml @@ -0,0 +1,18 @@ +@model PostRedictViewModel +@{ + Layout = null; +} + + + + +
+ @foreach(var o in Model.Parameters) { + + } +
+ + + diff --git a/BTCPayServer/Views/Wallets/WalletPSBT.cshtml b/BTCPayServer/Views/Wallets/WalletPSBT.cshtml index 7e5e9df13..3123baa6c 100644 --- a/BTCPayServer/Views/Wallets/WalletPSBT.cshtml +++ b/BTCPayServer/Views/Wallets/WalletPSBT.cshtml @@ -65,7 +65,7 @@ - + diff --git a/BTCPayServer/Views/Wallets/WalletSendLedger.cshtml b/BTCPayServer/Views/Wallets/WalletSendLedger.cshtml index 22a399eb0..9e2004f70 100644 --- a/BTCPayServer/Views/Wallets/WalletSendLedger.cshtml +++ b/BTCPayServer/Views/Wallets/WalletSendLedger.cshtml @@ -12,10 +12,11 @@
- - - - +

You can send money received by this store to an address with the help of your Ledger Wallet.
If you don't have a Ledger Wallet, use Electrum with your favorite hardware wallet to transfer crypto.
diff --git a/BTCPayServer/wwwroot/js/WalletSendLedger.js b/BTCPayServer/wwwroot/js/WalletSendLedger.js index 87d50727f..9dac37204 100644 --- a/BTCPayServer/wwwroot/js/WalletSendLedger.js +++ b/BTCPayServer/wwwroot/js/WalletSendLedger.js @@ -1,7 +1,6 @@ $(function () { var psbt = $("#PSBT").val(); var hintChange = $("#HintChange").val(); - var successPath = $("#SuccessPath").val(); var websocketPath = $("#WebsocketPath").val(); var loc = window.location, ws_uri; @@ -62,7 +61,8 @@ if (result.error) { WriteAlert("danger", result.error); } else { - window.location.replace(loc.protocol + "//" + loc.host + successPath + "?psbt=" + encodeURIComponent(result.psbt)); + $("#PSBT").val(result.psbt); + $("#broadcastForm").submit(); } }); };