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

Commit

Permalink
Auto-retry with increased timeout for attachToTangle & getTransaction…
Browse files Browse the repository at this point in the history
…sToApprove APIs (#1285)
  • Loading branch information
laumair authored and cvarley100 committed Apr 12, 2019
1 parent 3bf6322 commit 2016241
Show file tree
Hide file tree
Showing 4 changed files with 75 additions and 32 deletions.
2 changes: 2 additions & 0 deletions src/shared/config.js
Expand Up @@ -34,6 +34,8 @@ export const DEFAULT_BALANCES_THRESHOLD = 100;

export const BUNDLE_OUTPUTS_THRESHOLD = 50;

export const MAX_REQUEST_TIMEOUT = 60 * 1000 * 2;

export const DEFAULT_NODE_REQUEST_TIMEOUT = 6000 * 2;
export const GET_NODE_INFO_REQUEST_TIMEOUT = 2500;
export const GET_BALANCES_REQUEST_TIMEOUT = 6000;
Expand Down
1 change: 1 addition & 0 deletions src/shared/libs/errors.js
Expand Up @@ -57,4 +57,5 @@ export default {
LEDGER_CANCELLED: 'Ledger transaction cancelled',
LEDGER_DENIED: 'Ledger transaction denied by user',
LEDGER_INVALID_INDEX: 'Incorrect Ledger device or changed mnemonic',
REQUEST_TIMED_OUT: 'Request timed out',
};
67 changes: 36 additions & 31 deletions src/shared/libs/iota/extendedApi.js
Expand Up @@ -27,7 +27,7 @@ import {
isBundle,
isBundleTraversable,
} from './transfers';
import { EMPTY_HASH_TRYTES } from './utils';
import { EMPTY_HASH_TRYTES, withRequestTimeoutsHandler } from './utils';

/**
* Returns timeouts for specific quorum requests
Expand Down Expand Up @@ -505,36 +505,41 @@ const attachToTangleAsync = (provider, seedStore) => (
const shouldOffloadPow = get(seedStore, 'offloadPow') === true;

if (shouldOffloadPow) {
return new Promise((resolve, reject) => {
getIotaInstance(provider, getApiTimeout('attachToTangle')).api.attachToTangle(
trunkTransaction,
branchTransaction,
minWeightMagnitude,
// Make sure trytes are sorted properly
sortTransactionTrytesArray(trytes),
(err, attachedTrytes) => {
if (err) {
reject(err);
} else {
constructBundleFromAttachedTrytes(attachedTrytes, seedStore)
.then((transactionObjects) => {
if (
isBundle(transactionObjects) &&
isBundleTraversable(transactionObjects, trunkTransaction, branchTransaction)
) {
resolve({
transactionObjects,
trytes: attachedTrytes,
});
} else {
reject(new Error(Errors.INVALID_BUNDLE_CONSTRUCTED_WITH_REMOTE_POW));
}
})
.catch(reject);
}
},
);
});
const request = (requestTimeout) =>
new Promise((resolve, reject) => {
getIotaInstance(provider, requestTimeout).api.attachToTangle(
trunkTransaction,
branchTransaction,
minWeightMagnitude,
// Make sure trytes are sorted properly
sortTransactionTrytesArray(trytes),
(err, attachedTrytes) => {
if (err) {
reject(err);
} else {
constructBundleFromAttachedTrytes(attachedTrytes, seedStore)
.then((transactionObjects) => {
if (
isBundle(transactionObjects) &&
isBundleTraversable(transactionObjects, trunkTransaction, branchTransaction)
) {
resolve({
transactionObjects,
trytes: attachedTrytes,
});
} else {
reject(new Error(Errors.INVALID_BUNDLE_CONSTRUCTED_WITH_REMOTE_POW));
}
})
.catch(reject);
}
},
);
});

const defaultRequestTimeout = getApiTimeout('attachToTangle');

return withRequestTimeoutsHandler(defaultRequestTimeout)(request);
}

return seedStore
Expand Down
37 changes: 36 additions & 1 deletion src/shared/libs/iota/utils.js
Expand Up @@ -10,7 +10,7 @@ import URL from 'url-parse';
import { BigNumber } from 'bignumber.js';
import { iota } from './index';
import { isNodeHealthy } from './extendedApi';
import { NODELIST_URL } from '../../config';
import { NODELIST_URL, MAX_REQUEST_TIMEOUT } from '../../config';
import Errors from '../errors';

export const MAX_SEED_LENGTH = 81;
Expand Down Expand Up @@ -442,3 +442,38 @@ export const throwIfNodeNotHealthy = (provider) => {
return isSynced;
});
};

/**
* Handles timeouts for network requests made to IRI nodes
* Catches "request timeout" exceptions and retries network request with increased timeout
* See (https://github.com/iotaledger/iota.js/blob/master/lib/utils/makeRequest.js#L115)
*
* @method withRequestTimeoutsHandler
*
* @param {number} timeout
*
* @returns {function}
*/
export const withRequestTimeoutsHandler = (timeout) => {
let attempt = 1;

const getNextTimeout = () => attempt * timeout;

const handleTimeout = (promiseFunc) => {
return promiseFunc(getNextTimeout()).catch((error) => {
attempt += 1;

if (
(includes(error.message, Errors.REQUEST_TIMED_OUT) ||
includes(error.message, Errors.REQUEST_TIMED_OUT.toLowerCase())) &&
getNextTimeout() < MAX_REQUEST_TIMEOUT
) {
return handleTimeout(promiseFunc);
}

throw error;
});
};

return handleTimeout;
};

0 comments on commit 2016241

Please sign in to comment.