rwadurian/backend/services/identity-service/src/infrastructure/external/blockchain/blockchain-client.service.ts

218 lines
5.7 KiB
TypeScript

/**
* Blockchain Client Service
*
* identity-service 调用 blockchain-service API
* - 验证助记词
* - 从助记词派生地址
*/
import { Injectable, Logger } from '@nestjs/common';
import { HttpService } from '@nestjs/axios';
import { ConfigService } from '@nestjs/config';
import { firstValueFrom } from 'rxjs';
export interface VerifyMnemonicParams {
mnemonic: string;
expectedAddresses: Array<{
chainType: string;
address: string;
}>;
}
export interface VerifyMnemonicResult {
valid: boolean;
matchedAddresses: string[];
mismatchedAddresses: string[];
}
export interface VerifyMnemonicByAccountParams {
accountSequence: string; // 格式: D + YYMMDD + 5位序号
mnemonic: string;
}
export interface VerifyMnemonicHashResult {
valid: boolean;
message?: string;
}
export interface DerivedAddress {
chainType: string;
address: string;
}
@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}`);
}
/**
* 验证助记词是否匹配指定的钱包地址
*/
async verifyMnemonic(
params: VerifyMnemonicParams,
): Promise<VerifyMnemonicResult> {
this.logger.log(
`Verifying mnemonic against ${params.expectedAddresses.length} addresses`,
);
try {
const response = await firstValueFrom(
this.httpService.post<VerifyMnemonicResult>(
`${this.blockchainServiceUrl}/internal/verify-mnemonic`,
{
mnemonic: params.mnemonic,
expectedAddresses: params.expectedAddresses,
},
{
headers: { 'Content-Type': 'application/json' },
timeout: 30000,
},
),
);
this.logger.log(
`Mnemonic verification result: valid=${response.data.valid}`,
);
return response.data;
} catch (error) {
this.logger.error('Failed to verify mnemonic', error);
throw error;
}
}
/**
* 通过账户序列号验证助记词(用于账户恢复)
*/
async verifyMnemonicByAccount(
params: VerifyMnemonicByAccountParams,
): Promise<VerifyMnemonicHashResult> {
this.logger.log(`Verifying mnemonic for account ${params.accountSequence}`);
try {
const response = await firstValueFrom(
this.httpService.post<VerifyMnemonicHashResult>(
`${this.blockchainServiceUrl}/internal/verify-mnemonic-hash`,
{
accountSequence: params.accountSequence,
mnemonic: params.mnemonic,
},
{
headers: { 'Content-Type': 'application/json' },
timeout: 30000,
},
),
);
this.logger.log(
`Mnemonic verification result: valid=${response.data.valid}`,
);
return response.data;
} catch (error) {
this.logger.error('Failed to verify mnemonic', error);
throw error;
}
}
/**
* 从助记词派生所有链的钱包地址
*/
async deriveFromMnemonic(mnemonic: string): Promise<DerivedAddress[]> {
this.logger.log('Deriving addresses from mnemonic');
try {
const response = await firstValueFrom(
this.httpService.post<{ addresses: DerivedAddress[] }>(
`${this.blockchainServiceUrl}/internal/derive-from-mnemonic`,
{ mnemonic },
{
headers: { 'Content-Type': 'application/json' },
timeout: 30000,
},
),
);
this.logger.log(
`Derived ${response.data.addresses.length} addresses from mnemonic`,
);
return response.data.addresses;
} catch (error) {
this.logger.error('Failed to derive addresses from mnemonic', error);
throw error;
}
}
/**
* 标记助记词已备份
*/
async markMnemonicBackedUp(accountSequence: string): Promise<void> {
this.logger.log(
`Marking mnemonic as backed up for account ${accountSequence}`,
);
try {
await firstValueFrom(
this.httpService.put(
`${this.blockchainServiceUrl}/internal/mnemonic/backup`,
{ accountSequence },
{
headers: { 'Content-Type': 'application/json' },
timeout: 30000,
},
),
);
this.logger.log(
`Mnemonic marked as backed up for account ${accountSequence}`,
);
} catch (error) {
this.logger.error('Failed to mark mnemonic as backed up', error);
throw error;
}
}
/**
* 挂失助记词
*/
async revokeMnemonic(
accountSequence: string,
reason: string,
): Promise<{ success: boolean; message: string }> {
this.logger.log(
`Revoking mnemonic for account ${accountSequence}, reason: ${reason}`,
);
try {
const response = await firstValueFrom(
this.httpService.post<{ success: boolean; message: string }>(
`${this.blockchainServiceUrl}/internal/mnemonic/revoke`,
{ accountSequence, reason },
{
headers: { 'Content-Type': 'application/json' },
timeout: 30000,
},
),
);
this.logger.log(
`Mnemonic revoke result: success=${response.data.success}`,
);
return response.data;
} catch (error) {
this.logger.error('Failed to revoke mnemonic', error);
throw error;
}
}
}