rwadurian/backend/services/identity-service/src/infrastructure/external/blockchain/wallet-generator.service.sp...

297 lines
9.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { Test, TestingModule } from '@nestjs/testing';
import { ConfigService } from '@nestjs/config';
import { WalletGeneratorService } from './wallet-generator.service';
import { ChainType, Mnemonic } from '@/domain/value-objects';
describe('WalletGeneratorService', () => {
let service: WalletGeneratorService;
let configService: ConfigService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
WalletGeneratorService,
{
provide: ConfigService,
useValue: {
get: jest.fn().mockReturnValue('test-salt'),
},
},
],
}).compile();
service = module.get<WalletGeneratorService>(WalletGeneratorService);
configService = module.get<ConfigService>(ConfigService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
describe('generateWalletSystem', () => {
it('应该生成完整的钱包系统', () => {
const result = service.generateWalletSystem({
userId: '12345',
deviceId: 'test-device-id',
});
expect(result).toBeDefined();
expect(result.mnemonic).toBeDefined();
expect(result.mnemonic).toBeInstanceOf(Mnemonic);
expect(result.wallets).toBeDefined();
expect(result.wallets.size).toBe(3);
});
it('应该为所有链生成钱包地址', () => {
const result = service.generateWalletSystem({
userId: '12345',
deviceId: 'test-device-id',
});
expect(result.wallets.has(ChainType.KAVA)).toBe(true);
expect(result.wallets.has(ChainType.DST)).toBe(true);
expect(result.wallets.has(ChainType.BSC)).toBe(true);
});
it('生成的 KAVA 地址应该有正确的格式', () => {
const result = service.generateWalletSystem({
userId: '12345',
deviceId: 'test-device-id',
});
const kavaWallet = result.wallets.get(ChainType.KAVA);
expect(kavaWallet).toBeDefined();
expect(kavaWallet!.address).toMatch(/^kava1[a-z0-9]{38}$/);
});
it('生成的 DST 地址应该有正确的格式', () => {
const result = service.generateWalletSystem({
userId: '12345',
deviceId: 'test-device-id',
});
const dstWallet = result.wallets.get(ChainType.DST);
expect(dstWallet).toBeDefined();
expect(dstWallet!.address).toMatch(/^dst1[a-z0-9]{38}$/);
});
it('生成的 BSC 地址应该有正确的格式', () => {
const result = service.generateWalletSystem({
userId: '12345',
deviceId: 'test-device-id',
});
const bscWallet = result.wallets.get(ChainType.BSC);
expect(bscWallet).toBeDefined();
expect(bscWallet!.address).toMatch(/^0x[a-fA-F0-9]{40}$/);
});
it('每次生成的钱包应该不同', () => {
const result1 = service.generateWalletSystem({
userId: '11111',
deviceId: 'test-device-id-1',
});
const result2 = service.generateWalletSystem({
userId: '22222',
deviceId: 'test-device-id-2',
});
expect(result1.mnemonic.value).not.toBe(result2.mnemonic.value);
expect(result1.wallets.get(ChainType.KAVA)!.address).not.toBe(
result2.wallets.get(ChainType.KAVA)!.address
);
});
});
describe('recoverWalletSystem', () => {
it('应该使用助记词恢复钱包系统', () => {
// 先生成一个钱包系统
const original = service.generateWalletSystem({
userId: '12345',
deviceId: 'test-device-id',
});
// 使用助记词恢复
const recovered = service.recoverWalletSystem({
userId: '12345',
mnemonic: original.mnemonic,
deviceId: 'new-device-id',
});
expect(recovered.size).toBe(3);
// 验证恢复的地址与原地址相同
expect(recovered.get(ChainType.KAVA)!.address).toBe(
original.wallets.get(ChainType.KAVA)!.address
);
expect(recovered.get(ChainType.DST)!.address).toBe(
original.wallets.get(ChainType.DST)!.address
);
expect(recovered.get(ChainType.BSC)!.address).toBe(
original.wallets.get(ChainType.BSC)!.address
);
});
it('不同设备应该生成相同的地址(相同助记词)', () => {
const original = service.generateWalletSystem({
userId: '12345',
deviceId: 'device-1',
});
const recovered1 = service.recoverWalletSystem({
userId: '12345',
mnemonic: original.mnemonic,
deviceId: 'device-2',
});
const recovered2 = service.recoverWalletSystem({
userId: '12345',
mnemonic: original.mnemonic,
deviceId: 'device-3',
});
expect(recovered1.get(ChainType.KAVA)!.address).toBe(
recovered2.get(ChainType.KAVA)!.address
);
});
});
describe('deriveAddress', () => {
it('相同的助记词应该派生相同的地址', () => {
const mnemonic = Mnemonic.generate();
const address1 = service.deriveAddress(ChainType.KAVA, mnemonic);
const address2 = service.deriveAddress(ChainType.KAVA, mnemonic);
expect(address1).toBe(address2);
});
it('不同的助记词应该派生不同的地址', () => {
const mnemonic1 = Mnemonic.generate();
const mnemonic2 = Mnemonic.generate();
const address1 = service.deriveAddress(ChainType.KAVA, mnemonic1);
const address2 = service.deriveAddress(ChainType.KAVA, mnemonic2);
expect(address1).not.toBe(address2);
});
it('应该为不同的链派生不同的地址', () => {
const mnemonic = Mnemonic.generate();
const kavaAddress = service.deriveAddress(ChainType.KAVA, mnemonic);
const dstAddress = service.deriveAddress(ChainType.DST, mnemonic);
const bscAddress = service.deriveAddress(ChainType.BSC, mnemonic);
expect(kavaAddress).not.toBe(dstAddress);
expect(kavaAddress).not.toBe(bscAddress);
expect(dstAddress).not.toBe(bscAddress);
});
});
describe('verifyMnemonic', () => {
it('应该验证正确的助记词', () => {
const result = service.generateWalletSystem({
userId: '12345',
deviceId: 'test-device-id',
});
const kavaWallet = result.wallets.get(ChainType.KAVA)!;
const isValid = service.verifyMnemonic(
result.mnemonic,
ChainType.KAVA,
kavaWallet.address
);
expect(isValid).toBe(true);
});
it('应该拒绝错误的助记词', () => {
const result1 = service.generateWalletSystem({
userId: '11111',
deviceId: 'test-device-id-1',
});
const result2 = service.generateWalletSystem({
userId: '22222',
deviceId: 'test-device-id-2',
});
const kavaWallet1 = result1.wallets.get(ChainType.KAVA)!;
const isValid = service.verifyMnemonic(
result2.mnemonic,
ChainType.KAVA,
kavaWallet1.address
);
expect(isValid).toBe(false);
});
});
describe('加密和解密', () => {
it('应该正确加密和解密助记词', () => {
const mnemonic = 'test mnemonic phrase for encryption';
const key = 'encryption-key';
const encrypted = service.encryptMnemonic(mnemonic, key);
expect(encrypted).toBeDefined();
expect(encrypted.encrypted).toBeDefined();
expect(encrypted.iv).toBeDefined();
expect(encrypted.authTag).toBeDefined();
const decrypted = service.decryptMnemonic(encrypted, key);
expect(decrypted).toBe(mnemonic);
});
it('使用错误的密钥应该解密失败', () => {
const mnemonic = 'test mnemonic phrase';
const key = 'correct-key';
const wrongKey = 'wrong-key';
const encrypted = service.encryptMnemonic(mnemonic, key);
expect(() => {
service.decryptMnemonic(encrypted, wrongKey);
}).toThrow();
});
it('相同的助记词和密钥应该生成不同的密文(因为随机 IV', () => {
const mnemonic = 'test mnemonic phrase';
const key = 'encryption-key';
const encrypted1 = service.encryptMnemonic(mnemonic, key);
const encrypted2 = service.encryptMnemonic(mnemonic, key);
// 密文应该不同(因为 IV 不同)
expect(encrypted1.encrypted).not.toBe(encrypted2.encrypted);
expect(encrypted1.iv).not.toBe(encrypted2.iv);
// 但解密后应该相同
const decrypted1 = service.decryptMnemonic(encrypted1, key);
const decrypted2 = service.decryptMnemonic(encrypted2, key);
expect(decrypted1).toBe(mnemonic);
expect(decrypted2).toBe(mnemonic);
});
});
describe('deriveEncryptionKey', () => {
it('相同的输入应该生成相同的密钥', () => {
const key1 = service.deriveEncryptionKey('device-1', 'user-1');
const key2 = service.deriveEncryptionKey('device-1', 'user-1');
expect(key1).toBe(key2);
});
it('不同的输入应该生成不同的密钥', () => {
const key1 = service.deriveEncryptionKey('device-1', 'user-1');
const key2 = service.deriveEncryptionKey('device-2', 'user-1');
const key3 = service.deriveEncryptionKey('device-1', 'user-2');
expect(key1).not.toBe(key2);
expect(key1).not.toBe(key3);
expect(key2).not.toBe(key3);
});
});
});