218 lines
5.7 KiB
TypeScript
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;
|
|
}
|
|
}
|
|
}
|