Skip to content

Commit

Permalink
New import/export file. Rename hash to genesis (#1877)
Browse files Browse the repository at this point in the history
* fix: rename hash to genesis

* Tests for DP text parser (#1878)

* feat: adding tests for text parser

* fix: linter

* fix: adding more cases

* fix: add test for multiple derivation path

* fix: add test for multiple dp in one genesis

* fix: soft \ hard naming

* fix: import files (#1880)

---------

Co-authored-by: Anastasiia Sokolova <[email protected]>

---------

Co-authored-by: Anastasiia Sokolova <[email protected]>
  • Loading branch information
stepanLav and sokolova-an authored Jun 19, 2024
1 parent 055aaae commit c932a4a
Show file tree
Hide file tree
Showing 3 changed files with 195 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ function getExportStructure(rootAccountId: AccountId, accounts: Array<ChainAccou
const chainId = Array.isArray(account) ? account[0].chainId : account.chainId;
if (!set.has(chainId)) {
set.add(chainId);
output += `hash: ${chainId}\n`;
output += `genesis: ${chainId}\n`;
}
output += accountToDerivationExport(account);
});
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
import { importKeysUtils } from '../import-keys-utils';

describe('entities/dynamicDerivations/import-keys-utils/parseTextFile', () => {
test('should return null for invalid version', () => {
const fileContent = `version: 2\npublic address: 0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef`;
expect(importKeysUtils.parseTextFile(fileContent)).toBeNull();
});

test('should return null for invalid public address', () => {
const fileContent = `version: 1\npublic address: invalid_address`;
expect(importKeysUtils.parseTextFile(fileContent)).toBeNull();
});

test('should return null for missing derivation paths', () => {
const fileContent = `version: 1\npublic address: 0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef`;
expect(importKeysUtils.parseTextFile(fileContent)).toBeNull();
});

test('should parse text file with regular path', () => {
const fileContent = `version: 1\npublic address: 0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef\ngenesis: 0x91b171bb158e2d3848fa23a9f1c25182fb8e20313b2c1eb49219da7a70ce90c3\n//path: name [type]`;
const parsedData = importKeysUtils.parseTextFile(fileContent);
expect(parsedData).toEqual({
version: '1',
publicAddress: '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef',
derivationPaths: [
{
derivationPath: '//path',
name: 'name',
type: 'type',
chainId: '0x91b171bb158e2d3848fa23a9f1c25182fb8e20313b2c1eb49219da7a70ce90c3',
},
],
});
});

test('should parse text file with soft and hard path', () => {
const fileContent = `version: 1\npublic address: 0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef\ngenesis: 0x91b171bb158e2d3848fa23a9f1c25182fb8e20313b2c1eb49219da7a70ce90c3\n//hard/soft//hard/soft: name [type]`;
const parsedData = importKeysUtils.parseTextFile(fileContent);
expect(parsedData).toEqual({
version: '1',
publicAddress: '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef',
derivationPaths: [
{
derivationPath: '//hard/soft//hard/soft',
name: 'name',
type: 'type',
chainId: '0x91b171bb158e2d3848fa23a9f1c25182fb8e20313b2c1eb49219da7a70ce90c3',
},
],
});
});

test('should parse text file with soft path and shards', () => {
const fileContent = `version: 1\npublic address: 0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef\ngenesis: 0x91b171bb158e2d3848fa23a9f1c25182fb8e20313b2c1eb49219da7a70ce90c3\n/soft_path//0...10: name [type]`;
const parsedData = importKeysUtils.parseTextFile(fileContent);
expect(parsedData).toEqual({
version: '1',
publicAddress: '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef',
derivationPaths: [
{
derivationPath: '/soft_path',
sharded: '10',
name: 'name',
type: 'type',
chainId: '0x91b171bb158e2d3848fa23a9f1c25182fb8e20313b2c1eb49219da7a70ce90c3',
},
],
});
});

test('should parse text file with hard path and shards', () => {
const fileContent = `version: 1\npublic address: 0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef\ngenesis: 0x91b171bb158e2d3848fa23a9f1c25182fb8e20313b2c1eb49219da7a70ce90c3\n//hard_path//0...10: name [type]`;
const parsedData = importKeysUtils.parseTextFile(fileContent);
expect(parsedData).toEqual({
version: '1',
publicAddress: '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef',
derivationPaths: [
{
derivationPath: '//hard_path',
sharded: '10',
name: 'name',
type: 'type',
chainId: '0x91b171bb158e2d3848fa23a9f1c25182fb8e20313b2c1eb49219da7a70ce90c3',
},
],
});
});

test('should parse text file with sharded keys and different derivations', () => {
const fileContent = `version: 1\npublic address: 0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef\ngenesis: 0x91b171bb158e2d3848fa23a9f1c25182fb8e20313b2c1eb49219da7a70ce90c3\n//hard/soft//hard//0...10: name [type]`;
const parsedData = importKeysUtils.parseTextFile(fileContent);
expect(parsedData).toEqual({
version: '1',
publicAddress: '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef',
derivationPaths: [
{
derivationPath: '//hard/soft//hard',
sharded: '10',
name: 'name',
type: 'type',
chainId: '0x91b171bb158e2d3848fa23a9f1c25182fb8e20313b2c1eb49219da7a70ce90c3',
},
],
});
});

test('should parse text file with difficult symbols in dp', () => {
const fileContent = `version: 1\npublic address: 0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef\ngenesis: 0x91b171bb158e2d3848fa23a9f1c25182fb8e20313b2c1eb49219da7a70ce90c3\n//polkadot//~!@#$%^&*(*)_+QWE'1234567890-=//0...10: name [type]`;
const parsedData = importKeysUtils.parseTextFile(fileContent);
expect(parsedData).toEqual({
version: '1',
publicAddress: '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef',
derivationPaths: [
{
derivationPath: `//polkadot//~!@#$%^&*(*)_+QWE'1234567890-=`,
sharded: '10',
name: 'name',
type: 'type',
chainId: '0x91b171bb158e2d3848fa23a9f1c25182fb8e20313b2c1eb49219da7a70ce90c3',
},
],
});
});

test('should parse text file with multiple genesis and derivation paths', () => {
const fileContent = `version: 1\npublic address: 0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef\ngenesis: 0x91b171bb158e2d3848fa23a9f1c25182fb8e20313b2c1eb49219da7a70ce90c3\n//path1: name1 [type1]\ngenesis: 0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef\n//path2: name2 [type2]`;
const parsedData = importKeysUtils.parseTextFile(fileContent);
expect(parsedData).toEqual({
version: '1',
publicAddress: '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef',
derivationPaths: [
{
derivationPath: '//path1',
name: 'name1',
type: 'type1',
chainId: '0x91b171bb158e2d3848fa23a9f1c25182fb8e20313b2c1eb49219da7a70ce90c3',
},
{
derivationPath: '//path2',
name: 'name2',
type: 'type2',
chainId: '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef',
},
],
});
});

test('should parse text file with multiple derivation paths for one genesis', () => {
const fileContent = `version: 1\npublic address: 0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef\ngenesis: 0x91b171bb158e2d3848fa23a9f1c25182fb8e20313b2c1eb49219da7a70ce90c3\n//path1: name1 [type1]\n//path2: name2 [type2]`;
const parsedData = importKeysUtils.parseTextFile(fileContent);
expect(parsedData).toEqual({
version: '1',
publicAddress: '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef',
derivationPaths: [
{
derivationPath: '//path1',
name: 'name1',
type: 'type1',
chainId: '0x91b171bb158e2d3848fa23a9f1c25182fb8e20313b2c1eb49219da7a70ce90c3',
},
{
derivationPath: '//path2',
name: 'name2',
type: 'type2',
chainId: '0x91b171bb158e2d3848fa23a9f1c25182fb8e20313b2c1eb49219da7a70ce90c3',
},
],
});
});
});
34 changes: 24 additions & 10 deletions src/renderer/features/wallets/ImportKeys/lib/import-keys-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,22 @@ function isFileStructureValid(result: any): result is ParsedImportFile {
});
}

