From 582f906f42de837ab7caa942d4da0a31fdb93204 Mon Sep 17 00:00:00 2001 From: LianaHus Date: Tue, 14 May 2019 17:50:14 +0200 Subject: [PATCH 01/10] browser-test for signing a msg using ethutil --- package.json | 1 + src/app/tabs/runTab/model/settings.js | 5 ++ src/app/tabs/runTab/settings.js | 25 ++++-- src/app/tabs/settings-tab.js | 2 +- test-browser/helpers/contracts.js | 27 ++++++- .../{simpleContract.js => generalTests.js} | 77 ++++++++++++++++++- 6 files changed, 128 insertions(+), 9 deletions(-) rename test-browser/tests/{simpleContract.js => generalTests.js} (82%) 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 5d961c077c..cae8039a51 100644 --- a/src/app/tabs/runTab/model/settings.js +++ b/src/app/tabs/runTab/model/settings.js @@ -73,6 +73,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' @@ -93,6 +97,7 @@ class Settings { const hashedMsg = executionContext.web3().sha3(message) try { executionContext.web3().eth.sign(account, hashedMsg, (error, signedData) => { + if (!error) alert("Please check your provider") cb(error, hashedMsg, signedData) }) } catch (e) { diff --git a/src/app/tabs/runTab/settings.js b/src/app/tabs/runTab/settings.js index 18459fb070..165b3ba7c2 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,8 @@ 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()) + return addTooltip(`Account list is empty, please login to your wallet to load it`) var account = $txOrigin.selectedOptions[0].value var promptCb = (passphrase) => { @@ -231,13 +233,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..4721a9ebfc 100644 --- a/src/app/tabs/settings-tab.js +++ b/src/app/tabs/settings-tab.js @@ -67,7 +67,7 @@ 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')) 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..36d77e4b03 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 82% rename from test-browser/tests/simpleContract.js rename to test-browser/tests/generalTests.js index e525e3cf32..49aab073cc 100644 --- a/test-browser/tests/simpleContract.js +++ b/test-browser/tests/generalTests.js @@ -21,6 +21,8 @@ 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 .waitForElementVisible('#icon-panel', 10000) @@ -34,7 +36,8 @@ function runTests (browser) { testFailedImport, /* testGitHubImport */ addDeployLibTestFile, testAutoDeployLib, - testManualDeployLib + testManualDeployLib, + testSignature ], function () { browser.end() @@ -158,6 +161,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 +353,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); + } + }`} } ] From fa218bdca4d3c085bba5742360867be311a8da4c Mon Sep 17 00:00:00 2001 From: LianaHus Date: Tue, 14 May 2019 18:43:51 +0200 Subject: [PATCH 02/10] standart --- src/app/tabs/runTab/model/settings.js | 3 ++- src/app/tabs/runTab/settings.js | 3 ++- test-browser/helpers/contracts.js | 12 ++++++------ test-browser/tests/generalTests.js | 16 ++++++++-------- 4 files changed, 18 insertions(+), 16 deletions(-) diff --git a/src/app/tabs/runTab/model/settings.js b/src/app/tabs/runTab/model/settings.js index cae8039a51..a11f76b19a 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 @@ -97,7 +98,7 @@ class Settings { const hashedMsg = executionContext.web3().sha3(message) try { executionContext.web3().eth.sign(account, hashedMsg, (error, signedData) => { - if (!error) alert("Please check your provider") + if (!error) addTooltip('Please check your provider to approve') cb(error, hashedMsg, signedData) }) } catch (e) { diff --git a/src/app/tabs/runTab/settings.js b/src/app/tabs/runTab/settings.js index 165b3ba7c2..1d4350913e 100644 --- a/src/app/tabs/runTab/settings.js +++ b/src/app/tabs/runTab/settings.js @@ -223,8 +223,9 @@ 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()) + if (!$txOrigin.selectedOptions[0] && this.settings.isInjectedWeb3()) { return addTooltip(`Account list is empty, please login to your wallet to load it`) + } var account = $txOrigin.selectedOptions[0].value var promptCb = (passphrase) => { diff --git a/test-browser/helpers/contracts.js b/test-browser/helpers/contracts.js index 36d77e4b03..949afb0217 100644 --- a/test-browser/helpers/contracts.js +++ b/test-browser/helpers/contracts.js @@ -173,20 +173,20 @@ function signMsg (browser, msg, cb) { let hash, signature browser .click('i[id="remixRunSignMsg"]') - .setValue('textarea[id="prompt_text"]', msg, ()=>{ + .setValue('textarea[id="prompt_text"]', msg, () => { browser.modalFooterOKClick().perform( - (client, done)=>{ - browser.getText('span[id="remixRunSignMsgHash"]', (v)=>{ hash = v; done()}) + (client, done) => { + browser.getText('span[id="remixRunSignMsgHash"]', (v) => { hash = v; done() }) } ) .perform( - (client, done)=>{ - browser.getText('span[id="remixRunSignMsgSignature"]', (v)=>{ signature = v; done()}) + (client, done) => { + browser.getText('span[id="remixRunSignMsgSignature"]', (v) => { signature = v; done() }) } ) .modalFooterOKClick() .perform( - ()=>{ + () => { cb(hash, signature) } ) diff --git a/test-browser/tests/generalTests.js b/test-browser/tests/generalTests.js index 49aab073cc..35e7c25fdf 100644 --- a/test-browser/tests/generalTests.js +++ b/test-browser/tests/generalTests.js @@ -163,23 +163,23 @@ 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)=>{ + 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.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( + .clickFunction('ecrecovery - call', {types: 'bytes32 hash, bytes sig', values: `"${hash.value}","${signature.value}"`}).perform( + () => { + contractHelper.verifyCallReturnalue( browser, - "0x08970fed061e7747cd9a38d680a601510cb659fb", + '0x08970fed061e7747cd9a38d680a601510cb659fb', ['0: address: 0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c'], - ()=>{callback(null, browser)} + () => { callback(null, browser) } ) } ) From 3a890fea651edebcb56345cf7b9a19b851a350f7 Mon Sep 17 00:00:00 2001 From: LianaHus Date: Tue, 14 May 2019 18:54:00 +0200 Subject: [PATCH 03/10] edited error msg --- src/app/tabs/runTab/settings.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/app/tabs/runTab/settings.js b/src/app/tabs/runTab/settings.js index 1d4350913e..f780cf2654 100644 --- a/src/app/tabs/runTab/settings.js +++ b/src/app/tabs/runTab/settings.js @@ -224,8 +224,9 @@ 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()) { - return addTooltip(`Account list is empty, please login to your wallet to load it`) + 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) => { From 7ec6d4afbff4170423bc3ccca8ad7e233c833a91 Mon Sep 17 00:00:00 2001 From: LianaHus Date: Wed, 15 May 2019 09:15:37 +0200 Subject: [PATCH 04/10] test fix --- test-browser/tests/generalTests.js | 1 + 1 file changed, 1 insertion(+) diff --git a/test-browser/tests/generalTests.js b/test-browser/tests/generalTests.js index 35e7c25fdf..26fcd0c5c1 100644 --- a/test-browser/tests/generalTests.js +++ b/test-browser/tests/generalTests.js @@ -24,6 +24,7 @@ function runTests (browser) { browser.modalFooterOKClick = contractHelper.modalFooterOKClick browser.clickFunction = contractHelper.clickFunction browser.scrollInto = contractHelper.scrollInto + browser.verifyCallReturnalue = contractHelper.verifyCallReturnalue browser .waitForElementVisible('#icon-panel', 10000) .clickLaunchIcon('solidity') From a643d1d6b7c30ea870ae016a7751c10cced6b68d Mon Sep 17 00:00:00 2001 From: LianaHus Date: Wed, 15 May 2019 09:57:41 +0200 Subject: [PATCH 05/10] typo --- test-browser/tests/generalTests.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test-browser/tests/generalTests.js b/test-browser/tests/generalTests.js index 26fcd0c5c1..e820a54e3b 100644 --- a/test-browser/tests/generalTests.js +++ b/test-browser/tests/generalTests.js @@ -24,7 +24,7 @@ function runTests (browser) { browser.modalFooterOKClick = contractHelper.modalFooterOKClick browser.clickFunction = contractHelper.clickFunction browser.scrollInto = contractHelper.scrollInto - browser.verifyCallReturnalue = contractHelper.verifyCallReturnalue + browser.verifyCallReturnValue = contractHelper.verifyCallReturnValue browser .waitForElementVisible('#icon-panel', 10000) .clickLaunchIcon('solidity') @@ -176,7 +176,7 @@ function testSignature (browser, callback) { .click('.instance:nth-of-type(4) > div > button') .clickFunction('ecrecovery - call', {types: 'bytes32 hash, bytes sig', values: `"${hash.value}","${signature.value}"`}).perform( () => { - contractHelper.verifyCallReturnalue( + contractHelper.verifyCallReturnValue( browser, '0x08970fed061e7747cd9a38d680a601510cb659fb', ['0: address: 0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c'], From b0e09e74282d900a4f407df6b7b8947a82ebf36d Mon Sep 17 00:00:00 2001 From: LianaHus Date: Wed, 15 May 2019 10:51:04 +0200 Subject: [PATCH 06/10] fixed tooltip place --- src/app/tabs/runTab/model/settings.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/tabs/runTab/model/settings.js b/src/app/tabs/runTab/model/settings.js index a11f76b19a..5d28d4438a 100644 --- a/src/app/tabs/runTab/model/settings.js +++ b/src/app/tabs/runTab/model/settings.js @@ -97,8 +97,8 @@ 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) => { - if (!error) addTooltip('Please check your provider to approve') cb(error, hashedMsg, signedData) }) } catch (e) { From 89aea3bbdc3b4a50857f6ccc2a1292e9b77d8a20 Mon Sep 17 00:00:00 2001 From: yann300 Date: Wed, 15 May 2019 11:25:44 +0200 Subject: [PATCH 07/10] Update settings.js --- src/app/tabs/runTab/settings.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/tabs/runTab/settings.js b/src/app/tabs/runTab/settings.js index f780cf2654..0a4f060902 100644 --- a/src/app/tabs/runTab/settings.js +++ b/src/app/tabs/runTab/settings.js @@ -223,7 +223,7 @@ 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()) { + 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`) } From eccdab0d7c3a7a04409b2e7fb711d761e3a5301e Mon Sep 17 00:00:00 2001 From: Liana Husikyan Date: Wed, 15 May 2019 12:00:05 +0200 Subject: [PATCH 08/10] Update settings-tab.js --- src/app/tabs/settings-tab.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/app/tabs/settings-tab.js b/src/app/tabs/settings-tab.js index 4721a9ebfc..58e62f53c8 100644 --- a/src/app/tabs/settings-tab.js +++ b/src/app/tabs/settings-tab.js @@ -70,6 +70,7 @@ module.exports = class SettingsTab extends BaseApi { this._view.optionVM = yo`` if (this.config.get('settings/always-use-vm')) this._view.optionVM.setAttribute('checked', '') this._view.personal = yo`` + if (this.config.get('settings/always-use-vm') === undefined) this.config.set('settings/always-use-vm', true) if (this.config.get('settings/personal-mode')) this._view.personal.setAttribute('checked', '') var warnText = `Transaction sent over Web3 will use the web3.personal API - be sure the endpoint is opened before enabling it. This mode allows to provide the passphrase in the Remix interface without having to unlock the account. From a48c4083b20746e3aad01a96bcec3d3edaed7e04 Mon Sep 17 00:00:00 2001 From: Liana Husikyan Date: Wed, 15 May 2019 14:39:58 +0200 Subject: [PATCH 09/10] Update settings-tab.js --- src/app/tabs/settings-tab.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/tabs/settings-tab.js b/src/app/tabs/settings-tab.js index 58e62f53c8..9de5df831a 100644 --- a/src/app/tabs/settings-tab.js +++ b/src/app/tabs/settings-tab.js @@ -70,7 +70,7 @@ module.exports = class SettingsTab extends BaseApi { this._view.optionVM = yo`` if (this.config.get('settings/always-use-vm')) this._view.optionVM.setAttribute('checked', '') this._view.personal = 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') === undefined) this.config.set('settings/always-use-vm', true) if (this.config.get('settings/personal-mode')) this._view.personal.setAttribute('checked', '') var warnText = `Transaction sent over Web3 will use the web3.personal API - be sure the endpoint is opened before enabling it. This mode allows to provide the passphrase in the Remix interface without having to unlock the account. From 55b14578f1985b7d4f7d89a02c79a6be519b2b8f Mon Sep 17 00:00:00 2001 From: yann300 Date: Wed, 15 May 2019 14:54:25 +0200 Subject: [PATCH 10/10] Update settings-tab.js --- src/app/tabs/settings-tab.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/tabs/settings-tab.js b/src/app/tabs/settings-tab.js index 9de5df831a..5ed5d0f943 100644 --- a/src/app/tabs/settings-tab.js +++ b/src/app/tabs/settings-tab.js @@ -68,9 +68,9 @@ module.exports = class SettingsTab extends BaseApi { 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`` + 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/always-use-vm') === undefined) this.config.set('settings/always-use-vm', true) if (this.config.get('settings/personal-mode')) this._view.personal.setAttribute('checked', '') var warnText = `Transaction sent over Web3 will use the web3.personal API - be sure the endpoint is opened before enabling it. This mode allows to provide the passphrase in the Remix interface without having to unlock the account.