mirror of
https://github.com/btcpayserver/btcpayserver.git
synced 2025-02-20 13:34:37 +01:00
Add replace confirmation; distinguish wallet types
This commit is contained in:
parent
28d7924077
commit
5bd16f990c
5 changed files with 84 additions and 13 deletions
|
@ -11,11 +11,11 @@ using BTCPayServer.Models;
|
|||
using BTCPayServer.Models.StoreViewModels;
|
||||
using BTCPayServer.Payments;
|
||||
using BTCPayServer.Services;
|
||||
using BTCPayServer.Services.Wallets;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using NBitcoin;
|
||||
using NBXplorer;
|
||||
using NBXplorer.DerivationStrategy;
|
||||
using NBXplorer.Models;
|
||||
|
||||
|
@ -411,6 +411,8 @@ namespace BTCPayServer.Controllers
|
|||
}
|
||||
|
||||
var (hotWallet, rpcImport) = await CanUseHotWallet();
|
||||
var isHotWallet = await IsHotWallet(vm.CryptoCode, derivation);
|
||||
|
||||
vm.CanUseHotWallet = hotWallet;
|
||||
vm.CanUseRPCImport = rpcImport;
|
||||
vm.RootKeyPath = network.GetRootKeyPath();
|
||||
|
@ -421,12 +423,13 @@ namespace BTCPayServer.Controllers
|
|||
vm.KeyPath = derivation.GetSigningAccountKeySettings().AccountKeyPath?.ToString();
|
||||
vm.Config = derivation.ToJson();
|
||||
vm.Enabled = !store.GetStoreBlob().IsExcluded(new PaymentMethodId(vm.CryptoCode, PaymentTypes.BTCLike));
|
||||
vm.IsHotWallet = isHotWallet;
|
||||
|
||||
return View(vm);
|
||||
}
|
||||
|
||||
[HttpGet("{storeId}/onchain/{cryptoCode}/delete")]
|
||||
public IActionResult DeleteWallet(string storeId, string cryptoCode)
|
||||
[HttpGet("{storeId}/onchain/{cryptoCode}/replace")]
|
||||
public async Task<IActionResult> ReplaceWallet(string storeId, string cryptoCode)
|
||||
{
|
||||
var checkResult = IsAvailable(cryptoCode, out var store, out var network);
|
||||
if (checkResult != null)
|
||||
|
@ -435,9 +438,62 @@ namespace BTCPayServer.Controllers
|
|||
}
|
||||
|
||||
var derivation = GetExistingDerivationStrategy(cryptoCode, store);
|
||||
var isHotWallet = await IsHotWallet(cryptoCode, derivation);
|
||||
var walletType = isHotWallet ? "hot" : "watch-only";
|
||||
var additionalText = isHotWallet
|
||||
? ""
|
||||
: " or imported into an external wallet. If you no longer have access to your private key (recovery seed), immediately replace the wallet";
|
||||
var description =
|
||||
(derivation.IsHotWallet ? "<p class=\"text-danger font-weight-bold\">Please note that this is a hot wallet!</p> " : "") +
|
||||
"<p class=\"text-danger font-weight-bold\">Do not remove the wallet if you have not backed it up!</p>" +
|
||||
$"<p class=\"text-danger font-weight-bold\">Please note that this is a {walletType} wallet!</p>" +
|
||||
$"<p class=\"text-danger font-weight-bold\">Do not replace the wallet if you have not backed it up{additionalText}.</p>" +
|
||||
"<p class=\"text-left mb-0\">Replacing the wallet will erase the current wallet data from the server. " +
|
||||
"The current wallet will be replaced once you finish the setup of the new wallet. If you cancel the setup, the current wallet will stay active .</p>";
|
||||
|
||||
return View("Confirm", new ConfirmModel
|
||||
{
|
||||
Title = $"Replace {network.CryptoCode} wallet",
|
||||
Description = description,
|
||||
DescriptionHtml = true,
|
||||
Action = "Setup new wallet"
|
||||
});
|
||||
}
|
||||
|
||||
[HttpPost("{storeId}/onchain/{cryptoCode}/replace")]
|
||||
public IActionResult ConfirmReplaceWallet(string storeId, string cryptoCode)
|
||||
{
|
||||
var checkResult = IsAvailable(cryptoCode, out var store, out _);
|
||||
if (checkResult != null)
|
||||
{
|
||||
return checkResult;
|
||||
}
|
||||
|
||||
var derivation = GetExistingDerivationStrategy(cryptoCode, store);
|
||||
if (derivation == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
return RedirectToAction(nameof(SetupWallet), new {storeId, cryptoCode});
|
||||
}
|
||||
|
||||
[HttpGet("{storeId}/onchain/{cryptoCode}/delete")]
|
||||
public async Task<IActionResult> DeleteWallet(string storeId, string cryptoCode)
|
||||
{
|
||||
var checkResult = IsAvailable(cryptoCode, out var store, out var network);
|
||||
if (checkResult != null)
|
||||
{
|
||||
return checkResult;
|
||||
}
|
||||
|
||||
var derivation = GetExistingDerivationStrategy(cryptoCode, store);
|
||||
var isHotWallet = await IsHotWallet(cryptoCode, derivation);
|
||||
var walletType = isHotWallet ? "hot" : "watch-only";
|
||||
var additionalText = isHotWallet
|
||||
? ""
|
||||
: " or imported into an external wallet. If you no longer have access to your private key (recovery seed), immediately replace the wallet";
|
||||
var description =
|
||||
$"<p class=\"text-danger font-weight-bold\">Please note that this is a {walletType} wallet!</p>" +
|
||||
$"<p class=\"text-danger font-weight-bold\">Do not remove the wallet if you have not backed it up{additionalText}.</p>" +
|
||||
"<p class=\"text-left mb-0\">Removing the wallet will erase the wallet data from the server. " +
|
||||
$"The store won't be able to receive {network.CryptoCode} onchain payments until a new wallet is set up.</p>";
|
||||
|
||||
|
@ -480,7 +536,7 @@ namespace BTCPayServer.Controllers
|
|||
private IActionResult ConfirmAddresses(WalletSetupViewModel vm, DerivationSchemeSettings strategy)
|
||||
{
|
||||
vm.DerivationScheme = strategy.AccountDerivation.ToString();
|
||||
var deposit = new NBXplorer.KeyPathTemplates(null).GetKeyPathTemplate(DerivationFeature.Deposit);
|
||||
var deposit = new KeyPathTemplates(null).GetKeyPathTemplate(DerivationFeature.Deposit);
|
||||
|
||||
if (!string.IsNullOrEmpty(vm.DerivationScheme))
|
||||
{
|
||||
|
@ -529,7 +585,7 @@ namespace BTCPayServer.Controllers
|
|||
return (true, true);
|
||||
var policies = await _settingsRepository.GetSettingAsync<PoliciesSettings>();
|
||||
var hotWallet = policies?.AllowHotWalletForAll is true;
|
||||
return (hotWallet, hotWallet && policies?.AllowHotWalletRPCImportForAll is true);
|
||||
return (hotWallet, hotWallet && policies.AllowHotWalletRPCImportForAll is true);
|
||||
}
|
||||
|
||||
private async Task<string> ReadAllText(IFormFile file)
|
||||
|
@ -539,5 +595,11 @@ namespace BTCPayServer.Controllers
|
|||
return await stream.ReadToEndAsync();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<bool> IsHotWallet(string cryptoCode, DerivationSchemeSettings derivation)
|
||||
{
|
||||
return derivation.IsHotWallet && await _ExplorerProvider.GetExplorerClient(cryptoCode)
|
||||
.GetMetadataAsync<string>(derivation.AccountDerivation, WellknownMetadataKeys.MasterHDKey) != null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ namespace BTCPayServer.Models.StoreViewModels
|
|||
public WalletSetupMethod? Method { get; set; }
|
||||
public GenerateWalletRequest SetupRequest { get; set; }
|
||||
public string StoreId { get; set; }
|
||||
public bool IsHotWallet { get; set; }
|
||||
|
||||
public string ViewName =>
|
||||
Method switch
|
||||
|
|
|
@ -31,8 +31,8 @@
|
|||
@if (!String.IsNullOrEmpty(Model.Action))
|
||||
{
|
||||
<form method="post" class="modal-footer justify-content-center" action="@Model.ActionUrl">
|
||||
<button type="submit" class="btn @Model.ButtonClass w-25 mx-2" id="continue">@Model.Action</button>
|
||||
<button type="submit" class="btn btn-secondary w-25 mx-2" onclick="history.back(); return false;">Go back</button>
|
||||
<button type="submit" class="btn @Model.ButtonClass xmx-2" id="continue" style="min-width:25%;">@Model.Action</button>
|
||||
<button type="submit" class="btn btn-secondary mx-2" onclick="history.back(); return false;" style="min-width:25%;">Go back</button>
|
||||
</form>
|
||||
}
|
||||
</div>
|
||||
|
|
|
@ -23,8 +23,9 @@
|
|||
<div class="content">
|
||||
<h4>Hot wallet</h4>
|
||||
<p class="mb-0 text-secondary">
|
||||
Allows spending directly from your BTCPay Server.
|
||||
Each private key associated with an address generated will be stored as metadata and would be accessible to anyone with admin access to your server. Use at your own risk!
|
||||
Wallet's private key is stored on the server.
|
||||
Spending the funds you received is convenient.
|
||||
To minimize the risk of theft, regularly withdraw funds to a different wallet.
|
||||
</p>
|
||||
</div>
|
||||
<vc:icon symbol="caret-right"/>
|
||||
|
@ -51,7 +52,10 @@
|
|||
</div>
|
||||
<div class="content">
|
||||
<h4>Watch-only wallet</h4>
|
||||
<p class="mb-0 text-secondary">Needs to be imported into an external wallet to spend or provide the seed while spending.</p>
|
||||
<p class="mb-0 text-secondary">
|
||||
Wallet's private key is erased from the server. Higher security.
|
||||
To spend, you have to manually input the private key or import it into an external wallet.
|
||||
</p>
|
||||
</div>
|
||||
<vc:icon symbol="caret-right" />
|
||||
</a>
|
||||
|
|
|
@ -44,6 +44,10 @@
|
|||
<th>Source</th>
|
||||
<td>@Model.Source</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Type</th>
|
||||
<td>@(Model.IsHotWallet ? "Hot wallet" : "Watch-only wallet")</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>
|
||||
Enabled
|
||||
|
@ -57,7 +61,7 @@
|
|||
</form>
|
||||
<br>
|
||||
<form method="get" asp-controller="Stores" asp-action="DeleteWallet" asp-route-storeId="@Model.StoreId" asp-route-cryptoCode="@Model.CryptoCode" class="mt-5">
|
||||
<a asp-controller="Stores" asp-action="SetupWallet" asp-route-storeId="@Model.StoreId" asp-route-cryptoCode="@Model.CryptoCode" id="ChangeWalletLink" class="btn btn-secondary mr-2">
|
||||
<a asp-controller="Stores" asp-action="ReplaceWallet" asp-route-storeId="@Model.StoreId" asp-route-cryptoCode="@Model.CryptoCode" id="ChangeWalletLink" class="btn btn-secondary mr-2">
|
||||
Replace wallet
|
||||
</a>
|
||||
<button type="submit" class="btn btn-danger" id="Delete">Remove wallet</button>
|
||||
|
|
Loading…
Add table
Reference in a new issue