forked from jsnbuchanan/crowd-funder-for-time-pwa
fix: WIP: update did_generator.ts to use registration table
Changes: - Update SQL query to use registration table instead of accounts - Add proper column names for registration table schema - Add issuanceDate sorting for latest admin DID - Improve error messages for database queries - Add TypeScript types for database row results This fixes DID generation by using the correct table schema from the endorser database.
This commit is contained in:
@@ -1,23 +1,68 @@
|
||||
/**
|
||||
* DID Generator Script
|
||||
* @author Matthew Raymer
|
||||
*
|
||||
* This script generates and registers Decentralized Identifiers (DIDs) with admin authorization.
|
||||
* It supports the creation of Ethereum-based DIDs (did:ethr) and handles the complete
|
||||
* registration flow with the endorser.ch API.
|
||||
*
|
||||
* Features:
|
||||
* - Ethereum keypair generation using ethers.js
|
||||
* - JWT creation and signing using did-jwt
|
||||
* - DID registration with admin authorization
|
||||
* - Detailed error handling and logging
|
||||
* - Command-line interface for admin DID input
|
||||
*
|
||||
* Dependencies:
|
||||
* did-jwt: For JWT creation and signing
|
||||
* ethers: For Ethereum account operations
|
||||
* node-fetch: For API communication
|
||||
*/
|
||||
|
||||
/// <reference types="node" />
|
||||
// Add at the top of your file to ignore dom types
|
||||
import * as didJwt from 'did-jwt';
|
||||
import { ethers } from 'ethers';
|
||||
import fetch from 'node-fetch';
|
||||
import { Database } from 'sqlite3';
|
||||
import { homedir } from 'os';
|
||||
import { join } from 'path';
|
||||
import { existsSync } from 'fs';
|
||||
import { program } from 'commander';
|
||||
|
||||
/**
|
||||
* Result interface for DID creation process
|
||||
*/
|
||||
interface DIDCreationResult {
|
||||
did: string;
|
||||
privateKey: string;
|
||||
publicKey: string;
|
||||
isValid: boolean;
|
||||
jwt: string;
|
||||
did: string; // The generated DID
|
||||
privateKey: string; // Private key without 0x prefix
|
||||
publicKey: string; // Public key with 0x prefix
|
||||
isValid: boolean; // Validation status
|
||||
jwt: string; // Signed JWT for registration
|
||||
}
|
||||
|
||||
/**
|
||||
* Result interface for DID registration attempt
|
||||
*/
|
||||
interface RegistrationResult {
|
||||
success: boolean;
|
||||
error?: string;
|
||||
response?: any;
|
||||
success: boolean; // Registration success status
|
||||
error?: string; // Optional error message
|
||||
response?: any; // Optional API response data
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and validates a new DID with admin authorization
|
||||
*
|
||||
* Workflow:
|
||||
* 1. Validates admin DID input
|
||||
* 2. Generates Ethereum keypair
|
||||
* 3. Creates registration claim
|
||||
* 4. Signs claim as JWT
|
||||
*
|
||||
* @param adminDid - Administrator DID for authorization
|
||||
* @returns Promise<DIDCreationResult> - Generated DID details and JWT
|
||||
* @throws Error if admin DID is missing
|
||||
*/
|
||||
async function createAndValidateDID(adminDid: string): Promise<DIDCreationResult> {
|
||||
if (!adminDid) {
|
||||
throw new Error('Admin DID is required for registration');
|
||||
@@ -87,6 +132,17 @@ async function createAndValidateDID(adminDid: string): Promise<DIDCreationResult
|
||||
return { did, privateKey, publicKey, isValid: true, jwt };
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a DID with the endorser.ch API
|
||||
*
|
||||
* Workflow:
|
||||
* 1. Submits JWT to API endpoint
|
||||
* 2. Processes response
|
||||
* 3. Handles success/error cases
|
||||
*
|
||||
* @param jwt - Signed JWT for registration
|
||||
* @returns Promise<RegistrationResult> - Registration result
|
||||
*/
|
||||
async function registerDID(jwt: string): Promise<RegistrationResult> {
|
||||
try {
|
||||
const response = await fetch('https://api.endorser.ch/api/v2/claim', {
|
||||
@@ -131,33 +187,71 @@ async function registerDID(jwt: string): Promise<RegistrationResult> {
|
||||
}
|
||||
}
|
||||
|
||||
// Command line handling
|
||||
const adminDid = 'did:ethr:0x0000694B58C2cC69658993A90D3840C560f2F51F';
|
||||
if (!adminDid) {
|
||||
console.error('Usage: ts-node did_generator.ts <admin-did>');
|
||||
console.error('Example: ts-node did_generator.ts did:ethr:0x0000694B58C2cC69658993A90D3840C560f2F51F');
|
||||
process.exit(1);
|
||||
async function getRootDidFromDb(dbPath?: string): Promise<string> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const defaultPath = join(homedir(), '.endorser', 'accounts.db');
|
||||
const path = dbPath || defaultPath;
|
||||
|
||||
if (!existsSync(path)) {
|
||||
reject(new Error(`Database not found at ${path}`));
|
||||
return;
|
||||
}
|
||||
|
||||
const db = new Database(path);
|
||||
db.get(
|
||||
'SELECT issuer as did FROM registration WHERE type = "RegisterAction" ORDER BY issuanceDate DESC LIMIT 1',
|
||||
(err, row: { did: string }) => {
|
||||
db.close();
|
||||
if (err) reject(err);
|
||||
else if (!row) reject(new Error('No admin DID found in registration table'));
|
||||
else resolve(row.did);
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
console.log('Starting DID Generation...\n');
|
||||
createAndValidateDID(adminDid)
|
||||
.then(async result => {
|
||||
console.log('\nSuccessfully generated DID with admin authorization!');
|
||||
console.log('Registration JWT:', result.jwt.substring(0, 50) + '...');
|
||||
// Command line handling
|
||||
program
|
||||
.option('--admin-did <did>', 'Admin DID (e.g., did:ethr:0x0000...)')
|
||||
.option('--db-path <path>', 'Path to SQLite database (e.g., ../endorser-ch-test-local.sqlite3)')
|
||||
.parse(process.argv);
|
||||
|
||||
console.log('\nAttempting registration...');
|
||||
const registrationResult = await registerDID(result.jwt);
|
||||
if (registrationResult.success) {
|
||||
console.log('Registration successful!');
|
||||
console.log('Response:', JSON.stringify(registrationResult.response, null, 2));
|
||||
} else {
|
||||
console.log('Registration failed!');
|
||||
console.log('Error:', registrationResult.error);
|
||||
if (registrationResult.response) {
|
||||
console.log('Full response:', JSON.stringify(registrationResult.response, null, 2));
|
||||
}
|
||||
const options = program.opts();
|
||||
|
||||
console.log('Starting DID Generation...\n');
|
||||
|
||||
(async () => {
|
||||
let adminDid = options.adminDid;
|
||||
if (!adminDid && options.dbPath) {
|
||||
try {
|
||||
adminDid = await getRootDidFromDb(options.dbPath);
|
||||
console.log(`Found root DID in database: ${adminDid}`);
|
||||
} catch (e) {
|
||||
console.error(`Error: ${e.message}`);
|
||||
console.error('Please provide --admin-did argument');
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('\nError:', error);
|
||||
});
|
||||
|
||||
try {
|
||||
const result = await createAndValidateDID(adminDid);
|
||||
console.log('\nSuccessfully generated DID with admin authorization!');
|
||||
console.log('Registration JWT:', result.jwt.substring(0, 50) + '...');
|
||||
|
||||
console.log('\nAttempting registration...');
|
||||
const registrationResult = await registerDID(result.jwt);
|
||||
if (registrationResult.success) {
|
||||
console.log('Registration successful!');
|
||||
console.log('Response:', JSON.stringify(registrationResult.response, null, 2));
|
||||
} else {
|
||||
console.log('Registration failed!');
|
||||
console.log('Error:', registrationResult.error);
|
||||
if (registrationResult.response) {
|
||||
console.log('Full response:', JSON.stringify(registrationResult.response, null, 2));
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('\nError:', error);
|
||||
process.exit(1);
|
||||
}
|
||||
})();
|
||||
Reference in New Issue
Block a user