Skip to content

Commit

Permalink
add retries for commitment in case of connection errors
Browse files Browse the repository at this point in the history
  • Loading branch information
himanshu committed May 28, 2024
1 parent 0160ae8 commit 0bbada8
Show file tree
Hide file tree
Showing 2 changed files with 75 additions and 15 deletions.
60 changes: 59 additions & 1 deletion src/helpers/common.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { JRPCResponse } from "@toruslabs/constants";
import { Ecies } from "@toruslabs/eccrypto";
import JsonStringify from "json-stable-stringify";

import { EciesHex, VerifierLookupResponse } from "../interfaces";
import { CommitmentRequestResult, EciesHex, VerifierLookupResponse } from "../interfaces";

// this function normalizes the result from nodes before passing the result to threshold check function
// For ex: some fields returns by nodes might be different from each other
Expand Down Expand Up @@ -104,3 +105,60 @@ export function calculateMedian(arr: number[]): number {
const mid2 = sortedArr[arrSize / 2];
return (mid1 + mid2) / 2;
}

export function waitFor(milliseconds: number) {
return new Promise((resolve, reject) => {
// hack to bypass eslint warning.
if (milliseconds > 0) {
setTimeout(resolve, milliseconds);
} else {
reject(new Error("value of milliseconds must be greater than 0"));
}
});
}

export function retryCommitment(executionPromise: () => Promise<JRPCResponse<CommitmentRequestResult>>, maxRetries: number) {
// Notice that we declare an inner function here
// so we can encapsulate the retries and don't expose
// it to the caller. This is also a recursive function
async function retryWithBackoff(retries: number) {
try {
// we don't wait on the first attempt
if (retries > 0) {
// on every retry, we exponentially increase the time to wait.
// Here is how it looks for a `maxRetries` = 4
// (2 ** 1) * 100 = 200 ms
// (2 ** 2) * 100 = 400 ms
// (2 ** 3) * 100 = 800 ms
const timeToWait = 2 ** retries * 100;
await waitFor(timeToWait);
}
const a = await executionPromise();
return a;
} catch (e: unknown) {
const errorMsg = (e as Error).message;
const acceptedErrorMsgs = [
// Slow node
"Timed out",
"Failed to fetch",
"fetch failed",
"Load failed",
"cancelled",
"NetworkError when attempting to fetch resource.",
// Happens when the node is not reachable (dns issue etc)
"TypeError: Failed to fetch", // All except iOS and Firefox
"TypeError: cancelled", // iOS
"TypeError: NetworkError when attempting to fetch resource.", // Firefox
];

if (retries < maxRetries && (acceptedErrorMsgs.includes(errorMsg) || (errorMsg && errorMsg.includes("reason: getaddrinfo EAI_AGAIN")))) {
// only retry if we didn't reach the limit
// otherwise, let the caller handle the error
return retryWithBackoff(retries + 1);
}
throw e;
}
}

return retryWithBackoff(0);
}
30 changes: 16 additions & 14 deletions src/helpers/nodeUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import {
} from "../interfaces";
import log from "../loglevel";
import { Some } from "../some";
import { calculateMedian, kCombinations, normalizeKeysResult, thresholdSame } from "./common";
import { calculateMedian, kCombinations, normalizeKeysResult, retryCommitment, thresholdSame } from "./common";
import { generateAddressFromPrivKey, generateAddressFromPubKey, keccak256 } from "./keyUtils";
import { lagrangeInterpolation } from "./langrangeInterpolatePoly";
import { decryptNodeData, getMetadata, getOrSetNonce, getOrSetSapphireMetadataNonce } from "./metadataUtils";
Expand Down Expand Up @@ -209,19 +209,21 @@ export async function retrieveOrImportShare(params: {
VerifierIdentifier string `json:"verifieridentifier"`
}
*/
const p = post<JRPCResponse<CommitmentRequestResult>>(
endpoints[i],
generateJsonRPCObject(JRPC_METHODS.COMMITMENT_REQUEST, {
messageprefix: "mug00",
tokencommitment: tokenCommitment.slice(2),
temppubx: pubKeyX,
temppuby: pubKeyY,
verifieridentifier: verifier,
}),
null,
{ logTracingHeader: config.logRequestTracing }
);
promiseArr.push(p);
const p = () =>
post<JRPCResponse<CommitmentRequestResult>>(
endpoints[i],
generateJsonRPCObject(JRPC_METHODS.COMMITMENT_REQUEST, {
messageprefix: "mug00",
tokencommitment: tokenCommitment.slice(2),
temppubx: pubKeyX,
temppuby: pubKeyY,
verifieridentifier: verifier,
}),
null,
{ logTracingHeader: config.logRequestTracing }
);
const r = retryCommitment(p, 4);
promiseArr.push(r);
}
// send share request once k + t number of commitment requests have completed
return Some<void | JRPCResponse<CommitmentRequestResult>, (void | JRPCResponse<CommitmentRequestResult>)[]>(promiseArr, (resultArr) => {
Expand Down

0 comments on commit 0bbada8

Please sign in to comment.