Skip to content
This repository has been archived by the owner on Jun 7, 2023. It is now read-only.

Commit

Permalink
Shared: Block requests to nodes running beta, alpha and release IRI r…
Browse files Browse the repository at this point in the history
…eleases (#805)

* Block unsupported IRI version nodes

* Linting fixes

* Update src/shared/actions/alerts.js

Co-Authored-By: rihardsgravis <31288628+rihardsgravis@users.noreply.github.com>
  • Loading branch information
rihardsgravis authored and rajivshah3 committed Dec 16, 2018
1 parent 486db19 commit f244f89
Show file tree
Hide file tree
Showing 13 changed files with 84 additions and 43 deletions.
2 changes: 1 addition & 1 deletion src/shared/__tests__/actions/transfers.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -364,7 +364,7 @@ describe('actions: transfers', () => {
sandbox = sinon.sandbox.create();

sandbox.stub(iota.api, 'getNodeInfo').yields(null, {});
sandbox.stub(extendedApis, 'isNodeSynced').resolves(true);
sandbox.stub(extendedApis, 'isNodeHealthy').resolves(true);
sandbox.stub(iota.api, 'getTransactionsToApprove').yields(null, {
trunkTransaction:
'PMEL9E9ZACLGEUPHNX9TSLEBDKTIGXDERNQSURABASAIGPWTFB9WUIXQVPKIFTHUQBRXEYQJANBDZ9999',
Expand Down
32 changes: 24 additions & 8 deletions src/shared/__tests__/libs/iota/extendedApi.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import isEqual from 'lodash/isEqual';
import map from 'lodash/map';
import { expect } from 'chai';
import sinon from 'sinon';
import { getIotaInstance, isNodeSynced } from '../../../libs/iota/extendedApi';
import { getIotaInstance, isNodeHealthy } from '../../../libs/iota/extendedApi';
import { iota, SwitchingConfig } from '../../../libs/iota/index';
import trytes from '../../__samples__/trytes';
import { EMPTY_HASH_TRYTES } from '../../../libs/iota/utils';
Expand Down Expand Up @@ -32,7 +32,7 @@ describe('libs: iota/extendedApi', () => {
});
});

describe('#isNodeSynced', () => {
describe('#isNodeHealthy', () => {
let sandbox;

beforeEach(() => {
Expand All @@ -43,35 +43,50 @@ describe('libs: iota/extendedApi', () => {
sandbox.restore();
});

describe('when node runs an unsupported release', () => {
beforeEach(() => {
sandbox.stub(iota.api, 'getNodeInfo').yields(null, {
appVersion: '0.0.0-RC2',
});
});

it('should throw with an error "Node version not supported"', () => {
return isNodeHealthy().catch((error) => expect(error.message).to.equal('Node version not supported'));
});
});

describe('when latestMilestone is not equal to latestSolidSubtangleMilestone', () => {
beforeEach(() => {
sandbox.stub(iota.api, 'getNodeInfo').yields(null, {
appVersion: '0.0.0',
latestMilestone: EMPTY_HASH_TRYTES,
latestSolidSubtangleMilestone: 'U'.repeat(81),
});
});

it('should throw with an error "Node not synced"', () => {
return isNodeSynced().catch((error) => expect(error.message).to.equal('Node not synced'));
return isNodeHealthy().catch((error) => expect(error.message).to.equal('Node not synced'));
});
});

describe(`when latestMilestone is ${EMPTY_HASH_TRYTES}`, () => {
beforeEach(() => {
sandbox.stub(iota.api, 'getNodeInfo').yields(null, {
appVersion: '0.0.0',
latestMilestone: EMPTY_HASH_TRYTES,
latestSolidSubtangleMilestone: EMPTY_HASH_TRYTES,
});
});

it('should throw with an error "Node not synced"', () => {
return isNodeSynced().catch((error) => expect(error.message).to.equal('Node not synced'));
return isNodeHealthy().catch((error) => expect(error.message).to.equal('Node not synced'));
});
});

describe('when latestSolidSubtangleMilestoneIndex is 1 less than latestMilestoneIndex', () => {
beforeEach(() => {
sandbox.stub(iota.api, 'getNodeInfo').yields(null, {
appVersion: '0.0.0',
latestMilestoneIndex: 426550,
latestSolidSubtangleMilestoneIndex: 426550 - 1,
latestMilestone: 'U'.repeat(81),
Expand All @@ -82,7 +97,7 @@ describe('libs: iota/extendedApi', () => {
it('should return false if "timestamp" on trytes is from five minutes ago', () => {
const getTrytes = sinon.stub(iota.api, 'getTrytes').yields(null, trytes.zeroValue);

return isNodeSynced().then((result) => {
return isNodeHealthy().then((result) => {
expect(result).to.equal(false);
getTrytes.restore();
});
Expand All @@ -102,7 +117,7 @@ describe('libs: iota/extendedApi', () => {

const getTrytes = sinon.stub(iota.api, 'getTrytes').yields(null, trytesWithLatestTimestamp);

return isNodeSynced().then((result) => {
return isNodeHealthy().then((result) => {
expect(result).to.equal(true);
getTrytes.restore();
});
Expand All @@ -112,6 +127,7 @@ describe('libs: iota/extendedApi', () => {
describe(`when latestMilestone is not ${EMPTY_HASH_TRYTES} and is equal to latestSolidSubtangleMilestone`, () => {
beforeEach(() => {
sandbox.stub(iota.api, 'getNodeInfo').yields(null, {
appVersion: '0.0.0',
latestMilestone: 'U'.repeat(81),
latestSolidSubtangleMilestone: 'U'.repeat(81),
});
Expand All @@ -120,7 +136,7 @@ describe('libs: iota/extendedApi', () => {
it('should return false if "timestamp" on trytes is from five minutes ago', () => {
const getTrytes = sinon.stub(iota.api, 'getTrytes').yields(null, trytes.zeroValue);

return isNodeSynced().then((result) => {
return isNodeHealthy().then((result) => {
expect(result).to.equal(false);
getTrytes.restore();
});
Expand All @@ -140,7 +156,7 @@ describe('libs: iota/extendedApi', () => {

const getTrytes = sinon.stub(iota.api, 'getTrytes').yields(null, trytesWithLatestTimestamp);

return isNodeSynced().then((result) => {
return isNodeHealthy().then((result) => {
expect(result).to.equal(true);
getTrytes.restore();
});
Expand Down
12 changes: 6 additions & 6 deletions src/shared/__tests__/libs/iota/utils.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
convertFromTrytes,
getRandomNodes,
withRetriesOnDifferentNodes,
throwIfNodeNotSynced,
throwIfNodeNotHealthy,
} from '../../../libs/iota/utils';

describe('libs: iota/utils', () => {
Expand Down Expand Up @@ -137,12 +137,12 @@ describe('libs: iota/utils', () => {
});
});

describe('#throwIfNodeNotSynced', () => {
describe('#throwIfNodeNotHealthy', () => {
describe('when node is synced', () => {
it('should return true', () => {
const stub = sinon.stub(extendedApis, 'isNodeSynced').resolves(true);
const stub = sinon.stub(extendedApis, 'isNodeHealthy').resolves(true);

return throwIfNodeNotSynced('foo').then((isSynced) => {
return throwIfNodeNotHealthy('foo').then((isSynced) => {
expect(isSynced).to.equal(true);
stub.restore();
});
Expand All @@ -151,9 +151,9 @@ describe('libs: iota/utils', () => {

describe('when node is not synced', () => {
it('should return throw an error with message "Node not synced"', () => {
const stub = sinon.stub(extendedApis, 'isNodeSynced').resolves(false);
const stub = sinon.stub(extendedApis, 'isNodeHealthy').resolves(false);

return throwIfNodeNotSynced('foo')
return throwIfNodeNotHealthy('foo')
.then(() => {
throw new Error();
})
Expand Down
5 changes: 5 additions & 0 deletions src/shared/actions/accounts.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
generateSyncingErrorAlert,
generateAccountDeletedAlert,
generateNodeOutOfSyncErrorAlert,
generateUnsupportedNodeErrorAlert,
generateAccountSyncRetryAlert,
} from '../actions/alerts';
import { changeNode } from '../actions/settings';
Expand Down Expand Up @@ -436,6 +437,8 @@ export const getFullAccountInfo = (seedStore, accountName) => {
const dispatchErrors = () => {
if (err.message === Errors.NODE_NOT_SYNCED) {
dispatch(generateNodeOutOfSyncErrorAlert());
} else if (err.message === Errors.UNSUPPORTED_NODE) {
dispatch(generateUnsupportedNodeErrorAlert());
} else {
dispatch(generateAccountInfoErrorAlert(err));
}
Expand Down Expand Up @@ -479,6 +482,8 @@ export const manuallySyncAccount = (seedStore, accountName) => {
.catch((err) => {
if (err.message === Errors.NODE_NOT_SYNCED) {
dispatch(generateNodeOutOfSyncErrorAlert());
} else if (err.message === Errors.UNSUPPORTED_NODE) {
dispatch(generateUnsupportedNodeErrorAlert());
} else {
dispatch(generateSyncingErrorAlert(err));
}
Expand Down
11 changes: 11 additions & 0 deletions src/shared/actions/alerts.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,17 @@ export const generateNodeOutOfSyncErrorAlert = () => (dispatch) => {
dispatch(generateAlert('error', i18next.t('global:nodeOutOfSync'), i18next.t('global:nodeOutOfSyncExplanation')));
};

/**
* Generates an error alert if a network call is made to an unsupported node
*
* @method generateUnsupportedNodeErrorAlert
*
* @returns {function} dispatch
*/
export const generateUnsupportedNodeErrorAlert = () => (dispatch) => {
dispatch(generateAlert('error', i18next.t('global:experimentalNode'), i18next.t('global:experimentalNodeExplanation')));
};

/**
* Generates an error alert if something goes wrong during snapshot transition
*
Expand Down
19 changes: 7 additions & 12 deletions src/shared/actions/settings.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import get from 'lodash/get';
import keys from 'lodash/keys';
import { changeIotaNode } from '../libs/iota';
import { generateAlert } from './alerts';
import { changeIotaNode } from '../libs/iota/index';
import i18next from '../libs/i18next';
import { generateAlert, generateNodeOutOfSyncErrorAlert, generateUnsupportedNodeErrorAlert } from '../actions/alerts';
import { checkAttachToTangleAsync } from '../libs/iota/extendedApi';
import { getSelectedNodeFromState } from '../selectors/accounts';
import { throwIfNodeNotSynced } from '../libs/iota/utils';
import { throwIfNodeNotHealthy } from '../libs/iota/utils';
import Errors from '../libs/errors';

export const ActionTypes = {
Expand Down Expand Up @@ -431,7 +431,7 @@ export function setFullNode(node, addingCustomNode = false) {
return (dispatch) => {
dispatch(dispatcher.request());

throwIfNodeNotSynced(node)
throwIfNodeNotHealthy(node)
.then(() => checkAttachToTangleAsync(node))
.then((res) => {
// Change IOTA provider on the global iota instance
Expand Down Expand Up @@ -468,14 +468,9 @@ export function setFullNode(node, addingCustomNode = false) {
dispatch(dispatcher.error());

if (err.message === Errors.NODE_NOT_SYNCED) {
dispatch(
generateAlert(
'error',
i18next.t('settings:nodeChangeError'),
i18next.t('settings:thisNodeOutOfSync'),
7000,
),
);
dispatch(generateNodeOutOfSyncErrorAlert());
} else if (err.message === Errors.UNSUPPORTED_NODE) {
dispatch(generateUnsupportedNodeErrorAlert());
} else {
dispatch(dispatcher.alerts.defaultError(err));
}
Expand Down
7 changes: 5 additions & 2 deletions src/shared/actions/transfers.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import {
withRetriesOnDifferentNodes,
fetchRemoteNodes,
getRandomNodes,
throwIfNodeNotSynced,
throwIfNodeNotHealthy,
} from '../libs/iota/utils';
import { setNextStepAsActive, reset as resetProgress } from './progress';
import { clearSendFields } from './ui';
Expand Down Expand Up @@ -66,6 +66,7 @@ import {
generateTransferErrorAlert,
generatePromotionErrorAlert,
generateNodeOutOfSyncErrorAlert,
generateUnsupportedNodeErrorAlert,
generateTransactionSuccessAlert,
} from './alerts';
import i18next from '../libs/i18next.js';
Expand Down Expand Up @@ -745,6 +746,8 @@ export const makeTransaction = (seedStore, receiveAddress, value, message, accou

if (message === Errors.NODE_NOT_SYNCED) {
return dispatch(generateNodeOutOfSyncErrorAlert());
} else if (message === Errors.UNSUPPORTED_NODE) {
return dispatch(generateUnsupportedNodeErrorAlert());
} else if (message === Errors.KEY_REUSE) {
return dispatch(
generateAlert('error', i18next.t('global:keyReuse'), i18next.t('global:keyReuseError')),
Expand Down Expand Up @@ -855,7 +858,7 @@ export const retryFailedTransaction = (accountName, bundleHash, powFn) => (dispa
dispatch(retryFailedTransactionRequest());

return (
throwIfNodeNotSynced()
throwIfNodeNotHealthy()
// First check spent statuses against transaction addresses
.then(() =>
categoriseAddressesBySpentStatus()(
Expand Down
9 changes: 6 additions & 3 deletions src/shared/actions/wallet.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,15 @@ import {
generateTransitionErrorAlert,
generateAddressesSyncRetryAlert,
generateNodeOutOfSyncErrorAlert,
generateUnsupportedNodeErrorAlert,
} from '../actions/alerts';
import { setActiveStepIndex, startTrackingProgress, reset as resetProgress } from '../actions/progress';
import { changeNode } from '../actions/settings';
import { accumulateBalance, attachAndFormatAddress, syncAddresses } from '../libs/iota/addresses';
import i18next from '../libs/i18next';
import { syncAccountDuringSnapshotTransition } from '../libs/iota/accounts';
import { getBalancesAsync } from '../libs/iota/extendedApi';
import { withRetriesOnDifferentNodes, getRandomNodes, throwIfNodeNotSynced } from '../libs/iota/utils';
import { withRetriesOnDifferentNodes, getRandomNodes, throwIfNodeNotHealthy } from '../libs/iota/utils';
import Errors from '../libs/errors';
import {
selectedAccountStateFactory,
Expand Down Expand Up @@ -313,7 +314,7 @@ export const generateNewAddress = (seedStore, accountName, existingAccountData)
dispatch(generateNewAddressRequest());

const syncAddressesWithSyncedNode = (provider) => {
return (...args) => throwIfNodeNotSynced(provider).then(() => syncAddresses(provider)(...args));
return (...args) => throwIfNodeNotHealthy(provider).then(() => syncAddresses(provider)(...args));
};

const selectedNode = getSelectedNodeFromState(getState());
Expand Down Expand Up @@ -388,7 +389,7 @@ export const completeSnapshotTransition = (seedStore, accountName, addresses, po
dispatch(snapshotAttachToTangleRequest());

// Check node's health
throwIfNodeNotSynced()
throwIfNodeNotHealthy()
.then(() => getBalancesAsync()(addresses))
// Find balance on all addresses
.then((balances) => {
Expand Down Expand Up @@ -458,6 +459,8 @@ export const completeSnapshotTransition = (seedStore, accountName, addresses, po
.catch((error) => {
if (error.message === Errors.NODE_NOT_SYNCED) {
dispatch(generateNodeOutOfSyncErrorAlert());
} else if (error.message === Errors.UNSUPPORTED_NODE) {
dispatch(generateUnsupportedNodeErrorAlert());
} else {
dispatch(generateTransitionErrorAlert(error));
}
Expand Down
1 change: 1 addition & 0 deletions src/shared/libs/errors.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export default {
TRANSACTION_ALREADY_CONFIRMED: 'Transaction already confirmed.',
INCOMING_TRANSFERS: 'Incoming transfers to all selected inputs',
NODE_NOT_SYNCED: 'Node not synced',
UNSUPPORTED_NODE: 'Node version not supported',
INVALID_BUNDLE: 'Invalid bundle',
INVALID_BUNDLE_CONSTRUCTED_DURING_REATTACHMENT: 'Invalid bundle constructed during reattachment.',
INVALID_PARAMETERS: 'Invalid parameters',
Expand Down
6 changes: 3 additions & 3 deletions src/shared/libs/iota/accounts.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ import {
formatAddressData,
findSpendStatusesFromTransactionObjects,
} from './addresses';
import { EMPTY_HASH_TRYTES, throwIfNodeNotSynced } from './utils';
import { EMPTY_HASH_TRYTES, throwIfNodeNotHealthy } from './utils';

/**
* Takes in account data fetched from ledger.
Expand Down Expand Up @@ -109,7 +109,7 @@ export const getAccountData = (provider) => (seedStore, accountName) => {
hashes: [],
};

return throwIfNodeNotSynced(provider)
return throwIfNodeNotHealthy(provider)
.then(() => getFullAddressHistory(provider)(seedStore))
.then((history) => {
data = { ...data, ...history };
Expand Down Expand Up @@ -173,7 +173,7 @@ export const syncAccount = (provider) => (existingAccountState, seedStore, notif
const thisStateCopy = cloneDeep(existingAccountState);
const rescanAddresses = typeof seedStore === 'object';

return throwIfNodeNotSynced(provider)
return throwIfNodeNotHealthy(provider)
.then(
() =>
rescanAddresses
Expand Down
Loading

0 comments on commit f244f89

Please sign in to comment.