mirror of https://github.com/embarklabs/embark.git
Merge branch 'develop'
This commit is contained in:
commit
a6195d2517
|
@ -9,6 +9,9 @@
|
|||
"sourceType": "module",
|
||||
"ecmaVersion": 2017
|
||||
},
|
||||
"globals": {
|
||||
"__": true
|
||||
},
|
||||
"rules": {
|
||||
"accessor-pairs": "error",
|
||||
"array-bracket-newline": "error",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
language: node_js
|
||||
node_js:
|
||||
- "7"
|
||||
- "8"
|
||||
addons:
|
||||
code_climate:
|
||||
repo_token: 7454b1a666015e244c384d19f48c34e35d1ae58c3aa428ec542f10bbcb848358
|
||||
|
|
401
js/embark.js
401
js/embark.js
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
|
@ -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);
|
||||
});
|
||||
}
|
135
lib/cmd.js
135
lib/cmd.js
|
@ -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);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -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');
|
||||
}
|
||||
|
|
|
@ -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});
|
||||
}
|
||||
});
|
|
@ -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);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
|
@ -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
|
||||
};
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
|
@ -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;
|
||||
|
|
@ -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
|
@ -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 %>
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -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;
|
|
@ -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)
|
||||
];
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
|
@ -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);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
|
@ -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;
|
|
@ -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;
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
|
|
@ -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;
|
|
@ -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() {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
};
|
|
@ -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);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
|
@ -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):"
|
||||
}
|
|
@ -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}}...."
|
||||
}
|
|
@ -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}}...."
|
||||
}
|
|
@ -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}}...."
|
||||
}
|
245
lib/index.js
245
lib/index.js
|
@ -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;
|
||||
|
|
|
@ -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;
|
|
@ -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;
|
|
@ -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);
|
||||
};
|
|
@ -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;
|
|
@ -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;
|
|
@ -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;
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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'));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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;
|
|
@ -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 = {};
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
|
||||
|
|
|
@ -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});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
|
@ -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);
|
||||
}
|
||||
};
|
|
@ -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;
|
|
@ -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}`;
|
||||
};
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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'));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
});
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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'});
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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});
|
||||
});
|
||||
}
|
||||
});
|
|
@ -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;
|
|
@ -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;
|
|
@ -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;
|
|
@ -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;
|
|
@ -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);
|
||||
}
|
||||
});
|
|
@ -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;
|
|
@ -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;
|
|
@ -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});
|
||||
}
|
||||
}
|
||||
});
|
|
@ -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;
|
||||
});
|
||||
|
||||
}
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
|
|
@ -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));
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
File diff suppressed because it is too large
Load Diff
31
package.json
31
package.json
|
@ -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"
|
||||
}
|
||||
|
|
|
@ -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: ""
|
||||
}
|
||||
};
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
{
|
||||
"default": {
|
||||
"enabled": true,
|
||||
"provider": "whisper",
|
||||
"available_providers": ["whisper", "orbit"],
|
||||
"connection": {
|
||||
"host": "localhost",
|
||||
"port": 8546,
|
||||
"type": "ws"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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 ]
|
||||
//}
|
||||
}
|
||||
}
|
||||
};
|
|
@ -1,16 +0,0 @@
|
|||
{
|
||||
"default": {
|
||||
"deployment": {
|
||||
"host": "localhost",
|
||||
"port": 8545,
|
||||
"type": "rpc"
|
||||
},
|
||||
"dappConnection": [
|
||||
"$WEB3",
|
||||
"http://localhost:8545"
|
||||
],
|
||||
"gas": "auto",
|
||||
"contracts": {
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
module.exports = {
|
||||
default: {
|
||||
available_providers: ["ens"],
|
||||
provider: "ens"
|
||||
}
|
||||
};
|
|
@ -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/"
|
||||
}
|
||||
}
|
||||
};
|
|
@ -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/"
|
||||
}
|
||||
}
|
|
@ -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
Loading…
Reference in New Issue