Merge pull request #888 from ethereum/add_remix_simulator
Add remix simulator to main branchpull/7/head
commit
2cffd838a2
@ -0,0 +1,2 @@ |
|||||||
|
# `remix-simulator` |
||||||
|
|
@ -0,0 +1,4 @@ |
|||||||
|
#!/usr/bin/env node |
||||||
|
|
||||||
|
require('../src/server'); |
||||||
|
|
@ -0,0 +1,5 @@ |
|||||||
|
var Provider = require('./src/provider') |
||||||
|
|
||||||
|
module.exports = { |
||||||
|
Provider: Provider |
||||||
|
} |
@ -0,0 +1,86 @@ |
|||||||
|
{ |
||||||
|
"name": "remix-simulator", |
||||||
|
"version": "0.0.3", |
||||||
|
"description": "Ethereum IDE and tools for the web", |
||||||
|
"contributors": [ |
||||||
|
{ |
||||||
|
"name": "Iuri Matias", |
||||||
|
"email": "iuri@ethereum.org" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"name": "Yann Levreau", |
||||||
|
"email": "yann@ethdev.com" |
||||||
|
} |
||||||
|
], |
||||||
|
"main": "./index.js", |
||||||
|
"dependencies": { |
||||||
|
"ansi-gray": "^0.1.1", |
||||||
|
"babel-plugin-transform-object-assign": "^6.22.0", |
||||||
|
"babel-preset-es2017": "^6.24.1", |
||||||
|
"babelify": "^7.3.0", |
||||||
|
"body-parser": "^1.18.2", |
||||||
|
"color-support": "^1.1.3", |
||||||
|
"express": "^4.16.3", |
||||||
|
"fast-async": "^6.3.7", |
||||||
|
"merge": "^1.2.0", |
||||||
|
"remix-lib": "^0.2.5", |
||||||
|
"standard": "^10.0.3", |
||||||
|
"time-stamp": "^2.0.0", |
||||||
|
"web3": "1.0.0-beta.27" |
||||||
|
}, |
||||||
|
"scripts": { |
||||||
|
"test": "standard" |
||||||
|
}, |
||||||
|
"bin": { |
||||||
|
"ethsim": "./bin/ethsim", |
||||||
|
"remix-simulator": "./bin/ethsim" |
||||||
|
}, |
||||||
|
"standard": { |
||||||
|
"ignore": [ |
||||||
|
"node_modules/*" |
||||||
|
], |
||||||
|
"parser": "babel-eslint" |
||||||
|
}, |
||||||
|
"repository": { |
||||||
|
"type": "git", |
||||||
|
"url": "git+https://github.com/ethereum/remix.git" |
||||||
|
}, |
||||||
|
"author": "remix team", |
||||||
|
"license": "MIT", |
||||||
|
"bugs": { |
||||||
|
"url": "https://github.com/ethereum/remix/issues" |
||||||
|
}, |
||||||
|
"homepage": "https://github.com/ethereum/remix#readme", |
||||||
|
"browserify": { |
||||||
|
"transform": [ |
||||||
|
[ |
||||||
|
"babelify", |
||||||
|
{ |
||||||
|
"plugins": [ |
||||||
|
[ |
||||||
|
"fast-async", |
||||||
|
{ |
||||||
|
"runtimePatten": null, |
||||||
|
"compiler": { |
||||||
|
"promises": true, |
||||||
|
"es7": true, |
||||||
|
"noRuntime": true, |
||||||
|
"wrapAwait": true |
||||||
|
} |
||||||
|
} |
||||||
|
], |
||||||
|
"transform-object-assign" |
||||||
|
] |
||||||
|
} |
||||||
|
], |
||||||
|
[ |
||||||
|
"babelify", |
||||||
|
{ |
||||||
|
"presets": [ |
||||||
|
"es2017" |
||||||
|
] |
||||||
|
} |
||||||
|
] |
||||||
|
] |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,22 @@ |
|||||||
|
var Web3 = require('web3') |
||||||
|
|
||||||
|
var Accounts = function () { |
||||||
|
this.web3 = new Web3() |
||||||
|
// TODO: make it random and/or use remix-libs
|
||||||
|
this.accounts = [this.web3.eth.accounts.create(['abcd'])] |
||||||
|
|
||||||
|
this.accounts[this.accounts[0].address.toLowerCase()] = this.accounts[0] |
||||||
|
this.accounts[this.accounts[0].address.toLowerCase()].privateKey = Buffer.from(this.accounts[this.accounts[0].address.toLowerCase()].privateKey.slice(2), 'hex') |
||||||
|
} |
||||||
|
|
||||||
|
Accounts.prototype.methods = function () { |
||||||
|
return { |
||||||
|
eth_accounts: this.eth_accounts.bind(this) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
Accounts.prototype.eth_accounts = function (payload, cb) { |
||||||
|
return cb(null, this.accounts.map((x) => x.address)) |
||||||
|
} |
||||||
|
|
||||||
|
module.exports = Accounts |
@ -0,0 +1,42 @@ |
|||||||
|
|
||||||
|
var Blocks = function () { |
||||||
|
} |
||||||
|
|
||||||
|
Blocks.prototype.methods = function () { |
||||||
|
return { |
||||||
|
eth_getBlockByNumber: this.eth_getBlockByNumber.bind(this), |
||||||
|
eth_gasPrice: this.eth_gasPrice.bind(this) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
Blocks.prototype.eth_getBlockByNumber = function (payload, cb) { |
||||||
|
let b = { |
||||||
|
'difficulty': '0x0', |
||||||
|
'extraData': '0x', |
||||||
|
'gasLimit': '0x7a1200', |
||||||
|
'gasUsed': '0x0', |
||||||
|
'hash': '0xdb731f3622ef37b4da8db36903de029220dba74c41185f8429f916058b86559f', |
||||||
|
'logsBloom': '0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000', |
||||||
|
'miner': '0x3333333333333333333333333333333333333333', |
||||||
|
'mixHash': '0x0000000000000000000000000000000000000000000000000000000000000000', |
||||||
|
'nonce': '0x0000000000000042', |
||||||
|
'number': '0x0', |
||||||
|
'parentHash': '0x0000000000000000000000000000000000000000000000000000000000000000', |
||||||
|
'receiptsRoot': '0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421', |
||||||
|
'sha3Uncles': '0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347', |
||||||
|
'size': '0x1f8', |
||||||
|
'stateRoot': '0xb7917653f92e62394d2207d0f39a1320ff1cb93d1cee80d3c492627e00b219ff', |
||||||
|
'timestamp': '0x0', |
||||||
|
'totalDifficulty': '0x0', |
||||||
|
'transactions': [], |
||||||
|
'transactionsRoot': '0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421', |
||||||
|
'uncles': [] |
||||||
|
} |
||||||
|
cb(null, b) |
||||||
|
} |
||||||
|
|
||||||
|
Blocks.prototype.eth_gasPrice = function (payload, cb) { |
||||||
|
cb(null, 1) |
||||||
|
} |
||||||
|
|
||||||
|
module.exports = Blocks |
@ -0,0 +1,16 @@ |
|||||||
|
var version = require('../../package.json').version |
||||||
|
|
||||||
|
var Misc = function () { |
||||||
|
} |
||||||
|
|
||||||
|
Misc.prototype.methods = function () { |
||||||
|
return { |
||||||
|
web3_clientVersion: this.web3_clientVersion.bind(this) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
Misc.prototype.web3_clientVersion = function (payload, cb) { |
||||||
|
cb(null, 'Remix Simulator/' + version) |
||||||
|
} |
||||||
|
|
||||||
|
module.exports = Misc |
@ -0,0 +1,63 @@ |
|||||||
|
var RemixLib = require('remix-lib') |
||||||
|
var executionContext = RemixLib.execution.executionContext |
||||||
|
var processTx = require('./txProcess.js') |
||||||
|
|
||||||
|
var Transactions = function (accounts) { |
||||||
|
this.accounts = accounts |
||||||
|
// TODO: fix me; this is a temporary and very hackish thing just to get the getCode working for now
|
||||||
|
this.deployedContracts = {} |
||||||
|
} |
||||||
|
|
||||||
|
Transactions.prototype.methods = function () { |
||||||
|
return { |
||||||
|
eth_sendTransaction: this.eth_sendTransaction.bind(this), |
||||||
|
eth_getTransactionReceipt: this.eth_getTransactionReceipt.bind(this), |
||||||
|
eth_getCode: this.eth_getCode.bind(this), |
||||||
|
eth_call: this.eth_call.bind(this), |
||||||
|
eth_estimateGas: this.eth_estimateGas.bind(this) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
Transactions.prototype.eth_sendTransaction = function (payload, cb) { |
||||||
|
processTx(this.accounts, payload, false, cb) |
||||||
|
} |
||||||
|
|
||||||
|
Transactions.prototype.eth_getTransactionReceipt = function (payload, cb) { |
||||||
|
const self = this |
||||||
|
executionContext.web3().eth.getTransactionReceipt(payload.params[0], (error, receipt) => { |
||||||
|
if (error) { |
||||||
|
return cb(error) |
||||||
|
} |
||||||
|
self.deployedContracts[receipt.contractAddress] = receipt.data |
||||||
|
|
||||||
|
var r = { |
||||||
|
'transactionHash': receipt.hash, |
||||||
|
'transactionIndex': '0x00', |
||||||
|
'blockHash': '0x766d18646a06cf74faeabf38597314f84a82c3851859d9da9d94fc8d037269e5', |
||||||
|
'blockNumber': '0x06', |
||||||
|
'gasUsed': '0x06345f', |
||||||
|
'cumulativeGasUsed': '0x06345f', |
||||||
|
'contractAddress': receipt.contractAddress, |
||||||
|
'logs': receipt.logs, |
||||||
|
'status': 1 |
||||||
|
} |
||||||
|
|
||||||
|
cb(null, r) |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
Transactions.prototype.eth_estimateGas = function (payload, cb) { |
||||||
|
cb(null, 3000000) |
||||||
|
} |
||||||
|
|
||||||
|
Transactions.prototype.eth_getCode = function (payload, cb) { |
||||||
|
let address = payload.params[0] |
||||||
|
|
||||||
|
cb(null, this.deployedContracts[address] || '0x') |
||||||
|
} |
||||||
|
|
||||||
|
Transactions.prototype.eth_call = function (payload, cb) { |
||||||
|
processTx(this.accounts, payload, true, cb) |
||||||
|
} |
||||||
|
|
||||||
|
module.exports = Transactions |
@ -0,0 +1,96 @@ |
|||||||
|
var RemixLib = require('remix-lib') |
||||||
|
var TxExecution = RemixLib.execution.txExecution |
||||||
|
var TxRunner = RemixLib.execution.txRunner |
||||||
|
var executionContext = RemixLib.execution.executionContext |
||||||
|
|
||||||
|
function runCall (payload, from, to, data, value, gasLimit, txRunner, callbacks, callback) { |
||||||
|
let finalCallback = function (err, result) { |
||||||
|
if (err) { |
||||||
|
return callback(err) |
||||||
|
} |
||||||
|
|
||||||
|
let toReturn = '0x' + result.result.vm.return.toString('hex') |
||||||
|
if (toReturn === '0x') { |
||||||
|
toReturn = '0x0' |
||||||
|
} |
||||||
|
return callback(null, toReturn) |
||||||
|
} |
||||||
|
|
||||||
|
TxExecution.callFunction(from, to, data, value, gasLimit, null, txRunner, callbacks, finalCallback, true) |
||||||
|
} |
||||||
|
|
||||||
|
function runTx (payload, from, to, data, value, gasLimit, txRunner, callbacks, callback) { |
||||||
|
let finalCallback = function (err, result) { |
||||||
|
if (err) { |
||||||
|
return callback(err) |
||||||
|
} |
||||||
|
callback(null, result.transactionHash) |
||||||
|
} |
||||||
|
|
||||||
|
TxExecution.callFunction(from, to, data, value, gasLimit, null, txRunner, callbacks, finalCallback, false) |
||||||
|
} |
||||||
|
|
||||||
|
function createContract (payload, from, data, value, gasLimit, txRunner, callbacks, callback) { |
||||||
|
let finalCallback = function (err, result) { |
||||||
|
if (err) { |
||||||
|
return callback(err) |
||||||
|
} |
||||||
|
callback(null, result.transactionHash) |
||||||
|
} |
||||||
|
|
||||||
|
TxExecution.createContract(from, data, value, gasLimit, txRunner, callbacks, finalCallback) |
||||||
|
} |
||||||
|
|
||||||
|
function processTx (accounts, payload, isCall, callback) { |
||||||
|
let api = { |
||||||
|
logMessage: (msg) => { |
||||||
|
}, |
||||||
|
logHtmlMessage: (msg) => { |
||||||
|
}, |
||||||
|
config: { |
||||||
|
getUnpersistedProperty: (key) => { |
||||||
|
return true |
||||||
|
}, |
||||||
|
get: () => { |
||||||
|
return true |
||||||
|
} |
||||||
|
}, |
||||||
|
detectNetwork: (cb) => { |
||||||
|
cb() |
||||||
|
}, |
||||||
|
personalMode: () => { |
||||||
|
return false |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
executionContext.init(api.config) |
||||||
|
|
||||||
|
let txRunner = new TxRunner(accounts, api) |
||||||
|
let { from, to, data, value, gas } = payload.params[0] |
||||||
|
gas = gas || 3000000 |
||||||
|
|
||||||
|
let callbacks = { |
||||||
|
confirmationCb: (network, tx, gasEstimation, continueTxExecution, cancelCb) => { |
||||||
|
continueTxExecution(null) |
||||||
|
}, |
||||||
|
gasEstimationForceSend: (error, continueTxExecution, cancelCb) => { |
||||||
|
if (error) { |
||||||
|
continueTxExecution(error) |
||||||
|
} |
||||||
|
continueTxExecution() |
||||||
|
}, |
||||||
|
promptCb: (okCb, cancelCb) => { |
||||||
|
okCb() |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (isCall) { |
||||||
|
runCall(payload, from, to, data, value, gas, txRunner, callbacks, callback) |
||||||
|
} else if (to) { |
||||||
|
runTx(payload, from, to, data, value, gas, txRunner, callbacks, callback) |
||||||
|
} else { |
||||||
|
createContract(payload, from, data, value, gas, txRunner, callbacks, callback) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
module.exports = processTx |
@ -0,0 +1,15 @@ |
|||||||
|
|
||||||
|
var Whisper = function () { |
||||||
|
} |
||||||
|
|
||||||
|
Whisper.prototype.methods = function () { |
||||||
|
return { |
||||||
|
shh_version: this.shh_version.bind(this) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
Whisper.prototype.shh_version = function (payload, cb) { |
||||||
|
cb(null, 5) |
||||||
|
} |
||||||
|
|
||||||
|
module.exports = Whisper |
@ -0,0 +1,41 @@ |
|||||||
|
const log = require('./utils/logs.js') |
||||||
|
const merge = require('merge') |
||||||
|
|
||||||
|
const Accounts = require('./methods/accounts.js') |
||||||
|
const Blocks = require('./methods/blocks.js') |
||||||
|
const Misc = require('./methods/misc.js') |
||||||
|
const Transactions = require('./methods/transactions.js') |
||||||
|
const Whisper = require('./methods/whisper.js') |
||||||
|
|
||||||
|
var Provider = function () { |
||||||
|
this.Accounts = new Accounts() |
||||||
|
|
||||||
|
this.methods = {} |
||||||
|
this.methods = merge(this.methods, this.Accounts.methods()) |
||||||
|
this.methods = merge(this.methods, (new Blocks()).methods()) |
||||||
|
this.methods = merge(this.methods, (new Misc()).methods()) |
||||||
|
this.methods = merge(this.methods, (new Transactions(this.Accounts.accounts)).methods()) |
||||||
|
this.methods = merge(this.methods, (new Whisper()).methods()) |
||||||
|
} |
||||||
|
|
||||||
|
Provider.prototype.sendAsync = function (payload, callback) { |
||||||
|
log.info('payload method is ', payload.method) |
||||||
|
|
||||||
|
let method = this.methods[payload.method] |
||||||
|
if (method) { |
||||||
|
return method.call(method, payload, (err, result) => { |
||||||
|
if (err) { |
||||||
|
return callback(err) |
||||||
|
} |
||||||
|
let response = {'id': payload.id, 'jsonrpc': '2.0', 'result': result} |
||||||
|
callback(null, response) |
||||||
|
}) |
||||||
|
} |
||||||
|
callback(new Error('unknown method ' + payload.method)) |
||||||
|
} |
||||||
|
|
||||||
|
Provider.prototype.isConnected = function () { |
||||||
|
return true |
||||||
|
} |
||||||
|
|
||||||
|
module.exports = Provider |
@ -0,0 +1,25 @@ |
|||||||
|
const express = require('express') |
||||||
|
const bodyParser = require('body-parser') |
||||||
|
const app = express() |
||||||
|
const Provider = require('./provider') |
||||||
|
const log = require('./utils/logs.js') |
||||||
|
|
||||||
|
var provider = new Provider() |
||||||
|
|
||||||
|
app.use(bodyParser.urlencoded({extended: true})) |
||||||
|
app.use(bodyParser.json()) |
||||||
|
|
||||||
|
app.get('/', (req, res) => { |
||||||
|
res.send('Welcome to remix-simulator') |
||||||
|
}) |
||||||
|
|
||||||
|
app.use(function (req, res) { |
||||||
|
provider.sendAsync(req.body, (err, jsonResponse) => { |
||||||
|
if (err) { |
||||||
|
res.send({error: err}) |
||||||
|
} |
||||||
|
res.send(jsonResponse) |
||||||
|
}) |
||||||
|
}) |
||||||
|
|
||||||
|
app.listen(8545, () => log('Remix Simulator listening on port 8545!')) |
@ -0,0 +1,81 @@ |
|||||||
|
'use strict'; |
||||||
|
|
||||||
|
var gray = require('ansi-gray'); |
||||||
|
var timestamp = require('time-stamp'); |
||||||
|
var supportsColor = require('color-support'); |
||||||
|
|
||||||
|
function hasFlag(flag) { |
||||||
|
return ((typeof(process) !== 'undefined') && (process.argv.indexOf('--' + flag) !== -1)); |
||||||
|
} |
||||||
|
|
||||||
|
function addColor(str) { |
||||||
|
if (hasFlag('no-color')) { |
||||||
|
return str; |
||||||
|
} |
||||||
|
|
||||||
|
if (hasFlag('color')) { |
||||||
|
return gray(str); |
||||||
|
} |
||||||
|
|
||||||
|
if (supportsColor()) { |
||||||
|
return gray(str); |
||||||
|
} |
||||||
|
|
||||||
|
return str; |
||||||
|
} |
||||||
|
|
||||||
|
let logger = { |
||||||
|
stdout: function(arg) { |
||||||
|
if (typeof(process) === 'undefined' || !process.stdout) return; |
||||||
|
process.stdout.write(arg); |
||||||
|
}, |
||||||
|
stderr: function(arg) { |
||||||
|
if (typeof(process) === 'undefined' || process.stderr) return; |
||||||
|
process.stderr.write(arg); |
||||||
|
}, |
||||||
|
}; |
||||||
|
|
||||||
|
function getTimestamp(){ |
||||||
|
return '['+addColor(timestamp('HH:mm:ss'))+']'; |
||||||
|
} |
||||||
|
|
||||||
|
function log(){ |
||||||
|
var time = getTimestamp(); |
||||||
|
logger.stdout(time + ' '); |
||||||
|
console.log.apply(console, arguments); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
function info(){ |
||||||
|
var time = getTimestamp(); |
||||||
|
logger.stdout(time + ' '); |
||||||
|
console.info.apply(console, arguments); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
function dir(){ |
||||||
|
var time = getTimestamp(); |
||||||
|
logger.stdout(time + ' '); |
||||||
|
console.dir.apply(console, arguments); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
function warn(){ |
||||||
|
var time = getTimestamp(); |
||||||
|
logger.stderr(time + ' '); |
||||||
|
console.warn.apply(console, arguments); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
function error(){ |
||||||
|
var time = getTimestamp(); |
||||||
|
logger.stderr(time + ' '); |
||||||
|
console.error.apply(console, arguments); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
module.exports = log; |
||||||
|
module.exports.info = info; |
||||||
|
module.exports.dir = dir; |
||||||
|
module.exports.warn = warn; |
||||||
|
module.exports.error = error; |
Loading…
Reference in new issue