From a10fe6b6f4217b3984f861bb8ad69d075f672872 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Thu, 22 Dec 2016 16:28:14 -0800 Subject: [PATCH 01/18] Return keyring metadata on metamask state object Required making the getState methods for both keyringController and metamaskController async. They both now return promises, and the main metamask-controller.getState method is now nodeified. Will allow the UI to render loose keys differently than persisted keys. --- app/scripts/keyring-controller.js | 59 +++++++++++++++++++++--------- app/scripts/metamask-controller.js | 23 +++++++----- 2 files changed, 54 insertions(+), 28 deletions(-) diff --git a/app/scripts/keyring-controller.js b/app/scripts/keyring-controller.js index 4e9193ab2..d53de1ab6 100644 --- a/app/scripts/keyring-controller.js +++ b/app/scripts/keyring-controller.js @@ -91,26 +91,32 @@ module.exports = class KeyringController extends EventEmitter { const address = configManager.getSelectedAccount() const wallet = configManager.getWallet() // old style vault const vault = configManager.getVault() // new style vault + const keyrings = this.keyrings - return { - seedWords: this.configManager.getSeedWords(), - isInitialized: (!!wallet || !!vault), - isUnlocked: Boolean(this.password), - isDisclaimerConfirmed: this.configManager.getConfirmedDisclaimer(), // AUDIT this.configManager.getConfirmedDisclaimer(), - unconfTxs: this.configManager.unconfirmedTxs(), - transactions: this.configManager.getTxList(), - unconfMsgs: messageManager.unconfirmedMsgs(), - messages: messageManager.getMsgList(), - selectedAccount: address, - shapeShiftTxList: this.configManager.getShapeShiftTxList(), - currentFiat: this.configManager.getCurrentFiat(), - conversionRate: this.configManager.getConversionRate(), - conversionDate: this.configManager.getConversionDate(), - keyringTypes: this.keyringTypes.map(krt => krt.type), - identities: this.identities, - } + return Promise.all(keyrings.map(this.displayForKeyring)) + .then((displayKeyrings) => { + return { + seedWords: this.configManager.getSeedWords(), + isInitialized: (!!wallet || !!vault), + isUnlocked: Boolean(this.password), + isDisclaimerConfirmed: this.configManager.getConfirmedDisclaimer(), + unconfTxs: this.configManager.unconfirmedTxs(), + transactions: this.configManager.getTxList(), + unconfMsgs: messageManager.unconfirmedMsgs(), + messages: messageManager.getMsgList(), + selectedAccount: address, + shapeShiftTxList: this.configManager.getShapeShiftTxList(), + currentFiat: this.configManager.getCurrentFiat(), + conversionRate: this.configManager.getConversionRate(), + conversionDate: this.configManager.getConversionDate(), + keyringTypes: this.keyringTypes.map(krt => krt.type), + identities: this.identities, + keyrings: displayKeyrings, + } + }) } + // Create New Vault And Keychain // @string password - The password to encrypt the vault with // @@ -693,7 +699,7 @@ module.exports = class KeyringController extends EventEmitter { if (typeof password === 'string') { this.password = password } - return Promise.all(this.keyrings.map((keyring) => { + return Promise.all(this.keyrings.map((keyring, i) => { return Promise.all([keyring.type, keyring.serialize()]) .then((serializedKeyringArray) => { // Label the output values on each serialized Keyring: @@ -744,6 +750,7 @@ module.exports = class KeyringController extends EventEmitter { // On success, returns the resulting @Keyring instance. restoreKeyring (serialized) { const { type, data } = serialized + const Keyring = this.getKeyringClassForType(type) const keyring = new Keyring() return keyring.deserialize(data) @@ -816,6 +823,22 @@ module.exports = class KeyringController extends EventEmitter { }) } + // Display For Keyring + // @Keyring keyring + // + // returns Promise( @Object { type:String, accounts:Array } ) + // + // Is used for adding the current keyrings to the state object. + displayForKeyring (keyring) { + return keyring.getAccounts() + .then((accounts) => { + return { + type: keyring.type, + accounts: accounts, + } + }) + } + // Add Gas Buffer // @string gas (as hexadecimal value) // diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 983a590d7..78e5a6f0a 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -53,15 +53,18 @@ module.exports = class MetamaskController { } getState () { - return extend( - this.state, - this.ethStore.getState(), - this.configManager.getConfig(), - this.keyringController.getState(), - this.noticeController.getState(), { - lostAccounts: this.configManager.getLostAccounts(), - } - ) + return this.keyringController.getState() + .then((keyringControllerState) => { + return extend( + this.state, + this.ethStore.getState(), + this.configManager.getConfig(), + keyringControllerState, + this.noticeController.getState(), { + lostAccounts: this.configManager.getLostAccounts(), + } + ) + }) } getApi () { @@ -69,7 +72,7 @@ module.exports = class MetamaskController { const noticeController = this.noticeController return { - getState: (cb) => { cb(null, this.getState()) }, + getState: nodeify(this.getState.bind(this)), setRpcTarget: this.setRpcTarget.bind(this), setProviderType: this.setProviderType.bind(this), useEtherscanProvider: this.useEtherscanProvider.bind(this), From 1f1549904650af2e5502d2d4781a555386dfd084 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Thu, 22 Dec 2016 17:17:20 -0800 Subject: [PATCH 02/18] Show a "LOOSE" warning on accounts not belonging to HD Seed phrase --- ui/app/accounts/account-list-item.js | 18 ++++++++++++++---- ui/app/accounts/index.js | 14 +++++++++++--- ui/app/css/lib.css | 17 +++++++++++++++++ 3 files changed, 42 insertions(+), 7 deletions(-) diff --git a/ui/app/accounts/account-list-item.js b/ui/app/accounts/account-list-item.js index ef7b749e4..624e34581 100644 --- a/ui/app/accounts/account-list-item.js +++ b/ui/app/accounts/account-list-item.js @@ -15,19 +15,21 @@ function AccountListItem () { } AccountListItem.prototype.render = function () { - const identity = this.props.identity - var isSelected = this.props.selectedAccount === identity.address - var account = this.props.accounts[identity.address] + const { identity, selectedAccount, accounts, onShowDetail } = this.props + + const isSelected = selectedAccount === identity.address + const account = accounts[identity.address] const selectedClass = isSelected ? '.selected' : '' return ( h(`.accounts-list-option.flex-row.flex-space-between.pointer.hover-white${selectedClass}`, { key: `account-panel-${identity.address}`, - onClick: (event) => this.props.onShowDetail(identity.address, event), + onClick: (event) => onShowDetail(identity.address, event), }, [ h('.identicon-wrapper.flex-column.flex-center.select-none', [ this.pendingOrNot(), + this.indicateIfLoose(), h(Identicon, { address: identity.address, imageify: true, @@ -70,6 +72,14 @@ AccountListItem.prototype.render = function () { ) } +AccountListItem.prototype.indicateIfLoose = function () { + try { // Sometimes keyrings aren't loaded yet: + const type = this.props.keyring.type + const isLoose = type !== 'HD Key Tree' + return isLoose ? h('.pending-dot', 'LOOSE') : null + } catch (e) { return } +} + AccountListItem.prototype.pendingOrNot = function () { const pending = this.props.pending if (pending.length === 0) return null diff --git a/ui/app/accounts/index.js b/ui/app/accounts/index.js index fcb3a7b0f..edb15eafe 100644 --- a/ui/app/accounts/index.js +++ b/ui/app/accounts/index.js @@ -22,6 +22,7 @@ function mapStateToProps (state) { selectedAccount: state.metamask.selectedAccount, scrollToBottom: state.appState.scrollToBottom, pending, + keyrings: state.metamask.keyrings, } } @@ -31,9 +32,10 @@ function AccountsScreen () { } AccountsScreen.prototype.render = function () { - var state = this.props - var identityList = valuesFor(state.identities) - var unconfTxList = valuesFor(state.unconfTxs) + const props = this.props + const { keyrings } = props + const identityList = valuesFor(props.identities) + const unconfTxList = valuesFor(props.unconfTxs) return ( @@ -69,6 +71,11 @@ AccountsScreen.prototype.render = function () { } }) + const simpleAddress = identity.address.substring(2).toLowerCase() + const keyring = keyrings.find((kr) => { + return kr.accounts.includes(simpleAddress) + }) + return h(AccountListItem, { key: `acct-panel-${identity.address}`, identity, @@ -76,6 +83,7 @@ AccountsScreen.prototype.render = function () { accounts: this.props.accounts, onShowDetail: this.onShowDetail.bind(this), pending, + keyring, }) }), diff --git a/ui/app/css/lib.css b/ui/app/css/lib.css index f5f602729..abbf8667e 100644 --- a/ui/app/css/lib.css +++ b/ui/app/css/lib.css @@ -196,6 +196,23 @@ hr.horizontal-line { align-items: center; justify-content: center; padding: 4px; + z-index: 1; +} + +.keyring-label { + z-index: 1; + font-size: 11px; + background: rgba(255,0,0,0.8); + bottom: -47px; + color: white; + border-radius: 10px; + height: 20px; + min-width: 20px; + position: relative; + display: flex; + align-items: center; + justify-content: center; + padding: 4px; } .ether-balance { From e95c93756968284fbe0c968fd79b9d05498d280f Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Fri, 23 Dec 2016 17:09:24 -0800 Subject: [PATCH 03/18] Add additional migration test --- app/scripts/metamask-controller.js | 4 ++-- test/integration/lib/idStore-migrator-test.js | 21 +++++++++++++++++-- test/integration/mocks/badVault2.json | 1 + 3 files changed, 22 insertions(+), 4 deletions(-) create mode 100644 test/integration/mocks/badVault2.json diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 983a590d7..9d2f1494f 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -89,7 +89,7 @@ module.exports = class MetamaskController { setLocked: nodeify(keyringController.setLocked).bind(keyringController), submitPassword: (password, cb) => { this.migrateOldVaultIfAny(password) - .then(keyringController.submitPassword.bind(keyringController)) + .then(keyringController.submitPassword.bind(keyringController, password)) .then((newState) => { cb(null, newState) }) .catch((reason) => { cb(reason) }) }, @@ -452,7 +452,7 @@ module.exports = class MetamaskController { return this.idStoreMigrator.migratedVaultForPassword(password) .then(this.restoreOldVaultAccounts.bind(this)) .then(this.restoreOldLostAccounts.bind(this)) - .then(keyringController.persistAllKeyrings.bind(keyringController)) + .then(keyringController.persistAllKeyrings.bind(keyringController, password)) .then(() => password) } diff --git a/test/integration/lib/idStore-migrator-test.js b/test/integration/lib/idStore-migrator-test.js index 338896171..4ae30411d 100644 --- a/test/integration/lib/idStore-migrator-test.js +++ b/test/integration/lib/idStore-migrator-test.js @@ -1,6 +1,7 @@ -var KeyringController = require('../../../app/scripts/keyring-controller') var ConfigManager = require('../../../app/scripts/lib/config-manager') var IdStoreMigrator = require('../../../app/scripts/lib/idStore-migrator') +var SimpleKeyring = require('../../../app/scripts/keyrings/simple') +var normalize = require('../../../app/scripts/lib/sig-util').normalize var oldStyleVault = require('../mocks/oldVault.json') var badStyleVault = require('../mocks/badVault.json') @@ -68,7 +69,23 @@ QUnit.test('migrator:migratedVaultForPassword', function (assert) { assert.equal(lostAccounts.length, 1, 'one lost account') assert.equal(lostAccounts[0].address, '0xe15D894BeCB0354c501AE69429B05143679F39e0'.toLowerCase()) assert.ok(lostAccounts[0].privateKey, 'private key exported') - done() + + var lostAccount = lostAccounts[0] + var privateKey = lostAccount.privateKey + + var simple = new SimpleKeyring() + simple.deserialize([privateKey]) + .then(() => { + return simple.getAccounts() + }) + .then((accounts) => { + assert.equal(normalize(accounts[0]), lostAccount.address, 'recovered address.') + done() + }) + .catch((reason) => { + assert.ifError(reason) + done(reason) + }) }) }) diff --git a/test/integration/mocks/badVault2.json b/test/integration/mocks/badVault2.json new file mode 100644 index 000000000..4b7aec386 --- /dev/null +++ b/test/integration/mocks/badVault2.json @@ -0,0 +1 @@ +{"meta":{"version":4},"data":{"fiatCurrency":"USD","isConfirmed":true,"TOSHash":"a4f4e23f823a7ac51783e7ffba7914a911b09acdb97263296b7e14b527f80c5b","noticesList":[{"read":true,"date":"Fri Dec 16 2016","title":"Ending Morden Support","body":"Due to [recent events](https://blog.ethereum.org/2016/11/20/from-morden-to-ropsten/), MetaMask is now deprecating support for the Morden Test Network.\n\nUsers will still be able to access Morden through a locally hosted node, but we will no longer be providing hosted access to this network through [Infura](http://infura.io/).\n\nPlease use the new Ropsten Network as your new default test network.\n\nYou can fund your Ropsten account using the buy button on your account page.\n\nBest wishes!\nThe MetaMask Team\n\n","id":0}],"conversionRate":7.07341909,"conversionDate":1482539284,"wallet":"{\"encSeed\":{\"encStr\":\"LZsdN8lJzYkUe1UpmAalnERdgkBFt25gWDdK8kfQUwMAk/27XR+dc+8n5swgoF5qgwhc9LBgliEGNDs1Q/lnuld3aQLabkOeAW4BHS1vS7FxqKrzDS3iyzSuQO6wDQmGno/buuknVgDsKiyjW22hpt7vtVVWA+ZL1P3x6M0+AxGJjeGVrG+E8Q==\",\"nonce\":\"T6O9BmwmTj214XUK3KF0s3iCKo3OlrUD\"},\"ksData\":{\"m/44'/60'/0'/0\":{\"info\":{\"curve\":\"secp256k1\",\"purpose\":\"sign\"},\"encHdPathPriv\":{\"encStr\":\"GNNfZevCMlgMVh9y21y1UwrC9qcmH6XYq7v+9UoqbHnzPQJFlxidN5+x/Sldo72a6+5zJpQkkdZ+Q0lePrzvXfuSd3D/RO7WKFIKo9nAQI5+JWwz4INuCmVcmqCv2J4BTLGjrG8fp5pDJ62Bn0XHqkJo3gx3fpvs3cS66+ZKwg==\",\"nonce\":\"HRTlGj44khQs2veYHEF/GqTI1t0yYvyd\"},\"hdIndex\":3,\"encPrivKeys\":{\"e15d894becb0354c501ae69429b05143679f39e0\":{\"key\":\"ZAeZL9VcRUtiiO4VXOQKBFg787PR5R3iymjUeU5vpDRIqOXbjWN6N4ZNR8YpSXl+\",\"nonce\":\"xLsADagS8uqDYae6cImyhxF7o1kBDbPe\"},\"87658c15aefe7448008a28513a11b6b130ef4cd0\":{\"key\":\"ku0mm5s1agRJNAMYIJO0qeoDe+FqcbqdQI6azXF3GL1OLo6uMlt6I4qS+eeravFi\",\"nonce\":\"xdGfSUPKtkW8ge0SWIbbpahs/NyEMzn5\"},\"aa25854c0379e53c957ac9382e720c577fa31fd5\":{\"key\":\"NjpYC9FbiC95CTx/1kwgOHk5LSN9vl4RULEBbvwfVOjqSH8WixNoP3R6I/QyNIs2\",\"nonce\":\"M/HWpXXA9QvuZxEykkGQPJKKdz33ovQr\"}},\"addresses\":[\"e15d894becb0354c501ae69429b05143679f39e0\",\"87658c15aefe7448008a28513a11b6b130ef4cd0\",\"aa25854c0379e53c957ac9382e720c577fa31fd5\"]}},\"encHdRootPriv\":{\"encStr\":\"f+3prUOzl+95aNAV+ad6lZdsYZz120ZsL67ucjj3tiMXf/CC4X8XB9N2QguhoMy6fW+fATUsTdJe8+CbAAyb79V9HY0Pitzq9Yw/g1g0/Ii2JzsdGBriuMsPdwZSVqz+rvQFw/6Qms1xjW6cqa8S7kM2WA5l8RB1Ck6r5zaqbA==\",\"nonce\":\"oGahxNFekVxH9sg6PUCCHIByvo4WFSqm\"},\"salt\":\"N7xYoEA53yhSweOsEphku1UKkIEuZtX2MwLBhVM6RR8=\",\"version\":2}","config":{"provider":{"type":"testnet"},"selectedAccount":"0xe15d894becb0354c501ae69429b05143679f39e0"},"isDisclaimerConfirmed":true,"walletNicknames":{"0xac39b311dceb2a4b2f5d8461c1cdaf756f4f7ae9":"Account 1","0xd7c0cd9e7d2701c710d64fc492c7086679bdf7b4":"Account 2","0x1acfb961c5a8268eac8e09d6241a26cbeff42241":"Account 3"},"lostAccounts":["0xe15d894becb0354c501ae69429b05143679f39e0","0x87658c15aefe7448008a28513a11b6b130ef4cd0","0xaa25854c0379e53c957ac9382e720c577fa31fd5"]}} \ No newline at end of file From f6748f043d6356258600dd57918c57b6c10378e1 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Fri, 23 Dec 2016 17:22:36 -0800 Subject: [PATCH 04/18] Add loose account development state --- development/states/accounts-loose.json | 126 +++++++++++++++++++++++++ 1 file changed, 126 insertions(+) create mode 100644 development/states/accounts-loose.json diff --git a/development/states/accounts-loose.json b/development/states/accounts-loose.json new file mode 100644 index 000000000..fd0c93c9c --- /dev/null +++ b/development/states/accounts-loose.json @@ -0,0 +1,126 @@ +{ + "metamask": { + "isInitialized": true, + "isUnlocked": true, + "rpcTarget": "https://rawtestrpc.metamask.io/", + "identities": { + "0xac39b311dceb2a4b2f5d8461c1cdaf756f4f7ae9": { + "address": "0xac39b311dceb2a4b2f5d8461c1cdaf756f4f7ae9", + "name": "Account 1" + }, + "0xd7c0cd9e7d2701c710d64fc492c7086679bdf7b4": { + "address": "0xd7c0cd9e7d2701c710d64fc492c7086679bdf7b4", + "name": "Account 2" + }, + "0x1acfb961c5a8268eac8e09d6241a26cbeff42241": { + "address": "0x1acfb961c5a8268eac8e09d6241a26cbeff42241", + "name": "Account 3" + }, + "0xe15d894becb0354c501ae69429b05143679f39e0": { + "address": "0xe15d894becb0354c501ae69429b05143679f39e0", + "name": "Account 4" + }, + "0x87658c15aefe7448008a28513a11b6b130ef4cd0": { + "address": "0x87658c15aefe7448008a28513a11b6b130ef4cd0", + "name": "Account 5" + }, + "0xaa25854c0379e53c957ac9382e720c577fa31fd5": { + "address": "0xaa25854c0379e53c957ac9382e720c577fa31fd5", + "name": "Account 6" + } + }, + "unconfTxs": {}, + "currentFiat": "USD", + "conversionRate": 0, + "conversionDate": "N/A", + "noActiveNotices": true, + "network": "3", + "accounts": { + "0xac39b311dceb2a4b2f5d8461c1cdaf756f4f7ae9": { + "code": "0x", + "balance": "0x11f646fe14c9c000", + "nonce": "0x3", + "address": "0xac39b311dceb2a4b2f5d8461c1cdaf756f4f7ae9" + }, + "0xd7c0cd9e7d2701c710d64fc492c7086679bdf7b4": { + "code": "0x", + "balance": "0x0", + "nonce": "0x0", + "address": "0xd7c0cd9e7d2701c710d64fc492c7086679bdf7b4" + }, + "0x1acfb961c5a8268eac8e09d6241a26cbeff42241": { + "code": "0x", + "balance": "0x0", + "nonce": "0x0", + "address": "0x1acfb961c5a8268eac8e09d6241a26cbeff42241" + }, + "0xe15d894becb0354c501ae69429b05143679f39e0": { + "code": "0x", + "balance": "0x0", + "nonce": "0x0", + "address": "0xe15d894becb0354c501ae69429b05143679f39e0" + }, + "0x87658c15aefe7448008a28513a11b6b130ef4cd0": { + "code": "0x", + "balance": "0x0", + "nonce": "0x0", + "address": "0x87658c15aefe7448008a28513a11b6b130ef4cd0" + }, + "0xaa25854c0379e53c957ac9382e720c577fa31fd5": { + "code": "0x", + "balance": "0x0", + "nonce": "0x0", + "address": "0xaa25854c0379e53c957ac9382e720c577fa31fd5" + } + }, + "transactions": [], + "provider": { + "type": "testnet" + }, + "selectedAccount": "0x87658c15aefe7448008a28513a11b6b130ef4cd0", + "isDisclaimerConfirmed": true, + "unconfMsgs": {}, + "messages": [], + "shapeShiftTxList": [], + "keyringTypes": [ + "Simple Key Pair", + "HD Key Tree" + ], + "keyrings": [ + { + "type": "HD Key Tree", + "accounts": [ + "ac39b311dceb2a4b2f5d8461c1cdaf756f4f7ae9", + "d7c0cd9e7d2701c710d64fc492c7086679bdf7b4", + "1acfb961c5a8268eac8e09d6241a26cbeff42241" + ] + }, + { + "type": "Simple Key Pair", + "accounts": [ + "e15d894becb0354c501ae69429b05143679f39e0", + "87658c15aefe7448008a28513a11b6b130ef4cd0", + "aa25854c0379e53c957ac9382e720c577fa31fd5" + ] + } + ], + "lostAccounts": [] + }, + "appState": { + "menuOpen": false, + "currentView": { + "name": "accounts" + }, + "accountDetail": { + "subview": "transactions", + "accountExport": "none", + "privateKey": "" + }, + "transForward": true, + "isLoading": false, + "warning": null, + "scrollToBottom": false, + "forgottenPassword": false + }, + "identities": {} +} \ No newline at end of file From c05e04c611f44ff8bef2f1d617cb79d2a5157f2a Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Fri, 23 Dec 2016 17:22:46 -0800 Subject: [PATCH 05/18] Fix rendering of loose accounts --- ui/app/accounts/account-list-item.js | 4 ++-- ui/app/components/eth-balance.js | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/ui/app/accounts/account-list-item.js b/ui/app/accounts/account-list-item.js index 624e34581..16019c88a 100644 --- a/ui/app/accounts/account-list-item.js +++ b/ui/app/accounts/account-list-item.js @@ -50,7 +50,7 @@ AccountListItem.prototype.render = function () { }, }, ethUtil.toChecksumAddress(identity.address)), h(EthBalance, { - value: account.balance, + value: account && account.balance, style: { lineHeight: '7px', marginTop: '10px', @@ -76,7 +76,7 @@ AccountListItem.prototype.indicateIfLoose = function () { try { // Sometimes keyrings aren't loaded yet: const type = this.props.keyring.type const isLoose = type !== 'HD Key Tree' - return isLoose ? h('.pending-dot', 'LOOSE') : null + return isLoose ? h('.keyring-label', 'LOOSE') : null } catch (e) { return } } diff --git a/ui/app/components/eth-balance.js b/ui/app/components/eth-balance.js index 46127bed5..57ca84564 100644 --- a/ui/app/components/eth-balance.js +++ b/ui/app/components/eth-balance.js @@ -15,9 +15,10 @@ function EthBalanceComponent () { EthBalanceComponent.prototype.render = function () { var props = this.props + let { value } = props var style = props.style var needsParse = this.props.needsParse !== undefined ? this.props.needsParse : true - const value = formatBalance(props.value, 6, needsParse) + value = value ? formatBalance(value, 6, needsParse) : '...' var width = props.width return ( @@ -38,6 +39,7 @@ EthBalanceComponent.prototype.render = function () { EthBalanceComponent.prototype.renderBalance = function (value) { var props = this.props if (value === 'None') return value + if (value === '...') return value var balanceObj = generateBalanceObject(value, props.shorten ? 1 : 3) var balance var splitBalance = value.split(' ') From 5e8a344f973fabb331db9b491247396117aa67b1 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Fri, 23 Dec 2016 17:31:24 -0800 Subject: [PATCH 06/18] Correct getState test to be async --- test/unit/idStore-migration-test.js | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/test/unit/idStore-migration-test.js b/test/unit/idStore-migration-test.js index 8532ac914..ad4ce2096 100644 --- a/test/unit/idStore-migration-test.js +++ b/test/unit/idStore-migration-test.js @@ -79,11 +79,14 @@ describe('IdentityStore to KeyringController migration', function() { }) describe('entering a password', function() { - it('should identify an old wallet as an initialized keyring', function() { + it('should identify an old wallet as an initialized keyring', function(done) { keyringController.configManager.setWallet('something') - const state = keyringController.getState() - assert(state.isInitialized, 'old vault counted as initialized.') - assert(!state.lostAccounts, 'no lost accounts') + keyringController.getState() + .then((state) => { + assert(state.isInitialized, 'old vault counted as initialized.') + assert(!state.lostAccounts, 'no lost accounts') + done() + }) }) }) }) From edc5f9e821bf18fa6ec984e645790fba3457d0bb Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Mon, 2 Jan 2017 13:55:43 -0800 Subject: [PATCH 07/18] Add tolerance for hex prefixed private keys to simple keychain --- app/scripts/keyrings/simple.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/app/scripts/keyrings/simple.js b/app/scripts/keyrings/simple.js index 8f339cf80..6b4f9e710 100644 --- a/app/scripts/keyrings/simple.js +++ b/app/scripts/keyrings/simple.js @@ -19,9 +19,10 @@ class SimpleKeyring extends EventEmitter { return Promise.resolve(this.wallets.map(w => w.getPrivateKey().toString('hex'))) } - deserialize (wallets = []) { - this.wallets = wallets.map((w) => { - var b = new Buffer(w, 'hex') + deserialize (privateKeys = []) { + this.wallets = privateKeys.map((w) => { + const stripped = ethUtil.stripHexPrefix(w) + const b = new Buffer(stripped, 'hex') const wallet = Wallet.fromPrivateKey(b) return wallet }) From af2c7004b05ad985b9ec8fc16b8bbec1765bf062 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Mon, 2 Jan 2017 15:08:18 -0800 Subject: [PATCH 08/18] Make single letter variables more verbose --- app/scripts/keyring-controller.js | 2 +- app/scripts/keyrings/simple.js | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/scripts/keyring-controller.js b/app/scripts/keyring-controller.js index 05c4a26fa..92429f7f5 100644 --- a/app/scripts/keyring-controller.js +++ b/app/scripts/keyring-controller.js @@ -508,7 +508,7 @@ module.exports = class KeyringController extends EventEmitter { if (typeof password === 'string') { this.password = password } - return Promise.all(this.keyrings.map((keyring, i) => { + return Promise.all(this.keyrings.map((keyring) => { return Promise.all([keyring.type, keyring.serialize()]) .then((serializedKeyringArray) => { // Label the output values on each serialized Keyring: diff --git a/app/scripts/keyrings/simple.js b/app/scripts/keyrings/simple.js index 6b4f9e710..9717f1c45 100644 --- a/app/scripts/keyrings/simple.js +++ b/app/scripts/keyrings/simple.js @@ -20,10 +20,10 @@ class SimpleKeyring extends EventEmitter { } deserialize (privateKeys = []) { - this.wallets = privateKeys.map((w) => { - const stripped = ethUtil.stripHexPrefix(w) - const b = new Buffer(stripped, 'hex') - const wallet = Wallet.fromPrivateKey(b) + this.wallets = privateKeys.map((privateKey) => { + const stripped = ethUtil.stripHexPrefix(privateKey) + const buffer = new Buffer(stripped, 'hex') + const wallet = Wallet.fromPrivateKey(buffer) return wallet }) return Promise.resolve() From 3ebf029c04b15599f571a19524474df9680efa7d Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Tue, 3 Jan 2017 10:39:34 -0800 Subject: [PATCH 09/18] Update account list after adding account Fixed by finally making a function generator for a pattern we use frequently, communicating to the background process. Fixes #961 --- ui/app/actions.js | 34 ++++++++++++++++++++++++---------- 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/ui/app/actions.js b/ui/app/actions.js index 606460314..018baff73 100644 --- a/ui/app/actions.js +++ b/ui/app/actions.js @@ -153,7 +153,7 @@ var actions = { SHOW_NEW_KEYCHAIN: 'SHOW_NEW_KEYCHAIN', showNewKeychain: showNewKeychain, - + callBackgroundThenUpdate, } module.exports = actions @@ -269,15 +269,7 @@ function addNewKeyring (type, opts) { } function addNewAccount (ringNumber = 0) { - return (dispatch) => { - dispatch(actions.showLoadingIndication()) - background.addNewAccount(ringNumber, (err) => { - dispatch(this.hideLoadingIndication()) - if (err) { - return dispatch(actions.showWarning(err)) - } - }) - } + return callBackgroundThenUpdate(background.addNewAccount, ringNumber) } function showInfoPage () { @@ -476,6 +468,7 @@ function updateMetamaskState (newState) { function lockMetamask () { return (dispatch) => { + dispatch(actions.showLoadingIndication()) background.setLocked((err, newState) => { dispatch(actions.hideLoadingIndication()) if (err) { @@ -857,3 +850,24 @@ function shapeShiftRequest (query, options, cb) { return shapShiftReq.send() } } + +// Call Background Then Update +// +// A function generator for a common pattern wherein: +// We show loading indication. +// We call a background method. +// We hide loading indication. +// If it errored, we show a warning. +// If it didn't, we update the state. +function callBackgroundThenUpdate (method, ...args) { + return (dispatch) => { + dispatch(actions.showLoadingIndication()) + method.call(background, ...args, (err, newState) => { + dispatch(actions.hideLoadingIndication()) + if (err) { + return dispatch(actions.displayWarning(err.message)) + } + dispatch(actions.updateMetamaskState(newState)) + }) + } +} From 8b7b097034f274631917b2df8617c24feb09f69c Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Tue, 3 Jan 2017 10:42:09 -0800 Subject: [PATCH 10/18] Apply new pattern to repetitive functions --- ui/app/actions.js | 21 ++------------------- 1 file changed, 2 insertions(+), 19 deletions(-) diff --git a/ui/app/actions.js b/ui/app/actions.js index 018baff73..bc9b4a092 100644 --- a/ui/app/actions.js +++ b/ui/app/actions.js @@ -231,7 +231,6 @@ function createNewVaultAndKeychain (password) { if (err) { return dispatch(actions.showWarning(err.message)) } - dispatch(actions.updateMetamaskState(newState)) }) } } @@ -467,16 +466,7 @@ function updateMetamaskState (newState) { } function lockMetamask () { - return (dispatch) => { - dispatch(actions.showLoadingIndication()) - background.setLocked((err, newState) => { - dispatch(actions.hideLoadingIndication()) - if (err) { - return dispatch(actions.displayWarning(err.message)) - } - dispatch(actions.updateMetamaskState(newState)) - }) - } + return callBackgroundThenUpdate(background.setLocked) } function showAccountDetail (address) { @@ -586,14 +576,7 @@ function clearNotices () { } function markAccountsFound() { - return (dispatch) => { - dispatch(this.showLoadingIndication()) - background.markAccountsFound((err, newState) => { - dispatch(this.hideLoadingIndication()) - if (err) return dispatch(this.showWarning(err.message)) - dispatch(actions.updateMetamaskState(newState)) - }) - } + return callBackgroundThenUpdate(background.markAccountsFound) } // From 013e6a608f55c7a81e17a171d681cb21e041c9d9 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Tue, 3 Jan 2017 11:03:25 -0800 Subject: [PATCH 11/18] Corrected instances of showWarning to displayWarning --- ui/app/actions.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ui/app/actions.js b/ui/app/actions.js index bc9b4a092..96d76a50a 100644 --- a/ui/app/actions.js +++ b/ui/app/actions.js @@ -229,7 +229,7 @@ function createNewVaultAndKeychain (password) { return (dispatch) => { background.createNewVaultAndKeychain(password, (err, newState) => { if (err) { - return dispatch(actions.showWarning(err.message)) + return dispatch(actions.displayWarning(err.message)) } }) } @@ -261,7 +261,7 @@ function addNewKeyring (type, opts) { background.addNewKeyring(type, opts, (err) => { dispatch(this.hideLoadingIndication()) if (err) { - return dispatch(actions.showWarning(err)) + return dispatch(actions.displayWarning(err)) } }) } @@ -548,7 +548,7 @@ function markNoticeRead (notice) { background.markNoticeRead(notice, (err, notice) => { dispatch(this.hideLoadingIndication()) if (err) { - return dispatch(actions.showWarning(err)) + return dispatch(actions.displayWarning(err)) } if (notice) { return dispatch(actions.showNotice(notice)) From 1b9906372b5e36eb7467c7186e866dba237c8b2f Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Tue, 3 Jan 2017 11:10:20 -0800 Subject: [PATCH 12/18] Update UI state on ethStore updates UI was remarkably not relying on ethStore for updates, so things like account balances were frozen until user activity. Fixes #963 --- app/scripts/metamask-controller.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 3e27272b9..91def2569 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -60,6 +60,8 @@ module.exports = class MetamaskController { this.idStoreMigrator = new IdStoreMigrator({ configManager: this.configManager, }) + + this.ethStore.on('update', this.sendUpdate.bind(this)) } getState () { From 33b4d213f19c4c8105932b2f8b88421bbbf896d3 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Tue, 3 Jan 2017 11:15:06 -0800 Subject: [PATCH 13/18] Fix sendUpdate for new promisified getState method --- app/scripts/metamask-controller.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 91def2569..c0d2f3b4c 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -163,8 +163,12 @@ module.exports = class MetamaskController { } sendUpdate () { - this.listeners.forEach((remote) => { - remote.sendUpdate(this.getState()) + this.getState() + .then((state) => { + + this.listeners.forEach((remote) => { + remote.sendUpdate(state) + }) }) } From 68be5240195ba31a5b66e21857d2c409d3f7ea2e Mon Sep 17 00:00:00 2001 From: Kevin Serrano Date: Tue, 3 Jan 2017 11:23:27 -0800 Subject: [PATCH 14/18] Take out unneeded permissions from the app. --- app/manifest.json | 2 -- gulpfile.js | 30 ++++++++++++++++++++++++++++-- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/app/manifest.json b/app/manifest.json index 61775ed93..95dcfc31a 100644 --- a/app/manifest.json +++ b/app/manifest.json @@ -56,9 +56,7 @@ ], "permissions": [ "storage", - "tabs", "clipboardWrite", - "clipboardRead", "http://localhost:8545/" ], "web_accessible_resources": [ diff --git a/gulpfile.js b/gulpfile.js index 26ad0c1f8..ca69b36a8 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -46,6 +46,7 @@ gulp.task('copy:locales', copyTask({ './dist/firefox/_locales', './dist/chrome/_locales', './dist/edge/_locales', + './dist/opera/_locales', ] })) gulp.task('copy:images', copyTask({ @@ -54,6 +55,7 @@ gulp.task('copy:images', copyTask({ './dist/firefox/images', './dist/chrome/images', './dist/edge/images', + './dist/opera/images', ], })) gulp.task('copy:fonts', copyTask({ @@ -62,6 +64,7 @@ gulp.task('copy:fonts', copyTask({ './dist/firefox/fonts', './dist/chrome/fonts', './dist/edge/fonts', + './dist/opera/fonts', ], })) gulp.task('copy:reload', copyTask({ @@ -70,6 +73,7 @@ gulp.task('copy:reload', copyTask({ './dist/firefox/scripts', './dist/chrome/scripts', './dist/edge/scripts', + './dist/opera/scripts', ], pattern: '/chromereload.js', })) @@ -79,6 +83,7 @@ gulp.task('copy:root', copyTask({ './dist/firefox', './dist/chrome', './dist/edge', + './dist/opera', ], pattern: '/*', })) @@ -92,6 +97,21 @@ gulp.task('manifest:chrome', function() { .pipe(gulp.dest('./dist/chrome', { overwrite: true })) }) +gulp.task('manifest:opera', function() { + return gulp.src('./dist/opera/manifest.json') + .pipe(jsoneditor(function(json) { + json.permissions = [ + "storage", + "tabs", + "clipboardWrite", + "clipboardRead", + "http://localhost:8545/" + ] + return json + })) + .pipe(gulp.dest('./dist/opera', { overwrite: true })) +}) + gulp.task('manifest:production', function() { return gulp.src([ './dist/firefox/manifest.json', @@ -118,7 +138,7 @@ if (!disableLiveReload) { copyStrings.push('copy:reload') } -gulp.task('copy', gulp.series(gulp.parallel(...copyStrings), 'manifest:production', 'manifest:chrome')) +gulp.task('copy', gulp.series(gulp.parallel(...copyStrings), 'manifest:production', 'manifest:chrome', 'manifest:opera')) gulp.task('copy:watch', function(){ gulp.watch(['./app/{_locales,images}/*', './app/scripts/chromereload.js', './app/*.{html,json}'], gulp.series('copy')) }) @@ -188,7 +208,12 @@ gulp.task('zip:edge', () => { .pipe(zip(`metamask-edge-${manifest.version}.zip`)) .pipe(gulp.dest('builds')); }) -gulp.task('zip', gulp.parallel('zip:chrome', 'zip:firefox', 'zip:edge')) +gulp.task('zip:opera', () => { + return gulp.src('dist/opera/**') + .pipe(zip(`metamask-opera-${manifest.version}.zip`)) + .pipe(gulp.dest('builds')); +}) +gulp.task('zip', gulp.parallel('zip:chrome', 'zip:firefox', 'zip:edge', 'zip:opera')) // high level tasks @@ -255,6 +280,7 @@ function bundleTask(opts) { .pipe(gulp.dest('./dist/firefox/scripts')) .pipe(gulp.dest('./dist/chrome/scripts')) .pipe(gulp.dest('./dist/edge/scripts')) + .pipe(gulp.dest('./dist/opera/scripts')) .pipe(gulpif(!disableLiveReload,livereload())) ) From 68e27e6193d2390077fc29d6c565836e2362f5fd Mon Sep 17 00:00:00 2001 From: Kevin Serrano Date: Tue, 3 Jan 2017 11:24:03 -0800 Subject: [PATCH 15/18] Changelog. --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 239203553..674a05932 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## Current Master - Add a check for when a tx is included in a block. +- Remove certain non-essential permissions from certain builds. ## 2.14.1 2016-12-20 From 3363a38bfc3317e41ae1540cc630dcf7f11729e0 Mon Sep 17 00:00:00 2001 From: Kevin Serrano Date: Tue, 3 Jan 2017 11:26:19 -0800 Subject: [PATCH 16/18] Changelog. --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 674a05932..b10ff010f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,8 +2,8 @@ ## Current Master -- Add a check for when a tx is included in a block. - Remove certain non-essential permissions from certain builds. +- Add a check for when a tx is included in a block. ## 2.14.1 2016-12-20 From 10c818abac0901724cf7d98bf63728bdeadcdbed Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Tue, 3 Jan 2017 11:30:27 -0800 Subject: [PATCH 17/18] Restore missing line --- ui/app/actions.js | 1 + 1 file changed, 1 insertion(+) diff --git a/ui/app/actions.js b/ui/app/actions.js index 96d76a50a..2ffa352a4 100644 --- a/ui/app/actions.js +++ b/ui/app/actions.js @@ -231,6 +231,7 @@ function createNewVaultAndKeychain (password) { if (err) { return dispatch(actions.displayWarning(err.message)) } + dispatch(actions.updateMetamaskState(newState)) }) } } From 2c2cdc4475b0ac8a6a52186a470a49c6a085ca08 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Tue, 3 Jan 2017 11:33:15 -0800 Subject: [PATCH 18/18] Convert more actions to new simple pattern --- ui/app/actions.js | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/ui/app/actions.js b/ui/app/actions.js index 2ffa352a4..d63d36f19 100644 --- a/ui/app/actions.js +++ b/ui/app/actions.js @@ -226,14 +226,7 @@ function createNewVaultAndRestore (password, seed) { } function createNewVaultAndKeychain (password) { - return (dispatch) => { - background.createNewVaultAndKeychain(password, (err, newState) => { - if (err) { - return dispatch(actions.displayWarning(err.message)) - } - dispatch(actions.updateMetamaskState(newState)) - }) - } + return callBackgroundThenUpdate(background.createNewVaultAndKeychain, password) } function revealSeedConfirmation () { @@ -255,17 +248,8 @@ function requestRevealSeed (password) { } } - function addNewKeyring (type, opts) { - return (dispatch) => { - dispatch(actions.showLoadingIndication()) - background.addNewKeyring(type, opts, (err) => { - dispatch(this.hideLoadingIndication()) - if (err) { - return dispatch(actions.displayWarning(err)) - } - }) - } + return callBackgroundThenUpdate(background.addNewKeyring, type, opts) } function addNewAccount (ringNumber = 0) {