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])
};
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';

View File

@ -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 (
<Login credentials={this.props.credentials}
authenticate={this.props.authenticate}
error={this.props.authenticationError} />
<Login credentials={this.props.credentials}
authenticate={this.props.authenticate}
error={this.props.authenticationError} />
);
} else if (this.props.credentials.authenticating) {
return <React.Fragment/>;
return <React.Fragment />;
}
return (
<Layout location={this.props.location}
logout={this.props.logout}
toggleTheme={() => this.toggleTheme()}
currentTheme={this.props.theme}>
logout={this.props.logout}
toggleTheme={() => this.toggleTheme()}
currentTheme={this.props.theme}>
<React.Fragment>{routes}</React.Fragment>
</Layout>
);
@ -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));

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 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)
]);
}

View File

@ -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');
}

View File

@ -23,8 +23,12 @@ export function ansiToHtml(text) {
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) {
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;

View File

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

View File

@ -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;

View File

@ -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);
};

View File

@ -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(() => {});
}
});

View File

@ -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');

View File

@ -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);

View File

@ -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 %>
});

View File

@ -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
});
}

View File

@ -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