From f838739e1751aa8d1c60cc9ffe8639b33a957b6c Mon Sep 17 00:00:00 2001 From: Iuri Matias Date: Thu, 25 Jul 2019 12:20:39 -0400 Subject: [PATCH] implement embark-web3; add request2 to event bus with promise support --- dapps/tests/app/app/js/index.js | 3 +- packages/embark-code-runner/src/index.ts | 26 +-- packages/embark-code-runner/src/vm.ts | 20 ++- packages/embark-compiler/src/index.ts | 3 + packages/embark-web3/package.json | 4 +- .../embark-web3/src/contract-artifact.js.ejs | 13 ++ packages/embark-web3/src/index.js | 162 +++++++----------- .../src}/vanilla-contract.js.ejs | 0 packages/embark-web3/src/web3_init.js.ejs | 3 + packages/embark/src/cmd/cmd_controller.js | 37 ++-- packages/embark/src/lib/core/config.js | 4 +- packages/embark/src/lib/core/engine.js | 4 +- packages/embark/src/lib/core/events.js | 53 ++++++ .../{web3 => __web3_to_delete}/index.js | 25 --- .../__web3_to_delete/vanilla-contract.js.ejs | 9 + .../src/lib/modules/blockchain/index.js | 2 +- 16 files changed, 200 insertions(+), 168 deletions(-) create mode 100644 packages/embark-web3/src/contract-artifact.js.ejs rename packages/{embark/src/lib/modules/web3 => embark-web3/src}/vanilla-contract.js.ejs (100%) create mode 100644 packages/embark-web3/src/web3_init.js.ejs rename packages/embark/src/lib/modules/{web3 => __web3_to_delete}/index.js (85%) create mode 100644 packages/embark/src/lib/modules/__web3_to_delete/vanilla-contract.js.ejs diff --git a/dapps/tests/app/app/js/index.js b/dapps/tests/app/app/js/index.js index d07f5fa69..a9c2ddcb2 100644 --- a/dapps/tests/app/app/js/index.js +++ b/dapps/tests/app/app/js/index.js @@ -1,5 +1,6 @@ -import {SimpleStorage} from '../../embarkArtifacts/contracts'; +// import {SimpleStorage} from '../../embarkArtifacts/contracts'; +import SimpleStorage from '../../embarkArtifacts/contracts/SimpleStorage.js'; window.SimpleStorage = SimpleStorage; diff --git a/packages/embark-code-runner/src/index.ts b/packages/embark-code-runner/src/index.ts index 975fe7ec1..8099b4257 100644 --- a/packages/embark-code-runner/src/index.ts +++ b/packages/embark-code-runner/src/index.ts @@ -5,42 +5,34 @@ export { fs, VM }; import { Callback, Embark, Events, Logger } /* supplied by @types/embark in packages/embark-typings */ from "embark"; -// TODO: ideally shouldn't be needed or should be done through an API -import Web3 from "web3"; -const EmbarkJS = require("embarkjs"); - class CodeRunner { - private ready: boolean = false; - private blockchainConnected: boolean = false; private logger: Logger; private events: Events; private vm: VM; - private providerStates: { [key: string]: boolean } = {}; + constructor(embark: Embark, _options: any) { this.logger = embark.logger; this.events = embark.events; - EmbarkJS.environment = embark.env; this.vm = new VM({ require: { mock: { fs, }, }, - // TODO: ideally shouldn't be needed or should be done through an API sandbox: { - EmbarkJS, // TODO: can just use registerVar in the embarkjs plugin - Web3, // TODO: can just use registerVar in the web3.js plugin }, }, this.logger); this.registerEvents(); this.registerCommands(); - this.ready = true; } private registerEvents() { + // TODO: remove this on once all runcode:register have been converted to commands this.events.on("runcode:register", this.registerVar.bind(this)); + this.events.setCommandHandler("runcode:register", this.registerVar.bind(this)); + this.events.setCommandHandler("runcode:whitelist", this.whitelistVar.bind(this)); } private registerCommands() { @@ -50,6 +42,11 @@ class CodeRunner { this.events.setCommandHandler("runcode:eval", this.evalCode.bind(this)); } + private whitelistVar(varName: string, cb = () => { }) { + // @ts-ignore + this.vm._options.require.external.push(varName); // @ts-ignore + } + private registerVar(varName: string, code: any, cb = () => { }) { this.vm.registerVar(varName, code, cb); } @@ -61,8 +58,13 @@ class CodeRunner { return cb(null, ""); } + console.dir("running"); + console.dir(code); + this.vm.doEval(code, tolerateError, (err, result) => { if (err) { + console.dir("error") + console.dir(err) return cb(err); } diff --git a/packages/embark-code-runner/src/vm.ts b/packages/embark-code-runner/src/vm.ts index 4aad204f4..d738006a8 100644 --- a/packages/embark-code-runner/src/vm.ts +++ b/packages/embark-code-runner/src/vm.ts @@ -29,7 +29,8 @@ class VM { * Currently, all of the allowed external requires appear in the EmbarkJS scripts. If * the requires change in any of the EmbarkJS scripts, they will need to be updated here. */ - private _options: NodeVMOptions = { + // private _options: NodeVMOptions = { + public _options: NodeVMOptions = { require: { builtin: ["path", "util"], external: [ @@ -37,17 +38,18 @@ class VM { "@babel/runtime-corejs2/core-js/object/assign", "@babel/runtime-corejs2/core-js/promise", "@babel/runtime-corejs2/helpers/interopRequireDefault", - "embark-utils", + // "embark-utils", // TODO: ideally this shouldnt' be needed/here or should be configurable by the modules themselves somehow - "embarkjs-ens", - "embarkjs-ipfs", - "embarkjs-swarm", - "embarkjs-whisper", - "eth-ens-namehash", - "ipfs-api", + // "embarkjs-ens", + // "embarkjs-ipfs", + // "embarkjs-swarm", + // "embarkjs-whisper", + // "eth-ens-namehash", + // "ipfs-api", "rxjs", "rxjs/operators", - "swarm-api", + // "web3", + // "swarm-api", ], }, sandbox: { __dirname: dappPath() }, diff --git a/packages/embark-compiler/src/index.ts b/packages/embark-compiler/src/index.ts index e93a71684..ca904ab13 100644 --- a/packages/embark-compiler/src/index.ts +++ b/packages/embark-compiler/src/index.ts @@ -18,6 +18,9 @@ class Compiler { } private compile_contracts(contractFiles: any[], cb: any) { + console.dir("----- compile_contracts") + console.dir(contractFiles) + console.dir(cb) if (contractFiles.length === 0) { return cb(null, {}); } diff --git a/packages/embark-web3/package.json b/packages/embark-web3/package.json index f51c2853c..39f868fad 100644 --- a/packages/embark-web3/package.json +++ b/packages/embark-web3/package.json @@ -26,7 +26,7 @@ }, "main": "./dist/index.js", "scripts": { - "build": "cross-env BABEL_ENV=node babel src --extensions \".js\" --out-dir dist --root-mode upward --source-maps", + "build": "cross-env BABEL_ENV=node babel src --extensions \".js\" --out-dir dist --root-mode upward --source-maps --copy-files", "ci": "npm run qa", "clean": "npm run reset", "lint": "npm-run-all lint:*", @@ -46,7 +46,9 @@ "extends": "../../.eslintrc.json" }, "dependencies": { + "@babel/core": "7.2.2", "@babel/runtime-corejs2": "7.3.1", + "ejs": "2.6.1", "embark-core": "^4.1.0-beta.5", "embark-utils": "^4.1.0-beta.5", "embarkjs-web3": "^4.1.0-beta.4" diff --git a/packages/embark-web3/src/contract-artifact.js.ejs b/packages/embark-web3/src/contract-artifact.js.ejs new file mode 100644 index 000000000..dc0125963 --- /dev/null +++ b/packages/embark-web3/src/contract-artifact.js.ejs @@ -0,0 +1,13 @@ +const web3 = require("./web3_init.js") + +let <%- className %>Abi = <%- abi %>; +let <%- className %> = new web3.eth.Contract(<%- className %>Abi); +<%- className %>.options.address = '<%- contract.deployedAddress %>'; +<%- className %>.address = '<%- contract.deployedAddress %>'; +<%- className %>.options.from = web3.eth.defaultAccount; +<% if (gasLimit != false) { %> + <%- className %>.options.gas = <%- gasLimit %>; + <%- className %>.options.gasLimit = <%- gasLimit %>; +<% } %> + +module.exports = <%- className %>; diff --git a/packages/embark-web3/src/index.js b/packages/embark-web3/src/index.js index bf9cb7a07..f82d975ef 100644 --- a/packages/embark-web3/src/index.js +++ b/packages/embark-web3/src/index.js @@ -4,129 +4,89 @@ const { __ } = require('embark-i18n'); const { dappPath, embarkPath, normalizePath, toForwardSlashes } = require('embark-utils'); const constants = require('embark-core/constants'); const path = require('path'); +const Web3 = require('web3'); + +require('ejs'); +const Templates = { + vanilla_contract: require('./vanilla-contract.js.ejs'), + contract_artifact: require('./contract-artifact.js.ejs'), + web3_init: require('./web3_init.js.ejs'), +}; class EmbarkWeb3 { - constructor(embark, _options) { + constructor(embark, options) { + this.embarkConfig = embark.config.embarkConfig; this.embark = embark; this.logger = embark.logger; this.events = embark.events; this.fs = embark.fs; this.config = embark.config; - this.modulesPath = dappPath(embark.config.embarkConfig.generationDir, constants.dappArtifacts.symlinkDir); + let plugin = options.plugins.createPlugin('web3plugin', {}); - // this.addWeb3ToEmbarkJS(); + this.events.request("runcode:whitelist", 'web3', () => { }); + + this.events.on("blockchain:started", this.registerWeb3Object.bind(this)); + plugin.registerActionForEvent("pipeline:generateAll:before", this.addWeb3Artifact.bind(this)); + plugin.registerActionForEvent("deployment:contract:deployed", this.registerInVm.bind(this)); + plugin.registerActionForEvent("deployment:contract:deployed", this.registerArtifact.bind(this)); + + this.registerWeb3Help() } - async addWeb3ToEmbarkJS() { - let blockchainConnectorReady = false; - - code += "\nEmbarkJS.Blockchain.registerProvider('web3', embarkJSConnectorWeb3);"; - // code += "\nEmbarkJS.Blockchain.setProvider('web3', {});"; - - code += "\nEmbarkJS.Blockchain.setProvider('web3', {web3});"; - - this.events.request('runcode:eval', code, (err) => { - if (err) { - return cb(err); - } - }); + async registerWeb3Object() { + const web3 = new Web3("ws://localhost:8556"); + await this.events.request2("runcode:register", 'web3', web3); } - registerWeb3Help() { - this.events.request('console:register:helpCmd', { + async registerWeb3Help() { + await this.events.request2('console:register:helpCmd', { cmdName: "web3", cmdHelp: __("instantiated web3.js object configured to the current environment") - }, () => { }) - } - - // =============== - // =============== - // =============== - - let web3Location = await web3LocationPromise; - web3Location = normalizePath(web3Location, true); - - await this.registerVar('__Web3', require(web3Location)); - - const symlinkLocation = await this.generateSymlink(web3Location); - - let code = `\nconst Web3 = global.__Web3 || require('${symlinkLocation}');`; - code += `\nglobal.Web3 = Web3;`; - - let linkedModulePath = path.join(this.modulesPath, 'embarkjs-web3'); - if (process.platform === 'win32') linkedModulePath = linkedModulePath.replace(/\\/g, '\\\\'); - - code += `\n - const __embarkWeb3 = require('${linkedModulePath}'); - EmbarkJS.Blockchain.registerProvider('web3', __embarkWeb3.default || __embarkWeb3); - EmbarkJS.Blockchain.setProvider('web3', {}); - `; - - const configPath = toForwardSlashes(dappPath(this.config.embarkConfig.generationDir, constants.dappArtifacts.dir, constants.dappArtifacts.blockchain)); - - code += `\nif (!global.__Web3) {`; // Only connect when in the Dapp - code += `\n const web3ConnectionConfig = require('${configPath}');`; - code += `\n EmbarkJS.Blockchain.connect(web3ConnectionConfig, (err) => {if (err) { console.error(err); } });`; - code += `\n}`; - this.events.request('version:downloadIfNeeded', 'embarkjs-web3', (err, location) => { - if (err) { - this.logger.error(__('Error downloading embarkjs-web3')); - throw err; - } - - this.embark.addProviderInit("blockchain", code, () => { return true; }); - - // Make sure that we use our web3 for the console and the tests - code += `if (typeof web3 === 'undefined') { - throw new Error('Global web3 is not present'); - } - EmbarkJS.Blockchain.setProvider('web3', {web3});`; - - this.embark.addConsoleProviderInit("blockchain", code, () => { return true; }); - - this.embark.addGeneratedCode((cb) => { - return cb(null, code, 'embarkjs-web3', location); - }); }); } - getWeb3Location() { - return new Promise((resolve, reject) => { - this.events.request("version:get:web3", (web3Version) => { - if (web3Version === "1.0.0-beta") { - const nodePath = embarkPath('node_modules'); - const web3Path = require.resolve("web3", {paths: [nodePath]}); - return resolve(web3Path); - } - this.events.request("version:getPackageLocation", "web3", web3Version, (err, location) => { - if (err) { - return reject(err); - } - const locationPath = embarkPath(location); - resolve(locationPath); - }); - }); - }); + async registerInVm(params, cb) { + let contract = params.contract; + let abi = JSON.stringify(contract.abiDefinition); + let gasLimit = 6000000; + let contractCode = Templates.vanilla_contract({ className: contract.className, abi: abi, contract: contract, gasLimit: gasLimit }); + + try { + await this.events.request2('runcode:eval', contractCode); + await this.events.request2('runcode:eval', contract.className); + await this.events.request2("runcode:register", contract.className, result); + cb(); + } catch (err) { + cb(err); + } } - generateSymlink(location) { - return new Promise((resolve, reject) => { - this.events.request('code-generator:symlink:generate', location, 'web3', (err, symlinkDest) => { - if (err) { - return reject(err); - } - resolve(symlinkDest); - }); - }); + addWeb3Artifact(_params, cb) { + let web3Code = Templates.web3_init({}); + + this.events.request("pipeline:register", { + path: [this.embarkConfig.generationDir, 'contracts'], + file: 'web3_init.js', + format: 'js', + content: web3Code + }, cb); } - registerVar(name, code) { - return new Promise((resolve) => { - this.events.emit('runcode:register', name, code, () => { - resolve(); - }); - }); + registerArtifact(params, cb) { + let contract = params.contract; + let abi = JSON.stringify(contract.abiDefinition); + let gasLimit = 6000000; + + let contractCode = Templates.contract_artifact({ className: contract.className, abi: abi, contract: contract, gasLimit: gasLimit }); + + this.events.request("pipeline:register", { + path: [this.embarkConfig.generationDir, 'contracts'], + file: contract.className + '.js', + format: 'js', + content: contractCode + }, cb); } + } module.exports = EmbarkWeb3; diff --git a/packages/embark/src/lib/modules/web3/vanilla-contract.js.ejs b/packages/embark-web3/src/vanilla-contract.js.ejs similarity index 100% rename from packages/embark/src/lib/modules/web3/vanilla-contract.js.ejs rename to packages/embark-web3/src/vanilla-contract.js.ejs diff --git a/packages/embark-web3/src/web3_init.js.ejs b/packages/embark-web3/src/web3_init.js.ejs new file mode 100644 index 000000000..4123c9c9f --- /dev/null +++ b/packages/embark-web3/src/web3_init.js.ejs @@ -0,0 +1,3 @@ +const Web3 = require('web3') +const web3 = new Web3("ws://localhost:8556"); +module.exports = web3; diff --git a/packages/embark/src/cmd/cmd_controller.js b/packages/embark/src/cmd/cmd_controller.js index fb6a57903..989e57fc1 100644 --- a/packages/embark/src/cmd/cmd_controller.js +++ b/packages/embark/src/cmd/cmd_controller.js @@ -202,31 +202,40 @@ class EmbarkController { // } // engine.startService("fileWatcher"); - engine.startEngine(() => { + engine.startEngine(async () => { callback(); engine.events.request("webserver:start") - engine.events.request("config:contractsFiles", (contractsFiles) => { - engine.events.request("compiler:contracts:compile", contractsFiles, (err, compiledContracts) => { + let contractsFiles = await engine.events.request2("config:contractsFiles"); + + // engine.events.request("config:contractsFiles", (contractsFiles) => { + // engine.events.request("compiler:contracts:compile", contractsFiles, (err, compiledContracts) => { + let compiledContracts = await engine.events.request2("compiler:contracts:compile", contractsFiles); console.dir("compilation done") - // console.dir(compiledContracts) + console.dir(compiledContracts) console.dir("requesting contracts configuration") - engine.events.request("config:contractsConfig", (_contractsConfig) => { + // engine.events.request("config:contractsConfig", (_contractsConfig) => { + let _contractsConfig = await engine.events.request2("config:contractsConfig"); console.dir(_contractsConfig); let contractsConfig = cloneDeep(_contractsConfig); - engine.events.request("contracts:build", contractsConfig, compiledContracts, (err, contractsList, contractDependencies) => { - console.dir("contracts config build done") + // engine.events.request("contracts:build", contractsConfig, compiledContracts, (err, contractsList, contractDependencies) => { + let [contractsList, contractDependencies] = await engine.events.request2("contracts:build", contractsConfig, compiledContracts); - engine.events.request("deployment:contracts:deploy", contractsList, contractDependencies, () => { - console.dir("deployment done") - }) - }) - }) - }) - }) + // let [contractsList, contractDependencies] = await engine.events.request2("contracts:build", contractsConfig, compiledContracts); + console.dir("contracts config build done") + console.dir(contractsList) + console.dir(contractDependencies) + + // engine.events.request("deployment:contracts:deploy", contractsList, contractDependencies, () => { + await engine.events.request2("deployment:contracts:deploy", contractsList, contractDependencies); + console.dir("deployment done") + // }) + // }) + // }) + // }) }); }, diff --git a/packages/embark/src/lib/core/config.js b/packages/embark/src/lib/core/config.js index 5c348f692..989da7d11 100644 --- a/packages/embark/src/lib/core/config.js +++ b/packages/embark/src/lib/core/config.js @@ -69,7 +69,7 @@ Config.prototype.registerEvents = function() { }); this.events.setCommandHandler("config:contractsConfig", (cb) => { - cb(this.contractsConfig); + cb(null, this.contractsConfig); }); this.events.setCommandHandler("config:contractsConfig:set", this.setConfig.bind(this, 'contractsConfig')); @@ -79,7 +79,7 @@ Config.prototype.registerEvents = function() { this.events.setCommandHandler("config:communicationConfig:set", this.setConfig.bind(this, 'communicationConfig')); this.events.setCommandHandler("config:contractsFiles", (cb) => { - cb(this.contractsFiles); + cb(null, this.contractsFiles); }); // TODO: refactor this so reading the file can be done with a normal resolver or something that takes advantage of the plugin api diff --git a/packages/embark/src/lib/core/engine.js b/packages/embark/src/lib/core/engine.js index 3680f10b8..264fba1a8 100644 --- a/packages/embark/src/lib/core/engine.js +++ b/packages/embark/src/lib/core/engine.js @@ -186,8 +186,8 @@ class Engine { this.registerModule('blockchain-client'); this.registerModule('ethereum-blockchain-client'); - this.registerModule('web3', { plugins: this.plugins }); - this.registerModulePackage('embark-web3'); + // this.registerModule('web3', { plugins: this.plugins }); + this.registerModulePackage('embark-web3', {plugins: this.plugins}); } startService(serviceName, _options) { diff --git a/packages/embark/src/lib/core/events.js b/packages/embark/src/lib/core/events.js index 5be9b5e20..b1c57ddb4 100644 --- a/packages/embark/src/lib/core/events.js +++ b/packages/embark/src/lib/core/events.js @@ -49,6 +49,59 @@ EventEmitter.prototype.setHandler = function(requestName, cb) { return _setHandler.call(this, requestName, cb); }; +EventEmitter.prototype.get = function() { + let requestName = arguments[0]; + let other_args = [].slice.call(arguments, 1); + + log("get: ", requestName); + warnIfLegacy(requestName); + const listenerName = 'get:' + requestName; + + let promise = new Promise((resolve, reject) => { + return this.emit(listenerName, ...other_args, (err, res) => { + if (err) return reject(err); + return resolve(res); + }) + }); + + return promise; +}; + +EventEmitter.prototype.request2 = function() { + let requestName = arguments[0]; + let other_args = [].slice.call(arguments, 1); + + log("requesting: ", requestName); + console.log("requesting: " + requestName); + warnIfLegacy(requestName); + if (this._events && !this._events['request:' + requestName]) { + log("made request without listener: " + requestName) + console.log("made request without listener: " + requestName) + console.trace(); + } + + let promise = new Promise((resolve, reject) => { + console.dir("emitting... " + requestName) + + other_args.push( + (err, ...res) => { + if (err) return reject(err); + if (res.length && res.length > 1) { + return resolve(res); + } + return resolve(res[0]); + } + ) + + console.dir("----- other_args") + console.dir(other_args) + + this.emit('request:' + requestName, ...other_args) + }); + + return promise; +}; + EventEmitter.prototype.request = function() { let requestName = arguments[0]; let other_args = [].slice.call(arguments, 1); diff --git a/packages/embark/src/lib/modules/web3/index.js b/packages/embark/src/lib/modules/__web3_to_delete/index.js similarity index 85% rename from packages/embark/src/lib/modules/web3/index.js rename to packages/embark/src/lib/modules/__web3_to_delete/index.js index 413db8cb7..51bdc22ab 100644 --- a/packages/embark/src/lib/modules/web3/index.js +++ b/packages/embark/src/lib/modules/__web3_to_delete/index.js @@ -16,37 +16,12 @@ class Web3Plugin { this.plugins = options.plugins; let plugin = this.plugins.createPlugin('web3plugin', {}); - // plugin.registerActionForEvent("deployment:contract:deployed", this.registerInVm.bind(this)); plugin.registerActionForEvent("deployment:contract:deployed", this.addContractJSONToPipeline.bind(this)); plugin.registerActionForEvent("deployment:contract:deployed", this.addContractFileToPipeline.bind(this)); plugin.registerActionForEvent("pipeline:generateAll:before", this.addEmbarkJSNode.bind(this)); plugin.registerActionForEvent("pipeline:generateAll:before", this.addContractIndexToPipeline.bind(this)); } - registerInVm(params, cb) { - console.dir("-- registerInVm") - let contract = params.contract; - let abi = JSON.stringify(contract.abiDefinition); - let gasLimit = 6000000; - let contractCode = Templates.vanilla_contract({ className: contract.className, abi: abi, contract: contract, gasLimit: gasLimit }); - - this.events.request('runcode:eval', contractCode, (err) => { - if (err) { - console.dir("error!!!") - console.dir(err) - return cb(err); - } - this.events.request('runcode:eval', contract.className, (err, result) => { - if (err) { - console.dir("error!!!") - console.dir(err) - return cb(err); - } - this.events.emit("runcode:register", contract.className, result, () => { cb() }); - }); - }); - } - addContractJSONToPipeline(params, cb) { console.dir("-- addContractJSONToPipeline") // TODO: check if this is correct json object to generate diff --git a/packages/embark/src/lib/modules/__web3_to_delete/vanilla-contract.js.ejs b/packages/embark/src/lib/modules/__web3_to_delete/vanilla-contract.js.ejs new file mode 100644 index 000000000..908d5769e --- /dev/null +++ b/packages/embark/src/lib/modules/__web3_to_delete/vanilla-contract.js.ejs @@ -0,0 +1,9 @@ +<%- className %>Abi = <%- abi %>; +<%- className %> = new web3.eth.Contract(<%- className %>Abi); +<%- className %>.options.address = '<%- contract.deployedAddress %>'; +<%- className %>.address = '<%- contract.deployedAddress %>'; +<%- className %>.options.from = web3.eth.defaultAccount; +<% if (gasLimit != false) { %> + <%- className %>.options.gas = <%- gasLimit %>; + <%- className %>.options.gasLimit = <%- gasLimit %>; +<% } %> diff --git a/packages/embark/src/lib/modules/blockchain/index.js b/packages/embark/src/lib/modules/blockchain/index.js index f09fad6f8..721b01ae8 100644 --- a/packages/embark/src/lib/modules/blockchain/index.js +++ b/packages/embark/src/lib/modules/blockchain/index.js @@ -32,7 +32,7 @@ class Blockchain { } addArtifactFile(_params, cb) { - this.events.request("config:contractsConfig", (contractsConfig) => { + this.events.request("config:contractsConfig", (_err, contractsConfig) => { let config = { dappConnection: contractsConfig.dappConnection, dappAutoEnable: contractsConfig.dappAutoEnable,