/** * MPC Wallet Service * * 使用 MPC 2-of-3 协议生成三链钱包地址 * 并对地址进行签名验证 * * 调用路径: identity-service → mpc-service → mpc-system */ import { Injectable, Logger } from '@nestjs/common'; import { createHash } from 'crypto'; import { MpcClientService } from './mpc-client.service'; export interface MpcWalletGenerationParams { userId: string; username: string; // 用户名 (用于 MPC keygen) deviceId: string; } export interface ChainWalletInfo { chainType: 'KAVA' | 'DST' | 'BSC'; address: string; publicKey: string; addressDigest: string; signature: string; // 64 bytes hex (R + S) } export interface MpcWalletGenerationResult { publicKey: string; // MPC 公钥 delegateShare: string; // delegate share (加密的用户分片) serverParties: string[]; // 服务器 party IDs wallets: ChainWalletInfo[]; // 三条链的钱包信息 sessionId: string; // MPC 会话ID } @Injectable() export class MpcWalletService { private readonly logger = new Logger(MpcWalletService.name); // 三条链的地址生成配置 private readonly chainConfigs = { BSC: { name: 'Binance Smart Chain', prefix: '0x', derivationPath: "m/44'/60'/0'/0/0", // EVM 兼容链 addressType: 'evm' as const, }, KAVA: { name: 'Kava EVM', prefix: '0x', derivationPath: "m/44'/60'/0'/0/0", // Kava EVM 使用以太坊兼容地址 addressType: 'evm' as const, }, DST: { name: 'Durian Star Token', prefix: 'dst', // Cosmos Bech32 前缀 derivationPath: "m/44'/118'/0'/0/0", // Cosmos 标准路径 addressType: 'cosmos' as const, }, }; constructor( private readonly mpcClient: MpcClientService, ) {} /** * 使用 MPC 2-of-3 生成三链钱包 * * 流程: * 1. 生成 MPC 密钥 (2-of-3) * 2. 从公钥派生三条链的地址 * 3. 计算地址摘要 * 4. 使用 MPC 签名对摘要进行签名 * 5. 返回完整的钱包信息 */ async generateMpcWallet(params: MpcWalletGenerationParams): Promise { this.logger.log(`Generating MPC wallet for user=${params.userId}, username=${params.username}`); // Step 1: 生成 MPC 密钥 const keygenResult = await this.mpcClient.executeKeygen({ sessionId: this.mpcClient.generateSessionId(), username: params.username, threshold: 1, // t in t-of-n (2-of-3 means t=1) totalParties: 3, requireDelegate: true, }); this.logger.log(`MPC keygen completed: publicKey=${keygenResult.publicKey}`); // Step 2: 从公钥派生三条链的地址 const walletAddresses = await this.deriveChainAddresses(keygenResult.publicKey); // Step 3: 计算地址摘要 const addressDigest = this.computeAddressDigest(walletAddresses); // Step 4: 使用 MPC 签名对摘要进行签名 const signingResult = await this.mpcClient.executeSigning({ username: params.username, messageHash: addressDigest, }); this.logger.log(`MPC signing completed: signature=${signingResult.signature.slice(0, 16)}...`); // Step 5: 构建钱包信息 const wallets: ChainWalletInfo[] = walletAddresses.map((wa) => ({ chainType: wa.chainType as 'KAVA' | 'DST' | 'BSC', address: wa.address, publicKey: keygenResult.publicKey, addressDigest: this.computeSingleAddressDigest(wa.address, wa.chainType), signature: signingResult.signature, })); return { publicKey: keygenResult.publicKey, delegateShare: keygenResult.delegateShare.encryptedShare, serverParties: keygenResult.serverParties, wallets, sessionId: keygenResult.sessionId, }; } /** * 验证钱包地址签名 * * 用于检测地址是否被篡改 * * @param address 钱包地址 * @param chainType 链类型 * @param publicKey 公钥 (hex) * @param signature 签名 (64 bytes hex: R + S) */ async verifyWalletSignature( address: string, chainType: string, publicKey: string, signature: string, ): Promise { try { const { ethers } = await import('ethers'); // 签名格式: R (32 bytes) + S (32 bytes) = 64 bytes hex if (signature.length !== 128) { this.logger.error(`Invalid signature length: ${signature.length}, expected 128`); return false; } const r = '0x' + signature.slice(0, 64); const s = '0x' + signature.slice(64, 128); // 计算地址摘要 const digest = this.computeSingleAddressDigest(address, chainType); const digestBytes = Buffer.from(digest, 'hex'); // 尝试两种 recovery id for (const v of [27, 28]) { try { const sig = ethers.Signature.from({ r, s, v }); const recoveredPubKey = ethers.SigningKey.recoverPublicKey(digestBytes, sig); const compressedRecovered = ethers.SigningKey.computePublicKey(recoveredPubKey, true); if (compressedRecovered.slice(2).toLowerCase() === publicKey.toLowerCase()) { return true; } } catch { // 尝试下一个 v 值 } } return false; } catch (error) { this.logger.error(`Signature verification failed: ${error.message}`); return false; } } /** * 从 MPC 公钥派生三条链的地址 * * - BSC/KAVA: EVM 地址 (keccak256) * - DST: Cosmos Bech32 地址 (ripemd160(sha256)) */ private async deriveChainAddresses(publicKey: string): Promise<{ chainType: string; address: string }[]> { const { ethers } = await import('ethers'); const { bech32 } = await import('bech32'); // MPC 公钥 (压缩格式,33 bytes) const pubKeyHex = publicKey.startsWith('0x') ? publicKey : '0x' + publicKey; const compressedPubKeyBytes = Buffer.from(pubKeyHex.replace('0x', ''), 'hex'); // 解压公钥 (如果是压缩格式) let uncompressedPubKey: string; if (pubKeyHex.length === 68) { // 压缩格式 (33 bytes = 66 hex chars + 0x) uncompressedPubKey = ethers.SigningKey.computePublicKey(pubKeyHex, false); } else { uncompressedPubKey = pubKeyHex; } // ===== EVM 地址派生 (BSC, KAVA) ===== // 地址 = keccak256(公钥[1:])[12:] const pubKeyBytes = Buffer.from(uncompressedPubKey.slice(4), 'hex'); // 去掉 0x04 前缀 const addressHash = ethers.keccak256(pubKeyBytes); const evmAddress = ethers.getAddress('0x' + addressHash.slice(-40)); // ===== Cosmos 地址派生 (DST) ===== // 地址 = bech32(prefix, ripemd160(sha256(compressed_pubkey))) const sha256Hash = createHash('sha256').update(compressedPubKeyBytes).digest(); const ripemd160Hash = createHash('ripemd160').update(sha256Hash).digest(); const dstAddress = bech32.encode(this.chainConfigs.DST.prefix, bech32.toWords(ripemd160Hash)); return [ { chainType: 'BSC', address: evmAddress }, { chainType: 'KAVA', address: evmAddress }, { chainType: 'DST', address: dstAddress }, ]; } /** * 计算三个地址的联合摘要 * * digest = SHA256(BSC地址 + KAVA地址 + DST地址) */ private computeAddressDigest(addresses: { chainType: string; address: string }[]): string { // 按链类型排序以确保一致性 const sortedAddresses = [...addresses].sort((a, b) => a.chainType.localeCompare(b.chainType), ); // 拼接地址 const concatenated = sortedAddresses.map((a) => a.address.toLowerCase()).join(''); // 计算 SHA256 摘要 return createHash('sha256').update(concatenated).digest('hex'); } /** * 计算单个地址的摘要 */ private computeSingleAddressDigest(address: string, chainType: string): string { const message = `${chainType}:${address.toLowerCase()}`; return createHash('sha256').update(message).digest('hex'); } /** * 获取所有支持的链类型 */ getSupportedChains(): string[] { return Object.keys(this.chainConfigs); } }