Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(iam): test run for generating nullifiers with mishti #3160

Draft
wants to merge 8 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 13 additions & 2 deletions iam/__tests__/additional_signer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,22 @@
import request from "supertest";

// ---- Test subject
import { app } from "../src/index";
import { getEip712Issuer } from "../src/issuers";
import { app } from "../src/index.js";
import { getEip712Issuer } from "../src/issuers.js";

const issuer = getEip712Issuer();

jest.mock("../src/utils/oprf", () => ({
recordToNullifier: async ({ record }: any) => {
const crypto = await import("crypto");
const hash = crypto.createHash("sha256");

hash.update(JSON.stringify(record));

return hash.digest("hex");
},
}));

jest.mock("../src/utils/bans", () => ({
checkCredentialBans: jest.fn().mockImplementation((input) => Promise.resolve(input)),
}));
Expand Down
2 changes: 1 addition & 1 deletion iam/__tests__/autoVerification.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Request, Response } from "express";
import { isAddress } from "ethers";
import axios from "axios";
import { autoVerificationHandler } from "../src/utils/autoVerification";
import { autoVerificationHandler } from "../src/utils/autoVerification.js";
import { ApiError } from "../src/utils/helpers.js";
import { checkConditionsAndIssueCredentials } from "../src/utils/credentials.js";
import { VerifiableCredential } from "@gitcoin/passport-types";
Expand Down
2 changes: 1 addition & 1 deletion iam/__tests__/challenge.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { RequestPayload } from "@gitcoin/passport-types";
import { getChallenge } from "../src/utils/challenge";
import { getChallenge } from "../src/utils/challenge.js";

