From 25e6f82aa36443d1853fb412113ae04bd7d66445 Mon Sep 17 00:00:00 2001 From: "nicolas.dorier" Date: Mon, 25 May 2020 04:55:28 +0900 Subject: [PATCH] Refactor parameter passing in wallet functions --- .../BTCPayServer.Client.csproj | 2 +- .../BTCPayServer.Common.csproj | 2 +- BTCPayServer.Tests/PSBTTests.cs | 5 +- BTCPayServer/BTCPayServer.csproj | 4 +- .../Controllers/WalletsController.PSBT.cs | 53 +++++++----- BTCPayServer/Controllers/WalletsController.cs | 81 ++++++++++++------- .../WalletViewModels/SignWithSeedViewModel.cs | 2 +- .../WalletViewModels/SigningContextModel.cs | 13 +++ .../WalletPSBTReadyViewModel.cs | 2 +- .../WalletViewModels/WalletPSBTViewModel.cs | 2 +- .../WalletViewModels/WalletSendVaultModel.cs | 2 +- .../Views/Wallets/SignWithSeed.cshtml | 3 +- BTCPayServer/Views/Wallets/WalletPSBT.cshtml | 13 +-- .../Views/Wallets/WalletPSBTReady.cshtml | 13 +-- .../Views/Wallets/WalletSendVault.cshtml | 7 +- 15 files changed, 130 insertions(+), 74 deletions(-) create mode 100644 BTCPayServer/Models/WalletViewModels/SigningContextModel.cs diff --git a/BTCPayServer.Client/BTCPayServer.Client.csproj b/BTCPayServer.Client/BTCPayServer.Client.csproj index 51de680e0..56c8ae2a3 100644 --- a/BTCPayServer.Client/BTCPayServer.Client.csproj +++ b/BTCPayServer.Client/BTCPayServer.Client.csproj @@ -5,7 +5,7 @@ - + diff --git a/BTCPayServer.Common/BTCPayServer.Common.csproj b/BTCPayServer.Common/BTCPayServer.Common.csproj index 1908b7d92..68f78a97f 100644 --- a/BTCPayServer.Common/BTCPayServer.Common.csproj +++ b/BTCPayServer.Common/BTCPayServer.Common.csproj @@ -4,6 +4,6 @@ - + diff --git a/BTCPayServer.Tests/PSBTTests.cs b/BTCPayServer.Tests/PSBTTests.cs index 85130c43e..ddda2d953 100644 --- a/BTCPayServer.Tests/PSBTTests.cs +++ b/BTCPayServer.Tests/PSBTTests.cs @@ -120,7 +120,10 @@ namespace BTCPayServer.Tests Assert.True(signedPSBT2.TryFinalize(out _)); Assert.Equal(signedPSBT, signedPSBT2); - var ready = (await walletController.WalletPSBTReady(walletId, signedPSBT.ToBase64())).AssertViewModel(); + var ready = (await walletController.WalletPSBTReady(walletId, new WalletPSBTReadyViewModel() + { + PSBT = signedPSBT.ToBase64() + })).AssertViewModel(); Assert.Equal(signedPSBT.ToBase64(), ready.PSBT); psbt = AssertRedirectedPSBT(await walletController.WalletPSBTReady(walletId, ready, command: "analyze-psbt"), nameof(walletController.WalletPSBT)); Assert.Equal(signedPSBT.ToBase64(), psbt); diff --git a/BTCPayServer/BTCPayServer.csproj b/BTCPayServer/BTCPayServer.csproj index b8898f886..ecce21866 100644 --- a/BTCPayServer/BTCPayServer.csproj +++ b/BTCPayServer/BTCPayServer.csproj @@ -2,8 +2,8 @@ - false - $(DefineConstants);RAZOR_RUNTIME_COMPILE + Exe diff --git a/BTCPayServer/Controllers/WalletsController.PSBT.cs b/BTCPayServer/Controllers/WalletsController.PSBT.cs index 3d1c1f755..e52006afe 100644 --- a/BTCPayServer/Controllers/WalletsController.PSBT.cs +++ b/BTCPayServer/Controllers/WalletsController.PSBT.cs @@ -101,7 +101,7 @@ namespace BTCPayServer.Controllers return View(vm); } - var res = await TryHandleSigningCommands(walletId, psbt, command, vm.PayJoinEndpointUrl, null); + var res = await TryHandleSigningCommands(walletId, psbt, command, vm.SigningContext, null); if (res != null) { return res; @@ -126,11 +126,19 @@ namespace BTCPayServer.Controllers return View(vm); } TempData[WellKnownTempData.SuccessMessage] = "PSBT updated!"; - return RedirectToWalletPSBT(psbt, vm.FileName); + return RedirectToWalletPSBT(new WalletPSBTViewModel() + { + PSBT = psbt.ToBase64(), + FileName = vm.FileName, + SigningContext = vm.SigningContext + }); case "broadcast": { - return RedirectToWalletPSBTReady(psbt.ToBase64()); + return RedirectToWalletPSBTReady(new WalletPSBTReadyViewModel() + { + PSBT = psbt.ToBase64() + }); } case "combine": ModelState.Remove(nameof(vm.PSBT)); @@ -156,18 +164,12 @@ namespace BTCPayServer.Controllers [Route("{walletId}/psbt/ready")] public async Task WalletPSBTReady( [ModelBinder(typeof(WalletIdModelBinder))] - WalletId walletId, string psbt = null, - string signingKey = null, - string signingKeyPath = null, - string originalPsbt = null, - string payJoinEndpointUrl = null) + WalletId walletId, + WalletPSBTReadyViewModel vm) { + if (vm is null) + return NotFound(); var network = NetworkProvider.GetNetwork(walletId.CryptoCode); - var vm = new WalletPSBTReadyViewModel() { PSBT = psbt }; - vm.SigningKey = signingKey; - vm.SigningKeyPath = signingKeyPath; - vm.OriginalPSBT = originalPsbt; - vm.PayJoinEndpointUrl = payJoinEndpointUrl; var derivationSchemeSettings = GetDerivationSchemeSettings(walletId); if (derivationSchemeSettings == null) return NotFound(); @@ -288,7 +290,7 @@ namespace BTCPayServer.Controllers WalletId walletId, WalletPSBTReadyViewModel vm, string command = null, CancellationToken cancellationToken = default) { if (command == null) - return await WalletPSBTReady(walletId, vm.PSBT, vm.SigningKey, vm.SigningKeyPath, vm.OriginalPSBT, vm.PayJoinEndpointUrl); + return await WalletPSBTReady(walletId, vm); PSBT psbt = null; var network = NetworkProvider.GetNetwork(walletId.CryptoCode); DerivationSchemeSettings derivationSchemeSettings = null; @@ -312,7 +314,7 @@ namespace BTCPayServer.Controllers string error = null; try { - var proposedPayjoin = await GetPayjoinProposedTX(vm.PayJoinEndpointUrl, psbt, + var proposedPayjoin = await GetPayjoinProposedTX(vm.SigningContext.PayJoinEndpointUrl, psbt, derivationSchemeSettings, network, cancellationToken); try { @@ -357,7 +359,7 @@ namespace BTCPayServer.Controllers $"The amount being sent may appear higher but is in fact almost same.

" + $"If you cancel or refuse to sign this transaction, the payment will proceed without payjoin" }); - return ViewVault(walletId, proposedPayjoin, vm.PayJoinEndpointUrl, psbt); + return ViewVault(walletId, proposedPayjoin, vm.SigningContext, psbt); } } catch (PayjoinReceiverException ex) @@ -425,7 +427,11 @@ namespace BTCPayServer.Controllers return RedirectToWalletTransaction(walletId, transaction); } case "analyze-psbt": - return RedirectToWalletPSBT(psbt); + return RedirectToWalletPSBT(new WalletPSBTViewModel() + { + PSBT = psbt.ToBase64(), + SigningContext = vm.SigningContext + }); default: vm.GlobalError = "Unknown command"; return View(nameof(WalletPSBTReady),vm); @@ -457,20 +463,23 @@ namespace BTCPayServer.Controllers } sourcePSBT = sourcePSBT.Combine(psbt); TempData[WellKnownTempData.SuccessMessage] = "PSBT Successfully combined!"; - return RedirectToWalletPSBT(sourcePSBT); + return RedirectToWalletPSBT(new WalletPSBTViewModel() + { + PSBT = sourcePSBT.ToBase64() + }); } private async Task TryHandleSigningCommands(WalletId walletId, PSBT psbt, string command, - string payjoinEndpointUrl, BitcoinAddress changeAddress) + SigningContextModel signingContext, BitcoinAddress changeAddress) { switch (command ) { case "vault": - return ViewVault(walletId, psbt, payjoinEndpointUrl); + return ViewVault(walletId, psbt, signingContext); case "ledger": return ViewWalletSendLedger(walletId, psbt, changeAddress); case "seed": - return SignWithSeed(walletId, psbt.ToBase64(), payjoinEndpointUrl); + return SignWithSeed(walletId, psbt.ToBase64(), signingContext); case "nbx-seed": if (await CanUseHotWallet()) { @@ -480,7 +489,7 @@ namespace BTCPayServer.Controllers WellknownMetadataKeys.MasterHDKey); return SignWithSeed(walletId, - new SignWithSeedViewModel() {SeedOrKey = extKey, PSBT = psbt.ToBase64(), PayJoinEndpointUrl = payjoinEndpointUrl}); + new SignWithSeedViewModel() {SeedOrKey = extKey, PSBT = psbt.ToBase64(), SigningContext = signingContext }); } TempData.SetStatusMessageModel(new StatusMessageModel() { diff --git a/BTCPayServer/Controllers/WalletsController.cs b/BTCPayServer/Controllers/WalletsController.cs index 2dbb588ad..62b35ebfc 100644 --- a/BTCPayServer/Controllers/WalletsController.cs +++ b/BTCPayServer/Controllers/WalletsController.cs @@ -53,6 +53,7 @@ namespace BTCPayServer.Controllers private readonly DelayedTransactionBroadcaster _broadcaster; private readonly PayjoinClient _payjoinClient; private readonly LabelFactory _labelFactory; + public RateFetcher RateFetcher { get; } CurrencyNameTable _currencyTable; @@ -653,9 +654,14 @@ namespace BTCPayServer.Controllers return View(vm); } derivationScheme.RebaseKeyPaths(psbt.PSBT); - - - var res = await TryHandleSigningCommands(walletId, psbt.PSBT, command, vm.PayJoinEndpointUrl, psbt.ChangeAddress); + + var signingContext = new SigningContextModel() + { + PayJoinEndpointUrl = vm.PayJoinEndpointUrl, + EnforceLowR = psbt.Suggestions?.ShouldEnforceLowR + }; + + var res = await TryHandleSigningCommands(walletId, psbt.PSBT, command, signingContext, psbt.ChangeAddress); if (res != null) { return res; @@ -666,7 +672,12 @@ 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 RedirectToWalletPSBT(psbt.PSBT, name, vm.PayJoinEndpointUrl); + return RedirectToWalletPSBT(new WalletPSBTViewModel() + { + PSBT = psbt.PSBT.ToBase64(), + FileName = name, + SigningContext = signingContext + }); default: return View(vm); } @@ -729,11 +740,11 @@ namespace BTCPayServer.Controllers ModelState.Clear(); } - private IActionResult ViewVault(WalletId walletId, PSBT psbt, string payJoinEndpointUrl, PSBT originalPSBT = null) + private IActionResult ViewVault(WalletId walletId, PSBT psbt, SigningContextModel signingContext, PSBT originalPSBT = null) { return View(nameof(WalletSendVault), new WalletSendVaultModel() { - PayJoinEndpointUrl = payJoinEndpointUrl, + SigningContext = signingContext, WalletId = walletId.ToString(), OriginalPSBT = originalPSBT?.ToBase64(), PSBT = psbt.ToBase64(), @@ -746,42 +757,48 @@ namespace BTCPayServer.Controllers public IActionResult WalletSendVault([ModelBinder(typeof(WalletIdModelBinder))] WalletId walletId, WalletSendVaultModel model) { - return RedirectToWalletPSBTReady(model.PSBT, originalPsbt: model.OriginalPSBT, payJoinEndpointUrl: model.PayJoinEndpointUrl); + return RedirectToWalletPSBTReady(new WalletPSBTReadyViewModel() + { + PSBT = model.PSBT, + OriginalPSBT = model.OriginalPSBT, + SigningContext = model.SigningContext + }); } - private IActionResult RedirectToWalletPSBTReady(string psbt, string signingKey= null, string signingKeyPath = null, string originalPsbt = null, string payJoinEndpointUrl = null) + private IActionResult RedirectToWalletPSBTReady(WalletPSBTReadyViewModel vm) { - var vm = new PostRedirectViewModel() + var redirectVm = new PostRedirectViewModel() { AspController = "Wallets", AspAction = nameof(WalletPSBTReady), Parameters = { - new KeyValuePair("psbt", psbt), - new KeyValuePair("originalPsbt", originalPsbt), - new KeyValuePair("payJoinEndpointUrl", payJoinEndpointUrl), - new KeyValuePair("SigningKey", signingKey), - new KeyValuePair("SigningKeyPath", signingKeyPath) + new KeyValuePair("psbt", vm.PSBT), + new KeyValuePair("originalPsbt", vm.OriginalPSBT), + new KeyValuePair("SigningContext.PayJoinEndpointUrl", vm.SigningContext?.PayJoinEndpointUrl), + new KeyValuePair("SigningContext.EnforceLowR", vm.SigningContext?.EnforceLowR?.ToString(CultureInfo.InvariantCulture)), + new KeyValuePair("SigningKey", vm.SigningKey), + new KeyValuePair("SigningKeyPath", vm.SigningKeyPath) } }; - return View("PostRedirect", vm); + + return View("PostRedirect", redirectVm); } - private IActionResult RedirectToWalletPSBT(PSBT psbt, string fileName = null, string payJoinEndpointUrl = null) + private IActionResult RedirectToWalletPSBT(WalletPSBTViewModel vm) { - var vm = new PostRedirectViewModel() + var redirectVm = new PostRedirectViewModel() { AspController = "Wallets", AspAction = nameof(WalletPSBT), Parameters = { - new KeyValuePair("psbt", psbt.ToBase64()) + new KeyValuePair("psbt", vm.PSBT), + new KeyValuePair("fileName", vm.FileName), + new KeyValuePair("SigningContext.PayJoinEndpointUrl", vm.SigningContext?.PayJoinEndpointUrl), + new KeyValuePair("SigningContext.EnforceLowR", vm.SigningContext?.EnforceLowR?.ToString(CultureInfo.InvariantCulture)), } }; - if (!string.IsNullOrEmpty(fileName)) - vm.Parameters.Add(new KeyValuePair("fileName", fileName)); - if (!string.IsNullOrEmpty(payJoinEndpointUrl)) - vm.Parameters.Add(new KeyValuePair("payJoinEndpointUrl", payJoinEndpointUrl)); - return View("PostRedirect", vm); + return View("PostRedirect", redirectVm); } void SetAmbientPSBT(PSBT psbt) @@ -822,16 +839,19 @@ namespace BTCPayServer.Controllers public IActionResult SubmitLedger([ModelBinder(typeof(WalletIdModelBinder))] WalletId walletId, WalletSendLedgerModel model) { - return RedirectToWalletPSBTReady(model.PSBT); + return RedirectToWalletPSBTReady(new WalletPSBTReadyViewModel() + { + PSBT = model.PSBT + }); } [HttpGet("{walletId}/psbt/seed")] public IActionResult SignWithSeed([ModelBinder(typeof(WalletIdModelBinder))] - WalletId walletId,string psbt, string payJoinEndpointUrl) + WalletId walletId,string psbt, SigningContextModel signingContext) { return View(nameof(SignWithSeed), new SignWithSeedViewModel() { - PayJoinEndpointUrl = payJoinEndpointUrl, + SigningContext = signingContext, PSBT = psbt }); } @@ -899,7 +919,14 @@ namespace BTCPayServer.Controllers return View(viewModel); } ModelState.Remove(nameof(viewModel.PSBT)); - return RedirectToWalletPSBTReady(psbt.ToBase64(), signingKey.GetWif(network.NBitcoinNetwork).ToString(), rootedKeyPath?.ToString(), viewModel.OriginalPSBT, viewModel.PayJoinEndpointUrl); + return RedirectToWalletPSBTReady(new WalletPSBTReadyViewModel() + { + PSBT = psbt.ToBase64(), + SigningKey = signingKey.GetWif(network.NBitcoinNetwork).ToString(), + SigningKeyPath = rootedKeyPath?.ToString(), + OriginalPSBT = viewModel.OriginalPSBT, + SigningContext = viewModel.SigningContext + }); } diff --git a/BTCPayServer/Models/WalletViewModels/SignWithSeedViewModel.cs b/BTCPayServer/Models/WalletViewModels/SignWithSeedViewModel.cs index d0e5074b3..89a228c16 100644 --- a/BTCPayServer/Models/WalletViewModels/SignWithSeedViewModel.cs +++ b/BTCPayServer/Models/WalletViewModels/SignWithSeedViewModel.cs @@ -8,7 +8,7 @@ namespace BTCPayServer.Models.WalletViewModels public class SignWithSeedViewModel { public string OriginalPSBT { get; set; } - public string PayJoinEndpointUrl { get; set; } + public SigningContextModel SigningContext { get; set; } = new SigningContextModel(); [Required] public string PSBT { get; set; } [Required][Display(Name = "BIP39 Seed (12/24 word mnemonic phrase) or HD private key (xprv...)")] diff --git a/BTCPayServer/Models/WalletViewModels/SigningContextModel.cs b/BTCPayServer/Models/WalletViewModels/SigningContextModel.cs new file mode 100644 index 000000000..f30dd55e6 --- /dev/null +++ b/BTCPayServer/Models/WalletViewModels/SigningContextModel.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace BTCPayServer.Models.WalletViewModels +{ + public class SigningContextModel + { + public string PayJoinEndpointUrl { get; set; } + public bool? EnforceLowR { get; set; } + } +} diff --git a/BTCPayServer/Models/WalletViewModels/WalletPSBTReadyViewModel.cs b/BTCPayServer/Models/WalletViewModels/WalletPSBTReadyViewModel.cs index daed6978e..fab30fe52 100644 --- a/BTCPayServer/Models/WalletViewModels/WalletPSBTReadyViewModel.cs +++ b/BTCPayServer/Models/WalletViewModels/WalletPSBTReadyViewModel.cs @@ -8,7 +8,7 @@ namespace BTCPayServer.Models.WalletViewModels { public class WalletPSBTReadyViewModel { - public string PayJoinEndpointUrl { get; set; } + public SigningContextModel SigningContext { get; set; } = new SigningContextModel(); public string OriginalPSBT { get; set; } public string PSBT { get; set; } public string SigningKey { get; set; } diff --git a/BTCPayServer/Models/WalletViewModels/WalletPSBTViewModel.cs b/BTCPayServer/Models/WalletViewModels/WalletPSBTViewModel.cs index ccb244ecc..abd4c56de 100644 --- a/BTCPayServer/Models/WalletViewModels/WalletPSBTViewModel.cs +++ b/BTCPayServer/Models/WalletViewModels/WalletPSBTViewModel.cs @@ -10,7 +10,7 @@ namespace BTCPayServer.Models.WalletViewModels { public class WalletPSBTViewModel { - public string PayJoinEndpointUrl { get; set; } + public SigningContextModel SigningContext { get; set; } = new SigningContextModel(); public string CryptoCode { get; set; } public string Decoded { get; set; } string _FileName; diff --git a/BTCPayServer/Models/WalletViewModels/WalletSendVaultModel.cs b/BTCPayServer/Models/WalletViewModels/WalletSendVaultModel.cs index bf60de537..84e4de6c6 100644 --- a/BTCPayServer/Models/WalletViewModels/WalletSendVaultModel.cs +++ b/BTCPayServer/Models/WalletViewModels/WalletSendVaultModel.cs @@ -11,6 +11,6 @@ namespace BTCPayServer.Models.WalletViewModels public string WalletId { get; set; } public string PSBT { get; set; } public string WebsocketPath { get; set; } - public string PayJoinEndpointUrl { get; set; } + public SigningContextModel SigningContext { get; set; } = new SigningContextModel(); } } diff --git a/BTCPayServer/Views/Wallets/SignWithSeed.cshtml b/BTCPayServer/Views/Wallets/SignWithSeed.cshtml index 0abad6fb2..99ea1c8b0 100644 --- a/BTCPayServer/Views/Wallets/SignWithSeed.cshtml +++ b/BTCPayServer/Views/Wallets/SignWithSeed.cshtml @@ -29,7 +29,8 @@
- + +
diff --git a/BTCPayServer/Views/Wallets/WalletPSBT.cshtml b/BTCPayServer/Views/Wallets/WalletPSBT.cshtml index ce7486f2f..4d820615a 100644 --- a/BTCPayServer/Views/Wallets/WalletPSBT.cshtml +++ b/BTCPayServer/Views/Wallets/WalletPSBT.cshtml @@ -29,13 +29,14 @@

Decoded PSBT

- - - - - + + + + + + - +