Skip to content

Commit

Permalink
feat: add X/P private key export CP-9337 (#64)
Browse files Browse the repository at this point in the history
  • Loading branch information
gergelylovas authored Oct 10, 2024
1 parent f779698 commit 30e67e3
Show file tree
Hide file tree
Showing 7 changed files with 386 additions and 81 deletions.
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@
"@walletconnect/sign-client": "2.9.1",
"@walletconnect/utils": "2.9.2",
"argon2-browser": "1.18.0",
"bip32": "2.0.6",
"bip39": "3.0.4",
"bitcoinjs-lib": "5.2.0",
"bn.js": "5.2.1",
"date-fns": "2.28.0",
Expand Down
205 changes: 183 additions & 22 deletions src/background/services/accounts/handlers/getPrivateKey.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import { ExtensionRequest } from '@src/background/connections/extensionConnection/models';
import { GetPrivateKeyHandler } from './getPrivateKey';
import { AccountType, GetPrivateKeyErrorTypes } from '../models';
import {
AccountType,
GetPrivateKeyErrorTypes,
PrimaryAccount,
PrivateKeyChain,
} from '../models';
import { LockService } from '../../lock/LockService';
import { SecretType } from '../../secrets/models';
import { getWalletFromMnemonic } from '@avalabs/core-wallets-sdk';
Expand All @@ -13,15 +18,17 @@ jest.mock('@avalabs/core-wallets-sdk', () => ({
}));

describe('background/services/accounts/handlers/getPrivateKey.ts', () => {
const sercretServiceMock = {
const secretServiceMock = {
getWalletSecrets: jest.fn(),
getPrimaryAccountSecrets: jest.fn(),
getImportedAccountSecrets: jest.fn(),
} as any;
const lockServiceMock: jest.Mocked<LockService> = {
verifyPassword: jest.fn(),
} as any;
const accountsServiceMock: jest.Mocked<AccountsService> = {} as any;
const accountsServiceMock: jest.Mocked<AccountsService> = {
getAccountByID: jest.fn(),
} as any;

const request = {
id: '123',
Expand All @@ -31,7 +38,7 @@ describe('background/services/accounts/handlers/getPrivateKey.ts', () => {

const getHandler = () =>
new GetPrivateKeyHandler(
sercretServiceMock,
secretServiceMock,
lockServiceMock,
accountsServiceMock
);
Expand All @@ -45,7 +52,7 @@ describe('background/services/accounts/handlers/getPrivateKey.ts', () => {
});
});

it('should return an error when the password is missing', async () => {
it('should return an error when the chain is missing', async () => {
const handler = getHandler();
const result = await handler.handle(
buildRpcCall({
Expand All @@ -56,6 +63,42 @@ describe('background/services/accounts/handlers/getPrivateKey.ts', () => {
expect(result).toEqual({
...request,
params: [{ password: '' }],
error: {
type: GetPrivateKeyErrorTypes.Chain,
message: 'Invalid chain',
},
});
});

it('should return an error when the chain is unknown', async () => {
const handler = getHandler();
const result = await handler.handle(
buildRpcCall({
...request,
params: [{ password: '', chain: 'Z' }],
})
);
expect(result).toEqual({
...request,
params: [{ password: '', chain: 'Z' }],
error: {
type: GetPrivateKeyErrorTypes.Chain,
message: 'Invalid chain',
},
});
});

it('should return an error when the password is missing', async () => {
const handler = getHandler();
const result = await handler.handle(
buildRpcCall({
...request,
params: [{ password: '', chain: PrivateKeyChain.C }],
})
);
expect(result).toEqual({
...request,
params: [{ password: '', chain: PrivateKeyChain.C }],
error: {
type: GetPrivateKeyErrorTypes.Password,
message: 'The password is invalid',
Expand All @@ -70,12 +113,12 @@ describe('background/services/accounts/handlers/getPrivateKey.ts', () => {
const result = await handler.handle(
buildRpcCall({
...request,
params: [{ password: 'asd' }],
params: [{ password: 'asd', chain: PrivateKeyChain.C }],
})
);
expect(result).toEqual({
...request,
params: [{ password: 'asd' }],
params: [{ password: 'asd', chain: PrivateKeyChain.C }],
error: {
type: GetPrivateKeyErrorTypes.Password,
message: 'The password is invalid',
Expand All @@ -85,7 +128,7 @@ describe('background/services/accounts/handlers/getPrivateKey.ts', () => {

it('should return an error when the type is empty', async () => {
const handler = getHandler();
const params = [{ type: '', password: '123' }];
const params = [{ type: '', password: '123', chain: PrivateKeyChain.C }];
const result = await handler.handle(buildRpcCall({ ...request, params }));

expect(result).toEqual({
Expand All @@ -97,7 +140,9 @@ describe('background/services/accounts/handlers/getPrivateKey.ts', () => {

it('should return an error when the `SecretType` is not `Mnemonic` or the `AccountType` is not `IMPORTED`', async () => {
const handler = getHandler();
const params = [{ type: SecretType.Keystone, password: 'asd' }];
const params = [
{ type: SecretType.Keystone, password: 'asd', chain: PrivateKeyChain.C },
];
const result = await handler.handle(
buildRpcCall({
...request,
Expand All @@ -112,10 +157,18 @@ describe('background/services/accounts/handlers/getPrivateKey.ts', () => {
});
});

it('should return null when the secrets has no values', async () => {
sercretServiceMock.getPrimaryAccountSecrets.mockResolvedValue(null);
it('should return an error when account is not found', async () => {
secretServiceMock.getPrimaryAccountSecrets.mockResolvedValue(null);
accountsServiceMock.getAccountByID.mockReturnValue(undefined);
const handler = getHandler();
const params = [{ type: SecretType.Mnemonic, index: 0, password: 'asd' }];
const params = [
{
type: SecretType.Mnemonic,
index: 0,
password: 'asd',
chain: PrivateKeyChain.C,
},
];
const result = await handler.handle(
buildRpcCall({
...request,
Expand All @@ -127,21 +180,62 @@ describe('background/services/accounts/handlers/getPrivateKey.ts', () => {
params,
error: {
type: GetPrivateKeyErrorTypes.Mnemonic,
message: 'There is no mnemonic found',
message: 'Mnemonic not found',
},
});
});

it('should return error when the secrets has no values', async () => {
accountsServiceMock.getAccountByID.mockReturnValue({
index: 0,
} as PrimaryAccount);
secretServiceMock.getPrimaryAccountSecrets.mockResolvedValue(null);
const handler = getHandler();
const params = [
{
type: SecretType.Mnemonic,
index: 0,
password: 'asd',
chain: PrivateKeyChain.C,
},
];
const result = await handler.handle(
buildRpcCall({
...request,
params,
})
);
expect(result).toEqual({
...request,
params,
error: {
type: GetPrivateKeyErrorTypes.Mnemonic,
message: 'Mnemonic not found',
},
});
});

it('should return an error when the `signer.path` is undefined', async () => {
(getWalletFromMnemonic as jest.Mock).mockReturnValue({
path: undefined,
});
sercretServiceMock.getPrimaryAccountSecrets.mockResolvedValue({
accountsServiceMock.getAccountByID.mockReturnValue({
index: 0,
} as PrimaryAccount);
secretServiceMock.getPrimaryAccountSecrets.mockResolvedValue({
mnemonic: 'some-words-here',
secretType: SecretType.Mnemonic,
});
const handler = getHandler();

const params = [{ type: SecretType.Mnemonic, index: 0, password: 'asd' }];
const params = [
{
type: SecretType.Mnemonic,
index: 0,
password: 'asd',
chain: PrivateKeyChain.C,
},
];
const result = await handler.handle(
buildRpcCall({
...request,
Expand All @@ -157,13 +251,25 @@ describe('background/services/accounts/handlers/getPrivateKey.ts', () => {
},
});
});
it('should return the private key for the given account', async () => {
sercretServiceMock.getPrimaryAccountSecrets.mockResolvedValue({

it('should return the private key for the given account for C chain', async () => {
accountsServiceMock.getAccountByID.mockReturnValue({
index: 0,
} as PrimaryAccount);
secretServiceMock.getPrimaryAccountSecrets.mockResolvedValue({
mnemonic: 'some-words-here',
secretType: SecretType.Mnemonic,
});
const handler = getHandler();
const params = [{ type: SecretType.Mnemonic, index: 0, password: 'asd' }];
const params = [
{
type: SecretType.Mnemonic,
index: 0,
password: 'asd',
chain: PrivateKeyChain.C,
id: '123-123-123',
},
];
const result = await handler.handle(
buildRpcCall({
...request,
Expand All @@ -175,10 +281,58 @@ describe('background/services/accounts/handlers/getPrivateKey.ts', () => {
params,
result: '123123123',
});

expect(accountsServiceMock.getAccountByID).toHaveBeenCalledTimes(1);
expect(accountsServiceMock.getAccountByID).toHaveBeenCalledWith(
'123-123-123'
);
expect(secretServiceMock.getPrimaryAccountSecrets).toHaveBeenCalledTimes(1);
expect(secretServiceMock.getPrimaryAccountSecrets).toHaveBeenCalledWith({
index: 0,
});
});

it('should return the private key for the given account for X/P chain', async () => {
accountsServiceMock.getAccountByID.mockReturnValue({
index: 0,
} as PrimaryAccount);
secretServiceMock.getPrimaryAccountSecrets.mockResolvedValue({
mnemonic: 'fake mnemonic worlds',
secretType: SecretType.Mnemonic,
});
const handler = getHandler();
const params = [
{
type: SecretType.Mnemonic,
index: 0,
password: 'asd',
chain: PrivateKeyChain.XP,
},
];
const result = await handler.handle(
buildRpcCall({
...request,
params,
})
);
expect(result).toEqual({
...request,
params,
result:
'0xb80d02d83264ae22f52a6b4573218f14c613984a88d96c747a54f9d1c2482081',
});
});

it('should return the private key when the `AccountType` is imported and the `secretType` is `privateKey`', async () => {
const params = [{ type: AccountType.IMPORTED, id: 'asd', password: 'asd' }];
sercretServiceMock.getImportedAccountSecrets.mockResolvedValue({
const params = [
{
type: AccountType.IMPORTED,
id: 'asd',
password: 'asd',
chain: PrivateKeyChain.C,
},
];
secretServiceMock.getImportedAccountSecrets.mockResolvedValue({
secretType: SecretType.PrivateKey,
secret: 'secretKey',
});
Expand All @@ -196,8 +350,15 @@ describe('background/services/accounts/handlers/getPrivateKey.ts', () => {
});
});
it('should return error when the `AccountType` is imported and the `type` is not `privateKey`', async () => {
const params = [{ type: AccountType.IMPORTED, id: 'asd', password: 'asd' }];
sercretServiceMock.getImportedAccountSecrets.mockResolvedValue({
const params = [
{
type: AccountType.IMPORTED,
id: 'asd',
password: 'asd',
chain: PrivateKeyChain.C,
},
];
secretServiceMock.getImportedAccountSecrets.mockResolvedValue({
type: 'walletConnect',
secret: 'secretKey',
});
Expand Down
Loading

0 comments on commit 30e67e3

Please sign in to comment.