on edf00a4 help sign in
bridge/src/walletgen/views/Generate.js
473 lines 14 kB history raw
1import React from 'react';
2import * as sigil from 'sigil-js';
3import ob from 'urbit-ob';
4import classnames from 'classnames';
5import * as wg from '../lib/lib';
7import Button from '../components/Button';
8import ReactSVGComponents from '../components/ReactSVGComponents';
9import PaperCollateralRenderer from 'PaperCollateralRenderer';
11import { MAX_GALAXY, MIN_STAR, MAX_STAR, GEN_STATES } from '../lib/constants';
13const NEXT_STEP_NUM = 6;
15const shipInfo = ship => {
16 const name = ob.patp(ship);
17 const sig = sigil.pour({
18 colorway: ['black', 'white'],
19 patp: name,
20 renderer: ReactSVGComponents,
21 size: 233,
22 });
24 return ship < MAX_GALAXY
25 ? galaxyInfo(name, sig)
26 : ship < MAX_STAR
27 ? starInfo(name, sig)
28 : planetInfo(name, sig);
29};
31const infoHeader = ship => (
32 <div className={'row text-600 text-mono'} key={ship + '-header'}>
33 {ship + ' Wallet'}
34 </div>
35);
37const infoSigil = (ship, sig) => {
38 return (
39 <div className={''} key={'sigil-' + ship}>
40 {sig}
41 </div>
42 );
43};
45const getColor = (classOf, seedType) => {
46 if (classOf === 'galaxy') {
47 if (seedType === 'manage') return 'bg-medium';
48 if (seedType === 'voting') return 'bg-medium';
49 if (seedType === 'spawn') return 'bg-high';
50 if (seedType === 'master') return 'bg-very-high';
51 if (seedType === 'masterShard') return 'bg-very-high';
52 }
53 if (classOf === 'star') {
54 if (seedType === 'manage') return 'bg-low';
55 if (seedType === 'spawn') return 'bg-medium';
56 if (seedType === 'master') return 'bg-high';
57 }
58 if (classOf === 'planet') {
59 if (seedType === 'manage') return 'bg-low';
60 if (seedType === 'master') return 'bg-medium';
61 }
62};
64const infoManagementSeed = ship => {
65 const classOf = ob.clan(ship);
66 const colorClass = getColor(classOf, 'manage');
67 return (
68 <Tile
69 colorClass={colorClass}
70 key={`info-${ship}-manage`}
71 col1={`Management Proxy`}
72 col2={''}
73 />
74 );
75};
77const infoVotingSeed = ship => {
78 const classOf = ob.clan(ship);
79 const colorClass = getColor(classOf, 'voting');
80 return (
81 <Tile
82 colorClass={colorClass}
83 key={`info-${ship}-voting`}
84 col1={`Voting Proxy`}
85 col2={''}
86 />
87 );
88};
90const infoSpawnSeed = ship => {
91 const classOf = ob.clan(ship);
92 const colorClass = getColor(classOf, 'spawn');
93 return (
94 <Tile
95 colorClass={colorClass}
96 key={`info-${ship}-spawn`}
97 col1={`Spawn Proxy`}
98 col2={''}
99 />
100 );
101};
103const infoMasterTicket = ship => {
104 const classOf = ob.clan(ship);
105 const colorClass = getColor(classOf, 'master');
106 return (
107 <Tile
108 colorClass={colorClass}
109 key={`info-${ship}-master`}
110 col1={`Master Ticket`}
111 col2={''}
112 />
113 );
114};
116const infoMasterTicketShard = ship => {
117 const classOf = ob.clan(ship);
118 const colorClass = getColor(classOf, 'master');
119 return [
120 <Tile
121 colorClass={colorClass}
122 key={`info-${ship}-master-1`}
123 col1={`Master Ticket Shard 1 of 3`}
124 col2={''}
125 />,
126 <Tile
127 colorClass={colorClass}
128 key={`info-${ship}-master-2`}
129 col1={`Master Ticket Shard 2 of 3`}
130 col2={''}
131 />,
132 <Tile
133 colorClass={colorClass}
134 key={`info-${ship}-master-3`}
135 col1={`Master Ticket Shard 3 of 3`}
136 col2={''}
137 />,
138 ];
139};
141const Tile = props => {
142 return (
143 <div
144 className={`${props.colorClass} white row pl-4 text-mono`}
145 style={{ borderBottom: '2px solid black', height: '39px' }}
146 key={props.key}>
147 <div className={'col-md-8 flex align-center text-600'}>{props.col1}</div>
148 <div className={'col-md-4 flex align-center text-500'}>{props.col2}</div>
149 </div>
150 );
151};
153const galaxyInfo = (ship, sig) => (
154 <div className={'mt-8'} key={'wallet-' + ship}>
155 {infoHeader(ship)}
156 <div className={'row'} key={ship + '-body'}>
157 {infoSigil(ship, sig)}
158 <div className={'col-md-7 ml-2'} key={'info-' + ship}>
159 {infoMasterTicketShard(ship)}
160 {infoSpawnSeed(ship)}
161 {infoManagementSeed(ship)}
162 {infoVotingSeed(ship)}
163 </div>
164 </div>
165 </div>
166);
168const starInfo = (ship, sig) => (
169 <div className={'mt-8'} key={'wallet-' + ship}>
170 {infoHeader(ship)}
171 <div className={'row'} key={ship + '-body'}>
172 {infoSigil(ship, sig)}
173 <div className={'col-md-7 ml-2'} key={'info-' + ship}>
174 {infoMasterTicket(ship)}
175 {infoSpawnSeed(ship)}
176 {infoManagementSeed(ship)}
177 </div>
178 </div>
179 </div>
180);
182const planetInfo = (ship, sig) => (
183 <div className={'mt-8'} key={'wallet-' + ship}>
184 {infoHeader(ship)}
185 <div className={'row'} key={ship + '-body'}>
186 {infoSigil(ship, sig)}
187 <div className={'col-md-7 ml-2'} key={'info-' + ship}>
188 {infoMasterTicket(ship)}
189 {infoManagementSeed(ship)}
190 </div>
191 </div>
192 </div>
193);
195class Generate extends React.Component {
196 constructor(props) {
197 super(props);
198 this.handleGenerate = this.handleGenerate.bind(this);
199 }
201 handleGenerate = async () => {
202 const { props } = this;
203 const { setGlobalState } = this.props;
205 setGlobalState({ 'eny.state': GEN_STATES.ENY_PENDING });
207 const ships = [
208 ...props['profile.value'].galaxies,
209 ...props['profile.value'].stars,
210 ...props['profile.value'].planets,
211 ];
213 const enyPromises = ships.map(async ship => {
214 return {
215 ship,
216 ticket: await wg.makeTicket(ship),
217 };
218 });
220 const enyTable = await Promise.all(enyPromises);
222 setGlobalState({
223 'eny.value': enyTable,
224 'eny.state': GEN_STATES.ENY_SUCCESS,
225 'wallets.state': GEN_STATES.DERIVATION_PENDING,
226 });
228 await this.forciblyExecuteArgon2Once(enyTable);
230 const walletPromises = enyTable.map(async ({ ship, ticket }) => {
231 const boot = ship < MIN_STAR || ship > MAX_STAR;
232 return {
233 ship,
234 wallet: await wg.generateWallet(ship, ticket, boot),
235 };
236 });
238 const wallets = await Promise.all(walletPromises);
240 const reshapedWallet = wallets.reduce(
241 (acc, { ship, wallet }) => ({ ...acc, [ship]: wallet }),
242 {}
243 );
245 setGlobalState({
246 'wallets.value': reshapedWallet,
247 'wallets.state': GEN_STATES.DERIVATION_SUCCESS,
248 'paperCollateral.state': GEN_STATES.PAPER_PENDING,
249 });
250 };
252 forciblyExecuteArgon2Once = async enyTable => {
253 // NB (jtobin):
254 //
255 // following is some insane thing required so as not to run into bizarre
256 // emscripten errors
257 if (enyTable.length === 0) {
258 throw new Error('no ship information provided');
259 } else {
260 let throwawayEntry = enyTable[0];
261 // eslint-disable-next-line
262 let throwaway = await genWallet(
263 throwawayEntry.ship,
264 throwawayEntry.ticket,
265 () => {}
266 );
267 }
268 };
270 render() {
271 const { props } = this;
272 const { setGlobalState } = this.props;
274 const {
275 ENY_NOSTART,
276 ENY_PENDING,
277 ENY_SUCCESS,
278 ENY_FAILURE,
279 DERIVATION_NOSTART,
280 DERIVATION_PENDING,
281 DERIVATION_SUCCESS,
282 DERIVATION_FAILURE,
283 PAPER_NOSTART,
284 PAPER_PENDING,
285 PAPER_SUCCESS,
286 PAPER_FAILURE,
287 } = GEN_STATES;
289 const es = props['eny.state'];
290 const ws = props['wallets.state'];
291 const ps = props['paperCollateral.state'];
293 const ALL_SUCCESS =
294 es === ENY_SUCCESS && ws === DERIVATION_SUCCESS && ps === PAPER_SUCCESS;
296 const NONE_START =
297 es === ENY_NOSTART && ws === DERIVATION_NOSTART && ps === PAPER_NOSTART;
299 const ANY_ERROR =
300 es === ENY_FAILURE || ws === DERIVATION_FAILURE || ps === PAPER_FAILURE;
302 const ANY_PENDING =
303 es === ENY_PENDING || ws === DERIVATION_PENDING || ps === PAPER_PENDING;
305 const genButtonClass = classnames({
306 'bg-blue white': true,
307 'bg-green noClick black': ALL_SUCCESS,
308 });
310 const entropyBarClassnames = classnames({
311 'bg-blue': es === ENY_SUCCESS,
312 'bg-lightBlue': es === ENY_PENDING,
313 'bg-gray': es === ENY_NOSTART,
314 'bg-orange': es === ENY_FAILURE,
315 'bg-green': ALL_SUCCESS,
316 });
318 const walletsBarClassnames = classnames({
319 'bg-blue': ws === DERIVATION_SUCCESS,
320 'bg-lightBlue': ws === DERIVATION_PENDING,
321 'bg-gray': ws === DERIVATION_NOSTART,
322 'bg-orange': ws === DERIVATION_FAILURE,
323 'bg-green': ALL_SUCCESS,
324 });
326 const paperBarClassnames = classnames({
327 'bg-blue': ps === PAPER_SUCCESS,
328 'bg-lightBlue': ps === PAPER_PENDING,
329 'bg-gray': ps === PAPER_NOSTART,
330 'bg-orange': ps === PAPER_FAILURE,
331 'bg-green': ALL_SUCCESS,
332 });
334 const isGenerateButtonDisabled = () => {
335 if (NONE_START === true) return false;
336 if (ANY_PENDING === true) return true;
337 if (ANY_ERROR === true) return false;
338 if (ALL_SUCCESS === true) return true;
339 };
341 const getGenButtonText = () => {
342 // const N = props['wallets.genCounter'] + 1;
343 // const X = props['eny.value'].length;
344 let r = '';
345 if (NONE_START === true) r = 'Generate All';
346 if (es === ENY_PENDING) r = 'Generating Entropy';
347 // if (ws === DERIVATION_PENDING) r = `Generating Wallet ${N} / ${X}`;
348 if (ws === DERIVATION_PENDING) r = `Generating Wallets`;
349 if (ps === PAPER_PENDING) r = 'Making Paper Collateral';
350 if (ALL_SUCCESS === true) r = 'Success.';
351 if (ANY_ERROR === true) r = 'Try Again';
352 return r;
353 };
355 const disableGenerateButton = isGenerateButtonDisabled();
356 const genText = getGenButtonText();
358 const nextButtonClass = classnames({
359 'bg-blue white': ALL_SUCCESS === true,
360 'bg-gray black': ALL_SUCCESS !== true,
361 });
363 const nextButtonDisabled = ws !== ALL_SUCCESS;
365 return (
366 <div className={'row'}>
367 <div className={'col-md-8'}>
368 <div className={'col-md-8'}>
369 <h2>{`Review your paper wallet storage recommendations.`}</h2>
370 <p>
371 {`Each ship is placed into its own wallet with proxies that differ in
372 importance and security requirements. These are listed as examples below. A key to our security recommendations can be found to the right of the page.`}
373 </p>
374 </div>
376 {props['profile.value'].galaxies.map(ship => shipInfo(ship))}
377 {props['profile.value'].stars.map(ship => shipInfo(ship))}
378 {props['profile.value'].planets.map(ship => shipInfo(ship))}
379 </div>
381 <div className="col-md-3 col-md-offset-1 mt-9">
382 <div className="fixed" style={{ width: '230px' }}>
383 <div className="col-md-12 bg-very-high h-2 mt-4" />
384 <h4 className="text-600 mt-2">Very High Friction</h4>
385 <p className="text-sm" style={{ lineHeight: '16px' }}>
386 Extremely valuable. Cold storage on a 3 part sharded paper wallet.
387 Store each shard in a separate geographic location with protection
388 against fire, water and other elements.
389 </p>
390 <div className="col-md-12 bg-high h-2 mt-4" />
391 <h4 className="text-600 mt-2">High Friction</h4>
392 <p className="text-sm" style={{ lineHeight: '16px' }}>
393 Very valuable. Cold storage on a paper wallet in a different
394 location than your computer, such as a safe.
395 </p>
396 <div className="col-md-12 bg-medium h-2 mt-4" />
397 <h4 className="text-600 mt-2">Medium Friction</h4>
398 <p className="text-sm" style={{ lineHeight: '16px' }}>
399 Store cold in a paper wallet; can be stored in a desk drawer in a
400 trusted environment.
401 </p>
402 <div className="col-md-12 bg-low h-2 mt-4" />
403 <h4 className="text-600 mt-2">Low Friction</h4>
404 <p className="text-sm" style={{ lineHeight: '16px' }}>
405 Ok to store hot on a trusted networked computer.
406 </p>
407 </div>
408 </div>
410 <div className={'col-md-9 btn-tray col-'}>
411 <h3>{"Got it? Then it's time to generate your wallets."}</h3>
412 <p className={'col-md-8'}>
413 {
414 'If you have a large number of ships, this may take several minutes and the page may hang or become unresponsive. If you close this browser window, you will have to start from the beginning.'
415 }
416 </p>
418 <div className={'flex h-12 mt-8 mb-4'}>
419 <Button
420 className={`${genButtonClass} b-r-1-black w-50 m-0`}
421 text={genText}
422 onClick={this.handleGenerate}
423 disable={disableGenerateButton}
424 />
425 <div className={`${entropyBarClassnames} f-1 b-r-1-black`} />
426 <div className={`${walletsBarClassnames} f-3 b-r-1-black`} />
427 <div className={`${paperBarClassnames} f-3 b-r-1-black`} />
428 <Button
429 className={`${nextButtonClass} m-0`}
430 text={'View wallets →'}
431 disable={nextButtonDisabled}
432 onClick={() =>
433 setGlobalState({
434 route: '/Download',
435 currentStep: NEXT_STEP_NUM,
436 })
437 }
438 />
439 </div>
440 </div>
442 <div className={'extremely-hidden'}>
443 <div className={'text-300'}>{'text-300'}</div>
444 <div className={'text-400'}>{'text-400'}</div>
445 <div className={'text-500'}>{'text-500'}</div>
446 <div className={'text-600'}>{'text-600'}</div>
447 <div className={'text-400 text-mono'}>{'text-400 text-mono'}</div>
448 <div className={'text-600 text-mono'}>{'text-600 text-mono'}</div>
449 <div className={'text-500 h-font'}>{'text-500 h-font'}</div>
450 <div className={'text-600 h-font'}>{'text-600 h-font'}</div>
451 </div>
453 {props['wallets.state'] === GEN_STATES.DERIVATION_SUCCESS ? (
454 <PaperCollateralRenderer
455 wallet={props['wallets.value']}
456 className={'extremely-hidden'}
457 callback={data =>
458 setGlobalState({
459 'paperCollateral.value': data,
460 'paperCollateral.state': GEN_STATES.PAPER_SUCCESS,
461 })
462 }
463 mode={'REGISTRATION'}
464 />
465 ) : (
466 <div />
467 )}
468 </div>
469 );
470 }
473export default Generate;