Merge branch 'develop'

This commit is contained in:
Iuri Matias 2018-06-20 15:55:16 -04:00
commit a6195d2517
157 changed files with 13883 additions and 4646 deletions

View File

@ -9,6 +9,9 @@
"sourceType": "module",
"ecmaVersion": 2017
},
"globals": {
"__": true
},
"rules": {
"accessor-pairs": "error",
"array-bracket-newline": "error",

View File

@ -1,6 +1,6 @@
language: node_js
node_js:
- "7"
- "8"
addons:
code_climate:
repo_token: 7454b1a666015e244c384d19f48c34e35d1ae58c3aa428ec542f10bbcb848358

View File

@ -1,170 +1,235 @@
var EmbarkJS = {};
var EmbarkJS = {
onReady: function (cb) {
if (typeof (__embarkContext) === 'undefined') {
return cb();
}
return __embarkContext.execWhenReady(cb);
}
};
EmbarkJS.isNewWeb3 = function() {
var _web3 = new Web3();
EmbarkJS.isNewWeb3 = function (web3Obj) {
var _web3 = web3Obj || (new Web3());
if (typeof(_web3.version) === "string") {
return true;
}
return parseInt(_web3.version.api.split('.')[0], 10) >= 1;
};
EmbarkJS.Contract = function(options) {
var self = this;
var i, abiElement;
var ContractClass;
EmbarkJS.Contract = function (options) {
var self = this;
var i, abiElement;
var ContractClass;
this.abi = options.abi;
this.address = options.address;
this.code = '0x' + options.code;
//this.web3 = options.web3 || web3;
this.web3 = options.web3 || window.web3;
this.abi = options.abi;
this.address = options.address;
this.gas = options.gas;
this.code = '0x' + options.code;
//this.web3 = options.web3 || web3;
this.web3 = options.web3;
if (!this.web3 && typeof (web3) !== 'undefined') {
this.web3 = web3;
} else if (!this.web3) {
this.web3 = window.web3;
}
if (EmbarkJS.isNewWeb3()) {
// TODO:
// add default **from** address
// add gasPrice
ContractClass = new this.web3.eth.Contract(this.abi, this.address);
ContractClass.setProvider(this.web3.currentProvider);
ContractClass.options.data = this.code;
if (EmbarkJS.isNewWeb3(this.web3)) {
ContractClass = new this.web3.eth.Contract(this.abi, this.address);
ContractClass.setProvider(this.web3.currentProvider);
ContractClass.options.data = this.code;
ContractClass.options.from = this.from || this.web3.eth.defaultAccount;
ContractClass.abi = ContractClass.options.abi;
ContractClass.address = this.address;
ContractClass.gas = this.gas;
return ContractClass;
} else {
ContractClass = this.web3.eth.contract(this.abi);
let originalMethods = Object.keys(ContractClass);
this.eventList = [];
if (this.abi) {
for (i = 0; i < this.abi.length; i++) {
abiElement = this.abi[i];
if (abiElement.type === 'event') {
this.eventList.push(abiElement.name);
}
}
ContractClass._jsonInterface.forEach((abi) => {
if (originalMethods.indexOf(abi.name) >= 0) {
console.log(abi.name + " is a reserved word and cannot be used as a contract method, property or event");
return;
}
var messageEvents = function() {
this.cb = function() {};
if (!abi.inputs) {
return;
}
let numExpectedInputs = abi.inputs.length;
if (abi.type === 'function' && abi.constant) {
ContractClass[abi.name] = function () {
let options = {}, cb = null, args = Array.from(arguments || []).slice(0, numExpectedInputs);
if (typeof (arguments[numExpectedInputs]) === 'function') {
cb = arguments[numExpectedInputs];
} else if (typeof (arguments[numExpectedInputs]) === 'object') {
options = arguments[numExpectedInputs];
cb = arguments[numExpectedInputs + 1];
}
let ref = ContractClass.methods[abi.name];
let call = ref.apply(ref, ...arguments).call;
return call.apply(call, []);
};
} else if (abi.type === 'function') {
ContractClass[abi.name] = function () {
let options = {}, cb = null, args = Array.from(arguments || []).slice(0, numExpectedInputs);
if (typeof (arguments[numExpectedInputs]) === 'function') {
cb = arguments[numExpectedInputs];
} else if (typeof (arguments[numExpectedInputs]) === 'object') {
options = arguments[numExpectedInputs];
cb = arguments[numExpectedInputs + 1];
}
let ref = ContractClass.methods[abi.name];
let send = ref.apply(ref, args).send;
return send.apply(send, [options, cb]);
};
} else if (abi.type === 'event') {
ContractClass[abi.name] = function (options, cb) {
let ref = ContractClass.events[abi.name];
return ref.apply(ref, [options, cb]);
};
}
});
return ContractClass;
} else {
ContractClass = this.web3.eth.contract(this.abi);
this.eventList = [];
if (this.abi) {
for (i = 0; i < this.abi.length; i++) {
abiElement = this.abi[i];
if (abiElement.type === 'event') {
this.eventList.push(abiElement.name);
}
}
}
var messageEvents = function () {
this.cb = function () {
};
};
messageEvents.prototype.then = function(cb) {
this.cb = cb;
};
messageEvents.prototype.then = function (cb) {
this.cb = cb;
};
messageEvents.prototype.error = function(err) {
return err;
};
messageEvents.prototype.error = function (err) {
return err;
};
this._originalContractObject = ContractClass.at(this.address);
this._methods = Object.getOwnPropertyNames(this._originalContractObject).filter(function(p) {
// TODO: check for forbidden properties
if (self.eventList.indexOf(p) >= 0) {
this._originalContractObject = ContractClass.at(this.address);
this._methods = Object.getOwnPropertyNames(this._originalContractObject).filter(function (p) {
// TODO: check for forbidden properties
if (self.eventList.indexOf(p) >= 0) {
self[p] = function() {
var promise = new messageEvents();
var args = Array.prototype.slice.call(arguments);
args.push(function(err, result) {
self[p] = function () {
var promise = new messageEvents();
var args = Array.prototype.slice.call(arguments);
args.push(function (err, result) {
if (err) {
promise.error(err);
} else {
promise.cb(result);
}
});
self._originalContractObject[p].apply(self._originalContractObject[p], args);
return promise;
};
return true;
} else if (typeof self._originalContractObject[p] === 'function') {
self[p] = function (_args) {
var args = Array.prototype.slice.call(arguments);
var fn = self._originalContractObject[p];
var props = self.abi.find((x) => x.name == p);
var promise = new Promise(function (resolve, reject) {
args.push(function (err, transaction) {
promise.tx = transaction;
if (err) {
promise.error(err);
return reject(err);
}
var getConfirmation = function () {
self.web3.eth.getTransactionReceipt(transaction, function (err, receipt) {
if (err) {
return reject(err);
}
if (receipt !== null) {
return resolve(receipt);
}
setTimeout(getConfirmation, 1000);
});
};
if (typeof transaction !== "string" || props.constant) {
resolve(transaction);
} else {
promise.cb(result);
getConfirmation();
}
});
self._originalContractObject[p].apply(self._originalContractObject[p], args);
return promise;
};
return true;
} else if (typeof self._originalContractObject[p] === 'function') {
self[p] = function(_args) {
var args = Array.prototype.slice.call(arguments);
var fn = self._originalContractObject[p];
var props = self.abi.find((x) => x.name == p);
fn.apply(fn, args);
});
var promise = new Promise(function(resolve, reject) {
args.push(function(err, transaction) {
promise.tx = transaction;
if (err) {
return reject(err);
}
var getConfirmation = function() {
self.web3.eth.getTransactionReceipt(transaction, function(err, receipt) {
if (err) {
return reject(err);
}
if (receipt !== null) {
return resolve(receipt);
}
setTimeout(getConfirmation, 1000);
});
};
if (typeof(transaction) !== "string" || props.constant) {
resolve(transaction);
} else {
getConfirmation();
}
});
fn.apply(fn, args);
});
return promise;
};
return true;
}
return false;
});
}
return promise;
};
return true;
}
return false;
});
}
};
EmbarkJS.Contract.prototype.deploy = function(args, _options) {
var self = this;
var contractParams;
var options = _options || {};
EmbarkJS.Contract.prototype.deploy = function (args, _options) {
var self = this;
var contractParams;
var options = _options || {};
contractParams = args || [];
contractParams = args || [];
contractParams.push({
from: this.web3.eth.accounts[0],
data: this.code,
gas: options.gas || 800000
contractParams.push({
from: this.web3.eth.accounts[0],
data: this.code,
gas: options.gas || 800000
});
var contractObject = this.web3.eth.contract(this.abi);
var promise = new Promise(function (resolve, reject) {
contractParams.push(function (err, transaction) {
if (err) {
reject(err);
} else if (transaction.address !== undefined) {
resolve(new EmbarkJS.Contract({
abi: self.abi,
code: self.code,
address: transaction.address
}));
}
});
var contractObject = this.web3.eth.contract(this.abi);
var promise = new Promise(function(resolve, reject) {
contractParams.push(function(err, transaction) {
if (err) {
reject(err);
} else if (transaction.address !== undefined) {
resolve(new EmbarkJS.Contract({
abi: self.abi,
code: self.code,
address: transaction.address
}));
}
});
// returns promise
// deploys contract
// wraps it around EmbarkJS.Contract
contractObject["new"].apply(contractObject, contractParams);
});
// returns promise
// deploys contract
// wraps it around EmbarkJS.Contract
contractObject["new"].apply(contractObject, contractParams);
});
return promise;
return promise;
};
EmbarkJS.Contract.prototype.new = EmbarkJS.Contract.prototype.deploy;
EmbarkJS.Contract.prototype.at = function(address) {
return new EmbarkJS.Contract({ abi: this.abi, code: this.code, address: address });
EmbarkJS.Contract.prototype.at = function (address) {
return new EmbarkJS.Contract({abi: this.abi, code: this.code, address: address});
};
EmbarkJS.Contract.prototype.send = function(value, unit, _options) {
EmbarkJS.Contract.prototype.send = function (value, unit, _options) {
var options, wei;
if (typeof unit === 'object') {
options = unit;
@ -184,51 +249,54 @@ EmbarkJS.Storage = {};
EmbarkJS.Storage.Providers = {};
EmbarkJS.Storage.saveText = function(text) {
EmbarkJS.Storage.saveText = function (text) {
if (!this.currentStorage) {
throw new Error('Storage provider not set; e.g EmbarkJS.Storage.setProvider("ipfs")');
}
return this.currentStorage.saveText(text);
};
EmbarkJS.Storage.get = function(hash) {
EmbarkJS.Storage.get = function (hash) {
if (!this.currentStorage) {
throw new Error('Storage provider not set; e.g EmbarkJS.Storage.setProvider("ipfs")');
}
return this.currentStorage.get(hash);
};
EmbarkJS.Storage.uploadFile = function(inputSelector) {
EmbarkJS.Storage.uploadFile = function (inputSelector) {
if (!this.currentStorage) {
throw new Error('Storage provider not set; e.g EmbarkJS.Storage.setProvider("ipfs")');
}
return this.currentStorage.uploadFile(inputSelector);
};
EmbarkJS.Storage.getUrl = function(hash) {
EmbarkJS.Storage.getUrl = function (hash) {
if (!this.currentStorage) {
throw new Error('Storage provider not set; e.g EmbarkJS.Storage.setProvider("ipfs")');
}
return this.currentStorage.getUrl(hash);
};
EmbarkJS.Storage.registerProvider = function(providerName, obj) {
EmbarkJS.Storage.registerProvider = function (providerName, obj) {
EmbarkJS.Storage.Providers[providerName] = obj;
};
EmbarkJS.Storage.setProvider = function(provider, options) {
EmbarkJS.Storage.setProvider = function (provider, options) {
let providerObj = this.Providers[provider];
if (!providerObj) {
throw new Error('Unknown storage provider');
}
}
this.currentStorage = providerObj;
return providerObj.setProvider(options);
};
EmbarkJS.Storage.isAvailable = function(){
EmbarkJS.Storage.isAvailable = function () {
if (!this.currentStorage) {
throw new Error('Storage provider not set; e.g EmbarkJS.Storage.setProvider("ipfs")');
}
return this.currentStorage.isAvailable();
};
@ -236,11 +304,11 @@ EmbarkJS.Messages = {};
EmbarkJS.Messages.Providers = {};
EmbarkJS.Messages.registerProvider = function(providerName, obj) {
EmbarkJS.Messages.registerProvider = function (providerName, obj) {
EmbarkJS.Messages.Providers[providerName] = obj;
};
EmbarkJS.Messages.setProvider = function(provider, options) {
EmbarkJS.Messages.setProvider = function (provider, options) {
let providerObj = this.Providers[provider];
if (!providerObj) {
@ -252,30 +320,75 @@ EmbarkJS.Messages.setProvider = function(provider, options) {
return providerObj.setProvider(options);
};
EmbarkJS.Messages.isAvailable = function(){
EmbarkJS.Messages.isAvailable = function () {
return this.currentMessages.isAvailable();
};
EmbarkJS.Messages.sendMessage = function(options) {
EmbarkJS.Messages.sendMessage = function (options) {
if (!this.currentMessages) {
throw new Error('Messages provider not set; e.g EmbarkJS.Messages.setProvider("whisper")');
}
return this.currentMessages.sendMessage(options);
};
EmbarkJS.Messages.listenTo = function(options, callback) {
EmbarkJS.Messages.listenTo = function (options, callback) {
if (!this.currentMessages) {
throw new Error('Messages provider not set; e.g EmbarkJS.Messages.setProvider("whisper")');
}
return this.currentMessages.listenTo(options, callback);
};
EmbarkJS.Names = {};
EmbarkJS.Names.Providers = {};
EmbarkJS.Names.registerProvider = function (providerName, obj) {
EmbarkJS.Names.Providers[providerName] = obj;
};
EmbarkJS.Names.setProvider = function (provider, options) {
let providerObj = this.Providers[provider];
if (!providerObj) {
throw new Error('Unknown name system provider');
}
this.currentNameSystems = providerObj;
return providerObj.setProvider(options);
};
// resolve resolves a name into an identifier of some kind
EmbarkJS.Names.resolve = function (name) {
if (!this.currentNameSystems) {
throw new Error('Name system provider not set; e.g EmbarkJS.Names.setProvider("ens")');
}
return this.currentNameSystems.resolve(name);
};
// the reverse of resolve, resolves using an identifier to get to a name
EmbarkJS.Names.lookup = function (identifier) {
if (!this.currentNameSystems) {
throw new Error('Name system provider not set; e.g EmbarkJS.Names.setProvider("ens")');
}
return this.currentNameSystems.lookup(identifier);
};
// To Implement
/*
// register a name
EmbarkJS.Names.register = function(name, options) {
}
*/
EmbarkJS.Utils = {
fromAscii: function(str) {
fromAscii: function (str) {
var _web3 = new Web3();
return _web3.utils ? _web3.utils.fromAscii(str) : _web3.fromAscii(str);
},
toAscii: function(str) {
toAscii: function (str) {
var _web3 = new Web3();
return _web3.utils.toAscii(str);
}

396
js/embark_node.js Normal file
View File

@ -0,0 +1,396 @@
var EmbarkJS = {
onReady: function(cb) {
if (typeof (__embarkContext) === 'undefined') {
return cb();
}
return __embarkContext.execWhenReady(cb);
}
};
EmbarkJS.isNewWeb3 = function(web3Obj) {
var _web3 = web3Obj || (new Web3());
if (typeof(_web3.version) === "string") {
return true;
}
return parseInt(_web3.version.api.split('.')[0], 10) >= 1;
};
EmbarkJS.Contract = function(options) {
var self = this;
var i, abiElement;
var ContractClass;
this.abi = options.abi;
this.address = options.address;
this.gas = options.gas;
this.code = '0x' + options.code;
//this.web3 = options.web3 || web3;
this.web3 = options.web3;
if (!this.web3 && typeof (web3) !== 'undefined') {
this.web3 = web3;
} else if (!this.web3) {
this.web3 = window.web3;
}
if (EmbarkJS.isNewWeb3(this.web3)) {
ContractClass = new this.web3.eth.Contract(this.abi, this.address);
ContractClass.setProvider(this.web3.currentProvider);
ContractClass.options.data = this.code;
ContractClass.options.from = this.from || this.web3.eth.defaultAccount;
ContractClass.abi = ContractClass.options.abi;
ContractClass.address = this.address;
ContractClass.gas = this.gas;
let originalMethods = Object.keys(ContractClass);
ContractClass._jsonInterface.forEach((abi) => {
if (originalMethods.indexOf(abi.name) >= 0) {
console.log(abi.name + " is a reserved word and cannot be used as a contract method, property or event");
return;
}
if (!abi.inputs) {
return;
}
let numExpectedInputs = abi.inputs.length;
if (abi.type === 'function' && abi.constant) {
ContractClass[abi.name] = function() {
let options = {}, cb = null, args = Array.from(arguments || []).slice(0, numExpectedInputs);
if (typeof (arguments[numExpectedInputs]) === 'function') {
cb = arguments[numExpectedInputs];
} else if (typeof (arguments[numExpectedInputs]) === 'object') {
options = arguments[numExpectedInputs];
cb = arguments[numExpectedInputs + 1];
}
let ref = ContractClass.methods[abi.name];
let call = ref.apply(ref, ...arguments).call;
return call.apply(call, []);
};
} else if (abi.type === 'function') {
ContractClass[abi.name] = function() {
let options = {}, cb = null, args = Array.from(arguments || []).slice(0, numExpectedInputs);
if (typeof (arguments[numExpectedInputs]) === 'function') {
cb = arguments[numExpectedInputs];
} else if (typeof (arguments[numExpectedInputs]) === 'object') {
options = arguments[numExpectedInputs];
cb = arguments[numExpectedInputs + 1];
}
let ref = ContractClass.methods[abi.name];
let send = ref.apply(ref, args).send;
return send.apply(send, [options, cb]);
};
} else if (abi.type === 'event') {
ContractClass[abi.name] = function(options, cb) {
let ref = ContractClass.events[abi.name];
return ref.apply(ref, [options, cb]);
}
}
});
return ContractClass;
} else {
ContractClass = this.web3.eth.contract(this.abi);
this.eventList = [];
if (this.abi) {
for (i = 0; i < this.abi.length; i++) {
abiElement = this.abi[i];
if (abiElement.type === 'event') {
this.eventList.push(abiElement.name);
}
}
}
var messageEvents = function() {
this.cb = function() {};
};
messageEvents.prototype.then = function(cb) {
this.cb = cb;
};
messageEvents.prototype.error = function(err) {
return err;
};
this._originalContractObject = ContractClass.at(this.address);
this._methods = Object.getOwnPropertyNames(this._originalContractObject).filter(function(p) {
// TODO: check for forbidden properties
if (self.eventList.indexOf(p) >= 0) {
self[p] = function() {
var promise = new messageEvents();
var args = Array.prototype.slice.call(arguments);
args.push(function(err, result) {
if (err) {
promise.error(err);
} else {
promise.cb(result);
}
});
self._originalContractObject[p].apply(self._originalContractObject[p], args);
return promise;
};
return true;
} else if (typeof self._originalContractObject[p] === 'function') {
self[p] = function(_args) {
var args = Array.prototype.slice.call(arguments);
var fn = self._originalContractObject[p];
var props = self.abi.find((x) => x.name == p);
var promise = new Promise(function(resolve, reject) {
args.push(function(err, transaction) {
promise.tx = transaction;
if (err) {
return reject(err);
}
var getConfirmation = function() {
self.web3.eth.getTransactionReceipt(transaction, function(err, receipt) {
if (err) {
return reject(err);
}
if (receipt !== null) {
return resolve(receipt);
}
setTimeout(getConfirmation, 1000);
});
};
if (typeof(transaction) !== "string" || props.constant) {
resolve(transaction);
} else {
getConfirmation();
}
});
fn.apply(fn, args);
});
return promise;
};
return true;
}
return false;
});
}
};
EmbarkJS.Contract.prototype.deploy = function(args, _options) {
var self = this;
var contractParams;
var options = _options || {};
contractParams = args || [];
contractParams.push({
from: this.web3.eth.accounts[0],
data: this.code,
gas: options.gas || 800000
});
var contractObject = this.web3.eth.contract(this.abi);
var promise = new Promise(function(resolve, reject) {
contractParams.push(function(err, transaction) {
if (err) {
reject(err);
} else if (transaction.address !== undefined) {
resolve(new EmbarkJS.Contract({
abi: self.abi,
code: self.code,
address: transaction.address
}));
}
});
// returns promise
// deploys contract
// wraps it around EmbarkJS.Contract
contractObject["new"].apply(contractObject, contractParams);
});
return promise;
};
EmbarkJS.Contract.prototype.new = EmbarkJS.Contract.prototype.deploy;
EmbarkJS.Contract.prototype.at = function(address) {
return new EmbarkJS.Contract({ abi: this.abi, code: this.code, address: address });
};
EmbarkJS.Contract.prototype.send = function(value, unit, _options) {
var options, wei;
if (typeof unit === 'object') {
options = unit;
wei = value;
} else {
options = _options || {};
wei = this.web3.toWei(value, unit);
}
options.to = this.address;
options.value = wei;
this.web3.eth.sendTransaction(options);
};
EmbarkJS.Storage = {};
EmbarkJS.Storage.Providers = {};
EmbarkJS.Storage.saveText = function(text) {
if (!this.currentStorage) {
throw new Error('Storage provider not set; e.g EmbarkJS.Storage.setProvider("ipfs")');
}
return this.currentStorage.saveText(text);
};
EmbarkJS.Storage.get = function(hash) {
if (!this.currentStorage) {
throw new Error('Storage provider not set; e.g EmbarkJS.Storage.setProvider("ipfs")');
}
return this.currentStorage.get(hash);
};
EmbarkJS.Storage.uploadFile = function(inputSelector) {
if (!this.currentStorage) {
throw new Error('Storage provider not set; e.g EmbarkJS.Storage.setProvider("ipfs")');
}
return this.currentStorage.uploadFile(inputSelector);
};
EmbarkJS.Storage.getUrl = function(hash) {
if (!this.currentStorage) {
throw new Error('Storage provider not set; e.g EmbarkJS.Storage.setProvider("ipfs")');
}
return this.currentStorage.getUrl(hash);
};
EmbarkJS.Storage.registerProvider = function(providerName, obj) {
EmbarkJS.Storage.Providers[providerName] = obj;
};
EmbarkJS.Storage.setProvider = function(provider, options) {
let providerObj = this.Providers[provider];
if (!providerObj) {
throw new Error('Unknown storage provider');
}
this.currentStorage = providerObj;
return providerObj.setProvider(options);
};
EmbarkJS.Storage.isAvailable = function(){
if (!this.currentStorage) {
throw new Error('Storage provider not set; e.g EmbarkJS.Storage.setProvider("ipfs")');
}
return this.currentStorage.isAvailable();
};
EmbarkJS.Messages = {};
EmbarkJS.Messages.Providers = {};
EmbarkJS.Messages.registerProvider = function(providerName, obj) {
EmbarkJS.Messages.Providers[providerName] = obj;
};
EmbarkJS.Messages.setProvider = function(provider, options) {
let providerObj = this.Providers[provider];
if (!providerObj) {
throw new Error('Unknown messages provider');
}
this.currentMessages = providerObj;
return providerObj.setProvider(options);
};
EmbarkJS.Messages.isAvailable = function(){
return this.currentMessages.isAvailable();
};
EmbarkJS.Messages.sendMessage = function(options) {
if (!this.currentMessages) {
throw new Error('Messages provider not set; e.g EmbarkJS.Messages.setProvider("whisper")');
}
return this.currentMessages.sendMessage(options);
};
EmbarkJS.Messages.listenTo = function(options, callback) {
if (!this.currentMessages) {
throw new Error('Messages provider not set; e.g EmbarkJS.Messages.setProvider("whisper")');
}
return this.currentMessages.listenTo(options, callback);
};
EmbarkJS.Names = {};
EmbarkJS.Names.Providers = {};
EmbarkJS.Names.registerProvider = function(providerName, obj) {
EmbarkJS.Names.Providers[providerName] = obj;
}
EmbarkJS.Names.setProvider = function(provider, options) {
let providerObj = this.Providers[provider];
if (!providerObj) {
throw new Error('Unknown name system provider');
}
this.currentNameSystems = providerObj;
return providerObj.setProvider(options);
};
// resolve resolves a name into an identifier of some kind
EmbarkJS.Names.resolve = function(name) {
if (!this.currentNameSystems) {
throw new Error('Name system provider not set; e.g EmbarkJS.Names.setProvider("ens")');
}
return this.currentNameSystems.resolve(name);
}
// the reverse of resolve, resolves using an identifier to get to a name
EmbarkJS.Names.lookup = function(identifier) {
if (!this.currentNameSystems) {
throw new Error('Name system provider not set; e.g EmbarkJS.Names.setProvider("ens")');
}
return this.currentNameSystems.lookup(identifier);
}
// To Implement
/*
// register a name
EmbarkJS.Names.register = function(name, options) {
}
*/
EmbarkJS.Utils = {
fromAscii: function(str) {
var _web3 = new Web3();
return _web3.utils ? _web3.utils.fromAscii(str) : _web3.fromAscii(str);
},
toAscii: function(str) {
var _web3 = new Web3();
return _web3.utils.toAscii(str);
}
};
module.exports = EmbarkJS;

View File

@ -1,95 +0,0 @@
EmbarkJS.Messages.Orbit = {};
EmbarkJS.Messages.Orbit.setProvider = function(options) {
this.providerName = 'orbit';
this.currentMessages = EmbarkJS.Messages.Orbit;
if (options === undefined) {
ipfs = HaadIpfsApi('localhost', '5001');
} else {
ipfs = HaadIpfsApi(options.host, options.port);
}
this.currentMessages.orbit = new Orbit(ipfs);
if (typeof(web3) === "undefined") {
this.currentMessages.orbit.connect(Math.random().toString(36).substring(2));
} else {
this.currentMessages.orbit.connect(web3.eth.accounts[0]);
}
};
EmbarkJS.Messages.Orbit.sendMessage = function(options) {
var topics = options.topic || options.topics;
var data = options.data || options.payload;
if (topics === undefined) {
throw new Error("missing option: topic");
}
if (data === undefined) {
throw new Error("missing option: data");
}
if (typeof topics === 'string') {
topics = topics;
} else {
// TODO: better to just send to different channels instead
topics = topics.join(',');
}
this.orbit.join(topics);
var payload = JSON.stringify(data);
this.orbit.send(topics, data);
};
EmbarkJS.Messages.Orbit.listenTo = function(options) {
var self = this;
var topics = options.topic || options.topics;
if (typeof topics === 'string') {
topics = topics;
} else {
topics = topics.join(',');
}
this.orbit.join(topics);
var messageEvents = function() {
this.cb = function() {};
};
messageEvents.prototype.then = function(cb) {
this.cb = cb;
};
messageEvents.prototype.error = function(err) {
return err;
};
var promise = new messageEvents();
this.orbit.events.on('message', (channel, message) => {
// TODO: looks like sometimes it's receving messages from all topics
if (topics !== channel) return;
self.orbit.getPost(message.payload.value, true).then((post) => {
var data = {
topic: channel,
data: post.content,
from: post.meta.from.name,
time: (new Date(post.meta.ts))
};
promise.cb(post.content, data, post);
});
});
return promise;
};
// TODO: needs a real check for availability
// TODO: not tested as orbit is not loaded and therefore the provider is not available
EmbarkJS.Messages.Orbit.isAvailable = function(){
return new Promise((resolve) => {
if(!this.orbit) resolve(false);
resolve(true);
});
}

View File

@ -1,7 +1,6 @@
const program = require('commander');
const promptly = require('promptly');
const utils = require('./utils/utils.js');
const Embark = require('../lib/index');
const i18n = require('./i18n/i18n.js');
let embark = new Embark;
class Cmd {
@ -37,22 +36,25 @@ class Cmd {
try {
if (value.match(/^[a-zA-Z\s-]+$/)) return value;
} catch (e) {
throw new Error('Name must be only letters, spaces, or dashes');
throw new Error(__('Name must be only letters, spaces, or dashes'));
}
};
program
.command('new [name]')
.description('new application')
.option('--simple', 'create a barebones project meant only for contract development')
.description(__('New Application'))
.option('--simple', __('create a barebones project meant only for contract development'))
.option('--locale [locale]', __('language to use (default: en)'))
.action(function (name, options) {
i18n.setOrDetectLocale(options.locale);
if (name === undefined) {
return promptly.prompt("Name your app (default is embarkDApp):", {
const promptly = require('promptly');
return promptly.prompt(__("Name your app (default is %s):", 'embarkDapp'), {
default: "embarkDApp",
validator: validateName
}, function (err, inputvalue) {
if (err) {
console.error('Invalid name:', err.message);
console.error(__('Invalid name') + ':', err.message);
// Manually call retry
// The passed error has a retry method to easily prompt again.
err.retry();
@ -78,8 +80,10 @@ class Cmd {
demo() {
program
.command('demo')
.description('create a working dapp with a SimpleStorage contract')
.action(function () {
.option('--locale [locale]', __('language to use (default: en)'))
.description(__('create a working dapp with a SimpleStorage contract'))
.action(function (options) {
i18n.setOrDetectLocale(options.locale);
embark.generateTemplate('demo', './', 'embark_demo');
});
}
@ -87,13 +91,19 @@ class Cmd {
build() {
program
.command('build [environment]')
.option('--logfile [logfile]', 'filename to output logs (default: none)')
.option('--loglevel [loglevel]', 'level of logging to display ["error", "warn", "info", "debug", "trace"]', /^(error|warn|info|debug|trace)$/i, 'debug')
.description('deploy and build dapp at dist/ (default: development)')
.option('--contracts', 'only compile contracts into Embark wrappers')
.option('--logfile [logfile]', __('filename to output logs (default: none)'))
.option('-c, --client [client]', __('Use a specific ethereum client or simulator (supported: %s)', 'geth, testrpc'))
.option('--loglevel [loglevel]', __('level of logging to display') + ' ["error", "warn", "info", "debug", "trace"]', /^(error|warn|info|debug|trace)$/i, 'debug')
.option('--locale [locale]', __('language to use (default: en)'))
.description(__('deploy and build dapp at ') + 'dist/ (default: development)')
.action(function (env, _options) {
i18n.setOrDetectLocale(_options.locale);
_options.env = env || 'development';
_options.logFile = _options.logfile; // fix casing
_options.logLevel = _options.loglevel; // fix casing
_options.onlyCompile = _options.contracts;
_options.client = _options.client || 'geth';
embark.build(_options);
});
}
@ -101,19 +111,24 @@ class Cmd {
run() {
program
.command('run [environment]')
.option('-p, --port [port]', 'port to run the dev webserver (default: 8000)')
.option('-b, --host [host]', 'host to run the dev webserver (default: localhost)')
.option('--noserver', 'disable the development webserver')
.option('--nodashboard', 'simple mode, disables the dashboard')
.option('--no-color', 'no colors in case it\'s needed for compatbility purposes')
.option('--logfile [logfile]', 'filename to output logs (default: none)')
.option('--loglevel [loglevel]', 'level of logging to display ["error", "warn", "info", "debug", "trace"]', /^(error|warn|info|debug|trace)$/i, 'debug')
.description('run dapp (default: development)')
.option('-p, --port [port]', __('port to run the dev webserver (default: %s)', '8000'))
.option('-c, --client [client]', __('Use a specific ethereum client or simulator (supported: %s)', 'geth, testrpc'))
.option('-b, --host [host]', __('host to run the dev webserver (default: %s)', 'localhost'))
.option('--noserver', __('disable the development webserver'))
.option('--nodashboard', __('simple mode, disables the dashboard'))
.option('--no-color', __('no colors in case it\'s needed for compatbility purposes'))
.option('--logfile [logfile]', __('filename to output logs (default: %s)', 'none'))
.option('--loglevel [loglevel]', __('level of logging to display') + ' ["error", "warn", "info", "debug", "trace"]', /^(error|warn|info|debug|trace)$/i, 'debug')
.option('--locale [locale]', __('language to use (default: en)'))
.description(__('run dapp (default: %s)', 'development'))
.action(function (env, options) {
i18n.setOrDetectLocale(options.locale);
embark.run({
env: env || 'development',
serverPort: options.port,
serverHost: options.host,
client: options.client || 'geth',
locale: options.locale,
runWebserver: !options.noserver,
useDashboard: !options.nodashboard,
logFile: options.logfile,
@ -125,9 +140,11 @@ class Cmd {
blockchain() {
program
.command('blockchain [environment]')
.option('-c, --client [client]', 'Use a specific ethereum client or simulator (supported: geth, testrpc)')
.description('run blockchain server (default: development)')
.option('-c, --client [client]', __('Use a specific ethereum client or simulator (supported: %s)', 'geth, testrpc'))
.option('--locale [locale]', __('language to use (default: en)'))
.description(__('run blockchain server (default: %s)', 'development'))
.action(function (env, options) {
i18n.setOrDetectLocale(options.locale);
embark.initConfig(env || 'development', {
embarkConfig: 'embark.json',
interceptLogs: false
@ -139,15 +156,17 @@ class Cmd {
simulator() {
program
.command('simulator [environment]')
.description('run a fast ethereum rpc simulator')
.option('--testrpc', 'use testrpc as the rpc simulator [default]')
.option('-p, --port [port]', 'port to run the rpc simulator (default: 8545)')
.option('-h, --host [host]', 'host to run the rpc simulator (default: localhost)')
.option('-a, --accounts [numAccounts]', 'number of accounts (default: 10)')
.option('-e, --defaultBalanceEther [balance]', 'Amount of ether to assign each test account (default: 100)')
.option('-l, --gasLimit [gasLimit]', 'custom gas limit (default: 8000000)')
.description(__('run a fast ethereum rpc simulator'))
.option('--testrpc', __('use testrpc as the rpc simulator [%s]', 'default'))
.option('-p, --port [port]', __('port to run the rpc simulator (default: %s)', '8545'))
.option('-h, --host [host]', __('host to run the rpc simulator (default: %s)', 'localhost'))
.option('-a, --accounts [numAccounts]', __('number of accounts (default: %s)', '10'))
.option('-e, --defaultBalanceEther [balance]', __('Amount of ether to assign each test account (default: %s)', '100'))
.option('-l, --gasLimit [gasLimit]', __('custom gas limit (default: %s)', '8000000'))
.option('--locale [locale]', __('language to use (default: en)'))
.action(function (env, options) {
i18n.setOrDetectLocale(options.locale);
embark.initConfig(env || 'development', {
embarkConfig: 'embark.json',
interceptLogs: false
@ -165,37 +184,53 @@ class Cmd {
test() {
program
.command('test [file]')
.description('run tests')
.action(function (file) {
embark.initConfig('development', {
embarkConfig: 'embark.json', interceptLogs: false
});
embark.runTests(file);
.option('--locale [locale]', __('language to use (default: en)'))
.option('--loglevel [loglevel]', __('level of logging to display') + ' ["error", "warn", "info", "debug", "trace"]', /^(error|warn|info|debug|trace)$/i, 'warn')
.description(__('run tests'))
.action(function (file, options) {
i18n.setOrDetectLocale(options.locale);
embark.runTests({file, loglevel: options.loglevel});
});
}
upload() {
program
.command('upload <platform> [environment]')
.option('--logfile [logfile]', 'filename to output logs (default: none)')
.option('--loglevel [loglevel]', 'level of logging to display ["error", "warn", "info", "debug", "trace"]', /^(error|warn|info|debug|trace)$/i, 'debug')
.description('Upload your dapp to a decentralized storage (e.g embark upload ipfs).')
.action(function (platform, env, _options) {
.command('upload [environment]')
.option('--logfile [logfile]', __('filename to output logs (default: %s)', 'none'))
.option('--loglevel [loglevel]', __('level of logging to display') + ' ["error", "warn", "info", "debug", "trace"]', /^(error|warn|info|debug|trace)$/i, 'debug')
.option('--locale [locale]', __('language to use (default: en)'))
.option('-c, --client [client]', __('Use a specific ethereum client or simulator (supported: %s)', 'geth, testrpc'))
.description(__('Upload your dapp to a decentralized storage') + '.')
.action(function (env, _options) {
i18n.setOrDetectLocale(_options.locale);
if (env === "ipfs" || env === "swarm") {
console.warn(("did you mean " + "embark upload".bold + " ?").underline);
console.warn("In embark 3.1 forwards, the correct command is embark upload <environment> and the provider is configured in config/storage.js");
}
_options.env = env || 'development';
_options.logFile = _options.logfile; // fix casing
_options.logLevel = _options.loglevel; // fix casing
embark.upload(platform, _options);
_options.client = _options.client || 'geth';
embark.upload(_options);
});
}
graph() {
program
.command('graph [environment]')
.description('generates documentation based on the smart contracts configured')
.option('--skip-undeployed', __('Graph will not include undeployed contracts'))
.option('--skip-functions', __('Graph will not include functions'))
.option('--skip-events', __('Graph will not include events'))
.option('--locale [locale]', __('language to use (default: en)'))
.description(__('generates documentation based on the smart contracts configured'))
.action(function (env, options) {
i18n.setOrDetectLocale(options.locale);
embark.graph({
env: env || 'development',
logFile: options.logfile
logFile: options.logfile,
skipUndeployed: options.skipUndeployed,
skipFunctions: options.skipFunctions,
skipEvents: options.skipEvents
});
});
}
@ -203,8 +238,10 @@ class Cmd {
reset() {
program
.command('reset')
.description('resets embarks state on this dapp including clearing cache')
.action(function () {
.option('--locale [locale]', __('language to use (default: en)'))
.description(__('resets embarks state on this dapp including clearing cache'))
.action(function (options) {
i18n.setOrDetectLocale(options.locale);
embark.initConfig('development', {
embarkConfig: 'embark.json', interceptLogs: false
});
@ -215,7 +252,7 @@ class Cmd {
versionCmd() {
program
.command('version')
.description('output the version number')
.description(__('output the version number'))
.action(function () {
console.log(embark.version);
process.exit(0);
@ -225,17 +262,17 @@ class Cmd {
otherCommands() {
program
.action(function (cmd) {
console.log('unknown command "%s"'.red, cmd);
console.log((__('unknown command') + ' "%s"').red, cmd);
let utils = require('./utils/utils.js');
let dictionary = ['new', 'demo', 'build', 'run', 'blockchain', 'simulator', 'test', 'upload', 'version'];
let suggestion = utils.proposeAlternative(cmd, dictionary);
if (suggestion) {
console.log('did you mean "%s"?'.green, suggestion);
console.log((__('did you mean') + ' "%s"?').green, suggestion);
}
console.log("type embark --help to see the available commands");
process.exit(0);
});
}
}

View File

@ -1,17 +1,22 @@
var shelljs = require('shelljs');
const async = require('async');
const child_process = require('child_process');
const _ = require('underscore');
var fs = require('../../core/fs.js');
const fs = require('../../core/fs.js');
const constants = require('../../constants.json');
var GethCommands = require('./geth_commands.js');
const GethCommands = require('./geth_commands.js');
/*eslint complexity: ["error", 35]*/
/*eslint complexity: ["error", 36]*/
var Blockchain = function(options) {
this.blockchainConfig = options.blockchainConfig;
this.env = options.env || 'development';
this.client = options.client;
this.isDev = options.isDev;
this.onReadyCallback = options.onReadyCallback;
if ((this.blockchainConfig === {} || JSON.stringify(this.blockchainConfig) === '{"enabled":true}') && this.env !== 'development') {
console.log("===> warning: running default config on a non-development environment");
console.log("===> " + __("warning: running default config on a non-development environment"));
}
this.config = {
@ -31,18 +36,20 @@ var Blockchain = function(options) {
whisper: (this.blockchainConfig.whisper === undefined) || this.blockchainConfig.whisper,
maxpeers: ((this.blockchainConfig.maxpeers === 0) ? 0 : (this.blockchainConfig.maxpeers || 25)),
bootnodes: this.blockchainConfig.bootnodes || "",
rpcApi: (this.blockchainConfig.rpcApi || ['eth', 'web3', 'net']),
rpcApi: (this.blockchainConfig.rpcApi || ['eth', 'web3', 'net', 'debug']),
wsRPC: (this.blockchainConfig.wsRPC === undefined) || this.blockchainConfig.wsRPC,
wsHost: this.blockchainConfig.wsHost || 'localhost',
wsPort: this.blockchainConfig.wsPort || 8546,
wsOrigins: this.blockchainConfig.wsOrigins || false,
wsApi: (this.blockchainConfig.wsApi || ['eth', 'web3', 'net', 'shh']),
wsApi: (this.blockchainConfig.wsApi || ['eth', 'web3', 'net', 'shh', 'debug']),
vmdebug: this.blockchainConfig.vmdebug || false,
targetGasLimit: this.blockchainConfig.targetGasLimit || false,
light: this.blockchainConfig.light || false,
fast: this.blockchainConfig.fast || false
syncMode: this.blockchainConfig.syncMode,
verbosity: this.blockchainConfig.verbosity
};
this.setupProxy();
if (this.blockchainConfig === {} || JSON.stringify(this.blockchainConfig) === '{"enabled":true}') {
this.config.account = {};
this.config.account.password = fs.embarkPath("templates/boilerplate/config/development/password");
@ -50,90 +57,205 @@ var Blockchain = function(options) {
this.config.datadir = fs.embarkPath(".embark/development/datadir");
}
if (this.blockchainConfig.isDev) {
this.isDev = true;
} else if (this.blockchainConfig.isDev === false) {
this.isDev = false;
} else {
this.isDev = this.env === 'development';
const spaceMessage = 'The path for %s in blockchain config contains spaces, please remove them';
if (this.config.datadir && this.config.datadir.indexOf(' ') > 0) {
console.error(__(spaceMessage, 'datadir'));
process.exit();
}
if (this.config.account.password && this.config.account.password.indexOf(' ') > 0) {
console.error(__(spaceMessage, 'account.password'));
process.exit();
}
if (this.config.genesisBlock && this.config.genesisBlock.indexOf(' ') > 0) {
console.error(__(spaceMessage, 'genesisBlock'));
process.exit();
}
this.client = new options.client({config: this.config, env: this.env, isDev: this.isDev});
};
Blockchain.prototype.runCommand = function(cmd, options) {
console.log(("running: " + cmd.underline).green);
return shelljs.exec(cmd, options, (err, stdout, _stderr) => {
if (err && this.env === 'development' && stdout.indexOf('Failed to unlock developer account') > 0) {
console.warn('\nDevelopment blockchain has changed to use the --dev option.'.yellow);
console.warn('You can reset your workspace to fix the problem with'.yellow + ' embark reset'.cyan);
console.warn('Otherwise, you can change your data directory in blockchain.json (datadir)'.yellow);
}
});
Blockchain.prototype.setupProxy = function() {
this.config.proxy = true;
if (this.blockchainConfig.proxy === false) {
this.config.proxy = false;
return;
}
const proxy = require('../../core/proxy');
const Ipc = require('../../core/ipc');
let ipcObject = new Ipc({ipcRole: 'client'});
proxy.serve(ipcObject, this.config.rpcHost, this.config.rpcPort, false);
proxy.serve(ipcObject, this.config.wsHost, this.config.wsPort, true);
this.config.rpcPort += constants.blockchain.servicePortOnProxy;
this.config.wsPort += constants.blockchain.servicePortOnProxy;
};
Blockchain.prototype.runCommand = function(cmd, options, callback) {
console.log(__("running: %s", cmd.underline).green);
if (this.blockchainConfig.silent) {
options.silent = true;
}
return child_process.exec(cmd, options, callback);
};
Blockchain.prototype.run = function() {
var self = this;
console.log("===============================================================================".magenta);
console.log("===============================================================================".magenta);
console.log(("Embark Blockchain Using: " + this.client.name.underline).magenta);
console.log(__("Embark Blockchain Using: %s", this.client.name.underline).magenta);
console.log("===============================================================================".magenta);
console.log("===============================================================================".magenta);
if (!this.isClientInstalled()) {
console.log(("could not find " + this.config.geth_bin + " command; is " + this.client.name + " installed or in the PATH?").green);
return;
}
this.checkPathLength();
let address = '';
if (!this.isDev) {
address = this.initChainAndGetAddress();
}
this.client.mainCommand(address, function(cmd) {
self.runCommand(cmd, {async: true});
async.waterfall([
function checkInstallation(next) {
self.isClientInstalled((err) => {
if (err) {
console.log(__("could not find {{geth_bin}} command; is {{client_name}} installed or in the PATH?", {geth_bin: this.config.geth_bin, client_name: this.client.name}).green);
return next(err);
}
next();
});
},
function init(next) {
if (!self.isDev) {
return self.initChainAndGetAddress((err, addr) => {
address = addr;
next(err);
});
}
next();
},
function getMainCommand(next) {
self.client.mainCommand(address, function(cmd, args) {
next(null, cmd, args);
}, true);
}
], function (err, cmd, args) {
if (err) {
console.error(err);
return;
}
args = _.compact(args);
let full_cmd = cmd + " " + args.join(' ');
console.log(__("running: %s", full_cmd.underline).green);
self.child = child_process.spawn(cmd, args, {cwd: process.cwd()});
self.child.on('error', (err) => {
err = err.toString();
console.error('Blockchain error: ', err);
if (self.env === 'development' && err.indexOf('Failed to unlock') > 0) {
console.error('\n' + __('Development blockchain has changed to use the --dev option.').yellow);
console.error(__('You can reset your workspace to fix the problem with').yellow + ' embark reset'.cyan);
console.error(__('Otherwise, you can change your data directory in blockchain.json (datadir)').yellow);
}
});
self.child.stdout.on('data', (data) => {
console.log(`Geth error: ${data}`);
});
// Geth logs appear in stderr somehow
self.child.stderr.on('data', (data) => {
data = data.toString();
if (self.onReadyCallback && !self.readyCalled && data.indexOf('WebSocket endpoint opened') > -1) {
self.readyCalled = true;
self.onReadyCallback();
}
console.log('Geth: ' + data);
});
self.child.on('exit', (code) => {
if (code) {
console.error('Geth exited with error code ' + code);
}
});
});
};
Blockchain.prototype.isClientInstalled = function() {
let versionCmd = this.client.determineVersion();
let result = this.runCommand(versionCmd);
if (result.output === undefined || result.output.indexOf("not found") >= 0) {
return false;
Blockchain.prototype.kill = function() {
if (this.child) {
this.child.kill();
}
return true;
};
Blockchain.prototype.initChainAndGetAddress = function() {
var address = null, result;
Blockchain.prototype.checkPathLength = function() {
let dappPath = fs.dappPath('');
if (dappPath.length > 66) {
// console.error is captured and sent to the console output regardless of silent setting
console.error("===============================================================================".yellow);
console.error("===========> ".yellow + __('WARNING! DApp path length is too long: ').yellow + dappPath.yellow);
console.error("===========> ".yellow + __('This is known to cause issues with starting geth, please consider reducing your DApp path\'s length to 66 characters or less.').yellow);
console.error("===============================================================================".yellow);
}
};
Blockchain.prototype.isClientInstalled = function(callback) {
let versionCmd = this.client.determineVersionCommand();
this.runCommand(versionCmd, {}, (err, stdout, stderr) => {
if (err || stderr || !stdout || stdout.indexOf("not found") >= 0) {
return callback('Geth not found');
}
callback();
});
};
Blockchain.prototype.initChainAndGetAddress = function(callback) {
const self = this;
let address = null;
// ensure datadir exists, bypassing the interactive liabilities prompt.
this.datadir = '.embark/' + this.env + '/datadir';
fs.mkdirpSync(this.datadir);
self.datadir = '.embark/' + self.env + '/datadir';
// copy mining script
fs.copySync(fs.embarkPath("js"), ".embark/" + this.env + "/js", {overwrite: true});
// check if an account already exists, create one if not, return address
result = this.runCommand(this.client.listAccountsCommand());
if (result.output === undefined || result.output.match(/{(\w+)}/) === null || result.output.indexOf("Fatal") >= 0) {
console.log("no accounts found".green);
if (this.config.genesisBlock) {
console.log("initializing genesis block".green);
result = this.runCommand(this.client.initGenesisCommmand());
async.waterfall([
function makeDir(next) {
fs.mkdirp(self.datadir, (err, _result) => {
next(err);
});
},
function copy(next) {
// copy mining script
fs.copy(fs.embarkPath("js"), ".embark/" + self.env + "/js", {overwrite: true}, next);
},
function listAccounts(next) {
self.runCommand(self.client.listAccountsCommand(), {}, (err, stdout, stderr) => {
if (err || stderr || stdout === undefined || stdout.match(/{(\w+)}/) === null || stdout.indexOf("Fatal") >= 0) {
console.log(__("no accounts found").green);
return next();
}
console.log(__("already initialized").green);
address = stdout.match(/{(\w+)}/)[1];
next();
});
},
function genesisBlock(next) {
if (!self.config.genesisBlock) {
return next();
}
console.log(__("initializing genesis block").green);
self.runCommand(self.client.initGenesisCommmand(), {}, (err, _stdout, _stderr) => {
next(err);
});
},
function newAccount(next) {
self.runCommand(self.client.newAccountCommand(), {}, (err, stdout, _stderr) => {
if (err) {
return next(err);
}
address = stdout.match(/{(\w+)}/)[1];
next();
});
}
result = this.runCommand(this.client.newAccountCommand());
address = result.output.match(/{(\w+)}/)[1];
} else {
console.log("already initialized".green);
address = result.output.match(/{(\w+)}/)[1];
}
return address;
], (err) => {
callback(err, address);
});
};
var BlockchainClient = function(blockchainConfig, client, env) {
var BlockchainClient = function(blockchainConfig, client, env, isDev, onReadyCallback) {
// TODO add other clients at some point
if (client === 'geth') {
return new Blockchain({blockchainConfig: blockchainConfig, client: GethCommands, env: env});
return new Blockchain({blockchainConfig, client: GethCommands, env, isDev, onReadyCallback});
} else {
throw new Error('unknown client');
}

View File

@ -0,0 +1,47 @@
const ProcessWrapper = require('../../process/processWrapper');
const BlockchainClient = require('./blockchain');
const i18n = require('../../i18n/i18n.js');
const constants = require('../../constants');
let blockchainProcess;
class BlockchainProcess extends ProcessWrapper {
constructor(options) {
super();
this.blockchainConfig = options.blockchainConfig;
this.client = options.client;
this.env = options.env;
this.isDev = options.isDev;
i18n.setOrDetectLocale(options.locale);
this.blockchainConfig.silent = true;
this.blockchain = BlockchainClient(
this.blockchainConfig,
this.client,
this.env,
this.isDev,
this.blockchainReady.bind(this)
);
this.blockchain.run();
}
blockchainReady() {
blockchainProcess.send({result: constants.blockchain.blockchainReady});
}
kill() {
this.blockchain.kill();
}
}
process.on('message', (msg) => {
if (msg === 'exit') {
return blockchainProcess.kill();
}
if (msg.action === constants.blockchain.init) {
blockchainProcess = new BlockchainProcess(msg.options);
return blockchainProcess.send({result: constants.blockchain.initiated});
}
});

View File

@ -1,4 +1,4 @@
let async = require('async');
const async = require('async');
// TODO: make all of this async
class GethCommands {
@ -12,82 +12,82 @@ class GethCommands {
commonOptions() {
let config = this.config;
let cmd = "";
let cmd = [];
cmd += this.determineNetworkType(config);
cmd.push(this.determineNetworkType(config));
if (config.datadir) {
cmd += "--datadir=\"" + config.datadir + "\" ";
cmd.push(`--datadir=${config.datadir}`);
}
if (config.light) {
cmd += "--light ";
}
if (config.fast) {
cmd += "--fast ";
if (config.syncMode) {
cmd.push("--syncmode=" + config.syncMode);
}
if (config.account && config.account.password) {
cmd += "--password " + config.account.password + " ";
cmd.push(`--password=${config.account.password}`);
}
if (Number.isInteger(config.verbosity) && config.verbosity >=0 && config.verbosity <= 5) {
cmd.push("--verbosity=" + config.verbosity);
}
return cmd;
}
determineVersion() {
determineVersionCommand() {
return this.geth_bin + " version";
}
determineNetworkType(config) {
let cmd = "";
let cmd;
if (config.networkType === 'testnet') {
cmd += "--testnet ";
} else if (config.networkType === 'olympic') {
cmd += "--olympic ";
cmd = "--testnet";
} else if (config.networkType === 'rinkeby') {
cmd = "--rinkeby";
} else if (config.networkType === 'custom') {
cmd += "--networkid " + config.networkId + " ";
cmd = "--networkid=" + config.networkId;
}
return cmd;
}
initGenesisCommmand() {
let config = this.config;
let cmd = this.geth_bin + " " + this.commonOptions();
let cmd = this.geth_bin + " " + this.commonOptions().join(' ');
if (config.genesisBlock) {
cmd += "init \"" + config.genesisBlock + "\" ";
cmd += " init \"" + config.genesisBlock + "\" ";
}
return cmd;
}
newAccountCommand() {
return this.geth_bin + " " + this.commonOptions() + "account new ";
return this.geth_bin + " " + this.commonOptions().join(' ') + " account new ";
}
listAccountsCommand() {
return this.geth_bin + " " + this.commonOptions() + "account list ";
return this.geth_bin + " " + this.commonOptions().join(' ') + " account list ";
}
determineRpcOptions(config) {
let cmd = "";
let cmd = [];
cmd += "--port " + config.port + " ";
cmd += "--rpc ";
cmd += "--rpcport " + config.rpcPort + " ";
cmd += "--rpcaddr " + config.rpcHost + " ";
cmd.push("--port=" + config.port);
cmd.push("--rpc");
cmd.push("--rpcport=" + config.rpcPort);
cmd.push("--rpcaddr=" + config.rpcHost);
if (config.rpcCorsDomain) {
if (config.rpcCorsDomain === '*') {
console.log('==================================');
console.log('rpcCorsDomain set to *');
console.log('make sure you know what you are doing');
console.log(__('rpcCorsDomain set to *'));
console.log(__('make sure you know what you are doing'));
console.log('==================================');
}
cmd += "--rpccorsdomain=\"" + config.rpcCorsDomain + "\" ";
cmd.push("--rpccorsdomain=" + config.rpcCorsDomain);
} else {
console.log('==================================');
console.log('warning: cors is not set');
console.log(__('warning: cors is not set'));
console.log('==================================');
}
@ -95,23 +95,23 @@ class GethCommands {
}
determineWsOptions(config) {
let cmd = "";
let cmd = [];
if (config.wsRPC) {
cmd += "--ws ";
cmd += "--wsport " + config.wsPort + " ";
cmd += "--wsaddr " + config.wsHost + " ";
cmd.push("--ws");
cmd.push("--wsport=" + config.wsPort);
cmd.push("--wsaddr=" + config.wsHost);
if (config.wsOrigins) {
if (config.wsOrigins === '*') {
console.log('==================================');
console.log('wsOrigins set to *');
console.log('make sure you know what you are doing');
console.log(__('wsOrigins set to *'));
console.log(__('make sure you know what you are doing'));
console.log('==================================');
}
cmd += "--wsorigins \"" + config.wsOrigins + "\" ";
cmd.push("--wsorigins=" + config.wsOrigins);
} else {
console.log('==================================');
console.log('warning: wsOrigins is not set');
console.log(__('warning: wsOrigins is not set'));
console.log('==================================');
}
}
@ -122,47 +122,57 @@ class GethCommands {
mainCommand(address, done) {
let self = this;
let config = this.config;
let rpc_api = (this.config.rpcApi || ['eth', 'web3', 'net']);
let ws_api = (this.config.wsApi || ['eth', 'web3', 'net']);
let rpc_api = (this.config.rpcApi || ['eth', 'web3', 'net', 'debug']);
let ws_api = (this.config.wsApi || ['eth', 'web3', 'net', 'debug']);
let args = [];
async.series([
function commonOptions(callback) {
let cmd = self.commonOptions();
args = args.concat(cmd);
callback(null, cmd);
},
function rpcOptions(callback) {
let cmd = self.determineRpcOptions(self.config);
args = args.concat(cmd);
callback(null, cmd);
},
function wsOptions(callback) {
let cmd = self.determineWsOptions(self.config);
args = args.concat(cmd);
callback(null, cmd);
},
function dontGetPeers(callback) {
if (config.nodiscover) {
args.push("--nodiscover");
return callback(null, "--nodiscover");
}
callback(null, "");
},
function vmDebug(callback) {
if (config.vmdebug) {
args.push("--vmdebug");
return callback(null, "--vmdebug");
}
callback(null, "");
},
function maxPeers(callback) {
let cmd = "--maxpeers " + config.maxpeers;
let cmd = "--maxpeers=" + config.maxpeers;
args.push(cmd);
callback(null, cmd);
},
function mining(callback) {
if (config.mineWhenNeeded || config.mine) {
return callback(null, "--mine ");
args.push("--mine");
return callback(null, "--mine");
}
callback("");
},
function bootnodes(callback) {
if (config.bootnodes && config.bootnodes !== "" && config.bootnodes !== []) {
return callback(null, "--bootnodes " + config.bootnodes);
args.push("--bootnodes=" + config.bootnodes);
return callback(null, "--bootnodes=" + config.bootnodes);
}
callback("");
},
@ -172,15 +182,18 @@ class GethCommands {
if (ws_api.indexOf('shh') === -1) {
ws_api.push('shh');
}
args.push("--shh");
return callback(null, "--shh ");
}
callback("");
},
function rpcApi(callback) {
callback(null, '--rpcapi "' + rpc_api.join(',') + '"');
args.push('--rpcapi=' + rpc_api.join(','));
callback(null, '--rpcapi=' + rpc_api.join(','));
},
function wsApi(callback) {
callback(null, '--wsapi "' + ws_api.join(',') + '"');
args.push('--wsapi=' + ws_api.join(','));
callback(null, '--wsapi=' + ws_api.join(','));
},
function accountToUnlock(callback) {
let accountAddress = "";
@ -190,33 +203,37 @@ class GethCommands {
accountAddress = address;
}
if (accountAddress && !self.isDev) {
args.push("--unlock=" + accountAddress);
return callback(null, "--unlock=" + accountAddress);
}
callback(null, "");
},
function gasLimit(callback) {
if (config.targetGasLimit) {
return callback(null, "--targetgaslimit " + config.targetGasLimit);
args.push("--targetgaslimit=" + config.targetGasLimit);
return callback(null, "--targetgaslimit=" + config.targetGasLimit);
}
callback(null, "");
},
function mineWhenNeeded(callback) {
if (config.mineWhenNeeded && !self.isDev) {
args.push("js .embark/" + self.env + "/js/mine.js");
return callback(null, "js .embark/" + self.env + "/js/mine.js");
}
callback(null, "");
},
function isDev(callback) {
if (self.isDev) {
args.push('--dev');
return callback(null, '--dev');
}
callback(null, '');
}
], function (err, results) {
], function (err) {
if (err) {
throw new Error(err.message);
}
done(self.geth_bin + " " + results.join(" "));
return done(self.geth_bin, args);
});
}
}

View File

@ -6,15 +6,16 @@ class GraphGenerator {
this.engine = engine;
}
generate() {
generate(options) {
let id = 0;
let contractString = "";
let relationshipString = "";
let idMapping = {};
let contractInheritance = {};
for (let contract in this.engine.contractsManager.contracts) {
if(options.skipUndeployed && !this.engine.contractsManager.contracts[contract].deploy) continue;
id++;
idMapping[contract] = id;
@ -30,23 +31,27 @@ class GraphGenerator {
contractLabel += ": " + this.engine.contractsManager.contracts[contract].instanceOf;
tooltip += " instance of " + this.engine.contractsManager.contracts[contract].instanceOf;
} else {
contractLabel += "|";
if(!(options.skipFunctions === true && options.skipEvents === true)) contractLabel += "|";
for(let i = 0; i < this.engine.contractsManager.contracts[contract].abiDefinition.length; i++){
switch(this.engine.contractsManager.contracts[contract].abiDefinition[i].type){
let abiDef = this.engine.contractsManager.contracts[contract].abiDefinition[i];
if(abiDef.type == 'event' && options.skipEvents) continue;
if(['constructor', 'fallback'].indexOf(abiDef.type) > -1 && options.skipFunctions) continue;
switch(abiDef.type){
case 'fallback':
contractLabel += "«fallback»()\\l";
break;
case 'constructor':
case 'constructor':
contractLabel += "«constructor»(";
this.engine.contractsManager.contracts[contract].abiDefinition[i].inputs.forEach(function(elem, index){
abiDef.inputs.forEach(function(elem, index){
contractLabel += (index == 0 ? "" : ", ") + elem.type;
});
contractLabel += ")\\l";
break;
case 'event':
contractLabel += "«event»" + this.engine.contractsManager.contracts[contract].abiDefinition[i].name + "(";
this.engine.contractsManager.contracts[contract].abiDefinition[i].inputs.forEach(function(elem, index){
case 'event':
contractLabel += "«event»" + abiDef.name + "(";
abiDef.inputs.forEach(function(elem, index){
contractLabel += (index == 0 ? "" : ", ") + elem.type;
});
contractLabel += ")\\l";
@ -56,7 +61,7 @@ class GraphGenerator {
}
let fHashes = this.engine.contractsManager.contracts[contract].functionHashes;
if(fHashes != {} && fHashes != undefined){
if(fHashes != {} && fHashes != undefined && !options.skipFunctions){
for(let method in this.engine.contractsManager.contracts[contract].functionHashes){
contractLabel += method + '\\l';
}
@ -73,20 +78,23 @@ class GraphGenerator {
}
for (let c in this.engine.contractsManager.contractDependencies){
for (let c in this.engine.contractsManager.contractDependencies){
let contractDependencies = Array.from(new Set(this.engine.contractsManager.contractDependencies[c]));
contractDependencies.forEach(function(d){
contractDependencies.forEach((d) => {
if(idMapping[c] !== undefined && idMapping[d] !== undefined){
relationshipString += `${idMapping[d]}->${idMapping[c]}[constraint=true, arrowtail=diamond, tooltip="${c} uses ${d}"]\n`;
if(options.skipUndeployed && this.engine.contractsManager.contracts[c].deploy && this.engine.contractsManager.contracts[d].deploy){
relationshipString += `${idMapping[d]}->${idMapping[c]}[constraint=true, arrowtail=diamond, tooltip="${c} uses ${d}"]\n`;
}
}
});
}
for (let c in contractInheritance){
if(options.skipUndeployed && !this.engine.contractsManager.contracts[contractInheritance[c]].deploy) continue;
relationshipString += `${idMapping[contractInheritance[c]]}->${idMapping[c]}[tooltip="${c} instance of ${contractInheritance[c]}"]\n`;
}
let dot = `
digraph Contracts {
node[shape=record,style=filled]

View File

@ -4,5 +4,5 @@ module.exports = function() {
fs.removeSync('./chains.json');
fs.removeSync('.embark/');
fs.removeSync('dist/');
console.log("reset done!".green);
console.log(__("reset done!").green);
};

View File

@ -1,4 +1,7 @@
let shelljs = require('shelljs');
let proxy = require('../core/proxy');
const Ipc = require('../core/ipc');
const constants = require('../constants.json');
class Simulator {
constructor(options) {
@ -12,13 +15,17 @@ class Simulator {
const testrpc = shelljs.which('testrpc');
const ganache = shelljs.which('ganache-cli');
if (!testrpc && !ganache) {
this.logger.warn('Ganache CLI (TestRPC) is not installed on your machine');
this.logger.info('You can install it by running: npm -g install ganache-cli');
this.logger.warn(__('%s is not installed on your machine', 'Ganache CLI (TestRPC)'));
this.logger.info(__('You can install it by running: %s', 'npm -g install ganache-cli'));
process.exit();
}
cmds.push("-p " + (this.blockchainConfig.rpcPort || options.port || 8545));
cmds.push("-h " + (this.blockchainConfig.rpcHost || options.host || 'localhost'));
let useProxy = this.blockchainConfig.proxy || false;
let host = (options.host || this.blockchainConfig.rpcHost || 'localhost');
let port = (options.port || this.blockchainConfig.rpcPort || 8545);
cmds.push("-p " + (port + (useProxy ? constants.blockchain.servicePortOnProxy : 0)));
cmds.push("-h " + host);
cmds.push("-a " + (options.numAccounts || 10));
cmds.push("-e " + (options.defaultBalance || 100));
cmds.push("-l " + (options.gasLimit || 8000000));
@ -36,7 +43,14 @@ class Simulator {
}
const program = ganache ? 'ganache-cli' : 'testrpc';
shelljs.exec(`${program} ${cmds.join(' ')}`, {async : true});
if(useProxy){
let ipcObject = new Ipc({ipcRole: 'client'});
proxy.serve(ipcObject, host, port, false);
}
}
}

View File

@ -8,7 +8,7 @@ class TemplateGenerator {
generate(destinationFolder, name) {
let templatePath = fs.embarkPath(utils.joinPath('templates', this.templateName));
console.log('Initializing Embark Template....'.green);
console.log(__('Initializing Embark Template....').green);
let fspath = utils.joinPath(destinationFolder, name);
fs.copySync(templatePath, fspath);
@ -16,21 +16,19 @@ class TemplateGenerator {
utils.sed('package.json', '%APP_NAME%', name);
if (name === 'embark_demo') {
console.log('Installing packages...'.green);
console.log(__('Installing packages...').green);
utils.runCmd('npm install');
}
console.log('Init complete'.green);
console.log('\nApp ready at '.green + fspath);
console.log(__('Init complete').green);
console.log('\n' + __('App ready at ').green + fspath);
if (name === 'embark_demo') {
console.log('-------------------'.yellow);
console.log('Next steps:'.green);
console.log(__('Next steps:').green);
console.log(('-> ' + ('cd ' + fspath).bold.cyan).green);
console.log('-> '.green + 'embark blockchain'.bold.cyan + ' or '.green + 'embark simulator'.bold.cyan);
console.log('open another console in the same directory and run'.green);
console.log('-> '.green + 'embark run'.bold.cyan);
console.log('For more info go to http://embark.status.im'.green);
console.log(__('For more info go to http://embark.status.im').green);
}
}
}

View File

@ -0,0 +1,32 @@
// still needs to be run on a separate file due to the global context
var RunCode = require('./runCode.js');
class CodeRunner {
constructor(options) {
this.plugins = options.plugins;
this.logger = options.logger;
this.events = options.events;
// necessary to init the context
RunCode.initContext();
this.events.on("runcode:register", (varName, code) => {
RunCode.registerVar(varName, code);
});
this.events.setCommandHandler('runcode:eval', (code, cb) => {
if (!cb) {
cb = function() {};
}
try {
let result = RunCode.doEval(code);
cb(null, result);
} catch (e) {
cb(e);
}
});
}
}
module.exports = CodeRunner;

View File

@ -1,18 +1,16 @@
/*eslint no-unused-vars: off*/
let Web3 = require('web3');
let web3;
let __mainContext;
let __mainContext = this;
function initContext() {
doEval("__mainContext = this");
}
// ======================
// the eval is used for evaluating some of the contact calls for different purposes
// this should be at least moved to a different process and scope
// for now it is defined here
// ======================
function doEval(code, _web3) {
if (_web3) {
web3 = _web3;
}
function doEval(code) {
try {
// TODO: add trace log here
return eval(code);
@ -21,6 +19,12 @@ function doEval(code, _web3) {
}
}
function registerVar(varName, code) {
__mainContext[varName] = code;
}
module.exports = {
doEval: doEval
doEval: doEval,
registerVar: registerVar,
initContext: initContext
};

View File

@ -15,5 +15,31 @@
"events": {
"contractFilesChanged": "contractFilesChanged",
"contractConfigChanged": "contractConfigChanged"
},
"process": {
"processLaunchRequest": "process:launch-request",
"processLaunchComplete": "process:launch-complete",
"log": "log",
"events": {
"on": "on",
"request": "request",
"response": "response"
}
},
"pipeline": {
"init": "init",
"build": "build",
"initiated": "initiated",
"built": "built"
},
"blockchain": {
"blockchainReady": "blockchainReady",
"init": "init",
"initiated": "initiated",
"servicePortOnProxy": 10
},
"storage": {
"init": "init",
"initiated": "initiated"
}
}

View File

@ -0,0 +1,95 @@
const bip39 = require("bip39");
const hdkey = require('ethereumjs-wallet/hdkey');
const fs = require('../core/fs');
class AccountParser {
static parseAccountsConfig(accountsConfig, web3, logger) {
let accounts = [];
if (accountsConfig && accountsConfig.length) {
accountsConfig.forEach(accountConfig => {
const account = AccountParser.getAccount(accountConfig, web3, logger);
if (!account) {
return;
}
if (Array.isArray(account)) {
accounts = accounts.concat(account);
return;
}
accounts.push(account);
});
}
return accounts;
}
static getHexBalance(balanceString, web3) {
if (!balanceString) {
return 0xFFFFFFFFFFFFFFFFFF;
}
if (web3.utils.isHexStrict(balanceString)) {
return balanceString;
}
const match = balanceString.match(/([0-9]+) ?([a-zA-Z]*)/);
if (!match) {
throw new Error(__('Unrecognized balance string "%s"', balanceString));
}
if (!match[2]) {
return web3.utils.toHex(parseInt(match[1], 10));
}
return web3.utils.toHex(web3.utils.toWei(match[1], match[2]));
}
static getAccount(accountConfig, web3, logger) {
if (!logger) {
logger = console;
}
let hexBalance = null;
if (accountConfig.balance) {
hexBalance = AccountParser.getHexBalance(accountConfig.balance, web3);
}
if (accountConfig.privateKey) {
if (!accountConfig.privateKey.startsWith('0x')) {
accountConfig.privateKey = '0x' + accountConfig.privateKey;
}
if (!web3.utils.isHexStrict(accountConfig.privateKey)) {
logger.warn(`Private key ending with ${accountConfig.privateKey.substr(accountConfig.privateKey.length - 5)} is not a HEX string`);
return null;
}
return Object.assign(web3.eth.accounts.privateKeyToAccount(accountConfig.privateKey), {hexBalance});
}
if (accountConfig.privateKeyFile) {
let fileContent = fs.readFileSync(fs.dappPath(accountConfig.privateKeyFile)).toString();
fileContent = fileContent.trim().split(/[,;]/);
return fileContent.map((key, index) => {
if (!key.startsWith('0x')) {
key = '0x' + key;
}
if (!web3.utils.isHexStrict(key)) {
logger.warn(`Private key is not a HEX string in file ${accountConfig.privateKeyFile} at index ${index}`);
return null;
}
return Object.assign(web3.eth.accounts.privateKeyToAccount(key), {hexBalance});
});
}
if (accountConfig.mnemonic) {
const hdwallet = hdkey.fromMasterSeed(bip39.mnemonicToSeed(accountConfig.mnemonic.trim()));
const addressIndex = accountConfig.addressIndex || 0;
const numAddresses = accountConfig.numAddresses || 1;
const wallet_hdpath = accountConfig.hdpath || "m/44'/60'/0'/0/";
const accounts = [];
for (let i = addressIndex; i < addressIndex + numAddresses; i++) {
const wallet = hdwallet.derivePath(wallet_hdpath + i).getWallet();
accounts.push(Object.assign(web3.eth.accounts.privateKeyToAccount('0x' + wallet.getPrivateKey().toString('hex')), {hexBalance}));
}
return accounts;
}
logger.warn('Unsupported account configuration: ' + JSON.stringify(accountConfig));
logger.warn('Try using one of those: ' +
'{ "privateKey": "your-private-key", "privateKeyFile": "path/to/file/containing/key", "mnemonic": "12 word mnemonic" }');
return null;
}
}
module.exports = AccountParser;

293
lib/contracts/blockchain.js Normal file
View File

@ -0,0 +1,293 @@
const Web3 = require('web3');
const async = require('async');
const Provider = require('./provider.js');
const BlockchainProcessLauncher = require('../processes/blockchainProcessLauncher');
const utils = require('../utils/utils');
const constants = require('../constants');
const WEB3_READY = 'web3Ready';
class Blockchain {
constructor(options) {
this.plugins = options.plugins;
this.logger = options.logger;
this.events = options.events;
this.contractsConfig = options.contractsConfig;
this.blockchainConfig = options.blockchainConfig;
this.web3 = options.web3;
this.locale = options.locale;
this.isDev = options.isDev;
this.web3Endpoint = '';
this.isWeb3Ready = false;
this.web3StartedInProcess = false;
if (!this.web3) {
this.initWeb3();
} else {
this.isWeb3Ready = true;
}
this.registerServiceCheck();
this.registerRequests();
this.registerWeb3Object();
}
initWeb3(cb) {
if (!cb) {
cb = function(){};
}
if (this.isWeb3Ready) {
return cb();
}
const self = this;
this.web3 = new Web3();
if (this.contractsConfig.deployment.type !== "rpc" && this.contractsConfig.deployment.type !== "ws") {
const message = __("contracts config error: unknown deployment type %s", this.contractsConfig.deployment.type);
this.logger.error(message);
return cb(message);
}
const protocol = (this.contractsConfig.deployment.type === "rpc") ? this.contractsConfig.deployment.protocol : 'ws';
let provider;
this.web3Endpoint = utils.buildUrl(protocol, this.contractsConfig.deployment.host, this.contractsConfig.deployment.port);//`${protocol}://${this.contractsConfig.deployment.host}:${this.contractsConfig.deployment.port}`;
const providerOptions = {
web3: this.web3,
accountsConfig: this.contractsConfig.deployment.accounts,
blockchainConfig: this.blockchainConfig,
logger: this.logger,
isDev: this.isDev,
type: this.contractsConfig.deployment.type,
web3Endpoint: self.web3Endpoint
};
provider = new Provider(providerOptions);
async.waterfall([
function checkNode(next) {
self.assertNodeConnection(true, (err) => {
if (err && self.web3StartedInProcess) {
// Already started blockchain in another node, we really have a node problem
self.logger.error(__('Unable to start the blockchain process. Is Geth installed?').red);
return next(err);
}
if (!err) {
self.isWeb3Ready = true;
self.events.emit(WEB3_READY);
return next();
}
self.web3StartedInProcess = true;
self.startBlockchainNode(() => {
// Need to re-initialize web3 to connect to the new blockchain node
provider.stop();
self.initWeb3(cb);
});
});
},
function startProvider(next) {
provider.startWeb3Provider(next);
},
function fundAccountsIfNeeded(next) {
provider.fundAccounts(next);
}
], (err) => {
self.registerWeb3Object();
cb(err);
});
}
onReady(callback) {
if (this.isWeb3Ready) {
return callback();
}
this.events.once(WEB3_READY, () => {
callback();
});
}
startBlockchainNode(callback) {
const self = this;
let blockchainProcess = new BlockchainProcessLauncher({
events: self.events,
logger: self.logger,
normalizeInput: utils.normalizeInput,
blockchainConfig: self.blockchainConfig,
locale: self.locale,
isDev: self.isDev
});
blockchainProcess.startBlockchainNode();
self.events.once(constants.blockchain.blockchainReady, () => {
callback();
});
}
registerServiceCheck() {
const self = this;
const NO_NODE = 'noNode';
this.events.request("services:register", 'Ethereum', function (cb) {
async.waterfall([
function checkNodeConnection(next) {
self.assertNodeConnection(true, (err) => {
if (err) {
return next(NO_NODE, {name: "No Blockchain node found", status: 'off'});
}
next();
});
},
function checkVersion(next) {
// TODO: web3_clientVersion method is currently not implemented in web3.js 1.0
self.web3._requestManager.send({method: 'web3_clientVersion', params: []}, (err, version) => {
if (err) {
return next(null, {name: "Ethereum node (version unknown)", status: 'on'});
}
if (version.indexOf("/") < 0) {
return next(null, {name: version, status: 'on'});
}
let nodeName = version.split("/")[0];
let versionNumber = version.split("/")[1].split("-")[0];
let name = nodeName + " " + versionNumber + " (Ethereum)";
return next(null, {name: name, status: 'on'});
});
}
], (err, statusObj) => {
if (err && err !== NO_NODE) {
return cb(err);
}
cb(statusObj);
});
}, 5000, 'off');
}
registerRequests() {
const self = this;
this.events.setCommandHandler("blockchain:defaultAccount:get", function(cb) {
cb(self.defaultAccount);
});
this.events.setCommandHandler("blockchain:defaultAccount:set", function(account, cb) {
self.setDefaultAccount(account);
cb();
});
this.events.setCommandHandler("blockchain:block:byNumber", function(blockNumber, cb) {
self.getBlock(blockNumber, cb);
});
this.events.setCommandHandler("blockchain:gasPrice", function(cb) {
self.getGasPrice(cb);
});
}
defaultAccount() {
return this.web3.eth.defaultAccount;
}
setDefaultAccount(account) {
this.web3.eth.defaultAccount = account;
}
getAccounts(cb) {
this.web3.eth.getAccounts(cb);
}
getCode(address, cb) {
this.web3.eth.getCode(address, cb);
}
getBlock(blockNumber, cb) {
this.web3.eth.getBlock(blockNumber, cb);
}
getGasPrice(cb) {
this.web3.eth.getGasPrice(cb);
}
ContractObject(params) {
return new this.web3.eth.Contract(params.abi);
}
deployContractObject(contractObject, params) {
return contractObject.deploy({arguments: params.arguments, data: params.data});
}
estimateDeployContractGas(deployObject, cb) {
return deployObject.estimateGas().then((gasValue) => {
cb(null, gasValue);
}).catch(cb);
}
deployContractFromObject(deployContractObject, params, cb) {
deployContractObject.send({
from: params.from, gas: params.gas, gasPrice: params.gasPrice
}).on('receipt', function(receipt) {
if (receipt.contractAddress !== undefined) {
cb(null, receipt);
}
}).on('error', cb);
}
assertNodeConnection(noLogs, cb) {
if (typeof noLogs === 'function') {
cb = noLogs;
noLogs = false;
}
const NO_NODE_ERROR = Error("error connecting to blockchain node");
const self = this;
async.waterfall([
function checkInstance(next) {
if (!self.web3) {
return next(Error("no web3 instance found"));
}
next();
},
function checkProvider(next) {
if (self.web3.currentProvider === undefined) {
return next(NO_NODE_ERROR);
}
next();
},
function pingEndpoint(next) {
if (!self.contractsConfig || !self.contractsConfig.deployment || !self.contractsConfig.deployment.host) {
return next();
}
const {host, port, type, protocol} = self.contractsConfig.deployment;
utils.pingEndpoint(host, port, type, protocol, self.blockchainConfig.wsOrigins.split(',')[0], next);
}
], function (err) {
if (!noLogs && err === NO_NODE_ERROR) {
self.logger.error(("Couldn't connect to an Ethereum node are you sure it's on?").red);
self.logger.info("make sure you have an Ethereum node or simulator running. e.g 'embark blockchain'".magenta);
}
cb(err);
});
}
determineDefaultAccount(cb) {
const self = this;
self.getAccounts(function (err, accounts) {
if (err) {
self.logger.error(err);
return cb(new Error(err));
}
let accountConfig = self.blockchainConfig.account;
let selectedAccount = accountConfig && accountConfig.address;
self.setDefaultAccount(selectedAccount || accounts[0]);
cb();
});
}
registerWeb3Object() {
// doesn't feel quite right, should be a cmd or plugin method
// can just be a command without a callback
this.events.emit("runcode:register", "web3", this.web3);
}
}
module.exports = Blockchain;

View File

@ -1,5 +1,6 @@
let async = require('async');
let fs = require('../core/fs.js');
const utils = require('../utils/utils.js');
require('ejs');
const Templates = {
@ -13,7 +14,8 @@ const Templates = {
define_web3_simple: require('./code_templates/define-web3-simple.js.ejs'),
web3_connector: require('./code_templates/web3-connector.js.ejs'),
do_when_loaded: require('./code_templates/do-when-loaded.js.ejs'),
exec_when_env_loaded: require('./code_templates/exec-when-env-loaded.js.ejs')
exec_when_env_loaded: require('./code_templates/exec-when-env-loaded.js.ejs'),
embark_building_placeholder: require('./code_templates/embark-building-placeholder.html.ejs')
};
class CodeGenerator {
@ -24,7 +26,8 @@ class CodeGenerator {
this.contractsConfig = options.contractsConfig || {};
this.storageConfig = options.storageConfig || {};
this.communicationConfig = options.communicationConfig || {};
this.contractsManager = options.contractsManager;
this.namesystemConfig = options.namesystemConfig || {};
this.env = options.env || 'development';
this.plugins = options.plugins;
this.events = options.events;
}
@ -32,68 +35,61 @@ class CodeGenerator {
listenToCommands() {
let self = this;
this.events.setCommandHandlerOnce('provider-code', function(cb) {
this.events.setCommandHandler('provider-code', function(cb) {
let providerCode = self.generateProvider(false);
cb(providerCode);
});
// deprecated events; to remove in embark 2.7.0
this.events.setCommandHandlerOnce('abi-vanila', function(cb) {
let vanillaABI = self.generateABI({useEmbarkJS: false});
let contractsJSON = self.generateContractsJSON();
cb(vanillaABI, contractsJSON);
});
this.events.setCommandHandlerOnce('abi', function(cb) {
let embarkJSABI = self.generateABI({useEmbarkJS: true});
let contractsJSON = self.generateContractsJSON();
cb(embarkJSABI, contractsJSON);
});
this.events.setCommandHandlerOnce('abi-contracts-vanila', function(cb) {
let vanillaContractsABI = self.generateContracts(false, true, false);
let contractsJSON = self.generateContractsJSON();
cb(vanillaContractsABI, contractsJSON);
});
this.events.setCommandHandlerOnce('abi-vanila-deployment', function(cb) {
let vanillaABI = self.generateABI({useEmbarkJS: false, deployment: true});
let contractsJSON = self.generateContractsJSON();
cb(vanillaABI, contractsJSON);
});
// new events
this.events.setCommandHandlerOnce('code-vanila', function(cb) {
let vanillaABI = self.generateABI({useEmbarkJS: false});
let contractsJSON = self.generateContractsJSON();
cb(vanillaABI, contractsJSON);
this.events.setCommandHandler('code-vanila', function(cb) {
self.events.request("contracts:list", (_err, contractsList) => {
let vanillaABI = self.generateABI(contractsList, {useEmbarkJS: false});
let contractsJSON = self.generateContractsJSON(contractsList);
cb(vanillaABI, contractsJSON);
});
});
this.events.setCommandHandlerOnce('code', function(cb) {
let embarkJSABI = self.generateABI({useEmbarkJS: true});
let contractsJSON = self.generateContractsJSON();
cb(embarkJSABI, contractsJSON);
this.events.setCommandHandler('code', function(cb) {
self.events.request("contracts:list", (_err, contractsList) => {
let embarkJSABI = self.generateABI(contractsList, {useEmbarkJS: true});
let contractsJSON = self.generateContractsJSON(contractsList);
cb(embarkJSABI, contractsJSON);
});
});
this.events.setCommandHandlerOnce('code-contracts-vanila', function(cb) {
let vanillaContractsABI = self.generateContracts(false, true, false);
let contractsJSON = self.generateContractsJSON();
cb(vanillaContractsABI, contractsJSON);
this.events.setCommandHandler('code-contracts-vanila', function(cb) {
self.events.request("contracts:list", (_err, contractsList) => {
let vanillaContractsABI = self.generateContracts(contractsList, false, true, false);
let contractsJSON = self.generateContractsJSON(contractsList);
cb(vanillaContractsABI, contractsJSON);
});
});
this.events.setCommandHandlerOnce('code-vanila-deployment', function(cb) {
let vanillaABI = self.generateABI({useEmbarkJS: false, deployment: true});
let contractsJSON = self.generateContractsJSON();
this.events.setCommandHandler('code-vanila-deployment', function(cb) {
self.events.request("contracts:list", (_err, contractsList) => {
let vanillaABI = self.generateABI(contractsList, {useEmbarkJS: false, deployment: true});
let contractsJSON = self.generateContractsJSON(contractsList);
cb(vanillaABI, contractsJSON);
});
});
cb(vanillaABI, contractsJSON);
this.events.setCommandHandler('code-generator:web3js', function(cb) {
self.buildWeb3JS(cb);
});
self.events.setCommandHandler('code-generator:contract', (contractName, cb) => {
self.events.request('contracts:contract', contractName, (contract) => {
self.buildContractJS(contractName, self.generateContractJSON(contract, contract), cb);
});
});
self.events.setCommandHandler('code-generator:contract:vanilla', (contract, gasLimit, cb) => {
cb(self.generateContractCode(contract, gasLimit));
});
this.events.setCommandHandler('embark-building-placeholder', (cb) => {
self.buildPlaceholderPage(cb);
});
}
@ -138,7 +134,11 @@ class CodeGenerator {
web3Load = Templates.define_web3_simple({url: connection, done: 'done();'});
} else {
let connectionList = "[" + this.contractsConfig.dappConnection.map((x) => '"' + x + '"').join(',') + "]";
web3Load = Templates.web3_connector({connectionList: connectionList, done: 'done();'});
if (self.env === 'development') {
web3Load = Templates.web3_connector({connectionList: connectionList, done: 'done();', warnAboutMetamask: true});
} else {
web3Load = Templates.web3_connector({connectionList: connectionList, done: 'done();', warnAboutMetamask: true});
}
}
result += Templates.do_when_loaded({block: web3Load});
@ -147,16 +147,15 @@ class CodeGenerator {
return result;
}
generateContracts(useEmbarkJS, isDeployment, useLoader) {
generateContracts(contractsList, useEmbarkJS, isDeployment, useLoader) {
let self = this;
let result = "\n";
let contractsPlugins;
if (useLoader === false) {
for (let className in this.contractsManager.contracts) {
let contract = this.contractsManager.contracts[className];
for (let contract of contractsList) {
let abi = JSON.stringify(contract.abiDefinition);
result += Templates.vanilla_contract({className: className, abi: abi, contract: contract, gasLimit: 6000000});
result += Templates.vanilla_contract({className: contract.className, abi: abi, contract: contract, gasLimit: 6000000});
}
return result;
}
@ -171,12 +170,10 @@ class CodeGenerator {
if (this.plugins && contractsPlugins.length > 0) {
contractsPlugins.forEach(function (plugin) {
result += plugin.generateContracts({contracts: self.contractsManager.contracts});
result += plugin.generateContracts({contracts: contractsList});
});
} else {
for (let className in this.contractsManager.contracts) {
let contract = this.contractsManager.contracts[className];
for (let contract of contractsList) {
let abi = JSON.stringify(contract.abiDefinition);
let gasEstimates = JSON.stringify(contract.gasEstimates);
@ -184,9 +181,9 @@ class CodeGenerator {
if (useEmbarkJS) {
let contractAddress = contract.deployedAddress ? ("'" + contract.deployedAddress + "'") : "undefined";
block += Templates.embarkjs_contract({className: className, abi: abi, contract: contract, contractAddress: contractAddress, gasEstimates: gasEstimates});
block += Templates.embarkjs_contract({className: contract.className, abi: abi, contract: contract, contractAddress: contractAddress, gasEstimates: gasEstimates});
} else {
block += Templates.vanilla_contract({className: className, abi: abi, contract: contract, gasLimit: (isDeployment ? 6000000 : false)});
block += Templates.vanilla_contract({className: contract.className, abi: abi, contract: contract, gasLimit: (isDeployment ? 6000000 : false)});
}
result += Templates.exec_when_ready({block: block});
@ -204,6 +201,17 @@ class CodeGenerator {
return block;
}
generateNamesInitialization(useEmbarkJS) {
if (!useEmbarkJS || this.namesystemConfig === {}) return "";
let result = "\n";
result += Templates.define_when_env_loaded();
result += this._getInitCode('names', this.namesystemConfig);
return result;
}
generateStorageInitialization(useEmbarkJS) {
if (!useEmbarkJS || this.storageConfig === {}) return "";
@ -239,35 +247,39 @@ class CodeGenerator {
return result;
}
generateABI(options) {
generateABI(contractsList, options) {
let result = "";
result += this.generateProvider(options.deployment);
result += this.generateContracts(options.useEmbarkJS, options.deployment, true);
result += this.generateContracts(contractsList, options.useEmbarkJS, options.deployment, true);
result += this.generateStorageInitialization(options.useEmbarkJS);
result += this.generateCommunicationInitialization(options.useEmbarkJS);
result += this.generateNamesInitialization(options.useEmbarkJS);
return result;
}
generateContractsJSON() {
generateContractJSON(className, contract) {
let contractJSON = {};
contractJSON.contract_name = className;
contractJSON.address = contract.deployedAddress;
contractJSON.code = contract.code;
contractJSON.runtime_bytecode = contract.runtimeBytecode;
contractJSON.real_runtime_bytecode = contract.realRuntimeBytecode;
contractJSON.swarm_hash = contract.swarmHash;
contractJSON.gas_estimates = contract.gasEstimates;
contractJSON.function_hashes = contract.functionHashes;
contractJSON.abi = contract.abiDefinition;
return contractJSON;
}
generateContractsJSON(contractsList) {
let contracts = {};
for (let className in this.contractsManager.contracts) {
let contract = this.contractsManager.contracts[className];
let contractJSON = {};
contractJSON.contract_name = className;
contractJSON.address = contract.deployedAddress;
contractJSON.code = contract.code;
contractJSON.runtime_bytecode = contract.runtimeBytecode;
contractJSON.real_runtime_bytecode = contract.realRuntimeBytecode;
contractJSON.swarm_hash = contract.swarmHash;
contractJSON.gas_estimates = contract.gasEstimates;
contractJSON.function_hashes = contract.functionHashes;
contractJSON.abi = contract.abiDefinition;
contracts[className] = contractJSON;
for (let contract of contractsList) {
contracts[contract.className] = this.generateContractJSON(contract.className, contract);
}
return contracts;
@ -303,10 +315,9 @@ class CodeGenerator {
code += plugin.embarkjs_code.join('\n');
}
//code += "\n" + fs.readFileSync(fs.embarkPath('js/embarkjs/orbit.js')).toString();
code += self.generateCommunicationInitialization(true);
code += self.generateStorageInitialization(true);
code += self.generateNamesInitialization(true);
next();
},
function writeFile(next) {
@ -318,6 +329,63 @@ class CodeGenerator {
cb();
});
}
buildContractJS(contractName, contractJSON, cb) {
let contractCode = "";
contractCode += "import web3 from 'Embark/web3';\n";
contractCode += "import EmbarkJS from 'Embark/EmbarkJS';\n";
contractCode += "let " + contractName + "JSONConfig = " + JSON.stringify(contractJSON) + ";\n";
contractCode += "let " + contractName + " = new EmbarkJS.Contract(" + contractName + "JSONConfig);\n";
contractCode += "\n__embarkContext.execWhenReady(function() {\n";
contractCode += "\n" + contractName + ".setProvider(web3.currentProvider);\n";
contractCode += "\n" + contractName + ".options.from = web3.eth.defaultAccount;\n";
contractCode += "\n});\n";
contractCode += "export default " + contractName + ";\n";
cb(contractCode);
}
buildWeb3JS(cb) {
const self = this;
let code = "";
async.waterfall([
function getWeb3Location(next) {
self.events.request("version:get:web3", function(web3Version) {
if (web3Version === "1.0.0-beta") {
return next(null, utils.joinPath(fs.embarkPath("js/web3-1.0.min.js")));
} else {
self.events.request("version:getPackageLocation", "web3", web3Version, function(err, location) {
return next(null, fs.dappPath(location));
});
}
});
},
function getImports(web3Location, next) {
web3Location = web3Location.replace(/\\/g, '/'); // Import paths must always have forward slashes
code += "\nimport Web3 from '" + web3Location + "';\n";
code += "\n if (typeof web3 !== 'undefined') {";
code += "\n } else {";
code += "\n var web3 = new Web3();\n";
code += "\n }";
let providerCode = self.generateProvider(false);
code += providerCode;
code += "\nglobal.__embarkContext = __mainContext.__loadManagerInstance;\n";
code += "\nwindow.web3 = web3;\n";
code += "\nexport default web3;\n";
next(null, code);
}
], cb);
}
buildPlaceholderPage(cb) {
let html = Templates.embark_building_placeholder({buildingMsg: __('Embark is building, please wait...')});
cb(html);
}
}
module.exports = CodeGenerator;

File diff suppressed because one or more lines are too long

View File

@ -26,6 +26,11 @@ __reduce(<%- connectionList %>,function(prev, value, next) {
}, function(err, _result) {
__getAccounts(function(err, accounts) {
web3.eth.defaultAccount = accounts[0];
<% if (warnAboutMetamask) { %>
if (web3.eth.currentProvider.isMetaMask) {
console.log("Note: Embark has detected you are in the development environment and using Metamask, please make sure Metamask is connected to your local node");
}
<% } %>
<%- done %>
});
});

View File

@ -43,7 +43,7 @@ class Compiler {
function (err) {
contractFiles.forEach(file => {
if (!file.compiled) {
self.logger.warn(`${file.filename} doesn't have a compatible contract compiler. Maybe a plugin exists for it.`);
self.logger.warn(__("%s doesn't have a compatible contract compiler. Maybe a plugin exists for it.", file.filename));
}
});

View File

@ -0,0 +1,263 @@
let async = require('async');
//require("../utils/debug_util.js")(__filename, async);
let utils = require('../utils/utils.js');
class ContractDeployer {
constructor(options) {
const self = this;
this.blockchain = options.blockchain;
this.logger = options.logger;
this.events = options.events;
this.plugins = options.plugins;
this.gasLimit = options.gasLimit;
self.events.setCommandHandler('deploy:contract', (contract, cb) => {
self.checkAndDeployContract(contract, null, cb);
});
}
// TODO: determining the arguments could also be in a module since it's not
// part of ta 'normal' contract deployment
determineArguments(suppliedArgs, contract, callback) {
const self = this;
let args = suppliedArgs;
if (!Array.isArray(args)) {
args = [];
let abi = contract.abiDefinition.find((abi) => abi.type === 'constructor');
for (let input of abi.inputs) {
let inputValue = suppliedArgs[input.name];
if (!inputValue) {
this.logger.error(__("{{inputName}} has not been defined for {{className}} constructor", {inputName: input.name, className: contract.className}));
}
args.push(inputValue || "");
}
}
async.map(args, (arg, nextEachCb) => {
if (arg[0] === "$") {
let contractName = arg.substr(1);
self.events.request('contracts:contract', contractName, (referedContract) => {
nextEachCb(null, referedContract.deployedAddress);
});
} else if (Array.isArray(arg)) {
async.map(arg, (sub_arg, nextSubEachCb) => {
if (sub_arg[0] === "$") {
let contractName = sub_arg.substr(1);
self.events.request('contracts:contract', contractName, (referedContract) => {
nextSubEachCb(null, referedContract.deployedAddress);
});
} else {
nextSubEachCb(null, sub_arg);
}
}, (err, subRealArgs) => {
nextEachCb(null, subRealArgs);
});
} else {
nextEachCb(null, arg);
}
}, callback);
}
checkAndDeployContract(contract, params, callback) {
let self = this;
contract.error = false;
if (contract.deploy === false) {
self.events.emit("deploy:contract:undeployed", contract);
return callback();
}
async.waterfall([
function _determineArguments(next) {
self.determineArguments(params || contract.args, contract, (err, realArgs) => {
if (err) {
return next(err);
}
contract.realArgs = realArgs;
next();
});
},
function deployIt(next) {
if (contract.address !== undefined) {
try {
utils.toChecksumAddress(contract.address);
} catch(e) {
self.logger.error(__("error deploying %s", contract.className));
self.logger.error(e.message);
contract.error = e.message;
self.events.emit("deploy:contract:error", contract);
return next(e.message);
}
contract.deployedAddress = contract.address;
self.logger.info(contract.className.bold.cyan + __(" already deployed at ").green + contract.address.bold.cyan);
self.events.emit("deploy:contract:deployed", contract);
return next();
}
// TODO find a better way to do that
if (process.env.isTest) {
return self.deployContract(contract, next);
}
// TODO: this should be a plugin API instead, if not existing, it should by default deploy the contract
self.events.request("deploy:contract:shouldDeploy", contract, function(trackedContract) {
if (!trackedContract) {
return self.deployContract(contract, next);
}
self.blockchain.getCode(trackedContract.address, function(_getCodeErr, codeInChain) {
if (codeInChain !== "0x") {
self.contractAlreadyDeployed(contract, trackedContract, next);
} else {
self.deployContract(contract, next);
}
});
});
}
], callback);
}
contractAlreadyDeployed(contract, trackedContract, callback) {
const self = this;
self.logger.info(contract.className.bold.cyan + __(" already deployed at ").green + trackedContract.address.bold.cyan);
contract.deployedAddress = trackedContract.address;
self.events.emit("deploy:contract:deployed", contract);
// TODO: can be moved into a afterDeploy event
// just need to figure out the gasLimit coupling issue
self.events.request('code-generator:contract:vanilla', contract, contract._gasLimit, (contractCode) => {
self.events.request('runcode:eval', contractCode);
return callback();
});
}
deployContract(contract, callback) {
let self = this;
let accounts = [];
let contractParams = (contract.realArgs || contract.args).slice();
let contractCode = contract.code;
let deploymentAccount = self.blockchain.defaultAccount();
let deployObject;
async.waterfall([
// TODO: can potentially go to a beforeDeploy plugin
function getAccounts(next) {
self.blockchain.getAccounts(function (err, _accounts) {
if (err) {
return next(new Error(err));
}
accounts = _accounts;
// applying deployer account configuration, if any
if (typeof contract.fromIndex == 'number') {
deploymentAccount = accounts[contract.fromIndex];
if (deploymentAccount === undefined) {
return next(__("error deploying") + " " + contract.className + ": " + __("no account found at index") + " " + contract.fromIndex + __(" check the config"));
}
}
if (typeof contract.from == 'string' && typeof contract.fromIndex != 'undefined') {
self.logger.warn(__('Both "from" and "fromIndex" are defined for contract') + ' "' + contract.className + '". ' + __('Using "from" as deployer account.'));
}
if (typeof contract.from == 'string') {
deploymentAccount = contract.from;
}
deploymentAccount = deploymentAccount || accounts[0];
next();
});
},
function doLinking(next) {
self.events.request('contracts:list', (_err, contracts) => {
for (let contractObj of contracts) {
let filename = contractObj.filename;
let deployedAddress = contractObj.deployedAddress;
if (deployedAddress) {
deployedAddress = deployedAddress.substr(2);
}
let linkReference = '__' + filename + ":" + contractObj.className;
if (contractCode.indexOf(linkReference) < 0) {
continue;
}
if (linkReference.length > 40) {
return next(new Error(__("{{linkReference}} is too long, try reducing the path of the contract ({{filename}}) and/or its name {{contractName}}", {linkReference: linkReference, filename: filename, contractName: contractObj.className})));
}
let toReplace = linkReference + "_".repeat(40 - linkReference.length);
if (deployedAddress === undefined) {
let libraryName = contractObj.className;
return next(new Error(__("{{contractName}} needs {{libraryName}} but an address was not found, did you deploy it or configured an address?", {contractName: contract.className, libraryName: libraryName})));
}
contractCode = contractCode.replace(new RegExp(toReplace, "g"), deployedAddress);
}
// saving code changes back to contract object
contract.code = contractCode;
next();
});
},
function applyBeforeDeploy(next) {
self.plugins.emitAndRunActionsForEvent('deploy:contract:beforeDeploy', {contract: contract}, next);
},
function createDeployObject(next) {
let contractObject = self.blockchain.ContractObject({abi: contract.abiDefinition});
try {
const dataCode = contractCode.startsWith('0x') ? contractCode : "0x" + contractCode;
deployObject = self.blockchain.deployContractObject(contractObject, {arguments: contractParams, data: dataCode});
} catch(e) {
if (e.message.indexOf('Invalid number of parameters for "undefined"') >= 0) {
return next(new Error(__("attempted to deploy %s without specifying parameters", contract.className)));
} else {
return next(new Error(e));
}
}
next();
},
function estimateCorrectGas(next) {
if (contract.gas === 'auto') {
return self.blockchain.estimateDeployContractGas(deployObject, (err, gasValue) => {
if (err) {
return next(err);
}
contract.gas = gasValue;
next();
});
}
next();
},
function deployTheContract(next) {
self.logger.info(__("deploying") + " " + contract.className.bold.cyan + " " + __("with").green + " " + contract.gas + " " + __("gas").green);
self.blockchain.deployContractFromObject(deployObject, {
from: deploymentAccount,
gas: contract.gas,
gasPrice: contract.gasPrice
}, function(error, receipt) {
if (error) {
contract.error = error.message;
self.events.emit("deploy:contract:error", contract);
return next(new Error("error deploying =" + contract.className + "= due to error: " + error.message));
}
self.logger.info(contract.className.bold.cyan + " " + __("deployed at").green + " " + receipt.contractAddress.bold.cyan);
contract.deployedAddress = receipt.contractAddress;
contract.transactionHash = receipt.transactionHash;
self.events.emit("deploy:contract:receipt", receipt);
self.events.emit("deploy:contract:deployed", contract);
// TODO: can be moved into a afterDeploy event
// just need to figure out the gasLimit coupling issue
self.events.request('code-generator:contract:vanilla', contract, contract._gasLimit, (contractCode) => {
self.events.request('runcode:eval', contractCode);
self.plugins.runActionsForEvent('deploy:contract:deployed', {contract: contract}, () => {
return next(null, receipt);
});
});
});
}
], callback);
}
}
module.exports = ContractDeployer;

View File

@ -1,16 +1,16 @@
let toposort = require('toposort');
let async = require('async');
const cloneDeep = require('clone-deep');
let Compiler = require('./compiler.js');
let utils = require('../utils/utils.js');
const constants = require('../constants');
// TODO: create a contract object
class ContractsManager {
constructor(options) {
const self = this;
this.contractFiles = options.contractFiles;
this.contractsConfig = options.contractsConfig;
this.contractsConfig = cloneDeep(options.contractsConfig || {});
this.contracts = {};
this.logger = options.logger;
this.plugins = options.plugins;
@ -18,26 +18,66 @@ class ContractsManager {
this.gasLimit = options.gasLimit;
this.deployOnlyOnConfig = false;
this.events = options.events;
this.compileError = false;
this.events.on(constants.events.contractFilesChanged, (newContractFiles) => {
this.contractFiles = newContractFiles;
self.events.setCommandHandler('contracts:list', (cb) => {
cb(self.compileError, self.listContracts());
});
this.events.on(constants.events.contractConfigChanged, (newContracts) => {
this.contractsConfig = newContracts;
self.events.setCommandHandler("contracts:contract", (contractName, cb) => {
cb(self.getContract(contractName));
});
self.events.setCommandHandler("contracts:build", (configOnly, cb) => {
self.deployOnlyOnConfig = configOnly; // temporary, should refactor
self.build((err) => {
cb(err);
});
});
self.events.on("deploy:contract:error", (_contract) => {
self.events.emit('contractsState', self.contractsState());
});
self.events.on("deploy:contract:deployed", (_contract) => {
self.events.emit('contractsState', self.contractsState());
});
self.events.on("deploy:contract:undeployed", (_contract) => {
self.events.emit('contractsState', self.contractsState());
});
}
build(done) {
let self = this;
self.contracts = {};
async.waterfall([
function loadContractFiles(callback) {
self.events.request("config:contractsFiles", (contractsFiles) => {
self.contractsFiles = contractsFiles;
callback();
});
},
function loadContractConfigs(callback) {
self.events.request("config:contractsConfig", (contractsConfig) => {
self.contractsConfig = cloneDeep(contractsConfig);
callback();
});
},
function compileContracts(callback) {
let compiler = new Compiler({plugins: self.plugins, logger: self.logger});
compiler.compile_contracts(self.contractFiles, function (err, compiledObject) {
self.events.emit("status", __("Compiling..."));
if (process.env.isTest && self.compiledContracts && Object.keys(self.compiledContracts).length) {
// Only compile once for tests
return callback();
}
self.events.request("compiler:contracts", self.contractFiles, function (err, compiledObject) {
self.compiledContracts = compiledObject;
callback(err);
});
},
function prepareContractsFromConfig(callback) {
self.events.emit("status", __("Building..."));
let className, contract;
for (className in self.contractsConfig.contracts) {
contract = self.contractsConfig.contracts[className];
@ -49,7 +89,13 @@ class ContractsManager {
}
callback();
},
function prepareContractsFromCompilation(callback) {
function getGasPriceForNetwork(callback) {
if (self.contractsConfig.gasPrice) {
return callback(null, self.contractsConfig.gasPrice);
}
self.events.request("blockchain:gasPrice", callback);
},
function prepareContractsFromCompilation(gasPrice, callback) {
let className, compiledContract, contractConfig, contract;
for (className in self.compiledContracts) {
compiledContract = self.compiledContracts[className];
@ -68,7 +114,7 @@ class ContractsManager {
contract.gas = (contractConfig && contractConfig.gas) || self.contractsConfig.gas || 'auto';
contract.gasPrice = contract.gasPrice || self.contractsConfig.gasPrice;
contract.gasPrice = contract.gasPrice || gasPrice;
contract.type = 'file';
contract.className = className;
@ -86,7 +132,7 @@ class ContractsManager {
}
if (contract.code === "") {
self.logger.info("assuming " + className + " to be an interface");
self.logger.info(__("assuming %s to be an interface", className));
contract.deploy = false;
}
}
@ -108,15 +154,15 @@ class ContractsManager {
parentContract = self.contracts[parentContractName];
if (parentContract === className) {
self.logger.error(className + ": instanceOf is set to itself");
self.logger.error(__("%s : instanceOf is set to itself", className));
continue;
}
if (parentContract === undefined) {
self.logger.error(className + ": couldn't find instanceOf contract " + parentContractName);
self.logger.error(__("{{className}}: couldn't find instanceOf contract {{parentContractName}}", {className: className, parentContractName: parentContractName}));
let suggestion = utils.proposeAlternative(parentContractName, dictionary, [className, parentContractName]);
if (suggestion) {
self.logger.warn('did you mean "' + suggestion + '"?');
self.logger.warn(__('did you mean "%s"?', suggestion));
}
continue;
}
@ -126,7 +172,7 @@ class ContractsManager {
}
if (contract.code !== undefined) {
self.logger.error(className + " has code associated to it but it's configured as an instanceOf " + parentContractName);
self.logger.error(__("{{className}} has code associated to it but it's configured as an instanceOf {{parentContractName}}", {className: className, parentContractName: parentContractName}));
}
contract.code = parentContract.code;
@ -149,10 +195,10 @@ class ContractsManager {
contract = self.contracts[className];
if (contract.code === undefined) {
self.logger.error(className + " has no code associated");
self.logger.error(__("%s has no code associated", className));
let suggestion = utils.proposeAlternative(className, dictionary, [className]);
if (suggestion) {
self.logger.warn('did you mean "' + suggestion + '"?');
self.logger.warn(__('did you mean "%s"?', suggestion));
}
delete self.contracts[className];
}
@ -191,12 +237,14 @@ class ContractsManager {
if (arg[0] === "$") {
self.contractDependencies[className] = self.contractDependencies[className] || [];
self.contractDependencies[className].push(arg.substr(1));
self.checkDependency(className, arg.substr(1));
}
if (Array.isArray(arg)) {
for (let sub_arg of arg) {
if (sub_arg[0] === "$") {
self.contractDependencies[className] = self.contractDependencies[className] || [];
self.contractDependencies[className].push(sub_arg.substr(1));
self.checkDependency(className, sub_arg.substr(1));
}
}
}
@ -216,13 +264,32 @@ class ContractsManager {
}
], function (err, _result) {
if (err) {
self.logger.error("Error Compiling/Building contracts: " + err);
self.compileError = true;
self.events.emit("status", __("Compile/Build error"));
self.logger.error(__("Error Compiling/Building contracts: ") + err);
}else{
self.compileError = false;
}
self.logger.trace("finished".underline);
done(err, self);
});
}
checkDependency(className, dependencyName) {
if (!this.contractDependencies[className]) {
return;
}
if (!this.contracts[dependencyName]) {
this.logger.warn(__('{{className}} has a dependency on {{dependencyName}}', {className, dependencyName}) +
__(', but it is not present in the contracts'));
return;
}
if (!this.contracts[dependencyName].deploy) {
this.logger.warn(__('{{className}} has a dependency on {{dependencyName}}', {className, dependencyName}) +
__(', but it is not set to deploy. It could be an interface.'));
}
}
getContract(className) {
return this.contracts[className];
}
@ -242,9 +309,9 @@ class ContractsManager {
try {
orderedDependencies = toposort(converted_dependencies.filter((x) => x[0] != x[1])).reverse();
} catch(e) {
this.logger.error(("Error: " + e.message).red);
this.logger.error("there are two or more contracts that depend on each other in a cyclic manner".bold.red);
this.logger.error("Embark couldn't determine which one to deploy first".red);
this.logger.error((__("Error: ") + e.message).red);
this.logger.error(__("there are two or more contracts that depend on each other in a cyclic manner").bold.red);
this.logger.error(__("Embark couldn't determine which one to deploy first").red);
throw new Error("CyclicDependencyError");
//process.exit(0);
}
@ -279,7 +346,7 @@ class ContractsManager {
if (contract.deploy === false) {
contractData = [
className.green,
'Interface or set to not deploy'.green,
__('Interface or set to not deploy').green,
"\t\tn/a".green
];
} else if (contract.error) {
@ -292,7 +359,7 @@ class ContractsManager {
contractData = [
className.green,
(contract.deployedAddress || '...').green,
((contract.deployedAddress !== undefined) ? "\t\tDeployed".green : "\t\tPending".magenta)
((contract.deployedAddress !== undefined) ? ("\t\t" + __("Deployed")).green : ("\t\t" + __("Pending")).magenta)
];
}

View File

@ -1,381 +0,0 @@
let async = require('async');
//require("../utils/debug_util.js")(__filename, async);
let RunCode = require('../core/runCode.js');
let DeployTracker = require('./deploy_tracker.js');
let CodeGenerator = require('./code_generator.js');
class Deploy {
constructor(options) {
this.web3 = options.web3;
this.contractsManager = options.contractsManager;
this.logger = options.logger;
this.events = options.events;
this.env = options.env;
this.chainConfig = options.chainConfig;
this.plugins = options.plugins;
this.gasLimit = options.gasLimit;
}
initTracker(cb) {
this.deployTracker = new DeployTracker({
logger: this.logger, chainConfig: this.chainConfig, web3: this.web3, env: this.env
}, cb);
}
determineArguments(suppliedArgs, contract) {
let realArgs = [], l, arg, contractName, referedContract;
let args = suppliedArgs;
if (!Array.isArray(args)) {
args = [];
let abi = contract.abiDefinition.find((abi) => abi.type === 'constructor');
for (let input of abi.inputs) {
let inputValue = suppliedArgs[input.name];
if (!inputValue) {
this.logger.error(input.name + " has not been defined for " + contract.className + " constructor");
}
args.push(inputValue || "");
}
}
for (l = 0; l < args.length; l++) {
arg = args[l];
if (arg[0] === "$") {
contractName = arg.substr(1);
referedContract = this.contractsManager.getContract(contractName);
realArgs.push(referedContract.deployedAddress);
} else if (Array.isArray(arg)) {
let subRealArgs = [];
for (let sub_arg of arg) {
if (sub_arg[0] === "$") {
contractName = sub_arg.substr(1);
referedContract = this.contractsManager.getContract(contractName);
subRealArgs.push(referedContract.deployedAddress);
} else {
subRealArgs.push(sub_arg);
}
}
realArgs.push(subRealArgs);
} else {
realArgs.push(arg);
}
}
return realArgs;
}
checkAndDeployContract(contract, params, callback) {
let self = this;
let realArgs;
contract.error = false;
if (contract.deploy === false) {
self.events.emit('contractsState', self.contractsManager.contractsState());
return callback();
}
realArgs = self.determineArguments(params || contract.args, contract);
if (contract.address !== undefined) {
try {
this.web3.utils.toChecksumAddress(contract.address);
} catch(e) {
self.logger.error("error deploying " + contract.className);
self.logger.error(e.message);
contract.error = e.message;
self.events.emit('contractsState', self.contractsManager.contractsState());
return callback(e.message);
}
contract.deployedAddress = contract.address;
self.logger.info(contract.className.bold.cyan + " already deployed at ".green + contract.address.bold.cyan);
if (this.deployTracker) {
self.deployTracker.trackContract(contract.className, contract.realRuntimeBytecode, realArgs, contract.address);
self.deployTracker.save();
}
self.events.emit('contractsState', self.contractsManager.contractsState());
return callback();
}
if (!this.deployTracker) {
return self.contractToDeploy(contract, params, callback);
}
let trackedContract = self.deployTracker.getContract(contract.className, contract.realRuntimeBytecode, realArgs);
if (!trackedContract) {
return self.contractToDeploy(contract, params, callback);
}
this.web3.eth.getCode(trackedContract.address, function(_getCodeErr, codeInChain) {
if (codeInChain !== "0x") {
self.contractAlreadyDeployed(contract, trackedContract, callback);
} else {
self.contractToDeploy(contract, params, callback);
}
});
}
contractAlreadyDeployed(contract, trackedContract, callback) {
const self = this;
self.logger.info(contract.className.bold.cyan + " already deployed at ".green + trackedContract.address.bold.cyan);
contract.deployedAddress = trackedContract.address;
self.events.emit('contractsState', self.contractsManager.contractsState());
// always run contractCode so other functionality like 'afterDeploy' can also work
let codeGenerator = new CodeGenerator({contractsManager: self.contractsManager});
let contractCode = codeGenerator.generateContractCode(contract, self.gasLimit);
RunCode.doEval(contractCode, self.web3);
return callback();
}
contractToDeploy(contract, params, callback) {
const self = this;
let realArgs = self.determineArguments(params || contract.args, contract);
this.deployContract(contract, realArgs, function (err, address) {
if (err) {
return callback(new Error(err));
}
self.deployTracker.trackContract(contract.className, contract.realRuntimeBytecode, realArgs, address);
self.deployTracker.save();
self.events.emit('contractsState', self.contractsManager.contractsState());
// always run contractCode so other functionality like 'afterDeploy' can also work
let codeGenerator = new CodeGenerator({contractsManager: self.contractsManager});
let contractCode = codeGenerator.generateContractCode(contract, self.gasLimit);
RunCode.doEval(contractCode, self.web3);
if (contract.onDeploy !== undefined) {
self.logger.info('executing onDeploy commands');
let contractCode = codeGenerator.generateContractCode(contract, self.gasLimit);
RunCode.doEval(contractCode, self.web3);
let withErrors = false;
let regex = /\$\w+/g;
let onDeployCode = contract.onDeploy.map((cmd) => {
let realCmd = cmd.replace(regex, (match) => {
let referedContractName = match.slice(1);
let referedContract = self.contractsManager.getContract(referedContractName);
if (!referedContract) {
self.logger.error('error executing onDeploy for ' + contract.className);
self.logger.error(referedContractName + ' does not exist');
self.logger.error("error running onDeploy: " + cmd);
withErrors = true;
return;
}
if (referedContract && referedContract.deploy === false) {
self.logger.error('error executing onDeploy for ' + contract.className);
self.logger.error(referedContractName + " exists but has been set to not deploy");
self.logger.error("error running onDeploy: " + cmd);
withErrors = true;
return;
}
if (referedContract && !referedContract.deployedAddress) {
self.logger.error('error executing onDeploy for ' + contract.className);
self.logger.error("couldn't find a valid address for " + referedContractName + ". has it been deployed?");
self.logger.error("error running onDeploy: " + cmd);
withErrors = true;
return;
}
return referedContract.deployedAddress;
});
return realCmd;
});
if (withErrors) {
contract.error = "onDeployCmdError";
return callback(new Error("error running onDeploy"));
}
// TODO: convert to for to avoid repeated callback
for(let cmd of onDeployCode) {
self.logger.info("executing: " + cmd);
try {
RunCode.doEval(cmd, self.web3);
} catch(e) {
if (e.message.indexOf("invalid opcode") >= 0) {
self.logger.error('the transaction was rejected; this usually happens due to a throw or a require, it can also happen due to an invalid operation');
}
return callback(new Error(e));
}
}
}
callback();
});
}
deployContract(contract, params, callback) {
let self = this;
let accounts = [];
let contractParams = (params || contract.args).slice();
let contractCode = contract.code;
let deploymentAccount = self.web3.eth.defaultAccount;
let deployObject;
async.waterfall([
function getAccounts(next) {
self.web3.eth.getAccounts(function (err, _accounts) {
if (err) {
return next(new Error(err));
}
accounts = _accounts;
// applying deployer account configuration, if any
if (typeof contract.fromIndex == 'number') {
deploymentAccount = accounts[contract.fromIndex];
if (deploymentAccount === undefined) {
return next("error deploying " + contract.className + ": no account found at index " + contract.fromIndex + " check the config");
}
}
if (typeof contract.from == 'string' && typeof contract.fromIndex != 'undefined') {
self.logger.warn('Both "from" and "fromIndex" are defined for contract "'+contract.className+'". Using "from" as deployer account.');
}
if (typeof contract.from == 'string') {
deploymentAccount = contract.from;
}
deploymentAccount = deploymentAccount || accounts[0];
next();
});
},
function doLinking(next) {
// Applying linked contracts
let contractsList = self.contractsManager.listContracts();
for (let contractObj of contractsList) {
let filename = contractObj.filename;
let deployedAddress = contractObj.deployedAddress;
if (deployedAddress) {
deployedAddress = deployedAddress.substr(2);
}
let linkReference = '__' + filename + ":" + contractObj.className;
if (contractCode.indexOf(linkReference) < 0) {
continue;
}
if (linkReference.length > 40) {
return next(new Error(linkReference + " is too long, try reducing the path of the contract (" + filename + ") and/or its name " + contractObj.className));
}
let toReplace = linkReference + "_".repeat(40 - linkReference.length);
if (deployedAddress === undefined) {
let libraryName = contractObj.className;
return next(new Error(contract.className + " needs " + libraryName + " but an address was not found, did you deploy it or configured an address?"));
}
contractCode = contractCode.replace(new RegExp(toReplace, "g"), deployedAddress);
}
// saving code changes back to contract object
contract.code = contractCode;
next();
},
function applyBeforeDeploy(next) {
let beforeDeployPlugins = self.plugins.getPluginsFor('beforeDeploy');
//self.logger.info("applying beforeDeploy plugins...", beforeDeployPlugins.length);
async.eachSeries(beforeDeployPlugins, (plugin, eachPluginCb) => {
self.logger.info("running beforeDeploy plugin " + plugin.name + " .");
// calling each beforeDeploy handler declared by the plugin
async.eachSeries(plugin.beforeDeploy, (beforeDeployFn, eachCb) => {
function beforeDeployCb(resObj){
contract.code = resObj.contractCode;
eachCb();
}
beforeDeployFn({
embarkDeploy: self,
pluginConfig: plugin.pluginConfig,
deploymentAccount: deploymentAccount,
contract: contract,
callback: beforeDeployCb
}, beforeDeployCb);
}, () => {
//self.logger.info('All beforeDeploy handlers of the plugin has processed.');
eachPluginCb();
});
}, () => {
//self.logger.info('All beforeDeploy plugins has been processed.');
contractCode = contract.code;
next();
});
},
function createDeployObject(next) {
let contractObject = new self.web3.eth.Contract(contract.abiDefinition);
try {
const dataCode = contractCode.startsWith('0x') ? contractCode : "0x" + contractCode;
deployObject = contractObject.deploy({arguments: contractParams, data: dataCode});
} catch(e) {
if (e.message.indexOf('Invalid number of parameters for "undefined"') >= 0) {
return next(new Error("attempted to deploy " + contract.className + " without specifying parameters"));
} else {
return next(new Error(e));
}
}
next();
},
function estimateCorrectGas(next) {
if (contract.gas === 'auto') {
return deployObject.estimateGas().then((gasValue) => {
contract.gas = gasValue;
next();
}).catch(next);
}
next();
},
function deployTheContract(next) {
self.logger.info("deploying " + contract.className.bold.cyan + " with ".green + contract.gas + " gas".green);
deployObject.send({
from: deploymentAccount,
gas: contract.gas,
gasPrice: contract.gasPrice
}).on('receipt', function(receipt) {
if (receipt.contractAddress !== undefined) {
self.logger.info(contract.className.bold.cyan + " deployed at ".green + receipt.contractAddress.bold.cyan);
contract.deployedAddress = receipt.contractAddress;
contract.transactionHash = receipt.transactionHash;
self.events.emit('contractsState', self.contractsManager.contractsState());
return next(null, receipt.contractAddress);
}
self.events.emit('contractsState', self.contractsManager.contractsState());
}).on('error', function(error) {
self.events.emit('contractsState', self.contractsManager.contractsState());
return next(new Error("error deploying =" + contract.className + "= due to error: " + error.message));
});
}
], callback);
}
deployAll(done) {
let self = this;
this.logger.info("deploying contracts");
let contracts = this.contractsManager.listContracts();
async.eachOfSeries(contracts,
function (contract, key, callback) {
self.logger.trace(arguments);
self.checkAndDeployContract(contract, null, callback);
},
function (err, _results) {
if (err) {
self.logger.error("error deploying contracts");
self.logger.error(err.message);
self.logger.debug(err.stack);
}
if (contracts.length === 0) {
self.logger.info("no contracts found");
return done();
}
self.logger.info("finished deploying contracts");
self.logger.trace(arguments);
done(err);
}
);
}
}
module.exports = Deploy;

View File

@ -1,161 +1,117 @@
let async = require('async');
//require("../utils/debug_util.js")(__filename, async);
let Deploy = require('./deploy.js');
let RunCode = require('../core/runCode.js');
class DeployManager {
constructor(options) {
const self = this;
this.config = options.config;
this.logger = options.logger;
this.blockchainConfig = this.config.blockchainConfig;
this.events = options.events;
this.plugins = options.plugins;
this.web3 = options.web3;
this.chainConfig = (options.trackContracts !== false) ? this.config.chainTracker : false;
this.contractsManager = options.contractsManager;
this.blockchain = options.blockchain;
this.gasLimit = false;
this.fatalErrors = false;
this.deployOnlyOnConfig = false;
this.onlyCompile = options.onlyCompile !== undefined ? options.onlyCompile : false;
this.events.setCommandHandler('deploy:contracts', (cb) => {
self.deployContracts(cb);
});
}
deployAll(done) {
let self = this;
self.events.request('contracts:list', (err, contracts) => {
if (err) {
return done(err);
}
self.logger.info(__("deploying contracts"));
self.events.emit("deploy:beforeAll");
async.eachOfSeries(contracts,
function (contract, key, callback) {
contract._gasLimit = self.gasLimit;
self.events.request('deploy:contract', contract, (err) => {
callback(err);
});
},
function (err, _results) {
if (err) {
self.logger.error(__("error deploying contracts"));
self.logger.error(err.message);
self.logger.debug(err.stack);
}
if (contracts.length === 0) {
self.logger.info(__("no contracts found"));
return done();
}
self.logger.info(__("finished deploying contracts"));
done(err);
}
);
});
}
deployContracts(done) {
let self = this;
if (self.blockchainConfig === {} || self.blockchainConfig.enabled === false) {
self.logger.info("Blockchain component is disabled in the config".underline);
self.logger.info(__("Blockchain component is disabled in the config").underline);
this.events.emit('blockchainDisabled', {});
return done();
}
async.waterfall([
function buildContracts(callback) {
self.contractsManager.deployOnlyOnConfig = self.deployOnlyOnConfig; // temporary, should refactor
self.contractsManager.build(callback);
self.events.request("contracts:build", self.deployOnlyOnConfig, (err) => {
callback(err);
});
},
function checkCompileOnly(contractsManager, callback){
// TODO: shouldn't be necessary
function checkCompileOnly(callback){
if(self.onlyCompile){
self.events.emit('contractsDeployed', contractsManager);
self.events.emit('contractsDeployed');
return done();
}
return callback(null, contractsManager);
return callback();
},
function checkWeb3IsConnected(contractsManager, callback) {
if (!self.web3) {
return callback(Error("no web3 instance found"));
}
if (self.web3.currentProvider === undefined) {
self.logger.error(("Couldn't connect to an Ethereum node are you sure it's on?").red);
self.logger.info("make sure you have an Ethereum node or simulator running. e.g 'embark blockchain'".magenta);
return callback(Error("error connecting to blockchain node"));
}
self.web3.eth.getAccounts(function(err, _accounts) {
if (err) {
self.logger.error(("Couldn't connect to an Ethereum node are you sure it's on?").red);
self.logger.info("make sure you have an Ethereum node or simulator running. e.g 'embark blockchain'".magenta);
return callback(Error("error connecting to blockchain node"));
}
return callback(null, contractsManager, self.web3);
});
},
function setDefaultAccount(contractsManager, web3, callback) {
web3.eth.getAccounts(function (err, accounts) {
if (err) {
self.logger.error(err);
return callback(new Error(err));
}
let accountConfig = self.config.blockchainConfig.account;
let selectedAccount = accountConfig && accountConfig.address;
web3.eth.defaultAccount = (selectedAccount || accounts[0]);
callback(null, contractsManager, web3);
});
},
function deployAllContracts(contractsManager, web3, callback) {
let deploy = new Deploy({
web3: web3,
contractsManager: contractsManager,
logger: self.logger,
events: self.events,
chainConfig: self.chainConfig,
env: self.config.env,
plugins: self.plugins,
gasLimit: self.gasLimit
});
deploy.initTracker(function() {
deploy.deployAll(function (err) {
if (!err) {
self.events.emit('contractsDeployed', contractsManager);
}
if (err && self.fatalErrors) {
return callback(err);
}
callback(null, contractsManager, web3);
// TODO: could be implemented as an event (beforeDeployAll)
function checkIsConnectedToBlockchain(callback) {
self.blockchain.onReady(() => {
self.blockchain.assertNodeConnection((err) => {
callback(err);
});
});
},
function runAfterDeployCommands(contractsManager, web3, callback) {
let afterDeployCmds = self.config.contractsConfig.afterDeploy || [];
let withErrors = false;
let regex = /\$\w+/g;
let onDeployCode = afterDeployCmds.map((cmd) => {
let realCmd = cmd.replace(regex, (match) => {
let referedContractName = match.slice(1);
let referedContract = contractsManager.getContract(referedContractName);
if (!referedContract) {
self.logger.error(referedContractName + ' does not exist');
self.logger.error("error running afterDeploy: " + cmd);
withErrors = true;
return;
}
if (referedContract && referedContract.deploy === false) {
self.logger.error(referedContractName + " exists but has been set to not deploy");
self.logger.error("error running afterDeploy: " + cmd);
withErrors = true;
return;
}
if (referedContract && !referedContract.deployedAddress) {
self.logger.error("couldn't find a valid address for " + referedContractName + ". has it been deployed?");
self.logger.error("error running afterDeploy: " + cmd);
withErrors = true;
return;
}
return referedContract.deployedAddress;
});
return realCmd;
// TODO: this can be done on the fly or as part of the initialization
function determineDefaultAccount(callback) {
self.blockchain.determineDefaultAccount((err) => {
callback(err);
});
},
if (withErrors) {
return callback(new Error("error running afterDeploy"));
}
async.each(onDeployCode, (cmd, eachCb) => {
self.logger.info("executing: " + cmd);
try {
RunCode.doEval(cmd, web3);
} catch(e) {
if (e.message.indexOf("invalid opcode") >= 0) {
self.logger.error('the transaction was rejected; this usually happens due to a throw or a require, it can also happen due to an invalid operation');
}
return eachCb(new Error(e));
function deployAllContracts(callback) {
self.deployAll(function (err) {
if (!err) {
self.events.emit('contractsDeployed');
}
eachCb();
}, (err) => {
callback(err, contractsManager);
if (err && self.fatalErrors) {
return callback(err);
}
callback();
});
}
], function (err, result) {
if (err) {
done(err, null);
} else {
done(null, result);
},
function runAfterDeploy(callback) {
self.plugins.emitAndRunActionsForEvent('contracts:deploy:afterAll', callback);
}
], function (err, _result) {
done(err);
});
}

View File

@ -1,64 +0,0 @@
let fs = require('../core/fs.js');
class DeployTracker {
constructor(options, cb) {
const self = this;
this.logger = options.logger;
this.env = options.env;
this.chainConfig = options.chainConfig;
this.web3 = options.web3;
if (this.chainConfig === false) {
this.currentChain = {contracts: []};
return cb();
}
this.web3.eth.getBlock(0, function(err, block) {
let chainId = block.hash;
if (self.chainConfig[chainId] === undefined) {
self.chainConfig[chainId] = {contracts: {}};
}
self.currentChain = self.chainConfig[chainId];
self.currentChain.name = self.env;
cb();
});
// TODO: add other params
//this.currentChain.networkId = "";
//this.currentChain.networkType = "custom"
}
loadConfig(config) {
this.chainConfig = config;
return this;
}
trackContract(contractName, code, args, address) {
this.currentChain.contracts[this.web3.utils.sha3(code + contractName + args.join(','))] = {
name: contractName,
address: address
};
}
getContract(contractName, code, args) {
let contract = this.currentChain.contracts[this.web3.utils.sha3(code + contractName + args.join(','))];
if (contract && contract.address === undefined) {
return false;
}
return contract;
}
// TODO: abstract this
// chainConfig can be an abstract PersistentObject
save() {
if (this.chainConfig === false) {
return;
}
fs.writeJSONSync("./chains.json", this.chainConfig, {spaces: 2});
}
}
module.exports = DeployTracker;

View File

@ -0,0 +1,76 @@
const async = require('async');
const TARGET = 0x7FFFFFFFFFFFFFFF;
const ALREADY_FUNDED = 'alreadyFunded';
function fundAccount(web3, accountAddress, hexBalance, callback) {
if (!hexBalance) {
hexBalance = TARGET;
}
const targetBalance = (typeof hexBalance === 'string') ? parseInt(hexBalance, 16) : hexBalance;
let accountBalance;
let coinbaseAddress;
let lastNonce;
let gasPrice;
async.waterfall([
function getAccountBalance(next) {
web3.eth.getBalance(accountAddress, (err, balance) => {
if (err) {
return next(err);
}
if (balance >= targetBalance) {
return next(ALREADY_FUNDED);
}
accountBalance = balance;
next();
});
},
function getNeededParams(next) {
async.parallel([
function getCoinbaseAddress(paraCb) {
web3.eth.getCoinbase()
.then((address) => {
coinbaseAddress = address;
paraCb();
}).catch(paraCb);
},
function getGasPrice(paraCb) {
web3.eth.getGasPrice((err, price) => {
if (err) {
return paraCb(err);
}
gasPrice = price;
paraCb();
});
}
], (err, _result) => {
next(err);
});
},
function getNonce(next) {
web3.eth.getTransactionCount(coinbaseAddress, (err, nonce) => {
if (err) {
return next(err);
}
lastNonce = nonce;
next();
});
},
function sendTransaction(next) {
web3.eth.sendTransaction({
from: coinbaseAddress,
to: accountAddress,
value: targetBalance - accountBalance,
gasPrice: gasPrice,
nonce: lastNonce
}, next);
}
], (err) => {
if (err && err !== ALREADY_FUNDED) {
return callback(err);
}
callback();
});
}
module.exports = fundAccount;

113
lib/contracts/provider.js Normal file
View File

@ -0,0 +1,113 @@
const ProviderEngine = require('embark-web3-provider-engine');
const RpcSubprovider = require('embark-web3-provider-engine/subproviders/rpc');
const WsSubprovider = require('embark-web3-provider-engine/subproviders/websocket');
const async = require('async');
const AccountParser = require('./accountParser');
const fundAccount = require('./fundAccount');
const NO_ACCOUNTS = 'noAccounts';
class Provider {
constructor(options) {
this.web3 = options.web3;
this.accountsConfig = options.accountsConfig;
this.blockchainConfig = options.blockchainConfig;
this.type = options.type;
this.web3Endpoint = options.web3Endpoint;
this.logger = options.logger;
this.isDev = options.isDev;
this.engine = new ProviderEngine();
this.asyncMethods = {};
}
startWeb3Provider(callback) {
const self = this;
if (this.type === 'rpc') {
self.engine.addProvider(new RpcSubprovider({
rpcUrl: self.web3Endpoint
}));
} else if (this.type === 'ws') {
self.engine.addProvider(new WsSubprovider({
rpcUrl: self.web3Endpoint,
origin: this.blockchainConfig.wsOrigins.split(',')[0]
}));
} else {
return callback(__("contracts config error: unknown deployment type %s", this.type));
}
// network connectivity error
self.engine.on('error', (err) => {
// report connectivity errors
self.logger.error(err.stack);
});
self.engine.start();
self.web3.setProvider(self);
self.accounts = AccountParser.parseAccountsConfig(self.accountsConfig, self.web3, self.logger);
self.addresses = [];
async.waterfall([
function populateWeb3Wallet(next) {
if (!self.accounts.length) {
return next(NO_ACCOUNTS);
}
self.accounts.forEach(account => {
self.addresses.push(account.address);
self.web3.eth.accounts.wallet.add(account);
});
self.asyncMethods = {
eth_accounts: self.eth_accounts.bind(self)
};
next();
}
], function (err) {
if (err && err !== NO_ACCOUNTS) {
self.logger.error((err));
}
callback();
});
}
fundAccounts(callback) {
const self = this;
if (!self.accounts.length) {
return callback();
}
if (!self.isDev) {
return callback();
}
async.each(self.accounts, (account, eachCb) => {
fundAccount(self.web3, account.address, account.hexBalance, eachCb);
}, callback);
}
stop() {
this.engine.stop();
}
eth_accounts(payload, cb) {
return cb(null, this.addresses);
}
sendAsync(payload, callback) {
let method = this.asyncMethods[payload.method];
if (method) {
return method.call(method, payload, (err, result) => {
if (err) {
return callback(err);
}
let response = {'id': payload.id, 'jsonrpc': '2.0', 'result': result};
callback(null, response);
});
}
this.engine.sendAsync.apply(this.engine, arguments);
}
send() {
return this.engine.send.apply(this.engine, arguments);
}
}
module.exports = Provider;

View File

@ -7,6 +7,7 @@ const deepEqual = require('deep-equal');
const constants = require('../constants');
var Config = function(options) {
const self = this;
this.env = options.env;
this.blockchainConfig = {};
this.contractsConfig = {};
@ -22,6 +23,21 @@ var Config = function(options) {
this.events = options.events;
this.embarkConfig = {};
this.context = options.context || [constants.contexts.any];
self.events.setCommandHandler("config:contractsConfig", (cb) => {
cb(self.contractsConfig);
});
self.events.setCommandHandler("config:contractsFiles", (cb) => {
cb(self.contractsFiles);
});
// TODO: refactor this so reading the file can be done with a normal resolver or something that takes advantage of the plugin api
self.events.setCommandHandler("config:contractsFiles:add", (filename) => {
self.contractsFiles.push(new File({filename: filename, type: File.types.custom, path: filename, resolver: function(callback) {
callback(fs.readFileSync(filename).toString());
}}));
});
};
Config.prototype.loadConfigFiles = function(options) {
@ -31,20 +47,21 @@ Config.prototype.loadConfigFiles = function(options) {
}
if (!fs.existsSync(options.embarkConfig)){
this.logger.error('Cannot find file ' + options.embarkConfig + '. Please ensure you are running this command inside the Dapp folder');
this.logger.error(__('Cannot find file %s Please ensure you are running this command inside the Dapp folder', options.embarkConfig));
process.exit(1);
}
this.embarkConfig = fs.readJSONSync(options.embarkConfig);
this.embarkConfig.plugins = this.embarkConfig.plugins || {};
this.plugins = new Plugins({plugins: this.embarkConfig.plugins, logger: this.logger, interceptLogs: interceptLogs, events: this.events, config: this, context: this.context});
this.plugins = new Plugins({plugins: this.embarkConfig.plugins, logger: this.logger, interceptLogs: interceptLogs, events: this.events, config: this, context: this.context, env: this.env});
this.plugins.loadPlugins();
this.loadEmbarkConfigFile();
this.loadBlockchainConfigFile();
this.loadStorageConfigFile();
this.loadCommunicationConfigFile();
this.loadNameSystemConfigFile();
this.loadContractsConfigFile();
this.loadPipelineConfigFile();
@ -54,6 +71,8 @@ Config.prototype.loadConfigFiles = function(options) {
this.loadWebServerConfigFile();
this.loadChainTrackerFile();
this.loadPluginContractFiles();
this._updateBlockchainCors();
};
Config.prototype.reloadConfig = function() {
@ -61,11 +80,43 @@ Config.prototype.reloadConfig = function() {
this.loadBlockchainConfigFile();
this.loadStorageConfigFile();
this.loadCommunicationConfigFile();
this.loadNameSystemConfigFile();
this.loadContractsConfigFile();
this.loadPipelineConfigFile();
this.loadContractsConfigFile();
this.loadExternalContractsFiles();
this.loadChainTrackerFile();
this._updateBlockchainCors();
};
Config.prototype._updateBlockchainCors = function(){
let blockchainConfig = this.blockchainConfig;
let storageConfig = this.storageConfig;
let webServerConfig = this.webServerConfig;
let corsParts = [];
if(webServerConfig && webServerConfig.enabled) {
if(webServerConfig.host) corsParts.push(utils.buildUrlFromConfig(webServerConfig));
}
if(storageConfig && storageConfig.enabled) {
// if getUrl is specified in the config, that needs to be included in cors
// instead of the concatenated protocol://host:port
if(storageConfig.upload.getUrl) {
// remove /ipfs or /bzz: from getUrl if it's there
let getUrlParts = storageConfig.upload.getUrl.split('/');
getUrlParts = getUrlParts.slice(0, 3);
corsParts.push(getUrlParts.join('/'));
}
// use our modified getUrl or in case it wasn't specified, use a built url
else{
corsParts.push(utils.buildUrlFromConfig(storageConfig.upload));
}
}
let cors = corsParts.join(',');
if(blockchainConfig.rpcCorsDomain === 'auto' && cors.length) blockchainConfig.rpcCorsDomain = cors;
if(blockchainConfig.wsOrigins === 'auto' && cors.length) blockchainConfig.wsOrigins = cors;
};
Config.prototype._mergeConfig = function(configFilePath, defaultConfig, env, enabledByDefault) {
@ -75,15 +126,22 @@ Config.prototype._mergeConfig = function(configFilePath, defaultConfig, env, ena
return configToReturn;
}
if (!fs.existsSync(configFilePath)) {
// due to embark.json; TODO: refactor this
configFilePath = configFilePath.replace('.json','').replace('.js', '');
if (!fs.existsSync(configFilePath + '.js') && !fs.existsSync(configFilePath + '.json')) {
// TODO: remove this if
if (this.logger) {
this.logger.warn("no config file found at " + configFilePath + ". using default config");
this.logger.warn(__("no config file found at %s using default config", configFilePath));
}
return defaultConfig['default'] || {};
}
let config = fs.readJSONSync(configFilePath);
let config;
if (fs.existsSync(configFilePath + '.js')) {
config = require(fs.dappPath(configFilePath + '.js'));
} else {
config = fs.readJSONSync(configFilePath + '.json');
}
let configObject = utils.recursiveMerge(defaultConfig, config);
if (env) {
@ -109,7 +167,7 @@ Config.prototype.loadBlockchainConfigFile = function() {
}
};
let configFilePath = this._getFileOrOject(this.configDir, 'blockchain.json', 'blockchain');
let configFilePath = this._getFileOrOject(this.configDir, 'blockchain', 'blockchain');
this.blockchainConfig = this._mergeConfig(configFilePath, configObject, this.env, true);
};
@ -142,7 +200,7 @@ Config.prototype.loadContractsConfigFile = function() {
configObject = utils.recursiveMerge(configObject, pluginConfig);
});
let configFilePath = this._getFileOrOject(this.configDir, 'contracts.json', 'contracts');
let configFilePath = this._getFileOrOject(this.configDir, 'contracts', 'contracts');
const newContractsConfig = this._mergeConfig(configFilePath, configObject, this.env);
@ -162,7 +220,7 @@ Config.prototype.loadExternalContractsFiles = function() {
if (contract.file.startsWith('http') || contract.file.startsWith('git')) {
const fileObj = utils.getExternalContractUrl(contract.file);
if (!fileObj) {
return this.logger.error("HTTP contract file not found: " + contract.file);
return this.logger.error(__("HTTP contract file not found") + ": " + contract.file);
}
const localFile = fileObj.filePath;
this.contractsFiles.push(new File({filename: localFile, type: File.types.http, basedir: '', path: fileObj.url}));
@ -171,13 +229,13 @@ Config.prototype.loadExternalContractsFiles = function() {
} else if (fs.existsSync(path.join('./node_modules/', contract.file))) {
this.contractsFiles.push(new File({filename: path.join('./node_modules/', contract.file), type: File.types.dapp_file, basedir: '', path: path.join('./node_modules/', contract.file)}));
} else {
this.logger.error("contract file not found: " + contract.file);
this.logger.error(__("contract file not found") + ": " + contract.file);
}
}
};
Config.prototype.loadStorageConfigFile = function() {
var versions = utils.recursiveMerge({"ipfs-api": "17.2.4"}, this.embarkConfig.versions || {});
var versions = utils.recursiveMerge({"ipfs-api": "17.2.4", "p-iteration": "1.1.7"}, this.embarkConfig.versions || {});
var configObject = {
"default": {
@ -185,32 +243,50 @@ Config.prototype.loadStorageConfigFile = function() {
"enabled": true,
"available_providers": ["ipfs", "swarm"],
"ipfs_bin": "ipfs",
"provider": "ipfs",
"protocol": "http",
"host": "localhost",
"port": 5001,
"getUrl": "http://localhost:8080/ipfs/"
"upload": {
"provider": "ipfs",
"protocol": "http",
"host": "localhost",
"port": 5001,
"getUrl": "http://localhost:8080/ipfs/"
},
"dappConnection": [{"provider": "ipfs", "host": "localhost", "port": 5001, "getUrl": "http://localhost:8080/ipfs/"}]
}
};
let configFilePath = this._getFileOrOject(this.configDir, 'storage.json', 'storage');
let configFilePath = this._getFileOrOject(this.configDir, 'storage', 'storage');
this.storageConfig = this._mergeConfig(configFilePath, configObject, this.env);
};
Config.prototype.loadNameSystemConfigFile = function() {
// todo: spec out names for registration in the file itself for a dev chain
var configObject = {
"default": {
"available_providers": ["ens"],
"provider": "ens",
"enabled": true
}
};
let configFilePath = this._getFileOrOject(this.configDir, 'namesystem', 'namesystem');
this.namesystemConfig = this._mergeConfig(configFilePath, configObject, this.env);
};
Config.prototype.loadCommunicationConfigFile = function() {
var configObject = {
"default": {
"enabled": true,
"provider": "whisper",
"available_providers": ["whisper", "orbit"],
"available_providers": ["whisper"],
"connection": {
"host": "localhost", "port": 8546, "type": "ws"
}
}
};
let configFilePath = this._getFileOrOject(this.configDir, 'communication.json', 'communication');
let configFilePath = this._getFileOrOject(this.configDir, 'communication', 'communication');
this.communicationConfig = this._mergeConfig(configFilePath, configObject, this.env);
};
@ -220,7 +296,7 @@ Config.prototype.loadWebServerConfigFile = function() {
"enabled": true, "host": "localhost", "port": 8000
};
let configFilePath = this._getFileOrOject(this.configDir, 'webserver.json', 'webserver');
let configFilePath = this._getFileOrOject(this.configDir, 'webserver', 'webserver');
this.webServerConfig = this._mergeConfig(configFilePath, configObject, false);
};
@ -253,7 +329,7 @@ Config.prototype.loadPipelineConfigFile = function() {
Config.prototype.loadChainTrackerFile = function() {
if (!fs.existsSync(this.chainsFile)) {
this.logger.info(this.chainsFile + ' file not found, creating it...');
this.logger.info(this.chainsFile + ' ' + __('file not found, creating it...'));
fs.writeJSONSync(this.chainsFile, {});
}
@ -307,6 +383,7 @@ Config.prototype.loadFiles = function(files) {
return readFiles;
};
// NOTE: this doesn't work for internal modules
Config.prototype.loadPluginContractFiles = function() {
var self = this;

View File

@ -1,18 +1,14 @@
let Web3 = require('web3');
let Events = require('./events.js');
let Logger = require('./logger.js');
let Config = require('./config.js');
let ContractsManager = require('../contracts/contracts.js');
let DeployManager = require('../contracts/deploy_manager.js');
let CodeGenerator = require('../contracts/code_generator.js');
let ServicesMonitor = require('./services_monitor.js');
let Pipeline = require('../pipeline/pipeline.js');
let Watch = require('../pipeline/watch.js');
let LibraryManager = require('../versions/library_manager.js');
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;
@ -20,10 +16,14 @@ class Engine {
this.logLevel = options.logLevel;
this.events = options.events;
this.context = options.context;
this.useDashboard = options.useDashboard;
}
init(_options) {
let self = this;
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});
@ -31,11 +31,6 @@ class Engine {
this.config.loadConfigFiles({embarkConfig: this.embarkConfig, interceptLogs: this.interceptLogs});
this.plugins = this.config.plugins;
this.servicesMonitor = new ServicesMonitor({events: this.events, logger: this.logger});
this.servicesMonitor.addCheck('embarkVersion', function (cb) {
return cb({name: 'Embark ' + self.version, status: 'on'});
}, 0);
if (this.interceptLogs || this.interceptLogs === undefined) {
this.doInterceptLogs();
}
@ -46,77 +41,46 @@ class Engine {
let context = {};
context.console = console;
let normalizeInput = function(input) {
let args = Object.values(input);
if (args.length === 0) {
return "";
}
if (args.length === 1) {
if (Array.isArray(args[0])) { return args[0].join(','); }
return args[0] || "";
}
return ('[' + args.map((x) => {
if (x === null) { return "null"; }
if (x === undefined) { return "undefined"; }
if (Array.isArray(x)) { return x.join(','); }
return x;
}).toString() + ']');
};
context.console.log = function() {
self.logger.info(normalizeInput(arguments));
self.logger.info(utils.normalizeInput(arguments));
};
context.console.warn = function() {
self.logger.warn(normalizeInput(arguments));
self.logger.warn(utils.normalizeInput(arguments));
};
context.console.info = function() {
self.logger.info(normalizeInput(arguments));
self.logger.info(utils.normalizeInput(arguments));
};
context.console.debug = function() {
// TODO: ue JSON.stringify
self.logger.debug(normalizeInput(arguments));
self.logger.debug(utils.normalizeInput(arguments));
};
context.console.trace = function() {
self.logger.trace(normalizeInput(arguments));
self.logger.trace(utils.normalizeInput(arguments));
};
context.console.dir = function() {
self.logger.dir(normalizeInput(arguments));
self.logger.dir(utils.normalizeInput(arguments));
};
}
startMonitor() {
let self = this;
if (this.plugins) {
// --------
// TODO: this only works for services done on startup
// --------
let servicePlugins = this.plugins.getPluginsFor('serviceChecks');
servicePlugins.forEach(function (plugin) {
plugin.serviceChecks.forEach(function (pluginCheck) {
self.servicesMonitor.addCheck(pluginCheck.checkName, pluginCheck.checkFn, pluginCheck.time);
});
});
}
this.servicesMonitor.startMonitor();
}
registerModule(moduleName, options) {
this.plugins.loadInternalPlugin(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,
"ipfs": this.ipfsService,
"namingSystem": this.namingSystem,
"web3": this.web3Service,
"libraryManager": this.libraryManagerService,
"swarm": this.swarmService
"storage": this.storageService
};
let service = services[serviceName];
@ -131,9 +95,10 @@ class Engine {
}
pipelineService(_options) {
let self = this;
const self = this;
this.events.emit("status", "Building Assets");
let pipeline = new Pipeline({
const Pipeline = require('../pipeline/pipeline.js');
const pipeline = new Pipeline({
buildDir: this.config.buildDir,
contractsFiles: this.config.contractsFiles,
assetFiles: this.config.assetFiles,
@ -143,51 +108,93 @@ class Engine {
});
this.events.on('code-generator-ready', function () {
self.events.request('code', function (abi, contractsJSON) {
self.currentAbi = abi;
self.contractsJSON = contractsJSON;
pipeline.build(abi, contractsJSON, null, function() {
if (self.watch) {
self.watch.restart(); // Necessary because changing a file while it is writing can stop it from being watched
}
pipeline.build(abi, contractsJSON, null, () => {
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;
let generateCode = function (contractsManager) {
let codeGenerator = new CodeGenerator({
blockchainConfig: self.config.blockchainConfig,
contractsConfig: self.config.contractsConfig,
contractsManager: contractsManager,
plugins: self.plugins,
storageConfig: self.config.storageConfig,
communicationConfig: self.config.communicationConfig,
events: self.events
});
codeGenerator.listenToCommands();
codeGenerator.buildEmbarkJS(function() {
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');
});
};
this.events.on('contractsDeployed', generateCode);
this.events.on('blockchainDisabled', generateCode);
this.events.on('asset-changed', generateCode);
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;
this.registerModule('solidity', {
contractDirectories: self.config.contractDirectories
});
this.registerModule('vyper', {
contractDirectories: self.config.contractDirectories
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, useDashboard: this.useDashboard});
this.registerModule('vyper');
this.registerModule('profiler');
this.registerModule('fuzzer');
this.registerModule('deploytracker');
this.registerModule('specialconfigs');
this.registerModule('console_listener', {ipc: this.ipc});
const ContractsManager = require('../contracts/contracts.js');
this.contractsManager = new ContractsManager({
contractFiles: this.config.contractsFiles,
contractsConfig: this.config.contractsConfig,
@ -197,110 +204,90 @@ class Engine {
events: this.events
});
const DeployManager = require('../contracts/deploy_manager.js');
this.deployManager = new DeployManager({
web3: options.web3 || self.web3,
trackContracts: options.trackContracts,
blockchain: this.blockchain,
config: this.config,
logger: this.logger,
plugins: this.plugins,
events: this.events,
contractsManager: this.contractsManager,
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
});
this.events.on('file-event', function (fileType) {
// TODO: still need to redeploy contracts because the original contracts
// config is being corrupted
if (fileType === 'asset') {
self.events.emit('asset-changed', self.contractsManager);
}
// TODO: for now need to deploy on asset chanes as well
// because the contractsManager config is corrupted after a deploy
if (fileType === 'contract' || fileType === 'config') {
self.config.reloadConfig();
self.deployManager.deployContracts(function () {
});
}
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', {
addCheck: this.servicesMonitor.addCheck.bind(this.servicesMonitor)
});
this.registerModule('webserver');
}
ipfsService(_options) {
this.registerModule('ipfs', {
addCheck: this.servicesMonitor.addCheck.bind(this.servicesMonitor),
storageService(_options) {
this.registerModule('storage', {
storageConfig: this.config.storageConfig,
webServerConfig: this.config.webServerConfig,
blockchainConfig: this.config.blockchainConfig,
host: _options.host,
port: _options.port
});
}
swarmService(_options) {
this.registerModule('swarm', {
addCheck: this.servicesMonitor.addCheck.bind(this.servicesMonitor),
storageConfig: this.config.storageConfig,
bzz: _options.bzz
port: _options.port,
servicesMonitor: this.servicesMonitor,
events: this.events,
logger: this.logger,
context: this.context
});
}
web3Service(options) {
let self = this;
this.web3 = options.web3;
if (this.web3 === undefined) {
this.web3 = new Web3();
if (this.config.contractsConfig.deployment.type === "rpc") {
let web3Endpoint = 'http://' + this.config.contractsConfig.deployment.host + ':' + this.config.contractsConfig.deployment.port;
this.web3.setProvider(new this.web3.providers.HttpProvider(web3Endpoint));
} else {
throw new Error("contracts config error: unknown deployment type " + this.config.contractsConfig.deployment.type);
}
}
self.servicesMonitor.addCheck('Ethereum', function (cb) {
if (self.web3.currentProvider === undefined) {
return cb({name: "No Blockchain node found", status: 'off'});
}
self.web3.eth.getAccounts(function(err, _accounts) {
if (err) {
return cb({name: "No Blockchain node found", status: 'off'});
}
// TODO: web3_clientVersion method is currently not implemented in web3.js 1.0
self.web3._requestManager.send({method: 'web3_clientVersion', params: []}, (err, version) => {
if (err) {
return cb({name: "Ethereum node (version unknown)", status: 'on'});
}
if (version.indexOf("/") < 0) {
return cb({name: version, status: 'on'});
}
let nodeName = version.split("/")[0];
let versionNumber = version.split("/")[1].split("-")[0];
let name = nodeName + " " + versionNumber + " (Ethereum)";
return cb({name: name, status: 'on'});
});
});
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', {
addCheck: this.servicesMonitor.addCheck.bind(this.servicesMonitor),
communicationConfig: this.config.communicationConfig,
web3: this.web3
// 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

View File

@ -1,9 +1,9 @@
var EventEmitter = require('events');
function warnIfLegacy(eventName) {
const legacyEvents = ['abi-vanila', 'abi', 'abi-contracts-vanila', 'abi-vanila-deployment'];
const legacyEvents = [];
if (legacyEvents.indexOf(eventName) >= 0) {
console.info("this event is deprecated and will be removed in future versions: " + eventName);
console.info(__("this event is deprecated and will be removed in future versions %s", eventName));
}
}
@ -14,6 +14,7 @@ function log(eventType, eventName) {
//console.log(eventType, eventName);
}
EventEmitter.prototype._maxListeners = 300;
const _on = EventEmitter.prototype.on;
const _setHandler = EventEmitter.prototype.setHandler;
@ -39,10 +40,13 @@ EventEmitter.prototype.request = function() {
};
EventEmitter.prototype.setCommandHandler = function(requestName, cb) {
log("setting command handler for: ", requestName);
return this.on('request:' + requestName, function(_cb) {
log("setting command handler for: " + requestName);
let listener = function(_cb) {
cb.call(this, ...arguments);
});
};
// unlike events, commands can only have 1 handler
this.removeAllListeners('request:' + requestName);
return this.on('request:' + requestName, listener);
};
EventEmitter.prototype.setCommandHandlerOnce = function(requestName, cb) {

View File

@ -73,15 +73,25 @@ class File {
});
},
function downloadTheFile(next) {
let alreadyCalledBack = false;
function doCallback(err) {
if (alreadyCalledBack) {
return;
}
alreadyCalledBack = true;
next(err);
}
request(url)
.on('response', function (response) {
if (response.statusCode !== 200) {
next('Getting file returned code ' + response.statusCode);
doCallback('Getting file returned code ' + response.statusCode);
}
})
.on('error', next)
.on('error', doCallback)
.pipe(fs.createWriteStream(filename))
.on('finish', next);
.on('finish', () => {
doCallback();
});
},
function readFile(next) {
fs.readFile(filename, next);
@ -93,7 +103,7 @@ class File {
}
], (err, content) => {
if (err) {
console.error('Error while downloading the file', err);
console.error(__('Error while downloading the file'), err);
return callback('');
}
callback(content.toString());

View File

@ -11,14 +11,26 @@ function mkdirp() {
return fs.mkdirp.apply(fs.mkdirp, arguments);
}
function copy() {
return fs.copy.apply(fs.copy, arguments);
}
function copySync() {
return fs.copySync.apply(fs.copySync, arguments);
}
function move(){
return fs.move.apply(fs.move, arguments);
}
function appendFileSync() {
return fs.appendFileSync.apply(fs.writeFileSync, arguments);
}
function writeFile() {
return fs.writeFile.apply(fs.writeFileSync, arguments);
}
function writeFileSync() {
return fs.writeFileSync.apply(fs.writeFileSync, arguments);
}
@ -45,10 +57,18 @@ function writeJSONSync() {
return fs.writeJSONSync.apply(fs.writeJSONSync, arguments);
}
function writeJson() {
return fs.writeJson.apply(fs.writeJson, arguments);
}
function existsSync() {
return fs.existsSync.apply(fs.existsSync, arguments);
}
function access() {
return fs.access.apply(fs.access, arguments);
}
function removeSync() {
return fs.removeSync.apply(fs.removeSync, arguments);
}
@ -67,18 +87,23 @@ function createWriteStream() {
}
module.exports = {
mkdirpSync: mkdirpSync,
mkdirpSync,
mkdirp,
copySync: copySync,
copy,
copySync,
move,
readFile,
readFileSync: readFileSync,
appendFileSync: appendFileSync,
writeFileSync: writeFileSync,
readJSONSync: readJSONSync,
writeJSONSync: writeJSONSync,
existsSync: existsSync,
removeSync: removeSync,
embarkPath: embarkPath,
dappPath: dappPath,
readFileSync,
appendFileSync,
writeFile,
writeFileSync,
readJSONSync,
writeJson,
writeJSONSync,
access,
existsSync,
removeSync,
embarkPath,
dappPath,
createWriteStream
};

90
lib/core/ipc.js Normal file
View File

@ -0,0 +1,90 @@
let fs = require('./fs.js');
let ipc = require('node-ipc');
class IPC {
constructor(options) {
this.logger = options.logger;
this.socketPath = options.socketPath || fs.dappPath(".embark/embark.ipc");
this.ipcRole = options.ipcRole || "server";
ipc.config.silent = true;
this.connected = false;
}
connect(done) {
const self = this;
function connecting(_socket) {
let connectedBefore = false, alreadyDisconnected = false;
ipc.of['embark'].on('connect',function() {
connectedBefore = true;
if (!alreadyDisconnected) {
self.connected = true;
done();
}
});
ipc.of['embark'].on('disconnect',function() {
self.connected = false;
ipc.disconnect('embark');
// we only want to trigger the error callback the first time
if (!connectedBefore && !alreadyDisconnected) {
alreadyDisconnected = true;
done(new Error("no connection found"));
}
});
}
ipc.connectTo('embark', this.socketPath, connecting);
}
serve() {
ipc.serve(this.socketPath, () => {});
ipc.server.start();
this.logger.info(`pid ${process.pid} listening on ${this.socketPath}`);
}
on(action, done) {
const self = this;
ipc.server.on('message', function(data, socket) {
if (data.action !== action) {
return;
}
let reply = function(replyData) {
self.reply(socket, 'compile', replyData);
};
done(data.message, reply, socket);
});
}
reply(client, action, data) {
ipc.server.emit(client, 'message', {action: action, message: data});
}
once(action, cb) {
ipc.of['embark'].once('message', function(msg) {
if (msg.action !== action) {
return;
}
cb(msg.message);
});
}
request(action, data, cb) {
if (cb) {
this.once(action, cb);
}
ipc.of['embark'].emit('message', {action: action, message: data});
}
isClient() {
return this.ipcRole === 'client';
}
isServer() {
return this.ipcRole === 'server';
}
}
module.exports = IPC;

View File

@ -1,6 +1,7 @@
const fs = require('./fs.js');
const utils = require('../utils/utils.js');
const constants = require('../constants');
const _ = require('underscore');
// TODO: pass other params like blockchainConfig, contract files, etc..
var Plugin = function(options) {
@ -25,9 +26,13 @@ var Plugin = function(options) {
this.imports = [];
this.embarkjs_code = [];
this.embarkjs_init_code = {};
this.afterContractsDeployActions = [];
this.onDeployActions = [];
this.eventActions = {};
this.logger = options.logger;
this.events = options.events;
this.config = options.config;
this.env = options.env;
this.loaded = false;
this.currentContext = options.context;
this.acceptedContext = options.pluginConfig.context || [constants.contexts.any];
@ -55,8 +60,7 @@ Plugin.prototype.hasContext = function(context) {
Plugin.prototype.loadPlugin = function() {
if (!this.isContextValid()) {
console.log(this.acceptedContext);
this.logger.warn(`Plugin ${this.name} can only be loaded in the context of "${this.acceptedContext.join(', ')}"`);
this.logger.warn(__('Plugin {{name}} can only be loaded in the context of "{{contextes}}"', {name: this.name, contextes: this.acceptedContext.join(', ')}));
return false;
}
this.loaded = true;
@ -115,50 +119,53 @@ Plugin.prototype.interceptLogs = function(context) {
// TODO: add deploy provider
Plugin.prototype.registerClientWeb3Provider = function(cb) {
this.clientWeb3Providers.push(cb);
this.pluginTypes.push('clientWeb3Provider');
};
Plugin.prototype.registerBeforeDeploy = function(cb) {
this.beforeDeploy.push(cb);
this.pluginTypes.push('beforeDeploy');
this.addPluginType('clientWeb3Provider');
};
Plugin.prototype.registerContractsGeneration = function(cb) {
this.contractsGenerators.push(cb);
this.pluginTypes.push('contractGeneration');
this.addPluginType('contractGeneration');
};
Plugin.prototype.registerPipeline = function(matcthingFiles, cb) {
// TODO: generate error for more than one pipeline per plugin
this.pipeline.push({matcthingFiles: matcthingFiles, cb: cb});
this.pluginTypes.push('pipeline');
this.addPluginType('pipeline');
};
Plugin.prototype.addFileToPipeline = function(file, intendedPath, options) {
this.pipelineFiles.push({file: file, intendedPath: intendedPath, options: options});
this.pluginTypes.push('pipelineFiles');
this.addPluginType('pipelineFiles');
};
Plugin.prototype.addContractFile = function(file) {
if (this.isInternal) {
throw new Error("this API cannot work for internal modules. please use an event command instead: config:contractsFiles:add");
}
this.contractsFiles.push(file);
this.pluginTypes.push('contractFiles');
this.addPluginType('contractFiles');
};
Plugin.prototype.registerConsoleCommand = function(cb) {
this.console.push(cb);
this.pluginTypes.push('console');
this.addPluginType('console');
};
// TODO: this only works for services done on startup
Plugin.prototype.registerServiceCheck = function(checkName, checkFn, time) {
this.serviceChecks.push({checkName: checkName, checkFn: checkFn, time: time});
this.pluginTypes.push('serviceChecks');
this.addPluginType('serviceChecks');
};
Plugin.prototype.has = function(pluginType) {
return this.pluginTypes.indexOf(pluginType) >= 0;
};
Plugin.prototype.addPluginType = function(pluginType) {
this.pluginTypes.push(pluginType);
this.pluginTypes = _.uniq(this.pluginTypes);
};
Plugin.prototype.generateProvider = function(args) {
return this.clientWeb3Providers.map(function(cb) {
return cb.call(this, args);
@ -173,33 +180,41 @@ Plugin.prototype.generateContracts = function(args) {
Plugin.prototype.registerContractConfiguration = function(config) {
this.contractsConfigs.push(config);
this.pluginTypes.push('contractsConfig');
this.addPluginType('contractsConfig');
};
Plugin.prototype.registerCompiler = function(extension, cb) {
this.compilers.push({extension: extension, cb: cb});
this.pluginTypes.push('compilers');
this.addPluginType('compilers');
};
Plugin.prototype.registerUploadCommand = function(cmd, cb) {
this.uploadCmds.push({cmd: cmd, cb: cb});
this.pluginTypes.push('uploadCmds');
this.addPluginType('uploadCmds');
};
Plugin.prototype.addCodeToEmbarkJS = function(code) {
this.embarkjs_code.push(code);
this.pluginTypes.push('embarkjsCode');
this.addPluginType('embarkjsCode');
};
Plugin.prototype.addProviderInit = function(providerType, code, initCondition) {
this.embarkjs_init_code[providerType] = this.embarkjs_init_code[providerType] || [];
this.embarkjs_init_code[providerType].push([code, initCondition]);
this.pluginTypes.push('initCode');
this.addPluginType('initCode');
};
Plugin.prototype.registerImportFile = function(importName, importLocation) {
this.imports.push([importName, importLocation]);
this.pluginTypes.push('imports');
this.addPluginType('imports');
};
Plugin.prototype.registerActionForEvent = function(eventName, cb) {
if (!this.eventActions[eventName]) {
this.eventActions[eventName] = [];
}
this.eventActions[eventName].push(cb);
this.addPluginType('eventActions');
};
Plugin.prototype.runFilePipeline = function() {

View File

@ -1,5 +1,7 @@
const async = require('async');
var Plugin = require('./plugin.js');
var utils = require('../utils/utils.js');
var fs = require('../core/fs.js');
var Plugins = function(options) {
this.pluginList = options.plugins || [];
@ -10,6 +12,7 @@ var Plugins = function(options) {
this.events = options.events;
this.config = options.config;
this.context = options.context;
this.env = options.env;
};
Plugins.prototype.loadPlugins = function() {
@ -51,20 +54,21 @@ Plugins.prototype.createPlugin = function(pluginName, pluginConfig) {
};
Plugins.prototype.loadInternalPlugin = function(pluginName, pluginConfig) {
var pluginPath = utils.joinPath('../modules/', pluginName, 'index.js');
var pluginPath = fs.embarkPath('lib/modules/' + pluginName);
var plugin = require(pluginPath);
var pluginWrapper = new Plugin({
name: pluginName,
pluginModule: plugin,
pluginConfig: pluginConfig,
pluginConfig: pluginConfig || {},
logger: this.logger,
pluginPath: pluginPath,
interceptLogs: this.interceptLogs,
events: this.events,
config: this.config,
isInternal: true,
context: this.context
context: this.context,
env: this.env
});
pluginWrapper.loadInternalPlugin();
this.plugins.push(pluginWrapper);
@ -96,18 +100,60 @@ Plugins.prototype.getPluginsFor = function(pluginType) {
});
};
Plugins.prototype.getPluginsProperty = function(pluginType, property) {
Plugins.prototype.getPluginsProperty = function(pluginType, property, sub_property) {
let matchingPlugins = this.plugins.filter(function(plugin) {
return plugin.has(pluginType);
});
// Sort internal plugins first
matchingPlugins.sort((a, b) => {
if (a.isInternal) {
return -1;
}
if (b.isInternal) {
return 1;
}
return 0;
});
let matchingProperties = matchingPlugins.map((plugin) => {
if (sub_property) {
return plugin[property][sub_property];
}
return plugin[property];
});
// Remove empty properties
matchingProperties = matchingProperties.filter((property) => property);
//return flattened list
if (matchingProperties.length === 0) return [];
return matchingProperties.reduce((a,b) => { return a.concat(b); });
return matchingProperties.reduce((a,b) => { return a.concat(b); }) || [];
};
Plugins.prototype.runActionsForEvent = function(eventName, args, cb) {
if (typeof (args) === 'function') {
cb = args;
}
let actionPlugins = this.getPluginsProperty('eventActions', 'eventActions', eventName);
if (actionPlugins.length === 0) {
return cb();
}
async.eachLimit(actionPlugins, 1, function(plugin, nextEach) {
if (typeof (args) === 'function') {
plugin.call(plugin, nextEach);
} else {
//plugin.call(plugin, ...args, nextEach);
plugin.call(plugin, args, nextEach);
}
}, cb);
};
Plugins.prototype.emitAndRunActionsForEvent = function(eventName, args, cb) {
this.events.emit(eventName);
return this.runActionsForEvent(eventName, args, cb);
};
module.exports = Plugins;

125
lib/core/proxy.js Normal file
View File

@ -0,0 +1,125 @@
const httpProxy = require('http-proxy');
const http = require('http');
const constants = require('../constants.json');
let commList = {};
let transactions = {};
let receipts = {};
const parseRequest = function(reqBody){
let jsonO;
try {
jsonO = JSON.parse(reqBody);
} catch(e){
return; // Request is not a json. Do nothing
}
if(jsonO.method === "eth_sendTransaction"){
commList[jsonO.id] = {
type: 'contract-log',
address: jsonO.params[0].to,
data: jsonO.params[0].data
};
} else if(jsonO.method === "eth_getTransactionReceipt"){
if(transactions[jsonO.params[0]]){
transactions[jsonO.params[0]].receiptId = jsonO.id;
receipts[jsonO.id] = transactions[jsonO.params[0]].commListId;
}
}
};
const parseResponse = function(ipc, resBody){
let jsonO;
try {
jsonO = JSON.parse(resBody);
} catch(e) {
return; // Response is not a json. Do nothing
}
if(commList[jsonO.id]){
commList[jsonO.id].transactionHash = jsonO.result;
transactions[jsonO.result] = {commListId: jsonO.id};
} else if(receipts[jsonO.id] && jsonO.result && jsonO.result.blockNumber){
commList[receipts[jsonO.id]].blockNumber = jsonO.result.blockNumber;
commList[receipts[jsonO.id]].gasUsed = jsonO.result.gasUsed;
commList[receipts[jsonO.id]].status = jsonO.result.status;
if(ipc.connected && !ipc.connecting){
ipc.request('log', commList[receipts[jsonO.id]]);
} else {
ipc.connecting = true;
ipc.connect(() => {
ipc.connecting = false;
});
}
delete transactions[commList[receipts[jsonO.id]].transactionHash];
delete receipts[jsonO.id];
delete commList[jsonO.id];
}
};
exports.serve = function(ipc, host, port, ws){
let proxy = httpProxy.createProxyServer({
target: {
host,
port: port + constants.blockchain.servicePortOnProxy
},
ws: ws
});
proxy.on('error', function () {
console.log(__("Error forwarding requests to blockchain/simulator"));
process.exit();
});
proxy.on('proxyRes', (proxyRes) => {
let resBody = [];
proxyRes.on('data', (b) => resBody.push(b));
proxyRes.on('end', function () {
resBody = Buffer.concat(resBody).toString();
if(resBody){
parseResponse(ipc, resBody);
}
});
});
let server = http.createServer((req, res) => {
let reqBody = [];
req.on('data', (b) => { reqBody.push(b); })
.on('end', () => {
reqBody = Buffer.concat(reqBody).toString();
if(reqBody){
parseRequest(reqBody);
}
});
if(!ws){
proxy.web(req, res);
}
});
if(ws){
const WsParser = require('simples/lib/parsers/ws'); // npm install simples
server.on('upgrade', function (req, socket, head) {
proxy.ws(req, socket, head);
});
proxy.on('open', (proxySocket) => {
proxySocket.on('data', (data) => {
parseResponse(ipc, data.toString().substr(data.indexOf("{")));
});
});
proxy.on('proxyReqWs', (proxyReq, req, socket) => {
var parser = new WsParser(0, false);
socket.pipe(parser);
parser.on('frame', function (frame) {
parseRequest(frame.data);
});
});
}
server.listen(port);
};

View File

@ -2,12 +2,18 @@ let async = require('../utils/async_extend.js');
class ServicesMonitor {
constructor(options) {
const self = this;
this.events = options.events;
this.logger = options.logger;
this.plugins = options.plugins;
this.checkList = {};
this.checkTimers = {};
this.checkState = {};
this.working = false;
self.events.setCommandHandler("services:register", (checkName, checkFn, time, initialStatus) => {
self.addCheck(checkName, checkFn, time, initialStatus);
});
}
}
@ -44,9 +50,9 @@ ServicesMonitor.prototype.initCheck = function (checkName) {
});
};
ServicesMonitor.prototype.addCheck = function (checkName, checkFn, time) {
ServicesMonitor.prototype.addCheck = function (checkName, checkFn, time, initialState) {
this.logger.trace('add check: ' + checkName);
this.checkList[checkName] = {fn: checkFn, interval: time || 5000};
this.checkList[checkName] = {fn: checkFn, interval: time || 5000, status: initialState};
if (this.working) {
this.initCheck(checkName);
@ -65,12 +71,17 @@ ServicesMonitor.prototype.startMonitor = function () {
this.working = true;
this.logger.trace('startMonitor');
let servicePlugins = this.plugins.getPluginsProperty('serviceChecks', 'serviceChecks');
servicePlugins.forEach(function (pluginCheck) {
self.addCheck(pluginCheck.checkName, pluginCheck.checkFn, pluginCheck.time);
});
async.eachObject(this.checkList, function (checkName, check, callback) {
self.initCheck(checkName);
callback();
}, function (err) {
if (err) {
self.logger.error("error running service check");
self.logger.error(__("error running service check"));
self.logger.error(err.message);
}
});

View File

@ -1,5 +1,4 @@
let utils = require('../utils/utils.js');
let RunCode = require('../core/runCode.js');
class Console {
constructor(options) {
@ -10,25 +9,26 @@ class Console {
}
runCode(code) {
RunCode.doEval(code);
this.events.request('runcode:eval', code);
}
processEmbarkCmd (cmd) {
if (cmd === 'help') {
if (cmd === 'help' || cmd === __('help')) {
let helpText = [
'Welcome to Embark ' + this.version,
__('Welcome to Embark') + ' ' + this.version,
'',
'possible commands are:',
'versions - display versions in use for libraries and tools like web3 and solc',
__('possible commands are:'),
'versions - ' + __('display versions in use for libraries and tools like web3 and solc'),
// TODO: only if the blockchain is actually active!
// will need to pass te current embark state here
'web3 - instantiated web3.js object configured to the current environment',
'quit - to immediatly exit (alias: exit)',
'ipfs - ' + __('instantiated js-ipfs object configured to the current environment (available if ipfs is enabled)'),
'web3 - ' + __('instantiated web3.js object configured to the current environment'),
'quit - ' + __('to immediatly exit (alias: exit)'),
'',
'The web3 object and the interfaces for the deployed contracts and their methods are also available'
__('The web3 object and the interfaces for the deployed contracts and their methods are also available')
];
return helpText.join('\n');
} else if (['quit', 'exit', 'sair', 'sortir'].indexOf(cmd) >= 0) {
} else if (['quit', 'exit', 'sair', 'sortir', __('quit')].indexOf(cmd) >= 0) {
utils.exit();
}
return false;
@ -38,7 +38,7 @@ class Console {
var pluginCmds = this.plugins.getPluginsProperty('console', 'console');
for (let pluginCmd of pluginCmds) {
let pluginOutput = pluginCmd.call(this, cmd, {});
if (pluginOutput !== false && pluginOutput !== 'false') return callback(pluginOutput);
if (pluginOutput !== false && pluginOutput !== 'false' && pluginOutput !== undefined) return callback(pluginOutput);
}
let output = this.processEmbarkCmd(cmd);
@ -47,12 +47,13 @@ class Console {
}
try {
let result = RunCode.doEval(cmd);
return callback(result);
this.events.request('runcode:eval', cmd, (err, result) => {
callback(result);
});
}
catch (e) {
if (e.message.indexOf('not defined') > 0) {
return callback(("error: " + e.message).red + ("\nType " + "help".bold + " to see the list of available commands").cyan);
return callback(("error: " + e.message).red + ("\n" + __("Type") + " " + "help".bold + " " + __("to see the list of available commands")).cyan);
} else {
return callback(e.message);
}

View File

@ -1,4 +1,5 @@
let async = require('async');
let windowSize = require('window-size');
let Monitor = require('./monitor.js');
let Console = require('./console.js');
@ -11,6 +12,16 @@ class Dashboard {
this.version = options.version;
this.env = options.env;
this.contractsConfig = options.contractsConfig;
this.events.on('firstDeploymentDone', this.checkWindowSize.bind(this));
this.events.on('outputDone', this.checkWindowSize.bind(this));
}
checkWindowSize() {
let size = windowSize.get();
if (size.height < 40 || size.width < 118) {
this.logger.warn(__("tip: you can resize the terminal or disable the dashboard with") + " embark run --nodashboard".bold.underline);
}
}
start(done) {
@ -38,7 +49,7 @@ class Dashboard {
self.events.setCommandHandler("console:command", monitor.executeCmd.bind(monitor));
self.logger.info('========================'.bold.green);
self.logger.info(('Welcome to Embark ' + self.version).yellow.bold);
self.logger.info((__('Welcome to Embark') + ' ' + self.version).yellow.bold);
self.logger.info('========================'.bold.green);
// TODO: do this after monitor is rendered

View File

@ -1,4 +1,4 @@
let blessed = require("blessed");
let blessed = require("neo-blessed");
let CommandHistory = require('./command_history.js');
class Dashboard {
@ -67,7 +67,7 @@ class Dashboard {
setContracts(contracts) {
let data = [];
data.push(["Contract Name", "Address", "Status"]);
data.push([__("Contract Name"), __("Address"), __("Status")]);
contracts.forEach(function (row) {
data.push(row);
@ -84,7 +84,7 @@ class Dashboard {
layoutLog() {
this.log = blessed.box({
label: "Logs",
label: __("Logs"),
padding: 1,
width: "100%",
height: "55%",
@ -123,7 +123,7 @@ class Dashboard {
layoutModules() {
this.modules = blessed.box({
label: "Contracts",
label: __("Contracts"),
tags: true,
padding: 1,
width: "75%",
@ -166,7 +166,7 @@ class Dashboard {
layoutAssets() {
this.assets = blessed.box({
label: "Asset Pipeline",
label: __("Asset Pipeline"),
tags: true,
padding: 1,
width: "50%",
@ -217,7 +217,7 @@ class Dashboard {
this.status = blessed.box({
parent: this.wrapper,
label: "Environment",
label: __("Environment"),
tags: true,
padding: {
left: 1
@ -238,7 +238,7 @@ class Dashboard {
this.operations = blessed.box({
parent: this.wrapper,
label: "Status",
label: __("Status"),
tags: true,
padding: {
left: 1
@ -259,7 +259,7 @@ class Dashboard {
this.progress = blessed.box({
parent: this.wrapper,
label: "Available Services",
label: __("Available Services"),
tags: true,
padding: this.minimal ? {
left: 1
@ -270,6 +270,12 @@ class Dashboard {
border: {
type: "line"
},
scrollable: true,
alwaysScroll: true,
scrollbar: {
ch: " ",
inverse: true
},
style: {
fg: -1,
border: {
@ -283,7 +289,7 @@ class Dashboard {
layoutCmd() {
this.consoleBox = blessed.box({
label: 'Console',
label: __('Console'),
tags: true,
padding: 0,
width: '100%',
@ -324,6 +330,7 @@ class Dashboard {
let self = this;
this.input.key(["C-c"], function () {
self.events.emit('exit');
process.exit(0);
});

42
lib/i18n/i18n.js Normal file
View File

@ -0,0 +1,42 @@
const i18n = require('i18n');
const osLocale = require('os-locale');
const path = require('path');
const supported_languages = ['en', 'pt', 'fr'];
i18n.configure({
locales: supported_languages,
register: global,
updateFiles: false,
directory: path.join(__dirname, 'locales')
});
function isSupported(locale) {
return (supported_languages.indexOf(locale.substr(0, 2)) >= 0);
}
function setLocale(locale) {
i18n.setLocale(locale.substr(0, 2));
}
function setDefaultLocale() {
osLocale().then(setLocale).catch();
}
function setOrDetectLocale(locale) {
if (locale && !isSupported(locale)) {
console.log("===== locale " + locale + " not supported =====");
}
if (locale) {
return i18n.setLocale(locale.substr(0, 2));
}
setDefaultLocale();
}
setDefaultLocale();
module.exports = {
i18n: i18n,
setOrDetectLocale: setOrDetectLocale
};

170
lib/i18n/locales/en.json Normal file
View File

@ -0,0 +1,170 @@
{
"new_application": "new_application",
"Contract Name": "Contract Name",
"New Application": "New Application",
"create a barebones project meant only for contract development": "create a barebones project meant only for contract development",
"create a working dapp with a SimpleStorage contract": "create a working dapp with a SimpleStorage contract",
"filename to output logs (default: none)": "filename to output logs (default: none)",
"level of logging to display": "level of logging to display",
"deploy and build dapp at ": "deploy and build dapp at ",
"port to run the dev webserver (default: %s)": "port to run the dev webserver (default: %s)",
"host to run the dev webserver (default: %s)": "host to run the dev webserver (default: %s)",
"disable the development webserver": "disable the development webserver",
"simple mode, disables the dashboard": "simple mode, disables the dashboard",
"no colors in case it's needed for compatbility purposes": "no colors in case it's needed for compatbility purposes",
"filename to output logs (default: %s)": "filename to output logs (default: %s)",
"run dapp (default: %s)": "run dapp (default: %s)",
"Use a specific ethereum client or simulator (supported: %s)": "Use a specific ethereum client or simulator (supported: %s)",
"run blockchain server (default: %s)": "run blockchain server (default: %s)",
"run a fast ethereum rpc simulator": "run a fast ethereum rpc simulator",
"use testrpc as the rpc simulator [%s]": "use testrpc as the rpc simulator [%s]",
"port to run the rpc simulator (default: %s)": "port to run the rpc simulator (default: %s)",
"host to run the rpc simulator (default: %s)": "host to run the rpc simulator (default: %s)",
"number of accounts (default: %s)": "number of accounts (default: %s)",
"Amount of ether to assign each test account (default: %s)": "Amount of ether to assign each test account (default: %s)",
"custom gas limit (default: %s)": "custom gas limit (default: %s)",
"run tests": "run tests",
"resets embarks state on this dapp including clearing cache": "resets embarks state on this dapp including clearing cache",
"generates documentation based on the smart contracts configured": "generates documentation based on the smart contracts configured",
"Upload your dapp to a decentralized storage": "Upload your dapp to a decentralized storage",
"output the version number": "output the version number",
"Logs": "Logs",
"Environment": "Environment",
"Status": "Status",
"Available Services": "Available Services",
"Contracts": "Contracts",
"Console": "Console",
"Welcome to Embark": "Welcome to Embark",
"dashboard start": "dashboard start",
"loaded plugins": "loaded plugins",
"loading solc compiler": "loading solc compiler",
"compiling solidity contracts": "compiling solidity contracts",
"%s doesn't have a compatible contract compiler. Maybe a plugin exists for it.": "%s doesn't have a compatible contract compiler. Maybe a plugin exists for it.",
"assuming %s to be an interface": "assuming %s to be an interface",
"{{className}}: couldn't find instanceOf contract {{parentContractName}}": "{{className}}: couldn't find instanceOf contract {{parentContractName}}",
"did you mean \"%s\"?": "did you mean \"%s\"?",
"%s has no code associated": "%s has no code associated",
"deploying contracts": "deploying contracts",
" already deployed at ": " already deployed at ",
"Pending": "Pending",
"Interface or set to not deploy": "Interface or set to not deploy",
"Deployed": "Deployed",
"Address": "Address",
"running beforeDeploy plugin %s .": "running beforeDeploy plugin %s .",
"deploying": "deploying",
"with": "with",
"gas": "gas",
"error deploying": "error deploying",
"due to error": "due to error",
"error deploying contracts": "error deploying contracts",
"finished deploying contracts": "finished deploying contracts",
"error running afterDeploy: ": "error running afterDeploy: ",
"ready to watch file changes": "ready to watch file changes",
"Starting Server": "Starting Server",
"webserver available at": "webserver available at",
"Webserver": "Webserver",
"versions": "versions",
"possible commands are:": "possible commands are:",
"display versions in use for libraries and tools like web3 and solc": "display versions in use for libraries and tools like web3 and solc",
"instantiated web3.js object configured to the current environment": "instantiated web3.js object configured to the current environment",
"to immediatly exit (alias: exit)": "to immediatly exit (alias: exit)",
"The web3 object and the interfaces for the deployed contracts and their methods are also available": "The web3 object and the interfaces for the deployed contracts and their methods are also available",
"versions in use": "versions in use",
"language to use (default: en)": "language to use (default: en)",
"executing": "executing",
"writing file": "writing file",
"errors found while generating": "errors found while generating",
"Looking for documentation? You can find it at": "Looking for documentation? You can find it at",
"Ready": "Ready",
"Graph will not include undeployed contracts": "Graph will not include undeployed contracts",
"Graph will not include functions": "Graph will not include functions",
"Graph will not include events": "Graph will not include events",
"Embark Blockchain Using: %s": "Embark Blockchain Using: %s",
"running: %s": "running: %s",
"Initializing Embark Template....": "Initializing Embark Template....",
"Init complete": "Init complete",
"App ready at ": "App ready at ",
"already initialized": "already initialized",
"deployed at": "deployed at",
"executing onDeploy commands": "executing onDeploy commands",
"executing: ": "executing: ",
"no config file found at %s using default config": "no config file found at %s using default config",
"Development blockchain has changed to use the --dev option.": "Development blockchain has changed to use the --dev option.",
"You can reset your workspace to fix the problem with": "You can reset your workspace to fix the problem with",
"Otherwise, you can change your data directory in blockchain.json (datadir)": "Otherwise, you can change your data directory in blockchain.json (datadir)",
"tip: you can resize the terminal or disable the dashboard with": "tip: you can resize the terminal or disable the dashboard with",
"help": "help",
"quit": "quit",
"Error Compiling/Building contracts: ": "Error Compiling/Building contracts: ",
"file not found, creating it...": "file not found, creating it...",
"{{className}} has code associated to it but it's configured as an instanceOf {{parentContractName}}": "{{className}} has code associated to it but it's configured as an instanceOf {{parentContractName}}",
"downloading {{packageName}} {{version}}....": "downloading {{packageName}} {{version}}....",
"Swarm node is offline...": "Swarm node is offline...",
"Swarm node detected...": "Swarm node detected...",
"Cannot find file %s Please ensure you are running this command inside the Dapp folder": "Cannot find file %s Please ensure you are running this command inside the Dapp folder",
"compiling Vyper contracts": "compiling Vyper contracts",
"Vyper exited with error code ": "Vyper exited with error code ",
"attempted to deploy %s without specifying parameters": "attempted to deploy %s without specifying parameters",
"Ethereum node (version unknown)": "Ethereum node (version unknown)",
"No Blockchain node found": "No Blockchain node found",
"Couldn't connect to an Ethereum node are you sure it's on?": "Couldn't connect to an Ethereum node are you sure it's on?",
"make sure you have an Ethereum node or simulator running. e.g '%s'": "make sure you have an Ethereum node or simulator running. e.g '%s'",
"Embark is building, please wait...": "Embark is building, please wait...",
"finished building DApp and deploying to": "finished building DApp and deploying to",
"Type": "Type",
"to see the list of available commands": "to see the list of available commands",
"instantiated js-ipfs object configured to the current environment (available if ipfs is enabled)": "instantiated js-ipfs object configured to the current environment (available if ipfs is enabled)",
"reset done!": "reset done!",
"finished building": "finished building",
"finished deploying": "finished deploying",
"adding %s to ipfs": "adding %s to ipfs",
"DApp available at": "DApp available at",
"successfully uploaded to ipfs": "successfully uploaded to ipfs",
"Starting Blockchain node in another process": "Starting Blockchain node in another process",
"Blockchain node is ready": "Blockchain node is ready",
"terminating due to error": "terminating due to error",
"Unable to start the blockchain process. Is Geth installed?": "Unable to start the blockchain process. Is Geth installed?",
"Error while downloading the file": "Error while downloading the file",
"Error while loading the content of ": "Error while loading the content of ",
"Installing packages...": "Installing packages...",
"Next steps:": "Next steps:",
"open another console in the same directory and run": "open another console in the same directory and run",
"deploying to swarm!": "deploying to swarm!",
"adding %s to swarm": "adding %s to swarm",
"successfully uploaded to swarm": "successfully uploaded to swarm",
"Cannot upload: {{plaform}} node is not running on {{protocol}}://{{host}}{{port}}.": "Cannot upload: {{platform}} node is not running on {{protocol}}://{{host}}{{port}}.",
"no contracts found": "no contracts found",
"IPFS node is offline": "IPFS node is offline",
"IPFS node detected": "IPFS node detected",
"Webserver is offline": "Webserver is offline",
"DApp path length is too long: \"": "DApp path length is too long: \"",
"This is known to cause issues with some applications, please consider reducing your DApp path's length to 66 characters or less.": "This is known to cause issues with some applications, please consider reducing your DApp path's length to 66 characters or less.",
"For more info go to http://embark.status.im": "For more info go to http://embark.status.im",
"Cannot upload: {{platform}} node is not running on {{protocol}}://{{host}}{{port}}.": "Cannot upload: {{platform}} node is not running on {{protocol}}://{{host}}{{port}}.",
"DApp path length is too long: ": "DApp path length is too long: ",
"WARNING! DApp path length is too long: ": "WARNING! DApp path length is too long: ",
"This is known to cause issues with starting geth, please consider reducing your DApp path's length to 66 characters or less.": "This is known to cause issues with starting geth, please consider reducing your DApp path's length to 66 characters or less.",
"%s is not installed on your machine": "%s is not installed on your machine",
"You can install it by visiting: %s": "You can install it by visiting: %s",
"Starting ipfs process": "Starting ipfs process",
"ipfs process started": "ipfs process started",
"Starting swarm process": "Starting swarm process",
"unknown command": "unknown command",
"Simulator not found; Please install it with \"%s\"": "Simulator not found; Please install it with \"%s\"",
"IMPORTANT: if you using a NodeJS version older than 6.9.1 then you need to install an older version of testrpc \"%s\"": "IMPORTANT: if you using a NodeJS version older than 6.9.1 then you need to install an older version of testrpc \"%s\"",
"swarm process started": "swarm process started",
"Storage process for swarm ended before the end of this process. Code: 12": "Storage process for swarm ended before the end of this process. Code: 12",
"Storage process for swarm ended before the end of this process. Code: 1": "Storage process for swarm ended before the end of this process. Code: 1",
"Storage process for ipfs ended before the end of this process. Code: 12": "Storage process for ipfs ended before the end of this process. Code: 12",
"Blockchain process ended before the end of this process. Code: %s": "Blockchain process ended before the end of this process. Code: %s",
"Cannot start {{platform}} node on {{protocol}}://{{host}}{{port}}.": "Cannot start {{platform}} node on {{protocol}}://{{host}}{{port}}.",
"Cannot upload: {{platform}} node is not running on {{url}}.": "Cannot upload: {{platform}} node is not running on {{url}}.",
"Cannot start {{platform}} node on {{url}}.": "Cannot start {{platform}} node on {{url}}.",
"Storage process for swarm ended before the end of this process. Code: 0": "Storage process for swarm ended before the end of this process. Code: 0",
"error uploading to swarm": "error uploading to swarm",
"IPFS node detected": "IPFS node detected",
"Ethereum node detected": "Ethereum node detected",
"Starting swarm process": "Starting swarm process",
"Deployment Done": "Deployment Done",
"Name your app (default is %s):": "Name your app (default is %s):"
}

188
lib/i18n/locales/es.json Normal file
View File

@ -0,0 +1,188 @@
{
"New Application": "Nueva Aplicación",
"Contract Name": "Nombre del Contrato",
"Address": "Dirección",
"Status": "Estado",
"Embark Blockchain Using: %s": "Embark Blockchain esta usando el comando: %s",
"running: %s": "ejecutando: %s",
"already initialized": "ya esta inicializado",
"create a barebones project meant only for contract development": "crear un proyecto vacio destinado solo para el desarrollo de contratos",
"loading solc compiler": "cargando el compilador solc",
"Welcome to Embark": "Bienvenido a Embark",
"possible commands are:": "los comandos posibles son:",
"display versions in use for libraries and tools like web3 and solc": "mostrar versiones en uso para bibliotecas y herramientas como web3 y solc",
"instantiated web3.js object configured to the current environment": "objeto web3.js instanciado configurado para el entorno actual",
"to immediatly exit (alias: exit)": "para salir inmediatamente (alias: exit)",
"The web3 object and the interfaces for the deployed contracts and their methods are also available": "El objeto web3 y las interfaces para los contratos publicados y sus métodos también están disponibles",
"create a working dapp with a SimpleStorage contract": "Crear una dapp funcional con un contrato SimpleStorage",
"filename to output logs (default: none)": "fichero/archivo para salida de los logs (predefinido: none)",
"level of logging to display": "nivel de log para mostrar",
"deploy and build dapp at ": "Publica los contractos y construye la aplicación en ",
"port to run the dev webserver (default: %s)": "puerto para correr el servidor web para desarrollo (default: %s)",
"host to run the dev webserver (default: %s)": "host para correr el servidor web para desarrollo (default: %s)",
"disable the development webserver": "desactiva el servidor web para desarrollo",
"simple mode, disables the dashboard": "modo simple, desactiva el dashboard",
"no colors in case it's needed for compatbility purposes": "Sin colores, en caso de ser necessario para compatibilidad con la terminal",
"filename to output logs (default: %s)": "fichero/archivo para los logs (predefinido: %s)",
"run dapp (default: %s)": "ejecutar la dapp (applicación decentralizada) (predefinido: %s)",
"Use a specific ethereum client or simulator (supported: %s)": "Usa un cliente o un simulador de ethereum específico (soportado: %s)",
"run blockchain server (default: %s)": "ejecuta un nodo de blockchain (predefinido: %s)",
"run a fast ethereum rpc simulator": "ejecuta un simulador RPC de ethereum",
"use testrpc as the rpc simulator [%s]": "usa testrpc como simulator de rpc [%s]",
"port to run the rpc simulator (default: %s)": "puerto para ejecutar un simulador de rpc (predefinido: %s)",
"host to run the rpc simulator (default: %s)": "host para ejecutar un servidor de rpc (predefinido: %s)",
"number of accounts (default: %s)": "número de cuentas (predefinido: %s)",
"Amount of ether to assign each test account (default: %s)": "Cantidad de éter para asignar a cada cuenta de prueba (predefinido: %s)",
"custom gas limit (default: %s)": "límite de gas (predefinido: %s)",
"run tests": "ejecutar las pruebas",
"resets embarks state on this dapp including clearing cache": "restablece el estado de Embark en esta dapp, incluida la eliminación de la memoria caché",
"generates documentation based on the smart contracts configured": "genera documentación basada en los contratos inteligentes configurados",
"Upload your dapp to a decentralized storage": "Suba su dapp a un almacenamiento descentralizado",
"output the version number": "mostrar el número de versión",
"Logs": "Logs",
"Environment": "Ambiente",
"Available Services": "Servicios Disponibles",
"Contracts": "Contratos",
"Console": "Consola",
"dashboard start": "inicia el panel de control",
"loaded plugins": "plugins cargados",
"compiling solidity contracts": "Compilando contratos Solidity",
"%s doesn't have a compatible contract compiler. Maybe a plugin exists for it.": "%s no tiene un compilador de contrato compatible. Tal vez existe un plugin para ello.",
"assuming %s to be an interface": "suponiendo que %s sea una interfaz",
"{{className}}: couldn't find instanceOf contract {{parentContractName}}": "{{className}}: no se pudo encontrar la instancia (instanceOf) del contrato {{parentContractName}}",
"did you mean \"%s\"?": "querías decir \"%s\"?",
"%s has no code associated": "%s no tiene código asociado",
"deploying contracts": "publicando contratos",
"running beforeDeploy plugin %s .": "ejecutanto plugin beforeDeploy %s .",
"deploying": "publicando",
"with": "con",
"gas": "gas",
"Pending": "Pendiente",
"Interface or set to not deploy": "Interfaz o configurado para no publicar",
"error deploying": "error publicando",
"due to error": "debido a error",
"error deploying contracts": "error publicando contratos",
"finished deploying contracts": "publicación de contratos concluida",
"error running afterDeploy: ": "error ejecutando afterDeploy: ",
"ready to watch file changes": "listo para monitorear alteraciones en ficheros/archivos",
"Starting Server": "iniciando el servidor",
"webserver available at": "servidor web disponible en",
"Webserver": "Servidor Web",
" already deployed at ": " ya publicado en ",
"Deployed": "Publicado",
"Name must be only letters, spaces, or dashes": "El nombre debe ser solo letras, espacios o guiones",
"Name your app (default is %s)": "Nombre su aplicación (el valor predeterminado es %s)",
"Invalid name": "Nombre inválido",
"unknown command": "comando desconocido",
"did you mean": "querías decir",
"warning: running default config on a non-development environment": "aviso: ejecutando la configuración predefinida en un ambiente de no-desarrollo",
"could not find {{geth_bin}} command; is {{client_name}} installed or in the PATH?": "no se pudo encontrar el comando {{geth_bin}}; ¿Está {{client_name}} instalado o en la RUTA?",
"no accounts found": "no se encontraron cuentas",
"initializing genesis block": "inicializando bloque genesis",
"rpcCorsDomain set to *": "rpcCorsDomain definido como *",
"make sure you know what you are doing": "asegúrate de saber lo que estás haciendo",
"warning: cors is not set": "aviso: cors no está definido",
"wsOrigins set to *": "wsOrigins definido como *",
"warning: wsOrigins is not set": "aviso: wsOrigins no está definido",
"reset done!": "reset listo!",
"%s is not installed on your machine": "%s no está instalado en su máquina",
"You can install it by running: %s": "Puede instalarlo ejecutando: %s",
"Initializing Embark Template....": "Inicializando la plantilla de Embark....",
"Installing packages...": "Instalando paquetes...",
"Init complete": "Init completo",
"App ready at ": "Aplicación lista en ",
"Next steps:": "Próximos pasos:",
"open another console in the same directory and run": "abrir otra consola en el mismo directorio y ejecutar",
"For more info go to http://embark.status.im": "Para más información ve a http://embark.status.im",
"%s : instanceOf is set to itself": "%s : instanceOf se establece a sí mismo",
"{{className}} has code associated to it but it's configured as an instanceOf {{parentContractName}}": "{{className}} tiene un código asociado pero está configurado como una instancia de {{parentContractName}}",
"Error Compiling/Building contracts: ": "Error Compilando/Construyendo los contractos: ",
"Error: ": "Error: ",
"there are two or more contracts that depend on each other in a cyclic manner": "hay dos o más contratos que dependen el uno del otro de forma cíclica",
"Embark couldn't determine which one to deploy first": "Embark no pudo determinar cual publicar primero",
"{{inputName}} has not been defined for {{className}} constructor": "{{inputName}} no ha sido definido para el constructor de {{className}}",
"error deploying %s": "error publicando %s",
"executing onDeploy commands": "ejecutando comandos onDeploy",
"error executing onDeploy for ": "error ejecutando onDeploy para ",
" does not exist": " no existe",
"error running onDeploy: ": "error ejecutando onDeploy: ",
" exists but has been set to not deploy": " existe, pero se ha configurado para no implementar",
"couldn't find a valid address for %s has it been deployed?": "no pudo encontrar una dirección válida para %s ¿ha sido publicado?",
"executing: ": "ejecutando: ",
"the transaction was rejected; this usually happens due to a throw or a require": "la transacción fue rechazada; esto generalmente sucede debido a un throw o a un require",
"no account found at index": "no se encontró una cuenta en index",
" check the config": " verifique la configuración",
"Both \"from\" and \"fromIndex\" are defined for contract": "Ambos \"from\" y \"fromIndex\" están definidos para el contracto",
"Using \"from\" as deployer account.": "Usando \"from\" como cuenta de implementación.",
"{{linkReference}} is too long": "{{linkReference}} es muy largo",
"{{contractName}} needs {{libraryName}} but an address was not found, did you deploy it or configured an address?": "{{contractName}} necesita {{libraryName}} pero no se encontró una dirección, ¿la implementó o configuró una dirección?",
"attempted to deploy %s without specifying parameters": "intentado publicar %s sin especificar parámetros",
"deployed at": "publicado en",
"no contracts found": "no hay contractos encontrados",
"Blockchain component is disabled in the config": "El componente Blockchain está deshabilitado en la configuración",
"Couldn't connect to an Ethereum node are you sure it's on?": "No se pudo conectar a un nodo Ethereum, ¿está seguro de que está activado?",
"make sure you have an Ethereum node or simulator running. e.g '%s'": "asegurese de tener un nodo o simulador Ethereum en funcionamiento. e.g '%s'",
"executing": "ejecutando",
"the transaction was rejected; this usually happens due to a throw or a require, it can also happen due to an invalid operation": "la transacción fue rechazada; esto generalmente ocurre debido a un throw o un require, también puede ocurrir debido a una operación no válida",
"Cannot find file %s Please ensure you are running this command inside the Dapp folder": "No se puede encontrar el archivo %s Asegúrese de que está ejecutando este comando dentro de la carpeta de la DApp",
"no config file found at %s using default config": "archivo de configuración no encontrado en %s utilizado la configuración predefinida",
"HTTP contract file not found": "archivo de contracto HTTP no encontrado",
"contract file not found": "archivo de contracto no encontrado",
"file not found, creating it...": "archivo no encontrado, creándolo ...",
"No Blockchain node found": "No se encontró ningún nodo de Blockchain",
"Ethereum node (version unknown)": "Nodo Ethereum (versión desconocida)",
"this event is deprecated and will be removed in future versions %s": "este evento está en desuso y se eliminará en futuras versiones %s",
"Error while downloading the file": "Error descargando el archivo",
"Plugin {{name}} can only be loaded in the context of \"{{contextes}}\"": "El complemento {{name}} solo se puede cargar en el contexto de \"{{contextes}} \"",
"error running service check": "error al ejecutar la comprobación del servicio",
"help": "ayuda",
"quit": "salir",
"Type": "Tipo",
"to see the list of available commands": "para ver la lista de comandos disponibles",
"Asset Pipeline": "Asset Pipeline",
"Ethereum node detected": "Nodo Ethereum detectado",
"Deployment Done": "Publicación completada",
"Looking for documentation? You can find it at": "¿Buscando documentación? puede encontrarla en",
"Ready": "Listo",
"tip: you can resize the terminal or disable the dashboard with": "consejo: puede redimensionar la terminal o desactivar el tablero con",
"finished building": "construcción completada",
"Done": "Hecho",
"Cannot upload: {{platform}} node is not running on {{url}}.": "No se puede cargar: el nodo {{platform}} no se está ejecutando en {{url}}.",
"try \"{{ipfs}}\" or \"{{swarm}}\"": "intente \"{{ipfs}}\" o \"{{swarm}}\"",
"finished deploying": "publicación completada",
"finished building DApp and deploying to": "construcción de la DApp completada y publicada en",
"IPFS node detected": "Nodo de IPFS detectado",
"IPFS node is offline": "Nodo de IPFS no está en línea",
"not found or not in the path. Guessing %s for path": "no encontrado o no se encuentra en la ruta. Estimando %s para la ruta",
"adding %s to ipfs": "añadiendo %s a ipfs",
"DApp available at": "DApp disponible en",
"error uploading to ipfs": "error cargando a ipfs",
"successfully uploaded to ipfs": "cargado con éxito a ipfs",
"Error while loading the content of ": "Error al cargar el contenido de ",
"error compiling for unknown reasons": "error compilando por razones desconocidas",
"error compiling. There are sources available but no code could be compiled, likely due to fatal errors in the solidity code": "error compilando. Hay fuentes disponibles pero no se pudo compilar ningún código, probablemente debido a errores fatales en el código solidity",
"Swarm node detected...": "nodo Swarm detectado...",
"Swarm node is offline...": "el nodo de Swarm no se encuentra en línea...",
"deploying to swarm!": "publicando en swarm!",
"adding %s to swarm": "añadiendo %s a swarm",
"error uploading to swarm": "error cargando a swarm",
"successfully uploaded to swarm": "cargado exitosamente a swarm",
"Vyper exited with error code ": "Vyper salió con el código de error ",
"Execution returned no result": "La ejecución no devolvió resultados",
"compiling Vyper contracts": "compilando contractos Vyper",
"Webserver is offline": "el servidor web no se encuentra en línea",
"stopping webserver": "deteniendo el servidor web",
"a webserver is already running at": "un servidor web ya se está ejecutando en",
"no webserver is currently running": "ningún servidor web se está ejecutando actualmente",
"couldn't find file": "el archivo no pudo ser encontrado",
"errors found while generating": "errores encontrados al generar",
"writing file": "escribiendo archivo",
"Simulator not found; Please install it with \"%s\"": "Simulador no encontrado; Por favor instalarlo con \"%s\"",
"Tried to load testrpc but an error occurred. This is a problem with testrpc": "Intenté cargar testrpc pero ocurrió un error. Este es un problema con testrpc",
"IMPORTANT: if you using a NodeJS version older than 6.9.1 then you need to install an older version of testrpc \"%s\". Alternatively install node 6.9.1 and the testrpc 3.0": "IMPORTANTE: si usa una versión de NodeJS anterior a la 6.9.1, entonces necesita instalar una versión anterior de testrpc \"%s\". Alternativamente, instale el Node 6.9.1 y el testrpc 3.0",
"terminating due to error": "terminando debido a un error",
"There a a space in the version of {{versionKey}}. We corrected it for you ({{correction})": "Hay un espacio en la versión de {{versionKey}}. Lo corregimos por ti ({{correction})",
"versions": "versiones",
"versions in use": "versiones en uso",
"downloading {{packageName}} {{version}}....": "descargando {{packageName}} {{version}}...."
}

188
lib/i18n/locales/fr.json Normal file
View File

@ -0,0 +1,188 @@
{
"New Application": "Nouvelle Application",
"Contract Name": "Contrat",
"Address": "Addresse",
"Status": "Etat",
"Embark Blockchain Using: %s": "Embark Blockchain utilise: %s",
"running: %s": "en cours d'exécution: %s",
"already initialized": "Déjà initialisé",
"create a barebones project meant only for contract development": "créer un projet destiné uniquement au développement de contrats",
"loading solc compiler": "chargement du compilateur solc",
"Welcome to Embark": "Bienvenue sur Embark",
"possible commands are:": "les commandes possibles sont:",
"display versions in use for libraries and tools like web3 and solc": "liste des versions utilisées pour les bibliothèques et les outils comme web3 et solc",
"instantiated web3.js object configured to the current environment": "objet web3.js instancié configuré pour l'environnement actuel",
"to immediatly exit (alias: exit)": "quitter immédiatement (alias: exit)",
"The web3 object and the interfaces for the deployed contracts and their methods are also available": "L'objet web3 et les interfaces avec les contrats déployés et leurs méthodes sont également disponibles",
"create a working dapp with a SimpleStorage contract": "créer une dapp fonctionnelle avec un SimpleStorage contrat",
"filename to output logs (default: none)": "nom de fichier pour les journaux de sortie (par défaut: aucun)",
"level of logging to display": "niveau de journalisation à afficher",
"deploy and build dapp at ": "Publiez et créez la dapp dans ",
"port to run the dev webserver (default: %s)": "port pour exécuter le serveur de développement Web (par défaut:% s)",
"host to run the dev webserver (default: %s)": "hôte pour exécuter le serveur de développement Web (par défaut:% s)",
"disable the development webserver": "désactiver le serveur de développement Web",
"simple mode, disables the dashboard": "mode simple, désactive le tableau de bord",
"no colors in case it's needed for compatbility purposes": "pas de couleur au cas où cela serait nécessaire à des fins de compatibilité",
"filename to output logs (default: %s)": "nom de fichier pour les journaux de sortie (par défaut: %s)",
"run dapp (default: %s)": "lancer la dapp (par défaut: %s)",
"Use a specific ethereum client or simulator (supported: %s)": "Utiliser un client ou simulateur ethereum spécifique (supporté:% s)",
"run blockchain server (default: %s)": "exécuter le serveur blockchain (par défaut: %s)",
"run a fast ethereum rpc simulator": "exécuter un simulateur ethereum RPC rapide",
"use testrpc as the rpc simulator [%s]": "utiliser testrpc comme simulateur RPC [%s]",
"port to run the rpc simulator (default: %s)": "port pour exécuter le simulateur RPC (par défaut: %s)",
"host to run the rpc simulator (default: %s)": "hôte pour exécuter le simulateur RPC (par défaut: %s)",
"number of accounts (default: %s)": "nombre de comptes (par défaut: %s)",
"Amount of ether to assign each test account (default: %s)": "Quantité d'éther à affecter à chaque compte de test (par défaut: %s)",
"custom gas limit (default: %s)": "limite de gaz personnalisée (par défaut: %s)",
"run tests": "Exécuter les tests",
"resets embarks state on this dapp including clearing cache": "réinitialise l'état d'Embark dans cette dapp, y compris le cache",
"generates documentation based on the smart contracts configured": "génère la documentation basée sur les contrats configurés",
"Upload your dapp to a decentralized storage": "Téléchargez votre dapp dans un stockage décentralisé",
"output the version number": "afficher le numéro de version",
"Logs": "Journaux",
"Environment": "Environnement",
"Available Services": "Services disponibles",
"Contracts": "Contrats",
"Console": "Console",
"dashboard start": "démarrer le tableau de bord",
"loaded plugins": "plugins chargés",
"compiling solidity contracts": "compilation des contrats Solidity",
"%s doesn't have a compatible contract compiler. Maybe a plugin exists for it.": "%s n'a pas de compilateur de contrat compatible, il existe peut-être un plugin.",
"assuming %s to be an interface": "En supposant que %s soit une interface",
"{{className}}: couldn't find instanceOf contract {{parentContractName}}": "{{className}}: impossible de trouver un instance du contrat {{parentContractName}}",
"did you mean \"%s\"?": "Voulez-vous dire \"%s\"?",
"%s has no code associated": "%s n'a aucun code associé",
"deploying contracts": "déploiement des contrats",
"running beforeDeploy plugin %s .": "En cours d'exécution du plugin beforeDeploy %s .",
"deploying": "En cours de déploiement",
"with": "avec",
"gas": "gaz",
"Pending": "en attente",
"Interface or set to not deploy": "Interface ou configuré pour ne pas déployer",
"error deploying": "erreur de déploiement",
"due to error": "en raison d'une erreur",
"error deploying contracts": "erreur lors du déploiement de contrats",
"finished deploying contracts": "fini de déployer les contrats",
"error running afterDeploy: ": "erreur d'exécution AfterDeploy: ",
"ready to watch file changes": "Prêt à monitorer les changements de fichiers",
"Starting Server": "Démarrage du serveur",
"webserver available at": "serveur Web disponible à",
"Webserver": "Serveur Web",
" already deployed at ": " déjà déployé à ",
"Deployed": "Déployé",
"Name must be only letters, spaces, or dashes": "Le nom ne doit être que des lettres, des espaces ou des tirets",
"Name your app (default is %s)": "Nommez votre application (par défaut: %s)",
"Invalid name": "Nom incorrect",
"unknown command": "commande inconnue",
"did you mean": "vouliez-vous dire",
"warning: running default config on a non-development environment": "avertissement: exécution de la configuration par défaut sur un environnement de non-développement",
"could not find {{geth_bin}} command; is {{client_name}} installed or in the PATH?": "Impossible de trouver la commande {{geth_bin}}, est-ce que {{client_name}} est installé ou dans le PATH?",
"no accounts found": "Aucun compte trouvé",
"initializing genesis block": "initialisation du bloc de genèse",
"rpcCorsDomain set to *": "rpcCorsDomain défini sur *",
"make sure you know what you are doing": "Assurez-vous de savoir ce que vous faites",
"warning: cors is not set": "avertissement: cors n'est pas défini",
"wsOrigins set to *": "wsOrigins défini sur *",
"warning: wsOrigins is not set": "avertissement: wsOrigins n'est pas défini",
"reset done!": "réinitialisation terminée!",
"%s is not installed on your machine": "%s n'est pas installé sur votre machine",
"You can install it by running: %s": "Vous pouvez l'installer en exécutant: %s",
"Initializing Embark Template....": "Initialisation du modèle Embark....",
"Installing packages...": "Installation des paquets...",
"Init complete": "Initialisation terminée",
"App ready at ": "Application prête à ",
"Next steps:": "Prochaines étapes:",
"open another console in the same directory and run": "ouvrir une autre console dans le même répertoire et exécutez",
"For more info go to http://embark.status.im": "Pour plus d'informations, rendez-vous sur http://embark.status.im",
"%s : instanceOf is set to itself": "%s : instanceOf est défini sur lui-même",
"{{className}} has code associated to it but it's configured as an instanceOf {{parentContractName}}": "{{className}} a du code associé, mais il est configuré comme une instanceOf {{parentContractName}}",
"Error Compiling/Building contracts: ": "Erreur lors de la compilation/construction des contrats: ",
"Error: ": "Erreur: ",
"there are two or more contracts that depend on each other in a cyclic manner": "il y a deux ou plusieurs contrats qui dépendent l'un de l'autre de manière cyclique",
"Embark couldn't determine which one to deploy first": "Embark n'a pas pu déterminer lequel déployer en premier",
"{{inputName}} has not been defined for {{className}} constructor": "{{inputName}} n'a pas été défini pour le constructeur {{className}}",
"error deploying %s": "erreur de déploiement de %s",
"executing onDeploy commands": "Exécution des commandes onDeploy",
"error executing onDeploy for ": "erreur d'exécution onDeploy pour ",
" does not exist": " n'existe pas",
"error running onDeploy: ": "erreur d'exécution onDeploy: ",
" exists but has been set to not deploy": " existe, mais a été configuré pour ne pas se déployer",
"couldn't find a valid address for %s has it been deployed?": "impossible de trouver une adresse valide pour %s. A-t-il été déployé?",
"executing: ": "en cours d'exécution: ",
"the transaction was rejected; this usually happens due to a throw or a require": "la transaction a été rejetée, cela arrive généralement en raison d'un 'throw' ou d'un 'require'",
"no account found at index": "aucun compte trouvé à l'index",
" check the config": " vérifier la configuration",
"Both \"from\" and \"fromIndex\" are defined for contract": "\"from\" et \"fromIndex\" sont définis pour le contrat",
"Using \"from\" as deployer account.": "Embark utilise \"from\" comme compte de déploiement.",
"{{linkReference}} is too long": "{{linkReference}} est trop long",
"{{contractName}} needs {{libraryName}} but an address was not found, did you deploy it or configured an address?": "{{contractName}} a besoin de {{libraryName}} mais une adresse n'a pas été trouvée, l'avez-vous déployée ou avez-vous configuré une adresse?",
"attempted to deploy %s without specifying parameters": "a tenté de déployer %s sans spécifier de paramètres",
"deployed at": "déployé à",
"no contracts found": "aucun contrat trouvé",
"Blockchain component is disabled in the config": "Le composant Blockchain est désactivé dans la configuration",
"Couldn't connect to an Ethereum node are you sure it's on?": "Impossible de se connecter à un noeud Ethereum. Êtes-vous sûr qu'il est activé?",
"make sure you have an Ethereum node or simulator running. e.g '%s'": "Assurez-vous que vous avez un noeud Ethereum ou un simulateur en cours d'exécution, par exemple '%s'",
"executing": "en cours d'exécution",
"the transaction was rejected; this usually happens due to a throw or a require, it can also happen due to an invalid operation": "la transaction a été rejetée, cela arrive généralement en raison d'un 'throw' ou d'un 'require', cela peut aussi arriver en raison d'une opération invalide",
"Cannot find file %s Please ensure you are running this command inside the Dapp folder": "Impossible de trouver le fichier %s Veuillez vous assurer que vous exécutez cette commande dans le dossier Dapp",
"no config file found at %s using default config": "Aucun fichier de configuration trouvé sur %s en utilisant la configuration par défaut",
"HTTP contract file not found": "Fichier de contrat HTTP introuvable",
"contract file not found": "Fichier de contrat non trouvé",
"file not found, creating it...": "fichier introuvable, en cours de création...",
"No Blockchain node found": "Pas de noeud Blockchain trouvé",
"Ethereum node (version unknown)": "Nœud Ethereum (version inconnue)",
"this event is deprecated and will be removed in future versions %s": "cet événement est obsolète et sera supprimé dans les futures versions %s",
"Error while downloading the file": "Erreur lors du téléchargement du fichier",
"Plugin {{name}} can only be loaded in the context of \"{{contextes}}\"": "Le plugin {{name}} ne peut être chargé que dans le contexte de \"{{contextes}}\"",
"error running service check": "erreur lors de l'exécution du service de contrôle",
"help": "aide",
"quit": "quitter",
"Type": "Type",
"to see the list of available commands": "pour voir la liste des commandes disponibles",
"Asset Pipeline": "Asset Pipeline",
"Ethereum node detected": "Nœud Ethereum détecté",
"Deployment Done": "Déploiement effectué",
"Looking for documentation? You can find it at": "Vous cherchez de la documentation? Vous pouvez la trouver à",
"Ready": "Prêt",
"tip: you can resize the terminal or disable the dashboard with": "astuce: vous pouvez redimensionner le terminal ou désactiver le tableau de bord avec",
"finished building": "construction terminé",
"Done": "Terminé",
"Cannot upload: {{platform}} node is not running on {{url}}.": "Impossible de télécharger: le noeud {{platform}} ne fonctionne pas sur {{url}}.",
"try \"{{ipfs}}\" or \"{{swarm}}\"": "essayez \"{{ipfs}}\" ou \"{{swarm}}\"",
"finished deploying": "déploiement terminé",
"finished building DApp and deploying to": "construction de la DApp terminé et déploiement à",
"IPFS node detected": "Noeud IPFS détecté",
"IPFS node is offline": "Le noeud IPFS est hors ligne",
"not found or not in the path. Guessing %s for path": "introuvable ou pas dans le PATH. Embark devine %s pour le chemin.",
"adding %s to ipfs": "en cours d'ajout %s à ipfs",
"DApp available at": "DApp disponible à",
"error uploading to ipfs": "Erreur de téléchargement sur ipfs",
"successfully uploaded to ipfs": "Téléchargé avec succès sur ipfs",
"Error while loading the content of ": "Erreur lors du chargement du contenu de ",
"error compiling for unknown reasons": "erreur de compilation pour des raisons inconnues",
"error compiling. There are sources available but no code could be compiled, likely due to fatal errors in the solidity code": "erreur de compilation.Il existe des sources disponibles, mais aucun code n'a pu être compilé, probablement en raison d'erreurs fatales dans le code de solidity",
"Swarm node detected...": "Noeud Swarm détecté...",
"Swarm node is offline...": "Le nœud Swarm est hors ligne ...",
"deploying to swarm!": "déploiement de swarm!",
"adding %s to swarm": "Ajout de %s à swarm",
"error uploading to swarm": "Erreur de téléchargement dans le swarm",
"successfully uploaded to swarm": "téléchargé avec succès dans le swarm",
"Vyper exited with error code ": "Vyper a quitté avec le code d'erreur ",
"Execution returned no result": "L'exécution n'a renvoyé aucun résultat",
"compiling Vyper contracts": "compilation des contrats Vyper",
"Webserver is offline": "Le serveur Web est hors ligne",
"stopping webserver": "Serveur web en cours d'arrêt",
"a webserver is already running at": "un serveur web est déjà en cours d'exécution à",
"no webserver is currently running": "Aucun serveur Web n'est en cours d'exécution",
"couldn't find file": "Impossible de trouver le fichier",
"errors found while generating": "erreurs trouvées lors de la génération",
"writing file": "écriture du fichier",
"Simulator not found; Please install it with \"%s\"": "Simulateur introuvable, veuillez l'installer avec \"%s\"",
"Tried to load testrpc but an error occurred. This is a problem with testrpc": "Nous avons essayé de charger testrpc, mais une erreur s'est produite. Ceci est un problème avec testrpc",
"IMPORTANT: if you using a NodeJS version older than 6.9.1 then you need to install an older version of testrpc \"%s\". Alternatively install node 6.9.1 and the testrpc 3.0": "IMPORTANT: si vous utilisez une version NodeJS antérieure à la version 6.9.1, vous devez installer une ancienne version de testrpc \"%s\". Ou bien installer Node 6.9.1 et le testrpc 3.0",
"terminating due to error": "en cours de terminaison en raison d'une erreur",
"There a a space in the version of {{versionKey}}. We corrected it for you ({{correction})": "Il y a un espace dans la version de {{versionKey}}. Nous l'avons corrigé pour vous ({{correction})",
"versions": "versions",
"versions in use": "versions en cours d'utilisation",
"downloading {{packageName}} {{version}}....": "téléchargement {{packageName}} {{version}}...."
}

188
lib/i18n/locales/pt.json Normal file
View File

@ -0,0 +1,188 @@
{
"New Application": "Nova Aplicacao",
"Contract Name": "Contracto",
"Address": "Endereço",
"Status": "Estado",
"Embark Blockchain Using: %s": "Embark Blockchain esta usando o commando: %s",
"running: %s": "executando: %s",
"already initialized": "ja esta inicializado",
"create a barebones project meant only for contract development": "criar um projeto vazio destinado apenas ao desenvolvimento de contratos",
"loading solc compiler": "carregando o compilador solc",
"Welcome to Embark": "Bem-vindo ao Embark",
"possible commands are:": "comandos possíveis são:",
"display versions in use for libraries and tools like web3 and solc": "lista versões em uso para bibliotecas e ferramentas como web3 e solc",
"instantiated web3.js object configured to the current environment": "objeto web3.js instanciado configurado para o ambiente atual",
"to immediatly exit (alias: exit)": "para sair imediatamente (alias: exit)",
"The web3 object and the interfaces for the deployed contracts and their methods are also available": "O objeto web3 e as interfaces para os contratos implantados e seus métodos também estão disponíveis",
"create a working dapp with a SimpleStorage contract": "Cria uma dapp funcional com o contrato SimpleStorage",
"filename to output logs (default: none)": "ficheiro/arquivo para saída dos logs (predefinido: none)",
"level of logging to display": "nivel do log",
"deploy and build dapp at ": "Publica os contractos e constroi a applicacao em ",
"port to run the dev webserver (default: %s)": "porta para correr o servidor web para desenvolvimento (default: %s)",
"host to run the dev webserver (default: %s)": "host para correr o servidor web para desenvolvimento (default: %s)",
"disable the development webserver": "disativa o servidor web para desenvolvimento",
"simple mode, disables the dashboard": "modo simples, disativa o dashboard",
"no colors in case it's needed for compatbility purposes": "sem cores, em caso seja necessario para compabitilidade com a terminal",
"filename to output logs (default: %s)": "ficheiro/arquivo para os logs (predefinido: %s)",
"run dapp (default: %s)": "executa a dapp (applicacao decentralizada) (predefinido: %s)",
"Use a specific ethereum client or simulator (supported: %s)": "Usa um cliente ou simulador de ethereum específico (supportado: %s)",
"run blockchain server (default: %s)": "executa un node de blockchain (predefinido: %s)",
"run a fast ethereum rpc simulator": "executa um simulador RPC de ethereum",
"use testrpc as the rpc simulator [%s]": "usa testrpc como simulator de rpc [%s]",
"port to run the rpc simulator (default: %s)": "porta para executar simulador de rpc (predefinido: %s)",
"host to run the rpc simulator (default: %s)": "host para executar servidor de rpc (predefinido: %s)",
"number of accounts (default: %s)": "numero de contas (predefinido: %s)",
"Amount of ether to assign each test account (default: %s)": "Quantidade de éter para atribuir cada conta de teste (predefinido: %s)",
"custom gas limit (default: %s)": "limite de gás (predefinido: %s)",
"run tests": "executar os testes",
"resets embarks state on this dapp including clearing cache": "recomenca o estado do Embark nesta appliacao, incluindo a cache",
"generates documentation based on the smart contracts configured": "gera documentação baseada nos contratos configurados",
"Upload your dapp to a decentralized storage": "Carrega a appliacao para armazenamento descentralizado",
"output the version number": "produz a versão actual",
"Logs": "Logs",
"Environment": "Ambiente",
"Available Services": "Serviços Disponíveis",
"Contracts": "Contratos",
"Console": "Consola",
"dashboard start": "inicia o painel de controle",
"loaded plugins": "plugins carregados",
"compiling solidity contracts": "Compilando contratos Solidity",
"%s doesn't have a compatible contract compiler. Maybe a plugin exists for it.": "%s não tem um compilador de contrato compatível. Talvez exista um plugin para isso.",
"assuming %s to be an interface": "assumindo que %s é uma interface",
"{{className}}: couldn't find instanceOf contract {{parentContractName}}": "{{className}}: não foi possível encontrar instancia de (instanceOf) do contrato {{parentContractName}}",
"did you mean \"%s\"?": "você quis dizer \"%s\"?",
"%s has no code associated": "%s não tem código associado",
"deploying contracts": "publicando contratos",
"running beforeDeploy plugin %s .": "executando plugin beforeDeploy %s .",
"deploying": "publicando",
"with": "com",
"gas": "gas",
"Pending": "Pendente",
"Interface or set to not deploy": "Interface ou configurado para não ser publicado",
"error deploying": "erro de publicação",
"due to error": "devido a erro",
"error deploying contracts": "erro publicando contratos",
"finished deploying contracts": "publicação de contratos concluida",
"error running afterDeploy: ": "erro executado afterDeploy: ",
"ready to watch file changes": "pronto para monitorar alterações em ficheiros/arquivos",
"Starting Server": "iniciando o servidor",
"webserver available at": "servidor web disponivel em",
"Webserver": "Servidor Web",
" already deployed at ": " já publicado em ",
"Deployed": "Publicado",
"Name must be only letters, spaces, or dashes": "O nome deve ser apenas letras, espaços ou traços",
"Name your app (default is %s)": "Nome da aplicacao (predefinido is %s)",
"Invalid name": "Nome inválido",
"unknown command": "comando desconhecido",
"did you mean": "você quis dizer",
"warning: running default config on a non-development environment": "aviso: executando a configuração padrão em um ambiente de não desenvolvimento",
"could not find {{geth_bin}} command; is {{client_name}} installed or in the PATH?": "não foi possível encontrar o comando {{geth_bin}}; o {{client_name}} instalado ou no PATH?",
"no accounts found": "nenhuma conta encontrada",
"initializing genesis block": "inicializando o bloco de gênese",
"rpcCorsDomain set to *": "rpcCorsDomain definido como *",
"make sure you know what you are doing": "certifique-se de saber o que está fazendo",
"warning: cors is not set": "aviso: cors não está definido",
"wsOrigins set to *": "wsOrigins definido como *",
"warning: wsOrigins is not set": "aviso: wsOrigins não está definido",
"reset done!": "reset feito!",
"%s is not installed on your machine": "%s não está instalado na sua máquina",
"You can install it by running: %s": "Você pode instalá-lo executando: %s",
"Initializing Embark Template....": "Inicializando Embark Template....",
"Installing packages...": "Instalando pacotes...",
"Init complete": "Init complete",
"App ready at ": "App ready at ",
"Next steps:": "Next steps:",
"open another console in the same directory and run": "open another console in the same directory and run",
"For more info go to http://embark.status.im": "For more info go to http://embark.status.im",
"%s : instanceOf is set to itself": "%s : instanceOf is set to itself",
"{{className}} has code associated to it but it's configured as an instanceOf {{parentContractName}}": "{{className}} has code associated to it but it's configured as an instanceOf {{parentContractName}}",
"Error Compiling/Building contracts: ": "Error Compiling/Building contracts: ",
"Error: ": "Error: ",
"there are two or more contracts that depend on each other in a cyclic manner": "there are two or more contracts that depend on each other in a cyclic manner",
"Embark couldn't determine which one to deploy first": "Embark couldn't determine which one to deploy first",
"{{inputName}} has not been defined for {{className}} constructor": "{{inputName}} has not been defined for {{className}} constructor",
"error deploying %s": "error deploying %s",
"executing onDeploy commands": "executing onDeploy commands",
"error executing onDeploy for ": "error executing onDeploy for ",
" does not exist": " does not exist",
"error running onDeploy: ": "error running onDeploy: ",
" exists but has been set to not deploy": " exists but has been set to not deploy",
"couldn't find a valid address for %s has it been deployed?": "couldn't find a valid address for %s has it been deployed?",
"executing: ": "executing: ",
"the transaction was rejected; this usually happens due to a throw or a require": "the transaction was rejected; this usually happens due to a throw or a require",
"no account found at index": "no account found at index",
" check the config": " check the config",
"Both \"from\" and \"fromIndex\" are defined for contract": "Both \"from\" and \"fromIndex\" are defined for contract",
"Using \"from\" as deployer account.": "Using \"from\" as deployer account.",
"{{linkReference}} is too long": "{{linkReference}} is too long",
"{{contractName}} needs {{libraryName}} but an address was not found, did you deploy it or configured an address?": "{{contractName}} needs {{libraryName}} but an address was not found, did you deploy it or configured an address?",
"attempted to deploy %s without specifying parameters": "attempted to deploy %s without specifying parameters",
"deployed at": "deployed at",
"no contracts found": "no contracts found",
"Blockchain component is disabled in the config": "Blockchain component is disabled in the config",
"Couldn't connect to an Ethereum node are you sure it's on?": "Couldn't connect to an Ethereum node are you sure it's on?",
"make sure you have an Ethereum node or simulator running. e.g '%s'": "make sure you have an Ethereum node or simulator running. e.g '%s'",
"executing": "executing",
"the transaction was rejected; this usually happens due to a throw or a require, it can also happen due to an invalid operation": "the transaction was rejected; this usually happens due to a throw or a require, it can also happen due to an invalid operation",
"Cannot find file %s Please ensure you are running this command inside the Dapp folder": "Cannot find file %s Please ensure you are running this command inside the Dapp folder",
"no config file found at %s using default config": "no config file found at %s using default config",
"HTTP contract file not found": "HTTP contract file not found",
"contract file not found": "contract file not found",
"file not found, creating it...": "file not found, creating it...",
"No Blockchain node found": "No Blockchain node found",
"Ethereum node (version unknown)": "Ethereum node (version unknown)",
"this event is deprecated and will be removed in future versions %s": "this event is deprecated and will be removed in future versions %s",
"Error while downloading the file": "Error while downloading the file",
"Plugin {{name}} can only be loaded in the context of \"{{contextes}}\"": "Plugin {{name}} can only be loaded in the context of \"{{contextes}}\"",
"error running service check": "error running service check",
"help": "ajuda",
"quit": "sair",
"Type": "Type",
"to see the list of available commands": "to see the list of available commands",
"Asset Pipeline": "Asset Pipeline",
"Ethereum node detected": "Ethereum node detected",
"Deployment Done": "Deployment Done",
"Looking for documentation? You can find it at": "A procura de Documentacao? pode encontra-la em",
"Ready": "Ready",
"tip: you can resize the terminal or disable the dashboard with": "tip: you can resize the terminal or disable the dashboard with",
"finished building": "finished building",
"Done": "Done",
"Cannot upload: {{platform}} node is not running on {{url}}.": "Cannot upload: {{platform}} node is not running on {{url}}.",
"try \"{{ipfs}}\" or \"{{swarm}}\"": "try \"{{ipfs}}\" or \"{{swarm}}\"",
"finished deploying": "finished deploying",
"finished building DApp and deploying to": "finished building DApp and deploying to",
"IPFS node detected": "IPFS node detected",
"IPFS node is offline": "IPFS node is offline",
"not found or not in the path. Guessing %s for path": "not found or not in the path. Guessing %s for path",
"adding %s to ipfs": "adding %s to ipfs",
"DApp available at": "DApp available at",
"error uploading to ipfs": "error uploading to ipfs",
"successfully uploaded to ipfs": "successfully uploaded to ipfs",
"Error while loading the content of ": "Error while loading the content of ",
"error compiling for unknown reasons": "error compiling for unknown reasons",
"error compiling. There are sources available but no code could be compiled, likely due to fatal errors in the solidity code": "error compiling. There are sources available but no code could be compiled, likely due to fatal errors in the solidity code",
"Swarm node detected...": "Swarm node detected...",
"Swarm node is offline...": "Swarm node is offline...",
"deploying to swarm!": "deploying to swarm!",
"adding %s to swarm": "adding %s to swarm",
"error uploading to swarm": "error uploading to swarm",
"successfully uploaded to swarm": "successfully uploaded to swarm",
"Vyper exited with error code ": "Vyper exited with error code ",
"Execution returned no result": "Execution returned no result",
"compiling Vyper contracts": "compiling Vyper contracts",
"Webserver is offline": "Webserver is offline",
"stopping webserver": "stopping webserver",
"a webserver is already running at": "a webserver is already running at",
"no webserver is currently running": "no webserver is currently running",
"couldn't find file": "couldn't find file",
"errors found while generating": "errors found while generating",
"writing file": "escrevendo ficheiro",
"Simulator not found; Please install it with \"%s\"": "Simulator not found; Please install it with \"%s\"",
"Tried to load testrpc but an error occurred. This is a problem with testrpc": "Tried to load testrpc but an error occurred. This is a problem with testrpc",
"IMPORTANT: if you using a NodeJS version older than 6.9.1 then you need to install an older version of testrpc \"%s\". Alternatively install node 6.9.1 and the testrpc 3.0": "IMPORTANT: if you using a NodeJS version older than 6.9.1 then you need to install an older version of testrpc \"%s\". Alternatively install node 6.9.1 and the testrpc 3.0",
"terminating due to error": "terminating due to error",
"There a a space in the version of {{versionKey}}. We corrected it for you ({{correction})": "There a a space in the version of {{versionKey}}. We corrected it for you ({{correction})",
"versions": "versions",
"versions in use": "versions in use",
"downloading {{packageName}} {{version}}....": "downloading {{packageName}} {{version}}...."
}

View File

@ -1,26 +1,22 @@
let async = require('async');
const constants = require('./constants');
const _ = require('underscore');
// require("./utils/debug_util.js")(__filename, async);
require('colors');
// Override process.chdir so that we have a partial-implementation PWD for Windows
const realChdir = process.chdir;
process.chdir = (...args) => {
if (!process.env.PWD) {
process.env.PWD = process.cwd();
}
realChdir(...args);
if (!process.env.PWD) {
process.env.PWD = process.cwd();
}
realChdir(...args);
};
let Engine = require('./core/engine.js');
let version = require('../package.json').version;
class Embark {
constructor (options) {
constructor(options) {
this.version = version;
this.options = options || {};
}
@ -38,27 +34,27 @@ class Embark {
this.plugins = this.config.plugins;
}
isDev(env) {
if (this.config && this.config.blockchainConfig && this.config.blockchainConfig.isDev) {
return true;
} else if (this.config && this.config.blockchainConfig && this.config.blockchainConfig.isDev === false) {
return false;
}
return (env === 'development');
}
blockchain(env, client) {
this.context = [constants.contexts.blockchain];
let blockchainConfig = this.config.blockchainConfig;
let storageConfig = this.config.storageConfig;
let webServerConfig = this.config.webServerConfig;
if(blockchainConfig.rpcCorsDomain === 'auto') {
if(webServerConfig) blockchainConfig.rpcCorsDomain = `http://${webServerConfig.host}:${webServerConfig.port}`;
if(storageConfig) blockchainConfig.rpcCorsDomain += `${blockchainConfig.rpcCorsDomain.length ? ',' : ''}${storageConfig.protocol}://${storageConfig.host}:${storageConfig.port}`;
}
if(blockchainConfig.wsOrigins === 'auto') {
if(webServerConfig) blockchainConfig.wsOrigins = `http://${webServerConfig.host}:${webServerConfig.port}`;
if(storageConfig) blockchainConfig.wsOrigins += `${blockchainConfig.wsOrigins.length ? ',' : ''}${storageConfig.protocol}://${storageConfig.host}:${storageConfig.port}`;
}
return require('./cmds/blockchain/blockchain.js')(blockchainConfig, client, env).run();
return require('./cmds/blockchain/blockchain.js')(this.config.blockchainConfig, client, env, this.isDev(env)).run();
}
simulator(options) {
this.context = options.context || [constants.contexts.simulator, constants.contexts.blockchain];
let Simulator = require('./cmds/simulator.js');
let simulator = new Simulator({blockchainConfig: this.config.blockchainConfig, logger: this.logger});
let simulator = new Simulator({
blockchainConfig: this.config.blockchainConfig,
logger: this.logger
});
simulator.run(options);
}
@ -73,21 +69,25 @@ class Embark {
let self = this;
self.context = options.context || [constants.contexts.run, constants.contexts.build];
let Dashboard = require('./dashboard/dashboard.js');
let windowSize = require('window-size');
let engine = new Engine({
const Engine = require('./core/engine.js');
const engine = new Engine({
env: options.env,
client: options.client,
locale: options.locale,
isDev: this.isDev(options.env),
version: this.version,
embarkConfig: options.embarkConfig || 'embark.json',
logFile: options.logFile,
logLevel: options.logLevel,
context: self.context
context: self.context,
useDashboard: options.useDashboard
});
engine.init();
if (!options.useDashboard) {
engine.logger.info('========================'.bold.green);
engine.logger.info(('Welcome to Embark ' + this.version).yellow.bold);
engine.logger.info((__('Welcome to Embark') + ' ' + this.version).yellow.bold);
engine.logger.info('========================'.bold.green);
}
@ -106,48 +106,51 @@ class Embark {
contractsConfig: engine.config.contractsConfig
});
dashboard.start(function () {
engine.logger.info('dashboard start');
engine.logger.info(__('dashboard start'));
callback();
});
},
function (callback) {
let pluginList = engine.plugins.listPlugins();
if (pluginList.length > 0) {
engine.logger.info("loaded plugins: " + pluginList.join(", "));
engine.logger.info(__("loaded plugins") + ": " + pluginList.join(", "));
}
engine.startMonitor();
engine.startService("serviceMonitor");
engine.startService("libraryManager");
engine.startService("codeRunner");
engine.startService("web3");
engine.startService("pipeline");
engine.startService("codeGenerator");
engine.startService("deployment");
engine.startService(engine.config.storageConfig.provider, {bzz: engine.web3.bzz});
engine.startService("storage");
engine.startService("codeGenerator");
engine.startService("namingSystem");
engine.events.on('check:backOnline:Ethereum', function () {
engine.logger.info('Ethereum node detected..');
engine.logger.info(__('Ethereum node detected') + '..');
engine.config.reloadConfig();
engine.deployManager.deployContracts(function () {
engine.logger.info('Deployment Done');
engine.events.request('deploy:contracts', function (err) {
if (err) {
return;
}
engine.logger.info(__('Deployment Done'));
});
});
engine.events.on('outputDone', function () {
engine.logger.info("Looking for documentation? You can find it at ".cyan + "http://embark.readthedocs.io/".green.underline + ".".cyan);
engine.logger.info("Ready".underline);
engine.events.emit("status", "Ready".green);
engine.logger.info((__("Looking for documentation? You can find it at") + " ").cyan + "http://embark.status.im/docs/".green.underline + ".".cyan);
engine.logger.info(__("Ready").underline);
engine.events.emit("status", __("Ready").green);
});
engine.deployManager.deployContracts(function (err) {
engine.startService("fileWatcher");
if (options.runWebserver) {
engine.startService("webServer", {
host: options.serverHost,
port: options.serverPort
});
}
callback(err);
});
if (options.runWebserver) {
engine.startService("webServer", {
host: options.serverHost,
port: options.serverPort
});
}
engine.startService("fileWatcher");
callback();
}
], function (err, _result) {
if (err) {
@ -155,11 +158,6 @@ class Embark {
engine.logger.info(err.stack);
} else {
engine.events.emit('firstDeploymentDone');
let size = windowSize.get();
if (size.height < 40 || size.width < 118) {
engine.logger.warn("tip: you can resize the terminal or disable the dashboard with " + "embark run --nodashboard".bold.underline);
}
}
});
}
@ -167,8 +165,12 @@ class Embark {
build(options) {
this.context = options.context || [constants.contexts.build];
let engine = new Engine({
const Engine = require('./core/engine.js');
const engine = new Engine({
env: options.env,
client: options.client,
locale: options.locale,
isDev: this.isDev(options.env),
version: this.version,
embarkConfig: 'embark.json',
interceptLogs: false,
@ -186,20 +188,20 @@ class Embark {
function startServices(callback) {
let pluginList = engine.plugins.listPlugins();
if (pluginList.length > 0) {
engine.logger.info("loaded plugins: " + pluginList.join(", "));
engine.logger.info(__("loaded plugins") + ": " + pluginList.join(", "));
}
engine.startService("libraryManager");
engine.startService("codeRunner");
engine.startService("web3");
engine.startService("pipeline");
engine.startService("deployment", {onlyCompile: options.onlyCompile});
engine.startService("storage");
engine.startService("codeGenerator");
engine.startService("deployment");
engine.startService("ipfs");
engine.startService("swarm", {bzz: engine.web3.bzz});
callback();
},
function deploy(callback) {
engine.deployManager.deployContracts(function (err) {
engine.events.request('deploy:contracts', function (err) {
callback(err);
});
},
@ -215,26 +217,21 @@ class Embark {
engine.logger.error(err.message);
engine.logger.debug(err.stack);
} else {
engine.logger.info("Finished building".underline);
engine.logger.info(__("finished building").underline);
}
// needed due to child processes
process.exit();
});
}
initTests(options) {
this.context = options.context || [constants.contexts.test];
let Test = require('./tests/test.js');
options.context = this.context;
return new Test(options);
}
graph(options) {
this.context = options.context || [constants.contexts.graph];
options.onlyCompile = true;
let engine = new Engine({
const Engine = require('./core/engine.js');
const engine = new Engine({
env: options.env,
isDev: this.isDev(options.env),
version: this.version,
embarkConfig: options.embarkConfig || 'embark.json',
logFile: options.logFile,
@ -242,26 +239,23 @@ class Embark {
});
engine.init();
async.parallel([
async.waterfall([
function (callback) {
let pluginList = engine.plugins.listPlugins();
if (pluginList.length > 0) {
engine.logger.info("loaded plugins: " + pluginList.join(", "));
engine.logger.info(__("loaded plugins") + ": " + pluginList.join(", "));
}
engine.startMonitor();
engine.startService("serviceMonitor");
engine.startService("libraryManager");
engine.startService("pipeline");
engine.startService("codeGenerator");
engine.startService("deployment", {onlyCompile: true});
engine.startService("web3");
engine.startService("codeGenerator");
engine.deployManager.deployContracts(function (err) {
callback(err);
});
engine.events.request('deploy:contracts', callback);
}
], function (err, _result) {
], (err) => {
if (err) {
engine.logger.error(err.message);
engine.logger.info(err.stack);
@ -269,11 +263,11 @@ class Embark {
const GraphGenerator = require('./cmds/graph.js');
let graphGen = new GraphGenerator(engine);
graphGen.generate();
graphGen.generate(options);
engine.logger.info("Done".underline);
process.exit();
engine.logger.info(__("Done. %s generated", "./diagram.svg").underline);
}
process.exit();
});
}
@ -284,12 +278,15 @@ class Embark {
resetCmd();
}
upload(platform, options) {
upload(options) {
this.context = options.context || [constants.contexts.upload, constants.contexts.build];
let engine = new Engine({
const Engine = require('./core/engine.js');
const engine = new Engine({
env: options.env,
client: options.client,
locale: options.locale,
isDev: this.isDev(options.env),
version: this.version,
embarkConfig: 'embark.json',
interceptLogs: false,
@ -298,51 +295,37 @@ class Embark {
events: options.events,
logger: options.logger,
config: options.config,
plugins: options.plugins
plugins: options.plugins,
context: this.context
});
engine.init();
let platform = engine.config.storageConfig.upload.provider;
let cmdPlugin;
async.waterfall([
function startServices(callback) {
engine.startService("serviceMonitor");
engine.startService("libraryManager");
engine.startService("codeRunner");
engine.startService("web3");
engine.startService("pipeline");
engine.startService("codeGenerator");
engine.startService("deployment");
engine.startService(platform.toLowerCase(), {bzz: engine.web3.bzz});
engine.startMonitor();
engine.startService("storage");
engine.startService("codeGenerator");
callback();
},
function checkStorageService(callback){
let checkFn;
_.find(engine.servicesMonitor.checkList, (value, key) => {
if(key.toLowerCase() === platform.toLowerCase()){
checkFn = value;
return true;
}
});
if (!checkFn || typeof checkFn.fn !== 'function') {
return callback();
}
checkFn.fn(function (serviceCheckResult) {
if (!serviceCheckResult.status || serviceCheckResult.status === 'off') {
return callback({message: `Cannot upload: ${platform} node is not running on http://${engine.config.storageConfig.host}:${engine.config.storageConfig.port}.`});
}
callback();
});
},
function setupStoragePlugin(callback){
function setupStoragePlugin(callback) {
let pluginList = engine.plugins.listPlugins();
if (pluginList.length > 0) {
engine.logger.info("loaded plugins: " + pluginList.join(", "));
engine.logger.info(__("loaded plugins") + ": " + pluginList.join(", "));
}
// check use has input existing storage plugin
let cmdPlugins = engine.plugins.getPluginsFor('uploadCmds');
if (cmdPlugins.length > 0) {
cmdPlugin = cmdPlugins.find((pluginCmd) => {
return pluginCmd.uploadCmds.some(uploadCmd => {
@ -351,25 +334,23 @@ class Embark {
});
}
if (!cmdPlugin) {
engine.logger.info('try "embark upload ipfs" or "embark upload swarm"'.green);
return callback({message: 'unknown platform: ' + platform});
return callback({message: __('platform "{{platform}}" is specified as the upload provider, however no plugins have registered an upload command for "{{platform}}".', {platform: platform})});
}
callback();
},
function deploy(callback) {
// 2. upload to storage (outputDone event triggered after webpack finished)
engine.events.on('outputDone', function () {
cmdPlugin.uploadCmds[0].cb()
.then((success) => {
callback(null, success);
})
.catch(callback);
.then((success) => {
callback(null, success);
})
.catch(callback);
});
// 1. build the contracts and dapp webpack
engine.deployManager.deployContracts(function (err) {
engine.logger.info("finished deploying".underline);
if(err){
callback(err);
engine.events.request('deploy:contracts', function (err) {
engine.logger.info(__("finished deploying").underline);
if (err) {
callback(err);
}
});
}
@ -378,7 +359,7 @@ class Embark {
engine.logger.error(err.message);
engine.logger.debug(err.stack);
} else {
engine.logger.info(`finished building DApp and deploying to ${platform}`.underline);
engine.logger.info((__("finished building DApp and deploying to") + " " + platform).underline);
}
// needed due to child processes
@ -386,19 +367,11 @@ class Embark {
});
}
runTests(file) {
runTests(options) {
this.context = [constants.contexts.test];
let RunTests = require('./tests/run_tests.js');
RunTests.run(file);
RunTests.run(options);
}
}
// temporary until next refactor
Embark.initTests = function(options) {
let Test = require('./tests/test.js');
options.context = [constants.contexts.test];
return new Test(options);
};
module.exports = Embark;

View File

@ -0,0 +1,92 @@
const utils = require('../../utils/utils.js');
class ConsoleListener {
constructor(embark, options) {
this.logger = embark.logger;
this.ipc = options.ipc;
this.events = embark.events;
this.addressToContract = [];
this.contractsConfig = embark.config.contractsConfig;
this.contractsDeployed = false;
this._listenForLogRequests();
this.events.on("contractsDeployed", () => {
this.contractsDeployed = true;
this._updateContractList();
});
}
_updateContractList(){
this.events.request("contracts:list", (_err, contractsList) => {
if(_err) {
this.logger.error(__("no contracts found"));
return;
}
contractsList.forEach(contract => {
if(!contract.deployedAddress) return;
let address = contract.deployedAddress.toLowerCase();
if(!this.addressToContract[address]){
let funcSignatures = {};
contract.abiDefinition
.filter(func => func.type == "function")
.map(func => {
const name = func.name +
'(' +
(func.inputs ? func.inputs.map(input => input.type).join(',') : '') +
')';
funcSignatures[utils.sha3(name).substring(0, 10)] = {
name,
abi: func,
functionName: func.name
};
});
this.addressToContract[address] = {
name: contract.className,
functions: funcSignatures
};
}
});
});
}
_listenForLogRequests(){
if(this.ipc.ipcRole !== 'server') return;
this.ipc.on('log', (request) => {
if(request.type == 'contract-log'){
if(!this.contractsDeployed) return;
let {address, data, transactionHash, blockNumber, gasUsed, status} = request;
if(!this.addressToContract[address]){
this._updateContractList();
}
if(!this.addressToContract[address]) return;
const name = this.addressToContract[address].name;
const func = this.addressToContract[address].functions[data.substring(0, 10)];
const functionName = func.functionName;
const decodedParameters = utils.decodeParams(func.abi.inputs, data.substring(10));
let paramString = "";
if(func.abi.inputs){
func.abi.inputs.forEach((input) => {
let quote = input.type.indexOf("int") == -1 ? '"' : '';
paramString += quote + decodedParameters[input.name] + quote + ", ";
});
paramString = paramString.substring(0, paramString.length - 2);
}
gasUsed = utils.hexToNumber(gasUsed);
blockNumber = utils.hexToNumber(blockNumber);
this.logger.info(`Blockchain>`.underline + ` ${name}.${functionName}(${paramString})`.bold + ` | ${transactionHash} | gas:${gasUsed} | blk:${blockNumber} | status:${status}`);
} else {
this.logger.info(JSON.stringify(request));
}
});
}
}
module.exports = ConsoleListener;

View File

@ -0,0 +1,89 @@
let utils = require('../../utils/utils.js');
let fs = require('../../core/fs.js');
class DeployTracker {
constructor(embark, options) {
this.logger = embark.logger;
this.events = embark.events;
// TODO: unclear where it comes from
this.env = options.env;
//this.chainConfig = options.chainConfig;
this.chainConfig = embark.config.chainTracker;
this.registerEvents();
}
registerEvents() {
const self = this;
this.events.on("deploy:beforeAll", this.setCurrentChain.bind(this));
this.events.on("deploy:contract:deployed", (contract) => {
self.trackContract(contract.className, contract.realRuntimeBytecode, contract.realArgs, contract.deployedAddress);
self.save();
});
this.events.setCommandHandler("deploy:contract:shouldDeploy", (contract, cb) => {
let trackedContract = self.getContract(contract.className, contract.realRuntimeBytecode, contract.realArgs);
cb(trackedContract);
});
}
// TODO: just an event might not be enought to the async nature
// it needs to be a plugin api before deploy, that makes the deployment wait
setCurrentChain() {
const self = this;
if (this.chainConfig === false) {
this.currentChain = {contracts: []};
//return cb();
}
this.events.request("blockchain:block:byNumber", 0, function(_err, block) {
let chainId = block.hash;
if (self.chainConfig[chainId] === undefined) {
self.chainConfig[chainId] = {contracts: {}};
}
self.currentChain = self.chainConfig[chainId];
self.currentChain.name = self.env;
//cb();
});
}
loadConfig(config) {
this.chainConfig = config;
return this;
}
trackContract(contractName, code, args, address) {
if (!this.currentChain) return false;
this.currentChain.contracts[utils.sha3(code + contractName + args.join(','))] = {
name: contractName,
address: address
};
}
getContract(contractName, code, args) {
if (!this.currentChain) return false;
let contract = this.currentChain.contracts[utils.sha3(code + contractName + args.join(','))];
if (contract && contract.address === undefined) {
return false;
}
return contract;
}
// TODO: abstract this
// chainConfig can be an abstract PersistentObject
save() {
if (this.chainConfig === false) {
return;
}
fs.writeJSONSync("./chains.json", this.chainConfig, {spaces: 2});
}
}
module.exports = DeployTracker;

304
lib/modules/ens/embarkjs.js Normal file
View File

@ -0,0 +1,304 @@
import namehash from 'eth-ens-namehash';
/*global web3*/
let __embarkENS = {};
// registry interface for later
__embarkENS.registryInterface = [
{
"constant": true,
"inputs": [
{
"name": "node",
"type": "bytes32"
}
],
"name": "resolver",
"outputs": [
{
"name": "",
"type": "address"
}
],
"type": "function"
},
{
"constant": true,
"inputs": [
{
"name": "node",
"type": "bytes32"
}
],
"name": "owner",
"outputs": [
{
"name": "",
"type": "address"
}
],
"type": "function"
},
{
"constant": false,
"inputs": [
{
"name": "node",
"type": "bytes32"
},
{
"name": "resolver",
"type": "address"
}
],
"name": "setResolver",
"outputs": [],
"type": "function"
},
{
"constant": false,
"inputs": [
{
"name": "node",
"type": "bytes32"
},
{
"name": "label",
"type": "bytes32"
},
{
"name": "owner",
"type": "address"
}
],
"name": "setSubnodeOwner",
"outputs": [],
"type": "function"
},
{
"constant": false,
"inputs": [
{
"name": "node",
"type": "bytes32"
},
{
"name": "owner",
"type": "address"
}
],
"name": "setOwner",
"outputs": [],
"type": "function"
}
];
__embarkENS.resolverInterface = [
{
"constant": true,
"inputs": [
{
"name": "node",
"type": "bytes32"
}
],
"name": "addr",
"outputs": [
{
"name": "",
"type": "address"
}
],
"type": "function"
},
{
"constant": true,
"inputs": [
{
"name": "node",
"type": "bytes32"
}
],
"name": "content",
"outputs": [
{
"name": "",
"type": "bytes32"
}
],
"type": "function"
},
{
"constant": true,
"inputs": [
{
"name": "node",
"type": "bytes32"
}
],
"name": "name",
"outputs": [
{
"name": "",
"type": "string"
}
],
"type": "function"
},
{
"constant": true,
"inputs": [
{
"name": "node",
"type": "bytes32"
},
{
"name": "kind",
"type": "bytes32"
}
],
"name": "has",
"outputs": [
{
"name": "",
"type": "bool"
}
],
"type": "function"
},
{
"constant": false,
"inputs": [
{
"name": "node",
"type": "bytes32"
},
{
"name": "addr",
"type": "address"
}
],
"name": "setAddr",
"outputs": [],
"type": "function"
},
{
"constant": false,
"inputs": [
{
"name": "node",
"type": "bytes32"
},
{
"name": "hash",
"type": "bytes32"
}
],
"name": "setContent",
"outputs": [],
"type": "function"
},
{
"constant": false,
"inputs": [
{
"name": "node",
"type": "bytes32"
},
{
"name": "name",
"type": "string"
}
],
"name": "setName",
"outputs": [],
"type": "function"
},
{
"constant": true,
"inputs": [
{
"name": "node",
"type": "bytes32"
},
{
"name": "contentType",
"type": "uint256"
}
],
"name": "ABI",
"outputs": [
{
"name": "",
"type": "uint256"
},
{
"name": "",
"type": "bytes"
}
],
"payable": false,
"type": "function"
}
];
__embarkENS.registryAddresses = {
// Mainnet
"1": "0x314159265dd8dbb310642f98f50c066173c1259b",
// Ropsten
"3": "0x112234455c3a32fd11230c42e7bccd4a84e02010",
// Rinkeby
"4": "0xe7410170f87102DF0055eB195163A03B7F2Bff4A"
};
__embarkENS.setProvider = function () {
const self = this;
// get network id and then assign ENS contract based on that
let registryAddresses = this.registryAddresses;
this.ens = null;
web3.eth.net.getId().then(id => {
if (registryAddresses[id] !== undefined) {
self.ens = new web3.eth.Contract(self.registryInterface, registryAddresses[id]);
}
// todo: deploy at this point
}).catch(e => {
if (e.message.indexOf('Provider not set or invalid') > -1) {
console.warn('ENS is not available in this chain');
return;
}
console.error(e);
});
};
__embarkENS.resolve = function(name) {
const self = this;
if (self.ens === undefined) return;
let node = namehash.hash(name);
return self.ens.methods.resolver(node).call().then((resolverAddress) => {
let resolverContract = new web3.eth.Contract(self.resolverInterface, resolverAddress);
return resolverContract.methods.addr(node).call();
}).then((addr) => {
return addr;
}).catch(err => err);
};
__embarkENS.lookup = function(address) {
const self = this;
if (self.ens === undefined) return;
if (address.startsWith("0x")) address = address.slice(2);
let node = namehash.hash(address.toLowerCase() + ".addr.reverse");
return self.ens.methods.resolver(node).call().then((resolverAddress) => {
let resolverContract = new web3.eth.Contract(self.resolverInterface, resolverAddress);
return resolverContract.methods.name(node).call();
}).then((name) => {
if (name === "" || name === undefined) throw Error("ENS name not found");
return name;
}).catch(err => err);
};

55
lib/modules/ens/index.js Normal file
View File

@ -0,0 +1,55 @@
const fs = require('../../core/fs.js');
const utils = require('../../utils/utils.js');
class ENS {
constructor(embark, _options) {
this.logger = embark.logger;
this.events = embark.events;
this.namesConfig = embark.config.namesystemConfig;
this.embark = embark;
this.addENSToEmbarkJS();
this.addSetProvider();
}
addENSToEmbarkJS() {
const self = this;
// TODO: make this a shouldAdd condition
if (this.namesConfig === {}) {
return;
}
if ((this.namesConfig.available_providers.indexOf('ens') < 0) && (this.namesConfig.provider !== 'ens' || this.namesConfig.enabled !== true)) {
return;
}
self.events.request("version:get:eth-ens-namehash", function(EnsNamehashVersion) {
let currentEnsNamehashVersion = require('../../../package.json').dependencies["eth-ens-namehash"];
if (EnsNamehashVersion !== currentEnsNamehashVersion) {
self.events.request("version:getPackageLocation", "eth-ens-namehash", EnsNamehashVersion, function(err, location) {
self.embark.registerImportFile("eth-ens-namehash", fs.dappPath(location));
});
}
});
let code = "";
code += "\n" + fs.readFileSync(utils.joinPath(__dirname, 'embarkjs.js')).toString();
code += "\nEmbarkJS.Names.registerProvider('ens', __embarkENS);";
this.embark.addCodeToEmbarkJS(code);
}
addSetProvider() {
let config = JSON.stringify({});
let code = "\nEmbarkJS.Names.setProvider('ens'," + config + ");";
let shouldInit = (namesConfig) => {
return (namesConfig.provider === 'ens' && namesConfig.enabled === true);
};
this.embark.addProviderInit('names', code, shouldInit);
}
}
module.exports = ENS;

113
lib/modules/fuzzer/index.js Normal file
View File

@ -0,0 +1,113 @@
const utils = require('web3-utils');
const _ = require('underscore');
// generates random inputs based on the inputs of an ABI
class ContractFuzzer {
constructor(embark) {
this.embark = embark;
this.logger = embark.logger;
this.events = embark.events;
this.registerConsoleCommand();
}
// main function to call, takes in iteration number and a contract and returns a map object
// composed of method names -> fuzzed inputs.
generateFuzz(iterations, contract) {
const self = this;
let fuzzMap = {};
contract.abiDefinition.filter((x) => x.inputs && x.inputs.length != 0 && x.type != "event").forEach((abiMethod) => {
let name = abiMethod.type === "constructor" ? "constructor" : abiMethod.name;
let inputTypes = abiMethod.inputs.map(input => input.type);
fuzzMap[name] = {};
for (let i = 0; i < iterations; i++) {
fuzzMap[name][i] = inputTypes.map(input => this.getTypeFuzz(input));
self.logger.trace(name);
self.logger.trace("iteration: " + i + "\n" + fuzzMap[name][i]);
}
});
self.logger.trace('\n');
return fuzzMap;
}
getTypeFuzz(typeString) {
const self = this;
// Group 0: uint256[3]
// Group 1: uint256
// Group 2: uint
// Group 3: 256
// Group 4: [3]
// Group 5: 3
let regexObj = typeString.match(/((bool|int|uint|bytes|string|address)([0-9]*)?)(\[([0-9]*)\])*$/);
let type = regexObj[1];
let kind = regexObj[2];
let size = regexObj[3];
let array = regexObj[4];
let arraySize = regexObj[5];
switch(true) {
case array !== undefined: {
// if it's a dynamic array pick a number between 1 and 256 for length of array
let length = arraySize === undefined || arraySize === null || arraySize === '' ? Math.floor((Math.random() * 256) + 1) : arraySize;
return self.generateArrayOfType(length, type);
}
case kind == "bool":
return self.generateRandomBool();
case kind == "uint" || kind == "int":
return self.generateRandomInt(size);
case kind === "bytes" && size !== undefined:
return self.generateRandomStaticBytes(size);
case kind === "string" || kind === "bytes":
return self.generateRandomDynamicType();
case kind === "address":
return self.generateRandomAddress();
default:
throw new Error("Couldn't find proper ethereum abi type");
}
}
generateRandomBool() {
return _.sample([true, false]);
}
generateArrayOfType(length, type) {
var arr = [];
for (var i = 0; i < length; i++) arr.push(this.getTypeFuzz(type));
return arr;
}
generateRandomDynamicType() {
return Math.random().toString(36).slice(2);
}
generateRandomStaticBytes(size) {
return utils.randomHex(size);
}
generateRandomInt(size) {
return utils.toBN(utils.randomHex(size / 8));
}
generateRandomAddress() {
return utils.randomHex(20);
}
registerConsoleCommand() {
const self = this;
self.embark.registerConsoleCommand((cmd, _options) => {
let splitCmd = cmd.split(' ');
let cmdName = splitCmd[0];
let contractName = splitCmd[1];
let iterations = splitCmd[2] === undefined ? 1 : splitCmd[2];
if (cmdName === 'fuzz') {
self.events.request('contracts:contract', contractName, (contract) => {
self.logger.info("-- fuzzed vals for " + contractName);
this.generateFuzz(iterations, contract);
});
return "";
}
return false;
});
}
}
module.exports = ContractFuzzer;

View File

@ -0,0 +1,72 @@
/*global web3*/
const async = require('async');
const _ = require('underscore');
const ContractFuzzer = require('../fuzzer');
class GasEstimator {
constructor(embark) {
this.embark = embark;
this.logger = embark.logger;
this.events = embark.events;
this.fuzzer = new ContractFuzzer(embark);
}
estimateGas(contractName, cb) {
const self = this;
let gasMap = {};
self.events.request('contracts:contract', contractName, (contract) => {
let fuzzMap = self.fuzzer.generateFuzz(3, contract);
let contractObj = new web3.eth.Contract(contract.abiDefinition, contract.deployedAddress);
async.each(contract.abiDefinition.filter((x) => x.type !== "event"),
(abiMethod, gasCb) => {
let name = abiMethod.name;
if (abiMethod.type === "constructor") {
// already provided for us
gasMap['constructor'] = contract.gasEstimates.creation.totalCost.toString();
return gasCb(null, name, abiMethod.type);
} else if (abiMethod.type == "fallback") {
gasMap['fallback'] = contract.gasEstimates.external[""].toString();
return gasCb(null, name, abiMethod.type);
} else if (
(abiMethod.inputs === null || abiMethod.inputs === undefined || abiMethod.inputs.length === 0)
) {
// just run it and register it
contractObj.methods[name]
.apply(contractObj.methods[name], [])
.estimateGas((err, gasAmount) => {
if (err) return gasCb(err, name, abiMethod.type);
gasMap[name] = gasAmount;
return gasCb(null, name, abiMethod.type);
});
} else {
// async concatenate all the fuzz values and their gas cost outputs and check for equality
async.concat(fuzzMap[name], (values, getVarianceCb) => {
contractObj.methods[name].apply(contractObj.methods[name], values)
.estimateGas((err, gasAmount) => {
getVarianceCb(err, gasAmount);
});
}, (err, variance) => {
if (err) {
return gasCb(err, name, abiMethod.type);
} else if (_.isEqual(variance[0], variance[1]) && _.isEqual(variance[1], variance[2])) {
gasMap[name] = variance[0];
} else {
gasMap[name] = 'infinite';
}
return gasCb(null, name, abiMethod.type);
});
}
},
(err, name, type) => {
if (err) {
if (type === "constructor" || type === "fallback") name = type;
return cb(err, null, name);
}
cb(null, gasMap, null);
}
);
});
}
}
module.exports = GasEstimator;

View File

@ -7,23 +7,24 @@ __embarkIPFS.setProvider = function (options) {
var promise = new Promise(function (resolve, reject) {
try {
if (options === undefined) {
self.ipfsConnection = IpfsApi('localhost', '5001');
self._config = options;
self._ipfsConnection = IpfsApi('localhost', '5001');
self._getUrl = "http://localhost:8080/ipfs/";
} else {
var ipfsOptions = {host: options.server, protocol: 'http'};
var ipfsOptions = {host: options.host || options.server, protocol: 'http'};
if (options.protocol) {
ipfsOptions.protocol = options.protocol;
}
if (options.port && options.port !== 'false') {
ipfsOptions.port = options.port;
}
self.ipfsConnection = IpfsApi(ipfsOptions);
self._ipfsConnection = IpfsApi(ipfsOptions);
self._getUrl = options.getUrl || "http://localhost:8080/ipfs/";
}
resolve(self);
} catch (err) {
console.log(err);
self.ipfsConnection = null;
console.error(err);
self._ipfsConnection = null;
reject(new Error('Failed to connect to IPFS'));
}
});
@ -33,11 +34,11 @@ __embarkIPFS.setProvider = function (options) {
__embarkIPFS.saveText = function (text) {
const self = this;
var promise = new Promise(function (resolve, reject) {
if (!self.ipfsConnection) {
if (!self._ipfsConnection) {
var connectionError = new Error('No IPFS connection. Please ensure to call Embark.Storage.setProvider()');
reject(connectionError);
}
self.ipfsConnection.add(self.ipfsConnection.Buffer.from(text), function (err, result) {
self._ipfsConnection.add(self._ipfsConnection.Buffer.from(text), function (err, result) {
if (err) {
reject(err);
} else {
@ -54,11 +55,11 @@ __embarkIPFS.get = function (hash) {
// TODO: detect type, then convert if needed
//var ipfsHash = web3.toAscii(hash);
var promise = new Promise(function (resolve, reject) {
if (!self.ipfsConnection) {
if (!self._ipfsConnection) {
var connectionError = new Error('No IPFS connection. Please ensure to call Embark.Storage.setProvider()');
reject(connectionError);
}
self.ipfsConnection.get(hash, function (err, files) {
self._ipfsConnection.get(hash, function (err, files) {
if (err) {
return reject(err);
}
@ -78,15 +79,15 @@ __embarkIPFS.uploadFile = function (inputSelector) {
}
var promise = new Promise(function (resolve, reject) {
if (!self.ipfsConnection) {
if (!self._ipfsConnection) {
var connectionError = new Error('No IPFS connection. Please ensure to call Embark.Storage.setProvider()');
reject(connectionError);
}
var reader = new FileReader();
reader.onloadend = function () {
var fileContent = reader.result;
var buffer = self.ipfsConnection.Buffer.from(fileContent);
self.ipfsConnection.add(buffer, function (err, result) {
var buffer = self._ipfsConnection.Buffer.from(fileContent);
self._ipfsConnection.add(buffer, function (err, result) {
if (err) {
reject(err);
} else {
@ -102,10 +103,10 @@ __embarkIPFS.uploadFile = function (inputSelector) {
__embarkIPFS.isAvailable = function () {
return new Promise((resolve) => {
if (!this.ipfsConnection) {
if (!this._ipfsConnection) {
return resolve(false);
}
this.ipfsConnection.id()
this._ipfsConnection.id()
.then((id) => {
resolve(Boolean(id));
})
@ -119,3 +120,4 @@ __embarkIPFS.getUrl = function (hash) {
return (this._getUrl || "http://localhost:8080/ipfs/") + hash;
};

View File

@ -1,6 +1,8 @@
let UploadIPFS = require('./upload.js');
let utils = require('../../utils/utils.js');
let fs = require('../../core/fs.js');
const UploadIPFS = require('./upload.js');
const utils = require('../../utils/utils.js');
const fs = require('../../core/fs.js');
const IpfsApi = require('ipfs-api');
const _ = require('underscore');
class IPFS {
@ -8,22 +10,17 @@ class IPFS {
this.logger = embark.logger;
this.events = embark.events;
this.buildDir = options.buildDir;
this.storageConfig = options.storageConfig;
this.host = options.host || this.storageConfig.host;
this.port = options.port || this.storageConfig.port;
this.addCheck = options.addCheck;
this.storageConfig = embark.config.storageConfig;
this.host = options.host || this.storageConfig.upload.host;
this.port = options.port || this.storageConfig.upload.port;
this.protocol = options.protocol || this.storageConfig.upload.protocol;
this.embark = embark;
this.commandlineDeploy();
this.setServiceCheck();
this.addIPFSToEmbarkJS();
this.addSetProvider();
}
commandlineDeploy() {
let upload_ipfs = new UploadIPFS({
buildDir: this.buildDir || 'dist/',
storageConfig: this.storageConfig,
storageConfig: this.storageConfig.upload,
configIpfsBin: this.storageConfig.ipfs_bin || "ipfs"
});
@ -38,45 +35,49 @@ class IPFS {
if (!storageConfig.enabled) {
return;
}
if (storageConfig.provider !== 'ipfs' && storageConfig.available_providers.indexOf("ipfs") < 0) {
if (_.findWhere(this.storageConfig.dappConnection, {'provider': 'ipfs'}) === undefined && (storageConfig.upload.provider !== 'ipfs' || storageConfig.available_providers.indexOf("ipfs") < 0)) {
return;
}
self.events.on('check:backOnline:IPFS', function () {
self.logger.info('IPFS node detected..');
self.logger.info(__('IPFS node detected') + '..');
});
self.events.on('check:wentOffline:IPFS', function () {
self.logger.info('IPFS node is offline..');
self.logger.info(__('IPFS node is offline') + '..');
});
if (!self.addCheck) {
return;
}
self.addCheck('IPFS', function (cb) {
self.logger.trace("Checking IPFS version...");
utils.httpGetJson('http://' + self.host + ':' + self.port + '/api/v0/version', function (err, body) {
self.events.request("services:register", 'IPFS', function (cb) {
let url = (self.protocol || 'http') + '://' + self.host + ':' + self.port + '/api/v0/version';
self.logger.trace(`Checking IPFS version on ${url}...`);
if(self.protocol !== 'https'){
utils.httpGetJson(url, versionCb);
} else {
utils.httpsGetJson(url, versionCb);
}
function versionCb(err, body) {
if (err) {
self.logger.trace("Check IPFS version error: " + err);
self.logger.trace("IPFS unavailable");
return cb({name: "IPFS ", status: 'off'});
}
if (body.Version) {
self.logger.trace("IPFS available");
return cb({name: ("IPFS " + body.Version), status: 'on'});
}
self.logger.trace("IPFS available");
return cb({name: "IPFS ", status: 'on'});
});
}
});
}
addIPFSToEmbarkJS() {
addProviderToEmbarkJS() {
const self = this;
// TODO: make this a shouldAdd condition
if (this.storageConfig === {}) {
return;
}
if ((this.storageConfig.available_providers.indexOf('ipfs') < 0) && (this.storageConfig.provider !== 'ipfs' || this.storageConfig.enabled !== true)) {
if (this.storageConfig.available_providers.indexOf('ipfs') < 0 || _.findWhere(this.storageConfig.dappConnection, {'provider': 'ipfs'}) === undefined || this.storageConfig.enabled !== true) {
return;
}
@ -89,6 +90,15 @@ class IPFS {
}
});
self.events.request("version:get:p-iteration", function(pIterationVersion) {
let currentPIterationVersion = require('../../../package.json').dependencies["p-iteration"];
if (pIterationVersion !== currentPIterationVersion) {
self.events.request("version:getPackageLocation", "p-iteration", pIterationVersion, function(err, location) {
self.embark.registerImportFile("p-iteration", fs.dappPath(location));
});
}
});
let code = "";
code += "\n" + fs.readFileSync(utils.joinPath(__dirname, 'embarkjs.js')).toString();
code += "\nEmbarkJS.Storage.registerProvider('ipfs', __embarkIPFS);";
@ -96,21 +106,11 @@ class IPFS {
this.embark.addCodeToEmbarkJS(code);
}
addSetProvider() {
let config = JSON.stringify({
server: this.storageConfig.host,
port: this.storageConfig.port,
protocol: this.storageConfig.protocol,
getUrl: this.storageConfig.getUrl
});
let code = "\nEmbarkJS.Storage.setProvider('ipfs'," + config + ");";
let shouldInit = (storageConfig) => {
return (storageConfig.provider === 'ipfs' && storageConfig.enabled === true);
};
this.embark.addProviderInit('storage', code, shouldInit);
addObjectToConsole() {
let ipfs = IpfsApi(this.host, this.port);
this.events.emit("runcode:register", "ipfs", ipfs);
}
}
module.exports = IPFS;

View File

@ -20,7 +20,7 @@ class IPFS {
let ipfs_bin = shelljs.which(self.configIpfsBin);
if (ipfs_bin === 'ipfs not found' || !ipfs_bin) {
console.log(('=== WARNING: ' + self.configIpfsBin + ' not found or not in the path. Guessing ~/go/bin/ipfs for path').yellow);
console.log(('=== WARNING: ' + self.configIpfsBin + ' ' + __('not found or not in the path. Guessing %s for path', '~/go/bin/ipfs')).yellow);
ipfs_bin = "~/go/bin/ipfs";
}
@ -28,7 +28,7 @@ class IPFS {
},
function runCommand(ipfs_bin, callback) {
let cmd = `"${ipfs_bin}" add -r ${self.buildDir}`;
console.log(("=== adding " + self.buildDir + " to ipfs").green);
console.log(("=== " + __("adding %s to ipfs", self.buildDir)).green);
console.debug(cmd);
shelljs.exec(cmd, {silent:true}, function(code, stdout, stderr){ // {silent:true}: don't echo cmd output so it can be controlled via logLevel
console.log(stdout.green);
@ -43,18 +43,18 @@ class IPFS {
callback(null, dir_hash);
},
function printUrls(dir_hash, callback) {
console.log(("=== DApp available at http://localhost:8080/ipfs/" + dir_hash + "/").green);
console.log(("=== DApp available at http://gateway.ipfs.io/ipfs/" + dir_hash + "/").green);
console.log(("=== " + __("DApp available at") + " http://localhost:8080/ipfs/" + dir_hash + "/").green);
console.log(("=== " + __("DApp available at") + " http://ipfs.infura.io/ipfs/" + dir_hash + "/").green);
callback();
}
], function (err, _result) {
if (err) {
console.log("error uploading to ipfs".red);
console.log(__("error uploading to ipfs").red);
console.log(err);
reject(err);
}
else resolve('successfully uploaded to ipfs');
else resolve(__('successfully uploaded to ipfs'));
});
});
}

View File

@ -0,0 +1,72 @@
const asciiTable = require('ascii-table');
const GasEstimator = require('../gasEstimator');
class Profiler {
constructor(embark) {
this.embark = embark;
this.logger = embark.logger;
this.events = embark.events;
this.gasEstimator = new GasEstimator(embark);
this.registerConsoleCommand();
}
profile(contractName, contract) {
const self = this;
let table = new asciiTable(contractName);
table.setHeading('Function', 'Payable', 'Mutability', 'Inputs', 'Outputs', 'Gas Estimates');
self.gasEstimator.estimateGas(contractName, function(err, gastimates, name) {
if (err) {
self.logger.error('error found in method: ', name);
self.logger.error(JSON.stringify(err));
return;
}
contract.abiDefinition.forEach((abiMethod) => {
switch(abiMethod.type) {
case "constructor":
table.addRow("constructor", abiMethod.payable, abiMethod.stateMutability, self.formatParams(abiMethod.inputs), self.formatParams(abiMethod.outputs), gastimates['constructor']);
break;
case "fallback":
table.addRow("fallback", abiMethod.payable, abiMethod.stateMutability, self.formatParams(abiMethod.inputs), self.formatParams(abiMethod.outputs), gastimates['fallback']);
break;
default:
table.addRow(abiMethod.name, abiMethod.payable, abiMethod.stateMutability, self.formatParams(abiMethod.inputs), self.formatParams(abiMethod.outputs), gastimates[abiMethod.name]);
}
});
self.logger.info(table.toString());
});
}
formatParams(params) {
if (!params || !params.length) {
return "()";
}
let paramString = "(";
let mappedParams = params.map(param => param.type);
paramString += mappedParams.join(',');
paramString += ")";
return paramString;
}
registerConsoleCommand() {
const self = this;
self.embark.registerConsoleCommand((cmd, _options) => {
let cmdName = cmd.split(' ')[0];
let contractName = cmd.split(' ')[1];
if (cmdName === 'profile') {
self.events.request('contracts:contract', contractName, (contract) => {
if (!contract.deployedAddress) {
self.logger.info("-- couldn't profile " + contractName + " - it's not deployed or could be an interface");
return "";
}
self.logger.info("-- profile for " + contractName);
this.profile(contractName, contract);
});
return "";
}
return false;
});
}
}
module.exports = Profiler;

View File

@ -6,7 +6,11 @@ class Solidity {
constructor(embark, options) {
this.logger = embark.logger;
this.events = embark.events;
this.contractDirectories = options.contractDirectories;
this.ipc = options.ipc;
this.contractDirectories = embark.config.contractDirectories;
this.solcAlreadyLoaded = false;
this.solcW = null;
this.useDashboard = options.useDashboard;
embark.registerCompiler(".sol", this.compile_solidity.bind(this));
}
@ -17,7 +21,6 @@ class Solidity {
}
let self = this;
let input = {};
let solcW;
async.waterfall([
function prepareInput(callback) {
async.each(contractFiles,
@ -31,7 +34,7 @@ class Solidity {
file.content(function(fileContent) {
if (!fileContent) {
self.logger.error('Error while loading the content of ' + filename);
self.logger.error(__('Error while loading the content of ') + filename);
return fileCb();
}
input[filename] = {content: fileContent.replace(/\r\n/g, '\n')};
@ -44,19 +47,19 @@ class Solidity {
);
},
function loadCompiler(callback) {
// TODO: there ino need to load this twice
solcW = new SolcW({logger: self.logger, events: self.events});
if (solcW.isCompilerLoaded()) {
if (self.solcAlreadyLoaded) {
return callback();
}
self.solcW = new SolcW({logger: self.logger, events: self.events, ipc: self.ipc, useDashboard: self.useDashboard});
self.logger.info("loading solc compiler..");
solcW.load_compiler(function (err) {
self.logger.info(__("loading solc compiler") + "..");
self.solcW.load_compiler(function (err) {
self.solcAlreadyLoaded = true;
callback(err);
});
},
function compileContracts(callback) {
self.logger.info("compiling solidity contracts...");
self.logger.info(__("compiling solidity contracts") + "...");
let jsonObj = {
language: 'Solidity',
sources: input,
@ -73,7 +76,10 @@ class Solidity {
}
};
solcW.compile(jsonObj, function (output) {
self.solcW.compile(jsonObj, function (err, output) {
if(err){
return callback(err);
}
if (output.errors) {
for (let i=0; i<output.errors.length; i++) {
if (output.errors[i].type === 'Warning') {
@ -91,11 +97,11 @@ class Solidity {
let json = output.contracts;
if (!output || !output.contracts) {
return callback(new Error("error compiling for unknown reasons"));
return callback(new Error(__("error compiling for unknown reasons")));
}
if (Object.keys(output.contracts).length === 0 && output.sourceList.length > 0) {
return callback(new Error("error compiling. There are sources available but no code could be compiled, likely due to fatal errors in the solidity code").message);
return callback(new Error(__("error compiling. There are sources available but no code could be compiled, likely due to fatal errors in the solidity code")).message);
}
let compiled_object = {};

View File

@ -1,41 +1,91 @@
let solc;
const fs = require('fs-extra');
const path = require('path');
const constants = require('../../constants');
const Utils = require('../../utils/utils');
const ProcessWrapper = require('../../process/processWrapper');
const PluginManager = require('live-plugin-manager-git-fix').PluginManager;
const NpmTimer = require('../../versions/npmTimer');
function findImports(filename) {
if (filename.startsWith('http') || filename.startsWith('git')) {
const fileObj = Utils.getExternalContractUrl(filename);
filename = fileObj.filePath;
class SolcProcess extends ProcessWrapper {
constructor(options){
super();
this._logger = options.logger;
this._showSpinner = options.showSpinner === true;
}
if (fs.existsSync(filename)) {
return {contents: fs.readFileSync(filename).toString()};
findImports(filename) {
if (filename.startsWith('http') || filename.startsWith('git')) {
const fileObj = Utils.getExternalContractUrl(filename);
filename = fileObj.filePath;
}
if (fs.existsSync(filename)) {
return {contents: fs.readFileSync(filename).toString()};
}
if (fs.existsSync(path.join('./node_modules/', filename))) {
return {contents: fs.readFileSync(path.join('./node_modules/', filename)).toString()};
}
if (fs.existsSync(path.join(constants.httpContractsDirectory, filename))) {
return {contents: fs.readFileSync(path.join(constants.httpContractsDirectory, filename)).toString()};
}
return {error: 'File not found'};
}
if (fs.existsSync(path.join('./node_modules/', filename))) {
return {contents: fs.readFileSync(path.join('./node_modules/', filename)).toString()};
installAndLoadCompiler(solcVersion, packagePath){
let self = this;
return new Promise((resolve, reject) => {
let manager = new PluginManager({pluginsPath: packagePath});
let timer;
if (!fs.existsSync(packagePath)) {
timer = new NpmTimer({logger: self._logger, packageName: 'solc', version: solcVersion, showSpinner: self._showSpinner});
}
if(timer) timer.start();
manager.install('solc', solcVersion).then(() => {
self.solc = manager.require('solc');
if(timer) timer.end();
resolve();
}).catch(reject);
});
}
if (fs.existsSync(path.join(constants.httpContractsDirectory, filename))) {
return {contents: fs.readFileSync(path.join('./.embark/contracts', filename)).toString()};
compile(jsonObj, cb) {
// TODO: only available in 0.4.11; need to make versions warn about this
try {
let output = this.solc.compileStandardWrapper(JSON.stringify(jsonObj), this.findImports);
cb(null, output);
} catch (err) {
cb(err.message);
}
}
return {error: 'File not found'};
}
process.on('message', function (msg) {
if (msg.action === 'loadCompiler') {
solc = require(msg.solcLocation);
process.send({result: "loadedCompiler"});
let solcProcess;
process.on('message', (msg) => {
if (msg.action === "init") {
solcProcess = new SolcProcess(msg.options);
return process.send({result: "initiated"});
}
if (msg.action === 'compile') {
// TODO: only available in 0.4.11; need to make versions warn about this
let output = solc.compileStandardWrapper(JSON.stringify(msg.jsonObj), findImports);
process.send({result: "compilation", output: output});
else if (msg.action === 'loadCompiler') {
solcProcess.solc = require('solc');
return process.send({result: "loadedCompiler"});
}
else if (msg.action == 'installAndLoadCompiler') {
solcProcess.installAndLoadCompiler(msg.solcVersion, msg.packagePath).then(() => {
return process.send({result: "loadedCompiler"});
});
}
else if (msg.action === 'compile') {
solcProcess.compile(msg.jsonObj, (err, output) => {
process.send({result: "compilation-" + msg.id, err: err, output: output});
});
}
});
process.on('exit', function () {
process.exit(0);
});

View File

@ -1,58 +1,91 @@
let utils = require('../../utils/utils.js');
let fs = require('../../core/fs.js');
let solcProcess;
let compilerLoaded = false;
let currentSolcVersion = require('../../../package.json').dependencies.solc;
const ProcessLauncher = require('../../process/processLauncher');
const uuid = require('uuid/v1');
class SolcW {
constructor(options) {
this.logger = options.logger;
this.events = options.events;
this.ipc = options.ipc;
this.compilerLoaded = false;
this.solcProcess = null;
this.useDashboard = options.useDashboard;
}
load_compiler(done) {
const self = this;
if (compilerLoaded) {
done();
if (!self.ipc.isClient()) {
return self.load_compiler_internally(done);
}
solcProcess = require('child_process').fork(utils.joinPath(__dirname, '/solcP.js'));
solcProcess.once('message', function (msg) {
if (msg.result !== 'loadedCompiler') {
return;
self.ipc.connect((err) => {
if (err) {
return self.load_compiler_internally(done);
}
compilerLoaded = true;
self.compilerLoaded = true;
done();
});
}
this.events.request("version:get:solc", function(solcVersion) {
if (solcVersion === currentSolcVersion) {
solcProcess.send({action: 'loadCompiler', solcLocation: 'solc'});
} else {
self.events.request("version:getPackageLocation", "solc", solcVersion, function(err, location) {
load_compiler_internally(done) {
const self = this;
if (this.compilerLoaded) {
return done();
}
this.solcProcess = new ProcessLauncher({
modulePath: utils.joinPath(__dirname, 'solcP.js'),
logger: self.logger,
events: self.events
});
this.solcProcess.once("result", "initiated", () => {
this.events.request("version:get:solc", function(solcVersion) {
if (solcVersion === currentSolcVersion) {
return self.solcProcess.send({action: 'loadCompiler', requirePath: 'solc'});
}
self.events.request("version:getPackagePath", "solc", solcVersion, function(err, path) {
if (err) {
return done(err);
}
let requirePath = fs.dappPath(location);
solcProcess.send({action: 'loadCompiler', solcLocation: requirePath});
let requirePath = fs.dappPath(path);
self.solcProcess.send({action: 'installAndLoadCompiler', solcVersion: solcVersion, packagePath: requirePath});
});
}
});
});
this.solcProcess.once("result", "loadedCompiler", () => {
self.compilerLoaded = true;
done();
});
this.solcProcess.send({action: "init", options: {logger: self.logger, showSpinner: !self.useDashboard}});
if (this.ipc.isServer()) {
this.ipc.on('compile', self.compile.bind(this));
}
}
isCompilerLoaded() {
return (compilerLoaded === true);
return (this.compilerLoaded === true);
}
compile(jsonObj, done) {
solcProcess.once('message', function (msg) {
if (msg.result !== 'compilation') {
return;
const id = uuid();
if (this.ipc.isClient() && this.ipc.connected) {
return this.ipc.request('compile', jsonObj, done);
}
this.solcProcess.once('result', 'compilation-' + id, (msg) => {
if(msg.err) {
return done(msg.err);
}
done(JSON.parse(msg.output));
done(null, JSON.parse(msg.output));
});
solcProcess.send({action: 'compile', jsonObj: jsonObj});
this.solcProcess.send({action: 'compile', jsonObj: jsonObj, id});
}
}

View File

@ -0,0 +1,107 @@
const stringReplaceAsync = require('string-replace-async');
const async = require('async');
class SpecialConfigs {
constructor(embark, options) {
this.logger = embark.logger;
this.events = embark.events;
this.buildDir = options.buildDir;
this.embark = embark;
this.contractsConfig = embark.config.contractsConfig;
this.registerAfterDeployAction();
this.registerOnDeployAction();
}
replaceWithAddresses(cmd, cb) {
const self = this;
let regex = /\$\w+/g;
stringReplaceAsync.seq(cmd, regex, (match) => {
return (new Promise((resolve, reject) => {
let referedContractName = match.slice(1);
self.events.request('contracts:contract', referedContractName, (referedContract) => {
if (!referedContract) {
self.logger.error(referedContractName + ' does not exist');
self.logger.error("error running cmd: " + cmd);
return reject(new Error("ReferedContractDoesNotExist"));
}
if (referedContract && referedContract.deploy === false) {
self.logger.error(referedContractName + " exists but has been set to not deploy");
self.logger.error("error running cmd: " + cmd);
return reject(new Error("ReferedContracSetToNotdeploy"));
}
if (referedContract && !referedContract.deployedAddress) {
self.logger.error("couldn't find a valid address for " + referedContractName + ". has it been deployed?");
self.logger.error("error running cmd: " + cmd);
return reject(new Error("ReferedContractAddressNotFound"));
}
return resolve(referedContract.deployedAddress);
});
}));
}).then((result) => {
cb(null, result);
}).catch(cb);
}
registerAfterDeployAction() {
const self = this;
this.embark.registerActionForEvent("contracts:deploy:afterAll", (cb) => {
let afterDeployCmds = self.contractsConfig.afterDeploy || [];
async.mapLimit(afterDeployCmds, 1, (cmd, nextMapCb) => {
self.replaceWithAddresses(cmd, nextMapCb);
}, (err, onDeployCode) => {
if (err) {
self.logger.trace(err);
return cb(new Error("error running afterDeploy"));
}
self.runOnDeployCode(onDeployCode, cb);
});
});
}
runOnDeployCode(onDeployCode, callback) {
const self = this;
async.each(onDeployCode, (cmd, eachCb) => {
self.logger.info("==== executing: " + cmd);
self.events.request('runcode:eval', cmd, (err) => {
if (err && err.message.indexOf("invalid opcode") >= 0) {
self.logger.error('the transaction was rejected; this usually happens due to a throw or a require, it can also happen due to an invalid operation');
}
eachCb(err);
});
}, callback);
}
registerOnDeployAction() {
const self = this;
this.embark.registerActionForEvent("deploy:contract:deployed", (params, cb) => {
let contract = params.contract;
if (!contract.onDeploy) {
return cb();
}
self.logger.info(__('executing onDeploy commands'));
let onDeployCmds = contract.onDeploy;
async.mapLimit(onDeployCmds, 1, (cmd, nextMapCb) => {
self.replaceWithAddresses(cmd, nextMapCb);
}, (err, onDeployCode) => {
if (err) {
return cb(new Error("error running onDeploy for " + contract.className.cyan));
}
self.runOnDeployCode(onDeployCode, cb);
});
});
}
}
module.exports = SpecialConfigs;

View File

@ -0,0 +1,36 @@
/* global EmbarkJS */
import {findSeries} from 'p-iteration';
let __embarkStorage = {};
__embarkStorage.setProviders = async function (dappConnOptions) {
try {
let workingConnFound = await findSeries(dappConnOptions, async (dappConn) => {
if(dappConn === '$BZZ' || dappConn.provider === 'swarm'){
let options = dappConn;
if(dappConn === '$BZZ') options = {"useOnlyGivenProvider": true};
try{
await EmbarkJS.Storage.setProvider('swarm', options);
let isAvailable = await EmbarkJS.Storage.isAvailable();
return isAvailable;
}catch(err){
return false; // catch errors for when bzz object not initialised but config has requested it to be used
}
}
else if(dappConn.provider === 'ipfs') {
// set the provider then check the connection, if true, use that provider, else, check next provider
try{
await EmbarkJS.Storage.setProvider('ipfs', dappConn);
let isAvailable = await EmbarkJS.Storage.isAvailable();
return isAvailable;
} catch(err) {
return false;
}
}
});
if(!workingConnFound) throw new Error('Could not connect to a storage provider using any of the dappConnections in the storage config');
} catch (err) {
throw new Error('Failed to connect to a storage provider: ' + err.message);
}
};

View File

@ -0,0 +1,224 @@
const utils = require('../../utils/utils.js');
const fs = require('../../core/fs.js');
const _ = require('underscore');
const async = require('async');
const StorageProcessesLauncher = require('../../processes/storageProcesses/storageProcessesLauncher');
const constants = require('../../constants');
class Storage {
constructor(embark, options){
this._embark = embark;
this._options = options;
this._storageConfig = options.storageConfig;
this._webServerConfig = options.webServerConfig;
this._blockchainConfig = options.blockchainConfig;
this._servicesMonitor = options.servicesMonitor;
this._events = options.events;
this._logger = options.logger;
// filter list of dapp connections based on available_providers set in config
let hasSwarm = _.contains(this._storageConfig.available_providers, 'swarm'); // don't need to eval this in every loop iteration
// contains valid dapp storage providers
this._validDappProviders = _.filter(this._storageConfig.dappConnection, (conn) => {
return _.contains(this._storageConfig.available_providers, conn.provider) || (conn === '$BZZ' && hasSwarm);
});
this.initStorageForEmbark();
this.initStorageForDapp();
// don't start storage processes on build command, only on upload or run
if(_.contains(options.context, constants.contexts.upload) || _.contains(options.context, constants.contexts.run)){
this.startStorageProcesses();
}
}
_checkStorageEndpoint(platform, callback) {
let checkFn;
let self = this;
self._logger.trace(`Storage module: Checking ${platform} availability...`);
_.find(self._servicesMonitor.checkList, (value, key) => {
if(key.toLowerCase() === platform.toLowerCase()){
checkFn = value;
return true;
}
});
if (!checkFn || typeof checkFn.fn !== 'function') {
self._logger.trace(`Storage module: Check for ${platform} node does not exist.`);
return callback();
}
checkFn.fn(function (serviceCheckResult) {
if (!serviceCheckResult.status || serviceCheckResult.status === 'off') {
self._logger.trace(`Storage module: ${platform} node not available.`);
return callback('No node');
}
callback();
});
}
_startStorageNode(platform, callback) {
let self = this;
const storageProcessesLauncher = new StorageProcessesLauncher({
logger: self._logger,
events: self._events,
storageConfig: self._storageConfig,
webServerConfig: self._webServerConfig,
blockchainConfig: self._blockchainConfig
});
self._logger.trace(`Storage module: Launching ${platform} process...`);
return storageProcessesLauncher.launchProcess(platform.toLowerCase(), (err) => {
if (err) {
return callback(err);
}
callback();
});
}
/// Initializes a storage provider for Embark upload
initStorageForEmbark(){
let storageProviderCls = require(`../${this._storageConfig.upload.provider}/index.js`);
let uploadProvider = new storageProviderCls(this._embark, this._options); /*eslint no-new: "off"*/
if(typeof uploadProvider.commandlineDeploy == 'function') uploadProvider.commandlineDeploy();
if(typeof uploadProvider.setServiceCheck == 'function') uploadProvider.setServiceCheck();
if(typeof uploadProvider.addObjectToConsole == 'function') uploadProvider.addObjectToConsole();
}
/**
* Initializes a storage provider for EmbarkJS
*
* @return {void}
*/
initStorageForDapp(){
// now we need to add instantiate any dappConnection/available_providers storage providers to add
// their provider code to embarkjs
this._validDappProviders.forEach(dappConn => {
if(!dappConn.provider) return;
let storageProviderCls = require(`../${dappConn.provider}/index.js`);
// override options with dappConnection settings
let storageOptions = this._options;
storageOptions.protocol = dappConn.protocol;
storageOptions.host = dappConn.host;
storageOptions.port = dappConn.port;
// then instantiate the storage provdier class
let storageProvider = new storageProviderCls(this._embark, storageOptions); /*eslint no-new: "off"*/
// register the service check so we can use it to check if the process is running before spawning it
// check that it hasn't already been done above
if(dappConn.provider !== this._storageConfig.upload.provider){
if(typeof storageProvider.setServiceCheck == 'function') storageProvider.setServiceCheck();
}
// add __embarkSwarm and __embarkIPFS objects to EmbarkJS
if(typeof storageProvider.addProviderToEmbarkJS == 'function') storageProvider.addProviderToEmbarkJS();
});
// add the storage provider code (__embarkStorage) to embarkjs
this.addProviderToEmbarkJS();
// add the code to call setProviders in embarkjs after embark is ready
this.addSetProviders();
}
/**
* Adds the storage provider code (__embarkStorage) to embarkjs
*
* @returns {void}
*/
addProviderToEmbarkJS(){
// TODO: make this a shouldAdd condition
if (this._storageConfig === {} || !this._storageConfig.dappConnection || !this._storageConfig.dappConnection.length) {
return;
}
let code = "\n" + fs.readFileSync(utils.joinPath(__dirname, 'embarkjs.js')).toString();
this._embark.addCodeToEmbarkJS(code);
}
/**
* Adds the code to call setProviders in embarkjs after embark is ready
*
* @returns {void}
*/
addSetProviders() {
let code = `\n__embarkStorage.setProviders(${JSON.stringify(this._validDappProviders)});`;
let shouldInit = (storageConfig) => {
return (this._validDappProviders !== undefined && this._validDappProviders.length > 0 && storageConfig.enabled === true);
};
this._embark.addProviderInit('storage', code, shouldInit);
}
checkStorageService(platform, url, callback) {
const self = this;
// start the upload storage node
self._checkStorageEndpoint(platform, function (err) {
if (!err) {
return callback(null);
}
self._startStorageNode(platform, (err) => {
if (err) {
return callback(err);
}
// Check endpoint again to see if really did start
self._checkStorageEndpoint(platform, (err) => {
if (err) {
return callback(err);
}
callback(null);
});
});
});
}
startStorageProcesses(){
let platform = this._storageConfig.upload.provider;
let self = this;
let withErrors = false;
async.waterfall([
function _checkStorageService(callback){
self.checkStorageService(platform, utils.buildUrlFromConfig(self._storageConfig.upload), (err) => {
// log error and continue
if(err){
self._logger.error(err);
withErrors = true;
}
callback(null);
});
},
function checkDappConnectionStorageService(callback){
// start any dappConnection storage nodes
async.each(self._validDappProviders, function(dappConn, cb) {
if(!dappConn.provider || dappConn.provider === platform) {
return cb(null);
} // don't start the process we've just started above
self.checkStorageService(dappConn.provider, utils.buildUrlFromConfig(dappConn), (err) => {
// log error and continue
if(err){
self._logger.error(err);
withErrors = true;
}
cb(null);
});
}, callback);
}
], function (){
let strComplete = __('Finished starting all storage providers');
if(withErrors){
strComplete += ', ' + __('with errors.');
return self._logger.warn(strComplete);
}
self._logger.info(strComplete + '.');
});
}
}
module.exports = Storage;

View File

@ -3,36 +3,47 @@ let __embarkSwarm = {};
const bytes = require("eth-lib/lib/bytes");
__embarkSwarm.setProvider = function (options) {
this.bzz = web3.bzz;
this.protocol = options.protocol;
this.host = options.host;
this.port = options.port;
this.connectUrl = `${options.protocol}://${options.host}:${options.port}`;
this.connectError = new Error(`Cannot connect to Swarm node on ${this.connectUrl}`);
this._getUrl = options.getUrl || `${this.connectUrl}/bzzr:/`;
let protocol = options.protocol || 'http';
let port = options.port ? `:${options.port}` : '';
this._config = options;
this._connectUrl = `${protocol}://${options.host}${port}`;
this._connectError = new Error(`Cannot connect to Swarm node on ${this._connectUrl}`);
return new Promise((resolve, reject) => {
try {
if (!this.bzz.currentProvider) {
this.bzz.setProvider(`${options.protocol}://${options.host}:${options.port}`);
if (!web3.bzz.currentProvider && !options.useOnlyGivenProvider) {
web3.bzz.setProvider(this._connectUrl);
}
else if(options.useOnlyGivenProvider && web3.bzz.givenProvider !== null){
web3.bzz.setProvider(web3.bzz.givenProvider);
}
resolve(this);
} catch (err) {
console.log(err);
reject(this.connectError);
reject(this._connectError);
}
});
};
__embarkSwarm.isAvailable = function () {
return new Promise((resolve, reject) => {
if (!this.bzz) {
// if web3 swarm object doesn't exist
if (!web3.bzz) {
return resolve(false);
}
this.bzz.isAvailable()
// swarm obj exists, but has no provider set (seems to happen a LOT!),
// try setting the provider to our currently set provider again
else if(!web3.bzz.currentProvider && this._config.host){
web3.bzz.setProvider(this._connectUrl);
}
if (!web3.bzz.currentProvider) {
return resolve(false);
}
web3.bzz.isAvailable()
.then(resolve)
.catch(() => {
reject(this.connectError);
reject(this._connectError);
});
});
};
@ -41,9 +52,9 @@ __embarkSwarm.saveText = function (text) {
return new Promise((resolve, reject) => {
this.isAvailable().then((isAvailable) => {
if (!isAvailable) {
return reject(this.connectError);
return reject(this._connectError);
}
this.bzz.upload(text)
web3.bzz.upload(text)
.then(resolve)
.catch(reject);
}).catch(reject);
@ -54,9 +65,9 @@ __embarkSwarm.get = function (hash) {
return new Promise((resolve, reject) => {
this.isAvailable().then((isAvailable) => {
if (!isAvailable) {
return reject(this.connectError);
return reject(this._connectError);
}
this.bzz.download(hash)
web3.bzz.download(hash)
.then((uint8Array) => resolve(bytes.toString(bytes.fromUint8Array(uint8Array))))
.catch(reject);
}).catch(reject);
@ -76,9 +87,9 @@ __embarkSwarm.uploadFile = function (inputSelector) {
const fileContent = new Uint8Array(event.target.result);
this.isAvailable().then((isAvailable) => {
if (!isAvailable) {
return reject(this.connectError);
return reject(this._connectError);
}
this.bzz.upload(fileContent)
web3.bzz.upload(fileContent)
.then(resolve)
.catch(reject);
}).catch(reject);
@ -89,6 +100,6 @@ __embarkSwarm.uploadFile = function (inputSelector) {
};
__embarkSwarm.getUrl = function (hash) {
return this._getUrl + hash;
return `${this._config.getUrl || this._connectUrl + '/bzz:/'}${hash}`;
};

View File

@ -1,6 +1,8 @@
let UploadSwarm = require('./upload.js');
let utils = require('../../utils/utils.js');
let fs = require('../../core/fs.js');
const UploadSwarm = require('./upload.js');
const utils = require('../../utils/utils.js');
const fs = require('../../core/fs.js');
const Web3Bzz = require('web3-bzz');
const _ = require('underscore');
class Swarm {
@ -8,37 +10,29 @@ class Swarm {
this.logger = embark.logger;
this.events = embark.events;
this.buildDir = options.buildDir;
this.storageConfig = options.storageConfig;
this.storageConfig = embark.config.storageConfig;
this.host = options.host || this.storageConfig.host;
this.port = options.port || this.storageConfig.port;
this.addCheck = options.addCheck;
this.embark = embark;
this.bzz = options.bzz;
this.initSwarmProvider();
this.commandlineDeploy();
this.setServiceCheck();
this.addSwarmToEmbarkJS();
this.addSetProvider();
}
this.providerUrl = utils.buildUrl(options.protocol || options.storageConfig.upload.protocol, options.host || options.storageConfig.upload.host, options.port || options.storageConfig.upload.port);
initSwarmProvider(){
if(!this.bzz.currentProvider) {
this.bzz.setProvider(`http://${this.host}:${this.port}`);
}
this.getUrl = options.storageConfig.upload.getUrl || this.providerUrl + '/bzz:/';
this.bzz = new Web3Bzz(this.providerUrl);
}
commandlineDeploy() {
this.upload_swarm = new UploadSwarm({
buildDir: this.buildDir || 'dist/',
storageConfig: this.storageConfig,
getUrl: this.getUrl,
bzz: this.bzz
});
this.embark.registerUploadCommand('swarm', this.upload_swarm.deploy.bind(this.upload_swarm));
}
setServiceCheck() {
let self = this;
@ -47,26 +41,22 @@ class Swarm {
if (!storageConfig.enabled) {
return;
}
if (storageConfig.provider !== 'swarm' && storageConfig.available_providers.indexOf("swarm") < 0) {
if (_.findWhere(this.storageConfig.dappConnection, {'provider': 'swarm'}) === undefined && (storageConfig.upload.provider !== 'swarm' || storageConfig.available_providers.indexOf("swarm") < 0)) {
return;
}
this.events.on('check:backOnline:Swarm', function () {
self.logger.info('Swarm node detected...');
self.logger.info(__('Swarm node detected...'));
});
this.events.on('check:wentOffline:Swarm', function () {
self.logger.info('Swarm node is offline...');
self.logger.info(__('Swarm node is offline...'));
});
if (!this.addCheck) {
return;
}
// add check for console
this.addCheck('Swarm', function(cb){
self.logger.trace("Checking Swarm availability...");
self.events.request("services:register", 'Swarm', function(cb){
self.logger.trace(`Checking Swarm availability on ${self.bzz.currentProvider}...`);
self.bzz.isAvailable().then(result => {
self.logger.trace("Swarm " + (result ? '':'un') + "available");
return cb({name: "Swarm ", status: result ? 'on':'off'});
}).catch(err => {
self.logger.trace("Check Swarm availability error: " + err);
@ -75,38 +65,35 @@ class Swarm {
});
}
addSwarmToEmbarkJS() {
addProviderToEmbarkJS() {
let self = this;
// TODO: make this a shouldAdd condition
if (this.storageConfig === {}) {
return;
}
if ((this.storageConfig.available_providers.indexOf('swarm') < 0) && (this.storageConfig.provider !== 'swarm' || this.storageConfig.enabled !== true)) {
if (this.storageConfig.available_providers.indexOf('swarm') < 0 || _.findWhere(this.storageConfig.dappConnection, {'provider': 'swarm'}) === undefined || this.storageConfig.enabled !== true) {
return;
}
this.events.request("version:get:p-iteration", function(pIterationVersion) {
let currentPIterationVersion = require('../../../package.json').dependencies["p-iteration"];
if (pIterationVersion !== currentPIterationVersion) {
self.events.request("version:getPackageLocation", "p-iteration", pIterationVersion, function(err, location) {
if(err){
return self.logger.error("Error getting package location for p-iteration: " + err);
}
self.embark.registerImportFile("p-iteration", fs.dappPath(location));
});
}
});
let code = "";
code += "\n" + fs.readFileSync(utils.joinPath(__dirname, 'embarkjs.js')).toString();
code += "\nEmbarkJS.Storage.registerProvider('swarm', __embarkSwarm);";
this.embark.addCodeToEmbarkJS(code);
}
addSetProvider() {
let config = JSON.stringify({
host: this.storageConfig.host,
port: this.storageConfig.port,
protocol: this.storageConfig.protocol,
getUrl: this.storageConfig.getUrl
});
let code = "\nEmbarkJS.Storage.setProvider('swarm'," + config + ");";
let shouldInit = (storageConfig) => {
return (storageConfig.provider === 'swarm' && storageConfig.enabled === true);
};
this.embark.addProviderInit('storage', code, shouldInit);
}
}
module.exports = Swarm;

View File

@ -7,16 +7,17 @@ class Swarm {
this.buildDir = options.buildDir || 'dist/';
this.bzz = options.bzz;
this.storageConfig = options.storageConfig;
this.getUrl = options.getUrl;
}
deploy() {
return new Promise((resolve, reject) => {
console.log("deploying to swarm!");
console.log(__("deploying to swarm!"));
let self = this;
let bzz = this.bzz;
async.waterfall([
function runCommand(callback) {
console.log(("=== adding " + self.buildDir + " to swarm").green);
console.log(("=== " + __("adding %s to swarm", self.buildDir)).green);
bzz.upload({
path: self.buildDir, // path to data / file / directory
kind: "directory", // could also be "file" or "data"
@ -31,17 +32,18 @@ class Swarm {
if (!dir_hash) {
return callback('No directory hash was returned');
}
console.log((`=== DApp available at ${self.storageConfig.getUrl}${dir_hash}/`).green);
console.log(("=== " + __("DApp available at") + ` ${self.getUrl}${dir_hash}/`).green);
console.log(("=== " + __("DApp available at") + ` http://swarm-gateways.net/bzz:/${dir_hash}`).green);
callback();
}
], function (err, _result) {
if (err) {
console.log("error uploading to swarm".red);
console.log(__("error uploading to swarm").red);
console.log(err);
return reject(err);
}
resolve('successfully uploaded to swarm');
resolve(__('successfully uploaded to swarm'));
});
});
}

View File

@ -4,10 +4,10 @@ const path = require('path');
class Vyper {
constructor(embark, options) {
constructor(embark, _options) {
this.logger = embark.logger;
this.events = embark.events;
this.contractDirectories = options.contractDirectories;
this.contractDirectories = embark.config.contractDirectories;
// FIXME: Use array of extensions instead of duplicatiing
embark.registerCompiler(".py", this.compile_vyper.bind(this));
@ -21,10 +21,10 @@ class Vyper {
return callback(stderr);
}
if (code !== 0) {
return callback(`Vyper exited with error code ${code}`);
return callback(__('Vyper exited with error code ') + code);
}
if (!stdout) {
return callback('Execution returned no result');
return callback(__('Execution returned no result'));
}
callback(null, stdout.replace(/\n/g, ''));
});
@ -35,7 +35,15 @@ class Vyper {
if (!contractFiles || !contractFiles.length) {
return cb();
}
self.logger.info("compiling Vyper contracts...");
const vyper = shelljs.which('vyper');
if (!vyper) {
self.logger.warn(__('%s is not installed on your machine', 'Vyper'));
self.logger.info(__('You can install it by visiting: %s', 'https://vyper.readthedocs.io/en/latest/installing-vyper.html'));
return cb();
}
self.logger.info(__("compiling Vyper contracts") + "...");
const compiled_object = {};
async.each(contractFiles,
function (file, fileCb) {

View File

@ -7,7 +7,6 @@ class WebServer {
this.embark = embark;
this.logger = embark.logger;
this.events = embark.events;
this.addCheck = options.addCheck;
this.webServerConfig = embark.config.webServerConfig;
if (!this.webServerConfig.enabled) {
return;
@ -16,7 +15,7 @@ class WebServer {
this.host = options.host || this.webServerConfig.host;
this.port = options.port || this.webServerConfig.port;
this.events.emit("status", "Starting Server");
this.events.emit("status", __("Starting Server"));
this.server = new Server({logger: this.logger, host: this.host, port: this.port});
this.setServiceCheck();
@ -29,17 +28,16 @@ class WebServer {
setServiceCheck() {
let url = 'http://' + this.host + ':' + this.port;
//embark.registerServiceCheck('WebserverService', function (cb) {
this.addCheck('Webserver', function (cb) {
this.events.request("services:register", 'Webserver', function (cb) {
utils.checkIsAvailable(url, function (available) {
let devServer = 'Webserver (' + url + ')';
let devServer = __('Webserver') + ' (' + url + ')';
let serverStatus = (available ? 'on' : 'off');
return cb({name: devServer, status: serverStatus});
});
});
this.events.on('check:wentOffline:Webserver', () => {
this.logger.info("Webserver is offline");
this.logger.info(__("Webserver is offline"));
});
}
@ -57,7 +55,7 @@ class WebServer {
}
if (cmd === 'webserver stop') {
self.events.request("stop-webserver");
return "stopping webserver...";
return __("stopping webserver") + "...";
}
return false;
});

View File

@ -13,7 +13,7 @@ class Server {
start(callback) {
if (this.server && this.server.listening) {
this.logger.warn("a webserver is already running at " + ("http://" + this.hostname + ":" + this.port).bold.underline.green);
this.logger.warn(__("a webserver is already running at") + " " + ("http://" + this.hostname + ":" + this.port).bold.underline.green);
if (callback) {
callback();
}
@ -25,7 +25,7 @@ class Server {
serve(req, res, finalhandler(req, res));
}).withShutdown();
this.logger.info("webserver available at " + ("http://" + this.hostname + ":" + this.port).bold.underline.green);
this.logger.info(__("webserver available at") + " " + ("http://" + this.hostname + ":" + this.port).bold.underline.green);
this.server.listen(this.port, this.hostname);
if (callback) {
callback();
@ -34,7 +34,7 @@ class Server {
stop(callback) {
if (!this.server || !this.server.listening) {
this.logger.warn("no webserver is currently running");
this.logger.warn(__("no webserver is currently running"));
if (callback) {
callback();
}

View File

@ -1,24 +1,34 @@
let utils = require('../../utils/utils.js');
let fs = require('../../core/fs.js');
let Web3 = require('web3');
class Whisper {
constructor(embark, options) {
constructor(embark, _options) {
this.logger = embark.logger;
this.events = embark.events;
this.communicationConfig = options.communicationConfig;
this.addCheck = options.addCheck;
this.web3 = options.web3;
this.communicationConfig = embark.config.communicationConfig;
this.web3 = new Web3();
this.embark = embark;
this.connectToProvider();
this.setServiceCheck();
this.addWhisperToEmbarkJS();
this.addSetProvider();
}
connectToProvider() {
let {host, port} = this.communicationConfig.connection;
let web3Endpoint = 'ws://' + host + ':' + port;
this.web3.setProvider(new Web3.providers.WebsocketProvider(web3Endpoint, {headers: {Origin: "http://localhost:8000"}}));
}
setServiceCheck() {
const self = this;
self.addCheck('Whisper', function (cb) {
self.events.request("services:register", 'Whisper', function (cb) {
if (!self.web3.currentProvider || self.web3.currentProvider.connection.readyState !== 1) {
return self.connectToProvider();
}
self.web3.shh.getVersion(function (err, version) {
if (err || version == "2") {
return cb({name: 'Whisper', status: 'off'});

View File

@ -1,7 +1,9 @@
let fs = require('../core/fs.js');
let async = require('async');
var utils = require('../utils/utils.js');
const webpack = require("webpack");
const fs = require('../core/fs.js');
const async = require('async');
const ProcessLauncher = require('../process/processLauncher');
const utils = require('../utils/utils.js');
const constants = require('../constants');
require("babel-preset-react");
require("babel-preset-es2015");
@ -17,153 +19,183 @@ class Pipeline {
this.events = options.events;
this.logger = options.logger;
this.plugins = options.plugins;
this.pipelinePlugins = this.plugins.getPluginsFor('pipeline');
}
build(abi, contractsJSON, path, callback) {
let self = this;
const importsList = {};
let placeholderPage;
this.buildContracts(contractsJSON);
async.waterfall([
function createPlaceholderPage(next){
self.events.request('embark-building-placeholder', (html) => {
fs.mkdirpSync(self.buildDir); // create dist/ folder if not already exists
fs.writeFile(self.buildDir + 'index.html', html, next);
});
},
function buildTheContracts(next) {
self.buildContracts(next);
},
function buildWeb3(next) {
self.buildWeb3JS(next);
},
function createImportList(next) {
importsList["Embark/EmbarkJS"] = fs.dappPath(".embark", 'embark.js');
importsList["Embark/web3"] = fs.dappPath(".embark", 'web3_instance.js');
importsList["Embark/contracts"] = fs.dappPath(".embark/contracts", '');
self.buildWeb3JS(function() {
let importsList = {};
importsList["Embark/EmbarkJS"] = fs.dappPath(".embark", 'embark.js');
importsList["Embark/web3"] = fs.dappPath(".embark", 'web3_instance.js');
self.plugins.getPluginsProperty('imports', 'imports').forEach(function (importObject) {
self.plugins.getPluginsProperty('imports', 'imports').forEach(function (importObject) {
let [importName, importLocation] = importObject;
importsList[importName] = importLocation;
});
for (let contractName in contractsJSON) {
let contractCode = self.buildContractJS(contractName);
let filePath = fs.dappPath(".embark", contractName + '.js');
fs.writeFileSync(filePath, contractCode);
importsList["Embark/contracts/" + contractName] = filePath;
}
// limit:1 due to issues when downloading required files such as web3.js
async.eachOfLimit(self.assetFiles, 1, function (files, targetFile, cb) {
// limit:1 due to issues when downloading required files such as web3.js
async.mapLimit(files, 1,
function(file, fileCb) {
self.logger.trace("reading " + file.filename);
if (file.filename.indexOf('.js') >= 0) {
let realCwd;
async.waterfall([
function findImports(next) {
self.webpackRun(file.filename, {}, false, importsList, false, next);
},
function changeCwd(next) {
realCwd = utils.pwd();
process.chdir(fs.embarkPath(''));
next();
},
//function findImportsPhase2(next) {
// console.log("====> findImports_2");
// self.webpackRun(file.filename, {
// externals: function(context, request, callback) {
// if (request === utils.joinPath(fs.dappPath(), file.filename)) {
// callback();
// } else {
// //if (request.indexOf('Embark/contracts/') === 0) {
// // let contractName = request.split('/')[2];
// // let contractCode = self.buildContractJS(contractName);
// // let filePath = utils.joinPath(fs.dappPath(), ".embark", contractName + '.js');
// // fs.writeFileSync(filePath, contractCode);
// // importsList[request] = filePath;
// //}
// callback(null, "amd " + Math.random());
// }
// }
// }, true, importsList, next);
//},
function runWebpack(next) {
self.webpackRun(file.filename, {}, true, importsList, true, next);
},
function changeCwdBack(next) {
process.chdir(realCwd);
next();
}
], function(err, _result) {
if (err) {
process.chdir(realCwd);
self.logger.error(err);
return fileCb(err);
}
if (!fs.existsSync('./.embark/' + file.filename)) {
self.logger.error("couldn't find file: " + file.filename);
return fileCb("couldn't find file: " + file.filename);
}
let fileContent = fs.readFileSync('./.embark/' + file.filename).toString();
self.runPlugins(file, fileContent, fileCb);
});
} else {
file.content(function(fileContent) {
self.runPlugins(file, fileContent, fileCb);
});
}
},
function (err, contentFiles) {
if (err) {
self.logger.warn('errors found while generating ' + targetFile);
}
let dir = targetFile.split('/').slice(0, -1).join('/');
self.logger.trace("creating dir " + self.buildDir + dir);
fs.mkdirpSync(self.buildDir + dir);
// if it's a directory
if (targetFile.slice(-1) === '/' || targetFile.indexOf('.') === -1) {
let targetDir = targetFile;
if (targetDir.slice(-1) !== '/') {
targetDir = targetDir + '/';
}
contentFiles.map(function (file) {
let filename = file.filename.replace(file.basedir + '/', '');
self.logger.info("writing file " + (self.buildDir + targetDir + filename).bold.dim);
fs.copySync(file.path, self.buildDir + targetDir + filename, {overwrite: true});
});
} else {
let content = contentFiles.map(function (file) {
if (file === undefined) {
return "";
}
return file.content;
}).join("\n");
self.logger.info("writing file " + (self.buildDir + targetFile).bold.dim);
fs.writeFileSync(self.buildDir + targetFile, content);
}
cb();
}
);
},
function (_err, _results) {
callback();
});
});
next();
},
function writeContracts(next) {
self.events.request('contracts:list', (_err, contracts) => {
// ensure the .embark/contracts directory exists (create if not exists)
fs.mkdirp(fs.dappPath(".embark/contracts", ''), (err) => {
if(err) return next(err);
// Create a file .embark/contracts/index.js that requires all contract files
// Used to enable alternate import syntax:
// e.g. import {Token} from 'Embark/contracts'
// e.g. import * as Contracts from 'Embark/contracts'
let importsHelperFile = fs.createWriteStream(fs.dappPath(".embark/contracts", 'index.js'));
importsHelperFile.write('module.exports = {\n');
async.eachOf(contracts, (contract, idx, eachCb) => {
self.events.request('code-generator:contract', contract.className, (contractCode) => {
let filePath = fs.dappPath(".embark/contracts", contract.className + '.js');
importsList["Embark/contracts/" + contract.className] = filePath;
fs.writeFile(filePath, contractCode, eachCb);
// add the contract to the exports list to support alternate import syntax
importsHelperFile.write(`"${contract.className}": require('./${contract.className}').default`);
if(idx < contracts.length - 1) importsHelperFile.write(',\n'); // add a comma if we have more contracts to add
});
}, function(){
importsHelperFile.write('\n}'); // close the module.exports = {}
importsHelperFile.close(next); // close the write stream
});
});
});
},
function assetFileWrite(next) {
async.eachOf(self.assetFiles, function (files, targetFile, cb) {
async.map(files,
function (file, fileCb) {
self.logger.trace("reading " + file.filename);
// Not a JS file
if (file.filename.indexOf('.js') < 0) {
return file.content(function (fileContent) {
self.runPlugins(file, fileContent, fileCb);
});
}
// JS files
async.waterfall([
function runWebpack(next) {
const webpackProcess = new ProcessLauncher({
modulePath: utils.joinPath(__dirname, 'webpackProcess.js'),
logger: self.logger,
events: self.events
});
webpackProcess.send({action: constants.pipeline.init, options: {}});
webpackProcess.send({action: constants.pipeline.build, file, importsList});
webpackProcess.once('result', constants.pipeline.built, (msg) => {
webpackProcess.kill();
return next(msg.error);
});
},
function readFile(next) {
fs.readFile('./.embark/' + file.filename, (err, data) => {
if (err) {
return next(err);
}
next(null, data.toString());
});
},
function runPluginsOnContent(fileContent, next) {
self.runPlugins(file, fileContent, next);
}
], function (err, contentFile) {
if (err) {
self.logger.error(err);
return fileCb(err);
}
fileCb(null, contentFile);
});
},
function (err, contentFiles) {
if (err) {
self.logger.error(__('errors found while generating') + ' ' + targetFile);
}
let dir = targetFile.split('/').slice(0, -1).join('/');
self.logger.trace("creating dir " + self.buildDir + dir);
fs.mkdirpSync(self.buildDir + dir);
// if it's a directory
if (targetFile.slice(-1) === '/' || targetFile.indexOf('.') === -1) {
let targetDir = targetFile;
if (targetDir.slice(-1) !== '/') {
targetDir = targetDir + '/';
}
async.each(contentFiles, function (file, mapCb) {
let filename = file.filename.replace(file.basedir + '/', '');
self.logger.info("writing file " + (self.buildDir + targetDir + filename).bold.dim);
fs.copy(file.path, self.buildDir + targetDir + filename, {overwrite: true}, mapCb);
}, cb);
return;
}
let content = contentFiles.map(function (file) {
if (file === undefined) {
return "";
}
return file.content;
}).join("\n");
self.logger.info(__("writing file") + " " + (self.buildDir + targetFile).bold.dim);
if(new RegExp(/^index.html?/i).test(targetFile)){
targetFile = targetFile.replace('index', 'index-temp');
placeholderPage = targetFile;
}
fs.writeFile(self.buildDir + targetFile, content, cb);
}
);
},
next);
},
function removePlaceholderPage(next){
let placeholderFile = self.buildDir + placeholderPage;
fs.access(self.buildDir + placeholderPage, (err) => {
if (err) return next(); // index-temp doesn't exist, do nothing
// rename index-temp.htm/l to index.htm/l, effectively replacing our placeholder page
// with the contents of the built index.html page
fs.move(placeholderFile, placeholderFile.replace('index-temp', 'index'), {overwrite: true}, next);
});
}
], callback);
}
runPlugins(file, fileContent, fileCb) {
const self = this;
let pipelinePlugins = self.plugins.getPluginsFor('pipeline');
if (pipelinePlugins.length <= 0) {
if (self.pipelinePlugins.length <= 0) {
return fileCb(null, {content: fileContent, filename: file.filename, path: file.path, basedir: file.basedir, modified: true});
}
async.eachSeries(pipelinePlugins,
async.eachSeries(self.pipelinePlugins,
function(plugin, pluginCB) {
if (file.options && file.options.skipPipeline) {
return pluginCB();
@ -182,140 +214,44 @@ class Pipeline {
);
}
webpackRun(filename, options, includeModules, importsList, detectErrors, callback) {
let defaultOptions = {
entry: fs.dappPath(filename),
output: {
libraryTarget: 'umd',
path: fs.dappPath('.embark'),
filename: filename
buildContracts(cb) {
const self = this;
async.waterfall([
function makeDirectory(next) {
fs.mkdirp(fs.dappPath(self.buildDir, 'contracts'), (err, _result) => {
next(err);
});
},
resolve: {
alias: importsList,
modules: [
fs.embarkPath('node_modules'),
fs.dappPath('node_modules')
]
function getContracts(next) {
self.events.request('contracts:list', (err, contracts) => {
next(err, contracts);
});
},
externals: function(context, request, callback) {
callback();
function writeContractsJSON(contracts, next) {
async.each(contracts, (contract, eachCb) => {
fs.writeJson(fs.dappPath(self.buildDir, 'contracts', contract.className + ".json"), contract, {spaces: 2}, eachCb);
}, () => { next(); });
}
};
let webpackOptions = utils.recursiveMerge(defaultOptions, options);
if (includeModules) {
webpackOptions.module = {
rules: [
{
test: /\.css$/,
use: [{loader: "style-loader"}, {loader: "css-loader"}]
},
{
test: /\.scss$/,
use: [{loader: "style-loader"}, {loader: "css-loader"}]
},
{
test: /\.(png|woff|woff2|eot|ttf|svg)$/,
loader: 'url-loader?limit=100000'
},
{
test: /\.js$/,
loader: "babel-loader",
exclude: /(node_modules|bower_components)/,
options: {
presets: ['babel-preset-es2016', 'babel-preset-es2017', 'babel-preset-react'].map(require.resolve),
plugins: ["babel-plugin-webpack-aliases"].map(require.resolve),
compact: false
}
}
]
};
}
webpack(webpackOptions).run((_err, _stats) => {
if (!detectErrors) {
return callback();
}
if (_stats.hasErrors()) {
return callback(_stats.toJson().errors.join("\n"));
}
callback();
});
}
buildContracts(contractsJSON) {
fs.mkdirpSync(fs.dappPath(this.buildDir, 'contracts'));
for (let className in contractsJSON) {
let contract = contractsJSON[className];
fs.writeJSONSync(fs.dappPath(this.buildDir, 'contracts', className + ".json"), contract, {spaces: 2});
}
}
buildContractJS(contractName) {
let contractJSON = fs.readFileSync(fs.dappPath(this.buildDir, 'contracts', contractName + '.json')).toString();
let contractCode = "";
contractCode += "import web3 from 'Embark/web3';\n";
contractCode += "import EmbarkJS from 'Embark/EmbarkJS';\n";
contractCode += "let " + contractName + "JSONConfig = " + contractJSON + ";\n";
contractCode += "let " + contractName + " = new EmbarkJS.Contract(" + contractName + "JSONConfig);\n";
contractCode += "\n__embarkContext.execWhenReady(function() {\n";
contractCode += "\n" + contractName + ".setProvider(web3.currentProvider);\n";
contractCode += "\n" + contractName + ".options.from = web3.eth.defaultAccount;\n";
contractCode += "\n});\n";
contractCode += "export default " + contractName + ";\n";
return contractCode;
], cb);
}
buildWeb3JS(cb) {
const self = this;
let code = "";
async.waterfall([
function getWeb3Location(next) {
self.events.request("version:get:web3", function(web3Version) {
if (web3Version === "1.0.0-beta") {
return next(null, utils.joinPath(fs.embarkPath("js/web3-1.0.min.js")));
} else {
self.events.request("version:getPackageLocation", "web3", web3Version, function(err, location) {
return next(null, fs.dappPath(location));
});
}
function makeDirectory(next) {
fs.mkdirp(fs.dappPath(".embark"), (err, _result) => {
next(err);
});
},
function getImports(web3Location, next) {
web3Location = web3Location.replace(/\\/g, '/'); // Import paths must always have forward slashes
code += "\nimport Web3 from '" + web3Location + "';\n";
code += "\n if (typeof web3 !== 'undefined') {";
code += "\n } else {";
code += "\n var web3 = new Web3();\n";
code += "\n }";
self.events.request('provider-code', function(providerCode) {
code += providerCode;
code += "\nglobal.__embarkContext = __mainContext.__loadManagerInstance;\n";
code += "\nwindow.web3 = web3;\n";
code += "\nexport default web3;\n";
next();
});
function getWeb3Code(next) {
self.events.request('code-generator:web3js', next);
},
function writeFile(next) {
fs.mkdirpSync(fs.dappPath(".embark"));
fs.writeFileSync(fs.dappPath(".embark", 'web3_instance.js'), code);
next();
function writeFile(code, next) {
fs.writeFile(fs.dappPath(".embark", 'web3_instance.js'), code, next);
}
], function(_err, _result) {
cb();
});
], cb);
}
}
module.exports = Pipeline;

View File

@ -27,11 +27,11 @@ class Watch {
self.logger.trace('ready to watch contract changes');
});
this.watchConfigs(function () {
this.watchConfigs(embarkConfig, function () {
self.logger.trace('ready to watch config changes');
});
this.logger.info("ready to watch file changes");
this.logger.info(__("ready to watch file changes"));
}
restart() {
@ -41,10 +41,10 @@ class Watch {
stop() {
this.fileWatchers.forEach(fileWatcher => {
fileWatcher.close();
fileWatcher = null;
if (fileWatcher.shouldClose) return;
if (fileWatcher.isReady) fileWatcher.close();
fileWatcher.shouldClose = true;
});
this.fileWatchers = [];
}
watchAssets(embarkConfig, callback) {
@ -97,10 +97,19 @@ class Watch {
);
}
watchConfigs(callback) {
watchConfigs(embarkConfig, callback) {
let self = this;
this.watchFiles(
"config/**/contracts.json",
let contractsConfig;
if (typeof embarkConfig.config === 'object' || embarkConfig.config.contracts) {
contractsConfig = embarkConfig.config.contracts;
} else {
let contractsFolder = embarkConfig.config.replace(/\\/g, '/');
if (contractsFolder.charAt(contractsFolder.length - 1) !== '/') {
contractsFolder += '/';
}
contractsConfig = [`${contractsFolder}**/contracts.json`, `${contractsFolder}**/contracts.js`];
}
this.watchFiles(contractsConfig,
function (eventName, path) {
self.logger.info(`${eventName}: ${path}`);
self.events.emit('file-' + eventName, 'config', path);
@ -117,7 +126,7 @@ class Watch {
this.logger.trace(files);
let configWatcher = chokidar.watch(files, {
ignored: /[\/\\]\./, persistent: true, ignoreInitial: true, followSymlinks: true
ignored: /[\/\\]\.|tmp_/, persistent: true, ignoreInitial: true, followSymlinks: true
});
this.fileWatchers.push(configWatcher);
@ -125,7 +134,11 @@ class Watch {
.on('add', path => changeCallback('add', path))
.on('change', path => changeCallback('change', path))
.on('unlink', path => changeCallback('remove', path))
.on('ready', doneCallback);
.once('ready', () => {
configWatcher.isReady = true;
if (configWatcher.shouldClose) configWatcher.close();
doneCallback();
});
}
}

View File

@ -0,0 +1,121 @@
const async = require('async');
const webpack = require('webpack');
const utils = require('../utils/utils');
const fs = require('../core/fs');
const constants = require('../constants');
const HardSourceWebpackPlugin = require('hard-source-webpack-plugin');
const ProcessWrapper = require('../process/processWrapper');
let webpackProcess;
class WebpackProcess extends ProcessWrapper {
build(file, importsList, callback) {
const self = this;
let realCwd;
async.waterfall([
function findImports(next) {
self.webpackRun(file.filename, {}, false, importsList, false, next);
},
function changeCwd(next) {
realCwd = utils.pwd();
process.chdir(fs.embarkPath(''));
next();
},
function runWebpack(next) {
self.webpackRun(file.filename, {}, true, importsList, true, next);
},
function changeCwdBack(next) {
process.chdir(realCwd);
next();
}
], (err) => {
process.chdir(realCwd);
callback(err);
});
}
webpackRun(filename, options, includeModules, importsList, detectErrors, callback) {
let defaultOptions = {
entry: fs.dappPath(filename),
output: {
libraryTarget: 'umd',
path: fs.dappPath('.embark'),
filename: filename
},
resolve: {
alias: importsList,
modules: [
fs.embarkPath('node_modules'),
fs.dappPath('node_modules')
]
},
externals: function (context, request, callback) {
callback();
},
plugins: [new HardSourceWebpackPlugin()]
};
let webpackOptions = utils.recursiveMerge(defaultOptions, options);
if (includeModules) {
webpackOptions.module = {
rules: [
{
test: /\.css$/,
use: [{loader: "style-loader"}, {loader: "css-loader"}]
},
{
test: /\.scss$/,
use: [{loader: "style-loader"}, {loader: "css-loader"}]
},
{
test: /\.(png|woff|woff2|eot|ttf|svg)$/,
loader: 'url-loader?limit=100000'
},
{
test: /\.js$/,
loader: "babel-loader",
exclude: /(node_modules|bower_components)/,
options: {
presets: ['babel-preset-es2016', 'babel-preset-es2017', 'babel-preset-react'].map(require.resolve),
plugins: ["babel-plugin-webpack-aliases"].map(require.resolve),
compact: false
}
}
]
};
}
webpack(webpackOptions).run((err, stats) => {
if (err) {
console.error(err);
}
if (!detectErrors) {
return callback();
}
if (stats.hasErrors()) {
return callback(stats.toJson().errors.join("\n"));
}
callback();
});
}
}
process.on('message', (msg) => {
if (msg.action === constants.pipeline.init) {
webpackProcess = new WebpackProcess(msg.options);
return process.send({result: constants.pipeline.initiated});
}
if (msg.action === constants.pipeline.build) {
return webpackProcess.build(msg.file, msg.importsList, (err) => {
process.send({result: constants.pipeline.built, error: err});
});
}
});

View File

@ -0,0 +1,55 @@
const uuid = require('uuid/v1');
const constants = require('../constants');
class Events {
/**
* Constructs an event wrapper for processes.
* Handles sending an event message to the parent process and waiting for its response
* No need to create an instance of eventsWrapper for your own process, just extend ProcessWrapper
* Then, you an use `this.events.[on|request]` with the usual parameters you would use
*/
constructor() {
this.subscribedEvents = {};
this.listenToParentProcess();
}
listenToParentProcess() {
process.on('message', (msg) => {
if (!msg.event || msg.event !== constants.process.events.response) {
return;
}
if (!this.subscribedEvents[msg.eventId]) {
return;
}
this.subscribedEvents[msg.eventId](msg.result);
});
}
sendEvent() {
const eventType = arguments[0];
const requestName = arguments[1];
let args = [].slice.call(arguments, 2);
const eventId = uuid();
this.subscribedEvents[eventId] = args[args.length - 1];
args = args.splice(0, args.length - 2);
process.send({
event: eventType,
requestName,
args,
eventId: eventId
});
}
on() {
this.sendEvent(constants.process.events.on, ...arguments);
}
request() {
this.sendEvent(constants.process.events.request, ...arguments);
}
}
module.exports = Events;

View File

@ -0,0 +1,218 @@
const child_process = require('child_process');
const constants = require('../constants');
const path = require('path');
const utils = require('../utils/utils');
class ProcessLauncher {
/**
* Constructor of ProcessLauncher. Forks the module and sets up the message handling
* @param {Object} options Options tp start the process
* * modulePath {String} Absolute path to the module to fork
* * logger {Object} Logger
* * events {Function} Events Emitter instance
* @return {ProcessLauncher} The ProcessLauncher instance
*/
constructor(options) {
this.name = path.basename(options.modulePath);
this.process = child_process.fork(options.modulePath);
this.logger = options.logger;
this.events = options.events;
this.silent = options.silent;
this.exitCallback = options.exitCallback;
this.subscriptions = {};
this._subscribeToMessages();
}
// Subscribes to messages from the child process and delegates to the right methods
_subscribeToMessages() {
const self = this;
this.process.on('message', (msg) => {
if (msg.result === constants.process.log) {
return self._handleLog(msg);
}
if (msg.event) {
return self._handleEvent(msg);
}
self._checkSubscriptions(msg);
});
this.process.on('exit', (code) => {
if (self.exitCallback) {
return self.exitCallback(code);
}
if (code) {
this.logger.info(`Child Process ${this.name} exited with code ${code}`);
}
});
}
// Translates logs from the child process to the logger
_handleLog(msg) {
if (this.silent && msg.type !== 'error') {
return;
}
if (this.logger[msg.type]) {
return this.logger[msg.type](utils.normalizeInput(msg.message));
}
this.logger.debug(utils.normalizeInput(msg.message));
}
// Handle event calls from the child process
_handleEvent(msg) {
const self = this;
if (!self.events[msg.event]) {
self.logger.warn('Unknown event method called: ' + msg.event);
return;
}
if (!msg.args || !Array.isArray(msg.args)) {
msg.args = [];
}
// Add callback in the args
msg.args.push((result) => {
self.process.send({
event: constants.process.events.response,
result,
eventId: msg.eventId
});
});
self.events[msg.event](msg.requestName, ...msg.args);
}
// Looks at the subscriptions to see if there is a callback to call
_checkSubscriptions(msg) {
const messageKeys = Object.keys(msg);
const subscriptionsKeys = Object.keys(this.subscriptions);
let subscriptionsForKey;
let messageKey;
// Find if the message contains a key that we are subscribed to
messageKeys.some(_messageKey => {
return subscriptionsKeys.some(subscriptionKey => {
if (_messageKey === subscriptionKey) {
subscriptionsForKey = this.subscriptions[subscriptionKey];
messageKey = _messageKey;
return true;
}
return false;
});
});
if (subscriptionsForKey) {
// Find if we are subscribed to one of the values
let subsIndex = [];
const subscriptionsForValue = subscriptionsForKey.filter((sub, index) => {
if (msg[messageKey] === sub.value) {
subsIndex.push(index);
return true;
}
return false;
});
if (subscriptionsForValue.length) {
// We are subscribed to that message, call the callback
subscriptionsForValue.forEach((subscription, index) => {
subscription.callback(msg);
if (subscription.once) {
// Called only once, we can remove it
subscription = null;
this.subscriptions[messageKey].splice(subsIndex[index], 1);
}
});
}
}
}
/**
* Subscribe to a message using a key-value pair
* @param {String} key Message key to subscribe to
* @param {String} value Value that the above key must have for the callback to be called
* @param {Function} callback callback(response)
* @return {void}
*/
on(key, value, callback) {
if (this.subscriptions[key]) {
this.subscriptions[key].push({value, callback});
return;
}
this.subscriptions[key] = [{value, callback}];
}
/**
* Same as .on, but only triggers once
* @param {String} key Message key to subscribe to
* @param {String} value Value that the above key must have for the callback to be called
* @param {Function} callback callback(response)
* @return {void}
*/
once(key, value, callback) {
const obj = {value, callback, once: true};
if (this.subscriptions[key]) {
this.subscriptions[key].push(obj);
return;
}
this.subscriptions[key] = [obj];
}
/**
* Unsubscribes from a previously subscribed key-value pair (or key if no value)
* @param {String} key Message key to unsubscribe
* @param {String} value [Optional] Value of the key to unsubscribe
* If there is no value, unsubscribes from all the values of that key
* @return {void}
*/
unsubscribeTo(key, value) {
if (!value) {
this.subscriptions[key] = [];
}
if (this.subscriptions[key]) {
this.subscriptions[key].filter((val, index) => {
if (val.value === value) {
this.subscriptions[key].splice(index, 1);
}
});
}
}
/**
* Unsubscribes from all subscriptions
* @return {void}
*/
unsubscribeToAll() {
this.subscriptions = {};
}
/**
* Sends a message to the child process. Same as ChildProcess.send()
* @params {Object} message Message to send
* For other parameters, see:
* https://nodejs.org/api/child_process.html#child_process_subprocess_send_message_sendhandle_options_callback
* @return {void}
*/
send() {
if (!this.process.connected) {
return false;
}
return this.process.send(...arguments);
}
/**
* Disconnects the child process. It will exit on its own
* @return {void}
*/
disconnect() {
this.process.disconnect();
}
/**
* Kills the child process
* https://nodejs.org/api/child_process.html#child_process_subprocess_kill_signal
* @return {void}
*/
kill() {
this.process.kill(...arguments);
}
}
module.exports = ProcessLauncher;

View File

@ -0,0 +1,93 @@
const constants = require('../constants');
const Events = require('./eventsWrapper');
// Override process.chdir so that we have a partial-implementation PWD for Windows
const realChdir = process.chdir;
process.chdir = (...args) => {
if (!process.env.PWD) {
process.env.PWD = process.cwd();
}
realChdir(...args);
};
class ProcessWrapper {
/**
* Class from which process extend. Should not be instantiated alone.
* Manages the log interception so that all console.* get sent back to the parent process
* Also creates an Events instance. To use it, just do `this.events.[on|request]`
*
* @param {Options} _options Nothing for now
*/
constructor(_options) {
this.interceptLogs();
this.events = new Events();
this.pingParent();
}
// Ping parent to see if it is still alive. Otherwise, let's die
pingParent() {
const self = this;
self.retries = 0;
function error() {
if (self.retries > 2) {
self.kill();
process.exit();
}
self.retries++;
}
setInterval(() => {
try {
let result = self.send({action: 'ping'});
if (!result) {
return error();
}
self.retries = 0;
} catch (e) {
error();
}
}, 2000);
}
interceptLogs() {
const context = {};
context.console = console;
context.console.log = this._log.bind(this, 'log');
context.console.warn = this._log.bind(this, 'warn');
context.console.error = this._log.bind(this, 'error');
context.console.info = this._log.bind(this, 'info');
context.console.debug = this._log.bind(this, 'debug');
context.console.trace = this._log.bind(this, 'trace');
context.console.dir = this._log.bind(this, 'dir');
}
_log(type, ...messages) {
const isHardSource = messages.some(message => {
return (typeof message === 'string' && message.indexOf('hard-source') > -1);
});
if (isHardSource) {
return;
}
this.send({result: constants.process.log, message: messages, type});
}
send() {
if (!process.connected) {
return false;
}
return process.send(...arguments);
}
kill() {
// Should be implemented by derived class
console.log('Process killed');
}
}
process.on('exit', () => {
process.exit(0);
});
module.exports = ProcessWrapper;

View File

@ -0,0 +1,54 @@
const ProcessLauncher = require('../process/processLauncher');
const utils = require('../utils/utils.js');
const constants = require('../constants');
class BlockchainProcessLauncher {
constructor (options) {
this.events = options.events;
this.logger = options.logger;
this.normalizeInput = options.normalizeInput;
this.blockchainConfig = options.blockchainConfig;
this.locale = options.locale;
this.isDev = options.isDev;
}
processEnded(code) {
this.logger.error(__('Blockchain process ended before the end of this process. Code: %s', code));
}
startBlockchainNode() {
this.logger.info(__('Starting Blockchain node in another process').cyan);
this.blockchainProcess = new ProcessLauncher({
modulePath: utils.joinPath(__dirname, '../cmds/blockchain/blockchainProcess.js'),
logger: this.logger,
events: this.events,
silent: this.logger.logLevel !== 'trace',
exitCallback: this.processEnded.bind(this)
});
this.blockchainProcess.send({
action: constants.blockchain.init, options: {
blockchainConfig: this.blockchainConfig,
//client: this.client,
// TODO: assume for now it's geth
client: 'geth',
env: this.env,
isDev: this.isDev,
locale: this.locale
}
});
this.blockchainProcess.once('result', constants.blockchain.blockchainReady, () => {
this.logger.info(__('Blockchain node is ready').cyan);
this.events.emit(constants.blockchain.blockchainReady);
});
this.events.on('exit', () => {
this.blockchainProcess.send('exit');
});
}
}
module.exports = BlockchainProcessLauncher;

View File

@ -0,0 +1,134 @@
const child_process = require('child_process');
const ProcessWrapper = require('../../process/processWrapper');
const constants = require('../../constants');
const StorageUtils = require('./storageUtils');
let ipfsProcess; // eslint-disable-line no-unused-vars
class IPFSProcess extends ProcessWrapper {
constructor(options) {
super();
this.cors = options.cors;
this.command = StorageUtils.getCommand('ipfs', options);
this.checkIPFSVersion();
this.startIPFSDaemon();
}
checkIPFSVersion() {
child_process.exec(this.command + ' --version', {silent: true}, (err, stdout, _stderr) => {
if (err) {
console.error(err);
return;
}
const match = stdout.match(/[0-9]+\.[0-9]+\.[0-9]+/);
if (match[0]) {
const versions = match[0].split('.');
if (versions[0] <= 0 && versions[1] <= 4 && versions[2] <= 14) {
console.error(`You are using IPFS version ${match[0]} which has an issue with processes.`);
console.error(`Please update to IPFS version 0.4.15 or more recent: https://github.com/ipfs/ipfs-update`);
}
}
});
}
_bindChildEvents(childProcess){
const self = this;
childProcess.on('error', (err) => {
err = err.toString();
console.error('IPFS error: ', err);
});
childProcess.stderr.on('data', (data) => {
data = data.toString();
console.log(`IPFS error: ${data}`);
// `ipfs daemon called`, but `ipfs init` had not been run yet
if(!self.initCalled && data.indexOf('no IPFS repo found') > -1) {
self.initCalled = true;
let ipfsInitChild = child_process.spawn(this.command, ['init']);
self._bindChildEvents(ipfsInitChild);
}
});
childProcess.stdout.on('data', (data) => {
data = data.toString();
// ipfs init just run, and we have a successful result
// re-run `ipfs daemon`
if(self.initCalled && !self.readyCalled && data.indexOf('peer identity:') > -1) {
self.startIPFSDaemon();
}
else if (!self.readyCalled && data.indexOf('Daemon is ready') > -1) {
self.readyCalled = true;
// update IPFS cors before spawning a daemon (muhaha)
let ipfsCorsCmd = `${self.command} config --json API.HTTPHeaders.Access-Control-Allow-Origin "[\\"${self.cors.join('\\", \\"')}\\"]"`;
console.trace(`Updating IPFS CORS using command: ${ipfsCorsCmd}`);
child_process.exec(ipfsCorsCmd, {silent: true}, (err, stdout, _stderr) => {
if(err){
err = err.toString();
console.error('IPFS CORS update error: ', err);
}
if(_stderr){
_stderr = _stderr.toString();
console.error(`IPFS CORS update error: ${_stderr}`);
}
child_process.exec(self.command + ' config --json API.HTTPHeaders.Access-Control-Allow-Credentials "[\\"true\\"]"', {silent: true}, (err, stdout, _stderr) => {
if(err){
err = err.toString();
console.error('IPFS CORS update error: ', err);
}
if(_stderr){
_stderr = _stderr.toString();
console.error(`IPFS CORS update error: ${_stderr}`);
}
child_process.exec(self.command + ' config --json API.HTTPHeaders.Access-Control-Allow-Methods "[\\"PUT\\", \\"POST\\", \\"GET\\"]"', {silent: true}, (err, stdout, _stderr) => {
if(err){
err = err.toString();
console.error('IPFS CORS update error: ', err);
}
if(_stderr){
_stderr = _stderr.toString();
console.error(`IPFS CORS update error: ${_stderr}`);
}
self.send({result: constants.storage.initiated});
});
});
});
}
console.log('IPFS: ' + data);
});
childProcess.on('exit', (code) => {
if (code) {
console.error('IPFS exited with error code ' + code);
}
});
}
startIPFSDaemon() {
const self = this;
// spawn the daemon (muhaha)
this.child = child_process.spawn(this.command, ['daemon']);
self._bindChildEvents(this.child);
}
kill() {
if (this.child) {
this.child.kill();
}
}
}
process.on('message', (msg) => {
if (msg === 'exit') {
return ipfsProcess.kill();
}
if (msg.action === constants.storage.init) {
ipfsProcess = new IPFSProcess(msg.options);
}
});

View File

@ -0,0 +1,123 @@
const fs = require('../../core/fs');
const shellJs = require('shelljs');
const utils = require('../../utils/utils');
const ProcessLauncher = require('../../process/processLauncher');
const constants = require('../../constants');
const StorageUtils = require('./storageUtils');
class StorageProcessesLauncher {
constructor(options) {
this.logger = options.logger;
this.events = options.events;
this.storageConfig = options.storageConfig;
this.webServerConfig = options.webServerConfig;
this.blockchainConfig = options.blockchainConfig;
this.processes = {};
this.cors = this.buildCors();
this.events.on('exit', () => {
Object.keys(this.processes).forEach(processName => {
this.processes[processName].send('exit');
});
});
}
buildCors(storageName)
{
let corsParts = [];
// add our webserver CORS
if(this.webServerConfig.enabled){
if (this.webServerConfig && this.webServerConfig.host) {
corsParts.push(utils.buildUrlFromConfig(this.webServerConfig));
}
else corsParts.push('http://localhost:8000');
}
// add all dapp connection storage
if(this.storageConfig.enabled) {
this.storageConfig.dappConnection.forEach(dappConn => {
if(dappConn.provider === storageName) return; // do not add CORS URL for ourselves
if(dappConn.getUrl || dappConn.host){
// if getUrl is specified in the config, that needs to be included in cors
// instead of the concatenated protocol://host:port
if(dappConn.getUrl) {
// remove /ipfs or /bzz: from getUrl if it's there
let getUrlParts = dappConn.getUrl.split('/');
getUrlParts = getUrlParts.slice(0, 3);
corsParts.push(getUrlParts.join('/'));
}
// in case getUrl wasn't specified, use a built url
else{
corsParts.push(utils.buildUrlFromConfig(dappConn));
}
}
});
}
if(this.blockchainConfig.enabled) {
// add our rpc endpoints to CORS
if(this.blockchainConfig.rpcHost && this.blockchainConfig.rpcPort){
corsParts.push(`http://${this.blockchainConfig.rpcHost}:${this.blockchainConfig.rpcPort}`);
}
// add our ws endpoints to CORS
if(this.blockchainConfig.wsRPC && this.blockchainConfig.wsHost && this.blockchainConfig.wsPort){
corsParts.push(`ws://${this.blockchainConfig.wsHost}:${this.blockchainConfig.wsPort}`);
}
}
return corsParts;
}
processExited(storageName, code) {
this.logger.error(__(`Storage process for {{storageName}} ended before the end of this process. Code: {{code}}`, {storageName, code}));
}
launchProcess(storageName, callback) {
const self = this;
if (self.processes[storageName]) {
return callback(__('Storage process already started'));
}
const filePath = utils.joinPath(__dirname, `./${storageName}.js`);
fs.access(filePath, (err) => {
if (err) {
return callback(__('No process file for this storage type (%s) exists. Please start the process locally.', storageName));
}
const program = shellJs.which(StorageUtils.getCommand(storageName, self.storageConfig));
if (!program) {
self.logger.warn(__('{{storageName}} is not installed or your configuration is not right', {storageName}).yellow);
self.logger.info(__('You can install and get more information here: ').yellow + StorageUtils.getStorageInstallationSite(storageName).underline);
return callback(__('%s not installed', storageName));
}
self.logger.info(__(`Starting %s process`, storageName).cyan);
self.processes[storageName] = new ProcessLauncher({
modulePath: filePath,
logger: self.logger,
events: self.events,
silent: self.logger.logLevel !== 'trace',
exitCallback: self.processExited.bind(this, storageName)
});
self.processes[storageName].send({
action: constants.blockchain.init, options: {
storageConfig: self.storageConfig,
cors: self.buildCors(storageName)
}
});
self.processes[storageName].on('result', constants.storage.initiated, (msg) => {
if (msg.error) {
self.processes[storageName].disconnect();
delete self.processes[storageName];
return callback(msg.error);
}
self.logger.info(__(`${storageName} process started`).cyan);
callback();
});
});
}
}
module.exports = StorageProcessesLauncher;

View File

@ -0,0 +1,25 @@
const IPFS = 'ipfs';
const SWARM = 'swarm';
class StorageUtils {
static getCommand(storageName, config) {
if (storageName === IPFS) {
return IPFS;
}
if (storageName === SWARM) {
return config.swarmPath || SWARM;
}
return null;
}
static getStorageInstallationSite(storageName) {
if (storageName === IPFS) {
return 'https://ipfs.io/docs/install/';
}
if (storageName === SWARM) {
return 'http://swarm-guide.readthedocs.io/en/latest/installation.html';
}
}
}
module.exports = StorageUtils;

View File

@ -0,0 +1,74 @@
const child_process = require('child_process');
const ProcessWrapper = require('../../process/processWrapper');
const constants = require('../../constants');
const fs = require('../../core/fs');
const StorageUtils = require('./storageUtils');
let swarmProcess;
class SwarmProcess extends ProcessWrapper {
constructor(options) {
super();
this.storageConfig = options.storageConfig;
this.cors = options.cors;
this.command = StorageUtils.getCommand('swarm', this.storageConfig);
}
startSwarmDaemon() {
const self = this;
if (!this.storageConfig.account || !this.storageConfig.account.address || !this.storageConfig.account.password) {
return 'Account address and password are needed in the storage config to start the Swarm process';
}
const args = [
`--bzzaccount=${this.storageConfig.account.address}`,
`--password=${fs.dappPath(this.storageConfig.account.password)}`,
`--corsdomain=${self.cors.join(',')}`
];
console.trace('Starting swarm process with arguments: ' + args.join(' '));
this.child = child_process.spawn(this.command, args);
this.child.on('error', (err) => {
err = err.toString();
console.error('Swarm error: ', err);
});
this.child.stdout.on('data', (data) => {
data = data.toString();
console.log(`Swarm error: ${data}`);
});
// Swarm logs appear in stderr somehow
this.child.stderr.on('data', (data) => {
data = data.toString();
if (!self.readyCalled && data.indexOf('Swarm http proxy started') > -1) {
self.readyCalled = true;
self.send({result: constants.storage.initiated});
}
console.log('Swarm: ' + data);
});
this.child.on('exit', (code) => {
if (code) {
console.error('Swarm exited with error code ' + code);
}
});
}
kill() {
if (this.child) {
this.child.kill();
}
}
}
process.on('message', (msg) => {
if (msg === 'exit') {
return swarmProcess.kill();
}
if (msg.action === constants.storage.init) {
swarmProcess = new SwarmProcess(msg.options);
const error = swarmProcess.startSwarmDaemon();
if (error) {
swarmProcess.send({result: constants.storage.initiated, error});
}
}
});

View File

@ -1,80 +1,137 @@
const utils = require('../utils/utils.js');
const async = require('async');
const fs = require('fs-extra');
const Mocha = require('mocha');
const path = require('path');
const assert = require('assert');
const Test = require('./test');
function getFilesFromDir(filePath, cb) {
fs.readdir(filePath, (err, files) => {
if (err) {
return cb(err);
}
const testFiles = files.filter((file) => {
// Only keep the .js files
// TODO: make this a configuration in embark.json
return file.substr(-3) === '.js';
}).map((file) => {
return path.join(filePath, file);
});
cb(null, testFiles);
});
}
module.exports = {
run: function(filepath) {
const Mocha = require('mocha'),
fs = require('fs-extra'),
path = require('path');
const mocha = new Mocha();
if (filepath) {
if (filepath.substr(-1) === '/') {
// Add each .js file to the mocha instance
fs.readdirSync(filepath).filter(function(file){
// Only keep the .js files
// TODO: make this a configuration in embark.json
return file.substr(-3) === '.js';
}).forEach(function(file){
mocha.addFile(
path.join(filepath, file)
);
});
} else {
mocha.addFile(filepath);
}
} else {
var testDir = 'test/';
// Add each .js file to the mocha instance
fs.readdirSync(testDir).filter(function(file){
// Only keep the .js files
// TODO: make this a configuration in embark.json
return file.substr(-3) === '.js';
}).forEach(function(file){
mocha.addFile(
path.join(testDir, file)
);
});
run: function (options) {
process.env.isTest = true;
let failures = 0;
let filePath = options.file;
const loglevel = options.loglevel || 'warn';
if (!filePath) {
filePath = 'test/';
}
let Test = require('./test.js');
async.waterfall([
function checkIfDir(next) {
if (filePath.substr(-1) === '/') {
return next(null, null);
}
fs.stat(filePath, (err, stats) => {
if (err) {
return next(`File "${filePath}" doesn't exist or you don't have permission to it`.red);
}
if (stats.isDirectory()) {
return next(null, null);
}
next(null, [filePath]);
});
},
function getFiles(files, next) {
if (files) {
return next(null, files);
}
getFilesFromDir(filePath, (err, files) => {
if (err) {
console.error('Error while reading the directory');
return next(err);
}
next(null, files);
});
},
function setupGlobalNamespace(files, next) {
// TODO put default config
const test = new Test({loglevel});
global.embark = test;
global.assert = assert;
global.config = test.config.bind(test);
global.assert = require('assert');
let deprecatedWarning = function () {
console.error(__('%s are not supported anymore', 'EmbarkSpec & deployAll').red);
console.info(__('You can learn about the new revamped tests here: %s', 'https://embark.status.im/docs/testing.html'.underline));
process.exit();
};
let configOptions = {
gasPrice: 1
};
global.config = function(config) {
configOptions = utils.recursiveMerge(configOptions, config);
};
// TODO: check how to pass the options
//global.EmbarkSpec = new Test(options);
global.deployAll = deprecatedWarning;
global.EmbarkSpec = {};
global.EmbarkSpec.deployAll = deprecatedWarning;
// TODO: this global here might not be necessary at all
global.EmbarkSpec = new Test({});
global.web3 = global.EmbarkSpec.web3;
// Override require to enable `require('Embark/contracts/contractName');`
const Module = require('module');
const originalRequire = require('module').prototype.require;
Module.prototype.require = function (requireName) {
if (requireName.startsWith('Embark')) {
return test.require(...arguments);
}
return originalRequire.apply(this, arguments);
};
global.contract = function(describeName, callback) {
return Mocha.describe(describeName, callback);
};
// TODO: this global here might not be necessary at all
global.web3 = global.embark.web3;
global.contract = function (describeName, callback) {
return Mocha.describe(describeName, callback);
};
console.info('Compiling contracts'.cyan);
test.init((err) => {
next(err, files);
});
},
function executeForAllFiles(files, next) {
async.eachLimit(files, 1, (file, eachCb) => {
const mocha = new Mocha();
mocha.addFile(file);
mocha.suite.timeout(0);
mocha.suite.beforeAll('Wait for deploy', (done) => {
global.embark.onReady((err) => {
done(err);
});
});
mocha.run(function (fails) {
failures += fails;
// Mocha prints the error already
eachCb();
});
}, next);
}
], (err) => {
if (err) {
console.error(err);
process.exit(1);
}
if (failures) {
console.error(` > Total number of failures: ${failures}`.red.bold);
} else {
console.log(' > All tests passed'.green.bold);
}
// Run the tests.
let runner = mocha.run(function(failures) {
// Clean contracts folder for next test run
fs.remove('.embark/contracts', (_err) => {
process.on('exit', function () {
process.exit(failures); // exit with non-zero status if there were failures
});
process.exit();
process.exit(failures);
});
});
runner.on('suite', function() {
global.assert = require('assert');
global.EmbarkSpec = new Test({simulatorOptions: configOptions});
global.web3 = global.EmbarkSpec.web3;
});
}
};

View File

@ -1,108 +1,343 @@
var async = require('async');
//require("../utils/debug_util.js")(__filename, async);
var Web3 = require('web3');
var Engine = require('../core/engine.js');
var RunCode = require('../core/runCode.js');
var TestLogger = require('./test_logger.js');
const async = require('async');
const Engine = require('../core/engine.js');
const TestLogger = require('./test_logger.js');
const Web3 = require('web3');
const constants = require('../constants');
const Events = require('../core/events');
const cloneDeep = require('clone-deep');
const AccountParser = require('../contracts/accountParser');
const Provider = require('../contracts/provider');
const utils = require('../utils/utils');
var getSimulator = function() {
const EmbarkJS = require('../../js/embark_node');
function getSimulator() {
try {
var sim = require('ethereumjs-testrpc');
return sim;
return require('ganache-cli');
} catch (e) {
const moreInfo = 'For more information see https://github.com/trufflesuite/ganache-cli';
if (e.code === 'MODULE_NOT_FOUND') {
console.log('Simulator not found; Please install it with "npm install ethereumjs-testrpc --save"');
console.log('IMPORTANT: if you using a NodeJS version older than 6.9.1 then you need to install an older version of testrpc "npm install ethereumjs-testrpc@2.0 --save"');
console.log('For more information see https://github.com/ethereumjs/testrpc');
// TODO: should throw exception instead
return process.exit();
console.error(__('Simulator not found; Please install it with "%s"', 'npm install ganache-cli --save'));
console.error(moreInfo);
throw e;
}
console.log("==============");
console.log("Tried to load testrpc but an error occurred. This is a problem with testrpc");
console.log('IMPORTANT: if you using a NodeJS version older than 6.9.1 then you need to install an older version of testrpc "npm install ethereumjs-testrpc@2.0 --save". Alternatively install node 6.9.1 and the testrpc 3.0');
console.log("==============");
console.error("==============");
console.error(__("Tried to load Ganache CLI (testrpc), but an error occurred. This is a problem with Ganache CLI"));
console.error(moreInfo);
console.error("==============");
throw e;
}
};
}
var Test = function(options) {
this.options = options || {};
this.simOptions = this.options.simulatorOptions || {};
class Test {
constructor(options) {
this.options = options || {};
this.simOptions = {};
this.contracts = {};
this.events = new Events();
this.ready = true;
this.error = false;
this.builtContracts = {};
this.compiledContracts = {};
this.engine = new Engine({
env: this.options.env || 'test',
// TODO: confi will need to detect if this is a obj
embarkConfig: this.options.embarkConfig || 'embark.json',
interceptLogs: false
});
this.engine.init({
logger: new TestLogger({logLevel: 'debug'})
});
this.web3 = new Web3();
};
Test.prototype.deployAll = function(contractsConfig, cb) {
var self = this;
if (this.simOptions.node) {
this.web3.setProvider(new this.web3.providers.HttpProvider(this.simOptions.node));
} else {
this.sim = getSimulator();
this.web3.setProvider(this.sim.provider(this.simOptions));
this.web3 = new Web3();
}
async.waterfall([
function getConfig(callback) {
let _versions_default = self.engine.config.contractsConfig.versions;
self.engine.config.contractsConfig = {contracts: contractsConfig, versions: _versions_default};
callback();
},
function startServices(callback) {
//{abiType: 'contracts', embarkJS: false}
self.engine.startService("libraryManager");
self.engine.startService("codeGenerator");
self.engine.startService("deployment", {
web3: self.web3,
trackContracts: false
});
callback();
},
function deploy(callback) {
self.engine.events.on('code-generator-ready', function () {
self.engine.events.request('code-contracts-vanila', function(vanillaABI) {
callback(null, vanillaABI);
});
});
initWeb3Provider(callback) {
if (this.simOptions.host) {
let {host, port, type, protocol, accounts} = this.simOptions;
if (!protocol) {
protocol = (this.simOptions.type === "rpc") ? 'http' : 'ws';
}
const endpoint = `${protocol}://${host}:${port}`;
const providerOptions = {
web3: this.web3,
type,
accountsConfig: accounts,
blockchainConfig: this.engine.config.blockchainConfig,
logger: this.engine.logger,
isDev: false,
web3Endpoint: endpoint
};
console.info(`Connecting to node at ${endpoint}`.cyan);
return utils.pingEndpoint(host, port, type, protocol, this.engine.config.blockchainConfig.wsOrigins.split(',')[0], (err) => {
if (err) {
console.error(`Error connecting to the node, there might be an error in ${endpoint}`.red);
return callback(err);
}
this.provider = new Provider(providerOptions);
return this.provider.startWeb3Provider(callback);
});
}
if (this.simOptions.accounts) {
this.simOptions.accounts = this.simOptions.accounts.map((account) => {
return {balance: account.hexBalance, secretKey: account.privateKey};
});
}
this.sim = getSimulator();
this.web3.setProvider(this.sim.provider(this.simOptions));
callback();
}
initDeployServices() {
this.engine.startService("web3", {
web3: this.web3
});
this.engine.startService("deployment", {
trackContracts: false
//ipcRole: 'client' // disabled for now due to issues with ipc file
});
}
init(callback) {
const self = this;
this.engine = new Engine({
env: this.options.env || 'test',
// TODO: config will need to detect if this is a obj
embarkConfig: this.options.embarkConfig || 'embark.json',
interceptLogs: false
});
this.initWeb3Provider((err) => {
if (err) {
return callback(err);
}
this.engine.init({
logger: new TestLogger({logLevel: this.options.loglevel})
});
this.versions_default = this.engine.config.contractsConfig.versions;
// Reset contract config to nothing to make sure we deploy only what we want
this.engine.config.contractsConfig = {
contracts: {},
versions: this.versions_default
};
this.engine.startService("libraryManager");
this.engine.startService("codeRunner");
this.initDeployServices();
this.engine.startService("codeGenerator");
this.engine.contractsManager.build(() => {
self.builtContracts = cloneDeep(self.engine.contractsManager.contracts);
self.compiledContracts = cloneDeep(self.engine.contractsManager.compiledContracts);
callback();
});
});
}
onReady(callback) {
const self = this;
if (this.ready) {
return callback();
}
if (this.error) {
return callback(this.error);
}
let errorCallback, readyCallback;
errorCallback = (err) => {
self.events.removeListener('ready', readyCallback);
callback(err);
};
readyCallback = () => {
self.events.removeListener('deployError', errorCallback);
callback();
};
this.events.once('ready', readyCallback);
this.events.once('deployError', errorCallback);
}
checkDeploymentOptions(options, callback) {
const self = this;
if (!options.deployment) {
if (!self.simOptions.host && !self.simOptions.accounts) {
return callback();
}
self.simOptions = {};
} else {
self.simOptions = {};
let resetServices = false;
if (options.deployment.accounts) {
// Account setup
self.simOptions.accounts = AccountParser.parseAccountsConfig(options.deployment.accounts, self.web3);
resetServices = true;
}
if (options.deployment.host && options.deployment.port && options.deployment.type) {
if (options.deployment.type !== 'rpc' && options.deployment.type !== 'ws') {
callback(__("contracts config error: unknown deployment type %s", options.deployment.type));
}
Object.assign(self.simOptions, {host: options.deployment.host, port: options.deployment.port, type: options.deployment.type});
resetServices = true;
}
if (!resetServices) {
return callback();
}
}
self.initWeb3Provider((err) => {
if (err) {
return callback(err);
}
self.initDeployServices();
callback();
});
}
config(options, callback) {
const self = this;
if (typeof (options) === 'function') {
callback = options;
options = {};
}
if (!callback) {
callback = function () {
};
}
if (!options.contracts) {
options.contracts = {};
}
self.ready = false;
async.waterfall([
function checkDeploymentOpts(next) {
self.checkDeploymentOptions(options, next);
},
function resetContracts(next) {
self.engine.contractsManager.contracts = cloneDeep(self.builtContracts);
self.engine.contractsManager.compiledContracts = cloneDeep(self.compiledContracts);
next();
},
function deploy(next) {
self._deploy(options, (err, accounts) => {
if (err) {
self.events.emit('deployError', err);
self.error = err;
return next(err);
}
self.ready = true;
self.error = false;
self.events.emit('ready');
next(null, accounts);
});
}
], (err, accounts) => {
if (err) {
process.exit(1);
}
callback(null, accounts);
});
}
_deploy(config, callback) {
const self = this;
async.waterfall([
function getConfig(next) {
self.engine.config.contractsConfig = {contracts: config.contracts, versions: self.versions_default};
self.engine.events.emit(constants.events.contractConfigChanged, self.engine.config.contractsConfig);
next();
},
function getAccounts(next) {
self.web3.eth.getAccounts(function (err, accounts) {
if (err) {
return next(err);
}
self.accounts = accounts;
self.web3.eth.defaultAccount = accounts[0];
next(null, accounts);
});
},
function getBalance(accounts, next) {
self.web3.eth.getBalance(self.web3.eth.defaultAccount).then((balance) => {
if (parseInt(balance, 10) === 0) {
console.warn("Warning: default account has no funds");
}
next(null, accounts);
}).catch((err) => { next(err); });
},
function deploy(accounts, next) {
self.engine.deployManager.gasLimit = 6000000;
self.engine.contractsManager.gasLimit = 6000000;
self.engine.deployManager.fatalErrors = true;
self.engine.deployManager.deployOnlyOnConfig = true;
self.engine.deployManager.deployContracts(function(err, _result) {
if (err) {
callback(err);
self.engine.events.request('deploy:contracts', () => {
next(null, accounts);
});
},
function createContractObject(accounts, next) {
async.each(Object.keys(self.engine.contractsManager.contracts), (contractName, eachCb) => {
const contract = self.engine.contractsManager.contracts[contractName];
let data;
if (!self.contracts[contractName]) {
self.contracts[contractName] = {};
data = "";
} else {
data = self.contracts[contractName].options.data;
}
Object.assign(self.contracts[contractName], new EmbarkJS.Contract({abi: contract.abiDefinition, address: contract.deployedAddress, from: self.web3.eth.defaultAccount, gas: 6000000, web3: self.web3}));
self.contracts[contractName].address = contract.deployedAddress;
if (self.contracts[contractName].options) {
self.contracts[contractName].options.from = self.contracts[contractName].options.from || self.web3.eth.defaultAccount;
self.contracts[contractName].options.data = data;
self.contracts[contractName].options.gas = 6000000;
}
eachCb();
}, (err) => {
next(err, accounts);
});
}
], function(err, result) {
if (err) {
console.log('terminating due to error');
process.exit(1);
}
// this should be part of the waterfall and not just something done at the
// end
self.web3.eth.getAccounts(function(err, accounts) {
], function (err, accounts) {
if (err) {
throw new Error(err);
console.log(__('terminating due to error'));
return callback(err);
}
self.web3.eth.defaultAccount = accounts[0];
RunCode.doEval(result, self.web3);
//cb();
cb(accounts);
callback(null, accounts);
});
});
};
}
require(module) {
if (module.startsWith('Embark/contracts/')) {
const contractName = module.substr(17);
if (this.contracts[contractName]) {
return this.contracts[contractName];
}
let contract = this.engine.contractsManager.contracts[contractName];
if (!contract) {
const contractNames = Object.keys(this.engine.contractsManager.contracts);
// It is probably an instanceof
contractNames.find(contrName => {
// Find a contract with a similar name
if (contractName.indexOf(contrName) > -1) {
contract = this.engine.contractsManager.contracts[contrName];
return true;
}
return false;
});
// If still nothing, assign bogus one, we will redefine it anyway on deploy
if (!contract) {
console.warn(__('Could not recognize the contract name "%s"', contractName));
console.warn(__('If it is an instance of another contract, it will be reassigned on deploy'));
console.warn(__('Otherwise, you can rename the contract to contain the parent contract in the name eg: Token2 for Token'));
contract = this.engine.contractsManager.contracts[contractNames[0]];
}
}
this.contracts[contractName] = new EmbarkJS.Contract({abi: contract.abiDefinition, address: contract.address, from: this.web3.eth.defaultAccount, gas: 6000000, web3: this.web3});
this.contracts[contractName].address = contract.address;
this.contracts[contractName].options.data = contract.code;
this.contracts[contractName].options.gas = 6000000;
this.web3.eth.getAccounts().then((accounts) => {
this.contracts[contractName].options.from = contract.from || accounts[0];
});
return this.contracts[contractName];
}
throw new Error(__('Unknown module %s', module));
}
}
module.exports = Test;

View File

@ -5,21 +5,11 @@ require('colors');
class TestLogger {
constructor(options) {
this.logLevels = ['error', 'warn', 'info', 'debug', 'trace'];
this.logs = [];
this.logLevel = options.logLevel || 'info';
}
logFunction() {
this.logs.push(arguments);
//console.dir(arguments[0]);
}
contractsState() {
this.logs.push(arguments);
}
availableServices() {
this.logs.push(arguments);
console.log(...arguments);
}
error(txt) {

View File

@ -1,29 +1,23 @@
let path = require('path');
let globule = require('globule');
let merge = require('merge');
let http = require('follow-redirects').http;
let https = require('follow-redirects').https;
let shelljs = require('shelljs');
var tar = require('tar');
var propose = require('propose');
const constants = require('../constants');
//let fs = require('../core/fs.js');
let o_fs = require('fs-extra');
function joinPath() {
const path = require('path');
return path.join.apply(path.join, arguments);
}
function filesMatchingPattern(files) {
const globule = require('globule');
return globule.find(files, {nonull: true});
}
function fileMatchesPattern(patterns, intendedPath) {
const globule = require('globule');
return globule.isMatch(patterns, intendedPath);
}
function recursiveMerge(target, source) {
const merge = require('merge');
return merge.recursive(target, source);
}
@ -68,7 +62,60 @@ function httpGetJson(url, callback) {
});
}
function httpsGetJson(url, callback) {
httpGetRequest(https, url, function(err, body) {
try {
let parsed = JSON.parse(body);
return callback(err, parsed);
} catch(e) {
return callback(e);
}
});
}
function pingEndpoint(host, port, type, protocol, origin, callback) {
const options = {
protocolVersion: 13,
perMessageDeflate: true,
origin: origin,
host: host,
port: port
};
if (type === 'ws') {
options.headers = {
'Sec-WebSocket-Version': 13,
Connection: 'Upgrade',
Upgrade: 'websocket',
'Sec-WebSocket-Extensions': 'permessage-deflate; client_max_window_bits',
Origin: origin
};
}
let req;
// remove trailing api key from infura, ie rinkeby.infura.io/nmY8WtT4QfEwz2S7wTbl
if (options.host.indexOf('/') > -1){
options.host = options.host.split('/')[0];
}
if (protocol === 'https') {
req = require('https').get(options);
} else {
req = require('http').get(options);
}
req.on('error', (err) => {
callback(err);
});
req.on('response', (_response) => {
callback();
});
req.on('upgrade', (_res, _socket, _head) => {
callback();
});
}
function runCmd(cmd, options) {
const shelljs = require('shelljs');
let result = shelljs.exec(cmd, options || {silent: true});
if (result.code !== 0) {
console.log("error doing.. " + cmd);
@ -81,10 +128,12 @@ function runCmd(cmd, options) {
}
function cd(folder) {
const shelljs = require('shelljs');
shelljs.cd(folder);
}
function sed(file, pattern, replace) {
const shelljs = require('shelljs');
shelljs.sed('-i', pattern, replace, file);
}
@ -93,6 +142,7 @@ function exit(code) {
}
function downloadFile(url, dest, cb) {
const o_fs = require('fs-extra');
var file = o_fs.createWriteStream(dest);
(url.substring(0,5) === 'https' ? https : http).get(url, function(response) {
response.pipe(file);
@ -106,6 +156,8 @@ function downloadFile(url, dest, cb) {
}
function extractTar(filename, packageDirectory, cb) {
const o_fs = require('fs-extra');
const tar = require('tar');
o_fs.createReadStream(filename).pipe(
tar.x({
strip: 1,
@ -117,6 +169,7 @@ function extractTar(filename, packageDirectory, cb) {
}
function proposeAlternative(word, _dictionary, _exceptions) {
const propose = require('propose');
let exceptions = _exceptions || [];
let dictionary = _dictionary.filter((entry) => {
return exceptions.indexOf(entry) < 0;
@ -129,6 +182,7 @@ function pwd() {
}
function getExternalContractUrl(file) {
const constants = require('../constants');
let url;
const RAW_URL = 'https://raw.githubusercontent.com/';
const MALFORMED_ERROR = 'Malformed Github URL for ';
@ -173,6 +227,76 @@ function getExternalContractUrl(file) {
};
}
function hexToNumber(hex){
const Web3 = require('web3');
return Web3.utils.hexToNumber(hex);
}
function decodeParams(typesArray, hexString){
var Web3EthAbi = require('web3-eth-abi');
return Web3EthAbi.decodeParameters(typesArray, hexString);
}
function toChecksumAddress(address) {
const Web3 = require('web3');
return Web3.utils.toChecksumAddress(address);
}
function sha3(arg) {
const Web3 = require('web3');
return Web3.utils.sha3(arg);
}
function normalizeInput(input) {
let args = Object.values(input);
if (args.length === 0) {
return "";
}
if (args.length === 1) {
if (Array.isArray(args[0])) { return args[0].join(','); }
return args[0] || "";
}
return ('[' + args.map((x) => {
if (x === null) { return "null"; }
if (x === undefined) { return "undefined"; }
if (Array.isArray(x)) { return x.join(','); }
return x;
}).toString() + ']');
}
/**
* Builds a URL
*
* @param {string} protocol
* The URL protocol, defaults to http.
* @param {string} host
* The URL host, required.
* @param {string} port
* The URL port, default to empty string.
* @returns {string} the constructued URL, with defaults
*/
function buildUrl (protocol, host, port){
if(!host) throw new Error('utils.buildUrl: parameter \'host\' is required');
if(port) port = ':' + port;
else port = '';
return `${protocol || 'http'}://${host}${port}`;
}
/**
* Builds a URL
*
* @param {object} configObj Object containing protocol, host, and port to be used to construct the url.
* * protocol {String} (optional) The URL protocol, defaults to http.
* * host {String} (required) The URL host.
* * port {String} (optional) The URL port, default to empty string.
* @returns {string} the constructued URL, with defaults
*/
function buildUrlFromConfig (configObj){
if(!configObj) throw new Error('[utils.buildUrlFromConfig]: config object must cannot be null');
if(!configObj.host) throw new Error('[utils.buildUrlFromConfig]: object must contain a \'host\' property');
return this.buildUrl(configObj.protocol, configObj.host, configObj.port);
}
module.exports = {
joinPath: joinPath,
filesMatchingPattern: filesMatchingPattern,
@ -182,6 +306,10 @@ module.exports = {
httpGet: httpGet,
httpsGet: httpsGet,
httpGetJson: httpGetJson,
httpsGetJson: httpsGetJson,
hexToNumber: hexToNumber,
pingEndpoint,
decodeParams: decodeParams,
runCmd: runCmd,
cd: cd,
sed: sed,
@ -190,5 +318,10 @@ module.exports = {
extractTar: extractTar,
proposeAlternative: proposeAlternative,
pwd: pwd,
getExternalContractUrl
getExternalContractUrl,
toChecksumAddress: toChecksumAddress,
sha3: sha3,
normalizeInput,
buildUrl,
buildUrlFromConfig
};

View File

@ -23,15 +23,17 @@ class LibraryManager {
let solcVersionInConfig = this.contractsConfig.versions.solc;
let web3VersionInConfig = this.contractsConfig.versions["web3"];
let ipfsApiVersion = this.storageConfig.versions["ipfs-api"];
let pIterationVersion = this.storageConfig.versions["p-iteration"];
this.versions['solc'] = solcVersionInConfig;
this.versions['web3'] = web3VersionInConfig;
this.versions['ipfs-api'] = ipfsApiVersion;
this.versions['p-iteration'] = pIterationVersion;
Object.keys(this.versions).forEach(versionKey => {
const newVersion = this.versions[versionKey].trim();
if (newVersion !== this.versions[versionKey]) {
this.embark.logger.warn(`There a a space in the version of ${versionKey}. We corrected it for you ("${this.versions[versionKey]}" => "${newVersion}").`);
this.embark.logger.warn(__('There is a space in the version of {{versionKey}}. We corrected it for you ({{correction}}).', {versionKey: versionKey, correction: `"${this.versions[versionKey]}" => "${newVersion}"`}));
this.versions[versionKey] = newVersion;
}
});
@ -40,8 +42,8 @@ class LibraryManager {
registerCommands() {
const self = this;
this.embark.registerConsoleCommand((cmd, _options) => {
if (cmd === "versions") {
let text = ['versions in use:'];
if (cmd === "versions" || cmd === __('versions')) {
let text = [__('versions in use') + ':'];
for (let lib in self.versions) {
text.push(lib + ": " + self.versions[lib]);
}
@ -66,6 +68,9 @@ class LibraryManager {
this.embark.events.setCommandHandler('version:getPackageLocation', (libName, version, cb) => {
npm.getPackageVersion(libName, version, cb);
});
this.embark.events.setCommandHandler('version:getPackagePath', (libName, version, cb) => {
cb(null, Npm.getPackagePath(libName, version));
});
}
}

View File

@ -1,26 +1,57 @@
let fs = require('../core/fs.js');
let PluginManager = require('live-plugin-manager').PluginManager;
const fs = require('../core/fs.js');
const PluginManager = require('live-plugin-manager-git-fix').PluginManager;
require('colors');
const NpmTimer = require('./npmTimer');
class Npm {
constructor(options) {
this.logger = options.logger;
this._logger = options.logger;
this._packageName = options.packageName;
this._version = options.version;
this._installing = {};
}
static getPackagePath(packageName, version){
return './.embark/versions/' + packageName + '/' + version + '/' + packageName;
}
_isInstalling(packageName, version){
return typeof this._installing[packageName + version] != 'undefined';
}
getPackageVersion(packageName, version, callback) {
let packageDirectory = './.embark/versions/' + packageName + '/' + version + '/';
const packagePath = Npm.getPackagePath(packageName, version);
let manager = new PluginManager({pluginsPath: packageDirectory});
if (fs.existsSync(packageDirectory + packageName)) {
return callback(null, packageDirectory + packageName);
// check if this package already exists in the filesystem
if (fs.existsSync(packagePath)) {
return callback(null, packagePath);
}
this.logger.info("downloading " + packageName + " " + version + "....");
manager.install(packageName, version).then((result) => {
callback(null , result.location);
}).catch(callback);
const pluginManager = new PluginManager({pluginsPath: './.embark/versions/' + packageName + '/' + version + '/'});
// check if we're already installing this package
if(this._isInstalling(packageName, version)){
this._installing[packageName + version].push(callback);
}else{
this._installing[packageName + version] = [callback];
const timer = new NpmTimer({logger: this._logger, packageName: packageName, version: version});
timer.start();
// do the package download/install
pluginManager.install(packageName, version).then((result) => {
timer.end();
this._installing[packageName + version].forEach((cb) => {
cb(null, result.location);
});
delete this._installing[packageName + version];
}).catch(err => {
this._installing[packageName + version].forEach((cb) => {
cb(err);
});
});
}
}
}

99
lib/versions/npmTimer.js Normal file
View File

@ -0,0 +1,99 @@
const {PerformanceObserver, performance} = require('perf_hooks');
const _ = require('underscore');
require('colors');
const i18n = require('../i18n/i18n.js');
i18n.setOrDetectLocale('en');
class NpmTimer{
constructor(options){
this._logger = (options.logger && typeof options.logger.info == 'function') ? options.logger : console;
this._packageName = options.packageName;
this._version = options.version;
this._showSpinner = options.showSpinner || false;
this._spinnerStyle = options.spinnerStyle || 'dots';
this._interval = options.interval || 750;
// define mark and measurement names
this._startMark = 'downloadStart' + this._packageName + this._version;
this._ongoingMark = 'downloadOngoingMark' + this._packageName + this._version;
this._downloadOngoing = 'downloadOngoing' + this._packageName + this._version;
this._endMark = 'downloadEnd' + this._packageName + this._version;
this._downloadComplete = 'downloadComplete' + this._packageName + this._version;
this.observer.observe({entryTypes: ['measure']});
}
get observer(){
if(typeof this._observer == 'undefined'){
this._observer = new PerformanceObserver((items) => {
let entry;
let strDuration;
// find any download ongoing measurements we've made
entry = _.last(_.where(items.getEntries(), {name: this._downloadOngoing}));
if(entry){
// ongoing performance mark
strDuration = __('Downloading and installing {{packageName}} {{version}}... ({{duration}}ms elapsed)', {packageName: this._packageName, version: this._version, duration: entry.duration});
if(this._spinner) this._spinner.text = strDuration;
}
else{
// otherwise, find our download complete measurement
entry = _.last(_.where(items.getEntries(), {name: this._downloadComplete}));
if(entry){
strDuration = __('Finished downloading and installing {{packageName}} {{version}} in {{duration}}ms', {packageName: this._packageName, version: this._version, duration: entry.duration});
performance.clearMarks();
if(this._spinner) this._spinner.succeed(strDuration);
}
}
// log our measurement and make it red if it has taken too long
if(!this._showSpinner && entry && strDuration){
if(entry.duration > 4000){
strDuration = strDuration.red;
}
this._logger.info(strDuration);
}
});
}
return this._observer;
}
start(){
let self = this;
const strDownloadStart = __("Downloading and installing {{packageName}} {{version}}...", {packageName: this._packageName, version: this._version});
if(this._showSpinner){
const ora = require('ora');
this._spinner = ora({
spinner: this._spinnerStyle,
text: strDownloadStart
}).start();
}else{
this._logger.info(strDownloadStart);
}
// mark our start time
performance.mark(this._startMark);
// function that continually updates the console to show user that we're downloading a library
this._intOngoingDownload = setInterval(
function(){
performance.mark(self._ongoingMark);
performance.measure(self._downloadOngoing, self._startMark, self._ongoingMark);
}, this._interval);
}
end(){
// stop updating console for ongoing download
clearInterval(this._intOngoingDownload);
performance.mark(this._endMark);
performance.measure(this._downloadComplete, this._startMark, this._endMark);
}
}
module.exports = NpmTimer;

6816
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{
"name": "embark",
"version": "3.0.10",
"version": "3.1.0",
"description": "Embark is a framework that allows you to easily develop and deploy DApps",
"scripts": {
"lint": "./node_modules/.bin/eslint lib/",
@ -21,6 +21,7 @@
"url": "https://github.com/iurimatias/embark-framework.git"
},
"dependencies": {
"ascii-table": "0.0.9",
"async": "^2.0.1",
"babel-core": "^6.26.0",
"babel-loader": "^7.1.2",
@ -29,41 +30,55 @@
"babel-preset-es2016": "^6.24.1",
"babel-preset-es2017": "6.24.1",
"babel-preset-react": "^6.24.1",
"blessed": "^0.1.81",
"bip39": "^2.5.0",
"chokidar": "^2.0.3",
"clone-deep": "^4.0.0",
"colors": "^1.1.2",
"commander": "^2.15.1",
"css-loader": "^0.28.11",
"deep-equal": "^1.0.1",
"ejs": "^2.5.8",
"eth-ens-namehash": "^2.0.8",
"eth-lib": "^0.2.8",
"ethereumjs-testrpc": "^6.0.3",
"ethereumjs-wallet": "^0.6.0",
"file-loader": "^1.1.5",
"finalhandler": "^1.1.1",
"follow-redirects": "^1.2.4",
"fs-extra": "^2.0.0",
"ganache-cli": "6.1.0",
"globule": "^1.1.0",
"hard-source-webpack-plugin": "^0.6.6",
"http-proxy": "^1.17.0",
"http-shutdown": "^1.2.0",
"i18n": "^0.8.3",
"ipfs-api": "17.2.4",
"live-plugin-manager": "https://github.com/iurimatias/live-plugin-manager.git",
"live-plugin-manager-git-fix": "^0.12.1",
"merge": "^1.2.0",
"mocha": "^2.2.5",
"orbit-db": "^0.17.3",
"mocha": "^3.2.0",
"neo-blessed": "^0.2.0",
"node-http-proxy": "^0.2.3",
"node-ipc": "^9.1.1",
"ora": "^2.1.0",
"os-locale": "^2.1.0",
"p-iteration": "^1.1.7",
"parse-json": "^4.0.0",
"promptly": "^2.1.0",
"propose": "0.0.5",
"request": "^2.85.0",
"serve-static": "^1.11.1",
"shelljs": "^0.5.0",
"solc": "0.4.24",
"simples": "^0.8.8",
"string-replace-async": "^1.2.1",
"style-loader": "^0.19.0",
"tar": "^3.1.5",
"toposort": "^1.0.0",
"underscore": "^1.9.0",
"underscore.string": "^3.3.4",
"url-loader": "^0.6.2",
"uuid": "^3.2.1",
"viz.js": "^1.8.1",
"web3": "1.0.0-beta.34",
"embark-web3-provider-engine": "14.0.6",
"webpack": "^3.10.0",
"window-size": "^1.1.0"
},
@ -73,7 +88,6 @@
"ethereum",
"dapps",
"ipfs",
"orbit",
"solidity",
"solc",
"blockchain",
@ -89,7 +103,6 @@
"grunt-contrib-coffee": "^1.0.0",
"grunt-mocha-test": "^0.13.2",
"matchdep": "^1.0.1",
"mocha": "^3.2.0",
"mocha-sinon": "^1.1.4",
"sinon": "^4.5.0"
}

View File

@ -0,0 +1,62 @@
module.exports = {
development: {
enabled: true,
networkType: "custom", // Can be: testnet, rinkeby, livenet or custom, in which case, it will use the specified networkId
networkId: "1337", // Network id used when networkType is custom
isDev: true, // Uses and ephemeral proof-of-authority network with a pre-funded developer account, mining enabled
genesisBlock: "config/development/genesis.json", // Genesis block to initiate on first creation of a development node
datadir: ".embark/development/datadir", // Data directory for the databases and keystore
mineWhenNeeded: true, // Uses our custom script (if isDev is false) to mine only when needed
nodiscover: true, // Disables the peer discovery mechanism (manual peer addition)
maxpeers: 0, // Maximum number of network peers (network disabled if set to 0) (default: 25)
rpcHost: "localhost", // HTTP-RPC server listening interface (default: "localhost")
rpcPort: 8545, // HTTP-RPC server listening port (default: 8545)
rpcCorsDomain: "auto", // Comma separated list of domains from which to accept cross origin requests (browser enforced)
// When set to "auto", Embark will automatically set the cors to the address of the webserver
proxy: true, // Proxy is used to present meaningful information about transactions
account: {
// "address": "", // When specified, uses that address instead of the default one for the network
password: "config/development/password" // Password to unlock the account
},
targetGasLimit: 8000000, // Target gas limit sets the artificial target gas floor for the blocks to mine
wsRPC: true, // Enable the WS-RPC server
wsOrigins: "auto", // Origins from which to accept websockets requests
// When set to "auto", Embark will automatically set the cors to the address of the webserver
wsHost: "localhost", // WS-RPC server listening interface (default: "localhost")
wsPort: 8546, // WS-RPC server listening port (default: 8546)
simulatorMnemonic: "example exile argue silk regular smile grass bomb merge arm assist farm", // Mnemonic used by the simulator to generate a wallet
simulatorBlocktime: 0 // Specify blockTime in seconds for automatic mining. Default is 0 and no auto-mining.
},
testnet: {
enabled: true,
networkType: "testnet",
light: true,
rpcHost: "localhost",
rpcPort: 8545,
rpcCorsDomain: "http://localhost:8000",
account: {
password: "config/testnet/password"
}
},
livenet: {
enabled: true,
networkType: "livenet",
light: true,
rpcHost: "localhost",
rpcPort: 8545,
rpcCorsDomain: "http://localhost:8000",
account: {
password: "config/livenet/password"
}
},
privatenet: {
enabled: true,
networkType: "custom",
rpcHost: "localhost",
rpcPort: 8545,
rpcCorsDomain: "http://localhost:8000",
datadir: "yourdatadir",
networkId: "123",
bootnodes: ""
}
};

View File

@ -0,0 +1,13 @@
module.exports = {
default: {
enabled: true,
provider: "whisper", // Communication provider. Currently, Embark only supports whisper
available_providers: ["whisper"], // Array of available providers
connection: {
host: "localhost", // Host of the blockchain node
port: 8546, // Port of the blockchain node
type: "ws" // Type of connection (ws or rpc)
}
}
};

View File

@ -1,12 +0,0 @@
{
"default": {
"enabled": true,
"provider": "whisper",
"available_providers": ["whisper", "orbit"],
"connection": {
"host": "localhost",
"port": 8546,
"type": "ws"
}
}
}

View File

@ -0,0 +1,41 @@
module.exports = {
// default applies to all environments
default: {
// Blockchain node to deploy the contracts
deployment: {
host: "localhost", // Host of the blockchain node
port: 8545, // Port of the blockchain node
type: "rpc" // Type of connection (ws or rpc),
// Accounts to use instead of the default account to populate your wallet
/*,accounts: [
{
privateKey: "your_private_key",
balance: "5 ether" // You can set the balance of the account in the dev environment
// Balances are in Wei, but you can specify the unit with its name
},
{
privateKeyFile: "path/to/file" // You can put more than one key, separated by , or ;
},
{
mnemonic: "12 word mnemonic",
addressIndex: "0", // Optionnal. The index to start getting the address
numAddresses: "1", // Optionnal. The number of addresses to get
hdpath: "m/44'/60'/0'/0/" // Optionnal. HD derivation path
}
]*/
},
// order of connections the dapp should connect to
dappConnection: [
"$WEB3", // uses pre existing web3 object if available (e.g in Mist)
"ws://localhost:8546",
"http://localhost:8545"
],
gas: "auto",
contracts: {
// example:
//SimpleStorage: {
// args: [ 100 ]
//}
}
}
};

View File

@ -1,16 +0,0 @@
{
"default": {
"deployment": {
"host": "localhost",
"port": 8545,
"type": "rpc"
},
"dappConnection": [
"$WEB3",
"http://localhost:8545"
],
"gas": "auto",
"contracts": {
}
}
}

View File

@ -0,0 +1,6 @@
module.exports = {
default: {
available_providers: ["ens"],
provider: "ens"
}
};

View File

@ -0,0 +1,35 @@
module.exports = {
default: {
enabled: true,
ipfs_bin: "ipfs",
provider: "ipfs",
available_providers: ["ipfs"],
upload: {
host: "localhost",
port: 5001
},
dappConnection: [
{
provider: "ipfs",
host: "localhost",
port: 5001,
getUrl: "http://localhost:8080/ipfs/"
}
]
// Configuration to start Swarm in the same terminal as `embark run`
/*,account: {
address: "YOUR_ACCOUNT_ADDRESS", // Address of account accessing Swarm
password: "PATH/TO/PASSWORD/FILE" // File containing the password of the account
},
swarmPath: "PATH/TO/SWARM/EXECUTABLE" // Path to swarm executable (default: swarm)*/
},
development: {
enabled: true,
provider: "ipfs",
upload: {
host: "localhost",
port: 5001,
getUrl: "http://localhost:8080/ipfs/"
}
}
};

View File

@ -1,17 +0,0 @@
{
"default": {
"enabled": true,
"ipfs_bin": "ipfs",
"provider": "ipfs",
"available_providers": ["ipfs"],
"host": "localhost",
"port": 5001
},
"development": {
"enabled": true,
"provider": "ipfs",
"host": "localhost",
"port": 5001,
"getUrl": "http://localhost:8080/ipfs/"
}
}

View File

@ -0,0 +1,5 @@
module.exports = {
enabled: true,
host: "localhost",
port: 8000
};

Some files were not shown because too many files have changed in this diff Show More