From b8ead7720b275d100db703a024be027778fca31f Mon Sep 17 00:00:00 2001 From: Nityam Tripathi Date: Tue, 17 Mar 2026 13:40:36 +0530 Subject: [PATCH 1/3] rework-add-db --- .../.env.example | 33 + .../.gitignore | 3 + .../README.md | 75 ++- .../ecosystem.config.js | 55 ++ .../js/.env.example | 19 - .../js/index.js | 254 ------- .../{js => }/package-lock.json | 517 +++++++++++++- .../{js => }/package.json | 19 +- .../{static => public}/index.html | 3 - .../{static => public}/index.js | 22 +- .../keys/authV2/verification_key.json | 0 .../verification_key.json | 0 .../verification_key.json | 0 .../verification_key.json | 0 .../verification_key.json | 0 .../{static => public}/styles.css | 0 .../{js => src}/.gitignore | 0 .../config/verification-configs.js | 0 .../src/index.js | 632 ++++++++++++++++++ .../src/services/db-service.js | 89 +++ .../src/services/logger.js | 90 +++ .../src/services/redis-service.js | 399 +++++++++++ 22 files changed, 1891 insertions(+), 319 deletions(-) create mode 100644 Integration-for-verifiers (Billions Wallet)/.env.example create mode 100644 Integration-for-verifiers (Billions Wallet)/.gitignore create mode 100644 Integration-for-verifiers (Billions Wallet)/ecosystem.config.js delete mode 100644 Integration-for-verifiers (Billions Wallet)/js/.env.example delete mode 100644 Integration-for-verifiers (Billions Wallet)/js/index.js rename Integration-for-verifiers (Billions Wallet)/{js => }/package-lock.json (89%) rename Integration-for-verifiers (Billions Wallet)/{js => }/package.json (53%) rename Integration-for-verifiers (Billions Wallet)/{static => public}/index.html (99%) rename Integration-for-verifiers (Billions Wallet)/{static => public}/index.js (99%) rename Integration-for-verifiers (Billions Wallet)/{ => public}/keys/authV2/verification_key.json (100%) rename Integration-for-verifiers (Billions Wallet)/{ => public}/keys/credentialAtomicQueryMTPV2/verification_key.json (100%) rename Integration-for-verifiers (Billions Wallet)/{ => public}/keys/credentialAtomicQuerySigV2/verification_key.json (100%) rename Integration-for-verifiers (Billions Wallet)/{ => public}/keys/credentialAtomicQueryV3-beta.1/verification_key.json (100%) rename Integration-for-verifiers (Billions Wallet)/{ => public}/keys/linkedMultiQuery10-beta.1/verification_key.json (100%) rename Integration-for-verifiers (Billions Wallet)/{static => public}/styles.css (100%) rename Integration-for-verifiers (Billions Wallet)/{js => src}/.gitignore (100%) rename Integration-for-verifiers (Billions Wallet)/{js => src}/config/verification-configs.js (100%) create mode 100644 Integration-for-verifiers (Billions Wallet)/src/index.js create mode 100644 Integration-for-verifiers (Billions Wallet)/src/services/db-service.js create mode 100644 Integration-for-verifiers (Billions Wallet)/src/services/logger.js create mode 100644 Integration-for-verifiers (Billions Wallet)/src/services/redis-service.js diff --git a/Integration-for-verifiers (Billions Wallet)/.env.example b/Integration-for-verifiers (Billions Wallet)/.env.example new file mode 100644 index 00000000..55f06040 --- /dev/null +++ b/Integration-for-verifiers (Billions Wallet)/.env.example @@ -0,0 +1,33 @@ +# Server Configuration +PORT=8080 +HOST_URL=https://your-domain.com # Your production domain or ngrok URL for development + +# Verifier Configuration +#⚠️ CRITICAL: IMMUTABLE VALUES - DO NOT CHANGE AFTER FIRST USE (NULLIFIER_ID & VERIFIER_DID) +# These values CANNOT be changed once users start verifying, as changing them +# will result in different nullifier IDs and DIDs for the same users. +VERIFIER_DID=your_verifier_did_here # Download the Billions app and Login into it.Copy the DID created for your account to use as the Verifier. You can find that in settings. +USE_CASE=POU # Options: POH, POU, POVH +NULLIFIER_SESSION_ID=your_nullifier_session_id # Must be a positive BigInt for the proof request. + +#Use case selection +# POH (Proof of Humanity): Verify that the user is a real human via `Human` Credential +# POU (Proof of Uniqueness): Verify that the user is unique (anti-Sybil) via `Verified Human` credential +# POVH (Proof of Verified Humanity): Verify that the user is a real verified human via `Verified Human` Credential + +USE_CASE=POVH #Options: 'POH', 'POU' + +# Redis Configuration (Production) +# ⚠️ SECURITY: Never commit real credentials to code repositories +REDIS_HOST=your-redis-host.redis-cloud.com +REDIS_PORT=12345 +REDIS_USERNAME=your-redis-username +REDIS_PASSWORD=your-secure-redis-password-here + +# Database Configuration +DATABASE_URL="your-database-url-here" + +# Security Configuration (Production) +ALLOWED_ORIGINS=https://your-frontend-domain.com,https://your-mobile-app-domain.com # Comma-separated list +NODE_ENV=production # Set to 'production' for strict CORS +LOG_LEVEL=info # Options: error, warn, info, debug \ No newline at end of file diff --git a/Integration-for-verifiers (Billions Wallet)/.gitignore b/Integration-for-verifiers (Billions Wallet)/.gitignore new file mode 100644 index 00000000..8998f3aa --- /dev/null +++ b/Integration-for-verifiers (Billions Wallet)/.gitignore @@ -0,0 +1,3 @@ +.env +node_modules/ +logs/ diff --git a/Integration-for-verifiers (Billions Wallet)/README.md b/Integration-for-verifiers (Billions Wallet)/README.md index ba598e45..dca06b13 100644 --- a/Integration-for-verifiers (Billions Wallet)/README.md +++ b/Integration-for-verifiers (Billions Wallet)/README.md @@ -12,14 +12,50 @@ This project is a demo backend and static UI for integrating verifications via B 1. **Install dependencies** ```bash -cd "Integration-for-verifiers (Billions Wallet)/js" npm install ``` ### 2. Configure Environment Variables -Create a `.env` file based on the env.example in the `js/` directory with the following variables: +Create a `.env` file based on the `env.example` in the directory: +```bash +cp env.example .env +``` + +Edit the `.env` file with your configuration: + +```bash +# Server Configuration +PORT=8080 +HOST_URL=https://your-domain.com # Your production domain or ngrok URL for development + +# Verifier Configuration +#⚠️ CRITICAL: IMMUTABLE VALUES - DO NOT CHANGE AFTER FIRST USE (NULLIFIER_ID & VERIFIER_DID) +# These values CANNOT be changed once users start verifying, as changing them +# will result in different nullifier IDs and DIDs for the same users. +VERIFIER_DID=your_verifier_did_here # Download the Billions app and Login into it.Copy the DID created for your account to use as the Verifier. You can find that in settings. +USE_CASE=POU # Options: POH, POU, POVH +NULLIFIER_SESSION_ID=your_nullifier_session_id # Must be a positive BigInt for the proof request. + +#Use case selection +# POH (Proof of Humanity): Verify that the user is a real human via `Human` Credential +# POU (Proof of Uniqueness): Verify that the user is unique (anti-Sybil) via `Verified Human` credential +# POVH (Proof of Verified Humanity): Verify that the user is a real verified human via `Verified Human` Credential + +USE_CASE=POVH #Options: 'POH', 'POU' + +# Redis Configuration (Production) +# ⚠️ SECURITY: Never commit real credentials to code repositories +REDIS_HOST=your-redis-host.redis-cloud.com +REDIS_PORT=12345 +REDIS_USERNAME=your-redis-username +REDIS_PASSWORD=your-secure-redis-password-here + +# Security Configuration (Production) +ALLOWED_ORIGINS=https://your-frontend-domain.com,https://your-mobile-app-domain.com # Comma-separated list +NODE_ENV=production # Set to 'production' for strict CORS +LOG_LEVEL=info # Options: error, warn, info, debug ``` ### 3. Set Up Public Access @@ -31,30 +67,37 @@ The Billions mobile app sends callbacks to your server, so you need public acces ### 4. Start the Server +To start ans test the server in dev mode use: ```bash -node index.js +npm run pm2:dev ``` -You should see: -``` -🚀 Privado verifier backend running on port 8080 -🔧 Using verification configuration: POU (Verified Human) -``` +You should see a series of messages from PM2 indicating that it’s launching multiple instances of the backend in cluster mode. This confirms that your Billions Verifier backend has successfully started under PM2, leveraging multiple workers for scalability and fault tolerance. ### 5. Test the Integration 1. **Open your browser** to `http://localhost:8080` -2. **Reload** the page to start a session and invoke verification request. -2. **Click** the button to continue flow on Billions Web Wallet or **Scan the QR after downloading the APP** to continue the flow on the native app. -4. **Complete verification** in the app -5. **See status update** on the web page +2. **Check health status** at `http://localhost:8080/api/health` to verify Redis connection +3. **Reload** the page to start a session and invoke verification request. +4. **Click** the button to continue flow on Billions Web Wallet or **Scan the QR after downloading the APP** to continue the flow on the native app. +5. **Complete verification** in the app +6. **See status update** on the web page +### 6. Production Features ---- +This implementation includes production-ready features: + +- **Redis Storage**: Persistent storage for sessions and verifications +- **PM2**: Process Manager for Node.js applications with a built-in Load Balancer. +- Use a permament storage for sessions and verifications relared to uniquness and nullifier id. +- **Health Check**: `/api/health` endpoint for monitoring +- **Graceful Shutdown**: Proper cleanup of Redis connections +- **Error Handling**: Comprehensive error handling for Redis operations +- **TTL Management**: Automatic expiration of stored data +- **Security**: Rate limiting, CORS, and security headers -**Ready to integrate Billions verification into your application? Start with this example and customize it for your needs!** 🚀 -## License -MIT +--- + diff --git a/Integration-for-verifiers (Billions Wallet)/ecosystem.config.js b/Integration-for-verifiers (Billions Wallet)/ecosystem.config.js new file mode 100644 index 00000000..f7294220 --- /dev/null +++ b/Integration-for-verifiers (Billions Wallet)/ecosystem.config.js @@ -0,0 +1,55 @@ +module.exports = { + apps: [{ + name: 'billions-verifier', + script: 'src/index.js', + instances: 'max', // Use all CPU cores + exec_mode: 'cluster', // Enable cluster mode for load balancing + + // Environment configuration + env: { + NODE_ENV: 'development', + PORT: 8080 + }, + env_production: { + NODE_ENV: 'production', + PORT: 8080 + }, + + // Process management + max_memory_restart: '1G', + min_uptime: '10s', + max_restarts: 10, + restart_delay: 4000, + + // Logging + log_file: 'logs/pm2-combined.log', + out_file: 'logs/pm2-out.log', + error_file: 'logs/pm2-error.log', + log_date_format: 'YYYY-MM-DD HH:mm:ss Z', + merge_logs: true, + + // Monitoring + source_map_support: true, + instance_var: 'INSTANCE_ID', + + // Health monitoring + watch: false, // Set to true for development auto-restart + ignore_watch: ['node_modules', 'logs', '.git'], + + // Production optimizations + node_args: '--max-old-space-size=1024' + }], + + // Deployment configuration + deploy: { + production: { + user: 'deploy', + host: ['your-server.com'], + ref: 'origin/main', + repo: 'git@github.com:your-username/billions-verifier.git', + path: '/var/www/billions-verifier', + 'post-deploy': 'npm install && pm2 reload ecosystem.config.js --env production', + 'pre-setup': 'apt-get install git -y' + } + } +}; \ No newline at end of file diff --git a/Integration-for-verifiers (Billions Wallet)/js/.env.example b/Integration-for-verifiers (Billions Wallet)/js/.env.example deleted file mode 100644 index a848dd78..00000000 --- a/Integration-for-verifiers (Billions Wallet)/js/.env.example +++ /dev/null @@ -1,19 +0,0 @@ -# Server configuration -PORT=8080 -HOST_URL= - - -#⚠️ CRITICAL: IMMUTABLE VALUES - DO NOT CHANGE AFTER FIRST USE (NULLIFIER_ID & VERIFIER_DID) -# These values CANNOT be changed once users start verifying, as changing them -# will result in different nullifier IDs and DIDs for the same users. -VERIFIER_DID=did:iden3:billions:main:xyzabc # Download the Billions app and Login into it.Copy the DID created for your account to use as the Verifier. You can find that in settings. -NULLIFIER_SESSION_ID=1234 # Must be a positive BigInt for the proof request. - - - -#Use case selection -# POH (Proof of Humanity)**: Verify that the user is a real human via `Human` Credential -# POU (Proof of Uniqueness)**: Verify that the user is unique (anti-Sybil) via `Verified Human` credential - -USE_CASE=POU #Options: 'POH', 'POU' - diff --git a/Integration-for-verifiers (Billions Wallet)/js/index.js b/Integration-for-verifiers (Billions Wallet)/js/index.js deleted file mode 100644 index b08a4d5b..00000000 --- a/Integration-for-verifiers (Billions Wallet)/js/index.js +++ /dev/null @@ -1,254 +0,0 @@ -/** - * Privado Verifier Backend - * - * This Express server implements the Privado verification flow using iden3. - * - * Endpoints: - * - GET /api/verification-request: Generates a verification request and returns a universal link for the wallet. - * - POST /api/callback: Verifies the proof sent by the client and prevents replay attacks. - * - * NOTE: This implementation uses in-memory storage for sessions and verifications. - * For production, use a persistent store (e.g., Redis) for scalability and reliability. - */ - -require('dotenv').config(); -const path = require("path"); -const express = require("express"); -const helmet = require('helmet'); -const rateLimit = require('express-rate-limit'); -const { auth, resolver } = require("@iden3/js-iden3-auth"); -const getRawBody = require("raw-body"); -const cors = require('cors'); -const { v4: uuidv4 } = require('uuid'); -const { CircuitId, AtomicQueryV3PubSignals } = require('@0xpolygonid/js-sdk'); -const { randomInt } = require('crypto'); - -// Import configuration system -const { getConfig, createProofRequest } = require('./config/verification-configs'); - -const app = express(); -const port = process.env.PORT || 8080; -const byteEncoder = new TextEncoder(); - - -const USE_CASE = process.env.USE_CASE; -const nullifierSessionID = process.env.NULLIFIER_SESSION_ID; - -// Get the configuration for the selected use case -const currentConfig = getConfig(USE_CASE); - - -// Security middlewares -app.use(helmet({ - contentSecurityPolicy: { - directives: { - defaultSrc: ["'self'"], - scriptSrc: ["'self'", "https://cdn.jsdelivr.net"], - styleSrc: ["'self'", "'unsafe-inline'"], - imgSrc: ["'self'", "data:"], - objectSrc: ["'none'"] - } - } -})); - -app.use(cors({ - origin: '*', // Set to your frontend domain in production - methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'], - allowedHeaders: ['Content-Type', 'Authorization', 'X-Requested-With'], - credentials: true -})); -app.use(rateLimit({ windowMs: 1 * 60 * 1000, max: 100 })); // 100 requests/minute - -// Body parsing middleware -app.use(express.json()); -app.use(express.urlencoded({ extended: true })); - -// Serve static files (e.g., QR code page) -app.use(express.static("../static")); - -// In-memory maps for session and user verification -const requestMap = new Map(); // sessionId -> authRequest -const userVerificationMap = new Map(); // userDid -> { sessionId, verified } -const statusMap = new Map(); // requestId -> status - - - -/** - * GET /api/verification-request - * Generates a verification request and returns a universal link for the wallet. - */ -app.get("/api/verification-request", async (req, res) => { - console.log(`/verification-request called`) - try { - - const hostUrl = process.env.HOST_URL; //host url to this verifier - const sessionId = randomInt(10000) - const callbackURL = "/api/callback"; - - if (!hostUrl) { - console.log("Server misconfiguration: HOST_URL is missing") - } - - if (!process.env.VERIFIER_DID) { - console.log("Server misconfiguration: VERIFIER_DID is missing"); - } - - - const uri = `${hostUrl}${callbackURL}?sessionId=${sessionId}`; - - // Generate request for basic authentication using config - const request = auth.createAuthorizationRequest( - currentConfig.verification_description, - process.env.VERIFIER_DID, - uri - ); - - // Create proof request using the current configuration - const proofRequest = createProofRequest(USE_CASE, sessionId, nullifierSessionID); - - const scope = request.body.scope ?? []; - request.body.scope = [...scope, proofRequest]; - - // Store auth request in map associated with session ID - requestMap.set(sessionId, request); - - // Initialize status as pending - statusMap.set(proofRequest.id, "pending"); - - return res.status(200).json(request); - } catch (err) { - console.error("Error in /api/verification-request:", err); - return res.status(500).json({ error: "Internal server error" }); - } -}); - -/** - * GET /api/status/:id - * Returns the verification status for a given request ID. - */ -app.get("/api/status/:id", (req, res) => { - const requestId = parseInt(req.params.id); - const status = statusMap.get(requestId) || "not_found"; - - return res.status(200).json({ - requestId: requestId, - status: status, - }); -}); - -/** - * POST /api/callback - * Verifies the proof after sign-in callback. - * Prevents replay attacks by ensuring a userDid can only verify once. - */ -app.post("/api/callback", async (req, res) => { - console.log("/callback called") - const sessionId = parseInt(req.query.sessionId); - if (isNaN(sessionId)) { - return res.status(400).json({ message: "Invalid sessionId format" }); - } - - // Get JWZ token params from the post request - const raw = await getRawBody(req); - const tokenStr = raw.toString().trim(); - - - // Fetch authRequest from sessionID - const authRequest = requestMap.get(sessionId); - if (!authRequest) { - return res.status(400).json({ message: "Invalid or expired sessionId" }); - } - - // Set up resolvers for supported networks - const resolvers = { - ["billions:main"]: new resolver.EthStateResolver( - "https://rpc-mainnet.billions.network", - "0x3C9acB2205Aa72A05F6D77d708b5Cf85FCa3a896" - ), - ["privado:main"]: new resolver.EthStateResolver( - "https://rpc-mainnet.privado.id", - "0x3C9acB2205Aa72A05F6D77d708b5Cf85FCa3a896" - ), - ["billions:test"]: new resolver.EthStateResolver( - "https://billions-testnet-rpc.eu-north-2.gateway.fm", - "0x3C9acB2205Aa72A05F6D77d708b5Cf85FCa3a896" - ) - }; - - // Execute verification - const verifier = await auth.Verifier.newVerifier({ - stateResolver: resolvers, - circuitsDir: path.join(__dirname, "../keys"), - ipfsGatewayURL: "https://ipfs.io", - }); - - let authResponse; - try { - const opts = { - AcceptedStateTransitionDelay: 5 * 60 * 1000, // 5 minutes - }; - authResponse = await verifier.fullVerify(tokenStr, authRequest, opts); - - // Prevent replay attack: check if this user's nullifier is already verified - const nullifierProof = authResponse.body.scope.find( - (s) => s.circuitId === "credentialAtomicQueryV3-beta.1" && s.id === sessionId - ); - if (!nullifierProof) { - return res.status(400).json({ message: "No valid nullifier proof found in response." }); - } - - const pubSignals = new AtomicQueryV3PubSignals().pubSignalsUnmarshal( - byteEncoder.encode(JSON.stringify(nullifierProof.pub_signals)) - ); - - const nullifier = pubSignals.nullifier; - - if (userVerificationMap.has(nullifier) && userVerificationMap.get(nullifier).verified) { - return res.status(400).json({ message: "User with this did has been verified already." }); - } - - userVerificationMap.set(nullifier, { - sessionId: sessionId, - verified: true, - }); - - // Update status to success after successful verification - // Get the proof request ID from the authRequest - const proofRequestId = authRequest.body.scope.find(s => s.circuitId === "credentialAtomicQueryV3-beta.1")?.id; - if (proofRequestId) { - statusMap.set(proofRequestId, "success"); - } - - console.log(`✅ User ${authResponse.from} successfully verified for ${USE_CASE} (${currentConfig.name})`); - - } catch (error) { - console.error("Error in /api/callback:", error); - return res.status(500).send(error.message); - } - return res - .status(200) - .set("Content-Type", "application/json") - .send(authResponse); -}); - -// Start the server -const server = app.listen(port, () => { - console.log(`🚀 Privado verifier backend running on port ${port}`); - console.log(`🔧 Using verification configuration: ${USE_CASE} (${currentConfig.name})`); -}); - -// Graceful shutdown -process.on('SIGTERM', () => { - server.close(() => { - console.log('Process terminated'); - }); -}); - -/** - * NOTE: - * - For production, use HTTPS and a process manager (e.g., pm2). - * - Replace in-memory maps with a persistent store. - * - Set all secrets and config in environment variables. - * - Monitor and log errors for auditing and debugging. - */ - diff --git a/Integration-for-verifiers (Billions Wallet)/js/package-lock.json b/Integration-for-verifiers (Billions Wallet)/package-lock.json similarity index 89% rename from Integration-for-verifiers (Billions Wallet)/js/package-lock.json rename to Integration-for-verifiers (Billions Wallet)/package-lock.json index dcc7c75f..11536cb4 100644 --- a/Integration-for-verifiers (Billions Wallet)/js/package-lock.json +++ b/Integration-for-verifiers (Billions Wallet)/package-lock.json @@ -15,8 +15,11 @@ "express": "^4.18.2", "express-rate-limit": "^7.5.0", "helmet": "^8.1.0", + "pg": "^8.18.0", "raw-body": "^3.0.0", - "uuid": "^11.1.0" + "redis": "^4.6.12", + "uuid": "^11.1.0", + "winston": "^3.18.3" }, "devDependencies": { "@types/cors": "^2.8.17", @@ -80,6 +83,15 @@ "integrity": "sha512-96Z2IP3mYmF1Xg2cDm8f1gWGf/HUVedQ3FMifV4kG/PQ4yEP51xDtRAEfhVNt5f/uzpNkZHwWQuUcu6D6K+Ekw==", "license": "MIT" }, + "node_modules/@colors/colors": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz", + "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==", + "license": "MIT", + "engines": { + "node": ">=0.1.90" + } + }, "node_modules/@cspotcode/source-map-support": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", @@ -93,6 +105,17 @@ "node": ">=12" } }, + "node_modules/@dabh/diagnostics": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.8.tgz", + "integrity": "sha512-R4MSXTVnuMzGD7bzHdW2ZhhdPC/igELENcq5IjEverBvq5hn1SXCWcsi6eSsdWP0/Ur+SItRRjAktmdoX/8R/Q==", + "license": "MIT", + "dependencies": { + "@so-ric/colorspace": "^1.1.6", + "enabled": "2.0.x", + "kuler": "^2.0.0" + } + }, "node_modules/@digitalbazaar/http-client": { "version": "3.4.1", "resolved": "https://registry.npmjs.org/@digitalbazaar/http-client/-/http-client-3.4.1.tgz", @@ -1230,6 +1253,65 @@ "url": "https://paulmillr.com/funding/" } }, + "node_modules/@redis/bloom": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-1.2.0.tgz", + "integrity": "sha512-HG2DFjYKbpNmVXsa0keLHp/3leGJz1mjh09f2RLGGLQZzSHpkmZWuwJbAvo3QcRY8p80m5+ZdXZdYOSBLlp7Cg==", + "license": "MIT", + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, + "node_modules/@redis/client": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@redis/client/-/client-1.6.1.tgz", + "integrity": "sha512-/KCsg3xSlR+nCK8/8ZYSknYxvXHwubJrU82F3Lm1Fp6789VQ0/3RJKfsmRXjqfaTA++23CvC3hqmqe/2GEt6Kw==", + "license": "MIT", + "dependencies": { + "cluster-key-slot": "1.1.2", + "generic-pool": "3.9.0", + "yallist": "4.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@redis/graph": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@redis/graph/-/graph-1.1.1.tgz", + "integrity": "sha512-FEMTcTHZozZciLRl6GiiIB4zGm5z5F3F6a6FZCyrfxdKOhFlGkiAqlexWMBzCi4DcRoyiOsuLfW+cjlGWyExOw==", + "license": "MIT", + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, + "node_modules/@redis/json": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@redis/json/-/json-1.0.7.tgz", + "integrity": "sha512-6UyXfjVaTBTJtKNG4/9Z8PSpKE6XgSyEb8iwaqDcy+uKrd/DGYHTWkUdnQDyzm727V7p21WUMhsqz5oy65kPcQ==", + "license": "MIT", + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, + "node_modules/@redis/search": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@redis/search/-/search-1.2.0.tgz", + "integrity": "sha512-tYoDBbtqOVigEDMAcTGsRlMycIIjwMCgD8eR2t0NANeQmgK/lvxNAvYyb6bZDD4frHRhIHkJu2TBRvB0ERkOmw==", + "license": "MIT", + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, + "node_modules/@redis/time-series": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@redis/time-series/-/time-series-1.1.0.tgz", + "integrity": "sha512-c1Q99M5ljsIuc4YdaCwfUEXsofakb9c8+Zse2qxTadu8TalLXuAESzLvFAvNVbkmSlvlzIQOLpBCmWI9wTOt+g==", + "license": "MIT", + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, "node_modules/@scure/base": { "version": "1.2.6", "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.2.6.tgz", @@ -1239,6 +1321,16 @@ "url": "https://paulmillr.com/funding/" } }, + "node_modules/@so-ric/colorspace": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@so-ric/colorspace/-/colorspace-1.1.6.tgz", + "integrity": "sha512-/KiKkpHNOBgkFJwu9sh48LkHSMYGyuTcSFK/qMBdnOAlrRJzRSXAOFB5qwzaVQuDl8wAvHVMkaASQDReTahxuw==", + "license": "MIT", + "dependencies": { + "color": "^5.0.2", + "text-hex": "1.0.x" + } + }, "node_modules/@tsconfig/node10": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", @@ -1385,6 +1477,12 @@ "@types/send": "*" } }, + "node_modules/@types/triple-beam": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz", + "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==", + "license": "MIT" + }, "node_modules/@types/uuid": { "version": "10.0.0", "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-10.0.0.tgz", @@ -1520,8 +1618,7 @@ "version": "3.2.6", "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/at-least-node": { "version": "1.0.0", @@ -1819,6 +1916,28 @@ "calcwit": "calcwit.js" } }, + "node_modules/cluster-key-slot": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz", + "integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/color": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/color/-/color-5.0.2.tgz", + "integrity": "sha512-e2hz5BzbUPcYlIRHo8ieAhYgoajrJr+hWoceg6E345TPsATMUKqDgzt8fSXZJJbxfpiPzkWyphz8yn8At7q3fA==", + "license": "MIT", + "dependencies": { + "color-convert": "^3.0.1", + "color-string": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -1837,6 +1956,48 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "license": "MIT" }, + "node_modules/color-string": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-2.1.2.tgz", + "integrity": "sha512-RxmjYxbWemV9gKu4zPgiZagUxbH3RQpEIO77XoSSX0ivgABDZ+h8Zuash/EMFLTI4N9QgFPOJ6JQpPZKFxa+dA==", + "license": "MIT", + "dependencies": { + "color-name": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/color-string/node_modules/color-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.0.2.tgz", + "integrity": "sha512-9vEt7gE16EW7Eu7pvZnR0abW9z6ufzhXxGXZEVU9IqPdlsUiMwJeJfRtq0zePUmnbHGT9zajca7mX8zgoayo4A==", + "license": "MIT", + "engines": { + "node": ">=12.20" + } + }, + "node_modules/color/node_modules/color-convert": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-3.1.2.tgz", + "integrity": "sha512-UNqkvCDXstVck3kdowtOTWROIJQwafjOfXSmddoDrXo4cewMKmusCeF22Q24zvjR8nwWib/3S/dfyzPItPEiJg==", + "license": "MIT", + "dependencies": { + "color-name": "^2.0.0" + }, + "engines": { + "node": ">=14.6" + } + }, + "node_modules/color/node_modules/color-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.0.2.tgz", + "integrity": "sha512-9vEt7gE16EW7Eu7pvZnR0abW9z6ufzhXxGXZEVU9IqPdlsUiMwJeJfRtq0zePUmnbHGT9zajca7mX8zgoayo4A==", + "license": "MIT", + "engines": { + "node": ">=12.20" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -2076,6 +2237,12 @@ "integrity": "sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw==", "license": "MIT" }, + "node_modules/enabled": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", + "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==", + "license": "MIT" + }, "node_modules/encodeurl": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", @@ -2372,6 +2539,12 @@ "license": "GPL-3.0", "peer": true }, + "node_modules/fecha": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", + "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==", + "license": "MIT" + }, "node_modules/fetch-blob": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", @@ -2479,6 +2652,12 @@ "micromatch": "^4.0.2" } }, + "node_modules/fn.name": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", + "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==", + "license": "MIT" + }, "node_modules/formdata-polyfill": { "version": "4.0.10", "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", @@ -2539,6 +2718,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/generic-pool": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.9.0.tgz", + "integrity": "sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g==", + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, "node_modules/get-intrinsic": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", @@ -2804,6 +2992,18 @@ "node": ">=0.12.0" } }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-wsl": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", @@ -2947,6 +3147,12 @@ "graceful-fs": "^4.1.11" } }, + "node_modules/kuler": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", + "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==", + "license": "MIT" + }, "node_modules/ky": { "version": "0.33.3", "resolved": "https://registry.npmjs.org/ky/-/ky-0.33.3.tgz", @@ -2998,6 +3204,29 @@ "node": ">= 0.8.0" } }, + "node_modules/logform": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/logform/-/logform-2.7.0.tgz", + "integrity": "sha512-TFYA4jnP7PVbmlBIfhlSe+WKxs9dklXMTEGcBCIvLhE/Tn3H6Gk1norupVW7m5Cnd4bLcr08AytbyV/xj7f/kQ==", + "license": "MIT", + "dependencies": { + "@colors/colors": "1.6.0", + "@types/triple-beam": "^1.3.2", + "fecha": "^4.2.0", + "ms": "^2.1.1", + "safe-stable-stringify": "^2.3.1", + "triple-beam": "^1.3.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/logform/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, "node_modules/logplease": { "version": "1.2.15", "resolved": "https://registry.npmjs.org/logplease/-/logplease-1.2.15.tgz", @@ -3283,6 +3512,15 @@ "wrappy": "1" } }, + "node_modules/one-time": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", + "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", + "license": "MIT", + "dependencies": { + "fn.name": "1.x.x" + } + }, "node_modules/open": { "version": "7.4.2", "resolved": "https://registry.npmjs.org/open/-/open-7.4.2.tgz", @@ -3389,6 +3627,95 @@ "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", "license": "MIT" }, + "node_modules/pg": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.18.0.tgz", + "integrity": "sha512-xqrUDL1b9MbkydY/s+VZ6v+xiMUmOUk7SS9d/1kpyQxoJ6U9AO1oIJyUWVZojbfe5Cc/oluutcgFG4L9RDP1iQ==", + "license": "MIT", + "dependencies": { + "pg-connection-string": "^2.11.0", + "pg-pool": "^3.11.0", + "pg-protocol": "^1.11.0", + "pg-types": "2.2.0", + "pgpass": "1.0.5" + }, + "engines": { + "node": ">= 16.0.0" + }, + "optionalDependencies": { + "pg-cloudflare": "^1.3.0" + }, + "peerDependencies": { + "pg-native": ">=3.0.1" + }, + "peerDependenciesMeta": { + "pg-native": { + "optional": true + } + } + }, + "node_modules/pg-cloudflare": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.3.0.tgz", + "integrity": "sha512-6lswVVSztmHiRtD6I8hw4qP/nDm1EJbKMRhf3HCYaqud7frGysPv7FYJ5noZQdhQtN2xJnimfMtvQq21pdbzyQ==", + "license": "MIT", + "optional": true + }, + "node_modules/pg-connection-string": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.11.0.tgz", + "integrity": "sha512-kecgoJwhOpxYU21rZjULrmrBJ698U2RxXofKVzOn5UDj61BPj/qMb7diYUR1nLScCDbrztQFl1TaQZT0t1EtzQ==", + "license": "MIT" + }, + "node_modules/pg-int8": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", + "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==", + "license": "ISC", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/pg-pool": { + "version": "3.11.0", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.11.0.tgz", + "integrity": "sha512-MJYfvHwtGp870aeusDh+hg9apvOe2zmpZJpyt+BMtzUWlVqbhFmMK6bOBXLBUPd7iRtIF9fZplDc7KrPN3PN7w==", + "license": "MIT", + "peerDependencies": { + "pg": ">=8.0" + } + }, + "node_modules/pg-protocol": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.11.0.tgz", + "integrity": "sha512-pfsxk2M9M3BuGgDOfuy37VNRRX3jmKgMjcvAcWqNDpZSf4cUmv8HSOl5ViRQFsfARFn0KuUQTgLxVMbNq5NW3g==", + "license": "MIT" + }, + "node_modules/pg-types": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", + "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", + "license": "MIT", + "dependencies": { + "pg-int8": "1.0.1", + "postgres-array": "~2.0.0", + "postgres-bytea": "~1.0.0", + "postgres-date": "~1.0.4", + "postgres-interval": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pgpass": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz", + "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==", + "license": "MIT", + "dependencies": { + "split2": "^4.1.0" + } + }, "node_modules/picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", @@ -3401,6 +3728,45 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/postgres-array": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", + "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/postgres-bytea": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.1.tgz", + "integrity": "sha512-5+5HqXnsZPE65IJZSMkZtURARZelel2oXUEO8rH83VS/hxH5vv1uHquPg5wZs8yMAfdv971IU+kcPUczi7NVBQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-date": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", + "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-interval": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", + "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", + "license": "MIT", + "dependencies": { + "xtend": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/prelude-ls": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", @@ -3583,6 +3949,23 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "node_modules/redis": { + "version": "4.7.1", + "resolved": "https://registry.npmjs.org/redis/-/redis-4.7.1.tgz", + "integrity": "sha512-S1bJDnqLftzHXHP8JsT5II/CtHWQrASX5K96REjWjlmWKrviSOLWmM7QnRLstAWsu1VBBV1ffV6DzCvxNP0UJQ==", + "license": "MIT", + "workspaces": [ + "./packages/*" + ], + "dependencies": { + "@redis/bloom": "1.2.0", + "@redis/client": "1.6.1", + "@redis/graph": "1.1.1", + "@redis/json": "1.0.7", + "@redis/search": "1.2.0", + "@redis/time-series": "1.1.0" + } + }, "node_modules/require-from-string": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", @@ -3632,6 +4015,15 @@ ], "license": "MIT" }, + "node_modules/safe-stable-stringify": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", + "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", @@ -3881,6 +4273,24 @@ "node": ">=0.10.0" } }, + "node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "license": "ISC", + "engines": { + "node": ">= 10.x" + } + }, + "node_modules/stack-trace": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", + "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==", + "license": "MIT", + "engines": { + "node": "*" + } + }, "node_modules/static-eval": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/static-eval/-/static-eval-2.0.2.tgz", @@ -3921,6 +4331,12 @@ "node": ">=8" } }, + "node_modules/text-hex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", + "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==", + "license": "MIT" + }, "node_modules/tmp": { "version": "0.0.33", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", @@ -3954,6 +4370,15 @@ "node": ">=0.6" } }, + "node_modules/triple-beam": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz", + "integrity": "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==", + "license": "MIT", + "engines": { + "node": ">= 14.0.0" + } + }, "node_modules/tryer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/tryer/-/tryer-1.0.1.tgz", @@ -4005,13 +4430,6 @@ } } }, - "node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "extraneous": true, - "license": "0BSD" - }, "node_modules/type-check": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", @@ -4114,6 +4532,12 @@ "punycode": "^2.1.0" } }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, "node_modules/utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", @@ -4200,6 +4624,70 @@ "node": ">= 8" } }, + "node_modules/winston": { + "version": "3.18.3", + "resolved": "https://registry.npmjs.org/winston/-/winston-3.18.3.tgz", + "integrity": "sha512-NoBZauFNNWENgsnC9YpgyYwOVrl2m58PpQ8lNHjV3kosGs7KJ7Npk9pCUE+WJlawVSe8mykWDKWFSVfs3QO9ww==", + "license": "MIT", + "dependencies": { + "@colors/colors": "^1.6.0", + "@dabh/diagnostics": "^2.0.8", + "async": "^3.2.3", + "is-stream": "^2.0.0", + "logform": "^2.7.0", + "one-time": "^1.0.0", + "readable-stream": "^3.4.0", + "safe-stable-stringify": "^2.3.1", + "stack-trace": "0.0.x", + "triple-beam": "^1.3.0", + "winston-transport": "^4.9.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/winston-transport": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.9.0.tgz", + "integrity": "sha512-8drMJ4rkgaPo1Me4zD/3WLfI/zPdA9o2IipKODunnGDcuqbHwjsbB79ylv04LCGGzU0xQ6vTznOMpQGaLhhm6A==", + "license": "MIT", + "dependencies": { + "logform": "^2.7.0", + "readable-stream": "^3.6.2", + "triple-beam": "^1.3.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/winston-transport/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/winston/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/word-wrap": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", @@ -4237,6 +4725,15 @@ } } }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "license": "MIT", + "engines": { + "node": ">=0.4" + } + }, "node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", diff --git a/Integration-for-verifiers (Billions Wallet)/js/package.json b/Integration-for-verifiers (Billions Wallet)/package.json similarity index 53% rename from Integration-for-verifiers (Billions Wallet)/js/package.json rename to Integration-for-verifiers (Billions Wallet)/package.json index 4b364acd..aefbbc05 100644 --- a/Integration-for-verifiers (Billions Wallet)/js/package.json +++ b/Integration-for-verifiers (Billions Wallet)/package.json @@ -4,10 +4,16 @@ "description": "Billions Wallet Integration for Verifiers", "main": "dist/index.js", "scripts": { - "build": "tsc", - "start": "node dist/index.js", - "dev": "ts-node src/index.ts", - "watch": "tsc --watch" + "start": "node src/index.js", + "dev": "nodemon src/index.js", + "prod": "NODE_ENV=production node src/index.js", + "pm2:start": "pm2 start ecosystem.config.js --env production", + "pm2:dev": "pm2 start ecosystem.config.js", + "pm2:stop": "pm2 stop billions-verifier", + "pm2:restart": "pm2 restart billions-verifier", + "pm2:logs": "pm2 logs billions-verifier", + "pm2:monitor": "pm2 monit", + "logs": "tail -f logs/combined.log" }, "dependencies": { "@0xpolygonid/js-sdk": "^1.31.1", @@ -17,8 +23,11 @@ "express": "^4.18.2", "express-rate-limit": "^7.5.0", "helmet": "^8.1.0", + "pg": "^8.18.0", "raw-body": "^3.0.0", - "uuid": "^11.1.0" + "redis": "^4.6.12", + "uuid": "^11.1.0", + "winston": "^3.18.3" }, "devDependencies": { "@types/cors": "^2.8.17", diff --git a/Integration-for-verifiers (Billions Wallet)/static/index.html b/Integration-for-verifiers (Billions Wallet)/public/index.html similarity index 99% rename from Integration-for-verifiers (Billions Wallet)/static/index.html rename to Integration-for-verifiers (Billions Wallet)/public/index.html index 98773b93..ff1d05fb 100644 --- a/Integration-for-verifiers (Billions Wallet)/static/index.html +++ b/Integration-for-verifiers (Billions Wallet)/public/index.html @@ -17,7 +17,6 @@
Verify with BILLIONS
-
Developer Integration Demo
@@ -30,7 +29,6 @@
Verify directly in your browser.
- - diff --git a/Integration-for-verifiers (Billions Wallet)/static/index.js b/Integration-for-verifiers (Billions Wallet)/public/index.js similarity index 99% rename from Integration-for-verifiers (Billions Wallet)/static/index.js rename to Integration-for-verifiers (Billions Wallet)/public/index.js index e83a7d2e..373d6298 100644 --- a/Integration-for-verifiers (Billions Wallet)/static/index.js +++ b/Integration-for-verifiers (Billions Wallet)/public/index.js @@ -16,19 +16,19 @@ window.onload = () => { .then(data => { // Store verification data for wallet button verificationData = data; - + // Create the universal wallet URL using the ver (used for both QR code and button) const jsonString = JSON.stringify(data); const base64Data = btoa(jsonString); const walletUrl = `https://wallet.billions.network/#i_m=${base64Data}`; - + // Show and generate QR code const qrCodeEl = document.getElementById('qrcode'); const walletButtonContainer = document.getElementById('walletButtonContainer'); - + qrCodeEl.style.display = 'block'; walletButtonContainer.style.display = 'block'; - + // Check if QRCode library is loaded if (typeof QRCode !== 'undefined') { new QRCode(qrCodeEl, { @@ -92,7 +92,7 @@ function showNotification(type, title, message, showReload = false) { // Set content titleEl.textContent = title; messageEl.textContent = message; - + // Set icon and style based on type if (type === 'success') { icon.textContent = '✓'; @@ -115,7 +115,7 @@ function showNotification(type, title, message, showReload = false) { // Event listeners reloadBtn.onclick = () => window.location.reload(); closeBtn.onclick = () => overlay.classList.remove('show'); - + // Close on overlay click overlay.onclick = (e) => { if (e.target === overlay) { @@ -127,7 +127,7 @@ function showNotification(type, title, message, showReload = false) { // Function to poll status (optional based on your API) function pollStatus(requestId) { console.log(`🔄 Starting to poll status for request ID: ${requestId}`); - + const checkStatus = () => { fetch(`${baseUrl}api/status/${requestId}`) .then((response) => response.json()) @@ -171,7 +171,7 @@ function pollStatus(requestId) { // Setup wallet button functionality function setupWalletButton(walletUrl) { const walletBtn = document.getElementById('openWalletBtn'); - + walletBtn.addEventListener('click', () => { if (!verificationData) { console.error('No verification data available'); @@ -186,10 +186,10 @@ function setupWalletButton(walletUrl) { try { console.log('Opening Billions wallet with URL:', walletUrl); - + // Open the wallet URL in a new tab/window window.open(walletUrl, '_blank'); - + } catch (error) { console.error('Error creating wallet URL:', error); showNotification( @@ -201,5 +201,3 @@ function setupWalletButton(walletUrl) { } }); } - - diff --git a/Integration-for-verifiers (Billions Wallet)/keys/authV2/verification_key.json b/Integration-for-verifiers (Billions Wallet)/public/keys/authV2/verification_key.json similarity index 100% rename from Integration-for-verifiers (Billions Wallet)/keys/authV2/verification_key.json rename to Integration-for-verifiers (Billions Wallet)/public/keys/authV2/verification_key.json diff --git a/Integration-for-verifiers (Billions Wallet)/keys/credentialAtomicQueryMTPV2/verification_key.json b/Integration-for-verifiers (Billions Wallet)/public/keys/credentialAtomicQueryMTPV2/verification_key.json similarity index 100% rename from Integration-for-verifiers (Billions Wallet)/keys/credentialAtomicQueryMTPV2/verification_key.json rename to Integration-for-verifiers (Billions Wallet)/public/keys/credentialAtomicQueryMTPV2/verification_key.json diff --git a/Integration-for-verifiers (Billions Wallet)/keys/credentialAtomicQuerySigV2/verification_key.json b/Integration-for-verifiers (Billions Wallet)/public/keys/credentialAtomicQuerySigV2/verification_key.json similarity index 100% rename from Integration-for-verifiers (Billions Wallet)/keys/credentialAtomicQuerySigV2/verification_key.json rename to Integration-for-verifiers (Billions Wallet)/public/keys/credentialAtomicQuerySigV2/verification_key.json diff --git a/Integration-for-verifiers (Billions Wallet)/keys/credentialAtomicQueryV3-beta.1/verification_key.json b/Integration-for-verifiers (Billions Wallet)/public/keys/credentialAtomicQueryV3-beta.1/verification_key.json similarity index 100% rename from Integration-for-verifiers (Billions Wallet)/keys/credentialAtomicQueryV3-beta.1/verification_key.json rename to Integration-for-verifiers (Billions Wallet)/public/keys/credentialAtomicQueryV3-beta.1/verification_key.json diff --git a/Integration-for-verifiers (Billions Wallet)/keys/linkedMultiQuery10-beta.1/verification_key.json b/Integration-for-verifiers (Billions Wallet)/public/keys/linkedMultiQuery10-beta.1/verification_key.json similarity index 100% rename from Integration-for-verifiers (Billions Wallet)/keys/linkedMultiQuery10-beta.1/verification_key.json rename to Integration-for-verifiers (Billions Wallet)/public/keys/linkedMultiQuery10-beta.1/verification_key.json diff --git a/Integration-for-verifiers (Billions Wallet)/static/styles.css b/Integration-for-verifiers (Billions Wallet)/public/styles.css similarity index 100% rename from Integration-for-verifiers (Billions Wallet)/static/styles.css rename to Integration-for-verifiers (Billions Wallet)/public/styles.css diff --git a/Integration-for-verifiers (Billions Wallet)/js/.gitignore b/Integration-for-verifiers (Billions Wallet)/src/.gitignore similarity index 100% rename from Integration-for-verifiers (Billions Wallet)/js/.gitignore rename to Integration-for-verifiers (Billions Wallet)/src/.gitignore diff --git a/Integration-for-verifiers (Billions Wallet)/js/config/verification-configs.js b/Integration-for-verifiers (Billions Wallet)/src/config/verification-configs.js similarity index 100% rename from Integration-for-verifiers (Billions Wallet)/js/config/verification-configs.js rename to Integration-for-verifiers (Billions Wallet)/src/config/verification-configs.js diff --git a/Integration-for-verifiers (Billions Wallet)/src/index.js b/Integration-for-verifiers (Billions Wallet)/src/index.js new file mode 100644 index 00000000..9b828efe --- /dev/null +++ b/Integration-for-verifiers (Billions Wallet)/src/index.js @@ -0,0 +1,632 @@ + +// Load environment variables +require('dotenv').config(); + +// Core dependencies +const path = require("path"); +const express = require("express"); +const helmet = require('helmet'); +const rateLimit = require('express-rate-limit'); +const { auth, resolver } = require("@iden3/js-iden3-auth"); +const getRawBody = require("raw-body"); +const cors = require('cors'); +const { AtomicQueryV3PubSignals } = require('@0xpolygonid/js-sdk'); +const { randomInt, randomBytes } = require('crypto'); + +// Application modules +const { getConfig, createProofRequest } = require('./config/verification-configs'); +const redisService = require('./services/redis-service'); +const logger = require('./services/logger'); + +// Initialize Express app and configuration +const app = express(); +const port = process.env.PORT || 8080; +const byteEncoder = new TextEncoder(); + +// Environment configuration +const USE_CASE = process.env.USE_CASE; +const nullifierSessionID = process.env.NULLIFIER_SESSION_ID; +const currentConfig = getConfig(USE_CASE); + +// Cached verifier instance for performance +let cachedVerifier = null; +let verifierInitialized = false; + +// Generate 8-digit numeric session ID compatible with BigInt conversion +function generateSecureSessionId() { + return randomInt(10000000, 100000000); // Range: 10,000,000 to 99,999,999 +} + +// Initialize and cache verifier for performance +async function getVerifierInstance() { + if (cachedVerifier && verifierInitialized) { + return cachedVerifier; + } + + try { + logger.info('Initializing verifier instance...'); + const startTime = Date.now(); + + // Configure network resolvers for blockchain state verification + const resolvers = { + ["billions:main"]: new resolver.EthStateResolver( + "https://rpc-mainnet.billions.network", + "0x3C9acB2205Aa72A05F6D77d708b5Cf85FCa3a896" + ), + ["privado:main"]: new resolver.EthStateResolver( + "https://rpc-mainnet.privado.id", + "0x3C9acB2205Aa72A05F6D77d708b5Cf85FCa3a896" + ), + ["billions:test"]: new resolver.EthStateResolver( + "https://billions-testnet-rpc.eu-north-2.gateway.fm", + "0x3C9acB2205Aa72A05F6D77d708b5Cf85FCa3a896" + ) + }; + + // Initialize verifier with circuits and resolvers + cachedVerifier = await auth.Verifier.newVerifier({ + stateResolver: resolvers, + circuitsDir: path.join(process.cwd(), "public/keys"), + ipfsGatewayURL: "https://ipfs.io", + }); + + verifierInitialized = true; + const initTime = Date.now() - startTime; + logger.info('Verifier initialized successfully', { initTimeMs: initTime }); + + return cachedVerifier; + } catch (error) { + logger.error('Failed to initialize verifier', { error: error.message, stack: error.stack }); + throw error; + } +} + +// Security middleware +app.use(helmet({ + contentSecurityPolicy: { + directives: { + defaultSrc: ["'self'"], + scriptSrc: ["'self'", "https://cdn.jsdelivr.net"], // Allow QR code library + styleSrc: ["'self'", "'unsafe-inline'"], + imgSrc: ["'self'", "data:"], + objectSrc: ["'none'"] + } + } +})); + +// CORS configuration with environment-based security +const allowedOrigins = process.env.NODE_ENV === 'production' + ? (process.env.ALLOWED_ORIGINS || 'https://your-production-domain.com,https://your-app-domain.com').split(',').filter(Boolean) + : ['http://localhost:3000', 'http://localhost:8080', 'http://127.0.0.1:3000', 'http://127.0.0.1:8080']; + +app.use(cors({ + origin: (origin, callback) => { + // Allow requests with no origin (mobile apps, Postman, etc.) + if (!origin) return callback(null, true); + + // In development, be permissive + if (process.env.NODE_ENV === 'development') { + return callback(null, true); + } + + // In production, check whitelist + if (allowedOrigins.includes(origin)) { + return callback(null, true); + } + + // Block and log unauthorized origins + logger.logSecurityEvent('CORS_BLOCKED', { + origin, + allowedOrigins, + timestamp: new Date().toISOString() + }, 'warn'); + + return callback(new Error('Not allowed by CORS - Contact admin to whitelist your domain'), false); + }, + methods: ['GET', 'POST', 'OPTIONS'], + allowedHeaders: ['Content-Type', 'Authorization', 'X-Requested-With'], + credentials: true, + optionsSuccessStatus: 200 // Some legacy browsers choke on 204 +})); + +// Enhanced rate limiting with security features +app.use(rateLimit({ + windowMs: 1 * 60 * 1000, // 1 minute window + max: 100, // 100 requests per minute per IP + message: { + error: "Too many requests", + details: "Rate limit exceeded. Please try again later.", + retryAfter: "1 minute" + }, + standardHeaders: true, // Include rate limit info in headers + legacyHeaders: false, // Disable the X-RateLimit-* headers + handler: (req, res) => { + logger.logSecurityEvent('RATE_LIMIT_EXCEEDED', { + ip: req.ip, + userAgent: req.get('User-Agent'), + url: req.url, + method: req.method + }, 'warn'); + + res.status(429).json({ + error: "Too many requests", + details: "Rate limit exceeded. Please try again later.", + retryAfter: "1 minute" + }); + } +})); + +// Body parsing middleware with size limits for security +app.use(express.json({ + limit: '10mb', // Limit JSON payload size + strict: true, + type: 'application/json' +})); +app.use(express.urlencoded({ + extended: true, + limit: '10mb', // Limit URL-encoded payload size + parameterLimit: 1000 // Limit number of parameters +})); + +// Serve static frontend files +app.use(express.static("./public")); + +// Generate verification request for wallet authentication +app.get("/api/verification-request", async (req, res) => { + const startTime = Date.now(); + try { + const hostUrl = process.env.HOST_URL; + const sessionId = generateSecureSessionId(); + const callbackURL = "/api/callback"; + + // Validate required environment variables + if (!hostUrl) { + logger.error("Server misconfiguration: HOST_URL is missing"); + return res.status(500).json({ + error: "Server configuration error", + details: "Missing HOST_URL configuration" + }); + } + + if (!process.env.VERIFIER_DID) { + logger.error("Server misconfiguration: VERIFIER_DID is missing"); + return res.status(500).json({ + error: "Server configuration error", + details: "Missing VERIFIER_DID configuration" + }); + } + + + // Build callback URI with session ID + const uri = `${hostUrl}${callbackURL}?sessionId=${sessionId}`; + + // Create authorization request for iden3 protocol + const request = auth.createAuthorizationRequest( + currentConfig.verification_description, + process.env.VERIFIER_DID, + uri + ); + + // Add proof request to scope + const proofRequest = createProofRequest(USE_CASE, sessionId, nullifierSessionID); + const scope = request.body.scope ?? []; + request.body.scope = [...scope, proofRequest]; + + // Store in Redis for later verification with fallback handling + const authStored = await redisService.setAuthRequest(sessionId, request); + const statusStored = await redisService.setVerificationStatus(proofRequest.id, "pending"); + + // Log Redis storage status for monitoring + if (!authStored || !statusStored) { + logger.warn('Redis storage failed, verification may not persist across restarts', { + sessionId, + authStored, + statusStored + }); + } + + logger.info('Verification request generated', { sessionId, useCase: USE_CASE }); + logger.logRequest(req, res, Date.now() - startTime); + + return res.status(200).json(request); + } catch (err) { + logger.error("Error in /api/verification-request", { error: err.message, stack: err.stack }); + return res.status(500).json({ error: "Internal server error" }); + } +}); + +// Health check endpoint for monitoring Redis and server status +app.get('/api/health', async (req, res) => { + try { + const redisHealthy = await redisService.healthCheck(); + const redisStatus = redisService.getStatus(); + const cacheStats = redisService.getCacheStats(); + + // Determine overall health + const hasMemoryFallback = cacheStats.totalEntries > 0; + const status = redisHealthy ? 'healthy' : (hasMemoryFallback ? 'degraded' : 'unhealthy'); + const statusCode = redisHealthy ? 200 : (hasMemoryFallback ? 206 : 503); + + return res.status(statusCode).json({ + status: status, + timestamp: new Date().toISOString(), + redis: { + connected: redisHealthy, + host: process.env.REDIS_HOST, + circuitBreaker: redisStatus.circuitBreakerOpen, + failures: redisStatus.failures, + reconnectAttempts: redisStatus.reconnectAttempts + }, + memoryCache: { + active: !redisHealthy && hasMemoryFallback, + size: cacheStats.totalEntries, + utilization: `${cacheStats.utilizationPercent}%`, + averageAge: `${Math.round(cacheStats.averageAgeMs / 1000)}s` + }, + server: { + port: port, + useCase: USE_CASE, + config: currentConfig.name + } + }); + } catch (error) { + console.error('Error in /api/health:', error); + return res.status(503).json({ + status: 'unhealthy', + error: error.message, + timestamp: new Date().toISOString() + }); + } +}); + +// Get verification status for a given request ID +app.get("/api/status/:id", async (req, res) => { + try { + const requestId = parseInt(req.params.id); + + if (isNaN(requestId) || requestId <= 0) { + return res.status(400).json({ + error: "Invalid request ID", + details: "Request ID must be a positive integer" + }); + } + + const status = await redisService.getVerificationStatus(requestId) || "not_found"; + + return res.status(200).json({ + requestId: requestId, + status: status, + }); + } catch (error) { + console.error("Error in /api/status:", error); + return res.status(500).json({ + error: "Internal server error", + details: process.env.NODE_ENV === 'development' ? error.message : undefined + }); + } +}); + +// Verify the proof after wallet sign-in callback +app.post("/api/callback", async (req, res) => { + const startTime = Date.now(); + + try { + // Validate session ID format (8-digit number) + const sessionId = parseInt(req.query.sessionId); + if (isNaN(sessionId) || sessionId < 10000000 || sessionId >= 100000000) { + return res.status(400).json({ + error: "Invalid session ID", + details: "Session ID must be an 8-digit number" + }); + } + + // Extract and validate JWT token from request body with size limit + const raw = await getRawBody(req, { + length: req.headers['content-length'], + limit: '1mb', // Limit raw body to 1MB for security + encoding: 'utf8' + }); + const tokenStr = raw.toString().trim(); + + // ⚠️ TEMPORARY DEBUG: Log token for testing (REMOVE IN PRODUCTION) + console.log('\n🔥🔥🔥 === TOKEN CAPTURE (For Testing) ==='); + console.log('Full Token:', tokenStr); + console.log('Token Length:', tokenStr.length, 'characters'); + console.log('===========================================\n'); + + + // Validate token length for security + if (tokenStr.length > 100000) { // ~100KB limit for JWT + return res.status(400).json({ + error: "Token too large", + details: "Authentication token exceeds maximum allowed size" + }); + } + + if (!tokenStr) { + return res.status(400).json({ + error: "Missing token", + details: "No authentication token provided in request body" + }); + } + + // Retrieve stored auth request from Redis + const authRequest = await redisService.getAuthRequest(sessionId); + if (!authRequest) { + return res.status(400).json({ + error: "Invalid session", + details: "Session ID not found or expired" + }); + } + + // Get cached verifier instance for performance + const verifier = await getVerifierInstance(); + + // Verify the proof with 5-minute state transition delay + const opts = { + AcceptedStateTransitionDelay: 5 * 60 * 1000, // 5 minutes + }; + const authResponse = await verifier.fullVerify(tokenStr, authRequest, opts); + + // Validate response structure + if (!authResponse || !authResponse.body || !authResponse.body.scope) { + return res.status(400).json({ + error: "Invalid authentication response", + details: "Malformed response from verifier" + }); + } + + // Find the nullifier proof in the response scope + const nullifierProof = authResponse.body.scope.find( + (s) => s.circuitId === "credentialAtomicQueryV3-beta.1" && s.id === sessionId + ); + if (!nullifierProof) { + return res.status(400).json({ + error: "Invalid proof", + details: "No valid nullifier proof found in response" + }); + } + + // ========================================================================= + // NULLIFIER ANTI-REPLAY LOGIC (PostgreSQL Integration) + // ========================================================================= + + console.log('\n🔐 === NULLIFIER EXTRACTION & VALIDATION ==='); + + // Extract the Nullifier Hash from public signals + // The nullifier is typically at index 1 in credentialAtomicQueryV3 public signals + const nullifierHash = nullifierProof.pub_signals?.[1]; + + if (!nullifierHash) { + console.error('⚠️ WARNING: Could not extract nullifier from proof public signals'); + console.error(' Available signals:', nullifierProof.pub_signals); + logger.error('Nullifier extraction failed', { + sessionId, + circuitId: nullifierProof.circuitId, + signalCount: nullifierProof.pub_signals?.length + }); + + return res.status(400).json({ + error: "Invalid proof structure", + details: "Could not extract nullifier from proof signals" + }); + } + + console.log(`✅ Nullifier extracted successfully`); + console.log(` Hash (first 30 chars): ${nullifierHash.substring(0, 30)}...`); + console.log(` Full length: ${nullifierHash.length} characters`); + + // Import database service + const dbService = require('./services/db-service'); + + // [GATEKEEPER] Check if this nullifier has already been used (Replay Attack Detection) + console.log('\n🛡️ Performing anti-replay check...'); + const isReplay = await dbService.isUserVerified(nullifierHash); + + if (isReplay) { + console.error('\n🚨 === REPLAY ATTACK BLOCKED ==='); + console.error(' Nullifier:', nullifierHash.substring(0, 30) + '...'); + console.error(' Session ID:', sessionId); + console.error(' This identity has already verified.'); + + logger.logSecurityEvent('REPLAY_ATTACK_BLOCKED', { + nullifierHash: nullifierHash.substring(0, 20) + '...', + sessionId, + timestamp: new Date().toISOString() + }, 'warn'); + + return res.status(400).json({ + error: "Already Verified", + details: "This identity has already been used for verification. Each user can only verify once." + }); + } + + console.log('✅ Anti-replay check passed: This is a new user'); + + // [THE LOCK] Store the nullifier to prevent future reuse + console.log('\n💾 Storing nullifier in database...'); + try { + await dbService.setUserVerification(nullifierHash, sessionId); + console.log('✅ Nullifier stored successfully - Future replays will be blocked'); + } catch (dbError) { + console.error('\n❌ CRITICAL: Failed to store nullifier in database!'); + console.error(' Error:', dbError.message); + console.error(' This could allow replay attacks if not resolved.'); + + logger.error('Database storage failed', { + error: dbError.message, + nullifierHash: nullifierHash.substring(0, 20) + '...', + sessionId + }); + + // Decision: Should we still allow the verification if DB save fails? + // Option 1: Fail the request (safer but could impact user experience) + // Option 2: Allow it but log critical error (current implementation) + // For production, consider Option 1 for maximum security + } + + console.log('\n🎉 === VERIFICATION SUCCESSFUL ===\n'); + + // ========================================================================= + // END NULLIFIER LOGIC + // ========================================================================= + + // Update verification status to success + const proofRequestId = authRequest.body.scope.find(s => s.circuitId === "credentialAtomicQueryV3-beta.1")?.id; + if (proofRequestId) { + await redisService.setVerificationStatus(proofRequestId, "success"); + } + + logger.logVerification(sessionId, 'success', authResponse.from); + logger.logRequest(req, res, Date.now() - startTime); + + return res + .status(200) + .set("Content-Type", "application/json") + .send(authResponse); + + } catch (error) { + logger.error("Error in /api/callback", { error: error.message, stack: error.stack, sessionId }); + + // Handle verification-specific errors differently + if (error.message && error.message.includes('verification')) { + return res.status(400).json({ + error: "Verification failed", + details: process.env.NODE_ENV === 'development' ? error.message : "Authentication verification failed" + }); + } + + // Generic server error response + return res.status(500).json({ + error: "Internal server error", + details: process.env.NODE_ENV === 'development' ? error.message : undefined + }); + } +}); + +// Validate required environment variables +function validateEnvironment() { + const required = [ + 'USE_CASE', + 'VERIFIER_DID', + 'NULLIFIER_SESSION_ID', + 'HOST_URL', + 'REDIS_HOST', + 'REDIS_PORT', + 'REDIS_USERNAME', + 'REDIS_PASSWORD' + ]; + + const missing = required.filter(key => !process.env[key]); + + if (missing.length > 0) { + logger.error('Missing required environment variables', { missing }); + throw new Error(`Missing required environment variables: ${missing.join(', ')}`); + } + + // Validate USE_CASE value + const validUseCases = ['POH', 'POU', 'POVH']; + if (!validUseCases.includes(process.env.USE_CASE?.toUpperCase())) { + throw new Error(`Invalid USE_CASE: ${process.env.USE_CASE}. Must be one of: ${validUseCases.join(', ')}`); + } + + // Validate URLs + try { + new URL(process.env.HOST_URL); + } catch { + throw new Error(`Invalid HOST_URL: ${process.env.HOST_URL}. Must be a valid URL.`); + } + + // Validate Redis port + const port = parseInt(process.env.REDIS_PORT); + if (isNaN(port) || port < 1 || port > 65535) { + throw new Error(`Invalid REDIS_PORT: ${process.env.REDIS_PORT}. Must be a number between 1-65535.`); + } + + logger.info('Environment validation passed', { + useCase: process.env.USE_CASE, + nodeEnv: process.env.NODE_ENV + }); +} + +// Initialize Redis connection and start the Express server +async function startServer() { + try { + // Validate environment variables first + validateEnvironment(); + + // Validate production configuration + if (process.env.NODE_ENV === 'production') { + const origins = process.env.ALLOWED_ORIGINS || ''; + if (origins.includes('your-production-domain.com') || origins.includes('your-app-domain.com')) { + logger.logSecurityEvent('PRODUCTION_CONFIG_WARNING', { + message: 'Using placeholder domains in production CORS configuration', + allowedOrigins: origins, + recommendation: 'Update ALLOWED_ORIGINS environment variable with real domains' + }, 'error'); + } + + // Check for placeholder credentials in production + if (process.env.REDIS_PASSWORD?.includes('your-secure-redis-password-here')) { + logger.logSecurityEvent('PRODUCTION_SECURITY_WARNING', { + message: 'Using placeholder Redis credentials in production', + recommendation: 'Update Redis credentials with real values' + }, 'error'); + } + } + + // Attempt Redis connection but don't fail startup if unavailable + try { + await redisService.connect(); + logger.info('Redis connected successfully'); + } catch (redisError) { + logger.warn('Redis connection failed at startup, running in degraded mode', { + error: redisError.message + }); + // Server will still start but with degraded functionality + } + + const server = app.listen(port, () => { + logger.info('Server started successfully', { + port, + useCase: USE_CASE, + config: currentConfig.name, + nodeEnv: process.env.NODE_ENV, + corsMode: process.env.NODE_ENV === 'production' ? 'restricted' : 'permissive', + allowedOrigins: process.env.NODE_ENV === 'production' ? allowedOrigins.length : 'localhost', + redisConnected: redisService.isHealthy() + }); + }); + return server; + } catch (error) { + logger.error('Failed to start server', { error: error.message, stack: error.stack }); + process.exit(1); + } +} + +// Start the application +(async () => { + const server = await startServer(); +})(); + +// Graceful shutdown handlers +process.on('SIGTERM', async () => { + logger.info('Received SIGTERM, shutting down gracefully'); + server.close(async () => { + logger.info('HTTP server closed'); + await redisService.disconnect(); + logger.info('Process terminated gracefully'); + process.exit(0); + }); +}); + +process.on('SIGINT', async () => { + logger.info('Received SIGINT, shutting down gracefully'); + server.close(async () => { + logger.info('HTTP server closed'); + await redisService.disconnect(); + logger.info('Process terminated gracefully'); + process.exit(0); + }); +}); + diff --git a/Integration-for-verifiers (Billions Wallet)/src/services/db-service.js b/Integration-for-verifiers (Billions Wallet)/src/services/db-service.js new file mode 100644 index 00000000..4d393c18 --- /dev/null +++ b/Integration-for-verifiers (Billions Wallet)/src/services/db-service.js @@ -0,0 +1,89 @@ +const { Pool } = require('pg'); + +// PostgreSQL connection pool +const pool = new Pool({ + connectionString: process.env.DATABASE_URL, + ssl: { rejectUnauthorized: false } // Required for most cloud DBs +}); + +// Log connection status +pool.on('connect', () => { + console.log('✅ PostgreSQL pool connected'); +}); + +pool.on('error', (err) => { + console.error('❌ PostgreSQL pool error:', err); +}); + +module.exports = { + // Check if user exists (Anti-Replay Check) + async isUserVerified(nullifierHash) { + console.log(`🔍 Checking if nullifier exists in database: ${nullifierHash.substring(0, 20)}...`); + + try { + const res = await pool.query( + 'SELECT * FROM verified_nullifiers WHERE hash = $1', + [nullifierHash] + ); + + const isVerified = res.rows.length > 0; + + if (isVerified) { + console.log(`⚠️ REPLAY DETECTED: Nullifier already exists in database`); + console.log(` Hash: ${nullifierHash.substring(0, 20)}...`); + console.log(` Original verification: ${res.rows[0].timestamp}`); + } else { + console.log(`✅ Nullifier NOT FOUND in database (New user)`); + } + + return isVerified; + } catch (err) { + console.error('❌ Database Query Error (isUserVerified):', err.message); + console.error(' Stack:', err.stack); + console.error(' Nullifier Hash:', nullifierHash); + return false; // Fail safe - allow verification on DB error + } + }, + + // Save the user verification + async setUserVerification(nullifierHash, sessionId) { + console.log(`💾 Attempting to store nullifier in database...`); + console.log(` Hash: ${nullifierHash.substring(0, 20)}...`); + console.log(` Session ID: ${sessionId}`); + + try { + await pool.query( + 'INSERT INTO verified_nullifiers (hash, session_id) VALUES ($1, $2)', + [nullifierHash, sessionId] + ); + + console.log(`✅ SUCCESS: Nullifier permanently stored in database`); + console.log(` This user cannot verify again with this nullifier`); + return true; + } catch (err) { + console.error('❌ Database Insert Error (setUserVerification):', err.message); + console.error(' Code:', err.code); + console.error(' Detail:', err.detail); + console.error(' Stack:', err.stack); + + // Check if it's a duplicate key error + if (err.code === '23505') { + console.error(' DUPLICATE KEY: This nullifier already exists!'); + } + + throw err; + } + }, + + // Health check for database connection + async healthCheck() { + try { + const res = await pool.query('SELECT NOW()'); + console.log('✅ Database health check passed:', res.rows[0].now); + return true; + } catch (err) { + console.error('❌ Database health check failed:', err.message); + return false; + } + } +}; \ No newline at end of file diff --git a/Integration-for-verifiers (Billions Wallet)/src/services/logger.js b/Integration-for-verifiers (Billions Wallet)/src/services/logger.js new file mode 100644 index 00000000..23de788a --- /dev/null +++ b/Integration-for-verifiers (Billions Wallet)/src/services/logger.js @@ -0,0 +1,90 @@ +const winston = require('winston'); + +// Production-grade logging configuration +const logger = winston.createLogger({ + level: process.env.LOG_LEVEL || 'info', + format: winston.format.combine( + winston.format.timestamp(), + winston.format.errors({ stack: true }), + winston.format.json() + ), + defaultMeta: { + service: 'billions-verifier', + version: process.env.npm_package_version || '1.0.0' + }, + transports: [ + // Error logs to separate file + new winston.transports.File({ + filename: 'logs/error.log', + level: 'error', + maxsize: 5242880, // 5MB + maxFiles: 5, + tailable: true + }), + // All logs to combined file + new winston.transports.File({ + filename: 'logs/combined.log', + maxsize: 5242880, // 5MB + maxFiles: 10, + tailable: true + }) + ], +}); + +// Add console transport for development +if (process.env.NODE_ENV !== 'production') { + logger.add(new winston.transports.Console({ + format: winston.format.combine( + winston.format.colorize(), + winston.format.simple() + ) + })); +} + +// Production console logging (structured JSON) +if (process.env.NODE_ENV === 'production') { + logger.add(new winston.transports.Console({ + format: winston.format.json() + })); +} + +// Helper methods for common log patterns +logger.logRequest = (req, res, duration) => { + logger.info('HTTP Request', { + method: req.method, + url: req.url, + userAgent: req.get('User-Agent'), + ip: req.ip, + statusCode: res.statusCode, + duration: `${duration}ms` + }); +}; + +logger.logVerification = (sessionId, status, userDid = null) => { + logger.info('Verification Event', { + sessionId, + status, + userDid, + useCase: process.env.USE_CASE + }); +}; + +logger.logRedisOperation = (operation, key, success) => { + logger.debug('Redis Operation', { + operation, + key, + success, + timestamp: new Date().toISOString() + }); +}; + +logger.logSecurityEvent = (event, details, severity = 'warn') => { + logger[severity]('Security Event', { + event, + details, + timestamp: new Date().toISOString(), + alerting: severity === 'error' + }); +}; + +module.exports = logger; \ No newline at end of file diff --git a/Integration-for-verifiers (Billions Wallet)/src/services/redis-service.js b/Integration-for-verifiers (Billions Wallet)/src/services/redis-service.js new file mode 100644 index 00000000..d4255457 --- /dev/null +++ b/Integration-for-verifiers (Billions Wallet)/src/services/redis-service.js @@ -0,0 +1,399 @@ +const { createClient } = require('redis'); + +// Redis service for session management and verification storage +// Provides production-ready persistent storage with circuit breaker and in-memory fallback +class RedisService { + constructor() { + this.client = null; + this.isConnected = false; + this.reconnectAttempts = 0; + this.maxReconnectAttempts = 10; + this.reconnectDelay = 1000; // Start with 1 second + this.maxReconnectDelay = 30000; // Max 30 seconds + this.circuitBreakerFailures = 0; + this.circuitBreakerThreshold = 5; + this.circuitBreakerOpen = false; + this.circuitBreakerResetTime = 60000; // 1 minute + this.lastCircuitBreakerOpen = null; + + // In-memory fallback cache + this.memoryCache = new Map(); + this.maxCacheSize = 10000; // Limit memory usage + this.cacheCleanupInterval = 5 * 60 * 1000; // 5 minutes + this.initializeMemoryCache(); + } + + // Establish connection to Redis server + async connect() { + try { + // Create Redis client with environment configuration + this.client = createClient({ + username: process.env.REDIS_USERNAME, + password: process.env.REDIS_PASSWORD, + socket: { + host: process.env.REDIS_HOST, + port: parseInt(process.env.REDIS_PORT), + } + }); + + // Set up connection event handlers with recovery mechanisms + this.client.on('error', (err) => { + console.error('Redis Client Error:', err); + this.isConnected = false; + this.handleConnectionFailure(); + }); + + this.client.on('connect', () => { + console.log('✅ Redis connected successfully'); + this.isConnected = true; + this.reconnectAttempts = 0; + this.resetCircuitBreaker(); + }); + + this.client.on('disconnect', () => { + console.log('❌ Redis disconnected'); + this.isConnected = false; + this.scheduleReconnection(); + }); + + this.client.on('reconnecting', () => { + console.log('🔄 Redis reconnecting...'); + }); + + this.client.on('end', () => { + console.log('Redis connection ended'); + this.isConnected = false; + }); + + await this.client.connect(); + await this.client.ping(); + console.log('✅ Redis connection established and tested'); + + } catch (error) { + console.error('❌ Failed to connect to Redis:', error); + this.handleConnectionFailure(); + throw error; + } + } + + // Gracefully disconnect from Redis + async disconnect() { + if (this.client && this.isConnected) { + await this.client.quit(); + this.isConnected = false; + console.log('✅ Redis disconnected gracefully'); + } + } + + // Handle connection failures with circuit breaker + handleConnectionFailure() { + this.circuitBreakerFailures++; + + if (this.circuitBreakerFailures >= this.circuitBreakerThreshold) { + this.openCircuitBreaker(); + } + } + + // Open circuit breaker to prevent cascading failures + openCircuitBreaker() { + this.circuitBreakerOpen = true; + this.lastCircuitBreakerOpen = Date.now(); + console.log(`⚡ Circuit breaker opened after ${this.circuitBreakerFailures} failures`); + + // Schedule circuit breaker reset + setTimeout(() => { + this.resetCircuitBreaker(); + }, this.circuitBreakerResetTime); + } + + // Reset circuit breaker for retry attempts + resetCircuitBreaker() { + if (this.circuitBreakerOpen) { + console.log('🔄 Circuit breaker reset - allowing new attempts'); + } + this.circuitBreakerOpen = false; + this.circuitBreakerFailures = 0; + this.lastCircuitBreakerOpen = null; + } + + // Schedule reconnection with exponential backoff + scheduleReconnection() { + if (this.reconnectAttempts >= this.maxReconnectAttempts) { + console.error(`❌ Max reconnection attempts (${this.maxReconnectAttempts}) reached`); + return; + } + + const delay = Math.min( + this.reconnectDelay * Math.pow(2, this.reconnectAttempts), + this.maxReconnectDelay + ); + + this.reconnectAttempts++; + console.log(`⏰ Scheduling reconnection attempt ${this.reconnectAttempts} in ${delay}ms`); + + setTimeout(async () => { + try { + await this.connect(); + } catch (error) { + console.error('Reconnection attempt failed:', error); + } + }, delay); + } + + // Initialize memory cache with cleanup + initializeMemoryCache() { + // Periodic cleanup of expired entries + setInterval(() => { + this.cleanupExpiredEntries(); + }, this.cacheCleanupInterval); + + console.log('🧠 In-memory fallback cache initialized'); + } + + // Clean up expired entries from memory cache + cleanupExpiredEntries() { + const now = Date.now(); + let cleanedCount = 0; + + for (const [key, entry] of this.memoryCache.entries()) { + if (entry.expiresAt && entry.expiresAt < now) { + this.memoryCache.delete(key); + cleanedCount++; + } + } + + if (cleanedCount > 0) { + console.log(`🧹 Cleaned ${cleanedCount} expired entries from memory cache`); + } + } + + // Set value in memory cache with TTL + setMemoryCache(key, value, ttl = 3600) { + // Enforce cache size limit + if (this.memoryCache.size >= this.maxCacheSize) { + // Remove oldest entry (LRU-like behavior) + const firstKey = this.memoryCache.keys().next().value; + this.memoryCache.delete(firstKey); + } + + const expiresAt = ttl > 0 ? Date.now() + (ttl * 1000) : null; + this.memoryCache.set(key, { + value, + expiresAt, + createdAt: Date.now() + }); + + console.log(`🧠 Stored in memory cache: ${key} (TTL: ${ttl}s)`); + } + + // Get value from memory cache + getMemoryCache(key) { + const entry = this.memoryCache.get(key); + + if (!entry) { + return null; + } + + // Check if expired + if (entry.expiresAt && entry.expiresAt < Date.now()) { + this.memoryCache.delete(key); + return null; + } + + console.log(`🧠 Retrieved from memory cache: ${key}`); + return entry.value; + } + + // Execute Redis operation with in-memory fallback + async executeWithFallback(operation, fallbackKey = null, fallbackValue = null) { + // Check circuit breaker + if (this.circuitBreakerOpen) { + console.log('⚡ Circuit breaker open, using memory cache'); + return fallbackKey ? this.getMemoryCache(fallbackKey) : fallbackValue; + } + + // Check connection + if (!this.isConnected || !this.client) { + console.log('⚠️ Redis not connected, using memory cache'); + return fallbackKey ? this.getMemoryCache(fallbackKey) : fallbackValue; + } + + try { + const result = await operation(); + // Reset failure count on successful operation + this.circuitBreakerFailures = Math.max(0, this.circuitBreakerFailures - 1); + return result; + } catch (error) { + console.error('❌ Redis operation failed, using memory cache:', error); + this.handleConnectionFailure(); + return fallbackKey ? this.getMemoryCache(fallbackKey) : fallbackValue; + } + } + + // Store authentication request with TTL (default: 1 hour) + async setAuthRequest(sessionId, authRequest, ttl = 3600) { + const key = `auth_request:${sessionId}`; + + // Always store in memory cache as backup + this.setMemoryCache(key, authRequest, ttl); + + return await this.executeWithFallback(async () => { + await this.client.setEx(key, ttl, JSON.stringify(authRequest)); + console.log(`✅ Auth request stored in Redis for session ${sessionId}`); + return true; + }, null, true); // Return true even if Redis fails (stored in memory) + } + + // Retrieve authentication request by session ID + async getAuthRequest(sessionId) { + const key = `auth_request:${sessionId}`; + + return await this.executeWithFallback(async () => { + const result = await this.client.get(key); + const authRequest = result ? JSON.parse(result) : null; + + // Update memory cache if found in Redis + if (authRequest) { + this.setMemoryCache(key, authRequest, 3600); + } + + return authRequest; + }, key, null); // Fallback to memory cache using key + } + + // Remove authentication request from storage + async deleteAuthRequest(sessionId) { + try { + const key = `auth_request:${sessionId}`; + await this.client.del(key); + console.log(`✅ Auth request deleted for session ${sessionId}`); + } catch (error) { + console.error(`❌ Failed to delete auth request for session ${sessionId}:`, error); + } + } + + // Store user verification data with TTL (default: 24 hours) + async setUserVerification(nullifier, verificationData, ttl = 86400) { + try { + const key = `user_verification:${nullifier}`; + await this.client.setEx(key, ttl, JSON.stringify(verificationData)); + console.log(`✅ User verification stored for nullifier ${nullifier}`); + } catch (error) { + console.error(`❌ Failed to store user verification for nullifier ${nullifier}:`, error); + throw error; + } + } + + // Retrieve user verification data by nullifier + async getUserVerification(nullifier) { + try { + const key = `user_verification:${nullifier}`; + const result = await this.client.get(key); + return result ? JSON.parse(result) : null; + } catch (error) { + console.error(`❌ Failed to get user verification for nullifier ${nullifier}:`, error); + return null; + } + } + + // Check if user is already verified (anti-replay protection) + async isUserVerified(nullifier) { + try { + const verification = await this.getUserVerification(nullifier); + return verification && verification.verified === true; + } catch (error) { + console.error(`❌ Failed to check user verification for nullifier ${nullifier}:`, error); + return false; + } + } + + // Store verification status for polling (default TTL: 1 hour) + async setVerificationStatus(requestId, status, ttl = 3600) { + const key = `verification_status:${requestId}`; + + // Always store in memory cache as backup + this.setMemoryCache(key, status, ttl); + + return await this.executeWithFallback(async () => { + await this.client.setEx(key, ttl, status); + console.log(`✅ Verification status set in Redis for request ${requestId}: ${status}`); + return true; + }, null, true); // Return true even if Redis fails (stored in memory) + } + + // Get current verification status for frontend polling + async getVerificationStatus(requestId) { + const key = `verification_status:${requestId}`; + + return await this.executeWithFallback(async () => { + const result = await this.client.get(key); + + // Update memory cache if found in Redis + if (result) { + this.setMemoryCache(key, result, 3600); + } + + return result; + }, key, null); // Fallback to memory cache using key + } + + // Health check for monitoring systems + async healthCheck() { + try { + if (!this.client || !this.isConnected) { + return false; + } + const pong = await this.client.ping(); + return pong === 'PONG'; + } catch (error) { + console.error('❌ Redis health check failed:', error); + return false; + } + } + + // Quick connection status check + isHealthy() { + return this.isConnected && !this.circuitBreakerOpen; + } + + // Get detailed Redis service status for monitoring + getStatus() { + return { + connected: this.isConnected, + circuitBreakerOpen: this.circuitBreakerOpen, + failures: this.circuitBreakerFailures, + reconnectAttempts: this.reconnectAttempts, + lastFailure: this.lastCircuitBreakerOpen, + memoryCache: { + size: this.memoryCache.size, + maxSize: this.maxCacheSize, + utilizationPercent: Math.round((this.memoryCache.size / this.maxCacheSize) * 100) + } + }; + } + + // Get memory cache statistics + getCacheStats() { + const now = Date.now(); + let expiredCount = 0; + let totalAge = 0; + + for (const [key, entry] of this.memoryCache.entries()) { + if (entry.expiresAt && entry.expiresAt < now) { + expiredCount++; + } + totalAge += now - entry.createdAt; + } + + return { + totalEntries: this.memoryCache.size, + expiredEntries: expiredCount, + averageAgeMs: this.memoryCache.size > 0 ? Math.round(totalAge / this.memoryCache.size) : 0, + utilizationPercent: Math.round((this.memoryCache.size / this.maxCacheSize) * 100) + }; + } +} + +// Export singleton instance for application use +const redisService = new RedisService(); +module.exports = redisService; \ No newline at end of file From 50b2f1f1173877f54ddea214e86007c5b31808b8 Mon Sep 17 00:00:00 2001 From: Nityam Tripathi Date: Wed, 18 Mar 2026 14:07:40 +0530 Subject: [PATCH 2/3] fix-db --- .../package-lock.json | 99 ++-- .../package.json | 1 + .../public/index.js | 39 +- .../src/config/verification-configs.js | 47 +- .../src/index.js | 454 ++++-------------- .../src/services/db-service.js | 76 +-- .../src/services/logger.js | 27 +- .../src/services/redis-service.js | 198 ++------ 8 files changed, 217 insertions(+), 724 deletions(-) diff --git a/Integration-for-verifiers (Billions Wallet)/package-lock.json b/Integration-for-verifiers (Billions Wallet)/package-lock.json index 11536cb4..1bb75f89 100644 --- a/Integration-for-verifiers (Billions Wallet)/package-lock.json +++ b/Integration-for-verifiers (Billions Wallet)/package-lock.json @@ -10,6 +10,7 @@ "dependencies": { "@0xpolygonid/js-sdk": "^1.31.1", "@iden3/js-iden3-auth": "1.10.1", + "async-mutex": "^0.5.0", "cors": "^2.8.5", "dotenv": "^16.5.0", "express": "^4.18.2", @@ -877,15 +878,13 @@ "version": "0.0.2", "resolved": "https://registry.npmjs.org/@iden3/bigarray/-/bigarray-0.0.2.tgz", "integrity": "sha512-Xzdyxqm1bOFF6pdIsiHLLl3HkSLjbhqJHVyqaTxXt3RqXBEnmsUmEW47H7VOi/ak7TdkRpNkxjyK5Zbkm+y52g==", - "license": "GPL-3.0", - "peer": true + "license": "GPL-3.0" }, "node_modules/@iden3/binfileutils": { "version": "0.0.12", "resolved": "https://registry.npmjs.org/@iden3/binfileutils/-/binfileutils-0.0.12.tgz", "integrity": "sha512-naAmzuDufRIcoNfQ1d99d7hGHufLA3wZSibtr4dMe6ZeiOPV1KwOZWTJ1YVz4HbaWlpDuzVU72dS4ATQS4PXBQ==", "license": "GPL-3.0", - "peer": true, "dependencies": { "fastfile": "0.0.20", "ffjavascript": "^0.3.0" @@ -896,6 +895,7 @@ "resolved": "https://registry.npmjs.org/@iden3/js-crypto/-/js-crypto-1.3.1.tgz", "integrity": "sha512-zZefKE2+GpEMPPMPDKqNBIvKZH8ubs/+nwOx2+rRZj+gSI1P49e9Nz6vGb/4YGTTHdPU2/L4lReQ5p/bF23c6A==", "license": "AGPL-3.0", + "peer": true, "dependencies": { "@noble/hashes": "^1.8.0" } @@ -1092,6 +1092,7 @@ "resolved": "https://registry.npmjs.org/@iden3/js-iden3-core/-/js-iden3-core-1.7.0.tgz", "integrity": "sha512-rNTrrhfzMwIBWYGGIOQ9yNpuSPvKJxryKiiP6l0+oCguaDoDo18rrcz2oBOfiO0Q7wDb41v73sLh4pwOlzCHpg==", "license": "MIT or Apache-2.0", + "peer": true, "peerDependencies": { "@iden3/js-crypto": "1.3.1" } @@ -1102,6 +1103,7 @@ "integrity": "sha512-RwRG/+9bRKPKbdXyIvCI+pEnpSz/rBS7jCwX2B6Ocn83Sxpc9hKgRNglMeIk41bQMRib5q9dcNK1M1v9C1I8rg==", "hasInstallScript": true, "license": "MIT or Apache-2.0", + "peer": true, "dependencies": { "@js-temporal/polyfill": "0.5.1", "jsonld": "8.3.2", @@ -1139,6 +1141,7 @@ "resolved": "https://registry.npmjs.org/@iden3/js-jwz/-/js-jwz-1.10.0.tgz", "integrity": "sha512-y4gyloZhRxe7c1EuYrL7BKYvrZ3CILva3YsI2djB5I2haVYHL+9Uiw7K/6NCkH/Daq9+a7mzoY9VzHWCN1lgFw==", "license": "AGPL-3.0", + "peer": true, "peerDependencies": { "@iden3/js-crypto": "1.3.1", "@iden3/js-iden3-core": "1.7.0", @@ -1154,6 +1157,7 @@ "resolved": "https://registry.npmjs.org/@iden3/js-merkletree/-/js-merkletree-1.5.0.tgz", "integrity": "sha512-zGaGxU2o8NtgvUV7gkS8NUZv7S5zNvDmmPCaRFOLgDmbqlAf93WRBItq6E2y84czZpAYGxFZOb9+Dq3JgwFSSw==", "license": "MIT or Apache-2.0", + "peer": true, "peerDependencies": { "@iden3/js-crypto": "1.3.1", "idb-keyval": "6.2.2" @@ -1231,6 +1235,7 @@ "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.9.2.tgz", "integrity": "sha512-HxngEd2XUcg9xi20JkwlLCtYwfoFw4JGkuZpT+WlsPD4gB/cxkvTD8fSsoAnphGZhFdZYKeQIPCuFlWPm1uE0g==", "license": "MIT", + "peer": true, "dependencies": { "@noble/hashes": "1.8.0" }, @@ -1267,6 +1272,7 @@ "resolved": "https://registry.npmjs.org/@redis/client/-/client-1.6.1.tgz", "integrity": "sha512-/KCsg3xSlR+nCK8/8ZYSknYxvXHwubJrU82F3Lm1Fp6789VQ0/3RJKfsmRXjqfaTA++23CvC3hqmqe/2GEt6Kw==", "license": "MIT", + "peer": true, "dependencies": { "cluster-key-slot": "1.1.2", "generic-pool": "3.9.0", @@ -1436,6 +1442,7 @@ "integrity": "sha512-B2egV9wALML1JCpv3VQoQ+yesQKAmNMBIAY7OteVrikcOcAkWm+dGL6qpeCktPjAv6N1JLnhbNiqS35UpFyBsQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "undici-types": "~6.21.0" } @@ -1620,6 +1627,15 @@ "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", "license": "MIT" }, + "node_modules/async-mutex": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/async-mutex/-/async-mutex-0.5.0.tgz", + "integrity": "sha512-1A94B18jkJ3DYq284ohPxoXbfTA5HsQ7/Mf4DEhcyLx3Bz27Rh59iScbB6EPiP+B+joue6YCxcMXSbFC1tZKwA==", + "license": "MIT", + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/at-least-node": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", @@ -1633,8 +1649,7 @@ "version": "1.6.7", "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.7.tgz", "integrity": "sha512-OnAYlL5b7LEkALw87fUVafQw5rVR9RjwGd4KUwNQ6DrrNmaVaUCgLipfVlzrPQ4tWOR9P0IXGNOx50jYCCdSJg==", - "license": "Apache-2.0", - "peer": true + "license": "Apache-2.0" }, "node_modules/balanced-match": { "version": "1.0.2", @@ -1673,7 +1688,6 @@ "resolved": "https://registry.npmjs.org/bfj/-/bfj-7.1.0.tgz", "integrity": "sha512-I6MMLkn+anzNdCUp9hMRyui1HaNEUCco50lxbvNS4+EyXg8lN3nJ48PjPWtbH8UVS9CuMoaKE9U2V3l29DaRQw==", "license": "MIT", - "peer": true, "dependencies": { "bluebird": "^3.7.2", "check-types": "^11.2.3", @@ -1690,7 +1704,6 @@ "resolved": "https://registry.npmjs.org/blake2b-wasm/-/blake2b-wasm-2.4.0.tgz", "integrity": "sha512-S1kwmW2ZhZFFFOghcx73+ZajEfKBqhP82JMssxtLVMxlaPea1p9uoLiUZ5WYyHn0KddwbLc+0vh4wR0KBNoT5w==", "license": "MIT", - "peer": true, "dependencies": { "b4a": "^1.0.1", "nanoassert": "^2.0.0" @@ -1700,8 +1713,7 @@ "version": "3.7.2", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/bn.js": { "version": "5.2.2", @@ -1885,8 +1897,7 @@ "version": "11.2.3", "resolved": "https://registry.npmjs.org/check-types/-/check-types-11.2.3.tgz", "integrity": "sha512-+67P1GkJRaxQD6PKK0Et9DhwQB+vGg3PM5+aavopCpZT1lj9jeqfvpgTLAWErNj8qApkkmXlu/Ug74kmhagkXg==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/ci-info": { "version": "3.9.0", @@ -1908,7 +1919,6 @@ "resolved": "https://registry.npmjs.org/circom_runtime/-/circom_runtime-0.1.28.tgz", "integrity": "sha512-ACagpQ7zBRLKDl5xRZ4KpmYIcZDUjOiNRuxvXLqhnnlLSVY1Dbvh73TI853nqoR0oEbihtWmMSjgc5f+pXf/jQ==", "license": "Apache-2.0", - "peer": true, "dependencies": { "ffjavascript": "0.3.1" }, @@ -2096,8 +2106,7 @@ "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/define-data-property": { "version": "1.1.4", @@ -2205,7 +2214,6 @@ "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", "license": "Apache-2.0", - "peer": true, "dependencies": { "jake": "^10.8.5" }, @@ -2293,7 +2301,6 @@ "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz", "integrity": "sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==", "license": "BSD-2-Clause", - "peer": true, "dependencies": { "esprima": "^4.0.1", "estraverse": "^4.2.0", @@ -2316,7 +2323,6 @@ "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", "license": "BSD-2-Clause", - "peer": true, "bin": { "esparse": "bin/esparse.js", "esvalidate": "bin/esvalidate.js" @@ -2329,7 +2335,6 @@ "version": "1.2.2", "resolved": "https://registry.npmjs.org/esprima/-/esprima-1.2.2.tgz", "integrity": "sha512-+JpPZam9w5DuJ3Q67SqsMGtiHKENSMRVoxvArfJZK01/BfLEObtZ6orJa/MtoGNR/rfMgp5837T41PAmTwAv/A==", - "peer": true, "bin": { "esparse": "bin/esparse.js", "esvalidate": "bin/esvalidate.js" @@ -2343,7 +2348,6 @@ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", "license": "BSD-2-Clause", - "peer": true, "engines": { "node": ">=4.0" } @@ -2353,7 +2357,6 @@ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "license": "BSD-2-Clause", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -2463,6 +2466,7 @@ "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", "license": "MIT", + "peer": true, "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", @@ -2529,15 +2533,13 @@ "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/fastfile": { "version": "0.0.20", "resolved": "https://registry.npmjs.org/fastfile/-/fastfile-0.0.20.tgz", "integrity": "sha512-r5ZDbgImvVWCP0lA/cGNgQcZqR+aYdFx3u+CtJqUE510pBUVGMn4ulL/iRTI4tACTYsNJ736uzFxEBXesPAktA==", - "license": "GPL-3.0", - "peer": true + "license": "GPL-3.0" }, "node_modules/fecha": { "version": "4.2.3", @@ -2585,7 +2587,6 @@ "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", "license": "Apache-2.0", - "peer": true, "dependencies": { "minimatch": "^5.0.1" } @@ -2595,7 +2596,6 @@ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "license": "MIT", - "peer": true, "dependencies": { "balanced-match": "^1.0.0" } @@ -2605,7 +2605,6 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", "license": "ISC", - "peer": true, "dependencies": { "brace-expansion": "^2.0.1" }, @@ -2883,7 +2882,6 @@ "resolved": "https://registry.npmjs.org/hoopy/-/hoopy-0.1.4.tgz", "integrity": "sha512-HRcs+2mr52W0K+x8RzcLzuPPmVIKMSv97RGHy0Ea9y/mpcaK+xTrjICA04KAHi4GRzxliNqNJEFYWHghy3rSfQ==", "license": "MIT", - "peer": true, "engines": { "node": ">= 6.0.0" } @@ -2920,7 +2918,8 @@ "version": "6.2.2", "resolved": "https://registry.npmjs.org/idb-keyval/-/idb-keyval-6.2.2.tgz", "integrity": "sha512-yjD9nARJ/jb1g+CvD0tlhUHOrJ9Sy0P8T9MF3YaLlHnSRpwPfpTX0XIvpmw3gAJUmEu3FiICLBDPXVwyEvrleg==", - "license": "Apache-2.0" + "license": "Apache-2.0", + "peer": true }, "node_modules/ieee754": { "version": "1.2.1", @@ -3033,7 +3032,6 @@ "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.2.tgz", "integrity": "sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==", "license": "Apache-2.0", - "peer": true, "dependencies": { "async": "^3.2.3", "chalk": "^4.0.2", @@ -3131,7 +3129,6 @@ "resolved": "https://registry.npmjs.org/jsonpath/-/jsonpath-1.1.1.tgz", "integrity": "sha512-l6Cg7jRpixfbgoWgkrl77dgEj8RPvND0wMH6TwQmi9Qs4TFfS9u5cUFnbeKTwj5ga5Y3BTGGNI28k117LJ009w==", "license": "MIT", - "peer": true, "dependencies": { "esprima": "1.2.2", "static-eval": "2.0.2", @@ -3158,6 +3155,7 @@ "resolved": "https://registry.npmjs.org/ky/-/ky-0.33.3.tgz", "integrity": "sha512-CasD9OCEQSFIam2U8efFK81Yeg8vNMTBUqtMOHlrcWQHqUX3HeCl9Dr31u4toV7emlH8Mymk5+9p0lL6mKb/Xw==", "license": "MIT", + "peer": true, "engines": { "node": ">=14.16" }, @@ -3195,7 +3193,6 @@ "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==", "license": "MIT", - "peer": true, "dependencies": { "prelude-ls": "~1.1.2", "type-check": "~0.3.2" @@ -3231,8 +3228,7 @@ "version": "1.2.15", "resolved": "https://registry.npmjs.org/logplease/-/logplease-1.2.15.tgz", "integrity": "sha512-jLlHnlsPSJjpwUfcNyUxXCl33AYg2cHhIf9QhGL2T4iPT0XPB+xP1LRKFPgIg1M/sg9kAJvy94w9CzBNrfnstA==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/lru-cache": { "version": "6.0.0", @@ -3411,8 +3407,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/nanoassert/-/nanoassert-2.0.0.tgz", "integrity": "sha512-7vO7n28+aYO4J+8w96AzhmU8G+Y/xpPDJz/se19ICsqj/momRbb9mh9ZUtkoJ5X3nTnPdhEJyc0qnM6yAsHBaA==", - "license": "ISC", - "peer": true + "license": "ISC" }, "node_modules/negotiator": { "version": "0.6.3", @@ -3542,7 +3537,6 @@ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", "license": "MIT", - "peer": true, "dependencies": { "deep-is": "~0.1.3", "fast-levenshtein": "~2.0.6", @@ -3632,6 +3626,7 @@ "resolved": "https://registry.npmjs.org/pg/-/pg-8.18.0.tgz", "integrity": "sha512-xqrUDL1b9MbkydY/s+VZ6v+xiMUmOUk7SS9d/1kpyQxoJ6U9AO1oIJyUWVZojbfe5Cc/oluutcgFG4L9RDP1iQ==", "license": "MIT", + "peer": true, "dependencies": { "pg-connection-string": "^2.11.0", "pg-pool": "^3.11.0", @@ -3771,7 +3766,6 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==", - "peer": true, "engines": { "node": ">= 0.8.0" } @@ -3865,7 +3859,6 @@ "resolved": "https://registry.npmjs.org/r1csfile/-/r1csfile-0.0.48.tgz", "integrity": "sha512-kHRkKUJNaor31l05f2+RFzvcH5XSa7OfEfd/l4hzjte6NL6fjRkSMfZ4BjySW9wmfdwPOtq3mXurzPvPGEf5Tw==", "license": "GPL-3.0", - "peer": true, "dependencies": { "@iden3/bigarray": "0.0.2", "@iden3/binfileutils": "0.0.12", @@ -3878,7 +3871,6 @@ "resolved": "https://registry.npmjs.org/ffjavascript/-/ffjavascript-0.3.0.tgz", "integrity": "sha512-l7sR5kmU3gRwDy8g0Z2tYBXy5ttmafRPFOqY7S6af5cq51JqJWt5eQ/lSR/rs2wQNbDYaYlQr5O+OSUf/oMLoQ==", "license": "GPL-3.0", - "peer": true, "dependencies": { "wasmbuilder": "0.0.16", "wasmcurves": "0.2.2", @@ -4259,8 +4251,7 @@ "version": "0.8.0", "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz", "integrity": "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/source-map": { "version": "0.6.1", @@ -4268,7 +4259,6 @@ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "license": "BSD-3-Clause", "optional": true, - "peer": true, "engines": { "node": ">=0.10.0" } @@ -4296,7 +4286,6 @@ "resolved": "https://registry.npmjs.org/static-eval/-/static-eval-2.0.2.tgz", "integrity": "sha512-N/D219Hcr2bPjLxPiV+TQE++Tsmrady7TqAJugLy7Xk1EumfDWS/f5dtBbkRCGE7wKKXuYockQoj8Rm2/pVKyg==", "license": "MIT", - "peer": true, "dependencies": { "escodegen": "^1.8.1" } @@ -4383,8 +4372,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/tryer/-/tryer-1.0.1.tgz", "integrity": "sha512-c3zayb8/kWWpycWYg87P71E1S1ZL6b6IJxfb5fvsUgsf0S2MVGaDhDXXjDMpdCpfWXqptc+4mXwmiy1ypXqRAA==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/ts-node": { "version": "10.9.2", @@ -4430,12 +4418,17 @@ } } }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, "node_modules/type-check": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==", "license": "MIT", - "peer": true, "dependencies": { "prelude-ls": "~1.1.2" }, @@ -4462,6 +4455,7 @@ "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", "dev": true, "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -4483,8 +4477,7 @@ "version": "1.12.1", "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.12.1.tgz", "integrity": "sha512-hEQt0+ZLDVUMhebKxL4x1BTtDY7bavVofhZ9KZ4aI26X9SRaE+Y3m83XUL1UP2jn8ynjndwCCpEHdUG+9pP1Tw==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/undici": { "version": "5.29.0", @@ -4580,15 +4573,13 @@ "version": "0.0.16", "resolved": "https://registry.npmjs.org/wasmbuilder/-/wasmbuilder-0.0.16.tgz", "integrity": "sha512-Qx3lEFqaVvp1cEYW7Bfi+ebRJrOiwz2Ieu7ZG2l7YyeSJIok/reEQCQCuicj/Y32ITIJuGIM9xZQppGx5LrQdA==", - "license": "GPL-3.0", - "peer": true + "license": "GPL-3.0" }, "node_modules/wasmcurves": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/wasmcurves/-/wasmcurves-0.2.2.tgz", "integrity": "sha512-JRY908NkmKjFl4ytnTu5ED6AwPD+8VJ9oc94kdq7h5bIwbj0L4TDJ69mG+2aLs2SoCmGfqIesMWTEJjtYsoQXQ==", "license": "GPL-3.0", - "peer": true, "dependencies": { "wasmbuilder": "0.0.16" } @@ -4606,8 +4597,7 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/web-worker/-/web-worker-1.2.0.tgz", "integrity": "sha512-PgF341avzqyx60neE9DD+XS26MMNMoUQRz9NOZwW32nPQrF6p77f1htcnjBSEV8BGMKZ16choqUG4hyI0Hx7mA==", - "license": "Apache-2.0", - "peer": true + "license": "Apache-2.0" }, "node_modules/which": { "version": "2.0.2", @@ -4693,7 +4683,6 @@ "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", "license": "MIT", - "peer": true, "engines": { "node": ">=0.10.0" } diff --git a/Integration-for-verifiers (Billions Wallet)/package.json b/Integration-for-verifiers (Billions Wallet)/package.json index aefbbc05..fd1e7b24 100644 --- a/Integration-for-verifiers (Billions Wallet)/package.json +++ b/Integration-for-verifiers (Billions Wallet)/package.json @@ -18,6 +18,7 @@ "dependencies": { "@0xpolygonid/js-sdk": "^1.31.1", "@iden3/js-iden3-auth": "1.10.1", + "async-mutex": "^0.5.0", "cors": "^2.8.5", "dotenv": "^16.5.0", "express": "^4.18.2", diff --git a/Integration-for-verifiers (Billions Wallet)/public/index.js b/Integration-for-verifiers (Billions Wallet)/public/index.js index 373d6298..16ec26cb 100644 --- a/Integration-for-verifiers (Billions Wallet)/public/index.js +++ b/Integration-for-verifiers (Billions Wallet)/public/index.js @@ -55,11 +55,8 @@ window.onload = () => { // The request ID should be in the proof request scope const proofRequest = data.body?.scope?.find(s => s.id); if (proofRequest && proofRequest.id) { - console.log(`📋 Found request ID: ${proofRequest.id}`); pollStatus(proofRequest.id); } else { - console.error("❌ Could not find request ID in verification request"); - console.log("Available data:", data); showNotification( 'error', 'Configuration Error', @@ -124,31 +121,44 @@ function showNotification(type, title, message, showReload = false) { }; } -// Function to poll status (optional based on your API) -function pollStatus(requestId) { - console.log(`🔄 Starting to poll status for request ID: ${requestId}`); +function disableVerificationUI() { + const qrCodeEl = document.getElementById('qrcode'); + const walletBtn = document.getElementById('openWalletBtn'); + const subheader = document.querySelector('.subheader'); + + if (qrCodeEl) qrCodeEl.style.opacity = '0.2'; + if (walletBtn) { + walletBtn.disabled = true; + walletBtn.textContent = 'Already Verified'; + } + if (subheader) subheader.textContent = 'This identity has already been verified.'; +} +function pollStatus(requestId) { const checkStatus = () => { fetch(`${baseUrl}api/status/${requestId}`) .then((response) => response.json()) .then((data) => { - console.log("Status:", data.status); if (data.status === "pending") { - console.log("⏳ Still pending, checking again in 2 seconds..."); setTimeout(checkStatus, 2000); } else if (data.status === "success") { - console.log("✅ Verification completed successfully!"); showNotification( 'success', 'Verification Complete!', 'Your identity has been successfully verified using Billions. You can now reload the page to continue.', true ); + } else if (data.status === "already_verified") { + disableVerificationUI(); + showNotification( + 'error', + 'Already Verified', + 'This identity has already been used for verification. Each identity can only verify once.', + false + ); } else if (data.status === "not_found") { - console.log("❌ Request not found"); - setTimeout(checkStatus, 2000); // Keep trying in case of timing issues + setTimeout(checkStatus, 2000); } else { - console.log(`❓ Unknown status: ${data.status}`); setTimeout(checkStatus, 2000); } }) @@ -185,13 +195,8 @@ function setupWalletButton(walletUrl) { } try { - console.log('Opening Billions wallet with URL:', walletUrl); - - // Open the wallet URL in a new tab/window window.open(walletUrl, '_blank'); - } catch (error) { - console.error('Error creating wallet URL:', error); showNotification( 'error', 'URL Error', diff --git a/Integration-for-verifiers (Billions Wallet)/src/config/verification-configs.js b/Integration-for-verifiers (Billions Wallet)/src/config/verification-configs.js index b6fb35b8..7f899b81 100644 --- a/Integration-for-verifiers (Billions Wallet)/src/config/verification-configs.js +++ b/Integration-for-verifiers (Billions Wallet)/src/config/verification-configs.js @@ -1,12 +1,4 @@ -/** - * Verification Configurations for Different Use Cases - * - * This file contains all the credential verification configurations. - * Simply change the USE_CASE to switch between different verification types. - */ - const VERIFICATION_CONFIGS = { - // Proof of Humanity Configuration POH: { name: "Human Credential", verification_description: "Verify you are a human", @@ -20,7 +12,6 @@ const VERIFICATION_CONFIGS = { } }, - // Proof of Verified Humanity Configuration POVH: { name: "Verified Human Credential", verification_description: "Verify you are a verified human", @@ -35,7 +26,6 @@ const VERIFICATION_CONFIGS = { } }, - // Proof of Uniqueness POU: { name: "Uniqueness Credential", verification_description: "Verify you are a unique human", @@ -50,56 +40,27 @@ const VERIFICATION_CONFIGS = { } }; -/** - * Get configuration for a specific use case - * @param {string} useCase - The use case key (e.g., 'POU', 'KYC', 'AGE_VERIFICATION') - * @returns {Object} Configuration object for the specified use case - */ function getConfig(useCase) { const config = VERIFICATION_CONFIGS[useCase.toUpperCase()]; - if (!config) { - const available = Object.keys(VERIFICATION_CONFIGS).join(', '); - throw new Error(`Unknown verification use case: ${useCase}. Available options: ${available}`); + throw new Error(`Unknown use case: ${useCase}. Available: ${Object.keys(VERIFICATION_CONFIGS).join(', ')}`); } - - return { - ...config, - useCase: useCase.toUpperCase() - }; + return { ...config, useCase: useCase.toUpperCase() }; } -/** - * Create a proof request for a specific use case - * @param {string} useCase - The use case key - * @param {number} sessionId - Session ID for the request - * @param {string} nullifier - Nullifier for the session - * @returns {Object} Proof request object - */ function createProofRequest(useCase, sessionId, nullifier) { const config = getConfig(useCase); - return { circuitId: config.circuitId, id: sessionId, - params: { - nullifierSessionId: nullifier.toString() - }, + params: { nullifierSessionId: nullifier.toString() }, query: config.query }; } -/** - * Get all available use cases - * @returns {Array} Array of available use case keys - */ -function getAvailableUseCases() { - return Object.keys(VERIFICATION_CONFIGS); -} - module.exports = { VERIFICATION_CONFIGS, getConfig, createProofRequest, - getAvailableUseCases + getAvailableUseCases: () => Object.keys(VERIFICATION_CONFIGS) }; diff --git a/Integration-for-verifiers (Billions Wallet)/src/index.js b/Integration-for-verifiers (Billions Wallet)/src/index.js index 9b828efe..db5de490 100644 --- a/Integration-for-verifiers (Billions Wallet)/src/index.js +++ b/Integration-for-verifiers (Billions Wallet)/src/index.js @@ -1,8 +1,5 @@ - -// Load environment variables require('dotenv').config(); -// Core dependencies const path = require("path"); const express = require("express"); const helmet = require('helmet'); @@ -10,45 +7,34 @@ const rateLimit = require('express-rate-limit'); const { auth, resolver } = require("@iden3/js-iden3-auth"); const getRawBody = require("raw-body"); const cors = require('cors'); -const { AtomicQueryV3PubSignals } = require('@0xpolygonid/js-sdk'); -const { randomInt, randomBytes } = require('crypto'); +const { randomInt } = require('crypto'); -// Application modules const { getConfig, createProofRequest } = require('./config/verification-configs'); const redisService = require('./services/redis-service'); +const dbService = require('./services/db-service'); const logger = require('./services/logger'); -// Initialize Express app and configuration const app = express(); const port = process.env.PORT || 8080; -const byteEncoder = new TextEncoder(); -// Environment configuration const USE_CASE = process.env.USE_CASE; const nullifierSessionID = process.env.NULLIFIER_SESSION_ID; const currentConfig = getConfig(USE_CASE); -// Cached verifier instance for performance let cachedVerifier = null; -let verifierInitialized = false; -// Generate 8-digit numeric session ID compatible with BigInt conversion -function generateSecureSessionId() { - return randomInt(10000000, 100000000); // Range: 10,000,000 to 99,999,999 +function generateSessionId() { + return randomInt(10000000, 100000000); } -// Initialize and cache verifier for performance async function getVerifierInstance() { - if (cachedVerifier && verifierInitialized) { - return cachedVerifier; - } + if (cachedVerifier) return cachedVerifier; - try { - logger.info('Initializing verifier instance...'); - const startTime = Date.now(); + logger.info('Initializing verifier instance...'); + const t = Date.now(); - // Configure network resolvers for blockchain state verification - const resolvers = { + cachedVerifier = await auth.Verifier.newVerifier({ + stateResolver: { ["billions:main"]: new resolver.EthStateResolver( "https://rpc-mainnet.billions.network", "0x3C9acB2205Aa72A05F6D77d708b5Cf85FCa3a896" @@ -61,32 +47,20 @@ async function getVerifierInstance() { "https://billions-testnet-rpc.eu-north-2.gateway.fm", "0x3C9acB2205Aa72A05F6D77d708b5Cf85FCa3a896" ) - }; - - // Initialize verifier with circuits and resolvers - cachedVerifier = await auth.Verifier.newVerifier({ - stateResolver: resolvers, - circuitsDir: path.join(process.cwd(), "public/keys"), - ipfsGatewayURL: "https://ipfs.io", - }); - - verifierInitialized = true; - const initTime = Date.now() - startTime; - logger.info('Verifier initialized successfully', { initTimeMs: initTime }); + }, + circuitsDir: path.join(process.cwd(), "public/keys"), + ipfsGatewayURL: "https://ipfs.io", + }); - return cachedVerifier; - } catch (error) { - logger.error('Failed to initialize verifier', { error: error.message, stack: error.stack }); - throw error; - } + logger.info('Verifier initialized', { initTimeMs: Date.now() - t }); + return cachedVerifier; } -// Security middleware app.use(helmet({ contentSecurityPolicy: { directives: { defaultSrc: ["'self'"], - scriptSrc: ["'self'", "https://cdn.jsdelivr.net"], // Allow QR code library + scriptSrc: ["'self'", "https://cdn.jsdelivr.net"], styleSrc: ["'self'", "'unsafe-inline'"], imgSrc: ["'self'", "data:"], objectSrc: ["'none'"] @@ -94,135 +68,75 @@ app.use(helmet({ } })); -// CORS configuration with environment-based security const allowedOrigins = process.env.NODE_ENV === 'production' - ? (process.env.ALLOWED_ORIGINS || 'https://your-production-domain.com,https://your-app-domain.com').split(',').filter(Boolean) + ? (process.env.ALLOWED_ORIGINS || '').split(',').map(o => o.trim().replace(/\/$/, '')).filter(Boolean) : ['http://localhost:3000', 'http://localhost:8080', 'http://127.0.0.1:3000', 'http://127.0.0.1:8080']; app.use(cors({ origin: (origin, callback) => { - // Allow requests with no origin (mobile apps, Postman, etc.) if (!origin) return callback(null, true); + if (process.env.NODE_ENV !== 'production') return callback(null, true); - // In development, be permissive - if (process.env.NODE_ENV === 'development') { - return callback(null, true); - } - - // In production, check whitelist - if (allowedOrigins.includes(origin)) { - return callback(null, true); - } - - // Block and log unauthorized origins - logger.logSecurityEvent('CORS_BLOCKED', { - origin, - allowedOrigins, - timestamp: new Date().toISOString() - }, 'warn'); + if (allowedOrigins.includes(origin)) return callback(null, true); - return callback(new Error('Not allowed by CORS - Contact admin to whitelist your domain'), false); + logger.logSecurityEvent('CORS_BLOCKED', { origin, allowedOrigins }, 'warn'); + return callback(new Error('Not allowed by CORS'), false); }, methods: ['GET', 'POST', 'OPTIONS'], allowedHeaders: ['Content-Type', 'Authorization', 'X-Requested-With'], credentials: true, - optionsSuccessStatus: 200 // Some legacy browsers choke on 204 + optionsSuccessStatus: 200 })); -// Enhanced rate limiting with security features app.use(rateLimit({ - windowMs: 1 * 60 * 1000, // 1 minute window - max: 100, // 100 requests per minute per IP - message: { - error: "Too many requests", - details: "Rate limit exceeded. Please try again later.", - retryAfter: "1 minute" - }, - standardHeaders: true, // Include rate limit info in headers - legacyHeaders: false, // Disable the X-RateLimit-* headers + windowMs: 60 * 1000, + max: 100, + standardHeaders: true, + legacyHeaders: false, handler: (req, res) => { logger.logSecurityEvent('RATE_LIMIT_EXCEEDED', { ip: req.ip, - userAgent: req.get('User-Agent'), - url: req.url, - method: req.method + url: req.url }, 'warn'); - - res.status(429).json({ - error: "Too many requests", - details: "Rate limit exceeded. Please try again later.", - retryAfter: "1 minute" - }); + res.status(429).json({ error: "Too many requests", retryAfter: "1 minute" }); } })); -// Body parsing middleware with size limits for security -app.use(express.json({ - limit: '10mb', // Limit JSON payload size - strict: true, - type: 'application/json' -})); -app.use(express.urlencoded({ - extended: true, - limit: '10mb', // Limit URL-encoded payload size - parameterLimit: 1000 // Limit number of parameters -})); - -// Serve static frontend files +app.use(express.json({ limit: '10mb', strict: true })); +app.use(express.urlencoded({ extended: true, limit: '10mb', parameterLimit: 1000 })); app.use(express.static("./public")); -// Generate verification request for wallet authentication app.get("/api/verification-request", async (req, res) => { const startTime = Date.now(); try { const hostUrl = process.env.HOST_URL; - const sessionId = generateSecureSessionId(); - const callbackURL = "/api/callback"; - - // Validate required environment variables if (!hostUrl) { - logger.error("Server misconfiguration: HOST_URL is missing"); - return res.status(500).json({ - error: "Server configuration error", - details: "Missing HOST_URL configuration" - }); + logger.error("HOST_URL is not configured"); + return res.status(500).json({ error: "Server configuration error" }); } if (!process.env.VERIFIER_DID) { - logger.error("Server misconfiguration: VERIFIER_DID is missing"); - return res.status(500).json({ - error: "Server configuration error", - details: "Missing VERIFIER_DID configuration" - }); + logger.error("VERIFIER_DID is not configured"); + return res.status(500).json({ error: "Server configuration error" }); } + const sessionId = generateSessionId(); + const uri = `${hostUrl}/api/callback?sessionId=${sessionId}`; - // Build callback URI with session ID - const uri = `${hostUrl}${callbackURL}?sessionId=${sessionId}`; - - // Create authorization request for iden3 protocol const request = auth.createAuthorizationRequest( currentConfig.verification_description, process.env.VERIFIER_DID, uri ); - // Add proof request to scope const proofRequest = createProofRequest(USE_CASE, sessionId, nullifierSessionID); - const scope = request.body.scope ?? []; - request.body.scope = [...scope, proofRequest]; + request.body.scope = [...(request.body.scope ?? []), proofRequest]; - // Store in Redis for later verification with fallback handling const authStored = await redisService.setAuthRequest(sessionId, request); const statusStored = await redisService.setVerificationStatus(proofRequest.id, "pending"); - // Log Redis storage status for monitoring if (!authStored || !statusStored) { - logger.warn('Redis storage failed, verification may not persist across restarts', { - sessionId, - authStored, - statusStored - }); + logger.warn('Redis storage failed, falling back to memory cache', { sessionId }); } logger.info('Verification request generated', { sessionId, useCase: USE_CASE }); @@ -235,20 +149,18 @@ app.get("/api/verification-request", async (req, res) => { } }); -// Health check endpoint for monitoring Redis and server status app.get('/api/health', async (req, res) => { try { const redisHealthy = await redisService.healthCheck(); const redisStatus = redisService.getStatus(); const cacheStats = redisService.getCacheStats(); - // Determine overall health const hasMemoryFallback = cacheStats.totalEntries > 0; const status = redisHealthy ? 'healthy' : (hasMemoryFallback ? 'degraded' : 'unhealthy'); const statusCode = redisHealthy ? 200 : (hasMemoryFallback ? 206 : 503); return res.status(statusCode).json({ - status: status, + status, timestamp: new Date().toISOString(), redis: { connected: redisHealthy, @@ -264,215 +176,106 @@ app.get('/api/health', async (req, res) => { averageAge: `${Math.round(cacheStats.averageAgeMs / 1000)}s` }, server: { - port: port, + port, useCase: USE_CASE, config: currentConfig.name } }); } catch (error) { - console.error('Error in /api/health:', error); - return res.status(503).json({ - status: 'unhealthy', - error: error.message, - timestamp: new Date().toISOString() - }); + logger.error('Error in /api/health', { error: error.message }); + return res.status(503).json({ status: 'unhealthy', error: error.message }); } }); -// Get verification status for a given request ID app.get("/api/status/:id", async (req, res) => { try { const requestId = parseInt(req.params.id); if (isNaN(requestId) || requestId <= 0) { - return res.status(400).json({ - error: "Invalid request ID", - details: "Request ID must be a positive integer" - }); + return res.status(400).json({ error: "Invalid request ID" }); } const status = await redisService.getVerificationStatus(requestId) || "not_found"; - - return res.status(200).json({ - requestId: requestId, - status: status, - }); + return res.status(200).json({ requestId, status }); } catch (error) { - console.error("Error in /api/status:", error); - return res.status(500).json({ - error: "Internal server error", - details: process.env.NODE_ENV === 'development' ? error.message : undefined - }); + logger.error("Error in /api/status", { error: error.message }); + return res.status(500).json({ error: "Internal server error" }); } }); -// Verify the proof after wallet sign-in callback app.post("/api/callback", async (req, res) => { const startTime = Date.now(); try { - // Validate session ID format (8-digit number) const sessionId = parseInt(req.query.sessionId); if (isNaN(sessionId) || sessionId < 10000000 || sessionId >= 100000000) { - return res.status(400).json({ - error: "Invalid session ID", - details: "Session ID must be an 8-digit number" - }); + return res.status(400).json({ error: "Invalid session ID" }); } - // Extract and validate JWT token from request body with size limit const raw = await getRawBody(req, { length: req.headers['content-length'], - limit: '1mb', // Limit raw body to 1MB for security + limit: '1mb', encoding: 'utf8' }); const tokenStr = raw.toString().trim(); - // ⚠️ TEMPORARY DEBUG: Log token for testing (REMOVE IN PRODUCTION) - console.log('\n🔥🔥🔥 === TOKEN CAPTURE (For Testing) ==='); - console.log('Full Token:', tokenStr); - console.log('Token Length:', tokenStr.length, 'characters'); - console.log('===========================================\n'); - - - // Validate token length for security - if (tokenStr.length > 100000) { // ~100KB limit for JWT - return res.status(400).json({ - error: "Token too large", - details: "Authentication token exceeds maximum allowed size" - }); + if (!tokenStr) { + return res.status(400).json({ error: "Missing token" }); } - if (!tokenStr) { - return res.status(400).json({ - error: "Missing token", - details: "No authentication token provided in request body" - }); + if (tokenStr.length > 100000) { + return res.status(400).json({ error: "Token too large" }); } - // Retrieve stored auth request from Redis const authRequest = await redisService.getAuthRequest(sessionId); if (!authRequest) { - return res.status(400).json({ - error: "Invalid session", - details: "Session ID not found or expired" - }); + return res.status(400).json({ error: "Session not found or expired" }); } - // Get cached verifier instance for performance const verifier = await getVerifierInstance(); + const authResponse = await verifier.fullVerify(tokenStr, authRequest, { + AcceptedStateTransitionDelay: 5 * 60 * 1000, + }); - // Verify the proof with 5-minute state transition delay - const opts = { - AcceptedStateTransitionDelay: 5 * 60 * 1000, // 5 minutes - }; - const authResponse = await verifier.fullVerify(tokenStr, authRequest, opts); - - // Validate response structure - if (!authResponse || !authResponse.body || !authResponse.body.scope) { - return res.status(400).json({ - error: "Invalid authentication response", - details: "Malformed response from verifier" - }); + if (!authResponse?.body?.scope) { + return res.status(400).json({ error: "Malformed response from verifier" }); } - // Find the nullifier proof in the response scope const nullifierProof = authResponse.body.scope.find( (s) => s.circuitId === "credentialAtomicQueryV3-beta.1" && s.id === sessionId ); if (!nullifierProof) { - return res.status(400).json({ - error: "Invalid proof", - details: "No valid nullifier proof found in response" - }); + return res.status(400).json({ error: "No valid nullifier proof found" }); } - // ========================================================================= - // NULLIFIER ANTI-REPLAY LOGIC (PostgreSQL Integration) - // ========================================================================= - - console.log('\n🔐 === NULLIFIER EXTRACTION & VALIDATION ==='); - - // Extract the Nullifier Hash from public signals - // The nullifier is typically at index 1 in credentialAtomicQueryV3 public signals const nullifierHash = nullifierProof.pub_signals?.[1]; - if (!nullifierHash) { - console.error('⚠️ WARNING: Could not extract nullifier from proof public signals'); - console.error(' Available signals:', nullifierProof.pub_signals); logger.error('Nullifier extraction failed', { sessionId, - circuitId: nullifierProof.circuitId, signalCount: nullifierProof.pub_signals?.length }); - - return res.status(400).json({ - error: "Invalid proof structure", - details: "Could not extract nullifier from proof signals" - }); + return res.status(400).json({ error: "Could not extract nullifier from proof" }); } - console.log(`✅ Nullifier extracted successfully`); - console.log(` Hash (first 30 chars): ${nullifierHash.substring(0, 30)}...`); - console.log(` Full length: ${nullifierHash.length} characters`); - - // Import database service - const dbService = require('./services/db-service'); - - // [GATEKEEPER] Check if this nullifier has already been used (Replay Attack Detection) - console.log('\n🛡️ Performing anti-replay check...'); - const isReplay = await dbService.isUserVerified(nullifierHash); - - if (isReplay) { - console.error('\n🚨 === REPLAY ATTACK BLOCKED ==='); - console.error(' Nullifier:', nullifierHash.substring(0, 30) + '...'); - console.error(' Session ID:', sessionId); - console.error(' This identity has already verified.'); + const scopeEntry = authRequest.body.scope.find( + (s) => s.circuitId === "credentialAtomicQueryV3-beta.1" + ); + const proofRequestId = scopeEntry?.id; + const { claimed } = await dbService.claimNullifier(nullifierHash, sessionId); + if (!claimed) { logger.logSecurityEvent('REPLAY_ATTACK_BLOCKED', { nullifierHash: nullifierHash.substring(0, 20) + '...', - sessionId, - timestamp: new Date().toISOString() + sessionId }, 'warn'); - return res.status(400).json({ - error: "Already Verified", - details: "This identity has already been used for verification. Each user can only verify once." - }); - } - - console.log('✅ Anti-replay check passed: This is a new user'); - - // [THE LOCK] Store the nullifier to prevent future reuse - console.log('\n💾 Storing nullifier in database...'); - try { - await dbService.setUserVerification(nullifierHash, sessionId); - console.log('✅ Nullifier stored successfully - Future replays will be blocked'); - } catch (dbError) { - console.error('\n❌ CRITICAL: Failed to store nullifier in database!'); - console.error(' Error:', dbError.message); - console.error(' This could allow replay attacks if not resolved.'); - - logger.error('Database storage failed', { - error: dbError.message, - nullifierHash: nullifierHash.substring(0, 20) + '...', - sessionId - }); + if (proofRequestId) { + await redisService.setVerificationStatus(proofRequestId, "already_verified"); + } - // Decision: Should we still allow the verification if DB save fails? - // Option 1: Fail the request (safer but could impact user experience) - // Option 2: Allow it but log critical error (current implementation) - // For production, consider Option 1 for maximum security + return res.status(400).json({ error: "This identity has already been verified." }); } - console.log('\n🎉 === VERIFICATION SUCCESSFUL ===\n'); - - // ========================================================================= - // END NULLIFIER LOGIC - // ========================================================================= - - // Update verification status to success - const proofRequestId = authRequest.body.scope.find(s => s.circuitId === "credentialAtomicQueryV3-beta.1")?.id; if (proofRequestId) { await redisService.setVerificationStatus(proofRequestId, "success"); } @@ -480,153 +283,90 @@ app.post("/api/callback", async (req, res) => { logger.logVerification(sessionId, 'success', authResponse.from); logger.logRequest(req, res, Date.now() - startTime); - return res - .status(200) - .set("Content-Type", "application/json") - .send(authResponse); + return res.status(200).set("Content-Type", "application/json").send(authResponse); } catch (error) { - logger.error("Error in /api/callback", { error: error.message, stack: error.stack, sessionId }); + logger.error("Error in /api/callback", { error: error.message, stack: error.stack }); - // Handle verification-specific errors differently - if (error.message && error.message.includes('verification')) { + if (error.message?.includes('verification')) { return res.status(400).json({ error: "Verification failed", - details: process.env.NODE_ENV === 'development' ? error.message : "Authentication verification failed" + details: process.env.NODE_ENV === 'development' ? error.message : undefined }); } - // Generic server error response - return res.status(500).json({ - error: "Internal server error", - details: process.env.NODE_ENV === 'development' ? error.message : undefined - }); + return res.status(500).json({ error: "Internal server error" }); } }); -// Validate required environment variables function validateEnvironment() { const required = [ - 'USE_CASE', - 'VERIFIER_DID', - 'NULLIFIER_SESSION_ID', - 'HOST_URL', - 'REDIS_HOST', - 'REDIS_PORT', - 'REDIS_USERNAME', - 'REDIS_PASSWORD' + 'USE_CASE', 'VERIFIER_DID', 'NULLIFIER_SESSION_ID', + 'HOST_URL', 'REDIS_HOST', 'REDIS_PORT', 'REDIS_USERNAME', 'REDIS_PASSWORD' ]; const missing = required.filter(key => !process.env[key]); - if (missing.length > 0) { - logger.error('Missing required environment variables', { missing }); throw new Error(`Missing required environment variables: ${missing.join(', ')}`); } - // Validate USE_CASE value const validUseCases = ['POH', 'POU', 'POVH']; if (!validUseCases.includes(process.env.USE_CASE?.toUpperCase())) { throw new Error(`Invalid USE_CASE: ${process.env.USE_CASE}. Must be one of: ${validUseCases.join(', ')}`); } - // Validate URLs try { new URL(process.env.HOST_URL); } catch { - throw new Error(`Invalid HOST_URL: ${process.env.HOST_URL}. Must be a valid URL.`); + throw new Error(`Invalid HOST_URL: ${process.env.HOST_URL}`); } - // Validate Redis port - const port = parseInt(process.env.REDIS_PORT); - if (isNaN(port) || port < 1 || port > 65535) { - throw new Error(`Invalid REDIS_PORT: ${process.env.REDIS_PORT}. Must be a number between 1-65535.`); + const redisPort = parseInt(process.env.REDIS_PORT); + if (isNaN(redisPort) || redisPort < 1 || redisPort > 65535) { + throw new Error(`Invalid REDIS_PORT: ${process.env.REDIS_PORT}`); } - - logger.info('Environment validation passed', { - useCase: process.env.USE_CASE, - nodeEnv: process.env.NODE_ENV - }); } -// Initialize Redis connection and start the Express server async function startServer() { try { - // Validate environment variables first validateEnvironment(); - // Validate production configuration - if (process.env.NODE_ENV === 'production') { - const origins = process.env.ALLOWED_ORIGINS || ''; - if (origins.includes('your-production-domain.com') || origins.includes('your-app-domain.com')) { - logger.logSecurityEvent('PRODUCTION_CONFIG_WARNING', { - message: 'Using placeholder domains in production CORS configuration', - allowedOrigins: origins, - recommendation: 'Update ALLOWED_ORIGINS environment variable with real domains' - }, 'error'); - } - - // Check for placeholder credentials in production - if (process.env.REDIS_PASSWORD?.includes('your-secure-redis-password-here')) { - logger.logSecurityEvent('PRODUCTION_SECURITY_WARNING', { - message: 'Using placeholder Redis credentials in production', - recommendation: 'Update Redis credentials with real values' - }, 'error'); - } - } - - // Attempt Redis connection but don't fail startup if unavailable try { await redisService.connect(); - logger.info('Redis connected successfully'); + logger.info('Redis connected'); } catch (redisError) { - logger.warn('Redis connection failed at startup, running in degraded mode', { + logger.warn('Redis unavailable at startup, running in degraded mode', { error: redisError.message }); - // Server will still start but with degraded functionality } const server = app.listen(port, () => { - logger.info('Server started successfully', { + logger.info('Server started', { port, useCase: USE_CASE, - config: currentConfig.name, nodeEnv: process.env.NODE_ENV, - corsMode: process.env.NODE_ENV === 'production' ? 'restricted' : 'permissive', - allowedOrigins: process.env.NODE_ENV === 'production' ? allowedOrigins.length : 'localhost', redisConnected: redisService.isHealthy() }); }); + return server; } catch (error) { - logger.error('Failed to start server', { error: error.message, stack: error.stack }); + logger.error('Failed to start server', { error: error.message }); process.exit(1); } } -// Start the application (async () => { const server = await startServer(); -})(); -// Graceful shutdown handlers -process.on('SIGTERM', async () => { - logger.info('Received SIGTERM, shutting down gracefully'); - server.close(async () => { - logger.info('HTTP server closed'); - await redisService.disconnect(); - logger.info('Process terminated gracefully'); - process.exit(0); - }); -}); - -process.on('SIGINT', async () => { - logger.info('Received SIGINT, shutting down gracefully'); - server.close(async () => { - logger.info('HTTP server closed'); - await redisService.disconnect(); - logger.info('Process terminated gracefully'); - process.exit(0); - }); -}); + const shutdown = async (signal) => { + logger.info(`${signal} received, shutting down`); + server.close(async () => { + await redisService.disconnect(); + process.exit(0); + }); + }; + process.on('SIGTERM', () => shutdown('SIGTERM')); + process.on('SIGINT', () => shutdown('SIGINT')); +})(); diff --git a/Integration-for-verifiers (Billions Wallet)/src/services/db-service.js b/Integration-for-verifiers (Billions Wallet)/src/services/db-service.js index 4d393c18..d1bc9f36 100644 --- a/Integration-for-verifiers (Billions Wallet)/src/services/db-service.js +++ b/Integration-for-verifiers (Billions Wallet)/src/services/db-service.js @@ -1,89 +1,47 @@ const { Pool } = require('pg'); +const { Mutex } = require('async-mutex'); -// PostgreSQL connection pool const pool = new Pool({ connectionString: process.env.DATABASE_URL, - ssl: { rejectUnauthorized: false } // Required for most cloud DBs -}); - -// Log connection status -pool.on('connect', () => { - console.log('✅ PostgreSQL pool connected'); + ssl: { rejectUnauthorized: false } }); pool.on('error', (err) => { - console.error('❌ PostgreSQL pool error:', err); + console.error('PostgreSQL pool error:', err); }); -module.exports = { - // Check if user exists (Anti-Replay Check) - async isUserVerified(nullifierHash) { - console.log(`🔍 Checking if nullifier exists in database: ${nullifierHash.substring(0, 20)}...`); +const nullifierMutex = new Mutex(); +module.exports = { + async claimNullifier(nullifierHash, sessionId) { + const release = await nullifierMutex.acquire(); try { - const res = await pool.query( - 'SELECT * FROM verified_nullifiers WHERE hash = $1', + const existing = await pool.query( + 'SELECT 1 FROM verified_nullifiers WHERE hash = $1', [nullifierHash] ); - const isVerified = res.rows.length > 0; - - if (isVerified) { - console.log(`⚠️ REPLAY DETECTED: Nullifier already exists in database`); - console.log(` Hash: ${nullifierHash.substring(0, 20)}...`); - console.log(` Original verification: ${res.rows[0].timestamp}`); - } else { - console.log(`✅ Nullifier NOT FOUND in database (New user)`); + if (existing.rows.length > 0) { + return { claimed: false }; } - return isVerified; - } catch (err) { - console.error('❌ Database Query Error (isUserVerified):', err.message); - console.error(' Stack:', err.stack); - console.error(' Nullifier Hash:', nullifierHash); - return false; // Fail safe - allow verification on DB error - } - }, - - // Save the user verification - async setUserVerification(nullifierHash, sessionId) { - console.log(`💾 Attempting to store nullifier in database...`); - console.log(` Hash: ${nullifierHash.substring(0, 20)}...`); - console.log(` Session ID: ${sessionId}`); - - try { await pool.query( 'INSERT INTO verified_nullifiers (hash, session_id) VALUES ($1, $2)', [nullifierHash, sessionId] ); - console.log(`✅ SUCCESS: Nullifier permanently stored in database`); - console.log(` This user cannot verify again with this nullifier`); - return true; - } catch (err) { - console.error('❌ Database Insert Error (setUserVerification):', err.message); - console.error(' Code:', err.code); - console.error(' Detail:', err.detail); - console.error(' Stack:', err.stack); - - // Check if it's a duplicate key error - if (err.code === '23505') { - console.error(' DUPLICATE KEY: This nullifier already exists!'); - } - - throw err; + return { claimed: true }; + } finally { + release(); } }, - // Health check for database connection async healthCheck() { try { - const res = await pool.query('SELECT NOW()'); - console.log('✅ Database health check passed:', res.rows[0].now); + await pool.query('SELECT NOW()'); return true; - } catch (err) { - console.error('❌ Database health check failed:', err.message); + } catch { return false; } } -}; \ No newline at end of file +}; diff --git a/Integration-for-verifiers (Billions Wallet)/src/services/logger.js b/Integration-for-verifiers (Billions Wallet)/src/services/logger.js index 23de788a..ca4f7227 100644 --- a/Integration-for-verifiers (Billions Wallet)/src/services/logger.js +++ b/Integration-for-verifiers (Billions Wallet)/src/services/logger.js @@ -1,6 +1,5 @@ const winston = require('winston'); -// Production-grade logging configuration const logger = winston.createLogger({ level: process.env.LOG_LEVEL || 'info', format: winston.format.combine( @@ -13,25 +12,22 @@ const logger = winston.createLogger({ version: process.env.npm_package_version || '1.0.0' }, transports: [ - // Error logs to separate file new winston.transports.File({ filename: 'logs/error.log', level: 'error', - maxsize: 5242880, // 5MB + maxsize: 5242880, maxFiles: 5, tailable: true }), - // All logs to combined file new winston.transports.File({ filename: 'logs/combined.log', - maxsize: 5242880, // 5MB + maxsize: 5242880, maxFiles: 10, tailable: true }) ], }); -// Add console transport for development if (process.env.NODE_ENV !== 'production') { logger.add(new winston.transports.Console({ format: winston.format.combine( @@ -39,21 +35,16 @@ if (process.env.NODE_ENV !== 'production') { winston.format.simple() ) })); -} - -// Production console logging (structured JSON) -if (process.env.NODE_ENV === 'production') { +} else { logger.add(new winston.transports.Console({ format: winston.format.json() })); } -// Helper methods for common log patterns logger.logRequest = (req, res, duration) => { logger.info('HTTP Request', { method: req.method, url: req.url, - userAgent: req.get('User-Agent'), ip: req.ip, statusCode: res.statusCode, duration: `${duration}ms` @@ -69,22 +60,12 @@ logger.logVerification = (sessionId, status, userDid = null) => { }); }; -logger.logRedisOperation = (operation, key, success) => { - logger.debug('Redis Operation', { - operation, - key, - success, - timestamp: new Date().toISOString() - }); -}; - logger.logSecurityEvent = (event, details, severity = 'warn') => { logger[severity]('Security Event', { event, details, - timestamp: new Date().toISOString(), alerting: severity === 'error' }); }; -module.exports = logger; \ No newline at end of file +module.exports = logger; diff --git a/Integration-for-verifiers (Billions Wallet)/src/services/redis-service.js b/Integration-for-verifiers (Billions Wallet)/src/services/redis-service.js index d4255457..c036cdde 100644 --- a/Integration-for-verifiers (Billions Wallet)/src/services/redis-service.js +++ b/Integration-for-verifiers (Billions Wallet)/src/services/redis-service.js @@ -1,32 +1,28 @@ const { createClient } = require('redis'); -// Redis service for session management and verification storage -// Provides production-ready persistent storage with circuit breaker and in-memory fallback class RedisService { constructor() { this.client = null; this.isConnected = false; this.reconnectAttempts = 0; this.maxReconnectAttempts = 10; - this.reconnectDelay = 1000; // Start with 1 second - this.maxReconnectDelay = 30000; // Max 30 seconds + this.reconnectDelay = 1000; + this.maxReconnectDelay = 30000; this.circuitBreakerFailures = 0; this.circuitBreakerThreshold = 5; this.circuitBreakerOpen = false; - this.circuitBreakerResetTime = 60000; // 1 minute + this.circuitBreakerResetTime = 60000; this.lastCircuitBreakerOpen = null; - // In-memory fallback cache this.memoryCache = new Map(); - this.maxCacheSize = 10000; // Limit memory usage - this.cacheCleanupInterval = 5 * 60 * 1000; // 5 minutes - this.initializeMemoryCache(); + this.maxCacheSize = 10000; + this.cacheCleanupInterval = 5 * 60 * 1000; + + setInterval(() => this.cleanupExpiredEntries(), this.cacheCleanupInterval); } - // Establish connection to Redis server async connect() { try { - // Create Redis client with environment configuration this.client = createClient({ username: process.env.REDIS_USERNAME, password: process.env.REDIS_PASSWORD, @@ -36,7 +32,6 @@ class RedisService { } }); - // Set up connection event handlers with recovery mechanisms this.client.on('error', (err) => { console.error('Redis Client Error:', err); this.isConnected = false; @@ -44,82 +39,63 @@ class RedisService { }); this.client.on('connect', () => { - console.log('✅ Redis connected successfully'); this.isConnected = true; this.reconnectAttempts = 0; this.resetCircuitBreaker(); }); this.client.on('disconnect', () => { - console.log('❌ Redis disconnected'); this.isConnected = false; this.scheduleReconnection(); }); this.client.on('reconnecting', () => { - console.log('🔄 Redis reconnecting...'); + console.log('Redis reconnecting...'); }); this.client.on('end', () => { - console.log('Redis connection ended'); this.isConnected = false; }); await this.client.connect(); await this.client.ping(); - console.log('✅ Redis connection established and tested'); - } catch (error) { - console.error('❌ Failed to connect to Redis:', error); + console.error('Failed to connect to Redis:', error); this.handleConnectionFailure(); throw error; } } - // Gracefully disconnect from Redis async disconnect() { if (this.client && this.isConnected) { await this.client.quit(); this.isConnected = false; - console.log('✅ Redis disconnected gracefully'); } } - // Handle connection failures with circuit breaker handleConnectionFailure() { this.circuitBreakerFailures++; - if (this.circuitBreakerFailures >= this.circuitBreakerThreshold) { this.openCircuitBreaker(); } } - // Open circuit breaker to prevent cascading failures openCircuitBreaker() { this.circuitBreakerOpen = true; this.lastCircuitBreakerOpen = Date.now(); - console.log(`⚡ Circuit breaker opened after ${this.circuitBreakerFailures} failures`); - - // Schedule circuit breaker reset - setTimeout(() => { - this.resetCircuitBreaker(); - }, this.circuitBreakerResetTime); + console.log(`Circuit breaker opened after ${this.circuitBreakerFailures} failures`); + setTimeout(() => this.resetCircuitBreaker(), this.circuitBreakerResetTime); } - // Reset circuit breaker for retry attempts resetCircuitBreaker() { - if (this.circuitBreakerOpen) { - console.log('🔄 Circuit breaker reset - allowing new attempts'); - } this.circuitBreakerOpen = false; this.circuitBreakerFailures = 0; this.lastCircuitBreakerOpen = null; } - // Schedule reconnection with exponential backoff scheduleReconnection() { if (this.reconnectAttempts >= this.maxReconnectAttempts) { - console.error(`❌ Max reconnection attempts (${this.maxReconnectAttempts}) reached`); + console.error(`Max reconnection attempts (${this.maxReconnectAttempts}) reached`); return; } @@ -129,234 +105,121 @@ class RedisService { ); this.reconnectAttempts++; - console.log(`⏰ Scheduling reconnection attempt ${this.reconnectAttempts} in ${delay}ms`); setTimeout(async () => { try { await this.connect(); } catch (error) { - console.error('Reconnection attempt failed:', error); + console.error('Reconnection attempt failed:', error.message); } }, delay); } - // Initialize memory cache with cleanup - initializeMemoryCache() { - // Periodic cleanup of expired entries - setInterval(() => { - this.cleanupExpiredEntries(); - }, this.cacheCleanupInterval); - - console.log('🧠 In-memory fallback cache initialized'); - } - - // Clean up expired entries from memory cache cleanupExpiredEntries() { const now = Date.now(); - let cleanedCount = 0; - for (const [key, entry] of this.memoryCache.entries()) { if (entry.expiresAt && entry.expiresAt < now) { this.memoryCache.delete(key); - cleanedCount++; } } - - if (cleanedCount > 0) { - console.log(`🧹 Cleaned ${cleanedCount} expired entries from memory cache`); - } } - // Set value in memory cache with TTL setMemoryCache(key, value, ttl = 3600) { - // Enforce cache size limit if (this.memoryCache.size >= this.maxCacheSize) { - // Remove oldest entry (LRU-like behavior) const firstKey = this.memoryCache.keys().next().value; this.memoryCache.delete(firstKey); } - const expiresAt = ttl > 0 ? Date.now() + (ttl * 1000) : null; this.memoryCache.set(key, { value, - expiresAt, + expiresAt: ttl > 0 ? Date.now() + (ttl * 1000) : null, createdAt: Date.now() }); - - console.log(`🧠 Stored in memory cache: ${key} (TTL: ${ttl}s)`); } - // Get value from memory cache getMemoryCache(key) { const entry = this.memoryCache.get(key); + if (!entry) return null; - if (!entry) { - return null; - } - - // Check if expired if (entry.expiresAt && entry.expiresAt < Date.now()) { this.memoryCache.delete(key); return null; } - console.log(`🧠 Retrieved from memory cache: ${key}`); return entry.value; } - // Execute Redis operation with in-memory fallback async executeWithFallback(operation, fallbackKey = null, fallbackValue = null) { - // Check circuit breaker - if (this.circuitBreakerOpen) { - console.log('⚡ Circuit breaker open, using memory cache'); - return fallbackKey ? this.getMemoryCache(fallbackKey) : fallbackValue; - } - - // Check connection - if (!this.isConnected || !this.client) { - console.log('⚠️ Redis not connected, using memory cache'); + if (this.circuitBreakerOpen || !this.isConnected || !this.client) { return fallbackKey ? this.getMemoryCache(fallbackKey) : fallbackValue; } try { const result = await operation(); - // Reset failure count on successful operation this.circuitBreakerFailures = Math.max(0, this.circuitBreakerFailures - 1); return result; } catch (error) { - console.error('❌ Redis operation failed, using memory cache:', error); + console.error('Redis operation failed, falling back to memory cache:', error.message); this.handleConnectionFailure(); return fallbackKey ? this.getMemoryCache(fallbackKey) : fallbackValue; } } - // Store authentication request with TTL (default: 1 hour) async setAuthRequest(sessionId, authRequest, ttl = 3600) { const key = `auth_request:${sessionId}`; - - // Always store in memory cache as backup this.setMemoryCache(key, authRequest, ttl); return await this.executeWithFallback(async () => { await this.client.setEx(key, ttl, JSON.stringify(authRequest)); - console.log(`✅ Auth request stored in Redis for session ${sessionId}`); return true; - }, null, true); // Return true even if Redis fails (stored in memory) + }, null, true); } - // Retrieve authentication request by session ID async getAuthRequest(sessionId) { const key = `auth_request:${sessionId}`; return await this.executeWithFallback(async () => { const result = await this.client.get(key); const authRequest = result ? JSON.parse(result) : null; - - // Update memory cache if found in Redis - if (authRequest) { - this.setMemoryCache(key, authRequest, 3600); - } - + if (authRequest) this.setMemoryCache(key, authRequest, 3600); return authRequest; - }, key, null); // Fallback to memory cache using key + }, key, null); } - // Remove authentication request from storage - async deleteAuthRequest(sessionId) { - try { - const key = `auth_request:${sessionId}`; - await this.client.del(key); - console.log(`✅ Auth request deleted for session ${sessionId}`); - } catch (error) { - console.error(`❌ Failed to delete auth request for session ${sessionId}:`, error); - } - } - - // Store user verification data with TTL (default: 24 hours) - async setUserVerification(nullifier, verificationData, ttl = 86400) { - try { - const key = `user_verification:${nullifier}`; - await this.client.setEx(key, ttl, JSON.stringify(verificationData)); - console.log(`✅ User verification stored for nullifier ${nullifier}`); - } catch (error) { - console.error(`❌ Failed to store user verification for nullifier ${nullifier}:`, error); - throw error; - } - } - - // Retrieve user verification data by nullifier - async getUserVerification(nullifier) { - try { - const key = `user_verification:${nullifier}`; - const result = await this.client.get(key); - return result ? JSON.parse(result) : null; - } catch (error) { - console.error(`❌ Failed to get user verification for nullifier ${nullifier}:`, error); - return null; - } - } - - // Check if user is already verified (anti-replay protection) - async isUserVerified(nullifier) { - try { - const verification = await this.getUserVerification(nullifier); - return verification && verification.verified === true; - } catch (error) { - console.error(`❌ Failed to check user verification for nullifier ${nullifier}:`, error); - return false; - } - } - - // Store verification status for polling (default TTL: 1 hour) async setVerificationStatus(requestId, status, ttl = 3600) { const key = `verification_status:${requestId}`; - - // Always store in memory cache as backup this.setMemoryCache(key, status, ttl); return await this.executeWithFallback(async () => { await this.client.setEx(key, ttl, status); - console.log(`✅ Verification status set in Redis for request ${requestId}: ${status}`); return true; - }, null, true); // Return true even if Redis fails (stored in memory) + }, null, true); } - // Get current verification status for frontend polling async getVerificationStatus(requestId) { const key = `verification_status:${requestId}`; return await this.executeWithFallback(async () => { const result = await this.client.get(key); - - // Update memory cache if found in Redis - if (result) { - this.setMemoryCache(key, result, 3600); - } - + if (result) this.setMemoryCache(key, result, 3600); return result; - }, key, null); // Fallback to memory cache using key + }, key, null); } - // Health check for monitoring systems async healthCheck() { try { - if (!this.client || !this.isConnected) { - return false; - } + if (!this.client || !this.isConnected) return false; const pong = await this.client.ping(); return pong === 'PONG'; - } catch (error) { - console.error('❌ Redis health check failed:', error); + } catch { return false; } } - // Quick connection status check isHealthy() { return this.isConnected && !this.circuitBreakerOpen; } - // Get detailed Redis service status for monitoring getStatus() { return { connected: this.isConnected, @@ -372,16 +235,13 @@ class RedisService { }; } - // Get memory cache statistics getCacheStats() { const now = Date.now(); let expiredCount = 0; let totalAge = 0; - for (const [key, entry] of this.memoryCache.entries()) { - if (entry.expiresAt && entry.expiresAt < now) { - expiredCount++; - } + for (const [, entry] of this.memoryCache.entries()) { + if (entry.expiresAt && entry.expiresAt < now) expiredCount++; totalAge += now - entry.createdAt; } @@ -394,6 +254,4 @@ class RedisService { } } -// Export singleton instance for application use -const redisService = new RedisService(); -module.exports = redisService; \ No newline at end of file +module.exports = new RedisService(); From 7df841f6f9779ec7e92f78882743bcb6e23a4ac7 Mon Sep 17 00:00:00 2001 From: Nityam Tripathi Date: Wed, 18 Mar 2026 20:10:48 +0530 Subject: [PATCH 3/3] fix-memStore --- .../.env.example | 10 - .../README.md | 103 -- .../ecosystem.config.js | 4 +- .../package-lock.json | 1252 ++++++++++------- .../package.json | 6 +- .../src/index.js | 109 +- .../src/services/db-service.js | 47 - .../src/services/memory-store.js | 22 + .../src/services/nullifier-store.js | 19 + .../src/services/redis-service.js | 257 ---- 10 files changed, 849 insertions(+), 980 deletions(-) delete mode 100644 Integration-for-verifiers (Billions Wallet)/README.md delete mode 100644 Integration-for-verifiers (Billions Wallet)/src/services/db-service.js create mode 100644 Integration-for-verifiers (Billions Wallet)/src/services/memory-store.js create mode 100644 Integration-for-verifiers (Billions Wallet)/src/services/nullifier-store.js delete mode 100644 Integration-for-verifiers (Billions Wallet)/src/services/redis-service.js diff --git a/Integration-for-verifiers (Billions Wallet)/.env.example b/Integration-for-verifiers (Billions Wallet)/.env.example index 55f06040..b3f4f3ef 100644 --- a/Integration-for-verifiers (Billions Wallet)/.env.example +++ b/Integration-for-verifiers (Billions Wallet)/.env.example @@ -17,16 +17,6 @@ NULLIFIER_SESSION_ID=your_nullifier_session_id # Must be a positive BigInt for t USE_CASE=POVH #Options: 'POH', 'POU' -# Redis Configuration (Production) -# ⚠️ SECURITY: Never commit real credentials to code repositories -REDIS_HOST=your-redis-host.redis-cloud.com -REDIS_PORT=12345 -REDIS_USERNAME=your-redis-username -REDIS_PASSWORD=your-secure-redis-password-here - -# Database Configuration -DATABASE_URL="your-database-url-here" - # Security Configuration (Production) ALLOWED_ORIGINS=https://your-frontend-domain.com,https://your-mobile-app-domain.com # Comma-separated list NODE_ENV=production # Set to 'production' for strict CORS diff --git a/Integration-for-verifiers (Billions Wallet)/README.md b/Integration-for-verifiers (Billions Wallet)/README.md deleted file mode 100644 index dca06b13..00000000 --- a/Integration-for-verifiers (Billions Wallet)/README.md +++ /dev/null @@ -1,103 +0,0 @@ -# Billions Wallet Verification Integration Example - -This project is a demo backend and static UI for integrating verifications via Billions Wallet. It allows you to generate verification requests and verify zero-knowledge proofs from the wallet. - -## 📋 Supported Verification Types - -- **POH (Proof of Humanity)**: Verify users are real humans via `Human` credential -- **POU (Proof of Uniqueness)**: Verify user uniqueness via `Uniqueness` credential -- **POVH (Proof of Verified Humaity)** Verify that users are `Verified human` via Verified Human (passport/Aadhaar) cred -## Quick Start - -1. **Install dependencies** - -```bash -npm install -``` - -### 2. Configure Environment Variables - -Create a `.env` file based on the `env.example` in the directory: - -```bash -cp env.example .env -``` - -Edit the `.env` file with your configuration: - -```bash -# Server Configuration -PORT=8080 -HOST_URL=https://your-domain.com # Your production domain or ngrok URL for development - -# Verifier Configuration -#⚠️ CRITICAL: IMMUTABLE VALUES - DO NOT CHANGE AFTER FIRST USE (NULLIFIER_ID & VERIFIER_DID) -# These values CANNOT be changed once users start verifying, as changing them -# will result in different nullifier IDs and DIDs for the same users. -VERIFIER_DID=your_verifier_did_here # Download the Billions app and Login into it.Copy the DID created for your account to use as the Verifier. You can find that in settings. -USE_CASE=POU # Options: POH, POU, POVH -NULLIFIER_SESSION_ID=your_nullifier_session_id # Must be a positive BigInt for the proof request. - -#Use case selection -# POH (Proof of Humanity): Verify that the user is a real human via `Human` Credential -# POU (Proof of Uniqueness): Verify that the user is unique (anti-Sybil) via `Verified Human` credential -# POVH (Proof of Verified Humanity): Verify that the user is a real verified human via `Verified Human` Credential - -USE_CASE=POVH #Options: 'POH', 'POU' - -# Redis Configuration (Production) -# ⚠️ SECURITY: Never commit real credentials to code repositories -REDIS_HOST=your-redis-host.redis-cloud.com -REDIS_PORT=12345 -REDIS_USERNAME=your-redis-username -REDIS_PASSWORD=your-secure-redis-password-here - -# Security Configuration (Production) -ALLOWED_ORIGINS=https://your-frontend-domain.com,https://your-mobile-app-domain.com # Comma-separated list -NODE_ENV=production # Set to 'production' for strict CORS -LOG_LEVEL=info # Options: error, warn, info, debug -``` - -### 3. Set Up Public Access - -The Billions mobile app sends callbacks to your server, so you need public access. Use tools like: -- ngrok -- localtunnel - - -### 4. Start the Server - -To start ans test the server in dev mode use: -```bash -npm run pm2:dev -``` - -You should see a series of messages from PM2 indicating that it’s launching multiple instances of the backend in cluster mode. This confirms that your Billions Verifier backend has successfully started under PM2, leveraging multiple workers for scalability and fault tolerance. - -### 5. Test the Integration - -1. **Open your browser** to `http://localhost:8080` -2. **Check health status** at `http://localhost:8080/api/health` to verify Redis connection -3. **Reload** the page to start a session and invoke verification request. -4. **Click** the button to continue flow on Billions Web Wallet or **Scan the QR after downloading the APP** to continue the flow on the native app. -5. **Complete verification** in the app -6. **See status update** on the web page - -### 6. Production Features - -This implementation includes production-ready features: - -- **Redis Storage**: Persistent storage for sessions and verifications -- **PM2**: Process Manager for Node.js applications with a built-in Load Balancer. -- Use a permament storage for sessions and verifications relared to uniquness and nullifier id. -- **Health Check**: `/api/health` endpoint for monitoring -- **Graceful Shutdown**: Proper cleanup of Redis connections -- **Error Handling**: Comprehensive error handling for Redis operations -- **TTL Management**: Automatic expiration of stored data -- **Security**: Rate limiting, CORS, and security headers - - ---- - - - diff --git a/Integration-for-verifiers (Billions Wallet)/ecosystem.config.js b/Integration-for-verifiers (Billions Wallet)/ecosystem.config.js index f7294220..821536b1 100644 --- a/Integration-for-verifiers (Billions Wallet)/ecosystem.config.js +++ b/Integration-for-verifiers (Billions Wallet)/ecosystem.config.js @@ -2,8 +2,8 @@ module.exports = { apps: [{ name: 'billions-verifier', script: 'src/index.js', - instances: 'max', // Use all CPU cores - exec_mode: 'cluster', // Enable cluster mode for load balancing + instances: 1, + exec_mode: 'fork', // Environment configuration env: { diff --git a/Integration-for-verifiers (Billions Wallet)/package-lock.json b/Integration-for-verifiers (Billions Wallet)/package-lock.json index 1bb75f89..62f4395a 100644 --- a/Integration-for-verifiers (Billions Wallet)/package-lock.json +++ b/Integration-for-verifiers (Billions Wallet)/package-lock.json @@ -8,7 +8,7 @@ "name": "billions-wallet-verifier", "version": "1.0.0", "dependencies": { - "@0xpolygonid/js-sdk": "^1.31.1", + "@0xpolygonid/js-sdk": "^1.43.0", "@iden3/js-iden3-auth": "1.10.1", "async-mutex": "^0.5.0", "cors": "^2.8.5", @@ -16,74 +16,70 @@ "express": "^4.18.2", "express-rate-limit": "^7.5.0", "helmet": "^8.1.0", - "pg": "^8.18.0", "raw-body": "^3.0.0", - "redis": "^4.6.12", - "uuid": "^11.1.0", "winston": "^3.18.3" }, "devDependencies": { "@types/cors": "^2.8.17", "@types/express": "^4.17.21", "@types/node": "^22.10.2", - "@types/uuid": "^10.0.0", "ts-node": "^10.9.2", "typescript": "^5.7.2" } }, "node_modules/@0xpolygonid/js-sdk": { - "version": "1.31.1", - "resolved": "https://registry.npmjs.org/@0xpolygonid/js-sdk/-/js-sdk-1.31.1.tgz", - "integrity": "sha512-CgMYtUgm7fKgPKhNI1cRj8JeqiT9oI0xacjO/o1JqCKM+vEK1SZ+YbZrgE/DWYdqPg45iot9/cury8TjCs5vPQ==", + "version": "1.43.0", + "resolved": "https://registry.npmjs.org/@0xpolygonid/js-sdk/-/js-sdk-1.43.0.tgz", + "integrity": "sha512-Zsf92vtAtjPY4Fx0lJ0PZXr4AzhmZ0aqlPrKF283gyQJt3LIZLf9wdD9VaSI/rF7hlLi2mCuyj5zSOoNWlEbtQ==", "license": "MIT or Apache-2.0", "dependencies": { - "@iden3/onchain-non-merklized-issuer-base-abi": "^0.0.3", - "@iden3/universal-verifier-v2-abi": "^2.0.1", - "@noble/curves": "^1.9.2", - "ajv": "8.12.0", - "ajv-formats": "2.1.1", - "did-jwt": "8.0.4", + "@iden3/onchain-non-merklized-issuer-base-abi": "0.0.3", + "@iden3/universal-verifier-v2-abi": "2.0.2", + "@noble/curves": "1.9.2", + "@solana/web3.js": "1.98.4", + "ajv": "8.17.1", + "ajv-formats": "3.0.1", + "borsh": "0.7.0", + "canonicalize": "^2.1.0", + "did-jwt": "8.0.18", "did-resolver": "4.1.0", - "ethers": "^6.13.1", + "ethers": "6.15.0", "idb-keyval": "6.2.2", - "js-sha3": "0.9.3", - "jsonld": "8.3.1", - "pubsub-js": "1.9.4", - "uuid": "9.0.1" + "jose": "^6.1.0", + "jsonld": "8.3.3", + "pubsub-js": "1.9.5", + "quick-lru": "7.0.1", + "uuid": "13.0.0" }, "engines": { "node": ">=20.11.0" }, "peerDependencies": { - "@iden3/js-crypto": "1.3.1", - "@iden3/js-iden3-core": "1.7.0", - "@iden3/js-jsonld-merklization": "1.6.1", - "@iden3/js-jwz": "1.10.0", - "@iden3/js-merkletree": "1.5.0", + "@iden3/js-crypto": "1.3.2", + "@iden3/js-iden3-core": "1.8.0", + "@iden3/js-jsonld-merklization": "1.7.2", + "@iden3/js-jwz": "1.12.2", + "@iden3/js-merkletree": "1.5.1", "ffjavascript": "0.3.1", "rfc4648": "1.5.4", "snarkjs": "0.7.5" } }, - "node_modules/@0xpolygonid/js-sdk/node_modules/uuid": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "license": "MIT", - "bin": { - "uuid": "dist/bin/uuid" - } - }, "node_modules/@adraffy/ens-normalize": { "version": "1.10.1", "resolved": "https://registry.npmjs.org/@adraffy/ens-normalize/-/ens-normalize-1.10.1.tgz", "integrity": "sha512-96Z2IP3mYmF1Xg2cDm8f1gWGf/HUVedQ3FMifV4kG/PQ4yEP51xDtRAEfhVNt5f/uzpNkZHwWQuUcu6D6K+Ekw==", "license": "MIT" }, + "node_modules/@babel/runtime": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.2.tgz", + "integrity": "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@colors/colors": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz", @@ -891,13 +887,25 @@ } }, "node_modules/@iden3/js-crypto": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@iden3/js-crypto/-/js-crypto-1.3.1.tgz", - "integrity": "sha512-zZefKE2+GpEMPPMPDKqNBIvKZH8ubs/+nwOx2+rRZj+gSI1P49e9Nz6vGb/4YGTTHdPU2/L4lReQ5p/bF23c6A==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@iden3/js-crypto/-/js-crypto-1.3.2.tgz", + "integrity": "sha512-B1Fk8NLIhvEahFf02VKkmmXrw6Q+n+b3pbiFUYA9eesgoTZTSPtYtb+8A2jIKZrfdYKS5PlKJC8nNWmHUGk+6w==", "license": "AGPL-3.0", "peer": true, "dependencies": { - "@noble/hashes": "^1.8.0" + "@noble/hashes": "^2.0.1" + } + }, + "node_modules/@iden3/js-crypto/node_modules/@noble/hashes": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-2.0.1.tgz", + "integrity": "sha512-XlOlEbQcE9fmuXxrVTXCTlG2nlRXa9Rj3rr5Ue/+tX+nmkgbX720YHh0VR3hBF9xDvwnb8D2shVGOwNx+ulArw==", + "license": "MIT", + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" } }, "node_modules/@iden3/js-iden3-auth": { @@ -953,10 +961,22 @@ "snarkjs": "0.7.5" } }, + "node_modules/@iden3/js-iden3-auth/node_modules/@0xpolygonid/js-sdk/node_modules/@noble/hashes": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.2.tgz", + "integrity": "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==", + "license": "MIT", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/@iden3/js-iden3-auth/node_modules/@0xpolygonid/js-sdk/node_modules/ethers": { - "version": "6.15.0", - "resolved": "https://registry.npmjs.org/ethers/-/ethers-6.15.0.tgz", - "integrity": "sha512-Kf/3ZW54L4UT0pZtsY/rf+EkBU7Qi5nnhonjUb8yTXcxH3cdcWrV2cRyk0Xk/4jK6OoHhxxZHriyhje20If2hQ==", + "version": "6.16.0", + "resolved": "https://registry.npmjs.org/ethers/-/ethers-6.16.0.tgz", + "integrity": "sha512-U1wulmetNymijEhpSEQ7Ct/P/Jw9/e7R1j5XIbPRydgV2DjLVMsULDlNksq3RQnFgKoLlZf88ijYtWEXcPa07A==", "funding": [ { "type": "individual", @@ -993,14 +1013,106 @@ "url": "https://paulmillr.com/funding/" } }, - "node_modules/@iden3/js-iden3-auth/node_modules/@noble/hashes": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.2.tgz", - "integrity": "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==", - "license": "MIT", - "engines": { - "node": ">= 16" + "node_modules/@iden3/js-iden3-auth/node_modules/@iden3/js-crypto": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@iden3/js-crypto/-/js-crypto-1.3.1.tgz", + "integrity": "sha512-zZefKE2+GpEMPPMPDKqNBIvKZH8ubs/+nwOx2+rRZj+gSI1P49e9Nz6vGb/4YGTTHdPU2/L4lReQ5p/bF23c6A==", + "license": "AGPL-3.0", + "peer": true, + "dependencies": { + "@noble/hashes": "^1.8.0" + } + }, + "node_modules/@iden3/js-iden3-auth/node_modules/@iden3/js-iden3-core": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@iden3/js-iden3-core/-/js-iden3-core-1.7.0.tgz", + "integrity": "sha512-rNTrrhfzMwIBWYGGIOQ9yNpuSPvKJxryKiiP6l0+oCguaDoDo18rrcz2oBOfiO0Q7wDb41v73sLh4pwOlzCHpg==", + "license": "MIT or Apache-2.0", + "peer": true, + "peerDependencies": { + "@iden3/js-crypto": "1.3.1" + } + }, + "node_modules/@iden3/js-iden3-auth/node_modules/@iden3/js-jsonld-merklization": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@iden3/js-jsonld-merklization/-/js-jsonld-merklization-1.6.1.tgz", + "integrity": "sha512-RwRG/+9bRKPKbdXyIvCI+pEnpSz/rBS7jCwX2B6Ocn83Sxpc9hKgRNglMeIk41bQMRib5q9dcNK1M1v9C1I8rg==", + "hasInstallScript": true, + "license": "MIT or Apache-2.0", + "peer": true, + "dependencies": { + "@js-temporal/polyfill": "0.5.1", + "jsonld": "8.3.2", + "n3": "1.17.3", + "patch-package": "^8.0.0" + }, + "peerDependencies": { + "@iden3/js-crypto": "1.3.1", + "@iden3/js-merkletree": "1.5.0" + } + }, + "node_modules/@iden3/js-iden3-auth/node_modules/@iden3/js-jsonld-merklization/node_modules/canonicalize": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/canonicalize/-/canonicalize-1.0.8.tgz", + "integrity": "sha512-0CNTVCLZggSh7bc5VkX5WWPWO+cyZbNd07IHIsSXLia/eAq+r836hgk+8BKoEh7949Mda87VUOitx5OddVj64A==", + "license": "Apache-2.0" + }, + "node_modules/@iden3/js-iden3-auth/node_modules/@iden3/js-jsonld-merklization/node_modules/jsonld": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/jsonld/-/jsonld-8.3.2.tgz", + "integrity": "sha512-MwBbq95szLwt8eVQ1Bcfwmgju/Y5P2GdtlHE2ncyfuYjIdEhluUVyj1eudacf1mOkWIoS9GpDBTECqhmq7EOaA==", + "license": "BSD-3-Clause", + "dependencies": { + "@digitalbazaar/http-client": "^3.4.1", + "canonicalize": "^1.0.1", + "lru-cache": "^6.0.0", + "rdf-canonize": "^3.4.0" }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@iden3/js-iden3-auth/node_modules/@iden3/js-jwz": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@iden3/js-jwz/-/js-jwz-1.10.0.tgz", + "integrity": "sha512-y4gyloZhRxe7c1EuYrL7BKYvrZ3CILva3YsI2djB5I2haVYHL+9Uiw7K/6NCkH/Daq9+a7mzoY9VzHWCN1lgFw==", + "license": "AGPL-3.0", + "peer": true, + "peerDependencies": { + "@iden3/js-crypto": "1.3.1", + "@iden3/js-iden3-core": "1.7.0", + "@iden3/js-merkletree": "1.5.0", + "@noble/curves": "^1.9.1", + "ffjavascript": "0.3.1", + "rfc4648": "1.5.4", + "snarkjs": "0.7.5" + } + }, + "node_modules/@iden3/js-iden3-auth/node_modules/@iden3/js-merkletree": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@iden3/js-merkletree/-/js-merkletree-1.5.0.tgz", + "integrity": "sha512-zGaGxU2o8NtgvUV7gkS8NUZv7S5zNvDmmPCaRFOLgDmbqlAf93WRBItq6E2y84czZpAYGxFZOb9+Dq3JgwFSSw==", + "license": "MIT or Apache-2.0", + "peer": true, + "peerDependencies": { + "@iden3/js-crypto": "1.3.1", + "idb-keyval": "6.2.2" + } + }, + "node_modules/@iden3/js-iden3-auth/node_modules/@noble/ciphers": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/@noble/ciphers/-/ciphers-0.5.3.tgz", + "integrity": "sha512-B0+6IIHiqEs3BPMT0hcRmHvEj2QHOLu+uwt+tqDDeVd0oyVzh7BPrDcPjRnV1PV/5LaknXJJQvOuRGR0zQJz+w==", + "license": "MIT", + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@iden3/js-iden3-auth/node_modules/@scure/base": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.2.6.tgz", + "integrity": "sha512-g/nm5FgUa//MCj1gV09zTJTaM6KBAHqLN907YVQqf7zC49+DcO4B1so4ZX07Ef10Twr6nuqYEH9GEggFXA4Fmg==", + "license": "MIT", "funding": { "url": "https://paulmillr.com/funding/" } @@ -1014,6 +1126,56 @@ "undici-types": "~6.19.2" } }, + "node_modules/@iden3/js-iden3-auth/node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@iden3/js-iden3-auth/node_modules/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/@iden3/js-iden3-auth/node_modules/did-jwt": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/did-jwt/-/did-jwt-8.0.4.tgz", + "integrity": "sha512-KPtG7H+8GgKGMiDqFvOdNy5BBN3hpA+8xV7VygEnpst5oPIqjvcH3rTtnPF55a8bOxIzE2PudKGIXIQhekv7WA==", + "license": "Apache-2.0", + "dependencies": { + "@noble/ciphers": "^0.5.0", + "@noble/curves": "^1.0.0", + "@noble/hashes": "^1.3.0", + "@scure/base": "^1.1.3", + "canonicalize": "^2.0.0", + "did-resolver": "^4.1.0", + "multibase": "^4.0.6", + "multiformats": "^9.6.2", + "uint8arrays": "3.1.1" + } + }, "node_modules/@iden3/js-iden3-auth/node_modules/ethers": { "version": "5.8.0", "resolved": "https://registry.npmjs.org/ethers/-/ethers-5.8.0.tgz", @@ -1062,6 +1224,46 @@ "@ethersproject/wordlists": "5.8.0" } }, + "node_modules/@iden3/js-iden3-auth/node_modules/jsonld": { + "version": "8.3.1", + "resolved": "https://registry.npmjs.org/jsonld/-/jsonld-8.3.1.tgz", + "integrity": "sha512-tYfKpWL56meSJCHS91Ph0+EUThHZOZ8bKuboME4998SF+Kkukp2PhCPdRCvA7tsGUKr9FvSoyIRqJPuImBcBuA==", + "license": "BSD-3-Clause", + "dependencies": { + "@digitalbazaar/http-client": "^3.4.1", + "canonicalize": "^1.0.1", + "lru-cache": "^6.0.0", + "rdf-canonize": "^3.4.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@iden3/js-iden3-auth/node_modules/jsonld/node_modules/canonicalize": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/canonicalize/-/canonicalize-1.0.8.tgz", + "integrity": "sha512-0CNTVCLZggSh7bc5VkX5WWPWO+cyZbNd07IHIsSXLia/eAq+r836hgk+8BKoEh7949Mda87VUOitx5OddVj64A==", + "license": "Apache-2.0" + }, + "node_modules/@iden3/js-iden3-auth/node_modules/n3": { + "version": "1.17.3", + "resolved": "https://registry.npmjs.org/n3/-/n3-1.17.3.tgz", + "integrity": "sha512-ZHc24eZi2GIJcJQVxtL6NT3g+mTHRNeTVfXWELzeUOirqLrh2AAyg0nfYZ/kryJWKFSCgO37DGB6Ok3qmGgEcA==", + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.1.2", + "readable-stream": "^4.0.0" + }, + "engines": { + "node": ">=12.0" + } + }, + "node_modules/@iden3/js-iden3-auth/node_modules/pubsub-js": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/pubsub-js/-/pubsub-js-1.9.4.tgz", + "integrity": "sha512-hJYpaDvPH4w8ZX/0Fdf9ma1AwRgU353GfbaVfPjfJQf1KxZ2iHaHl3fAUw1qlJIR5dr4F3RzjGaWohYUEyoh7A==", + "license": "MIT" + }, "node_modules/@iden3/js-iden3-auth/node_modules/tslib": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", @@ -1088,78 +1290,55 @@ } }, "node_modules/@iden3/js-iden3-core": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@iden3/js-iden3-core/-/js-iden3-core-1.7.0.tgz", - "integrity": "sha512-rNTrrhfzMwIBWYGGIOQ9yNpuSPvKJxryKiiP6l0+oCguaDoDo18rrcz2oBOfiO0Q7wDb41v73sLh4pwOlzCHpg==", + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@iden3/js-iden3-core/-/js-iden3-core-1.8.0.tgz", + "integrity": "sha512-HoKRgVsCsq78r4EwFe98rdZKOgwEFXqV2SMwZ8bl2jIvUbabJQBTL1IYc7SGh0KvEpVisPq5NuwHJaSyvghqPQ==", "license": "MIT or Apache-2.0", "peer": true, "peerDependencies": { - "@iden3/js-crypto": "1.3.1" + "@iden3/js-crypto": "1.3.2" } }, "node_modules/@iden3/js-jsonld-merklization": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/@iden3/js-jsonld-merklization/-/js-jsonld-merklization-1.6.1.tgz", - "integrity": "sha512-RwRG/+9bRKPKbdXyIvCI+pEnpSz/rBS7jCwX2B6Ocn83Sxpc9hKgRNglMeIk41bQMRib5q9dcNK1M1v9C1I8rg==", - "hasInstallScript": true, + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/@iden3/js-jsonld-merklization/-/js-jsonld-merklization-1.7.2.tgz", + "integrity": "sha512-pXC2YO0yyD6YXgqZCivxmCxBFGs14sLS/AoQzjos8pNDrb3bYm3s+eMfz+4GwCKNpzzTFWSMHJNxzp/HUTEQZA==", "license": "MIT or Apache-2.0", "peer": true, "dependencies": { "@js-temporal/polyfill": "0.5.1", - "jsonld": "8.3.2", - "n3": "1.17.3", - "patch-package": "^8.0.0" + "jsonld": "8.3.3", + "n3": "1.26.0" }, "peerDependencies": { - "@iden3/js-crypto": "1.3.1", - "@iden3/js-merkletree": "1.5.0" - } - }, - "node_modules/@iden3/js-jsonld-merklization/node_modules/canonicalize": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/canonicalize/-/canonicalize-1.0.8.tgz", - "integrity": "sha512-0CNTVCLZggSh7bc5VkX5WWPWO+cyZbNd07IHIsSXLia/eAq+r836hgk+8BKoEh7949Mda87VUOitx5OddVj64A==", - "license": "Apache-2.0" - }, - "node_modules/@iden3/js-jsonld-merklization/node_modules/jsonld": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/jsonld/-/jsonld-8.3.2.tgz", - "integrity": "sha512-MwBbq95szLwt8eVQ1Bcfwmgju/Y5P2GdtlHE2ncyfuYjIdEhluUVyj1eudacf1mOkWIoS9GpDBTECqhmq7EOaA==", - "license": "BSD-3-Clause", - "dependencies": { - "@digitalbazaar/http-client": "^3.4.1", - "canonicalize": "^1.0.1", - "lru-cache": "^6.0.0", - "rdf-canonize": "^3.4.0" - }, - "engines": { - "node": ">=14" + "@iden3/js-crypto": "1.3.2", + "@iden3/js-merkletree": "1.5.1" } }, "node_modules/@iden3/js-jwz": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/@iden3/js-jwz/-/js-jwz-1.10.0.tgz", - "integrity": "sha512-y4gyloZhRxe7c1EuYrL7BKYvrZ3CILva3YsI2djB5I2haVYHL+9Uiw7K/6NCkH/Daq9+a7mzoY9VzHWCN1lgFw==", + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@iden3/js-jwz/-/js-jwz-1.12.2.tgz", + "integrity": "sha512-IfAJz84u1zLPJqEWGLahGFiKYCyeNY+3JpPft/3pVtgm10tYZIBobV4F+g/o2FdruoEgpFtFNUQ1o/7zsmwFEQ==", "license": "AGPL-3.0", "peer": true, "peerDependencies": { - "@iden3/js-crypto": "1.3.1", - "@iden3/js-iden3-core": "1.7.0", - "@iden3/js-merkletree": "1.5.0", - "@noble/curves": "^1.9.1", + "@iden3/js-crypto": "1.3.2", + "@iden3/js-iden3-core": "1.8.0", + "@iden3/js-merkletree": "1.5.1", + "@noble/curves": "^1.9.2", "ffjavascript": "0.3.1", "rfc4648": "1.5.4", "snarkjs": "0.7.5" } }, "node_modules/@iden3/js-merkletree": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@iden3/js-merkletree/-/js-merkletree-1.5.0.tgz", - "integrity": "sha512-zGaGxU2o8NtgvUV7gkS8NUZv7S5zNvDmmPCaRFOLgDmbqlAf93WRBItq6E2y84czZpAYGxFZOb9+Dq3JgwFSSw==", + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@iden3/js-merkletree/-/js-merkletree-1.5.1.tgz", + "integrity": "sha512-EABFNqnxXDvS7uZL5wAgHP4VzAnOo3STxADj2Ian/BYKNfWZjOe+WjsVizCU/7jJuafJDEZOeCLbZeIhHRzwLg==", "license": "MIT or Apache-2.0", "peer": true, "peerDependencies": { - "@iden3/js-crypto": "1.3.1", + "@iden3/js-crypto": "1.3.2", "idb-keyval": "6.2.2" } }, @@ -1222,10 +1401,13 @@ "license": "MIT" }, "node_modules/@noble/ciphers": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/@noble/ciphers/-/ciphers-0.5.3.tgz", - "integrity": "sha512-B0+6IIHiqEs3BPMT0hcRmHvEj2QHOLu+uwt+tqDDeVd0oyVzh7BPrDcPjRnV1PV/5LaknXJJQvOuRGR0zQJz+w==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@noble/ciphers/-/ciphers-1.3.0.tgz", + "integrity": "sha512-2I0gnIVPtfnMw9ee9h1dJG7tp81+8Ob3OJb3Mv37rx5L40/b0i7djjCVvGOVqc9AEIQyvyu1i6ypKdFw8R8gQw==", "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, "funding": { "url": "https://paulmillr.com/funding/" } @@ -1258,83 +1440,149 @@ "url": "https://paulmillr.com/funding/" } }, - "node_modules/@redis/bloom": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-1.2.0.tgz", - "integrity": "sha512-HG2DFjYKbpNmVXsa0keLHp/3leGJz1mjh09f2RLGGLQZzSHpkmZWuwJbAvo3QcRY8p80m5+ZdXZdYOSBLlp7Cg==", + "node_modules/@scure/base": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@scure/base/-/base-2.0.0.tgz", + "integrity": "sha512-3E1kpuZginKkek01ovG8krQ0Z44E3DHPjc5S2rjJw9lZn3KSQOs8S7wqikF/AH7iRanHypj85uGyxk0XAyC37w==", "license": "MIT", - "peerDependencies": { - "@redis/client": "^1.0.0" + "funding": { + "url": "https://paulmillr.com/funding/" } }, - "node_modules/@redis/client": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/@redis/client/-/client-1.6.1.tgz", - "integrity": "sha512-/KCsg3xSlR+nCK8/8ZYSknYxvXHwubJrU82F3Lm1Fp6789VQ0/3RJKfsmRXjqfaTA++23CvC3hqmqe/2GEt6Kw==", + "node_modules/@so-ric/colorspace": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@so-ric/colorspace/-/colorspace-1.1.6.tgz", + "integrity": "sha512-/KiKkpHNOBgkFJwu9sh48LkHSMYGyuTcSFK/qMBdnOAlrRJzRSXAOFB5qwzaVQuDl8wAvHVMkaASQDReTahxuw==", + "license": "MIT", + "dependencies": { + "color": "^5.0.2", + "text-hex": "1.0.x" + } + }, + "node_modules/@solana/buffer-layout": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@solana/buffer-layout/-/buffer-layout-4.0.1.tgz", + "integrity": "sha512-E1ImOIAD1tBZFRdjeM4/pzTiTApC0AOBGwyAMS4fwIodCWArzJ3DWdoh8cKxeFM2fElkxBh2Aqts1BPC373rHA==", "license": "MIT", - "peer": true, "dependencies": { - "cluster-key-slot": "1.1.2", - "generic-pool": "3.9.0", - "yallist": "4.0.0" + "buffer": "~6.0.3" }, "engines": { - "node": ">=14" + "node": ">=5.10" } }, - "node_modules/@redis/graph": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@redis/graph/-/graph-1.1.1.tgz", - "integrity": "sha512-FEMTcTHZozZciLRl6GiiIB4zGm5z5F3F6a6FZCyrfxdKOhFlGkiAqlexWMBzCi4DcRoyiOsuLfW+cjlGWyExOw==", + "node_modules/@solana/codecs-core": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@solana/codecs-core/-/codecs-core-2.3.0.tgz", + "integrity": "sha512-oG+VZzN6YhBHIoSKgS5ESM9VIGzhWjEHEGNPSibiDTxFhsFWxNaz8LbMDPjBUE69r9wmdGLkrQ+wVPbnJcZPvw==", "license": "MIT", + "dependencies": { + "@solana/errors": "2.3.0" + }, + "engines": { + "node": ">=20.18.0" + }, "peerDependencies": { - "@redis/client": "^1.0.0" + "typescript": ">=5.3.3" } }, - "node_modules/@redis/json": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/@redis/json/-/json-1.0.7.tgz", - "integrity": "sha512-6UyXfjVaTBTJtKNG4/9Z8PSpKE6XgSyEb8iwaqDcy+uKrd/DGYHTWkUdnQDyzm727V7p21WUMhsqz5oy65kPcQ==", + "node_modules/@solana/codecs-numbers": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@solana/codecs-numbers/-/codecs-numbers-2.3.0.tgz", + "integrity": "sha512-jFvvwKJKffvG7Iz9dmN51OGB7JBcy2CJ6Xf3NqD/VP90xak66m/Lg48T01u5IQ/hc15mChVHiBm+HHuOFDUrQg==", "license": "MIT", + "dependencies": { + "@solana/codecs-core": "2.3.0", + "@solana/errors": "2.3.0" + }, + "engines": { + "node": ">=20.18.0" + }, "peerDependencies": { - "@redis/client": "^1.0.0" + "typescript": ">=5.3.3" } }, - "node_modules/@redis/search": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@redis/search/-/search-1.2.0.tgz", - "integrity": "sha512-tYoDBbtqOVigEDMAcTGsRlMycIIjwMCgD8eR2t0NANeQmgK/lvxNAvYyb6bZDD4frHRhIHkJu2TBRvB0ERkOmw==", + "node_modules/@solana/errors": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@solana/errors/-/errors-2.3.0.tgz", + "integrity": "sha512-66RI9MAbwYV0UtP7kGcTBVLxJgUxoZGm8Fbc0ah+lGiAw17Gugco6+9GrJCV83VyF2mDWyYnYM9qdI3yjgpnaQ==", "license": "MIT", + "dependencies": { + "chalk": "^5.4.1", + "commander": "^14.0.0" + }, + "bin": { + "errors": "bin/cli.mjs" + }, + "engines": { + "node": ">=20.18.0" + }, "peerDependencies": { - "@redis/client": "^1.0.0" + "typescript": ">=5.3.3" } }, - "node_modules/@redis/time-series": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@redis/time-series/-/time-series-1.1.0.tgz", - "integrity": "sha512-c1Q99M5ljsIuc4YdaCwfUEXsofakb9c8+Zse2qxTadu8TalLXuAESzLvFAvNVbkmSlvlzIQOLpBCmWI9wTOt+g==", + "node_modules/@solana/errors/node_modules/chalk": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", "license": "MIT", - "peerDependencies": { - "@redis/client": "^1.0.0" + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/@scure/base": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.2.6.tgz", - "integrity": "sha512-g/nm5FgUa//MCj1gV09zTJTaM6KBAHqLN907YVQqf7zC49+DcO4B1so4ZX07Ef10Twr6nuqYEH9GEggFXA4Fmg==", + "node_modules/@solana/web3.js": { + "version": "1.98.4", + "resolved": "https://registry.npmjs.org/@solana/web3.js/-/web3.js-1.98.4.tgz", + "integrity": "sha512-vv9lfnvjUsRiq//+j5pBdXig0IQdtzA0BRZ3bXEP4KaIyF1CcaydWqgyzQgfZMNIsWNWmG+AUHwPy4AHOD6gpw==", "license": "MIT", - "funding": { - "url": "https://paulmillr.com/funding/" + "dependencies": { + "@babel/runtime": "^7.25.0", + "@noble/curves": "^1.4.2", + "@noble/hashes": "^1.4.0", + "@solana/buffer-layout": "^4.0.1", + "@solana/codecs-numbers": "^2.1.0", + "agentkeepalive": "^4.5.0", + "bn.js": "^5.2.1", + "borsh": "^0.7.0", + "bs58": "^4.0.1", + "buffer": "6.0.3", + "fast-stable-stringify": "^1.0.0", + "jayson": "^4.1.1", + "node-fetch": "^2.7.0", + "rpc-websockets": "^9.0.2", + "superstruct": "^2.0.2" } }, - "node_modules/@so-ric/colorspace": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/@so-ric/colorspace/-/colorspace-1.1.6.tgz", - "integrity": "sha512-/KiKkpHNOBgkFJwu9sh48LkHSMYGyuTcSFK/qMBdnOAlrRJzRSXAOFB5qwzaVQuDl8wAvHVMkaASQDReTahxuw==", + "node_modules/@solana/web3.js/node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", "license": "MIT", "dependencies": { - "color": "^5.0.2", - "text-hex": "1.0.x" + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/@swc/helpers": { + "version": "0.5.19", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.19.tgz", + "integrity": "sha512-QamiFeIK3txNjgUTNppE6MiG3p7TdninpZu0E0PbqVh1a9FNLT2FRhisaa4NcaX52XVhA5l7Pk58Ft7Sqi/2sA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.8.0" } }, "node_modules/@tsconfig/node10": { @@ -1380,7 +1628,6 @@ "version": "3.4.38", "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", - "dev": true, "license": "MIT", "dependencies": { "@types/node": "*" @@ -1440,7 +1687,6 @@ "version": "22.16.0", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.16.0.tgz", "integrity": "sha512-B2egV9wALML1JCpv3VQoQ+yesQKAmNMBIAY7OteVrikcOcAkWm+dGL6qpeCktPjAv6N1JLnhbNiqS35UpFyBsQ==", - "dev": true, "license": "MIT", "peer": true, "dependencies": { @@ -1494,9 +1740,17 @@ "version": "10.0.0", "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-10.0.0.tgz", "integrity": "sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==", - "dev": true, "license": "MIT" }, + "node_modules/@types/ws": { + "version": "7.4.7", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-7.4.7.tgz", + "integrity": "sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@yarnpkg/lockfile": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz", @@ -1560,16 +1814,28 @@ "integrity": "sha512-G965FqalsNyrPqgEGON7nIx1e/OVENSgiEIzyC63haUMuvNnwIgIjMs52hlTCKhkBny7A2ORNlfY9Zu+jmGk1Q==", "license": "MIT" }, + "node_modules/agentkeepalive": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.6.0.tgz", + "integrity": "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==", + "license": "MIT", + "dependencies": { + "humanize-ms": "^1.2.1" + }, + "engines": { + "node": ">= 8.0.0" + } + }, "node_modules/ajv": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", - "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "license": "MIT", "dependencies": { - "fast-deep-equal": "^3.1.1", + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" + "require-from-string": "^2.0.2" }, "funding": { "type": "github", @@ -1577,9 +1843,9 @@ } }, "node_modules/ajv-formats": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", - "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", "license": "MIT", "dependencies": { "ajv": "^8.0.0" @@ -1636,15 +1902,6 @@ "tslib": "^2.4.0" } }, - "node_modules/at-least-node": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", - "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", - "license": "ISC", - "engines": { - "node": ">= 4.0.0" - } - }, "node_modules/b4a": { "version": "1.6.7", "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.7.tgz", @@ -1657,6 +1914,15 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "license": "MIT" }, + "node_modules/base-x": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.11.tgz", + "integrity": "sha512-xz7wQ8xDhdyP7tQxwdteLYeFfS68tSMNCZ/Y37WJ4bhGfKPpqEIlmIyueQHqOyoPhE6xNUqjzRr8ra0eF9VRvA==", + "license": "MIT", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, "node_modules/base64-js": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", @@ -1760,6 +2026,17 @@ "node": ">= 0.8" } }, + "node_modules/borsh": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/borsh/-/borsh-0.7.0.tgz", + "integrity": "sha512-CLCsZGIBCFnPtkNnieW/a8wmreDmfUtjU2m9yHrzPXIlNbqVs0AQrSatSG6vdNYUqdc83tkQi2eHfF98ubzQLA==", + "license": "Apache-2.0", + "dependencies": { + "bn.js": "^5.2.0", + "bs58": "^4.0.0", + "text-encoding-utf-8": "^1.0.2" + } + }, "node_modules/brace-expansion": { "version": "1.1.12", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", @@ -1788,6 +2065,15 @@ "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==", "license": "MIT" }, + "node_modules/bs58": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz", + "integrity": "sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw==", + "license": "MIT", + "dependencies": { + "base-x": "^3.0.2" + } + }, "node_modules/buffer": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", @@ -1926,15 +2212,6 @@ "calcwit": "calcwit.js" } }, - "node_modules/cluster-key-slot": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz", - "integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==", - "license": "Apache-2.0", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/color": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/color/-/color-5.0.2.tgz", @@ -2008,6 +2285,15 @@ "node": ">=12.20" } }, + "node_modules/commander": { + "version": "14.0.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.3.tgz", + "integrity": "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==", + "license": "MIT", + "engines": { + "node": ">=20" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -2125,6 +2411,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/delay": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/delay/-/delay-5.0.0.tgz", + "integrity": "sha512-ReEBKkIfe4ya47wlPYf/gu5ib6yUG0/Aez0JQZQz94kiWtRQvZIQbTiehsnwHvLSWJnQdhVeqYue7Id1dKr0qw==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", @@ -2145,15 +2443,15 @@ } }, "node_modules/did-jwt": { - "version": "8.0.4", - "resolved": "https://registry.npmjs.org/did-jwt/-/did-jwt-8.0.4.tgz", - "integrity": "sha512-KPtG7H+8GgKGMiDqFvOdNy5BBN3hpA+8xV7VygEnpst5oPIqjvcH3rTtnPF55a8bOxIzE2PudKGIXIQhekv7WA==", + "version": "8.0.18", + "resolved": "https://registry.npmjs.org/did-jwt/-/did-jwt-8.0.18.tgz", + "integrity": "sha512-yS3Y+aUKjYqRFrgR/RY77NuOOqS7SFfvfFH4THhWD6+hkxeUZcKQSsdNZ12QR1Vd48yP9exwae2wzbuOZn0NqQ==", "license": "Apache-2.0", "dependencies": { - "@noble/ciphers": "^0.5.0", + "@noble/ciphers": "^1.0.0", "@noble/curves": "^1.0.0", "@noble/hashes": "^1.3.0", - "@scure/base": "^1.1.3", + "@scure/base": "^2.0.0", "canonicalize": "^2.0.0", "did-resolver": "^4.1.0", "multibase": "^4.0.6", @@ -2290,6 +2588,21 @@ "node": ">= 0.4" } }, + "node_modules/es6-promise": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", + "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==", + "license": "MIT" + }, + "node_modules/es6-promisify": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", + "integrity": "sha512-C+d6UdsYDk0lMebHNR4S2NybQMMngAOnOwYBQjTOiv0MkoJMP0Myw2mgpDLBcpfCmRLxyFqYhS/CfOENq4SJhQ==", + "license": "MIT", + "dependencies": { + "es6-promise": "^4.0.3" + } + }, "node_modules/escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", @@ -2452,6 +2765,12 @@ "node": ">=6" } }, + "node_modules/eventemitter3": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz", + "integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==", + "license": "MIT" + }, "node_modules/events": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", @@ -2523,6 +2842,14 @@ "express": ">= 4.11" } }, + "node_modules/eyes": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz", + "integrity": "sha512-GipyPsXO1anza0AOZdy69Im7hGFCNB7Y/NGjDlZGJ3GJJLtwNSb2vrzYrTYJRrRloVx7pl+bhUaTB8yiccPvFQ==", + "engines": { + "node": "> 0.1.90" + } + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -2535,6 +2862,28 @@ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "license": "MIT" }, + "node_modules/fast-stable-stringify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fast-stable-stringify/-/fast-stable-stringify-1.0.0.tgz", + "integrity": "sha512-wpYMUmFu5f00Sm0cj2pfivpmawLZ0NKdviQ4w9zJeR8JVtOpOxHmLaJuj0vxvGqMJQWyP/COUkF75/57OKyRag==", + "license": "MIT" + }, + "node_modules/fast-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", + "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, "node_modules/fastfile": { "version": "0.0.20", "resolved": "https://registry.npmjs.org/fastfile/-/fastfile-0.0.20.tgz", @@ -2688,26 +3037,19 @@ } }, "node_modules/fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", "license": "MIT", "dependencies": { - "at-least-node": "^1.0.0", "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" }, "engines": { - "node": ">=10" + "node": ">=12" } }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "license": "ISC" - }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", @@ -2717,15 +3059,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/generic-pool": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.9.0.tgz", - "integrity": "sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g==", - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, "node_modules/get-intrinsic": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", @@ -2763,27 +3096,6 @@ "node": ">= 0.4" } }, - "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/gopd": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", @@ -2902,6 +3214,15 @@ "node": ">= 0.8" } }, + "node_modules/humanize-ms": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", + "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.0.0" + } + }, "node_modules/iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", @@ -2941,17 +3262,6 @@ ], "license": "BSD-3-Clause" }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", - "license": "ISC", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", @@ -3027,6 +3337,15 @@ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "license": "ISC" }, + "node_modules/isomorphic-ws": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-4.0.1.tgz", + "integrity": "sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w==", + "license": "MIT", + "peerDependencies": { + "ws": "*" + } + }, "node_modules/jake": { "version": "10.9.2", "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.2.tgz", @@ -3045,6 +3364,83 @@ "node": ">=10" } }, + "node_modules/jayson": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/jayson/-/jayson-4.3.0.tgz", + "integrity": "sha512-AauzHcUcqs8OBnCHOkJY280VaTiCm57AbuO7lqzcw7JapGj50BisE3xhksye4zlTSR1+1tAz67wLTl8tEH1obQ==", + "license": "MIT", + "dependencies": { + "@types/connect": "^3.4.33", + "@types/node": "^12.12.54", + "@types/ws": "^7.4.4", + "commander": "^2.20.3", + "delay": "^5.0.0", + "es6-promisify": "^5.0.0", + "eyes": "^0.1.8", + "isomorphic-ws": "^4.0.1", + "json-stringify-safe": "^5.0.1", + "stream-json": "^1.9.1", + "uuid": "^8.3.2", + "ws": "^7.5.10" + }, + "bin": { + "jayson": "bin/jayson.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jayson/node_modules/@types/node": { + "version": "12.20.55", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.55.tgz", + "integrity": "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==", + "license": "MIT" + }, + "node_modules/jayson/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "license": "MIT" + }, + "node_modules/jayson/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/jayson/node_modules/ws": { + "version": "7.5.10", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", + "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", + "license": "MIT", + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/jose": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/jose/-/jose-6.2.1.tgz", + "integrity": "sha512-jUaKr1yrbfaImV7R2TN/b3IcZzsw38/chqMpo2XJ7i2F8AfM/lA4G1goC3JVEwg0H7UldTmSt3P68nt31W7/mw==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, "node_modules/js-sha3": { "version": "0.9.3", "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.9.3.tgz", @@ -3082,10 +3478,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", + "license": "ISC" + }, "node_modules/jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", + "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", "license": "MIT", "dependencies": { "universalify": "^2.0.0" @@ -3104,9 +3506,9 @@ } }, "node_modules/jsonld": { - "version": "8.3.1", - "resolved": "https://registry.npmjs.org/jsonld/-/jsonld-8.3.1.tgz", - "integrity": "sha512-tYfKpWL56meSJCHS91Ph0+EUThHZOZ8bKuboME4998SF+Kkukp2PhCPdRCvA7tsGUKr9FvSoyIRqJPuImBcBuA==", + "version": "8.3.3", + "resolved": "https://registry.npmjs.org/jsonld/-/jsonld-8.3.3.tgz", + "integrity": "sha512-9YcilrF+dLfg9NTEof/mJLMtbdX1RJ8dbWtJgE00cMOIohb1lIyJl710vFiTaiHTl6ZYODJuBd32xFvUhmv3kg==", "license": "BSD-3-Clause", "dependencies": { "@digitalbazaar/http-client": "^3.4.1", @@ -3391,12 +3793,12 @@ "license": "(Apache-2.0 AND MIT)" }, "node_modules/n3": { - "version": "1.17.3", - "resolved": "https://registry.npmjs.org/n3/-/n3-1.17.3.tgz", - "integrity": "sha512-ZHc24eZi2GIJcJQVxtL6NT3g+mTHRNeTVfXWELzeUOirqLrh2AAyg0nfYZ/kryJWKFSCgO37DGB6Ok3qmGgEcA==", + "version": "1.26.0", + "resolved": "https://registry.npmjs.org/n3/-/n3-1.26.0.tgz", + "integrity": "sha512-SQknS0ua90rN+3RHuk8BeIqeYyqIH/+ecViZxX08jR4j6MugqWRjtONl3uANG/crWXnOM2WIqBJtjIhVYFha+w==", "license": "MIT", "dependencies": { - "queue-microtask": "^1.1.2", + "buffer": "^6.0.3", "readable-stream": "^4.0.0" }, "engines": { @@ -3456,6 +3858,18 @@ "url": "https://opencollective.com/node-fetch" } }, + "node_modules/node-gyp-build": { + "version": "4.8.4", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz", + "integrity": "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==", + "license": "MIT", + "optional": true, + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -3498,15 +3912,6 @@ "node": ">= 0.8" } }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "license": "ISC", - "dependencies": { - "wrappy": "1" - } - }, "node_modules/one-time": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", @@ -3549,15 +3954,6 @@ "node": ">= 0.8.0" } }, - "node_modules/os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -3568,9 +3964,9 @@ } }, "node_modules/patch-package": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/patch-package/-/patch-package-8.0.0.tgz", - "integrity": "sha512-da8BVIhzjtgScwDJ2TtKsfT5JFWz1hYoBl9rUQ1f38MC2HwnEIkK8VN3dKMKcP7P7bvvgzNDbfNHtx3MsQb5vA==", + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/patch-package/-/patch-package-8.0.1.tgz", + "integrity": "sha512-VsKRIA8f5uqHQ7NGhwIna6Bx6D9s/1iXlA1hthBVBEbkq+t4kXD0HHt+rJhf/Z+Ci0F/HCB2hvn0qLdLG+Qxlw==", "license": "MIT", "dependencies": { "@yarnpkg/lockfile": "^1.1.0", @@ -3578,15 +3974,14 @@ "ci-info": "^3.7.0", "cross-spawn": "^7.0.3", "find-yarn-workspace-root": "^2.0.0", - "fs-extra": "^9.0.0", + "fs-extra": "^10.0.0", "json-stable-stringify": "^1.0.2", "klaw-sync": "^6.0.0", "minimist": "^1.2.6", "open": "^7.4.2", - "rimraf": "^2.6.3", "semver": "^7.5.3", "slash": "^2.0.0", - "tmp": "^0.0.33", + "tmp": "^0.2.4", "yaml": "^2.2.2" }, "bin": { @@ -3597,15 +3992,6 @@ "npm": ">5" } }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", @@ -3621,96 +4007,6 @@ "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", "license": "MIT" }, - "node_modules/pg": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/pg/-/pg-8.18.0.tgz", - "integrity": "sha512-xqrUDL1b9MbkydY/s+VZ6v+xiMUmOUk7SS9d/1kpyQxoJ6U9AO1oIJyUWVZojbfe5Cc/oluutcgFG4L9RDP1iQ==", - "license": "MIT", - "peer": true, - "dependencies": { - "pg-connection-string": "^2.11.0", - "pg-pool": "^3.11.0", - "pg-protocol": "^1.11.0", - "pg-types": "2.2.0", - "pgpass": "1.0.5" - }, - "engines": { - "node": ">= 16.0.0" - }, - "optionalDependencies": { - "pg-cloudflare": "^1.3.0" - }, - "peerDependencies": { - "pg-native": ">=3.0.1" - }, - "peerDependenciesMeta": { - "pg-native": { - "optional": true - } - } - }, - "node_modules/pg-cloudflare": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.3.0.tgz", - "integrity": "sha512-6lswVVSztmHiRtD6I8hw4qP/nDm1EJbKMRhf3HCYaqud7frGysPv7FYJ5noZQdhQtN2xJnimfMtvQq21pdbzyQ==", - "license": "MIT", - "optional": true - }, - "node_modules/pg-connection-string": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.11.0.tgz", - "integrity": "sha512-kecgoJwhOpxYU21rZjULrmrBJ698U2RxXofKVzOn5UDj61BPj/qMb7diYUR1nLScCDbrztQFl1TaQZT0t1EtzQ==", - "license": "MIT" - }, - "node_modules/pg-int8": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", - "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==", - "license": "ISC", - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/pg-pool": { - "version": "3.11.0", - "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.11.0.tgz", - "integrity": "sha512-MJYfvHwtGp870aeusDh+hg9apvOe2zmpZJpyt+BMtzUWlVqbhFmMK6bOBXLBUPd7iRtIF9fZplDc7KrPN3PN7w==", - "license": "MIT", - "peerDependencies": { - "pg": ">=8.0" - } - }, - "node_modules/pg-protocol": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.11.0.tgz", - "integrity": "sha512-pfsxk2M9M3BuGgDOfuy37VNRRX3jmKgMjcvAcWqNDpZSf4cUmv8HSOl5ViRQFsfARFn0KuUQTgLxVMbNq5NW3g==", - "license": "MIT" - }, - "node_modules/pg-types": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", - "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", - "license": "MIT", - "dependencies": { - "pg-int8": "1.0.1", - "postgres-array": "~2.0.0", - "postgres-bytea": "~1.0.0", - "postgres-date": "~1.0.4", - "postgres-interval": "^1.1.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/pgpass": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz", - "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==", - "license": "MIT", - "dependencies": { - "split2": "^4.1.0" - } - }, "node_modules/picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", @@ -3723,45 +4019,6 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/postgres-array": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", - "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/postgres-bytea": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.1.tgz", - "integrity": "sha512-5+5HqXnsZPE65IJZSMkZtURARZelel2oXUEO8rH83VS/hxH5vv1uHquPg5wZs8yMAfdv971IU+kcPUczi7NVBQ==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postgres-date": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", - "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postgres-interval": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", - "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", - "license": "MIT", - "dependencies": { - "xtend": "^4.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/prelude-ls": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", @@ -3793,9 +4050,9 @@ } }, "node_modules/pubsub-js": { - "version": "1.9.4", - "resolved": "https://registry.npmjs.org/pubsub-js/-/pubsub-js-1.9.4.tgz", - "integrity": "sha512-hJYpaDvPH4w8ZX/0Fdf9ma1AwRgU353GfbaVfPjfJQf1KxZ2iHaHl3fAUw1qlJIR5dr4F3RzjGaWohYUEyoh7A==", + "version": "1.9.5", + "resolved": "https://registry.npmjs.org/pubsub-js/-/pubsub-js-1.9.5.tgz", + "integrity": "sha512-5MZ0I9i5JWVO7SizvOviKvZU2qaBbl2KQX150FAA+fJBwYpwOUId7aNygURWSdPzlsA/xZ/InUKXqBbzM0czTA==", "license": "MIT" }, "node_modules/punycode": { @@ -3941,23 +4198,6 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, - "node_modules/redis": { - "version": "4.7.1", - "resolved": "https://registry.npmjs.org/redis/-/redis-4.7.1.tgz", - "integrity": "sha512-S1bJDnqLftzHXHP8JsT5II/CtHWQrASX5K96REjWjlmWKrviSOLWmM7QnRLstAWsu1VBBV1ffV6DzCvxNP0UJQ==", - "license": "MIT", - "workspaces": [ - "./packages/*" - ], - "dependencies": { - "@redis/bloom": "1.2.0", - "@redis/client": "1.6.1", - "@redis/graph": "1.1.1", - "@redis/json": "1.0.7", - "@redis/search": "1.2.0", - "@redis/time-series": "1.1.0" - } - }, "node_modules/require-from-string": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", @@ -3974,17 +4214,49 @@ "license": "MIT", "peer": true }, - "node_modules/rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "deprecated": "Rimraf versions prior to v4 are no longer supported", - "license": "ISC", + "node_modules/rpc-websockets": { + "version": "9.3.5", + "resolved": "https://registry.npmjs.org/rpc-websockets/-/rpc-websockets-9.3.5.tgz", + "integrity": "sha512-4mAmr+AEhPYJ9TmDtxF3r3ZcbWy7W8kvZ4PoZYw/Xgp2J7WixjwTgiQZsoTDvch5nimmg3Ay6/0Kuh9oIvVs9A==", + "license": "LGPL-3.0-only", "dependencies": { - "glob": "^7.1.3" + "@swc/helpers": "^0.5.11", + "@types/uuid": "^10.0.0", + "@types/ws": "^8.2.2", + "buffer": "^6.0.3", + "eventemitter3": "^5.0.1", + "uuid": "^11.0.0", + "ws": "^8.5.0" }, + "funding": { + "type": "paypal", + "url": "https://paypal.me/kozjak" + }, + "optionalDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^6.0.0" + } + }, + "node_modules/rpc-websockets/node_modules/@types/ws": { + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", + "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/rpc-websockets/node_modules/uuid": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz", + "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", "bin": { - "rimraf": "bin.js" + "uuid": "dist/esm/bin/uuid" } }, "node_modules/safe-buffer": { @@ -4029,9 +4301,9 @@ "license": "MIT" }, "node_modules/semver": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", - "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -4263,15 +4535,6 @@ "node": ">=0.10.0" } }, - "node_modules/split2": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", - "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", - "license": "ISC", - "engines": { - "node": ">= 10.x" - } - }, "node_modules/stack-trace": { "version": "0.0.10", "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", @@ -4299,6 +4562,21 @@ "node": ">= 0.8" } }, + "node_modules/stream-chain": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/stream-chain/-/stream-chain-2.2.5.tgz", + "integrity": "sha512-1TJmBx6aSWqZ4tx7aTpBDXK0/e2hhcNSTV8+CbFJtDjbb+I1mZ8lHit0Grw9GRT+6JbIrrDd8esncgBi8aBXGA==", + "license": "BSD-3-Clause" + }, + "node_modules/stream-json": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/stream-json/-/stream-json-1.9.1.tgz", + "integrity": "sha512-uWkjJ+2Nt/LO9Z/JyKZbMusL8Dkh97uUBTv3AJQ74y07lVahLY4eEFsPsE97pxYBwr8nnjMAIch5eqI0gPShyw==", + "license": "BSD-3-Clause", + "dependencies": { + "stream-chain": "^2.2.5" + } + }, "node_modules/string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -4308,6 +4586,15 @@ "safe-buffer": "~5.2.0" } }, + "node_modules/superstruct": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/superstruct/-/superstruct-2.0.2.tgz", + "integrity": "sha512-uV+TFRZdXsqXTL2pRvujROjdZQ4RAlBUS5BTh9IGm+jTqQntYThciG/qu57Gs69yjnVUSqdxF9YLmSnpupBW9A==", + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -4320,6 +4607,11 @@ "node": ">=8" } }, + "node_modules/text-encoding-utf-8": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/text-encoding-utf-8/-/text-encoding-utf-8-1.0.2.tgz", + "integrity": "sha512-8bw4MY9WjdsD2aMtO0OzOCY3pXGYNx2d2FfHRVUKkiCPDWjKuOlhLVASS+pD7VkLTVjW268LYJHwsnPFlBpbAg==" + }, "node_modules/text-hex": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", @@ -4327,15 +4619,12 @@ "license": "MIT" }, "node_modules/tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.5.tgz", + "integrity": "sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==", "license": "MIT", - "dependencies": { - "os-tmpdir": "~1.0.2" - }, "engines": { - "node": ">=0.6.0" + "node": ">=14.14" } }, "node_modules/to-regex-range": { @@ -4359,6 +4648,12 @@ "node": ">=0.6" } }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "license": "MIT" + }, "node_modules/triple-beam": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz", @@ -4453,7 +4748,6 @@ "version": "5.8.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", - "dev": true, "license": "Apache-2.0", "peer": true, "bin": { @@ -4495,7 +4789,6 @@ "version": "6.21.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", - "dev": true, "license": "MIT" }, "node_modules/universalify": { @@ -4541,16 +4834,16 @@ } }, "node_modules/uuid": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz", - "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==", + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-13.0.0.tgz", + "integrity": "sha512-XQegIaBTVUjSHliKqcnFqYypAd4S+WCYt5NIeRs6w/UAry7z8Y9j5ZwRRL4kzq9U3sD6v+85er9FvkEaBpji2w==", "funding": [ "https://github.com/sponsors/broofa", "https://github.com/sponsors/ctavan" ], "license": "MIT", "bin": { - "uuid": "dist/esm/bin/uuid" + "uuid": "dist-node/bin/uuid" } }, "node_modules/v8-compile-cache-lib": { @@ -4599,6 +4892,22 @@ "integrity": "sha512-PgF341avzqyx60neE9DD+XS26MMNMoUQRz9NOZwW32nPQrF6p77f1htcnjBSEV8BGMKZ16choqUG4hyI0Hx7mA==", "license": "Apache-2.0" }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "license": "BSD-2-Clause" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -4687,17 +4996,12 @@ "node": ">=0.10.0" } }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "license": "ISC" - }, "node_modules/ws": { "version": "8.17.1", "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", "license": "MIT", + "peer": true, "engines": { "node": ">=10.0.0" }, @@ -4714,15 +5018,6 @@ } } }, - "node_modules/xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", - "license": "MIT", - "engines": { - "node": ">=0.4" - } - }, "node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", @@ -4730,15 +5025,18 @@ "license": "ISC" }, "node_modules/yaml": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.0.tgz", - "integrity": "sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ==", + "version": "2.8.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.2.tgz", + "integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==", "license": "ISC", "bin": { "yaml": "bin.mjs" }, "engines": { "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" } }, "node_modules/yn": { diff --git a/Integration-for-verifiers (Billions Wallet)/package.json b/Integration-for-verifiers (Billions Wallet)/package.json index fd1e7b24..bf8b60e1 100644 --- a/Integration-for-verifiers (Billions Wallet)/package.json +++ b/Integration-for-verifiers (Billions Wallet)/package.json @@ -16,7 +16,7 @@ "logs": "tail -f logs/combined.log" }, "dependencies": { - "@0xpolygonid/js-sdk": "^1.31.1", + "@0xpolygonid/js-sdk": "^1.43.0", "@iden3/js-iden3-auth": "1.10.1", "async-mutex": "^0.5.0", "cors": "^2.8.5", @@ -24,17 +24,13 @@ "express": "^4.18.2", "express-rate-limit": "^7.5.0", "helmet": "^8.1.0", - "pg": "^8.18.0", "raw-body": "^3.0.0", - "redis": "^4.6.12", - "uuid": "^11.1.0", "winston": "^3.18.3" }, "devDependencies": { "@types/cors": "^2.8.17", "@types/express": "^4.17.21", "@types/node": "^22.10.2", - "@types/uuid": "^10.0.0", "ts-node": "^10.9.2", "typescript": "^5.7.2" } diff --git a/Integration-for-verifiers (Billions Wallet)/src/index.js b/Integration-for-verifiers (Billions Wallet)/src/index.js index db5de490..43fc7f02 100644 --- a/Integration-for-verifiers (Billions Wallet)/src/index.js +++ b/Integration-for-verifiers (Billions Wallet)/src/index.js @@ -10,8 +10,8 @@ const cors = require('cors'); const { randomInt } = require('crypto'); const { getConfig, createProofRequest } = require('./config/verification-configs'); -const redisService = require('./services/redis-service'); -const dbService = require('./services/db-service'); +const memoryStore = require('./services/memory-store'); +const nullifierStore = require('./services/nullifier-store'); const logger = require('./services/logger'); const app = express(); @@ -56,6 +56,8 @@ async function getVerifierInstance() { return cachedVerifier; } +app.set('trust proxy', 1); + app.use(helmet({ contentSecurityPolicy: { directives: { @@ -132,12 +134,8 @@ app.get("/api/verification-request", async (req, res) => { const proofRequest = createProofRequest(USE_CASE, sessionId, nullifierSessionID); request.body.scope = [...(request.body.scope ?? []), proofRequest]; - const authStored = await redisService.setAuthRequest(sessionId, request); - const statusStored = await redisService.setVerificationStatus(proofRequest.id, "pending"); - - if (!authStored || !statusStored) { - logger.warn('Redis storage failed, falling back to memory cache', { sessionId }); - } + memoryStore.setAuthRequest(sessionId, request); + memoryStore.setVerificationStatus(proofRequest.id, "pending"); logger.info('Verification request generated', { sessionId, useCase: USE_CASE }); logger.logRequest(req, res, Date.now() - startTime); @@ -149,58 +147,27 @@ app.get("/api/verification-request", async (req, res) => { } }); -app.get('/api/health', async (req, res) => { - try { - const redisHealthy = await redisService.healthCheck(); - const redisStatus = redisService.getStatus(); - const cacheStats = redisService.getCacheStats(); - - const hasMemoryFallback = cacheStats.totalEntries > 0; - const status = redisHealthy ? 'healthy' : (hasMemoryFallback ? 'degraded' : 'unhealthy'); - const statusCode = redisHealthy ? 200 : (hasMemoryFallback ? 206 : 503); - - return res.status(statusCode).json({ - status, - timestamp: new Date().toISOString(), - redis: { - connected: redisHealthy, - host: process.env.REDIS_HOST, - circuitBreaker: redisStatus.circuitBreakerOpen, - failures: redisStatus.failures, - reconnectAttempts: redisStatus.reconnectAttempts - }, - memoryCache: { - active: !redisHealthy && hasMemoryFallback, - size: cacheStats.totalEntries, - utilization: `${cacheStats.utilizationPercent}%`, - averageAge: `${Math.round(cacheStats.averageAgeMs / 1000)}s` - }, - server: { - port, - useCase: USE_CASE, - config: currentConfig.name - } - }); - } catch (error) { - logger.error('Error in /api/health', { error: error.message }); - return res.status(503).json({ status: 'unhealthy', error: error.message }); - } +app.get('/api/health', (_req, res) => { + return res.status(200).json({ + status: 'healthy', + timestamp: new Date().toISOString(), + server: { + port, + useCase: USE_CASE, + config: currentConfig.name + } + }); }); -app.get("/api/status/:id", async (req, res) => { - try { - const requestId = parseInt(req.params.id); - - if (isNaN(requestId) || requestId <= 0) { - return res.status(400).json({ error: "Invalid request ID" }); - } +app.get("/api/status/:id", (req, res) => { + const requestId = parseInt(req.params.id); - const status = await redisService.getVerificationStatus(requestId) || "not_found"; - return res.status(200).json({ requestId, status }); - } catch (error) { - logger.error("Error in /api/status", { error: error.message }); - return res.status(500).json({ error: "Internal server error" }); + if (isNaN(requestId) || requestId <= 0) { + return res.status(400).json({ error: "Invalid request ID" }); } + + const status = memoryStore.getVerificationStatus(requestId) || "not_found"; + return res.status(200).json({ requestId, status }); }); app.post("/api/callback", async (req, res) => { @@ -227,7 +194,7 @@ app.post("/api/callback", async (req, res) => { return res.status(400).json({ error: "Token too large" }); } - const authRequest = await redisService.getAuthRequest(sessionId); + const authRequest = await memoryStore.getAuthRequest(sessionId); if (!authRequest) { return res.status(400).json({ error: "Session not found or expired" }); } @@ -262,7 +229,7 @@ app.post("/api/callback", async (req, res) => { ); const proofRequestId = scopeEntry?.id; - const { claimed } = await dbService.claimNullifier(nullifierHash, sessionId); + const { claimed } = await nullifierStore.claimNullifier(nullifierHash); if (!claimed) { logger.logSecurityEvent('REPLAY_ATTACK_BLOCKED', { nullifierHash: nullifierHash.substring(0, 20) + '...', @@ -270,14 +237,14 @@ app.post("/api/callback", async (req, res) => { }, 'warn'); if (proofRequestId) { - await redisService.setVerificationStatus(proofRequestId, "already_verified"); + memoryStore.setVerificationStatus(proofRequestId, "already_verified"); } return res.status(400).json({ error: "This identity has already been verified." }); } if (proofRequestId) { - await redisService.setVerificationStatus(proofRequestId, "success"); + memoryStore.setVerificationStatus(proofRequestId, "success"); } logger.logVerification(sessionId, 'success', authResponse.from); @@ -301,8 +268,7 @@ app.post("/api/callback", async (req, res) => { function validateEnvironment() { const required = [ - 'USE_CASE', 'VERIFIER_DID', 'NULLIFIER_SESSION_ID', - 'HOST_URL', 'REDIS_HOST', 'REDIS_PORT', 'REDIS_USERNAME', 'REDIS_PASSWORD' + 'USE_CASE', 'VERIFIER_DID', 'NULLIFIER_SESSION_ID', 'HOST_URL' ]; const missing = required.filter(key => !process.env[key]); @@ -321,31 +287,17 @@ function validateEnvironment() { throw new Error(`Invalid HOST_URL: ${process.env.HOST_URL}`); } - const redisPort = parseInt(process.env.REDIS_PORT); - if (isNaN(redisPort) || redisPort < 1 || redisPort > 65535) { - throw new Error(`Invalid REDIS_PORT: ${process.env.REDIS_PORT}`); - } } async function startServer() { try { validateEnvironment(); - try { - await redisService.connect(); - logger.info('Redis connected'); - } catch (redisError) { - logger.warn('Redis unavailable at startup, running in degraded mode', { - error: redisError.message - }); - } - const server = app.listen(port, () => { logger.info('Server started', { port, useCase: USE_CASE, - nodeEnv: process.env.NODE_ENV, - redisConnected: redisService.isHealthy() + nodeEnv: process.env.NODE_ENV }); }); @@ -361,8 +313,7 @@ async function startServer() { const shutdown = async (signal) => { logger.info(`${signal} received, shutting down`); - server.close(async () => { - await redisService.disconnect(); + server.close(() => { process.exit(0); }); }; diff --git a/Integration-for-verifiers (Billions Wallet)/src/services/db-service.js b/Integration-for-verifiers (Billions Wallet)/src/services/db-service.js deleted file mode 100644 index d1bc9f36..00000000 --- a/Integration-for-verifiers (Billions Wallet)/src/services/db-service.js +++ /dev/null @@ -1,47 +0,0 @@ -const { Pool } = require('pg'); -const { Mutex } = require('async-mutex'); - -const pool = new Pool({ - connectionString: process.env.DATABASE_URL, - ssl: { rejectUnauthorized: false } -}); - -pool.on('error', (err) => { - console.error('PostgreSQL pool error:', err); -}); - -const nullifierMutex = new Mutex(); - -module.exports = { - async claimNullifier(nullifierHash, sessionId) { - const release = await nullifierMutex.acquire(); - try { - const existing = await pool.query( - 'SELECT 1 FROM verified_nullifiers WHERE hash = $1', - [nullifierHash] - ); - - if (existing.rows.length > 0) { - return { claimed: false }; - } - - await pool.query( - 'INSERT INTO verified_nullifiers (hash, session_id) VALUES ($1, $2)', - [nullifierHash, sessionId] - ); - - return { claimed: true }; - } finally { - release(); - } - }, - - async healthCheck() { - try { - await pool.query('SELECT NOW()'); - return true; - } catch { - return false; - } - } -}; diff --git a/Integration-for-verifiers (Billions Wallet)/src/services/memory-store.js b/Integration-for-verifiers (Billions Wallet)/src/services/memory-store.js new file mode 100644 index 00000000..d2f2ace5 --- /dev/null +++ b/Integration-for-verifiers (Billions Wallet)/src/services/memory-store.js @@ -0,0 +1,22 @@ +const authRequests = new Map(); +const verificationStatuses = new Map(); + +module.exports = { + setAuthRequest(sessionId, authRequest) { + authRequests.set(sessionId, authRequest); + return true; + }, + + getAuthRequest(sessionId) { + return authRequests.get(sessionId) || null; + }, + + setVerificationStatus(requestId, status) { + verificationStatuses.set(requestId, status); + return true; + }, + + getVerificationStatus(requestId) { + return verificationStatuses.get(requestId) || null; + } +}; diff --git a/Integration-for-verifiers (Billions Wallet)/src/services/nullifier-store.js b/Integration-for-verifiers (Billions Wallet)/src/services/nullifier-store.js new file mode 100644 index 00000000..c0ff41fa --- /dev/null +++ b/Integration-for-verifiers (Billions Wallet)/src/services/nullifier-store.js @@ -0,0 +1,19 @@ +const { Mutex } = require('async-mutex'); + +const nullifierMutex = new Mutex(); +const claimedNullifiers = new Set(); + +module.exports = { + async claimNullifier(nullifierHash) { + const release = await nullifierMutex.acquire(); + try { + if (claimedNullifiers.has(nullifierHash)) { + return { claimed: false }; + } + claimedNullifiers.add(nullifierHash); + return { claimed: true }; + } finally { + release(); + } + } +}; diff --git a/Integration-for-verifiers (Billions Wallet)/src/services/redis-service.js b/Integration-for-verifiers (Billions Wallet)/src/services/redis-service.js deleted file mode 100644 index c036cdde..00000000 --- a/Integration-for-verifiers (Billions Wallet)/src/services/redis-service.js +++ /dev/null @@ -1,257 +0,0 @@ -const { createClient } = require('redis'); - -class RedisService { - constructor() { - this.client = null; - this.isConnected = false; - this.reconnectAttempts = 0; - this.maxReconnectAttempts = 10; - this.reconnectDelay = 1000; - this.maxReconnectDelay = 30000; - this.circuitBreakerFailures = 0; - this.circuitBreakerThreshold = 5; - this.circuitBreakerOpen = false; - this.circuitBreakerResetTime = 60000; - this.lastCircuitBreakerOpen = null; - - this.memoryCache = new Map(); - this.maxCacheSize = 10000; - this.cacheCleanupInterval = 5 * 60 * 1000; - - setInterval(() => this.cleanupExpiredEntries(), this.cacheCleanupInterval); - } - - async connect() { - try { - this.client = createClient({ - username: process.env.REDIS_USERNAME, - password: process.env.REDIS_PASSWORD, - socket: { - host: process.env.REDIS_HOST, - port: parseInt(process.env.REDIS_PORT), - } - }); - - this.client.on('error', (err) => { - console.error('Redis Client Error:', err); - this.isConnected = false; - this.handleConnectionFailure(); - }); - - this.client.on('connect', () => { - this.isConnected = true; - this.reconnectAttempts = 0; - this.resetCircuitBreaker(); - }); - - this.client.on('disconnect', () => { - this.isConnected = false; - this.scheduleReconnection(); - }); - - this.client.on('reconnecting', () => { - console.log('Redis reconnecting...'); - }); - - this.client.on('end', () => { - this.isConnected = false; - }); - - await this.client.connect(); - await this.client.ping(); - } catch (error) { - console.error('Failed to connect to Redis:', error); - this.handleConnectionFailure(); - throw error; - } - } - - async disconnect() { - if (this.client && this.isConnected) { - await this.client.quit(); - this.isConnected = false; - } - } - - handleConnectionFailure() { - this.circuitBreakerFailures++; - if (this.circuitBreakerFailures >= this.circuitBreakerThreshold) { - this.openCircuitBreaker(); - } - } - - openCircuitBreaker() { - this.circuitBreakerOpen = true; - this.lastCircuitBreakerOpen = Date.now(); - console.log(`Circuit breaker opened after ${this.circuitBreakerFailures} failures`); - setTimeout(() => this.resetCircuitBreaker(), this.circuitBreakerResetTime); - } - - resetCircuitBreaker() { - this.circuitBreakerOpen = false; - this.circuitBreakerFailures = 0; - this.lastCircuitBreakerOpen = null; - } - - scheduleReconnection() { - if (this.reconnectAttempts >= this.maxReconnectAttempts) { - console.error(`Max reconnection attempts (${this.maxReconnectAttempts}) reached`); - return; - } - - const delay = Math.min( - this.reconnectDelay * Math.pow(2, this.reconnectAttempts), - this.maxReconnectDelay - ); - - this.reconnectAttempts++; - - setTimeout(async () => { - try { - await this.connect(); - } catch (error) { - console.error('Reconnection attempt failed:', error.message); - } - }, delay); - } - - cleanupExpiredEntries() { - const now = Date.now(); - for (const [key, entry] of this.memoryCache.entries()) { - if (entry.expiresAt && entry.expiresAt < now) { - this.memoryCache.delete(key); - } - } - } - - setMemoryCache(key, value, ttl = 3600) { - if (this.memoryCache.size >= this.maxCacheSize) { - const firstKey = this.memoryCache.keys().next().value; - this.memoryCache.delete(firstKey); - } - - this.memoryCache.set(key, { - value, - expiresAt: ttl > 0 ? Date.now() + (ttl * 1000) : null, - createdAt: Date.now() - }); - } - - getMemoryCache(key) { - const entry = this.memoryCache.get(key); - if (!entry) return null; - - if (entry.expiresAt && entry.expiresAt < Date.now()) { - this.memoryCache.delete(key); - return null; - } - - return entry.value; - } - - async executeWithFallback(operation, fallbackKey = null, fallbackValue = null) { - if (this.circuitBreakerOpen || !this.isConnected || !this.client) { - return fallbackKey ? this.getMemoryCache(fallbackKey) : fallbackValue; - } - - try { - const result = await operation(); - this.circuitBreakerFailures = Math.max(0, this.circuitBreakerFailures - 1); - return result; - } catch (error) { - console.error('Redis operation failed, falling back to memory cache:', error.message); - this.handleConnectionFailure(); - return fallbackKey ? this.getMemoryCache(fallbackKey) : fallbackValue; - } - } - - async setAuthRequest(sessionId, authRequest, ttl = 3600) { - const key = `auth_request:${sessionId}`; - this.setMemoryCache(key, authRequest, ttl); - - return await this.executeWithFallback(async () => { - await this.client.setEx(key, ttl, JSON.stringify(authRequest)); - return true; - }, null, true); - } - - async getAuthRequest(sessionId) { - const key = `auth_request:${sessionId}`; - - return await this.executeWithFallback(async () => { - const result = await this.client.get(key); - const authRequest = result ? JSON.parse(result) : null; - if (authRequest) this.setMemoryCache(key, authRequest, 3600); - return authRequest; - }, key, null); - } - - async setVerificationStatus(requestId, status, ttl = 3600) { - const key = `verification_status:${requestId}`; - this.setMemoryCache(key, status, ttl); - - return await this.executeWithFallback(async () => { - await this.client.setEx(key, ttl, status); - return true; - }, null, true); - } - - async getVerificationStatus(requestId) { - const key = `verification_status:${requestId}`; - - return await this.executeWithFallback(async () => { - const result = await this.client.get(key); - if (result) this.setMemoryCache(key, result, 3600); - return result; - }, key, null); - } - - async healthCheck() { - try { - if (!this.client || !this.isConnected) return false; - const pong = await this.client.ping(); - return pong === 'PONG'; - } catch { - return false; - } - } - - isHealthy() { - return this.isConnected && !this.circuitBreakerOpen; - } - - getStatus() { - return { - connected: this.isConnected, - circuitBreakerOpen: this.circuitBreakerOpen, - failures: this.circuitBreakerFailures, - reconnectAttempts: this.reconnectAttempts, - lastFailure: this.lastCircuitBreakerOpen, - memoryCache: { - size: this.memoryCache.size, - maxSize: this.maxCacheSize, - utilizationPercent: Math.round((this.memoryCache.size / this.maxCacheSize) * 100) - } - }; - } - - getCacheStats() { - const now = Date.now(); - let expiredCount = 0; - let totalAge = 0; - - for (const [, entry] of this.memoryCache.entries()) { - if (entry.expiresAt && entry.expiresAt < now) expiredCount++; - totalAge += now - entry.createdAt; - } - - return { - totalEntries: this.memoryCache.size, - expiredEntries: expiredCount, - averageAgeMs: this.memoryCache.size > 0 ? Math.round(totalAge / this.memoryCache.size) : 0, - utilizationPercent: Math.round((this.memoryCache.size / this.maxCacheSize) * 100) - }; - } -} - -module.exports = new RedisService();