describe("getChallenge", () => {
it("returns a challenge for SignerChallenge", () => {
Expand Down
4 changes: 2 additions & 2 deletions iam/__tests__/credential_issuance.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import axios from "axios";
import { checkCredentialBans } from "../src/utils/bans";
import { checkCredentialBans } from "../src/utils/bans.js";
import { ErrorResponseBody } from "@gitcoin/passport-types";
import { ApiError } from "../src/utils/helpers";
import { ApiError } from "../src/utils/helpers.js";

jest.mock("axios");
const mockedAxios = axios as jest.Mocked<typeof axios>;
Expand Down
2 changes: 1 addition & 1 deletion iam/__tests__/easFees.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { getEASFeeAmount } from "../src/utils/easFees";
import { getEASFeeAmount } from "../src/utils/easFees.js";
import { parseEther } from "ethers";
import Moralis from "moralis";
import { PassportCache } from "@gitcoin/passport-platforms";
Expand Down
4 changes: 2 additions & 2 deletions iam/__tests__/easPassportSchema.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as easPassportModule from "../src/utils/easPassportSchema";
import * as easStampModule from "../src/utils/easStampSchema";
import * as easPassportModule from "../src/utils/easPassportSchema.js";
import * as easStampModule from "../src/utils/easStampSchema.js";
import onchainInfo from "../../deployments/onchainInfo.json";

import { VerifiableCredential } from "@gitcoin/passport-types";
Expand Down
2 changes: 1 addition & 1 deletion iam/__tests__/easStampSchema.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import * as easStampModule from "../src/utils/easStampSchema";
import * as easStampModule from "../src/utils/easStampSchema.js";
import { VerifiableCredential } from "@gitcoin/passport-types";
import { NO_EXPIRATION, ZERO_BYTES32 } from "@ethereum-attestation-service/eas-sdk";
import { SchemaEncoder } from "@ethereum-attestation-service/eas-sdk";
Expand Down
36 changes: 26 additions & 10 deletions iam/__tests__/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import * as DIDKit from "@spruceid/didkit-wasm-node";
import { PassportCache, providers } from "@gitcoin/passport-platforms";

// ---- Test subject
import { app } from "../src/index";
import { getAttestationDomainSeparator } from "../src/utils/attestations";
import { app } from "../src/index.js";
import { getAttestationDomainSeparator } from "../src/utils/attestations.js";

// ---- Types
import {
Expand All @@ -21,17 +21,28 @@ import {
import { MultiAttestationRequest, ZERO_BYTES32, NO_EXPIRATION } from "@ethereum-attestation-service/eas-sdk";

import { parseEther } from "ethers";
import * as easFeesMock from "../src/utils/easFees";
import * as easFeesMock from "../src/utils/easFees.js";
import * as identityMock from "@gitcoin/passport-identity";
import * as easSchemaMock from "../src/utils/easStampSchema";
import * as easPassportSchemaMock from "../src/utils/easPassportSchema";
import { IAMError } from "../src/utils/scorerService";
import { VerifyDidChallengeBaseError, verifyDidChallenge } from "../src/utils/verifyDidChallenge";
import { getEip712Issuer } from "../src/issuers";
import { toJsonObject } from "../src/utils/json";
import * as easSchemaMock from "../src/utils/easStampSchema.js";
import * as easPassportSchemaMock from "../src/utils/easPassportSchema.js";
import { IAMError } from "../src/utils/scorerService.js";
import { VerifyDidChallengeBaseError, verifyDidChallenge } from "../src/utils/verifyDidChallenge.js";
import { getEip712Issuer } from "../src/issuers.js";
import { toJsonObject } from "../src/utils/json.js";

const issuer = getEip712Issuer();

jest.mock("../src/utils/oprf", () => ({
recordToNullifier: async ({ record }: any) => {
const crypto = await import("crypto");
const hash = crypto.createHash("sha256");

hash.update(JSON.stringify(record));

return hash.digest("hex");
},
}));

jest.mock("../src/utils/bans", () => ({
checkCredentialBans: jest.fn().mockImplementation((input) => Promise.resolve(input)),
}));
Expand Down Expand Up @@ -831,12 +842,17 @@ describe("POST /verify", function () {
jest.spyOn(identityMock, "verifyCredential").mockResolvedValue(true);

// create a req against the express app
await request(app)
const response = await request(app)
.post("/api/v0.0.0/verify")
.send({ challenge, payload })
.set("Accept", "application/json")
.expect(200)
.expect("Content-Type", /json/);

response.body.forEach((item: any) => {
expect(item).toHaveProperty("record");
expect(item.record).toMatchObject({ type: "Simple" });
});
});
it("should not issue credential for additional signer when invalid address is provided", async () => {
(identityMock.verifyCredential as jest.Mock).mockResolvedValueOnce(true);
Expand Down
10 changes: 5 additions & 5 deletions iam/__tests__/index_eas_score.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@ import request from "supertest";
import { PassportCache } from "@gitcoin/passport-platforms";

// ---- Test subject
import { app } from "../src/index";
import { app } from "../src/index.js";

import { MultiAttestationRequest, ZERO_BYTES32, NO_EXPIRATION } from "@ethereum-attestation-service/eas-sdk";

import * as identityMock from "@gitcoin/passport-identity";
import * as easSchemaMock from "../src/utils/easStampSchema";
import * as easPassportSchemaMock from "../src/utils/easPassportSchema";
import { IAMError } from "../src/utils/scorerService";
import { serializeJson, toJsonObject } from "../src/utils/json";
import * as easSchemaMock from "../src/utils/easStampSchema.js";
import * as easPassportSchemaMock from "../src/utils/easPassportSchema.js";
import { IAMError } from "../src/utils/scorerService.js";
import { serializeJson, toJsonObject } from "../src/utils/json.js";

jest.mock("@gitcoin/passport-identity", () => ({
...jest.requireActual("@gitcoin/passport-identity"),
Expand Down
4 changes: 2 additions & 2 deletions iam/__tests__/scorerService.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { fetchPassportScore } from "../src/utils/scorerService";
import { fetchPassportScore } from "../src/utils/scorerService.js";

// Import the entire module to help with typing
import * as scorerService from "../src/utils/scorerService";
import * as scorerService from "../src/utils/scorerService.js";

// Type for the mocked function (adjust as needed based on the actual implementation)
type MockedFunction = jest.MockedFunction<typeof scorerService.fetchPassportScore>;
Expand Down
8 changes: 4 additions & 4 deletions iam/__tests__/scrollDevBadge.test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { Request, Response } from "express";
import { Signature, Contract } from "ethers";
import { scrollDevBadgeHandler, getScrollRpcUrl } from "../src/utils/scrollDevBadge";
import { getAttestationSignerForChain } from "../src/utils/attestations";
import { hasValidIssuer } from "../src/issuers";
import { getEASFeeAmount } from "../src/utils/easFees";
import { scrollDevBadgeHandler, getScrollRpcUrl } from "../src/utils/scrollDevBadge.js";
import { getAttestationSignerForChain } from "../src/utils/attestations.js";
import { hasValidIssuer } from "../src/issuers.js";
import { getEASFeeAmount } from "../src/utils/easFees.js";

// Mock external dependencies
jest.mock("@spruceid/didkit-wasm-node");
Expand Down
3 changes: 3 additions & 0 deletions iam/jest.setup.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,6 @@ process.env.SCROLL_BADGE_PROVIDER_INFO =
'{"DeveloperList#PassportCommiterLevel1#6a51c84c":{"contractAddress":"0x71A848A38fFCcA5c7A431F2BB411Ab632Fa0c456","level":1}}';
process.env.SCROLL_BADGE_ATTESTATION_SCHEMA_UID =
"0xa35b5470ebb301aa5d309a8ee6ea258cad680ea112c86e456d5f2254448afc74";
process.env.MISHTI_CLIENT_PRIVATE_KEY =
"0x04d16281ff3bf268b29cdd684183f72542757d24ae9fdfb863e7c755e599163a";
process.env.MISHTI_RELAY_URL = "http://127.0.0.1:8081";
6 changes: 3 additions & 3 deletions iam/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,17 +31,17 @@
"@gitcoin/passport-identity": "^1.0.0",
"@gitcoin/passport-platforms": "^1.0.0",
"@gitcoin/passport-types": "^1.0.0",
"@holonym-foundation/mishtiwasm": "^0.3.0-robustnet",
"@ipld/dag-cbor": "^7.0.3",
"@spruceid/didkit-wasm-node": "^0.2.1",
"multiformats": "^13.0.1",
"axios": "^0.27.2",
"axios": "^1.7.9",
"brightid_sdk": "^1.0.1",
"cors": "^2.8.5",
"dids": "^5.0.2",
"dotenv": "^16.0.0",
"ethers": "^6.13.4",
"express": "4",
"google-auth-library": "^7.14.1",
"@ipld/dag-cbor": "^7.0.3",
"luxon": "^2.4.0",
"moralis": "^2.24.2",
"multiformats": "^9.9.0",
Expand Down
72 changes: 20 additions & 52 deletions iam/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,58 +50,25 @@

// ---- Config - check for all required env variables
// We want to prevent the app from starting with default values or if it is misconfigured
const configErrors = [];

if (!process.env.IAM_JWK) {
configErrors.push("IAM_JWK is required");
}

if (!process.env.ATTESTATION_SIGNER_PRIVATE_KEY) {
configErrors.push("ATTESTATION_SIGNER_PRIVATE_KEY is required");
}

if (!process.env.TESTNET_ATTESTATION_SIGNER_PRIVATE_KEY) {
configErrors.push("TESTNET_ATTESTATION_SIGNER_PRIVATE_KEY is required");
}

if (!process.env.ALLO_SCORER_ID) {
configErrors.push("ALLO_SCORER_ID is required");
}

if (!process.env.SCORER_ENDPOINT) {
configErrors.push("SCORER_ENDPOINT is required");
}

if (!process.env.SCORER_API_KEY) {
configErrors.push("SCORER_API_KEY is required");
}

if (!process.env.EAS_GITCOIN_STAMP_SCHEMA) {
configErrors.push("EAS_GITCOIN_STAMP_SCHEMA is required");
}

if (!process.env.MORALIS_API_KEY) {
configErrors.push("MORALIS_API_KEY is required");
}

if (!process.env.IAM_JWK_EIP712) {
configErrors.push("IAM_JWK_EIP712 is required");
}

if (!process.env.EAS_FEE_USD) {
configErrors.push("EAS_FEE_USD is required");
}

if (!process.env.SCROLL_BADGE_PROVIDER_INFO) {
configErrors.push("SCROLL_BADGE_PROVIDER_INFO is required");
}

if (!process.env.SCROLL_BADGE_ATTESTATION_SCHEMA_UID) {
configErrors.push("SCROLL_BADGE_ATTESTATION_SCHEMA_UID is required");
}

if (configErrors.length > 0) {
configErrors.forEach((error) => console.error(error)); // eslint-disable-line no-console
const missingEnvVars = [
"IAM_JWK",
"ATTESTATION_SIGNER_PRIVATE_KEY",
"TESTNET_ATTESTATION_SIGNER_PRIVATE_KEY",
"ALLO_SCORER_ID",
"SCORER_ENDPOINT",
"SCORER_API_KEY",
"EAS_GITCOIN_STAMP_SCHEMA",
"MORALIS_API_KEY",
"IAM_JWK_EIP712",
"EAS_FEE_USD",
"SCROLL_BADGE_PROVIDER_INFO",
"SCROLL_BADGE_ATTESTATION_SCHEMA_UID",
"MISHTI_CLIENT_PRIVATE_KEY",
"MISHTI_RELAY_URL",
].filter((env) => !process.env[env]);

if (missingEnvVars.length > 0) {
missingEnvVars.forEach((envVar) => console.error(`${envVar} is required`));

Check warning on line 71 in iam/src/index.ts

View workflow job for this annotation

GitHub Actions / Build and Test

Unexpected console statement

Check warning on line 71 in iam/src/index.ts

View workflow job for this annotation

GitHub Actions / Build and Test

Unexpected console statement

Check warning on line 71 in iam/src/index.ts

View workflow job for this annotation

GitHub Actions / Check Provider Bitmaps

Unexpected console statement
throw new Error("Missing required configuration");
}

Expand Down Expand Up @@ -254,6 +221,7 @@
return void errorRes(res, "Unable to verify payload", 401);
})
.catch((error) => {
console.log("ERROR", error);

Check warning on line 224 in iam/src/index.ts

View workflow job for this annotation

GitHub Actions / Build and Test

Unexpected console statement

Check warning on line 224 in iam/src/index.ts

View workflow job for this annotation

GitHub Actions / Build and Test

Unexpected console statement

Check warning on line 224 in iam/src/index.ts

View workflow job for this annotation

GitHub Actions / Check Provider Bitmaps

Unexpected console statement
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TODO remove

if (error instanceof ApiError) {
return void errorRes(res, error.message, error.code);
}
Expand Down Expand Up @@ -348,7 +316,7 @@
}
});

app.post("/api/v0.0.0/scroll/dev", scrollDevBadgeHandler);

Check warning on line 319 in iam/src/index.ts

View workflow job for this annotation

GitHub Actions / Build and Test

Promise returned in function argument where a void return was expected

Check warning on line 319 in iam/src/index.ts

View workflow job for this annotation

GitHub Actions / Build and Test

Promise returned in function argument where a void return was expected

Check warning on line 319 in iam/src/index.ts

View workflow job for this annotation

GitHub Actions / Check Provider Bitmaps

Promise returned in function argument where a void return was expected

// Expose entry point for getting eas payload for moving stamps on-chain (Passport Attestations)
// This function will receive an array of stamps, validate them and return an array of eas payloads
Expand Down Expand Up @@ -439,7 +407,7 @@
});

// Expose entry point for getting eas payload for moving only the score on-chain (Score Attestations)
app.post("/api/v0.0.0/eas/score", async (req: Request, res: Response) => {

Check warning on line 410 in iam/src/index.ts

View workflow job for this annotation

GitHub Actions / Build and Test

Promise returned in function argument where a void return was expected

Check warning on line 410 in iam/src/index.ts

View workflow job for this annotation

GitHub Actions / Build and Test

Promise returned in function argument where a void return was expected

Check warning on line 410 in iam/src/index.ts

View workflow job for this annotation

GitHub Actions / Check Provider Bitmaps

Promise returned in function argument where a void return was expected
try {
const { recipient, nonce, chainIdHex, customScorerId } = req.body as EasRequestBody;
if (!Object.keys(onchainInfo).includes(chainIdHex)) {
Expand Down Expand Up @@ -494,7 +462,7 @@
}
});

app.post("/api/v0.0.0/auto-verification", autoVerificationHandler);

Check warning on line 465 in iam/src/index.ts

View workflow job for this annotation

GitHub Actions / Build and Test

Promise returned in function argument where a void return was expected

Check warning on line 465 in iam/src/index.ts

View workflow job for this annotation

GitHub Actions / Build and Test

Promise returned in function argument where a void return was expected

Check warning on line 465 in iam/src/index.ts

View workflow job for this annotation

GitHub Actions / Check Provider Bitmaps

Promise returned in function argument where a void return was expected

// procedure endpoints
app.use("/procedure", procedureRouter);
Expand Down
42 changes: 42 additions & 0 deletions iam/src/missingTypes.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
declare module "ceramic-cacao" {
export declare type Cacao = {
h: Header;
p: Payload;
s?: Signature;
};

export declare namespace Cacao {
function fromBlockBytes(bytes: Uint8Array): Promise<Cacao>;
}
}

declare module "multiformats/cid" {
export class CID {
static decode(bytes: Uint8Array): CID;
toString(): string;
}
}

declare module "multiformats/block" {
export class CID {
static decode(bytes: Uint8Array): CID;
toString(): string;
}

export class Block {
cid: CID;
}

export function encode<T, Code extends number, Algorithm_1 extends number>({

Check warning on line 30 in iam/src/missingTypes.d.ts

View workflow job for this annotation

GitHub Actions / Build and Test

'Algorithm_1' is defined but never used

Check warning on line 30 in iam/src/missingTypes.d.ts

View workflow job for this annotation

GitHub Actions / Build and Test

'Algorithm_1' is defined but never used

Check warning on line 30 in iam/src/missingTypes.d.ts

View workflow job for this annotation

GitHub Actions / Check Provider Bitmaps

'Algorithm_1' is defined but never used
value,
codec,
hasher,
}: {
value: T;
codec: import("./codecs/interface").BlockEncoder<Code, T>;
hasher: import("./hashes/interface").MultihashHasher<number>;
}): Promise<Block<T>>;
}

declare module "multiformats/hashes/sha2";
declare module "@ipld/dag-cbor";
2 changes: 1 addition & 1 deletion iam/src/scripts/buildProviderBitMapInfo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import { join } from "path";
import axios from "axios";

import { StampMetadata, mapBitMapInfo } from "../utils/easPassportSchema";
import { StampMetadata, mapBitMapInfo } from "../utils/easPassportSchema.js";

dotenv.config();

Expand All @@ -17,15 +17,15 @@
const bitMapInfo = mapBitMapInfo(stampMetadata.data);

const outPath = join(__dirname, "..", "static", "providerBitMapInfo.json");
console.log(`Saving platform info to JSON file at ${outPath}`);

Check warning on line 20 in iam/src/scripts/buildProviderBitMapInfo.ts

View workflow job for this annotation

GitHub Actions / Build and Test

Unexpected console statement

Check warning on line 20 in iam/src/scripts/buildProviderBitMapInfo.ts

View workflow job for this annotation

GitHub Actions / Build and Test

Unexpected console statement

Check warning on line 20 in iam/src/scripts/buildProviderBitMapInfo.ts

View workflow job for this annotation

GitHub Actions / Check Provider Bitmaps

Unexpected console statement

writeFileSync(outPath, JSON.stringify(bitMapInfo));
};

formatProviderBitMapInfo()
.catch((err) => {
console.error(err);

Check warning on line 27 in iam/src/scripts/buildProviderBitMapInfo.ts

View workflow job for this annotation

GitHub Actions / Build and Test

Unexpected console statement

Check warning on line 27 in iam/src/scripts/buildProviderBitMapInfo.ts

View workflow job for this annotation

GitHub Actions / Build and Test

Unexpected console statement

Check warning on line 27 in iam/src/scripts/buildProviderBitMapInfo.ts

View workflow job for this annotation

GitHub Actions / Check Provider Bitmaps

Unexpected console statement
})
.finally(() => {
console.log("Done! BitMap info saved");

Check warning on line 30 in iam/src/scripts/buildProviderBitMapInfo.ts

View workflow job for this annotation

GitHub Actions / Build and Test

Unexpected console statement

Check warning on line 30 in iam/src/scripts/buildProviderBitMapInfo.ts

View workflow job for this annotation

GitHub Actions / Build and Test

Unexpected console statement

Check warning on line 30 in iam/src/scripts/buildProviderBitMapInfo.ts

View workflow job for this annotation

GitHub Actions / Check Provider Bitmaps

Unexpected console statement
});
11 changes: 8 additions & 3 deletions iam/src/utils/credentials.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
ProviderContext,
VerifiedPayload,
VerifiableCredential,
ProofRecord,
} from "@gitcoin/passport-types";

import { getIssuerKey } from "../issuers.js";
Expand All @@ -20,6 +21,7 @@ import { issueHashedCredential, verifyCredential } from "@gitcoin/passport-ident
import { providers, platforms } from "@gitcoin/passport-platforms";
import { ApiError } from "./helpers.js";
import { checkCredentialBans } from "./bans.js";
import { recordToNullifier } from "./oprf.js";

const providerTypePlatformMap = Object.entries(platforms).reduce(
(acc, [platformName, { providers }]) => {
Expand Down Expand Up @@ -66,7 +68,8 @@ const issueCredentials = async (
results.map(async ({ verifyResult, code: verifyCode, error: verifyError, type }) => {
let code = verifyCode;
let error = verifyError;
let record, credential;
let record: ProofRecord | undefined;
let credential;

try {
// check if the request is valid against the selected Identity Provider
Expand All @@ -89,10 +92,12 @@ const issueCredentials = async (
address,
record,
verifyResult.expiresInSeconds,
payload.signatureType
payload.signatureType,
() => recordToNullifier({ record })
));
}
} catch {
} catch (e) {
console.error(e);
error = "Unable to produce a verifiable credential";
code = 500;
}
Expand Down
Loading
Loading