Skip to content

Commit 8c14969

Browse files
authored
feat(1280): Implementation of allow passing key to fresh created account (#1531)
Signed-off-by: matevszm <mateusz.marcinkowski@blockydevs.com>
1 parent 3b72f8e commit 8c14969

File tree

10 files changed

+273
-109
lines changed

10 files changed

+273
-109
lines changed

src/__tests__/mocks/fixtures.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ export const ED25519_HEX_PUBLIC_KEY =
1919
'99813fcbebfebca118fd96c08d15973ac60713bf3c129aebae6be2d626117336';
2020
export const ED25519_HEX_PUBLIC_KEY_WITH_0X = `0x${ED25519_HEX_PUBLIC_KEY}`;
2121
export const ECDSA_EVM_ADDRESS = '0x48dfcb856aab92c0152c1de06b3263e3be3bde2c';
22+
export const ACCOUNT_ID_EVM_ADDRESS_9999 =
23+
'0x000000000000000000000000000000000000270f';
24+
export const ACCOUNT_ID_EVM_ADDRESS_8888 =
25+
'0x00000000000000000000000000000000000022b8';
2226
export const MOCK_PUBLIC_KEY =
2327
'0000000000000000000000000000000000000000000000000000000000000000';
2428

src/core/services/account/__tests__/unit/account-transaction-service.test.ts

Lines changed: 1 addition & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import { ECDSA_HEX_PUBLIC_KEY } from '@/__tests__/mocks/fixtures';
1212
import { makeLogger } from '@/__tests__/mocks/mocks';
1313
import { ValidationError } from '@/core/errors';
1414
import { AccountServiceImpl } from '@/core/services/account/account-transaction-service';
15-
import { KeyAlgorithm } from '@/core/shared/constants';
1615

1716
import {
1817
createMockAccountCreateTransaction,
@@ -59,11 +58,10 @@ describe('AccountServiceImpl', () => {
5958
});
6059

6160
describe('createAccount', () => {
62-
it('should create account with ECDSA key type', () => {
61+
it('should create account with provided public key', () => {
6362
const params = {
6463
balanceRaw: 100_000_000n,
6564
publicKey: ECDSA_HEX_PUBLIC_KEY,
66-
keyType: KeyAlgorithm.ECDSA,
6765
};
6866

6967
const result = accountService.createAccount(params);
@@ -73,43 +71,13 @@ describe('AccountServiceImpl', () => {
7371
expect(mockTransaction.setInitialBalance).toHaveBeenCalledWith(
7472
mockHbarInstance,
7573
);
76-
expect(mockTransaction.setECDSAKeyWithAlias).toHaveBeenCalledWith(
77-
mockPublicKeyInstance,
78-
);
79-
expect(mockTransaction.setKeyWithoutAlias).not.toHaveBeenCalled();
80-
expect(result.transaction).toBe(mockTransaction);
81-
expect(result.publicKey).toBe(ECDSA_HEX_PUBLIC_KEY);
82-
});
83-
84-
it('should create account with ED25519 key type', () => {
85-
const params = {
86-
balanceRaw: 50_000_000n,
87-
publicKey: ECDSA_HEX_PUBLIC_KEY,
88-
keyType: KeyAlgorithm.ED25519,
89-
};
90-
91-
const result = accountService.createAccount(params);
92-
9374
expect(mockTransaction.setKeyWithoutAlias).toHaveBeenCalledWith(
9475
mockPublicKeyInstance,
9576
);
96-
expect(mockTransaction.setECDSAKeyWithAlias).not.toHaveBeenCalled();
9777
expect(result.transaction).toBe(mockTransaction);
9878
expect(result.publicKey).toBe(ECDSA_HEX_PUBLIC_KEY);
9979
});
10080

101-
it('should default to ECDSA when keyType is not specified', () => {
102-
const params = {
103-
balanceRaw: 100_000_000n,
104-
publicKey: ECDSA_HEX_PUBLIC_KEY,
105-
};
106-
107-
accountService.createAccount(params);
108-
109-
expect(mockTransaction.setECDSAKeyWithAlias).toHaveBeenCalled();
110-
expect(mockTransaction.setKeyWithoutAlias).not.toHaveBeenCalled();
111-
});
112-
11381
it('should set max auto associations when specified', () => {
11482
const params = {
11583
balanceRaw: 100_000_000n,

src/core/services/account/account-transaction-service.interface.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
import type { KeyAlgorithm } from '@/core/shared/constants';
2-
31
/**
42
* Interface for Account-related operations
53
* All account services must implement this interface
@@ -26,7 +24,6 @@ export interface CreateAccountParams {
2624
balanceRaw: bigint;
2725
maxAutoAssociations?: number;
2826
publicKey: string;
29-
keyType?: KeyAlgorithm;
3027
}
3128

3229
// Import Hedera SDK types

src/core/services/account/account-transaction-service.ts

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ import {
1818
} from '@hashgraph/sdk';
1919

2020
import { ValidationError } from '@/core/errors';
21-
import { KeyAlgorithm } from '@/core/shared/constants';
2221

2322
export class AccountServiceImpl implements AccountService {
2423
private logger: Logger;
@@ -42,12 +41,7 @@ export class AccountServiceImpl implements AccountService {
4241
balance || 0,
4342
);
4443

45-
const keyType = params.keyType || KeyAlgorithm.ECDSA;
46-
if (keyType === KeyAlgorithm.ECDSA) {
47-
transaction.setECDSAKeyWithAlias(publicKey);
48-
} else {
49-
transaction.setKeyWithoutAlias(publicKey);
50-
}
44+
transaction.setKeyWithoutAlias(publicKey);
5145

5246
if (params.maxAutoAssociations && params.maxAutoAssociations > 0) {
5347
transaction.setMaxAutomaticTokenAssociations(

src/plugins/account/__tests__/unit/create.test.ts

Lines changed: 164 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
import type { CoreApi } from '@/core/core-api/core-api.interface';
2+
import type { KeyResolverService } from '@/core/services/key-resolver/key-resolver-service.interface';
23
import type { HederaMirrornodeService } from '@/core/services/mirrornode/hedera-mirrornode-service.interface';
34
import type { TransactionResult } from '@/core/services/tx-execution/tx-execution-service.interface';
45

56
import '@/core/utils/json-serialize';
67

78
import {
8-
ECDSA_EVM_ADDRESS,
9+
ACCOUNT_ID_EVM_ADDRESS_8888,
10+
ACCOUNT_ID_EVM_ADDRESS_9999,
11+
ECDSA_HEX_PRIVATE_KEY,
912
ECDSA_HEX_PUBLIC_KEY,
1013
ED25519_HEX_PUBLIC_KEY,
1114
} from '@/__tests__/mocks/fixtures';
@@ -77,7 +80,6 @@ describe('account plugin - create command (ADR-003)', () => {
7780
balanceRaw: 500000000000n,
7881
maxAutoAssociations: 3,
7982
publicKey: 'pub-key-test',
80-
keyType: KeyAlgorithm.ECDSA,
8183
});
8284
expect(signing.signAndExecute).toHaveBeenCalled();
8385
expect(alias.register).toHaveBeenCalledWith(
@@ -98,7 +100,7 @@ describe('account plugin - create command (ADR-003)', () => {
98100
type: KeyAlgorithm.ECDSA,
99101
network: 'testnet',
100102
keyRefId: 'kr_test123',
101-
evmAddress: ECDSA_EVM_ADDRESS,
103+
evmAddress: ACCOUNT_ID_EVM_ADDRESS_9999,
102104
}),
103105
);
104106

@@ -109,7 +111,7 @@ describe('account plugin - create command (ADR-003)', () => {
109111
expect(output.type).toBe(KeyAlgorithm.ECDSA);
110112
expect(output.network).toBe('testnet');
111113
expect(output.transactionId).toBe('0.0.1234@1234567890.000000000');
112-
expect(output.evmAddress).toBe(ECDSA_EVM_ADDRESS);
114+
expect(output.evmAddress).toBe(ACCOUNT_ID_EVM_ADDRESS_9999);
113115
expect(output.publicKey).toBe(ECDSA_HEX_PUBLIC_KEY);
114116
});
115117

@@ -218,16 +220,171 @@ describe('account plugin - create command (ADR-003)', () => {
218220
);
219221
expect(account.createAccount).toHaveBeenCalledWith(
220222
expect.objectContaining({
221-
keyType: KeyAlgorithm.ECDSA,
223+
publicKey: 'pub-key-test',
222224
}),
223225
);
224226

225227
const output = assertOutput(result.result, CreateAccountOutputSchema);
226228
expect(output.type).toBe(KeyAlgorithm.ECDSA);
227-
expect(output.evmAddress).toBe(ECDSA_EVM_ADDRESS);
229+
expect(output.evmAddress).toBe(ACCOUNT_ID_EVM_ADDRESS_8888);
228230
expect(output.publicKey).toBe(ECDSA_HEX_PUBLIC_KEY);
229231
});
230232

233+
test('creates account with provided private key (--key ecdsa:private:xxx)', async () => {
234+
const logger = makeLogger();
235+
const saveAccountMock = jest.fn();
236+
MockedHelper.mockImplementation(() => ({ saveAccount: saveAccountMock }));
237+
238+
const { account, signing, networkMock, kms, alias, mirror, keyResolver } =
239+
makeApiMocksForAccountCreate({
240+
createAccountImpl: jest.fn().mockReturnValue({
241+
transaction: {},
242+
publicKey: ECDSA_HEX_PUBLIC_KEY,
243+
}),
244+
signAndExecuteImpl: jest.fn().mockResolvedValue({
245+
transactionId: '0.0.1234@1234567890.000000004',
246+
success: true,
247+
accountId: '0.0.6666',
248+
receipt: { status: { status: 'success' } },
249+
}),
250+
keyResolverGetPublicKeyImpl: jest.fn().mockResolvedValue({
251+
keyRefId: 'kr_provided123',
252+
publicKey: ECDSA_HEX_PUBLIC_KEY,
253+
}),
254+
});
255+
256+
kms.get = jest.fn().mockReturnValue({
257+
keyRefId: 'kr_provided123',
258+
publicKey: ECDSA_HEX_PUBLIC_KEY,
259+
keyAlgorithm: KeyAlgorithm.ECDSA,
260+
keyManager: 'local',
261+
labels: [],
262+
createdAt: '',
263+
updatedAt: '',
264+
});
265+
266+
const api: Partial<CoreApi> = {
267+
account,
268+
txExecution: signing,
269+
network: networkMock,
270+
kms,
271+
alias,
272+
mirror: mirror as HederaMirrornodeService,
273+
keyResolver: keyResolver as KeyResolverService,
274+
logger,
275+
};
276+
277+
const args = makeArgs(api, logger, {
278+
balance: '1000',
279+
key: `ecdsa:private:${ECDSA_HEX_PRIVATE_KEY}`,
280+
});
281+
282+
const result = await createAccount(args);
283+
284+
expect(kms.createLocalPrivateKey).not.toHaveBeenCalled();
285+
expect(keyResolver.getPublicKey).toHaveBeenCalled();
286+
expect(account.createAccount).toHaveBeenCalledWith(
287+
expect.objectContaining({
288+
publicKey: ECDSA_HEX_PUBLIC_KEY,
289+
}),
290+
);
291+
292+
const output = assertOutput(result.result, CreateAccountOutputSchema);
293+
expect(output.accountId).toBe('0.0.6666');
294+
expect(output.type).toBe(KeyAlgorithm.ECDSA);
295+
expect(output.publicKey).toBe(ECDSA_HEX_PUBLIC_KEY);
296+
});
297+
298+
test('creates account with key reference (--key kr_xxx)', async () => {
299+
const logger = makeLogger();
300+
const saveAccountMock = jest.fn();
301+
MockedHelper.mockImplementation(() => ({ saveAccount: saveAccountMock }));
302+
303+
const { account, signing, networkMock, kms, alias, mirror, keyResolver } =
304+
makeApiMocksForAccountCreate({
305+
createAccountImpl: jest.fn().mockReturnValue({
306+
transaction: {},
307+
publicKey: ECDSA_HEX_PUBLIC_KEY,
308+
}),
309+
signAndExecuteImpl: jest.fn().mockResolvedValue({
310+
transactionId: '0.0.1234@1234567890.000000005',
311+
success: true,
312+
accountId: '0.0.5555',
313+
receipt: { status: { status: 'success' } },
314+
}),
315+
keyResolverGetPublicKeyImpl: jest.fn().mockResolvedValue({
316+
keyRefId: 'kr_test123',
317+
publicKey: ECDSA_HEX_PUBLIC_KEY,
318+
}),
319+
});
320+
321+
kms.get = jest.fn().mockReturnValue({
322+
keyRefId: 'kr_test123',
323+
publicKey: ECDSA_HEX_PUBLIC_KEY,
324+
keyAlgorithm: KeyAlgorithm.ECDSA,
325+
keyManager: 'local',
326+
labels: [],
327+
createdAt: '',
328+
updatedAt: '',
329+
});
330+
331+
const api: Partial<CoreApi> = {
332+
account,
333+
txExecution: signing,
334+
network: networkMock,
335+
kms,
336+
alias,
337+
mirror: mirror as HederaMirrornodeService,
338+
keyResolver: keyResolver as KeyResolverService,
339+
logger,
340+
};
341+
342+
const args = makeArgs(api, logger, {
343+
balance: '1000',
344+
key: 'kr_test123',
345+
});
346+
347+
const result = await createAccount(args);
348+
349+
expect(kms.createLocalPrivateKey).not.toHaveBeenCalled();
350+
expect(keyResolver.getPublicKey).toHaveBeenCalled();
351+
expect(account.createAccount).toHaveBeenCalledWith(
352+
expect.objectContaining({
353+
publicKey: ECDSA_HEX_PUBLIC_KEY,
354+
}),
355+
);
356+
357+
const output = assertOutput(result.result, CreateAccountOutputSchema);
358+
expect(output.accountId).toBe('0.0.5555');
359+
});
360+
361+
test('throws ValidationError when both --key and --key-type are provided', async () => {
362+
const logger = makeLogger();
363+
MockedHelper.mockImplementation(() => ({ saveAccount: jest.fn() }));
364+
365+
const { account, signing, networkMock, kms, alias, mirror, keyResolver } =
366+
makeApiMocksForAccountCreate({});
367+
368+
const api: Partial<CoreApi> = {
369+
account,
370+
txExecution: signing,
371+
network: networkMock,
372+
kms,
373+
alias,
374+
mirror: mirror as HederaMirrornodeService,
375+
keyResolver: keyResolver as KeyResolverService,
376+
logger,
377+
};
378+
379+
const args = makeArgs(api, logger, {
380+
balance: '1000',
381+
key: `ecdsa:private:${ECDSA_HEX_PRIVATE_KEY}`,
382+
keyType: KeyAlgorithm.ECDSA,
383+
});
384+
385+
await expect(createAccount(args)).rejects.toThrow();
386+
});
387+
231388
test('creates account with ED25519 key type', async () => {
232389
const logger = makeLogger();
233390
const saveAccountMock = jest.fn();
@@ -272,7 +429,7 @@ describe('account plugin - create command (ADR-003)', () => {
272429
);
273430
expect(account.createAccount).toHaveBeenCalledWith(
274431
expect.objectContaining({
275-
keyType: KeyAlgorithm.ED25519,
432+
publicKey: 'pub-key-test',
276433
}),
277434
);
278435

src/plugins/account/__tests__/unit/helpers/mocks.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,7 @@ export interface ApiMocksConfig {
239239
signAndExecuteImpl?: jest.Mock;
240240
network?: 'testnet' | 'mainnet' | 'previewnet';
241241
operatorBalance?: bigint;
242+
keyResolverGetPublicKeyImpl?: jest.Mock;
242243
}
243244

244245
/**
@@ -254,6 +255,7 @@ export const makeApiMocksForAccountCreate = ({
254255
signAndExecuteImpl,
255256
network = 'testnet',
256257
operatorBalance = OPERATOR_SUFFICIENT_BALANCE,
258+
keyResolverGetPublicKeyImpl,
257259
}: ApiMocksConfig) => {
258260
const account: jest.Mocked<AccountService> = {
259261
createAccount: createAccountImpl || jest.fn(),
@@ -284,5 +286,18 @@ export const makeApiMocksForAccountCreate = ({
284286

285287
const alias = makeGlobalAliasMock();
286288

287-
return { account, signing, networkMock, kms, alias, mirror };
289+
const keyResolver = {
290+
getPublicKey:
291+
keyResolverGetPublicKeyImpl ??
292+
jest.fn().mockResolvedValue({
293+
keyRefId: 'kr_provided123',
294+
publicKey: 'provided-pub-key',
295+
}),
296+
resolveAccountCredentials: jest.fn(),
297+
resolveAccountCredentialsWithFallback: jest.fn(),
298+
resolveDestination: jest.fn(),
299+
resolveSigningKey: jest.fn(),
300+
};
301+
302+
return { account, signing, networkMock, kms, alias, mirror, keyResolver };
288303
};

0 commit comments

Comments
 (0)