anon-identity
Version:
Decentralized identity framework with DIDs, Verifiable Credentials, and privacy-preserving selective disclosure
216 lines • 8.46 kB
JavaScript
;
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