// 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 = '
'; html += `
${defect.heading}
`; for (const p of defect.paragraphs) { html += `

${p}

`; } html += "
"; return html; } module.exports = { NON_MASTER_XPRV, walletDefect, walletDefectHtml, };