Support 24 words mnemonic

This commit is contained in:
Victor Baranov 2020-06-12 17:44:31 +03:00
parent 3ec0cccd32
commit 7dc88f0e13
6 changed files with 130 additions and 82 deletions

View File

@ -2,6 +2,7 @@
## Current Master ## Current Master
- [#389](https://github.com/poanetwork/nifty-wallet/pull/389) - (Feature) Support 24 words mnemonic phrase
- [#388](https://github.com/poanetwork/nifty-wallet/pull/388) - (Feature) "Send all" option for simple coin transfers - [#388](https://github.com/poanetwork/nifty-wallet/pull/388) - (Feature) "Send all" option for simple coin transfers
- [#385](https://github.com/poanetwork/nifty-wallet/pull/385) - (Feature) Display value of current pending tx's nonce on send tx screen - [#385](https://github.com/poanetwork/nifty-wallet/pull/385) - (Feature) Display value of current pending tx's nonce on send tx screen
- [#384](https://github.com/poanetwork/nifty-wallet/pull/384) - (Fix) placement of HW Connect button title - [#384](https://github.com/poanetwork/nifty-wallet/pull/384) - (Fix) placement of HW Connect button title

View File

@ -41,7 +41,8 @@ class ImportSeedPhraseScreen extends Component {
let seedPhraseError = null let seedPhraseError = null
if (seedPhrase) { if (seedPhrase) {
if (this.parseSeedPhrase(seedPhrase).split(' ').length !== 12) { const wordsCount = this.parseSeedPhrase(seedPhrase).split(' ').length
if (wordsCount !== 12 && wordsCount !== 24) {
seedPhraseError = this.context.t('seedPhraseReq') seedPhraseError = this.context.t('seedPhraseReq')
} else if (!validateMnemonic(seedPhrase)) { } else if (!validateMnemonic(seedPhrase)) {
seedPhraseError = this.context.t('invalidSeedPhrase') seedPhraseError = this.context.t('invalidSeedPhrase')

View File

@ -1,15 +1,83 @@
const inherits = require('util').inherits import { Component } from 'react'
const Component = require('react').Component import { connect } from 'react-redux'
const connect = require('react-redux').connect import PropTypes from 'prop-types'
const h = require('react-hyperscript') const h = require('react-hyperscript')
const actions = require('../../../../ui/app/actions') const { confirmSeedWords, showAccountDetail } = require('../../../../ui/app/actions')
const exportAsFile = require('../../util').exportAsFile const { exportAsFile } = require('../../util')
module.exports = connect(mapStateToProps)(CreateVaultCompleteScreen) class CreateVaultCompleteScreen extends Component {
inherits(CreateVaultCompleteScreen, Component) static propTypes = {
function CreateVaultCompleteScreen () { seed: PropTypes.string,
Component.call(this) cachedSeed: PropTypes.string,
confirmSeedWords: PropTypes.func,
showAccountDetail: PropTypes.func,
};
render () {
const state = this.props
const seed = state.seed || state.cachedSeed || ''
const wordsCount = seed.split(' ').length
return (
h('.initialize-screen.flex-column.flex-center.flex-grow', [
h('h3.flex-center.section-title', {
style: {
background: '#ffffff',
color: '#333333',
marginBottom: 8,
width: '100%',
padding: '30px 6px 6px 6px',
},
}, [
'Vault Created',
]),
h('div', {
style: {
fontSize: '1em',
margin: '10px 30px',
textAlign: 'center',
},
}, [
h('div.error', `These ${wordsCount} words are the only way to restore your Nifty Wallet accounts.\nSave them somewhere safe and secret.`),
]),
h('textarea.twelve-word-phrase', {
readOnly: true,
value: seed,
}),
h('button', {
onClick: () => this.confirmSeedWords()
.then(account => this.showAccountDetail(account)),
style: {
margin: '24px',
fontSize: '0.9em',
marginBottom: '10px',
},
}, 'I\'ve copied it somewhere safe'),
h('button', {
onClick: () => exportAsFile(`Nifty Wallet Seed Words`, seed),
style: {
margin: '10px',
fontSize: '0.9em',
},
}, 'Save Seed Words As File'),
])
)
}
confirmSeedWords () {
return this.props.confirmSeedWords()
}
showAccountDetail (account) {
return this.props.showAccountDetail(account)
}
} }
function mapStateToProps (state) { function mapStateToProps (state) {
@ -19,71 +87,11 @@ function mapStateToProps (state) {
} }
} }
CreateVaultCompleteScreen.prototype.render = function () { function mapDispatchToProps (dispatch) {
const state = this.props return {
const seed = state.seed || state.cachedSeed || '' confirmSeedWords: () => dispatch(confirmSeedWords()),
showAccountDetail: (account) => dispatch(showAccountDetail(account)),
return ( }
h('.initialize-screen.flex-column.flex-center.flex-grow', [
// // subtitle and nav
// h('.section-title.flex-row.flex-center', [
// h('h2.page-subtitle', 'Vault Created'),
// ]),
h('h3.flex-center.section-title', {
style: {
background: '#ffffff',
color: '#333333',
marginBottom: 8,
width: '100%',
padding: '30px 6px 6px 6px',
},
}, [
'Vault Created',
]),
h('div', {
style: {
fontSize: '1em',
margin: '10px 30px',
textAlign: 'center',
},
}, [
h('div.error', 'These 12 words are the only way to restore your Nifty Wallet accounts.\nSave them somewhere safe and secret.'),
]),
h('textarea.twelve-word-phrase', {
readOnly: true,
value: seed,
}),
h('button', {
onClick: () => this.confirmSeedWords()
.then(account => this.showAccountDetail(account)),
style: {
margin: '24px',
fontSize: '0.9em',
marginBottom: '10px',
},
}, 'I\'ve copied it somewhere safe'),
h('button', {
onClick: () => exportAsFile(`Nifty Wallet Seed Words`, seed),
style: {
margin: '10px',
fontSize: '0.9em',
},
}, 'Save Seed Words As File'),
])
)
} }
CreateVaultCompleteScreen.prototype.confirmSeedWords = function () { module.exports = connect(mapStateToProps, mapDispatchToProps)(CreateVaultCompleteScreen)
return this.props.dispatch(actions.confirmSeedWords())
}
CreateVaultCompleteScreen.prototype.showAccountDetail = function (account) {
return this.props.dispatch(actions.showAccountDetail(account))
}

View File

@ -179,8 +179,9 @@ RestoreVaultScreen.prototype.createNewVaultAndRestore = function () {
this.props.dispatch(actions.displayWarning(this.warning)) this.props.dispatch(actions.displayWarning(this.warning))
return return
} }
if (seed.split(' ').length !== 12) { const wordsCount = seed.split(' ').length
this.warning = 'seed phrases are 12 words long' if (wordsCount !== 12 && wordsCount !== 24) {
this.warning = 'seed phrases are 12 or 24 words long'
this.props.dispatch(actions.displayWarning(this.warning)) this.props.dispatch(actions.displayWarning(this.warning))
return return
} }

12
package-lock.json generated
View File

@ -25149,7 +25149,8 @@
"dependencies": { "dependencies": {
"mkdirp": { "mkdirp": {
"version": "0.5.1", "version": "0.5.1",
"resolved": "", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
"integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
"dev": true, "dev": true,
"requires": { "requires": {
"minimist": "0.0.8" "minimist": "0.0.8"
@ -26507,7 +26508,8 @@
}, },
"decompress": { "decompress": {
"version": "4.2.0", "version": "4.2.0",
"resolved": "", "resolved": "https://registry.npmjs.org/decompress/-/decompress-4.2.0.tgz",
"integrity": "sha1-eu3YVCflqS2s/lVnSnxQXpbQH50=",
"requires": { "requires": {
"decompress-tar": "^4.0.0", "decompress-tar": "^4.0.0",
"decompress-tarbz2": "^4.0.0", "decompress-tarbz2": "^4.0.0",
@ -35601,7 +35603,8 @@
"dependencies": { "dependencies": {
"minimist": { "minimist": {
"version": "1.2.0", "version": "1.2.0",
"resolved": "", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
"dev": true "dev": true
} }
} }
@ -35622,7 +35625,8 @@
"dependencies": { "dependencies": {
"mkdirp": { "mkdirp": {
"version": "0.5.1", "version": "0.5.1",
"resolved": "", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
"integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
"requires": { "requires": {
"minimist": "0.0.8" "minimist": "0.0.8"
} }

View File

@ -1,6 +1,7 @@
const assert = require('assert') const assert = require('assert')
const { screens, menus, NETWORKS } = require('../elements') const { screens, menus, NETWORKS } = require('../elements')
const testSeedPhrase = 'horn among position unable audit puzzle cannon apology gun autumn plug parrot' const testSeedPhrase = 'horn among position unable audit puzzle cannon apology gun autumn plug parrot'
const test24SeedPhrase = 'gravity trophy shrimp suspect sheriff avocado label trust dove tragic pitch title network myself spell task protect smooth sword diary brain blossom under bulb'
const importGanacheSeedPhrase = async (f, account2, password) => { const importGanacheSeedPhrase = async (f, account2, password) => {
it('logs out', async () => { it('logs out', async () => {
@ -12,6 +13,38 @@ const importGanacheSeedPhrase = async (f, account2, password) => {
await logOut.click() await logOut.click()
}) })
it('restores from 24 seed phrase', async () => {
const restoreSeedLink = await f.waitUntilShowUp(screens.lock.linkRestore)
assert.equal(await restoreSeedLink.getText(), screens.lock.linkRestoreText)
await restoreSeedLink.click()
})
it('adds 24 words seed phrase', async () => {
const seedTextArea = await f.waitUntilShowUp(screens.restoreVault.textArea)
await seedTextArea.sendKeys(test24SeedPhrase)
let field = await f.driver.findElement(screens.restoreVault.fieldPassword)
await field.sendKeys(password)
field = await f.driver.findElement(screens.restoreVault.fieldPasswordConfirm)
await field.sendKeys(password)
field = await f.waitUntilShowUp(screens.restoreVault.buttos.ok)
await f.click(field)
})
it('balance renders', async () => {
const balance = await f.waitUntilShowUp(screens.main.balance)
assert.equal(await balance.getText(), '0', "balance isn't correct")
})
it('logs out', async () => {
await f.setProvider(NETWORKS.LOCALHOST)
const menu = await f.waitUntilShowUp(menus.sandwich.menu)
await menu.click()
const logOut = await f.waitUntilShowUp(menus.sandwich.logOut)
assert.equal(await logOut.getText(), menus.sandwich.textLogOut)
await logOut.click()
})
it('restores from seed phrase', async () => { it('restores from seed phrase', async () => {
const restoreSeedLink = await f.waitUntilShowUp(screens.lock.linkRestore) const restoreSeedLink = await f.waitUntilShowUp(screens.lock.linkRestore)
assert.equal(await restoreSeedLink.getText(), screens.lock.linkRestoreText) assert.equal(await restoreSeedLink.getText(), screens.lock.linkRestoreText)