Merge pull request #1220 from embark-framework/feat/enable-regular-txs

feat(@embark/core): Disable regular txs until needed
This commit is contained in:
Eric Mastro 2019-01-11 23:38:50 +11:00 committed by GitHub
commit 05803fcf42
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 182 additions and 39 deletions

View File

@ -426,6 +426,13 @@ export const removeEditorTabs = {
failure: () => action(REMOVE_EDITOR_TABS[FAILURE]) 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 // Web Socket
export const WATCH_NEW_PROCESS_LOGS = 'WATCH_NEW_PROCESS_LOGS'; export const WATCH_NEW_PROCESS_LOGS = 'WATCH_NEW_PROCESS_LOGS';
export const STOP_NEW_PROCESS_LOGS = 'STOP_NEW_PROCESS_LOGS'; export const STOP_NEW_PROCESS_LOGS = 'STOP_NEW_PROCESS_LOGS';

View File

@ -6,7 +6,7 @@ import routes from '../routes';
import Login from '../components/Login'; import Login from '../components/Login';
import Layout from "../components/Layout"; import Layout from "../components/Layout";
import {DEFAULT_HOST} from '../constants'; 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 {Helmet} from "react-helmet";
import { import {
@ -16,6 +16,7 @@ import {
plugins as pluginsAction, plugins as pluginsAction,
listenToServices as listenToServicesAction, listenToServices as listenToServicesAction,
listenToContracts as listenToContractsAction, listenToContracts as listenToContractsAction,
initRegularTxs as initRegularTxsAction,
changeTheme, fetchTheme changeTheme, fetchTheme
} from '../actions'; } from '../actions';
@ -25,6 +26,8 @@ import {
getCredentials, getAuthenticationError, getProcesses, getTheme getCredentials, getAuthenticationError, getProcesses, getTheme
} from '../reducers/selectors'; } from '../reducers/selectors';
const ENABLE_REGULAR_TXS = 'enableRegularTxs';
class AppContainer extends Component { class AppContainer extends Component {
componentDidMount() { componentDidMount() {
this.props.fetchCredentials(); this.props.fetchCredentials();
@ -50,26 +53,28 @@ class AppContainer extends Component {
const queryToken = getQueryToken(this.props.location); const queryToken = getQueryToken(this.props.location);
if (queryToken && !(queryToken === this.props.credentials.token && if (queryToken && !(queryToken === this.props.credentials.token &&
this.props.credentials.host === DEFAULT_HOST)) { this.props.credentials.host === DEFAULT_HOST)) {
return true; return true;
} }
if (!this.props.credentials.authenticated && if (!this.props.credentials.authenticated &&
this.props.credentials.host && this.props.credentials.host &&
this.props.credentials.token) { this.props.credentials.token) {
return true; return true;
} }
return false; return false;
} }
componentDidUpdate(){ componentDidUpdate() {
if (this.requireAuthentication()) { if (this.requireAuthentication()) {
this.doAuthenticate(); this.doAuthenticate();
} }
if (getQueryToken(this.props.location) && const enableRegularTxs = !!getQueryParam(this.props.location, ENABLE_REGULAR_TXS);
(!this.props.credentials.authenticating ||
if (getQueryToken(this.props.location) &&
(!this.props.credentials.authenticating ||
this.props.credentials.authenticated)) { this.props.credentials.authenticated)) {
this.props.history.replace(stripQueryToken(this.props.location)); this.props.history.replace(stripQueryToken(this.props.location));
} }
@ -80,6 +85,10 @@ class AppContainer extends Component {
this.props.listenToServices(); this.props.listenToServices();
this.props.fetchPlugins(); this.props.fetchPlugins();
this.props.listenToContracts(); 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() { renderBody() {
if (this.shouldRenderLogin()) { if (this.shouldRenderLogin()) {
return ( return (
<Login credentials={this.props.credentials} <Login credentials={this.props.credentials}
authenticate={this.props.authenticate} authenticate={this.props.authenticate}
error={this.props.authenticationError} /> error={this.props.authenticationError} />
); );
} else if (this.props.credentials.authenticating) { } else if (this.props.credentials.authenticating) {
return <React.Fragment/>; return <React.Fragment />;
} }
return ( return (
<Layout location={this.props.location} <Layout location={this.props.location}
logout={this.props.logout} logout={this.props.logout}
toggleTheme={() => this.toggleTheme()} toggleTheme={() => this.toggleTheme()}
currentTheme={this.props.theme}> currentTheme={this.props.theme}>
<React.Fragment>{routes}</React.Fragment> <React.Fragment>{routes}</React.Fragment>
</Layout> </Layout>
); );
@ -148,7 +157,8 @@ AppContainer.propTypes = {
fetchTheme: PropTypes.func, fetchTheme: PropTypes.func,
history: PropTypes.object, history: PropTypes.object,
listenToServices: PropTypes.func, listenToServices: PropTypes.func,
listenToContracts: PropTypes.func listenToContracts: PropTypes.func,
initRegularTxs: PropTypes.func
}; };
function mapStateToProps(state) { function mapStateToProps(state) {
@ -173,6 +183,7 @@ export default withRouter(connect(
fetchPlugins: pluginsAction.request, fetchPlugins: pluginsAction.request,
changeTheme: changeTheme.request, changeTheme: changeTheme.request,
fetchTheme: fetchTheme.request, fetchTheme: fetchTheme.request,
listenToContracts: listenToContractsAction listenToContracts: listenToContractsAction,
initRegularTxs: initRegularTxsAction.request
}, },
)(AppContainer)); )(AppContainer));

View File

@ -79,6 +79,7 @@ export const debugStepIntoForward = doRequest.bind(null, actions.debugStepIntoFo
export const debugStepIntoBackward = doRequest.bind(null, actions.debugStepIntoBackward, api.debugStepIntoBackward); export const debugStepIntoBackward = doRequest.bind(null, actions.debugStepIntoBackward, api.debugStepIntoBackward);
export const toggleBreakpoint = doRequest.bind(null, actions.toggleBreakpoint, api.toggleBreakpoint); export const toggleBreakpoint = doRequest.bind(null, actions.toggleBreakpoint, api.toggleBreakpoint);
export const authenticate = doRequest.bind(null, actions.authenticate, api.authenticate); 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 fetchCredentials = doRequest.bind(null, actions.fetchCredentials, storage.fetchCredentials);
export const saveCredentials = doRequest.bind(null, actions.saveCredentials, storage.saveCredentials); 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); yield takeEvery(actions.REMOVE_EDITOR_TABS[actions.SUCCESS], fetchEditorTabs);
} }
export function *watchInitRegularTxs() {
yield takeEvery(actions.INIT_REGULAR_TXS[actions.REQUEST], initRegularTxs);
}
function createChannel(socket) { function createChannel(socket) {
return eventChannel(emit => { return eventChannel(emit => {
socket.onmessage = ((message) => { socket.onmessage = ((message) => {
@ -585,6 +590,7 @@ export default function *root() {
fork(watchRemoveEditorTabsSuccess), fork(watchRemoveEditorTabsSuccess),
fork(watchPostFileSuccess), fork(watchPostFileSuccess),
fork(watchPostFolderSuccess), fork(watchPostFolderSuccess),
fork(watchListenContracts) fork(watchListenContracts),
fork(watchInitRegularTxs)
]); ]);
} }

View File

@ -228,6 +228,10 @@ export function toggleBreakpoint(payload) {
return post('/debugger/breakpoint', {params: payload, credentials: payload.credentials}); 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) { export function listenToDebugger(credentials) {
return websocket(credentials, '/debugger'); return websocket(credentials, '/debugger');
} }

View File

@ -23,8 +23,12 @@ export function ansiToHtml(text) {
return convert.toHtml(text.replace(/\n/g,'<br>')); return convert.toHtml(text.replace(/\n/g,'<br>'));
} }
export function getQueryParam(location, param) {
return qs.parse(location.search, {ignoreQueryPrefix: true})[param];
}
export function getQueryToken(location) { export function getQueryToken(location) {
return qs.parse(location.search, {ignoreQueryPrefix: true}).token; return getQueryParam(location, 'token');
} }
export function getDebuggerTransactionHash(location) { export function getDebuggerTransactionHash(location) {
@ -32,9 +36,13 @@ export function getDebuggerTransactionHash(location) {
} }
export function stripQueryToken(location) { export function stripQueryToken(location) {
return stripQueryParam(location, 'token');
}
export function stripQueryParam(location, param) {
const _location = Object.assign({}, location); const _location = Object.assign({}, location);
_location.search = _location.search.replace( _location.search = _location.search.replace(
/(\?|&?)(token=[\w-]*)(&?)/, new RegExp(`(\\?|&?)(${param}=[\\w-]*)(&?)`),
(_, p1, p2, p3) => (p2 ? (p3 === '&' ? p1 : '') : '') (_, p1, p2, p3) => (p2 ? (p3 === '&' ? p1 : '') : '')
); );
return _location; return _location;

View File

@ -53,7 +53,9 @@
"eth_sendTransaction": "eth_sendTransaction", "eth_sendTransaction": "eth_sendTransaction",
"eth_sendRawTransaction": "eth_sendRawTransaction", "eth_sendRawTransaction": "eth_sendRawTransaction",
"eth_getTransactionReceipt": "eth_getTransactionReceipt" "eth_getTransactionReceipt": "eth_getTransactionReceipt"
} },
"startRegularTxs": "startRegularTxs",
"stopRegularTxs": "stopRegularTxs"
}, },
"storage": { "storage": {
"init": "init", "init": "init",

View File

@ -26,6 +26,9 @@ class BlockchainListener {
if (this.ipc.isServer()) { if (this.ipc.isServer()) {
this._listenToBlockchainLogs(); this._listenToBlockchainLogs();
this._listenToCommands();
this._registerConsoleCommands();
this._registerApiEndpoint();
} }
} }
@ -40,6 +43,43 @@ class BlockchainListener {
this.processLogsApi.logHandler.handleLog({logLevel, message}); 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; module.exports = BlockchainListener;

View File

@ -65,6 +65,8 @@ var Blockchain = function(userConfig, clientClass) {
proxy: this.userConfig.proxy proxy: this.userConfig.proxy
}; };
this.devFunds = null;
if (this.userConfig.accounts) { if (this.userConfig.accounts) {
const nodeAccounts = this.userConfig.accounts.find(account => account.nodeAccounts); const nodeAccounts = this.userConfig.accounts.find(account => account.nodeAccounts);
if (nodeAccounts) { if (nodeAccounts) {
@ -120,7 +122,7 @@ Blockchain.prototype.initStandaloneProcess = function () {
if (this.isStandalone) { if (this.isStandalone) {
// on every log logged in logger (say that 3x fast), send the log // 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) // 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) { if (this.ipc.connected) {
this.ipc.request('blockchain:log', {logLevel, message}); this.ipc.request('blockchain:log', {logLevel, message});
} }
@ -133,7 +135,14 @@ Blockchain.prototype.initStandaloneProcess = function () {
// `embark run` without restarting `embark blockchain`) // `embark run` without restarting `embark blockchain`)
setInterval(() => { setInterval(() => {
if (!this.ipc.connected) { 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); }, IPC_CONNECT_INTERVAL);
} }
@ -244,11 +253,6 @@ Blockchain.prototype.run = function () {
data = data.toString(); data = data.toString();
if (!self.readyCalled && self.client.isReady(data)) { if (!self.readyCalled && self.client.isReady(data)) {
self.readyCalled = true; self.readyCalled = true;
if (self.isDev) {
self.fundAccounts((err) => {
if (err) this.logger.error('Error funding accounts', err);
});
}
if (self.config.proxy) { if (self.config.proxy) {
await self.setupProxy(); await self.setupProxy();
} }
@ -280,14 +284,50 @@ Blockchain.prototype.run = function () {
}; };
Blockchain.prototype.fundAccounts = function(cb) { Blockchain.prototype.fundAccounts = function(cb) {
DevFunds.new({blockchainConfig: this.config}).then(devFunds => { if(this.isDev && this.devFunds){
devFunds.fundAccounts(this.client.needKeepAlive(), (err) => { this.devFunds.fundAccounts((err) => {
cb(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 () { 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) { if (this.onReadyCallback) {
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') { if ((userConfig === {} || JSON.stringify(userConfig) === '{"enabled":true}') && env !== 'development') {
logger.info("===> " + __("warning: running default config on a non-development environment")); 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.onExitCallback = onExitCallback;
userConfig.logger = logger; userConfig.logger = logger;
userConfig.certOptions = certOptions; userConfig.certOptions = certOptions;
userConfig.isStandalone = isStandalone;
return new Blockchain(userConfig, clientClass); return new Blockchain(userConfig, clientClass);
}; };

View File

@ -52,4 +52,10 @@ process.on('message', (msg) => {
blockchainProcess = new BlockchainProcess(msg.options); blockchainProcess = new BlockchainProcess(msg.options);
return blockchainProcess.send({result: constants.blockchain.initiated}); 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(() => {});
}
}); });

View File

@ -38,7 +38,8 @@ class BlockchainProcessLauncher {
env: this.env, env: this.env,
isDev: this.isDev, isDev: this.isDev,
locale: this.locale, 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.events.on('logs:ethereum:disable', () => {
this.blockchainProcess.silent = true; 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.events.on('exit', () => {
this.blockchainProcess.send('exit'); this.blockchainProcess.send('exit');

View File

@ -60,20 +60,28 @@ class DevFunds {
this.web3.eth.sendTransaction({value: "1000000000000000", to: "0xA2817254cb8E7b6269D1689c3E0eBadbB78889d1", from: this.web3.eth.defaultAccount}); this.web3.eth.sendTransaction({value: "1000000000000000", to: "0xA2817254cb8E7b6269D1689c3E0eBadbB78889d1", from: this.web3.eth.defaultAccount});
} }
_regularTxs(cb) { startRegularTxs(cb) {
const self = this; const self = this;
self.web3.eth.net.getId().then((networkId) => { self.web3.eth.net.getId().then((networkId) => {
self.networkId = networkId; self.networkId = networkId;
if (self.networkId !== 1337) { if (self.networkId !== 1337) {
return; return;
} }
setInterval(function() { self._sendTx(); }, 1500); this.regularTxsInt = setInterval(function() { self._sendTx(); }, 1500);
if (cb) { if (cb) {
cb(); cb();
} }
}); });
} }
stopRegularTxs(cb) {
if(this.regularTxsInt) {
clearInterval(this.regularTxsInt);
return cb();
}
cb('Regular txs not enabled.');
}
_fundAccounts(balance, cb) { _fundAccounts(balance, cb) {
async.each(this.accounts, (account, next) => { async.each(this.accounts, (account, next) => {
this.web3.eth.getBalance(account).then(currBalance => { this.web3.eth.getBalance(account).then(currBalance => {
@ -113,13 +121,12 @@ class DevFunds {
}, cb); }, cb);
} }
fundAccounts(pingForever = false, cb) { fundAccounts(cb) {
if (!this.web3) { if (!this.web3) {
return cb(); return cb();
} }
async.waterfall([ async.waterfall([
(next) => { (next) => {
if (pingForever) this._regularTxs();
this._fundAccounts(this.balance, next); this._fundAccounts(this.balance, next);
} }
], cb); ], cb);

View File

@ -1,4 +1,4 @@
EmbarkJS.Blockchain.autoEnable = <%= autoEnable %>; EmbarkJS.Blockchain.autoEnable = <%= autoEnable %>;
EmbarkJS.Blockchain.connect(<%- connectionList %>, {warnAboutMetamask: <%= warnAboutMetamask %>}, function(err) { EmbarkJS.Blockchain.connect(<%- connectionList %>, {warnAboutMetamask: <%= warnAboutMetamask %>, blockchainClient: "<%= blockchainClient %>"}, function(err) {
<%- done %> <%- done %>
}); });

View File

@ -26,6 +26,7 @@ class CodeGenerator {
this.storageConfig = embark.config.storageConfig || {}; this.storageConfig = embark.config.storageConfig || {};
this.communicationConfig = embark.config.communicationConfig || {}; this.communicationConfig = embark.config.communicationConfig || {};
this.namesystemConfig = embark.config.namesystemConfig || {}; this.namesystemConfig = embark.config.namesystemConfig || {};
this.webServerConfig = embark.config.webServerConfig || {};
this.env = options.env || 'development'; this.env = options.env || 'development';
this.plugins = options.plugins; this.plugins = options.plugins;
this.events = embark.events; this.events = embark.events;
@ -124,7 +125,8 @@ class CodeGenerator {
autoEnable: this.contractsConfig.dappAutoEnable, autoEnable: this.contractsConfig.dappAutoEnable,
connectionList: connectionList, connectionList: connectionList,
done: 'done(err);', done: 'done(err);',
warnAboutMetamask: isDev warnAboutMetamask: isDev,
blockchainClient: this.blockchainConfig.ethereumClientName
}); });
} }

View File

@ -110,7 +110,7 @@ describe('embark.DevFunds', function() {
// provider.injectResult('0x11f4d0A3c12e86B4b5F39B213F7E19D048276DAe'); // send tx response // provider.injectResult('0x11f4d0A3c12e86B4b5F39B213F7E19D048276DAe'); // send tx response
}); });
devFunds.fundAccounts(devFunds.balance, (errFundAccounts) => { devFunds.fundAccounts((errFundAccounts) => {
assert.equal(errFundAccounts, null); assert.equal(errFundAccounts, null);
// inject response for web3.eth.getAccounts // inject response for web3.eth.getAccounts