diff --git a/packages/core/core/constants.json b/packages/core/core/constants.json index fcb9a659a..d2afd9ebe 100644 --- a/packages/core/core/constants.json +++ b/packages/core/core/constants.json @@ -66,7 +66,9 @@ "eth_getTransactionReceipt": "eth_getTransactionReceipt", "eth_call": "eth_call", "eth_accounts": "eth_accounts", - "personal_listAccounts": "personal_listAccounts" + "eth_signTypedData": "eth_signTypedData", + "personal_listAccounts": "personal_listAccounts", + "personal_newAccount": "personal_newAccount" } }, "storage": { @@ -106,4 +108,4 @@ "environments": { "development": "development" } -} +} \ No newline at end of file diff --git a/packages/core/core/index.d.ts b/packages/core/core/index.d.ts deleted file mode 100644 index 235f4e7b4..000000000 --- a/packages/core/core/index.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -declare module "embark-core" { - export class IPC { - constructor(options: {ipcRole: string}); - } -} diff --git a/packages/core/core/src/config.ts b/packages/core/core/src/config.ts index b8afa0775..2c4ad8ff8 100644 --- a/packages/core/core/src/config.ts +++ b/packages/core/core/src/config.ts @@ -42,7 +42,7 @@ export class Config { pipelineConfig: any = {}; - namesystemConfig: any = {}; + namesystemConfig: any = {}; communicationConfig: any = {}; @@ -88,7 +88,7 @@ export class Config { constructor(options) { this.env = options.env || 'default'; - this.webServerConfig = options.webServerConfig; + this.webServerConfig = options.webServerConfig; this.configDir = options.configDir || DEFAULT_CONFIG_PATH; this.chainsFile = options.chainsFile; this.plugins = options.plugins; @@ -132,8 +132,8 @@ export class Config { // TODO: refactor this so reading the file can be done with a normal resolver or something that takes advantage of the plugin api this.events.setCommandHandler("config:contractsFiles:add", (filename, resolver) => { - resolver = resolver || function(callback) { callback(fs.readFileSync(filename).toString()); }; - this.contractsFiles.push(new File({path: filename, originalPath: filename, type: Types.custom, resolver})); + resolver = resolver || function (callback) { callback(fs.readFileSync(filename).toString()); }; + this.contractsFiles.push(new File({ path: filename, originalPath: filename, type: Types.custom, resolver })); }); this.events.setCommandHandler("config:contractsFiles:reset", (cb) => { @@ -179,7 +179,6 @@ export class Config { env: this.env, version: this.version }); - this.plugins.loadPlugins(); this.loadEmbarkConfigFile(); this.loadBlockchainConfigFile(); @@ -208,6 +207,7 @@ export class Config { this.loadAssetFiles(); this.loadContractsConfigFile(); this.loadExternalContractsFiles(); + this.loadPluginContractFiles(); this._updateBlockchainCors(); } @@ -302,7 +302,7 @@ export class Config { if (env) { if (env === 'test' && !configObject[env]) { // Disabled all configs in tests as they are opt in - return Object.assign({}, defaultConfig.default, {enabled: false}); + return Object.assign({}, defaultConfig.default, { enabled: false }); } return recursiveMerge(configObject.default || {}, configObject[env]); } else if (env !== false) { @@ -343,8 +343,8 @@ export class Config { switch (envConfig.miningMode) { case 'dev': envConfig.isDev = true; break; case 'auto': envConfig.isDev = false; envConfig.mineWhenNeeded = true; break; - case 'always': envConfig.isDev = false; envConfig.mineWhenNeeded = false; envConfig.mine = true; break; - case 'off': envConfig.isDev = false; envConfig.mineWhenNeeded = false; envConfig.mine = false; break; + case 'always': envConfig.isDev = false; envConfig.mineWhenNeeded = false; envConfig.mine = true; break; + case 'off': envConfig.isDev = false; envConfig.mineWhenNeeded = false; envConfig.mine = false; break; default: envConfig.isDev = false; } if (envConfig.cors) { @@ -395,10 +395,10 @@ export class Config { port: this.blockchainConfig.wsPort, type: 'ws' } : { - host: this.blockchainConfig.rpcHost, - port: this.blockchainConfig.rpcPort, - type: 'rpc' - }; + host: this.blockchainConfig.rpcHost, + port: this.blockchainConfig.rpcPort, + type: 'rpc' + }; this.blockchainConfig.endpoint = buildUrlFromConfig(urlConfig); this.blockchainConfig.isAutoEndpoint = true; } @@ -448,7 +448,7 @@ export class Config { let configObject = getContractDefaults(this.embarkConfig.versions); const contractsConfigs = this.plugins.getPluginsProperty('contractsConfig', 'contractsConfigs'); - contractsConfigs.forEach(function(pluginConfig) { + contractsConfigs.forEach(function (pluginConfig) { configObject = recursiveMerge(configObject, pluginConfig); }); @@ -485,7 +485,7 @@ export class Config { const contracts = this.contractsConfig.contracts; const storageConfig = this.storageConfig; if (storageConfig && storageConfig.upload && storageConfig.upload.getUrl) { - this.providerUrl = storageConfig.upload.getUrl; + this.providerUrl = storageConfig.upload.getUrl; } for (const contractName in contracts) { const contract = contracts[contractName]; @@ -534,11 +534,11 @@ export class Config { upload: { provider: "ipfs", protocol: "http", - host : defaultHost, + host: defaultHost, port: 5001, getUrl: "http://localhost:8080/ipfs/" }, - dappConnection: [{provider: "ipfs", host: "localhost", port: 5001, getUrl: "http://localhost:8080/ipfs/"}] + dappConnection: [{ provider: "ipfs", host: "localhost", port: 5001, getUrl: "http://localhost:8080/ipfs/" }] } }; @@ -610,7 +610,7 @@ export class Config { } } if (configFilePath === false) { - this.webServerConfig = {enabled: false}; + this.webServerConfig = { enabled: false }; return; } if (this.webServerConfig) { @@ -649,7 +649,7 @@ export class Config { }); this.contractDirectories.push(constants.httpContractsDirectory); - this.buildDir = this.embarkConfig.buildDir; + this.buildDir = this.embarkConfig.buildDir; this.configDir = this.embarkConfig.config; } @@ -698,11 +698,11 @@ export class Config { const readFiles: File[] = []; const storageConfig = self.storageConfig; - originalFiles.filter(function(file) { + originalFiles.filter(function (file) { return (file[0] === '$' || file.indexOf('.') >= 0); - }).filter(function(file) { + }).filter(function (file) { const basedir = findMatchingExpression(file, files); - readFiles.push(new File({path: file, originalPath: file, type: Types.dappFile, basedir, storageConfig})); + readFiles.push(new File({ path: file, originalPath: file, type: Types.dappFile, basedir, storageConfig })); }); const filesFromPlugins: File[] = []; @@ -718,7 +718,7 @@ export class Config { self.logger.error(err.message); } }); - filesFromPlugins.filter(function(file) { + filesFromPlugins.filter(function (file) { if ((file.intendedPath && fileMatchesPattern(files, file.intendedPath)) || fileMatchesPattern(files, file.file)) { readFiles.push(file); } @@ -735,7 +735,8 @@ export class Config { contractsPlugins.forEach((plugin: Plugin) => { plugin.contractsFiles.forEach(file => { const filename = file.replace('./', ''); - self.contractsFiles.push(new File({ path: filename, originalPath: path.join(plugin.pluginPath, filename), pluginPath: plugin.pluginPath, type: Types.custom, storageConfig, + self.contractsFiles.push(new File({ + path: filename, originalPath: path.join(plugin.pluginPath, filename), pluginPath: plugin.pluginPath, type: Types.custom, storageConfig, resolver(callback) { callback(plugin.loadPluginFile(file)); } diff --git a/packages/core/core/src/engine.ts b/packages/core/core/src/engine.ts index dc4a7ff93..5b7ce758a 100644 --- a/packages/core/core/src/engine.ts +++ b/packages/core/core/src/engine.ts @@ -78,13 +78,13 @@ export class Engine { } init(_options, callback) { - callback = callback || function() {}; + callback = callback || function () { }; const options = _options || {}; this.events = options.events || this.events || new Events(); - this.logger = options.logger || new Logger({context: this.context, logLevel: options.logLevel || this.logLevel || 'info', events: this.events, logFile: this.logFile}); - this.config = new Config({env: this.env, logger: this.logger, events: this.events, context: this.context, webServerConfig: this.webServerConfig, version: this.version, package: this.package}); - this.config.loadConfigFiles({embarkConfig: this.embarkConfig, interceptLogs: this.interceptLogs}); + this.logger = options.logger || new Logger({ context: this.context, logLevel: options.logLevel || this.logLevel || 'info', events: this.events, logFile: this.logFile }); + this.config = new Config({ env: this.env, logger: this.logger, events: this.events, context: this.context, webServerConfig: this.webServerConfig, version: this.version, package: this.package }); + this.config.loadConfigFiles({ embarkConfig: this.embarkConfig, interceptLogs: this.interceptLogs }); this.plugins = this.config.plugins; this.isDev = this.config && this.config.blockchainConfig && (this.config.blockchainConfig.isDev || this.config.blockchainConfig.default); @@ -92,7 +92,7 @@ export class Engine { interceptLogs(console, this.logger); } - this.ipc = new IPC({logger: this.logger, ipcRole: this.ipcRole}); + this.ipc = new IPC({ logger: this.logger, ipcRole: this.ipcRole }); if (this.ipc.isClient()) { return this.ipc.connect((_err) => { callback(); @@ -105,6 +105,13 @@ export class Engine { callback(); } + loadDappPlugins() { + if (this.config) { + this.config.plugins.loadPlugins(); + this.config.reloadConfig(); + } + } + startEngine(cb) { if (this.plugins) { this.plugins.emitAndRunActionsForEvent("embark:engine:started", {}, (err) => { @@ -189,26 +196,26 @@ export class Engine { plugins: this.plugins }); - this.servicesMonitor = new ServicesMonitor({events: this.events, logger: this.logger, plugins: this.plugins}); + this.servicesMonitor = new ServicesMonitor({ events: this.events, logger: this.logger, plugins: this.plugins }); if (this.servicesMonitor) { this.servicesMonitor.addCheck('Embark', (cb) => { - return cb({name: 'Embark ' + this.version, status: 'on'}); + return cb({ name: 'Embark ' + this.version, status: 'on' }); }, 0); if (this.plugins) { const plugin = this.plugins.createPlugin('coreservicesplugin', {}); - plugin.registerActionForEvent("embark:engine:started", (_params, cb) => { + plugin.registerActionForEvent("embark:engine:started", undefined, (_params, cb) => { this.servicesMonitor && this.servicesMonitor.startMonitor(); cb(); }); } } - this.registerModulePackage('embark-code-runner', {ipc: this.ipc}); + this.registerModulePackage('embark-code-runner', { ipc: this.ipc }); // TODO: we shouldn't need useDashboard - this.registerModulePackage('embark-library-manager', {useDashboard: this.useDashboard}); + this.registerModulePackage('embark-library-manager', { useDashboard: this.useDashboard }); } consoleComponents() { @@ -233,18 +240,18 @@ export class Engine { stackComponents(options) { this.registerModulePackage('embark-pipeline', { plugins: this.plugins }); this.registerModulePackage('embark-blockchain', { plugins: this.plugins }); - this.registerModulePackage('embark-proxy', {plugins: this.plugins}); + this.registerModulePackage('embark-proxy', { plugins: this.plugins }); // TODO: coverage param should be part of the request compilation command, not an option here // some other params in the options might not longer be relevant, in fact we probably don't need options anymore - this.registerModulePackage('embark-compiler', {plugins: this.plugins, isCoverage: options.isCoverage}); - this.registerModulePackage('embark-contracts-manager', {plugins: this.plugins, compileOnceOnly: options.compileOnceOnly}); - this.registerModulePackage('embark-deployment', {plugins: this.plugins, onlyCompile: options.onlyCompile}); + this.registerModulePackage('embark-compiler', { plugins: this.plugins, isCoverage: options.isCoverage }); + this.registerModulePackage('embark-contracts-manager', { plugins: this.plugins, compileOnceOnly: options.compileOnceOnly }); + this.registerModulePackage('embark-deployment', { plugins: this.plugins, onlyCompile: options.onlyCompile }); this.registerModulePackage('embark-blockchain-client'); this.registerModulePackage('embark-storage'); this.registerModulePackage('embark-communication'); this.registerModulePackage('embark-namesystem'); this.registerModulePackage('embark-process-logs-api-manager'); - this.registerModulePackage('embark-embarkjs', {plugins: this.plugins}); + this.registerModulePackage('embark-embarkjs', { plugins: this.plugins }); } blockchainComponents() { @@ -266,15 +273,15 @@ export class Engine { } testComponents(options) { - this.registerModulePackage('embark-test-runner', {plugins: this.plugins}); - this.registerModulePackage('embark-coverage', {plugins: this.plugins, coverage: options.coverage}); - this.registerModulePackage('embark-solidity-tests', {plugins: this.plugins, coverage: options.coverage}); - this.registerModulePackage('embark-mocha-tests', {plugins: this.plugins, coverage: options.coverage}); + this.registerModulePackage('embark-test-runner', { plugins: this.plugins }); + this.registerModulePackage('embark-coverage', { plugins: this.plugins, coverage: options.coverage }); + this.registerModulePackage('embark-solidity-tests', { plugins: this.plugins, coverage: options.coverage }); + this.registerModulePackage('embark-mocha-tests', { plugins: this.plugins, coverage: options.coverage }); } compilerComponents(_options) { // TODO: should be moved (they are plugins) - this.registerModulePackage('embark-solidity', {ipc: this.ipc, useDashboard: this.useDashboard}); + this.registerModulePackage('embark-solidity', { ipc: this.ipc, useDashboard: this.useDashboard }); this.registerModulePackage('embark-vyper'); } @@ -283,7 +290,8 @@ export class Engine { this.registerModulePackage('embark-ethereum-blockchain-client'); this.registerModulePackage('embark-web3'); this.registerModulePackage('embark-accounts-manager'); - this.registerModulePackage('embark-specialconfigs', {plugins: this.plugins}); + this.registerModulePackage('embark-rpc-manager'); + this.registerModulePackage('embark-specialconfigs', { plugins: this.plugins }); this.registerModulePackage('embark-transaction-logger'); this.registerModulePackage('embark-transaction-tracker'); this.registerModulePackage('embark-profiler'); @@ -303,10 +311,10 @@ export class Engine { } cockpitModules() { - this.registerModulePackage('embark-authenticator', {singleUseAuthToken: this.singleUseAuthToken}); - this.registerModulePackage('embark-api', {plugins: this.plugins}); + this.registerModulePackage('embark-authenticator', { singleUseAuthToken: this.singleUseAuthToken }); + this.registerModulePackage('embark-api', { plugins: this.plugins }); // Register logs for the cockpit console - this.events.request('process:logs:register', {processName: EMBARK_PROCESS_NAME, eventName: "log", silent: false, alwaysAlreadyLogged: true}); + this.events.request('process:logs:register', { processName: EMBARK_PROCESS_NAME, eventName: "log", silent: false, alwaysAlreadyLogged: true }); } } @@ -314,23 +322,23 @@ function interceptLogs(consoleContext, logger) { const context: any = {}; context.console = consoleContext; - context.console.log = function() { + context.console.log = function () { logger.info(normalizeInput(arguments)); }; - context.console.warn = function() { + context.console.warn = function () { logger.warn(normalizeInput(arguments)); }; - context.console.info = function() { + context.console.info = function () { logger.info(normalizeInput(arguments)); }; - context.console.debug = function() { + context.console.debug = function () { // TODO: ue JSON.stringify logger.debug(normalizeInput(arguments)); }; - context.console.trace = function() { + context.console.trace = function () { logger.trace(normalizeInput(arguments)); }; - context.console.dir = function() { + context.console.dir = function () { logger.dir(normalizeInput(arguments)); }; } diff --git a/packages/core/core/src/plugin.ts b/packages/core/core/src/plugin.ts index bcda6474d..fde7d36af 100644 --- a/packages/core/core/src/plugin.ts +++ b/packages/core/core/src/plugin.ts @@ -125,6 +125,10 @@ export class Plugin { if (!Array.isArray(this.acceptedContext)) { this.acceptedContext = [this.acceptedContext]; } + this.registerActionForEvent("tests:config:updated", { priority: 30 }, async ({ accounts }, cb) => { + this.config.blockchainConfig.accounts = accounts; + cb(null, null); + }); } _log(type) { @@ -157,8 +161,8 @@ export class Plugin { } loadPlugin() { - if (!this.isContextValid()) { - this.logger.warn(__('Plugin {{name}} can only be loaded in the context of "{{contexts}}"', {name: this.name, contexts: this.acceptedContext.join(', ')})); + if (!this.isContextValid()) { + this.logger.warn(__('Plugin {{name}} can only be loaded in the context of "{{contexts}}"', { name: this.name, contexts: this.acceptedContext.join(', ') })); return false; } this.loaded = true; @@ -217,12 +221,12 @@ export class Plugin { registerPipeline(matcthingFiles, cb) { // TODO: generate error for more than one pipeline per plugin - this.pipeline.push({matcthingFiles, cb}); + this.pipeline.push({ matcthingFiles, cb }); this.addPluginType('pipeline'); } registerDappGenerator(framework, cb) { - this.dappGenerators.push({framework, cb}); + this.dappGenerators.push({ framework, cb }); this.pluginTypes.push('dappGenerator'); } @@ -231,7 +235,7 @@ export class Plugin { } addFileToPipeline(file, intendedPath, options) { - this.pipelineFiles.push({file, intendedPath, options}); + this.pipelineFiles.push({ file, intendedPath, options }); this.addPluginType('pipelineFiles'); } @@ -254,7 +258,7 @@ export class Plugin { // TODO: this only works for services done on startup registerServiceCheck(checkName, checkFn, time) { - this.serviceChecks.push({checkName, checkFn, time}); + this.serviceChecks.push({ checkName, checkFn, time }); this.addPluginType('serviceChecks'); } @@ -268,13 +272,13 @@ export class Plugin { } generateProvider(args) { - return this.clientWeb3Providers.map(function(cb) { + return this.clientWeb3Providers.map(function (cb) { return cb.call(this, args); }).join("\n"); } generateContracts(args) { - return this.contractsGenerators.map(function(cb) { + return this.contractsGenerators.map(function (cb) { return cb.call(this, args); }).join("\n"); } @@ -285,12 +289,12 @@ export class Plugin { } registerCompiler(extension, cb) { - this.compilers.push({extension, cb}); + this.compilers.push({ extension, cb }); this.addPluginType('compilers'); } registerUploadCommand(cmd, cb) { - this.uploadCmds.push({cmd, cb}); + this.uploadCmds.push({ cmd, cb }); this.addPluginType('uploadCmds'); } @@ -335,26 +339,26 @@ export class Plugin { if (!this.eventActions[eventName]) { this.eventActions[eventName] = []; } - this.eventActions[eventName].push({action: cb, options: Object.assign({priority: DEFAULT_ACTION_PRIORITY}, options)}); + this.eventActions[eventName].push({ action: cb, options: Object.assign({ priority: DEFAULT_ACTION_PRIORITY }, options) }); this.addPluginType('eventActions'); } registerAPICall(method, endpoint, cb) { - this.apiCalls.push({method, endpoint, cb}); + this.apiCalls.push({ method, endpoint, cb }); this.addPluginType('apiCalls'); - this.events.emit('plugins:register:api', {method, endpoint, cb}); + this.events.emit('plugins:register:api', { method, endpoint, cb }); } runFilePipeline() { return this.pipelineFiles.map(file => { - let obj: any = {}; - obj.filename = file.file.replace('./', ''); - obj.content = this.loadPluginFile(file.file).toString(); - obj.intendedPath = file.intendedPath; - obj.options = file.options; - obj.path = this.pathToFile(obj.filename); + let obj: any = {}; + obj.filename = file.file.replace('./', ''); + obj.content = this.loadPluginFile(file.file).toString(); + obj.intendedPath = file.intendedPath; + obj.options = file.options; + obj.path = this.pathToFile(obj.filename); - return obj; + return obj; }); } diff --git a/packages/core/typings/index.d.ts b/packages/core/typings/index.d.ts index e918b9020..ee62d5193 100644 --- a/packages/core/typings/index.d.ts +++ b/packages/core/typings/index.d.ts @@ -1,3 +1,4 @@ +import './src/omg-js-util'; import './src/prettier-plugin-solidity'; import './src/remix-debug-debugtest'; diff --git a/packages/core/typings/src/omg-js-util/index.d.ts b/packages/core/typings/src/omg-js-util/index.d.ts new file mode 100644 index 000000000..d01b2140e --- /dev/null +++ b/packages/core/typings/src/omg-js-util/index.d.ts @@ -0,0 +1 @@ +declare module '@omisego/omg-js-util'; diff --git a/packages/core/utils/src/accountParser.js b/packages/core/utils/src/accountParser.js index 0413a5abd..4d44c8f66 100644 --- a/packages/core/utils/src/accountParser.js +++ b/packages/core/utils/src/accountParser.js @@ -12,6 +12,9 @@ const ERROR_ACCOUNT = 'ERROR_ACCOUNT'; export default class AccountParser { static parseAccountsConfig(accountsConfig, web3, dappPath, logger, nodeAccounts) { let accounts = []; + if (!(accountsConfig && accountsConfig.length)) { + return nodeAccounts; + } if (accountsConfig && accountsConfig.length) { accountsConfig.forEach(accountConfig => { let account = AccountParser.getAccount(accountConfig, web3, dappPath, logger, nodeAccounts); diff --git a/packages/embark/package.json b/packages/embark/package.json index af28451f9..86c059135 100644 --- a/packages/embark/package.json +++ b/packages/embark/package.json @@ -202,4 +202,4 @@ "node": ">=10.17.0 <12.0.0" } } -} +} \ No newline at end of file diff --git a/packages/embark/src/cmd/cmd_controller.js b/packages/embark/src/cmd/cmd_controller.js index 3f51b36a7..c61e7f1ee 100644 --- a/packages/embark/src/cmd/cmd_controller.js +++ b/packages/embark/src/cmd/cmd_controller.js @@ -4,7 +4,7 @@ import { dappPath, embarkPath, joinPath, setUpEnv } from 'embark-utils'; import { Logger } from 'embark-logger'; let async = require('async'); const constants = require('embark-core/constants'); -const {reset: embarkReset, paths: defaultResetPaths} = require('embark-reset'); +const { reset: embarkReset, paths: defaultResetPaths } = require('embark-reset'); const cloneDeep = require('clone-deep'); setUpEnv(joinPath(__dirname, '../../')); @@ -26,10 +26,10 @@ class EmbarkController { initConfig(env, options) { this.events = new Events(); - this.logger = new Logger({logLevel: Logger.logLevels.debug, events: this.events, context: this.context}); + this.logger = new Logger({ logLevel: Logger.logLevels.debug, events: this.events, context: this.context }); this.logger.info('foo'); - this.config = new Config({env: env, logger: this.logger, events: this.events, context: this.context, version: this.version}); + this.config = new Config({ env: env, logger: this.logger, events: this.events, context: this.context, version: this.version }); this.config.loadConfigFiles(options); this.plugins = this.config.plugins; } @@ -62,7 +62,7 @@ class EmbarkController { engine.startEngine(async () => { try { - const alreadyStarted = await engine.events.request2("blockchain:node:start", Object.assign(engine.config.blockchainConfig, {isStandalone: true})); + const alreadyStarted = await engine.events.request2("blockchain:node:start", Object.assign(engine.config.blockchainConfig, { isStandalone: true })); if (alreadyStarted) { engine.logger.warn(__('Blockchain process already started. No need to run `embark blockchain`')); process.exit(0); @@ -147,10 +147,6 @@ class EmbarkController { }); }, function (callback) { - const pluginList = engine.plugins.listPlugins(); - if (pluginList.length > 0) { - engine.logger.info(__("loaded plugins") + ": " + pluginList.join(", ")); - } engine.registerModuleGroup("coreComponents"); engine.registerModuleGroup("stackComponents"); @@ -167,7 +163,14 @@ class EmbarkController { engine.registerModuleGroup("filewatcher"); engine.registerModuleGroup("storage"); engine.registerModuleGroup("cockpit"); - engine.registerModulePackage('embark-deploy-tracker', {plugins: engine.plugins}); + engine.registerModulePackage('embark-deploy-tracker', { plugins: engine.plugins }); + + // load custom plugins + engine.loadDappPlugins(); + let pluginList = engine.plugins.listPlugins(); + if (pluginList.length > 0) { + engine.logger.info(__("loaded plugins") + ": " + pluginList.join(", ")); + } const plugin = engine.plugins.createPlugin('cmdcontrollerplugin', {}); plugin.registerActionForEvent("embark:engine:started", async (_params, cb) => { @@ -274,7 +277,7 @@ class EmbarkController { engine.registerModuleGroup("pipeline"); engine.registerModuleGroup("communication"); engine.registerModuleGroup("namesystem"); - engine.registerModulePackage('embark-deploy-tracker', {plugins: engine.plugins}); + engine.registerModulePackage('embark-deploy-tracker', { plugins: engine.plugins }); engine.registerModuleGroup("blockchain"); @@ -353,7 +356,7 @@ class EmbarkController { package: pkg }); - const isSecondaryProcess = (engine) => {return engine.ipc.connected && engine.ipc.isClient();}; + const isSecondaryProcess = (engine) => { return engine.ipc.connected && engine.ipc.isClient(); }; async.waterfall([ callback => { @@ -382,7 +385,7 @@ class EmbarkController { if (!isSecondaryProcess(engine)) { engine.registerModuleGroup("cockpit"); } - engine.registerModulePackage('embark-deploy-tracker', {plugins: engine.plugins}); + engine.registerModulePackage('embark-deploy-tracker', { plugins: engine.plugins }); callback(); }, @@ -525,7 +528,7 @@ class EmbarkController { return path.charAt(path.length - 1) === '/' ? path.substr(0, path.length - 1) : path; })) ]; - await embarkReset({removePaths}); + await embarkReset({ removePaths }); } ejectWebpack() { @@ -587,7 +590,7 @@ class EmbarkController { engine.startService("web3"); engine.startService("processManager"); engine.startService("codeRunner"); - engine.startService("deployment", {onlyCompile: true}); + engine.startService("deployment", { onlyCompile: true }); callback(); }, @@ -607,7 +610,7 @@ class EmbarkController { engine.logger.error(err.message || err); } engine.logger.info(__("finished generating the UI").underline); - engine.logger.info(__("To see the result, execute {{cmd}} and go to /{{contract}}.html", {cmd: 'embark run'.underline, contract: options.contract})); + engine.logger.info(__("To see the result, execute {{cmd}} and go to /{{contract}}.html", { cmd: 'embark run'.underline, contract: options.contract })); process.exit(err ? 1 : 0); }); @@ -668,7 +671,7 @@ class EmbarkController { engine.registerModuleGroup("webserver"); engine.registerModuleGroup("filewatcher"); engine.registerModuleGroup("storage"); - engine.registerModulePackage('embark-deploy-tracker', {plugins: engine.plugins}); + engine.registerModulePackage('embark-deploy-tracker', { plugins: engine.plugins }); const plugin = engine.plugins.createPlugin('cmdcontrollerplugin', {}); plugin.registerActionForEvent("embark:engine:started", async (_params, cb) => { @@ -733,12 +736,7 @@ class EmbarkController { function initEngine(next) { engine.init({}, next); }, - function loadPlugins(next) { - let pluginList = engine.plugins.listPlugins(); - if (pluginList.length > 0) { - engine.logger.info(__("loaded plugins") + ": " + pluginList.join(", ")); - } - + function loadModules(next) { engine.registerModuleGroup("coreComponents"); engine.registerModuleGroup("stackComponents"); @@ -747,11 +745,23 @@ class EmbarkController { engine.registerModuleGroup("contracts"); engine.registerModuleGroup("pipeline"); engine.registerModuleGroup("tests", options); - engine.registerModulePackage('embark-deploy-tracker', {plugins: engine.plugins, trackContracts: false}); + engine.registerModulePackage('embark-deploy-tracker', { plugins: engine.plugins, trackContracts: false }); engine.registerModuleGroup("namesystem"); engine.registerModuleGroup("storage"); engine.registerModuleGroup("communication"); + next(); + }, + function loadDappPlugins(next) { + // load custom plugins + engine.loadDappPlugins(); + let pluginList = engine.plugins.listPlugins(); + if (pluginList.length > 0) { + engine.logger.info(__("loaded plugins") + ": " + pluginList.join(", ")); + } + next(); + }, + function startEngine(next) { engine.startEngine(next); }, function startNodes(next) { @@ -767,7 +777,7 @@ class EmbarkController { engine.events.request2('tests:run', options, next); } ], (err, passes, fails) => { - if(err) { + if (err) { engine.logger.error(`Error occurred while running tests: ${err.message || err}`); } @@ -780,14 +790,14 @@ module.exports = EmbarkController; async function compileAndDeploySmartContracts(engine) { try { - let contractsFiles = await engine.events.request2("config:contractsFiles"); - let compiledContracts = await engine.events.request2("compiler:contracts:compile", contractsFiles); - let _contractsConfig = await engine.events.request2("config:contractsConfig"); - let contractsConfig = cloneDeep(_contractsConfig); - let [contractsList, contractDependencies] = await engine.events.request2("contracts:build", contractsConfig, compiledContracts); - await engine.events.request2("deployment:contracts:deploy", contractsList, contractDependencies); - await engine.events.request2('pipeline:generateAll'); - engine.events.emit('outputDone'); + let contractsFiles = await engine.events.request2("config:contractsFiles"); + let compiledContracts = await engine.events.request2("compiler:contracts:compile", contractsFiles); + let _contractsConfig = await engine.events.request2("config:contractsConfig"); + let contractsConfig = cloneDeep(_contractsConfig); + let [contractsList, contractDependencies] = await engine.events.request2("contracts:build", contractsConfig, compiledContracts); + await engine.events.request2("deployment:contracts:deploy", contractsList, contractDependencies); + await engine.events.request2('pipeline:generateAll'); + engine.events.emit('outputDone'); } catch (e) { console.log(e); } @@ -809,7 +819,7 @@ async function setupCargoAndWatcher(engine) { }); let fileTimeout; - engine.events.on('file-event', ({fileType, _path}) => { + engine.events.on('file-event', ({ fileType, _path }) => { clearTimeout(fileTimeout); // Throttle file changes so we re-write only once for all files fileTimeout = setTimeout(async () => { diff --git a/packages/embark/src/test/config.js b/packages/embark/src/test/config.js index dd521b7c1..118b01ed2 100644 --- a/packages/embark/src/test/config.js +++ b/packages/embark/src/test/config.js @@ -122,7 +122,7 @@ describe('embark.Config', function () { "mineWhenNeeded": true, "nodiscover": true, "maxpeers": 0, - "simulatorBlocktime": 0, + "simulatorBlocktime": 0, "miningMode": "auto", "gasPrice": "8000000", "targetGasLimit": "20000000", diff --git a/packages/plugins/accounts-manager/src/fundAccount.ts b/packages/plugins/accounts-manager/src/fundAccount.ts index ef0ea300a..41fe86279 100644 --- a/packages/plugins/accounts-manager/src/fundAccount.ts +++ b/packages/plugins/accounts-manager/src/fundAccount.ts @@ -2,7 +2,7 @@ import Web3 from "web3"; const TARGET = 0x7FFFFFFFFFFFFFFF; -export default async function fundAccount(web3: Web3, accountAddress: string, coinbaseAddress: string, hexBalance: string | number | undefined) { +export default async function fundAccount(web3: Web3, accountAddress: string, coinbaseAddress: string, hexBalance?: string | number) { if (!hexBalance) { hexBalance = TARGET; } diff --git a/packages/plugins/accounts-manager/src/index.ts b/packages/plugins/accounts-manager/src/index.ts index 05c9f2f22..a3de28e59 100644 --- a/packages/plugins/accounts-manager/src/index.ts +++ b/packages/plugins/accounts-manager/src/index.ts @@ -1,66 +1,36 @@ import async from "async"; -import { Embark, Events } /* supplied by @types/embark in packages/embark-typings */ from "embark"; +import { Callback, Embark, Events } /* supplied by @types/embark in packages/embark-typings */ from "embark"; import { __ } from "embark-i18n"; import { AccountParser, dappPath } from "embark-utils"; import { Logger } from 'embark-logger'; import Web3 from "web3"; -const { blockchain: blockchainConstants } = require("embark-core/constants"); import fundAccount from "./fundAccount"; -function arrayEqual(arrayA: string[], arrayB: string[]) { - if (arrayA.length !== arrayB.length) { - return false; - } else { - return arrayA.every((address, index) => Web3.utils.toChecksumAddress(address) === Web3.utils.toChecksumAddress(arrayB[index])); - } -} - export default class AccountsManager { private readonly logger: Logger; private readonly events: Events; - private accounts: any[] = []; - private nodeAccounts: string[] = []; private _web3: Web3 | null = null; - private ready = false; - private signTransactionQueue: any; - private nonceCache: any = {}; + private _accounts: any[] | null = null; constructor(private readonly embark: Embark, _options: any) { this.logger = embark.logger; this.events = embark.events; - this.embark.registerActionForEvent("blockchain:proxy:request", this.checkBlockchainRequest.bind(this)); - this.embark.registerActionForEvent("blockchain:proxy:response", this.checkBlockchainResponse.bind(this)); - + this.parseAndFundAccounts(); this.events.on("blockchain:started", () => { this._web3 = null; - this.parseAndFundAccounts(null); - }); - this.embark.registerActionForEvent("accounts:reseted", async (params, cb) => { - this.ready = false; - await this.parseAndFundAccounts(params.accounts); - cb(null, null); }); - // Allow to run transaction in parallel by resolving the nonce manually. - // For each transaction, resolve the nonce by taking the max of current transaction count and the cache we keep locally. - // Update the nonce and sign it - this.signTransactionQueue = async.queue(({ payload, account }, callback: (error: any, result: any) => void) => { - this.getNonce(payload.from, async (err: any, newNonce: number) => { - if (err) { - return callback(err, null); - } - payload.nonce = newNonce; - const web3 = await this.web3; - web3.eth.accounts.signTransaction(payload, account.privateKey, (signingError: any, result: any) => { - if (signingError) { - return callback(signingError, null); - } - callback(null, result.rawTransaction); - }); - }); - }, 1); + this.embark.registerActionForEvent("tests:config:updated", undefined, async (params, cb) => { + // reset accounts backing variable as the accounts in config may have changed and + // web.eth.getAccounts may return a different value now + this._accounts = null; + + // as the accounts may have changed, we need to fund the accounts again + await this.parseAndFundAccounts(); + cb(null, null); + }); } get web3() { @@ -73,86 +43,35 @@ export default class AccountsManager { })(); } - private async getNonce(address: string, callback: (error: any, result: any) => void) { - const web3 = await this.web3; - web3.eth.getTransactionCount(address, undefined, (error: any, transactionCount: number) => { - if (error) { - return callback(error, null); - } - if (this.nonceCache[address] === undefined) { - this.nonceCache[address] = -1; - } - - if (transactionCount > this.nonceCache[address]) { - this.nonceCache[address] = transactionCount; - return callback(null, this.nonceCache[address]); - } - - this.nonceCache[address]++; - callback(null, this.nonceCache[address]); - }); - } - - private async checkBlockchainRequest(params: any, callback: (error: any, result: any) => void) { - if (!this.ready) { - return callback(null, params); - } - if (params.reqData.method === blockchainConstants.transactionMethods.eth_sendTransaction && this.accounts.length) { - // Check if we have that account in our wallet - const account = this.accounts.find((acc) => Web3.utils.toChecksumAddress(acc.address) === Web3.utils.toChecksumAddress(params.reqData.params[0].from)); - if (account && account.privateKey) { - return this.signTransactionQueue.push({ payload: params.reqData.params[0], account }, (err: any, newPayload: any) => { - if (err) { - return callback(err, null); - } - params.reqData.method = blockchainConstants.transactionMethods.eth_sendRawTransaction; - params.reqData.params = [newPayload]; - callback(err, params); - }); - } - } - callback(null, params); - } - - private async checkBlockchainResponse(params: any, callback: (error: any, result: any) => void) { - if (!this.ready) { - return callback(null, params); - } - if ((params.reqData.method === blockchainConstants.transactionMethods.eth_accounts || - params.reqData.method === blockchainConstants.transactionMethods.personal_listAccounts) && this.accounts.length) { - if (!arrayEqual(params.respData.result, this.nodeAccounts)) { - this.nodeAccounts = params.respData.result; + get accounts() { + return (async () => { + if (!this._accounts) { const web3 = await this.web3; - this.accounts = AccountParser.parseAccountsConfig(this.embark.config.blockchainConfig.accounts, web3, dappPath(), this.logger, this.nodeAccounts); + this._accounts = await web3.eth.getAccounts(); } - params.respData.result = this.accounts.map((acc) => { - if (acc.address) { - return acc.address; - } - return acc; - }); - return callback(null, params); - } - callback(null, params); + return this._accounts || []; + })(); } - private async parseAndFundAccounts(accounts: any[] | null) { + private async parseAndFundAccounts() { const web3 = await this.web3; + const accounts = await this.accounts; - const nodeAccounts = await web3.eth.getAccounts(); - this.nodeAccounts = nodeAccounts; - this.accounts = AccountParser.parseAccountsConfig(accounts || this.embark.config.blockchainConfig.accounts, web3, dappPath(), this.logger, nodeAccounts); - - if (!this.accounts.length || !this.embark.config.blockchainConfig.isDev) { - this.ready = true; + if (!accounts.length || !this.embark.config.blockchainConfig.isDev) { return; } try { const coinbase = await web3.eth.getCoinbase(); - const accts = this.accounts - .filter((account) => account.address); + const acctsFromConfig = AccountParser.parseAccountsConfig(this.embark.config.blockchainConfig.accounts, web3, dappPath(), this.logger, accounts); + const accountsWithBalance = accounts.map((address) => { + const acctFromConfig = acctsFromConfig.find((acctCfg) => acctCfg.address === address); + return { + address, + hexBalance: acctFromConfig ? acctFromConfig.hexBalance : undefined + }; + }); - async.eachLimit(accts, 1, (acct, eachCb) => { + async.eachLimit(accountsWithBalance, 1, (acct, eachCb) => { fundAccount(web3, acct.address, coinbase, acct.hexBalance) .then(() => { eachCb(); @@ -162,6 +81,5 @@ export default class AccountsManager { } catch (err) { this.logger.error(__("Error funding accounts"), err.message || err); } - this.ready = true; } } diff --git a/packages/plugins/coverage/src/lib/index.ts b/packages/plugins/coverage/src/lib/index.ts index 033e27a20..bac2156d6 100644 --- a/packages/plugins/coverage/src/lib/index.ts +++ b/packages/plugins/coverage/src/lib/index.ts @@ -77,7 +77,7 @@ export default class Coverage { this.fs.ensureDirSync(path.join(dappPath(), ".embark")); const coveragePath = path.join(dappPath(), ".embark", "coverage.json"); - const coverageReport = this.contracts.reduce((acc: {[name: string]: ICoverage}, contract) => { + const coverageReport = this.contracts.reduce((acc: { [name: string]: ICoverage }, contract) => { if (contract.source) { acc[contract.filepath] = contract.coverage; } @@ -90,7 +90,7 @@ export default class Coverage { private collectEvents(web3Contracts: Web3Contract[]) { return web3Contracts.map(async (web3Contract) => { - const events = await web3Contract.getPastEvents("allEvents", {fromBlock: 0}); + const events = await web3Contract.getPastEvents("allEvents", { fromBlock: 0 }); this.contracts.forEach((contract) => contract.updateCoverage(events)); }); } @@ -99,7 +99,7 @@ export default class Coverage { return new Promise((resolve) => { const address = deployedContract.deployedAddress; const abi = deployedContract.abiDefinition; - this.embark.events.request("blockchain:contract:create", {address, abi}, (web3Contract: Web3Contract) => { + this.embark.events.request("blockchain:contract:create", { address, abi }, (web3Contract: Web3Contract) => { resolve(web3Contract); }); }); @@ -107,7 +107,7 @@ export default class Coverage { private getDeployedContracts() { return new Promise((resolve, reject) => { - this.embark.events.request("contracts:all", (error: Error, contracts: {[name: string]: Contract}) => { + this.embark.events.request("contracts:all", (error: Error, contracts: { [name: string]: Contract }) => { if (error) { return reject(error); } @@ -118,7 +118,7 @@ export default class Coverage { private async getWeb3Contracts() { const web3Contracts = this.deployedContracts.filter((deployedContract) => deployedContract.deployedAddress) - .map((deployedContract) => this.getWeb3Contract(deployedContract)); + .map((deployedContract) => this.getWeb3Contract(deployedContract)); return (await Promise.all(web3Contracts)).concat(this.web3Contracts); } diff --git a/packages/plugins/geth/src/check.js b/packages/plugins/geth/src/check.js index fe9874462..2db581df9 100644 --- a/packages/plugins/geth/src/check.js +++ b/packages/plugins/geth/src/check.js @@ -1,12 +1,15 @@ const WebSocket = require("ws"); const http = require("http"); -const LIVENESS_CHECK=`{"jsonrpc":"2.0","method":"web3_clientVersion","params":[],"id":42}`; +const LIVENESS_CHECK = `{"jsonrpc":"2.0","method":"web3_clientVersion","params":[],"id":42}`; const parseAndRespond = (data, cb) => { let resp; try { resp = JSON.parse(data); + if (resp.error) { + return cb(resp.error); + } } catch (e) { return cb('Version data is not valid JSON'); } diff --git a/packages/plugins/rpc-manager/.npmrc b/packages/plugins/rpc-manager/.npmrc new file mode 100644 index 000000000..e031d3432 --- /dev/null +++ b/packages/plugins/rpc-manager/.npmrc @@ -0,0 +1,4 @@ +engine-strict = true +package-lock = false +save-exact = true +scripts-prepend-node-path = true diff --git a/packages/plugins/rpc-manager/README.md b/packages/plugins/rpc-manager/README.md new file mode 100644 index 000000000..13141a833 --- /dev/null +++ b/packages/plugins/rpc-manager/README.md @@ -0,0 +1,9 @@ +embark-rpc-manager +========================== + +> Embark RPC Manager + +Modifies RPC calls to/from Embark (to/from `embark-proxy`). + +Visit [embark.status.im](https://embark.status.im/) to get started with +[Embark](https://github.com/embark-framework/embark). diff --git a/packages/plugins/rpc-manager/package.json b/packages/plugins/rpc-manager/package.json new file mode 100644 index 000000000..e2cf65fe4 --- /dev/null +++ b/packages/plugins/rpc-manager/package.json @@ -0,0 +1,76 @@ +{ + "name": "embark-rpc-manager", + "version": "5.0.0-alpha.1", + "description": "Embark RPC Manager", + "main": "./dist/lib/index.js", + "repository": { + "directory": "packages/plugins/embark-rpc-manager", + "type": "git", + "url": "https://github.com/embark-framework/embark/" + }, + "author": "Iuri Matias ", + "license": "MIT", + "bugs": "https://github.com/embark-framework/embark/issues", + "keywords": [ + "blockchain", + "dapps", + "ethereum", + "ipfs", + "serverless", + "solc", + "solidity" + ], + "files": [ + "dist/" + ], + "embark-collective": { + "build:node": true + }, + "scripts": { + "_build": "npm run solo -- build", + "ci": "npm run qa", + "clean": "npm run reset", + "lint": "npm-run-all lint:*", + "lint:js": "eslint src/", + "lint:ts": "tslint -c tslint.json \"src/**/*.ts\"", + "qa": "npm-run-all lint typecheck _build test", + "reset": "npx rimraf .nyc_output coverage dist embark-*.tgz package", + "solo": "embark-solo", + "test": "nyc --reporter=html --reporter=json mocha \"dist/test/**/*.js\" --exit --no-timeouts --require source-map-support/register", + "typecheck": "tsc" + }, + "eslintConfig": { + "extends": "../../../.eslintrc.json" + }, + "dependencies": { + "@babel/runtime-corejs3": "7.6.3", + "@omisego/omg-js-util": "2.0.0-v0.2", + "async": "2.6.1", + "embark-core": "^5.0.0-alpha.1", + "embark-i18n": "^5.0.0-alpha.1", + "embark-utils": "^5.0.0-alpha.1", + "web3": "1.2.1" + }, + "devDependencies": { + "@types/async": "2.0.50", + "cross-env": "5.2.0", + "eslint": "5.7.0", + "mocha": "6.2.0", + "npm-run-all": "4.1.5", + "rimraf": "3.0.0", + "tslint": "5.16.0", + "typescript": "3.6.3" + }, + "engines": { + "node": ">=10.17.0 <12.0.0", + "npm": ">=6.11.3", + "yarn": ">=1.19.1" + }, + "nyc": { + "exclude": [ + "**/node_modules/**", + "coverage/**", + "dist/test/**" + ] + } +} \ No newline at end of file diff --git a/packages/plugins/rpc-manager/src/lib/eth_accounts.ts b/packages/plugins/rpc-manager/src/lib/eth_accounts.ts new file mode 100644 index 000000000..56c5114ea --- /dev/null +++ b/packages/plugins/rpc-manager/src/lib/eth_accounts.ts @@ -0,0 +1,54 @@ +import { Callback, Embark, Events, Logger } /* supplied by @types/embark in packages/embark-typings */ from "embark"; +import Web3 from "web3"; +const { blockchain: blockchainConstants } = require("embark-core/constants"); +import { __ } from "embark-i18n"; +import RpcModifier from "./rpcModifier"; + +const METHODS_TO_MODIFY = [ + blockchainConstants.transactionMethods.eth_accounts, + blockchainConstants.transactionMethods.personal_listAccounts, +]; + +function arrayEqual(arrayA: string[], arrayB: string[]) { + if (!(arrayA && arrayB) || arrayA.length !== arrayB.length) { + return false; + } else { + return arrayA.every((address, index) => Web3.utils.toChecksumAddress(address) === Web3.utils.toChecksumAddress(arrayB[index])); + } +} + +export default class EthAccounts extends RpcModifier { + constructor(embark: Embark, rpcModifierEvents: Events) { + super(embark, rpcModifierEvents); + + this.embark.registerActionForEvent("blockchain:proxy:response", undefined, this.checkResponseFor_eth_accounts.bind(this)); + } + + private async checkResponseFor_eth_accounts(params: any, callback: Callback) { + + if (!(METHODS_TO_MODIFY.includes(params.reqData.method))) { + return callback(null, params); + } + + this.logger.trace(__(`Modifying blockchain '${params.reqData.method}' response:`)); + this.logger.trace(__(`Original request/response data: ${JSON.stringify(params)}`)); + + try { + if (!arrayEqual(params.respData.result, this._nodeAccounts || [])) { + // reset backing variables so accounts is recalculated + await this.rpcModifierEvents.request2("nodeAccounts:updated", params.respData.result); + } + const accounts = await this.accounts; + if (!(accounts && accounts.length)) { + return callback(null, params); + } + + params.respData.result = accounts.map((acc) => acc.address || acc); + this.logger.trace(__(`Modified request/response data: ${JSON.stringify(params)}`)); + } catch (err) { + return callback(err); + } + + return callback(null, params); + } +} diff --git a/packages/plugins/rpc-manager/src/lib/eth_sendTransaction.ts b/packages/plugins/rpc-manager/src/lib/eth_sendTransaction.ts new file mode 100644 index 000000000..c93626c98 --- /dev/null +++ b/packages/plugins/rpc-manager/src/lib/eth_sendTransaction.ts @@ -0,0 +1,86 @@ +import async from "async"; +import { Callback, Embark, Events, Logger } /* supplied by @types/embark in packages/embark-typings */ from "embark"; +import { __ } from "embark-i18n"; +import Web3 from "web3"; +const { blockchain: blockchainConstants } = require("embark-core/constants"); +import RpcModifier from "./rpcModifier"; + +export default class EthSendTransaction extends RpcModifier { + private signTransactionQueue: any; + private nonceCache: any = {}; + constructor(embark: Embark, rpcModifierEvents: Events) { + super(embark, rpcModifierEvents); + + embark.registerActionForEvent("blockchain:proxy:request", undefined, this.checkRequestFor_eth_sendTransaction.bind(this)); + + // Allow to run transaction in parallel by resolving the nonce manually. + // For each transaction, resolve the nonce by taking the max of current transaction count and the cache we keep locally. + // Update the nonce and sign it + this.signTransactionQueue = async.queue(({ payload, account }, callback: Callback) => { + this.getNonce(payload.from, async (err: any, newNonce: number) => { + if (err) { + return callback(err, null); + } + payload.nonce = newNonce; + const web3 = await this.web3; + web3.eth.accounts.signTransaction(payload, account.privateKey, (signingError: any, result: any) => { + if (signingError) { + return callback(signingError, null); + } + callback(null, result.rawTransaction); + }); + }); + }, 1); + } + + private async getNonce(address: string, callback: Callback) { + const web3 = await this.web3; + web3.eth.getTransactionCount(address, undefined, (error: any, transactionCount: number) => { + if (error) { + return callback(error, null); + } + if (this.nonceCache[address] === undefined) { + this.nonceCache[address] = -1; + } + + if (transactionCount > this.nonceCache[address]) { + this.nonceCache[address] = transactionCount; + return callback(null, this.nonceCache[address]); + } + + this.nonceCache[address]++; + callback(null, this.nonceCache[address]); + }); + } + private async checkRequestFor_eth_sendTransaction(params: any, callback: Callback) { + if (!(params.reqData.method === blockchainConstants.transactionMethods.eth_sendTransaction)) { + return callback(null, params); + } + const accounts = await this.accounts; + if (!(accounts && accounts.length)) { + return callback(null, params); + } + + this.logger.trace(__(`Modifying blockchain '${params.reqData.method}' request:`)); + this.logger.trace(__(`Original request data: ${JSON.stringify(params)}`)); + + try { + // Check if we have that account in our wallet + const account = accounts.find((acc) => Web3.utils.toChecksumAddress(acc.address) === Web3.utils.toChecksumAddress(params.reqData.params[0].from)); + if (account && account.privateKey) { + return this.signTransactionQueue.push({ payload: params.reqData.params[0], account }, (err: any, newPayload: any) => { + if (err) { + return callback(err, null); + } + params.reqData.method = blockchainConstants.transactionMethods.eth_sendRawTransaction; + params.reqData.params = [newPayload]; + callback(err, params); + }); + } + } catch (err) { + return callback(err); + } + this.logger.trace(__(`Modified request/response data: ${JSON.stringify(params)}`)); + callback(null, params); + } +} diff --git a/packages/plugins/rpc-manager/src/lib/eth_signTypedData.ts b/packages/plugins/rpc-manager/src/lib/eth_signTypedData.ts new file mode 100644 index 000000000..b49afeef4 --- /dev/null +++ b/packages/plugins/rpc-manager/src/lib/eth_signTypedData.ts @@ -0,0 +1,61 @@ +import { sign, transaction } from "@omisego/omg-js-util"; +import { Callback, Embark, Events, Logger } /* supplied by @types/embark in packages/embark-typings */ from "embark"; +import { __ } from "embark-i18n"; +import Web3 from "web3"; +import RpcModifier from "./rpcModifier"; + +export default class EthSignTypedData extends RpcModifier { + constructor(embark: Embark, rpcModifierEvents: Events) { + super(embark, rpcModifierEvents); + + this.embark.registerActionForEvent("blockchain:proxy:request", undefined, this.checkRequestFor_eth_signTypedData.bind(this)); + this.embark.registerActionForEvent("blockchain:proxy:response", undefined, this.checkResponseFor_eth_signTypedData.bind(this)); + } + + private async checkRequestFor_eth_signTypedData(params: any, callback: Callback) { + // check for: + // - eth_signTypedData + // - eth_signTypedData_v3 + // - eth_signTypedData_v4 + // - personal_signTypedData (parity) + if (params.reqData.method.includes("signTypedData")) { + // indicate that we do not want this call to go to the node + params.sendToNode = false; + return callback(null, params); + } + callback(null, params); + } + private async checkResponseFor_eth_signTypedData(params: any, callback: Callback) { + + // check for: + // - eth_signTypedData + // - eth_signTypedData_v3 + // - eth_signTypedData_v4 + // - personal_signTypedData (parity) + if (!params.reqData.method.includes("signTypedData")) { + return callback(null, params); + } + + this.logger.trace(__(`Modifying blockchain '${params.reqData.method}' response:`)); + this.logger.trace(__(`Original request/response data: ${JSON.stringify(params)}`)); + + try { + const accounts = await this.accounts; + const [fromAddr, typedData] = params.reqData.params; + const account = accounts.find((acc) => Web3.utils.toChecksumAddress(acc.address) === Web3.utils.toChecksumAddress(fromAddr)); + if (!(account && account.privateKey)) { + return callback( + new Error(__("Could not sign transaction because Embark does not have a private key associated with '%s'. " + + "Please ensure you have configured your account(s) to use a mnemonic, privateKey, or privateKeyFile.", fromAddr))); + } + const toSign = transaction.getToSignHash(typeof typedData === "string" ? JSON.parse(typedData) : typedData); + const signature = sign(toSign, [account.privateKey]); + + params.respData.result = signature[0]; + this.logger.trace(__(`Modified request/response data: ${JSON.stringify(params)}`)); + } catch (err) { + return callback(err); + } + callback(null, params); + } +} diff --git a/packages/plugins/rpc-manager/src/lib/index.ts b/packages/plugins/rpc-manager/src/lib/index.ts new file mode 100644 index 000000000..b3faa9cef --- /dev/null +++ b/packages/plugins/rpc-manager/src/lib/index.ts @@ -0,0 +1,45 @@ +import { Callback, Embark, Events } from "embark-core"; +import { Logger } from 'embark-logger'; +import Web3 from "web3"; +import EthAccounts from "./eth_accounts"; +import EthSendTransaction from "./eth_sendTransaction"; +import EthSignTypedData from "./eth_signTypedData"; +import PersonalNewAccount from "./personal_newAccount"; +import RpcModifier from "./rpcModifier"; + +export default class RpcManager { + + private modifiers: RpcModifier[] = []; + private _web3: Web3 | null = null; + private rpcModifierEvents: Events; + private logger: Logger; + private events: Events; + public _accounts: any[] | null = null; + public _nodeAccounts: any[] | null = null; + constructor(private readonly embark: Embark) { + this.events = embark.events; + this.logger = embark.logger; + this.rpcModifierEvents = new Events(); + this.init(); + } + + private async init() { + + this.rpcModifierEvents.setCommandHandler("nodeAccounts:updated", this.updateAccounts.bind(this)); + this.rpcModifierEvents.setCommandHandler("nodeAccounts:added", async (addedNodeAccount: any, cb: Callback) => { + if (!this._nodeAccounts) { + this._nodeAccounts = [addedNodeAccount]; + } else { + this._nodeAccounts.push(addedNodeAccount); + } + return this.updateAccounts(this._nodeAccounts, cb); + }); + this.modifiers = [PersonalNewAccount, EthAccounts, EthSendTransaction, EthSignTypedData].map((rpcModifier) => new rpcModifier(this.embark, this.rpcModifierEvents)); + } + private async updateAccounts(updatedNodeAccounts: any[], cb: Callback) { + for (const modifier of this.modifiers) { + await (modifier.nodeAccounts = Promise.resolve(updatedNodeAccounts)); + } + cb(); + } +} diff --git a/packages/plugins/rpc-manager/src/lib/personal_newAccount.ts b/packages/plugins/rpc-manager/src/lib/personal_newAccount.ts new file mode 100644 index 000000000..cd35f81e0 --- /dev/null +++ b/packages/plugins/rpc-manager/src/lib/personal_newAccount.ts @@ -0,0 +1,23 @@ +import { Callback, Embark, Events } /* supplied by @types/embark in packages/embark-typings */ from "embark"; +import Web3 from "web3"; +const { blockchain: blockchainConstants } = require("embark-core/constants"); +import { __ } from "embark-i18n"; +import RpcModifier from "./rpcModifier"; +export default class PersonalNewAccount extends RpcModifier { + constructor(embark: Embark, rpcModifierEvents: Events) { + super(embark, rpcModifierEvents); + + embark.registerActionForEvent("blockchain:proxy:response", undefined, this.checkResponseFor_personal_newAccount.bind(this)); + } + + private async checkResponseFor_personal_newAccount(params: any, callback: Callback) { + if (params.reqData.method !== blockchainConstants.transactionMethods.personal_newAccount) { + return callback(null, params); + } + + // emit event so tx modifiers can refresh accounts + await this.rpcModifierEvents.request2("nodeAccounts:added", params.respData.result); + + callback(null, params); + } +} diff --git a/packages/plugins/rpc-manager/src/lib/rpcModifier.ts b/packages/plugins/rpc-manager/src/lib/rpcModifier.ts new file mode 100644 index 000000000..1fd86218d --- /dev/null +++ b/packages/plugins/rpc-manager/src/lib/rpcModifier.ts @@ -0,0 +1,68 @@ +import { Embark, Events, Logger } /* supplied by @types/embark in packages/embark-typings */ from "embark"; +import { AccountParser, dappPath } from "embark-utils"; +import Web3 from "web3"; + +export default class RpcModifier { + public events: Events; + public logger: Logger; + private _web3: Web3 | null = null; + private _accounts: any[] | null = null; + protected _nodeAccounts: any[] | null = null; + constructor(readonly embark: Embark, readonly rpcModifierEvents: Events) { + this.events = embark.events; + this.logger = embark.logger; + + this.embark.registerActionForEvent("tests:config:updated", { priority: 40 }, async (_params, cb) => { + // reset accounts backing variable as the accounts in config may have changed and + // web.eth.getAccounts may return a different value now + this._accounts = null; + this._nodeAccounts = null; + cb(null, null); + }); + } + + protected get web3() { + return (async () => { + if (!this._web3) { + const provider = await this.events.request2("blockchain:client:provider", "ethereum"); + this._web3 = new Web3(provider); + } + return this._web3; + })(); + } + + public get nodeAccounts() { + return (async () => { + if (!this._nodeAccounts) { + const web3 = await this.web3; + this._nodeAccounts = await web3.eth.getAccounts(); + } + return this._nodeAccounts || []; + })(); + } + + public set nodeAccounts(nodeAccounts: Promise) { + (async () => { + this._nodeAccounts = await nodeAccounts; + // reset accounts backing variable as it neesd to be recalculated + this._accounts = null; + })(); + } + + protected get accounts() { + return (async () => { + if (!this._accounts) { + const web3 = await this.web3; + const nodeAccounts = await this.nodeAccounts; + this._accounts = AccountParser.parseAccountsConfig(this.embark.config.blockchainConfig.accounts, web3, dappPath(), this.logger, nodeAccounts); + } + return this._accounts || []; + })(); + } + + protected set accounts(accounts) { + (async () => { + this._accounts = await accounts; + })(); + } +} diff --git a/packages/plugins/rpc-manager/src/test/transaction_manager_test.js b/packages/plugins/rpc-manager/src/test/transaction_manager_test.js new file mode 100644 index 000000000..e69de29bb diff --git a/packages/plugins/rpc-manager/tsconfig.json b/packages/plugins/rpc-manager/tsconfig.json new file mode 100644 index 000000000..1bb65da9e --- /dev/null +++ b/packages/plugins/rpc-manager/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../../tsconfig.json", + "include": ["src/**/*"] +} diff --git a/packages/plugins/rpc-manager/tslint.json b/packages/plugins/rpc-manager/tslint.json new file mode 100644 index 000000000..1f63906f0 --- /dev/null +++ b/packages/plugins/rpc-manager/tslint.json @@ -0,0 +1,3 @@ +{ + "extends": "../../../tslint.json" +} diff --git a/packages/stack/embarkjs/src/embarkjs-artifact.js.ejs b/packages/stack/embarkjs/src/embarkjs-artifact.js.ejs index 58269d993..e9c1e8708 100644 --- a/packages/stack/embarkjs/src/embarkjs-artifact.js.ejs +++ b/packages/stack/embarkjs/src/embarkjs-artifact.js.ejs @@ -52,4 +52,13 @@ if (typeof WebSocket !== 'undefined') { } <% } %> -export default EmbarkJS; +<% for (let stackName in (customPlugins || [])) { %> + <% for (let pluginName in (customPlugins[stackName] || [])) { %> + let __embark<%- pluginName %> = require('<%- customPlugins[stackName][pluginName] %>'); + __embark<%- pluginName %> = __embark<%- pluginName %>.default || __embark<%- pluginName %>; + const customPluginConfig = require('./config/<%- stackName %>.json'); + EmbarkJS.<%- stackName %> = new __embark<%- pluginName %>({pluginConfig: customPluginConfig}); + <% }; %> +<% }; %> + +export default EmbarkJS; \ No newline at end of file diff --git a/packages/stack/embarkjs/src/index.js b/packages/stack/embarkjs/src/index.js index a5e9c4a1d..c063ff279 100644 --- a/packages/stack/embarkjs/src/index.js +++ b/packages/stack/embarkjs/src/index.js @@ -18,19 +18,29 @@ class EmbarkJS { this.events.request("runcode:whitelist", 'embarkjs', () => { this.registerEmbarkJS(); - }); + }); this.embarkJSPlugins = {}; + this.customEmbarkJSPlugins = {}; this.events.setCommandHandler("embarkjs:plugin:register", (stackName, pluginName, packageName) => { this.embarkJSPlugins[stackName] = this.embarkJSPlugins[stackName] || {}; this.embarkJSPlugins[stackName][pluginName] = packageName; }); + this.events.setCommandHandler("embarkjs:plugin:register:custom", (stackName, pluginName, packageName) => { + this.customEmbarkJSPlugins[stackName] = this.customEmbarkJSPlugins[stackName] || {}; + this.customEmbarkJSPlugins[stackName][pluginName] = packageName; + }); this.events.setCommandHandler("embarkjs:console:register", (stackName, pluginName, packageName, cb) => { - this.events.request("runcode:whitelist", packageName, () => { }); + this.events.request("runcode:whitelist", packageName, () => {}); this.registerEmbarkJSPlugin(stackName, pluginName, packageName, cb || (() => {})); }); + this.events.setCommandHandler("embarkjs:console:regsiter:custom", (stackName, pluginName, packageName, options, cb) => { + this.events.request("runcode:whitelist", packageName, () => {}); + this.registerCustomEmbarkJSPluginInVm(stackName, pluginName, packageName, options, cb || (() => {})); + }); + this.events.setCommandHandler("embarkjs:console:setProvider", this.setProvider.bind(this)); this.events.setCommandHandler("embarkjs:contract:generate", this.addContractArtifact.bind(this)); this.events.setCommandHandler("embarkjs:contract:runInVm", this.runInVm.bind(this)); @@ -65,8 +75,27 @@ class EmbarkJS { cb(); } + async registerCustomEmbarkJSPluginInVm(stackName, pluginName, packageName, options, cb) { + await this.registerEmbarkJS(); + + const customPluginCode = ` + let __embark${pluginName} = require('${packageName}'); + __embark${pluginName} = __embark${pluginName}.default || __embark${pluginName}; + const customPluginOptions = ${JSON.stringify(options)}; + EmbarkJS.${stackName} = new __embark${pluginName}({pluginConfig: customPluginOptions}); + EmbarkJS.${stackName}.init(); + `; + + await this.events.request2('runcode:eval', customPluginCode); + cb(); + } + addEmbarkJSArtifact(_params, cb) { - const embarkjsCode = Templates.embarkjs_artifact({ plugins: this.embarkJSPlugins, hasWebserver: this.embark.config.webServerConfig.enabled }); + const embarkjsCode = Templates.embarkjs_artifact({ + plugins: this.embarkJSPlugins, + hasWebserver: this.embark.config.webServerConfig.enabled, + customPlugins: this.customEmbarkJSPlugins + }); // TODO: generate a .node file this.events.request("pipeline:register", { @@ -142,10 +171,10 @@ class EmbarkJS { const result = await this.events.request2('runcode:eval', contract.className); result.currentProvider = provider; await this.events.request2("runcode:register", contract.className, result); - cb(); } catch (err) { - cb(err); + return cb(err); } + cb(); } } diff --git a/packages/stack/proxy/src/index.ts b/packages/stack/proxy/src/index.ts index 0827162de..9d0dba921 100644 --- a/packages/stack/proxy/src/index.ts +++ b/packages/stack/proxy/src/index.ts @@ -9,7 +9,8 @@ const constants = require("embark-core/constants"); export default class ProxyManager { private readonly logger: Logger; private readonly events: Events; - private proxy: any; + private wsProxy: any; + private httpProxy: any; private plugins: any; private readonly host: string; private rpcPort = 0; @@ -79,7 +80,7 @@ export default class ProxyManager { if (!this.embark.config.blockchainConfig.proxy) { return; } - if (this.proxy) { + if (this.httpProxy || this.wsProxy) { throw new Error("Proxy is already started"); } const port = await findNextPort(this.embark.config.blockchainConfig.rpcPort + constants.blockchain.servicePortOnProxy); @@ -88,23 +89,47 @@ export default class ProxyManager { this.wsPort = port + 1; this.isWs = clientName === constants.blockchain.vm || (/wss?/).test(this.embark.config.blockchainConfig.endpoint); - this.proxy = await new Proxy({ - endpoint: clientName === constants.blockchain.vm ? constants.blockchain.vm : this.embark.config.blockchainConfig.endpoint, - events: this.events, - isWs: this.isWs, - logger: this.logger, - plugins: this.plugins, - vms: this.vms, - }); - - await this.proxy.serve( - this.host, - this.isWs ? this.wsPort : this.rpcPort, - ); + // HTTP + if (clientName !== constants.blockchain.vm) { + this.httpProxy = await new Proxy({ + endpoint: this.embark.config.blockchainConfig.endpoint, + events: this.events, + isWs: false, + logger: this.logger, + plugins: this.plugins, + vms: this.vms, + }) + .serve( + this.host, + this.rpcPort, + ); + this.logger.info(`HTTP Proxy for node endpoint ${this.embark.config.blockchainConfig.endpoint} listening on ${buildUrl("http", this.host, this.rpcPort, "rpc")}`); + } + if (this.isWs) { + const endpoint = clientName === constants.blockchain.vm ? constants.blockchain.vm : this.embark.config.blockchainConfig.endpoint; + this.wsProxy = await new Proxy({ + endpoint, + events: this.events, + isWs: true, + logger: this.logger, + plugins: this.plugins, + vms: this.vms, + }) + .serve( + this.host, + this.wsPort, + ); + this.logger.info(`WS Proxy for node endpoint ${endpoint} listening on ${buildUrl("ws", this.host, this.wsPort, "ws")}`); + } } - private stopProxy() { - this.proxy.stop(); - this.proxy = null; + if (this.wsProxy) { + this.wsProxy.stop(); + this.wsProxy = null; + } + if (this.httpProxy) { + this.httpProxy.stop(); + this.httpProxy = null; + } } } diff --git a/packages/stack/proxy/src/proxy.js b/packages/stack/proxy/src/proxy.js index ee084d5b2..38018e4a1 100644 --- a/packages/stack/proxy/src/proxy.js +++ b/packages/stack/proxy/src/proxy.js @@ -173,27 +173,28 @@ export class Proxy { // stripped out by modifying the response (via actions for blockchain:proxy:response) respData.error = fwdReqErr.message || fwdReqErr; } + } - try { - const modifiedResp = await this.emitActionsForResponse(modifiedRequest.reqData, respData); - // Send back to the client - if (modifiedResp && modifiedResp.respData && modifiedResp.respData.error) { - // error returned from the node and it wasn't stripped by our response actions - const error = modifiedResp.respData.error.message || modifiedResp.respData.error; - this.logger.error(__(`Error returned from the node: ${error}`)); - const rpcErrorObj = { "jsonrpc": "2.0", "error": { "code": -32603, "message": error }, "id": modifiedResp.respData.id }; - return this.respondError(transport, rpcErrorObj); - } - this.respondOK(transport, modifiedResp.respData); - } - catch (resError) { - // if was an error in response actions (resError), send the error in the response - const error = resError.message || resError; - this.logger.error(__(`Error executing response actions: ${error}`)); - const rpcErrorObj = { "jsonrpc": "2.0", "error": { "code": -32603, "message": error }, "id": modifiedRequest.reqData.id }; + try { + const modifiedResp = await this.emitActionsForResponse(modifiedRequest.reqData, respData); + // Send back to the client + if (modifiedResp && modifiedResp.respData && modifiedResp.respData.error) { + // error returned from the node and it wasn't stripped by our response actions + const error = modifiedResp.respData.error.message || modifiedResp.respData.error; + this.logger.error(__(`Error returned from the node: ${error}`)); + const rpcErrorObj = { "jsonrpc": "2.0", "error": { "code": -32603, "message": error }, "id": modifiedResp.respData.id }; return this.respondError(transport, rpcErrorObj); } + this.respondOK(transport, modifiedResp.respData); } + catch (resError) { + // if was an error in response actions (resError), send the error in the response + const error = resError.message || resError; + this.logger.error(__(`Error executing response actions: ${error}`)); + const rpcErrorObj = { "jsonrpc": "2.0", "error": { "code": -32603, "message": error }, "id": modifiedRequest.reqData.id }; + return this.respondError(transport, rpcErrorObj); + } + } diff --git a/packages/stack/test-runner/src/lib/index.js b/packages/stack/test-runner/src/lib/index.js index 8db382e95..2c760cd98 100644 --- a/packages/stack/test-runner/src/lib/index.js +++ b/packages/stack/test-runner/src/lib/index.js @@ -225,7 +225,7 @@ class TestRunner { Object.assign(this.simOptions, {host, port, type, protocol, accounts, client: config.blockchain && config.blockchain.client}); if (!resetServices && !deepEqual(accounts, ogAccounts)) { - return this.plugins.emitAndRunActionsForEvent("accounts:reseted", {accounts}, cb); + return this.plugins.emitAndRunActionsForEvent("tests:config:updated", {accounts}, cb); } if (!resetServices) { diff --git a/yarn.lock b/yarn.lock index 08eff4813..83c39d7a3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2598,6 +2598,17 @@ universal-user-agent "^3.0.0" url-template "^2.0.8" +"@omisego/omg-js-util@2.0.0-v0.2": + version "2.0.0-v0.2" + resolved "https://registry.yarnpkg.com/@omisego/omg-js-util/-/omg-js-util-2.0.0-v0.2.tgz#30b5882cc8a45446e7206576240caade614c8590" + integrity sha512-J5J5Q2XC7ZkpfQJiXJxii/66Tn3x4rhnhky3BmL++XEBoK6I34wgWMs/7dzsejWNHmmhGv/M/OZb+QBI+HlvlA== + dependencies: + eth-sig-util "^2.1.1" + ethereumjs-util "^6.0.0" + js-sha3 "^0.8.0" + number-to-bn "^1.7.0" + rlp "^2.2.2" + "@reach/router@^1.2.1": version "1.2.1" resolved "https://registry.yarnpkg.com/@reach/router/-/router-1.2.1.tgz#34ae3541a5ac44fa7796e5506a5d7274a162be4e" @@ -3146,6 +3157,11 @@ dependencies: defer-to-connect "^1.0.1" +"@types/async@2.0.50": + version "2.0.50" + resolved "https://registry.yarnpkg.com/@types/async/-/async-2.0.50.tgz#117540e026d64e1846093abbd5adc7e27fda7bcb" + integrity sha512-VMhZMMQgV1zsR+lX/0IBfAk+8Eb7dPVMWiQGFAt3qjo5x7Ml6b77jUo0e1C3ToD+XRDXqtrfw+6AB0uUsPEr3Q== + "@types/async@3.0.3": version "3.0.3" resolved "https://registry.yarnpkg.com/@types/async/-/async-3.0.3.tgz#ea3694128c757580e4f9328cd941b81d9c3e9bf6" @@ -5096,7 +5112,7 @@ bn.js@4.11.6: resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.6.tgz#53344adb14617a13f6e8dd2ce28905d1c0ba3215" integrity sha1-UzRK2xRhehP26N0s4okF0cC6MhU= -bn.js@4.11.8, bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.11.0, bn.js@^4.11.1, bn.js@^4.11.6, bn.js@^4.11.8, bn.js@^4.4.0, bn.js@^4.8.0: +bn.js@4.11.8, bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.10.0, bn.js@^4.11.0, bn.js@^4.11.1, bn.js@^4.11.6, bn.js@^4.11.8, bn.js@^4.4.0, bn.js@^4.8.0: version "4.11.8" resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.8.tgz#2cde09eb5ee341f484746bb0309b3253b1b1442f" integrity sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA== @@ -8543,6 +8559,18 @@ eth-lib@0.2.7: elliptic "^6.4.0" xhr-request-promise "^0.1.2" +eth-sig-util@^2.1.1: + version "2.4.4" + resolved "https://registry.yarnpkg.com/eth-sig-util/-/eth-sig-util-2.4.4.tgz#8804ead83de8648bcf81eadbfac1e3ccdd360aea" + integrity sha512-iWGqEJwsUMgtk8AqQQqIDTjMz+pW8s2Sq8gN640dh9U9HoEFQJO3m6ro96DgV6hMB2LYu8F5812LQyynOgCbEw== + dependencies: + buffer "^5.2.1" + elliptic "^6.4.0" + ethereumjs-abi "0.6.5" + ethereumjs-util "^5.1.1" + tweetnacl "^1.0.0" + tweetnacl-util "^0.15.0" + ethashjs@~0.0.7: version "0.0.7" resolved "https://registry.yarnpkg.com/ethashjs/-/ethashjs-0.0.7.tgz#30bfe4196726690a0c59d3b8272e70d4d0c34bae" @@ -8570,6 +8598,14 @@ ethereum-common@^0.0.18: resolved "https://registry.yarnpkg.com/ethereum-common/-/ethereum-common-0.0.18.tgz#2fdc3576f232903358976eb39da783213ff9523f" integrity sha1-L9w1dvIykDNYl26znaeDIT/5Uj8= +ethereumjs-abi@0.6.5: + version "0.6.5" + resolved "https://registry.yarnpkg.com/ethereumjs-abi/-/ethereumjs-abi-0.6.5.tgz#5a637ef16ab43473fa72a29ad90871405b3f5241" + integrity sha1-WmN+8Wq0NHP6cqKa2QhxQFs/UkE= + dependencies: + bn.js "^4.10.0" + ethereumjs-util "^4.3.0" + ethereumjs-account@^2.0.3: version "2.0.5" resolved "https://registry.yarnpkg.com/ethereumjs-account/-/ethereumjs-account-2.0.5.tgz#eeafc62de544cb07b0ee44b10f572c9c49e00a84" @@ -8668,7 +8704,7 @@ ethereumjs-util@6.0.0, ethereumjs-util@~6.0.0: safe-buffer "^5.1.1" secp256k1 "^3.0.1" -ethereumjs-util@^4.0.1, ethereumjs-util@^4.5.0: +ethereumjs-util@^4.0.1, ethereumjs-util@^4.3.0, ethereumjs-util@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-4.5.0.tgz#3e9428b317eebda3d7260d854fddda954b1f1bc6" integrity sha1-PpQosxfuvaPXJg2FT93alUsfG8Y= @@ -8679,7 +8715,7 @@ ethereumjs-util@^4.0.1, ethereumjs-util@^4.5.0: rlp "^2.0.0" secp256k1 "^3.0.1" -ethereumjs-util@^5.0.0, ethereumjs-util@^5.1.2, ethereumjs-util@^5.2.0: +ethereumjs-util@^5.0.0, ethereumjs-util@^5.1.1, ethereumjs-util@^5.1.2, ethereumjs-util@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-5.2.0.tgz#3e0c0d1741471acf1036052d048623dee54ad642" integrity sha512-CJAKdI0wgMbQFLlLRtZKGcy/L6pzVRgelIZqRqNbuVFM3K9VEnyfbcvz0ncWMRNCe4kaHWjwRYQcYMucmwsnWA== @@ -14562,7 +14598,7 @@ number-is-nan@^1.0.0: resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0= -number-to-bn@1.7.0: +number-to-bn@1.7.0, number-to-bn@^1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/number-to-bn/-/number-to-bn-1.7.0.tgz#bb3623592f7e5f9e0030b1977bd41a0c53fe1ea0" integrity sha1-uzYjWS9+X54AMLGXe9QaDFP+HqA= @@ -18058,7 +18094,7 @@ ripemd160@^2.0.0, ripemd160@^2.0.1: hash-base "^3.0.0" inherits "^2.0.1" -rlp@^2.0.0: +rlp@^2.0.0, rlp@^2.2.2: version "2.2.3" resolved "https://registry.yarnpkg.com/rlp/-/rlp-2.2.3.tgz#7f94aef86cec412df87d5ea1d8cb116a47d45f0e" integrity sha512-l6YVrI7+d2vpW6D6rS05x2Xrmq8oW7v3pieZOJKBEdjuTF4Kz/iwk55Zyh1Zaz+KOB2kC8+2jZlp2u9L4tTzCQ== @@ -20179,6 +20215,11 @@ tunnel-agent@^0.6.0: dependencies: safe-buffer "^5.0.1" +tweetnacl-util@^0.15.0: + version "0.15.0" + resolved "https://registry.yarnpkg.com/tweetnacl-util/-/tweetnacl-util-0.15.0.tgz#4576c1cee5e2d63d207fee52f1ba02819480bc75" + integrity sha1-RXbBzuXi1j0gf+5S8boCgZSAvHU= + tweetnacl@^0.14.3, tweetnacl@~0.14.0: version "0.14.5" resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64"