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