1import React from 'react'
2import * as more from 'more-entropy'
3import lodash from 'lodash'
4import * as sigil from 'sigil-js'
5import ob from 'urbit-ob'
6import classnames from 'classnames'
7// temporarily use keygen-js with argon2u that is configured with 520MB
8import * as kg from '../../../node_modules/urbit-key-generation/dist/index'
11import Button from '../components/Button'
12import ReactSVGComponents from '../components/ReactSVGComponents'
13import PaperCollateralRenderer from 'PaperCollateralRenderer'
15import { MAX_GALAXY, MIN_STAR, MAX_STAR, MIN_PLANET,
16 GALAXY_ENTROPY_BITS, STAR_ENTROPY_BITS, PLANET_ENTROPY_BITS,
19 } from '../lib/constants'
21const SEED_LENGTH_BYTES = SEED_ENTROPY_BITS / 8
22const NEXT_STEP_NUM = 6;
25const shipInfo = ship => {
26 const name = ob.patp(ship)
27 const sig = sigil.pour({
28 colorway: ['black', 'white'],
30 renderer: ReactSVGComponents,
34 return ship < MAX_GALAXY
35 ? galaxyInfo(name, sig)
38 : planetInfo(name, sig)
41const infoHeader = ship =>
42 <div className={'row text-600 text-mono'} key={ ship + '-header' }>
46const infoSigil = (ship, sig) => {
48 <div className={ '' } key={ 'sigil-' + ship }>
54const getColor = (classOf, seedType) => {
55 if (classOf === 'galaxy') {
56 if (seedType === 'manage') return 'bg-medium';
57 if (seedType === 'voting') return 'bg-medium';
58 if (seedType === 'spawn') return 'bg-high';
59 if (seedType === 'master') return 'bg-very-high';
60 if (seedType === 'masterShard') return 'bg-very-high';
62 if (classOf === 'star') {
63 if (seedType === 'manage') return 'bg-low';
64 if (seedType === 'spawn') return 'bg-medium';
65 if (seedType === 'master') return 'bg-high';
67 if (classOf === 'planet') {
68 if (seedType === 'manage') return 'bg-low';
69 if (seedType === 'master') return 'bg-medium';
76const infoManagementSeed = ship => {
77 const classOf = ob.clan(ship)
78 const colorClass = getColor(classOf, 'manage')
79 return <Tile colorClass={colorClass} key={`info-${ship}-manage`} col1={`Management Proxy`} col2={''} />
83const infoVotingSeed = ship => {
84 const classOf = ob.clan(ship)
85 const colorClass = getColor(classOf, 'voting')
86 return <Tile colorClass={colorClass} key={`info-${ship}-voting`} col1={`Voting Proxy`} col2={''} />
91const infoSpawnSeed = ship => {
92 const classOf = ob.clan(ship)
93 const colorClass = getColor(classOf, 'spawn')
94 return <Tile colorClass={colorClass} key={`info-${ship}-spawn`} col1={`Spawn Proxy`} col2={''} />
99const infoMasterTicket = ship => {
100 const classOf = ob.clan(ship);
101 const colorClass = getColor(classOf, 'master');
102 return <Tile colorClass={colorClass} key={`info-${ship}-master`} col1={`Master Ticket`} col2={''} />;
107const infoMasterTicketShard = ship => {
108 const classOf = ob.clan(ship);
109 const colorClass = getColor(classOf, 'master');
111 <Tile colorClass={colorClass} key={`info-${ship}-master-1`} col1={`Master Ticket Shard 1 of 3`} col2={''} />,
112 <Tile colorClass={colorClass} key={`info-${ship}-master-2`} col1={`Master Ticket Shard 2 of 3`} col2={''} />,
113 <Tile colorClass={colorClass} key={`info-${ship}-master-3`} col1={`Master Ticket Shard 3 of 3`} col2={''} />,
119const Tile = (props) => {
122 className={ `${props.colorClass} white row pl-4 text-mono` }
123 style={{borderBottom:'2px solid black', height: '39px' }}
126 <div className={'col-md-8 flex align-center text-600'}>{props.col1}</div>
127 <div className={'col-md-4 flex align-center text-500'}>{props.col2}</div>
133const galaxyInfo = (ship, sig) =>
134 <div className={'mt-8'} key={ 'wallet-' + ship }>
136 <div className={'row'} key={ ship + '-body' }>
137 { infoSigil(ship, sig) }
138 <div className={ 'col-md-7 ml-2' } key={ 'info-' + ship }>
139 { infoMasterTicketShard(ship) }
140 { infoSpawnSeed(ship) }
141 { infoManagementSeed(ship) }
142 { infoVotingSeed(ship) }
147const starInfo = (ship, sig) =>
148 <div className={'mt-8'} key={ 'wallet-' + ship }>
150 <div className={'row'} key={ ship + '-body' }>
151 { infoSigil(ship, sig) }
152 <div className={ 'col-md-7 ml-2' } key={ 'info-' + ship }>
153 { infoMasterTicket(ship) }
154 { infoSpawnSeed(ship) }
155 { infoManagementSeed(ship) }
160const planetInfo = (ship, sig) =>
161 <div className={'mt-8'} key={ 'wallet-' + ship }>
163 <div className={'row'} key={ ship + '-body' }>
164 { infoSigil(ship, sig) }
165 <div className={ 'col-md-7 ml-2' } key={ 'info-' + ship }>
166 { infoMasterTicket(ship) }
167 { infoManagementSeed(ship) }
174const makeTicket = ship => {
176 const bits = ship < MIN_STAR
177 ? GALAXY_ENTROPY_BITS
180 : PLANET_ENTROPY_BITS
182 const bytes = bits / 8
183 const some = new Uint8Array(bytes)
184 window.crypto.getRandomValues(some)
186 const gen = new more.Generator()
188 return new Promise((resolve, reject) => {
189 gen.generate(bits, result => {
190 const chunked = lodash.chunk(result, 2)
191 const desired = chunked.slice(0, bytes) // only take required entropy
192 const more = lodash.flatMap(desired, arr => arr[0] ^ arr[1])
193 const entropy = lodash.zipWith(some, more, (x, y) => x ^ y)
194 const buf = Buffer.from(entropy)
195 const patq = ob.hex2patq(buf.toString('hex'))
197 reject('Entropy generation failed')
205const genWallet = async (ship, ticket, cb) => {
207 const boot = ship < MIN_STAR || ship > MAX_STAR;
211 seedSize: SEED_LENGTH_BYTES,
218 const wallet = await kg.generateWallet(config);
220 // This is here to notify the anyone who opens console because the thread
221 // hangs, blocking UI updates so this cannot be doen in the UI
222 console.log('Generating Wallet for ship address: ', ship);
231class Generate extends React.Component {
234 this.handleGenerate = this.handleGenerate.bind(this);
237 handleGenerate = async () => {
238 const { props } = this;
239 const { setGlobalState } = this.props;
241 setGlobalState({ 'eny.state': GEN_STATES.ENY_PENDING });
244 ...props['profile.value'].galaxies,
245 ...props['profile.value'].stars,
246 ...props['profile.value'].planets,
249 const enyPromises = ships.map(async ship => {
252 ticket: await makeTicket(ship),
256 const enyTable = await Promise.all(enyPromises);
259 'eny.value': enyTable,
260 'eny.state': GEN_STATES.ENY_SUCCESS,
261 'wallets.state': GEN_STATES.DERIVATION_PENDING,
264 await this.forciblyExecuteArgon2Once(enyTable);
266 const walletPromises = enyTable.map(async ({ ship, ticket }) => {
269 wallet: await genWallet(ship, ticket),
273 const wallets = await Promise.all(walletPromises);
275 const reshapedWallet = wallets.reduce((acc, {ship, wallet}) => ({...acc, [ship]: wallet }), {})
278 'wallets.value': reshapedWallet,
279 'wallets.state': GEN_STATES.DERIVATION_SUCCESS,
280 'paperCollateral.state': GEN_STATES.PAPER_PENDING,
286 forciblyExecuteArgon2Once = async enyTable => {
289 // following is some insane thing required so as not to run into bizarre
291 if (enyTable.length === 0) {
292 throw new Error('no ship information provided')
294 let throwawayEntry = enyTable[0]
295 // eslint-disable-next-line
296 let throwaway = await genWallet(throwawayEntry.ship, throwawayEntry.ticket, () => {})
303 const { props } = this;
304 const { setGlobalState } = this.props;
321 const es = props['eny.state'];
322 const ws = props['wallets.state'];
323 const ps = props['paperCollateral.state'];
327 es === ENY_SUCCESS &&
328 ws === DERIVATION_SUCCESS &&
329 ps === PAPER_SUCCESS;
332 es === ENY_NOSTART &&
333 ws === DERIVATION_NOSTART &&
334 ps === PAPER_NOSTART;
337 es === ENY_FAILURE ||
338 ws === DERIVATION_FAILURE ||
339 ps === PAPER_FAILURE;
342 es === ENY_PENDING ||
343 ws === DERIVATION_PENDING ||
344 ps === PAPER_PENDING;
346 const genButtonClass = classnames({
347 'bg-blue white': true,
348 'bg-green noClick black': ALL_SUCCESS,
351 const entropyBarClassnames = classnames({
352 'bg-blue': es === ENY_SUCCESS,
353 'bg-lightBlue': es === ENY_PENDING,
354 'bg-gray': es === ENY_NOSTART,
355 'bg-orange': es === ENY_FAILURE,
356 'bg-green': ALL_SUCCESS,
359 const walletsBarClassnames = classnames({
360 'bg-blue': ws === DERIVATION_SUCCESS,
361 'bg-lightBlue': ws === DERIVATION_PENDING,
362 'bg-gray': ws === DERIVATION_NOSTART,
363 'bg-orange': ws === DERIVATION_FAILURE,
364 'bg-green': ALL_SUCCESS,
367 const paperBarClassnames = classnames({
368 'bg-blue': ps === PAPER_SUCCESS,
369 'bg-lightBlue': ps === PAPER_PENDING,
370 'bg-gray': ps === PAPER_NOSTART,
371 'bg-orange': ps === PAPER_FAILURE,
372 'bg-green': ALL_SUCCESS,
375 const isGenerateButtonDisabled = () => {
376 if (NONE_START === true) return false;
377 if (ANY_PENDING === true) return true;
378 if (ANY_ERROR === true) return false;
379 if (ALL_SUCCESS === true) return true;
382 const getGenButtonText = () => {
383 // const N = props['wallets.genCounter'] + 1;
384 // const X = props['eny.value'].length;
386 if (NONE_START === true) r = 'Generate All';
387 if (es === ENY_PENDING) r = 'Generating Entropy';
388 // if (ws === DERIVATION_PENDING) r = `Generating Wallet ${N} / ${X}`;
389 if (ws === DERIVATION_PENDING) r = `Generating Wallets`;
390 if (ps === PAPER_PENDING) r = 'Making Paper Collateral';
391 if (ALL_SUCCESS === true) r = 'Success.';
392 if (ANY_ERROR === true) r = 'Try Again';
396 const disableGenerateButton = isGenerateButtonDisabled();
397 const genText = getGenButtonText();
399 const nextButtonClass = classnames({
400 'bg-blue white': ALL_SUCCESS === true,
401 'bg-gray black': ALL_SUCCESS !== true,
404 const nextButtonDisabled = ws !== ALL_SUCCESS;
407 <div className={'row'}>
408 <div className={'col-md-8'}>
409 <div className={'col-md-8'}>
410 <h2>{`Review your paper wallet storage recommendations.`}</h2>
412 {`Each ship is placed into its own wallet with proxies that differ in
413 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.`}
417 { props['profile.value'].galaxies.map(ship => shipInfo(ship)) }
418 { props['profile.value'].stars.map(ship => shipInfo(ship)) }
419 { props['profile.value'].planets.map(ship => shipInfo(ship)) }
423 <div className="col-md-3 col-md-offset-1 mt-9">
424 <div className="fixed" style={{width: '230px'}}>
425 <div className="col-md-12 bg-very-high h-2 mt-4"></div>
426 <h4 className="text-600 mt-2">Very High Friction</h4>
427 <p className="text-sm" style={{lineHeight: '16px'}}>
428 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.
430 <div className="col-md-12 bg-high h-2 mt-4"></div>
431 <h4 className="text-600 mt-2">High Friction</h4>
432 <p className="text-sm" style={{lineHeight: '16px'}}>
433 Very valuable. Cold storage on a paper wallet in a different location than your computer, such as a safe.
435 <div className="col-md-12 bg-medium h-2 mt-4"></div>
436 <h4 className="text-600 mt-2">Medium Friction</h4>
437 <p className="text-sm" style={{lineHeight: '16px'}}>
438 Store cold in a paper wallet; can be stored in a desk drawer in a trusted environment.
440 <div className="col-md-12 bg-low h-2 mt-4"></div>
441 <h4 className="text-600 mt-2">Low Friction</h4>
442 <p className="text-sm" style={{lineHeight: '16px'}}>
443 Ok to store hot on a trusted networked computer.
448 <div className={'col-md-9 btn-tray col-'}>
449 <h3>{"Got it? Then it's time to generate your wallets."}</h3>
450 <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>
452 <div className={'flex h-12 mt-8 mb-4'}>
454 className={ `${genButtonClass} b-r-1-black w-50 m-0` }
456 onClick={ this.handleGenerate }
457 disable={disableGenerateButton} />
458 <div className={`${entropyBarClassnames} f-1 b-r-1-black`} />
459 <div className={`${walletsBarClassnames} f-3 b-r-1-black`} />
460 <div className={`${paperBarClassnames} f-3 b-r-1-black`} />
462 className={ `${nextButtonClass} m-0` }
463 text={'View wallets →'}
464 disable={ nextButtonDisabled }
465 onClick= { () => setGlobalState({ route: '/Download' , 'currentStep': NEXT_STEP_NUM })} />
469 <div className={'extremely-hidden'}>
470 <div className={'text-300'}>{'text-300'}</div>
471 <div className={'text-400'}>{'text-400'}</div>
472 <div className={'text-500'}>{'text-500'}</div>
473 <div className={'text-600'}>{'text-600'}</div>
474 <div className={'text-400 text-mono'}>{'text-400 text-mono'}</div>
475 <div className={'text-600 text-mono'}>{'text-600 text-mono'}</div>
476 <div className={'text-500 h-font'}>{'text-500 h-font'}</div>
477 <div className={'text-600 h-font'}>{'text-600 h-font'}</div>
480 { props['wallets.state'] === GEN_STATES.DERIVATION_SUCCESS
481 ? <PaperCollateralRenderer
482 wallet={props['wallets.value']}
483 className={'extremely-hidden'}
484 callback={data => setGlobalState({
485 'paperCollateral.value': data,
486 'paperCollateral.state': GEN_STATES.PAPER_SUCCESS
488 mode={'REGISTRATION'} />
497export default Generate
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list