Skip to content
This repository has been archived by the owner on Jun 10, 2022. It is now read-only.

Commit

Permalink
Split HD path by network type (#353) (#354)
Browse files Browse the repository at this point in the history
* Fixed #23
- Split hd path by network type

* Updated changelog
  • Loading branch information
rg911 authored Feb 3, 2021
1 parent d53f134 commit dbfb4ca
Show file tree
Hide file tree
Showing 10 changed files with 220 additions and 53 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file.

The changelog format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).

## [0.24.0] - 04-Feb-2021

### Changed

- Split coint type index in HD path by network type. MAIN_NET: `4343`; TESE_NET:`1`.

## [0.23.1] - 19-Jan-2021

### Changed
Expand Down
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,14 @@ Command Line Interface (CLI) to interact with Symbol.

## Important Notes

- [0.24.x](CHANGELOG.md#0240-02-Feb-2021) - **0.10.0.6 (Pre-Launch)**
- [0.23.x](CHANGELOG.md#0230-15-Jan-2021) - **0.10.0.x Milestone**
- [0.22.x](CHANGELOG.md#0220-06-Oct-2020) - **0.10.0.x Milestone**
- [0.21.x](CHANGELOG.md#0211-31-Jul-2020) - **0.9.6.3 Milestone**
- [0.20.x](CHANGELOG.md#0201-27-May-2020) - **0.9.5.1 Milestone**

0.23.x breaks compatibility with the ``profiles`` (Testnet change) saved using previous versions of the software.
Before installing ``symbol-cli@0.23.x``, backup and delete the file ``~ \.symbolrc.json``.
0.24.x breaks compatibility with HD account derivation in ``profiles`` (Testnet) saved using previous versions of the software.
Before installing ``symbol-cli@0.24.x``, backup and delete the file ``~ \.symbolrc.json``.

Find the complete release notes [here](CHANGELOG.md).

Expand Down
52 changes: 20 additions & 32 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "symbol-cli",
"version": "0.23.2",
"version": "0.24.0",
"description": "Command Line Interface (CLI) to interact with Symbol",
"main": "index.js",
"scripts": {
Expand Down Expand Up @@ -42,7 +42,7 @@
"prompts": "^2.4.0",
"rxjs": "^6.6.3",
"symbol-hd-wallets": "0.14.0",
"symbol-sdk": "0.23.0",
"symbol-sdk": "0.23.1",
"symbol-uri-scheme": "0.6.0",
"update-notifier": "^4.1.0",
"utf8": "^3.0.0"
Expand Down
2 changes: 1 addition & 1 deletion src/commands/account/generate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ export default class extends CreateProfileCommand {
} else {
const pathNumber = await new PathNumberResolver().resolve(options);
const mnemonic = MnemonicPassPhrase.createRandom().plain;
const privateKey = DerivationService.getPrivateKeyFromMnemonic(mnemonic, pathNumber);
const privateKey = DerivationService.getPrivateKeyFromMnemonic(mnemonic, pathNumber, networkType);
console.log(
AccountCredentialsTable.createFromAccount(
Account.createFromPrivateKey(privateKey, networkType),
Expand Down
5 changes: 4 additions & 1 deletion src/interfaces/create.profile.command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,10 @@ export class AccountCredentialsTable {
this.table.push([
'Path',
// Address paths are 0-based but shown as 1-based to the users
`Seed wallet n. ${args.pathNumber + 1} (${DerivationService.getPathFromPathNumber(args.pathNumber)})`,
`Seed wallet n. ${args.pathNumber + 1} (${DerivationService.getPathFromPathNumber(
args.pathNumber,
args.account.networkType,
)})`,
]);
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/models/hdProfile.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,8 @@ export class HdProfile extends Profile {
* @returns
*/
public static create(args: HdProfileCreation) {
const path = DerivationService.getPathFromPathNumber(args.pathNumber);
const privateKey = DerivationService.getPrivateKeyFromMnemonic(args.mnemonic, args.pathNumber);
const path = DerivationService.getPathFromPathNumber(args.pathNumber, args.networkType);
const privateKey = DerivationService.getPrivateKeyFromMnemonic(args.mnemonic, args.pathNumber, args.networkType);
const simpleWallet = SimpleWallet.createFromPrivateKey(args.name, args.password, privateKey, args.networkType);
return new HdProfile(
simpleWallet,
Expand Down
20 changes: 16 additions & 4 deletions src/services/derivation.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
*/

import { ExtendedKey, MnemonicPassPhrase, Wallet } from 'symbol-hd-wallets';
import { NetworkType } from 'symbol-sdk/dist/src/model/network/NetworkType';

const MIN_PATH_NUMBER = 0;
const MAX_PATH_NUMBER = 9;
Expand All @@ -26,9 +27,10 @@ export class DerivationService {
* Returns a path given a path number
* @static
* @param {number} pathNumber
* @param {NetworkType} networkType
* @returns {string}
*/
public static getPathFromPathNumber(pathNumber: number): string {
public static getPathFromPathNumber(pathNumber: number, networkType: NetworkType): string {
const index = parseInt(`${pathNumber}`, 10);

if (index < MIN_PATH_NUMBER || index > MAX_PATH_NUMBER) {
Expand All @@ -39,7 +41,7 @@ export class DerivationService {
throw new Error(`The given path index is invalid (${pathNumber})`);
}

return `m/44'/4343'/${pathNumber}'/0'/0'`;
return `m/44'/${DerivationService.getPathCoinType(networkType)}'/${pathNumber}'/0'/0'`;
}

/**
Expand All @@ -57,12 +59,22 @@ export class DerivationService {
* @static
* @param {string} mnemonic
* @param {number} pathNumber
* @param {NetworkType} networkType
* @returns {string}
*/
public static getPrivateKeyFromMnemonic(mnemonic: string, pathNumber: number): string {
const path = this.getPathFromPathNumber(pathNumber);
public static getPrivateKeyFromMnemonic(mnemonic: string, pathNumber: number, networkType: NetworkType): string {
const path = this.getPathFromPathNumber(pathNumber, networkType);
const seed = new MnemonicPassPhrase(mnemonic).toSeed().toString('hex');
const extendedKey = ExtendedKey.createFromSeed(seed);
return new Wallet(extendedKey).getChildAccountPrivateKey(path);
}

/**
* Get coin type in HD path by network type
* @param networkType Symbol network type
* @returns {string}
*/
public static getPathCoinType(networkType: NetworkType) {
return networkType === NetworkType.MAIN_NET ? `4343` : `1`;
}
}
110 changes: 108 additions & 2 deletions test/models/hdProfile.model.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,12 @@ import { epochAdjustment } from '../../src/models/profile.model';

const networkCurrency = NetworkCurrency.createFromDTO({ namespaceId: 'symbol.xym', divisibility: 6 });

describe('HdProfile', () => {
describe('HdProfile MainNet', () => {
it('should create a profile from mnemonic', () => {
const generationHash = 'defaultGenerationHash';
const isDefault = true;
const name = 'default';
const networkType = NetworkType.TEST_NET;
const networkType = NetworkType.MAIN_NET;
const password = new Password('password');
const url = 'http://localhost:3000';
const mnemonic = 'test';
Expand Down Expand Up @@ -129,3 +129,109 @@ describe('HdProfile', () => {
expect(profile.toDTO().simpleWallet.encryptedPrivateKey).to.deep.equal(DTO.simpleWallet.encryptedPrivateKey);
});
});

describe('HdProfile TestNet', () => {
it('should create a profile from mnemonic', () => {
const generationHash = 'defaultGenerationHash';
const isDefault = true;
const name = 'default';
const networkType = NetworkType.TEST_NET;
const password = new Password('password');
const url = 'http://localhost:3000';
const mnemonic = 'test';
const expectedPath = "m/44'/1'/0'/0'/0'";
const pathNumber = 0;

const profile = HdProfile.create({
generationHash,
isDefault,
name,
networkCurrency,
epochAdjustment,
networkType,
password,
url,
mnemonic,
pathNumber,
});

expect(profile.networkGenerationHash).to.equal(generationHash);
expect(profile.type).to.equal('HD');
expect(profile.isDefault).to.equal('1');
expect(profile.name).to.equal(name);
expect(profile.networkType).to.equal(networkType);
expect(profile.url).to.equal(url);
expect(profile.encryptedPassphrase).to.exist;
expect(profile.pathNumber).to.equal(pathNumber);
expect(profile.path).to.equal(expectedPath);
expect(profile).instanceOf(HdProfile);
});

it('createFromDTO should instantiate an HD wallet properly', () => {
const networkType = NetworkType.TEST_NET;
const name = 'profile name';
const password = new Password('password');
const simpleWallet = SimpleWallet.createFromPrivateKey(name, password, '0'.repeat(64), networkType);
const encryptedPassphrase = 'encryptedPassphrase';
const path = "m/44'/1'/0'/0'/0'";
const url = 'http://localhost:3000';
const networkGenerationHash = 'generationHash';
const version = 3;
const isDefault = '0';
const type = 'HD';

const profile = HdProfile.createFromDTO({
simpleWallet: simpleWallet.toDTO(),
url,
networkGenerationHash,
epochAdjustment: epochAdjustment,
networkCurrency: networkCurrency.toDTO(),
version,
default: isDefault,
type,
encryptedPassphrase,
path,
});

expect(profile.simpleWallet.encryptedPrivateKey).to.deep.equal(simpleWallet.encryptedPrivateKey);
expect(profile.networkGenerationHash).to.equal(networkGenerationHash);
expect(profile.type).to.equal(type);
expect(profile.isDefault).to.equal(isDefault);
expect(profile.name).to.equal(name);
expect(profile.networkType).to.equal(networkType);
expect(profile.url).to.equal(url);
expect(profile.encryptedPassphrase).to.equal(encryptedPassphrase);
expect(profile.path).to.equal(path);
expect(profile.pathNumber).to.equal(0);
expect(profile).instanceOf(HdProfile);
});

it('should create a DTO', () => {
const DTO = {
simpleWallet: {
name: 'profile name',
network: 152,
address: {
address: 'TA4E47MGAO57ZJFORKCFSPADBMWHLX7UKMZJKAO',
networkType: 152,
},
creationDate: '2020-04-09T17:54:30.421',
schema: 'simple_v1',
encryptedPrivateKey: 'd47e526995324428257af732684420dbe718047cc8ae2fdfa7ae49f820a300c691ac558b8669d45105acc438b04df83e',
},
url: 'http://localhost:3000',
epochAdjustment: epochAdjustment,
networkGenerationHash: 'generationHash',
networkCurrency: { namespaceId: 'symbol.xym', divisibility: 6 },
version: 3,
default: '1',
type: 'HD',
encryptedPassphrase: 'encryptedPassphrase',
path: "m/44'/1'/0'/0'/0'",
};

const profile = HdProfile.createFromDTO(DTO);
expect(profile).instanceOf(HdProfile);
expect(profile.toDTO().simpleWallet.encryptedPrivateKey).to.deep.equal(DTO.simpleWallet.encryptedPrivateKey);
});
});
Loading

0 comments on commit dbfb4ca

Please sign in to comment.