Skip to content

Commit

Permalink
merge main
Browse files Browse the repository at this point in the history
  • Loading branch information
calebtuttle committed Jul 1, 2024
2 parents 3b834f3 + ebc959f commit b881892
Show file tree
Hide file tree
Showing 8 changed files with 11,325 additions and 12,931 deletions.
23,952 changes: 11,025 additions & 12,927 deletions package-lock.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"@holonym-foundation/utils": "^1.0.1",
"@sendgrid/mail": "^7.7.0",
"@squirrel-labs/peanut-sdk": "^0.3.34",
"@verax-attestation-registry/verax-sdk": "^1.8.1",
"axios": "^0.27.2",
"big-integer": "^1.6.51",
"circomlibjs-old": "npm:circomlibjs@^0.0.8",
Expand Down
34 changes: 34 additions & 0 deletions src/constants/abi/HubV3ABI.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
export default [
"constructor(address)",
"event Approval(address indexed,address indexed,uint256 indexed)",
"event ApprovalForAll(address indexed,address indexed,bool)",
"event OwnershipTransferred(address indexed,address indexed)",
"event Transfer(address indexed,address indexed,uint256 indexed)",
"function approve(address,uint256)",
"function balanceOf(address) view returns (uint256)",
"function changeVerifier(address)",
"function collectFees()",
"function fees(bytes32) view returns (uint256)",
"function getApproved(uint256) view returns (address)",
"function getIdentifier(address,bytes32) pure returns (bytes32)",
"function getSBT(address,bytes32) view returns (tuple(uint256,uint256[],bool))",
"function isApprovedForAll(address,address) view returns (bool)",
"function name() view returns (string)",
"function owner() view returns (address)",
"function ownerOf(uint256) view returns (address)",
"function renounceOwnership()",
"function revokeSBT(address,bytes32)",
"function safeTransferFrom(address,address,uint256)",
"function safeTransferFrom(address,address,uint256,bytes)",
"function sbtOwners(bytes32) view returns (uint256, bool)",
"function setSBT(bytes32,uint256,uint256,uint256,uint256,uint256[],bytes) payable",
"function setApprovalForAll(address,bool)",
"function setFee(bytes32,uint256)",
"function supportsInterface(bytes4) view returns (bool)",
"function symbol() view returns (string)",
"function tokenURI(uint256) view returns (string)",
"function transferFrom(address,address,uint256)",
"function transferOwnership(address)",
"function usedNullifiers(uint256) view returns (bool)",
"function getSBTByNullifier(uint256 nullifier) view returns (tuple(uint256 expiry, uint256[] publicValues, bool revoked) sbt)",
];
5 changes: 5 additions & 0 deletions src/constants/contractAddresses.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,9 @@ export default {
"optimism-goerli": "0xBA8a4C5c1f36Dc802d51FEEfF3aa4ef97Dae4B10",
},
},
HubV3: {
mainnet: {
optimism: "0x2AA822e264F8cc31A2b9C22f39e5551241e94DfB",
}
}
};
18 changes: 18 additions & 0 deletions src/constants/misc.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,3 +86,21 @@ export const payPalApiUrlBase =
export const idvSessionUSDPrice = 10.0;

export const amlSessionUSDPrice = 2.5;

export const defaultActionId = 123456789;

export const kycIssuerAddress =
"0x03fae82f38bf01d9799d57fdda64fad4ac44e4c2c2f16c5bf8e1873d0a3e1993";
export const phoneIssuerAddress =
"0x40b8810cbaed9647b54d18cc98b720e1e8876be5d8e7089d3c079fc61c30a4";
// export const phoneIssuerAddress =
// process.env.NODE_ENV === "production"
// ? "0x40b8810cbaed9647b54d18cc98b720e1e8876be5d8e7089d3c079fc61c30a4"
// : "0x2998cab3d07a64315f1e8399ecef60a19f478231663f8740703bd30a42a91ed4";

export const v3KYCSybilResistanceCircuitId =
"0x729d660e1c02e4e419745e617d643f897a538673ccf1051e093bbfa58b0a120b";
export const v3PhoneSybilResistanceCircuitId =
"0xbce052cf723dca06a21bd3cf838bc518931730fb3db7859fc9cc86f0d5483495";
export const v3EPassportSybilResistanceCircuitId =
"0xf2ce248b529343e105f7b3c16459da619281c5f81cf716d28f7df9f87667364d";
4 changes: 2 additions & 2 deletions src/init.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ const { Schema } = mongoose;
if (process.env.ENVIRONMENT == "dev") mongoose.set("debug", true);

