Refactor parameter passing in wallet functions

This commit is contained in:
nicolas.dorier 2020-05-25 04:55:28 +09:00
parent d22993871f
commit 25e6f82aa3
No known key found for this signature in database
GPG Key ID: 6618763EF09186FE
15 changed files with 130 additions and 74 deletions

View File

@ -5,7 +5,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="NBitcoin" Version="5.0.35" />
<PackageReference Include="NBitcoin" Version="5.0.38" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
</ItemGroup>

View File

@ -4,6 +4,6 @@
<ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App" />
<PackageReference Include="NBXplorer.Client" Version="3.0.11" />
<PackageReference Include="NBXplorer.Client" Version="3.0.15" />
</ItemGroup>
</Project>

View File

@ -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<WalletPSBTReadyViewModel>();
var ready = (await walletController.WalletPSBTReady(walletId, new WalletPSBTReadyViewModel()
{
PSBT = signedPSBT.ToBase64()
})).AssertViewModel<WalletPSBTReadyViewModel>();
Assert.Equal(signedPSBT.ToBase64(), ready.PSBT);
psbt = AssertRedirectedPSBT(await walletController.WalletPSBTReady(walletId, ready, command: "analyze-psbt"), nameof(walletController.WalletPSBT));
Assert.Equal(signedPSBT.ToBase64(), psbt);

View File

@ -2,8 +2,8 @@
<Import Project="../Build/Version.csproj" Condition="Exists('../Build/Version.csproj')" />
<Import Project="../Build/Common.csproj" />
<PropertyGroup Condition="'$(Configuration)' == 'Debug' And '$(RazorCompileOnBuild)' != 'true'">
<RazorCompileOnBuild>false</RazorCompileOnBuild>
<DefineConstants>$(DefineConstants);RAZOR_RUNTIME_COMPILE</DefineConstants>
<!--<RazorCompileOnBuild>false</RazorCompileOnBuild>
<DefineConstants>$(DefineConstants);RAZOR_RUNTIME_COMPILE</DefineConstants>-->
</PropertyGroup>
<PropertyGroup>
<OutputType>Exe</OutputType>

View File

@ -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<IActionResult> 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<BTCPayNetwork>(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<BTCPayNetwork>(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.<br/><br/>" +
$"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<IActionResult> 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()
{

View File

@ -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<string, string>("psbt", psbt),
new KeyValuePair<string, string>("originalPsbt", originalPsbt),
new KeyValuePair<string, string>("payJoinEndpointUrl", payJoinEndpointUrl),
new KeyValuePair<string, string>("SigningKey", signingKey),
new KeyValuePair<string, string>("SigningKeyPath", signingKeyPath)
new KeyValuePair<string, string>("psbt", vm.PSBT),
new KeyValuePair<string, string>("originalPsbt", vm.OriginalPSBT),
new KeyValuePair<string, string>("SigningContext.PayJoinEndpointUrl", vm.SigningContext?.PayJoinEndpointUrl),
new KeyValuePair<string, string>("SigningContext.EnforceLowR", vm.SigningContext?.EnforceLowR?.ToString(CultureInfo.InvariantCulture)),
new KeyValuePair<string, string>("SigningKey", vm.SigningKey),
new KeyValuePair<string, string>("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<string, string>("psbt", psbt.ToBase64())
new KeyValuePair<string, string>("psbt", vm.PSBT),
new KeyValuePair<string, string>("fileName", vm.FileName),
new KeyValuePair<string, string>("SigningContext.PayJoinEndpointUrl", vm.SigningContext?.PayJoinEndpointUrl),
new KeyValuePair<string, string>("SigningContext.EnforceLowR", vm.SigningContext?.EnforceLowR?.ToString(CultureInfo.InvariantCulture)),
}
};
if (!string.IsNullOrEmpty(fileName))
vm.Parameters.Add(new KeyValuePair<string, string>("fileName", fileName));
if (!string.IsNullOrEmpty(payJoinEndpointUrl))
vm.Parameters.Add(new KeyValuePair<string, string>("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
});
}

View File

@ -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...)")]

View File

@ -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; }
}
}

View File

@ -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; }

View File

@ -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;

View File

@ -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();
}
}

View File

