/** * Backup Client Service * * mpc-service 调用 backup-service 存储/获取 delegate share * backup-service 负责安全存储 MPC 分片备份 */ import { Injectable, Logger } from '@nestjs/common'; import { HttpService } from '@nestjs/axios'; import { ConfigService } from '@nestjs/config'; import { firstValueFrom } from 'rxjs'; import * as jwt from 'jsonwebtoken'; export interface StoreBackupShareParams { userId: string; accountSequence: number; username: string; publicKey: string; partyId: string; partyIndex: number; encryptedShare: string; } export interface RetrieveBackupShareParams { userId: string; publicKey: string; } export interface BackupShareResult { encryptedShareData: string; partyIndex: number; publicKey: string; } @Injectable() export class BackupClientService { private readonly logger = new Logger(BackupClientService.name); private readonly backupServiceUrl: string; private readonly serviceJwtSecret: string; private readonly enabled: boolean; constructor( private readonly httpService: HttpService, private readonly configService: ConfigService, ) { this.backupServiceUrl = this.configService.get('BACKUP_SERVICE_URL', 'http://localhost:3002'); this.serviceJwtSecret = this.configService.get('SERVICE_JWT_SECRET', ''); this.enabled = this.configService.get('BACKUP_SERVICE_ENABLED', 'true') === 'true'; this.logger.log(`[INIT] BackupClientService initialized`); this.logger.log(`[INIT] URL: ${this.backupServiceUrl}`); this.logger.log(`[INIT] Enabled: ${this.enabled}`); } /** * 检查 backup-service 是否启用 */ isEnabled(): boolean { return this.enabled && !!this.serviceJwtSecret; } /** * 存储 delegate share 到 backup-service */ async storeBackupShare(params: StoreBackupShareParams): Promise { if (!this.isEnabled()) { this.logger.warn('Backup service is disabled, skipping backup share storage'); return; } this.logger.log(`Storing backup share: userId=${params.userId}, username=${params.username}`); try { const serviceToken = this.generateServiceToken(); await firstValueFrom( this.httpService.post( `${this.backupServiceUrl}/backup-share/store`, { userId: params.userId, accountSequence: params.accountSequence, publicKey: params.publicKey, encryptedShareData: params.encryptedShare, }, { headers: { 'Content-Type': 'application/json', 'X-Service-Token': serviceToken, }, timeout: 30000, }, ), ); this.logger.log(`Backup share stored successfully: userId=${params.userId}`); } catch (error) { this.logger.error(`Failed to store backup share: userId=${params.userId}`, error); throw error; // 抛出异常,让调用方处理 } } /** * 从 backup-service 获取 delegate share (用于签名) */ async retrieveBackupShare(params: RetrieveBackupShareParams): Promise { if (!this.isEnabled()) { this.logger.warn('Backup service is disabled'); return null; } this.logger.log(`Retrieving backup share: userId=${params.userId}`); try { const serviceToken = this.generateServiceToken(); const response = await firstValueFrom( this.httpService.post( `${this.backupServiceUrl}/backup-share/retrieve`, { userId: params.userId, publicKey: params.publicKey, }, { headers: { 'Content-Type': 'application/json', 'X-Service-Token': serviceToken, }, timeout: 30000, }, ), ); this.logger.log(`Backup share retrieved: userId=${params.userId}`); return response.data; } catch (error) { this.logger.error(`Failed to retrieve backup share: userId=${params.userId}`, error); return null; } } /** * 生成服务间认证 JWT */ private generateServiceToken(): string { return jwt.sign( { service: 'mpc-service', iat: Math.floor(Date.now() / 1000), }, this.serviceJwtSecret, { expiresIn: '5m' }, ); } }