on c19ce42 help sign in
bridge/src/walletgen/views/Generate.js
430 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'
8import Button from '../components/Button'
9import ReactSVGComponents from '../components/ReactSVGComponents'
10import PaperCollateralRenderer from 'PaperCollateralRenderer'
12import { MAX_GALAXY, MIN_STAR, MAX_STAR, GEN_STATES } from '../lib/constants'
14const NEXT_STEP_NUM = 6;
16const shipInfo = ship => {
17 const name = ob.patp(ship)
18 const sig = sigil.pour({
19 colorway: ['black', 'white'],
20 patp: name,
21 renderer: ReactSVGComponents,
22 size: 233
23 })
25 return ship < MAX_GALAXY
26 ? galaxyInfo(name, sig)
27 : ship < MAX_STAR
28 ? starInfo(name, sig)
29 : planetInfo(name, sig)
32const infoHeader = ship =>
33 <div className={'row text-600 text-mono'} key={ ship + '-header' }>
34 {ship + ' Wallet'}
35 </div>
37const infoSigil = (ship, sig) => {
38 return (
39 <div className={ '' } key={ 'sigil-' + ship }>
40 { sig }
41 </div>
42 )
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 }
67const infoManagementSeed = ship => {
68 const classOf = ob.clan(ship)
69 const colorClass = getColor(classOf, 'manage')
70 return <Tile colorClass={colorClass} key={`info-${ship}-manage`} col1={`Management Proxy`} col2={''} />
74const infoVotingSeed = ship => {
75 const classOf = ob.clan(ship)
76 const colorClass = getColor(classOf, 'voting')
77 return <Tile colorClass={colorClass} key={`info-${ship}-voting`} col1={`Voting Proxy`} col2={''} />
82const infoSpawnSeed = ship => {
83 const classOf = ob.clan(ship)
84 const colorClass = getColor(classOf, 'spawn')
85 return <Tile colorClass={colorClass} key={`info-${ship}-spawn`} col1={`Spawn Proxy`} col2={''} />
90const infoMasterTicket = ship => {
91 const classOf = ob.clan(ship);
92 const colorClass = getColor(classOf, 'master');
93 return <Tile colorClass={colorClass} key={`info-${ship}-master`} col1={`Master Ticket`} col2={''} />;
94};
98const infoMasterTicketShard = ship => {
99 const classOf = ob.clan(ship);
100 const colorClass = getColor(classOf, 'master');
101 return [
102 <Tile colorClass={colorClass} key={`info-${ship}-master-1`} col1={`Master Ticket Shard 1 of 3`} col2={''} />,
103 <Tile colorClass={colorClass} key={`info-${ship}-master-2`} col1={`Master Ticket Shard 2 of 3`} col2={''} />,
104 <Tile colorClass={colorClass} key={`info-${ship}-master-3`} col1={`Master Ticket Shard 3 of 3`} col2={''} />,
105 ];
106};
110const Tile = (props) => {
111 return (
112 <div
113 className={ `${props.colorClass} white row pl-4 text-mono` }
114 style={{borderBottom:'2px solid black', height: '39px' }}
115 key={props.key}>
117 <div className={'col-md-8 flex align-center text-600'}>{props.col1}</div>
118 <div className={'col-md-4 flex align-center text-500'}>{props.col2}</div>
119 </div>
120 )
124const galaxyInfo = (ship, sig) =>
125 <div className={'mt-8'} key={ 'wallet-' + ship }>
126 { infoHeader(ship) }
127 <div className={'row'} key={ ship + '-body' }>
128 { infoSigil(ship, sig) }
129 <div className={ 'col-md-7 ml-2' } key={ 'info-' + ship }>
130 { infoMasterTicketShard(ship) }
131 { infoSpawnSeed(ship) }
132 { infoManagementSeed(ship) }
133 { infoVotingSeed(ship) }
134 </div>
135 </div>
136 </div>
138const starInfo = (ship, sig) =>
139 <div className={'mt-8'} key={ 'wallet-' + ship }>
140 { infoHeader(ship) }
141 <div className={'row'} key={ ship + '-body' }>
142 { infoSigil(ship, sig) }
143 <div className={ 'col-md-7 ml-2' } key={ 'info-' + ship }>
144 { infoMasterTicket(ship) }
145 { infoSpawnSeed(ship) }
146 { infoManagementSeed(ship) }
147 </div>
148 </div>
149 </div>
151const planetInfo = (ship, sig) =>
152 <div className={'mt-8'} key={ 'wallet-' + ship }>
153 { infoHeader(ship) }
154 <div className={'row'} key={ ship + '-body' }>
155 { infoSigil(ship, sig) }
156 <div className={ 'col-md-7 ml-2' } key={ 'info-' + ship }>
157 { infoMasterTicket(ship) }
158 { infoManagementSeed(ship) }
159 </div>
160 </div>
161 </div>
163class Generate extends React.Component {
164 constructor(props) {
165 super(props)
166 this.handleGenerate = this.handleGenerate.bind(this);
167 }
169 handleGenerate = async () => {
170 const { props } = this;
171 const { setGlobalState } = this.props;
173 setGlobalState({ 'eny.state': GEN_STATES.ENY_PENDING });
175 const ships = [
176 ...props['profile.value'].galaxies,
177 ...props['profile.value'].stars,
178 ...props['profile.value'].planets,
179 ];
181 const enyPromises = ships.map(async ship => {
182 return {
183 ship,
184 ticket: await wg.makeTicket(ship),
185 };
186 });
188 const enyTable = await Promise.all(enyPromises);
190 setGlobalState({
191 'eny.value': enyTable,
192 'eny.state': GEN_STATES.ENY_SUCCESS,
193 'wallets.state': GEN_STATES.DERIVATION_PENDING,
194 });
196 await this.forciblyExecuteArgon2Once(enyTable);
198 const walletPromises = enyTable.map(async ({ ship, ticket }) => {
199 const boot = ship < MIN_STAR || ship > MAX_STAR;
200 return {
201 ship,
202 wallet: await wg.generateWallet(ship, ticket, boot),
203 };
204 });
206 const wallets = await Promise.all(walletPromises);
208 const reshapedWallet = wallets.reduce((acc, {ship, wallet}) => ({...acc, [ship]: wallet }), {})
210 setGlobalState({
211 'wallets.value': reshapedWallet,
212 'wallets.state': GEN_STATES.DERIVATION_SUCCESS,
213 'paperCollateral.state': GEN_STATES.PAPER_PENDING,
214 });
215 }
219 forciblyExecuteArgon2Once = async enyTable => {
220 // NB (jtobin):
221 //
222 // following is some insane thing required so as not to run into bizarre
223 // emscripten errors
224 if (enyTable.length === 0) {
225 throw new Error('no ship information provided')
226 } else {
227 let throwawayEntry = enyTable[0]
228 // eslint-disable-next-line
229 let throwaway = await genWallet(throwawayEntry.ship, throwawayEntry.ticket, () => {})
230 };
231 }
235 render() {
236 const { props } = this;
237 const { setGlobalState } = this.props;
239 const {
240 ENY_NOSTART,
241 ENY_PENDING,
242 ENY_SUCCESS,
243 ENY_FAILURE,
244 DERIVATION_NOSTART,
245 DERIVATION_PENDING,
246 DERIVATION_SUCCESS,
247 DERIVATION_FAILURE,
248 PAPER_NOSTART,
249 PAPER_PENDING,
250 PAPER_SUCCESS,
251 PAPER_FAILURE,
252 } = GEN_STATES;
254 const es = props['eny.state'];
255 const ws = props['wallets.state'];
256 const ps = props['paperCollateral.state'];
259 const ALL_SUCCESS =
260 es === ENY_SUCCESS &&
261 ws === DERIVATION_SUCCESS &&
262 ps === PAPER_SUCCESS;
264 const NONE_START =
265 es === ENY_NOSTART &&
266 ws === DERIVATION_NOSTART &&
267 ps === PAPER_NOSTART;
269 const ANY_ERROR =
270 es === ENY_FAILURE ||
271 ws === DERIVATION_FAILURE ||
272 ps === PAPER_FAILURE;
274 const ANY_PENDING =
275 es === ENY_PENDING ||
276 ws === DERIVATION_PENDING ||
277 ps === PAPER_PENDING;
279 const genButtonClass = classnames({
280 'bg-blue white': true,
281 'bg-green noClick black': ALL_SUCCESS,
282 });
284 const entropyBarClassnames = classnames({
285 'bg-blue': es === ENY_SUCCESS,
286 'bg-lightBlue': es === ENY_PENDING,
287 'bg-gray': es === ENY_NOSTART,
288 'bg-orange': es === ENY_FAILURE,
289 'bg-green': ALL_SUCCESS,
290 });
292 const walletsBarClassnames = classnames({
293 'bg-blue': ws === DERIVATION_SUCCESS,
294 'bg-lightBlue': ws === DERIVATION_PENDING,
295 'bg-gray': ws === DERIVATION_NOSTART,
296 'bg-orange': ws === DERIVATION_FAILURE,
297 'bg-green': ALL_SUCCESS,
298 });
300 const paperBarClassnames = classnames({
301 'bg-blue': ps === PAPER_SUCCESS,
302 'bg-lightBlue': ps === PAPER_PENDING,
303 'bg-gray': ps === PAPER_NOSTART,
304 'bg-orange': ps === PAPER_FAILURE,
305 'bg-green': ALL_SUCCESS,
306 });
308 const isGenerateButtonDisabled = () => {
309 if (NONE_START === true) return false;
310 if (ANY_PENDING === true) return true;
311 if (ANY_ERROR === true) return false;
312 if (ALL_SUCCESS === true) return true;
313 };
315 const getGenButtonText = () => {
316 // const N = props['wallets.genCounter'] + 1;
317 // const X = props['eny.value'].length;
318 let r = '';
319 if (NONE_START === true) r = 'Generate All';
320 if (es === ENY_PENDING) r = 'Generating Entropy';
321 // if (ws === DERIVATION_PENDING) r = `Generating Wallet ${N} / ${X}`;
322 if (ws === DERIVATION_PENDING) r = `Generating Wallets`;
323 if (ps === PAPER_PENDING) r = 'Making Paper Collateral';
324 if (ALL_SUCCESS === true) r = 'Success.';
325 if (ANY_ERROR === true) r = 'Try Again';
326 return r;
327 }
329 const disableGenerateButton = isGenerateButtonDisabled();
330 const genText = getGenButtonText();
332 const nextButtonClass = classnames({
333 'bg-blue white': ALL_SUCCESS === true,
334 'bg-gray black': ALL_SUCCESS !== true,
335 })
337 const nextButtonDisabled = ws !== ALL_SUCCESS;
339 return(
340 <div className={'row'}>
341 <div className={'col-md-8'}>
342 <div className={'col-md-8'}>
343 <h2>{`Review your paper wallet storage recommendations.`}</h2>
344 <p>
345 {`Each ship is placed into its own wallet with proxies that differ in
346 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.`}
347 </p>
348 </div>
350 { props['profile.value'].galaxies.map(ship => shipInfo(ship)) }
351 { props['profile.value'].stars.map(ship => shipInfo(ship)) }
352 { props['profile.value'].planets.map(ship => shipInfo(ship)) }
354 </div>
356 <div className="col-md-3 col-md-offset-1 mt-9">
357 <div className="fixed" style={{width: '230px'}}>
358 <div className="col-md-12 bg-very-high h-2 mt-4"></div>
359 <h4 className="text-600 mt-2">Very High Friction</h4>
360 <p className="text-sm" style={{lineHeight: '16px'}}>
361 Extremely valuable. Cold storage on a 3 part sharded paper wallet. Store each shard in a separate geographic location with protection against fire, water and other elements.
362 </p>
363 <div className="col-md-12 bg-high h-2 mt-4"></div>
364 <h4 className="text-600 mt-2">High Friction</h4>
365 <p className="text-sm" style={{lineHeight: '16px'}}>
366 Very valuable. Cold storage on a paper wallet in a different location than your computer, such as a safe.
367 </p>
368 <div className="col-md-12 bg-medium h-2 mt-4"></div>
369 <h4 className="text-600 mt-2">Medium Friction</h4>
370 <p className="text-sm" style={{lineHeight: '16px'}}>
371 Store cold in a paper wallet; can be stored in a desk drawer in a trusted environment.
372 </p>
373 <div className="col-md-12 bg-low h-2 mt-4"></div>
374 <h4 className="text-600 mt-2">Low Friction</h4>
375 <p className="text-sm" style={{lineHeight: '16px'}}>
376 Ok to store hot on a trusted networked computer.
377 </p>
378 </div>
379 </div>
381 <div className={'col-md-9 btn-tray col-'}>
382 <h3>{"Got it? Then it's time to generate your wallets."}</h3>
383 <p className={'col-md-8'}>{"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."}</p>
385 <div className={'flex h-12 mt-8 mb-4'}>
386 <Button
387 className={ `${genButtonClass} b-r-1-black w-50 m-0` }
388 text={genText}
389 onClick={ this.handleGenerate }
390 disable={disableGenerateButton} />
391 <div className={`${entropyBarClassnames} f-1 b-r-1-black`} />
392 <div className={`${walletsBarClassnames} f-3 b-r-1-black`} />
393 <div className={`${paperBarClassnames} f-3 b-r-1-black`} />
394 <Button
395 className={ `${nextButtonClass} m-0` }
396 text={'View wallets →'}
397 disable={ nextButtonDisabled }
398 onClick= { () => setGlobalState({ route: '/Download' , 'currentStep': NEXT_STEP_NUM })} />
399 </div>
400 </div>
402 <div className={'extremely-hidden'}>
403 <div className={'text-300'}>{'text-300'}</div>
404 <div className={'text-400'}>{'text-400'}</div>
405 <div className={'text-500'}>{'text-500'}</div>
406 <div className={'text-600'}>{'text-600'}</div>
407 <div className={'text-400 text-mono'}>{'text-400 text-mono'}</div>
408 <div className={'text-600 text-mono'}>{'text-600 text-mono'}</div>
409 <div className={'text-500 h-font'}>{'text-500 h-font'}</div>
410 <div className={'text-600 h-font'}>{'text-600 h-font'}</div>
411 </div>
413 { props['wallets.state'] === GEN_STATES.DERIVATION_SUCCESS
414 ? <PaperCollateralRenderer
415 wallet={props['wallets.value']}
416 className={'extremely-hidden'}
417 callback={data => setGlobalState({
418 'paperCollateral.value': data,
419 'paperCollateral.state': GEN_STATES.PAPER_SUCCESS
420 })}
421 mode={'REGISTRATION'} />
422 : <div />
423 }
425 </div>
426 )
427 }
430export default Generate