@ -29,7 +29,8 @@
<form method="post" asp-action="SignWithSeed" asp-route-walletId="@this.Context.GetRouteValue("walletId")">
<input type="hidden" asp-for="OriginalPSBT" />
<input type="hidden" asp-for="PSBT" />
<input type="hidden" asp-for="PayJoinEndpointUrl" />
<input type="hidden" asp-for="SigningContext.PayJoinEndpointUrl" />
<input type="hidden" asp-for="SigningContext.EnforceLowR" />
<div class="form-group">
<label asp-for="SeedOrKey"></label>
<input asp-for="SeedOrKey" class="form-control" />

View File

@ -29,13 +29,14 @@
<h3>Decoded PSBT</h3>
<div class="form-group">
<form method="post" asp-action="WalletPSBT" asp-route-walletId="@this.Context.GetRouteValue("walletId")">
<input type="hidden" asp-for="CryptoCode"/>
<input type="hidden" asp-for="PayJoinEndpointUrl"/>
<input type="hidden" asp-for="NBXSeedAvailable"/>
<input type="hidden" asp-for="PSBT"/>
<input type="hidden" asp-for="FileName"/>
<input type="hidden" asp-for="CryptoCode" />
<input type="hidden" asp-for="SigningContext.PayJoinEndpointUrl" />
<input type="hidden" asp-for="SigningContext.EnforceLowR" />
<input type="hidden" asp-for="NBXSeedAvailable" />
<input type="hidden" asp-for="PSBT" />
<input type="hidden" asp-for="FileName" />
<partial name="WalletSigningMenu" model="@((Model.CryptoCode, Model.NBXSeedAvailable))"/>
<partial name="WalletSigningMenu" model="@((Model.CryptoCode, Model.NBXSeedAvailable))" />
<div class="dropdown d-inline-block">
<button class="btn btn-secondary dropdown-toggle" type="button" id="OtherActions" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
Other actions...

View File

@ -137,14 +137,15 @@
<div class="row">
<div class="col-lg-12 text-center">
<form method="post" asp-action="WalletPSBTReady" asp-route-walletId="@this.Context.GetRouteValue("walletId")">
<input type="hidden" asp-for="PSBT" value="@Model.PSBT"/>
<input type="hidden" asp-for="OriginalPSBT"/>
<input type="hidden" asp-for="SigningKey"/>
<input type="hidden" asp-for="SigningKeyPath"/>
<input type="hidden" asp-for="PayJoinEndpointUrl"/>
<input type="hidden" asp-for="PSBT" value="@Model.PSBT" />
<input type="hidden" asp-for="OriginalPSBT" />
<input type="hidden" asp-for="SigningKey" />
<input type="hidden" asp-for="SigningKeyPath" />
<input type="hidden" asp-for="SigningContext.PayJoinEndpointUrl" />
<input type="hidden" asp-for="SigningContext.EnforceLowR" />
@if (!Model.HasErrors)
{
@if (!string.IsNullOrEmpty(Model.PayJoinEndpointUrl))
@if (!string.IsNullOrEmpty(Model.SigningContext?.PayJoinEndpointUrl))
{
<button type="submit" class="btn btn-primary" name="command" value="payjoin">Broadcast (Payjoin)</button>
<span> or </span>

View File

@ -22,10 +22,11 @@
<div id="body" class="col-md-10">
<form id="broadcastForm" asp-action="WalletSendVault" asp-route-walletId="@this.Context.GetRouteValue("walletId")" method="post" style="display:none;">
<input type="hidden" id="WalletId" asp-for="WalletId" />
<input type="hidden" id="PSBT" asp-for="PSBT" value="@Model.PSBT"/>
<input type="hidden" id="OriginalPSBT" asp-for="OriginalPSBT" value="@Model.OriginalPSBT"/>
<input type="hidden" id="PSBT" asp-for="PSBT" value="@Model.PSBT" />
<input type="hidden" id="OriginalPSBT" asp-for="OriginalPSBT" value="@Model.OriginalPSBT" />
<input type="hidden" asp-for="WebsocketPath" />
<input type="hidden" asp-for="PayJoinEndpointUrl" />
<input type="hidden" asp-for="SigningContext.PayJoinEndpointUrl" />
<input type="hidden" asp-for="SigningContext.EnforceLowR" />
</form>
<div id="vaultPlaceholder"></div>
<button id="vault-confirm" class="btn btn-primary" style="display:none;"></button>