Merge pull request #2590 from ethereum/refactor_logic9

refactor vm provider to use remix-simulator
pull/5370/head
yann300 5 years ago committed by GitHub
commit 64a4b95fc2
  1. 1075
      package-lock.json
  2. 12
      package.json
  3. 2
      src/app/tabs/runTab/settings.js
  4. 18
      src/blockchain/blockchain.js
  5. 8
      src/blockchain/providers/injected.js
  6. 8
      src/blockchain/providers/node.js
  7. 73
      src/blockchain/providers/vm.js
  8. 16
      test-browser/commands/selectAccount.js
  9. 1
      test-browser/tests/ballot.js
  10. 2
      test-browser/tests/libraryDeployment.js
  11. 1
      test-browser/tests/publishContract.js
  12. 15
      test-browser/tests/recorder.js
  13. 5
      test-browser/tests/signingMessage.js
  14. 1
      test-browser/tests/specialFunctions.js
  15. 1
      test-browser/tests/transactionExecution.js

1075
package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -56,11 +56,12 @@
"npm-run-all": "^4.0.2", "npm-run-all": "^4.0.2",
"onchange": "^3.2.1", "onchange": "^3.2.1",
"remix-analyzer": "0.3.23", "remix-analyzer": "0.3.23",
"remix-debug": "0.3.26", "remix-debug": "0.3.27",
"remix-lib": "0.4.22", "remix-lib": "0.4.23",
"remix-solidity": "0.3.25", "remix-solidity": "0.3.26",
"remix-tabs": "1.0.48", "remix-tabs": "1.0.48",
"remix-tests": "0.1.28", "remix-tests": "0.1.29",
"remix-simulator": "0.1.9-beta.1",
"remixd": "0.1.8-alpha.10", "remixd": "0.1.8-alpha.10",
"request": "^2.83.0", "request": "^2.83.0",
"rimraf": "^2.6.1", "rimraf": "^2.6.1",
@ -145,13 +146,14 @@
"remix-ide": "./bin/remix-ide" "remix-ide": "./bin/remix-ide"
}, },
"scripts": { "scripts": {
"setupremix": "npm run linkremixdebug && npm run linkremixlib && npm run linkremixsolidity && npm run linkremixanalyzer && npm run linkremixtests", "setupremix": "npm run linkremixdebug && npm run linkremixlib && npm run linkremixsolidity && npm run linkremixanalyzer && npm run linkremixtests && npm run linkremixsimulator",
"pullremix": "git clone https://github.com/ethereum/remix", "pullremix": "git clone https://github.com/ethereum/remix",
"linkremixlib": "cd node_modules && rm -rf remix-lib && ln -s ../../remix/remix-lib remix-lib && cd ..", "linkremixlib": "cd node_modules && rm -rf remix-lib && ln -s ../../remix/remix-lib remix-lib && cd ..",
"linkremixsolidity": "cd node_modules && rm -rf remix-solidity && ln -s ../../remix/remix-solidity remix-solidity && cd ..", "linkremixsolidity": "cd node_modules && rm -rf remix-solidity && ln -s ../../remix/remix-solidity remix-solidity && cd ..",
"linkremixtests": "cd node_modules && rm -rf remix-tests && ln -s ../../remix/remix-tests remix-tests && cd ..", "linkremixtests": "cd node_modules && rm -rf remix-tests && ln -s ../../remix/remix-tests remix-tests && cd ..",
"linkremixdebug": "cd node_modules && rm -rf remix-debug && ln -s ../../remix/remix-debug remix-debug && cd ..", "linkremixdebug": "cd node_modules && rm -rf remix-debug && ln -s ../../remix/remix-debug remix-debug && cd ..",
"linkremixanalyzer": "cd node_modules && rm -rf remix-analyzer && ln -s ../../remix/remix-analyzer remix-analyzer && cd ..", "linkremixanalyzer": "cd node_modules && rm -rf remix-analyzer && ln -s ../../remix/remix-analyzer remix-analyzer && cd ..",
"linkremixsimulator": "cd node_modules && rm -rf remix-simulator && ln -s ../../remix/remix-simulator remix-simulator && cd ..",
"build": "browserify src/index.js -o build/app.js --exclude solc", "build": "browserify src/index.js -o build/app.js --exclude solc",
"build_debugger": "browserify src/app/debugger/remix-debugger/index.js -o src/app/debugger/remix-debugger/build/app.js", "build_debugger": "browserify src/app/debugger/remix-debugger/index.js -o src/app/debugger/remix-debugger/build/app.js",
"browsertest": "sleep 5 && npm run nightwatch_local", "browsertest": "sleep 5 && npm run nightwatch_local",

@ -97,7 +97,7 @@ class SettingsUI {
</span> </span>
</label> </label>
<div class="${css.account}"> <div class="${css.account}">
<select name="txorigin" class="form-control ${css.select} custom-select" id="txorigin"></select> <select data-id="runTabSelectAccount" name="txorigin" class="form-control ${css.select} custom-select" id="txorigin"></select>
${copyToClipboard(() => document.querySelector('#runTabView #txorigin').value)} ${copyToClipboard(() => document.querySelector('#runTabView #txorigin').value)}
<i id="remixRunSignMsg" class="fas fa-edit ${css.icon}" aria-hidden="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-hidden="true" onclick=${this.signMessage.bind(this)} title="Sign a message using this account key"></i>
</div> </div>

@ -56,18 +56,6 @@ class Blockchain {
this.executionContext.event.register('removeProvider', (name) => { this.executionContext.event.register('removeProvider', (name) => {
this.event.trigger('removeProvider', [name]) this.event.trigger('removeProvider', [name])
}) })
// this.udapp.event.register('initiatingTransaction', (timestamp, tx, payLoad) => {
// this.event.trigger('initiatingTransaction', [timestamp, tx, payLoad])
// })
// this.udapp.event.register('transactionExecuted', (error, from, to, data, call, txResult, timestamp) => {
// this.event.trigger('transactionExecuted', [error, from, to, data, call, txResult, timestamp])
// })
// this.udapp.event.register('transactionBroadcasted', (txhash, networkName) => {
// this.event.trigger('transactionBroadcasted', [txhash, networkName])
// })
} }
setupProviders () { setupProviders () {
@ -321,7 +309,8 @@ class Blockchain {
resetEnvironment () { resetEnvironment () {
this.getCurrentProvider().resetEnvironment() this.getCurrentProvider().resetEnvironment()
// TODO: most params here can be refactored away in txRunner // TODO: most params here can be refactored away in txRunner
this.txRunner = new TxRunner(this.providers.vm.accounts, { // this.txRunner = new TxRunner(this.providers.vm.accounts, {
this.txRunner = new TxRunner(this.providers.vm.RemixSimulatorProvider.Accounts.accounts, {
// TODO: only used to check value of doNotShowTransactionConfirmationAgain property // TODO: only used to check value of doNotShowTransactionConfirmationAgain property
config: this.config, config: this.config,
// TODO: to refactor, TxRunner already has access to executionContext // TODO: to refactor, TxRunner already has access to executionContext
@ -430,7 +419,8 @@ class Blockchain {
if (err) return next(err) if (err) return next(err)
if (!address) return next('No accounts available') if (!address) return next('No accounts available')
if (self.executionContext.isVM() && !self.providers.vm.accounts[address]) { // if (self.executionContext.isVM() && !self.providers.vm.accounts[address]) {
if (self.executionContext.isVM() && !self.providers.vm.RemixSimulatorProvider.Accounts.accounts[address]) {
return next('Invalid account selected') return next('Invalid account selected')
} }
next(null, address, value, gasLimit) next(null, address, value, gasLimit)

@ -1,5 +1,5 @@
const Web3 = require('web3') const Web3 = require('web3')
const { stripHexPrefix } = require('ethereumjs-util') const { stripHexPrefix, hashPersonalMessage } = require('ethereumjs-util')
class InjectedProvider { class InjectedProvider {
@ -35,10 +35,10 @@ class InjectedProvider {
} }
signMessage (message, account, _passphrase, cb) { signMessage (message, account, _passphrase, cb) {
const hashedMsg = Web3.utils.sha3(message) const messageHash = hashPersonalMessage(Buffer.from(message))
try { try {
this.executionContext.web3().eth.sign(hashedMsg, account, (error, signedData) => { this.executionContext.web3().eth.sign(message, account, (error, signedData) => {
cb(error, hashedMsg, signedData) cb(error, '0x' + messageHash.toString('hex'), signedData)
}) })
} catch (e) { } catch (e) {
cb(e.message) cb(e.message)

@ -1,5 +1,5 @@
const Web3 = require('web3') const Web3 = require('web3')
const { stripHexPrefix } = require('ethereumjs-util') const { stripHexPrefix, hashPersonalMessage } = require('ethereumjs-util')
const Personal = require('web3-eth-personal') const Personal = require('web3-eth-personal')
class NodeProvider { class NodeProvider {
@ -43,11 +43,11 @@ class NodeProvider {
} }
signMessage (message, account, passphrase, cb) { signMessage (message, account, passphrase, cb) {
const hashedMsg = Web3.utils.sha3(message) const messageHash = hashPersonalMessage(Buffer.from(message))
try { try {
const personal = new Personal(this.executionContext.web3().currentProvider) const personal = new Personal(this.executionContext.web3().currentProvider)
personal.sign(hashedMsg, account, passphrase, (error, signedData) => { personal.sign(message, account, passphrase, (error, signedData) => {
cb(error, hashedMsg, signedData) cb(error, '0x' + messageHash.toString('hex'), signedData)
}) })
} catch (e) { } catch (e) {
cb(e.message) cb(e.message)

@ -1,91 +1,66 @@
const Web3 = require('web3') const Web3 = require('web3')
const { BN, privateToAddress, toChecksumAddress, isValidPrivate, stripHexPrefix } = require('ethereumjs-util') const { BN, privateToAddress, stripHexPrefix, hashPersonalMessage } = require('ethereumjs-util')
const crypto = require('crypto') const RemixSimulator = require('remix-simulator')
const ethJSUtil = require('ethereumjs-util')
class VMProvider { class VMProvider {
constructor (executionContext) { constructor (executionContext) {
this.executionContext = executionContext this.executionContext = executionContext
this.RemixSimulatorProvider = new RemixSimulator.Provider({executionContext: this.executionContext})
this.RemixSimulatorProvider.init()
this.web3 = new Web3(this.RemixSimulatorProvider)
this.accounts = {} this.accounts = {}
} }
getAccounts (cb) { getAccounts (cb) {
if (!this.accounts) { this.web3.eth.getAccounts((err, accounts) => {
cb('No accounts?') if (err) {
return cb('No accounts?') return cb('No accounts?')
} }
return cb(null, Object.keys(this.accounts)) return cb(null, accounts)
})
} }
resetEnvironment () { resetEnvironment () {
this.RemixSimulatorProvider.Accounts.resetAccounts()
this.accounts = {} this.accounts = {}
this._addAccount('3cd7232cd6f3fc66a57a6bedc1a8ed6c228fff0a327e169c2bcc5e869ed49511', '0x56BC75E2D63100000')
this._addAccount('2ac6c190b09897cd8987869cc7b918cfea07ee82038d492abce033c75c1b1d0c', '0x56BC75E2D63100000')
this._addAccount('dae9801649ba2d95a21e688b56f77905e5667c44ce868ec83f82e838712a2c7a', '0x56BC75E2D63100000')
this._addAccount('d74aa6d18aa79a05f3473dd030a97d3305737cbc8337d940344345c1f6b72eea', '0x56BC75E2D63100000')
this._addAccount('71975fbf7fe448e004ac7ae54cad0a383c3906055a65468714156a07385e96ce', '0x56BC75E2D63100000')
}
/** Add an account to the list of account (only for Javascript VM) */
_addAccount (privateKey, balance) {
privateKey = Buffer.from(privateKey, 'hex')
const address = privateToAddress(privateKey)
// FIXME: we don't care about the callback, but we should still make this proper
let stateManager = this.executionContext.vm().stateManager
stateManager.getAccount(address, (error, account) => {
if (error) return console.log(error)
account.balance = balance || '0xf00000000000000001'
stateManager.putAccount(address, account, (error) => {
if (error) console.log(error)
})
})
this.accounts[toChecksumAddress('0x' + address.toString('hex'))] = { privateKey, nonce: 0 }
} }
// TODO: is still here because of the plugin API
// can be removed later when we update the API
createVMAccount (newAccount) { createVMAccount (newAccount) {
const { privateKey, balance } = newAccount const { privateKey, balance } = newAccount
this._addAccount(privateKey, balance) this.RemixSimulatorProvider.Accounts._addAccount(privateKey, balance)
const privKey = Buffer.from(privateKey, 'hex') const privKey = Buffer.from(privateKey, 'hex')
return '0x' + privateToAddress(privKey).toString('hex') return '0x' + privateToAddress(privKey).toString('hex')
} }
newAccount (_passwordPromptCb, cb) { newAccount (_passwordPromptCb, cb) {
let privateKey this.RemixSimulatorProvider.Accounts.newAccount(cb)
do {
privateKey = crypto.randomBytes(32)
} while (!isValidPrivate(privateKey))
this._addAccount(privateKey, '0x56BC75E2D63100000')
return cb(null, '0x' + privateToAddress(privateKey).toString('hex'))
} }
getBalanceInEther (address, cb) { getBalanceInEther (address, cb) {
address = stripHexPrefix(address) address = stripHexPrefix(address)
this.web3.eth.getBalance(address, (err, res) => {
this.executionContext.vm().stateManager.getAccount(Buffer.from(address, 'hex'), (err, res) => {
if (err) { if (err) {
return cb('Account not found') return cb(err)
} }
cb(null, Web3.utils.fromWei(new BN(res.balance).toString(10), 'ether')) cb(null, Web3.utils.fromWei(new BN(res).toString(10), 'ether'))
}) })
} }
getGasPrice (cb) { getGasPrice (cb) {
this.executionContext.web3().eth.getGasPrice(cb) this.web3.eth.getGasPrice(cb)
} }
signMessage (message, account, _passphrase, cb) { signMessage (message, account, _passphrase, cb) {
const personalMsg = ethJSUtil.hashPersonalMessage(Buffer.from(message)) const messageHash = hashPersonalMessage(Buffer.from(message))
const privKey = this.accounts[account].privateKey this.web3.eth.sign(message, account, (error, signedData) => {
try { if (error) {
const rsv = ethJSUtil.ecsign(personalMsg, privKey) return cb(error)
const signedData = ethJSUtil.toRpcSig(rsv.v, rsv.r, rsv.s)
cb(null, '0x' + personalMsg.toString('hex'), signedData)
} catch (e) {
cb(e.message)
} }
cb(null, '0x' + messageHash.toString('hex'), signedData)
})
} }
getProvider () { getProvider () {

@ -0,0 +1,16 @@
const EventEmitter = require('events')
class SelectAccount extends EventEmitter {
command (account) {
if (account) {
this.api
.click(`select[data-id="runTabSelectAccount"] [value="${account}"]`)
.perform(() => {
this.emit('complete')
})
} else this.emit('complete')
return this
}
}
module.exports = SelectAccount

@ -20,6 +20,7 @@ module.exports = {
.clickLaunchIcon('solidity') .clickLaunchIcon('solidity')
.testContracts('Untitled.sol', sources[0]['browser/Untitled.sol'], ['Ballot']) .testContracts('Untitled.sol', sources[0]['browser/Untitled.sol'], ['Ballot'])
.clickLaunchIcon('udapp') .clickLaunchIcon('udapp')
.selectAccount('0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c')
.setValue('input[placeholder="bytes32[] proposalNames"]', '["0x48656c6c6f20576f726c64210000000000000000000000000000000000000000"]') .setValue('input[placeholder="bytes32[] proposalNames"]', '["0x48656c6c6f20576f726c64210000000000000000000000000000000000000000"]')
.click('#runTabView button[class^="instanceButton"]') .click('#runTabView button[class^="instanceButton"]')
.waitForElementPresent('.instance:nth-of-type(2)') .waitForElementPresent('.instance:nth-of-type(2)')

@ -11,6 +11,8 @@ module.exports = {
}, },
'Add Lib Test File': function (browser) { 'Add Lib Test File': function (browser) {
browser.addFile('Untitled5.sol', sources[0]['browser/Untitled5.sol']) browser.addFile('Untitled5.sol', sources[0]['browser/Untitled5.sol'])
.clickLaunchIcon('udapp')
.selectAccount('0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c') // this account will be used for this test suite
}, },
'Test Auto Deploy Lib': function (browser) { 'Test Auto Deploy Lib': function (browser) {
let addressRef let addressRef

@ -22,6 +22,7 @@ module.exports = {
done() done()
}) })
.modalFooterOKClick() .modalFooterOKClick()
.end()
}, },
'Publish on Swarm': '' + function (browser) { 'Publish on Swarm': '' + function (browser) {
browser browser

@ -13,6 +13,7 @@ module.exports = {
var addressRef var addressRef
browser.addFile('scenario.json', {content: records}) browser.addFile('scenario.json', {content: records})
.clickLaunchIcon('udapp') .clickLaunchIcon('udapp')
.selectAccount('0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c') // this account will be used for this test suite
.click('div[class^="cardContainer"] i[class^="arrow"]') .click('div[class^="cardContainer"] i[class^="arrow"]')
.click('#runTabView .runtransaction') .click('#runTabView .runtransaction')
.waitForElementPresent('.instance:nth-of-type(2)') .waitForElementPresent('.instance:nth-of-type(2)')
@ -68,7 +69,7 @@ var sources = [{'browser/testRecorder.sol': {content: `contract testRecorder {
var records = `{ var records = `{
"accounts": { "accounts": {
"account{0}": "0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c" "account{3}": "0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c"
}, },
"linkReferences": { "linkReferences": {
"testLib": "created{1512830014773}" "testLib": "created{1512830014773}"
@ -85,7 +86,7 @@ var records = `{
"linkReferences": {}, "linkReferences": {},
"inputs": "()", "inputs": "()",
"type": "constructor", "type": "constructor",
"from": "account{0}" "from": "account{3}"
} }
}, },
{ {
@ -111,7 +112,7 @@ var records = `{
"name": "", "name": "",
"type": "constructor", "type": "constructor",
"inputs": "(uint256)", "inputs": "(uint256)",
"from": "account{0}" "from": "account{3}"
} }
}, },
{ {
@ -127,7 +128,7 @@ var records = `{
"name": "set", "name": "set",
"inputs": "(uint256,address)", "inputs": "(uint256,address)",
"type": "function", "type": "function",
"from": "account{0}" "from": "account{3}"
} }
} }
], ],
@ -226,7 +227,7 @@ var records = `{
var scenario = { var scenario = {
'accounts': { 'accounts': {
'account{0}': '0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c' 'account{3}': '0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c'
}, },
'linkReferences': {}, 'linkReferences': {},
'transactions': [ 'transactions': [
@ -244,7 +245,7 @@ var scenario = {
'name': '', 'name': '',
'type': 'constructor', 'type': 'constructor',
'inputs': '(uint256)', 'inputs': '(uint256)',
'from': 'account{0}' 'from': 'account{3}'
} }
}, },
{ {
@ -259,7 +260,7 @@ var scenario = {
'name': 'set', 'name': 'set',
'inputs': '(uint256)', 'inputs': '(uint256)',
'type': 'function', 'type': 'function',
'from': 'account{0}' 'from': 'account{3}'
} }
} }
], ],

@ -11,7 +11,10 @@ module.exports = {
}, },
'Test Signature': function (browser) { 'Test Signature': function (browser) {
let hash, signature let hash, signature
browser.clickLaunchIcon('udapp').signMessage('test message', (h, s) => { browser
.clickLaunchIcon('udapp')
.selectAccount('0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c') // this account will be used for this test suite
.signMessage('test message', (h, s) => {
hash = h hash = h
signature = s signature = s
console.log('hash', hash) console.log('hash', hash)

@ -13,6 +13,7 @@ module.exports = {
browser.waitForElementVisible('#icon-panel', 10000) browser.waitForElementVisible('#icon-panel', 10000)
.testContracts('receiveAndFallback.sol', sources[0]['browser/receiveAndFallback.sol'], ['CheckSpecials']) // compile .testContracts('receiveAndFallback.sol', sources[0]['browser/receiveAndFallback.sol'], ['CheckSpecials']) // compile
.clickLaunchIcon('udapp') .clickLaunchIcon('udapp')
.selectAccount('0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c') // this account will be used for this test suite
.selectContract('CheckSpecials') .selectContract('CheckSpecials')
.createContract('') // deploy .createContract('') // deploy
.clickInstance(0) .clickInstance(0)

@ -13,6 +13,7 @@ module.exports = {
'Execute Simple Contract and Test Terminal': function (browser) { 'Execute Simple Contract and Test Terminal': function (browser) {
browser.testContracts('Untitled.sol', sources[0]['browser/Untitled.sol'], ['TestContract']) browser.testContracts('Untitled.sol', sources[0]['browser/Untitled.sol'], ['TestContract'])
.clickLaunchIcon('udapp') .clickLaunchIcon('udapp')
.selectAccount('0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c') // this account will be used for this test suite
.click('#runTabView button[class^="instanceButton"]') .click('#runTabView button[class^="instanceButton"]')
.waitForElementPresent('.instance:nth-of-type(2)') .waitForElementPresent('.instance:nth-of-type(2)')
.click('.instance:nth-of-type(2) > div > button') .click('.instance:nth-of-type(2) > div > button')

Loading…
Cancel
Save