diff --git a/BTCPayServer/Controllers/VaultController.cs b/BTCPayServer/Controllers/VaultController.cs index c21d7c938..a75797d50 100644 --- a/BTCPayServer/Controllers/VaultController.cs +++ b/BTCPayServer/Controllers/VaultController.cs @@ -80,16 +80,28 @@ namespace BTCPayServer.Controllers return true; } if ((deviceEntry.Code is HwiErrorCode.DeviceNotReady || deviceEntry.NeedsPinSent is true) - && !pinProvided - // Trezor T always show the pin on screen - && (deviceEntry.Model != HardwareWalletModels.Trezor_T || deviceEntry.Model != HardwareWalletModels.Trezor_T_Simulator)) + && !pinProvided) { - await websocketHelper.Send("{ \"error\": \"need-pin\"}", cancellationToken); + if (IsTrezorT(deviceEntry)) + { + await websocketHelper.Send("{ \"error\": \"need-pin-on-device\"}", cancellationToken); + } + else + { + await websocketHelper.Send("{ \"error\": \"need-pin\"}", cancellationToken); + } return true; } if ((deviceEntry.Code is HwiErrorCode.DeviceNotReady || deviceEntry.NeedsPassphraseSent is true) && password == null) { - await websocketHelper.Send("{ \"error\": \"need-passphrase\"}", cancellationToken); + if (IsTrezorT(deviceEntry)) + { + await websocketHelper.Send("{ \"error\": \"need-passphrase-on-device\"}", cancellationToken); + } + else + { + await websocketHelper.Send("{ \"error\": \"need-passphrase\"}", cancellationToken); + } return true; } return false; @@ -247,18 +259,25 @@ namespace BTCPayServer.Controllers ScriptPubKeyType = ScriptPubKeyType.Legacy }); } + else + { + await websocketHelper.Send("{ \"error\": \"invalid-addresstype\"}", cancellationToken); + continue; + } result.Add(new JProperty("strategy", strategy.ToString())); result.Add(new JProperty("accountKey", xpub.ToString())); result.Add(new JProperty("keyPath", keyPath.ToString())); await websocketHelper.Send(result.ToString(), cancellationToken); break; + case "refresh-device": case "ask-device": + DeviceSelector deviceSelector = (command == "refresh-device" && deviceEntry != null ? deviceEntry.DeviceSelector : null); password = null; pinProvided = false; deviceEntry = null; device = null; var entries = (await hwi.EnumerateEntriesAsync(cancellationToken)).ToList(); - deviceEntry = entries.FirstOrDefault(); + deviceEntry = entries.Where(h => deviceSelector == null || SameSelector(deviceSelector, h.DeviceSelector)).FirstOrDefault(); if (deviceEntry == null) { await websocketHelper.Send("{ \"error\": \"no-device\"}", cancellationToken); @@ -304,6 +323,27 @@ namespace BTCPayServer.Controllers return new EmptyResult(); } + private bool SameSelector(DeviceSelector a, DeviceSelector b) + { + var aargs = new List(); + a.AddArgs(aargs); + var bargs = new List(); + b.AddArgs(bargs); + if (aargs.Count != bargs.Count) + return false; + for (int i = 0; i < aargs.Count; i++) + { + if (aargs[i] != bargs[i]) + return false; + } + return true; + } + + private static bool IsTrezorT(HwiEnumerateEntry deviceEntry) + { + return (deviceEntry.Model != HardwareWalletModels.Trezor_T || deviceEntry.Model != HardwareWalletModels.Trezor_T_Simulator); + } + public StoreData CurrentStore { get diff --git a/BTCPayServer/wwwroot/js/vaultbridge.ui.js b/BTCPayServer/wwwroot/js/vaultbridge.ui.js index 620c253a3..d2eb30d09 100644 --- a/BTCPayServer/wwwroot/js/vaultbridge.ui.js +++ b/BTCPayServer/wwwroot/js/vaultbridge.ui.js @@ -42,11 +42,13 @@ var vaultui = (function () { unexpectedError: new VaultFeedback("failed", "An unexpected error happened.", "vault-feedback3", "unknown-error"), invalidNetwork: new VaultFeedback("failed", "The device is targetting a different chain.", "vault-feedback3", "invalid-network"), needPin: new VaultFeedback("?", "Enter the pin.", "vault-feedback3", "need-pin"), + needPinOnDevice: new VaultFeedback("?", "Please, enter the pin on the device.", "vault-feedback3", "need-pin-on-device"), incorrectPin: new VaultFeedback("failed", "Incorrect pin code.", "vault-feedback3", "incorrect-pin"), invalidPasswordConfirmation: new VaultFeedback("failed", "Invalid password confirmation.", "vault-feedback3", "invalid-password-confirm"), wrongWallet: new VaultFeedback("failed", "This device can't sign the transaction. (Wrong device, wrong passphrase or wrong device fingerprint in your wallet settings)", "vault-feedback3", "wrong-wallet"), wrongKeyPath: new VaultFeedback("failed", "This device can't sign the transaction. (The wallet keypath in your wallet settings seems incorrect)", "vault-feedback3", "wrong-keypath"), 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"), signingTransaction: new VaultFeedback("?", "Signing the transaction...", "vault-feedback3", "ask-signing"), signingRejected: new VaultFeedback("failed", "The user refused to sign the transaction", "vault-feedback3", "user-reject"), }; @@ -123,6 +125,23 @@ var vaultui = (function () { if (await self.askForPassphrase()) return true; } + if (json.error === "need-pin-on-device" || json.error === "need-passphrase-on-device") { + handled = true; + if (json.error === "need-pin-on-device") { + showError(VaultFeedbacks.needPinOnDevice); + } else { + showError(VaultFeedbacks.needPassphraseOnDevice); + } + await waitClickContinue(); + self.bridge.socket.send("refresh-device"); + var json = await self.bridge.waitBackendMessage(); + if (json.hasOwnProperty("error")) { + showError(json); + return false; + } + return true; + } + if (!handled) { showError(json); } @@ -203,7 +222,6 @@ var vaultui = (function () { $("#vault-confirm").css("display", "block"); $("#vault-confirm").text("Confirm"); return new Promise(function (resolve, reject) { - var pinCode = ""; $("#vault-confirm").click(async function (e) { e.preventDefault(); $("#vault-xpub").css("display", "none"); @@ -212,7 +230,24 @@ var vaultui = (function () { resolve({ addressType: $("select[name=\"addressType\"]").val(), accountNumber: parseInt($("select[name=\"accountNumber\"]").val()) - }); + }); + }); + }); + }; + + + /** + * @returns {Promise} + */ + this.waitClickContinue = function () { + $("#vault-confirm").css("display", "block"); + $("#vault-confirm").text("Continue"); + return new Promise(function (resolve, reject) { + $("#vault-confirm").click(async function (e) { + e.preventDefault(); + $("#vault-confirm").css("display", "none"); + $(this).unbind(); + resolve(""); }); }); }; @@ -287,7 +322,7 @@ var vaultui = (function () { this.askForPin = async function () { if (!await self.ensureConnectedToBackend()) return false; - + self.bridge.socket.send("ask-pin"); var json = await self.bridge.waitBackendMessage(); if (json.hasOwnProperty("error")) {