diff --git a/embark-ui/src/actions/index.js b/embark-ui/src/actions/index.js
index a17d6ef07..2caf23e3b 100644
--- a/embark-ui/src/actions/index.js
+++ b/embark-ui/src/actions/index.js
@@ -426,6 +426,13 @@ export const removeEditorTabs = {
failure: () => action(REMOVE_EDITOR_TABS[FAILURE])
};
+export const INIT_REGULAR_TXS = createRequestTypes('INIT_REGULAR_TXS');
+export const initRegularTxs = {
+ request: () => action(INIT_REGULAR_TXS[REQUEST], {mode: 'on'}),
+ success: () => action(INIT_REGULAR_TXS[SUCCESS]),
+ failure: () => action(INIT_REGULAR_TXS[FAILURE])
+};
+
// Web Socket
export const WATCH_NEW_PROCESS_LOGS = 'WATCH_NEW_PROCESS_LOGS';
export const STOP_NEW_PROCESS_LOGS = 'STOP_NEW_PROCESS_LOGS';
diff --git a/embark-ui/src/containers/AppContainer.js b/embark-ui/src/containers/AppContainer.js
index 2e1d810ad..e38122234 100644
--- a/embark-ui/src/containers/AppContainer.js
+++ b/embark-ui/src/containers/AppContainer.js
@@ -6,7 +6,7 @@ import routes from '../routes';
import Login from '../components/Login';
import Layout from "../components/Layout";
import {DEFAULT_HOST} from '../constants';
-import {getQueryToken, stripQueryToken} from '../utils/utils';
+import {getQueryToken, stripQueryToken, getQueryParam, stripQueryParam} from '../utils/utils';
import {Helmet} from "react-helmet";
import {
@@ -16,6 +16,7 @@ import {
plugins as pluginsAction,
listenToServices as listenToServicesAction,
listenToContracts as listenToContractsAction,
+ initRegularTxs as initRegularTxsAction,
changeTheme, fetchTheme
} from '../actions';
@@ -25,6 +26,8 @@ import {
getCredentials, getAuthenticationError, getProcesses, getTheme
} from '../reducers/selectors';
+const ENABLE_REGULAR_TXS = 'enableRegularTxs';
+
class AppContainer extends Component {
componentDidMount() {
this.props.fetchCredentials();
@@ -50,26 +53,28 @@ class AppContainer extends Component {
const queryToken = getQueryToken(this.props.location);
if (queryToken && !(queryToken === this.props.credentials.token &&
- this.props.credentials.host === DEFAULT_HOST)) {
+ this.props.credentials.host === DEFAULT_HOST)) {
return true;
}
if (!this.props.credentials.authenticated &&
- this.props.credentials.host &&
- this.props.credentials.token) {
+ this.props.credentials.host &&
+ this.props.credentials.token) {
return true;
}
return false;
}
- componentDidUpdate(){
+ componentDidUpdate() {
if (this.requireAuthentication()) {
this.doAuthenticate();
}
- if (getQueryToken(this.props.location) &&
- (!this.props.credentials.authenticating ||
+ const enableRegularTxs = !!getQueryParam(this.props.location, ENABLE_REGULAR_TXS);
+
+ if (getQueryToken(this.props.location) &&
+ (!this.props.credentials.authenticating ||
this.props.credentials.authenticated)) {
this.props.history.replace(stripQueryToken(this.props.location));
}
@@ -80,6 +85,10 @@ class AppContainer extends Component {
this.props.listenToServices();
this.props.fetchPlugins();
this.props.listenToContracts();
+ if (enableRegularTxs) {
+ this.props.initRegularTxs();
+ this.props.history.replace(stripQueryParam(this.props.location, ENABLE_REGULAR_TXS));
+ }
}
}
@@ -99,18 +108,18 @@ class AppContainer extends Component {
renderBody() {
if (this.shouldRenderLogin()) {
return (
-
+
);
} else if (this.props.credentials.authenticating) {
- return ;
+ return ;
}
return (
this.toggleTheme()}
- currentTheme={this.props.theme}>
+ logout={this.props.logout}
+ toggleTheme={() => this.toggleTheme()}
+ currentTheme={this.props.theme}>
{routes}
);
@@ -148,7 +157,8 @@ AppContainer.propTypes = {
fetchTheme: PropTypes.func,
history: PropTypes.object,
listenToServices: PropTypes.func,
- listenToContracts: PropTypes.func
+ listenToContracts: PropTypes.func,
+ initRegularTxs: PropTypes.func
};
function mapStateToProps(state) {
@@ -173,6 +183,7 @@ export default withRouter(connect(
fetchPlugins: pluginsAction.request,
changeTheme: changeTheme.request,
fetchTheme: fetchTheme.request,
- listenToContracts: listenToContractsAction
+ listenToContracts: listenToContractsAction,
+ initRegularTxs: initRegularTxsAction.request
},
)(AppContainer));
diff --git a/embark-ui/src/sagas/index.js b/embark-ui/src/sagas/index.js
index 8f1df6c05..3c21ca5e8 100644
--- a/embark-ui/src/sagas/index.js
+++ b/embark-ui/src/sagas/index.js
@@ -79,6 +79,7 @@ export const debugStepIntoForward = doRequest.bind(null, actions.debugStepIntoFo
export const debugStepIntoBackward = doRequest.bind(null, actions.debugStepIntoBackward, api.debugStepIntoBackward);
export const toggleBreakpoint = doRequest.bind(null, actions.toggleBreakpoint, api.toggleBreakpoint);
export const authenticate = doRequest.bind(null, actions.authenticate, api.authenticate);
+export const initRegularTxs = doRequest.bind(null, actions.initRegularTxs, api.initRegularTxs);
export const fetchCredentials = doRequest.bind(null, actions.fetchCredentials, storage.fetchCredentials);
export const saveCredentials = doRequest.bind(null, actions.saveCredentials, storage.saveCredentials);
@@ -343,6 +344,10 @@ export function *watchRemoveEditorTabsSuccess() {
yield takeEvery(actions.REMOVE_EDITOR_TABS[actions.SUCCESS], fetchEditorTabs);
}
+export function *watchInitRegularTxs() {
+ yield takeEvery(actions.INIT_REGULAR_TXS[actions.REQUEST], initRegularTxs);
+}
+
function createChannel(socket) {
return eventChannel(emit => {
socket.onmessage = ((message) => {
@@ -585,6 +590,7 @@ export default function *root() {
fork(watchRemoveEditorTabsSuccess),
fork(watchPostFileSuccess),
fork(watchPostFolderSuccess),
- fork(watchListenContracts)
+ fork(watchListenContracts),
+ fork(watchInitRegularTxs)
]);
}
diff --git a/embark-ui/src/services/api.js b/embark-ui/src/services/api.js
index abaef78ab..9db453969 100644
--- a/embark-ui/src/services/api.js
+++ b/embark-ui/src/services/api.js
@@ -228,6 +228,10 @@ export function toggleBreakpoint(payload) {
return post('/debugger/breakpoint', {params: payload, credentials: payload.credentials});
}
+export function initRegularTxs(payload) {
+ return get('/regular-txs', {params: payload, credentials: payload.credentials});
+}
+
export function listenToDebugger(credentials) {
return websocket(credentials, '/debugger');
}
diff --git a/embark-ui/src/utils/utils.js b/embark-ui/src/utils/utils.js
index 51ddd1402..fe1629fbd 100644
--- a/embark-ui/src/utils/utils.js
+++ b/embark-ui/src/utils/utils.js
@@ -23,8 +23,12 @@ export function ansiToHtml(text) {
return convert.toHtml(text.replace(/\n/g,'
'));
}
+export function getQueryParam(location, param) {
+ return qs.parse(location.search, {ignoreQueryPrefix: true})[param];
+}
+
export function getQueryToken(location) {
- return qs.parse(location.search, {ignoreQueryPrefix: true}).token;
+ return getQueryParam(location, 'token');
}
export function getDebuggerTransactionHash(location) {
@@ -32,9 +36,13 @@ export function getDebuggerTransactionHash(location) {
}
export function stripQueryToken(location) {
+ return stripQueryParam(location, 'token');
+}
+
+export function stripQueryParam(location, param) {
const _location = Object.assign({}, location);
_location.search = _location.search.replace(
- /(\?|&?)(token=[\w-]*)(&?)/,
+ new RegExp(`(\\?|&?)(${param}=[\\w-]*)(&?)`),
(_, p1, p2, p3) => (p2 ? (p3 === '&' ? p1 : '') : '')
);
return _location;
diff --git a/src/lib/constants.json b/src/lib/constants.json
index 47bfa9a27..3599f9587 100644
--- a/src/lib/constants.json
+++ b/src/lib/constants.json
@@ -53,7 +53,9 @@
"eth_sendTransaction": "eth_sendTransaction",
"eth_sendRawTransaction": "eth_sendRawTransaction",
"eth_getTransactionReceipt": "eth_getTransactionReceipt"
- }
+ },
+ "startRegularTxs": "startRegularTxs",
+ "stopRegularTxs": "stopRegularTxs"
},
"storage": {
"init": "init",
diff --git a/src/lib/modules/blockchain_listener/index.js b/src/lib/modules/blockchain_listener/index.js
index 4a436db6d..527506313 100644
--- a/src/lib/modules/blockchain_listener/index.js
+++ b/src/lib/modules/blockchain_listener/index.js
@@ -26,6 +26,9 @@ class BlockchainListener {
if (this.ipc.isServer()) {
this._listenToBlockchainLogs();
+ this._listenToCommands();
+ this._registerConsoleCommands();
+ this._registerApiEndpoint();
}
}
@@ -40,6 +43,43 @@ class BlockchainListener {
this.processLogsApi.logHandler.handleLog({logLevel, message});
});
}
+
+ _registerConsoleCommands() {
+ this.embark.registerConsoleCommand({
+ description: 'Toggles regular transactions used to prevent transactions from getting stuck when using Geth and Metamask',
+ matches: ['regularTxs on', 'regularTxs off'],
+ usage: "regularTxs on/off",
+ process: (cmd, callback) => {
+ const eventCmd = `regularTxs:${cmd.trim().endsWith('on') ? 'start' : 'stop'}`;
+ this.events.request(eventCmd, callback);
+ }
+ });
+ }
+
+ _registerApiEndpoint() {
+ this.embark.registerAPICall(
+ 'get',
+ '/embark-api/regular-txs',
+ (req, _res) => {
+ this.events.request(`regularTxs:${req.query.mode === 'on' ? 'start' : 'stop'}`);
+ }
+ );
+ }
+
+ _listenToCommands() {
+
+ this.events.setCommandHandler('regularTxs:start', (cb) => {
+ this.events.emit('regularTxs:start');
+ this.ipc.broadcast('regularTxs', 'start');
+ return cb(null, 'Enabling regular transactions');
+ });
+
+ this.events.setCommandHandler('regularTxs:stop', (cb) => {
+ this.events.emit('regularTxs:stop');
+ this.ipc.broadcast('regularTxs', 'stop');
+ return cb(null, 'Disabling regular transactions');
+ });
+ }
}
module.exports = BlockchainListener;
diff --git a/src/lib/modules/blockchain_process/blockchain.js b/src/lib/modules/blockchain_process/blockchain.js
index 9758ecd47..9da45f8af 100644
--- a/src/lib/modules/blockchain_process/blockchain.js
+++ b/src/lib/modules/blockchain_process/blockchain.js
@@ -65,6 +65,8 @@ var Blockchain = function(userConfig, clientClass) {
proxy: this.userConfig.proxy
};
+ this.devFunds = null;
+
if (this.userConfig.accounts) {
const nodeAccounts = this.userConfig.accounts.find(account => account.nodeAccounts);
if (nodeAccounts) {
@@ -120,7 +122,7 @@ Blockchain.prototype.initStandaloneProcess = function () {
if (this.isStandalone) {
// 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.events.on('log', (logLevel, message) => {
+ this.logger.events.on('log', (logLevel, message) => {
if (this.ipc.connected) {
this.ipc.request('blockchain:log', {logLevel, message});
}
@@ -133,7 +135,14 @@ Blockchain.prototype.initStandaloneProcess = function () {
// `embark run` without restarting `embark blockchain`)
setInterval(() => {
if (!this.ipc.connected) {
- this.ipc.connect(() => {});
+ this.ipc.connect(() => {
+ if (this.ipc.connected) {
+ this.ipc.listenTo('regularTxs', (mode) => {
+ if(mode === 'start') this.startRegularTxs(() => {});
+ else if (mode === 'stop') this.stopRegularTxs(() => {});
+ });
+ }
+ });
}
}, IPC_CONNECT_INTERVAL);
}
@@ -244,11 +253,6 @@ Blockchain.prototype.run = function () {
data = data.toString();
if (!self.readyCalled && self.client.isReady(data)) {
self.readyCalled = true;
- if (self.isDev) {
- self.fundAccounts((err) => {
- if (err) this.logger.error('Error funding accounts', err);
- });
- }
if (self.config.proxy) {
await self.setupProxy();
}
@@ -280,14 +284,50 @@ Blockchain.prototype.run = function () {
};
Blockchain.prototype.fundAccounts = function(cb) {
- DevFunds.new({blockchainConfig: this.config}).then(devFunds => {
- devFunds.fundAccounts(this.client.needKeepAlive(), (err) => {
+ if(this.isDev && this.devFunds){
+ this.devFunds.fundAccounts((err) => {
cb(err);
});
- });
+ }
+};
+
+Blockchain.prototype.startRegularTxs = function(cb) {
+ if (this.client.needKeepAlive() && this.devFunds){
+ return this.devFunds.startRegularTxs(() => {
+ this.logger.info('Regular transactions have been enabled.');
+ cb();
+ });
+ }
+ cb();
+};
+
+Blockchain.prototype.stopRegularTxs = function(cb) {
+ if (this.client.needKeepAlive() && this.devFunds){
+ return this.devFunds.stopRegularTxs(() => {
+ this.logger.info('Regular transactions have been disabled.');
+ cb();
+ });
+ }
+ cb();
};
Blockchain.prototype.readyCallback = function () {
+ if (this.isDev) {
+ if(!this.devFunds) {
+ DevFunds.new({blockchainConfig: this.config}).then(devFunds => {
+ this.devFunds = devFunds;
+ this.fundAccounts((err) => {
+ if (err) this.logger.error('Error funding accounts', err);
+ });
+ });
+ }
+ else {
+ this.fundAccounts((err) => {
+ if (err) this.logger.error('Error funding accounts', err);
+ });
+ }
+ }
+
if (this.onReadyCallback) {
this.onReadyCallback();
}
@@ -450,7 +490,7 @@ Blockchain.prototype.initChainAndGetAddress = function (callback) {
});
};
-var BlockchainClient = function(userConfig, clientName, env, certOptions, onReadyCallback, onExitCallback, logger, _events, _isStandalone) {
+var BlockchainClient = function(userConfig, clientName, env, certOptions, onReadyCallback, onExitCallback, logger, _events, isStandalone) {
if ((userConfig === {} || JSON.stringify(userConfig) === '{"enabled":true}') && env !== 'development') {
logger.info("===> " + __("warning: running default config on a non-development environment"));
}
@@ -478,6 +518,7 @@ var BlockchainClient = function(userConfig, clientName, env, certOptions, onRead
userConfig.onExitCallback = onExitCallback;
userConfig.logger = logger;
userConfig.certOptions = certOptions;
+ userConfig.isStandalone = isStandalone;
return new Blockchain(userConfig, clientClass);
};
diff --git a/src/lib/modules/blockchain_process/blockchainProcess.js b/src/lib/modules/blockchain_process/blockchainProcess.js
index e44d72223..b68754976 100644
--- a/src/lib/modules/blockchain_process/blockchainProcess.js
+++ b/src/lib/modules/blockchain_process/blockchainProcess.js
@@ -52,4 +52,10 @@ process.on('message', (msg) => {
blockchainProcess = new BlockchainProcess(msg.options);
return blockchainProcess.send({result: constants.blockchain.initiated});
}
+ else if(msg.action === constants.blockchain.startRegularTxs){
+ blockchainProcess.blockchain.startRegularTxs(() => {});
+ }
+ else if(msg.action === constants.blockchain.stopRegularTxs){
+ blockchainProcess.blockchain.stopRegularTxs(() => {});
+ }
});
diff --git a/src/lib/modules/blockchain_process/blockchainProcessLauncher.js b/src/lib/modules/blockchain_process/blockchainProcessLauncher.js
index 6bf058b72..e8cdd50c5 100644
--- a/src/lib/modules/blockchain_process/blockchainProcessLauncher.js
+++ b/src/lib/modules/blockchain_process/blockchainProcessLauncher.js
@@ -38,7 +38,8 @@ class BlockchainProcessLauncher {
env: this.env,
isDev: this.isDev,
locale: this.locale,
- certOptions: this.embark.config.webServerConfig.certOptions
+ certOptions: this.embark.config.webServerConfig.certOptions,
+ events: this.events
}
});
@@ -62,6 +63,14 @@ class BlockchainProcessLauncher {
this.events.on('logs:ethereum:disable', () => {
this.blockchainProcess.silent = true;
});
+
+ this.events.on('regularTxs:start', () => {
+ this.blockchainProcess.send({action: constants.blockchain.startRegularTxs});
+ });
+
+ this.events.on('regularTxs:stop', () => {
+ this.blockchainProcess.send({action: constants.blockchain.stopRegularTxs});
+ });
this.events.on('exit', () => {
this.blockchainProcess.send('exit');
diff --git a/src/lib/modules/blockchain_process/dev_funds.js b/src/lib/modules/blockchain_process/dev_funds.js
index bc012d0c7..a269b77a3 100644
--- a/src/lib/modules/blockchain_process/dev_funds.js
+++ b/src/lib/modules/blockchain_process/dev_funds.js
@@ -60,20 +60,28 @@ class DevFunds {
this.web3.eth.sendTransaction({value: "1000000000000000", to: "0xA2817254cb8E7b6269D1689c3E0eBadbB78889d1", from: this.web3.eth.defaultAccount});
}
- _regularTxs(cb) {
+ startRegularTxs(cb) {
const self = this;
self.web3.eth.net.getId().then((networkId) => {
self.networkId = networkId;
if (self.networkId !== 1337) {
return;
}
- setInterval(function() { self._sendTx(); }, 1500);
+ this.regularTxsInt = setInterval(function() { self._sendTx(); }, 1500);
if (cb) {
cb();
}
});
}
+ stopRegularTxs(cb) {
+ if(this.regularTxsInt) {
+ clearInterval(this.regularTxsInt);
+ return cb();
+ }
+ cb('Regular txs not enabled.');
+ }
+
_fundAccounts(balance, cb) {
async.each(this.accounts, (account, next) => {
this.web3.eth.getBalance(account).then(currBalance => {
@@ -113,13 +121,12 @@ class DevFunds {
}, cb);
}
- fundAccounts(pingForever = false, cb) {
+ fundAccounts(cb) {
if (!this.web3) {
return cb();
}
async.waterfall([
(next) => {
- if (pingForever) this._regularTxs();
this._fundAccounts(this.balance, next);
}
], cb);
diff --git a/src/lib/modules/code_generator/code_templates/web3-connector.js.ejs b/src/lib/modules/code_generator/code_templates/web3-connector.js.ejs
index 69a2625ec..d88ccd659 100644
--- a/src/lib/modules/code_generator/code_templates/web3-connector.js.ejs
+++ b/src/lib/modules/code_generator/code_templates/web3-connector.js.ejs
@@ -1,4 +1,4 @@
EmbarkJS.Blockchain.autoEnable = <%= autoEnable %>;
-EmbarkJS.Blockchain.connect(<%- connectionList %>, {warnAboutMetamask: <%= warnAboutMetamask %>}, function(err) {
+EmbarkJS.Blockchain.connect(<%- connectionList %>, {warnAboutMetamask: <%= warnAboutMetamask %>, blockchainClient: "<%= blockchainClient %>"}, function(err) {
<%- done %>
});
diff --git a/src/lib/modules/code_generator/index.js b/src/lib/modules/code_generator/index.js
index c25db16f2..23c2419c4 100644
--- a/src/lib/modules/code_generator/index.js
+++ b/src/lib/modules/code_generator/index.js
@@ -26,6 +26,7 @@ class CodeGenerator {
this.storageConfig = embark.config.storageConfig || {};
this.communicationConfig = embark.config.communicationConfig || {};
this.namesystemConfig = embark.config.namesystemConfig || {};
+ this.webServerConfig = embark.config.webServerConfig || {};
this.env = options.env || 'development';
this.plugins = options.plugins;
this.events = embark.events;
@@ -124,7 +125,8 @@ class CodeGenerator {
autoEnable: this.contractsConfig.dappAutoEnable,
connectionList: connectionList,
done: 'done(err);',
- warnAboutMetamask: isDev
+ warnAboutMetamask: isDev,
+ blockchainClient: this.blockchainConfig.ethereumClientName
});
}
diff --git a/src/test/devFunds.js b/src/test/devFunds.js
index 730aad34f..db503a382 100644
--- a/src/test/devFunds.js
+++ b/src/test/devFunds.js
@@ -110,7 +110,7 @@ describe('embark.DevFunds', function() {
// provider.injectResult('0x11f4d0A3c12e86B4b5F39B213F7E19D048276DAe'); // send tx response
});
- devFunds.fundAccounts(devFunds.balance, (errFundAccounts) => {
+ devFunds.fundAccounts((errFundAccounts) => {
assert.equal(errFundAccounts, null);
// inject response for web3.eth.getAccounts