on 21d1c95 help sign in
bridge/src/lib/validators.js
289 lines 7.0 kB history raw
1import * as bip39 from 'bip39';
2import * as ob from 'urbit-ob';
3import { identity, includes } from 'lodash';
5import { isValidAddress, ETH_ZERO_ADDR, ETH_ZERO_ADDR_SHORT } from './wallet';
6import patp2dec from './patp2dec';
7import { patpStringLength } from './lib';
8import { MIN_GALAXY, MAX_GALAXY } from './constants';
10// NOTE: do not use the /g modifier for these regexes
11// https://stackoverflow.com/a/21373261
12// https://stackoverflow.com/a/1520853
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,}))$/;
17// is this 64 hex chars?
18const plain64CharHexValue = /[0-9A-Fa-f]{64}/;
20// is this only hex values with a 0x prefix?
21const isHexString = /0x[0-9A-Fa-f]/;
23// Wraps single validation functions in a controlled and predictable way.
24export const simpleValidatorWrapper = ({
25 prevMessage,
26 transform = identity,
27 validator,
28 error,
29}) => {
30 // If a previous validation has already failed, skip this validation and
31 // return the prev message to the next stage in the validation function chain.
32 // Failed validations should drop all the way down the chain and drop out of
33 // the output.
34 if (!prevMessage.pass) {
35 return prevMessage;
36 }
38 // Run the validator and return the result.
39 const pass = validator(prevMessage.data);
40 const data = pass ? transform(prevMessage.data) : prevMessage.data;
42 return newMessage(data, pass, !pass && error);
43};
45// Validation message
46// {
47// data: ...
48// pass: ...
49// error: ...
50// }
51// Creates a new validation message in a uniform way.
52const newMessage = (data, pass, error) => ({
53 // The input data
54 data,
55 // Has the data passed validation?
56 pass,
57 // If data has failed a validator, the error message goes here.
58 error,
59});
61// A validator that always passes.
62export const kDefaultValidator = data => newMessage(data, true, null);
64// Validates a bip39 mnemonic
65export const validateMnemonic = m =>
66 simpleValidatorWrapper({
67 prevMessage: m,
68 validator: d => bip39.validateMnemonic(d),
69 error: 'This is not a valid mnemonic.',
70 });
72// Checks an empty field
73export const validateNotEmpty = m =>
74 simpleValidatorWrapper({
75 prevMessage: m,
76 validator: d => {
77 try {
78 return d.length > 0;
79 } catch {
80 return false;
81 }
82 },
83 error: 'This field is required.',
84 });
86// Checks if a patp is a valid galaxy
87export const validateGalaxy = m =>
88 simpleValidatorWrapper({
89 prevMessage: m,
90 validator: d => {
91 let point;
92 try {
93 point = patp2dec(d);
94 return point >= MIN_GALAXY && point <= MAX_GALAXY;
95 } catch (e) {
96 return false;
97 }
98 },
99 error: 'This is not a valid galaxy.',
100 });
102export const validatePoint = m =>
103 simpleValidatorWrapper({
104 prevMessage: m,
105 validator: d => {
106 try {
107 return ob.isValidPatp(d);
108 } catch (e) {
109 return false;
110 }
111 },
112 error: 'This is not a valid point.',
113 });
115export const validateTicket = m =>
116 simpleValidatorWrapper({
117 prevMessage: m,
118 validator: d => {
119 try {
120 return ob.isValidPatq(d);
121 } catch (e) {
122 return false;
123 }
124 },
125 error: 'This is not a valid ticket.',
126 });
128export const validateShard = m =>
129 simpleValidatorWrapper({
130 prevMessage: m,
131 validator: d => {
132 try {
133 return ob.isValidPatq(d);
134 } catch (e) {
135 return false;
136 }
137 },
138 error: 'This is not a valid shard.',
139 });
141export const validateOneOf = (options = []) => m =>
142 simpleValidatorWrapper({
143 prevMessage: m,
144 validator: d => includes(options, d),
145 error: `Is not a valid option.`,
146 });
148export const validateHexString = m =>
149 simpleValidatorWrapper({
150 prevMessage: m,
151 validator: d => isHexString.test(d),
152 error: 'This is not a valid hex string.',
153 });
155// @deprecate
156export const validateNetworkKey = m =>
157 simpleValidatorWrapper({
158 prevMessage: m,
159 validator: d => plain64CharHexValue.test(d),
160 error: 'This is not a valid network key.',
161 });
163// @deprecate
164export const validateNetworkSeed = m =>
165 simpleValidatorWrapper({
166 prevMessage: m,
167 validator: d => plain64CharHexValue.test(d),
168 error: 'This is not a valid network seed.',
169 });
171// Checks if a string is a valid ethereum address
172export const validateEthereumAddress = m =>
173 simpleValidatorWrapper({
174 prevMessage: m,
175 validator: d => isValidAddress(d),
176 error: 'This is not a valid Ethereum address.',
177 });
179export const validateEmail = m =>
180 simpleValidatorWrapper({
181 prevMessage: m,
182 validator: d => emailRegExp.test(d),
183 error: 'This is not a valid email address.',
184 });
186export const validateInt = m =>
187 simpleValidatorWrapper({
188 prevMessage: m,
189 transform: d => parseInt(d, 10),
190 validator: d => {
191 try {
192 parseInt(d, 10);
193 return;
194 } catch {
195 return false;
196 }
197 },
198 error: 'This is not a valid number.',
199 });
201export const validateExactly = (value, error) => m =>
202 simpleValidatorWrapper({
203 prevMessage: m,
204 validator: d => d === value,
205 error,
206 });
208export const validateNotAny = (values = []) => m =>
209 simpleValidatorWrapper({
210 prevMessage: m,
211 validator: d => !values.includes(d),
212 error: `Cannot be ${m.data}.`,
213 });
215export const validateLength = l => m =>
216 simpleValidatorWrapper({
217 prevMessage: m,
218 validator: d => {
219 try {
220 return d.length === l;
221 } catch {
222 return false;
223 }
224 },
225 error: `Must be exactly ${l} characters.`,
226 });
228export const validateHexLength = l => m =>
229 simpleValidatorWrapper({
230 prevMessage: m,
231 validator: d => {
232 try {
233 return d.length === l + 2;
234 } catch {
235 return false;
236 }
237 },
238 error: `Must be exactly ${l} hex characters.`,
239 });
241export const validateMaximumLength = l => m =>
242 simpleValidatorWrapper({
243 prevMessage: m,
244 validator: d => {
245 try {
246 return d.length <= l;
247 } catch {
248 return false;
249 }
250 },
251 error: `Must be ${l} characters or fewer.`,
252 });
254export const validateGreaterThan = l => m =>
255 simpleValidatorWrapper({
256 prevMessage: m,
257 validator: d => d > l,
258 error: `Must be at least ${l}.`,
259 });
261export const validateInSet = (set, error) => m =>
262 simpleValidatorWrapper({
263 prevMessage: m,
264 validator: d => set.has(d),
265 error,
266 });
268export const validateNameInNumberSet = (set, error) => m =>
269 simpleValidatorWrapper({
270 prevMessage: m,
271 validator: p => {
272 const num = patp2dec(p);
273 return set.has(num);
274 },
275 error,
276 });
278export const validatePatpByteLength = byteLength => {
279 return validateLength(patpStringLength(byteLength));
280};
282export const validateMaximumPatpByteLength = byteLength => {
283 return validateMaximumLength(patpStringLength(byteLength));
284};
286export const validateNotNullAddress = validateNotAny([
287 ETH_ZERO_ADDR,
288 ETH_ZERO_ADDR_SHORT,
289]);