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