function processString(str: string) {
const parts = str.split('//');
const lastPart = parts[parts.length - 1];
const shardPattern = /^0\.\.\.(\d+)$/;

const match = lastPart.match(shardPattern);
if (match) {
const shard = match[1];
const processedString = parts.slice(0, -1).join('//');

return { path: processedString, shard };
} else {
return { path: str };
}
}

function parseTextFile(fileContent: string): ParsedData | null {
const lines = fileContent
.split('\n')
Expand All @@ -87,7 +103,7 @@ function parseTextFile(fileContent: string): ParsedData | null {
const derivationPaths = [];
for (let i = 2; i < lines.length; i++) {
const line = lines[i];
const chainIdMatch = line.match(/^hash: (0x[a-fA-F0-9]{64})$/);
const chainIdMatch = line.match(/^genesis: (0x[a-fA-F0-9]{64})$/);
if (chainIdMatch) {
const chainId = chainIdMatch[1];
if (!chainId.startsWith('0x')) {
Expand All @@ -96,18 +112,16 @@ function parseTextFile(fileContent: string): ParsedData | null {
currentChainId = chainIdMatch[1];
}

const derivationPathMatch = line.match(/^(\/\/[^\s:]*):\s*([^[]+?)\s*\[([^\]]+)\]$/);
// To match paths with sharded keys correctly
const derivationPathShardedMatch = line.match(
/^(\/\/[^\s:/]*\/\/[^\s:/]*)(?:\/\/0\.\.\.(\d+))?:\s*([^[]+?)\s*\[([^\]]+)\]$/,
);
const derivationPathMatch = line.match(/^(\/{1,2}[^\s:]*):\s*([^[]+?)\s*\[([^\]]+)\]$/);

if (derivationPathMatch) {
const { path, shard } = processString(derivationPathMatch[1]);

const derivationPathParams = {
derivationPath: derivationPathShardedMatch?.[1] || derivationPathMatch[1],
sharded: derivationPathShardedMatch?.[2],
name: derivationPathShardedMatch?.[3] || derivationPathMatch[2],
type: (derivationPathShardedMatch?.[4] || derivationPathMatch[3]) as KeyType,
derivationPath: path,
sharded: shard,
name: derivationPathMatch[2],
type: derivationPathMatch[3] as KeyType,
chainId: currentChainId,
};
derivationPaths.push(derivationPathParams);
Expand Down

0 comments on commit c932a4a

Please sign in to comment.