Each control that leads to a signature or to the private key now has a test that it refuses a defective wallet before decrypting anything: Send on the main, address and token screens, Export Private Key, and both approval screens, as drawn and as clicked. Send on the confirmation screen had no such check. The Send buttons stand in front of it, but the popup reopens onto it from a saved view, so it now refuses the same way. The comments that said the wallet's key cannot be derived now say that getSignerForAddress refuses it, and the walletDefects module comment names both earlier import paths. Model: opus-5-5
91 lines
4.1 KiB
JavaScript
91 lines
4.1 KiB
JavaScript
// Wallets already in stored state whose key cannot be used, and the copy that
|
|
// explains them.
|
|
//
|
|
// Refusing a non-master extended private key at import time does nothing for a
|
|
// wallet imported before that refusal existed. Such a wallet is detected here,
|
|
// at wallet-list render time, so the user meets the explanation on the list
|
|
// screen rather than an exception on the send screen. Nothing here modifies or
|
|
// removes a wallet: the record is the user's data.
|
|
|
|
const { parseExtendedKey } = require("./wallet");
|
|
|
|
const NON_MASTER_XPRV = "non-master-xprv";
|
|
|
|
// An "xprv" wallet stores the neutered BIP-44 Ethereum node, four levels below
|
|
// the key that was imported: the current import path derives the absolute
|
|
// m/44'/60'/0'/0 from a depth-0 key, and the path before #210 (57959b7)
|
|
// derived the same four levels as a relative path beneath whatever depth it
|
|
// was given. A master import therefore stores a depth-4 xpub and a depth-d
|
|
// import stores depth d + 4, which makes the stored xpub an exact read on the
|
|
// imported key's depth — and it is readable without the password, unlike the
|
|
// key itself.
|
|
//
|
|
// The first import path (7a7f9c5) does not fit: it stored the imported key's
|
|
// own xpub with no derivation, so a wallet it wrote is judged wrongly here (a
|
|
// master import as defective, a depth-4 import as sound). 57959b7 replaced it
|
|
// in the same push, and no tag contains it.
|
|
const BIP44_ETH_XPUB_DEPTH = 4;
|
|
|
|
const DEFECTS = {
|
|
[NON_MASTER_XPRV]: {
|
|
id: NON_MASTER_XPRV,
|
|
heading: "This wallet's addresses were derived incorrectly.",
|
|
paragraphs: [
|
|
"This wallet was imported from an extended private key that is " +
|
|
"not a master key. An earlier version applied the Ethereum " +
|
|
"derivation path beneath that key instead of from a master " +
|
|
"key, so the addresses listed here are not the ones that key " +
|
|
"produces under the standard path.",
|
|
"Signing and sending are disabled for this wallet. The addresses " +
|
|
"do descend from the extended private key you imported, so " +
|
|
"anything they hold is still reachable by software that " +
|
|
"repeats the same non-standard derivation. Check them in a " +
|
|
"block explorer before deciding what to do.",
|
|
"To see the addresses this key produces under the standard path, " +
|
|
"import the master extended private key, or the recovery " +
|
|
"phrase it came from, as a new wallet. Nothing here has been " +
|
|
"changed or removed, and this wallet stays until you delete " +
|
|
"it yourself.",
|
|
],
|
|
// One line, for the flash on a blocked Send and the inline error on
|
|
// the approval screens. It must fit on the flash line; see showFlash()
|
|
// in src/popup/views/helpers.js.
|
|
shortMessage: "This wallet cannot sign. See the wallet list.",
|
|
},
|
|
};
|
|
|
|
// The defect record for a wallet, or null if there is nothing wrong with it
|
|
// that this module can see. Read-only.
|
|
//
|
|
// A wallet whose xpub will not parse gets null rather than a defect: there is
|
|
// no basis in that case to tell the user their key was not a master key, and a
|
|
// wrong explanation is worse than none.
|
|
function walletDefect(walletData) {
|
|
if (!walletData || walletData.type !== "xprv") return null;
|
|
const node = parseExtendedKey(walletData.xpub);
|
|
if (!node) return null;
|
|
if (node.depth === BIP44_ETH_XPUB_DEPTH) return null;
|
|
return DEFECTS[NON_MASTER_XPRV];
|
|
}
|
|
|
|
// The notice block for the wallet list, or "" for a wallet with no defect.
|
|
// The copy is fixed text from this module, so it needs no escaping.
|
|
function walletDefectHtml(walletData) {
|
|
const defect = walletDefect(walletData);
|
|
if (!defect) return "";
|
|
let html =
|
|
'<div class="border border-red-500 border-dashed p-2 my-1 text-xs text-red-500">';
|
|
html += `<div class="font-bold mb-1">${defect.heading}</div>`;
|
|
for (const p of defect.paragraphs) {
|
|
html += `<p class="mb-1">${p}</p>`;
|
|
}
|
|
html += "</div>";
|
|
return html;
|
|
}
|
|
|
|
module.exports = {
|
|
NON_MASTER_XPRV,
|
|
walletDefect,
|
|
walletDefectHtml,
|
|
};
|