diff --git a/package.json b/package.json
index de9330fb41..a55f364317 100644
--- a/package.json
+++ b/package.json
@@ -171,6 +171,7 @@
"make-mock-compiler": "node ci/makeMockCompiler.js",
"minify": "uglifyjs --in-source-map inline --source-map-inline -c warnings=false",
"nightwatch_local": "nightwatch --config nightwatch.js --env local",
+ "nightwatch_local_general": "nightwatch ./test-browser/tests/generalTests.js --config nightwatch.js --env local ",
"nightwatch_local_debugger": "nightwatch --config nightwatch_debugger.js --env local",
"nightwatch_remote_chrome": "nightwatch --config nightwatch.js --env chrome",
"nightwatch_remote_firefox": "nightwatch --config nightwatch.js --env default",
diff --git a/src/app/tabs/runTab/model/settings.js b/src/app/tabs/runTab/model/settings.js
index 1e20960a71..d5671fdbe0 100644
--- a/src/app/tabs/runTab/model/settings.js
+++ b/src/app/tabs/runTab/model/settings.js
@@ -1,6 +1,7 @@
var ethJSUtil = require('ethereumjs-util')
var Personal = require('web3-eth-personal')
var remixLib = require('remix-lib')
+const addTooltip = require('../../../ui/tooltip')
var EventManager = remixLib.EventManager
var executionContext = remixLib.execution.executionContext
@@ -73,6 +74,10 @@ class Settings {
return (!isVM && !isInjected)
}
+ isInjectedWeb3 () {
+ return executionContext.getProvider() === 'injected'
+ }
+
signMessage (message, account, passphrase, cb) {
var isVM = executionContext.isVM()
var isInjected = executionContext.getProvider() === 'injected'
@@ -92,6 +97,7 @@ class Settings {
if (isInjected) {
const hashedMsg = executionContext.web3().sha3(message)
try {
+ addTooltip('Please check your provider to approve')
executionContext.web3().eth.sign(account, hashedMsg, (error, signedData) => {
cb(error.message, hashedMsg, signedData)
})
diff --git a/src/app/tabs/runTab/settings.js b/src/app/tabs/runTab/settings.js
index 18459fb070..0a4f060902 100644
--- a/src/app/tabs/runTab/settings.js
+++ b/src/app/tabs/runTab/settings.js
@@ -58,11 +58,11 @@ class SettingsUI {
${copyToClipboard(() => document.querySelector('#runTabView #txorigin').value)}
-
+
`
@@ -223,6 +223,10 @@ class SettingsUI {
var signMessageDialog = { 'title': 'Sign a message', 'text': 'Enter a message to sign', 'inputvalue': 'Message to sign' }
var $txOrigin = this.el.querySelector('#txorigin')
+ if (!$txOrigin.selectedOptions[0] && (this.settings.isInjectedWeb3() || this.settings.isWeb3Provider())) {
+ return addTooltip(`Account list is empty, please make sure the current provider is properly connected to remix`)
+ }
+
var account = $txOrigin.selectedOptions[0].value
var promptCb = (passphrase) => {
@@ -231,13 +235,26 @@ class SettingsUI {
if (err) {
return addTooltip(err)
}
- modalDialogCustom.alert(yo`
hash:${msgHash}
signature:${signedData}
`)
+ modalDialogCustom.alert(yo`
+
+ hash:
+ ${msgHash}
+
signature:
+ ${signedData}
+
+ `)
})
}, false)
}
if (this.settings.isWeb3Provider()) {
- return modalDialogCustom.promptPassphrase('Passphrase to sign a message', 'Enter your passphrase for this account to sign the message', '', promptCb, false)
+ return modalDialogCustom.promptPassphrase(
+ 'Passphrase to sign a message',
+ 'Enter your passphrase for this account to sign the message',
+ '',
+ promptCb,
+ false
+ )
}
promptCb()
})
diff --git a/src/app/tabs/settings-tab.js b/src/app/tabs/settings-tab.js
index 821e6957d0..5ed5d0f943 100644
--- a/src/app/tabs/settings-tab.js
+++ b/src/app/tabs/settings-tab.js
@@ -67,7 +67,8 @@ module.exports = class SettingsTab extends BaseApi {
var gistAddToken = yo` { this.config.set('settings/gist-access-token', gistAccessToken.value); tooltip('Access token saved') }} value="Save" type="button">`
var gistRemoveToken = yo` { gistAccessToken.value = ''; this.config.set('settings/gist-access-token', ''); tooltip('Access token removed') }} value="Remove" type="button">`
this._view.gistToken = yo`${gistAccessToken}${copyToClipboard(() => this.config.get('settings/gist-access-token'))}${gistAddToken}${gistRemoveToken}
`
- this._view.optionVM = yo``
+ this._view.optionVM = yo``
+ if (this.config.get('settings/always-use-vm') === undefined) this.config.set('settings/always-use-vm', true)
if (this.config.get('settings/always-use-vm')) this._view.optionVM.setAttribute('checked', '')
this._view.personal = yo``
if (this.config.get('settings/personal-mode')) this._view.personal.setAttribute('checked', '')
diff --git a/test-browser/helpers/contracts.js b/test-browser/helpers/contracts.js
index d022a0a2ba..949afb0217 100644
--- a/test-browser/helpers/contracts.js
+++ b/test-browser/helpers/contracts.js
@@ -25,7 +25,8 @@ module.exports = {
removeFile,
getAddressAtPosition,
clickLaunchIcon,
- scrollInto
+ scrollInto,
+ signMsg
}
function clickLaunchIcon (icon) {
@@ -168,6 +169,30 @@ function scrollInto (target) {
})
}
+function signMsg (browser, msg, cb) {
+ let hash, signature
+ browser
+ .click('i[id="remixRunSignMsg"]')
+ .setValue('textarea[id="prompt_text"]', msg, () => {
+ browser.modalFooterOKClick().perform(
+ (client, done) => {
+ browser.getText('span[id="remixRunSignMsgHash"]', (v) => { hash = v; done() })
+ }
+ )
+ .perform(
+ (client, done) => {
+ browser.getText('span[id="remixRunSignMsgSignature"]', (v) => { signature = v; done() })
+ }
+ )
+ .modalFooterOKClick()
+ .perform(
+ () => {
+ cb(hash, signature)
+ }
+ )
+ })
+}
+
function _scrollInto (browser, target, cb) {
browser.execute(function (target) {
document.querySelector(target).scrollIntoView()
diff --git a/test-browser/tests/simpleContract.js b/test-browser/tests/generalTests.js
similarity index 81%
rename from test-browser/tests/simpleContract.js
rename to test-browser/tests/generalTests.js
index e525e3cf32..e820a54e3b 100644
--- a/test-browser/tests/simpleContract.js
+++ b/test-browser/tests/generalTests.js
@@ -21,7 +21,10 @@ function runTests (browser) {
browser.setEditorValue = contractHelper.setEditorValue
browser.getEditorValue = contractHelper.getEditorValue
browser.clickLaunchIcon = contractHelper.clickLaunchIcon
+ browser.modalFooterOKClick = contractHelper.modalFooterOKClick
+ browser.clickFunction = contractHelper.clickFunction
browser.scrollInto = contractHelper.scrollInto
+ browser.verifyCallReturnValue = contractHelper.verifyCallReturnValue
browser
.waitForElementVisible('#icon-panel', 10000)
.clickLaunchIcon('solidity')
@@ -34,7 +37,8 @@ function runTests (browser) {
testFailedImport, /* testGitHubImport */
addDeployLibTestFile,
testAutoDeployLib,
- testManualDeployLib
+ testManualDeployLib,
+ testSignature
],
function () {
browser.end()
@@ -158,6 +162,36 @@ function checkDeployShouldSucceed (browser, address, callback) {
})
}
+function testSignature (browser, callback) {
+ let hash, signature
+ browser.clickLaunchIcon('run').pause(4000).perform((client, done) => {
+ contractHelper.signMsg(browser, 'test message', (h, s) => {
+ hash = h
+ signature = s
+ contractHelper.addFile(browser, 'signMassage.sol', sources[6]['browser/signMassage.sol'], () => {
+ contractHelper.switchFile(browser, 'browser/signMassage.sol', () => {
+ contractHelper.selectContract(browser, 'ECVerify', () => { // deploy lib
+ contractHelper.createContract(browser, '', () => {
+ browser.waitForElementPresent('.instance:nth-of-type(4)')
+ .click('.instance:nth-of-type(4) > div > button')
+ .clickFunction('ecrecovery - call', {types: 'bytes32 hash, bytes sig', values: `"${hash.value}","${signature.value}"`}).perform(
+ () => {
+ contractHelper.verifyCallReturnValue(
+ browser,
+ '0x08970fed061e7747cd9a38d680a601510cb659fb',
+ ['0: address: 0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c'],
+ () => { callback(null, browser) }
+ )
+ }
+ )
+ })
+ })
+ })
+ })
+ })
+ })
+}
+
/*
function testGitHubImport (browser, callback) {
contractHelper.addFile(browser, 'Untitled4.sol', sources[3]['browser/Untitled4.sol'], () => {
@@ -320,6 +354,46 @@ var sources = [
function get () public view returns (uint) {
return lib.getInt();
}
-}`}
+ }`}
+ },
+ {
+ 'browser/signMassage.sol': {content: `
+ contract SignMassageTest {
+ function testRecovery(bytes32 h, uint8 v, bytes32 r, bytes32 s) public pure returns (address) {
+ return ecrecover(h, v, r, s);
+ }
+ }
+
+ library ECVerify {
+ function ecrecovery(bytes32 hash, bytes memory sig) public pure returns (address) {
+ bytes32 r;
+ bytes32 s;
+ uint8 v;
+
+ if (sig.length != 65) {
+ return address(0);
+ }
+
+ assembly {
+ r := mload(add(sig, 32))
+ s := mload(add(sig, 64))
+ v := and(mload(add(sig, 65)), 255)
+ }
+
+ if (v < 27) {
+ v += 27;
+ }
+
+ if (v != 27 && v != 28) {
+ return address(0);
+ }
+
+ return ecrecover(hash, v, r, s);
+ }
+
+ function ecverify(bytes32 hash, bytes memory sig, address signer) public pure returns (bool) {
+ return signer == ecrecovery(hash, sig);
+ }
+ }`}
}
]