on af19266 help sign in
bridge/src/lib/validators.js
251 lines 6.2 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';
9// NOTE: do not use the /g modifier for these regexes
10// https://stackoverflow.com/a/21373261
11// https://stackoverflow.com/a/1520853
13// via: https://emailregex.com/
14const 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// is this 64 hex chars?
17const plain64CharHexValue = /[0-9A-Fa-f]{64}/;
19// is this only hex values with a 0x prefix?
20const isHexString = /0x[0-9A-Fa-f]/;
22// Wraps single validation functions in a controlled and predictable way.
23export const simpleValidatorWrapper = ({
24 prevMessage,
25 transform = identity,
26 validator,
27 error,
28}) => {
29 // If a previous validation has already failed, skip this validation and
30 // return the prev message to the next stage in the validation function chain.
31 // Failed validations should drop all the way down the chain and drop out of
32 // the output.
33 if (!prevMessage.pass) {
34 return prevMessage;
35 }
37 // Run the validator and return the result.
38 const pass = validator(prevMessage.data);
39 const data = pass ? transform(prevMessage.data) : prevMessage.data;
41 return newMessage(data, pass, !pass && error);
42};
44// Validation message
45// {
46// data: ...
47// pass: ...
48// error: ...
49// }
50// Creates a new validation message in a uniform way.
51const newMessage = (data, pass, error) => ({
52 // The input data
53 data,
54 // Has the data passed validation?
55 pass,
56 // If data has failed a validator, the error message goes here.
57 error,
58});
60// A validator that always passes.
61export const kDefaultValidator = data => newMessage(data, true, null);
63// Validates a bip39 mnemonic
64export const validateMnemonic = m =>
65 simpleValidatorWrapper({
66 prevMessage: m,
67 validator: d => bip39.validateMnemonic(d),
68 error: 'This is not a valid mnemonic.',
69 });
71// Checks an empty field
72export const validateNotEmpty = m =>
73 simpleValidatorWrapper({
74 prevMessage: m,
75 validator: d => {
76 try {
77 return d.length > 1;
78 } catch {
79 return false;
80 }
81 },
82 error: 'This field is required.',
83 });
85// Checks if a patp is a valid galaxy
86export const validateGalaxy = m =>
87 simpleValidatorWrapper({
88 prevMessage: m,
89 validator: d => {
90 let point;
91 try {
92 point = patp2dec(d);
93 return point >= 0 && point < 256;
94 } catch (e) {
95 return false;
96 }
97 },
98 error: 'This is not a valid galaxy',
99 });
101export const validatePoint = m =>
102 simpleValidatorWrapper({
103 prevMessage: m,
104 validator: d => {
105 try {
106 return ob.isValidPatp(d);
107 } catch (e) {
108 return false;
109 }
110 },
111 error: 'This is not a valid point',
112 });
114export const validateTicket = m =>
115 simpleValidatorWrapper({
116 prevMessage: m,
117 validator: d => {
118 try {
119 return ob.isValidPatq(d);
120 } catch (e) {
121 return false;
122 }
123 },
124 error: 'This is not a valid ticket',
125 });
127export const validateShard = m =>
128 simpleValidatorWrapper({
129 prevMessage: m,
130 validator: d => {
131 try {
132 return ob.isValidPatq(d);
133 } catch (e) {
134 return false;
135 }
136 },
137 error: 'This is not a valid shard',
138 });
140export const validateOneOf = (options = []) => m =>
141 simpleValidatorWrapper({
142 prevMessage: m,
143 validator: d => includes(options, d),
144 error: `Is not a valid option.`,
145 });
147export const validateHexString = m =>
148 simpleValidatorWrapper({
149 prevMessage: m,
150 validator: d => isHexString.test(d),
151 error: 'This is not a valid hex string',
152 });
154// @deprecate
155export const validateNetworkKey = m =>
156 simpleValidatorWrapper({
157 prevMessage: m,
158 validator: d => plain64CharHexValue.test(d),
159 error: 'This is not a valid network key',
160 });
162// @deprecate
163export const validateNetworkSeed = m =>
164 simpleValidatorWrapper({
165 prevMessage: m,
166 validator: d => plain64CharHexValue.test(d),
167 error: 'This is not a valid network seed',
168 });
170// Checks if a string is a valid ethereum address
171export const validateEthereumAddress = m =>
172 simpleValidatorWrapper({
173 prevMessage: m,
174 validator: d => isValidAddress(d),
175 error: 'This is not a valid Ethereum address',
176 });
178export const validateEmail = m =>
179 simpleValidatorWrapper({
180 prevMessage: m,
181 validator: d => emailRegExp.test(d),
182 error: 'This is not a valid email address',
183 });
185export const validateInt = m =>
186 simpleValidatorWrapper({
187 prevMessage: m,
188 transform: d => parseInt(d, 10),
189 validator: d => {
190 try {
191 parseInt(d, 10);
192 return;
193 } catch {
194 return false;
195 }
196 },
197 error: 'This is not a valid number',
198 });
200export const validateExactly = (value, error) => m =>
201 simpleValidatorWrapper({
202 prevMessage: m,
203 validator: d => d === value,
204 error,
205 });
207export const validateNotEqual = (values = []) => m =>
208 simpleValidatorWrapper({
209 prevMessage: m,
210 validator: d => !values.includes(d),
211 error: `Cannot be ${m.data}`,
212 });
214export const validateLength = l => m =>
215 simpleValidatorWrapper({
216 prevMessage: m,
217 validator: d => {
218 try {
219 return d.length === l;
220 } catch {
221 return false;
222 }
223 },
224 error: `Must be exactly ${l} characters`,
225 });
227export const validateMaximumLength = l => m =>
228 simpleValidatorWrapper({
229 prevMessage: m,
230 validator: d => {
231 try {
232 return d.length <= l;
233 } catch {
234 return false;
235 }
236 },
237 error: `Must be ${l} characters or fewer.`,
238 });
240export const validatePatpByteLength = byteLength => {
241 return validateLength(patpStringLength(byteLength));
242};
244export const validateMaximumPatpByteLength = byteLength => {
245 return validateMaximumLength(patpStringLength(byteLength));
246};
248export const validateNotNullAddress = validateNotEqual(
249 [ETH_ZERO_ADDR, ETH_ZERO_ADDR_SHORT],
250 'Cannot be 0x0'
251);