Merge pull request #1972 from ethereum/master_l

browser-test for signing a msg using ethutil
pull/1/head
yann300 6 years ago committed by GitHub
commit c62fbafbbf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      package.json
  2. 6
      src/app/tabs/runTab/model/settings.js
  3. 27
      src/app/tabs/runTab/settings.js
  4. 3
      src/app/tabs/settings-tab.js
  5. 27
      test-browser/helpers/contracts.js
  6. 78
      test-browser/tests/generalTests.js

@ -171,6 +171,7 @@
"make-mock-compiler": "node ci/makeMockCompiler.js", "make-mock-compiler": "node ci/makeMockCompiler.js",
"minify": "uglifyjs --in-source-map inline --source-map-inline -c warnings=false", "minify": "uglifyjs --in-source-map inline --source-map-inline -c warnings=false",
"nightwatch_local": "nightwatch --config nightwatch.js --env local", "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_local_debugger": "nightwatch --config nightwatch_debugger.js --env local",
"nightwatch_remote_chrome": "nightwatch --config nightwatch.js --env chrome", "nightwatch_remote_chrome": "nightwatch --config nightwatch.js --env chrome",
"nightwatch_remote_firefox": "nightwatch --config nightwatch.js --env default", "nightwatch_remote_firefox": "nightwatch --config nightwatch.js --env default",

@ -1,6 +1,7 @@
var ethJSUtil = require('ethereumjs-util') var ethJSUtil = require('ethereumjs-util')
var Personal = require('web3-eth-personal') var Personal = require('web3-eth-personal')
var remixLib = require('remix-lib') var remixLib = require('remix-lib')
const addTooltip = require('../../../ui/tooltip')
var EventManager = remixLib.EventManager var EventManager = remixLib.EventManager
var executionContext = remixLib.execution.executionContext var executionContext = remixLib.execution.executionContext
@ -73,6 +74,10 @@ class Settings {
return (!isVM && !isInjected) return (!isVM && !isInjected)
} }
isInjectedWeb3 () {
return executionContext.getProvider() === 'injected'
}
signMessage (message, account, passphrase, cb) { signMessage (message, account, passphrase, cb) {
var isVM = executionContext.isVM() var isVM = executionContext.isVM()
var isInjected = executionContext.getProvider() === 'injected' var isInjected = executionContext.getProvider() === 'injected'
@ -92,6 +97,7 @@ class Settings {
if (isInjected) { if (isInjected) {
const hashedMsg = executionContext.web3().sha3(message) const hashedMsg = executionContext.web3().sha3(message)
try { try {
addTooltip('Please check your provider to approve')
executionContext.web3().eth.sign(account, hashedMsg, (error, signedData) => { executionContext.web3().eth.sign(account, hashedMsg, (error, signedData) => {
cb(error.message, hashedMsg, signedData) cb(error.message, hashedMsg, signedData)
}) })

@ -58,11 +58,11 @@ class SettingsUI {
<select id="selectExEnvOptions" onchange=${() => { this.updateNetwork() }} class="form-control ${css.select}"> <select id="selectExEnvOptions" onchange=${() => { this.updateNetwork() }} class="form-control ${css.select}">
<option id="vm-mode" <option id="vm-mode"
title="Execution environment does not connect to any node, everything is local and in memory only." title="Execution environment does not connect to any node, everything is local and in memory only."
value="vm" checked name="executionContext"> JavaScript VM value="vm" name="executionContext"> JavaScript VM
</option> </option>
<option id="injected-mode" <option id="injected-mode"
title="Execution environment has been provided by Metamask or similar provider." title="Execution environment has been provided by Metamask or similar provider."
value="injected" checked name="executionContext"> Injected Web3 value="injected" name="executionContext"> Injected Web3
</option> </option>
<option id="web3-mode" <option id="web3-mode"
title="Execution environment connects to node at localhost (or via IPC if available), transactions will be sent to the network and can cause loss of money or worse! title="Execution environment connects to node at localhost (or via IPC if available), transactions will be sent to the network and can cause loss of money or worse!
@ -92,7 +92,7 @@ class SettingsUI {
<div class=${css.account}> <div class=${css.account}>
<select name="txorigin" class="form-control ${css.select}" id="txorigin"></select> <select name="txorigin" class="form-control ${css.select}" id="txorigin"></select>
${copyToClipboard(() => document.querySelector('#runTabView #txorigin').value)} ${copyToClipboard(() => document.querySelector('#runTabView #txorigin').value)}
<i class="fas fa-edit ${css.icon}" aria-hiden="true" onclick=${this.signMessage.bind(this)} title="Sign a message using this account key"></i> <i id="remixRunSignMsg" class="fas fa-edit ${css.icon}" aria-hiden="true" onclick=${this.signMessage.bind(this)} title="Sign a message using this account key"></i>
</div> </div>
</div> </div>
` `
@ -223,6 +223,10 @@ class SettingsUI {
var signMessageDialog = { 'title': 'Sign a message', 'text': 'Enter a message to sign', 'inputvalue': 'Message to sign' } var signMessageDialog = { 'title': 'Sign a message', 'text': 'Enter a message to sign', 'inputvalue': 'Message to sign' }
var $txOrigin = this.el.querySelector('#txorigin') 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 account = $txOrigin.selectedOptions[0].value
var promptCb = (passphrase) => { var promptCb = (passphrase) => {
@ -231,13 +235,26 @@ class SettingsUI {
if (err) { if (err) {
return addTooltip(err) return addTooltip(err)
} }
modalDialogCustom.alert(yo`<div><b>hash:</b>${msgHash}<br><b>signature:</b>${signedData}</div>`) modalDialogCustom.alert(yo`
<div>
<b>hash:</b><br>
<span id="remixRunSignMsgHash">${msgHash}</span>
<br><b>signature:</b><br>
<span id="remixRunSignMsgSignature">${signedData}</span>
</div>
`)
}) })
}, false) }, false)
} }
if (this.settings.isWeb3Provider()) { 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() promptCb()
}) })

