1
0
mirror of https://github.com/kremalicious/metamask-extension.git synced 2024-11-26 12:29:06 +01:00

Merge branch 'master' of github.com:MetaMask/metamask-plugin into json-rpc-engine-bump

This commit is contained in:
kumavis 2017-09-19 11:27:49 -07:00
commit 77e02d264a
26 changed files with 191 additions and 262 deletions

View File

@ -2,11 +2,22 @@
## Current Master
- Fix bug that would sometimes display transactions as failed that could be successfully mined.
## 3.10.2 2017-9-18
rollback to 3.10.0 due to bug
## 3.10.1 2017-9-18
- Add ability to export private keys as a file.
- Add ability to export seed words as a file.
- Changed state logs to a file download than a clipboard copy.
- Add specific error for failed recipient address checksum.
- Fixed a long standing memory leak associated with filters installed by dapps
- Fix link to support center.
- Fixed tooltip icon locations to avoid overflow.
- Warn users when a dapp proposes a high gas limit (90% of blockGasLimit or higher)
## 3.10.0 2017-9-11

View File

@ -1,7 +1,7 @@
{
"name": "MetaMask",
"short_name": "Metamask",
"version": "3.10.0",
"version": "3.10.2",
"manifest_version": 2,
"author": "https://metamask.io",
"description": "Ethereum Browser Extension",

View File

@ -76,6 +76,9 @@ module.exports = class PendingTransactionTracker extends EventEmitter {
Dont marked as failed if the error is a "known" transaction warning
"there is already a transaction with the same sender-nonce
but higher/same gas price"
Also don't mark as failed if it has ever been broadcast successfully.
A successful broadcast means it may still be mined.
*/
const errorMessage = err.message.toLowerCase()
const isKnownTx = (
@ -88,6 +91,7 @@ module.exports = class PendingTransactionTracker extends EventEmitter {
// other
|| errorMessage.includes('gateway timeout')
|| errorMessage.includes('nonce too low')
|| txMeta.retryCount > 1
)
// ignore resubmit warnings, return early
if (isKnownTx) return
@ -117,10 +121,12 @@ module.exports = class PendingTransactionTracker extends EventEmitter {
// Only auto-submit already-signed txs:
if (!('rawTx' in txMeta)) return
// Increment a try counter.
txMeta.retryCount++
const rawTx = txMeta.rawTx
return await this.publishTransaction(rawTx)
const txHash = await this.publishTransaction(rawTx)
// Increment successful tries:
txMeta.retryCount++
return txHash
}
async _checkPendingTx (txMeta) {

View File

@ -3,7 +3,7 @@ machine:
version: 8.1.4
test:
override:
- "npm run ci"
- "npm test"
dependencies:
pre:
- sudo apt-get update

View File

@ -14,13 +14,13 @@
</body>
<style>
html, body, #app-content, .super-dev-container {
html, body, #test-container, .super-dev-container {
height: 100%;
width: 100%;
position: relative;
background: white;
}
.mock-app-root {
#app-content {
background: #F7F7F7;
}
</style>

View File

@ -18,13 +18,14 @@
</body>
<style>
html, body, #app-content, .super-dev-container {
html, body, #test-container, .super-dev-container {
height: 100%;
width: 100%;
position: relative;
background: white;
}
.mock-app-root {
#app-content {
background: #F7F7F7;
}
</style>

View File

@ -1,7 +1,6 @@
const createParentStream = require('iframe-stream').ParentStream
const SWcontroller = require('client-sw-ready-event/lib/sw-client.js')
const SwStream = require('sw-stream/lib/sw-stream.js')
const SetupUntrustedComunication = ('./lib/setup-untrusted-connection.js')
let intervalDelay = Math.floor(Math.random() * (30000 - 1000)) + 1000
const background = new SWcontroller({
@ -12,7 +11,7 @@ const background = new SWcontroller({
})
const pageStream = createParentStream()
background.on('ready', (_) => {
background.on('ready', () => {
let swStream = SwStream({
serviceWorker: background.controller,
context: 'dapp',

View File

@ -2,8 +2,6 @@ const injectCss = require('inject-css')
const SWcontroller = require('client-sw-ready-event/lib/sw-client.js')
const SwStream = require('sw-stream/lib/sw-stream.js')
const MetaMaskUiCss = require('../../ui/css')
const setupIframe = require('./lib/setup-iframe.js')
const MetamaskInpageProvider = require('../../app/scripts/lib/inpage-provider.js')
const MetamascaraPlatform = require('../../app/scripts/platforms/window')
const startPopup = require('../../app/scripts/popup-core')
@ -17,6 +15,7 @@ const container = document.getElementById('app-content')
var name = 'popup'
window.METAMASK_UI_TYPE = name
window.METAMASK_PLATFORM_TYPE = 'mascara'
let intervalDelay = Math.floor(Math.random() * (30000 - 1000)) + 1000
@ -32,25 +31,39 @@ const connectApp = function (readSw) {
serviceWorker: background.controller,
context: name,
})
startPopup({container, connectionStream}, (err, store) => {
if (err) return displayCriticalError(err)
store.subscribe(() => {
const state = store.getState()
if (state.appState.shouldClose) window.close()
return new Promise((resolve, reject) => {
startPopup({ container, connectionStream }, (err, store) => {
console.log('hello from MetaMascara ui!')
if (err) reject(err)
store.subscribe(() => {
const state = store.getState()
if (state.appState.shouldClose) window.close()
})
resolve()
})
})
}
background.on('ready', (sw) => {
background.removeListener('updatefound', connectApp)
connectApp(sw)
background.on('ready', async (sw) => {
try {
background.removeListener('updatefound', connectApp)
await timeout(1000)
await connectApp(sw)
console.log('hello from cb ready event!')
} catch (e) {
console.error(e)
}
})
background.on('updatefound', () => window.location.reload())
background.on('updatefound', windowReload)
background.startWorker()
.then(() => {
setTimeout(() => {
const appContent = document.getElementById(`app-content`)
if (!appContent.children.length) window.location.reload()
}, 2000)
})
console.log('hello from MetaMascara ui!')
function windowReload() {
if (window.METAMASK_SKIP_RELOAD) return
window.location.reload()
}
function timeout (time) {
return new Promise((resolve) => {
setTimeout(resolve, time || 1500)
})
}

View File

@ -1,21 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>QUnit Example</title>
<link rel="stylesheet" href="https://code.jquery.com/qunit/qunit-2.0.0.css">
</head>
<body>
<div id="qunit"></div>
<div id="qunit-fixture"></div>
<script src="https://code.jquery.com/qunit/qunit-2.0.0.js"></script>
<script src="./jquery-3.1.0.min.js"></script>
<script src="./helpers.js"></script>
<script src="./test-bundle.js"></script>
<script src="/testem.js"></script>
<div id="app-content"></div>
<script src="./bundle.js"></script>
</body>
</html>

View File

@ -1,119 +0,0 @@
const PASSWORD = 'password123'
QUnit.module('first time usage')
QUnit.test('render init screen', function (assert) {
var done = assert.async()
let app
wait(1000).then(function() {
app = $('#app-content').contents()
const recurseNotices = function () {
let button = app.find('button')
if (button.html() === 'Accept') {
let termsPage = app.find('.markdown')[0]
termsPage.scrollTop = termsPage.scrollHeight
return wait().then(() => {
button.click()
return wait()
}).then(() => {
return recurseNotices()
})
} else {
return wait()
}
}
return recurseNotices()
}).then(function() {
// Scroll through terms
var title = app.find('h1').text()
assert.equal(title, 'MetaMask', 'title screen')
// enter password
var pwBox = app.find('#password-box')[0]
var confBox = app.find('#password-box-confirm')[0]
pwBox.value = PASSWORD
confBox.value = PASSWORD
return wait()
}).then(function() {
// create vault
var createButton = app.find('button.primary')[0]
createButton.click()
return wait(1500)
}).then(function() {
var created = app.find('h3')[0]
assert.equal(created.textContent, 'Vault Created', 'Vault created screen')
// Agree button
var button = app.find('button')[0]
assert.ok(button, 'button present')
button.click()
return wait(1000)
}).then(function() {
var detail = app.find('.account-detail-section')[0]
assert.ok(detail, 'Account detail section loaded.')
var sandwich = app.find('.sandwich-expando')[0]
sandwich.click()
return wait()
}).then(function() {
var sandwich = app.find('.menu-droppo')[0]
var children = sandwich.children
var lock = children[children.length - 2]
assert.ok(lock, 'Lock menu item found')
lock.click()
return wait(1000)
}).then(function() {
var pwBox = app.find('#password-box')[0]
pwBox.value = PASSWORD
var createButton = app.find('button.primary')[0]
createButton.click()
return wait(1000)
}).then(function() {
var detail = app.find('.account-detail-section')[0]
assert.ok(detail, 'Account detail section loaded again.')
return wait()
}).then(function (){
var qrButton = app.find('.fa.fa-qrcode')[0]
qrButton.click()
return wait(1000)
}).then(function (){
var qrHeader = app.find('.qr-header')[0]
var qrContainer = app.find('#qr-container')[0]
assert.equal(qrHeader.textContent, 'Account 1', 'Should show account label.')
assert.ok(qrContainer, 'QR Container found')
return wait()
}).then(function (){
var networkMenu = app.find('.network-indicator')[0]
networkMenu.click()
return wait()
}).then(function (){
var networkMenu = app.find('.network-indicator')[0]
var children = networkMenu.children
children.length[3]
assert.ok(children, 'All network options present')
done()
})
})

12
mascara/test/test-ui.js Normal file
View File

@ -0,0 +1,12 @@
const Helper = require('./util/mascara-test-helper.js')
window.addEventListener('load', () => {
window.METAMASK_SKIP_RELOAD = true
// inject app container
const body = document.body
const container = document.createElement('div')
container.id = 'app-content'
body.appendChild(container)
// start ui
require('../src/ui.js')
})

View File

@ -1,13 +0,0 @@
launch_in_dev:
- Chrome
- Firefox
- Opera
launch_in_ci:
- Chrome
- Firefox
- Opera
framework:
- qunit
before_tests: "npm run mascaraCi"
after_tests: "rm ./background.js ./test-bundle.js ./bundle.js"
test_page: "./index.html"

View File

@ -1,5 +0,0 @@
const Helper = require('./util/mascara-test-helper.js')
window.addEventListener('load', () => {
require('../src/ui.js')
})

View File

@ -94,9 +94,8 @@ startApp()
function startApp(){
const body = document.body
const container = document.createElement('div')
container.id = 'app-content'
container.id = 'test-container'
body.appendChild(container)
console.log('container', container)
render(
h('.super-dev-container', [
@ -113,7 +112,7 @@ function startApp(){
h(Selector, { actions, selectedKey: selectedView, states, store }),
h('.mock-app-root', {
h('#app-content', {
style: {
height: '500px',
width: '360px',

View File

@ -6,30 +6,33 @@
"scripts": {
"start": "npm run dev",
"dev": "gulp dev --debug",
"disc": "gulp disc --debug",
"clear": "rm -rf node_modules/eth-contract-metadata && rm -rf node_modules/eth-phishing-detect",
"dist": "npm run clear && npm install && gulp dist",
"test": "npm run lint && npm run test-unit && npm run test-integration",
"test-unit": "METAMASK_ENV=test mocha --require test/helper.js --recursive \"test/unit/**/*.js\"",
"single-test": "METAMASK_ENV=test mocha --require test/helper.js",
"test-integration": "npm run buildMock && npm run buildCiUnits && karma start",
"test-coverage": "nyc npm run test-unit && if [ $COVERALLS_REPO_TOKEN ]; then nyc report --reporter=text-lcov | coveralls; fi",
"ci": "npm run lint && npm run test-coverage && npm run test-integration",
"lint": "gulp lint",
"buildCiUnits": "node test/integration/index.js",
"watch": "mocha watch --recursive \"test/unit/**/*.js\"",
"genStates": "node development/genStates.js",
"ui": "npm run genStates && beefy ui-dev.js:bundle.js --live --open --index=./development/index.html --cwd ./",
"ui": "npm run test:flat:build:states && beefy ui-dev.js:bundle.js --live --open --index=./development/index.html --cwd ./",
"mock": "beefy mock-dev.js:bundle.js --live --open --index=./development/index.html --cwd ./",
"buildMock": "npm run genStates && browserify ./mock-dev.js -o ./development/bundle.js",
"watch": "mocha watch --recursive \"test/unit/**/*.js\"",
"mascara": "node ./mascara/example/server",
"dist": "npm run dist:clear && npm install && gulp dist",
"dist:clear": "rm -rf node_modules/eth-contract-metadata && rm -rf node_modules/eth-phishing-detect",
"test": "npm run lint && npm run test:coverage && npm run test:integration",
"test:unit": "METAMASK_ENV=test mocha --require test/helper.js --recursive \"test/unit/**/*.js\"",
"test:single": "METAMASK_ENV=test mocha --require test/helper.js",
"test:integration": "npm run test:flat && npm run test:mascara",
"test:coverage": "nyc npm run test:unit && npm run test:coveralls-upload",
"test:coveralls-upload": "if [ $COVERALLS_REPO_TOKEN ]; then nyc report --reporter=text-lcov | coveralls; fi",
"test:flat": "npm run test:flat:build && karma start test/flat.conf.js",
"test:flat:build": "npm run test:flat:build:ui && npm run test:flat:build:tests",
"test:flat:build:tests": "node test/integration/index.js",
"test:flat:build:states": "node development/genStates.js",
"test:flat:build:ui": "npm run test:flat:build:states && browserify ./mock-dev.js -o ./development/bundle.js",
"test:mascara": "npm run test:mascara:build && karma start test/mascara.conf.js",
"test:mascara:build": "mkdir -p dist/mascara && npm run test:mascara:build:ui && npm run test:mascara:build:background && npm run test:mascara:build:tests",
"test:mascara:build:ui": "browserify mascara/test/test-ui.js -o dist/mascara/ui.js",
"test:mascara:build:background": "browserify mascara/src/background.js -o dist/mascara/background.js",
"test:mascara:build:tests": "browserify test/integration/lib/first-time.js -o dist/mascara/tests.js",
"lint": "gulp lint",
"disc": "gulp disc --debug",
"announce": "node development/announcer.js",
"generateNotice": "node notices/notice-generator.js",
"deleteNotice": "node notices/notice-delete.js",
"mascara": "node ./mascara/example/server",
"buildMascaraCi": "browserify mascara/test/window-load.js -o mascara/test/bundle.js",
"buildMascaraSWCi": "browserify mascara/src/background.js -o mascara/test/background.js",
"mascaraCi": "npm run buildMascaraCi && npm run buildMascaraSWCi && node mascara/test/index.js",
"testMascara": "cd mascara/test && npm run mascaraCi && testem ci -P 3"
"deleteNotice": "node notices/notice-delete.js"
},
"browserify": {
"transform": [

View File

@ -2,7 +2,7 @@
// Generated on Mon Sep 11 2017 18:45:48 GMT-0700 (PDT)
module.exports = function(config) {
config.set({
return {
// base path that will be used to resolve all patterns (eg. files, exclude)
basePath: process.cwd(),
@ -16,9 +16,7 @@ module.exports = function(config) {
// list of files / patterns to load in the browser
files: [
'development/bundle.js',
'test/integration/jquery-3.1.0.min.js',
'test/integration/bundle.js',
{ pattern: 'dist/chrome/images/**/*.*', watched: false, included: false, served: true },
{ pattern: 'dist/chrome/fonts/**/*.*', watched: false, included: false, served: true },
],
@ -57,5 +55,5 @@ module.exports = function(config) {
// Concurrency level
// how many browser should be started simultaneous
concurrency: Infinity
})
}
}

8
test/flat.conf.js Normal file
View File

@ -0,0 +1,8 @@
const getBaseConfig = require('./base.conf.js')
module.exports = function(config) {
const settings = getBaseConfig(config)
settings.files.push('development/bundle.js')
settings.files.push('test/integration/bundle.js')
config.set(settings)
}

View File

@ -10,19 +10,12 @@ QUnit.test('render init screen', (assert) => {
})
})
// QUnit.testDone(({ module, name, total, passed, failed, skipped, todo, runtime }) => {
// if (failed > 0) {
// const app = $('iframe').contents()[0].documentElement
// console.warn('Test failures - dumping DOM:')
// console.log(app.innerHTML)
// }
// })
async function runFirstTimeUsageTest(assert, done) {
let waitTime = 0
if (window.METAMASK_PLATFORM_TYPE === 'mascara') waitTime = 4000
await timeout(waitTime)
await timeout()
const app = $('#app-content .mock-app-root')
const app = $('#app-content')
// recurse notices
while (true) {
@ -32,10 +25,12 @@ async function runFirstTimeUsageTest(assert, done) {
const termsPage = app.find('.markdown')[0]
termsPage.scrollTop = termsPage.scrollHeight
await timeout()
console.log('Clearing notice')
button.click()
await timeout()
} else {
// exit loop
console.log('No more notices...')
break
}
}
@ -58,7 +53,7 @@ async function runFirstTimeUsageTest(assert, done) {
const createButton = app.find('button.primary')[0]
createButton.click()
await timeout(1500)
await timeout(3000)
const created = app.find('h3')[0]
assert.equal(created.textContent, 'Vault Created', 'Vault created screen')
@ -129,10 +124,8 @@ async function runFirstTimeUsageTest(assert, done) {
assert.ok(children2, 'All network options present')
}
function timeout(time) {
return new Promise(function (resolve, reject) {
setTimeout(function () {
resolve()
}, time * 3 || 1500)
function timeout (time) {
return new Promise((resolve, reject) => {
setTimeout(resolve, time || 1500)
})
}

17
test/mascara.conf.js Normal file
View File

@ -0,0 +1,17 @@
const getBaseConfig = require('./base.conf.js')
module.exports = function(config) {
const settings = getBaseConfig(config)
// ui and tests
settings.files.push('dist/mascara/ui.js')
settings.files.push('dist/mascara/tests.js')
// service worker background
settings.files.push({ pattern: 'dist/mascara/background.js', watched: false, included: false, served: true }),
settings.proxies['/background.js'] = '/base/dist/mascara/background.js'
// use this to keep the browser open for debugging
settings.browserNoActivityTimeout = 10000000
config.set(settings)
}

View File

@ -61,7 +61,7 @@ const actions = {
var css = MetaMaskUiCss()
injectCss(css)
const container = document.querySelector('#app-content')
const container = document.querySelector('#test-container')
// parse opts
var store = configureStore(states[selectedView])
@ -72,7 +72,7 @@ render(
h(Selector, { actions, selectedKey: selectedView, states, store }),
h('.mock-app-root', {
h('#app-content', {
style: {
height: '500px',
width: '360px',

View File

@ -52,7 +52,9 @@ PendingTx.prototype.render = function () {
const gas = txParams.gas
const gasBn = hexToBn(gas)
const gasLimit = new BN(parseInt(blockGasLimit))
const safeGasLimit = this.bnMultiplyByFraction(gasLimit, 19, 20).toString(10)
const safeGasLimitBN = this.bnMultiplyByFraction(gasLimit, 19, 20)
const saferGasLimitBN = this.bnMultiplyByFraction(gasLimit, 18, 20)
const safeGasLimit = safeGasLimitBN.toString(10)
// Gas Price
const gasPrice = txParams.gasPrice || MIN_GAS_PRICE_BN.toString(16)
@ -66,6 +68,8 @@ PendingTx.prototype.render = function () {
const balanceBn = hexToBn(balance)
const insufficientBalance = balanceBn.lt(maxCost)
const dangerousGasLimit = gasBn.gte(saferGasLimitBN)
const gasLimitSpecified = txMeta.gasLimitSpecified
const buyDisabled = insufficientBalance || !this.state.valid || !isValidAddress || this.state.submitting
const showRejectAll = props.unconfTxListLength > 1
@ -263,33 +267,44 @@ PendingTx.prototype.render = function () {
text-transform: uppercase;
}
`),
h('.cell.row', {
style: {
textAlign: 'center',
},
}, [
txMeta.simulationFails ?
h('.error', {
style: {
fontSize: '0.9em',
},
}, 'Transaction Error. Exception thrown in contract code.')
: null,
txMeta.simulationFails ?
h('.error', {
style: {
marginLeft: 50,
fontSize: '0.9em',
},
}, 'Transaction Error. Exception thrown in contract code.')
: null,
!isValidAddress ?
h('.error', {
style: {
fontSize: '0.9em',
},
}, 'Recipient address is invalid. Sending this transaction will result in a loss of ETH.')
: null,
!isValidAddress ?
h('.error', {
style: {
marginLeft: 50,
fontSize: '0.9em',
},
}, 'Recipient address is invalid. Sending this transaction will result in a loss of ETH.')
: null,
insufficientBalance ?
h('span.error', {
style: {
fontSize: '0.9em',
},
}, 'Insufficient balance for transaction')
: null,
(dangerousGasLimit && !gasLimitSpecified) ?
h('span.error', {
style: {
fontSize: '0.9em',
},
}, 'Gas limit set dangerously high. Approving this transaction is likely to fail.')
: null,
]),
insufficientBalance ?
h('span.error', {
style: {
marginLeft: 50,
fontSize: '0.9em',
},
}, 'Insufficient balance for transaction')
: null,
// send + cancel
h('.flex-row.flex-space-around.conf-buttons', {

View File

@ -17,6 +17,6 @@ Tooltip.prototype.render = function () {
return h(ReactTooltip, {
position: position || 'left',
title,
fixed: false,
fixed: true,
}, children)
}

View File

@ -35,7 +35,7 @@ TransactionIcon.prototype.render = function () {
case 'submitted':
return h(Tooltip, {
title: 'Pending',
position: 'bottom',
position: 'right',
}, [
h('i.fa.fa-ellipsis-h', {
style: {

View File

@ -65,7 +65,7 @@ TransactionListItem.prototype.render = function () {
h(Tooltip, {
title: 'Transaction Number',
position: 'bottom',
position: 'right',
}, [
h('span', {
style: {

View File

@ -262,6 +262,11 @@ SendTransactionScreen.prototype.onSubmit = function () {
return this.props.dispatch(actions.displayWarning(message))
}
if ((util.isInvalidChecksumAddress(recipient))) {
message = 'Recipient address checksum is invalid.'
return this.props.dispatch(actions.displayWarning(message))
}
if ((!util.isValidAddress(recipient) && !txData) || (!recipient && !txData)) {
message = 'Recipient address is invalid.'
return this.props.dispatch(actions.displayWarning(message))

View File

@ -37,6 +37,7 @@ module.exports = {
bnTable: bnTable,
isHex: isHex,
exportAsFile: exportAsFile,
isInvalidChecksumAddress,
}
function valuesFor (obj) {
@ -66,6 +67,12 @@ function isValidAddress (address) {
return (isAllOneCase(prefixed) && ethUtil.isValidAddress(prefixed)) || ethUtil.isValidChecksumAddress(prefixed)
}
function isInvalidChecksumAddress (address) {
var prefixed = ethUtil.addHexPrefix(address)
if (address === '0x0000000000000000000000000000000000000000') return false
return !isAllOneCase(prefixed) && !ethUtil.isValidChecksumAddress(prefixed) && ethUtil.isValidAddress(prefixed)
}
function isAllOneCase (address) {
if (!address) return true
var lower = address.toLowerCase()