297 lines
9.2 KiB
TypeScript
297 lines
9.2 KiB
TypeScript
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);
|
||
});
|
||
});
|
||
});
|