mirror of
https://github.com/aljazceru/btcpayserver.git
synced 2025-12-17 22:14:26 +01:00
Can display address on device at confirmation screen
This commit is contained in:
@@ -337,8 +337,10 @@ namespace BTCPayServer.Controllers
|
|||||||
|
|
||||||
for (int i = 0; i < 10; i++)
|
for (int i = 0; i < 10; i++)
|
||||||
{
|
{
|
||||||
|
var keyPath = deposit.GetKeyPath((uint)i);
|
||||||
|
var rootedKeyPath = vm.GetAccountKeypath()?.Derive(keyPath);
|
||||||
var address = line.Derive((uint)i);
|
var address = line.Derive((uint)i);
|
||||||
vm.AddressSamples.Add((deposit.GetKeyPath((uint)i).ToString(), address.ScriptPubKey.GetDestinationAddress(strategy.Network.NBitcoinNetwork).ToString()));
|
vm.AddressSamples.Add((keyPath.ToString(), address.ScriptPubKey.GetDestinationAddress(strategy.Network.NBitcoinNetwork).ToString(), rootedKeyPath));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
vm.Confirmation = true;
|
vm.Confirmation = true;
|
||||||
|
|||||||
@@ -168,6 +168,15 @@ namespace BTCPayServer.Controllers
|
|||||||
o.Add("psbt", psbt.ToBase64());
|
o.Add("psbt", psbt.ToBase64());
|
||||||
await websocketHelper.Send(o.ToString(), cancellationToken);
|
await websocketHelper.Send(o.ToString(), cancellationToken);
|
||||||
break;
|
break;
|
||||||
|
case "display-address":
|
||||||
|
if (await RequireDeviceUnlocking())
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
var k = RootedKeyPath.Parse(await websocketHelper.NextMessageAsync(cancellationToken));
|
||||||
|
await device.DisplayAddressAsync(GetScriptPubKeyType(k), k.KeyPath, cancellationToken);
|
||||||
|
await websocketHelper.Send("{ \"info\": \"ok\"}", cancellationToken);
|
||||||
|
break;
|
||||||
case "ask-pin":
|
case "ask-pin":
|
||||||
if (device == null)
|
if (device == null)
|
||||||
{
|
{
|
||||||
@@ -324,6 +333,18 @@ askdevice:
|
|||||||
return new EmptyResult();
|
return new EmptyResult();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ScriptPubKeyType GetScriptPubKeyType(RootedKeyPath keyPath)
|
||||||
|
{
|
||||||
|
var path = keyPath.KeyPath.ToString();
|
||||||
|
if (path.StartsWith("84'", StringComparison.OrdinalIgnoreCase))
|
||||||
|
return ScriptPubKeyType.Segwit;
|
||||||
|
if (path.StartsWith("49'", StringComparison.OrdinalIgnoreCase))
|
||||||
|
return ScriptPubKeyType.SegwitP2SH;
|
||||||
|
if (path.StartsWith("44'", StringComparison.OrdinalIgnoreCase))
|
||||||
|
return ScriptPubKeyType.Legacy;
|
||||||
|
throw new NotSupportedException("Unsupported keypath");
|
||||||
|
}
|
||||||
|
|
||||||
private bool SameSelector(DeviceSelector a, DeviceSelector b)
|
private bool SameSelector(DeviceSelector a, DeviceSelector b)
|
||||||
{
|
{
|
||||||
var aargs = new List<string>();
|
var aargs = new List<string>();
|
||||||
|
|||||||
@@ -19,10 +19,10 @@ namespace BTCPayServer.Models.StoreViewModels
|
|||||||
get; set;
|
get; set;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<(string KeyPath, string Address)> AddressSamples
|
public List<(string KeyPath, string Address, RootedKeyPath RootedKeyPath)> AddressSamples
|
||||||
{
|
{
|
||||||
get; set;
|
get; set;
|
||||||
} = new List<(string KeyPath, string Address)>();
|
} = new List<(string KeyPath, string Address, RootedKeyPath RootedKeyPath)>();
|
||||||
|
|
||||||
public string CryptoCode { get; set; }
|
public string CryptoCode { get; set; }
|
||||||
public string KeyPath { get; set; }
|
public string KeyPath { get; set; }
|
||||||
@@ -41,5 +41,16 @@ namespace BTCPayServer.Models.StoreViewModels
|
|||||||
public string DerivationSchemeFormat { get; set; }
|
public string DerivationSchemeFormat { get; set; }
|
||||||
public string AccountKey { get; set; }
|
public string AccountKey { get; set; }
|
||||||
public BTCPayNetwork Network { get; set; }
|
public BTCPayNetwork Network { get; set; }
|
||||||
|
|
||||||
|
public RootedKeyPath GetAccountKeypath()
|
||||||
|
{
|
||||||
|
if (KeyPath != null && RootFingerprint != null &&
|
||||||
|
NBitcoin.KeyPath.TryParse(KeyPath, out var p) &&
|
||||||
|
HDFingerprint.TryParse(RootFingerprint, out var fp))
|
||||||
|
{
|
||||||
|
return new RootedKeyPath(fp, p);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
@section HeadScripts {
|
@section HeadScripts {
|
||||||
<style type="text/css">
|
<style type="text/css">
|
||||||
|
|
||||||
.hw-fields {
|
.hw-fields {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
@@ -18,12 +19,42 @@
|
|||||||
<div asp-validation-summary="All" class="text-danger"></div>
|
<div asp-validation-summary="All" class="text-danger"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="modal fade" id="btcpayservervault" tabindex="-1" role="dialog" aria-labelledby="btcpayservervault" aria-hidden="true">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<partial name="VaultElements" />
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-8">
|
<div class="col-md-8">
|
||||||
|
<div id="WebsocketPath" style="display:none;">@Url.Action("VaultBridgeConnection", "Vault", new { cryptoCode = Model.CryptoCode })</div>
|
||||||
@if (!Model.Confirmation)
|
@if (!Model.Confirmation)
|
||||||
{
|
{
|
||||||
<partial name="AddDerivationSchemes_HardwareWalletDialogs" model="@Model" />
|
<partial name="AddDerivationSchemes_HardwareWalletDialogs" model="@Model" />
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<template id="btcpayservervault_template">
|
||||||
|
<div class="modal-dialog" role="document">
|
||||||
|
<form class="modal-content" form method="post" enctype="multipart/form-data">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title" id="exampleModalLabel">Address verification</h5>
|
||||||
|
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||||
|
<span aria-hidden="true">×</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<p>Confirm on the device that you see address <b id="displayedAddress"></b></p>
|
||||||
|
<div class="form-group">
|
||||||
|
<div id="vaultPlaceholder"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
|
||||||
|
<button id="vault-confirm" class="btn btn-primary" style="display:none;"></button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
}
|
||||||
<form method="post">
|
<form method="post">
|
||||||
|
|
||||||
<input id="Config" asp-for="Config" type="hidden" />
|
<input id="Config" asp-for="Config" type="hidden" />
|
||||||
@@ -50,8 +81,9 @@
|
|||||||
<div class="dropdown-menu dropdown-menu-right">
|
<div class="dropdown-menu dropdown-menu-right">
|
||||||
<button class="dropdown-item" type="button">... Coldcard (air gap)</button>
|
<button class="dropdown-item" type="button">... Coldcard (air gap)</button>
|
||||||
<button class="dropdown-item check-for-ledger" type="button">... Ledger Wallet</button>
|
<button class="dropdown-item check-for-ledger" type="button">... Ledger Wallet</button>
|
||||||
@if (Model.CryptoCode == "BTC") {
|
@if (Model.CryptoCode == "BTC")
|
||||||
<button class="dropdown-item check-for-vault" type="button">... the vault (preview)</button>
|
{
|
||||||
|
<button class="dropdown-item check-for-vault" type="button">... the vault (preview)</button>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -133,6 +165,10 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<th>Key path</th>
|
<th>Key path</th>
|
||||||
<th>Address</th>
|
<th>Address</th>
|
||||||
|
@if (Model.Source == "Vault")
|
||||||
|
{
|
||||||
|
<th>Actions</th>
|
||||||
|
}
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
@@ -141,6 +177,10 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<td>@sample.KeyPath</td>
|
<td>@sample.KeyPath</td>
|
||||||
<td>@sample.Address</td>
|
<td>@sample.Address</td>
|
||||||
|
@if (Model.Source == "Vault")
|
||||||
|
{
|
||||||
|
<td><a class="showaddress" href="#" onclick='showAddress(@Safe.Json(sample.RootedKeyPath.ToString()), @Safe.Json(sample.Address)); return false;'>Show on device</a></td>
|
||||||
|
}
|
||||||
</tr>
|
</tr>
|
||||||
}
|
}
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|||||||
@@ -80,8 +80,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="WebsocketPath" style="display:none;">@Url.Action("VaultBridgeConnection", "Vault", new { cryptoCode = Model.CryptoCode })</div>
|
|
||||||
|
|
||||||
<template id="btcpayservervault_template">
|
<template id="btcpayservervault_template">
|
||||||
<div class="modal-dialog" role="document">
|
<div class="modal-dialog" role="document">
|
||||||
<form class="modal-content" form method="post" enctype="multipart/form-data">
|
<form class="modal-content" form method="post" enctype="multipart/form-data">
|
||||||
@@ -123,7 +121,3 @@
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<div class="modal fade" id="btcpayservervault" tabindex="-1" role="dialog" aria-labelledby="btcpayservervault" aria-hidden="true">
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<partial name="VaultElements" />
|
|
||||||
|
|||||||
@@ -87,6 +87,42 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function getVaultUI() {
|
||||||
|
var websocketPath = $("#WebsocketPath").text();
|
||||||
|
var loc = window.location, ws_uri;
|
||||||
|
if (loc.protocol === "https:") {
|
||||||
|
ws_uri = "wss:";
|
||||||
|
} else {
|
||||||
|
ws_uri = "ws:";
|
||||||
|
}
|
||||||
|
ws_uri += "//" + loc.host;
|
||||||
|
ws_uri += websocketPath;
|
||||||
|
return new vaultui.VaultBridgeUI(ws_uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
function showModal() {
|
||||||
|
var html = $("#btcpayservervault_template").html();
|
||||||
|
$("#btcpayservervault").html(html);
|
||||||
|
html = $("#VaultConnection").html();
|
||||||
|
$("#vaultPlaceholder").html(html);
|
||||||
|
$('#btcpayservervault').modal();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function showAddress(rootedKeyPath, address) {
|
||||||
|
$(".showaddress").addClass("disabled");
|
||||||
|
showModal();
|
||||||
|
$("#btcpayservervault #displayedAddress").text(address);
|
||||||
|
var vaultUI = getVaultUI();
|
||||||
|
$('#btcpayservervault').on('hidden.bs.modal', function () {
|
||||||
|
vaultUI.closeBridge();
|
||||||
|
$(".showaddress").removeClass("disabled");
|
||||||
|
});
|
||||||
|
if (await vaultUI.askForDevice())
|
||||||
|
await vaultUI.askForDisplayAddress(rootedKeyPath);
|
||||||
|
$('#btcpayservervault').modal("hide");
|
||||||
|
}
|
||||||
|
|
||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
var ledgerInit = false;
|
var ledgerInit = false;
|
||||||
$(".check-for-ledger").on("click", function () {
|
$(".check-for-ledger").on("click", function () {
|
||||||
@@ -102,16 +138,6 @@ $(document).ready(function () {
|
|||||||
$("#" + id).css("display", "block");
|
$("#" + id).css("display", "block");
|
||||||
}
|
}
|
||||||
|
|
||||||
var websocketPath = $("#WebsocketPath").text();
|
|
||||||
var loc = window.location, ws_uri;
|
|
||||||
if (loc.protocol === "https:") {
|
|
||||||
ws_uri = "wss:";
|
|
||||||
} else {
|
|
||||||
ws_uri = "ws:";
|
|
||||||
}
|
|
||||||
ws_uri += "//" + loc.host;
|
|
||||||
ws_uri += websocketPath;
|
|
||||||
|
|
||||||
function displayXPubs(xpub) {
|
function displayXPubs(xpub) {
|
||||||
$("#DerivationScheme").val(xpub.strategy);
|
$("#DerivationScheme").val(xpub.strategy);
|
||||||
$("#RootFingerprint").val(xpub.fingerprint);
|
$("#RootFingerprint").val(xpub.fingerprint);
|
||||||
@@ -124,12 +150,8 @@ $(document).ready(function () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$(".check-for-vault").on("click", async function () {
|
$(".check-for-vault").on("click", async function () {
|
||||||
var html = $("#btcpayservervault_template").html();
|
var vaultUI = getVaultUI();
|
||||||
$("#btcpayservervault").html(html);
|
showModal();
|
||||||
html = $("#VaultConnection").html();
|
|
||||||
$("#vaultPlaceholder").html(html);
|
|
||||||
$('#btcpayservervault').modal();
|
|
||||||
var vaultUI = new vaultui.VaultBridgeUI(ws_uri);
|
|
||||||
$('#btcpayservervault').on('hidden.bs.modal', function () {
|
$('#btcpayservervault').on('hidden.bs.modal', function () {
|
||||||
vaultUI.closeBridge();
|
vaultUI.closeBridge();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ var vaultui = (function () {
|
|||||||
needPassphrase: new VaultFeedback("?", "Enter the passphrase.", "vault-feedback3", "need-passphrase"),
|
needPassphrase: new VaultFeedback("?", "Enter the passphrase.", "vault-feedback3", "need-passphrase"),
|
||||||
needPassphraseOnDevice: new VaultFeedback("?", "Please, enter the passphrase on the device.", "vault-feedback3", "need-passphrase-on-device"),
|
needPassphraseOnDevice: new VaultFeedback("?", "Please, enter the passphrase on the device.", "vault-feedback3", "need-passphrase-on-device"),
|
||||||
signingTransaction: new VaultFeedback("?", "Please review and confirm the transaction on your device...", "vault-feedback3", "ask-signing"),
|
signingTransaction: new VaultFeedback("?", "Please review and confirm the transaction on your device...", "vault-feedback3", "ask-signing"),
|
||||||
|
reviewAddress: new VaultFeedback("?", "Please review the address on your device...", "vault-feedback3", "ask-signing"),
|
||||||
signingRejected: new VaultFeedback("failed", "The user refused to sign the transaction", "vault-feedback3", "user-reject"),
|
signingRejected: new VaultFeedback("failed", "The user refused to sign the transaction", "vault-feedback3", "user-reject"),
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -175,6 +176,20 @@ var vaultui = (function () {
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
this.askForDisplayAddress = async function (rootedKeyPath) {
|
||||||
|
if (!await self.ensureConnectedToBackend())
|
||||||
|
return false;
|
||||||
|
show(VaultFeedbacks.reviewAddress);
|
||||||
|
self.bridge.socket.send("display-address");
|
||||||
|
self.bridge.socket.send(rootedKeyPath);
|
||||||
|
var json = await self.bridge.waitBackendMessage();
|
||||||
|
if (json.hasOwnProperty("error")) {
|
||||||
|
if (await needRetry(json))
|
||||||
|
return await self.askForDisplayAddress(rootedKeyPath);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
this.askForDevice = async function () {
|
this.askForDevice = async function () {
|
||||||
if (!await self.ensureConnectedToBackend())
|
if (!await self.ensureConnectedToBackend())
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
Reference in New Issue
Block a user