@ -67,7 +67,8 @@ module.exports = class SettingsTab extends BaseApi {
var gistAddToken = yo`<input class="${css.savegisttoken} btn btn-sm btn-primary" id="savegisttoken" onclick=${() => { this.config.set('settings/gist-access-token', gistAccessToken.value); tooltip('Access token saved') }} value="Save" type="button">` var gistAddToken = yo`<input class="${css.savegisttoken} btn btn-sm btn-primary" id="savegisttoken" onclick=${() => { this.config.set('settings/gist-access-token', gistAccessToken.value); tooltip('Access token saved') }} value="Save" type="button">`
var gistRemoveToken = yo`<input class="btn btn-sm btn-primary" id="removegisttoken" onclick=${() => { gistAccessToken.value = ''; this.config.set('settings/gist-access-token', ''); tooltip('Access token removed') }} value="Remove" type="button">` var gistRemoveToken = yo`<input class="btn btn-sm btn-primary" id="removegisttoken" onclick=${() => { gistAccessToken.value = ''; this.config.set('settings/gist-access-token', ''); tooltip('Access token removed') }} value="Remove" type="button">`
this._view.gistToken = yo`<div class="${css.checkboxText}">${gistAccessToken}${copyToClipboard(() => this.config.get('settings/gist-access-token'))}${gistAddToken}${gistRemoveToken}</div>` this._view.gistToken = yo`<div class="${css.checkboxText}">${gistAccessToken}${copyToClipboard(() => this.config.get('settings/gist-access-token'))}${gistAddToken}${gistRemoveToken}</div>`
this._view.optionVM = yo`<input onchange=${onchangeOption} class="align-middle form-check-input" id="alwaysUseVM" type="checkbox">` this._view.optionVM = yo`<input onchange=${onchangeOption} checked class="align-middle form-check-input" id="alwaysUseVM" type="checkbox">`
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', '') if (this.config.get('settings/always-use-vm')) this._view.optionVM.setAttribute('checked', '')
this._view.personal = yo`<input onchange=${onchangePersonal} id="personal" type="checkbox" class="align-middle form-check-input">` this._view.personal = yo`<input onchange=${onchangePersonal} id="personal" type="checkbox" class="align-middle form-check-input">`
if (this.config.get('settings/personal-mode')) this._view.personal.setAttribute('checked', '') if (this.config.get('settings/personal-mode')) this._view.personal.setAttribute('checked', '')

@ -25,7 +25,8 @@ module.exports = {
removeFile, removeFile,
getAddressAtPosition, getAddressAtPosition,
clickLaunchIcon, clickLaunchIcon,
scrollInto scrollInto,
signMsg
} }
function clickLaunchIcon (icon) { 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) { function _scrollInto (browser, target, cb) {
browser.execute(function (target) { browser.execute(function (target) {
document.querySelector(target).scrollIntoView() document.querySelector(target).scrollIntoView()

@ -21,7 +21,10 @@ function runTests (browser) {
browser.setEditorValue = contractHelper.setEditorValue browser.setEditorValue = contractHelper.setEditorValue
browser.getEditorValue = contractHelper.getEditorValue browser.getEditorValue = contractHelper.getEditorValue
browser.clickLaunchIcon = contractHelper.clickLaunchIcon browser.clickLaunchIcon = contractHelper.clickLaunchIcon
browser.modalFooterOKClick = contractHelper.modalFooterOKClick
browser.clickFunction = contractHelper.clickFunction
browser.scrollInto = contractHelper.scrollInto browser.scrollInto = contractHelper.scrollInto
browser.verifyCallReturnValue = contractHelper.verifyCallReturnValue
browser browser
.waitForElementVisible('#icon-panel', 10000) .waitForElementVisible('#icon-panel', 10000)
.clickLaunchIcon('solidity') .clickLaunchIcon('solidity')
@ -34,7 +37,8 @@ function runTests (browser) {
testFailedImport, /* testGitHubImport */ testFailedImport, /* testGitHubImport */
addDeployLibTestFile, addDeployLibTestFile,
testAutoDeployLib, testAutoDeployLib,
testManualDeployLib testManualDeployLib,
testSignature
], ],
function () { function () {
browser.end() 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) { function testGitHubImport (browser, callback) {
contractHelper.addFile(browser, 'Untitled4.sol', sources[3]['browser/Untitled4.sol'], () => { contractHelper.addFile(browser, 'Untitled4.sol', sources[3]['browser/Untitled4.sol'], () => {
@ -320,6 +354,46 @@ var sources = [
function get () public view returns (uint) { function get () public view returns (uint) {
return lib.getInt(); 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);
}
}`}
} }
] ]
Loading…
Cancel
Save