mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-11-22 18:00:18 +01:00
Merge branch 'master' into display_network
This commit is contained in:
commit
b2b4b4dd25
@ -2,10 +2,15 @@
|
||||
|
||||
## Current Master
|
||||
|
||||
- show network conection in title bar
|
||||
- show network status in title bar
|
||||
- Added seed word recovery to config screen.
|
||||
|
||||
## 2.2.0 2016-06-02
|
||||
|
||||
- Redesigned init, vault create, vault restore and seed confirmation screens.
|
||||
- Added pending transactions to transaction list on account screen.
|
||||
- Clicking a pending transaction takes you back to the transaction approval screen.
|
||||
- Update provider-engine to fix intermittent out of gas errors.
|
||||
|
||||
## 2.1.0 2016-05-26
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "__MSG_appName__",
|
||||
"short_name": "Metamask",
|
||||
"version": "2.1.0",
|
||||
"version": "2.2.0",
|
||||
"manifest_version": 2,
|
||||
"description": "__MSG_appDescription__",
|
||||
"icons": {
|
||||
|
@ -202,6 +202,8 @@ function setupControllerConnection(stream){
|
||||
exportAccount: idStore.exportAccount.bind(idStore),
|
||||
revealAccount: idStore.revealAccount.bind(idStore),
|
||||
saveAccountLabel: idStore.saveAccountLabel.bind(idStore),
|
||||
tryPassword: idStore.tryPassword.bind(idStore),
|
||||
recoverSeed: idStore.recoverSeed.bind(idStore),
|
||||
})
|
||||
stream.pipe(dnode).pipe(stream)
|
||||
dnode.on('remote', function(remote){
|
||||
|
@ -59,6 +59,13 @@ IdentityStore.prototype.createNewVault = function(password, entropy, cb){
|
||||
})
|
||||
}
|
||||
|
||||
IdentityStore.prototype.recoverSeed = function(cb){
|
||||
configManager.setShowSeedWords(true)
|
||||
if (!this._idmgmt) return cb(new Error('Unauthenticated. Please sign in.'))
|
||||
var seedWords = this._idmgmt.getSeed()
|
||||
cb(null, seedWords)
|
||||
}
|
||||
|
||||
IdentityStore.prototype.recoverFromSeed = function(password, seed, cb){
|
||||
this._createIdmgmt(password, seed, null, (err) => {
|
||||
if (err) return cb(err)
|
||||
@ -156,7 +163,7 @@ IdentityStore.prototype.setLocked = function(cb){
|
||||
}
|
||||
|
||||
IdentityStore.prototype.submitPassword = function(password, cb){
|
||||
this._tryPassword(password, (err) => {
|
||||
this.tryPassword(password, (err) => {
|
||||
if (err) return cb(err)
|
||||
// load identities before returning...
|
||||
this._loadIdentities()
|
||||
@ -372,7 +379,7 @@ IdentityStore.prototype._mayBeFauceting = function(i) {
|
||||
// keyStore managment - unlocking + deserialization
|
||||
//
|
||||
|
||||
IdentityStore.prototype._tryPassword = function(password, cb){
|
||||
IdentityStore.prototype.tryPassword = function(password, cb){
|
||||
this._createIdmgmt(password, null, null, cb)
|
||||
}
|
||||
|
||||
|
@ -8,24 +8,35 @@ module.exports = {
|
||||
createMsgNotification: createMsgNotification,
|
||||
}
|
||||
|
||||
// notification button press
|
||||
chrome.notifications.onButtonClicked.addListener(function(notificationId, buttonIndex){
|
||||
var handlers = notificationHandlers[notificationId]
|
||||
if (buttonIndex === 0) {
|
||||
handlers.confirm()
|
||||
} else {
|
||||
handlers.cancel()
|
||||
}
|
||||
chrome.notifications.clear(notificationId)
|
||||
})
|
||||
setupListeners()
|
||||
|
||||
// notification teardown
|
||||
chrome.notifications.onClosed.addListener(function(notificationId){
|
||||
delete notificationHandlers[notificationId]
|
||||
})
|
||||
function setupListeners(){
|
||||
|
||||
// guard for chrome bug https://github.com/MetaMask/metamask-plugin/issues/236
|
||||
if (!chrome.notifications) return console.error('Chrome notifications API missing...')
|
||||
|
||||
// notification button press
|
||||
chrome.notifications.onButtonClicked.addListener(function(notificationId, buttonIndex){
|
||||
var handlers = notificationHandlers[notificationId]
|
||||
if (buttonIndex === 0) {
|
||||
handlers.confirm()
|
||||
} else {
|
||||
handlers.cancel()
|
||||
}
|
||||
chrome.notifications.clear(notificationId)
|
||||
})
|
||||
|
||||
// notification teardown
|
||||
chrome.notifications.onClosed.addListener(function(notificationId){
|
||||
delete notificationHandlers[notificationId]
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
// creation helper
|
||||
function createUnlockRequestNotification(opts){
|
||||
// guard for chrome bug https://github.com/MetaMask/metamask-plugin/issues/236
|
||||
if (!chrome.notifications) return console.error('Chrome notifications API missing...')
|
||||
var message = 'An Ethereum app has requested a signature. Please unlock your account.'
|
||||
|
||||
var id = createId()
|
||||
@ -39,6 +50,8 @@ function createUnlockRequestNotification(opts){
|
||||
}
|
||||
|
||||
function createTxNotification(opts){
|
||||
// guard for chrome bug https://github.com/MetaMask/metamask-plugin/issues/236
|
||||
if (!chrome.notifications) return console.error('Chrome notifications API missing...')
|
||||
var message = [
|
||||
'Submitted by '+opts.txParams.origin,
|
||||
'to: '+uiUtils.addressSummary(opts.txParams.to),
|
||||
@ -67,6 +80,8 @@ function createTxNotification(opts){
|
||||
}
|
||||
|
||||
function createMsgNotification(opts){
|
||||
// guard for chrome bug https://github.com/MetaMask/metamask-plugin/issues/236
|
||||
if (!chrome.notifications) return console.error('Chrome notifications API missing...')
|
||||
var message = [
|
||||
'Submitted by '+opts.msgParams.origin,
|
||||
'to be signed by: '+uiUtils.addressSummary(opts.msgParams.from),
|
||||
|
@ -77,6 +77,13 @@ describe('util', function() {
|
||||
assert.ok(!result)
|
||||
})
|
||||
|
||||
it('should recognize this sample hashed address', function() {
|
||||
const address = '0x5Fda30Bb72B8Dfe20e48A00dFc108d0915BE9Bb0'
|
||||
const result = util.isValidAddress(address)
|
||||
const hashed = ethUtil.toChecksumAddress(address.toLowerCase())
|
||||
assert.equal(hashed, address, 'example is hashed correctly')
|
||||
assert.ok(result, 'is valid by our check')
|
||||
})
|
||||
})
|
||||
|
||||
describe('numericBalance', function() {
|
||||
|
@ -31,6 +31,10 @@ var actions = {
|
||||
createNewVaultInProgress: createNewVaultInProgress,
|
||||
showNewVaultSeed: showNewVaultSeed,
|
||||
showInfoPage: showInfoPage,
|
||||
// seed recovery actions
|
||||
REVEAL_SEED_CONFIRMATION: 'REVEAL_SEED_CONFIRMATION',
|
||||
revealSeedConfirmation: revealSeedConfirmation,
|
||||
requestRevealSeed: requestRevealSeed,
|
||||
// unlock screen
|
||||
UNLOCK_IN_PROGRESS: 'UNLOCK_IN_PROGRESS',
|
||||
UNLOCK_FAILED: 'UNLOCK_FAILED',
|
||||
@ -163,6 +167,26 @@ function createNewVault(password, entropy) {
|
||||
}
|
||||
}
|
||||
|
||||
function revealSeedConfirmation() {
|
||||
return {
|
||||
type: this.REVEAL_SEED_CONFIRMATION,
|
||||
}
|
||||
}
|
||||
|
||||
function requestRevealSeed(password) {
|
||||
return (dispatch) => {
|
||||
dispatch(actions.showLoadingIndication())
|
||||
_accountManager.tryPassword(password, (err, seed) => {
|
||||
dispatch(actions.hideLoadingIndication())
|
||||
if (err) return dispatch(actions.displayWarning(err.message))
|
||||
_accountManager.recoverSeed((err, seed) => {
|
||||
if (err) return dispatch(actions.displayWarning(err.message))
|
||||
dispatch(actions.showNewVaultSeed(seed))
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function recoverFromSeed(password, seed) {
|
||||
return (dispatch) => {
|
||||
// dispatch(actions.createNewVaultInProgress())
|
||||
@ -410,9 +434,10 @@ function previousTx() {
|
||||
}
|
||||
}
|
||||
|
||||
function showConfigPage() {
|
||||
function showConfigPage(transitionForward = true) {
|
||||
return {
|
||||
type: actions.SHOW_CONFIG_PAGE,
|
||||
value: transitionForward,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -21,6 +21,7 @@ const SendTransactionScreen = require('./send')
|
||||
const ConfirmTxScreen = require('./conf-tx')
|
||||
// other views
|
||||
const ConfigScreen = require('./config')
|
||||
const RevealSeedConfirmation = require('./recover-seed/confirmation')
|
||||
const InfoScreen = require('./info')
|
||||
const LoadingIndicator = require('./loading')
|
||||
const txHelper = require('../lib/tx-helper')
|
||||
@ -228,6 +229,9 @@ App.prototype.renderPrimary = function(){
|
||||
case 'config':
|
||||
return h(ConfigScreen, {key: 'config'})
|
||||
|
||||
case 'reveal-seed-conf':
|
||||
return h(RevealSeedConfirmation, {key: 'reveal-seed-conf'})
|
||||
|
||||
case 'info':
|
||||
return h(InfoScreen, {key: 'info'})
|
||||
|
||||
|
@ -78,7 +78,7 @@ ConfigScreen.prototype.render = function() {
|
||||
]),
|
||||
|
||||
h('div', [
|
||||
h('button', {
|
||||
h('button.spaced', {
|
||||
style: {
|
||||
alignSelf: 'center',
|
||||
},
|
||||
@ -86,11 +86,11 @@ ConfigScreen.prototype.render = function() {
|
||||
event.preventDefault()
|
||||
state.dispatch(actions.setProviderType('mainnet'))
|
||||
}
|
||||
}, 'Use Main Network')
|
||||
}, 'Use Main Network'),
|
||||
]),
|
||||
|
||||
h('div', [
|
||||
h('button', {
|
||||
h('button.spaced', {
|
||||
style: {
|
||||
alignSelf: 'center',
|
||||
},
|
||||
@ -98,11 +98,11 @@ ConfigScreen.prototype.render = function() {
|
||||
event.preventDefault()
|
||||
state.dispatch(actions.setProviderType('testnet'))
|
||||
}
|
||||
}, 'Use Morden Test Network')
|
||||
}, 'Use Morden Test Network'),
|
||||
]),
|
||||
|
||||
h('div', [
|
||||
h('button', {
|
||||
h('button.spaced', {
|
||||
style: {
|
||||
alignSelf: 'center',
|
||||
},
|
||||
@ -110,7 +110,25 @@ ConfigScreen.prototype.render = function() {
|
||||
event.preventDefault()
|
||||
state.dispatch(actions.setRpcTarget('http://localhost:8545/'))
|
||||
}
|
||||
}, 'Use http://localhost:8545')
|
||||
}, 'Use http://localhost:8545'),
|
||||
]),
|
||||
|
||||
h('hr.horizontal-line'),
|
||||
|
||||
h('div', {
|
||||
style: {
|
||||
marginTop: '20px',
|
||||
}
|
||||
}, [
|
||||
h('button', {
|
||||
style: {
|
||||
alignSelf: 'center',
|
||||
},
|
||||
onClick(event) {
|
||||
event.preventDefault()
|
||||
state.dispatch(actions.revealSeedConfirmation())
|
||||
}
|
||||
}, 'Reveal Seed Words')
|
||||
]),
|
||||
|
||||
]),
|
||||
|
@ -45,6 +45,10 @@ button {
|
||||
transition: transform 50ms ease-in;
|
||||
}
|
||||
|
||||
button.spaced {
|
||||
margin: 2px;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
transform: scale(1.1);
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ function CreateVaultCompleteScreen() {
|
||||
|
||||
function mapStateToProps(state) {
|
||||
return {
|
||||
seed: state.appState.currentView.context,
|
||||
seed: state.appState.currentView.seedWords,
|
||||
cachedSeed: state.metamask.seedWords,
|
||||
}
|
||||
}
|
||||
|
149
ui/app/recover-seed/confirmation.js
Normal file
149
ui/app/recover-seed/confirmation.js
Normal file
@ -0,0 +1,149 @@
|
||||
const inherits = require('util').inherits
|
||||
|
||||
const Component = require('react').Component
|
||||
const connect = require('react-redux').connect
|
||||
const h = require('react-hyperscript')
|
||||
const actions = require('../actions')
|
||||
|
||||
module.exports = connect(mapStateToProps)(RevealSeedConfirmatoin)
|
||||
|
||||
|
||||
inherits(RevealSeedConfirmatoin, Component)
|
||||
function RevealSeedConfirmatoin() {
|
||||
Component.call(this)
|
||||
}
|
||||
|
||||
function mapStateToProps(state) {
|
||||
return {
|
||||
warning: state.appState.warning,
|
||||
}
|
||||
}
|
||||
|
||||
RevealSeedConfirmatoin.prototype.confirmationPhrase = 'I understand'
|
||||
|
||||
RevealSeedConfirmatoin.prototype.render = function() {
|
||||
const props = this.props
|
||||
const state = this.state
|
||||
|
||||
return (
|
||||
|
||||
h('.initialize-screen.flex-column.flex-center.flex-grow', [
|
||||
|
||||
h('h3.flex-center.text-transform-uppercase', {
|
||||
style: {
|
||||
background: '#EBEBEB',
|
||||
color: '#AEAEAE',
|
||||
marginBottom: 24,
|
||||
width: '100%',
|
||||
fontSize: '20px',
|
||||
padding: 6,
|
||||
},
|
||||
}, [
|
||||
'Reveal Seed Words',
|
||||
]),
|
||||
|
||||
h('.div', {
|
||||
style: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
padding: '20px',
|
||||
justifyContent: 'center',
|
||||
}
|
||||
}, [
|
||||
|
||||
h('h4', 'Do not recover your seed words in a public place! These words can be used to steal all your accounts.'),
|
||||
|
||||
// confirmation
|
||||
h('input.large-input.letter-spacey', {
|
||||
type: 'password',
|
||||
id: 'password-box',
|
||||
placeholder: 'Enter your password to confirm',
|
||||
onKeyPress: this.checkConfirmation.bind(this),
|
||||
style: {
|
||||
width: 260,
|
||||
marginTop: '12px',
|
||||
},
|
||||
}),
|
||||
|
||||
h(`h4${state && state.confirmationWrong ? '.error' : ''}`, {
|
||||
style: {
|
||||
marginTop: '12px',
|
||||
}
|
||||
}, `Enter the phrase "I understand" to proceed.`),
|
||||
|
||||
// confirm confirmation
|
||||
h('input.large-input.letter-spacey', {
|
||||
type: 'text',
|
||||
id: 'confirm-box',
|
||||
placeholder: this.confirmationPhrase,
|
||||
onKeyPress: this.checkConfirmation.bind(this),
|
||||
style: {
|
||||
width: 260,
|
||||
marginTop: 16,
|
||||
},
|
||||
}),
|
||||
|
||||
h('.flex-row.flex-space-between', {
|
||||
style: {
|
||||
marginTop: 30,
|
||||
width: '50%',
|
||||
},
|
||||
}, [
|
||||
// cancel
|
||||
h('button.primary', {
|
||||
onClick: this.goHome.bind(this),
|
||||
}, 'CANCEL'),
|
||||
|
||||
// submit
|
||||
h('button.primary', {
|
||||
onClick: this.revealSeedWords.bind(this),
|
||||
}, 'OK'),
|
||||
|
||||
]),
|
||||
|
||||
(props.warning) && (
|
||||
h('span.error', {
|
||||
style: {
|
||||
margin: '20px',
|
||||
}
|
||||
}, props.warning.split('-'))
|
||||
),
|
||||
|
||||
props.inProgress && (
|
||||
h('span.in-progress-notification', 'Generating Seed...')
|
||||
),
|
||||
]),
|
||||
])
|
||||
)
|
||||
}
|
||||
|
||||
RevealSeedConfirmatoin.prototype.componentDidMount = function(){
|
||||
document.getElementById('password-box').focus()
|
||||
}
|
||||
|
||||
RevealSeedConfirmatoin.prototype.goHome = function() {
|
||||
this.props.dispatch(actions.showConfigPage(false))
|
||||
}
|
||||
|
||||
// create vault
|
||||
|
||||
RevealSeedConfirmatoin.prototype.checkConfirmation = function(event) {
|
||||
if (event.key === 'Enter') {
|
||||
event.preventDefault()
|
||||
this.revealSeedWords()
|
||||
}
|
||||
}
|
||||
|
||||
RevealSeedConfirmatoin.prototype.revealSeedWords = function(){
|
||||
this.setState({ confirmationWrong: false })
|
||||
|
||||
const confirmBox = document.getElementById('confirm-box')
|
||||
const confirmation = confirmBox.value
|
||||
if (confirmation !== this.confirmationPhrase) {
|
||||
confirmBox.value = ''
|
||||
return this.setState({ confirmationWrong: true })
|
||||
}
|
||||
|
||||
var password = document.getElementById('password-box').value
|
||||
this.props.dispatch(actions.requestRevealSeed(password))
|
||||
}
|
@ -25,10 +25,11 @@ function reduceApp(state, action) {
|
||||
}
|
||||
|
||||
// confirm seed words
|
||||
var seedWords = state.metamask.seedWords
|
||||
var seedConfView = {
|
||||
name: 'createVaultComplete',
|
||||
seedWords,
|
||||
}
|
||||
var seedWords = state.metamask.seedWords
|
||||
|
||||
var appState = extend({
|
||||
menuOpen: false,
|
||||
@ -85,7 +86,7 @@ function reduceApp(state, action) {
|
||||
name: 'config',
|
||||
context: appState.currentView.context,
|
||||
},
|
||||
transForward: true,
|
||||
transForward: action.value,
|
||||
})
|
||||
|
||||
case actions.SHOW_INFO_PAGE:
|
||||
@ -111,7 +112,7 @@ function reduceApp(state, action) {
|
||||
return extend(appState, {
|
||||
currentView: {
|
||||
name: 'createVaultComplete',
|
||||
context: action.value,
|
||||
seedWords: action.value,
|
||||
},
|
||||
transForward: true,
|
||||
isLoading: false,
|
||||
@ -144,6 +145,18 @@ function reduceApp(state, action) {
|
||||
warning: null,
|
||||
})
|
||||
|
||||
// reveal seed words
|
||||
|
||||
case actions.REVEAL_SEED_CONFIRMATION:
|
||||
return extend(appState, {
|
||||
currentView: {
|
||||
name: 'reveal-seed-conf',
|
||||
},
|
||||
transForward: true,
|
||||
warning: null,
|
||||
})
|
||||
|
||||
|
||||
// accounts
|
||||
|
||||
case actions.SET_SELECTED_ACCOUNT:
|
||||
@ -198,6 +211,7 @@ function reduceApp(state, action) {
|
||||
return extend(appState, {
|
||||
currentView: {
|
||||
name: seedWords ? 'createVaultComplete' : 'accounts',
|
||||
seedWords,
|
||||
},
|
||||
transForward: true,
|
||||
isLoading: false,
|
||||
|
@ -52,7 +52,7 @@ function addressSummary(address) {
|
||||
|
||||
function isValidAddress(address) {
|
||||
var prefixed = ethUtil.addHexPrefix(address)
|
||||
return isAllOneCase(prefixed) && ethUtil.isValidAddress(prefixed) || ethUtil.isValidChecksumAddress(prefixed)
|
||||
return (isAllOneCase(prefixed) && ethUtil.isValidAddress(prefixed)) || ethUtil.isValidChecksumAddress(prefixed)
|
||||
}
|
||||
|
||||
function isAllOneCase(address) {
|
||||
|
Loading…
Reference in New Issue
Block a user