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