UNPKG

anon-identity

Version:

Decentralized identity framework with DIDs, Verifiable Credentials, and privacy-preserving selective disclosure

216 lines 8.46 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.RevocationMerkleTree = exports.BatchOperationsManager = void 0; const ethers_1 = require("ethers"); class BatchOperationsManager { constructor(batchSize = 10, flushIntervalMs = 5000) { this.pendingOperations = []; this.batchSize = batchSize; this.flushIntervalMs = flushIntervalMs; } addOperation(operation) { this.pendingOperations.push(operation); // Auto-flush if batch size reached if (this.pendingOperations.length >= this.batchSize) { this.flush(); } else { // Set timeout for auto-flush this.resetFlushTimeout(); } } resetFlushTimeout() { if (this.flushTimeout) { clearTimeout(this.flushTimeout); } this.flushTimeout = setTimeout(() => this.flush(), this.flushIntervalMs); } async flush() { if (this.flushTimeout) { clearTimeout(this.flushTimeout); this.flushTimeout = undefined; } if (this.pendingOperations.length === 0) { return; } const operations = [...this.pendingOperations]; this.pendingOperations = []; // Group operations by type for efficient batching const grouped = this.groupOperationsByType(operations); // Execute each group in parallel const promises = []; for (const [type, ops] of Object.entries(grouped)) { switch (type) { case 'registerDID': promises.push(this.batchRegisterDIDs(ops)); break; case 'updateDID': promises.push(this.batchUpdateDIDs(ops)); break; case 'publishRevocation': promises.push(this.batchPublishRevocations(ops)); break; case 'registerSchema': promises.push(this.batchRegisterSchemas(ops)); break; case 'revokeCredentials': promises.push(this.batchRevokeCredentials(ops)); break; } } await Promise.all(promises); } groupOperationsByType(operations) { return operations.reduce((acc, op) => { if (!acc[op.type]) { acc[op.type] = []; } acc[op.type].push(op); return acc; }, {}); } // Batch operation implementations async batchRegisterDIDs(operations) { // In a real implementation, this would call a batch register contract method // For now, we'll simulate the batch processing console.log(`Batch registering ${operations.length} DIDs`); } async batchUpdateDIDs(operations) { console.log(`Batch updating ${operations.length} DIDs`); } async batchPublishRevocations(operations) { // Combine all revocations by issuer for efficiency const byIssuer = new Map(); for (const op of operations) { const { issuerDID, credentialHashes } = op.data; if (!byIssuer.has(issuerDID)) { byIssuer.set(issuerDID, []); } byIssuer.get(issuerDID).push(...credentialHashes); } console.log(`Batch publishing revocations for ${byIssuer.size} issuers`); } async batchRegisterSchemas(operations) { console.log(`Batch registering ${operations.length} schemas`); } async batchRevokeCredentials(operations) { // Group by issuer for efficient batch revocation const byIssuer = new Map(); for (const op of operations) { const { issuerDID, credentialHash } = op.data; if (!byIssuer.has(issuerDID)) { byIssuer.set(issuerDID, new Set()); } byIssuer.get(issuerDID).add(credentialHash); } console.log(`Batch revoking credentials for ${byIssuer.size} issuers`); } // Gas estimation helpers static estimateBatchGas(operations) { // Base gas for batch transaction let gasEstimate = 21000n; // Add estimated gas per operation type for (const op of operations) { switch (op.type) { case 'registerDID': gasEstimate += 80000n; // Estimated gas for DID registration break; case 'updateDID': gasEstimate += 50000n; // Estimated gas for DID update break; case 'publishRevocation': gasEstimate += 60000n; // Estimated gas for revocation break; case 'registerSchema': gasEstimate += 100000n; // Estimated gas for schema registration break; case 'revokeCredentials': gasEstimate += 40000n; // Estimated gas per credential revocation break; } } // Add 20% buffer return (gasEstimate * 120n) / 100n; } static calculateBatchSavings(operations) { const individualGas = BigInt(operations.length) * 21000n + this.estimateBatchGas(operations); const batchGas = this.estimateBatchGas(operations); const savings = individualGas - batchGas; const savingsPercent = Number((savings * 100n) / individualGas); return { individualGas, batchGas, savings, savingsPercent, }; } } exports.BatchOperationsManager = BatchOperationsManager; // Merkle tree implementation for efficient revocation proofs class RevocationMerkleTree { constructor(revokedCredentialHashes) { this.leaves = revokedCredentialHashes.sort(); this.layers = this.buildTree(); } buildTree() { const layers = [this.leaves]; while (layers[layers.length - 1].length > 1) { const currentLayer = layers[layers.length - 1]; const nextLayer = []; for (let i = 0; i < currentLayer.length; i += 2) { if (i + 1 < currentLayer.length) { const combined = ethers_1.ethers.concat([currentLayer[i], currentLayer[i + 1]]); nextLayer.push(ethers_1.ethers.keccak256(combined)); } else { nextLayer.push(currentLayer[i]); } } layers.push(nextLayer); } return layers; } getRoot() { return this.layers.length > 0 ? this.layers[this.layers.length - 1][0] : ethers_1.ethers.ZeroHash; } getProof(credentialHash) { const index = this.leaves.indexOf(credentialHash); if (index === -1) { return []; } const proof = []; let currentIndex = index; for (let i = 0; i < this.layers.length - 1; i++) { const currentLayer = this.layers[i]; const isRightNode = currentIndex % 2 === 1; const siblingIndex = isRightNode ? currentIndex - 1 : currentIndex + 1; if (siblingIndex < currentLayer.length) { proof.push(currentLayer[siblingIndex]); } currentIndex = Math.floor(currentIndex / 2); } return proof; } verify(credentialHash, proof, root) { let computedHash = credentialHash; let index = this.leaves.indexOf(credentialHash); for (const proofElement of proof) { const isRightNode = index % 2 === 1; if (isRightNode) { computedHash = ethers_1.ethers.keccak256(ethers_1.ethers.concat([proofElement, computedHash])); } else { computedHash = ethers_1.ethers.keccak256(ethers_1.ethers.concat([computedHash, proofElement])); } index = Math.floor(index / 2); } return computedHash === root; } // Static method to create a merkle tree from a revocation list static fromRevocationList(revocationList) { const hashes = revocationList.revokedCredentialIds.map(id => ethers_1.ethers.keccak256(ethers_1.ethers.toUtf8Bytes(id))); return new RevocationMerkleTree(hashes); } } exports.RevocationMerkleTree = RevocationMerkleTree; //# sourceMappingURL=blockchain-batch-operations.js.map