anon-identity
Version:
Decentralized identity framework with DIDs, Verifiable Credentials, and privacy-preserving selective disclosure
238 lines • 9.97 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.BatchOperations = void 0;
const verification_errors_1 = require("./verification-errors");
class BatchOperations {
constructor(options = {}) {
this.options = options;
this.defaultOptions = {
maxConcurrency: 10,
timeout: 30000, // 30 seconds
continueOnError: true
};
this.options = { ...this.defaultOptions, ...options };
}
/**
* Verify multiple presentations in parallel with controlled concurrency
*/
async batchVerifyPresentations(presentations, verifyFunction) {
const results = [];
const chunks = this.chunkArray(presentations, this.options.maxConcurrency);
for (const chunk of chunks) {
const chunkPromises = chunk.map(async (presentation, index) => {
const startTime = Date.now();
const presentationIndex = presentations.indexOf(presentation);
try {
// Apply timeout to individual verification
const result = await this.withTimeout(verifyFunction(presentation), this.options.timeout);
const processingTime = Date.now() - startTime;
return {
presentationIndex,
presentationId: this.extractPresentationId(presentation),
result,
processingTime
};
}
catch (error) {
const processingTime = Date.now() - startTime;
const verificationError = error instanceof Error
? new verification_errors_1.VerificationError(verification_errors_1.VerificationErrorCode.NETWORK_ERROR, `Batch verification failed: ${error.message}`)
: new verification_errors_1.VerificationError(verification_errors_1.VerificationErrorCode.NETWORK_ERROR, 'Unknown batch verification error');
return {
presentationIndex,
presentationId: this.extractPresentationId(presentation),
result: {
valid: false,
errors: [verificationError],
timestamp: new Date()
},
processingTime
};
}
});
try {
const chunkResults = await Promise.all(chunkPromises);
results.push(...chunkResults);
}
catch (error) {
if (!this.options.continueOnError) {
throw error;
}
// Individual errors are already handled in the map function
}
}
return results.sort((a, b) => a.presentationIndex - b.presentationIndex);
}
/**
* Check multiple credential revocations in parallel
*/
async batchCheckRevocations(credentialIds, checkFunction) {
const results = new Map();
const chunks = this.chunkArray(credentialIds, this.options.maxConcurrency);
for (const chunk of chunks) {
const chunkPromises = chunk.map(async (credentialId) => {
const startTime = Date.now();
try {
const isRevoked = await this.withTimeout(checkFunction(credentialId), this.options.timeout);
const processingTime = Date.now() - startTime;
return {
credentialId,
result: {
credentialId,
isRevoked,
processingTime
}
};
}
catch (error) {
const processingTime = Date.now() - startTime;
const verificationError = error instanceof Error
? new verification_errors_1.VerificationError(verification_errors_1.VerificationErrorCode.NETWORK_ERROR, `Revocation check failed: ${error.message}`)
: new verification_errors_1.VerificationError(verification_errors_1.VerificationErrorCode.NETWORK_ERROR, 'Unknown revocation check error');
return {
credentialId,
result: {
credentialId,
isRevoked: false, // Default to not revoked on error
error: verificationError,
processingTime
}
};
}
});
try {
const chunkResults = await Promise.all(chunkPromises);
chunkResults.forEach(({ credentialId, result }) => {
results.set(credentialId, result);
});
}
catch (error) {
if (!this.options.continueOnError) {
throw error;
}
}
}
return results;
}
/**
* Batch verify presentations with revocation checks
*/
async batchVerifyWithRevocationCheck(presentations, verifyFunction, checkRevocationFunction) {
// First, verify all presentations
const verificationResults = await this.batchVerifyPresentations(presentations, verifyFunction);
// Extract all credential IDs from valid presentations
const credentialIds = new Set();
verificationResults.forEach(result => {
if (result.result.valid && result.result.credentials) {
result.result.credentials.forEach(cred => credentialIds.add(cred.id));
}
});
// Batch check revocations
const revocationResults = await this.batchCheckRevocations(Array.from(credentialIds), checkRevocationFunction);
// Update verification results with revocation status
return verificationResults.map(result => {
if (!result.result.valid || !result.result.credentials) {
return result;
}
const revokedCredentials = result.result.credentials.filter(cred => {
const revocationResult = revocationResults.get(cred.id);
return revocationResult?.isRevoked || false;
});
if (revokedCredentials.length > 0) {
const revokedErrors = revokedCredentials.map(cred => verification_errors_1.VerificationError.revokedCredential(cred.id, cred.issuer));
return {
...result,
result: {
...result.result,
valid: false,
errors: [...(result.result.errors || []), ...revokedErrors]
}
};
}
return result;
});
}
/**
* Get batch operation statistics
*/
generateBatchStatistics(results) {
const total = results.length;
const valid = results.filter(r => r.result.valid).length;
const invalid = total - valid;
const processingTimes = results.map(r => r.processingTime);
const averageProcessingTime = processingTimes.reduce((a, b) => a + b, 0) / total;
const maxProcessingTime = Math.max(...processingTimes);
const minProcessingTime = Math.min(...processingTimes);
const errorDistribution = {};
results.forEach(result => {
if (result.result.errors) {
result.result.errors.forEach(error => {
const code = error.code || 'UNKNOWN';
errorDistribution[code] = (errorDistribution[code] || 0) + 1;
});
}
});
return {
total,
valid,
invalid,
averageProcessingTime,
maxProcessingTime,
minProcessingTime,
errorDistribution
};
}
/**
* Filter results by criteria
*/
filterResults(results, criteria) {
return results.filter(result => {
if (criteria.validOnly && !result.result.valid) {
return false;
}
if (criteria.maxProcessingTime && result.processingTime > criteria.maxProcessingTime) {
return false;
}
if (criteria.minProcessingTime && result.processingTime < criteria.minProcessingTime) {
return false;
}
if (criteria.excludeErrorCodes && result.result.errors) {
const hasExcludedError = result.result.errors.some(error => criteria.excludeErrorCodes.includes(error.code));
if (hasExcludedError) {
return false;
}
}
return true;
});
}
/**
* Utility: Add timeout to a promise
*/
async withTimeout(promise, timeoutMs) {
const timeoutPromise = new Promise((_, reject) => {
setTimeout(() => reject(new Error(`Operation timed out after ${timeoutMs}ms`)), timeoutMs);
});
return Promise.race([promise, timeoutPromise]);
}
/**
* Utility: Split array into chunks
*/
chunkArray(array, chunkSize) {
const chunks = [];
for (let i = 0; i < array.length; i += chunkSize) {
chunks.push(array.slice(i, i + chunkSize));
}
return chunks;
}
/**
* Extract presentation ID from presentation object
*/
extractPresentationId(presentation) {
// Look for id in various possible locations
return presentation.id ||
presentation.proof?.id ||
undefined;
}
}
exports.BatchOperations = BatchOperations;
//# sourceMappingURL=batch-operations.js.map