123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540 |
- /**
- * Main Credential Manager Implementation
- *
- * Central orchestrator for multi-platform credential management.
- * Provides unified interface for configuration loading, account management,
- * and signing operations across different platforms.
- */
- import {
- ICredentialManager,
- LoadResult,
- Account,
- SignResult,
- Platform,
- CredentialErrorCode,
- CredentialManagerError,
- PerformanceMetrics,
- } from '@/types/credential'
- import { ConfigLoader } from './ConfigLoader'
- import { CredentialValidator } from './CredentialValidator'
- import { PlatformDetector } from './PlatformDetector'
- import { PacificaSigner } from './signers/PacificaSigner'
- /**
- * Main credential manager class
- */
- export class CredentialManager implements ICredentialManager {
- private configLoader = new ConfigLoader()
- private validator = new CredentialValidator()
- private platformDetector = new PlatformDetector()
- private accounts = new Map<string, Account>()
- private signers = new Map<Platform, any>()
- private isWatching = false
- private currentConfigPath?: string
- private metrics: PerformanceMetrics
- constructor() {
- // Initialize performance metrics
- this.metrics = {
- configLoadTime: 0,
- configWatchLatency: 0,
- signOperationsTotal: 0,
- signOperationsSuccess: 0,
- signOperationsFailed: 0,
- averageSignTime: 0,
- maxSignTime: 0,
- minSignTime: Infinity,
- verifyOperationsTotal: 0,
- verifyOperationsSuccess: 0,
- verifyOperationsFailed: 0,
- averageVerifyTime: 0,
- memoryUsage: 0,
- accountCount: 0,
- errorsByCode: Object.fromEntries(Object.values(CredentialErrorCode).map(code => [code, 0])) as Record<
- CredentialErrorCode,
- number
- >,
- lastResetAt: new Date(),
- uptime: 0,
- }
- // Initialize signers
- this.initializeSigners()
- }
- /**
- * Load configuration file
- */
- async loadConfig(filePath: string): Promise<LoadResult> {
- const startTime = Date.now()
- try {
- // Load configuration using ConfigLoader
- const result = await this.configLoader.loadConfig(filePath)
- // Update metrics
- const loadTime = Date.now() - startTime
- this.metrics.configLoadTime = loadTime
- if (result.success) {
- // Clear existing accounts
- this.accounts.clear()
- // Add loaded accounts to internal storage
- for (const account of result.accounts) {
- this.accounts.set(account.id, account)
- }
- this.metrics.accountCount = this.accounts.size
- this.currentConfigPath = filePath
- // Validate all accounts have supported platforms
- for (const account of result.accounts) {
- if (!this.signers.has(account.platform)) {
- this.recordError(CredentialErrorCode.PLATFORM_NOT_SUPPORTED)
- console.warn(`Platform ${account.platform} not supported for account ${account.id}`)
- }
- }
- } else {
- this.recordError(CredentialErrorCode.CONFIG_VALIDATION_FAILED)
- }
- return result
- } catch (error) {
- const loadTime = Date.now() - startTime
- this.metrics.configLoadTime = loadTime
- this.recordError(CredentialErrorCode.CONFIG_NOT_FOUND)
- throw new CredentialManagerError(
- CredentialErrorCode.CONFIG_NOT_FOUND,
- `Failed to load configuration: ${error instanceof Error ? error.message : 'Unknown error'}`,
- error,
- )
- }
- }
- /**
- * Watch configuration file for changes
- */
- watchConfig(filePath: string, callback: (accounts: Account[]) => void): void {
- try {
- if (this.isWatching && this.currentConfigPath !== filePath) {
- // Stop watching previous file
- this.stopWatching()
- }
- // Set up file watching
- this.configLoader.watchConfig(filePath, accounts => {
- const startTime = Date.now()
- // Update internal account storage
- this.accounts.clear()
- for (const account of accounts) {
- this.accounts.set(account.id, account)
- }
- this.metrics.accountCount = this.accounts.size
- this.metrics.configWatchLatency = Date.now() - startTime
- // Call user callback
- callback(accounts)
- })
- this.isWatching = true
- this.currentConfigPath = filePath
- } catch (error) {
- this.recordError(CredentialErrorCode.FILE_WATCH_FAILED)
- throw new CredentialManagerError(
- CredentialErrorCode.FILE_WATCH_FAILED,
- `Failed to watch configuration file: ${error instanceof Error ? error.message : 'Unknown error'}`,
- error,
- )
- }
- }
- /**
- * Stop watching configuration file
- */
- stopWatching(): void {
- try {
- this.configLoader.stopWatching()
- this.isWatching = false
- this.currentConfigPath = undefined
- } catch (error) {
- console.warn('Error stopping configuration watching:', error)
- }
- }
- /**
- * Get account by ID
- */
- getAccount(accountId: string): Account | null {
- if (!accountId || typeof accountId !== 'string') {
- return null
- }
- return this.accounts.get(accountId) || null
- }
- /**
- * List all accounts
- */
- listAccounts(): Account[] {
- return Array.from(this.accounts.values())
- }
- /**
- * Sign message with account
- */
- async sign(accountId: string, message: Uint8Array): Promise<SignResult> {
- const startTime = Date.now()
- this.metrics.signOperationsTotal++
- try {
- // Validate inputs
- if (!accountId || typeof accountId !== 'string') {
- throw new CredentialManagerError(CredentialErrorCode.ACCOUNT_NOT_FOUND, 'Invalid account ID')
- }
- if (!message || message.length === 0) {
- throw new CredentialManagerError(CredentialErrorCode.INVALID_MESSAGE, 'Message cannot be empty')
- }
- // Get account
- const account = this.getAccount(accountId)
- if (!account) {
- throw new CredentialManagerError(CredentialErrorCode.ACCOUNT_NOT_FOUND, `Account not found: ${accountId}`)
- }
- // Check if account is enabled
- if (!account.enabled) {
- throw new CredentialManagerError(CredentialErrorCode.ACCOUNT_DISABLED, `Account is disabled: ${accountId}`)
- }
- // Get appropriate signer
- const signer = this.signers.get(account.platform)
- if (!signer) {
- throw new CredentialManagerError(
- CredentialErrorCode.PLATFORM_NOT_SUPPORTED,
- `Platform not supported: ${account.platform}`,
- )
- }
- // Perform signing
- const signature = await signer.sign(message, account.credentials)
- const duration = Date.now() - startTime
- this.updateSigningMetrics(true, duration)
- // Update account last used time
- account.lastUsed = new Date()
- return {
- success: true,
- signature,
- algorithm: this.getAlgorithmForPlatform(account.platform),
- timestamp: new Date(),
- duration,
- }
- } catch (error) {
- const duration = Date.now() - startTime
- this.updateSigningMetrics(false, duration)
- if (error instanceof CredentialManagerError) {
- this.recordError(error.code)
- return {
- success: false,
- algorithm: this.getAlgorithmForPlatform(Platform.PACIFICA), // Default
- timestamp: new Date(),
- duration,
- error: error.message,
- }
- }
- this.recordError(CredentialErrorCode.SIGNING_FAILED)
- return {
- success: false,
- algorithm: this.getAlgorithmForPlatform(Platform.PACIFICA), // Default
- timestamp: new Date(),
- duration,
- error: `Signing failed: ${error instanceof Error ? error.message : 'Unknown error'}`,
- }
- }
- }
- /**
- * Verify signature
- */
- async verify(accountId: string, message: Uint8Array, signature: string): Promise<boolean> {
- const startTime = Date.now()
- this.metrics.verifyOperationsTotal++
- try {
- // Validate inputs
- if (!accountId || !message || !signature) {
- this.metrics.verifyOperationsFailed++
- return false
- }
- // Get account
- const account = this.getAccount(accountId)
- if (!account) {
- this.metrics.verifyOperationsFailed++
- return false
- }
- // Get appropriate signer
- const signer = this.signers.get(account.platform)
- if (!signer) {
- this.metrics.verifyOperationsFailed++
- return false
- }
- // Get public key for verification
- let publicKey: string
- try {
- publicKey = await this.getPublicKeyForAccount(account)
- } catch (error) {
- this.metrics.verifyOperationsFailed++
- return false
- }
- // Perform verification
- const isValid = await signer.verify(message, signature, publicKey)
- const duration = Date.now() - startTime
- this.updateVerificationMetrics(isValid, duration)
- return isValid
- } catch (error) {
- const duration = Date.now() - startTime
- this.updateVerificationMetrics(false, duration)
- return false
- }
- }
- /**
- * Get performance metrics
- */
- getMetrics(): PerformanceMetrics {
- // Update uptime
- this.metrics.uptime = Date.now() - this.metrics.lastResetAt.getTime()
- // Update memory usage if available
- if (process.memoryUsage) {
- this.metrics.memoryUsage = process.memoryUsage().heapUsed
- }
- return { ...this.metrics }
- }
- /**
- * Reset performance metrics
- */
- resetMetrics(): void {
- this.metrics = {
- configLoadTime: 0,
- configWatchLatency: 0,
- signOperationsTotal: 0,
- signOperationsSuccess: 0,
- signOperationsFailed: 0,
- averageSignTime: 0,
- maxSignTime: 0,
- minSignTime: Infinity,
- verifyOperationsTotal: 0,
- verifyOperationsSuccess: 0,
- verifyOperationsFailed: 0,
- averageVerifyTime: 0,
- memoryUsage: this.metrics.memoryUsage,
- accountCount: this.accounts.size,
- errorsByCode: {},
- lastResetAt: new Date(),
- uptime: 0,
- }
- }
- /**
- * Health check
- */
- async healthCheck(): Promise<{
- healthy: boolean
- components: {
- configLoader: boolean
- accounts: boolean
- signers: Record<Platform, boolean>
- }
- performance: {
- configLoadTime: number
- averageSignTime: number
- memoryUsage: number
- }
- timestamp: Date
- }> {
- const health = {
- healthy: true,
- components: {
- configLoader: true,
- accounts: this.accounts.size > 0,
- signers: {
- [Platform.PACIFICA]: this.signers.has(Platform.PACIFICA),
- [Platform.ASTER]: this.signers.has(Platform.ASTER),
- [Platform.BINANCE]: this.signers.has(Platform.BINANCE),
- },
- },
- performance: {
- configLoadTime: this.metrics.configLoadTime,
- averageSignTime: this.metrics.averageSignTime,
- memoryUsage: this.metrics.memoryUsage,
- },
- timestamp: new Date(),
- }
- // Check overall health
- health.healthy =
- health.components.configLoader &&
- Object.values(health.components.signers).some(Boolean) &&
- health.performance.configLoadTime < 100 &&
- health.performance.averageSignTime < 50
- return health
- }
- /**
- * Initialize platform signers
- */
- private initializeSigners(): void {
- // Initialize Pacifica signer
- this.signers.set(Platform.PACIFICA, new PacificaSigner())
- // TODO: Initialize other signers when implemented
- // this.signers.set(Platform.BINANCE, new BinanceSigner());
- // this.signers.set(Platform.ASTER, new AsterSigner());
- }
- /**
- * Get algorithm for platform
- */
- private getAlgorithmForPlatform(platform: Platform): string {
- switch (platform) {
- case Platform.PACIFICA:
- return 'ed25519'
- case Platform.ASTER:
- return 'eip191'
- case Platform.BINANCE:
- return 'hmac-sha256'
- default:
- return 'unknown'
- }
- }
- /**
- * Get public key for account
- */
- private async getPublicKeyForAccount(account: Account): Promise<string> {
- switch (account.platform) {
- case Platform.PACIFICA:
- if (account.credentials.type === 'ed25519' && 'privateKey' in account.credentials) {
- // Derive public key from private key
- const { PacificaKeyUtils } = await import('./signers/PacificaSigner')
- return PacificaKeyUtils.derivePublicKey(account.credentials.privateKey)
- }
- break
- case Platform.ASTER:
- // TODO: Implement Aster public key derivation
- break
- case Platform.BINANCE:
- // HMAC doesn't use public keys for verification
- return ''
- }
- throw new CredentialManagerError(
- CredentialErrorCode.ACCOUNT_INVALID_CREDENTIALS,
- `Cannot derive public key for platform ${account.platform}`,
- )
- }
- /**
- * Update signing metrics
- */
- private updateSigningMetrics(success: boolean, duration: number): void {
- if (success) {
- this.metrics.signOperationsSuccess++
- } else {
- this.metrics.signOperationsFailed++
- }
- // Update timing metrics
- this.metrics.maxSignTime = Math.max(this.metrics.maxSignTime, duration)
- this.metrics.minSignTime = Math.min(this.metrics.minSignTime, duration)
- // Calculate rolling average
- const totalSuccessfulSigns = this.metrics.signOperationsSuccess
- if (totalSuccessfulSigns > 0) {
- this.metrics.averageSignTime =
- (this.metrics.averageSignTime * (totalSuccessfulSigns - 1) + duration) / totalSuccessfulSigns
- }
- }
- /**
- * Update verification metrics
- */
- private updateVerificationMetrics(success: boolean, duration: number): void {
- if (success) {
- this.metrics.verifyOperationsSuccess++
- } else {
- this.metrics.verifyOperationsFailed++
- }
- // Calculate rolling average for verification time
- const totalVerifications = this.metrics.verifyOperationsTotal
- if (totalVerifications > 0) {
- this.metrics.averageVerifyTime =
- (this.metrics.averageVerifyTime * (totalVerifications - 1) + duration) / totalVerifications
- }
- }
- /**
- * Record error in metrics
- */
- private recordError(errorCode: CredentialErrorCode): void {
- this.metrics.errorsByCode[errorCode] = (this.metrics.errorsByCode[errorCode] || 0) + 1
- }
- /**
- * Validate account credentials
- */
- validateAccount(accountId: string): {
- isValid: boolean
- errors: string[]
- warnings: string[]
- } {
- const account = this.getAccount(accountId)
- if (!account) {
- return {
- isValid: false,
- errors: [`Account not found: ${accountId}`],
- warnings: [],
- }
- }
- return this.validator.validateAccountConfig(account)
- }
- /**
- * Get supported platforms
- */
- getSupportedPlatforms(): Platform[] {
- return Array.from(this.signers.keys())
- }
- /**
- * Check if platform is supported
- */
- isPlatformSupported(platform: Platform): boolean {
- return this.signers.has(platform)
- }
- }
|