feat(mpc-service): add blockchain-service client for address derivation
- Add BlockchainClientService to call blockchain-service /internal/derive-address - Call derive addresses after keygen completes with MPC public key - Include derived addresses (BSC, KAVA, DST) in keygen completed event 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
cf308efecf
commit
0ab1bf0dcc
|
|
@ -14,6 +14,7 @@ import {
|
||||||
KeygenRequestedPayload,
|
KeygenRequestedPayload,
|
||||||
} from '../../infrastructure/messaging/kafka/event-consumer.service';
|
} from '../../infrastructure/messaging/kafka/event-consumer.service';
|
||||||
import { BackupClientService } from '../../infrastructure/external/backup';
|
import { BackupClientService } from '../../infrastructure/external/backup';
|
||||||
|
import { BlockchainClientService } from '../../infrastructure/external/blockchain';
|
||||||
import { KeygenStartedEvent } from '../../domain/events/keygen-started.event';
|
import { KeygenStartedEvent } from '../../domain/events/keygen-started.event';
|
||||||
import { KeygenCompletedEvent } from '../../domain/events/keygen-completed.event';
|
import { KeygenCompletedEvent } from '../../domain/events/keygen-completed.event';
|
||||||
import { SessionFailedEvent } from '../../domain/events/session-failed.event';
|
import { SessionFailedEvent } from '../../domain/events/session-failed.event';
|
||||||
|
|
@ -28,6 +29,7 @@ export class KeygenRequestedHandler implements OnModuleInit {
|
||||||
private readonly eventPublisher: EventPublisherService,
|
private readonly eventPublisher: EventPublisherService,
|
||||||
private readonly mpcCoordinator: MPCCoordinatorService,
|
private readonly mpcCoordinator: MPCCoordinatorService,
|
||||||
private readonly backupClient: BackupClientService,
|
private readonly backupClient: BackupClientService,
|
||||||
|
private readonly blockchainClient: BlockchainClientService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async onModuleInit() {
|
async onModuleInit() {
|
||||||
|
|
@ -74,6 +76,20 @@ export class KeygenRequestedHandler implements OnModuleInit {
|
||||||
// Cache public key
|
// Cache public key
|
||||||
await this.mpcCoordinator.savePublicKeyCache(username, result.publicKey);
|
await this.mpcCoordinator.savePublicKeyCache(username, result.publicKey);
|
||||||
|
|
||||||
|
// Step 3: Derive addresses for all chains (BSC, KAVA, DST) via blockchain-service
|
||||||
|
let derivedAddresses: { chainType: string; address: string }[] = [];
|
||||||
|
try {
|
||||||
|
const deriveResult = await this.blockchainClient.deriveAddresses({
|
||||||
|
userId,
|
||||||
|
publicKey: result.publicKey,
|
||||||
|
});
|
||||||
|
derivedAddresses = deriveResult.addresses;
|
||||||
|
this.logger.log(`Addresses derived: userId=${userId}, count=${derivedAddresses.length}`);
|
||||||
|
} catch (deriveError) {
|
||||||
|
// 地址派生失败不阻塞主流程,但记录错误
|
||||||
|
this.logger.error(`Failed to derive addresses: userId=${userId}`, deriveError);
|
||||||
|
}
|
||||||
|
|
||||||
// Save delegate share to backup-service if exists
|
// Save delegate share to backup-service if exists
|
||||||
if (result.delegateShare) {
|
if (result.delegateShare) {
|
||||||
// 1. 保存到本地缓存(用于签名时快速访问)
|
// 1. 保存到本地缓存(用于签名时快速访问)
|
||||||
|
|
@ -115,6 +131,7 @@ export class KeygenRequestedHandler implements OnModuleInit {
|
||||||
userId,
|
userId,
|
||||||
username,
|
username,
|
||||||
delegateShare: result.delegateShare,
|
delegateShare: result.delegateShare,
|
||||||
|
derivedAddresses, // BSC, KAVA, DST addresses
|
||||||
serverParties: [], // mpc-system manages this
|
serverParties: [], // mpc-system manages this
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
102
backend/services/mpc-service/src/infrastructure/external/blockchain/blockchain-client.service.ts
vendored
Normal file
102
backend/services/mpc-service/src/infrastructure/external/blockchain/blockchain-client.service.ts
vendored
Normal file
|
|
@ -0,0 +1,102 @@
|
||||||
|
/**
|
||||||
|
* Blockchain Client Service
|
||||||
|
*
|
||||||
|
* mpc-service 调用 blockchain-service 派生钱包地址
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Injectable, Logger } from '@nestjs/common';
|
||||||
|
import { HttpService } from '@nestjs/axios';
|
||||||
|
import { ConfigService } from '@nestjs/config';
|
||||||
|
import { firstValueFrom } from 'rxjs';
|
||||||
|
|
||||||
|
export interface DeriveAddressParams {
|
||||||
|
userId: string;
|
||||||
|
publicKey: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DerivedAddress {
|
||||||
|
chainType: string;
|
||||||
|
address: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DeriveAddressResult {
|
||||||
|
userId: string;
|
||||||
|
publicKey: string;
|
||||||
|
addresses: DerivedAddress[];
|
||||||
|
}
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class BlockchainClientService {
|
||||||
|
private readonly logger = new Logger(BlockchainClientService.name);
|
||||||
|
private readonly blockchainServiceUrl: string;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private readonly httpService: HttpService,
|
||||||
|
private readonly configService: ConfigService,
|
||||||
|
) {
|
||||||
|
this.blockchainServiceUrl = this.configService.get<string>(
|
||||||
|
'BLOCKCHAIN_SERVICE_URL',
|
||||||
|
'http://blockchain-service:3000',
|
||||||
|
);
|
||||||
|
this.logger.log(`[INIT] BlockchainClientService initialized`);
|
||||||
|
this.logger.log(`[INIT] URL: ${this.blockchainServiceUrl}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从公钥派生多链地址 (BSC, KAVA, DST)
|
||||||
|
*/
|
||||||
|
async deriveAddresses(params: DeriveAddressParams): Promise<DeriveAddressResult> {
|
||||||
|
this.logger.log(`Deriving addresses: userId=${params.userId}, publicKey=${params.publicKey.substring(0, 20)}...`);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await firstValueFrom(
|
||||||
|
this.httpService.post<DeriveAddressResult>(
|
||||||
|
`${this.blockchainServiceUrl}/internal/derive-address`,
|
||||||
|
{
|
||||||
|
userId: params.userId,
|
||||||
|
publicKey: params.publicKey,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
timeout: 30000,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
this.logger.log(`Addresses derived successfully: userId=${params.userId}, count=${response.data.addresses.length}`);
|
||||||
|
for (const addr of response.data.addresses) {
|
||||||
|
this.logger.log(` ${addr.chainType}: ${addr.address}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return response.data;
|
||||||
|
} catch (error) {
|
||||||
|
this.logger.error(`Failed to derive addresses: userId=${params.userId}`, error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取用户已有的地址
|
||||||
|
*/
|
||||||
|
async getUserAddresses(userId: string): Promise<DerivedAddress[]> {
|
||||||
|
this.logger.log(`Getting user addresses: userId=${userId}`);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await firstValueFrom(
|
||||||
|
this.httpService.get<{ userId: string; addresses: DerivedAddress[] }>(
|
||||||
|
`${this.blockchainServiceUrl}/internal/user/${userId}/addresses`,
|
||||||
|
{
|
||||||
|
timeout: 30000,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
return response.data.addresses;
|
||||||
|
} catch (error) {
|
||||||
|
this.logger.error(`Failed to get user addresses: userId=${userId}`, error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
export * from './blockchain-client.service';
|
||||||
|
|
@ -5,6 +5,7 @@
|
||||||
* - PrismaService 用于缓存公钥和 delegate share
|
* - PrismaService 用于缓存公钥和 delegate share
|
||||||
* - Kafka 事件发布和消费
|
* - Kafka 事件发布和消费
|
||||||
* - BackupClientService 用于存储 delegate share 到 backup-service
|
* - BackupClientService 用于存储 delegate share 到 backup-service
|
||||||
|
* - BlockchainClientService 用于派生钱包地址
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Global, Module } from '@nestjs/common';
|
import { Global, Module } from '@nestjs/common';
|
||||||
|
|
@ -20,6 +21,7 @@ import { EventConsumerService } from './messaging/kafka/event-consumer.service';
|
||||||
|
|
||||||
// External Services
|
// External Services
|
||||||
import { BackupClientService } from './external/backup';
|
import { BackupClientService } from './external/backup';
|
||||||
|
import { BlockchainClientService } from './external/blockchain';
|
||||||
|
|
||||||
@Global()
|
@Global()
|
||||||
@Module({
|
@Module({
|
||||||
|
|
@ -40,12 +42,14 @@ import { BackupClientService } from './external/backup';
|
||||||
|
|
||||||
// External Services
|
// External Services
|
||||||
BackupClientService,
|
BackupClientService,
|
||||||
|
BlockchainClientService,
|
||||||
],
|
],
|
||||||
exports: [
|
exports: [
|
||||||
PrismaService,
|
PrismaService,
|
||||||
EventPublisherService,
|
EventPublisherService,
|
||||||
EventConsumerService,
|
EventConsumerService,
|
||||||
BackupClientService,
|
BackupClientService,
|
||||||
|
BlockchainClientService,
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class InfrastructureModule {}
|
export class InfrastructureModule {}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue