Add regex for BIP44 Strings (#1035)
* Add regex check for BIP44 paths * Add tests for BIP44 regex * Remove '/config' from excluded paths in jest * update path * Update Validator tests & remove old dPathRegex tests * Test hardcoded dpaths
This commit is contained in:
parent
b3da25c3f7
commit
81d4444dff
|
@ -15,7 +15,7 @@ export const ETH_LEDGER: DPath = {
|
|||
|
||||
export const ETC_LEDGER: DPath = {
|
||||
label: 'Ledger (ETC)',
|
||||
value: "m/44'/60'/160720'/0'"
|
||||
value: "m/44'/60'/160720'/0"
|
||||
};
|
||||
|
||||
export const ETC_TREZOR: DPath = {
|
||||
|
@ -43,5 +43,30 @@ export const ETH_SINGULAR: DPath = {
|
|||
value: "m/0'/0'/0'"
|
||||
};
|
||||
|
||||
export const DPaths: DPath[] = [
|
||||
ETH_DEFAULT,
|
||||
ETH_TREZOR,
|
||||
ETH_LEDGER,
|
||||
ETC_LEDGER,
|
||||
ETC_TREZOR,
|
||||
ETH_TESTNET,
|
||||
EXP_DEFAULT,
|
||||
UBQ_DEFAULT
|
||||
];
|
||||
|
||||
// PATHS TO BE INCLUDED REGARDLESS OF WALLET FORMAT
|
||||
export const EXTRA_PATHS = [ETH_SINGULAR];
|
||||
|
||||
// Full length deterministic wallet paths from BIP44
|
||||
// https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki
|
||||
// normal path length is 4, ledger is the exception at 3
|
||||
|
||||
// m / purpose' / coin_type' / account' / change / address_index
|
||||
// | | | | |
|
||||
// | constant | index | index | 0 or 1 |
|
||||
// |__________|____________|__________|________|
|
||||
|
||||
// whitespace strings are evaluated the same way as nospace strings, except they allow optional spaces between each portion of the string
|
||||
// ie. "m / 44' / 0' / 0'" is valid, "m / 4 4' / 0' / 0'" is invalid
|
||||
export const dPathRegex = /m\/44'\/[0-9]+\'\/[0-9]+(\'+$|\'+(\/[0-1]+$))/;
|
||||
// export const whitespaceDPathRegex = /m\s*\/\s*44'\s*\/\s*[0-9]+\'\s*\/\s*[0-9]+(\'+$|\'+\s*(\/\s*[0-1]+$))/;
|
||||
|
|
|
@ -2,3 +2,4 @@ export * from './data';
|
|||
export * from './bity';
|
||||
export * from './addressMessages';
|
||||
export * from './helpArticles';
|
||||
export * from './dpaths';
|
||||
|
|
|
@ -11,6 +11,7 @@ import {
|
|||
GAS_PRICE_GWEI_LOWER_BOUND,
|
||||
GAS_PRICE_GWEI_UPPER_BOUND
|
||||
} from 'config/constants';
|
||||
import { dPathRegex } from 'config/dpaths';
|
||||
|
||||
// FIXME we probably want to do checksum checks sideways
|
||||
export function isValidETHAddress(address: string): boolean {
|
||||
|
@ -121,13 +122,8 @@ export function isPositiveIntegerOrZero(num: number): boolean {
|
|||
return num >= 0 && parseInt(num.toString(), 10) === num;
|
||||
}
|
||||
|
||||
// Full length deterministic wallet paths from BIP44
|
||||
// https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki
|
||||
// normal path length is 4, ledger is the exception at 3
|
||||
export function isValidPath(dPath: string) {
|
||||
// TODO: use a regex to detect proper paths
|
||||
const len = dPath.split("'/").length;
|
||||
return len === 3 || len === 4;
|
||||
return dPathRegex.test(dPath);
|
||||
}
|
||||
|
||||
export const isValidValue = (value: string) =>
|
||||
|
|
|
@ -10,9 +10,9 @@
|
|||
"\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$":
|
||||
"<rootDir>/jest_config/__mocks__/fileMock.ts",
|
||||
"\\.(css|scss|less)$": "<rootDir>/jest_config/__mocks__/styleMock.ts",
|
||||
"\\.worker.ts":"<rootDir>/jest_config/__mocks__/workerMock.js"
|
||||
"\\.worker.ts": "<rootDir>/jest_config/__mocks__/workerMock.js"
|
||||
},
|
||||
"testPathIgnorePatterns": ["<rootDir>/common/config"],
|
||||
"testPathIgnorePatterns": [],
|
||||
"setupFiles": [
|
||||
"<rootDir>/jest_config/setupJest.js",
|
||||
"<rootDir>/jest_config/__mocks__/localStorage.ts"
|
||||
|
|
|
@ -8,10 +8,10 @@
|
|||
"moduleFileExtensions": ["ts", "tsx", "js", "jsx", "json"],
|
||||
"moduleNameMapper": {
|
||||
"\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$":
|
||||
"<rootDir>/jest_config/__mocks__/fileMock.ts",
|
||||
"<rootDir>/jest_config/__mocks__/fileMock.ts",
|
||||
"\\.(css|scss|less)$": "<rootDir>/jest_config/__mocks__/styleMock.ts"
|
||||
},
|
||||
"testPathIgnorePatterns": ["<rootDir>/common/config"],
|
||||
"testPathIgnorePatterns": [],
|
||||
"setupFiles": [
|
||||
"<rootDir>/jest_config/setupJest.js",
|
||||
"<rootDir>/jest_config/__mocks__/localStorage.ts"
|
||||
|
|
|
@ -4,6 +4,8 @@ import {
|
|||
isValidPath,
|
||||
isValidPrivKey
|
||||
} from '../../common/libs/validators';
|
||||
import { DPaths } from 'config/dpaths';
|
||||
import { valid, invalid } from '../utils/testStrings';
|
||||
|
||||
const VALID_BTC_ADDRESS = '1MEWT2SGbqtz6mPCgFcnea8XmWV5Z4Wc6';
|
||||
const VALID_ETH_ADDRESS = '0x7cB57B5A97eAbe94205C07890BE4c1aD31E486A8';
|
||||
|
@ -26,9 +28,6 @@ describe('Validator', () => {
|
|||
it('should validate incorrect ETH address as false', () => {
|
||||
expect(isValidETHAddress('nonsense' + VALID_ETH_ADDRESS + 'nonsense')).toBeFalsy();
|
||||
});
|
||||
it('should validate a correct DPath as true', () => {
|
||||
expect(isValidPath("m/44'/60'/0'/0")).toBeTruthy();
|
||||
});
|
||||
it('should validate an incorrect DPath as false', () => {
|
||||
expect(isValidPath('m/44/60/0/0')).toBeFalsy();
|
||||
});
|
||||
|
@ -45,3 +44,21 @@ describe('Validator', () => {
|
|||
expect(isValidPrivKey(VALID_ETH_PRIVATE_BUFFER)).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Validator', () => {
|
||||
it('should validate correct DPaths as true', () => {
|
||||
valid.forEach(path => {
|
||||
expect(isValidPath(path)).toBeTruthy();
|
||||
});
|
||||
});
|
||||
it('should validate incorrect DPaths as false', () => {
|
||||
invalid.forEach(path => {
|
||||
expect(isValidPath(path)).toBeFalsy();
|
||||
});
|
||||
});
|
||||
it('should validate hardcoded DPaths as true', () => {
|
||||
DPaths.forEach(DPath => {
|
||||
expect(isValidPath(DPath.value)).toBeTruthy();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -0,0 +1,107 @@
|
|||
// Deterministic Wallet Path test strings
|
||||
// https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki
|
||||
|
||||
// m / purpose' / coin_type' / account' / change / address_index
|
||||
const length5 = [
|
||||
"m/44'/0'/0'/0/0",
|
||||
"m/44'/0'/0'/0/1",
|
||||
"m/44'/0'/0'/1/0",
|
||||
"m/44'/0'/0'/1/1",
|
||||
"m/44'/0'/1'/0/0",
|
||||
"m/44'/0'/1'/0/1",
|
||||
"m/44'/0'/1'/1/0",
|
||||
"m/44'/0'/1'/1/1",
|
||||
"m/44'/1'/0'/0/0",
|
||||
"m/44'/1'/0'/0/1",
|
||||
"m/44'/1'/0'/1/0",
|
||||
"m/44'/1'/0'/1/1",
|
||||
"m/44'/1'/1'/0/0",
|
||||
"m/44'/1'/1'/0/1",
|
||||
"m/44'/1'/1'/1/0",
|
||||
"m/44'/1'/1'/1/1"
|
||||
];
|
||||
|
||||
const wsLength5 = [
|
||||
"m / 44' / 0' / 0' / 0 / 0",
|
||||
"m / 44' / 0' / 0' / 0 / 1",
|
||||
"m / 44' / 0' / 0' / 1 / 0",
|
||||
"m / 44' / 0' / 0' / 1 / 1",
|
||||
"m / 44' / 0' / 1' / 0 / 0",
|
||||
"m / 44' / 0' / 1' / 0 / 1",
|
||||
"m / 44' / 0' / 1' / 1 / 0",
|
||||
"m / 44' / 0' / 1' / 1 / 1",
|
||||
"m / 44' / 1' / 0' / 0 / 0",
|
||||
"m / 44' / 1' / 0' / 0 / 1",
|
||||
"m / 44' / 1' / 0' / 1 / 0",
|
||||
"m / 44' / 1' / 0' / 1 / 1",
|
||||
"m / 44' / 1' / 1' / 0 / 0",
|
||||
"m / 44' / 1' / 1' / 0 / 1",
|
||||
"m / 44' / 1' / 1' / 1 / 0",
|
||||
"m / 44' / 1' / 1' / 1 / 1"
|
||||
];
|
||||
|
||||
// m / purpose' / coin_type' / account' / change
|
||||
const length4 = [
|
||||
"m/44'/0'/0'/0",
|
||||
"m/44'/0'/0'/1",
|
||||
"m/44'/0'/1'/0",
|
||||
"m/44'/0'/1'/1",
|
||||
"m/44'/1'/0'/0",
|
||||
"m/44'/1'/0'/1",
|
||||
"m/44'/1'/1'/0",
|
||||
"m/44'/1'/1'/1"
|
||||
];
|
||||
|
||||
const wsLength4 = [
|
||||
"m / 44' / 0' / 0' / 0",
|
||||
"m / 44' / 0' / 0' / 1",
|
||||
"m / 44' / 0' / 1' / 0",
|
||||
"m / 44' / 0' / 1' / 1",
|
||||
"m / 44' / 1' / 0' / 0",
|
||||
"m / 44' / 1' / 0' / 1",
|
||||
"m / 44' / 1' / 1' / 0",
|
||||
"m / 44' / 1' / 1' / 1"
|
||||
];
|
||||
|
||||
// m / purpose' / coin_type' / account'
|
||||
const length3 = ["m/44'/0'/0'", "m/44'/0'/1'", "m/44'/1'/0'", "m/44'/1'/1'"];
|
||||
|
||||
const wsLength3 = [
|
||||
"m / 44' / 0' / 0'",
|
||||
"m / 44' / 0' / 1'",
|
||||
"m / 44' / 1' / 0'",
|
||||
"m / 44' / 1' / 1'"
|
||||
];
|
||||
|
||||
// 'coin_type' accepts an index (0 - infinity)
|
||||
// https://github.com/satoshilabs/slips/blob/master/slip-0044.md
|
||||
// m / purpose' / coin_type' / account' / change
|
||||
const validCoinType = ["m/44'/3'/0'/0", "m/44'/60'/0'/0", "m/44'/37310'/0'/0"];
|
||||
const invalidCoinType = ["m/44'/-1'/0'/0", "m/44'/3.2'/0'/0", "m/44'/twenty'/0'/0"];
|
||||
|
||||
// 'account' accepts an index (0 - infinity)
|
||||
// m / purpose' / coin_type' / account' / change
|
||||
const validAccount = ["m/44'/0'/3'/0", "m/44'/0'/30'/0", "m/44'/0'/97878'/0"];
|
||||
const invalidAccount = ["m/44'/0'/-1'/0", "m/44'/0'/a'/0", "m/44'/0'/9.3'/0"];
|
||||
|
||||
// 'change' accepts a boolean (0 - 1)
|
||||
// m / purpose' / coin_type' / account' / change
|
||||
const validChange = ["m/44'/0'/0'/1", "m/44'/0'/0'/0"];
|
||||
const inValidChange = ["m/44'/0'/0'/3", "m/44'/0'/0'/-1", "m/44'/0'/0'/0.5", "m/44'/0'/0'/a"];
|
||||
|
||||
const validPaths = [...validCoinType, ...validAccount, ...validChange];
|
||||
const inValidPaths = [
|
||||
...length5,
|
||||
...wsLength5,
|
||||
...invalidCoinType,
|
||||
...invalidAccount,
|
||||
...inValidChange
|
||||
];
|
||||
|
||||
export const valid = [...length4, ...length3, ...validPaths];
|
||||
export const invalid = [...wsLength4, ...wsLength3, ...inValidPaths];
|
||||
|
||||
// whitespace strings are evaluated the same way as nospace strings, except they allow optional spaces between each portion of the string
|
||||
// ie. "m / 44' / 0' / 0'" is valid, "m / 4 4' / 0' / 0'" is invalid
|
||||
export const whitespaceValid = [...wsLength4, ...wsLength3, ...valid];
|
||||
export const whitespaceInvalid = [...invalid];
|
Loading…
Reference in New Issue