function validateEnv() {
assert.ok(process.env.PRIVATE_KEY, "PRIVATE_KEY environment variable is not set");
assert.ok(process.env.ADDRESS, "ADDRESS environment variable is not set");
// assert.ok(process.env.PRIVATE_KEY, "PRIVATE_KEY environment variable is not set");
// assert.ok(process.env.ADDRESS, "ADDRESS environment variable is not set");

assert.ok(
process.env.HOLONYM_ISSUER_PRIVKEY,
Expand Down
6 changes: 4 additions & 2 deletions src/routes/admin.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ import { setSessionIdvProvider } from "../services/admin/set-session-idv-provide
import { userSessions } from "../services/admin/user-sessions.js";
import { failSession } from "../services/admin/fail-session.js";
import { refundUnusedTransaction } from "../services/admin/refund-unused-transaction.js";
import { refundFailedSession } from "../services/admin/refund-failed-session.js"
import { refundFailedSession } from "../services/admin/refund-failed-session.js";
import { issueVeraxAttestation } from "../services/admin/issue-verax-attestation.js";

const router = express.Router();

Expand All @@ -21,6 +22,7 @@ router.post("/set-session-idv-provider", setSessionIdvProvider);
router.post("/user-sessions", userSessions);
router.post("/fail-session", failSession);
router.post("/refund-unused-transaction", refundUnusedTransaction);
router.post("/refund-failed-session", refundFailedSession)
router.post("/refund-failed-session", refundFailedSession);
router.post("/issue-verax-attestation", issueVeraxAttestation);

export default router;
236 changes: 236 additions & 0 deletions src/services/admin/issue-verax-attestation.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,236 @@
import { ethers } from "ethers";
import { VeraxSdk } from "@verax-attestation-registry/verax-sdk";
import {
optimismProvider,
defaultActionId,
kycIssuerAddress,
phoneIssuerAddress,
v3KYCSybilResistanceCircuitId,
v3PhoneSybilResistanceCircuitId,
// v3EPassportSybilResistanceCircuitId
} from "../../constants/misc.js";
import HubV3ABI from "../../constants/abi/HubV3ABI.js";
import contractAddresses from "../../constants/contractAddresses.js";
import logger from "../../utils/logger.js";

const postEndpointLogger = logger.child({
msgPrefix: "[POST /admin/issue-verax-attestation] ",
});

const veraxSdk = new VeraxSdk(
VeraxSdk.DEFAULT_LINEA_MAINNET,
// address,
'0xB1f50c6C34C72346b1229e5C80587D0D659556Fd',
// privateKey
process.env.RELAYER_PRIVATE_KEY
);

const hubV3Address = contractAddresses.HubV3.mainnet.optimism;

/**
* @param subject Should be user's address
*/
function getAttestations(subject) {
return veraxSdk.attestation.findBy(
undefined,
undefined,
{
// Holonym's schemaId
schemaId: "0x1c14fd320660a59a50eb1f795116193a59c26f2463c0705b79d8cb97aa9f419b",
// Our relayer/attester
attester: "0xB1f50c6C34C72346b1229e5C80587D0D659556Fd",
// Our address of interest
subject,
}
);
}

function validateKYCAttestation(attestation) {
const { circuitId, publicValues, revoked } = attestation.decodedPayload[0]

const actionId = publicValues[2].toString();
const issuer = publicValues[4];

// Make sure circuitId matches KYC circuit ID
if (circuitId != v3KYCSybilResistanceCircuitId) {
return {
error: "Invalid circuit ID"
}
}

// Validate action ID
if (actionId != defaultActionId.toString()) {
return {
error: "Invalid action ID"
}
}

// Make sure issuer is the KYC Holonym issuer
if (issuer != BigInt(kycIssuerAddress)) {
return {
error: "Invalid KYC issuer"
}
}
}

function validatePhoneAttestation(attestation) {
const { circuitId, publicValues, revoked } = attestation.decodedPayload[0]

const actionId = publicValues[2].toString();
const issuer = publicValues[4];

// Make sure circuitId matches KYC circuit ID
if (circuitId != v3PhoneSybilResistanceCircuitId) {
return {
error: "Invalid circuit ID"
}
}

// Validate action ID
if (actionId != defaultActionId.toString()) {
return {
error: "Invalid action ID"
}
}

// Make sure issuer is the KYC Holonym issuer
if (issuer != BigInt(phoneIssuerAddress)) {
return {
error: "Invalid KYC issuer"
}
}
}

async function getAndValidateV3SBT(address, circuitId, issuerAddress) {
const hubV3Contract = new ethers.Contract(hubV3Address, HubV3ABI, optimismProvider);

// Check v3 contract for KYC SBT
const sbt = await hubV3Contract.getSBT(address, circuitId);

const publicValues = sbt[1];
const actionIdInSBT = publicValues[2].toString();
const issuerAddressInSBT = publicValues[4].toHexString();

const actionIdIsValid = defaultActionId.toString() == actionIdInSBT;
const issuerIsValid = issuerAddress == issuerAddressInSBT;

if (actionIdIsValid && issuerIsValid) {
return sbt;
}
}

function getAndValidateV3KYCSBT(address) {
return getAndValidateV3SBT(address, v3KYCSybilResistanceCircuitId, kycIssuerAddress);
}

function getAndValidateV3PhoneSBT(address) {
return getAndValidateV3SBT(address, v3PhoneSybilResistanceCircuitId, phoneIssuerAddress);
}

function getSBT(address, attestationType) {
if (attestationType == 'kyc') {
return getAndValidateV3KYCSBT(address);
}

if (attestationType == 'phone') {
return getAndValidateV3PhoneSBT(address);
}
}

async function attest(subject, circuitId, publicValues, revoked) {
const portalAddr = "0x5631Aecf3283922b6bf36D7485Eb460f244bfac1"
const schemaId = "0x1c14fd320660a59a50eb1f795116193a59c26f2463c0705b79d8cb97aa9f419b"
const expiry = Math.floor(BigInt(publicValues[0]).toString());
await veraxSdk.portal.attest(
portalAddr,
{
schemaId,
expirationDate: expiry,
subject,
// (string circuitId, uint256[] publicValues, bool revoked)
attestationData: [{
circuitId: circuitId,
publicValues: publicValues,
revoked: revoked
}],
},
[]
)
}

/**
* ENDPOINT.
*/
async function issueVeraxAttestation(req, res) {
try {
const apiKey = req.headers["x-api-key"];

if (apiKey !== process.env.ADMIN_API_KEY_LOW_PRIVILEGE) {
return res.status(401).json({ error: "Invalid API key." });
}

const address = req.body.address;
const attestationType = req.body.attestationType;

if (['kyc', 'phone'].indexOf(attestationType) === -1) {
return res.status(400).json({ error: "Invalid attestation type" });
}

// ---------- Make sure user doesn't already have this attestation ----------
const attestations = await getAttestations(address);
const kycAttestation = attestations.filter(
(attestation) => attestation.decodedPayload[0].circuitId == v3KYCSybilResistanceCircuitId
)[0];
const phoneAttestation = attestations.filter(
(attestation) => attestation.decodedPayload[0].circuitId == v3PhoneSybilResistanceCircuitId
)[0];

if (attestationType == 'kyc' && kycAttestation) {
const validationResult = validateKYCAttestation(kycAttestation)
if (!validationResult?.error) {
return res.status(400).json({ error: "User already has a KYC attestation" });
}
}

if (attestationType == 'phone' && phoneAttestation) {
const validationResult = validatePhoneAttestation(phoneAttestation)
if (!validationResult?.error) {
return res.status(400).json({ error: "User already has a phone attestation" });
}
}

// ---------- Make sure user has the required SBT ----------
try {
const sbt = await getSBT(address, attestationType);

if (!sbt) {
return res.status(400).json({ error: "User does not have the required SBT" });
}
} catch (err) {
if ((err.errorArgs?.[0] ?? "").includes("SBT is expired")) {
return res.status(400).json({ error: "User's SBT is expired" });
}
}

// ---------- Issue attestation ----------
const publicValues = sbt[1];
let circuitId = null;
if (attestationType == 'kyc') {
circuitId = v3KYCSybilResistanceCircuitId;
} else if (attestationType == 'phone') {
circuitId = v3PhoneSybilResistanceCircuitId;
}
const revoked = sbt[2];
await attest(address, circuitId, publicValues, revoked);

return res.status(200).json({
message: `Successfully issued ${attestationType} attestation to ${address}`,
});
} catch (err) {
console.log(err)
postEndpointLogger.error({ error: err });
return res.status(500).json({ error: "An unknown error occurred" });
}
}

export { issueVeraxAttestation };

0 comments on commit b881892

Please sign in to comment.