on master help sign in
feat: hex inputs enforce 0x prefix and private key inputs strip it. solves #229
115c41eMatt Condon (shrugs)
src/form/Inputs.js+18−2
@@ -7,6 +7,8 @@ import {
7 buildFormatter,
7 buildFormatter,
8 downcase,
8 downcase,
9 ensurePatFormat,
9 ensurePatFormat,
10+ stripHexPrefix,
11+ ensureHexPrefix,
10} from 'form/formatters';
12} from 'form/formatters';
11import { DEFAULT_HD_PATH } from 'lib/wallet';
13import { DEFAULT_HD_PATH } from 'lib/wallet';
12import InputSigil from 'components/InputSigil';
14import InputSigil from 'components/InputSigil';
@@ -17,7 +19,9 @@ const PLACEHOLDER_MNEMONIC =
17 'example crew supreme gesture quantum web media hazard theory mercy wing kitten';
19 'example crew supreme gesture quantum web media hazard theory mercy wing kitten';
18const PLACEHOLDER_TICKET = '~sampel-ticlyt-migfun-falmel';
20const PLACEHOLDER_TICKET = '~sampel-ticlyt-migfun-falmel';
19const PLACEHOLDER_ADDRESS = '0x';
21const PLACEHOLDER_ADDRESS = '0x';
22+const PLACEHOLDER_HEX = '0x';
20−const PLACEHOLDER_PRIVATE_KEY = '0x';
23+const PLACEHOLDER_PRIVATE_KEY =
24+ 'a44de2416ee6beb2f323fab48b432925c9785808d33a6ca6d7ba00b45e9370c3';
21const PLACEHOLDER_EMAIL = 'email@example.com';
25const PLACEHOLDER_EMAIL = 'email@example.com';
22
26
23const formatPat = buildFormatter([downcase, ensurePatFormat]);
27const formatPat = buildFormatter([downcase, ensurePatFormat]);
@@ -136,11 +140,23 @@ export function HexInput({ ...rest }) {
136 return (
140 return (
137 <Input
141 <Input
138 type="text"
142 type="text"
139− placeholder={PLACEHOLDER_PRIVATE_KEY}
143+ placeholder={PLACEHOLDER_HEX}
140 autoCapitalize="none"
144 autoCapitalize="none"
141 autoComplete="off"
145 autoComplete="off"
142 autoCorrect="off"
146 autoCorrect="off"
143 mono
147 mono
148+ config={{ parse: ensureHexPrefix }}
149+ {...rest}
150+ />
151+ );
152+}
153+
154+export function PrivateKeyInput({ ...rest }) {
155+ return (
156+ <HexInput
157+ type="password"
158+ placeholder={PLACEHOLDER_PRIVATE_KEY}
159+ config={{ parse: stripHexPrefix }}
144 {...rest}
160 {...rest}
145 />
161 />
146 );
162 );
src/form/formatters.js+18−7
@@ -1,5 +1,6 @@
1import { compose, strSplice } from 'lib/lib';
1import { compose, strSplice } from 'lib/lib';
2
2
3+const HEX_PREFIX = '0x';
3const TICKET_MAX_BYTE_LEN = 32; // tickets can be as large as 32 bytes
4const TICKET_MAX_BYTE_LEN = 32; // tickets can be as large as 32 bytes
4const PAT_BLOCK_CHAR_LENGTH = 6; // pat{p,q} blocks are 6 characters long
5const PAT_BLOCK_CHAR_LENGTH = 6; // pat{p,q} blocks are 6 characters long
5
6
@@ -38,6 +39,14 @@ export const ensurePatFormat = s => {
38 return s;
39 return s;
39};
40};
40
41
42+export const ensureHexPrefix = s => {
43+ if (!s || s.length < HEX_PREFIX.length) {
44+ return s || '';
45+ }
46+
47+ return s.indexOf(HEX_PREFIX) !== 0 ? `${HEX_PREFIX}${s}` : s;
48+};
49+
41export const convertToNumber = s => {
50export const convertToNumber = s => {
42 try {
51 try {
43 return parseInt(s, 10);
52 return parseInt(s, 10);
@@ -54,10 +63,12 @@ export const downcase = s => {
54 return s.toLowerCase();
63 return s.toLowerCase();
55};
64};
56
65
57−// const hideAllButLast = s => {
66+export const stripHexPrefix = s => {
58−// const ll = s[s.length -1];
67+ if (!s) {
59−// const bs = fill(s.length - 1, '•')
68+ return s;
60−// return `${bs}${ll}`
69+ }
61−// };
70+
62−//
71+ return s.toLowerCase().indexOf(HEX_PREFIX) === 0
63−// const hideAll = s => fill(s.length, '•');
72+ ? s.slice(HEX_PREFIX.length)
73+ : s;
74+};
src/form/validators.js+14−1
@@ -13,9 +13,14 @@ import {
13 validateGreaterThan,
13 validateGreaterThan,
14 validateEmail,
14 validateEmail,
15 validateNotNullAddress,
15 validateNotNullAddress,
16+ validateHexPrefix,
17+ validateLength,
16} from 'lib/validators';
18} from 'lib/validators';
17import isPromise from 'lib/isPromise';
19import isPromise from 'lib/isPromise';
18
20
21+const PRIVATE_KEY_CHAR_LENGTH = 64; // 64 hex characters
22+const ADDRESS_CHAR_LENGTH = 40; // 40 hex characters
23+
19// iterate over validators, exiting early if there's an error
24// iterate over validators, exiting early if there's an error
20const buildValidator = (validators = [], validate) => value => {
25const buildValidator = (validators = [], validate) => value => {
21 for (const validator of validators) {
26 for (const validator of validators) {
@@ -75,15 +80,23 @@ export const buildSelectValidator = options =>
75export const buildHexValidator = length =>
80export const buildHexValidator = length =>
76 buildValidator([
81 buildValidator([
77 validateNotEmpty,
82 validateNotEmpty,
83+ validateHexPrefix,
78 validateHexString,
84 validateHexString,
79 validateHexLength(length),
85 validateHexLength(length),
80 ]);
86 ]);
87+export const buildPrivateKeyValidator = () =>
88+ buildValidator([
89+ validateNotEmpty,
90+ validateHexString,
91+ validateLength(PRIVATE_KEY_CHAR_LENGTH),
92+ ]);
81export const buildUploadValidator = () => buildValidator([validateNotEmpty]);
93export const buildUploadValidator = () => buildValidator([validateNotEmpty]);
82export const buildAddressValidator = () =>
94export const buildAddressValidator = () =>
83 buildValidator([
95 buildValidator([
84 validateNotEmpty,
96 validateNotEmpty,
97+ validateHexPrefix,
85 validateHexString,
98 validateHexString,
86− validateHexLength(40),
99+ validateHexLength(ADDRESS_CHAR_LENGTH),
87 validateNotNullAddress,
100 validateNotNullAddress,
88 validateEthereumAddress,
101 validateEthereumAddress,
89 ]);
102 ]);
src/lib/validators.js+8−2
@@ -14,8 +14,11 @@ import { MIN_GALAXY, MAX_GALAXY } from './constants';
14// via: https://emailregex.com/
14// via: https://emailregex.com/
15const emailRegExp = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
15const emailRegExp = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
16
16
17−// is this only hex values with a 0x prefix?
17+// validate only strings that use hex characters
18−const isHexString = /0x[0-9A-Fa-f]/;
18+const isHexString = /[0-9A-Fa-f]/;
19+
20+// validate only strings that are prefixed with 0x
21+const isHexPrefixed = /^0x/;
19
22
20// Validates a bip39 mnemonic
23// Validates a bip39 mnemonic
21export const validateMnemonic = v =>
24export const validateMnemonic = v =>
@@ -64,6 +67,9 @@ export const validateOneOf = (options = []) => v =>
64export const validateHexString = v =>
67export const validateHexString = v =>
65 !isHexString.test(v) && 'This is not a valid hex string.';
68 !isHexString.test(v) && 'This is not a valid hex string.';
66
69
70+export const validateHexPrefix = v =>
71+ !isHexPrefixed.test(v) && 'Must include 0x prefix.';
72+
67export const validateEthereumAddress = v =>
73export const validateEthereumAddress = v =>
68 !isValidAddress(v) && 'This is not a valid Ethereum address.';
74 !isValidAddress(v) && 'This is not a valid Ethereum address.';
69
75
src/views/Login/PrivateKey.js+4−4
@@ -9,8 +9,8 @@ import useLoginView from 'lib/useLoginView';
9
9
10import FormError from 'form/FormError';
10import FormError from 'form/FormError';
11import BridgeForm from 'form/BridgeForm';
11import BridgeForm from 'form/BridgeForm';
12−import { HexInput } from 'form/Inputs';
12+import { PrivateKeyInput } from 'form/Inputs';
13−import { buildHexValidator, composeValidator } from 'form/validators';
13+import { composeValidator, buildPrivateKeyValidator } from 'form/validators';
14
14
15import SubmitButton from 'form/SubmitButton';
15import SubmitButton from 'form/SubmitButton';
16
16
@@ -22,7 +22,7 @@ export default function PrivateKey({ className, goHome }) {
22 const validate = useMemo(
22 const validate = useMemo(
23 () =>
23 () =>
24 composeValidator({
24 composeValidator({
25− privateKey: buildHexValidator(64),
25+ privateKey: buildPrivateKeyValidator(64),
26 }),
26 }),
27 []
27 []
28 );
28 );
@@ -47,7 +47,7 @@ export default function PrivateKey({ className, goHome }) {
47 <>
47 <>
48 <Grid.Item
48 <Grid.Item
49 full
49 full
50− as={HexInput}
50+ as={PrivateKeyInput}
51 name="privateKey"
51 name="privateKey"
52 label="Private key"
52 label="Private key"
53 />
53 />