Skip to content

Commit

Permalink
fix: allow retry with multiple providers
Browse files Browse the repository at this point in the history
  • Loading branch information
phanshiyu committed Jul 4, 2024
1 parent 9a9d9d6 commit 373bc0f
Show file tree
Hide file tree
Showing 7 changed files with 127 additions and 95 deletions.
Binary file added govtechsg-oa-verify-0.0.0-development.tgz
Binary file not shown.
8 changes: 6 additions & 2 deletions src/common/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,12 @@ export const getDefaultProvider = (options: VerificationBuilderOptionsWithNetwor
};

// getProvider is a function to get an existing provider or to get a Default provider, when given the options
export const getProvider = (options: VerificationBuilderOptions): providers.Provider => {
return options.provider ?? getDefaultProvider(options);
export const getProvider = (options: VerificationBuilderOptions): providers.Provider[] => {
const providers = Array.isArray(options.provider) ? options.provider : options.provider ? [options.provider] : [];
if (!providers.length && !options.provider) {
providers.push(getDefaultProvider(options));
}
return providers;
};

/**
Expand Down
4 changes: 2 additions & 2 deletions src/types/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { Reason } from "./error";
export type PromiseCallback = (promises: Promise<VerificationFragment>[]) => void;

export interface VerificationBuilderOptionsWithProvider {
provider: providers.Provider;
provider: providers.Provider | providers.Provider[];
resolver?: Resolver;
}

Expand All @@ -22,7 +22,7 @@ export interface VerificationBuilderOptionsWithNetwork {
export type VerificationBuilderOptions = VerificationBuilderOptionsWithProvider | VerificationBuilderOptionsWithNetwork;

export interface VerifierOptions {
provider: providers.Provider;
provider: providers.Provider | providers.Provider[];
resolver?: Resolver;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { getData, utils, v2, v3, WrappedDocument } from "@govtechsg/open-attestation";
import { DocumentStore__factory } from "@govtechsg/document-store-ethers-v5";
import { providers } from "ethers";
import { errors, providers } from "ethers";
import { VerificationFragmentType, Verifier, VerifierOptions } from "../../../types/core";
import { OpenAttestationEthereumDocumentStoreStatusCode, Reason } from "../../../types/error";
import { CodedError } from "../../../common/error";
Expand Down Expand Up @@ -46,51 +46,62 @@ export const isIssuedOnDocumentStore = async ({
merkleRoot: string;
targetHash: string;
proofs: string[];
provider: providers.Provider;
provider: providers.Provider | providers.Provider[];
}): Promise<DocumentStoreIssuanceStatus> => {
const documentStoreContract = DocumentStore__factory.connect(documentStore, provider);
const providers = Array.isArray(provider) ? provider : [provider];
const queryProviderIndex = Math.floor(Math.random() * providers.length);
const documentStoreContractQueryProviders = providers.map((p) => DocumentStore__factory.connect(documentStore, p));

try {
const isBatchable = await isBatchableDocumentStore(documentStoreContract);
let tries = 0;
for (;;) {
const documentStoreContract =
documentStoreContractQueryProviders[(queryProviderIndex + tries) % documentStoreContractQueryProviders.length];
try {
const isBatchable = await isBatchableDocumentStore(documentStoreContract);

let issued: boolean;
if (isBatchable) {
issued = await documentStoreContract["isIssued(bytes32,bytes32,bytes32[])"](merkleRoot, targetHash, proofs);
} else {
issued = await documentStoreContract["isIssued(bytes32)"](merkleRoot);
let issued: boolean;
if (isBatchable) {
issued = await documentStoreContract["isIssued(bytes32,bytes32,bytes32[])"](merkleRoot, targetHash, proofs);
} else {
issued = await documentStoreContract["isIssued(bytes32)"](merkleRoot);
}
return issued
? {
issued: true,
address: documentStore,
}
: {
issued: false,
address: documentStore,
reason: {
message: `Document ${merkleRoot} has not been issued under contract ${documentStore}`,
code: OpenAttestationEthereumDocumentStoreStatusCode.DOCUMENT_NOT_ISSUED,
codeString:
OpenAttestationEthereumDocumentStoreStatusCode[
OpenAttestationEthereumDocumentStoreStatusCode.DOCUMENT_NOT_ISSUED
],
},
};
} catch (error: any) {
if (error.code === errors.NETWORK_ERROR && tries < 3) {
tries++;
continue;
}
// If error can be decoded and it's because of document is not issued, we return false
// Else allow error to continue to bubble up
return {
issued: false,
address: documentStore,
reason: {
message: decodeError(error),
code: OpenAttestationEthereumDocumentStoreStatusCode.DOCUMENT_NOT_ISSUED,
codeString:
OpenAttestationEthereumDocumentStoreStatusCode[
OpenAttestationEthereumDocumentStoreStatusCode.DOCUMENT_NOT_ISSUED
],
},
};
}
return issued
? {
issued: true,
address: documentStore,
}
: {
issued: false,
address: documentStore,
reason: {
message: `Document ${merkleRoot} has not been issued under contract ${documentStore}`,
code: OpenAttestationEthereumDocumentStoreStatusCode.DOCUMENT_NOT_ISSUED,
codeString:
OpenAttestationEthereumDocumentStoreStatusCode[
OpenAttestationEthereumDocumentStoreStatusCode.DOCUMENT_NOT_ISSUED
],
},
};
} catch (error) {
// If error can be decoded and it's because of document is not issued, we return false
// Else allow error to continue to bubble up
return {
issued: false,
address: documentStore,
reason: {
message: decodeError(error),
code: OpenAttestationEthereumDocumentStoreStatusCode.DOCUMENT_NOT_ISSUED,
codeString:
OpenAttestationEthereumDocumentStoreStatusCode[
OpenAttestationEthereumDocumentStoreStatusCode.DOCUMENT_NOT_ISSUED
],
},
};
}
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,11 @@ const verify: VerifierType["verify"] = async (document, options) => {
);
const tokenRegistry = getTokenRegistry(document);
const merkleRoot = getMerkleRoot(document);
const mintStatus = await isTokenMintedOnRegistry({ tokenRegistry, merkleRoot, provider: options.provider });
const mintStatus = await isTokenMintedOnRegistry({
tokenRegistry,
merkleRoot,
provider: Array.isArray(options.provider) ? options.provider[0] : options.provider,
});

if (ValidTokenRegistryStatus.guard(mintStatus)) {
const fragment = {
Expand Down
106 changes: 59 additions & 47 deletions src/verifiers/documentStatus/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,57 +77,69 @@ export const isRevokedOnDocumentStore = async ({
}: {
documentStore: string;
merkleRoot: string;
provider: providers.Provider;
provider: providers.Provider | providers.Provider[];
targetHash: Hash;
proofs: Hash[];
}): Promise<RevocationStatus> => {
try {
const documentStoreContract = DocumentStore__factory.connect(documentStore, provider);
const isBatchable = await isBatchableDocumentStore(documentStoreContract);
let revoked: boolean;
if (isBatchable) {
revoked = (await documentStoreContract["isRevoked(bytes32,bytes32,bytes32[])"](
merkleRoot,
targetHash,
proofs
)) as boolean;
} else {
const intermediateHashes = getIntermediateHashes(targetHash, proofs);
revoked = await isAnyHashRevoked(documentStoreContract, intermediateHashes);
}
const providers = Array.isArray(provider) ? provider : [provider];
const queryProviderIndex = Math.floor(Math.random() * providers.length);
const documentStoreContractQueryProviders = providers.map((p) => DocumentStore__factory.connect(documentStore, p));

let tries = 0;
for (;;) {
const documentStoreContract =
documentStoreContractQueryProviders[(queryProviderIndex + tries) % documentStoreContractQueryProviders.length];
try {
const isBatchable = await isBatchableDocumentStore(documentStoreContract);
let revoked: boolean;
if (isBatchable) {
revoked = (await documentStoreContract["isRevoked(bytes32,bytes32,bytes32[])"](
merkleRoot,
targetHash,
proofs
)) as boolean;
} else {
const intermediateHashes = getIntermediateHashes(targetHash, proofs);
revoked = await isAnyHashRevoked(documentStoreContract, intermediateHashes);
}

return revoked
? {
revoked: true,
address: documentStore,
reason: {
message: `Document ${merkleRoot} has been revoked under contract ${documentStore}`,
code: OpenAttestationEthereumDocumentStoreStatusCode.DOCUMENT_REVOKED,
codeString:
OpenAttestationEthereumDocumentStoreStatusCode[
OpenAttestationEthereumDocumentStoreStatusCode.DOCUMENT_REVOKED
],
},
}
: {
revoked: false,
address: documentStore,
};
} catch (error) {
// If error can be decoded and it's because of document is not revoked, we return false
// Else allow error to continue to bubble up
return {
revoked: true,
address: documentStore,
reason: {
message: decodeError(error),
code: OpenAttestationEthereumDocumentStoreStatusCode.DOCUMENT_REVOKED,
codeString:
OpenAttestationEthereumDocumentStoreStatusCode[
OpenAttestationEthereumDocumentStoreStatusCode.DOCUMENT_REVOKED
],
},
};
return revoked
? {
revoked: true,
address: documentStore,
reason: {
message: `Document ${merkleRoot} has been revoked under contract ${documentStore}`,
code: OpenAttestationEthereumDocumentStoreStatusCode.DOCUMENT_REVOKED,
codeString:
OpenAttestationEthereumDocumentStoreStatusCode[
OpenAttestationEthereumDocumentStoreStatusCode.DOCUMENT_REVOKED
],
},
}
: {
revoked: false,
address: documentStore,
};
} catch (error: any) {
if (error.code === errors.NETWORK_ERROR && tries < 3) {
tries++;
continue;
}
// If error can be decoded and it's because of document is not revoked, we return false
// Else allow error to continue to bubble up
return {
revoked: true,
address: documentStore,
reason: {
message: decodeError(error),
code: OpenAttestationEthereumDocumentStoreStatusCode.DOCUMENT_REVOKED,
codeString:
OpenAttestationEthereumDocumentStoreStatusCode[
OpenAttestationEthereumDocumentStoreStatusCode.DOCUMENT_REVOKED
],
},
};
}
}
};

Expand Down
3 changes: 2 additions & 1 deletion src/verifiers/issuerIdentity/dnsTxt/openAttestationDnsTxt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ const resolveIssuerIdentity = async (
smartContractAddress: string,
options: VerifierOptions
): Promise<DnsTxtVerificationStatus> => {
const network = await options.provider.getNetwork();
const provider = Array.isArray(options.provider) ? options.provider[0] : options.provider;
const network = await provider.getNetwork();
const records = await getDocumentStoreRecords(location);
const matchingRecord = records.find(
(record) =>
Expand Down

0 comments on commit 373bc0f

Please sign in to comment.