Merge pull request #356 from poanetwork/vb-rsk-dpath-update

Custom derivation paths and access to funds in accounts derived from ETH dPath
This commit is contained in:
Victor Baranov 2020-05-07 18:25:01 +03:00 committed by GitHub
commit 7662c11542
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 250 additions and 103 deletions

View File

@ -2,9 +2,11 @@
## Current Master
- [#356](https://github.com/poanetwork/nifty-wallet/pull/356) - (Backwards-compatibility feature) Custom derivation paths and access to funds in accounts derived from ETH dPath
- [#379](https://github.com/poanetwork/nifty-wallet/pull/379) - (Feature) Ability to set custom nonce of tx
- [#377](https://github.com/poanetwork/nifty-wallet/pull/377) - (Fix) Sign message screen: do not decode message if it is not hex encoded
- [#364](https://github.com/poanetwork/nifty-wallet/pull/364) - (Fix) notifications order in batch requests
- [#364](https://github.com/poanetwork/nifty-wallet/pull/364) - Fix notifications order in batch requests
## 5.0.3 Fri May 01 2020
@ -14,6 +16,7 @@
- [#368](https://github.com/poanetwork/nifty-wallet/pull/368) - (Fix) Ability to import Keystore file if it is not secured by password
- [#366](https://github.com/poanetwork/nifty-wallet/pull/366) - (Fix) Increase max token symbol length up to 12
- [#363](https://github.com/poanetwork/nifty-wallet/pull/363) - (Fix) token decimals display in pending tx screen
- [#356](https://github.com/poanetwork/nifty-wallet/pull/356) - (Backwards-compatibility feature) Custom derivation paths and access to funds in accounts derived from ETH dPath
## 5.0.2 Thu Apr 16 2020

View File

@ -12,10 +12,10 @@ import createJsonRpcClient from './createJsonRpcClient'
import createLocalhostClient from './createLocalhostClient'
const createPocketClient = require('./createPocketClient')
const { createSwappableProxy, createEventEmitterProxy } = require('swappable-obj-proxy')
const ethNetProps = require('eth-net-props')
import ethNetProps from 'eth-net-props'
import parse from 'url-parse'
const networks = { networkList: {} }
const { isKnownProvider, getDPath } = require('../../../../old-ui/app/util')
const { isKnownProvider } = require('../../../../old-ui/app/util')
const {
ROPSTEN,
@ -205,8 +205,6 @@ module.exports = class NetworkController extends EventEmitter {
const previousNetworkID = this.getNetworkState()
this.setNetworkState('loading')
this._configureProvider(opts)
const dPath = getDPath(opts.type)
this.store.updateState({ dPath })
this.emit('networkDidChange', opts.type, previousNetworkID)
}

View File

@ -1,5 +1,5 @@
const KeyringController = require('eth-keychain-controller')
const log = require('loglevel')
import KeyringController from 'eth-keychain-controller'
import log from 'loglevel'
const { getDPath } = require('../../../old-ui/app/util')
const seedPhraseVerifier = {
@ -17,14 +17,14 @@ const seedPhraseVerifier = {
* @returns {Promise<void>} Promises undefined
*
*/
verifyAccounts (createdAccounts, seedWords, network) {
verifyAccounts (createdAccounts, seedWords, network, isCreatedWithCorrectDPath) {
return new Promise((resolve, reject) => {
if (!createdAccounts || createdAccounts.length < 1) {
return reject(new Error('No created accounts defined.'))
}
const dPath = getDPath(network)
const dPath = getDPath(network, isCreatedWithCorrectDPath)
const keyringController = new KeyringController({})
const Keyring = keyringController.getKeyringClassForType('HD Key Tree')
const opts = {
@ -56,4 +56,4 @@ const seedPhraseVerifier = {
},
}
module.exports = seedPhraseVerifier
export default seedPhraseVerifier

View File

@ -52,7 +52,7 @@ const version = require('../manifest.json').version
import ethUtil, { BN } from 'ethereumjs-util'
const GWEI_BN = new BN('1000000000')
import percentile from 'percentile'
const seedPhraseVerifier = require('./lib/seed-phrase-verifier')
import seedPhraseVerifier from './lib/seed-phrase-verifier'
import log from 'loglevel'
const TrezorKeyring = require('eth-trezor-keyring')
const LedgerBridgeKeyring = require('eth-ledger-bridge-keyring')
@ -169,26 +169,32 @@ module.exports = class MetamaskController extends EventEmitter {
})
// ensure accountTracker updates balances after network change
this.networkController.on('networkDidChange', (newType, previousNetworkIDStr) => {
const dPath = getDPath(newType)
this.deriveKeyringFromNewDPath(dPath)
.then(accounts => {
this.accountTracker._updateAccounts()
this.detectTokensController.restartTokenDetection()
this.networkController.on('networkDidChange', (newNetworkType, previousNetworkIDStr) => {
this.keyringController.isCreatedWithCorrectDPath()
.then(isCreatedWithCorrectDPath => {
const dPath = getDPath(newNetworkType, isCreatedWithCorrectDPath)
this.deriveKeyringFromNewDPath(dPath)
.then(_accounts => {
this.accountTracker._updateAccounts()
this.detectTokensController.restartTokenDetection()
const previousNetworkID = parseInt(previousNetworkIDStr, 10)
const nextNetwork = getNetworkID({network: newType})
const nextNetworkID = parseInt(nextNetwork && nextNetwork.netId, 10)
if (nextNetworkID !== previousNetworkID) {
const isPreviousETC = previousNetworkID === CLASSIC_CODE
const isPreviousRSK = ifRSK(previousNetworkID)
const isNextETC = nextNetworkID === CLASSIC_CODE
const isNextRSK = ifRSK(nextNetworkID)
if (isPreviousETC || isPreviousRSK || isNextETC || isNextRSK) {
this.forgetDevice(LEDGER, false)
this.forgetDevice(TREZOR, false)
const previousNetworkID = parseInt(previousNetworkIDStr, 10)
const nextNetwork = getNetworkID({network: newNetworkType})
const nextNetworkID = parseInt(nextNetwork && nextNetwork.netId, 10)
if (nextNetworkID !== previousNetworkID) {
const isPreviousETC = previousNetworkID === CLASSIC_CODE
const isPreviousRSK = ifRSK(previousNetworkID)
const isNextETC = nextNetworkID === CLASSIC_CODE
const isNextRSK = ifRSK(nextNetworkID)
if (isPreviousETC || isPreviousRSK || isNextETC || isNextRSK) {
this.forgetDevice(LEDGER, false)
this.forgetDevice(TREZOR, false)
}
}
}
})
.catch(e => {
console.log(e)
})
})
.catch(e => {
console.log(e)
@ -493,6 +499,7 @@ module.exports = class MetamaskController extends EventEmitter {
addNewKeyring: nodeify(keyringController.addNewKeyring, keyringController),
addNewMultisig: nodeify(keyringController.addNewMultisig, keyringController),
exportAccount: nodeify(keyringController.exportAccount, keyringController),
isCreatedWithCorrectDPath: nodeify(keyringController.isCreatedWithCorrectDPath, keyringController),
// txController
cancelTransaction: nodeify(txController.cancelTransaction, txController),
@ -582,7 +589,7 @@ module.exports = class MetamaskController extends EventEmitter {
* @param {} password
* @param {} seed
*/
async createNewVaultAndRestore (password, seed) {
async createNewVaultAndRestore (password, seed, dPath) {
const releaseLock = await this.createVaultMutex.acquire()
try {
let accounts, lastBalance
@ -592,9 +599,8 @@ module.exports = class MetamaskController extends EventEmitter {
// clear known identities
this.preferencesController.setAddresses([])
// create new vault
const network = this.networkController.getProviderConfig().type
const dPath = getDPath(network)
this.store.updateState({dPath})
const networkType = this.networkController.getProviderConfig().type
const isCreatedWithCorrectDPath = true
const vault = await keyringController.createNewVaultAndRestore(password, seed, dPath)
const ethQuery = new EthQuery(this.provider)
@ -606,7 +612,7 @@ module.exports = class MetamaskController extends EventEmitter {
throw new Error('MetamaskController - No HD Key Tree found')
}
setDPath(primaryKeyring, network)
setDPath(primaryKeyring, networkType, isCreatedWithCorrectDPath)
// seek out the first zero balance
while (lastBalance !== '0x0') {
@ -911,13 +917,14 @@ module.exports = class MetamaskController extends EventEmitter {
* @returns {} keyState
*/
async addNewAccount () {
const primaryKeyring = this.keyringController.getKeyringsByType('HD Key Tree')[0]
const keyringController = this.keyringController
const primaryKeyring = keyringController.getKeyringsByType('HD Key Tree')[0]
if (!primaryKeyring) {
throw new Error('MetamaskController - No HD Key Tree found')
}
const network = this.networkController.getProviderConfig().type
setDPath(primaryKeyring, network)
const keyringController = this.keyringController
const networkType = this.networkController.getProviderConfig().type
const isCreatedWithCorrectDPath = await keyringController.isCreatedWithCorrectDPath()
setDPath(primaryKeyring, networkType, isCreatedWithCorrectDPath)
const oldAccounts = await keyringController.getAccounts()
const keyState = await keyringController.addNewAccount(primaryKeyring)
const newAccounts = await keyringController.getAccounts()
@ -965,12 +972,14 @@ module.exports = class MetamaskController extends EventEmitter {
* @returns {Promise<string>} Seed phrase to be confirmed by the user.
*/
async verifySeedPhrase () {
const primaryKeyring = this.keyringController.getKeyringsByType('HD Key Tree')[0]
const keyringController = this.keyringController
const isCreatedWithCorrectDPath = await keyringController.isCreatedWithCorrectDPath()
const primaryKeyring = keyringController.getKeyringsByType('HD Key Tree')[0]
if (!primaryKeyring) {
throw new Error('MetamaskController - No HD Key Tree found')
}
const network = this.networkController.getProviderConfig().type
setDPath(primaryKeyring, network)
const networkType = this.networkController.getProviderConfig().type
setDPath(primaryKeyring, networkType, isCreatedWithCorrectDPath)
const serialized = await primaryKeyring.serialize()
const seedWords = serialized.mnemonic
@ -981,7 +990,7 @@ module.exports = class MetamaskController extends EventEmitter {
}
try {
await seedPhraseVerifier.verifyAccounts(accounts, seedWords, network)
await seedPhraseVerifier.verifyAccounts(accounts, seedWords, networkType, isCreatedWithCorrectDPath)
return seedWords
} catch (err) {
log.error(err.message)
@ -1086,8 +1095,6 @@ module.exports = class MetamaskController extends EventEmitter {
const privateKey = await accountImporter.importAccount(strategy, args)
keyring = await this.keyringController.addNewKeyring('Simple Key Pair', [ privateKey ])
}
const network = this.networkController.getProviderConfig().type
setDPath(keyring, network)
const accounts = await keyring.getAccounts()
// update accounts in preferences controller
const allAccounts = await this.keyringController.getAccounts()

Binary file not shown.

Binary file not shown.

View File

@ -4,7 +4,7 @@ const Component = require('react').Component
const h = require('react-hyperscript')
const connect = require('react-redux').connect
const actions = require('../../ui/app/actions')
const { getCurrentKeyring, ifContractAcc, valuesFor, toChecksumAddress } = require('./util')
const { getCurrentKeyring, ifContractAcc, valuesFor, toChecksumAddress, ifLooseAcc, ifRSK, ifETC } = require('./util')
const Identicon = require('./components/identicon')
const EthBalance = require('./components/eth-balance')
const TransactionList = require('./components/transaction-list')
@ -14,10 +14,10 @@ const TabBar = require('./components/tab-bar')
const TokenList = require('./components/token-list')
const AccountDropdowns = require('./components/account-dropdowns/account-dropdowns.component').AccountDropdowns
const CopyButton = require('./components/copy/copy-button')
const ToastComponent = require('./components/toast')
import * as Toast from './components/toast'
import { getMetaMaskAccounts } from '../../ui/app/selectors'
module.exports = connect(mapStateToProps)(AccountDetailScreen)
module.exports = connect(mapStateToProps, mapDispatchToProps)(AccountDetailScreen)
function mapStateToProps (state) {
const accounts = getMetaMaskAccounts(state)
@ -42,11 +42,66 @@ function mapStateToProps (state) {
}
}
function mapDispatchToProps (dispatch) {
return {
actions: {
showSendPage: () => dispatch(actions.showSendPage()),
showSendContractPage: ({ methodSelected, methodABI, inputValues }) => dispatch(actions.showSendContractPage({methodSelected, methodABI, inputValues})),
buyEthView: (selected) => dispatch(actions.buyEthView(selected)),
viewPendingTx: (txId) => dispatch(actions.viewPendingTx(txId)),
setAccountLabel: (account, label) => dispatch(actions.setAccountLabel(account, label)),
showRemoveTokenPage: (token) => dispatch(actions.showRemoveTokenPage(token)),
showAddSuggestedTokenPage: () => dispatch(actions.showAddSuggestedTokenPage()),
showAddTokenPage: () => dispatch(actions.showAddTokenPage()),
setCurrentAccountTab: (key) => dispatch(actions.setCurrentAccountTab(key)),
displayToast: (msg) => dispatch(actions.displayToast(msg)),
isCreatedWithCorrectDPath: () => dispatch(actions.isCreatedWithCorrectDPath()),
},
}
}
inherits(AccountDetailScreen, Component)
function AccountDetailScreen () {
Component.call(this)
}
AccountDetailScreen.prototype.componentDidMount = function () {
const props = this.props
const { address, network, keyrings, identities } = props
props.actions.isCreatedWithCorrectDPath()
.then(isCreatedWithCorrectDPath => {
if (!isCreatedWithCorrectDPath) {
const currentKeyring = getCurrentKeyring(address, network, keyrings, identities)
if (!ifLooseAcc(currentKeyring) && !ifContractAcc(currentKeyring) && (ifRSK(network) || ifETC(network))) {
props.actions.displayToast(Toast.ERROR_ON_INCORRECT_DPATH)
}
}
})
}
AccountDetailScreen.prototype.componentWillUpdate = function (nextProps) {
const {
network: oldNet,
} = this.props
const {
network: newNet,
} = nextProps
if (oldNet !== newNet) {
const props = this.props
const { address, keyrings, identities } = props
props.actions.isCreatedWithCorrectDPath()
.then(isCreatedWithCorrectDPath => {
if (!isCreatedWithCorrectDPath) {
const currentKeyring = getCurrentKeyring(address, newNet, keyrings, identities)
if (!ifLooseAcc(currentKeyring) && !ifContractAcc(currentKeyring) && (ifRSK(newNet) || ifETC(newNet))) {
props.actions.displayToast(Toast.ERROR_ON_INCORRECT_DPATH)
}
}
})
}
}
AccountDetailScreen.prototype.render = function () {
const props = this.props
const { network, conversionRate, currentCurrency } = props
@ -56,7 +111,7 @@ AccountDetailScreen.prototype.render = function () {
const account = props.accounts[selected]
if (Object.keys(props.suggestedTokens).length > 0) {
this.props.dispatch(actions.showAddSuggestedTokenPage())
this.props.actions.showAddSuggestedTokenPage()
}
const currentKeyring = getCurrentKeyring(props.address, network, props.keyrings, props.identities)
@ -65,8 +120,9 @@ AccountDetailScreen.prototype.render = function () {
h('.account-detail-section.full-flex-height', [
h(ToastComponent, {
isSuccess: false,
h(Toast.ToastComponent, {
type: Toast.TOAST_TYPE_ERROR,
hideManually: true,
}),
// identicon, label, balance, etc
@ -108,7 +164,7 @@ AccountDetailScreen.prototype.render = function () {
isEditingLabel: false,
},
saveText: (text) => {
props.dispatch(actions.setAccountLabel(selected, text))
props.actions.setAccountLabel(selected, text)
},
}, [
@ -223,16 +279,16 @@ AccountDetailScreen.prototype.render = function () {
h('.flex-grow'),
!ifContractAcc(currentKeyring) ? h('button', {
onClick: () => props.dispatch(actions.buyEthView(selected)),
onClick: () => props.actions.buyEthView(selected),
style: { marginRight: '10px' },
}, 'Buy') : null,
h('button', {
onClick: () => {
if (ifContractAcc(currentKeyring)) {
return props.dispatch(actions.showSendContractPage({}))
return props.actions.showSendContractPage({})
} else {
return props.dispatch(actions.showSendPage())
return props.actions.showSendPage()
}
},
}, ifContractAcc(currentKeyring) ? 'Execute methods' : 'Send'),
@ -278,7 +334,7 @@ AccountDetailScreen.prototype.tabSections = function () {
],
defaultTab: currentAccountTab || 'history',
tabSelected: (key) => {
this.props.dispatch(actions.setCurrentAccountTab(key))
this.props.actions.setCurrentAccountTab(key)
},
}),
@ -297,8 +353,8 @@ AccountDetailScreen.prototype.tabSwitchView = function () {
userAddress: address,
network,
tokens,
addToken: () => this.props.dispatch(actions.showAddTokenPage()),
removeToken: (token) => this.props.dispatch(actions.showRemoveTokenPage(token)),
addToken: () => this.props.actions.showAddTokenPage(),
removeToken: (token) => this.props.actions.showRemoveTokenPage(token),
})
default:
return this.transactionList()
@ -317,7 +373,7 @@ AccountDetailScreen.prototype.transactionList = function () {
address,
shapeShiftTxList,
viewPendingTx: (txId) => {
this.props.dispatch(actions.viewPendingTx(txId))
this.props.actions.viewPendingTx(txId)
},
})
}

View File

@ -4,10 +4,11 @@ import actions from '../../../../ui/app/actions'
import { connect } from 'react-redux'
import { DropdownMenuItem } from '../dropdown'
import Identicon from '../identicon'
import { ifLooseAcc, ifContractAcc, ifHardwareAcc } from '../../util'
import { ifLooseAcc, ifContractAcc, ifHardwareAcc, ifRSK, ifETC } from '../../util'
import { getHdPaths, isLedger } from '../connect-hardware/util'
import { LEDGER } from '../connect-hardware/enum'
import { importTypes, labels } from '../../accounts/import/enums'
import { ERROR_ON_INCORRECT_DPATH } from '../toast'
class AccountsDropdownItemView extends Component {
static propTypes = {
@ -100,7 +101,7 @@ class AccountsDropdownItemView extends Component {
}
}
ifProxyAcc (address, setProxy) {
ifProxyAcc (address, _setProxy) {
return new Promise((resolve, reject) => {
this.props.actions.getContract(address)
.then(contractProps => {
@ -138,6 +139,15 @@ class AccountsDropdownItemView extends Component {
} else {
this.preventToast()
}
} else if (!ifLooseAcc(keyring) && !ifContractAcc(keyring) && (ifRSK(this.props.network) || ifETC(this.props.network))) {
this.props.actions.isCreatedWithCorrectDPath()
.then(isCreatedWithCorrectDPath => {
if (isCreatedWithCorrectDPath) {
this.preventToast()
} else {
this.props.actions.displayToast(ERROR_ON_INCORRECT_DPATH)
}
})
} else {
this.preventToast()
}
@ -152,6 +162,13 @@ class AccountsDropdownItemView extends Component {
}
}
function mapStateToProps (state) {
const result = {
network: state.metamask.network,
}
return result
}
const mapDispatchToProps = (dispatch) => {
return {
@ -163,10 +180,11 @@ const mapDispatchToProps = (dispatch) => {
return dispatch(actions.connectHardwareAndUnlockAddress(deviceName, hdPath, address))
},
displayToast: (msg) => dispatch(actions.displayToast(msg)),
isCreatedWithCorrectDPath: () => dispatch(actions.isCreatedWithCorrectDPath()),
},
}
}
module.exports = {
AccountsDropdownItemView: connect(null, mapDispatchToProps)(AccountsDropdownItemView),
AccountsDropdownItemView: connect(mapStateToProps, mapDispatchToProps)(AccountsDropdownItemView),
}

View File

@ -25,7 +25,7 @@ const { tokenInfoGetter, calcTokenAmount } = require('../../../ui/app/token-util
import BigNumber from 'bignumber.js'
import ethNetProps from 'eth-net-props'
import { getMetaMaskAccounts } from '../../../ui/app/selectors'
import ToastComponent from './toast'
import * as Toast from './toast'
const MIN_GAS_PRICE_BN = new BN('0')
const MIN_GAS_LIMIT_BN = new BN('21000')
@ -158,14 +158,15 @@ class PendingTx extends Component {
fontSize: '14px',
}
const isError = txMeta.simulationFails || !isValidAddress || insufficientBalance || (dangerousGasLimit && !gasLimitSpecified)
return (
h('div', {
key: txMeta.id,
}, [
h(ToastComponent, {
isSuccess: false,
h(Toast.ToastComponent, {
type: Toast.TOAST_TYPE_ERROR,
}),
h('form#pending-tx-form', {

View File

@ -5,7 +5,7 @@ import PersistentForm from '../../../lib/persistent-form'
import SendProfile from './send-profile'
import SendHeader from './send-header'
import ErrorComponent from '../error'
import ToastComponent from '../toast'
import * as Toast from '../toast'
import Select from 'react-select'
import actions from '../../../../ui/app/actions'
import abi from 'web3-eth-abi'
@ -154,7 +154,7 @@ class SendTransactionScreen extends PersistentForm {
<SendProfile />
<SendHeader title="Execute Method" />
<ErrorComponent error={error} />
<ToastComponent isSuccess={true} />
<Toast.ToastComponent type={Toast.TOAST_TYPE_SUCCESS} />
<div style={{ padding: '0 30px' }}>
<Select
clearable={false}

View File

@ -15,7 +15,7 @@ import SendProfile from './send-profile'
import SendHeader from './send-header'
import ErrorComponent from '../error'
import { getMetaMaskAccounts } from '../../../../ui/app/selectors'
import ToastComponent from '../toast'
import * as Toast from '../toast'
const optionalDataLabelStyle = {
background: '#ffffff',
@ -42,7 +42,7 @@ class SendTransactionScreen extends PersistentForm {
return (
<div className="send-screen flex-column flex-grow">
<ToastComponent isSuccess={false} />
<Toast.ToastComponent type={Toast.TOAST_TYPE_ERROR} />
<SendProfile/>
<SendHeader

View File

@ -4,12 +4,18 @@ import { connect } from 'react-redux'
import classnames from 'classnames'
import actions from '../../../ui/app/actions'
const TOAST_TYPE_SUCCESS = 'success'
const TOAST_TYPE_ERROR = 'error'
const ERROR_ON_INCORRECT_DPATH = 'The account is derived from ETH derivation path despite you connected to another chain. If you are ready to switch to correct derivation path, just restore from the same seed phrase.'
class ToastComponent extends Component {
static propTypes = {
msg: PropTypes.string,
toastMsg: PropTypes.string,
isSuccess: PropTypes.bool,
type: PropTypes.string,
hideToast: PropTypes.func,
hideManually: PropTypes.bool,
}
constructor (props) {
@ -19,10 +25,12 @@ class ToastComponent extends Component {
componentDidUpdate (prevProps) {
if ((!prevProps.msg && this.props.msg) || (!prevProps.toastMsg && this.props.toastMsg)) {
this.timerID = setTimeout(() => {
this.props.hideToast()
clearTimeout(this.timerID)
}, 4000)
if (!this.props.hideManually) {
this.timerID = setTimeout(() => {
this.props.hideToast()
clearTimeout(this.timerID)
}, 4000)
}
}
}
@ -31,15 +39,23 @@ class ToastComponent extends Component {
clearTimeout(this.timerID)
}
_getClass (type) {
switch (type) {
case TOAST_TYPE_SUCCESS:
return 'green'
case TOAST_TYPE_ERROR:
return 'red'
default:
return 'green'
}
}
render () {
let toastMsg = this.props.msg || this.props.toastMsg
toastMsg = (toastMsg && toastMsg.message) || toastMsg
return toastMsg ? (
<div
className={classnames('toast', {
'green': this.props.isSuccess,
'red': !this.props.isSuccess,
})}
className={classnames('toast', this._getClass(this.props.type))}
onClick={(e) => this.props.hideToast()}
>{toastMsg}</div>
) : null
@ -58,4 +74,9 @@ function mapDispatchToProps (dispatch) {
}
}
module.exports = connect(mapStateToProps, mapDispatchToProps)(ToastComponent)
module.exports = {
ToastComponent: connect(mapStateToProps, mapDispatchToProps)(ToastComponent),
TOAST_TYPE_SUCCESS,
TOAST_TYPE_ERROR,
ERROR_ON_INCORRECT_DPATH,
}

View File

@ -293,7 +293,7 @@ app sections
color: #ffffff !important;
font-size: 12px;
text-align: center;
padding: 10px;
padding: 20px;
width: 357px;
line-height: 14px;
position: fixed;

View File

@ -4,6 +4,7 @@ const Component = require('react').Component
const connect = require('react-redux').connect
const h = require('react-hyperscript')
const actions = require('../../../../../ui/app/actions')
const { getDPath } = require('../../../util')
module.exports = connect(mapStateToProps)(RevealSeedConfirmation)
@ -16,6 +17,7 @@ function mapStateToProps (state) {
return {
warning: state.appState.warning,
dPath: state.metamask.dPath,
provider: state.metamask.provider,
}
}
@ -115,7 +117,9 @@ RevealSeedConfirmation.prototype.checkConfirmation = function (event) {
}
}
RevealSeedConfirmation.prototype.revealSeedWords = function () {
RevealSeedConfirmation.prototype.revealSeedWords = async function () {
const password = document.getElementById('password-box').value
this.props.dispatch(actions.requestRevealSeed(password, this.props.dPath))
const isCreatedWithCorrectDPath = this.props.dispatch(actions.isCreatedWithCorrectDPath())
const dPath = getDPath(this.props.provider.type, isCreatedWithCorrectDPath)
this.props.dispatch(actions.requestRevealSeed(password, dPath))
}

View File

@ -3,6 +3,7 @@ const PersistentForm = require('../../../lib/persistent-form')
const connect = require('react-redux').connect
const h = require('react-hyperscript')
const actions = require('../../../../ui/app/actions')
const { getDPath } = require('../../util')
module.exports = connect(mapStateToProps)(RestoreVaultScreen)
@ -15,6 +16,7 @@ function mapStateToProps (state) {
return {
warning: state.appState.warning,
forgottenPassword: state.appState.forgottenPassword,
provider: state.metamask.provider,
}
}
@ -185,5 +187,7 @@ RestoreVaultScreen.prototype.createNewVaultAndRestore = function () {
// submit
this.warning = null
this.props.dispatch(actions.displayWarning(this.warning))
this.props.dispatch(actions.createNewVaultAndRestore(password, seed))
const isCreatedWithCorrectDPath = true
const dPath = getDPath(this.props.provider.type, isCreatedWithCorrectDPath)
this.props.dispatch(actions.createNewVaultAndRestore(password, seed, dPath))
}

View File

@ -111,11 +111,13 @@ UnlockScreen.prototype.onKeyPress = function (event) {
UnlockScreen.prototype.submitPassword = async function (event) {
const element = event.target
const password = element.value
const props = this.props
// reset input
element.value = ''
try {
const dPath = getDPath(this.props.provider.type) || this.props.dPath
await this.props.dispatch(actions.tryUnlockMetamask(password, dPath))
const isCreatedWithCorrectDPath = await props.dispatch(actions.isCreatedWithCorrectDPath())
const dPath = getDPath(props.provider.type, isCreatedWithCorrectDPath) || props.dPath
await props.dispatch(actions.tryUnlockMetamask(password, dPath))
} catch (e) {
log.error(e)
}

View File

@ -39,7 +39,7 @@ const {
RSK,
RSK_TESTNET,
RSK_TICK,
// customDPaths,
customDPaths,
} = require('../../app/scripts/controllers/network/enums')
const valueTable = {
@ -88,6 +88,7 @@ module.exports = {
ifHardwareAcc,
getAllKeyRingsAccounts,
ifRSK,
ifETC,
ifRSKByProviderType,
ifPOA,
toChecksumAddress,
@ -432,6 +433,12 @@ function ifRSK (network) {
return numericNet === RSK_CODE || numericNet === RSK_TESTNET_CODE
}
function ifETC (network) {
if (!network) return false
const numericNet = isNaN(network) ? network : parseInt(network)
return numericNet === CLASSIC_CODE
}
function ifRSKByProviderType (type) {
if (!type) return false
return type === RSK || type === RSK_TESTNET
@ -557,14 +564,16 @@ function getNetworkID ({ network }) {
}
}
function getDPath (network) {
// todo: return when the robust solution will be ready
return `m/44'/60'/0'/0`
// return customDPaths[network] || `m/44'/60'/0'/0`
function getDPath (networkType, isCreatedWithCorrectDPath) {
if (isCreatedWithCorrectDPath) {
return customDPaths[networkType] || `m/44'/60'/0'/0`
} else {
return `m/44'/60'/0'/0`
}
}
function setDPath (keyring, network) {
const dPath = getDPath(network)
function setDPath (keyring, networkType, isCreatedWithCorrectDPath) {
const dPath = getDPath(networkType, isCreatedWithCorrectDPath)
if (dPath && keyring.setHdPath) {
keyring.setHdPath(dPath)
}

View File

@ -1,9 +1,7 @@
const path = require('path')
const Func = require('./func').Functions
const account1 = '0x2E428ABd9313D256d64D1f69fe3929C3BE18fD1f'
// todo:
// const account1RSK = '0x7a9bc05F7441d862d1B83CB724861a9872FF43fe'
const account1RSK = '0x2E428aBd9313D256d64D1f69fe3929c3Be18Fd1F'
const account1RSK = '0x7a9bc05F7441d862d1B83CB724861a9872FF43fe'
const account2 = '0xd7b7AFeCa35e32594e29504771aC847E2a803742'
const testsFolder = './test-cases'
const setup = require(`${testsFolder}/setup.spec`)

View File

@ -1,8 +1,8 @@
const assert = require('assert')
const clone = require('clone')
const KeyringController = require('eth-keychain-controller')
import assert from 'assert'
import clone from 'clone'
import KeyringController from 'eth-keychain-controller'
import seedPhraseVerifier from '../../../app/scripts/lib/seed-phrase-verifier'
const firstTimeState = require('../../../app/scripts/first-time-state')
const seedPhraseVerifier = require('../../../app/scripts/lib/seed-phrase-verifier')
const mockEncryptor = require('../../lib/mock-encryptor')
describe('SeedPhraseVerifier', function () {

View File

@ -235,7 +235,7 @@ describe('Actions', () => {
createNewVaultAndRestoreSpy = sinon.stub(background, 'createNewVaultAndRestore')
createNewVaultAndRestoreSpy.callsFake((password, seed, callback) => {
createNewVaultAndRestoreSpy.callsFake((password, seed, dPath, callback) => {
callback(new Error('error'))
})

View File

@ -370,6 +370,7 @@ const actions = {
getRequestAccountTabIds,
setOpenMetamaskTabsIDs,
getOpenMetamaskTabsIds,
isCreatedWithCorrectDPath,
closeCurrentNotificationWindow,
closeNotificationWindow,
}
@ -406,7 +407,12 @@ function tryUnlockMetamask (password, dPath) {
})
.then(() => {
dispatch(actions.unlockSucceeded())
return forceUpdateMetamaskState(dispatch)
return updateMetamaskStateFromBackground()
.then(newState => {
newState = Object.assign(newState, {dPath: dPath})
dispatch(actions.updateMetamaskState(newState))
forceUpdateMetamaskState(dispatch)
})
})
.catch((err) => {
log.error(err)
@ -436,6 +442,21 @@ function tryUnlockMetamask (password, dPath) {
}
}
function isCreatedWithCorrectDPath () {
return dispatch => {
return new Promise((resolve, reject) => {
background.isCreatedWithCorrectDPath((err, isCreatedWithCorrectDPath) => {
if (err) {
dispatch(actions.displayWarning(err.message))
return reject(err)
}
resolve(isCreatedWithCorrectDPath)
})
})
}
}
function transitionForward () {
return {
type: this.TRANSITION_FORWARD,
@ -468,7 +489,7 @@ function confirmSeedWords () {
}
}
function createNewVaultAndRestore (password, seed) {
function createNewVaultAndRestore (password, seed, dPath) {
return (dispatch) => {
dispatch(actions.showLoadingIndication())
log.debug(`background.createNewVaultAndRestore`)
@ -479,7 +500,7 @@ function createNewVaultAndRestore (password, seed) {
return reject(err)
}
background.createNewVaultAndRestore(password, seed, (err) => {
background.createNewVaultAndRestore(password, seed, dPath, (err) => {
if (err) {
return reject(err)
}
@ -491,6 +512,11 @@ function createNewVaultAndRestore (password, seed) {
.then(() => dispatch(actions.unMarkPasswordForgotten()))
.then(() => {
dispatch(actions.showAccountsPage())
updateMetamaskStateFromBackground()
.then(newState => {
newState = Object.assign(newState, {dPath: dPath})
dispatch(actions.updateMetamaskState(newState))
})
dispatch(actions.hideLoadingIndication())
})
.catch(err => {

View File

@ -1,8 +1,8 @@
const extend = require('xtend')
import extend from 'xtend'
import log from 'loglevel'
const actions = require('../actions')
const txHelper = require('../../lib/tx-helper')
const { customHdPaths } = require('../../../old-ui/app/components/connect-hardware/util.js')
const log = require('loglevel')
module.exports = reduceApp