87 lines
3.9 KiB
JavaScript
87 lines
3.9 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 pre-#210 path 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.
|
|
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 sentence for the places that have room for one: the flash on a
|
|
// blocked Send, the inline error on the approval screens.
|
|
shortMessage:
|
|
"This wallet cannot sign, because it was imported from an " +
|
|
"extended private key that is not a master key. The wallet list " +
|
|
"explains what happened.",
|
|
},
|
|
};
|
|
|
|
// 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,
|
|
};
|