From 7b5c60f321a204efba0cd72ce6e734df8bd3470a Mon Sep 17 00:00:00 2001 From: Armani Ferrante Date: Fri, 12 Feb 2021 13:44:46 +0800 Subject: [PATCH] Path dropdown --- package.json | 1 + src/pages/LoginPage.js | 82 ++++++++++++++++++------ src/utils/walletProvider/localStorage.js | 18 ++++-- yarn.lock | 31 +++++++-- 4 files changed, 103 insertions(+), 29 deletions(-) diff --git a/package.json b/package.json index 50cbe93a..71d7a227 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "bn.js": "^5.1.2", "bs58": "^4.0.1", "buffer-layout": "^1.2.0", + "ed25519-hd-key": "^1.2.0", "immutable-tuple": "^0.4.10", "mdi-material-ui": "^6.21.0", "notistack": "^1.0.2", diff --git a/src/pages/LoginPage.js b/src/pages/LoginPage.js index 69f599c4..424400e4 100644 --- a/src/pages/LoginPage.js +++ b/src/pages/LoginPage.js @@ -6,8 +6,10 @@ import { mnemonicToSeed, storeMnemonicAndSeed, } from '../utils/wallet-seed'; -import { getAccountFromSeed } from '../utils/walletProvider/localStorage.js'; -import { DERIVATION_PATH } from '../utils/walletProvider/localStorage'; +import { + getAccountFromSeed, + DERIVATION_PATH, +} from '../utils/walletProvider/localStorage.js'; import { useSolanaExplorerUrlSuffix } from '../utils/connection'; import Container from '@material-ui/core/Container'; import LoadingIndicator from '../components/LoadingIndicator'; @@ -16,11 +18,15 @@ import Card from '@material-ui/core/Card'; import CardContent from '@material-ui/core/CardContent'; import { Typography } from '@material-ui/core'; import TextField from '@material-ui/core/TextField'; +import InputLabel from '@material-ui/core/InputLabel'; import Checkbox from '@material-ui/core/Checkbox'; +import FormControl from '@material-ui/core/FormControl'; import FormControlLabel from '@material-ui/core/FormControlLabel'; import CardActions from '@material-ui/core/CardActions'; import Button from '@material-ui/core/Button'; import Switch from '@material-ui/core/Switch'; +import Select from '@material-ui/core/Select'; +import MenuItem from '@material-ui/core/MenuItem'; import { useCallAsync } from '../utils/notifications'; import Link from '@material-ui/core/Link'; import { validateMnemonic } from 'bip39'; @@ -315,19 +321,29 @@ function RestoreWalletForm({ goBack }) { } function DerivedAccounts({ goBack, mnemonic, seed, password }) { - const [useBip44, setUseBip44] = useState(true); const callAsync = useCallAsync(); const urlSuffix = useSolanaExplorerUrlSuffix(); - const derivationPath = useBip44 - ? DERIVATION_PATH.bip44 - : DERIVATION_PATH.deprecated; + const [dPathMenuItem, setDPathMenuItem] = useState( + DerivationPathMenuItem.Bip44, + ); const accounts = [...Array(10)].map((_, idx) => { - return getAccountFromSeed(Buffer.from(seed, 'hex'), idx, derivationPath); + return getAccountFromSeed( + Buffer.from(seed, 'hex'), + idx, + toDerivationPath(dPathMenuItem), + ); }); function submit() { - callAsync(storeMnemonicAndSeed(mnemonic, seed, password, derivationPath)); + callAsync( + storeMnemonicAndSeed( + mnemonic, + seed, + password, + toDerivationPath(dPathMenuItem), + ), + ); } return ( @@ -342,16 +358,22 @@ function DerivedAccounts({ goBack, mnemonic, seed, password }) { Derivable Accounts - setUseBip44(!useBip44)} - /> - } - labelPlacement={'start'} - label={useBip44 ? 'BIP 44' : 'Deprecated'} - /> + + + {accounts.map((acc) => { return ( @@ -381,3 +403,27 @@ function DerivedAccounts({ goBack, mnemonic, seed, password }) { ); } + +// Material UI's Select doesn't render properly when using an `undefined` value, +// so we define this type and the subsequent `toDerivationPath` translaotr as a +// workaround. +// +// DERIVATION_PATH.deprecated is always undefined. +const DerivationPathMenuItem = { + Deprecated: 0, + Bip44: 1, + Bip44Change: 2, +}; + +function toDerivationPath(dPathMenuItem) { + switch (dPathMenuItem) { + case DerivationPathMenuItem.Deprecated: + return DERIVATION_PATH.deprecated; + case DerivationPathMenuItem.Bip44: + return DERIVATION_PATH.bip44; + case DerivationPathMenuItem.Bip44Change: + return DERIVATION_PATH.bip44Change; + default: + throw new Error(`invalid derivation path: ${dPathMenuItem}`); + } +} diff --git a/src/utils/walletProvider/localStorage.js b/src/utils/walletProvider/localStorage.js index ec530b4a..fbabc166 100644 --- a/src/utils/walletProvider/localStorage.js +++ b/src/utils/walletProvider/localStorage.js @@ -3,29 +3,37 @@ import * as bip32 from 'bip32'; import nacl from 'tweetnacl'; import { Account } from '@solana/web3.js'; import bs58 from 'bs58'; +import { derivePath } from 'ed25519-hd-key'; export const DERIVATION_PATH = { deprecated: undefined, bip44: 'bip44', + bip44Change: 'bip44Change', }; +// morning toast tornado know rebuild vote calm can middle prison census there export function getAccountFromSeed( seed, walletIndex, dPath = undefined, accountIndex = 0, ) { - const path = derivationPath(walletIndex, dPath, accountIndex); - const derivedSeed = bip32.fromSeed(seed).derivePath(path).privateKey; + console.log('dpath', dPath); + const derivedSeed = deriveSeed(seed, walletIndex, dPath, accountIndex); return new Account(nacl.sign.keyPair.fromSeed(derivedSeed).secretKey); } -function derivationPath(walletIndex, derivationPath, accountIndex) { +function deriveSeed(seed, walletIndex, derivationPath, accountIndex) { switch (derivationPath) { case DERIVATION_PATH.deprecated: - return `m/501'/${walletIndex}'/0/${accountIndex}`; + const path = `m/501'/${walletIndex}'/0/${accountIndex}`; + return bip32.fromSeed(seed).derivePath(path).privateKey; case DERIVATION_PATH.bip44: - return `m/44'/501'/${walletIndex}'/${accountIndex}`; + const path44 = `m/44'/501'/${walletIndex}'`; + return derivePath(path44, seed).key; + case DERIVATION_PATH.bip44Change: + const path44Change = `m/44'/501'/${walletIndex}'/0'`; + return derivePath(path44Change, seed).key; default: throw new Error(`invalid derivation path: ${derivationPath}`); } diff --git a/yarn.lock b/yarn.lock index ecb295fe..ac65f31e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3424,6 +3424,16 @@ bip32@^2.0.5: typeforce "^1.11.5" wif "^2.0.6" +bip39@3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/bip39/-/bip39-3.0.2.tgz#2baf42ff3071fc9ddd5103de92e8f80d9257ee32" + integrity sha512-J4E1r2N0tUylTKt07ibXvhpT2c5pyAFgvuA5q1H9uDy6dEGpjV8jmymh3MTYJDLCNbIVClSB9FbND49I6N24MQ== + dependencies: + "@types/node" "11.11.6" + create-hash "^1.1.0" + pbkdf2 "^3.0.9" + randombytes "^2.0.1" + bip39@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/bip39/-/bip39-3.0.3.tgz#4a8b79067d6ed2e74f9199ac994a2ab61b176760" @@ -4438,7 +4448,7 @@ create-hash@^1.1.0, create-hash@^1.1.2, create-hash@^1.2.0: ripemd160 "^2.0.1" sha.js "^2.4.0" -create-hmac@^1.1.0, create-hmac@^1.1.4, create-hmac@^1.1.7: +create-hmac@1.1.7, create-hmac@^1.1.0, create-hmac@^1.1.4, create-hmac@^1.1.7: version "1.1.7" resolved "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.7.tgz#69170c78b3ab957147b2b8b04572e47ead2243ff" integrity sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg== @@ -5159,6 +5169,15 @@ ecc-jsbn@~0.1.1: jsbn "~0.1.0" safer-buffer "^2.1.0" +ed25519-hd-key@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/ed25519-hd-key/-/ed25519-hd-key-1.2.0.tgz#819d43c6a96477c9385bd121dccc94dbc6c6598c" + integrity sha512-pwES3tQ4Z8g3sfIBZEgtuTwFtHq5AlB9L8k9a48k7qPn74q2OmgrrgkdwyJ+P2GVTOBVCClAC7w21Wpksso3gw== + dependencies: + bip39 "3.0.2" + create-hmac "1.1.7" + tweetnacl "1.0.3" + ee-first@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" @@ -12715,16 +12734,16 @@ tunnel-agent@^0.6.0: dependencies: safe-buffer "^5.0.1" +tweetnacl@1.0.3, tweetnacl@^1.0.0, tweetnacl@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-1.0.3.tgz#ac0af71680458d8a6378d0d0d050ab1407d35596" + integrity sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw== + tweetnacl@^0.14.3, tweetnacl@~0.14.0: version "0.14.5" resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= -tweetnacl@^1.0.0, tweetnacl@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-1.0.3.tgz#ac0af71680458d8a6378d0d0d050ab1407d35596" - integrity sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw== - type-check@^0.4.0, type-check@~0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1"