mirror of https://github.com/embarklabs/embark.git
chore(@embark/): remove old modules blockchain-process & code-generator; remove old methods from engine.js (#1875)
chore (@embark/) remove old modules blockchain-process and code-generator chore (@embark/) remove old module blockchain-process chore (@embark/) remove embark blockchain & embark simulator cmds chore (@embark/) remove old methods from engine.js Revert "chore (@embark/) remove embark blockchain & embark simulator cmds" This reverts commit 703a424fb9e720b9342acb4cf9d00530f7393059. re-add cockpit modules re-add cockpit modules
This commit is contained in:
parent
0b9670fd33
commit
43386ff35d
|
@ -1,4 +0,0 @@
|
|||
engine-strict = true
|
||||
package-lock = false
|
||||
save-exact = true
|
||||
scripts-prepend-node-path = true
|
|
@ -1,71 +0,0 @@
|
|||
# Change Log
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [4.1.1](https://github.com/embark-framework/embark/compare/v4.1.0...v4.1.1) (2019-08-28)
|
||||
|
||||
**Note:** Version bump only for package embark-blockchain-connector
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [4.1.0](https://github.com/embark-framework/embark/compare/v4.1.0-beta.6...v4.1.0) (2019-08-12)
|
||||
|
||||
**Note:** Version bump only for package embark-blockchain-connector
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [4.1.0-beta.6](https://github.com/embark-framework/embark/compare/v4.1.0-beta.5...v4.1.0-beta.6) (2019-08-09)
|
||||
|
||||
**Note:** Version bump only for package embark-blockchain-connector
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [4.1.0-beta.5](https://github.com/embark-framework/embark/compare/v4.1.0-beta.4...v4.1.0-beta.5) (2019-07-10)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **@cockpit/explorers:** consistently display "Mined on" timestamps ([52d54f0](https://github.com/embark-framework/embark/commit/52d54f0))
|
||||
* **@embark/deployment:** don't over estimate gas when running tests against non-simulator nodes ([d76a82a](https://github.com/embark-framework/embark/commit/d76a82a)), closes [/github.com/trufflesuite/ganache-core/blob/8ad1ab29deccbbb4018f6961d0eb7ec984ad8fcb/lib/utils/gasEstimation.js#L33-L39](https://github.com//github.com/trufflesuite/ganache-core/blob/8ad1ab29deccbbb4018f6961d0eb7ec984ad8fcb/lib/utils/gasEstimation.js/issues/L33-L39)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [4.1.0-beta.4](https://github.com/embark-framework/embark/compare/v4.1.0-beta.3...v4.1.0-beta.4) (2019-06-27)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **@embark/blockchain-connector:** Add command to get full account info ([71cb161](https://github.com/embark-framework/embark/commit/71cb161))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [4.1.0-beta.3](https://github.com/embark-framework/embark/compare/v4.1.0-beta.2...v4.1.0-beta.3) (2019-06-07)
|
||||
|
||||
**Note:** Version bump only for package embark-blockchain-connector
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [4.1.0-beta.2](https://github.com/embark-framework/embark/compare/v4.1.0-beta.1...v4.1.0-beta.2) (2019-05-22)
|
||||
|
||||
**Note:** Version bump only for package embark-blockchain-connector
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [4.1.0-beta.1](https://github.com/embark-framework/embark/compare/v4.1.0-beta.0...v4.1.0-beta.1) (2019-05-15)
|
||||
|
||||
**Note:** Version bump only for package embark-blockchain-connector
|
|
@ -1,6 +0,0 @@
|
|||
# `embark-blockchain-connecter`
|
||||
|
||||
> Blockchain Connector module for Embark
|
||||
|
||||
Visit [embark.status.im](https://embark.status.im/) to get started with
|
||||
[Embark](https://github.com/embark-framework/embark).
|
|
@ -1,75 +0,0 @@
|
|||
{
|
||||
"name": "embark-blockchain-connector",
|
||||
"version": "4.1.1",
|
||||
"author": "Iuri Matias <iuri.matias@gmail.com>",
|
||||
"contributors": [],
|
||||
"description": "Blockchain Connector for Embark",
|
||||
"homepage": "https://github.com/embark-framework/embark/tree/master/packages/embark-blockchain-connector#readme",
|
||||
"bugs": "https://github.com/embark-framework/embark/issues",
|
||||
"keywords": [
|
||||
"blockchain",
|
||||
"dapps",
|
||||
"ethereum",
|
||||
"ipfs",
|
||||
"serverless",
|
||||
"solc",
|
||||
"solidity"
|
||||
],
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
"directory": "packages/embark-blockchain-connector",
|
||||
"type": "git",
|
||||
"url": "https://github.com/embark-framework/embark.git"
|
||||
},
|
||||
"main": "./dist/index.js",
|
||||
"scripts": {
|
||||
"build": "cross-env BABEL_ENV=node babel src --extensions \".js\" --out-dir dist --root-mode upward --source-maps",
|
||||
"ci": "npm run qa",
|
||||
"clean": "npm run reset",
|
||||
"lint": "npm-run-all lint:*",
|
||||
"lint:js": "eslint src/",
|
||||
"// lint:ts": "tslint -c tslint.json \"src/**/*.ts\"",
|
||||
"package": "npm pack",
|
||||
"// qa": "npm-run-all lint typecheck build package",
|
||||
"qa": "npm-run-all lint build package",
|
||||
"reset": "npx rimraf dist embark-*.tgz package",
|
||||
"start": "npm run watch",
|
||||
"// typecheck": "tsc",
|
||||
"watch": "run-p watch:*",
|
||||
"watch:build": "npm run build -- --verbose --watch",
|
||||
"// watch:typecheck": "npm run typecheck -- --preserveWatchOutput --watch"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": "../../.eslintrc.json"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime-corejs2": "7.3.1",
|
||||
"async": "2.6.1",
|
||||
"embark-core": "^4.1.1",
|
||||
"embark-i18n": "^4.1.1",
|
||||
"embark-utils": "^4.1.1",
|
||||
"embarkjs": "^4.1.1",
|
||||
"ethereumjs-tx": "1.3.7",
|
||||
"ethereumjs-util": "6.0.0",
|
||||
"ganache-cli": "6.4.3",
|
||||
"web3": "1.2.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "7.2.3",
|
||||
"@babel/core": "7.2.2",
|
||||
"cross-env": "5.2.0",
|
||||
"eslint": "5.7.0",
|
||||
"npm-run-all": "4.1.5",
|
||||
"rimraf": "3.0.0",
|
||||
"tslint": "5.16.0",
|
||||
"typescript": "3.4.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8.12.0 <12.0.0",
|
||||
"npm": ">=6.4.1",
|
||||
"yarn": ">=1.12.3"
|
||||
}
|
||||
}
|
|
@ -1,77 +0,0 @@
|
|||
const async = require('async');
|
||||
const TARGET = 0x7FFFFFFFFFFFFFFF;
|
||||
const ALREADY_FUNDED = 'alreadyFunded';
|
||||
|
||||
function fundAccount(web3, accountAddress, hexBalance, callback) {
|
||||
if (!hexBalance) {
|
||||
hexBalance = TARGET;
|
||||
}
|
||||
const targetBalance = web3.utils.toBN(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);
|
||||
}
|
||||
balance = web3.utils.toBN(balance);
|
||||
if (balance.gte(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.sub(accountBalance),
|
||||
gasPrice: gasPrice,
|
||||
nonce: lastNonce
|
||||
}, next);
|
||||
}
|
||||
], (err) => {
|
||||
if (err && err !== ALREADY_FUNDED) {
|
||||
return callback(err);
|
||||
}
|
||||
callback();
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = fundAccount;
|
|
@ -1,866 +0,0 @@
|
|||
import { __ } from 'embark-i18n';
|
||||
const Web3 = require('web3');
|
||||
const async = require('async');
|
||||
const Provider = require('./provider.js');
|
||||
const ethUtil = require('ethereumjs-util');
|
||||
const constants = require('embark-core/constants');
|
||||
const embarkJsUtils = require('embarkjs').Utils;
|
||||
const {bigNumberify} = require('ethers/utils/bignumber');
|
||||
const RLP = require('ethers/utils/rlp');
|
||||
import {AccountParser, buildUrl, dappPath, deconstructUrl} from 'embark-utils';
|
||||
|
||||
const WEB3_READY = 'blockchain:ready';
|
||||
|
||||
const BLOCK_LIMIT = 100;
|
||||
const BALANCE_10_ETHER_IN_HEX = '0x8AC7230489E80000';
|
||||
|
||||
// TODO: consider another name, this is the blockchain connector
|
||||
class BlockchainConnector {
|
||||
constructor(embark, options) {
|
||||
const self = this;
|
||||
this.embark = embark;
|
||||
this.plugins = options.plugins;
|
||||
this.logger = embark.logger;
|
||||
this.events = embark.events;
|
||||
this.config = embark.config;
|
||||
this.web3 = options.web3;
|
||||
this.isDev = options.isDev;
|
||||
this.isWeb3Ready = false;
|
||||
this.wait = options.wait;
|
||||
this.contractsSubscriptions = [];
|
||||
this.contractsEvents = [];
|
||||
this.fs = embark.fs;
|
||||
this.logFile = dappPath(".embark", "contractEvents.json");
|
||||
|
||||
this.writeLogFile = async.cargo((tasks, callback) => {
|
||||
const data = this._readEvents();
|
||||
|
||||
tasks.forEach(task => {
|
||||
data[new Date().getTime()] = task;
|
||||
});
|
||||
|
||||
this.fs.writeJson(this.logFile, data, err => {
|
||||
if (err) {
|
||||
console.error(err);
|
||||
}
|
||||
callback();
|
||||
});
|
||||
});
|
||||
|
||||
// self.events.setCommandHandler("blockchain:ready", self.onReady.bind(this));
|
||||
|
||||
// self.events.setCommandHandler("blockchain:web3:isReady", (cb) => {
|
||||
// cb(self.isWeb3Ready);
|
||||
// });
|
||||
|
||||
self.events.setCommandHandler("blockchain:object", (cb) => {
|
||||
cb(self);
|
||||
});
|
||||
|
||||
self.events.setCommandHandler("blockchain:getTransaction", (txHash, cb) => {
|
||||
self.getTransactionByHash(txHash, cb);
|
||||
});
|
||||
|
||||
embark.registerActionForEvent("contracts:deploy:afterAll", this.subscribeToContractEvents.bind(this));
|
||||
|
||||
if (!this.web3) {
|
||||
this.initWeb3(err => {
|
||||
if (err) {
|
||||
this.logger.error(__('Error initiating Web3 provider'), err.message || err);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.isWeb3Ready = true;
|
||||
}
|
||||
|
||||
this.registerServiceCheck();
|
||||
this.registerRequests();
|
||||
this.registerAPIRequests();
|
||||
this.registerEvents();
|
||||
this.subscribeToPendingTransactions();
|
||||
}
|
||||
|
||||
initWeb3(cb) {
|
||||
if (!cb) {
|
||||
cb = function(){};
|
||||
}
|
||||
if (this.isWeb3Ready) {
|
||||
this.events.emit(WEB3_READY);
|
||||
return cb();
|
||||
}
|
||||
|
||||
const self = this;
|
||||
this.web3 = new Web3();
|
||||
|
||||
if (self.wait) {
|
||||
self.wait = false;
|
||||
return cb();
|
||||
}
|
||||
|
||||
if (this.config.blockchainConfig.type === 'vm') {
|
||||
return this._setupVM(cb);
|
||||
}
|
||||
|
||||
let {type, host, port, protocol} = deconstructUrl(this.config.blockchainConfig.endpoint);
|
||||
if (!protocol) {
|
||||
protocol = (type === "rpc") ? 'http' : 'ws';
|
||||
}
|
||||
|
||||
if (!BlockchainConnector.ACCEPTED_TYPES.includes(type)) {
|
||||
this.logger.error(__("contracts config error: unknown deployment type %s", type));
|
||||
this.logger.error(__("Accepted types are: %s", BlockchainConnector.ACCEPTED_TYPES.join(', ')));
|
||||
}
|
||||
|
||||
if (type === 'rpc') {
|
||||
this.logger.warn('Using RPC as deployment connection type is deprecated. It is recommended to use WS.');
|
||||
}
|
||||
|
||||
protocol = (type === "rpc") ? protocol : 'ws';
|
||||
|
||||
const web3Endpoint = buildUrl(protocol, host, port);
|
||||
|
||||
const providerOptions = {
|
||||
web3: this.web3,
|
||||
blockchainConfig: this.config.blockchainConfig,
|
||||
logger: this.logger,
|
||||
isDev: this.isDev,
|
||||
type: type,
|
||||
web3Endpoint,
|
||||
events: this.events,
|
||||
fs: this.fs
|
||||
};
|
||||
this.provider = new Provider(providerOptions);
|
||||
|
||||
// self.events.request("processes:launch", "blockchain", (err) => {
|
||||
// if (err) {
|
||||
// return self.logger.error(err);
|
||||
// }
|
||||
// setTimeout(() => {
|
||||
self.provider.startWeb3Provider(async () => {
|
||||
try {
|
||||
const blockNumber = await self.web3.eth.getBlockNumber();
|
||||
await self.web3.eth.getBlock(blockNumber);
|
||||
self.provider.fundAccounts(() => {
|
||||
self._emitWeb3Ready();
|
||||
cb();
|
||||
});
|
||||
} catch (e) {
|
||||
const errorMessage = e.message || e;
|
||||
if (errorMessage.indexOf('no suitable peers available') > 0) {
|
||||
self.logger.warn(errorMessage);
|
||||
self.logger.warn(__('Your node is probably not synchronized. Wait until your node is synchronized before deploying'));
|
||||
process.exit(1);
|
||||
}
|
||||
self.logger.error(errorMessage);
|
||||
cb(errorMessage);
|
||||
}
|
||||
|
||||
try {
|
||||
const id = await this.getNetworkId();
|
||||
let networkId = self.config.blockchainConfig.networkId;
|
||||
if (!networkId &&
|
||||
constants.blockchain.networkIds[self.config.blockchainConfig.networkType]) {
|
||||
networkId = constants.blockchain.networkIds[self.config.blockchainConfig.networkType];
|
||||
}
|
||||
if (networkId && id.toString() !== networkId.toString()) {
|
||||
self.logger.warn(__('Connected to a blockchain node on network {{realId}} while your config specifies {{configId}}', {realId: id, configId: networkId}));
|
||||
self.logger.warn(__('Make sure you started the right blockchain node'));
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
});
|
||||
// });
|
||||
}
|
||||
|
||||
_setupVM(cb) {
|
||||
const sim = this._getSimulator();
|
||||
const coverage = !!this.config.blockchainConfig.coverage;
|
||||
|
||||
let accounts = null;
|
||||
if (this.config.blockchainConfig.accounts) {
|
||||
accounts = AccountParser.parseAccountsConfig(this.config.blockchainConfig.accounts, this.web3, dappPath());
|
||||
accounts = accounts.map((account) => {
|
||||
if (!account.hexBalance) {
|
||||
account.hexBalance = BALANCE_10_ETHER_IN_HEX;
|
||||
}
|
||||
return {balance: account.hexBalance, secretKey: account.privateKey};
|
||||
});
|
||||
}
|
||||
|
||||
const options = Object.assign(this.config.blockchainConfig.endpoint ? deconstructUrl(this.config.blockchainConfig.endpoint): {}, {
|
||||
gasPrice: "0x01",
|
||||
gasLimit: "0xfffffffffffff",
|
||||
allowUnlimitedContractSize: coverage,
|
||||
accounts
|
||||
});
|
||||
this.provider = sim.provider(options);
|
||||
|
||||
if (coverage) {
|
||||
// Here we patch the sendAsync method on the provider. The goal behind this is to force pure/constant/view calls to become
|
||||
// transactions, so that we can pull in execution traces and account for those executions in code coverage.
|
||||
//
|
||||
// Instead of a simple call, here's what happens:
|
||||
//
|
||||
// 1) A transaction is sent with the same payload, and a pre-defined gas price;
|
||||
// 2) We wait for the transaction to be mined by asking for the receipt;
|
||||
// 3) Once we get the receipt back, we dispatch the real call and pass the original callback;
|
||||
//
|
||||
// This will still allow tests to get the return value from the call and run contracts unmodified.
|
||||
this.provider.realSendAsync = this.provider.sendAsync.bind(this.provider);
|
||||
this.provider.sendAsync = (payload, cb) => {
|
||||
if(payload.method !== 'eth_call') {
|
||||
return this.provider.realSendAsync(payload, cb);
|
||||
}
|
||||
this.events.request('reporter:toggleGasListener');
|
||||
let newParams = Object.assign({}, payload.params[0]);
|
||||
let newPayload = {
|
||||
id: payload.id + 1,
|
||||
method: constants.blockchain.transactionMethods.eth_sendTransaction,
|
||||
params: [newParams],
|
||||
jsonrpc: payload.jsonrpc
|
||||
};
|
||||
|
||||
this.provider.realSendAsync(newPayload, (_err, response) => {
|
||||
let txHash = response.result;
|
||||
this.web3.eth.getTransactionReceipt(txHash, (_err, _res) => {
|
||||
this.events.request('reporter:toggleGasListener');
|
||||
this.provider.realSendAsync(payload, cb);
|
||||
});
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
this.web3.setProvider(this.provider);
|
||||
this._emitWeb3Ready();
|
||||
cb();
|
||||
}
|
||||
|
||||
_emitWeb3Ready() {
|
||||
this.registerWeb3Object(() => {
|
||||
this.isWeb3Ready = true;
|
||||
this.events.emit(WEB3_READY);
|
||||
});
|
||||
this.subscribeToPendingTransactions();
|
||||
}
|
||||
|
||||
_getSimulator() {
|
||||
try {
|
||||
return require('ganache-cli');
|
||||
} catch (e) {
|
||||
const moreInfo = 'For more information see https://github.com/trufflesuite/ganache-cli';
|
||||
if (e.code === 'MODULE_NOT_FOUND') {
|
||||
this.logger.error(__('Simulator not found; Please install it with "%s"', 'npm install ganache-cli --save'));
|
||||
this.logger.error(moreInfo);
|
||||
throw e;
|
||||
}
|
||||
this.logger.error("==============");
|
||||
this.logger.error(__("Tried to load Ganache CLI (testrpc), but an error occurred. This is a problem with Ganache CLI"));
|
||||
this.logger.error(moreInfo);
|
||||
this.logger.error("==============");
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
registerEvents() {
|
||||
this.events.on('check:wentOffline:Ethereum', () => {
|
||||
this.logger.warn('Ethereum went offline: stopping web3 provider...');
|
||||
this.provider.stop();
|
||||
this.isWeb3Ready = false;
|
||||
});
|
||||
|
||||
this.events.on('blockchain:contracts:event', this._saveEvent.bind(this));
|
||||
}
|
||||
|
||||
onReady(callback) {
|
||||
if (this.isWeb3Ready) {
|
||||
return callback();
|
||||
}
|
||||
|
||||
this.events.once(WEB3_READY, () => {
|
||||
callback();
|
||||
});
|
||||
}
|
||||
|
||||
registerServiceCheck() {
|
||||
const self = this;
|
||||
const NO_NODE = 'noNode';
|
||||
|
||||
this.events.request("services:register", 'Ethereum', function(cb) {
|
||||
async.waterfall([
|
||||
function checkNodeConnection(next) {
|
||||
if (!self.provider || !self.provider.connected()) {
|
||||
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.x
|
||||
self.web3._requestManager.send({method: 'web3_clientVersion', params: []}, (err, version) => {
|
||||
if (err || !version) {
|
||||
self.isWeb3Ready = false;
|
||||
return next(null, {name: "Ethereum node not found", status: 'off'});
|
||||
}
|
||||
if (version.indexOf("/") < 0) {
|
||||
self.events.emit(WEB3_READY);
|
||||
self.isWeb3Ready = true;
|
||||
return next(null, {name: version, status: 'on'});
|
||||
}
|
||||
let nodeName = version.split("/")[0];
|
||||
let versionNumber = version.split("/")[1].split("-")[0];
|
||||
let name = nodeName + " " + versionNumber + " (Ethereum)";
|
||||
|
||||
self.events.emit(WEB3_READY);
|
||||
self.isWeb3Ready = true;
|
||||
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:reset", function(cb) {
|
||||
self.isWeb3Ready = false;
|
||||
self.initWeb3((err) => {
|
||||
if (err) {
|
||||
return cb(err);
|
||||
}
|
||||
self.events.emit('blockchain:reseted');
|
||||
cb();
|
||||
});
|
||||
});
|
||||
|
||||
this.events.setCommandHandler("blockchain:get", function(cb) {
|
||||
cb(self.web3);
|
||||
});
|
||||
|
||||
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:getAccounts", function(cb) {
|
||||
self.getAccounts(cb);
|
||||
});
|
||||
|
||||
this.events.setCommandHandler("blockchain:getBalance", function(address, cb) {
|
||||
self.getBalance(address, cb);
|
||||
});
|
||||
|
||||
this.events.setCommandHandler("blockchain:block:byNumber", function(blockNumber, cb) {
|
||||
self.getBlock(blockNumber, cb);
|
||||
});
|
||||
|
||||
this.events.setCommandHandler("blockchain:block:byHash", function(blockHash, cb) {
|
||||
self.getBlock(blockHash, cb);
|
||||
});
|
||||
|
||||
this.events.setCommandHandler("blockchain:gasPrice", function(cb) {
|
||||
self.getGasPrice(cb);
|
||||
});
|
||||
|
||||
this.events.setCommandHandler("blockchain:networkId", function(cb) {
|
||||
self.getNetworkId().then(cb);
|
||||
});
|
||||
|
||||
this.events.setCommandHandler("blockchain:contract:create", function(params, cb) {
|
||||
cb(self.ContractObject(params));
|
||||
});
|
||||
}
|
||||
|
||||
registerAPIRequests() {
|
||||
const self = this;
|
||||
|
||||
let plugin = self.plugins.createPlugin('blockchain', {});
|
||||
plugin.registerAPICall(
|
||||
'get',
|
||||
'/embark-api/blockchain/accounts',
|
||||
(req, res) => {
|
||||
self.getAccountsWithTransactionCount(res.send.bind(res));
|
||||
}
|
||||
);
|
||||
|
||||
plugin.registerAPICall(
|
||||
'get',
|
||||
'/embark-api/blockchain/accounts/:address',
|
||||
(req, res) => {
|
||||
self.getAccount(req.params.address, res.send.bind(res));
|
||||
}
|
||||
);
|
||||
|
||||
plugin.registerAPICall(
|
||||
'get',
|
||||
'/embark-api/blockchain/blocks',
|
||||
(req, res) => {
|
||||
const from = parseInt(req.query.from, 10);
|
||||
const limit = req.query.limit || 10;
|
||||
self.getBlocks(from, limit, !!req.query.txObjects, !!req.query.txReceipts, res.send.bind(res));
|
||||
}
|
||||
);
|
||||
|
||||
plugin.registerAPICall(
|
||||
'get',
|
||||
'/embark-api/blockchain/blocks/:blockNumber',
|
||||
(req, res) => {
|
||||
self.getBlock(req.params.blockNumber, (err, block) => {
|
||||
if (err) {
|
||||
self.logger.error(err);
|
||||
}
|
||||
res.send(block);
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
plugin.registerAPICall(
|
||||
'get',
|
||||
'/embark-api/blockchain/transactions',
|
||||
(req, res) => {
|
||||
let blockFrom = parseInt(req.query.blockFrom, 10);
|
||||
let blockLimit = req.query.blockLimit || 10;
|
||||
self.getTransactions(blockFrom, blockLimit, res.send.bind(res));
|
||||
}
|
||||
);
|
||||
|
||||
plugin.registerAPICall(
|
||||
'get',
|
||||
'/embark-api/blockchain/transactions/:hash',
|
||||
(req, res) => {
|
||||
self.getTransactionByHash(req.params.hash, (err, transaction) => {
|
||||
if (!err) return res.send(transaction);
|
||||
|
||||
self.getTransactionByRawTransactionHash(req.params.hash, (err, transaction) => {
|
||||
if(err) return res.send({ error: "Could not find or decode transaction hash" });
|
||||
res.send(transaction);
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
plugin.registerAPICall(
|
||||
'ws',
|
||||
'/embark-api/blockchain/blockHeader',
|
||||
(ws) => {
|
||||
self.events.on('block:header', (block) => {
|
||||
ws.send(JSON.stringify({block: block}), () => {});
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
plugin.registerAPICall(
|
||||
'ws',
|
||||
'/embark-api/blockchain/contracts/event',
|
||||
(ws) => {
|
||||
this.events.on('blockchain:contracts:event', (data) => {
|
||||
ws.send(JSON.stringify(data), () => {});
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
plugin.registerAPICall(
|
||||
'get',
|
||||
'/embark-api/blockchain/contracts/events',
|
||||
(_req, res) => {
|
||||
res.send(JSON.stringify(this._getEvents()));
|
||||
}
|
||||
);
|
||||
|
||||
plugin.registerAPICall(
|
||||
'post',
|
||||
'/embark-api/messages/sign',
|
||||
(req, res) => {
|
||||
const signer = req.body.address;
|
||||
const message = req.body.message;
|
||||
this.web3.eth.sign(message, signer).then(signature => {
|
||||
res.send({signer, signature, message});
|
||||
}).catch(e => res.send({ error: e.message }));
|
||||
}
|
||||
);
|
||||
|
||||
plugin.registerAPICall(
|
||||
'post',
|
||||
'/embark-api/messages/verify',
|
||||
(req, res) => {
|
||||
let signature;
|
||||
try {
|
||||
signature = JSON.parse(req.body.message);
|
||||
} catch(e) {
|
||||
return res.send({ error: e.message });
|
||||
}
|
||||
|
||||
this.web3.eth.personal.ecRecover(signature.message, signature.signature)
|
||||
.then(address => res.send({address}))
|
||||
.catch(e => res.send({ error: e.message }));
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
getAccountsWithTransactionCount(callback) {
|
||||
let self = this;
|
||||
self.getAccounts((err, addresses) => {
|
||||
let accounts = [];
|
||||
async.eachOf(addresses, (address, index, eachCb) => {
|
||||
let account = {address, index};
|
||||
async.waterfall([
|
||||
function(callback) {
|
||||
self.getTransactionCount(address, (err, count) => {
|
||||
if (err) {
|
||||
self.logger.error(err);
|
||||
account.transactionCount = 0;
|
||||
} else {
|
||||
account.transactionCount = count;
|
||||
}
|
||||
callback(null, account);
|
||||
});
|
||||
},
|
||||
function(account, callback) {
|
||||
self.getBalance(address, (err, balance) => {
|
||||
if (err) {
|
||||
self.logger.error(err);
|
||||
account.balance = 0;
|
||||
} else {
|
||||
account.balance = self.web3.utils.fromWei(balance);
|
||||
}
|
||||
callback(null, account);
|
||||
});
|
||||
}
|
||||
], function(_err, account) {
|
||||
accounts.push(account);
|
||||
eachCb();
|
||||
});
|
||||
}, function() {
|
||||
callback(accounts);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
getAccount(address, callback) {
|
||||
let self = this;
|
||||
async.waterfall([
|
||||
function(next) {
|
||||
self.getAccountsWithTransactionCount((accounts) => {
|
||||
let account = accounts.find((a) => a.address === address);
|
||||
if (!account) {
|
||||
return next("No account found with this address");
|
||||
}
|
||||
next(null, account);
|
||||
});
|
||||
},
|
||||
function(account, next) {
|
||||
self.getBlockNumber((err, blockNumber) => {
|
||||
if (err) {
|
||||
self.logger.error(err);
|
||||
next(err);
|
||||
} else {
|
||||
next(null, blockNumber, account);
|
||||
}
|
||||
});
|
||||
},
|
||||
function(blockNumber, account, next) {
|
||||
self.getTransactions(blockNumber - BLOCK_LIMIT, BLOCK_LIMIT, (transactions) => {
|
||||
account.transactions = transactions.filter((transaction) => transaction.from === address);
|
||||
next(null, account);
|
||||
});
|
||||
}
|
||||
], function(err, result) {
|
||||
if (err) {
|
||||
callback();
|
||||
}
|
||||
callback(result);
|
||||
});
|
||||
}
|
||||
|
||||
getTransactions(blockFrom, blockLimit, callback) {
|
||||
this.getBlocks(blockFrom, blockLimit, true, true, (blocks) => {
|
||||
let transactions = blocks.reduce((acc, block) => {
|
||||
if (!block || !block.transactions) {
|
||||
return acc;
|
||||
}
|
||||
return acc.concat(block.transactions);
|
||||
}, []);
|
||||
callback(transactions);
|
||||
});
|
||||
}
|
||||
|
||||
getBlocks(from, limit, returnTransactionObjects, includeTransactionReceipts, callback) {
|
||||
let self = this;
|
||||
let blocks = [];
|
||||
async.waterfall([
|
||||
function(next) {
|
||||
if (!isNaN(from)) {
|
||||
return next();
|
||||
}
|
||||
self.getBlockNumber((err, blockNumber) => {
|
||||
if (err) {
|
||||
self.logger.error(err);
|
||||
from = 0;
|
||||
} else {
|
||||
from = blockNumber;
|
||||
}
|
||||
next();
|
||||
});
|
||||
},
|
||||
function(next) {
|
||||
async.times(limit, function(n, eachCb) {
|
||||
self.web3.eth.getBlock(from - n, returnTransactionObjects, function(err, block) {
|
||||
if (err) {
|
||||
// FIXME Returns an error because we are too low
|
||||
return eachCb();
|
||||
}
|
||||
if (!block) {
|
||||
return eachCb();
|
||||
}
|
||||
if (!(returnTransactionObjects && includeTransactionReceipts) ||
|
||||
!(block.transactions && block.transactions.length)) {
|
||||
blocks.push(block);
|
||||
return eachCb();
|
||||
}
|
||||
return Promise.all(block.transactions.map(tx => (
|
||||
self.web3.eth.getTransactionReceipt(tx.hash)
|
||||
)))
|
||||
.then(receipts => {
|
||||
block.transactions.forEach((tx, index) => {
|
||||
tx['receipt'] = receipts[index];
|
||||
tx['timestamp'] = block.timestamp;
|
||||
});
|
||||
blocks.push(block);
|
||||
eachCb();
|
||||
})
|
||||
.catch((err) => {
|
||||
self.logger.error(err.message || err);
|
||||
eachCb();
|
||||
});
|
||||
});
|
||||
}, next);
|
||||
}
|
||||
], function() {
|
||||
callback(blocks);
|
||||
});
|
||||
}
|
||||
|
||||
defaultAccount() {
|
||||
return this.web3.eth.defaultAccount;
|
||||
}
|
||||
|
||||
getBlockNumber(cb) {
|
||||
return this.web3.eth.getBlockNumber(cb);
|
||||
}
|
||||
|
||||
setDefaultAccount(account) {
|
||||
this.web3.eth.defaultAccount = account;
|
||||
}
|
||||
|
||||
getAccounts(cb) {
|
||||
this.web3.eth.getAccounts(cb);
|
||||
}
|
||||
|
||||
getTransactionCount(address, cb) {
|
||||
this.web3.eth.getTransactionCount(address, cb);
|
||||
}
|
||||
|
||||
getBalance(address, cb) {
|
||||
this.web3.eth.getBalance(address, cb);
|
||||
}
|
||||
|
||||
getCode(address, cb) {
|
||||
this.web3.eth.getCode(address, cb);
|
||||
}
|
||||
|
||||
getBlock(blockNumber, cb) {
|
||||
this.web3.eth.getBlock(blockNumber, true, (err, block) => {
|
||||
if (err) return cb(err);
|
||||
if (block.transactions && block.transactions.length) {
|
||||
block.transactions.forEach(tx => {
|
||||
tx.timestamp = block.timestamp;
|
||||
});
|
||||
}
|
||||
cb(null, block);
|
||||
});
|
||||
}
|
||||
|
||||
getTransactionByHash(hash, cb) {
|
||||
this.web3.eth.getTransaction(hash, cb);
|
||||
}
|
||||
|
||||
getTransactionByRawTransactionHash(hash, cb) {
|
||||
let rawData, decoded;
|
||||
|
||||
try {
|
||||
rawData = Buffer.from(ethUtil.stripHexPrefix(hash), 'hex');
|
||||
decoded = RLP.decode(rawData);
|
||||
} catch(e) {
|
||||
return cb("could not decode transaction");
|
||||
}
|
||||
|
||||
const [
|
||||
nonce,
|
||||
gasPrice,
|
||||
gasLimit,
|
||||
to,
|
||||
value,
|
||||
data,
|
||||
v,
|
||||
r,
|
||||
s
|
||||
] = decoded;
|
||||
|
||||
const transaction = {
|
||||
nonce: bigNumberify(nonce).toNumber(),
|
||||
gasPrice: bigNumberify(gasPrice).toNumber(),
|
||||
gasLimit: bigNumberify(gasLimit).toNumber(),
|
||||
data: data,
|
||||
v: `0x${v.toString('hex').toLowerCase()}`,
|
||||
r: `0x${r.toString('hex').toLowerCase()}`,
|
||||
s: `0x${s.toString('hex').toLowerCase()}`,
|
||||
value: value.toString('utf8'),
|
||||
to: `0x${to.toString('hex').toLowerCase()}`
|
||||
};
|
||||
|
||||
cb(null, transaction);
|
||||
}
|
||||
|
||||
getGasPrice(cb) {
|
||||
const self = this;
|
||||
this.onReady(() => {
|
||||
self.web3.eth.getGasPrice(cb);
|
||||
});
|
||||
}
|
||||
|
||||
getClientVersion(cb) {
|
||||
this.web3._requestManager.send({method: 'web3_clientVersion', params: []}, cb);
|
||||
}
|
||||
|
||||
getNetworkId() {
|
||||
return this.web3.eth.net.getId();
|
||||
}
|
||||
|
||||
getTransaction(hash, cb) {
|
||||
return this.web3.eth.getTransaction(hash, cb);
|
||||
}
|
||||
|
||||
ContractObject(params) {
|
||||
return new this.web3.eth.Contract(params.abi, params.address);
|
||||
}
|
||||
|
||||
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, hashCb) {
|
||||
embarkJsUtils.secureSend(this.web3, deployContractObject, {
|
||||
from: params.from, gas: params.gas, gasPrice: params.gasPrice
|
||||
}, true, cb, hashCb);
|
||||
}
|
||||
|
||||
determineDefaultAccount(cb) {
|
||||
const self = this;
|
||||
self.getAccounts(function(err, accounts) {
|
||||
if (err) {
|
||||
self.logger.error(err);
|
||||
return cb(new Error(err));
|
||||
}
|
||||
let accountConfig = self.config.blockchainConfig.account;
|
||||
let selectedAccount = accountConfig && accountConfig.address;
|
||||
const defaultAccount = selectedAccount || accounts[0];
|
||||
self.setDefaultAccount(defaultAccount);
|
||||
cb(null, defaultAccount);
|
||||
});
|
||||
}
|
||||
|
||||
registerWeb3Object(cb = () => {}) {
|
||||
// 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, cb);
|
||||
}
|
||||
|
||||
subscribeToPendingTransactions() {
|
||||
const self = this;
|
||||
this.onReady(() => {
|
||||
if (self.logsSubscription) {
|
||||
self.logsSubscription.unsubscribe();
|
||||
}
|
||||
self.logsSubscription = self.web3.eth
|
||||
.subscribe('newBlockHeaders', () => {})
|
||||
.on("data", function (blockHeader) {
|
||||
self.events.emit('block:header', blockHeader);
|
||||
});
|
||||
|
||||
if (self.pendingSubscription) {
|
||||
self.pendingSubscription.unsubscribe();
|
||||
}
|
||||
self.pendingSubscription = self.web3.eth
|
||||
.subscribe('pendingTransactions', function(error, transaction){
|
||||
if (!error) {
|
||||
self.events.emit('block:pending:transaction', transaction);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
subscribeToContractEvents(callback) {
|
||||
this.contractsSubscriptions.forEach((eventEmitter) => {
|
||||
const reqMgr = eventEmitter.options.requestManager;
|
||||
// attempting an eth_unsubscribe when not connected throws an
|
||||
// "connection not open on send()" error
|
||||
if(reqMgr && reqMgr.provider && reqMgr.provider.connected) {
|
||||
eventEmitter.unsubscribe();
|
||||
}
|
||||
});
|
||||
this.contractsSubscriptions = [];
|
||||
this.events.request("contracts:list", (_err, contractsList) => {
|
||||
contractsList.forEach(contractObject => {
|
||||
if (!contractObject.deployedAddress){
|
||||
return;
|
||||
}
|
||||
|
||||
const contract = this.ContractObject({abi: contractObject.abiDefinition, address: contractObject.deployedAddress});
|
||||
const eventEmitter = contract.events.allEvents();
|
||||
this.contractsSubscriptions.push(eventEmitter);
|
||||
eventEmitter.on('data', (data) => {
|
||||
const dataWithName = Object.assign(data, {name: contractObject.className});
|
||||
this.contractsEvents.push(dataWithName);
|
||||
this.events.emit('blockchain:contracts:event', dataWithName);
|
||||
});
|
||||
});
|
||||
callback();
|
||||
});
|
||||
}
|
||||
|
||||
_getEvents() {
|
||||
const data = this._readEvents();
|
||||
return Object.values(data).reverse();
|
||||
}
|
||||
|
||||
_saveEvent(event) {
|
||||
this.writeLogFile.push(event);
|
||||
}
|
||||
|
||||
_readEvents() {
|
||||
this.fs.ensureFileSync(this.logFile);
|
||||
try {
|
||||
return JSON.parse(this.fs.readFileSync(this.logFile));
|
||||
} catch(_error) {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BlockchainConnector.ACCEPTED_TYPES = ['rpc', 'ws', 'vm'];
|
||||
module.exports = BlockchainConnector;
|
|
@ -1,191 +0,0 @@
|
|||
import {__} from 'embark-i18n';
|
||||
const async = require('async');
|
||||
const { AccountParser, dappPath } = require('embark-utils');
|
||||
const fundAccount = require('./fundAccount');
|
||||
const constants = require('embark-core/constants');
|
||||
const Transaction = require('ethereumjs-tx');
|
||||
const ethUtil = require('ethereumjs-util');
|
||||
|
||||
class Provider {
|
||||
constructor(options) {
|
||||
this.web3 = options.web3;
|
||||
this.blockchainConfig = options.blockchainConfig;
|
||||
this.type = options.type;
|
||||
this.web3Endpoint = options.web3Endpoint;
|
||||
this.logger = options.logger;
|
||||
this.isDev = options.isDev;
|
||||
this.events = options.events;
|
||||
this.nonceCache = {};
|
||||
this.accounts = [];
|
||||
|
||||
this.events.setCommandHandler("blockchain:provider:contract:accounts:get", cb => {
|
||||
const accounts = this.accounts.map(a => a.address || a);
|
||||
cb(null, accounts);
|
||||
});
|
||||
this.events.setCommandHandler("blockchain:provider:contract:accounts:getAll", (cb) => {
|
||||
cb(null, this.accounts);
|
||||
});
|
||||
}
|
||||
|
||||
getNonce(address, callback) {
|
||||
this.web3.eth.getTransactionCount(address, (_error, transactionCount) => {
|
||||
if(this.nonceCache[address] === undefined) {
|
||||
this.nonceCache[address] = -1;
|
||||
}
|
||||
|
||||
if (transactionCount > this.nonceCache[address]) {
|
||||
this.nonceCache[address] = transactionCount;
|
||||
return callback(this.nonceCache[address]);
|
||||
}
|
||||
|
||||
this.nonceCache[address]++;
|
||||
callback(this.nonceCache[address]);
|
||||
});
|
||||
}
|
||||
|
||||
startWeb3Provider(callback) {
|
||||
const self = this;
|
||||
|
||||
if (this.type === 'rpc') {
|
||||
self.provider = new this.web3.providers.HttpProvider(self.web3Endpoint);
|
||||
} else if (this.type === 'ws') {
|
||||
// Note: don't pass to the provider things like {headers: {Origin: "embark"}}. Origin header is for browser to fill
|
||||
// to protect user, it has no meaning if it is used server-side. See here for more details: https://github.com/ethereum/go-ethereum/issues/16608
|
||||
// Moreover, Parity reject origins that are not urls so if you try to connect with Origin: "embark" it gives the following error:
|
||||
// << Blocked connection to WebSockets server from untrusted origin: Some("embark") >>
|
||||
// The best choice is to use void origin, BUT Geth rejects void origin, so to keep both clients happy we can use http://embark
|
||||
self.provider = new this.web3.providers.WebsocketProvider(self.web3Endpoint, {
|
||||
headers: {Origin: constants.embarkResourceOrigin},
|
||||
// TODO remove this when Geth fixes this: https://github.com/ethereum/go-ethereum/issues/16846
|
||||
clientConfig: {
|
||||
fragmentationThreshold: 81920
|
||||
}
|
||||
});
|
||||
|
||||
self.provider.on('error', () => self.logger.error('Websocket Error'));
|
||||
self.provider.on('end', () => self.logger.error('Websocket connection ended'));
|
||||
} else {
|
||||
return callback(__("contracts config error: unknown deployment type %s", this.type));
|
||||
}
|
||||
self.web3.setProvider(self.provider);
|
||||
|
||||
self.web3.eth.getAccounts((err, accounts = []) => {
|
||||
if (err) {
|
||||
self.logger.warn('Error while getting the node\'s accounts.', err.message || err);
|
||||
}
|
||||
|
||||
self.accounts = AccountParser.parseAccountsConfig(self.blockchainConfig.accounts, self.web3, dappPath(), self.logger, accounts);
|
||||
|
||||
if (!self.accounts.length) {
|
||||
self.accounts = accounts;
|
||||
}
|
||||
self.addresses = [];
|
||||
|
||||
self.accounts.forEach(account => {
|
||||
self.addresses.push(account.address || account);
|
||||
if (account.privateKey) {
|
||||
self.web3.eth.accounts.wallet.add(account);
|
||||
}
|
||||
});
|
||||
// Normalize addresses and remove duplicates
|
||||
self.addresses = [...new Set(self.addresses.map(ethUtil.toChecksumAddress))];
|
||||
|
||||
if (self.accounts.length) {
|
||||
self.web3.eth.defaultAccount = self.addresses[0];
|
||||
}
|
||||
|
||||
const realSend = self.provider.send.bind(self.provider);
|
||||
|
||||
// Allow to run transaction in parallel by resolving
|
||||
// the nonce manually.
|
||||
// For each transaction, resolve the nonce by taking the
|
||||
// max of current transaction count and the cache we keep
|
||||
// locally.
|
||||
// Deconstruct the transaction and update the nonce.
|
||||
// Before updating the transaction, it must be signed.
|
||||
self.runTransaction = async.queue(({payload}, callback) => {
|
||||
const rawTx = payload.params[0];
|
||||
const rawData = Buffer.from(ethUtil.stripHexPrefix(rawTx), 'hex');
|
||||
const tx = new Transaction(rawData, 'hex');
|
||||
const address = '0x' + tx.getSenderAddress().toString('hex').toLowerCase();
|
||||
|
||||
self.getNonce(address, (newNonce) => {
|
||||
tx.nonce = newNonce;
|
||||
const key = ethUtil.stripHexPrefix(self.web3.eth.accounts.wallet[address].privateKey);
|
||||
const privKey = Buffer.from(key, 'hex');
|
||||
tx.sign(privKey);
|
||||
payload.params[0] = '0x' + tx.serialize().toString('hex');
|
||||
return realSend(payload, (error, result) => {
|
||||
self.web3.eth.getTransaction(result.result, () => {
|
||||
callback(error, result);
|
||||
});
|
||||
});
|
||||
});
|
||||
}, 1);
|
||||
|
||||
self.provider.send = function(payload, cb) {
|
||||
if (payload.method === 'eth_accounts' || payload.method === 'personal_listAccounts') {
|
||||
return realSend(payload, function(err, result) {
|
||||
if (err) {
|
||||
return cb(err);
|
||||
}
|
||||
if (self.accounts.length) {
|
||||
result.result = [
|
||||
...new Set([
|
||||
...self.addresses,
|
||||
...result.result.map(ethUtil.toChecksumAddress)
|
||||
])
|
||||
];
|
||||
}
|
||||
cb(null, result);
|
||||
});
|
||||
} else if (payload.method === constants.blockchain.transactionMethods.eth_sendRawTransaction) {
|
||||
return self.runTransaction.push({payload}, cb);
|
||||
}
|
||||
|
||||
realSend(payload, cb);
|
||||
};
|
||||
callback();
|
||||
});
|
||||
}
|
||||
|
||||
connected() {
|
||||
if (this.type === 'rpc') {
|
||||
return !!this.provider;
|
||||
} else if (this.type === 'ws') {
|
||||
return this.provider && this.provider.connection._connection && this.provider.connection._connection.connected;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
stop() {
|
||||
if (this.provider && this.provider.removeAllListeners) {
|
||||
this.provider.removeAllListeners('connect');
|
||||
this.provider.removeAllListeners('error');
|
||||
this.provider.removeAllListeners('end');
|
||||
this.provider.removeAllListeners('data');
|
||||
this.provider.responseCallbacks = {};
|
||||
}
|
||||
this.provider = null;
|
||||
this.web3.setProvider(null);
|
||||
}
|
||||
|
||||
fundAccounts(callback) {
|
||||
const self = this;
|
||||
if (!self.accounts.length) {
|
||||
return callback();
|
||||
}
|
||||
if (!self.isDev) {
|
||||
return callback();
|
||||
}
|
||||
async.eachLimit(self.accounts, 1, (account, eachCb) => {
|
||||
if (!account.address) {
|
||||
return eachCb();
|
||||
}
|
||||
fundAccount(self.web3, account.address, account.hexBalance, eachCb);
|
||||
}, callback);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Provider;
|
|
@ -1,4 +0,0 @@
|
|||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"include": ["src/**/*"]
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
{
|
||||
"extends": "../../tslint.json"
|
||||
}
|
|
@ -1,4 +0,0 @@
|
|||
engine-strict = true
|
||||
package-lock = false
|
||||
save-exact = true
|
||||
scripts-prepend-node-path = true
|
|
@ -1,64 +0,0 @@
|
|||
# Change Log
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [4.1.1](https://github.com/embark-framework/embark/compare/v4.1.0...v4.1.1) (2019-08-28)
|
||||
|
||||
**Note:** Version bump only for package embark-blockchain-process
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [4.1.0](https://github.com/embark-framework/embark/compare/v4.1.0-beta.6...v4.1.0) (2019-08-12)
|
||||
|
||||
**Note:** Version bump only for package embark-blockchain-process
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [4.1.0-beta.6](https://github.com/embark-framework/embark/compare/v4.1.0-beta.5...v4.1.0-beta.6) (2019-08-09)
|
||||
|
||||
**Note:** Version bump only for package embark-blockchain-process
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [4.1.0-beta.5](https://github.com/embark-framework/embark/compare/v4.1.0-beta.4...v4.1.0-beta.5) (2019-07-10)
|
||||
|
||||
**Note:** Version bump only for package embark-blockchain-process
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [4.1.0-beta.4](https://github.com/embark-framework/embark/compare/v4.1.0-beta.3...v4.1.0-beta.4) (2019-06-27)
|
||||
|
||||
**Note:** Version bump only for package embark-blockchain-process
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [4.1.0-beta.3](https://github.com/embark-framework/embark/compare/v4.1.0-beta.2...v4.1.0-beta.3) (2019-06-07)
|
||||
|
||||
**Note:** Version bump only for package embark-blockchain-process
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [4.1.0-beta.2](https://github.com/embark-framework/embark/compare/v4.1.0-beta.1...v4.1.0-beta.2) (2019-05-22)
|
||||
|
||||
**Note:** Version bump only for package embark-blockchain-process
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [4.1.0-beta.1](https://github.com/embark-framework/embark/compare/v4.1.0-beta.0...v4.1.0-beta.1) (2019-05-15)
|
||||
|
||||
**Note:** Version bump only for package embark-blockchain-process
|
|
@ -1,6 +0,0 @@
|
|||
# `embark-blockchain-process`
|
||||
|
||||
> Blockchain Process abstraction for Embark
|
||||
|
||||
Visit [embark.status.im](https://embark.status.im/) to get started with
|
||||
[Embark](https://github.com/embark-framework/embark).
|
|
@ -1,77 +0,0 @@
|
|||
{
|
||||
"name": "embark-blockchain-process",
|
||||
"version": "4.1.1",
|
||||
"author": "Iuri Matias <iuri.matias@gmail.com>",
|
||||
"contributors": [],
|
||||
"description": "Blockchain process abstraction for Embark",
|
||||
"homepage": "https://github.com/embark-framework/embark/tree/master/packages/embark-blockchain-process#readme",
|
||||
"bugs": "https://github.com/embark-framework/embark/issues",
|
||||
"keywords": [
|
||||
"blockchain",
|
||||
"dapps",
|
||||
"ethereum",
|
||||
"ipfs",
|
||||
"serverless",
|
||||
"solc",
|
||||
"solidity"
|
||||
],
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
"directory": "packages/embark-blockchain-process",
|
||||
"type": "git",
|
||||
"url": "https://github.com/embark-framework/embark.git"
|
||||
},
|
||||
"main": "./dist/index.js",
|
||||
"scripts": {
|
||||
"build": "cross-env BABEL_ENV=node babel src --extensions \".js\" --out-dir dist --root-mode upward --source-maps",
|
||||
"ci": "npm run qa",
|
||||
"clean": "npm run reset",
|
||||
"lint": "npm-run-all lint:*",
|
||||
"lint:js": "eslint src/",
|
||||
"// lint:ts": "tslint -c tslint.json \"src/**/*.ts\"",
|
||||
"package": "npm pack",
|
||||
"// qa": "npm-run-all lint typecheck build package",
|
||||
"qa": "npm-run-all lint build package",
|
||||
"reset": "npx rimraf dist embark-*.tgz package",
|
||||
"start": "npm run watch",
|
||||
"// typecheck": "tsc",
|
||||
"watch": "run-p watch:*",
|
||||
"watch:build": "npm run build -- --verbose --watch",
|
||||
"// watch:typecheck": "npm run typecheck -- --preserveWatchOutput --watch"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": "../../.eslintrc.json"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime-corejs2": "7.3.1",
|
||||
"async": "2.6.1",
|
||||
"embark-core": "^4.1.1",
|
||||
"embark-i18n": "^4.1.1",
|
||||
"embark-logger": "^4.1.1",
|
||||
"embark-utils": "^4.1.1",
|
||||
"fs-extra": "7.0.1",
|
||||
"netcat": "1.3.5",
|
||||
"pkg-up": "2.0.0",
|
||||
"semver": "5.6.0",
|
||||
"shelljs": "0.8.3",
|
||||
"ws": "7.1.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "7.2.3",
|
||||
"@babel/core": "7.2.2",
|
||||
"cross-env": "5.2.0",
|
||||
"eslint": "5.7.0",
|
||||
"npm-run-all": "4.1.5",
|
||||
"rimraf": "3.0.0",
|
||||
"tslint": "5.16.0",
|
||||
"typescript": "3.4.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8.12.0 <12.0.0",
|
||||
"npm": ">=6.4.1",
|
||||
"yarn": ">=1.12.3"
|
||||
}
|
||||
}
|
|
@ -1,446 +0,0 @@
|
|||
import { __ } from 'embark-i18n';
|
||||
const fs = require('fs-extra');
|
||||
const async = require('async');
|
||||
const {spawn, exec} = require('child_process');
|
||||
const path = require('path');
|
||||
const constants = require('embark-core/constants');
|
||||
const GethClient = require('./gethClient.js');
|
||||
const ParityClient = require('./parityClient.js');
|
||||
import { IPC } from 'embark-core';
|
||||
|
||||
import { compact, dappPath, defaultHost, dockerHostSwap, embarkPath} from 'embark-utils';
|
||||
const Logger = require('embark-logger');
|
||||
|
||||
// time between IPC connection attempts (in ms)
|
||||
const IPC_CONNECT_INTERVAL = 2000;
|
||||
|
||||
/*eslint complexity: ["error", 50]*/
|
||||
var Blockchain = function(userConfig, clientClass) {
|
||||
this.userConfig = userConfig;
|
||||
this.env = userConfig.env || 'development';
|
||||
this.isDev = userConfig.isDev;
|
||||
this.onReadyCallback = userConfig.onReadyCallback || (() => {});
|
||||
this.onExitCallback = userConfig.onExitCallback;
|
||||
this.logger = userConfig.logger || new Logger({logLevel: 'debug', context: constants.contexts.blockchain}); // do not pass in events as we don't want any log events emitted
|
||||
this.events = userConfig.events;
|
||||
this.isStandalone = userConfig.isStandalone;
|
||||
this.certOptions = userConfig.certOptions;
|
||||
|
||||
|
||||
let defaultWsApi = clientClass.DEFAULTS.WS_API;
|
||||
if (this.isDev) defaultWsApi = clientClass.DEFAULTS.DEV_WS_API;
|
||||
|
||||
this.config = {
|
||||
silent: this.userConfig.silent,
|
||||
client: this.userConfig.client,
|
||||
ethereumClientBin: this.userConfig.ethereumClientBin || this.userConfig.client,
|
||||
networkType: this.userConfig.networkType || clientClass.DEFAULTS.NETWORK_TYPE,
|
||||
networkId: this.userConfig.networkId || clientClass.DEFAULTS.NETWORK_ID,
|
||||
genesisBlock: this.userConfig.genesisBlock || false,
|
||||
datadir: this.userConfig.datadir,
|
||||
mineWhenNeeded: this.userConfig.mineWhenNeeded || false,
|
||||
rpcHost: dockerHostSwap(this.userConfig.rpcHost) || defaultHost,
|
||||
rpcPort: this.userConfig.rpcPort || 8545,
|
||||
rpcCorsDomain: this.userConfig.rpcCorsDomain || false,
|
||||
rpcApi: this.userConfig.rpcApi || clientClass.DEFAULTS.RPC_API,
|
||||
port: this.userConfig.port || 30303,
|
||||
nodiscover: this.userConfig.nodiscover || false,
|
||||
mine: this.userConfig.mine || false,
|
||||
account: {},
|
||||
whisper: (this.userConfig.whisper !== false),
|
||||
maxpeers: ((this.userConfig.maxpeers === 0) ? 0 : (this.userConfig.maxpeers || 25)),
|
||||
bootnodes: this.userConfig.bootnodes || "",
|
||||
wsRPC: (this.userConfig.wsRPC !== false),
|
||||
wsHost: dockerHostSwap(this.userConfig.wsHost) || defaultHost,
|
||||
wsPort: this.userConfig.wsPort || 8546,
|
||||
wsOrigins: this.userConfig.wsOrigins || false,
|
||||
wsApi: this.userConfig.wsApi || defaultWsApi,
|
||||
vmdebug: this.userConfig.vmdebug || false,
|
||||
targetGasLimit: this.userConfig.targetGasLimit || false,
|
||||
syncMode: this.userConfig.syncMode || this.userConfig.syncmode,
|
||||
verbosity: this.userConfig.verbosity
|
||||
};
|
||||
|
||||
this.devFunds = null;
|
||||
|
||||
if (this.userConfig.accounts) {
|
||||
const nodeAccounts = this.userConfig.accounts.find(account => account.nodeAccounts);
|
||||
if (nodeAccounts) {
|
||||
this.config.account = {
|
||||
numAccounts: nodeAccounts.numAddresses || 1,
|
||||
password: nodeAccounts.password,
|
||||
balance: nodeAccounts.balance
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (this.userConfig === {} || this.userConfig.default || JSON.stringify(this.userConfig) === '{"client":"geth"}') {
|
||||
if (this.env === 'development') {
|
||||
this.isDev = true;
|
||||
} else {
|
||||
this.config.genesisBlock = embarkPath("templates/boilerplate/config/privatenet/genesis.json");
|
||||
}
|
||||
this.config.datadir = dappPath(".embark/development/datadir");
|
||||
this.config.wsOrigins = this.config.wsOrigins || "http://localhost:8000";
|
||||
this.config.rpcCorsDomain = this.config.rpcCorsDomain || "http://localhost:8000";
|
||||
this.config.targetGasLimit = 8000000;
|
||||
}
|
||||
this.config.account.devPassword = path.join(this.config.datadir, "devPassword");
|
||||
|
||||
const spaceMessage = 'The path for %s in blockchain config contains spaces, please remove them';
|
||||
if (this.config.datadir && this.config.datadir.indexOf(' ') > 0) {
|
||||
this.logger.error(__(spaceMessage, 'datadir'));
|
||||
process.exit(1);
|
||||
}
|
||||
if (this.config.account.password && this.config.account.password.indexOf(' ') > 0) {
|
||||
this.logger.error(__(spaceMessage, 'accounts.password'));
|
||||
process.exit(1);
|
||||
}
|
||||
if (this.config.genesisBlock && this.config.genesisBlock.indexOf(' ') > 0) {
|
||||
this.logger.error(__(spaceMessage, 'genesisBlock'));
|
||||
process.exit(1);
|
||||
}
|
||||
this.client = new clientClass({config: this.config, env: this.env, isDev: this.isDev});
|
||||
|
||||
this.initStandaloneProcess();
|
||||
};
|
||||
|
||||
/**
|
||||
* Polls for a connection to an IPC server (generally this is set up
|
||||
* in the Embark process). Once connected, any logs logged to the
|
||||
* Logger will be shipped off to the IPC server. In the case of `embark
|
||||
* run`, the BlockchainListener module is listening for these logs.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
Blockchain.prototype.initStandaloneProcess = function () {
|
||||
if (this.isStandalone) {
|
||||
let logQueue = [];
|
||||
|
||||
// on every log logged in logger (say that 3x fast), send the log
|
||||
// to the IPC serve listening (only if we're connected of course)
|
||||
this.logger.events.on('log', (logLevel, message) => {
|
||||
if (this.ipc.connected) {
|
||||
this.ipc.request('blockchain:log', {logLevel, message});
|
||||
} else {
|
||||
logQueue.push({logLevel, message});
|
||||
}
|
||||
});
|
||||
|
||||
this.ipc = new IPC({ipcRole: 'client'});
|
||||
|
||||
// Wait for an IPC server to start (ie `embark run`) by polling `.connect()`.
|
||||
// Do not kill this interval as the IPC server may restart (ie restart
|
||||
// `embark run` without restarting `embark blockchain`)
|
||||
setInterval(() => {
|
||||
if (!this.ipc.connected) {
|
||||
this.ipc.connect(() => {
|
||||
if (this.ipc.connected) {
|
||||
logQueue.forEach(message => { this.ipc.request('blockchain:log', message); });
|
||||
logQueue = [];
|
||||
this.ipc.client.on('process:blockchain:stop', () => {
|
||||
this.kill();
|
||||
process.exit(0);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}, IPC_CONNECT_INTERVAL);
|
||||
}
|
||||
};
|
||||
|
||||
Blockchain.prototype.runCommand = function (cmd, options, callback) {
|
||||
this.logger.info(__("running: %s", cmd.underline).green);
|
||||
if (this.config.silent) {
|
||||
options.silent = true;
|
||||
}
|
||||
return exec(cmd, options, callback);
|
||||
};
|
||||
|
||||
Blockchain.prototype.run = function () {
|
||||
var self = this;
|
||||
this.logger.info("===============================================================================".magenta);
|
||||
this.logger.info("===============================================================================".magenta);
|
||||
this.logger.info(__("Embark Blockchain using %s", self.client.prettyName.underline).magenta);
|
||||
this.logger.info("===============================================================================".magenta);
|
||||
this.logger.info("===============================================================================".magenta);
|
||||
|
||||
if (self.client.name === constants.blockchain.clients.geth) this.checkPathLength();
|
||||
|
||||
let address = '';
|
||||
async.waterfall([
|
||||
function checkInstallation(next) {
|
||||
self.isClientInstalled((err) => {
|
||||
if (err) {
|
||||
return next({message: err});
|
||||
}
|
||||
next();
|
||||
});
|
||||
},
|
||||
function init(next) {
|
||||
if (self.isDev) {
|
||||
return self.initDevChain((err) => {
|
||||
next(err);
|
||||
});
|
||||
}
|
||||
return self.initChainAndGetAddress((err, addr) => {
|
||||
address = addr;
|
||||
next(err);
|
||||
});
|
||||
},
|
||||
function getMainCommand(next) {
|
||||
self.client.mainCommand(address, function (cmd, args) {
|
||||
next(null, cmd, args);
|
||||
}, true);
|
||||
}
|
||||
], function(err, cmd, args) {
|
||||
if (err) {
|
||||
self.logger.error(err.message);
|
||||
return;
|
||||
}
|
||||
args = compact(args);
|
||||
|
||||
let full_cmd = cmd + " " + args.join(' ');
|
||||
self.logger.info(__("running: %s", full_cmd.underline).green);
|
||||
self.child = spawn(cmd, args, {cwd: process.cwd()});
|
||||
|
||||
self.child.on('error', (err) => {
|
||||
err = err.toString();
|
||||
self.logger.error('Blockchain error: ', err);
|
||||
if (self.env === 'development' && err.indexOf('Failed to unlock') > 0) {
|
||||
self.logger.error('\n' + __('Development blockchain has changed to use the --dev option.').yellow);
|
||||
self.logger.error(__('You can reset your workspace to fix the problem with').yellow + ' embark reset'.cyan);
|
||||
self.logger.error(__('Otherwise, you can change your data directory in blockchain.json (datadir)').yellow);
|
||||
}
|
||||
});
|
||||
|
||||
// TOCHECK I don't understand why stderr and stdout are reverted.
|
||||
// This happens with Geth and Parity, so it does not seems a client problem
|
||||
self.child.stdout.on('data', (data) => {
|
||||
self.logger.info(`${self.client.name} error: ${data}`);
|
||||
});
|
||||
|
||||
self.child.stderr.on('data', async (data) => {
|
||||
data = data.toString();
|
||||
if (!self.readyCalled && self.client.isReady(data)) {
|
||||
self.readyCalled = true;
|
||||
self.readyCallback();
|
||||
}
|
||||
self.logger.info(`${self.client.name}: ${data}`);
|
||||
});
|
||||
|
||||
self.child.on('exit', (code) => {
|
||||
let strCode;
|
||||
if (code) {
|
||||
strCode = 'with error code ' + code;
|
||||
} else {
|
||||
strCode = 'with no error code (manually killed?)';
|
||||
}
|
||||
self.logger.error(self.client.name + ' exited ' + strCode);
|
||||
if (self.onExitCallback) {
|
||||
self.onExitCallback();
|
||||
}
|
||||
});
|
||||
|
||||
self.child.on('uncaughtException', (err) => {
|
||||
self.logger.error('Uncaught ' + self.client.name + ' exception', err);
|
||||
if (self.onExitCallback) {
|
||||
self.onExitCallback();
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
Blockchain.prototype.readyCallback = function () {
|
||||
if (this.onReadyCallback) {
|
||||
this.onReadyCallback();
|
||||
}
|
||||
if (this.config.mineWhenNeeded && !this.isDev) {
|
||||
this.miner = this.client.getMiner();
|
||||
}
|
||||
};
|
||||
|
||||
Blockchain.prototype.kill = function () {
|
||||
if (this.child) {
|
||||
this.child.kill();
|
||||
}
|
||||
};
|
||||
|
||||
Blockchain.prototype.checkPathLength = function () {
|
||||
let _dappPath = dappPath('');
|
||||
if (_dappPath.length > 66) {
|
||||
// this.logger.error is captured and sent to the console output regardless of silent setting
|
||||
this.logger.error("===============================================================================".yellow);
|
||||
this.logger.error("===========> ".yellow + __('WARNING! ÐApp path length is too long: ').yellow + _dappPath.yellow);
|
||||
this.logger.error("===========> ".yellow + __('This is known to cause issues with starting geth, please consider reducing your ÐApp path\'s length to 66 characters or less.').yellow);
|
||||
this.logger.error("===============================================================================".yellow);
|
||||
}
|
||||
};
|
||||
|
||||
Blockchain.prototype.isClientInstalled = function (callback) {
|
||||
let versionCmd = this.client.determineVersionCommand();
|
||||
this.runCommand(versionCmd, {}, (err, stdout, stderr) => {
|
||||
if (err || !stdout || stderr.indexOf("not found") >= 0 || stdout.indexOf("not found") >= 0) {
|
||||
return callback(__('Ethereum client bin not found:') + ' ' + this.client.getBinaryPath());
|
||||
}
|
||||
const parsedVersion = this.client.parseVersion(stdout);
|
||||
const supported = this.client.isSupportedVersion(parsedVersion);
|
||||
if (supported === undefined) {
|
||||
this.logger.error((__('WARNING! Ethereum client version could not be determined or compared with version range') + ' ' + this.client.versSupported + __(', for best results please use a supported version')).yellow);
|
||||
} else if (!supported) {
|
||||
this.logger.error((__('WARNING! Ethereum client version unsupported, for best results please use a version in range') + ' ' + this.client.versSupported).yellow);
|
||||
}
|
||||
callback();
|
||||
});
|
||||
};
|
||||
|
||||
Blockchain.prototype.initDevChain = function(callback) {
|
||||
const self = this;
|
||||
const ACCOUNTS_ALREADY_PRESENT = 'accounts_already_present';
|
||||
// Init the dev chain
|
||||
self.client.initDevChain(self.config.datadir, (err) => {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
const accountsToCreate = self.config.account && self.config.account.numAccounts;
|
||||
if (!accountsToCreate) return callback();
|
||||
|
||||
// Create other accounts
|
||||
async.waterfall([
|
||||
function listAccounts(next) {
|
||||
self.runCommand(self.client.listAccountsCommand(), {}, (err, stdout, _stderr) => {
|
||||
if (err || stdout === undefined || stdout.indexOf("Fatal") >= 0) {
|
||||
console.log(__("no accounts found").green);
|
||||
return next();
|
||||
}
|
||||
// List current addresses
|
||||
self.config.unlockAddressList = self.client.parseListAccountsCommandResultToAddressList(stdout);
|
||||
// Count current addresses and remove the default account from the count (because password can be different)
|
||||
let addressCount = self.config.unlockAddressList.length;
|
||||
if (addressCount < accountsToCreate) {
|
||||
next(null, accountsToCreate - addressCount);
|
||||
} else {
|
||||
next(ACCOUNTS_ALREADY_PRESENT);
|
||||
}
|
||||
});
|
||||
},
|
||||
function newAccounts(accountsToCreate, next) {
|
||||
var accountNumber = 0;
|
||||
async.whilst(
|
||||
function() {
|
||||
return accountNumber < accountsToCreate;
|
||||
},
|
||||
function(callback) {
|
||||
accountNumber++;
|
||||
self.runCommand(self.client.newAccountCommand(), {}, (err, stdout, _stderr) => {
|
||||
if (err) {
|
||||
return callback(err, accountNumber);
|
||||
}
|
||||
self.config.unlockAddressList.push(self.client.parseNewAccountCommandResultToAddress(stdout));
|
||||
callback(null, accountNumber);
|
||||
});
|
||||
},
|
||||
function(err) {
|
||||
next(err);
|
||||
}
|
||||
);
|
||||
}
|
||||
], (err) => {
|
||||
if (err && err !== ACCOUNTS_ALREADY_PRESENT) {
|
||||
console.log(err);
|
||||
return callback(err);
|
||||
}
|
||||
callback();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
Blockchain.prototype.initChainAndGetAddress = function (callback) {
|
||||
const self = this;
|
||||
let address = null;
|
||||
const ALREADY_INITIALIZED = 'already';
|
||||
|
||||
// ensure datadir exists, bypassing the interactive liabilities prompt.
|
||||
self.datadir = self.config.datadir;
|
||||
|
||||
async.waterfall([
|
||||
function makeDir(next) {
|
||||
fs.mkdirp(self.datadir, (err, _result) => {
|
||||
next(err);
|
||||
});
|
||||
},
|
||||
function listAccounts(next) {
|
||||
self.runCommand(self.client.listAccountsCommand(), {}, (err, stdout, _stderr) => {
|
||||
if (err || stdout === undefined || stdout.indexOf("Fatal") >= 0) {
|
||||
self.logger.info(__("no accounts found").green);
|
||||
return next();
|
||||
}
|
||||
let firstAccountFound = self.client.parseListAccountsCommandResultToAddress(stdout);
|
||||
if (firstAccountFound === undefined || firstAccountFound === "") {
|
||||
console.log(__("no accounts found").green);
|
||||
return next();
|
||||
}
|
||||
self.logger.info(__("already initialized").green);
|
||||
address = firstAccountFound;
|
||||
next(ALREADY_INITIALIZED);
|
||||
});
|
||||
},
|
||||
function genesisBlock(next) {
|
||||
//There's no genesis init with Parity. Custom network are set in the chain property at startup
|
||||
if (!self.config.genesisBlock || self.client.name === constants.blockchain.clients.parity) {
|
||||
return next();
|
||||
}
|
||||
self.logger.info(__("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 = self.client.parseNewAccountCommandResultToAddress(stdout);
|
||||
next();
|
||||
});
|
||||
}
|
||||
], (err) => {
|
||||
if (err === ALREADY_INITIALIZED) {
|
||||
err = null;
|
||||
}
|
||||
callback(err, address);
|
||||
});
|
||||
};
|
||||
|
||||
export function BlockchainClient(userConfig, options) {
|
||||
if ((userConfig === {} || JSON.stringify(userConfig) === '{"enabled":true}') && options.env !== 'development') {
|
||||
options.logger.info("===> " + __("warning: running default config on a non-development environment"));
|
||||
}
|
||||
// if client is not set in preferences, default is geth
|
||||
if (!userConfig.client) userConfig.client = constants.blockchain.clients.geth;
|
||||
// if clientName is set, it overrides preferences
|
||||
if (options.clientName) userConfig.client = options.clientName;
|
||||
// Choose correct client instance based on clientName
|
||||
let clientClass;
|
||||
switch (userConfig.client) {
|
||||
case constants.blockchain.clients.geth:
|
||||
clientClass = GethClient;
|
||||
break;
|
||||
|
||||
case constants.blockchain.clients.parity:
|
||||
clientClass = ParityClient;
|
||||
break;
|
||||
default:
|
||||
console.error(__('Unknown client "%s". Please use one of the following: %s', userConfig.client, Object.keys(constants.blockchain.clients).join(', ')));
|
||||
process.exit(1);
|
||||
}
|
||||
userConfig.isDev = (userConfig.isDev || userConfig.default);
|
||||
userConfig.env = options.env;
|
||||
userConfig.onReadyCallback = options.onReadyCallback;
|
||||
userConfig.onExitCallback = options.onExitCallback;
|
||||
userConfig.logger = options.logger;
|
||||
userConfig.certOptions = options.certOptions;
|
||||
userConfig.isStandalone = options.isStandalone;
|
||||
return new Blockchain(userConfig, clientClass);
|
||||
}
|
|
@ -1,57 +0,0 @@
|
|||
import * as i18n from 'embark-i18n';
|
||||
import { ProcessWrapper } from 'embark-core';
|
||||
const constants = require('embark-core/constants');
|
||||
import { BlockchainClient } from './blockchain';
|
||||
|
||||
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;
|
||||
this.certOptions = options.certOptions;
|
||||
|
||||
i18n.setOrDetectLocale(options.locale);
|
||||
|
||||
this.blockchainConfig.silent = true;
|
||||
this.blockchain = BlockchainClient(
|
||||
this.blockchainConfig,
|
||||
{
|
||||
clientName: this.client,
|
||||
env: this.env,
|
||||
certOptions: this.certOptions,
|
||||
onReadyCallback: this.blockchainReady.bind(this),
|
||||
onExitCallback: this.blockchainExit.bind(this),
|
||||
logger: console
|
||||
}
|
||||
);
|
||||
|
||||
this.blockchain.run();
|
||||
}
|
||||
|
||||
blockchainReady() {
|
||||
blockchainProcess.send({result: constants.blockchain.blockchainReady});
|
||||
}
|
||||
|
||||
blockchainExit() {
|
||||
// tell our parent process that ethereum client has exited
|
||||
blockchainProcess.send({result: constants.blockchain.blockchainExit});
|
||||
}
|
||||
|
||||
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,81 +0,0 @@
|
|||
import { __ } from 'embark-i18n';
|
||||
import { ProcessLauncher } from 'embark-core';
|
||||
import { joinPath } from 'embark-utils';
|
||||
const constants = require('embark-core/constants');
|
||||
|
||||
export 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;
|
||||
this.client = options.client;
|
||||
this.embark = options.embark;
|
||||
}
|
||||
|
||||
processEnded(code) {
|
||||
this.logger.error(__('Blockchain process ended before the end of this process. Try running blockchain in a separate process using `$ embark blockchain`. Code: %s', code));
|
||||
}
|
||||
|
||||
startBlockchainNode() {
|
||||
this.logger.info(__('Starting Blockchain node in another process').cyan);
|
||||
|
||||
this.blockchainProcess = new ProcessLauncher({
|
||||
name: 'blockchain',
|
||||
modulePath: joinPath(__dirname, './blockchainProcess.js'),
|
||||
logger: this.logger,
|
||||
events: this.events,
|
||||
silent: this.logger.logLevel !== 'trace',
|
||||
exitCallback: this.processEnded.bind(this),
|
||||
embark: this.embark
|
||||
});
|
||||
this.blockchainProcess.send({
|
||||
action: constants.blockchain.init, options: {
|
||||
blockchainConfig: this.blockchainConfig,
|
||||
client: this.client,
|
||||
env: this.env,
|
||||
isDev: this.isDev,
|
||||
locale: this.locale,
|
||||
certOptions: this.embark.config.webServerConfig.certOptions,
|
||||
events: this.events
|
||||
}
|
||||
});
|
||||
|
||||
this.blockchainProcess.once('result', constants.blockchain.blockchainReady, () => {
|
||||
this.logger.info(__('Blockchain node is ready').cyan);
|
||||
this.events.emit(constants.blockchain.blockchainReady);
|
||||
});
|
||||
|
||||
this.blockchainProcess.once('result', constants.blockchain.blockchainExit, () => {
|
||||
// tell everyone that our blockchain process (ie geth) died
|
||||
this.events.emit(constants.blockchain.blockchainExit);
|
||||
|
||||
// then kill off the blockchain process
|
||||
this.blockchainProcess.kill();
|
||||
});
|
||||
|
||||
this.events.on('logs:ethereum:enable', () => {
|
||||
this.blockchainProcess.silent = false;
|
||||
});
|
||||
|
||||
this.events.on('logs:ethereum:disable', () => {
|
||||
this.blockchainProcess.silent = true;
|
||||
});
|
||||
|
||||
this.events.on('exit', () => {
|
||||
this.blockchainProcess.send('exit');
|
||||
});
|
||||
}
|
||||
|
||||
stopBlockchainNode(cb) {
|
||||
if(this.blockchainProcess) {
|
||||
this.events.once(constants.blockchain.blockchainExit, cb);
|
||||
this.blockchainProcess.exitCallback = () => {}; // don't show error message as the process was killed on purpose
|
||||
this.blockchainProcess.send('exit');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,395 +0,0 @@
|
|||
import { __ } from 'embark-i18n';
|
||||
import { dappPath, ipcPath } from 'embark-utils';
|
||||
const async = require('async');
|
||||
const {exec, spawn} = require('child_process');
|
||||
const path = require('path');
|
||||
const GethMiner = require('./miner');
|
||||
const semver = require('semver');
|
||||
const constants = require('embark-core/constants');
|
||||
|
||||
const DEFAULTS = {
|
||||
"BIN": "geth",
|
||||
"VERSIONS_SUPPORTED": ">=1.8.14",
|
||||
"NETWORK_TYPE": "custom",
|
||||
"NETWORK_ID": 1337,
|
||||
"RPC_API": ['eth', 'web3', 'net', 'debug', 'personal'],
|
||||
"WS_API": ['eth', 'web3', 'net', 'shh', 'debug', 'pubsub', 'personal'],
|
||||
"DEV_WS_API": ['eth', 'web3', 'net', 'shh', 'debug', 'pubsub', 'personal'],
|
||||
"TARGET_GAS_LIMIT": 8000000
|
||||
};
|
||||
|
||||
// TODO: make all of this async
|
||||
class GethClient {
|
||||
|
||||
static get DEFAULTS() {
|
||||
return DEFAULTS;
|
||||
}
|
||||
|
||||
constructor(options) {
|
||||
this.config = options && options.hasOwnProperty('config') ? options.config : {};
|
||||
this.env = options && options.hasOwnProperty('env') ? options.env : 'development';
|
||||
this.isDev = options && options.hasOwnProperty('isDev') ? options.isDev : (this.env === 'development');
|
||||
this.name = constants.blockchain.clients.geth;
|
||||
this.prettyName = "Go-Ethereum (https://github.com/ethereum/go-ethereum)";
|
||||
this.bin = this.config.ethereumClientBin || DEFAULTS.BIN;
|
||||
this.versSupported = DEFAULTS.VERSIONS_SUPPORTED;
|
||||
this.httpReady = false;
|
||||
this.wsReady = !this.config.wsRPC;
|
||||
}
|
||||
|
||||
isReady(data) {
|
||||
if (data.indexOf('HTTP endpoint opened') > -1) {
|
||||
this.httpReady = true;
|
||||
}
|
||||
if (data.indexOf('WebSocket endpoint opened') > -1) {
|
||||
this.wsReady = true;
|
||||
}
|
||||
return this.httpReady && this.wsReady;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the client needs some sort of 'keep alive' transactions to avoid freezing by inactivity
|
||||
* @returns {boolean} if keep alive is needed
|
||||
*/
|
||||
needKeepAlive() {
|
||||
// TODO: check version also (geth version < 1.8.15)
|
||||
if (this.isDev) {
|
||||
// Trigger regular txs due to a bug in geth (< 1.8.15) and stuck transactions in --dev mode.
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
commonOptions() {
|
||||
let config = this.config;
|
||||
let cmd = [];
|
||||
|
||||
cmd.push(this.determineNetworkType(config));
|
||||
|
||||
if (config.datadir) {
|
||||
cmd.push(`--datadir=${config.datadir}`);
|
||||
}
|
||||
|
||||
if (config.syncMode) {
|
||||
cmd.push("--syncmode=" + config.syncMode);
|
||||
}
|
||||
|
||||
if(this.runAsArchival(config)) {
|
||||
cmd.push("--gcmode=archive");
|
||||
}
|
||||
|
||||
if (config.account && config.account.password) {
|
||||
const resolvedPath = path.resolve(dappPath(), config.account.password);
|
||||
cmd.push(`--password=${resolvedPath}`);
|
||||
}
|
||||
|
||||
if (Number.isInteger(config.verbosity) && config.verbosity >= 0 && config.verbosity <= 5) {
|
||||
cmd.push("--verbosity=" + config.verbosity);
|
||||
}
|
||||
|
||||
cmd.push(`--ipcpath=${ipcPath('geth.ipc', true)}`);
|
||||
|
||||
return cmd;
|
||||
}
|
||||
|
||||
getMiner() {
|
||||
return new GethMiner({datadir: this.config.datadir});
|
||||
}
|
||||
|
||||
getBinaryPath() {
|
||||
return this.bin;
|
||||
}
|
||||
|
||||
determineVersionCommand() {
|
||||
return this.bin + " version";
|
||||
}
|
||||
|
||||
parseVersion(rawVersionOutput) {
|
||||
let parsed;
|
||||
const match = rawVersionOutput.match(/Version: (.*)/);
|
||||
if (match) {
|
||||
parsed = match[1].trim();
|
||||
}
|
||||
return parsed;
|
||||
}
|
||||
|
||||
isSupportedVersion(parsedVersion) {
|
||||
let test;
|
||||
try {
|
||||
let v = semver(parsedVersion);
|
||||
v = `${v.major}.${v.minor}.${v.patch}`;
|
||||
test = semver.Range(this.versSupported).test(semver(v));
|
||||
if (typeof test !== 'boolean') {
|
||||
test = undefined;
|
||||
}
|
||||
} finally {
|
||||
// eslint-disable-next-line no-unsafe-finally
|
||||
return test;
|
||||
}
|
||||
}
|
||||
|
||||
determineNetworkType(config) {
|
||||
let cmd;
|
||||
if (config.networkType === 'testnet') {
|
||||
cmd = "--testnet";
|
||||
} else if (config.networkType === 'rinkeby') {
|
||||
cmd = "--rinkeby";
|
||||
} else if (config.networkType === 'custom') {
|
||||
cmd = "--networkid=" + config.networkId;
|
||||
}
|
||||
return cmd;
|
||||
}
|
||||
|
||||
runAsArchival(config) {
|
||||
return config.networkId === 1337 || config.archivalMode;
|
||||
}
|
||||
|
||||
initGenesisCommmand() {
|
||||
let config = this.config;
|
||||
let cmd = this.bin + " " + this.commonOptions().join(' ');
|
||||
if (config.genesisBlock) {
|
||||
cmd += " init \"" + config.genesisBlock + "\" ";
|
||||
}
|
||||
return cmd;
|
||||
}
|
||||
|
||||
newAccountCommand() {
|
||||
if (!(this.config.account && this.config.account.password)) {
|
||||
console.warn(__('Your blockchain config is missing a password and creating an account may fail. Please consider updating ').yellow + __('config/blockchain > accounts').cyan + __(' then re-run the command').yellow);
|
||||
}
|
||||
return this.bin + " " + this.commonOptions().join(' ') + " account new ";
|
||||
}
|
||||
|
||||
parseNewAccountCommandResultToAddress(data = "") {
|
||||
if (data.match(/{(\w+)}/)) return "0x" + data.match(/{(\w+)}/)[1];
|
||||
return "";
|
||||
}
|
||||
|
||||
listAccountsCommand() {
|
||||
return this.bin + " " + this.commonOptions().join(' ') + " account list ";
|
||||
}
|
||||
|
||||
parseListAccountsCommandResultToAddress(data = "") {
|
||||
if (data.match(/{(\w+)}/)) return "0x" + data.match(/{(\w+)}/)[1];
|
||||
return "";
|
||||
}
|
||||
|
||||
parseListAccountsCommandResultToAddressList(data = "") {
|
||||
const regex = RegExp(/{(\w+)}/g);
|
||||
let match;
|
||||
const accounts = [];
|
||||
while ((match = regex.exec(data)) !== null) {
|
||||
accounts.push('0x' + match[1]);
|
||||
}
|
||||
return accounts;
|
||||
}
|
||||
|
||||
parseListAccountsCommandResultToAddressCount(data = "") {
|
||||
const count = this.parseListAccountsCommandResultToAddressList(data).length;
|
||||
return (count > 0 ? count : 0);
|
||||
}
|
||||
|
||||
determineRpcOptions(config) {
|
||||
let cmd = [];
|
||||
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.warn('==================================');
|
||||
console.warn(__('rpcCorsDomain set to *'));
|
||||
console.warn(__('make sure you know what you are doing'));
|
||||
console.warn('==================================');
|
||||
}
|
||||
cmd.push("--rpccorsdomain=" + config.rpcCorsDomain);
|
||||
} else {
|
||||
console.warn('==================================');
|
||||
console.warn(__('warning: cors is not set'));
|
||||
console.warn('==================================');
|
||||
}
|
||||
return cmd;
|
||||
}
|
||||
|
||||
determineWsOptions(config) {
|
||||
let cmd = [];
|
||||
if (config.wsRPC) {
|
||||
cmd.push("--ws");
|
||||
cmd.push("--wsport=" + config.wsPort);
|
||||
cmd.push("--wsaddr=" + config.wsHost);
|
||||
if (config.wsOrigins) {
|
||||
if (config.wsOrigins === '*') {
|
||||
console.warn('==================================');
|
||||
console.warn(__('wsOrigins set to *'));
|
||||
console.warn(__('make sure you know what you are doing'));
|
||||
console.warn('==================================');
|
||||
}
|
||||
cmd.push("--wsorigins=" + config.wsOrigins);
|
||||
} else {
|
||||
console.warn('==================================');
|
||||
console.warn(__('warning: wsOrigins is not set'));
|
||||
console.warn('==================================');
|
||||
}
|
||||
}
|
||||
return cmd;
|
||||
}
|
||||
|
||||
initDevChain(datadir, callback) {
|
||||
exec(this.listAccountsCommand(), {}, (err, stdout, _stderr) => {
|
||||
const readyTimeout = setTimeout(() => {
|
||||
this.child.kill();
|
||||
return cb(__('Geth dev command never returned a developer account'));
|
||||
}, 10 * 1000);
|
||||
|
||||
function cb(err) {
|
||||
clearTimeout(readyTimeout);
|
||||
callback(err);
|
||||
}
|
||||
|
||||
if (err || stdout === undefined || stdout.indexOf("Fatal") >= 0) {
|
||||
return cb(err || stdout);
|
||||
}
|
||||
this.config.unlockAddressList = this.parseListAccountsCommandResultToAddressList(stdout);
|
||||
if (this.config.unlockAddressList.length) {
|
||||
return cb();
|
||||
}
|
||||
|
||||
// No accounts. We need to run the geth --dev command for it to create the dev account
|
||||
const args = this.commonOptions();
|
||||
args.push('--dev');
|
||||
console.log(__('Creating Geth dev account. Please wait...'));
|
||||
this.child = spawn(this.bin, args, {cwd: process.cwd()});
|
||||
|
||||
this.child.stderr.on('data', async (data) => {
|
||||
data = data.toString();
|
||||
if (data.indexOf('Using developer account') > -1) {
|
||||
this.child.kill();
|
||||
cb();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
mainCommand(address, done) {
|
||||
let self = this;
|
||||
let config = this.config;
|
||||
let rpc_api = this.config.rpcApi;
|
||||
let ws_api = this.config.wsApi;
|
||||
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;
|
||||
args.push(cmd);
|
||||
callback(null, cmd);
|
||||
},
|
||||
function mining(callback) {
|
||||
if (config.mineWhenNeeded || config.mine) {
|
||||
args.push("--mine");
|
||||
return callback(null, "--mine");
|
||||
}
|
||||
callback("");
|
||||
},
|
||||
function bootnodes(callback) {
|
||||
if (config.bootnodes && config.bootnodes !== "" && config.bootnodes !== []) {
|
||||
args.push("--bootnodes=" + config.bootnodes);
|
||||
return callback(null, "--bootnodes=" + config.bootnodes);
|
||||
}
|
||||
callback("");
|
||||
},
|
||||
function whisper(callback) {
|
||||
if (config.whisper) {
|
||||
rpc_api.push('shh');
|
||||
if (ws_api.indexOf('shh') === -1) {
|
||||
ws_api.push('shh');
|
||||
}
|
||||
args.push("--shh");
|
||||
return callback(null, "--shh ");
|
||||
}
|
||||
callback("");
|
||||
},
|
||||
function rpcApi(callback) {
|
||||
args.push('--rpcapi=' + rpc_api.join(','));
|
||||
callback(null, '--rpcapi=' + rpc_api.join(','));
|
||||
},
|
||||
function wsApi(callback) {
|
||||
args.push('--wsapi=' + ws_api.join(','));
|
||||
callback(null, '--wsapi=' + ws_api.join(','));
|
||||
},
|
||||
function accountToUnlock(callback) {
|
||||
if (self.isDev && self.config.unlockAddressList) {
|
||||
// The first address is the dev account, that is automatically unlocked by the client using blank password
|
||||
args.push("--unlock=" + self.config.unlockAddressList.slice(1));
|
||||
return callback(null, "--unlock=" + self.config.unlockAddressList.slice(1));
|
||||
}
|
||||
let accountAddress = "";
|
||||
if (config.account && config.account.address) {
|
||||
accountAddress = config.account.address;
|
||||
} else {
|
||||
accountAddress = address;
|
||||
}
|
||||
if (accountAddress) {
|
||||
if (!(self.config && self.config.account && self.config.account.password)) {
|
||||
console.warn(__("\n===== Password needed =====\nPassword for account {{account}} not found. Unlocking this account may fail. Please ensure a password is specified in config/blockchain.js > {{env}} > account > password.\n", {
|
||||
account: address,
|
||||
env: self.env
|
||||
}));
|
||||
}
|
||||
args.push("--unlock=" + accountAddress);
|
||||
return callback(null, "--unlock=" + accountAddress);
|
||||
}
|
||||
callback(null, "");
|
||||
},
|
||||
function gasLimit(callback) {
|
||||
if (config.targetGasLimit) {
|
||||
args.push("--miner.gastarget=" + config.targetGasLimit);
|
||||
return callback(null, "--miner.gastarget=" + config.targetGasLimit);
|
||||
}
|
||||
callback(null, "");
|
||||
},
|
||||
function isDev(callback) {
|
||||
if (self.isDev) {
|
||||
args.push('--dev');
|
||||
return callback(null, '--dev');
|
||||
}
|
||||
callback(null, '');
|
||||
}
|
||||
], function(err) {
|
||||
if (err) {
|
||||
throw new Error(err.message);
|
||||
}
|
||||
return done(self.bin, args);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = GethClient;
|
||||
|
|
@ -1,153 +0,0 @@
|
|||
import { __ } from 'embark-i18n';
|
||||
const async = require('async');
|
||||
const { normalizeInput, deconstructUrl } = require('embark-utils');
|
||||
const constants = require('embark-core/constants');
|
||||
import { BlockchainProcessLauncher } from './blockchainProcessLauncher';
|
||||
import {pingEndpoint} from 'embark-utils';
|
||||
|
||||
export { BlockchainClient } from './blockchain';
|
||||
export { Simulator } from './simulator';
|
||||
|
||||
export default class BlockchainModule {
|
||||
|
||||
constructor(embark, options) {
|
||||
this.logger = embark.logger;
|
||||
this.events = embark.events;
|
||||
this.blockchainConfig = embark.config.blockchainConfig;
|
||||
this.embark = embark;
|
||||
this.locale = options.locale;
|
||||
this.isDev = options.isDev;
|
||||
this.ipc = options.ipc;
|
||||
this.client = options.client;
|
||||
this.blockchainProcess = null;
|
||||
|
||||
this.registerBlockchainProcess();
|
||||
}
|
||||
|
||||
registerBlockchainProcess() {
|
||||
this.events.request('processes:register', 'blockchain', {
|
||||
launchFn: (cb) => {
|
||||
this.assertNodeConnection(true, (connected) => {
|
||||
if (connected) return cb();
|
||||
this.startBlockchainNode(cb);
|
||||
this.listenToCommands();
|
||||
this.registerConsoleCommands();
|
||||
});
|
||||
},
|
||||
stopFn: (cb) => { this.stopBlockchainNode(cb); }
|
||||
});
|
||||
|
||||
if (!this.ipc.isServer()) return;
|
||||
this.ipc.on('blockchain:node', (_message, cb) => {
|
||||
cb(null, this.blockchainConfig.endpoint);
|
||||
});
|
||||
}
|
||||
|
||||
listenToCommands() {
|
||||
this.events.setCommandHandler('logs:ethereum:enable', (cb) => {
|
||||
this.events.emit('logs:ethereum:enable');
|
||||
return cb(null, 'Enabling Geth logs');
|
||||
});
|
||||
|
||||
this.events.setCommandHandler('logs:ethereum:disable', (cb) => {
|
||||
this.events.emit('logs:ethereum:disable');
|
||||
return cb(null, 'Disabling Geth logs');
|
||||
});
|
||||
}
|
||||
|
||||
registerConsoleCommands() {
|
||||
this.embark.registerConsoleCommand({
|
||||
matches: ['log blockchain on'],
|
||||
process: (cmd, callback) => {
|
||||
this.events.request('logs:ethereum:enable', callback);
|
||||
}
|
||||
});
|
||||
this.embark.registerConsoleCommand({
|
||||
matches: ['log blockchain off'],
|
||||
process: (cmd, callback) => {
|
||||
this.events.request('logs:ethereum:disable', callback);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
assertNodeConnection(noLogs, cb) {
|
||||
if (typeof noLogs === 'function') {
|
||||
cb = noLogs;
|
||||
noLogs = false;
|
||||
}
|
||||
const self = this;
|
||||
|
||||
async.waterfall([
|
||||
function checkWeb3State(next) {
|
||||
self.events.request("blockchain:web3:isReady", (connected) => {
|
||||
if (connected) {
|
||||
return next(connected);
|
||||
}
|
||||
next();
|
||||
});
|
||||
},
|
||||
function _pingEndpoint(next) {
|
||||
if (!self.blockchainConfig || !self.blockchainConfig.endpoint) {
|
||||
return next();
|
||||
}
|
||||
const {host, port, type, protocol} = deconstructUrl(self.blockchainConfig.endpoint);
|
||||
pingEndpoint(host, port, type, protocol, self.blockchainConfig.wsOrigins.split(',')[0], next);
|
||||
}
|
||||
], function(err) {
|
||||
if (err === true || err === undefined) {
|
||||
return cb(true);
|
||||
}
|
||||
return cb(false);
|
||||
});
|
||||
}
|
||||
|
||||
startBlockchainNode(callback) {
|
||||
const self = this;
|
||||
|
||||
this.blockchainProcess = new BlockchainProcessLauncher({
|
||||
events: self.events,
|
||||
logger: self.logger,
|
||||
normalizeInput,
|
||||
blockchainConfig: self.blockchainConfig,
|
||||
locale: self.locale,
|
||||
client: self.client,
|
||||
isDev: self.isDev,
|
||||
embark: this.embark
|
||||
});
|
||||
|
||||
this.blockchainProcess.startBlockchainNode();
|
||||
this.events.once(constants.blockchain.blockchainReady, () => {
|
||||
this.assertNodeConnection(true, (connected) => {
|
||||
if (!connected) {
|
||||
return callback(__('Blockchain process is ready, but still cannot connect to it. Check your host, port and protocol in your contracts config'));
|
||||
}
|
||||
this.events.removeListener(constants.blockchain.blockchainExit, callback);
|
||||
callback();
|
||||
});
|
||||
});
|
||||
this.events.once(constants.blockchain.blockchainExit, callback);
|
||||
}
|
||||
|
||||
stopBlockchainNode(cb) {
|
||||
const message = __(`The blockchain process has been stopped. It can be restarted by running ${"service blockchain on".bold} in the Embark console.`);
|
||||
if (this.ipc.isServer()) {
|
||||
if(!this.ipc.connected) {
|
||||
this.ipc.connect(() => {
|
||||
this.ipc.broadcast('process:blockchain:stop');
|
||||
this.logger.info(message);
|
||||
});
|
||||
}
|
||||
else this.ipc.broadcast('process:blockchain:stop');
|
||||
}
|
||||
|
||||
if(!this.blockchainProcess) {
|
||||
return cb();
|
||||
}
|
||||
|
||||
this.blockchainProcess.stopBlockchainNode(() => {
|
||||
this.logger.info(message);
|
||||
cb();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -1,318 +0,0 @@
|
|||
const async = require('async');
|
||||
const NetcatClient = require('netcat/client');
|
||||
|
||||
import { ipcPath } from 'embark-utils';
|
||||
|
||||
//Constants
|
||||
const minerStart = 'miner_start';
|
||||
const minerStop = 'miner_stop';
|
||||
const getHashRate = 'miner_getHashrate';
|
||||
const getCoinbase = 'eth_coinbase';
|
||||
const getBalance = 'eth_getBalance';
|
||||
const newBlockFilter = 'eth_newBlockFilter';
|
||||
const pendingBlockFilter = 'eth_newPendingTransactionFilter';
|
||||
const getChanges = 'eth_getFilterChanges';
|
||||
const getBlockCount = 'eth_getBlockTransactionCountByNumber';
|
||||
|
||||
class GethMiner {
|
||||
constructor(options) {
|
||||
const self = this;
|
||||
// TODO: Find a way to load mining config from YML.
|
||||
// In the meantime, just set an empty config object
|
||||
this.config = {};
|
||||
this.datadir = options.datadir;
|
||||
self.interval = null;
|
||||
self.callback = null;
|
||||
self.started = null;
|
||||
|
||||
self.commandQueue = async.queue((task, callback) => {
|
||||
self.callback = callback;
|
||||
self.client.send(JSON.stringify({"jsonrpc": "2.0", "method": task.method, "params": task.params || [], "id": 1}));
|
||||
}, 1);
|
||||
|
||||
const defaults = {
|
||||
interval_ms: 15000,
|
||||
initial_ether: 15000000000000000000,
|
||||
mine_pending_txns: true,
|
||||
mine_periodically: false,
|
||||
mine_normally: false,
|
||||
threads: 1
|
||||
};
|
||||
|
||||
for (let key in defaults) {
|
||||
if (this.config[key] === undefined) {
|
||||
this.config[key] = defaults[key];
|
||||
}
|
||||
}
|
||||
|
||||
this.client = new NetcatClient();
|
||||
this.client.unixSocket(ipcPath('geth.ipc', true))
|
||||
.enc('utf8')
|
||||
.connect()
|
||||
.on('data', (response) => {
|
||||
try {
|
||||
response = JSON.parse(response);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
return;
|
||||
}
|
||||
if (self.callback) {
|
||||
self.callback(response.error, response.result);
|
||||
}
|
||||
});
|
||||
|
||||
if (this.config.mine_normally) {
|
||||
this.startMiner();
|
||||
return;
|
||||
}
|
||||
|
||||
self.stopMiner(() => {
|
||||
self.fundAccount(function (err) {
|
||||
if (err) {
|
||||
console.error(err);
|
||||
return;
|
||||
}
|
||||
if (self.config.mine_periodically) self.start_periodic_mining();
|
||||
if (self.config.mine_pending_txns) self.start_transaction_mining();
|
||||
});
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
sendCommand(method, params, callback) {
|
||||
if (typeof params === 'function') {
|
||||
callback = params;
|
||||
params = [];
|
||||
}
|
||||
if (!callback) {
|
||||
callback = function () {
|
||||
};
|
||||
}
|
||||
this.commandQueue.push({method, params: params || []}, callback);
|
||||
}
|
||||
|
||||
startMiner(callback) {
|
||||
if (this.started) {
|
||||
if (callback) {
|
||||
callback();
|
||||
}
|
||||
return;
|
||||
}
|
||||
this.started = true;
|
||||
this.sendCommand(minerStart, callback);
|
||||
}
|
||||
|
||||
stopMiner(callback) {
|
||||
if (!this.started) {
|
||||
if (callback) {
|
||||
callback();
|
||||
}
|
||||
return;
|
||||
}
|
||||
this.started = false;
|
||||
this.sendCommand(minerStop, callback);
|
||||
}
|
||||
|
||||
getCoinbase(callback) {
|
||||
if (this.coinbase) {
|
||||
return callback(null, this.coinbase);
|
||||
}
|
||||
this.sendCommand(getCoinbase, (err, result) => {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
this.coinbase = result;
|
||||
if (!this.coinbase) {
|
||||
return callback('Failed getting coinbase account');
|
||||
}
|
||||
callback(null, this.coinbase);
|
||||
});
|
||||
}
|
||||
|
||||
accountFunded(callback) {
|
||||
const self = this;
|
||||
self.getCoinbase((err, coinbase) => {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
self.sendCommand(getBalance, [coinbase, 'latest'], (err, result) => {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
callback(null, parseInt(result, 16) >= self.config.initial_ether);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
watchBlocks(filterCommand, callback, delay) {
|
||||
const self = this;
|
||||
self.sendCommand(filterCommand, (err, filterId) => {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
self.interval = setInterval(() => {
|
||||
self.sendCommand(getChanges, [filterId], (err, changes) => {
|
||||
if (err) {
|
||||
console.error(err);
|
||||
return;
|
||||
}
|
||||
if (!changes || !changes.length) {
|
||||
return;
|
||||
}
|
||||
callback(null, changes);
|
||||
});
|
||||
}, delay || 1000);
|
||||
});
|
||||
}
|
||||
|
||||
mineUntilFunded(callback) {
|
||||
const self = this;
|
||||
this.startMiner();
|
||||
self.watchBlocks(newBlockFilter, (err) => {
|
||||
if (err) {
|
||||
console.error(err);
|
||||
return;
|
||||
}
|
||||
self.accountFunded((err, funded) => {
|
||||
if (funded) {
|
||||
clearTimeout(self.interval);
|
||||
self.stopMiner();
|
||||
callback();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
fundAccount(callback) {
|
||||
const self = this;
|
||||
|
||||
self.accountFunded((err, funded) => {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
if (funded) {
|
||||
return callback();
|
||||
}
|
||||
|
||||
console.log("== Funding account");
|
||||
self.mineUntilFunded(callback);
|
||||
});
|
||||
}
|
||||
|
||||
pendingTransactions(callback) {
|
||||
const self = this;
|
||||
self.sendCommand(getBlockCount, ['pending'], (err, hexCount) => {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
callback(null, parseInt(hexCount, 16));
|
||||
});
|
||||
}
|
||||
|
||||
start_periodic_mining() {
|
||||
const self = this;
|
||||
const WAIT = 'wait';
|
||||
let last_mined_ms = Date.now();
|
||||
let timeout_set = false;
|
||||
let next_block_in_ms;
|
||||
|
||||
self.startMiner();
|
||||
self.watchBlocks(newBlockFilter, (err) => {
|
||||
if (err) {
|
||||
console.error(err);
|
||||
return;
|
||||
}
|
||||
if (timeout_set) {
|
||||
return;
|
||||
}
|
||||
async.waterfall([
|
||||
function checkPendingTransactions(next) {
|
||||
if (!self.config.mine_pending_txns) {
|
||||
return next();
|
||||
}
|
||||
self.pendingTransactions((err, count) => {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
if (count) {
|
||||
return next(WAIT);
|
||||
}
|
||||
next();
|
||||
});
|
||||
},
|
||||
function stopMiner(next) {
|
||||
timeout_set = true;
|
||||
|
||||
const now = Date.now();
|
||||
const ms_since_block = now - last_mined_ms;
|
||||
last_mined_ms = now;
|
||||
|
||||
if (ms_since_block > self.config.interval_ms) {
|
||||
next_block_in_ms = 0;
|
||||
} else {
|
||||
next_block_in_ms = (self.config.interval_ms - ms_since_block);
|
||||
}
|
||||
self.stopMiner();
|
||||
console.log("== Looking for next block in " + next_block_in_ms + "ms");
|
||||
next();
|
||||
},
|
||||
function startAfterTimeout(next) {
|
||||
setTimeout(function () {
|
||||
console.log("== Looking for next block");
|
||||
timeout_set = false;
|
||||
self.startMiner();
|
||||
next();
|
||||
}, next_block_in_ms);
|
||||
}
|
||||
], (err) => {
|
||||
if (err === WAIT) {
|
||||
return;
|
||||
}
|
||||
if (err) {
|
||||
console.error(err);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
start_transaction_mining() {
|
||||
const self = this;
|
||||
const pendingTrasactionsMessage = "== Pending transactions! Looking for next block...";
|
||||
self.watchBlocks(pendingBlockFilter, (err) => {
|
||||
if (err) {
|
||||
console.error(err);
|
||||
return;
|
||||
}
|
||||
self.sendCommand(getHashRate, (err, result) => {
|
||||
if (result > 0) return;
|
||||
|
||||
console.log(pendingTrasactionsMessage);
|
||||
self.startMiner();
|
||||
});
|
||||
}, 2000);
|
||||
|
||||
if (self.config.mine_periodically) return;
|
||||
|
||||
self.watchBlocks(newBlockFilter, (err) => {
|
||||
if (err) {
|
||||
console.error(err);
|
||||
return;
|
||||
}
|
||||
self.pendingTransactions((err, count) => {
|
||||
if (err) {
|
||||
console.error(err);
|
||||
return;
|
||||
}
|
||||
if (!count) {
|
||||
console.log("== No transactions left. Stopping miner...");
|
||||
self.stopMiner();
|
||||
} else {
|
||||
console.log(pendingTrasactionsMessage);
|
||||
self.startMiner();
|
||||
}
|
||||
});
|
||||
}, 2000);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = GethMiner;
|
|
@ -1,407 +0,0 @@
|
|||
import { __ } from 'embark-i18n';
|
||||
import { dappPath } from 'embark-utils';
|
||||
import * as fs from 'fs-extra';
|
||||
const async = require('async');
|
||||
const path = require('path');
|
||||
const os = require('os');
|
||||
const semver = require('semver');
|
||||
const constants = require('embark-core/constants');
|
||||
|
||||
const DEFAULTS = {
|
||||
"BIN": "parity",
|
||||
"VERSIONS_SUPPORTED": ">=2.0.0",
|
||||
"NETWORK_TYPE": "dev",
|
||||
"NETWORK_ID": 17,
|
||||
"RPC_API": ["web3", "eth", "pubsub", "net", "parity", "private", "parity_pubsub", "traces", "rpc", "shh", "shh_pubsub"],
|
||||
"WS_API": ["web3", "eth", "pubsub", "net", "parity", "private", "parity_pubsub", "traces", "rpc", "shh", "shh_pubsub"],
|
||||
"DEV_WS_API": ["web3", "eth", "pubsub", "net", "parity", "private", "parity_pubsub", "traces", "rpc", "shh", "shh_pubsub", "personal"],
|
||||
"TARGET_GAS_LIMIT": 8000000,
|
||||
"DEV_ACCOUNT": "0x00a329c0648769a73afac7f9381e08fb43dbea72",
|
||||
"DEV_WALLET": {
|
||||
"id": "d9460e00-6895-8f58-f40c-bb57aebe6c00",
|
||||
"version": 3,
|
||||
"crypto": {
|
||||
"cipher": "aes-128-ctr",
|
||||
"cipherparams": {"iv": "74245f453143f9d06a095c6e6e309d5d"},
|
||||
"ciphertext": "2fa611c4aa66452ef81bd1bd288f9d1ed14edf61aa68fc518093f97c791cf719",
|
||||
"kdf": "pbkdf2",
|
||||
"kdfparams": {"c": 10240, "dklen": 32, "prf": "hmac-sha256", "salt": "73b74e437a1144eb9a775e196f450a23ab415ce2c17083c225ddbb725f279b98"},
|
||||
"mac": "f5882ae121e4597bd133136bf15dcbcc1bb2417a25ad205041a50c59def812a8"
|
||||
},
|
||||
"address": "00a329c0648769a73afac7f9381e08fb43dbea72",
|
||||
"name": "Development Account",
|
||||
"meta": "{\"description\":\"Never use this account outside of development chain!\",\"passwordHint\":\"Password is empty string\"}"
|
||||
}
|
||||
};
|
||||
|
||||
const safePush = function(set, value) {
|
||||
if (set.indexOf(value) === -1) {
|
||||
set.push(value);
|
||||
}
|
||||
};
|
||||
|
||||
class ParityClient {
|
||||
|
||||
static get DEFAULTS() {
|
||||
return DEFAULTS;
|
||||
}
|
||||
|
||||
constructor(options) {
|
||||
this.config = options && options.hasOwnProperty('config') ? options.config : {};
|
||||
this.env = options && options.hasOwnProperty('env') ? options.env : 'development';
|
||||
this.isDev = options && options.hasOwnProperty('isDev') ? options.isDev : (this.env === 'development');
|
||||
this.name = constants.blockchain.clients.parity;
|
||||
this.prettyName = "Parity-Ethereum (https://github.com/paritytech/parity-ethereum)";
|
||||
this.bin = this.config.ethereumClientBin || DEFAULTS.BIN;
|
||||
this.versSupported = DEFAULTS.VERSIONS_SUPPORTED;
|
||||
}
|
||||
|
||||
isReady(data) {
|
||||
return data.indexOf('Public node URL') > -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the client needs some sort of 'keep alive' transactions to avoid freezing by inactivity
|
||||
* @returns {boolean} if keep alive is needed
|
||||
*/
|
||||
needKeepAlive() {
|
||||
return false;
|
||||
}
|
||||
|
||||
commonOptions() {
|
||||
let config = this.config;
|
||||
let cmd = [];
|
||||
|
||||
cmd.push(this.determineNetworkType(config));
|
||||
|
||||
if (config.networkId) {
|
||||
cmd.push(`--network-id=${config.networkId}`);
|
||||
}
|
||||
|
||||
if (config.datadir) {
|
||||
cmd.push(`--base-path=${config.datadir}`);
|
||||
}
|
||||
|
||||
if (config.syncMode === 'light') {
|
||||
cmd.push("--light");
|
||||
} else if (config.syncMode === 'fast') {
|
||||
cmd.push("--pruning=fast");
|
||||
} else if (config.syncMode === 'full') {
|
||||
cmd.push("--pruning=archive");
|
||||
}
|
||||
|
||||
// In dev mode we store all users passwords in the devPassword file, so Parity can unlock all users from the start
|
||||
if (this.isDev) cmd.push(`--password=${config.account.devPassword}`);
|
||||
else if (config.account && config.account.password) {
|
||||
cmd.push(`--password=${config.account.password}`);
|
||||
}
|
||||
|
||||
if (Number.isInteger(config.verbosity) && config.verbosity >= 0 && config.verbosity <= 5) {
|
||||
switch (config.verbosity) {
|
||||
case 0: // No option to silent Parity, go to less verbose
|
||||
case 1:
|
||||
cmd.push("--logging=error");
|
||||
break;
|
||||
case 2:
|
||||
cmd.push("--logging=warn");
|
||||
break;
|
||||
case 3:
|
||||
cmd.push("--logging=info");
|
||||
break;
|
||||
case 4: // Debug is the max verbosity for Parity
|
||||
case 5:
|
||||
cmd.push("--logging=debug");
|
||||
break;
|
||||
default:
|
||||
cmd.push("--logging=info");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(this.runAsArchival(config)) {
|
||||
cmd.push("--pruning=archive");
|
||||
}
|
||||
|
||||
return cmd;
|
||||
}
|
||||
|
||||
getMiner() {
|
||||
console.warn(__("Miner requested, but Parity does not embed a miner! Use Geth or install ethminer (https://github.com/ethereum-mining/ethminer)").yellow);
|
||||
return;
|
||||
}
|
||||
|
||||
getBinaryPath() {
|
||||
return this.bin;
|
||||
}
|
||||
|
||||
determineVersionCommand() {
|
||||
return this.bin + " --version";
|
||||
}
|
||||
|
||||
parseVersion(rawVersionOutput) {
|
||||
let parsed;
|
||||
const match = rawVersionOutput.match(/version Parity(?:-Ethereum)?\/(.*?)\//);
|
||||
if (match) {
|
||||
parsed = match[1].trim();
|
||||
}
|
||||
return parsed;
|
||||
}
|
||||
|
||||
runAsArchival(config) {
|
||||
return config.networkId === 1337 || config.archivalMode;
|
||||
}
|
||||
|
||||
isSupportedVersion(parsedVersion) {
|
||||
let test;
|
||||
try {
|
||||
let v = semver(parsedVersion);
|
||||
v = `${v.major}.${v.minor}.${v.patch}`;
|
||||
test = semver.Range(this.versSupported).test(semver(v));
|
||||
if (typeof test !== 'boolean') {
|
||||
test = undefined;
|
||||
}
|
||||
} finally {
|
||||
// eslint-disable-next-line no-unsafe-finally
|
||||
return test;
|
||||
}
|
||||
}
|
||||
|
||||
determineNetworkType(config) {
|
||||
if (this.isDev) {
|
||||
return "--chain=dev";
|
||||
}
|
||||
if (config.networkType === 'rinkeby') {
|
||||
console.warn(__('Parity does not support the Rinkeby PoA network, switching to Kovan PoA network'));
|
||||
config.networkType = 'kovan';
|
||||
} else if (config.networkType === 'testnet') {
|
||||
console.warn(__('Parity "testnet" corresponds to Kovan network, switching to Ropsten to be compliant with Geth parameters'));
|
||||
config.networkType = "ropsten";
|
||||
}
|
||||
if (config.genesisBlock) {
|
||||
config.networkType = config.genesisBlock;
|
||||
}
|
||||
return "--chain=" + config.networkType;
|
||||
}
|
||||
|
||||
newAccountCommand() {
|
||||
return this.bin + " " + this.commonOptions().join(' ') + " account new ";
|
||||
}
|
||||
|
||||
parseNewAccountCommandResultToAddress(data = "") {
|
||||
return data.replace(/^\n|\n$/g, "");
|
||||
}
|
||||
|
||||
listAccountsCommand() {
|
||||
return this.bin + " " + this.commonOptions().join(' ') + " account list ";
|
||||
}
|
||||
|
||||
parseListAccountsCommandResultToAddress(data = "") {
|
||||
return data.replace(/^\n|\n$/g, "").split('\n')[0];
|
||||
}
|
||||
|
||||
parseListAccountsCommandResultToAddressList(data = "") {
|
||||
const list = data.split('\n');
|
||||
return list.filter(acc => acc);
|
||||
}
|
||||
|
||||
parseListAccountsCommandResultToAddressCount(data = "") {
|
||||
const count = this.parseListAccountsCommandResultToAddressList(data).length;
|
||||
return (count > 0 ? count : 0);
|
||||
}
|
||||
|
||||
determineRpcOptions(config) {
|
||||
let cmd = [];
|
||||
cmd.push("--port=" + config.port);
|
||||
cmd.push("--jsonrpc-port=" + config.rpcPort);
|
||||
cmd.push("--jsonrpc-interface=" + (config.rpcHost === 'localhost' ? 'local' : config.rpcHost));
|
||||
if (config.rpcCorsDomain) {
|
||||
if (config.rpcCorsDomain === '*') {
|
||||
console.warn('==================================');
|
||||
console.warn(__('rpcCorsDomain set to "all"'));
|
||||
console.warn(__('make sure you know what you are doing'));
|
||||
console.warn('==================================');
|
||||
}
|
||||
cmd.push("--jsonrpc-cors=" + (config.rpcCorsDomain === '*' ? 'all' : config.rpcCorsDomain));
|
||||
} else {
|
||||
console.warn('==================================');
|
||||
console.warn(__('warning: cors is not set'));
|
||||
console.warn('==================================');
|
||||
}
|
||||
cmd.push("--jsonrpc-hosts=all");
|
||||
return cmd;
|
||||
}
|
||||
|
||||
determineWsOptions(config) {
|
||||
let cmd = [];
|
||||
if (config.wsRPC) {
|
||||
cmd.push("--ws-port=" + config.wsPort);
|
||||
cmd.push("--ws-interface=" + (config.wsHost === 'localhost' ? 'local' : config.wsHost));
|
||||
if (config.wsOrigins) {
|
||||
const origins = config.wsOrigins.split(',');
|
||||
if (origins.includes('*') || origins.includes("all")) {
|
||||
console.warn('==================================');
|
||||
console.warn(__('wsOrigins set to "all"'));
|
||||
console.warn(__('make sure you know what you are doing'));
|
||||
console.warn('==================================');
|
||||
cmd.push("--ws-origins=all");
|
||||
} else {
|
||||
cmd.push("--ws-origins=" + config.wsOrigins);
|
||||
}
|
||||
} else {
|
||||
console.warn('==================================');
|
||||
console.warn(__('warning: wsOrigins is not set'));
|
||||
console.warn('==================================');
|
||||
}
|
||||
cmd.push("--ws-hosts=all");
|
||||
}
|
||||
return cmd;
|
||||
}
|
||||
|
||||
initDevChain(datadir, callback) {
|
||||
// Parity requires specific initialization also for the dev chain
|
||||
const self = this;
|
||||
const keysDataDir = datadir + '/keys/DevelopmentChain';
|
||||
async.waterfall([
|
||||
function makeDir(next) {
|
||||
fs.mkdirp(keysDataDir, (err, _result) => {
|
||||
next(err);
|
||||
});
|
||||
},
|
||||
function createDevAccount(next) {
|
||||
self.createDevAccount(keysDataDir, next);
|
||||
},
|
||||
function mkDevPasswordDir(next) {
|
||||
fs.mkdirp(path.dirname(self.config.account.devPassword), (err, _result) => {
|
||||
next(err);
|
||||
});
|
||||
},
|
||||
function getText(next) {
|
||||
if (!self.config.account.password) {
|
||||
return next(null, os.EOL + 'dev_password');
|
||||
}
|
||||
fs.readFile(dappPath(self.config.account.password), {encoding: 'utf8'}, (err, content) => {
|
||||
next(err, os.EOL + content);
|
||||
});
|
||||
},
|
||||
function updatePasswordFile(passwordList, next) {
|
||||
fs.writeFile(self.config.account.devPassword, passwordList, next);
|
||||
}
|
||||
], (err) => {
|
||||
callback(err);
|
||||
});
|
||||
}
|
||||
|
||||
createDevAccount(keysDataDir, cb) {
|
||||
const devAccountWallet = keysDataDir + '/dev.wallet';
|
||||
fs.writeFile(devAccountWallet, JSON.stringify(DEFAULTS.DEV_WALLET), function(err) {
|
||||
if (err) {
|
||||
return cb(err);
|
||||
}
|
||||
cb();
|
||||
});
|
||||
}
|
||||
|
||||
mainCommand(address, done) {
|
||||
let self = this;
|
||||
let config = this.config;
|
||||
let rpc_api = this.config.rpcApi;
|
||||
let ws_api = this.config.wsApi;
|
||||
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("--no-discovery");
|
||||
return callback(null, "--no-discovery");
|
||||
}
|
||||
callback(null, "");
|
||||
},
|
||||
function vmDebug(callback) {
|
||||
if (config.vmdebug) {
|
||||
args.push("--tracing on");
|
||||
return callback(null, "--tracing on");
|
||||
}
|
||||
callback(null, "");
|
||||
},
|
||||
function maxPeers(callback) {
|
||||
let cmd = "--max-peers=" + config.maxpeers;
|
||||
args.push(cmd);
|
||||
callback(null, cmd);
|
||||
},
|
||||
function bootnodes(callback) {
|
||||
if (config.bootnodes && config.bootnodes !== "" && config.bootnodes !== []) {
|
||||
args.push("--bootnodes=" + config.bootnodes);
|
||||
return callback(null, "--bootnodes=" + config.bootnodes);
|
||||
}
|
||||
callback("");
|
||||
},
|
||||
function whisper(callback) {
|
||||
if (config.whisper) {
|
||||
safePush(rpc_api, 'shh');
|
||||
safePush(rpc_api, 'shh_pubsub');
|
||||
safePush(ws_api, 'shh');
|
||||
safePush(ws_api, 'shh_pubsub');
|
||||
args.push("--whisper");
|
||||
return callback(null, "--whisper");
|
||||
}
|
||||
callback("");
|
||||
},
|
||||
function rpcApi(callback) {
|
||||
args.push('--jsonrpc-apis=' + rpc_api.join(','));
|
||||
callback(null, '--jsonrpc-apis=' + rpc_api.join(','));
|
||||
},
|
||||
function wsApi(callback) {
|
||||
args.push('--ws-apis=' + ws_api.join(','));
|
||||
callback(null, '--ws-apis=' + ws_api.join(','));
|
||||
},
|
||||
function accountToUnlock(callback) {
|
||||
if (self.isDev) {
|
||||
let unlockAddressList = self.config.unlockAddressList ? self.config.unlockAddressList : DEFAULTS.DEV_ACCOUNT;
|
||||
args.push("--unlock=" + unlockAddressList);
|
||||
return callback(null, "--unlock=" + unlockAddressList);
|
||||
}
|
||||
let accountAddress = "";
|
||||
if (config.account && config.account.address) {
|
||||
accountAddress = config.account.address;
|
||||
} else {
|
||||
accountAddress = address;
|
||||
}
|
||||
if (accountAddress && !self.isDev) {
|
||||
args.push("--unlock=" + accountAddress);
|
||||
return callback(null, "--unlock=" + accountAddress);
|
||||
}
|
||||
callback(null, "");
|
||||
},
|
||||
function gasLimit(callback) {
|
||||
if (config.targetGasLimit) {
|
||||
args.push("--gas-floor-target=" + config.targetGasLimit);
|
||||
return callback(null, "--gas-floor-target=" + config.targetGasLimit);
|
||||
}
|
||||
// Default Parity gas limit is 4700000: let's set to the geth default
|
||||
args.push("--gas-floor-target=" + DEFAULTS.TARGET_GAS_LIMIT);
|
||||
return callback(null, "--gas-floor-target=" + DEFAULTS.TARGET_GAS_LIMIT);
|
||||
}
|
||||
], function(err) {
|
||||
if (err) {
|
||||
throw new Error(err.message);
|
||||
}
|
||||
return done(self.bin, args);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ParityClient;
|
|
@ -1,88 +0,0 @@
|
|||
const path = require('path');
|
||||
const pkgUp = require('pkg-up');
|
||||
let shelljs = require('shelljs');
|
||||
import {AccountParser, dappPath, defaultHost, dockerHostSwap, embarkPath, deconstructUrl} from 'embark-utils';
|
||||
|
||||
export class Simulator {
|
||||
constructor(options) {
|
||||
this.blockchainConfig = options.blockchainConfig;
|
||||
this.contractsConfig = options.contractsConfig;
|
||||
this.logger = options.logger;
|
||||
}
|
||||
|
||||
/*eslint complexity: ["error", 26]*/
|
||||
run(options) {
|
||||
let cmds = [];
|
||||
|
||||
let {host, port} = deconstructUrl(this.blockchainConfig.endpoint);
|
||||
host = (dockerHostSwap(options.host || host) || defaultHost);
|
||||
port = (options.port || port || 8545);
|
||||
port = parseInt(port, 10);
|
||||
|
||||
cmds.push("-p " + port);
|
||||
cmds.push("-h " + host);
|
||||
cmds.push("-l " + (options.gasLimit || this.blockchainConfig.targetGasLimit || 8000000));
|
||||
|
||||
// adding mnemonic only if it is defined in the blockchainConfig or options
|
||||
let mnemonicAccount = this.blockchainConfig.accounts ? this.blockchainConfig.accounts.find(acc => acc.mnemonic) : {};
|
||||
mnemonicAccount = mnemonicAccount || {};
|
||||
const simulatorMnemonic = mnemonicAccount.mnemonic || options.simulatorMnemonic;
|
||||
|
||||
if (simulatorMnemonic) {
|
||||
cmds.push("--mnemonic \"" + (simulatorMnemonic) + "\"");
|
||||
}
|
||||
cmds.push("-a " + (options.numAccounts || mnemonicAccount.numAddresses || 10));
|
||||
cmds.push("-e " + (options.defaultBalance || mnemonicAccount.balance || 100));
|
||||
|
||||
// as ganache-cli documentation explains, the simulatorAccounts configuration overrides a mnemonic
|
||||
let simulatorAccounts = this.blockchainConfig.simulatorAccounts || options.simulatorAccounts;
|
||||
if (simulatorAccounts && simulatorAccounts.length > 0) {
|
||||
let web3 = new (require('web3'))();
|
||||
let parsedAccounts;
|
||||
try {
|
||||
parsedAccounts = AccountParser.parseAccountsConfig(simulatorAccounts, web3, dappPath(), this.logger);
|
||||
} catch (e) {
|
||||
this.logger.error(e.message);
|
||||
process.exit(1);
|
||||
}
|
||||
parsedAccounts.forEach((account) => {
|
||||
let cmd = '--account="' + account.privateKey + ',' + account.hexBalance + '"';
|
||||
cmds.push(cmd);
|
||||
});
|
||||
}
|
||||
|
||||
// adding blocktime only if it is defined in the blockchainConfig or options
|
||||
let simulatorBlocktime = this.blockchainConfig.simulatorBlocktime || options.simulatorBlocktime;
|
||||
if (simulatorBlocktime) {
|
||||
cmds.push("-b \"" + (simulatorBlocktime) + "\"");
|
||||
}
|
||||
|
||||
// Setting up network id for simulator from blockchainConfig or options.
|
||||
// Otherwise ganache-cli would make random network id.
|
||||
let networkId = this.blockchainConfig.networkId || options.networkId;
|
||||
if (networkId) { // Don't handle networkId=="0" because it is not a valid networkId for ganache-cli.
|
||||
cmds.push("--networkId " + networkId);
|
||||
}
|
||||
|
||||
this.runCommand(cmds, host, port);
|
||||
}
|
||||
|
||||
runCommand(cmds) {
|
||||
const ganache_main = require.resolve('ganache-cli', {paths: [embarkPath('node_modules')]});
|
||||
const ganache_json = pkgUp.sync(path.dirname(ganache_main));
|
||||
const ganache_root = path.dirname(ganache_json);
|
||||
const ganache_bin = require(ganache_json).bin;
|
||||
let ganache;
|
||||
if (typeof ganache_bin === 'string') {
|
||||
ganache = path.join(ganache_root, ganache_bin);
|
||||
} else {
|
||||
ganache = path.join(ganache_root, ganache_bin['ganache-cli']);
|
||||
}
|
||||
|
||||
const programName = 'ganache-cli';
|
||||
const program = ganache;
|
||||
console.log(`running: ${programName} ${cmds.join(' ')}`);
|
||||
|
||||
shelljs.exec(`node ${program} ${cmds.join(' ')}`, {async: true});
|
||||
}
|
||||
}
|
|
@ -1,4 +0,0 @@
|
|||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"include": ["src/**/*"]
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
{
|
||||
"extends": "../../tslint.json"
|
||||
}
|
|
@ -1,4 +0,0 @@
|
|||
engine-strict = true
|
||||
package-lock = false
|
||||
save-exact = true
|
||||
scripts-prepend-node-path = true
|
|
@ -1,70 +0,0 @@
|
|||
# Change Log
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [4.1.1](https://github.com/embark-framework/embark/compare/v4.1.0...v4.1.1) (2019-08-28)
|
||||
|
||||
**Note:** Version bump only for package embark-code-generator
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [4.1.0](https://github.com/embark-framework/embark/compare/v4.1.0-beta.6...v4.1.0) (2019-08-12)
|
||||
|
||||
**Note:** Version bump only for package embark-code-generator
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [4.1.0-beta.6](https://github.com/embark-framework/embark/compare/v4.1.0-beta.5...v4.1.0-beta.6) (2019-08-09)
|
||||
|
||||
**Note:** Version bump only for package embark-code-generator
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [4.1.0-beta.5](https://github.com/embark-framework/embark/compare/v4.1.0-beta.4...v4.1.0-beta.5) (2019-07-10)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **@embark/code-generator:** use plugins for contract generation ([c87d7da](https://github.com/embark-framework/embark/commit/c87d7da))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [4.1.0-beta.4](https://github.com/embark-framework/embark/compare/v4.1.0-beta.3...v4.1.0-beta.4) (2019-06-27)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* alleviate races re: embarkjs by introducing Plugin#addGeneratedCode and related refactors ([fc4faa8](https://github.com/embark-framework/embark/commit/fc4faa8))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [4.1.0-beta.3](https://github.com/embark-framework/embark/compare/v4.1.0-beta.2...v4.1.0-beta.3) (2019-06-07)
|
||||
|
||||
**Note:** Version bump only for package embark-code-generator
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [4.1.0-beta.2](https://github.com/embark-framework/embark/compare/v4.1.0-beta.1...v4.1.0-beta.2) (2019-05-22)
|
||||
|
||||
**Note:** Version bump only for package embark-code-generator
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [4.1.0-beta.1](https://github.com/embark-framework/embark/compare/v4.1.0-beta.0...v4.1.0-beta.1) (2019-05-15)
|
||||
|
||||
**Note:** Version bump only for package embark-code-generator
|
|
@ -1,6 +0,0 @@
|
|||
# `embark-code-generator`
|
||||
|
||||
Embark code generator
|
||||
|
||||
Visit [embark.status.im](https://embark.status.im/) to get started with
|
||||
[Embark](https://github.com/embark-framework/embark).
|
|
@ -1,73 +0,0 @@
|
|||
{
|
||||
"name": "embark-code-generator",
|
||||
"version": "4.1.1",
|
||||
"author": "Iuri Matias <iuri.matias@gmail.com>",
|
||||
"contributors": [],
|
||||
"description": "Embark code generator",
|
||||
"homepage": "https://github.com/embark-framework/embark/tree/master/packages/embark-code-generator#readme",
|
||||
"bugs": "https://github.com/embark-framework/embark/issues",
|
||||
"keywords": [
|
||||
"blockchain",
|
||||
"dapps",
|
||||
"ethereum",
|
||||
"ipfs",
|
||||
"serverless",
|
||||
"solc",
|
||||
"solidity"
|
||||
],
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
"directory": "packages/embark-code-generator",
|
||||
"type": "git",
|
||||
"url": "https://github.com/embark-framework/embark.git"
|
||||
},
|
||||
"main": "./dist/index.js",
|
||||
"scripts": {
|
||||
"build": "cross-env BABEL_ENV=node babel src --extensions \".js\" --out-dir dist --root-mode upward --source-maps --copy-files",
|
||||
"ci": "npm run qa",
|
||||
"clean": "npm run reset",
|
||||
"lint": "npm-run-all lint:*",
|
||||
"lint:js": "eslint src/",
|
||||
"// lint:ts": "tslint -c tslint.json \"src/**/*.ts\"",
|
||||
"package": "npm pack",
|
||||
"// qa": "npm-run-all lint typecheck build package",
|
||||
"qa": "npm-run-all lint build package",
|
||||
"reset": "npx rimraf dist embark-*.tgz package",
|
||||
"start": "npm run watch",
|
||||
"// typecheck": "tsc",
|
||||
"watch": "run-p watch:*",
|
||||
"watch:build": "npm run build -- --verbose --watch",
|
||||
"// watch:typecheck": "npm run typecheck -- --preserveWatchOutput --watch"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": "../../.eslintrc.json"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/core": "7.2.2",
|
||||
"@babel/runtime-corejs2": "7.3.1",
|
||||
"async": "2.6.1",
|
||||
"ejs": "2.6.1",
|
||||
"embark-core": "^4.1.1",
|
||||
"embark-i18n": "^4.1.1",
|
||||
"embark-utils": "^4.1.1",
|
||||
"fs-extra": "7.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "7.2.3",
|
||||
"@babel/core": "7.2.2",
|
||||
"cross-env": "5.2.0",
|
||||
"eslint": "5.7.0",
|
||||
"npm-run-all": "4.1.5",
|
||||
"rimraf": "3.0.0",
|
||||
"tslint": "5.16.0",
|
||||
"typescript": "3.4.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8.12.0 <12.0.0",
|
||||
"npm": ">=6.4.1",
|
||||
"yarn": ">=1.12.3"
|
||||
}
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
if (typeof WebSocket !== 'undefined') {
|
||||
const ws = new WebSocket(`${location.protocol === 'https:' ? 'wss' : 'ws'}://${location.hostname}:${location.port}`);
|
||||
ws.addEventListener('message', (evt) => {
|
||||
if (evt.data === 'outputDone') {
|
||||
location.reload(true);
|
||||
}
|
||||
});
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
var whenEnvIsLoaded = function(cb) {
|
||||
if (typeof document !== 'undefined' && document !== null && !/comp|inter|loaded/.test(document.readyState)) {
|
||||
document.addEventListener('DOMContentLoaded', cb);
|
||||
} else {
|
||||
cb();
|
||||
}
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
whenEnvIsLoaded(function(){
|
||||
__mainContext.__loadManagerInstance.doFirst(function(done) {
|
||||
<%- block %>
|
||||
});
|
||||
|
||||
EmbarkJS.environment = "<%- environment %>";
|
||||
});
|
|
@ -1 +0,0 @@
|
|||
__mainContext.<%- className %> = new EmbarkJS.Blockchain.Contract({abi: <%- abi %>, address: <%- contractAddress %>, code: '<%- contract.code %>', gasEstimates: <%- gasEstimates %>});
|
|
@ -1,3 +0,0 @@
|
|||
whenEnvIsLoaded(function() {
|
||||
<%- block %>
|
||||
});
|
|
@ -1,3 +0,0 @@
|
|||
__mainContext.__loadManagerInstance.execWhenReady(function() {
|
||||
<%- block %>
|
||||
});
|
|
@ -1,4 +0,0 @@
|
|||
__mainContext.__LoadManager = function() { this.list = []; this.done = false; this.err = null; }
|
||||
__mainContext.__LoadManager.prototype.execWhenReady = function(cb) { if (this.done) { cb(this.err); } else { this.list.push(cb) } }
|
||||
__mainContext.__LoadManager.prototype.doFirst = function(todo) { var self = this; todo(function(err) { self.done = true; self.err = err; self.list.map((x) => x.apply(x, [self.err])) }) }
|
||||
__mainContext.__loadManagerInstance = new __mainContext.__LoadManager();
|
|
@ -1,3 +0,0 @@
|
|||
var __mainContext = __mainContext || (
|
||||
this ? this : typeof self !== 'undefined' ? self : void 0
|
||||
);
|
|
@ -1,9 +0,0 @@
|
|||
<%- className %>Abi = <%- abi %>;
|
||||
<%- className %> = new web3.eth.Contract(<%- className %>Abi);
|
||||
<%- className %>.options.address = '<%- contract.deployedAddress %>';
|
||||
<%- className %>.address = '<%- contract.deployedAddress %>';
|
||||
<%- className %>.options.from = web3.eth.defaultAccount;
|
||||
<% if (gasLimit != false) { %>
|
||||
<%- className %>.options.gas = <%- gasLimit %>;
|
||||
<%- className %>.options.gasLimit = <%- gasLimit %>;
|
||||
<% } %>
|
|
@ -1,544 +0,0 @@
|
|||
import { __ } from 'embark-i18n';
|
||||
import { dappPath, embarkPath, joinPath, toForwardSlashes } from 'embark-utils';
|
||||
import * as fs from 'fs-extra';
|
||||
import { transform } from "@babel/core";
|
||||
const async = require('async');
|
||||
const constants = require('embark-core/constants');
|
||||
const path = require('path');
|
||||
|
||||
require('ejs');
|
||||
const Templates = {
|
||||
vanilla_contract: require('./code_templates/vanilla-contract.js.ejs'),
|
||||
embarkjs_contract: require('./code_templates/embarkjs-contract.js.ejs'),
|
||||
exec_when_ready: require('./code_templates/exec-when-ready.js.ejs'),
|
||||
load_manager: require('./code_templates/load-manager.js.ejs'),
|
||||
define_when_env_loaded: require('./code_templates/define-when-env-loaded.js.ejs'),
|
||||
main_context: require('./code_templates/main-context.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')
|
||||
};
|
||||
|
||||
class CodeGenerator {
|
||||
constructor(embark, options) {
|
||||
this.ready = false;
|
||||
this.blockchainConfig = embark.config.blockchainConfig || {};
|
||||
this.embarkConfig = embark.config.embarkConfig;
|
||||
this.dappConfigs = {};
|
||||
this.logger = embark.logger;
|
||||
this.rpcHost = this.blockchainConfig.rpcHost || '';
|
||||
this.rpcPort = this.blockchainConfig.rpcPort || '';
|
||||
this.contractsConfig = embark.config.contractsConfig || {};
|
||||
this.config = embark.config;
|
||||
this.env = options.env || 'development';
|
||||
this.plugins = options.plugins;
|
||||
this.events = embark.events;
|
||||
|
||||
this.listenToCommands();
|
||||
this.ready = true;
|
||||
this.events.emit('code-generator:ready');
|
||||
}
|
||||
|
||||
listenToCommands() {
|
||||
this.events.on('config:load:contracts', this.generateContractConfig.bind(this));
|
||||
this.events.on('config:load:storage', this.generateStorageConfig.bind(this));
|
||||
this.events.on('config:load:communication', this.generateCommunicationConfig.bind(this));
|
||||
|
||||
this.events.setCommandHandler('code', (cb) => {
|
||||
this.events.request("contracts:list", (_err, contractsList) => {
|
||||
let embarkJSABI = this.generateABI(contractsList, {useEmbarkJS: true});
|
||||
let contractsJSON = this.generateContractsJSON(contractsList);
|
||||
cb(embarkJSABI, contractsJSON);
|
||||
});
|
||||
});
|
||||
|
||||
this.events.setCommandHandler('code-generator:contract', (contractName, cb) => {
|
||||
this.events.request('contracts:contract', contractName, (contract) => {
|
||||
this.buildContractJS(contract, cb);
|
||||
});
|
||||
});
|
||||
|
||||
this.events.setCommandHandler('code-generator:contract:vanilla', (contract, gasLimit, cb) => {
|
||||
cb(this.generateContractCode(contract, gasLimit));
|
||||
});
|
||||
|
||||
this.events.setCommandHandler('code-generator:contract:custom', (contract, cb) => {
|
||||
const customCode = this.generateCustomContractCode(contract);
|
||||
if (!customCode) {
|
||||
// Fallback to generate code from vanilla contract generator.
|
||||
//
|
||||
// TODO: can be moved into a afterDeploy event
|
||||
// just need to figure out the gasLimit coupling issue
|
||||
return cb(this.generateContractCode(contract, contract._gasLimit || false));
|
||||
}
|
||||
cb(customCode);
|
||||
});
|
||||
|
||||
this.events.setCommandHandler('code-generator:embarkjs:provider-code', (cb) => {
|
||||
cb(this.getEmbarkJsProviderCode());
|
||||
});
|
||||
|
||||
this.events.setCommandHandler('code-generator:embarkjs:set-provider-code', (cb) => {
|
||||
cb(this.getSetProviderCode());
|
||||
});
|
||||
|
||||
this.events.setCommandHandler('code-generator:embarkjs:init-provider-code', (cb) => {
|
||||
cb(this.getInitProviderCode());
|
||||
});
|
||||
|
||||
this.events.setCommandHandler('code-generator:symlink:generate', (...args) => {
|
||||
this.generateSymlink(...args);
|
||||
});
|
||||
|
||||
this.events.setCommandHandler("code-generator:embarkjs:build", (cb) => {
|
||||
this.buildEmbarkJS(cb);
|
||||
});
|
||||
|
||||
this.events.setCommandHandler('code-generator:ready', (cb) => {
|
||||
if (this.ready) {
|
||||
return cb();
|
||||
}
|
||||
this.events.once('code-generator:ready', cb);
|
||||
});
|
||||
}
|
||||
|
||||
getSetProviderCode() {
|
||||
let code = "\n";
|
||||
code += this.generateCommunicationInitialization(true);
|
||||
code += this.generateStorageInitialization(true);
|
||||
code += this.generateNamesInitialization(true);
|
||||
return code;
|
||||
}
|
||||
|
||||
generateContracts(contractsList, useEmbarkJS, isDeployment, useLoader) {
|
||||
let self = this;
|
||||
let result = "\n";
|
||||
let contractsPlugins;
|
||||
|
||||
if (useLoader === false) {
|
||||
for (let contract of contractsList) {
|
||||
let abi = JSON.stringify(contract.abiDefinition);
|
||||
result += Templates.vanilla_contract({className: contract.className, abi: abi, contract: contract, gasLimit: constants.codeGenerator.gasLimit});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
if (self.blockchainConfig === {} || self.blockchainConfig.enabled === false) {
|
||||
return "";
|
||||
}
|
||||
|
||||
if (this.plugins) {
|
||||
contractsPlugins = this.plugins.getPluginsFor('contractGeneration');
|
||||
}
|
||||
|
||||
if (this.plugins && contractsPlugins.length > 0) {
|
||||
contractsPlugins.forEach(function (plugin) {
|
||||
result += plugin.generateContracts({contracts: contractsList});
|
||||
});
|
||||
} else {
|
||||
for (let contract of contractsList) {
|
||||
let abi = JSON.stringify(contract.abiDefinition);
|
||||
let gasEstimates = JSON.stringify(contract.gasEstimates);
|
||||
|
||||
let block = "";
|
||||
|
||||
if (useEmbarkJS) {
|
||||
let contractAddress = contract.deployedAddress ? ("'" + contract.deployedAddress + "'") : "undefined";
|
||||
block += Templates.embarkjs_contract({className: contract.className, abi: abi, contract: contract, contractAddress: contractAddress, gasEstimates: gasEstimates});
|
||||
} else {
|
||||
block += Templates.vanilla_contract({className: contract.className, abi: abi, contract: contract, gasLimit: (isDeployment ? constants.codeGenerator.gasLimit : false)});
|
||||
}
|
||||
result += Templates.exec_when_ready({block: block});
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
checkIfNeedsUpdate(file, newOutput, callback) {
|
||||
fs.readFile(file, (err, content) => {
|
||||
if (err) {
|
||||
return callback(null, true);
|
||||
}
|
||||
callback(null, content.toString() !== newOutput);
|
||||
});
|
||||
}
|
||||
|
||||
generateContractConfig(contractConfig, callback = () => {}) {
|
||||
this.dappConfigs.blockchain = {
|
||||
dappConnection: contractConfig.dappConnection,
|
||||
dappAutoEnable: contractConfig.dappAutoEnable,
|
||||
warnIfMetamask: this.blockchainConfig.isDev,
|
||||
blockchainClient: this.blockchainConfig.client
|
||||
};
|
||||
this.generateArtifact(this.dappConfigs.blockchain, constants.dappArtifacts.blockchain, constants.dappArtifacts.dir, (err, path, _updated) => {
|
||||
callback(err, path);
|
||||
});
|
||||
}
|
||||
|
||||
generateStorageConfig(storageConfig) {
|
||||
this.dappConfigs.storage = {
|
||||
dappConnection: storageConfig.dappConnection
|
||||
};
|
||||
this.generateArtifact(this.dappConfigs.storage, constants.dappArtifacts.storage, constants.dappArtifacts.dir);
|
||||
}
|
||||
|
||||
generateCommunicationConfig(communicationConfig) {
|
||||
this.dappConfigs.communication = {
|
||||
connection: communicationConfig.connection
|
||||
};
|
||||
this.generateArtifact(this.dappConfigs.communication, constants.dappArtifacts.communication, constants.dappArtifacts.dir);
|
||||
}
|
||||
|
||||
generateArtifact(artifactInput, fileName, dirName, cb = () => {}) {
|
||||
const dir = joinPath(this.embarkConfig.generationDir, dirName);
|
||||
const filePath = joinPath(dir, fileName);
|
||||
if (typeof artifactInput !== 'string') {
|
||||
artifactInput = JSON.stringify(artifactInput, null, 2);
|
||||
}
|
||||
async.waterfall([
|
||||
(next) => {
|
||||
fs.mkdirp(dir, next);
|
||||
},
|
||||
(_dir, next) => {
|
||||
this.checkIfNeedsUpdate(filePath, artifactInput, next);
|
||||
},
|
||||
(needsUpdate, next) => {
|
||||
if (!needsUpdate) {
|
||||
return next(null, false);
|
||||
}
|
||||
fs.writeFile(filePath, artifactInput, (err) => {
|
||||
next(err, true);
|
||||
});
|
||||
}
|
||||
], (err, updated) => {
|
||||
if (err) {
|
||||
this.logger.error(err.message || err);
|
||||
}
|
||||
cb(err, filePath, updated);
|
||||
});
|
||||
}
|
||||
|
||||
generateContractCode(contract, gasLimit) {
|
||||
let abi = JSON.stringify(contract.abiDefinition);
|
||||
|
||||
let block = "";
|
||||
block += Templates.vanilla_contract({className: contract.className, abi: abi, contract: contract, gasLimit: gasLimit});
|
||||
return block;
|
||||
}
|
||||
|
||||
|
||||
generateCustomContractCode(contract) {
|
||||
const customContractGeneratorPlugin = this.plugins.getPluginsFor('customContractGeneration').splice(-1)[0];
|
||||
if (!customContractGeneratorPlugin) {
|
||||
return null;
|
||||
}
|
||||
return customContractGeneratorPlugin.generateCustomContractCode(contract);
|
||||
}
|
||||
|
||||
generateNamesInitialization(useEmbarkJS) {
|
||||
if (!useEmbarkJS || this.config.namesystemConfig === {}) return "";
|
||||
|
||||
let result = "\n";
|
||||
result += Templates.define_when_env_loaded();
|
||||
result += this._getInitCode('names', this.config.namesystemConfig);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
generateStorageInitialization(useEmbarkJS) {
|
||||
if (!useEmbarkJS || this.config.storageConfig === {}) return "";
|
||||
|
||||
let result = "\n";
|
||||
result += Templates.define_when_env_loaded();
|
||||
result += this._getInitCode('storage', this.config.storageConfig);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
generateCommunicationInitialization(useEmbarkJS) {
|
||||
if (!useEmbarkJS || this.config.communicationConfig === {}) return "";
|
||||
|
||||
let result = "\n";
|
||||
result += Templates.define_when_env_loaded();
|
||||
result += this._getInitCode('communication', this.config.communicationConfig);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
_getInitCode(codeType, config) {
|
||||
let result = "";
|
||||
let pluginsWithCode = this.plugins.getPluginsFor('initCode');
|
||||
for (let plugin of pluginsWithCode) {
|
||||
let initCodes = plugin.embarkjs_init_code[codeType] || [];
|
||||
for (let initCode of initCodes) {
|
||||
let [block, shouldInit] = initCode;
|
||||
if (shouldInit.call(plugin, config)) {
|
||||
result += Templates.exec_when_env_loaded({block: block});
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
generateABI(contractsList, options) {
|
||||
let result = "";
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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 contract of contractsList) {
|
||||
contracts[contract.className] = this.generateContractJSON(contract.className, contract);
|
||||
}
|
||||
|
||||
return contracts;
|
||||
}
|
||||
|
||||
buildEmbarkJS(cb) {
|
||||
const self = this;
|
||||
let embarkjsCode = '';
|
||||
let code = "/* eslint-disable */";
|
||||
|
||||
async.waterfall([
|
||||
// TODO: here due to a race condition when running embark build
|
||||
function generateConfig(next) {
|
||||
self.events.request("config:contractsConfig", (contractsConfig) => {
|
||||
self.generateContractConfig(contractsConfig, () => {
|
||||
next();
|
||||
});
|
||||
});
|
||||
},
|
||||
function getEmbarkJsLocation(next) {
|
||||
self.events.request('version:downloadIfNeeded', 'embarkjs', (err, location) => {
|
||||
if (err) {
|
||||
self.logger.error(__('Error downloading EmbarkJS'));
|
||||
return next(err);
|
||||
}
|
||||
next(null, location);
|
||||
});
|
||||
},
|
||||
function generateSymlink(location, next) {
|
||||
self.generateSymlink(location, 'embarkjs', (err, symlinkDest) => {
|
||||
if (err) {
|
||||
self.logger.error(__('Error creating a symlink to EmbarkJS'));
|
||||
return next(err);
|
||||
}
|
||||
embarkjsCode += `\nconst EmbarkJS = require("${symlinkDest}").default || require("${symlinkDest}");`;
|
||||
embarkjsCode += `\nEmbarkJS.environment = '${self.env}';`;
|
||||
embarkjsCode += "\nglobal.EmbarkJS = EmbarkJS;";
|
||||
code += "\n" + embarkjsCode + "\n";
|
||||
next();
|
||||
});
|
||||
},
|
||||
function addCodeFromDependencies(next) {
|
||||
async.eachSeries(
|
||||
self.plugins.getPluginsFor('generatedCode'),
|
||||
(plugin, callback) => {
|
||||
async.eachSeries(
|
||||
plugin.generated_code,
|
||||
(codeCall, callback) => {
|
||||
codeCall((err, generatedCode, packageName, location) => {
|
||||
if (err) return callback(err);
|
||||
self.generateSymlink(location, packageName, (err, _symlinkDest) => {
|
||||
if (err) {
|
||||
self.logger.error(__(`Error creating a symlink to ${packageName}`));
|
||||
return callback(err);
|
||||
}
|
||||
code += generatedCode;
|
||||
callback();
|
||||
});
|
||||
});
|
||||
},
|
||||
(err) => { callback(err); }
|
||||
);
|
||||
},
|
||||
(err) => { next(err); }
|
||||
);
|
||||
},
|
||||
function getJSCode(next) {
|
||||
code += self.getEmbarkJsProviderCode();
|
||||
code += self.generateCommunicationInitialization(true);
|
||||
code += self.generateStorageInitialization(true);
|
||||
code += self.generateNamesInitialization(true);
|
||||
code += self.getReloadPageCode();
|
||||
code += "\nexport default EmbarkJS;";
|
||||
code += "\nif (typeof module !== 'undefined' && module.exports) {" +
|
||||
"\n\tmodule.exports = EmbarkJS;" +
|
||||
"\n}";
|
||||
code += '\n/* eslint-enable */\n';
|
||||
|
||||
next();
|
||||
},
|
||||
function writeFile(next) {
|
||||
self.generateArtifact(code, constants.dappArtifacts.embarkjs, '', next);
|
||||
},
|
||||
function transformCode(artifactPath, updated, next) {
|
||||
if (!updated) {
|
||||
return next();
|
||||
}
|
||||
transform(code, {
|
||||
cwd: embarkPath(),
|
||||
"presets": [
|
||||
[
|
||||
"@babel/preset-env", {
|
||||
"targets": {
|
||||
"node": "8.11.3"
|
||||
}
|
||||
}
|
||||
]
|
||||
]
|
||||
}, (err, result) => {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
self.generateArtifact(result.code, constants.dappArtifacts.embarkjsnode, '', next);
|
||||
});
|
||||
}
|
||||
], function(_err, _result) {
|
||||
cb();
|
||||
});
|
||||
}
|
||||
|
||||
getReloadPageCode() {
|
||||
return this.env === 'development' ? fs.readFileSync(path.join(__dirname, '/code/reload-on-change.js'), 'utf8') : '';
|
||||
}
|
||||
|
||||
getEmbarkJsProviderCode() {
|
||||
return this.plugins.getPluginsFor('embarkjsCode').reduce((code, plugin) => (
|
||||
code += plugin.embarkjs_code.join('\n')
|
||||
), '');
|
||||
}
|
||||
|
||||
getInitProviderCode() {
|
||||
const codeTypes = {
|
||||
blockchain: this.config.blockchainConfig || {},
|
||||
communication: this.config.communicationConfig || {},
|
||||
names: this.config.namesystemConfig || {},
|
||||
storage: this.config.storageConfig || {}
|
||||
};
|
||||
|
||||
return this.plugins.getPluginsFor("initConsoleCode").reduce((acc, plugin) => {
|
||||
Object.keys(codeTypes).forEach((codeTypeName) => {
|
||||
(plugin.embarkjs_init_console_code[codeTypeName] || []).forEach((initCode) => {
|
||||
const [block, shouldInit] = initCode;
|
||||
if (shouldInit.call(plugin, codeTypes[codeTypeName])) {
|
||||
acc += block;
|
||||
}
|
||||
});
|
||||
});
|
||||
return acc;
|
||||
}, "");
|
||||
}
|
||||
|
||||
buildContractJS(contract, cb) {
|
||||
const contractName = contract.className;
|
||||
|
||||
if (this.plugins) {
|
||||
const contractsPlugins = this.plugins.getPluginsFor('contractGeneration');
|
||||
if (contractsPlugins.length > 0) {
|
||||
let result = '';
|
||||
contractsPlugins.forEach(function (plugin) {
|
||||
result += plugin.generateContracts({contracts: {[contractName]: contract}});
|
||||
});
|
||||
return this.generateArtifact(result, contractName + '.js', constants.dappArtifacts.contractsJs, (err, path, _updated) => {
|
||||
cb(err, path);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const contractJSON = this.generateContractJSON(contractName, contract);
|
||||
const contractCode = `
|
||||
"use strict";
|
||||
|
||||
const isNode = (typeof process !== 'undefined' && process.versions && process.versions.node);
|
||||
const lib = isNode ? '../embarkjs.node' : '../embarkjs';
|
||||
|
||||
const EmbarkJSNode = isNode && require('../embarkjs.node');
|
||||
let EmbarkJSBrowser;
|
||||
try {
|
||||
EmbarkJSBrowser = require('../embarkjs').default;
|
||||
} catch(e) {};
|
||||
|
||||
const EmbarkJS = isNode ? EmbarkJSNode : EmbarkJSBrowser;
|
||||
|
||||
let ${contractName}JSONConfig = ${JSON.stringify(contractJSON)};
|
||||
let ${contractName} = new EmbarkJS.Blockchain.Contract(${contractName}JSONConfig);
|
||||
module.exports = ${contractName};
|
||||
`.trim().replace(/^[\t\s]+/gm, '');
|
||||
|
||||
this.generateArtifact(contractCode, contractName + '.js', constants.dappArtifacts.contractsJs, (err, path, _updated) => {
|
||||
cb(err, path);
|
||||
});
|
||||
}
|
||||
|
||||
generateSymlink(target, name, callback) {
|
||||
async.waterfall([
|
||||
// Make directory
|
||||
next => {
|
||||
const symlinkDir = dappPath(this.embarkConfig.generationDir, constants.dappArtifacts.symlinkDir);
|
||||
fs.mkdirp(symlinkDir, (err) => {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
next(null, toForwardSlashes(joinPath(symlinkDir, name)));
|
||||
});
|
||||
},
|
||||
// Remove old symlink because they are not overwritable
|
||||
(symlinkDest, next) => {
|
||||
fs.remove(symlinkDest, (err) => {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
next(null, symlinkDest);
|
||||
});
|
||||
},
|
||||
// Make target a directory as files don't work on Windows
|
||||
(symlinkDest, next) => {
|
||||
fs.stat(target, (err, stats) => {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
let finalTarget = target;
|
||||
if (stats.isFile()) {
|
||||
finalTarget = path.dirname(target);
|
||||
}
|
||||
next(null, symlinkDest, finalTarget);
|
||||
});
|
||||
},
|
||||
(symlinkDest, finalTarget, next) => {
|
||||
fs.symlink(finalTarget, symlinkDest, 'junction', (err) => {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
next(null, symlinkDest);
|
||||
});
|
||||
}
|
||||
], callback);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = CodeGenerator;
|
|
@ -1,4 +0,0 @@
|
|||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"include": ["src/**/*"]
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
{
|
||||
"extends": "../../tslint.json"
|
||||
}
|
|
@ -93,10 +93,7 @@
|
|||
"embark-accounts-manager": "^4.1.1",
|
||||
"embark-api": "^4.1.1",
|
||||
"embark-authenticator": "^4.1.1",
|
||||
"embark-blockchain-connector": "^4.1.1",
|
||||
"embark-blockchain-listener": "^4.1.1",
|
||||
"embark-blockchain-process": "^4.1.1",
|
||||
"embark-code-generator": "^4.1.1",
|
||||
"embark-code-runner": "^4.1.1",
|
||||
"embark-compiler": "^4.1.1",
|
||||
"embark-console": "^4.1.1",
|
||||
|
|
|
@ -15,7 +15,6 @@ class Cmd {
|
|||
this.demo();
|
||||
this.build();
|
||||
this.run();
|
||||
this.run2();
|
||||
this.console();
|
||||
this.blockchain();
|
||||
this.simulator();
|
||||
|
@ -170,42 +169,6 @@ class Cmd {
|
|||
});
|
||||
}
|
||||
|
||||
run2() {
|
||||
program
|
||||
.command('run2 [environment]')
|
||||
.option('-p, --port [port]', __('port to run the dev webserver (default: %s)', '8000'))
|
||||
.option('-c, --client [client]', __('Use a specific ethereum client [%s] (default: %s)', 'geth, parity', 'geth'))
|
||||
.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('--nobrowser', __('prevent the development webserver from automatically opening a web browser'))
|
||||
.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)'))
|
||||
.option('--pipeline [pipeline]', __('webpack config to use (default: development)'))
|
||||
.option('--no-single-use-auth-token', __('disable the single use of token in cockpit'))
|
||||
.description(__('run dapp (default: %s)', 'development'))
|
||||
.action(function(env, options) {
|
||||
setOrDetectLocale(options.locale);
|
||||
embark.run2({
|
||||
env: env || 'development',
|
||||
serverPort: options.port,
|
||||
serverHost: options.host,
|
||||
client: options.client,
|
||||
locale: options.locale,
|
||||
runWebserver: !options.noserver ? null : false,
|
||||
useDashboard: !options.nodashboard,
|
||||
logFile: options.logfile,
|
||||
logLevel: options.loglevel,
|
||||
webpackConfigName: options.pipeline || 'development',
|
||||
openBrowser: !options.nobrowser ? null : false,
|
||||
singleUseAuthToken: options.singleUseAuthToken
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
console() {
|
||||
program
|
||||
.command('console [environment]')
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import {Simulator} from 'embark-blockchain-process';
|
||||
import {__} from 'embark-i18n';
|
||||
import {dappPath, embarkPath} from 'embark-utils';
|
||||
import findUp from 'find-up';
|
||||
|
@ -71,16 +70,15 @@ class EmbarkController {
|
|||
});
|
||||
});
|
||||
}
|
||||
|
||||
simulator(options) {
|
||||
this.context = options.context || [constants.contexts.simulator, constants.contexts.blockchain];
|
||||
let simulator = new Simulator({
|
||||
blockchainConfig: this.config.blockchainConfig,
|
||||
contractsConfig: this.config.contractsConfig,
|
||||
logger: this.logger,
|
||||
fs
|
||||
});
|
||||
simulator.run(options);
|
||||
simulator(options) {
|
||||
// this.context = options.context || [constants.contexts.simulator, constants.contexts.blockchain];
|
||||
// let simulator = new Simulator({
|
||||
// blockchainConfig: this.config.blockchainConfig,
|
||||
// contractsConfig: this.config.contractsConfig,
|
||||
// logger: this.logger,
|
||||
// fs
|
||||
// });
|
||||
// simulator.run(options);
|
||||
}
|
||||
|
||||
generateTemplate(templateName, destinationFolder, name, url) {
|
||||
|
@ -94,8 +92,8 @@ class EmbarkController {
|
|||
templateGenerator.generate(destinationFolder, name);
|
||||
}
|
||||
|
||||
run2(options) {
|
||||
const self = this;
|
||||
run(options) {
|
||||
let self = this;
|
||||
self.context = options.context || [constants.contexts.run, constants.contexts.build];
|
||||
let Dashboard = require('./dashboard/dashboard.js');
|
||||
|
||||
|
@ -230,137 +228,6 @@ class EmbarkController {
|
|||
});
|
||||
}
|
||||
|
||||
run(options) {
|
||||
let self = this;
|
||||
self.context = options.context || [constants.contexts.run, constants.contexts.build];
|
||||
let Dashboard = require('./dashboard/dashboard.js');
|
||||
|
||||
const webServerConfig = {};
|
||||
|
||||
if (options.runWebserver !== null && options.runWebserver !== undefined) {
|
||||
webServerConfig.enabled = options.runWebserver;
|
||||
}
|
||||
|
||||
if (options.serverHost !== null && options.serverHost !== undefined) {
|
||||
webServerConfig.host = options.serverHost;
|
||||
}
|
||||
|
||||
if (options.serverPort !== null && options.serverPort !== undefined) {
|
||||
webServerConfig.port = options.serverPort;
|
||||
}
|
||||
|
||||
if (options.openBrowser !== null && options.openBrowser !== undefined) {
|
||||
webServerConfig.openBrowser = options.openBrowser;
|
||||
}
|
||||
|
||||
const Engine = require('../lib/core/engine.js');
|
||||
const engine = new Engine({
|
||||
env: options.env,
|
||||
client: options.client,
|
||||
locale: options.locale,
|
||||
version: this.version,
|
||||
embarkConfig: options.embarkConfig || 'embark.json',
|
||||
logFile: options.logFile,
|
||||
logLevel: options.logLevel,
|
||||
context: self.context,
|
||||
useDashboard: options.useDashboard,
|
||||
webServerConfig: webServerConfig,
|
||||
webpackConfigName: options.webpackConfigName,
|
||||
singleUseAuthToken: options.singleUseAuthToken,
|
||||
ipcRole: 'server'
|
||||
});
|
||||
|
||||
async.waterfall([
|
||||
function initEngine(callback) {
|
||||
engine.init({}, () => {
|
||||
engine.startService("embarkListener");
|
||||
if (!options.useDashboard) {
|
||||
engine.logger.info('========================'.bold.green);
|
||||
engine.logger.info((__('Welcome to Embark') + ' ' + engine.version).yellow.bold);
|
||||
engine.logger.info('========================'.bold.green);
|
||||
}
|
||||
callback();
|
||||
});
|
||||
},
|
||||
function (callback) {
|
||||
engine.startService("libraryManager").installAll((err) => callback(err ? err : null));
|
||||
},
|
||||
function (callback) {
|
||||
let pluginList = engine.plugins.listPlugins();
|
||||
if (pluginList.length > 0) {
|
||||
engine.logger.info(__("loaded plugins") + ": " + pluginList.join(", "));
|
||||
}
|
||||
|
||||
engine.startService("web3");
|
||||
engine.startService("processManager");
|
||||
engine.startService("coreProcess");
|
||||
engine.startService("blockchainListener");
|
||||
engine.startService("serviceMonitor");
|
||||
engine.startService("codeRunner");
|
||||
engine.startService("pipeline");
|
||||
engine.startService("deployment");
|
||||
engine.startService("storage");
|
||||
engine.startService("codeGenerator");
|
||||
engine.startService("console");
|
||||
engine.startService("cockpit");
|
||||
engine.startService("pluginCommand");
|
||||
engine.startService("blockchain");
|
||||
|
||||
engine.events.on('check:backOnline:Ethereum', function () {
|
||||
engine.logger.info(__('Ethereum node detected') + '..');
|
||||
engine.config.reloadConfig();
|
||||
engine.events.request('deploy:contracts', function (err) {
|
||||
if (err) {
|
||||
return engine.logger.error(err.message || err);
|
||||
}
|
||||
engine.logger.info(__('Deployment Done'));
|
||||
});
|
||||
});
|
||||
|
||||
engine.events.on('outputDone', function () {
|
||||
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);
|
||||
});
|
||||
|
||||
if (webServerConfig.enabled !== false) {
|
||||
engine.startService("webServer");
|
||||
}
|
||||
engine.startService("fileWatcher");
|
||||
|
||||
engine.startEngine(() => {
|
||||
callback();
|
||||
});
|
||||
},
|
||||
function startDashboard(callback) {
|
||||
if (!options.useDashboard) {
|
||||
return callback();
|
||||
}
|
||||
|
||||
let dashboard = new Dashboard({
|
||||
events: engine.events,
|
||||
logger: engine.logger,
|
||||
plugins: engine.plugins,
|
||||
version: self.version,
|
||||
env: engine.env,
|
||||
ipc: engine.ipc
|
||||
});
|
||||
dashboard.start(function () {
|
||||
engine.logger.info(__('dashboard start'));
|
||||
callback();
|
||||
});
|
||||
}
|
||||
], function (err, _result) {
|
||||
if (err) {
|
||||
engine.logger.error(err.message);
|
||||
engine.logger.info(err.stack);
|
||||
process.exit(1);
|
||||
} else {
|
||||
engine.events.emit('firstDeploymentDone');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
build(options) {
|
||||
this.context = options.context || [constants.contexts.build];
|
||||
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import {ProcessManager, IPC} from 'embark-core';
|
||||
|
||||
const EMBARK_PROCESS_NAME = 'embark';
|
||||
|
||||
const utils = require('../utils/utils');
|
||||
const Logger = require('embark-logger');
|
||||
|
||||
const EMBARK_PROCESS_NAME = 'embark';
|
||||
|
||||
class Engine {
|
||||
constructor(options) {
|
||||
this.env = options.env;
|
||||
|
@ -210,16 +210,7 @@ class Engine {
|
|||
}
|
||||
|
||||
contractsComponents(_options) {
|
||||
// this.registerModulePackage('embark-blockchain-connector', {
|
||||
// isDev: this.isDev,
|
||||
// locale: this.locale,
|
||||
// plugins: this.plugins,
|
||||
// web3: options.web3,
|
||||
// wait: options.wait
|
||||
// });
|
||||
|
||||
this.registerModule('ethereum-blockchain-client');
|
||||
// this.registerModule('web3', { plugins: this.plugins });
|
||||
this.registerModulePackage('embark-web3');
|
||||
this.registerModulePackage('embark-accounts-manager');
|
||||
this.registerModulePackage('embark-specialconfigs', {plugins: this.plugins});
|
||||
|
@ -240,161 +231,6 @@ class Engine {
|
|||
this.registerModulePackage('embark-ens');
|
||||
}
|
||||
|
||||
// ================
|
||||
// ================
|
||||
// ================
|
||||
// To be removed
|
||||
// ================
|
||||
|
||||
startService(serviceName, _options) {
|
||||
let options = _options || {};
|
||||
|
||||
let services = {
|
||||
"serviceMonitor": this.serviceMonitor,
|
||||
"pipeline": this.pipelineService,
|
||||
"cockpit": this.cockpitService,
|
||||
"codeRunner": this.codeRunnerService,
|
||||
"codeGenerator": this.codeGeneratorService,
|
||||
"compiler": this.setupCompilerAndContractsManagerService,
|
||||
"deployment": this.deploymentService,
|
||||
"fileWatcher": this.fileWatchService,
|
||||
"webServer": this.webServerService,
|
||||
"console": this.console,
|
||||
"web3": this.web3Service,
|
||||
"libraryManager": this.libraryManagerService,
|
||||
"processManager": this.processManagerService,
|
||||
"storage": this.storageService,
|
||||
"pluginCommand": this.pluginCommandService,
|
||||
"graph": this.graphService,
|
||||
"testRunner": this.testRunnerService,
|
||||
"codeCoverage": this.codeCoverageService,
|
||||
"scaffolding": this.scaffoldingService,
|
||||
"coreProcess": this.coreProcessService,
|
||||
"processApi": this.processApiService,
|
||||
"blockchainListener": this.blockchainListenerService,
|
||||
"blockchain": this.blockchainComponents
|
||||
};
|
||||
|
||||
let service = services[serviceName];
|
||||
|
||||
if (!service) {
|
||||
throw new Error("unknown service: " + serviceName);
|
||||
}
|
||||
|
||||
// need to be careful with circular references due to passing the web3 object
|
||||
//this.logger.trace("calling: " + serviceName + "(" + JSON.stringify(options) + ")");
|
||||
return service.apply(this, [options]);
|
||||
}
|
||||
|
||||
embarkListenerService(_options) {
|
||||
this.registerModulePackage('embark-listener');
|
||||
}
|
||||
|
||||
blockchainListenerService(_options) {
|
||||
this.registerModulePackage('embark-blockchain-listener', {
|
||||
ipc: this.ipc
|
||||
});
|
||||
}
|
||||
|
||||
coreProcessService(_options) {
|
||||
this.registerModulePackage('embark-core/process', {
|
||||
events: this.events
|
||||
});
|
||||
}
|
||||
|
||||
processManagerService(_options) {
|
||||
this.processManager = new ProcessManager({
|
||||
events: this.events,
|
||||
logger: this.logger,
|
||||
plugins: this.plugins
|
||||
});
|
||||
}
|
||||
|
||||
graphService(_options) {
|
||||
this.registerModulePackage('embark-graph');
|
||||
}
|
||||
|
||||
scaffoldingService(_options) {
|
||||
this.registerModulePackage('embark-scaffolding', {plugins: this.plugins});
|
||||
}
|
||||
|
||||
serviceMonitor() {
|
||||
const ServicesMonitor = require('./services_monitor.js');
|
||||
this.servicesMonitor = new ServicesMonitor({events: this.events, logger: this.logger, plugins: this.plugins});
|
||||
this.servicesMonitor.addCheck('Embark', function (cb) {
|
||||
return cb({name: 'Embark ' + self.version, status: 'on'});
|
||||
}, 0);
|
||||
this.servicesMonitor.startMonitor();
|
||||
}
|
||||
|
||||
pluginCommandService() {
|
||||
this.registerModulePackage('embark-plugin-cmd', {embarkConfigFile: this.embarkConfig, embarkConfig: this.config.embarkConfig, packageFile: 'package.json'});
|
||||
}
|
||||
|
||||
codeRunnerService(_options) {
|
||||
this.registerModulePackage('embark-code-runner', {
|
||||
ipc: this.ipc
|
||||
});
|
||||
}
|
||||
|
||||
codeGeneratorService(_options) {
|
||||
return;
|
||||
// let self = this;
|
||||
//
|
||||
// this.registerModulePackage('embark-code-generator', {plugins: self.plugins, env: self.env});
|
||||
//
|
||||
// const generateCode = function (modifiedAssets) {
|
||||
// // self.events.request("module:storage:onReady", () => {
|
||||
// self.events.request("code-generator:embarkjs:build", () => {
|
||||
// self.events.emit('code-generator-ready', modifiedAssets);
|
||||
// });
|
||||
// // });
|
||||
// };
|
||||
// const cargo = async.cargo((tasks, callback) => {
|
||||
// const modifiedAssets = tasks.map(task => task.modifiedAsset).filter(asset => asset); // filter null elements
|
||||
// generateCode(modifiedAssets);
|
||||
// self.events.once('outputDone', callback);
|
||||
// });
|
||||
// const addToCargo = function (modifiedAsset) {
|
||||
// cargo.push({modifiedAsset});
|
||||
// };
|
||||
//
|
||||
// this.events.on('contractsDeployed', addToCargo);
|
||||
// this.events.on('blockchainDisabled', addToCargo);
|
||||
// this.events.on('asset-changed', addToCargo);
|
||||
}
|
||||
|
||||
setupCompilerAndContractsManagerService(options) {
|
||||
this.registerModulePackage('embark-compiler', {plugins: this.plugins, isCoverage: options.isCoverage});
|
||||
this.registerModulePackage('embark-solidity', {ipc: this.ipc, useDashboard: this.useDashboard});
|
||||
this.registerModulePackage('embark-vyper');
|
||||
this.registerModulePackage('embark-contracts-manager', {plugins: this.plugins, compileOnceOnly: options.compileOnceOnly});
|
||||
}
|
||||
|
||||
deploymentService(options) {
|
||||
let self = this;
|
||||
|
||||
this.setupCompilerAndContractsManagerService(options);
|
||||
this.registerModulePackage('embark-solidity', {ipc: self.ipc, useDashboard: this.useDashboard});
|
||||
this.registerModulePackage('embark-vyper');
|
||||
this.registerModulePackage('embark-profiler');
|
||||
this.registerModulePackage('embark-deploy-tracker', {trackContracts: options.trackContracts});
|
||||
this.registerModulePackage('embark-specialconfigs');
|
||||
this.registerModulePackage('embark-console-listener', {ipc: self.ipc});
|
||||
this.registerModulePackage('embark-deployment', {plugins: this.plugins, onlyCompile: options.onlyCompile});
|
||||
this.registerModulePackage('embark-transaction-tracker');
|
||||
this.registerModulePackage('embark-debugger');
|
||||
}
|
||||
|
||||
fileWatchService() {
|
||||
this.registerModulePackage('embark-watcher');
|
||||
this.events.request('watcher:start');
|
||||
}
|
||||
|
||||
cockpitService() {
|
||||
this.registerModulePackage('embark-authenticator', {singleUseAuthToken: this.singleUseAuthToken});
|
||||
this.registerModulePackage('embark-api', {plugins: this.plugins});
|
||||
}
|
||||
|
||||
cockpitModules() {
|
||||
this.registerModulePackage('embark-authenticator', {singleUseAuthToken: this.singleUseAuthToken});
|
||||
|
@ -403,65 +239,6 @@ class Engine {
|
|||
this.events.request('process:logs:register', {processName: EMBARK_PROCESS_NAME, eventName: "log", silent: false, alwaysAlreadyLogged: true});
|
||||
}
|
||||
|
||||
webServerService() {
|
||||
this.registerModulePackage('embark-webserver');
|
||||
}
|
||||
|
||||
storageService(_options) {
|
||||
this.registerModulePackage('embark-storage', {plugins: this.plugins});
|
||||
this.registerModulePackage('embark-ipfs');
|
||||
this.registerModulePackage('embark-swarm');
|
||||
// this.registerModulePackage('embark-swarm');
|
||||
|
||||
// this.events.setCommandHandler("module:storage:reset", (cb) => {
|
||||
// async.parallel([
|
||||
// (paraCb) => {
|
||||
// this.events.request("module:ipfs:reset", paraCb);
|
||||
// },
|
||||
// (paraCb) => {
|
||||
// this.events.request("module:swarm:reset", paraCb);
|
||||
// },
|
||||
// (paraCb) => {
|
||||
// this.events.request("module:storageJS:reset", paraCb);
|
||||
// }
|
||||
// ], cb);
|
||||
// });
|
||||
}
|
||||
|
||||
web3Service(options) {
|
||||
this.registerModulePackage('embark-web3', {plugins: this.plugins});
|
||||
|
||||
this.registerModulePackage('embark-blockchain-process', {
|
||||
client: this.client,
|
||||
locale: this.locale,
|
||||
isDev: this.isDev,
|
||||
ipc: this.ipc
|
||||
});
|
||||
|
||||
this.registerModulePackage('embark-blockchain-connector', {
|
||||
isDev: this.isDev,
|
||||
locale: this.locale,
|
||||
plugins: this.plugins,
|
||||
web3: options.web3,
|
||||
wait: options.wait
|
||||
});
|
||||
|
||||
this.registerModulePackage('embark-whisper', {plugins: this.plugins});
|
||||
this.registerModule('web3', {plugins: this.plugins});
|
||||
}
|
||||
|
||||
libraryManagerService(_options) {
|
||||
return this.registerModulePackage('embark-library-manager', {useDashboard: this.useDashboard});
|
||||
}
|
||||
|
||||
codeCoverageService(_options) {
|
||||
this.registerModulePackage('embark-coverage');
|
||||
}
|
||||
|
||||
testRunnerService(options) {
|
||||
this.registerModulePackage('embark-test-runner', Object.assign(options, {ipc: this.ipc}));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = Engine;
|
||||
|
|
|
@ -1,144 +0,0 @@
|
|||
/*globals describe, it*/
|
||||
import { BlockchainClient } from 'embark-blockchain-process';
|
||||
const constants = require('embark-core/constants');
|
||||
import { dappPath, defaultHost} from 'embark-utils';
|
||||
const path = require('path');
|
||||
const fs = require('../lib/core/fs.js');
|
||||
|
||||
const assert = require('assert');
|
||||
|
||||
describe('embark.Blockchain', function() {
|
||||
|
||||
describe('#initializer', function() {
|
||||
|
||||
describe('with empty config', function() {
|
||||
it('should have a default config', function(done) {
|
||||
const blockchain = new BlockchainClient({}, { fs });
|
||||
const expectedConfig = {
|
||||
networkType: 'custom',
|
||||
genesisBlock: false,
|
||||
client: 'geth',
|
||||
ethereumClientBin: 'geth',
|
||||
datadir: dappPath(".embark/development/datadir"),
|
||||
mineWhenNeeded: false,
|
||||
rpcHost: defaultHost,
|
||||
rpcPort: 8545,
|
||||
rpcApi: ['eth', 'web3', 'net', 'debug', "personal"],
|
||||
rpcCorsDomain: "http://localhost:8000",
|
||||
networkId: 1337,
|
||||
port: 30303,
|
||||
nodiscover: false,
|
||||
maxpeers: 25,
|
||||
mine: false,
|
||||
vmdebug: false,
|
||||
whisper: true,
|
||||
bootnodes: "",
|
||||
wsApi: ["eth", "web3", "net", "shh", "debug", "pubsub", "personal"],
|
||||
wsHost: defaultHost,
|
||||
wsOrigins: "http://localhost:8000",
|
||||
wsPort: 8546,
|
||||
wsRPC: true,
|
||||
targetGasLimit: 8000000,
|
||||
syncMode: undefined,
|
||||
verbosity: undefined,
|
||||
proxy: undefined,
|
||||
silent: undefined
|
||||
};
|
||||
expectedConfig.account = {devPassword: path.join(expectedConfig.datadir, "devPassword")};
|
||||
|
||||
assert.deepEqual(blockchain.config, expectedConfig);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
describe('with config', function() {
|
||||
it('should take config params', function(done) {
|
||||
let config = {
|
||||
networkType: 'livenet',
|
||||
genesisBlock: 'foo/bar/genesis.json',
|
||||
client: 'parity',
|
||||
ethereumClientBin: 'parity',
|
||||
datadir: '/foo/datadir/',
|
||||
mineWhenNeeded: true,
|
||||
rpcHost: defaultHost,
|
||||
rpcPort: 12345,
|
||||
rpcApi: ['eth', 'web3', 'net', 'debug', "personal"],
|
||||
rpcCorsDomain: true,
|
||||
networkId: 1,
|
||||
port: 123456,
|
||||
nodiscover: true,
|
||||
maxpeers: 25,
|
||||
mine: true,
|
||||
vmdebug: false,
|
||||
whisper: false,
|
||||
bootnodes: "",
|
||||
wsApi: ["eth", "web3", "net", "shh", "debug", "personal"],
|
||||
wsHost: defaultHost,
|
||||
wsOrigins: false,
|
||||
wsPort: 12346,
|
||||
wsRPC: true,
|
||||
targetGasLimit: false,
|
||||
syncMode: undefined,
|
||||
verbosity: undefined,
|
||||
proxy: true,
|
||||
accounts: [
|
||||
{
|
||||
nodeAccounts: true,
|
||||
numAddresses: "2",
|
||||
password: "config/development/devpassword"
|
||||
},
|
||||
{
|
||||
mnemonic: "example exile argue silk regular smile grass bomb merge arm assist farm",
|
||||
numAddresses: "3"
|
||||
}
|
||||
]
|
||||
};
|
||||
let blockchain = new BlockchainClient(config, { fs });
|
||||
|
||||
let expectedConfig = {
|
||||
networkType: 'livenet',
|
||||
genesisBlock: 'foo/bar/genesis.json',
|
||||
client: 'parity',
|
||||
ethereumClientBin: 'parity',
|
||||
datadir: '/foo/datadir/',
|
||||
mineWhenNeeded: true,
|
||||
rpcHost: defaultHost,
|
||||
rpcPort: 12345,
|
||||
rpcApi: ['eth', 'web3', 'net', 'debug', 'personal'],
|
||||
rpcCorsDomain: true,
|
||||
networkId: 1,
|
||||
port: 123456,
|
||||
nodiscover: true,
|
||||
maxpeers: 25,
|
||||
mine: true,
|
||||
vmdebug: false,
|
||||
whisper: false,
|
||||
bootnodes: "",
|
||||
wsApi: ["eth", "web3", "net", "shh", "debug", "personal"],
|
||||
wsHost: defaultHost,
|
||||
wsOrigins: false,
|
||||
wsPort: 12346,
|
||||
wsRPC: true,
|
||||
targetGasLimit: false,
|
||||
syncMode: undefined,
|
||||
verbosity: undefined,
|
||||
proxy: true,
|
||||
silent: undefined,
|
||||
account: {
|
||||
numAccounts: "2",
|
||||
devPassword: path.normalize("/foo/datadir/devPassword"),
|
||||
password: "config/development/devpassword",
|
||||
balance: undefined
|
||||
}
|
||||
};
|
||||
// We check also proxy's ports because proxy is set to true
|
||||
expectedConfig.wsPort += constants.blockchain.servicePortOnProxy;
|
||||
expectedConfig.rpcPort += constants.blockchain.servicePortOnProxy;
|
||||
|
||||
assert.deepEqual(blockchain.config, expectedConfig);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
});
|
|
@ -1,66 +0,0 @@
|
|||
/*globals describe, it*/
|
||||
let CodeGenerator = require('embark-code-generator');
|
||||
let assert = require('assert');
|
||||
|
||||
function replaceCRLF(string) {
|
||||
return string.replace(/\r\n/g, '\n');
|
||||
}
|
||||
|
||||
// TODO: instead 'eval' the code with a fake web3 object
|
||||
// and check the generate code interacts as expected
|
||||
describe('embark.CodeGenerator', function() {
|
||||
this.timeout(0);
|
||||
|
||||
describe('#generateContracts', function() {
|
||||
let contracts = [
|
||||
{
|
||||
className: 'SimpleStorage',
|
||||
abiDefinition: [{"constant":true,"inputs":[],"name":"storedData","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"x","type":"uint256"}],"name":"set","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"get","outputs":[{"name":"retVal","type":"uint256"}],"payable":false,"type":"function"},{"inputs":[{"name":"initialValue","type":"uint256"}],"type":"constructor"}],
|
||||
gasEstimates: 12000,
|
||||
deployedAddress: "0x123",
|
||||
code: '12345'
|
||||
},
|
||||
{
|
||||
className: 'Foo',
|
||||
abiDefinition: [{"constant":true,"inputs":[],"name":"storedData","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"x","type":"uint256"}],"name":"set","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"get","outputs":[{"name":"retVal","type":"uint256"}],"payable":false,"type":"function"},{"inputs":[{"name":"initialValue","type":"uint256"}],"type":"constructor"}],
|
||||
gasEstimates: 12000,
|
||||
deployedAddress: "0x124",
|
||||
code: '123456'
|
||||
}
|
||||
];
|
||||
|
||||
const currentSolcVersion = require('../../package.json').dependencies.solc;
|
||||
const TestEvents = {
|
||||
request: (cmd, cb) => {
|
||||
cb(currentSolcVersion);
|
||||
},
|
||||
setCommandHandler: () => {
|
||||
},
|
||||
on: () => {},
|
||||
emit: () => {}
|
||||
};
|
||||
let generator = new CodeGenerator({config: {blockchainConfig: {}}, events: TestEvents}, {});
|
||||
|
||||
describe('with EmbarkJS', function() {
|
||||
let withEmbarkJS = true;
|
||||
|
||||
it('should generate contract code', function() {
|
||||
var contractCode = "\n__mainContext.__loadManagerInstance.execWhenReady(function() {\n __mainContext.SimpleStorage = new EmbarkJS.Blockchain.Contract({abi: [{\"constant\":true,\"inputs\":[],\"name\":\"storedData\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"x\",\"type\":\"uint256\"}],\"name\":\"set\",\"outputs\":[],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"get\",\"outputs\":[{\"name\":\"retVal\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"inputs\":[{\"name\":\"initialValue\",\"type\":\"uint256\"}],\"type\":\"constructor\"}], address: '0x123', code: '12345', gasEstimates: 12000});\n\n});\n__mainContext.__loadManagerInstance.execWhenReady(function() {\n __mainContext.Foo = new EmbarkJS.Blockchain.Contract({abi: [{\"constant\":true,\"inputs\":[],\"name\":\"storedData\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"x\",\"type\":\"uint256\"}],\"name\":\"set\",\"outputs\":[],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"get\",\"outputs\":[{\"name\":\"retVal\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"inputs\":[{\"name\":\"initialValue\",\"type\":\"uint256\"}],\"type\":\"constructor\"}], address: '0x124', code: '123456', gasEstimates: 12000});\n\n});\n";
|
||||
assert.strictEqual(replaceCRLF(generator.generateContracts(contracts, withEmbarkJS)), contractCode);
|
||||
});
|
||||
});
|
||||
|
||||
describe('with default interface', function() {
|
||||
let withEmbarkJS = false;
|
||||
|
||||
it('should generate contract code', function() {
|
||||
var contractCode = "\n__mainContext.__loadManagerInstance.execWhenReady(function() {\n SimpleStorageAbi = [{\"constant\":true,\"inputs\":[],\"name\":\"storedData\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"x\",\"type\":\"uint256\"}],\"name\":\"set\",\"outputs\":[],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"get\",\"outputs\":[{\"name\":\"retVal\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"inputs\":[{\"name\":\"initialValue\",\"type\":\"uint256\"}],\"type\":\"constructor\"}];\nSimpleStorage = new web3.eth.Contract(SimpleStorageAbi);\nSimpleStorage.options.address = '0x123';\nSimpleStorage.address = '0x123';\nSimpleStorage.options.from = web3.eth.defaultAccount;\n\n\n});\n__mainContext.__loadManagerInstance.execWhenReady(function() {\n FooAbi = [{\"constant\":true,\"inputs\":[],\"name\":\"storedData\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"x\",\"type\":\"uint256\"}],\"name\":\"set\",\"outputs\":[],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"get\",\"outputs\":[{\"name\":\"retVal\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"inputs\":[{\"name\":\"initialValue\",\"type\":\"uint256\"}],\"type\":\"constructor\"}];\nFoo = new web3.eth.Contract(FooAbi);\nFoo.options.address = '0x124';\nFoo.address = '0x124';\nFoo.options.from = web3.eth.defaultAccount;\n\n\n});\n";
|
||||
assert.strictEqual(replaceCRLF(generator.generateContracts(contracts, withEmbarkJS)), contractCode);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
//describe('#generateABI', function() {
|
||||
//});
|
||||
});
|
|
@ -1,158 +0,0 @@
|
|||
/*globals describe, it, before*/
|
||||
const {
|
||||
expect
|
||||
} = require('chai');
|
||||
const sinon = require('sinon');
|
||||
import { IPC } from 'embark-core';
|
||||
let fs = require('../lib/core/fs');
|
||||
const { Proxy } = require('embark-blockchain-process');
|
||||
const constants = require('embark-core/constants');
|
||||
|
||||
describe('embark.Proxy', function () {
|
||||
let ipc, proxy, ipcRequests;
|
||||
before(function () {
|
||||
ipc = new IPC({
|
||||
ipcRole: 'none',
|
||||
fs: fs
|
||||
});
|
||||
ipcRequests = [];
|
||||
|
||||
ipc.connected = true;
|
||||
sinon.stub(ipc, 'request').callsFake((...args) => {
|
||||
ipcRequests.push(args);
|
||||
});
|
||||
|
||||
proxy = new Proxy(ipc);
|
||||
});
|
||||
|
||||
describe('#trackRequest', function () {
|
||||
it('should handle eth_sendTransaction', function (done) {
|
||||
const to = "to";
|
||||
const data = "data";
|
||||
|
||||
proxy.trackRequest({ws: true, data: {
|
||||
id: 1,
|
||||
method: constants.blockchain.transactionMethods.eth_sendTransaction,
|
||||
params: [{
|
||||
to: to,
|
||||
data: data
|
||||
}]
|
||||
}});
|
||||
|
||||
expect(proxy.commList[1]).to.deep.equal({
|
||||
type: 'contract-log',
|
||||
address: to,
|
||||
data: data
|
||||
});
|
||||
done();
|
||||
});
|
||||
|
||||
it('should handle eth_sendRawTransaction', function (done) {
|
||||
const to = "0x2e6242a07ea1c4e79ecc5c69a2dccae19878a280";
|
||||
const data = "0x60fe47b1000000000000000000000000000000000000000000000000000000000000115c";
|
||||
|
||||
proxy.trackRequest({ws: true, data: {
|
||||
id: 1,
|
||||
method: constants.blockchain.transactionMethods.eth_sendRawTransaction,
|
||||
params: ["0xf8852701826857942e6242a07ea1c4e79ecc5c69a2dccae19878a28080a460fe47b1000000000000000000000000000000000000000000000000000000000000115c820a96a04d6e3cbb86d80a75cd51da02bf8f0cc9893d64ca7956ce21b350cc143aa8a023a05878a850e4e7810d08093add07df405298280fdd901ecbabb74e73422cb5e0b0"]
|
||||
}});
|
||||
|
||||
expect(proxy.commList[1]).to.deep.equal({
|
||||
type: 'contract-log',
|
||||
address: to,
|
||||
data: data
|
||||
});
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
describe('#trackResponse', function () {
|
||||
describe('when the response is a transaction', function () {
|
||||
before(function () {
|
||||
proxy.trackRequest({ws: true, data: {
|
||||
id: 1,
|
||||
method: constants.blockchain.transactionMethods.eth_sendTransaction,
|
||||
params: [{to: "to", data: "data" }]
|
||||
}});
|
||||
});
|
||||
|
||||
it('should populate the transaction when it is a known id', function (done) {
|
||||
proxy.trackResponse({
|
||||
id: 1,
|
||||
result: 1
|
||||
});
|
||||
|
||||
expect(proxy.transactions[1]).to.deep.equal({
|
||||
commListId: 1
|
||||
});
|
||||
done();
|
||||
});
|
||||
|
||||
it('should not populate the transaction when it is a unknown id', function (done) {
|
||||
proxy.trackResponse({
|
||||
id: 2
|
||||
});
|
||||
|
||||
expect(proxy.transactions[2]).to.be.equal(undefined);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the response is a receipt', function () {
|
||||
it('should make an ipc call', function (done) {
|
||||
proxy.trackRequest({ws: true, data: {
|
||||
id: 3,
|
||||
method: constants.blockchain.transactionMethods.eth_sendTransaction,
|
||||
params: [{
|
||||
to: "to",
|
||||
data: "data"
|
||||
}]
|
||||
}});
|
||||
|
||||
proxy.trackResponse({
|
||||
id: 3,
|
||||
result: {
|
||||
to: "to",
|
||||
data: "data"
|
||||
}
|
||||
});
|
||||
|
||||
proxy.trackRequest({ws: true, data: {
|
||||
id: 4,
|
||||
method: constants.blockchain.transactionMethods.eth_getTransactionReceipt,
|
||||
params: [{
|
||||
to: "to",
|
||||
data: "data"
|
||||
}]
|
||||
}});
|
||||
|
||||
expect(proxy.receipts[4]).to.be.equal(3);
|
||||
|
||||
proxy.trackResponse({
|
||||
id: 4,
|
||||
result: {
|
||||
blockNumber: 666,
|
||||
gasUsed: 122,
|
||||
status: 'ok'
|
||||
}
|
||||
});
|
||||
|
||||
expect(ipcRequests[0]).to.deep.equal([
|
||||
"log", {
|
||||
"address": "to",
|
||||
"blockNumber": 666,
|
||||
"data": "data",
|
||||
"gasUsed": 122,
|
||||
"status": "ok",
|
||||
"transactionHash": {
|
||||
"data": "data",
|
||||
"to": "to"
|
||||
},
|
||||
"type": "contract-log"
|
||||
}
|
||||
]);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue