import React from 'react' import * as more from 'more-entropy' import lodash from 'lodash' import * as sigil from 'sigil-js' import ob from 'urbit-ob' import classnames from 'classnames' // temporarily use keygen-js with argon2u that is configured with 520MB import * as kg from '../../../node_modules/urbit-key-generation/dist/index' import Button from '../components/Button' import ReactSVGComponents from '../components/ReactSVGComponents' import PaperCollateralRenderer from 'PaperCollateralRenderer' import { MAX_GALAXY, MIN_STAR, MAX_STAR, MIN_PLANET, GALAXY_ENTROPY_BITS, STAR_ENTROPY_BITS, PLANET_ENTROPY_BITS, SEED_ENTROPY_BITS, GEN_STATES } from '../lib/constants' const SEED_LENGTH_BYTES = SEED_ENTROPY_BITS / 8 const NEXT_STEP_NUM = 6; const shipInfo = ship => { const name = ob.patp(ship) const sig = sigil.pour({ colorway: ['black', 'white'], patp: name, renderer: ReactSVGComponents, size: 233 }) return ship < MAX_GALAXY ? galaxyInfo(name, sig) : ship < MAX_STAR ? starInfo(name, sig) : planetInfo(name, sig) } const infoHeader = ship =>
{ship + ' Wallet'}
const infoSigil = (ship, sig) => { return (
{ sig }
) } const getColor = (classOf, seedType) => { if (classOf === 'galaxy') { if (seedType === 'manage') return 'bg-medium'; if (seedType === 'voting') return 'bg-medium'; if (seedType === 'spawn') return 'bg-high'; if (seedType === 'master') return 'bg-very-high'; if (seedType === 'masterShard') return 'bg-very-high'; } if (classOf === 'star') { if (seedType === 'manage') return 'bg-low'; if (seedType === 'spawn') return 'bg-medium'; if (seedType === 'master') return 'bg-high'; } if (classOf === 'planet') { if (seedType === 'manage') return 'bg-low'; if (seedType === 'master') return 'bg-medium'; } } const infoManagementSeed = ship => { const classOf = ob.clan(ship) const colorClass = getColor(classOf, 'manage') return } const infoVotingSeed = ship => { const classOf = ob.clan(ship) const colorClass = getColor(classOf, 'voting') return } const infoSpawnSeed = ship => { const classOf = ob.clan(ship) const colorClass = getColor(classOf, 'spawn') return } const infoMasterTicket = ship => { const classOf = ob.clan(ship); const colorClass = getColor(classOf, 'master'); return ; }; const infoMasterTicketShard = ship => { const classOf = ob.clan(ship); const colorClass = getColor(classOf, 'master'); return [ , , , ]; }; const Tile = (props) => { return (
{props.col1}
{props.col2}
) } const galaxyInfo = (ship, sig) =>
{ infoHeader(ship) }
{ infoSigil(ship, sig) }
{ infoMasterTicketShard(ship) } { infoSpawnSeed(ship) } { infoManagementSeed(ship) } { infoVotingSeed(ship) }
const starInfo = (ship, sig) =>
{ infoHeader(ship) }
{ infoSigil(ship, sig) }
{ infoMasterTicket(ship) } { infoSpawnSeed(ship) } { infoManagementSeed(ship) }
const planetInfo = (ship, sig) =>
{ infoHeader(ship) }
{ infoSigil(ship, sig) }
{ infoMasterTicket(ship) } { infoManagementSeed(ship) }
const makeTicket = ship => { const bits = ship < MIN_STAR ? GALAXY_ENTROPY_BITS : ship < MIN_PLANET ? STAR_ENTROPY_BITS : PLANET_ENTROPY_BITS const bytes = bits / 8 const some = new Uint8Array(bytes) window.crypto.getRandomValues(some) const gen = new more.Generator() return new Promise((resolve, reject) => { gen.generate(bits, result => { const chunked = lodash.chunk(result, 2) const desired = chunked.slice(0, bytes) // only take required entropy const more = lodash.flatMap(desired, arr => arr[0] ^ arr[1]) const entropy = lodash.zipWith(some, more, (x, y) => x ^ y) const buf = Buffer.from(entropy) const patq = ob.hex2patq(buf.toString('hex')) resolve(patq) reject('Entropy generation failed') }) }) } const genWallet = async (ship, ticket, cb) => { const boot = ship < MIN_STAR || ship > MAX_STAR; const config = { ticket: ticket, seedSize: SEED_LENGTH_BYTES, ship: ship, password: '', revisions: {}, boot: boot }; const wallet = await kg.generateWallet(config); // This is here to notify the anyone who opens console because the thread // hangs, blocking UI updates so this cannot be doen in the UI console.log('Generating Wallet for ship address: ', ship); return wallet; } class Generate extends React.Component { constructor(props) { super(props) this.handleGenerate = this.handleGenerate.bind(this); } handleGenerate = async () => { const { props } = this; const { setGlobalState } = this.props; setGlobalState({ 'eny.state': GEN_STATES.ENY_PENDING }); const ships = [ ...props['profile.value'].galaxies, ...props['profile.value'].stars, ...props['profile.value'].planets, ]; const enyPromises = ships.map(async ship => { return { ship, ticket: await makeTicket(ship), }; }); const enyTable = await Promise.all(enyPromises); setGlobalState({ 'eny.value': enyTable, 'eny.state': GEN_STATES.ENY_SUCCESS, 'wallets.state': GEN_STATES.DERIVATION_PENDING, }); await this.forciblyExecuteArgon2Once(enyTable); const walletPromises = enyTable.map(async ({ ship, ticket }) => { return { ship, wallet: await genWallet(ship, ticket), }; }); const wallets = await Promise.all(walletPromises); const reshapedWallet = wallets.reduce((acc, {ship, wallet}) => ({...acc, [ship]: wallet }), {}) setGlobalState({ 'wallets.value': reshapedWallet, 'wallets.state': GEN_STATES.DERIVATION_SUCCESS, 'paperCollateral.state': GEN_STATES.PAPER_PENDING, }); } forciblyExecuteArgon2Once = async enyTable => { // NB (jtobin): // // following is some insane thing required so as not to run into bizarre // emscripten errors if (enyTable.length === 0) { throw new Error('no ship information provided') } else { let throwawayEntry = enyTable[0] // eslint-disable-next-line let throwaway = await genWallet(throwawayEntry.ship, throwawayEntry.ticket, () => {}) }; } render() { const { props } = this; const { setGlobalState } = this.props; const { ENY_NOSTART, ENY_PENDING, ENY_SUCCESS, ENY_FAILURE, DERIVATION_NOSTART, DERIVATION_PENDING, DERIVATION_SUCCESS, DERIVATION_FAILURE, PAPER_NOSTART, PAPER_PENDING, PAPER_SUCCESS, PAPER_FAILURE, } = GEN_STATES; const es = props['eny.state']; const ws = props['wallets.state']; const ps = props['paperCollateral.state']; const ALL_SUCCESS = es === ENY_SUCCESS && ws === DERIVATION_SUCCESS && ps === PAPER_SUCCESS; const NONE_START = es === ENY_NOSTART && ws === DERIVATION_NOSTART && ps === PAPER_NOSTART; const ANY_ERROR = es === ENY_FAILURE || ws === DERIVATION_FAILURE || ps === PAPER_FAILURE; const ANY_PENDING = es === ENY_PENDING || ws === DERIVATION_PENDING || ps === PAPER_PENDING; const genButtonClass = classnames({ 'bg-blue white': true, 'bg-green noClick black': ALL_SUCCESS, }); const entropyBarClassnames = classnames({ 'bg-blue': es === ENY_SUCCESS, 'bg-lightBlue': es === ENY_PENDING, 'bg-gray': es === ENY_NOSTART, 'bg-orange': es === ENY_FAILURE, 'bg-green': ALL_SUCCESS, }); const walletsBarClassnames = classnames({ 'bg-blue': ws === DERIVATION_SUCCESS, 'bg-lightBlue': ws === DERIVATION_PENDING, 'bg-gray': ws === DERIVATION_NOSTART, 'bg-orange': ws === DERIVATION_FAILURE, 'bg-green': ALL_SUCCESS, }); const paperBarClassnames = classnames({ 'bg-blue': ps === PAPER_SUCCESS, 'bg-lightBlue': ps === PAPER_PENDING, 'bg-gray': ps === PAPER_NOSTART, 'bg-orange': ps === PAPER_FAILURE, 'bg-green': ALL_SUCCESS, }); const isGenerateButtonDisabled = () => { if (NONE_START === true) return false; if (ANY_PENDING === true) return true; if (ANY_ERROR === true) return false; if (ALL_SUCCESS === true) return true; }; const getGenButtonText = () => { // const N = props['wallets.genCounter'] + 1; // const X = props['eny.value'].length; let r = ''; if (NONE_START === true) r = 'Generate All'; if (es === ENY_PENDING) r = 'Generating Entropy'; // if (ws === DERIVATION_PENDING) r = `Generating Wallet ${N} / ${X}`; if (ws === DERIVATION_PENDING) r = `Generating Wallets`; if (ps === PAPER_PENDING) r = 'Making Paper Collateral'; if (ALL_SUCCESS === true) r = 'Success.'; if (ANY_ERROR === true) r = 'Try Again'; return r; } const disableGenerateButton = isGenerateButtonDisabled(); const genText = getGenButtonText(); const nextButtonClass = classnames({ 'bg-blue white': ALL_SUCCESS === true, 'bg-gray black': ALL_SUCCESS !== true, }) const nextButtonDisabled = ws !== ALL_SUCCESS; return(

{`Review your paper wallet storage recommendations.`}

{`Each ship is placed into its own wallet with proxies that differ in 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.`}

{ props['profile.value'].galaxies.map(ship => shipInfo(ship)) } { props['profile.value'].stars.map(ship => shipInfo(ship)) } { props['profile.value'].planets.map(ship => shipInfo(ship)) }

Very High Friction

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.

High Friction

Very valuable. Cold storage on a paper wallet in a different location than your computer, such as a safe.

Medium Friction

Store cold in a paper wallet; can be stored in a desk drawer in a trusted environment.

Low Friction

Ok to store hot on a trusted networked computer.

{"Got it? Then it's time to generate your wallets."}

{"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."}

{'text-300'}
{'text-400'}
{'text-500'}
{'text-600'}
{'text-400 text-mono'}
{'text-600 text-mono'}
{'text-500 h-font'}
{'text-600 h-font'}
{ props['wallets.state'] === GEN_STATES.DERIVATION_SUCCESS ? setGlobalState({ 'paperCollateral.value': data, 'paperCollateral.state': GEN_STATES.PAPER_SUCCESS })} mode={'REGISTRATION'} /> :
}
) } } export default Generate