implement embark-web3; add request2 to event bus with promise support

This commit is contained in:
Iuri Matias 2019-07-25 12:20:39 -04:00
parent dc2e68f0f2
commit f838739e17
16 changed files with 200 additions and 168 deletions

View File

@ -1,5 +1,6 @@
import {SimpleStorage} from '../../embarkArtifacts/contracts';
// import {SimpleStorage} from '../../embarkArtifacts/contracts';
import SimpleStorage from '../../embarkArtifacts/contracts/SimpleStorage.js';
window.SimpleStorage = SimpleStorage;

View File

@ -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);
}

View File

@ -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() },

View File

@ -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, {});
}

View File

@ -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"

View File

@ -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 %>;

View File

@ -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;

View File

@ -0,0 +1,3 @@
const Web3 = require('web3')
const web3 = new Web3("ws://localhost:8556");
module.exports = web3;

View File

@ -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")
// })
// })
// })
// })
});
},

View File

@ -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

View File

@ -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) {

View File

@ -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);

View File

@ -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

View File

@ -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 %>;
<% } %>

View File

@ -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,