embark-area-51/lib/core/engine.js

336 lines
11 KiB
JavaScript

const async = require('async');
const utils = require('../utils/utils');
const IPC = require('./ipc');
class Engine {
constructor(options) {
this.env = options.env;
this.isDev = options.isDev;
this.client = options.client;
this.locale = options.locale;
this.embarkConfig = options.embarkConfig;
this.interceptLogs = options.interceptLogs;
this.version = options.version;
this.logFile = options.logFile;
this.logLevel = options.logLevel;
this.events = options.events;
this.context = options.context;
}
init(_options) {
const Events = require('./events.js');
const Logger = require('./logger.js');
const Config = require('./config.js');
let options = _options || {};
this.events = options.events || this.events || new Events();
this.logger = options.logger || new Logger({logLevel: options.logLevel || this.logLevel || 'debug', events: this.events, logFile: this.logFile});
this.config = new Config({env: this.env, logger: this.logger, events: this.events, context: this.context});
this.config.loadConfigFiles({embarkConfig: this.embarkConfig, interceptLogs: this.interceptLogs});
this.plugins = this.config.plugins;
if (this.interceptLogs || this.interceptLogs === undefined) {
this.doInterceptLogs();
}
}
doInterceptLogs() {
var self = this;
let context = {};
context.console = console;
context.console.log = function() {
self.logger.info(utils.normalizeInput(arguments));
};
context.console.warn = function() {
self.logger.warn(utils.normalizeInput(arguments));
};
context.console.info = function() {
self.logger.info(utils.normalizeInput(arguments));
};
context.console.debug = function() {
// TODO: ue JSON.stringify
self.logger.debug(utils.normalizeInput(arguments));
};
context.console.trace = function() {
self.logger.trace(utils.normalizeInput(arguments));
};
context.console.dir = function() {
self.logger.dir(utils.normalizeInput(arguments));
};
}
registerModule(moduleName, options) {
this.plugins.loadInternalPlugin(moduleName, options || {});
}
startService(serviceName, _options) {
let options = _options || {};
let services = {
"serviceMonitor": this.serviceMonitor,
"pipeline": this.pipelineService,
"codeRunner": this.codeRunnerService,
"codeGenerator": this.codeGeneratorService,
"deployment": this.deploymentService,
"fileWatcher": this.fileWatchService,
"webServer": this.webServerService,
"namingSystem": this.namingSystem,
"web3": this.web3Service,
"libraryManager": this.libraryManagerService,
"storage": this.storageService
};
let service = services[serviceName];
if (!service) {
throw new Error("unknown service: " + serviceName);
}
// need to be careful with circular references due to passing the web3 object
//this.logger.trace("calling: " + serviceName + "(" + JSON.stringify(options) + ")");
return service.apply(this, [options]);
}
pipelineService(_options) {
const self = this;
this.events.emit("status", "Building Assets");
const Pipeline = require('../pipeline/pipeline.js');
const pipeline = new Pipeline({
buildDir: this.config.buildDir,
contractsFiles: this.config.contractsFiles,
assetFiles: this.config.assetFiles,
events: this.events,
logger: this.logger,
plugins: this.plugins
});
this.events.on('code-generator-ready', function () {
self.events.request('code', function (abi, contractsJSON) {
pipeline.build(abi, contractsJSON, null, () => {
if (self.watch) {
self.watch.restart(); // Necessary because changing a file while it is writing can stop it from being watched
}
self.events.emit('outputDone');
});
});
});
}
serviceMonitor() {
const self = this;
const ServicesMonitor = require('./services_monitor.js');
this.servicesMonitor = new ServicesMonitor({events: this.events, logger: this.logger, plugins: this.plugins});
this.servicesMonitor.addCheck('embarkVersion', function (cb) {
return cb({name: 'Embark ' + self.version, status: 'on'});
}, 0);
this.servicesMonitor.startMonitor();
}
namingSystem(_options) {
this.registerModule('ens');
}
codeRunnerService(_options) {
const CodeRunner = require('../coderunner/codeRunner.js');
this.codeRunner = new CodeRunner({
plugins: this.plugins,
events: this.events,
logger: this.logger
});
}
codeGeneratorService(_options) {
let self = this;
const CodeGenerator = require('../contracts/code_generator.js');
this.codeGenerator = new CodeGenerator({
blockchainConfig: self.config.blockchainConfig,
contractsConfig: self.config.contractsConfig,
plugins: self.plugins,
storageConfig: self.config.storageConfig,
namesystemConfig: self.config.namesystemConfig,
communicationConfig: self.config.communicationConfig,
events: self.events,
env: self.env
});
this.codeGenerator.listenToCommands();
const generateCode = function () {
self.codeGenerator.buildEmbarkJS(function() {
self.events.emit('code-generator-ready');
});
};
const cargo = async.cargo((_tasks, callback) => {
generateCode();
self.events.once('outputDone', callback);
});
const addToCargo = function () {
cargo.push({});
};
this.events.on('contractsDeployed', addToCargo);
this.events.on('blockchainDisabled', addToCargo);
this.events.on('asset-changed', addToCargo);
}
deploymentService(options) {
let self = this;
const Compiler = require('../contracts/compiler.js');
let compiler = new Compiler({plugins: self.plugins, logger: self.logger});
this.events.setCommandHandler("compiler:contracts", function(contractFiles, cb) {
compiler.compile_contracts(contractFiles, cb);
});
this.ipc = new IPC({logger: this.logger, ipcRole: options.ipcRole});
if (this.ipc.isServer()) {
this.ipc.serve();
}
this.registerModule('solidity', {ipc: this.ipc});
this.registerModule('vyper');
this.registerModule('profiler');
this.registerModule('fuzzer');
this.registerModule('deploytracker');
this.registerModule('specialconfigs');
const ContractsManager = require('../contracts/contracts.js');
this.contractsManager = new ContractsManager({
contractFiles: this.config.contractsFiles,
contractsConfig: this.config.contractsConfig,
logger: this.logger,
plugins: this.plugins,
gasLimit: false,
events: this.events
});
const DeployManager = require('../contracts/deploy_manager.js');
this.deployManager = new DeployManager({
blockchain: this.blockchain,
config: this.config,
logger: this.logger,
plugins: this.plugins,
events: this.events,
onlyCompile: options.onlyCompile
});
const ContractDeployer = require('../contracts/contract_deployer.js');
this.contractDeployer = new ContractDeployer({
blockchain: this.blockchain,
logger: this.logger,
events: this.events,
plugins: this.plugins
});
// Console logger
// TODO: extract to its own file
let addressToContract = {};
this.ipc.on('log', (jsonObj) => {
if(jsonObj.type == 'contract-log'){
if(!addressToContract[jsonObj.address]){
let contractList = Object.keys(this.config.contractsConfig.contracts);
for(let i = 0; i < contractList.length; i++){
let cont = this.config.contractsConfig.contracts[contractList[i]];
if(!addressToContract[cont.deployedAddress.toLowerCase()]){
let funcSignatures = {};
cont.abiDefinition
.filter(func => func.type == "function")
.map(func => func.name + '(' +
(func.inputs ? func.inputs.map(input => input.type).join(',') : '') +
')')
.forEach(func => {
funcSignatures[utils.sha3(func).substring(0, 10)] = func;
});
addressToContract[cont.deployedAddress.toLowerCase()] = {
name: contractList[i],
functions: funcSignatures
};
}
}
}
let funcHash = addressToContract[jsonObj.address].functions[jsonObj.data.substring(0, 10)];
this.logger.debug(addressToContract[jsonObj.address].name + "." + funcHash + " : " + jsonObj.transactionHash);
} else {
this.logger.info(jsonObj);
}
});
this.events.on('file-event', function (fileType) {
clearTimeout(self.fileTimeout);
self.fileTimeout = setTimeout(() => {
// TODO: still need to redeploy contracts because the original contracts
// config is being corrupted
if (fileType === 'asset') {
// Throttle file changes so we re-write only once for all files
self.events.emit('asset-changed', self.contractsManager);
}
// TODO: for now need to deploy on asset changes as well
// because the contractsManager config is corrupted after a deploy
if (fileType === 'contract' || fileType === 'config') {
self.config.reloadConfig();
self.events.request('deploy:contracts', () => {});
}
}, 50);
});
}
fileWatchService(_options) {
this.events.emit("status", "Watching for changes");
const Watch = require('../pipeline/watch.js');
this.watch = new Watch({logger: this.logger, events: this.events});
this.watch.start();
}
webServerService() {
this.registerModule('webserver');
}
storageService(_options) {
this.registerModule('storage', {
storageConfig: this.config.storageConfig,
webServerConfig: this.config.webServerConfig,
blockchainConfig: this.config.blockchainConfig,
host: _options.host,
port: _options.port,
servicesMonitor: this.servicesMonitor,
events: this.events,
logger: this.logger,
context: this.context
});
}
web3Service(options) {
const Blockchain = require('../contracts/blockchain.js');
this.blockchain = new Blockchain({
contractsConfig: this.config.contractsConfig,
blockchainConfig: this.config.blockchainConfig,
events: this.events,
logger: this.logger,
isDev: this.isDev,
locale: this.locale,
web3: options.web3
});
this.registerModule('whisper', {
// TODO: this should not be needed and should be deducted from the config instead
// the eth provider is not necessary the same as the whisper one
web3: this.blockchain.web3
});
}
libraryManagerService(_options) {
const LibraryManager = require('../versions/library_manager.js');
this.libraryManager = new LibraryManager({
plugins: this.plugins,
config: this.config
});
}
}
module.exports = Engine;